aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Gabriel <mike.gabriel@das-netzwerkteam.de>2011-11-19 15:59:16 +0100
committerMike Gabriel <mike.gabriel@das-netzwerkteam.de>2011-11-19 15:59:16 +0100
commita48361b11a5abb5a345dac5ec83a8f56c4d50b74 (patch)
treea16fb870a072450bb45ee09ac4df9887bfa7f98e
parent9997e13bb583de4012914006c7507839a4e11227 (diff)
parent232dfc41d41390bfffa75ec2ed065c109fa03a0e (diff)
downloadnx-libs-a48361b11a5abb5a345dac5ec83a8f56c4d50b74.tar.gz
nx-libs-a48361b11a5abb5a345dac5ec83a8f56c4d50b74.tar.bz2
nx-libs-a48361b11a5abb5a345dac5ec83a8f56c4d50b74.zip
Merge branch 'nxcomp'
-rw-r--r--nxcomp/ActionCache.cpp39
-rw-r--r--nxcomp/ActionCache.h41
-rw-r--r--nxcomp/ActionCacheCompat.h45
-rw-r--r--nxcomp/Agent.cpp72
-rw-r--r--nxcomp/Agent.h243
-rw-r--r--nxcomp/Alpha.cpp126
-rw-r--r--nxcomp/Alpha.h27
-rw-r--r--nxcomp/Auth.cpp650
-rw-r--r--nxcomp/Auth.h119
-rw-r--r--nxcomp/Bitmap.cpp106
-rw-r--r--nxcomp/Bitmap.h28
-rw-r--r--nxcomp/BlockCache.cpp69
-rw-r--r--nxcomp/BlockCache.h59
-rw-r--r--nxcomp/BlockCacheSet.cpp135
-rw-r--r--nxcomp/BlockCacheSet.h41
-rw-r--r--nxcomp/CHANGELOG3778
-rw-r--r--nxcomp/COPYING339
-rw-r--r--nxcomp/ChangeGC.cpp172
-rw-r--r--nxcomp/ChangeGC.h177
-rw-r--r--nxcomp/ChangeGCCompat.cpp131
-rw-r--r--nxcomp/ChangeGCCompat.h170
-rw-r--r--nxcomp/ChangeProperty.cpp179
-rw-r--r--nxcomp/ChangeProperty.h181
-rw-r--r--nxcomp/Channel.cpp2125
-rw-r--r--nxcomp/Channel.h656
-rw-r--r--nxcomp/ChannelCache.cpp56
-rw-r--r--nxcomp/ChannelCache.h60
-rw-r--r--nxcomp/ChannelStore.h46
-rw-r--r--nxcomp/CharCache.cpp61
-rw-r--r--nxcomp/CharCache.h83
-rw-r--r--nxcomp/Children.cpp1038
-rw-r--r--nxcomp/ClearArea.cpp113
-rw-r--r--nxcomp/ClearArea.h174
-rw-r--r--nxcomp/ClientCache.cpp389
-rw-r--r--nxcomp/ClientCache.h429
-rw-r--r--nxcomp/ClientChannel.cpp8244
-rw-r--r--nxcomp/ClientChannel.h466
-rw-r--r--nxcomp/ClientProxy.cpp538
-rw-r--r--nxcomp/ClientProxy.h108
-rw-r--r--nxcomp/ClientReadBuffer.cpp166
-rw-r--r--nxcomp/ClientReadBuffer.h57
-rw-r--r--nxcomp/ClientStore.cpp228
-rw-r--r--nxcomp/ClientStore.h135
-rw-r--r--nxcomp/Colormap.cpp94
-rw-r--r--nxcomp/Colormap.h24
-rw-r--r--nxcomp/ConfigureWindow.cpp130
-rw-r--r--nxcomp/ConfigureWindow.h170
-rw-r--r--nxcomp/Control.cpp896
-rw-r--r--nxcomp/Control.h747
-rw-r--r--nxcomp/CopyArea.cpp187
-rw-r--r--nxcomp/CopyArea.h184
-rw-r--r--nxcomp/CreateGC.cpp226
-rw-r--r--nxcomp/CreateGC.h178
-rw-r--r--nxcomp/CreatePixmap.cpp268
-rw-r--r--nxcomp/CreatePixmap.h154
-rw-r--r--nxcomp/CreatePixmapCompat.cpp272
-rw-r--r--nxcomp/CreatePixmapCompat.h154
-rw-r--r--nxcomp/DecodeBuffer.cpp692
-rw-r--r--nxcomp/DecodeBuffer.h142
-rw-r--r--nxcomp/EncodeBuffer.cpp660
-rw-r--r--nxcomp/EncodeBuffer.h187
-rw-r--r--nxcomp/FillPoly.cpp227
-rw-r--r--nxcomp/FillPoly.h198
-rw-r--r--nxcomp/Fork.cpp98
-rw-r--r--nxcomp/Fork.h23
-rw-r--r--nxcomp/FreeCache.h34
-rw-r--r--nxcomp/GenericChannel.cpp483
-rw-r--r--nxcomp/GenericChannel.h448
-rw-r--r--nxcomp/GenericReadBuffer.cpp70
-rw-r--r--nxcomp/GenericReadBuffer.h53
-rw-r--r--nxcomp/GenericReply.cpp293
-rw-r--r--nxcomp/GenericReply.h154
-rw-r--r--nxcomp/GenericRequest.cpp327
-rw-r--r--nxcomp/GenericRequest.h153
-rw-r--r--nxcomp/GetImage.cpp165
-rw-r--r--nxcomp/GetImage.h182
-rw-r--r--nxcomp/GetImageReply.cpp185
-rw-r--r--nxcomp/GetImageReply.h143
-rw-r--r--nxcomp/GetProperty.cpp107
-rw-r--r--nxcomp/GetProperty.h175
-rw-r--r--nxcomp/GetPropertyReply.cpp295
-rw-r--r--nxcomp/GetPropertyReply.h153
-rw-r--r--nxcomp/ImageText16.cpp219
-rw-r--r--nxcomp/ImageText16.h182
-rw-r--r--nxcomp/ImageText8.cpp219
-rw-r--r--nxcomp/ImageText8.h182
-rw-r--r--nxcomp/IntCache.cpp218
-rw-r--r--nxcomp/IntCache.h111
-rw-r--r--nxcomp/InternAtom.cpp119
-rw-r--r--nxcomp/InternAtom.h170
-rw-r--r--nxcomp/Jpeg.cpp875
-rw-r--r--nxcomp/Jpeg.h28
-rw-r--r--nxcomp/Keeper.cpp600
-rw-r--r--nxcomp/Keeper.h191
-rw-r--r--nxcomp/LICENSE37
-rw-r--r--nxcomp/List.cpp100
-rw-r--r--nxcomp/List.h87
-rw-r--r--nxcomp/ListFontsReply.cpp204
-rw-r--r--nxcomp/ListFontsReply.h139
-rw-r--r--nxcomp/Loop.cpp16683
-rw-r--r--nxcomp/MD5.c399
-rw-r--r--nxcomp/MD5.h91
-rw-r--r--nxcomp/Makefile.in279
-rw-r--r--nxcomp/Message.cpp2345
-rw-r--r--nxcomp/Message.h1102
-rw-r--r--nxcomp/Misc.cpp1893
-rw-r--r--nxcomp/Misc.h262
-rw-r--r--nxcomp/NX.h449
-rw-r--r--nxcomp/NXalert.h268
-rw-r--r--nxcomp/NXmitshm.h48
-rw-r--r--nxcomp/NXpack.h133
-rw-r--r--nxcomp/NXproto.h439
-rw-r--r--nxcomp/NXrender.h70
-rw-r--r--nxcomp/NXvars.h193
-rw-r--r--nxcomp/OpcodeCache.h45
-rw-r--r--nxcomp/OpcodeStore.cpp76
-rw-r--r--nxcomp/OpcodeStore.h83
-rw-r--r--nxcomp/Pack.c172
-rw-r--r--nxcomp/Pgn.cpp794
-rw-r--r--nxcomp/Pgn.h34
-rw-r--r--nxcomp/Pipe.cpp412
-rw-r--r--nxcomp/Pipe.h27
-rw-r--r--nxcomp/PolyArc.cpp150
-rw-r--r--nxcomp/PolyArc.h177
-rw-r--r--nxcomp/PolyFillArc.cpp150
-rw-r--r--nxcomp/PolyFillArc.h177
-rw-r--r--nxcomp/PolyFillRectangle.cpp148
-rw-r--r--nxcomp/PolyFillRectangle.h177
-rw-r--r--nxcomp/PolyLine.cpp170
-rw-r--r--nxcomp/PolyLine.h178
-rw-r--r--nxcomp/PolyPoint.cpp170
-rw-r--r--nxcomp/PolyPoint.h178
-rw-r--r--nxcomp/PolySegment.cpp150
-rw-r--r--nxcomp/PolySegment.h177
-rw-r--r--nxcomp/PolyText16.cpp300
-rw-r--r--nxcomp/PolyText16.h180
-rw-r--r--nxcomp/PolyText8.cpp298
-rw-r--r--nxcomp/PolyText8.h180
-rw-r--r--nxcomp/PositionCacheCompat.cpp45
-rw-r--r--nxcomp/PositionCacheCompat.h41
-rw-r--r--nxcomp/Proxy.cpp6450
-rw-r--r--nxcomp/Proxy.h1263
-rw-r--r--nxcomp/ProxyReadBuffer.cpp199
-rw-r--r--nxcomp/ProxyReadBuffer.h49
-rw-r--r--nxcomp/PutImage.cpp405
-rw-r--r--nxcomp/PutImage.h166
-rw-r--r--nxcomp/PutPackedImage.cpp595
-rw-r--r--nxcomp/PutPackedImage.h211
-rw-r--r--nxcomp/QueryFontReply.cpp145
-rw-r--r--nxcomp/QueryFontReply.h129
-rw-r--r--nxcomp/README21
-rw-r--r--nxcomp/README-IPAQ21
-rw-r--r--nxcomp/ReadBuffer.cpp627
-rw-r--r--nxcomp/ReadBuffer.h120
-rw-r--r--nxcomp/RenderAddGlyphs.cpp221
-rw-r--r--nxcomp/RenderAddGlyphs.h80
-rw-r--r--nxcomp/RenderChangePicture.cpp226
-rw-r--r--nxcomp/RenderChangePicture.h80
-rw-r--r--nxcomp/RenderComposite.cpp388
-rw-r--r--nxcomp/RenderComposite.h80
-rw-r--r--nxcomp/RenderCompositeCompat.cpp320
-rw-r--r--nxcomp/RenderCompositeCompat.h80
-rw-r--r--nxcomp/RenderCompositeGlyphs.cpp679
-rw-r--r--nxcomp/RenderCompositeGlyphs.h93
-rw-r--r--nxcomp/RenderCompositeGlyphsCompat.cpp602
-rw-r--r--nxcomp/RenderCompositeGlyphsCompat.h80
-rw-r--r--nxcomp/RenderCreateGlyphSet.cpp173
-rw-r--r--nxcomp/RenderCreateGlyphSet.h80
-rw-r--r--nxcomp/RenderCreateGlyphSetCompat.cpp231
-rw-r--r--nxcomp/RenderCreateGlyphSetCompat.h80
-rw-r--r--nxcomp/RenderCreatePicture.cpp266
-rw-r--r--nxcomp/RenderCreatePicture.h80
-rw-r--r--nxcomp/RenderCreatePictureCompat.cpp262
-rw-r--r--nxcomp/RenderCreatePictureCompat.h80
-rw-r--r--nxcomp/RenderExtension.cpp567
-rw-r--r--nxcomp/RenderExtension.h504
-rw-r--r--nxcomp/RenderFillRectangles.cpp225
-rw-r--r--nxcomp/RenderFillRectangles.h80
-rw-r--r--nxcomp/RenderFreeGlyphSet.cpp154
-rw-r--r--nxcomp/RenderFreeGlyphSet.h80
-rw-r--r--nxcomp/RenderFreePicture.cpp154
-rw-r--r--nxcomp/RenderFreePicture.h80
-rw-r--r--nxcomp/RenderFreePictureCompat.cpp158
-rw-r--r--nxcomp/RenderFreePictureCompat.h80
-rw-r--r--nxcomp/RenderGenericRequest.cpp258
-rw-r--r--nxcomp/RenderGenericRequest.h81
-rw-r--r--nxcomp/RenderMinorExtensionHeaders.h34
-rw-r--r--nxcomp/RenderMinorExtensionMethods.h73
-rw-r--r--nxcomp/RenderMinorExtensionTags.h186
-rw-r--r--nxcomp/RenderPictureClip.cpp291
-rw-r--r--nxcomp/RenderPictureClip.h80
-rw-r--r--nxcomp/RenderPictureClipCompat.cpp237
-rw-r--r--nxcomp/RenderPictureClipCompat.h80
-rw-r--r--nxcomp/RenderPictureFilter.cpp266
-rw-r--r--nxcomp/RenderPictureFilter.h80
-rw-r--r--nxcomp/RenderPictureTransform.cpp202
-rw-r--r--nxcomp/RenderPictureTransform.h80
-rw-r--r--nxcomp/RenderTrapezoids.cpp360
-rw-r--r--nxcomp/RenderTrapezoids.h80
-rw-r--r--nxcomp/RenderTriangles.cpp350
-rw-r--r--nxcomp/RenderTriangles.h80
-rw-r--r--nxcomp/Rgb.cpp94
-rw-r--r--nxcomp/Rgb.h28
-rw-r--r--nxcomp/Rle.cpp94
-rw-r--r--nxcomp/Rle.h28
-rw-r--r--nxcomp/SendEvent.cpp292
-rw-r--r--nxcomp/SendEvent.h187
-rw-r--r--nxcomp/SequenceQueue.cpp162
-rw-r--r--nxcomp/SequenceQueue.h83
-rw-r--r--nxcomp/ServerCache.cpp186
-rw-r--r--nxcomp/ServerCache.h305
-rw-r--r--nxcomp/ServerChannel.cpp8258
-rw-r--r--nxcomp/ServerChannel.h536
-rw-r--r--nxcomp/ServerProxy.cpp605
-rw-r--r--nxcomp/ServerProxy.h147
-rw-r--r--nxcomp/ServerReadBuffer.cpp235
-rw-r--r--nxcomp/ServerReadBuffer.h65
-rw-r--r--nxcomp/ServerStore.cpp171
-rw-r--r--nxcomp/ServerStore.h75
-rw-r--r--nxcomp/SetClipRectangles.cpp142
-rw-r--r--nxcomp/SetClipRectangles.h179
-rw-r--r--nxcomp/SetUnpackAlpha.cpp257
-rw-r--r--nxcomp/SetUnpackAlpha.h158
-rw-r--r--nxcomp/SetUnpackAlphaCompat.cpp250
-rw-r--r--nxcomp/SetUnpackAlphaCompat.h149
-rw-r--r--nxcomp/SetUnpackColormap.cpp257
-rw-r--r--nxcomp/SetUnpackColormap.h157
-rw-r--r--nxcomp/SetUnpackColormapCompat.cpp262
-rw-r--r--nxcomp/SetUnpackColormapCompat.h149
-rw-r--r--nxcomp/SetUnpackGeometry.cpp293
-rw-r--r--nxcomp/SetUnpackGeometry.h159
-rw-r--r--nxcomp/ShapeExtension.cpp296
-rw-r--r--nxcomp/ShapeExtension.h157
-rw-r--r--nxcomp/Socket.cpp745
-rw-r--r--nxcomp/Socket.h88
-rw-r--r--nxcomp/Split.cpp1846
-rw-r--r--nxcomp/Split.h535
-rw-r--r--nxcomp/StaticCompressor.cpp420
-rw-r--r--nxcomp/StaticCompressor.h72
-rw-r--r--nxcomp/Statistics.cpp1995
-rw-r--r--nxcomp/Statistics.h737
-rw-r--r--nxcomp/TextCompressor.cpp77
-rw-r--r--nxcomp/TextCompressor.h49
-rw-r--r--nxcomp/Timestamp.cpp65
-rw-r--r--nxcomp/Timestamp.h299
-rw-r--r--nxcomp/TranslateCoords.cpp103
-rw-r--r--nxcomp/TranslateCoords.h177
-rw-r--r--nxcomp/Transport.cpp3056
-rw-r--r--nxcomp/Transport.h569
-rw-r--r--nxcomp/Types.h255
-rw-r--r--nxcomp/Unpack.cpp1502
-rw-r--r--nxcomp/Unpack.h141
-rw-r--r--nxcomp/Utils.cpp35
-rw-r--r--nxcomp/VERSION1
-rw-r--r--nxcomp/Vars.c46
-rw-r--r--nxcomp/WriteBuffer.cpp488
-rw-r--r--nxcomp/WriteBuffer.h126
-rw-r--r--nxcomp/XidCache.cpp39
-rw-r--r--nxcomp/XidCache.h41
-rw-r--r--nxcomp/Z.cpp134
-rw-r--r--nxcomp/Z.h29
-rwxr-xr-xnxcomp/configure5923
-rw-r--r--nxcomp/configure.in393
-rwxr-xr-xnxcomp/install-sh238
-rwxr-xr-xnxcomp/mkinstalldirs34
265 files changed, 118610 insertions, 0 deletions
diff --git a/nxcomp/ActionCache.cpp b/nxcomp/ActionCache.cpp
new file mode 100644
index 000000000..79b670021
--- /dev/null
+++ b/nxcomp/ActionCache.cpp
@@ -0,0 +1,39 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "Control.h"
+
+#include "ActionCache.h"
+
+ActionCache::ActionCache()
+{
+ for (int i = 0; i < 256; i++)
+ {
+ base_[i] = new IntCache(8);
+ }
+
+ slot_ = 0;
+ last_ = 0;
+}
+
+ActionCache::~ActionCache()
+{
+ for (int i = 0; i < 256; i++)
+ {
+ delete base_[i];
+ }
+}
diff --git a/nxcomp/ActionCache.h b/nxcomp/ActionCache.h
new file mode 100644
index 000000000..23265fcf2
--- /dev/null
+++ b/nxcomp/ActionCache.h
@@ -0,0 +1,41 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef ActionCache_H
+#define ActionCache_H
+
+#include "IntCache.h"
+
+class ActionCache
+{
+ friend class EncodeBuffer;
+ friend class DecodeBuffer;
+
+ public:
+
+ ActionCache();
+ ~ActionCache();
+
+ private:
+
+ IntCache *base_[256];
+
+ unsigned int slot_;
+ unsigned short last_;
+};
+
+#endif /* ActionCache_H */
diff --git a/nxcomp/ActionCacheCompat.h b/nxcomp/ActionCacheCompat.h
new file mode 100644
index 000000000..8281db826
--- /dev/null
+++ b/nxcomp/ActionCacheCompat.h
@@ -0,0 +1,45 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef ActionCacheCompat_H
+#define ActionCacheCompat_H
+
+#include "CharCache.h"
+
+class ActionCacheCompat
+{
+ friend class EncodeBuffer;
+ friend class DecodeBuffer;
+
+ public:
+
+ ActionCacheCompat()
+ {
+ slot_ = 0;
+ }
+
+ ~ActionCacheCompat()
+ {
+ }
+
+ private:
+
+ CharCache base_[4];
+ unsigned char slot_;
+};
+
+#endif /* ActionCacheCompat_H */
diff --git a/nxcomp/Agent.cpp b/nxcomp/Agent.cpp
new file mode 100644
index 000000000..c0b729d06
--- /dev/null
+++ b/nxcomp/Agent.cpp
@@ -0,0 +1,72 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "Misc.h"
+#include "Agent.h"
+#include "Proxy.h"
+
+extern Proxy *proxy;
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+Agent::Agent(int fd[2])
+{
+ remoteFd_ = fd[0];
+ localFd_ = fd[1];
+
+ transport_ = new AgentTransport(localFd_);
+
+ if (transport_ == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Agent: PANIC! Can't create the memory-to-memory transport "
+ << "for FD#" << localFd_ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't create the memory-to-memory transport "
+ << "for FD#" << localFd_ << ".\n";
+
+ HandleCleanup();
+ }
+
+ FD_ZERO(&saveRead_);
+ FD_ZERO(&saveWrite_);
+
+ canRead_ = 0;
+
+ #ifdef DEBUG
+ *logofs << "Agent: Created agent object at " << this
+ << ".\n" << logofs_flush;
+ #endif
+}
+
+Agent::~Agent()
+{
+ delete transport_;
+
+ #ifdef DEBUG
+ *logofs << "Agent: Deleted agent object at " << this
+ << ".\n" << logofs_flush;
+ #endif
+}
diff --git a/nxcomp/Agent.h b/nxcomp/Agent.h
new file mode 100644
index 000000000..fac5acd43
--- /dev/null
+++ b/nxcomp/Agent.h
@@ -0,0 +1,243 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef Agent_H
+#define Agent_H
+
+#include <unistd.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/select.h>
+
+#include "Misc.h"
+#include "Transport.h"
+#include "Proxy.h"
+
+extern Proxy *proxy;
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+class Agent
+{
+ public:
+
+ //
+ // Must be created by passing the fake descriptor that
+ // will be used for simulating socket communication
+ // betwen the agent and the proxy. I/O will take place
+ // by copying data to the agent's read and write buf-
+ // fers.
+ //
+
+ Agent(int fd[2]);
+
+ ~Agent();
+
+ AgentTransport *getTransport() const
+ {
+ return transport_;
+ }
+
+ void saveReadMask(fd_set *readSet)
+ {
+ saveRead_ = *readSet;
+ }
+
+ void saveWriteMask(fd_set *writeSet)
+ {
+ saveWrite_ = *writeSet;
+ }
+
+ void clearReadMask(fd_set *readSet)
+ {
+ FD_CLR(remoteFd_, readSet);
+ FD_CLR(localFd_, readSet);
+ }
+
+ void clearWriteMask(fd_set *writeSet)
+ {
+ FD_CLR(remoteFd_, writeSet);
+ FD_CLR(localFd_, writeSet);
+ }
+
+ void setLocalRead(fd_set *readSet, int *result)
+ {
+ (*result)++;
+
+ FD_SET(localFd_, readSet);
+ }
+
+ void setRemoteRead(fd_set *readSet, int *result)
+ {
+ (*result)++;
+
+ FD_SET(remoteFd_, readSet);
+ }
+
+ void setRemoteWrite(fd_set *writeSet, int *result)
+ {
+ (*result)++;
+
+ FD_SET(remoteFd_, writeSet);
+ }
+
+ fd_set *getSavedReadMask()
+ {
+ return &saveRead_;
+ }
+
+ fd_set *getSavedWriteMask()
+ {
+ return &saveWrite_;
+ }
+
+ int getRemoteFd() const
+ {
+ return remoteFd_;
+ }
+
+ int getLocalFd() const
+ {
+ return localFd_;
+ }
+
+ int getProxyFd() const
+ {
+ return proxy -> getFd();
+ }
+
+ int isValid() const
+ {
+ return (transport_ != NULL);
+ }
+
+ int localReadable()
+ {
+ return (transport_ -> readable() != 0);
+ }
+
+ //
+ // Check if we can process more data from
+ // the agent descriptor and cache the result
+ // to avoid multiple calls. This must be
+ // always called before querying the other
+ // functions.
+ //
+
+ void saveChannelState()
+ {
+ canRead_ = (proxy != NULL ? proxy -> canRead(localFd_) : 0);
+ }
+
+ int remoteCanRead(const fd_set * const readSet)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Agent: remoteCanRead() is " <<
+ (FD_ISSET(remoteFd_, readSet) && transport_ -> dequeuable() != 0)
+ << " with FD_ISSET() " << (int) FD_ISSET(remoteFd_, readSet)
+ << " and dequeuable " << transport_ -> dequeuable()
+ << ".\n" << logofs_flush;
+ #endif
+
+ return (FD_ISSET(remoteFd_, readSet) &&
+ transport_ -> dequeuable() != 0);
+ }
+
+ int remoteCanWrite(const fd_set * const writeSet)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Agent: remoteCanWrite() is " <<
+ (FD_ISSET(remoteFd_, writeSet) && transport_ ->
+ queuable() != 0 && canRead_ == 1) << " with FD_ISSET() "
+ << (int) FD_ISSET(remoteFd_, writeSet) << " queueable "
+ << transport_ -> queuable() << " channel can read "
+ << canRead_ << ".\n" << logofs_flush;
+ #endif
+
+ return (FD_ISSET(remoteFd_, writeSet) &&
+ transport_ -> queuable() != 0 &&
+ canRead_ == 1);
+ }
+
+ int localCanRead()
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Agent: localCanRead() is " <<
+ (transport_ -> readable() != 0 && canRead_ == 1)
+ << " with readable " << transport_ -> readable()
+ << " channel can read " << canRead_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return (transport_ -> readable() != 0 &&
+ canRead_ == 1);
+ }
+
+ int proxyCanRead()
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Agent: proxyCanRead() is " << proxy -> canRead()
+ << ".\n" << logofs_flush;
+ #endif
+
+ return (proxy -> canRead());
+ }
+
+ int proxyCanRead(const fd_set * const readSet)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Agent: proxyCanRead() is "
+ << ((int) FD_ISSET(proxy -> getFd(), readSet)
+ << ".\n" << logofs_flush;
+ #endif
+
+ return (FD_ISSET(proxy -> getFd(), readSet));
+ }
+
+ int enqueueData(const char *data, const int size) const
+ {
+ return transport_ -> enqueue(data, size);
+ }
+
+ int dequeueData(char *data, int size) const
+ {
+ return transport_ -> dequeue(data, size);
+ }
+
+ int dequeuableData() const
+ {
+ return transport_ -> dequeuable();
+ }
+
+ private:
+
+ int remoteFd_;
+ int localFd_;
+
+ fd_set saveRead_;
+ fd_set saveWrite_;
+
+ int canRead_;
+
+ AgentTransport *transport_;
+};
+
+#endif /* Agent_H */
diff --git a/nxcomp/Alpha.cpp b/nxcomp/Alpha.cpp
new file mode 100644
index 000000000..931101495
--- /dev/null
+++ b/nxcomp/Alpha.cpp
@@ -0,0 +1,126 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "Misc.h"
+#include "Unpack.h"
+#include "Alpha.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+int UnpackAlpha(unsigned char method, unsigned char *src_data, int src_size,
+ unsigned char *dst_data, int dst_size)
+{
+ if (*src_data == 0)
+ {
+ if (dst_size != src_size - 1)
+ {
+ #ifdef TEST
+ *logofs << "UnpackAlpha: PANIC! Invalid destination size "
+ << dst_size << " with source " << src_size
+ << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "UnpackAlpha: Expanding " << src_size - 1
+ << " bytes of plain alpha data.\n" << logofs_flush;
+ #endif
+
+ memcpy(dst_data, src_data + 1, src_size - 1);
+
+ return 1;
+ }
+
+ unsigned int check_size = dst_size;
+
+ int result = ZDecompress(&unpackStream, dst_data, &check_size,
+ src_data + 1, src_size - 1);
+
+ if (result != Z_OK)
+ {
+ #ifdef PANIC
+ *logofs << "UnpackAlpha: PANIC! Failure decompressing alpha data. "
+ << "Error is '" << zError(result) << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decompressing alpha data. "
+ << "Error is '" << zError(result) << "'.\n";
+
+ return -1;
+ }
+ else if (check_size != (unsigned int) dst_size)
+ {
+ #ifdef PANIC
+ *logofs << "UnpackAlpha: PANIC! Size mismatch in alpha data. "
+ << "Resulting size is " << check_size << " with "
+ << "expected size " << dst_size << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Size mismatch in alpha data. "
+ << "Resulting size is " << check_size << " with "
+ << "expected size " << dst_size << ".\n";
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "UnpackAlpha: Decompressed " << src_size - 1
+ << " bytes to " << dst_size << " bytes of alpha data.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int UnpackAlpha(T_alpha *alpha, unsigned char *dst_data,
+ int dst_size, int big_endian)
+{
+ unsigned int count = dst_size >> 2;
+
+ unsigned int i;
+
+ int shift;
+
+ if (count != alpha -> entries)
+ {
+ #ifdef WARNING
+ *logofs << "UnpackAlpha: WARNING! Not applying the alpha with "
+ << count << " elements needed and " << alpha -> entries
+ << " available.\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ shift = (big_endian == 1 ? 0 : 3);
+
+ for (i = 0; i < count; i++)
+ {
+ *(dst_data + shift) = *(alpha -> data + i);
+
+ dst_data += 4;
+ }
+
+ return 1;
+}
diff --git a/nxcomp/Alpha.h b/nxcomp/Alpha.h
new file mode 100644
index 000000000..80620e1aa
--- /dev/null
+++ b/nxcomp/Alpha.h
@@ -0,0 +1,27 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef Alpha_H
+#define Alpha_H
+
+int UnpackAlpha(unsigned char method, unsigned char *src_data, int src_size,
+ unsigned char *dst_data, int dst_size);
+
+int UnpackAlpha(T_alpha *alpha, unsigned char *dst_data,
+ int dst_size, int big_endian);
+
+#endif /* Aplha_H */
diff --git a/nxcomp/Auth.cpp b/nxcomp/Auth.cpp
new file mode 100644
index 000000000..d8e999132
--- /dev/null
+++ b/nxcomp/Auth.cpp
@@ -0,0 +1,650 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "Auth.h"
+
+#include "Misc.h"
+#include "Control.h"
+#include "Timestamp.h"
+#include "Pipe.h"
+
+#define DEFAULT_STRING_LIMIT 512
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Store the provided cookie as our 'fake' cookie, then
+// read the 'real' cookie from the current X authority
+// file.
+//
+
+Auth::Auth(char *display, char *cookie)
+{
+ display_ = NULL;
+
+ file_ = NULL;
+
+ last_ = nullTimestamp();
+
+ fakeCookie_ = NULL;
+ realCookie_ = NULL;
+
+ fakeData_ = NULL;
+ realData_ = NULL;
+
+ dataSize_ = 0;
+
+ generatedCookie_ = 0;
+
+ if (display == NULL || *display == '\0' || cookie == NULL ||
+ *cookie == '\0' || strlen(cookie) != 32)
+ {
+ #ifdef PANIC
+ *logofs << "Auth: PANIC! Can't create the X authorization data "
+ << "with cookie '" << cookie << "' and display '"
+ << display << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't create the X authorization data "
+ << "with cookie '" << cookie << "' and display '"
+ << display << "'.\n";
+
+ return;
+ }
+
+ #ifdef TEST
+ *logofs << "Auth: Creating X authorization data with cookie '"
+ << cookie << "' and display '" << display << "'.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Get a local copy of all parameters.
+ //
+
+ display_ = new char[strlen(display) + 1];
+ file_ = new char[DEFAULT_STRING_LIMIT];
+
+ fakeCookie_ = new char[strlen(cookie) + 1];
+ realCookie_ = new char[DEFAULT_STRING_LIMIT];
+
+ if (display_ == NULL || file_ == NULL ||
+ fakeCookie_ == NULL || realCookie_ == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Auth: PANIC! Cannot allocate memory for the X "
+ << "authorization data.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot allocate memory for the X "
+ << "authorization data.\n";
+
+ return;
+ }
+
+ strcpy(display_, display);
+
+ *file_ = '\0';
+
+ strcpy(fakeCookie_, cookie);
+
+ *realCookie_ = '\0';
+
+ //
+ // Get the real cookie from the authorization file.
+ //
+
+ updateCookie();
+}
+
+Auth::~Auth()
+{
+ delete [] display_;
+ delete [] file_;
+
+ delete [] fakeCookie_;
+ delete [] realCookie_;
+
+ delete [] fakeData_;
+ delete [] realData_;
+}
+
+//
+// At the present moment the cookie is read only once,
+// at the time the instance is initialized. If the auth
+// file changes along the life of the session, the old
+// cookie will be used. This works with X servers beca-
+// use of an undocumented "feature". See nx-X11.
+//
+
+int Auth::updateCookie()
+{
+ if (isTimestamp(last_) == 0)
+ {
+ #ifdef TEST
+ *logofs << "Auth: Reading the X authorization file "
+ << "with last update at " << strMsTimestamp(last_)
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (getCookie() == 1 && validateCookie() == 1)
+ {
+ //
+ // It should rather be the modification time
+ // the auth file, so we can read it again if
+ // the file is changed.
+ //
+
+ #ifdef TEST
+ *logofs << "Auth: Setting last X authorization file "
+ << "update at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ last_ = getTimestamp();
+
+ return 1;
+ }
+
+ #ifdef PANIC
+ *logofs << "Auth: PANIC! Cannot read the cookie from the X "
+ << "authorization file.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot read the cookie from the X "
+ << "authorization file.\n";
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "Auth: WARNING! Skipping check on the X "
+ << "authorization file.\n" << logofs_flush;
+ #endif
+
+ return 0;
+}
+
+int Auth::getCookie()
+{
+ //
+ // Check the name of the auth file that we are going to use.
+ // It can be either the value of the XAUTHORITY environment
+ // or the default .Xauthority file in the user's home.
+ //
+
+ char *environment;
+
+ environment = getenv("XAUTHORITY");
+
+ if (environment != NULL && *environment != '\0')
+ {
+ strncpy(file_, environment, DEFAULT_STRING_LIMIT - 1);
+ }
+ else
+ {
+ snprintf(file_, DEFAULT_STRING_LIMIT - 1, "%s/.Xauthority",
+ control -> HomePath);
+ }
+
+ *(file_ + DEFAULT_STRING_LIMIT - 1) = '\0';
+
+ #ifdef TEST
+ *logofs << "Auth: Using X authorization file '" << file_
+ << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // Use the nxauth command on Windows and the Mac, xauth
+ // on all the other platforms. On Windows and on the Mac
+ // we assume that the nxauth command is located under
+ // bin in the client installation directory. On all the
+ // other platforms we use the default xauth command that
+ // is in our path.
+ //
+
+ char command[DEFAULT_STRING_LIMIT];
+
+ #if defined(__CYGWIN32__) || defined(__APPLE__)
+
+ snprintf(command, DEFAULT_STRING_LIMIT - 1,
+ "%s/bin/nxauth", control -> SystemPath);
+
+ *(command + DEFAULT_STRING_LIMIT - 1) = '\0';
+
+ #else
+
+ strcpy(command, "xauth");
+
+ #endif
+
+ #ifdef TEST
+ *logofs << "Auth: Using X auth command '" << command
+ << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // The SSH code forces using the unix:n port when passing localhost:n.
+ // This is probably because localhost:n can fail to return a valid
+ // entry on machines where the hostname for localhost doesn't match
+ // exactly the 'localhost' string. For example, on a freshly installed
+ // Fedora Core 3 I get a 'localhost.localdomain/unix:0' entry. Query-
+ // ing 'xauth list localhost:0' results in an empty result, while the
+ // query 'xauth list unix:0' works as expected. Note anyway that if
+ // the cookie for the TCP connection on 'localhost' is set to a dif-
+ // ferent cookie than the one for the Unix connections, both SSH and
+ // NX will match the wrong cookie and session will fail.
+ //
+
+ char line[DEFAULT_STRING_LIMIT];
+
+ if (strncmp(display_, "localhost:", 10) == 0)
+ {
+ snprintf(line, DEFAULT_STRING_LIMIT, "unix:%s", display_ + 10);
+ }
+ else
+ {
+ snprintf(line, DEFAULT_STRING_LIMIT, "%.200s", display_);
+ }
+
+ const char *parameters[256];
+
+ parameters[0] = command;
+ parameters[1] = command;
+ parameters[2] = "-f";
+ parameters[3] = file_;
+ parameters[4] = "list";
+ parameters[5] = line;
+ parameters[6] = NULL;
+
+ #ifdef TEST
+ *logofs << "Auth: Executing command ";
+
+ for (int i = 0; i < 256 && parameters[i] != NULL; i++)
+ {
+ *logofs << "[" << parameters[i] << "]";
+ }
+
+ *logofs << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Use the popen() function to read the result
+ // of the command. We would better use our own
+ // implementation.
+ //
+
+ FILE *data = Popen((char *const *) parameters, "r");
+
+ int result = -1;
+
+ if (data == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Auth: PANIC! Failed to execute the X auth command.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to execute the X auth command.\n";
+
+ goto AuthGetCookieResult;
+ }
+
+ if (fgets(line, DEFAULT_STRING_LIMIT, data) == NULL)
+ {
+ #ifdef WARNING
+ *logofs << "Auth: WARNING! Failed to read data from the X "
+ << "auth command.\n" << logofs_flush;
+ #endif
+
+ #ifdef TEST
+ cerr << "Warning" << ": Failed to read data from the X "
+ << "auth command.\n";
+ #endif
+
+ #ifdef PANIC
+ *logofs << "Auth: WARNING! Generating a fake cookie for "
+ << "X authentication.\n" << logofs_flush;
+ #endif
+
+ #ifdef TEST
+ cerr << "Warning" << ": Generating a fake cookie for "
+ << "X authentication.\n";
+ #endif
+
+ generateCookie(realCookie_);
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Auth: Checking cookie in string '" << line
+ << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // Skip the hostname in the authority entry
+ // just in case it includes some white spaces.
+ //
+
+ char *cookie = NULL;
+
+ cookie = index(line, ':');
+
+ if (cookie == NULL)
+ {
+ cookie = line;
+ }
+
+ if (sscanf(cookie, "%*s %*s %511s", realCookie_) != 1)
+ {
+ #ifdef PANIC
+ *logofs << "Auth: PANIC! Failed to identify the cookie "
+ << "in string '" << line << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to identify the cookie "
+ << "in string '" << line << "'.\n";
+
+ goto AuthGetCookieResult;
+ }
+
+ #ifdef TEST
+ *logofs << "Auth: Got cookie '" << realCookie_
+ << "' from file '" << file_ << "'.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ result = 1;
+
+AuthGetCookieResult:
+
+ if (data != NULL)
+ {
+ Pclose(data);
+ }
+
+ return result;
+}
+
+int Auth::validateCookie()
+{
+ unsigned int length = strlen(realCookie_);
+
+ if (length > DEFAULT_STRING_LIMIT / 2 - 1 ||
+ strlen(fakeCookie_) != length)
+ {
+ #ifdef PANIC
+ *logofs << "Auth: PANIC! Size mismatch between cookies '"
+ << realCookie_ << "' and '" << fakeCookie_ << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Size mismatch between cookies '"
+ << realCookie_ << "' and '" << fakeCookie_ << "'.\n";
+
+ goto AuthValidateCookieError;
+ }
+
+ //
+ // The length of the resulting data will be
+ // half the size of the Hex cookie.
+ //
+
+ length = length / 2;
+
+ fakeData_ = new char[length];
+ realData_ = new char[length];
+
+ if (fakeData_ == NULL || realData_ == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Auth: PANIC! Cannot allocate memory for the binary X "
+ << "authorization data.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot allocate memory for the binary X "
+ << "authorization data.\n";
+
+ goto AuthValidateCookieError;
+ }
+
+ //
+ // Translate the real cookie from Hex data
+ // to its binary representation.
+ //
+
+ unsigned int value;
+
+ for (unsigned int i = 0; i < length; i++)
+ {
+ if (sscanf(realCookie_ + 2 * i, "%2x", &value) != 1)
+ {
+ #ifdef PANIC
+ *logofs << "Auth: PANIC! Bad X authorization data in real "
+ << "cookie '" << realCookie_ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Bad X authorization data in real cookie '"
+ << realCookie_ << "'.\n";
+
+ goto AuthValidateCookieError;
+ }
+
+ realData_[i] = value;
+
+ if (sscanf(fakeCookie_ + 2 * i, "%2x", &value) != 1)
+ {
+ #ifdef PANIC
+ *logofs << "Auth: PANIC! Bad X authorization data in fake "
+ << "cookie '" << fakeCookie_ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Bad X authorization data in fake cookie '"
+ << fakeCookie_ << "'.\n";
+
+ goto AuthValidateCookieError;
+ }
+
+ fakeData_[i] = value;
+ }
+
+ dataSize_ = length;
+
+ #ifdef TEST
+ *logofs << "Auth: Validated real cookie '"
+ << realCookie_ << "' and fake cookie '" << fakeCookie_
+ << "' with data with size " << dataSize_ << ".\n"
+ << logofs_flush;
+
+ *logofs << "Auth: Ready to accept incoming connections.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+
+AuthValidateCookieError:
+
+ delete [] fakeData_;
+ delete [] realData_;
+
+ fakeData_ = NULL;
+ realData_ = NULL;
+
+ dataSize_ = 0;
+
+ return -1;
+}
+
+int Auth::checkCookie(unsigned char *buffer)
+{
+ if (isValid() != 1)
+ {
+ #ifdef PANIC
+ *logofs << "Auth: PANIC! Attempt to check the X cookie with "
+ << "invalid authorization data.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Attempt to check the X cookie with "
+ << "invalid authorization data.\n";
+
+ return -1;
+ }
+
+ const char *protoName = "MIT-MAGIC-COOKIE-1";
+ int protoSize = strlen(protoName);
+
+ int matchedProtoSize;
+ int matchedDataSize;
+
+ if (buffer[0] == 0x42)
+ {
+ //
+ // Byte order is MSB first.
+ //
+
+ matchedProtoSize = 256 * buffer[6] + buffer[7];
+ matchedDataSize = 256 * buffer[8] + buffer[9];
+ }
+ else if (buffer[0] == 0x6c)
+ {
+ //
+ // Byte order is LSB first.
+ //
+
+ matchedProtoSize = buffer[6] + 256 * buffer[7];
+ matchedDataSize = buffer[8] + 256 * buffer[9];
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "Auth: WARNING! Bad X connection data in the buffer.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Bad X connection data in the buffer.\n";
+
+ return -1;
+ }
+
+ //
+ // Check if both the authentication protocol
+ // and the fake cookie match our data.
+ //
+
+ int protoOffset = 12;
+
+ #ifdef TEST
+ *logofs << "Auth: Received a protocol size of "
+ << matchedProtoSize << " bytes.\n"
+ << logofs_flush;
+ #endif
+
+ if (matchedProtoSize != protoSize ||
+ memcmp(buffer + protoOffset, protoName, protoSize) != 0)
+ {
+ #ifdef WARNING
+ *logofs << "Auth: WARNING! Protocol mismatch or no X "
+ << "authentication data.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Protocol mismatch or no X "
+ << "authentication data.\n";
+
+ return -1;
+ }
+
+ int dataOffset = protoOffset + ((matchedProtoSize + 3) & ~3);
+
+ #ifdef TEST
+ *logofs << "Auth: Received a data size of "
+ << matchedDataSize << " bytes.\n"
+ << logofs_flush;
+ #endif
+
+ if (matchedDataSize != dataSize_ ||
+ memcmp(buffer + dataOffset, fakeData_, dataSize_) != 0)
+ {
+ #ifdef WARNING
+ *logofs << "Auth: WARNING! Cookie mismatch in the X "
+ << "authentication data.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Cookie mismatch in the X "
+ << "authentication data.\n";
+
+ return -1;
+ }
+
+ //
+ // Everything is OK. Replace the fake data.
+ //
+
+ #ifdef TEST
+ *logofs << "Auth: Replacing fake X authentication data "
+ << "with the real data.\n" << logofs_flush;
+ #endif
+
+ memcpy(buffer + dataOffset, realData_, dataSize_);
+
+ return 1;
+}
+
+void Auth::generateCookie(char *cookie)
+{
+ //
+ // Code is from the SSH implementation, except that
+ // we use a much weaker random number generator.
+ // This is not critical, anyway, as this is just a
+ // fake cookie. The X server doesn't have a cookie
+ // for the display, so it will ignore the value we
+ // feed to it.
+ //
+
+ T_timestamp timer = getTimestamp();
+
+ srand((unsigned int) timer.tv_usec);
+
+ unsigned int data = rand();
+
+ for (int i = 0; i < 16; i++)
+ {
+ if (i % 4 == 0)
+ {
+ data = rand();
+ }
+
+ snprintf(cookie + 2 * i, 3, "%02x", data & 0xff);
+
+ data >>= 8;
+ }
+
+ generatedCookie_ = 1;
+
+ #ifdef TEST
+ *logofs << "Auth: Generated X cookie string '"
+ << cookie << "'.\n" << logofs_flush;
+ #endif
+}
diff --git a/nxcomp/Auth.h b/nxcomp/Auth.h
new file mode 100644
index 000000000..aac1d1e3f
--- /dev/null
+++ b/nxcomp/Auth.h
@@ -0,0 +1,119 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef Auth_H
+#define Auth_H
+
+#include "Timestamp.h"
+
+//
+// Handle the forwarding of authorization credentials
+// to the X server by replacing the fake cookie with
+// the real cookie as it is read from the auth file.
+// At the moment only the MIT-MAGIC-COOKIE-1 cookies
+// are recognized. The implementation is based on the
+// corresponding code found in the SSH client.
+//
+
+class Auth
+{
+ public:
+
+ //
+ // Must be created by passing the fake cookie that
+ // will be forwarded by the remote end and with the
+ // real X display that is going to be used for the
+ // session.
+ //
+
+ Auth(char *display, char *cookie);
+
+ ~Auth();
+
+ int isValid()
+ {
+ return (isTimestamp(last_) == 1 && fakeCookie_ != NULL &&
+ *fakeCookie_ != '\0' && realCookie_ != NULL &&
+ *realCookie_ != '\0' && fakeData_ != NULL &&
+ realData_ != NULL && dataSize_ != 0);
+ }
+
+ int isFake() const
+ {
+ return generatedCookie_;
+ }
+
+ //
+ // Method called in the channel class to find if the
+ // provided cookie matches the fake one. If the data
+ // matches, the fake cookie is replaced with the real
+ // one.
+ //
+
+ int checkCookie(unsigned char *buffer);
+
+ protected:
+
+ //
+ // Update the real cookie for the display. If called
+ // a further time, check if the auth file is changed
+ // and get the new cookie.
+ //
+
+ int updateCookie();
+
+ //
+ // Find out which authorization file is to be used
+ // and query the cookie for the current display.
+ //
+
+ int getCookie();
+
+ //
+ // Extract the binary data from the cookies so that
+ // data can be directly compared at the time it is
+ // taken from the X request.
+ //
+
+ int validateCookie();
+
+ //
+ // Generate a fake random cookie and copy it to the
+ // provided string.
+ //
+
+ void generateCookie(char *cookie);
+
+ private:
+
+ char *display_;
+ char *file_;
+
+ T_timestamp last_;
+
+ char *fakeCookie_;
+ char *realCookie_;
+
+ char *fakeData_;
+ char *realData_;
+
+ int dataSize_;
+
+ int generatedCookie_;
+};
+
+#endif /* Auth_H */
diff --git a/nxcomp/Bitmap.cpp b/nxcomp/Bitmap.cpp
new file mode 100644
index 000000000..b5bad226a
--- /dev/null
+++ b/nxcomp/Bitmap.cpp
@@ -0,0 +1,106 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "Misc.h"
+#include "Bitmap.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+int UnpackBitmap(T_geometry *geometry, unsigned char method, unsigned char *src_data,
+ int src_size, int dst_bpp, int dst_width, int dst_height,
+ unsigned char *dst_data, int dst_size)
+{
+ if (dst_bpp != 32)
+ {
+ #ifdef TEST
+ *logofs << "UnpackBitmap: Nothing to do with "
+ << "image of " << dst_bpp << " bits per plane "
+ << "and size " << src_size << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (src_size != dst_size)
+ {
+ #ifdef PANIC
+ *logofs << "UnpackBitmap: PANIC! Size mismatch with "
+ << src_size << " bytes in the source and "
+ << dst_size << " in the destination.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ memcpy(dst_data, src_data, src_size);
+
+ return 1;
+ }
+ else if (src_size != dst_width * dst_height * 3 ||
+ dst_size != dst_width * dst_height * 4)
+ {
+ #ifdef PANIC
+ *logofs << "UnpackBitmap: PANIC! Size mismatch with "
+ << src_size << " bytes in the source and "
+ << dst_size << " in the destination.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ /*
+ * Insert the 4th byte in the bitmap.
+ */
+
+ unsigned char *next_src = src_data;
+ unsigned char *next_dst = dst_data;
+
+ if (geometry -> image_byte_order == LSBFirst)
+ {
+ while (next_src < src_data + src_size)
+ {
+ *next_dst++ = *next_src++;
+ *next_dst++ = *next_src++;
+ *next_dst++ = *next_src++;
+
+ next_dst++;
+ }
+ }
+ else
+ {
+ while (next_src < src_data + src_size)
+ {
+ next_dst++;
+
+ *next_dst++ = *next_src++;
+ *next_dst++ = *next_src++;
+ *next_dst++ = *next_src++;
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "UnpackBitmap: Unpacked " << src_size
+ << " bytes to a buffer of " << dst_size
+ << " with " << dst_bpp << " bits per plane.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
diff --git a/nxcomp/Bitmap.h b/nxcomp/Bitmap.h
new file mode 100644
index 000000000..57a1b35bb
--- /dev/null
+++ b/nxcomp/Bitmap.h
@@ -0,0 +1,28 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef Bitmap_H
+#define Bitmap_H
+
+#include "Unpack.h"
+
+int UnpackBitmap(T_geometry *geometry, unsigned char method,
+ unsigned char *src_data, int src_size, int dst_bpp,
+ int dst_width, int dst_height, unsigned char *dst_data,
+ int dst_size);
+
+#endif /* Bitmap_H */
diff --git a/nxcomp/BlockCache.cpp b/nxcomp/BlockCache.cpp
new file mode 100644
index 000000000..f885290f0
--- /dev/null
+++ b/nxcomp/BlockCache.cpp
@@ -0,0 +1,69 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include <string.h>
+#include "BlockCache.h"
+
+
+int BlockCache::compare(unsigned int size, const unsigned char *data,
+ int overwrite)
+{
+ int match = 0;
+ if (size == size_)
+ {
+ match = 1;
+ for (unsigned int i = 0; i < size_; i++)
+ if (data[i] != buffer_[i])
+ {
+ match = 0;
+ break;
+ }
+ }
+ if (!match && overwrite)
+ set(size, data);
+ return match;
+}
+
+
+void BlockCache::set(unsigned int size, const unsigned char *data)
+{
+ if (size_ < size)
+ {
+ delete[]buffer_;
+ buffer_ = new unsigned char[size];
+ }
+ size_ = size;
+ memcpy(buffer_, data, size);
+ checksum_ = checksum(size, data);
+}
+
+
+unsigned int BlockCache::checksum(unsigned int size, const unsigned char *data)
+{
+ unsigned int sum = 0;
+ unsigned int shift = 0;
+ const unsigned char *next = data;
+ for (unsigned int i = 0; i < size; i++)
+ {
+ unsigned int value = (unsigned int) *next++;
+ sum += (value << shift);
+ shift++;
+ if (shift == 8)
+ shift = 0;
+ }
+ return sum;
+}
diff --git a/nxcomp/BlockCache.h b/nxcomp/BlockCache.h
new file mode 100644
index 000000000..b9146ceea
--- /dev/null
+++ b/nxcomp/BlockCache.h
@@ -0,0 +1,59 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef BlockCache_H
+#define BlockCache_H
+
+
+// Cache to hold an arbitrary-length block of bytes
+
+class BlockCache
+{
+ public:
+ BlockCache():buffer_(0), size_(0), checksum_(0)
+ {
+ }
+ ~BlockCache()
+ {
+ delete[]buffer_;
+ }
+ int compare(unsigned int size, const unsigned char *data,
+ int overwrite = 1);
+ void set(unsigned int size, const unsigned char *data);
+
+ unsigned int getLength() const
+ {
+ return size_;
+ }
+ unsigned int getChecksum() const
+ {
+ return checksum_;
+ }
+ const unsigned char *getData() const
+ {
+ return buffer_;
+ }
+
+ static unsigned int checksum(unsigned int size, const unsigned char *data);
+
+private:
+ unsigned char *buffer_;
+ unsigned int size_;
+ unsigned int checksum_;
+};
+
+#endif /* BlockCache_H */
diff --git a/nxcomp/BlockCacheSet.cpp b/nxcomp/BlockCacheSet.cpp
new file mode 100644
index 000000000..8959ba2b3
--- /dev/null
+++ b/nxcomp/BlockCacheSet.cpp
@@ -0,0 +1,135 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "Misc.h"
+#include "BlockCacheSet.h"
+
+
+BlockCacheSet::BlockCacheSet(unsigned int numCaches):
+ caches_(new BlockCache *[numCaches]), size_(numCaches),
+ length_(0)
+{
+ for (unsigned int i = 0; i < numCaches; i++)
+ caches_[i] = new BlockCache();
+}
+
+
+BlockCacheSet::~BlockCacheSet()
+{
+ //
+ // TODO: There is still a strange segfault occurring
+ // at random time under Cygwin, when proxy is being
+ // shutdown. Problem appeared just after upgrading
+ // to the latest version of the Cygwin DLL. A stack
+ // trace, obtained at the last minute, reveals that
+ // failure happens in this destructor.
+ //
+
+ #ifndef __CYGWIN32__
+
+ for (unsigned int i = 0; i < size_; i++)
+ delete caches_[i];
+ delete[]caches_;
+
+ #endif /* ifdef __CYGWIN32__ */
+}
+
+
+int
+BlockCacheSet::lookup(unsigned int dataLength, const unsigned char *data,
+ unsigned int &index)
+{
+ unsigned int checksum = BlockCache::checksum(dataLength, data);
+ for (unsigned int i = 0; i < length_; i++)
+ if ((caches_[i]->getChecksum() == checksum) &&
+ (caches_[i]->compare(dataLength, data, 0)))
+ {
+ // match
+ index = i;
+ if (i)
+ {
+ BlockCache *save = caches_[i];
+ unsigned int target = (i >> 1);
+ do
+ {
+ caches_[i] = caches_[i - 1];
+ i--;
+ }
+ while (i > target);
+ caches_[target] = save;
+ }
+ return 1;
+ }
+ // no match
+ unsigned int insertionPoint = (length_ >> 1);
+ unsigned int start;
+ if (length_ >= size_)
+ start = size_ - 1;
+ else
+ {
+ start = length_;
+ length_++;
+ }
+ BlockCache *save = caches_[start];
+ for (unsigned int k = start; k > insertionPoint; k--)
+ caches_[k] = caches_[k - 1];
+ caches_[insertionPoint] = save;
+ save->set(dataLength, data);
+ return 0;
+}
+
+
+void
+BlockCacheSet::get(unsigned index, unsigned int &size,
+ const unsigned char *&data)
+{
+ size = caches_[index]->getLength();
+ data = caches_[index]->getData();
+ if (index)
+ {
+ BlockCache *save = caches_[index];
+ unsigned int target = (index >> 1);
+ do
+ {
+ caches_[index] = caches_[index - 1];
+ index--;
+ }
+ while (index > target);
+ caches_[target] = save;
+ }
+}
+
+
+
+void
+BlockCacheSet::set(unsigned int dataLength, const unsigned char *data)
+{
+ unsigned int insertionPoint = (length_ >> 1);
+ unsigned int start;
+ if (length_ >= size_)
+ start = size_ - 1;
+ else
+ {
+ start = length_;
+ length_++;
+ }
+ BlockCache *save = caches_[start];
+ for (unsigned int k = start; k > insertionPoint; k--)
+ caches_[k] = caches_[k - 1];
+ caches_[insertionPoint] = save;
+ save->set(dataLength, data);
+}
diff --git a/nxcomp/BlockCacheSet.h b/nxcomp/BlockCacheSet.h
new file mode 100644
index 000000000..e27b18088
--- /dev/null
+++ b/nxcomp/BlockCacheSet.h
@@ -0,0 +1,41 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef BlockCacheSet_H
+#define BlockCacheSet_H
+
+#include "BlockCache.h"
+
+
+class BlockCacheSet
+{
+ public:
+ BlockCacheSet(unsigned int numCaches);
+ ~BlockCacheSet();
+
+ int lookup(unsigned int size, const unsigned char *data,
+ unsigned int &index);
+ void get(unsigned int index, unsigned int &size, const unsigned char *&data);
+ void set(unsigned int size, const unsigned char *data);
+
+ private:
+ BlockCache ** caches_;
+ unsigned int size_;
+ unsigned int length_;
+};
+
+#endif /* BlockCacheSet_H */
diff --git a/nxcomp/CHANGELOG b/nxcomp/CHANGELOG
new file mode 100644
index 000000000..b7ef0d97a
--- /dev/null
+++ b/nxcomp/CHANGELOG
@@ -0,0 +1,3778 @@
+ChangeLog:
+
+nxcomp-3.5.0-2
+
+- Fixed TR11H02398. Solved a race condition when creating channels.
+
+nxcomp-3.5.0-1
+
+- Opened the 3.5.0 branch based on nxcomp-3.4.0-7.
+
+- Updated copyright to year 2011.
+
+nxcomp-3.4.0-7
+
+- Fixed TR03H02334. Modified the UNIX domain socket checks on MacOSX
+ to be compliant with the standard introduced in OSX 10.6.3.
+
+nxcomp-3.4.0-6
+
+- Solved compilation problems on Solaris.
+
+nxcomp-3.4.0-5
+
+- Solved compilation problems on GCC 4.4.
+
+nxcomp-3.4.0-4
+
+- Added reference to fixed TR02H02325.
+
+nxcomp-3.4.0-3
+
+- Updated copyright to year 2010.
+
+nxcomp-3.4.0-2
+
+- Fixed TR03G02204. Changed the parsing of X authority entries in
+ order to handle the case where the hostname includes white spaces.
+
+- Fixed TR02H02325. Bug in PNG decompression on 16bpp displays.
+
+nxcomp-3.4.0-1
+
+- Opened the 3.4.0 branch based on nxcomp-3.3.0-4.
+
+- Changed version number.
+
+- Updated copyright to year 2009.
+
+nxcomp-3.3.0-4
+
+- Check if the variable storing the ping time exceeded the maximum
+ integer value.
+
+- Recover incorrect sequence number when the proxy is not connected
+ to an agent.
+
+nxcomp-3.3.0-3
+
+- Removed a condition in ClientChannel that caused a loss in event
+ sequence numbers.
+
+nxcomp-3.3.0-2
+
+- Updated VERSION.
+
+nxcomp-3.3.0-1
+
+- Opened the 3.3.0 branch based on nxcomp-3.2.0-7.
+
+nxcomp-3.2.0-7
+
+- Solved a compilation problem on GCC 4.3.
+
+nxcomp-3.2.0-6
+
+- Changes considering that unsetenv() returns void on Mac OS X.
+
+nxcomp-3.2.0-5
+
+- Fixed TR03F02024. Assume the launchd unix socket as X socket.
+
+- Changed the authorization cookie retrieval when using the launchd
+ socket on MacOSX, in order to wait for the X server start.
+
+nxcomp-3.2.0-4
+
+- Fixed TR03F02026. Unset environment variable LD_LIBRARY_PATH before
+ calling the exec function running nxclient.
+
+nxcomp-3.2.0-3
+
+- Fix addMsTimestamp() and subMsTimestamp(). Remove the check for
+ a valid input in diffTimestamp() and diffUsTimestamp().
+
+nxcomp-3.2.0-2
+
+- Fixed TR12E01973. Now the correct number of bits are used for the
+ number of points in a X_FillPoly request.
+
+nxcomp-3.2.0-1
+
+- Opened the 3.2.0 branch based on nxcomp-3.1.0-6.
+
+nxcomp-3.1.0-6
+
+- Always use a timeout of 50 ms to update the congestion counter.
+
+nxcomp-3.1.0-5
+
+- Solve the possible deadlock caused by both proxies running out of
+ tokens at the same time.
+
+- In ServerProxy::handleCheckDrop() copy the list since the function
+ can delete the elements.
+
+nxcomp-3.1.0-4
+
+- Classes ProxyReadBuffer and ServerReadBuffer returned an invalid
+ number of characters to read in the case of a readable() failure.
+
+- Connections to proxy versions newer than 3.0.0 were not accepted.
+
+- Reading options from file could fail if options contained spaces
+ or other characters interrupting the fscanf()'s matching of input.
+
+- The proxy link could not be flushed if a ping request was replied
+ after the end of the read loop.
+
+nxcomp-3.1.0-3
+
+- Fixed a compilation warning on GCC 2.95.
+
+nxcomp-3.1.0-2
+
+- Solved TR09E01852. Modified the encoding of the number of rects
+ in the XSetClipRectangles request.
+
+nxcomp-3.1.0-1
+
+- Opened the 3.1.0 branch based on nxcomp-3.0.0-46.
+
+nxcomp-3.0.0-46
+
+- Fixed TR09E01865 and TR08E01831. Assume the old behavior of compr-
+ essing the CUPS, SMB, HTTP and Font channels when connected to an
+ old proxy version.
+
+nxcomp-3.0.0-45
+
+- Retry the fork() 30 times, but only on Cygwin (Sob!).
+
+nxcomp-3.0.0-44
+
+- In the case of an error, try again to fork() after a second, for
+ a maximum of 5 times. This seems to constitute a valid workaround
+ to the intermittent fork() failures observed on Windows.
+
+- Fixed NXTransDestroy() to show the 'broken connection' dialog in
+ the case of a link failure on the client.
+
+- Handled the 'terminating' and 'terminated' messages as new proxy
+ stages.
+
+- Force all the directories and files created by the library to be
+ readable only by the user.
+
+- Don't complain if the shared memory segment can't be initialized
+ on Windows.
+
+nxcomp-3.0.0-43
+
+- Prevent the reallocation of the read buffer in the proxy and the
+ agent transport if the buffer was borrowed by an upper layer.
+
+- Let the house-keeping process remove the cache directories that
+ are empty and have not be used since more than 30 days.
+
+- Added the missing destruction of the ZLIB stream used for image
+ decompression at proxy shutdown.
+
+nxcomp-3.0.0-42
+
+- In handleFastWriteReply(), moved the initialization of the 'next'
+ pointer inside the '#ifndef __sun' block. This should fix the re-
+ maining encoding problems on SPARCs.
+
+nxcomp-3.0.0-41
+
+- VMWare virtual machines can have the system timer deadly broken.
+ In the case of timeout, try to send a ping regardless we are the
+ client or the server proxy to force a write by the remote.
+
+- Removed the messages 'Info: Using image cache parameters...' and
+ 'Info: Using image streaming parameters...' from the session log
+ if the image cache is not enabled.
+
+nxcomp-3.0.0-40
+
+- Removed the messages 'Info: Shutting down the NX transport' and
+ 'Info: End of NX transport requested by...' at the session tear
+ down.
+
+- Removed the remaining log output.
+
+nxcomp-3.0.0-39
+
+- Don't reset the timer on proxy shutdown.
+
+nxcomp-3.0.0-38
+
+- Don't reinstall the signal handler if the signal was enabled and
+ the NXTransSignal() action is NX_SIGNAL_FORWARD.
+
+nxcomp-3.0.0-37
+
+- Return a congestion level 9 if the agent channel is in congestion
+ state.
+
+- Set the idle timeout (used to update the congestion state) based
+ on the link type. The timeout is 0 ms with link LAN and 50 ms in
+ the case of MODEM, with the other values in the between.
+
+nxcomp-3.0.0-36
+
+- Changed the encoding of the RenderCompositeGlyphs requests. Use
+ a separate cache for X and Y. Send a single bit for offset X and
+ offset Y if they match the source coordinates.
+
+- Always use the memcpy() in the handleFastWrite*() on Sun, as the
+ alternative code doesn't work on SPARC if the buffer is not word
+ aligned.
+
+nxcomp-3.0.0-35
+
+- Ensure a smooth transition between different congestion states.
+ If the current congestion counter is greater than the previous,
+ ramp up the value quickly by taking the current counter. If the
+ new congestion counter is less than the previous, ramp down the
+ reported value slowly by calculating the average of the last 8
+ updates. Reset the congestion counter to 0 if no token reply is
+ pending and the proxy is idle until a timeout.
+
+nxcomp-3.0.0-34
+
+- Calculate the current congestion as the average of the last 3
+ frames sent.
+
+nxcomp-3.0.0-33
+
+- When sending a X_RenderCompositeGlyph update, send a single bit
+ if the source X and Y match the values in cache.
+
+- Save the check on the bytes readable from the proxy descriptor
+ after the select.
+
+nxcomp-3.0.0-32
+
+- Send the X_GetImage replies uncompressed to better leverage the
+ stream compression.
+
+- Before entering in congestion state, force a read to see whether
+ the remote proxy aborted the connection.
+
+nxcomp-3.0.0-31
+
+- Try to avoid using memcpy() in the channels' handleFastWrite*().
+
+- Changed the copyright notices at the beginning of the files that
+ were referring to NXPROXY to refer to NXCOMP.
+
+nxcomp-3.0.0-30
+
+- Set the agent and proxy descriptors as ready, when appropriate,
+ also in the case of an interrupt.
+
+nxcomp-3.0.0-29
+
+- On the X server side, read more events from the X server just be-
+ fore and just after having written some data. On the X client si-
+ de, apply the same to the proxy link.
+
+- X11 channels originate a congestion control code as soon as the-
+ re is some data left from the previous write.
+
+nxcomp-3.0.0-28
+
+- Displaced automatically the "channel unresponsive" and "no data
+ received from remote" dialogs if the error condition is ceased.
+
+- Rewritten the channel drain procedure.
+
+- Tried a different approach to the channel congestion handling.
+ The X11 channels are not drained after a blocking write but only
+ if a further write is requested and the channel is still in con-
+ gestion state. Benchmarks show that this is still sub-optimal,
+ compared to sending the congestion code immediately also for X11
+ channels. See the next version.
+
+nxcomp-3.0.0-27
+
+- Ignored the agent option 'defer'.
+
+- Dropped support for the 1.4.0 versions.
+
+- More fixes to support the 1.5.0.
+
+nxcomp-3.0.0-26
+
+- The channel read buffer can inherit the data to be encoded from
+ the caller, instead of taking it from the transport. This is le-
+ veraged by the agent channel to save the copy.
+
+- Ignored the agent option 'clients'.
+
+- Removed the deprecated code checking the CPU limits.
+
+- Removed the counters that were duplicated in the Statistics and
+ the Control classes.
+
+nxcomp-3.0.0-25
+
+- Improved the effectiveness of the caching of the RenderComposite-
+ Glyphs by using a differential encoding for the length, delta x
+ and delta y of the first string.
+
+- Better handle the compatibility between different formats of the
+ cache files.
+
+nxcomp-3.0.0-24
+
+- Fixed the compatibility problem with the 1.5.0 versions.
+
+- Removed the constants NXDisplayReadable, NXDisplayFlushable and
+ NXDisplayCongestion. Now nxcompext has three distinct functions.
+
+nxcomp-3.0.0-23
+
+- Removed support for Rdp, Tight and Hextile packed images decod-
+ ing since they have been made obsolete by the new NX server.
+
+- Use the value 0 for the action is_hit and 1 for is_added. This
+ increases the frequency of zeroes in the encode buffer, giving
+ better compression.
+
+- Changed the copyright attribution from Medialogic to NoMachine.
+
+nxcomp-3.0.0-22
+
+- Implemented an alternate encoding reducing the size of the int
+ caches to 16 elements and transmitting the values in blocks of
+ 4 bits. Although this encoding reduces the effectiveness of
+ the differential compression, it allows better compression of
+ the final stream, resulting in a 10% to 20% gain in all condi-
+ tions. The new encoding is experimental, as it would make the
+ 3.0.0 incompatible with all the previous NX versions. Use this
+ package as a reference for future implementations.
+
+nxcomp-3.0.0-21
+
+- Encode the first two coordinates of a FillPoly message by using
+ a differential encoding, thus the origin of the polygon is not
+ included in the checksum. This is aimed to work in combination
+ with the agent converting all the FillPoly requests to the rela-
+ tive coordinate mode.
+
+- Add the coordinate mode to the checksum of the PolyPoint and Po-
+ lyLine messages so that it doesn't need to be encoded separately.
+
+- Disable the streaming for all messages. This saves the encoding
+ of a boolean for each message that could potentially be streamed.
+
+- Don't try to match the block size when encoding a value based on
+ an IntCache. This saves the additional bool in 90% of the cases.
+
+- Removed support for the former RDP and RFB session types.
+
+- Due to the above changes, this version is incompatible with the
+ previous 3.0.0 releases.
+
+nxcomp-3.0.0-20
+
+- Added support for the 'lossy', 'lossless' and 'adaptive' pack me-
+ thod literals. These values activate the dynamic selection of the
+ pack method by the agent.
+
+- Made the 'adaptive' pack method the default for all link types.
+
+- The default stream compression level is now 6 for link type ISDN,
+ 4 for ADSL and 1 for WAN. The default data compression level is
+ always 1, except for LAN that is 0.
+
+nxcomp-3.0.0-19
+
+- Print in the session log the X11 display that the proxy is imper-
+ sonating or where it is forwarding the connections.
+
+nxcomp-3.0.0-18
+
+- Implemented decoding of the PACK_BITMAP_16M_COLORS pack method.
+ This is a very simple encoder removing the 4th byte in 32 bits-
+ per-plane images. This encoder is intended to better leverage
+ the stream compression on low bandwidth links.
+
+- Removed the code cleaning the padding bytes in RenderAddGlyphs
+ and RenderCompositeGlyphs as the operation is now performed in
+ Xrender.
+
+- Removed the "Info: Synchronizing local and remote caches." and
+ "Info: Using remote server '...'." lines from the session log.
+
+nxcomp-3.0.0-17
+
+- Determine the CPU endianess at compile time only on Linux and
+ Cygwin.
+
+nxcomp-3.0.0-16
+
+- Optimized the memory allocation of the ReadBuffer classes. The
+ buffers from the Transport classes are used, when possible, to
+ avoid the copy.
+
+nxcomp-3.0.0-15
+
+- Reworked the code dealing with the channel congestion control
+ messages for increased efficiency.
+
+- Removed the pending_ flag from the proxy and channel class mem-
+ bers. If needed, the classes will query the transport buffers
+ to see whether there is data pending.
+
+- Removed the additional parameter from NXTransFlush().
+
+- The proxy doesn't issue anymore a sync control message on a X
+ reply. Anyway it will respond to the request for compatibility
+ with older versions.
+
+- Removed the dynamic selection of the encoding method for the
+ X_QueryColors request and X_ListFonts, X_QueryColors, X_Query-
+ Font replies. Now all of them use the differential encoding to
+ better leverage the stream compression.
+
+- Fixed to build on SUNs where we don't have endian.h.
+
+nxcomp-3.0.0-14
+
+- When handling partial proxy messages, don't try to determine how
+ many bytes still need to be read if the ZLIB stream compression
+ is enabled.
+
+- If a message can't be located, the ReadBuffer class checks if it
+ is possible to consume the data already in the transport buffer.
+
+- Optimize the MD5 routines by determining the CPU endianess at
+ compile time.
+
+- Force Drawable and GContext to be CARD32 in NXproto.h.
+
+nxcomp-3.0.0-13
+
+- Changed the semantic of the NXDisplayFlushHandler callback. The
+ function is called when new data is sent to the remote proxy or,
+ with a 0 length, upon receiving a new token reply, so that the
+ agent can update its congestion state.
+
+- Added the NXTransCongestion() function. It returns a value bet-
+ ween 0 and 9 indicating the congestion level based on the tokens
+ remaining. A value of 9 means that the link is congested and no
+ further data can be sent.
+
+- Removed the congestion and synchronization callbacks. They are
+ superseded by the new NXTransCongestion() implementation.
+
+nxcomp-3.0.0-12
+
+- Send the token replies at the end of the encode loop instead of
+ flushing the buffer immediately.
+
+- Fixed TR05E01687. Correctly detect CTRL-ALT-SHIFT-ESC keystrokes.
+
+- Renamed the option 'block' as 'tile'.
+
+- Ignored the option 'menu', targeting the X11 agent.
+
+nxcomp-3.0.0-11
+
+- Removed the 'interactive' attribute from generic channels. We now
+ use the 'prioritized' attribute which yields the same result.
+
+- Simplified the channels' read loop by always reading only once.
+
+- The channels flush the link as soon as the token is exceeded even
+ if the flush policy is set to deferred.
+
+- Reserved more cache memory for the images when running a shadow
+ session.
+
+nxcomp-3.0.0-10
+
+- Added the NXDisplayWriteHandler callback definition.
+
+- Removed the code that forced the proxy to retain in the cache the
+ images referenced at session startup.
+
+- Disabled the persistent image cache since this is not supported
+ by the current agent. This will save the overhead caused by the
+ image house-keeping process.
+
+nxcomp-3.0.0-9
+
+- Fixed a cast problem in GetBytesReadable().
+
+nxcomp-3.0.0-8
+
+- Added the NXTransTime() utility. It return the time in milliseconds
+ elapsed since the last call to the same function. Useful for bench-
+ marking the Xlib layer.
+
+- Ignored the option 'block' targeting the X11 agent.
+
+nxcomp-3.0.0-7
+
+- Ignored the options 'shadow', 'shadowmode' targeting the X11 agent.
+
+- Added the session type 'shadow'.
+
+nxcomp-3.0.0-6
+
+- Fixed TR08D01484. Fixed a bug in the cleanup of the RenderAddGlyphs
+ messages. We clean up only the glyphs with depth 8, width greater
+ than 1 and pixmap byte pad 4.
+
+- Added file COPYING.
+
+nxcomp-3.0.0-5
+
+- Updated the copyright notices to the current year.
+
+nxcomp-3.0.0-4
+
+- Imported changes up to nxcomp-2.1.0-6.
+
+- Fixed TR12D01563. Changed the configure script to always build the
+ library with the -fPIC option. This is related to the way SELinux
+ works.
+
+- Fixed the dependency from local endianess for 16-bit color RDP un-
+ packing functions. This caused wrong colors to be displayed when the
+ proxy and the X server were running on two separate machines having
+ different endianess.
+
+- Fixed TR10D01523. The Channel::handleDrain() method now accounts
+ for the fact that agent connections can't be drained.
+
+- Changed the Tight and Hextile decoding functions to use the X ser-
+ ver's byte order.
+
+nxcomp-3.0.0-3
+
+- Changed the main loop to handle the 3.0.0 version.
+
+nxcomp-3.0.0-2
+
+- Updated the file VERSION.
+
+nxcomp-3.0.0-1
+
+- Opened the 3.0.0 branch based on nxcomp-2.0.0-81.
+
+nxcomp-2.0.0-81
+
+- Fixed the dependency from local endianess for JPEG and PNG decomp-
+ ression functions. This caused wrong colors to be displayed when
+ the proxy and the X server were running on two separate machines
+ having different endianess.
+
+- Fixed the number of entries in the handleColormap() function.
+
+nxcomp-2.0.0-80
+
+- Fixed TR06D01404. Removed the warning issued when calling the
+ AgentTransport::drain() method.
+
+nxcomp-2.0.0-79
+
+- Updated copyright to year 2006.
+
+nxcomp-2.0.0-78
+
+- Fixed a bug in the cleanup of the RenderAddGlyphs messages.
+
+- Minor improvements to the encoding of the RenderCompositeGlyphs
+ and RenderComposite requests.
+
+- Added 'client' to the list of the ignored options.
+
+nxcomp-2.0.0-77
+
+- Added cleanup of the padding bytes in the RenderCompositeGlyphs
+ and RenderAddGlyphs requests.
+
+nxcomp-2.0.0-76
+
+- Before adding the frame's bytes to the tokens, the frame length
+ is normalized based on the stream compression ratio of the last
+ frame sent. This avoids entering the drain loop too early when
+ the data is highly compressible.
+
+- Moved the render messages' private data in a union to reduce the
+ footprint of the render store.
+
+nxcomp-2.0.0-75
+
+- The action performed on the remote message store and the affected
+ position are encoded as a single value. This saves approximately
+ 1 bit per each message.
+
+- Created more Compat classes to handle the backward compatibility
+ with the 1.4.0 and 1.5.0 versions.
+
+nxcomp-2.0.0-74
+
+- Added support for the RLE pack method and made it the default if
+ the selected link type is LAN.
+
+- Removed the legacy support for the 'render' option provided at X
+ server side.
+
+- When defining LAME in ClientChannel.h, the channel has the chance
+ of suppressing more opcodes by the handleTaintLameRequest() call.
+ This is for test purposes.
+
+nxcomp-2.0.0-73
+
+- The X_NXSetUnpackColormap and X_NXSetUnpackAlpha now carry their
+ data in compressed form. The alpha data is compressed using the
+ ZLIB RLE encoding, while the colormap data is compressed using
+ the default ZLIB deflate.
+
+- Thanks to the above changes, ZLIB data compression of all messa-
+ ges which do not have a specific encoding is turned off, so that
+ we can make better use of the final stream compression.
+
+- Created new message structures to handle the compatibility with
+ the old proxy versions. When connected to an old proxy version
+ the agent should use the NXSetUnpackColormapCompat() and NXSet-
+ UnpackAlpha() interfaces.
+
+nxcomp-2.0.0-72
+
+- Solved a compatibility problem with the 1.X.X versions due to the
+ new render encodings.
+
+nxcomp-2.0.0-71
+
+- Improvements to X_RenderSetPictureClipRectangles.
+
+- Fixed handleUnpack() to pass the correct byte order parameter to
+ to the unpack alpha function.
+
+- Added the --parent parameter to the client even in pulldown mode.
+
+- Fixed a deallocation bug in StaticCompressor.
+
+nxcomp-2.0.0-70
+
+- Added compression of X_RenderSetPictureFilter, X_RenderSetPicture-
+ Transform, X_RenderTrapezoids and X_RenderTriangles.
+
+nxcomp-2.0.0-69
+
+- Changed the format of the persistent cache to accomodate the new
+ encoding of the render opcodes. Caches from the 1.4.0 and 1.5.0
+ should be still loaded and saved correctly when connected to and
+ old version of the library.
+
+- Improved the encoding of the X_CreatePixmap, X_ChangeGC, X_Render-
+ CompositeGlyphs, X_RenderComposite, X_RenderCreateGlyphSet, X_Rend-
+ erFreePicture and X_RenderCreatePicture messages. Added new classes
+ to handle the backward compatibility.
+
+- Made the static compression and decompression code reside in the
+ same class.
+
+nxcomp-2.0.0-68
+
+- Improved the encoding of the X_RenderCreateGlyphSet and X_Render-
+ FreeGlyphSet requests.
+
+nxcomp-2.0.0-67
+
+- Modified the following session messages:
+
+ From: "Session: Session starting at..."
+ To: "Session: Starting session at..."
+
+ From: "Session: Session terminating at..."
+ To: "Session: Terminating session at..."
+
+- Improved the encoding of the requests creating or freeing an XID.
+ This affects X_CreateGC, X_CreateWindow, X_CreatePixmap, X_Render-
+ CreatePicture, X_FreeGC, X_DestroyWindow, X_FreePixmap, X_Render-
+ FreePicture.
+
+- Added 'streaming' and 'backingstore' to the list of options used
+ by agents.
+
+- Modified the info messages to print the ZLIB compression paramet-
+ ers to be more compact and only use two lines.
+
+nxcomp-2.0.0-66
+
+- When part of an agent, the proxy will wait for the NXTransDestroy()
+ function to initiate the shutdown.
+
+nxcomp-2.0.0-65
+
+- Starting from this version the NX client can control the behaviour
+ of the client proxy by monitoring the messages marked as "Session".
+
+ At the early initialization the proxy will print:
+
+ "Session: Session starting at '...'.
+
+ The ellipsis here represent the current timestamp, as reported by
+ the POSIX function ctime():
+
+ Once the session is running, the proxy will print:
+
+ "Session: Session started at '...'.
+
+ At the time the session is being shut down, the proxy will print:
+
+ "Session: Session terminating at '...'.
+
+ Followed by:
+
+ Session: Session terminated at '...'.
+
+ The message "Info: Starting X protocol compression." is removed,
+ replaced by the message 'Session started at...' with the same
+ meaning.
+
+ Once the message 'Session: Session terminated at..." is read from
+ the output, the client can optionally wait for the proxy process
+ to complete and terminate with an exit code 0.
+
+- The "Session" messages are not printed when the proxy is running
+ on the NX server side, as part of an agent.
+
+nxcomp-2.0.0-64
+
+- Reduced the default token limit from 32 to 24. The synchronizat-
+ ion callback is triggered by the tokens going below half the li-
+ mit.
+
+nxcomp-2.0.0-63
+
+- Added -I/usr/include/c++ to makedepend.
+
+nxcomp-2.0.0-62
+
+- The house-keeping process is now started upon the initialization,
+ if the memory cache is enabled, and, on the X server side, when
+ the remote actually requested access to the image cache.
+
+nxcomp-2.0.0-61
+
+- Added a NX_HANDLER_SYNCHRONIZATION callback to NXTransHandler().
+ The handler is called when the agent can use the available band-
+ width to synchronize X objects that are corrupted or incomplete.
+
+ The reason can be:
+
+ NX_SYNCHRONIZATION_BEGIN: The synchronizationis is allowed.
+
+ NX_SYNCHRONIZATION_END: The synchronizationis must stop.
+
+ The reason can be determined by agents by using the NXBeginSynch-
+ ronization and NXEndSynchronization values defined in NXvars.h.
+
+- Bytes from 14 to 24 in the X_NXGetControlParameters reply report,
+ respectively, the frame timeout, the ping timeout, the preferred
+ image split mode and the split size threshold.
+
+nxcomp-2.0.0-60
+
+- The following messages used by the NX client and server have been
+ changed. The NX client and server should be modified accordingly.
+
+ From: "Info: Shutting down the link and exiting."
+ To: "Info: Shutting down the NX transport."
+
+ From: "Info: End of session requested by remote proxy."
+ To: "Info: End of NX transport requested by remote"
+
+ From: "Info: End of session requested by agent termination."
+ To: "Info: End of NX transport requested by agent."
+
+ From: "Info: End of session requested by signal '...'."
+ To: "Info: End of NX transport requested by signal '...'."
+
+ From: "Info: Shutting down the link and exiting."
+ To: "Info: Shutting down the NX transport."
+
+ From: "Info: Waiting for cleanup timeout to complete."
+ To: "Info: Waiting the cleanup timeout to complete."
+
+ From: "Info: Waiting for watchdog process to complete."
+ To: "Info: Waiting the watchdog process to complete."
+
+nxcomp-2.0.0-59
+
+- Added more debug information regarding the timing of packets sent
+ and received.
+
+- Added the possibility to redirect the log output to a file that can
+ be later shared with nxssh running in "binder" mode. This is useful
+ when testing the forwarding of the SSHD connection by nxssh to the
+ agent. Activated by defining BINDER in Loop.cpp.
+
+nxcomp-2.0.0-58
+
+- Fixed the unpack alpha procedure to only rely on the image's byte
+ order.
+
+- Solved the X_PutImage encoding problem introduced in the 2.0.0-56
+ version and restored the compatibility with the 1.5.0 version.
+
+nxcomp-2.0.0-57
+
+- Added a NXTransFile() function. It returns the name of the files
+ used by the proxy for the current session.
+
+ The type parameter can be:
+
+ NX_FILE_SESSION: Usually the file 'session' in the user's session
+ directory.
+
+ NX_FILE_ERRORS: The file used for the diagnostic output. Usually
+ the file 'errors' in the session directory.
+
+ NX_FILE_OPTIONS: The file containing the NX options, if any.
+
+ NX_FILE_STATS: The file used for the statistics output.
+
+ The returned string is allocated in static memory. The caller must
+ copy the string upon returning from the function, without freeing
+ the pointer.
+
+- Fixed the PANIC in the handling of the abort split messages.
+
+- The encode and decode routines may write one byte past the nominal
+ end of the encode buffer. This additional byte is now included in
+ the payload. This is actually a bug, but harmless.
+
+nxcomp-2.0.0-56
+
+- Added the X_NXAbortSplit request. It can be used to interrupt the
+ streaming of all the messages currently in the split store for
+ the specified resource. Depending if the split store did or did
+ not contain split messages, the client will receive an immediate
+ end-split or no-split notification.
+
+- Removed the split modes NXSplitModeEager and NXSplitModeLazy. The
+ available modes are now NXSplitModeSync and NXSplitModeAsync. The
+ mode NXSplitModeAsync replaces the two old modes and is now the
+ default. Similarly to NXSplitModeEager, it makes the proxy split
+ only messages that exceed a size threshold, but the threshold is
+ generally smaller than in the previous versions.
+
+ #define NXSplitModeDefault 0
+ #define NXSplitModeAsync 1
+ #define NXSplitModeSync 2
+
+- Renamed X_NXSplit to X_NXSplitData, X_NXAbortSplit to X_NXSplitEv-
+ ent.
+
+- The proxy now enters a drain loop, when in congestion state, and
+ waits until the decongestion. Also the X channels are drained if
+ blocked.
+
+- The select is restarted immediately if only the proxy is selected
+ and the data read doesn't make a complete message.
+
+- The size of the shared memory segment used by the X server proxy
+ is now negotiated at startup and the selected value is printed in
+ the session log.
+
+- Added the option 'encrypted'. It tells the local proxy if it is
+ running as part of a program providing encryption of the point to
+ point communication. This is useful to determine if the proxy can
+ block waiting for data or needs to yield to the client to let it
+ handle the remote connection.
+
+nxcomp-2.0.0-55
+
+- Fixed handleRestart() to correctly send the end-split event only
+ after the split sequence has been closed by the client.
+
+- Fixed a possible memory error in the Keeper class occurring if the
+ image caches were erased while in the middle of a loop.
+
+nxcomp-2.0.0-54
+
+- Added a '--parent' option when running the client in dialog mode.
+ This is the process id that needs to be signaled in the case the
+ user chooses to do so.
+
+nxcomp-2.0.0-53
+
+- Solved a bug in the abort split procedure. The procedure assumed
+ that the split had to be at the head of the list. This may not be
+ the case if the split was loaded from disk asynchronously, after
+ a different split with the same checksum was recomposed.
+
+- By defining STRICT in Loop.cpp, the number of available tokens is
+ set to 1. This can be used to trigger the congestion more often
+ than it would possible when testing the software with the default
+ settings.
+
+- Added more debug output to the procedures handling the streaming
+ of the images.
+
+- Reset the channel's split pending and congestion flags in the fin-
+ ish procedure. Not doing so caused some additional loops at chan-
+ nel shutdown.
+
+- Fixed a bug on Cygwin that prevented the log files to be succes-
+ sfully reopened when the size limit was exceeded.
+
+- Failures to write to the agent transport are reported sooner when
+ the channel is finished. This saves a few more loops at session
+ shutdown.
+
+nxcomp-2.0.0-52
+
+- Started implementing a new handler to let the agent include arbit-
+ rary data in the transport statistics. The parameter, in this case,
+ is a pointer to a pointer to a null terminated string. The pointer
+ is set at the time the handler is registered. The pointed string
+ will have to be filled by the agent with its statistics data. For
+ now, only the interfaces and the stubs exist.
+
+- Disabled the timestamp cache as it still causes rounding problems
+ when calculating the current bitrate.
+
+nxcomp-2.0.0-51
+
+- Removed SelectPackMethod(), not used anymore, from Pack.c.
+
+nxcomp-2.0.0-50
+
+- Added the X_NXFinishSplit request. It forces the proxy to comple-
+ tely transfer all the split messages for the given resource, and
+ then notify the agent.
+
+- Fixed the statistics to not account the split bits to the control
+ token.
+
+nxcomp-2.0.0-49
+
+- Fixed a bug that caused the split timeout to be reset even if a
+ different channel had splits to send.
+
+- Removed an error condition that arose when a split operation was
+ requested by a different channel than the first to connect. The
+ reason is that an auxiliary channel (like the one created by the
+ server to store the keyboard configuration) can be the first to
+ open a connection to the X server, then can come the agent, that
+ is actually using the NX opcodes. The proxy will now simply print
+ a debug message.
+
+- Removed a further error condition that was assumed by the proxy
+ if a pending flush was detected while no channel was selected for
+ output. This can actually happen if the channel was dropped and,
+ in the meanwhile, the user requested the proxy statistics.
+
+nxcomp-2.0.0-48
+
+- Added a 'type' parameter to NXTransFlush(). The new NX_FLUSH_IDLE
+ type should be used by agents when they have finished handling all
+ their clients and are going to wait for more input. This is the
+ case, for example, of the X agent returning from WaitForSomething()
+ after having set a small timeout to find out if there are clients
+ ready, or the case of a RDP agent finding that there are no opcodes
+ to read from the RDP server. The 'idle' flush should be requested
+ also when the X11 agent is waiting for a split to complete. When
+ the flag is used, the proxy uses the available bandwidth to encode
+ more low-priority data, like the payload of images being streamed.
+
+nxcomp-2.0.0-47
+
+- Added a new RGB image encoder. For now the encoder uses a static
+ Z stream to compress the image data in the destination buffer and
+ allows the agent to use the simplest encoding by still separating
+ the alpha channel from the image data. The new encoder can be the
+ the base for implementing color reduction by dithering or a color-
+ mapped translation of the image similar to PNG, but without the
+ PNG overhead and with the colormap being sent to the client using
+ the NXSetUnpackColormap() opcode.
+
+- Put the routines implementing static deflating and inflating of
+ a ZLIB buffer from the Compressor and Decompressor classes to Z.h
+ and Z.cpp, so that they can be reused by the new RGB encoder.
+
+nxcomp-2.0.0-46
+
+- Fixed a segfault arising when trying to start a child without the
+ NX transport.
+
+nxcomp-2.0.0-45
+
+- Undefined the MATCH directive.
+
+nxcomp-2.0.0-44
+
+- Changed the encoding of the X_CreatePixmap, X_CreateGC, X_Render-
+ CreatePicture, X_CreateWindow, RenderCreateGlyphSet.cpp. The Xid
+ of the drawable is included in the checksum of the X_CreateGC and
+ X_CreatePixmap message. This leverages the way GC and Pixmaps are
+ allocated in the X agent to obtain better compression.
+
+- Removed the code handling the reset of the proxy connection. This
+ functionality was unused since when the persistence is handled by
+ the agent.
+
+- Added a specific section in the statistics output for the render
+ extension.
+
+nxcomp-2.0.0-43
+
+- Fixed a bug that could have caused the sending of multiple 'end
+ split' messages for the same split sequence.
+
+- Added handling of the option 'strict'.
+
+nxcomp-2.0.0-42
+
+- Added a missing channel switch directive when encoding abort split
+ messages at the server side. This could cause a decoding error on
+ the client.
+
+nxcomp-2.0.0-41
+
+- Ensured that the link is flushed immediately on the X server side
+ when important keyboard or mouse activity is detected in input.
+
+- Implemented a timestamp cache to avoid calling gettimeofday() when
+ it is enough to use the last timestamp gotten from the system. The
+ cache can be disabled by undefining CACHE_TIMESTAMP in Timestamp.h.
+
+nxcomp-2.0.0-40
+
+- Added the karma delay field to the X_NXGetControlParameters reply.
+
+- Solved a bug with the decoding of split messages likely to happen
+ only when using link type LAN.
+
+nxcomp-2.0.0-39
+
+- Implemented separate flow control for the generic channels and the
+ image streaming data.
+
+- Improved the split procedure to load a message from the disk cache
+ if found after it was originally discarded because locked.
+
+nxcomp-2.0.0-38
+
+- Renamed the 'shmem' option as 'shseg'. The 'shmem' option becomes
+ specific to the agent and will be used together with 'shpix' option
+ to indicate if, respectively, the shared memory extension and the
+ use of the shared pixmaps must be enabled in the agent server. The
+ new option will be instead used by the proxy to determine the size
+ of the memory segment shared between the proxy and the X server.
+
+- Added the 'shmem', 'shpix', 'keyboard' and 'clipboard' to the list
+ of ignored options. The 'keyboard' option should become a synonym
+ of 'kbtype'. The 'clipboard' option will tell the NX agent if the
+ clipboard support should be disabled for security reasons.
+
+nxcomp-2.0.0-37
+
+- Ensured that, when running a raw session, the persistent cache is
+ loaded and saved only once in the session lifetime.
+
+- Added the 'product' keyword to the list of recognized options. The
+ option is ignored by the proxy, but can be used by the client or
+ server to facilitate the support.
+
+nxcomp-2.0.0-36
+
+- Added the remote version number in the X_NXGetControlParameters
+ reply.
+
+- Solved a bug related to the removal of the split stores.
+
+- Removed the unused code dealing with the flush timeouts.
+
+nxcomp-2.0.0-35
+
+- Added an alert to notify the user about the failure of the XDMCP
+ session.
+
+nxcomp-2.0.0-34
+
+- The active channels and the agent resources are now stored in a
+ list container.
+
+- Added the X_NXFreeSplit request. The request is currently unused
+ because the split store is freed automatically when it becomes
+ empty, but can be useful in future if we want to implement other
+ otpimizations.
+
+nxcomp-2.0.0-33
+
+- There is now a split store for each agent resource. This allows
+ the proxy to divide the bandwidth among the agent resources.
+
+- A simple round-robin load-balancing is implemented, dividing the
+ bandwidth equally among all the split stores.
+
+- If there is a previous request being streamed for the same res-
+ ource, cached messages are appended to the split store and then
+ committed in the original order. This makes possible to stream
+ the colormap and the alpha channel data even if the split invol-
+ ves multiple PutImage or PutPackedImage operations in a single
+ start-split/end-split sequence.
+
+- Implemented the commit store class. Made the commit store expand
+ the message in the final buffer, instead of leveraging the mes-
+ sage store.
+
+- Channels having a drop command pending are checked before trying
+ to load or save the cache. This prevents a potential problem with
+ the protocol violation error that could be reported by the client
+ side proxy.
+
+- The current bitrate is now calculated at the time the transport
+ actually writes the data to the network, not at the time the data
+ is appended to the transport by the upper layers. The reason is
+ that the data can be compressed by the stream compressor and so
+ we can calculate the real bitrate only after having flushed the
+ ZLIB stream. This fixes the wrong numbers that could be reported
+ by the statistics in the previous versions.
+
+nxcomp-2.0.0-32
+
+- More progress in the implementation of the new image streaming.
+ This version has been tested to work when connecting to an 1.5.0
+ server. Due to the significant changes in the way the streaming
+ works in the newer versions, split is never activated by a 2.0.0
+ server connecting to a 1.5.0 client.
+
+- Fixed the NXTransClose() function to check if the fd matches the
+ NX agent connection.
+
+- Fixed the includes in Socket.h to compile on Solaris and MacOSX.
+
+nxcomp-2.0.0-31
+
+- Changed the NX transport functions that didn't require a NX fd
+ parameter to include one. The value can be NX_FD_ANY to signify
+ any running NX transport.
+
+nxcomp-2.0.0-30
+
+- More progress in the new implementation of image streaming.
+
+- This version is apparently compatible with the 1.5.0 client but
+ needs further testing.
+
+nxcomp-2.0.0-29
+
+- Implemented the NXTransHandler() interface and made the proxy
+ use the callback, when it is installed, instead of the notifi-
+ cation events.
+
+- Modified the agent transport to report an EOF condition as soon
+ as the channel is dropped, instead of reporting the error only
+ after the proxy shutdown. This allows the agent to handle its
+ clients while the proxy is handling the grace timeout.
+
+- Added a test facility to verify if the client and server caches
+ match at shutdown.
+
+- More advances in the new implementation of streaming.
+
+nxcomp-2.0.0-28
+
+- Made the session shutdown faster, normally below 1 second. The
+ grace time is now 500 ms once all channels have been dropped.
+
+nxcomp-2.0.0-27
+
+- The house-keeping process will now run a number of iterations
+ (currently 100) and then will exit. The proxy will check the
+ exit code and, if it was requested by the child, will restart
+ it. This is intended to keep the memory consumption low, as I
+ noted that after a big number of iterations the performance of
+ the allocator start to degrade quickly.
+
+nxcomp-2.0.0-26
+
+- Started implementing the NXTransHandler() interface to let the X
+ application hook a callback and be notified by the proxy when an
+ event occurs. At the moment only congestion events are supported.
+ The interface makes possible for the caller to provide a generic
+ parameter. This parameter is passed back to the handler function.
+ The parameter is expected to be the Xlib display pointer, given
+ that the proxy doesn't have access to the Xlib structures.
+
+- Moved all the internal variables shared between Xlib, nxcompext
+ and the X server in nxcomp. Declarations and function prototypes
+ moved to NXvars.h.
+
+- Removed the NXGetCleanupParameters() and NXGetImageParameters()
+ interfaces and the remaining references to the unused display
+ buffer and image cleanup functions.
+
+- In this version the images streaming functionality is disabled.
+ Because of that, expect compatibility problems when connecting
+ to a different nxcomp version.
+
+nxcomp-2.0.0-25
+
+- Forced the end of the session earlier if a shutdown has been re-
+ quested by the local side but the remote proxy is not responding.
+
+nxcomp-2.0.0-24
+
+- Changed the loop deleting the file items in Keeper to pick the
+ first element instead of looping through the iterator.
+
+- More problems with the libstdc++ allocators. During the tests, I
+ found that the Keeper class can leak industrial amounts of memory.
+ Valgrind says that the objects that are not freed are the File
+ items we allocate in collect(). As this looked suspicious, I log-
+ ged the constructors and destructors and can say that everything
+ is OK on our side. Unfortunately, by removing the spleeps used
+ to let the program work silently in background, one can find that
+ the memory occupation still grows by a MB every few seconds. This
+ is less a problem on Windows, where the memory occupation remains
+ almost constant. See the comment on __USE_MALLOC below.
+
+- Increased the maximum size of a message that the proxy is allo-
+ wed to cache to 4MB. This makes possible to cache many replies
+ (like the X_QueryFont replies) that often exceeded the previous
+ limit.
+
+nxcomp-2.0.0-23
+
+- Fixed a compatibility problem with the 1.5.0 that arose when
+ encoding an image with caching and streaming temporarily disa-
+ bled.
+
+- Removed the __USE_MALLOC define in Types.h because this is what
+ the g++ developers want. I'll try to avoid flames and will not
+ comment on this.
+
+- Added a --with-use-malloc configure option. This option should
+ be used when building the release packages on GCC versions that
+ support it.
+
+- Started fixing the commit procedure to be able to deal with the
+ alpha channel.
+
+nxcomp-2.0.0-22
+
+- Prevented channels from being dropped while in the middle of a
+ read loop.
+
+- Added a small utility ensuring that the SIGCHLD we receive in
+ the main loop can be always identified as pertaining to an our
+ child.
+
+- Increased the space available for the control messages at the
+ beginning of the encode buffer and ensured that there is always
+ enough space for adding a token message, in the case this is
+ required by the flush procedure.
+
+- Made all cache files readable only by the user.
+
+nxcomp-2.0.0-21
+
+- Solved a bug in the streaming of images that caused the agent's
+ clients to never resume when connected to an old proxy version.
+
+nxcomp-2.0.0-20
+
+- Disabled again streaming of the alpha channel data. By enabling
+ it, the agent randomly fails to repaint the exposed regions. The
+ reason is to be investigated.
+
+nxcomp-2.0.0-19
+
+- Implemented the split mode NXSplitModeSync. When this mode is
+ selected, the proxy will not send data for the split at the head
+ of the list until the remote peer has confirmed that the message
+ is not available in the image cache. This is going to be useful
+ in combination with the lazy encoding.
+
+- Improved the error detection in the case of failure when saving
+ a recomposed split on disk.
+
+- Added X_NXSetUnpackAlpha to the set of requests that is possible
+ to stream.
+
+nxcomp-2.0.0-18
+
+- Enabled the 2.0.0 features that were disabled for test purposes.
+
+- Changed the LICENSE file to state that the software is only made
+ available under the version 2 of the GPL.
+
+nxcomp-2.0.0-17
+
+- Introduced a new channel of type 'slave'. Opening a slave channel
+ should let the proxy fork a new NX client instance (or whatever
+ is in the NX_SLAVE environment) and pass to it the channel's desc-
+ riptors. This client instance should authenticate the peer, for
+ example using the proxy cookie (see how this is implemented in
+ nxsensor) and then negotiate the service to be used on the channel.
+ This can be used to implement forwarding of arbitrary ports or ad-
+ ditional services, like a simple file transfer protocol. For now
+ the implementation is incomplete. Opening a slave channel will
+ result in the remote proxy just dropping the channel connection.
+
+- The proxy is forcibly flushed at the end of each loop. This is a
+ temporary fix until the agents are updated to flush the proxy link
+ on demand.
+
+- Added a new alert to be used in the case the RDP session is closed
+ and replaced with a new session using the same Windows account.
+
+- Added a forward declaration of class RenderMinorExtensionStore in
+ file RenderExtension.h to compile using gcc 4.0.X.
+
+- Added removal of all .orig file when running a 'make clean'.
+
+- The code uses again the 2.0.0 style shutdown (the one waiting for
+ the watchdog process to terminate).
+
+nxcomp-2.0.0-16
+
+- Added the NXTransChannel() interface to allow agents to create
+ new channels connected to the port that will be forwarded by the
+ remote proxy. The function returns 1 on success, 0 if the NX
+ transport is not running, or -1 in the case of error. On success,
+ the descriptor provided by the caller can be used for all the
+ subsequent I/O, including selecting the descriptor to check if
+ I/O is possible. The type parameter not only tells to the proxy
+ the remote port where the channel has to be connected, but gives
+ a hint about the type of data that will be carried by the channel,
+ so that the proxy can optimize the compression and schedule the
+ traffic on the slow link.
+
+ The type can be:
+
+ NX_CHANNEL_X: The channel will carry X traffic and it
+ will be connected to the remote X display.
+
+ NX_CHANNEL_CUPS: The channel will carry CUPS/IPP protocol.
+
+ NX_CHANNEL_SMB: The channel will carry SMB/CIFS protocol.
+
+ NX_CHANNEL_MEDIA: The channel will transport audio or other
+ multimedia data.
+
+ NX_CHANNEL_HTTP: The channel will carry HTTP protocol.
+
+ NX_CHANNEL_FONT: The channel will forward a X font server
+ connection.
+
+ Only the proxy running at the NX server/X client side will be
+ able to create a X, CUPS, SMB, MEDIA and HTTP channel. A proxy
+ running at the NX client/X server side can create font server
+ connections. The channel creation will also fail if the remote
+ end was not set up to forward the connection.
+
+ To create a new channel the agent will have to set up a socket-
+ pair and pass to the proxy one of the socket descriptors.
+
+ Example:
+
+ #include <sys/types.h>
+ #include <sys/socket.h>
+
+ int fds[2];
+
+ if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) < 0)
+ {
+ ...
+ }
+ else
+ {
+ //
+ // Use fds[0] locally and let the
+ // proxy use fds[1].
+ //
+
+ if (NXTransChannel(fds[1], NX_CHANNEL_X) <= 0)
+ {
+ ...
+ }
+
+ //
+ // The agent can now use fds[0] in
+ // read(), write() and select()
+ // system calls.
+ //
+
+ ...
+ }
+
+ Note that all the I/O on the descriptor should be non-blocking,
+ to give a chance to the NX transport to run in the background
+ and handle the data that will be fed to the agent's side of the
+ socketpair. This will happen automatically, as long as the agent
+ uses the XSelect() version of the select() function (as it is
+ normal whenever performing Xlib I/O). In all the other cases,
+ like presumably in the agent's main loop, the agent will have
+ to loop through NXTransPrepare(), NXTransSelect() and NXTrans-
+ Execute() functions explicitly, adding to the sets the descript-
+ ors that are awaited by the agent. Please check the implementa-
+ tion of _XSelect() in nx-X11/lib/X11/XlibInt.c for an example.
+
+nxcomp-2.0.0-15
+
+- Some changes aimed at finding a workaround to the unreliability
+ of the font server connections. The X server seems to be really
+ picky about the network latency, so we try to read immediately
+ from the channel just being created, instead of waiting for a
+ new loop. Additionally the proxy will keep reading from the font
+ server connections even after the maximum length of the frame
+ is exceeded, in the hope that it will be able to send a complete
+ message in a single frame.
+
+- updated the various internal consistency tests run when the TEST
+ or INFO flags are enabled.
+
+nxcomp-2.0.0-14
+
+- It seems that the closure of the socket sometimes takes several
+ seconds, on Windows, after the connection is broken, with the
+ result that the alert can be shown long after the user has gone
+ after the failed session. We now skip the closure of the proxy
+ link on Windows if we are about to exit.
+
+- The code uses the 1.5.0 style shutdown (the one waiting for the
+ term signal in the main process) to be able to test the library
+ with the old server. To activate the new shutdown procedure be
+ sure the define SHUTDOWN_COMPATIBLE_1_5_0 is unset.
+
+nxcomp-2.0.0-13
+
+- Ensured that static members in channels are reset at the time a
+ new proxy is created.
+
+- Adjusted the token parameters for link modes other than modem.
+
+nxcomp-2.0.0-12
+
+- Handled compatibility with 1.X.X versions not sending the count
+ field in the token.
+
+- The proxy link is now flushed automatically if the agent has not
+ set the deferred flush mode.
+
+nxcomp-2.0.0-11
+
+- Significant modifications to allow the proxy to keep data in the
+ encode buffer across multiple loops, until the data is flushed
+ because an explicit request by the agent or. Besides maximizing
+ the amount of data sent in a single frame, this makes it possi-
+ ble to move any packet coalescence strategy in the agent, while
+ still using an 'immediate' flush policy.
+
+- Modified the procedure handling the proxy congestion. Each token
+ now carries a counter which represents the multiple of TokenBytes
+ data bytes that need to be confirmed by the remote end. The ser-
+ ver side proxy will need to reply by including the value of the
+ counter. When sending a new token, the tokens are decremented by
+ the counter and the client side proxy enters in congestion when
+ the available tokens becomes zero or negative.
+
+nxcomp-2.0.0-10
+
+- Implemented specific encoding of the X_NXSetCacheParameters
+ request.
+
+- Handled the compatibility with older versions not supporting the
+ new request. In this case the request is handled at the local si-
+ de, preventing the X requests to be cached or split, even without
+ the cooperation of the remote side.
+
+nxcomp-2.0.0-9
+
+- Modified the shutdown procedure to not rely on the TERM signal
+ when the proxy needs to wait for the server to complete. We will
+ now print the following message in the session log:
+
+ Info: Watchdog running with pid 'n'.
+ Info: Waiting for watchdog process to complete.
+
+ The NX server will have to kill the process with the given pid
+ to mandate the proxy shutdown. This solves the TR11C01192.
+
+- Added the X_NXSetCacheParameters request. It tells to the proxy
+ how to handle caching of X requests, namely if requests should
+ be stored in the memory cache, split in smaller data chunks and,
+ in the case of images, saved on disk in the persistent image
+ cache. The request affects all the X messages until the proxy
+ is further configured.
+
+- Enabling and disabling loading and saving of persistent images
+ due to a X_NXSetCacheParameters is still to be implemented.
+
+- Implemented font channel compatibility with older versions. If
+ the font port is queried and the remote proxy doesn't support
+ the tunneling of font server connections, an empty string is
+ returned.
+
+- The option 'resize', used by agents, is now silently ignored.
+ This solves the TR10C01061.
+
+- Added the missing reference to TR08C000981.
+
+nxcomp-2.0.0-8
+
+- Discarded the first idea of setting the font path implicitly in
+ the X server side proxy, by means of a multi-pass operation and
+ added the NXGetFontParameters() request and reply. If the proxy
+ has been configured accordingly, the request returns the X font
+ path that can be set by the agent to tunnel the font server con-
+ nections through the NX link.
+
+- Changed the parsing of the font server port to be able to handle
+ the defaults.
+
+nxcomp-2.0.0-7
+
+- More work on tunneling of font server connections.
+
+- The synchronization requests are now replied at the same time
+ as a new request is received, by checking if the channel has
+ already gone past the awaited sequence number. This avoids the
+ delay caused by the previous versions in the case the expected
+ reply had been handled asynchronously, inside the write loop.
+
+nxcomp-2.0.0-6
+
+- The display is again passed to the client using the -display
+ parameter.
+
+- The option 'type' is now ignored at X server side. Support at
+ the X server side had been left for compatibility with older
+ versions of the proxy.
+
+- Added direct support for more session types, namely 'unix-cde',
+ 'unix-xdm', 'unix-console', 'unix-default'. The 'unix-console'
+ and 'unix-default', similarly to 'unix-application', will now
+ trigger the 'raw' mode.
+
+nxcomp-2.0.0-5
+
+- Renamed Png.h and Png.cpp to Pgn.h and Pgn.cpp to avoid name
+ clashes on Windows.
+
+- The distclean target now removes the autom4te.cache directory.
+
+nxcomp-2.0.0-4
+
+- Added the 'kill=n' option to let the proxy add the process to
+ the list of daemons that must be terminated at session shutdown.
+ Multiple 'kill=n' options can be specified. The proxy will send
+ to each process a SIGTERM signal before exiting.
+
+- Removed the code that forcibly disabled the RENDER extension on
+ Solaris. Eventual compatibility problems will be now handled in
+ the NX agent.
+
+- The path to the client executable is now assumed to be in the
+ NX_CLIENT environment. If the variable is not set, the proxy
+ will guess the location based on the target platform. This is
+ part of the FR11C01215.
+
+nxcomp-2.0.0-3
+
+- More code cleanup. Protocol step 5 and cache step 2 are assumed
+ by default. This allows us to remove the various channel caches
+ that are not used anymore.
+
+- Removed the outdated encodeBegin() and encodeEnd() methods.
+
+nxcomp-2.0.0-2
+
+- Added compatibility with the 1.5.0 and 1.4.0 versions. Protocol
+ version older than the 1.4.0 will cause the proxy to drop the
+ connection.
+
+- Rewritten the version negotiatiation logic to better handle the
+ compatibility between the local and the remote version.
+
+- Removed the conditional code handling the compatibility with
+ the protocol encodings from 1 to 4.
+
+- Small adjustments to the session negotiation procedure to give
+ more hints in the case of errors. A dialog is now issued if the
+ remote proxy drops the connection due to a probable authentica-
+ tion failure or when the remote proxy version is not compatible
+ with our version.
+
+nxcomp-2.0.0-1
+
+- Opened the 2.0.0 branch based on nxcomp-1.5.0-80.
+
+nxcomp-1.5.0-80
+
+- Fixed a bug that prevented the Unix socket to be created in the
+ right directory. This affected the ability of the server to run
+ NX sessions without the agent encoding.
+
+nxcomp-1.5.0-79
+
+- Solved a bug in the 15 to 16 bpp conversion for RDP sessions.
+
+- Corrected a typo in the PANIC message of Unpack16To24 function.
+
+- Optimized the 16 bpp RDP decompression routine.
+
+nxcomp-1.5.0-78
+
+- Implemented the FR11C01215. The following environment variables
+ are checked to determine the location of the relevant NX direct-
+ ories:
+
+ $NX_ROOT The root NX directory is the place where the session
+ directory and the cache files are created. This is
+ usually overridden by passing the 'root=' option. By
+ default, the root NX directory is assumed to be the
+ directory '.nx' in the user's home.
+
+ $NX_SYSTEM The directory where NX programs and libraries reside.
+ If not set, the value is assumed to be '/usr/NX'.
+ Programs, libraries and data files are respectedly
+ searched in the 'bin', 'lib' and 'share' subdirecto-
+ ries.
+
+ $NX_HOME The NX user's home directory. If $NX_ROOT is not set
+ or invalid, the user's NX directory is created here.
+
+ $NX_TEMP The directory where the X11 Unix Domain Sockets and
+ all temporary files are to be created.
+
+ $NX_CLIENT The full path to the nxclient executable. If the va-
+ riable is not set, the nxclient executable will be
+ run assuming that the program is in the system path.
+ This can be useful on platforms like Windows and the
+ Mac where nxclient is located in a different direct-
+ ory compared to the other programs, to make easier
+ for the user to execute the program from the shell.
+
+ Other environment variables:
+
+ $HOME The variable is checked in the case $NX_HOME is not
+ set, null or invalid.
+
+ $TEMP The variable is checked whenever the $NX_TEMP direct-
+ ory is not set, null or invalid.
+
+ $PATH The path where all executables are searched, except
+ nxclient. If $NX_CLIENT is not set, also the client
+ executable is searched in the system path.
+
+ $XAUTHORITY
+ This is the file containing the X11 authorization
+ cookies. If not set, the file is assumed to be in
+ the user's home (either $NX_HOME or $HOME).
+
+ $LD_LIBRARY_PATH
+ System-wide library search order. This should be set
+ by the program invoking the proxy.
+
+- Extended the usage message to document the relevant environment.
+
+- Made the WaitChild() function interruptible. This allows the user
+ to terminate the connection procedure immediately, by sending a
+ signal to the proxy process while an abort dialog is shown in the
+ foreground. This is part of the solution to the TR11C01216.
+
+- Implementation of the FR11C01215 is not yet complete. The client
+ program and the nxauth/xauth are still searched using the previ-
+ ous logic. In particular, the NX_CLIENT variable is ignored.
+
+nxcomp-1.5.0-77
+
+- Corrected the typo in the alert dealing with VNC authentication
+ failures. This solves the TR11C01199.
+
+nxcomp-1.5.0-76
+
+- Added the missing initialization in the client and server proxy
+ constructors.
+
+nxcomp-1.5.0-75
+
+- Starting from this version the build procedure will automatically
+ create a DLL on Windows.
+
+- Disabled exceptions in the compiled code.
+
+- Disabled run-time type information to further reduce the size of
+ the executable.
+
+- Compiling with GCC 4.0.0 and -fno-exceptions produces a warning
+ in some files, due to control apparently reaching the end of a
+ non void function. This is bogus and has been confirmed to be a
+ GCC 4.0.0 bug solved in more recent versions.
+
+nxcomp-1.5.0-74
+
+- Imported changes from nxcomp-1.6.0-4.
+
+- Solved a compilation error on Solaris and MacOSX platforms.
+
+- The 'font' option now accepts either a numeric value (for a TCP
+ port) or a 'unix/:port' or 'tcp/:port' specification. In the case
+ of Unix ports, we assume that a port specification "unix/:7100"
+ corresponds to the "/tmp/.font-unix/fs7100" socket and a port like
+ "unix/:-1" corresponds to "/tmp/.font-unix/fs-1".
+
+- An absolute file path is also accepted as a valid socket. This is
+ useful to test the forwarding of the font server connection, for
+ example by running a normal X client and pointing the socket to
+ the X server.
+
+- Protocol step 7 is now only assumed when connecting to a remote
+ proxy version 1.6.0 or later.
+
+nxcomp-1.5.0-73
+
+- Use the include files from nx-X11 if the nx-X11/include directory
+ is found. The previous configure checked the presence of nx-X11/
+ exports/include, that might not be built at the time this library
+ is compiled.
+
+nxcomp-1.5.0-72
+
+- Fixed a problem on AMD64 due to the size of the area pointed by
+ the argument of NXTransReadable(). The ioctl() requires a pointer
+ to an int, at least on Linux. The original _X11TransBytesReadable()
+ function simply calls the ioctl() by passing the pointer that is
+ provided. Returning the value assuming a pointer to a long crashes
+ some applications, among them xterm. Now NXTransReadable() follows
+ the same schema of the ioctl() call and stores the result assuming
+ a pointer to an int.
+
+nxcomp-1.5.0-71
+
+- Ensured that the NX trasport is destroyed on NXTransExit() even if
+ the parent has overridden our signal handlers.
+
+- Added attribute 'noreturn' to NXTransExit().
+
+- Small change to the shutdown procedure to send the "Waiting for a
+ further signal" just after the watchdog is started.
+
+nxcomp-1.5.0-70
+
+- Added the definition of protocol step 7. The new protocol step is
+ implicitly assumed when activating the font server channel, so that
+ NX clients and servers may decide what to do by verifying the patch
+ version of the peer software. This makes possible to activate the
+ feature without stepping up the 1.5.0 version of the software.
+
+nxcomp-1.5.0-69
+
+- Added the -fPIC GCC flag when compiling on AMD64 architectures.
+
+- Small changes to configure.in to have specific CFLAGS.
+
+- Created a new configure using autoconf 2.59.
+
+- Added the 'mask=n' option to determine how channel ids are distri-
+ buted between client ans server. By default, channels whose ids are
+ multiple of 8 (starting from 0) are reserved for the client side.
+ All other channels can be allocated at the NX server side.
+
+- A check is missing on the protocol supported by the remote side,
+ so it is likely that mixing this version with older 1.5.0 will not
+ work.
+
+- The release has the debug logs enabled.
+
+nxcomp-1.5.0-68
+
+- Added provision for opening new channels on both client and server
+ side. This required small changes to the way channel ids are mapped
+ and retrieved, so that both sides now support bidirectional mapping
+ of file descriptors to channels.
+
+- Made the code handling allocation of new generic channels to reside
+ in the base Proxy class.
+
+nxcomp-1.5.0-67
+
+- Renamed the 'embedded keyboard' channels as 'aux' channel.
+
+- Renamed 'samba' channels as 'smb'.
+
+- The 'samba' and 'smb' options can now be used interchangeably. The
+ same applies to the 'keybd' and 'aux' options, used to set up the
+ auxiliary X channel.
+
+- Added a new font channel. The channel is used to forward X font
+ server connections from the X server on the NX client to the remote
+ NX server. The channel is not yet functional and requires change to
+ the channel allocation mechanism.
+
+- Added a common interface to create new listening sockets. This is
+ currently used for additional services, but not for the X display
+ sockets.
+
+- Simplified the interface used to accept new connections to channels
+ being forwarded.
+
+- Removed the -V option as nxproxy is not using dlopen() anymore.
+
+- Removed the inclusion of ClientChannel.h in Proxy.cpp that caused
+ test symbols to be reverted to undefined.
+
+nxcomp-1.5.0-66
+
+- Made failures setting the IPTOS_LOWDELAY on the proxy socket to
+ cause a warning, instead of an error.
+
+- Made the clean target delete the Cygwin specific libraries.
+
+- Updated the configure and Message.h to deal with GCC 4. Solves the
+ TR08C000981.
+
+nxcomp-1.5.0-65
+
+- Removed the warning issued on parsing the agent option 'rootless'.
+ Solves the TR08C00959.
+
+- MacOSX 10.4 defines socklen_t. Made the definition conditional so
+ that we can still support older versions. Solves the TR07C00926.
+
+- Updated the ChangeLog to include references to the solved TRs.
+
+nxcomp-1.5.0-64
+
+- Imported the 1.6.0 changes in the maintenance 1.5.0.
+
+- Removed a wrong assertion that might cause the session to fail
+ when the software was compiled with TEST enabled in Proxy.cpp.
+
+- The nxclient dialog process is signaled with SIGKILL on Windows,
+ as the SIGTERM is ignored. This solves the TR07C00929.
+
+- Ensured the JPEG error flag is always set before jumping out of
+ the Jpeg decompression.
+
+- Skipped errors encontered setting the TCP_NODELAY flag on Mac.
+ Solves TR08C00940.
+
+- Few cosmetic changes.
+
+nxcomp-1.5.0-63
+
+- Ensured that the parent is checked often in the keeper process,
+ so that, in the case of a premature death, the child is exited
+ earlier.
+
+nxcomp-1.5.0-62
+
+- Some performance tuning of the LAN and WAN link modes.
+
+nxcomp-1.5.0-61
+
+- Fixed the problems arisen with loading or saving the image files
+ on Windows by forcing the streams to be opened in binary mode.
+ These problems have been triggered by the recent Cygwin upgrade.
+
+- Removed the logs that had be left enabled for test purposes.
+
+nxcomp-1.5.0-60
+
+- Made all errors encountered while unpacking an image just print
+ a warning in the session log. Such errors are not fatal but the
+ client monitors the state of the session at startup, so that by
+ marking image decompression errors as such may cause the session
+ to be aborted.
+
+- Marked as warnings also errors encountered by trying to load an
+ image from disk.
+
+nxcomp-1.5.0-59
+
+- When detected, the CTRL+ALT+SHIFT+ESC sequence is removed from
+ the event stream.
+
+- Also modified the message in the session log to issue a warning,
+ instead of an error.
+
+nxcomp-1.5.0-58
+
+- Added a setjmp() before yielding the control to the JPEG library.
+ This makes possible to recover from the JPEG decompression errors
+ that were previously fatal.
+
+- In the commit split request, the client id is now encoded by the
+ client channel beside the propagate flag. The client id is igno-
+ red by the decoding party. As in the old protocol versions, the
+ committing client is taken from the client id that was originally
+ sent together with the packed image.
+
+- Fixed the compilation problem when defining OPCODES in Misc.cpp.
+
+- Skipped the synchronous flush of the proxy link when connecting
+ to a previous NX version.
+
+- Ensured that the new alerts are only requested when connected to
+ a compatible proxy.
+
+- Ignored the option 'fullscreen', targeting the agents.
+
+nxcomp-1.5.0-57
+
+- Added more alerts and changed the name of those dealing with the
+ resume of a session.
+
+- Added the MIXED define. When set, the proxy will let all logs go
+ to the standard error. This is useful to interleave the Xlib log
+ output with the proxy output in a single file.
+
+- Added a new alert to report an I/O error at agent reconnection.
+ This is one of those alert that can't be actually shown, but are
+ included to offer a consistent view to the agent implementation.
+
+nxcomp-1.5.0-56
+
+- Added the split mode NXSplitModeSync. By selecting this mode,
+ the proxy will try to empty the split store immediately, until
+ all messages marked in such mode will be synchronized. The im-
+ plementation is left to future versions.
+
+ #define NXSplitModeSync 3
+
+- Added the messages specific to RDP and VNC sessions in NXalert.h.
+ Included are also a few alerts related to changes to the color
+ depth and desktop geometry that may be eventually mandated by the
+ remote server.
+
+- Corrected a typo in NXalert.h
+
+nxcomp-1.5.0-55
+
+- Added the 'mode' field in the X_NXStartSplit request. It determi-
+ nes the strategy that the proxy will adopt to handle the image.
+ If set to 'eager', the proxy will only split the messages whose
+ size exceeds the split threshold (the threshold can be found in
+ the X_NXGetControlParameters reply). If mode is set to lazy, the
+ proxy will split any image that it is not able to find in its
+ cache.
+
+ The opcode and the two available modes are defined in NXproto.h,
+ currently:
+
+ #define NXSplitModeDefault 0
+ #define NXSplitModeEager 1
+ #define NXSplitModeLazy 2
+
+- All requests related to image streaming now carry a 'resource' id.
+ The id is currently ignored by the proxy in the case of X_NXCom-
+ mitSplit requests.
+
+- Added a new NXSetSplitMode() request. It determines the strategy
+ that the proxy will adopt to stream the images. If set to 'eager'
+ the proxy will only split the messages whose size exceeds the
+ split threshold, otherwise it will split all the images, regard-
+ less their size. This is in preparation of the lazy encoding in
+ agent.
+
+- Slightly decreased the startup timeout compared to the value it
+ had in the 1.4.0. This timeout is used to retain the images used
+ during session startup in the persistent cache.
+
+nxcomp-1.5.0-54
+
+- Modified the text of the begin-reconnection alert to mention the
+ speed of the connection detected by the agent.
+
+nxcomp-1.5.0-53
+
+- Reworked the handling of the abort-split events issued by the re-
+ remote proxy so that we now finalize the procedure and restart the
+ client asynchronously, in the middle of the write loop.
+
+nxcomp-1.5.0-52
+
+- Rewritten the internal interface to the notification events. There
+ are now five different split notification events:
+
+ NXNoSplitNotify The operation didn't cause any actual split.
+ The client can be immediately restarted.
+
+ NXStartSplitNotify A split is ongoing. The client should be
+ suspended until the end of the split.
+
+ NXCommitSplitNotify A complete request has been recomposed. The
+ client can commit the given request to the
+ X server.
+
+ NXEndSplitNotify The split operation was duly completed. The
+ client can be restarted.
+
+ NXEmptySplitNotify No more split operation are pending. The
+ agent can use this information to implement
+ specific strategies, requiring for example
+ that all messages have been recomposed at
+ the remote end. This can include updating
+ the drawables that were not synchronized
+ because of the lazy encoding.
+
+- Removed the X_NXSync and X_NXKarma operations, not used anymore by
+ the NX agents.
+
+- Increasing or decreasing a timeout makes it respectively equal to
+ the base timeout or to the base timeout / 4.
+
+nxcomp-1.5.0-51
+
+- Improved the mechanism by which synchronization replies are sent
+ to the X client proxy.
+
+- Using the leftPad field as passed by the nxagent 1.5.0-58 doesn't
+ seem to work with the MIT-SHM. The workaround is to disable the
+ MIT-SHM operation in the case of 1 bpp images.
+
+- Removed the limitation on the size of the shared memory segment
+ that had been introduced for test purposes.
+
+nxcomp-1.5.0-50
+
+- Solved compilation problem when --with-info was not given.
+
+nxcomp-1.5.0-49
+
+- Improved support for color matching from 15 and 16 bpp to 24 and
+ 32 bpp in RDP sessions.
+
+- Solved TR05C00910. This was about "shadows" that might appear in
+ RDP sessions when small elements like tooltips were drawn.
+
+nxcomp-1.5.0-48
+
+- Added a 'commit' field in X_NXCommitSplit request. When zero, the
+ X server side proxy will unlock the message in the message store,
+ without actually committing the image to the X server. The new
+ field required changes to the encoding. This means that this ver-
+ sion is not compatible with older 1.5.0 nxcomp releases.
+
+- Added a new control message. This is used by NXTransFlush() to for-
+ ce a roundtrip between the proxies and speed-up the handling of
+ the synchronous replies.
+
+nxcomp-1.5.0-47
+
+- Made the timeouts dynamic, based on the user input and the current
+ bitrate.
+
+- Set the available tokens to 8. This allows for 16 KB to be sent on
+ a modem before having to wait for a reply.
+
+- Added provision for removing a dialog on the remote end. This can
+ be used, for example, to get rid of a message that was displayed
+ by the agent at the beginning of a lengthy operation.
+
+- Fixed a fault that arose when trying to run the proxy on a display
+ whose name began with the 'nx' string.
+
+- Running a client or server session on a display host beginning with
+ 'nx' should be now possible.
+
+- Flush is now always immediate when there is already data queued in
+ the TCP socket.
+
+nxcomp-1.5.0-46
+
+- Created the NXalert.h header from Alert.h to share the alert codes
+ with the agents. The new NXTransAlert() interface can be used to
+ start an alert on the remote side.
+
+- Created the NXTransParse*() and NXTransCleanup() wrappers.
+
+- Temporarily using the single-dash style when passing parameters to
+ nxclient until the double-dash style is fixed.
+
+nxcomp-1.5.0-45
+
+- Motion events are now flushed at the time the X server channel is
+ sending the token reply.
+
+- Renamed NXDialog() to NXTransDialog() and added a window ID parame-
+ ter, to be used by the agent when starting the "pulldown" dialogs.
+
+- Also renamed the other NX utility functions.
+
+nxcomp-1.5.0-44
+
+- Solved a bug that prevented auxiliary X connections from working
+ with the agent.
+
+- Added the missing check on the proxy pointer in HandleTimer() as
+ it may be actually called before the proxy is created.
+
+nxcomp-1.5.0-43
+
+- Started tuning the performance. For now, the available tokens are
+ set to an improbable value of 10000.
+
+nxcomp-1.5.0-42
+
+- Disabled the X server side remote expose events for test purposes.
+ This is intended uncover the display problems introduced by the
+ new handling of exposures in the X11 agent.
+
+- Fixed some timestamp related functions to avoid rounding problems.
+
+- Moved the timestamps of split, frame and flush from the Control
+ to the Proxy class.
+
+- Made the X server side proxy to skip updating the counters related
+ to token management.
+
+- Made also the X server side use deferred flushes.
+
+- Modified handleFrame() to manage both pings and data frames and
+ send the token before the actual write operation.
+
+- Cleaned the pseudo-files used to load the control parameters.
+
+- Cleaned up the FIXME's in the lower layers. More FIXME's remain in
+ the frontend classes.
+
+- Added a further counter to the statistics tracking the number of
+ network writes performed on the proxy link.
+
+nxcomp-1.5.0-41
+
+- Now the watchdog process doesn't kill the parent. The termination
+ event is determined by monitoring the child. This is advisable to
+ avoid interfering with the signal handling in agent.
+
+- The house-keeping process is not started if differential compres-
+ sion (and thus caching of images) is disabled.
+
+- Wait for the watchdog to terminate before starting the house-keep-
+ ing process.
+
+- Fixed the problem of gray stripes in white areas when using 15bpp
+ RDP compression. This solves the TR06C00916.
+
+- Added a log in Message.cpp to tell when an image is removed from
+ the memory cache.
+
+- Removed most of the logs. Only left the logs directly related to
+ the deferred flushes.
+
+- Removed a warning due to a missing definition in NX.h.
+
+nxcomp-1.5.0-40
+
+- Added the signal '12' to the set of blocked signal on Cygwin. This
+ is what is delivered to the application when it tries to create a
+ shared memory segment when the cygserver is not running. The call
+ later returns "Operation not implemented", only useful if you have
+ survived to the signal.
+
+- Modified the configure script to not link against cygipc.
+
+- Improved error handling in the negotiation phase.
+
+- Reset the last signal received when aborting a connection attempt.
+ Not doing that might cause a warning.
+
+nxcomp-1.5.0-39
+
+- Both channels and proxy can now deal with incomplete messages by
+ waiting for the full data to become available. This is not going
+ to work when the transport is connected to an agent that is run-
+ ning in-process, so the timeout must be really small.
+
+- Made the main loop more compact by letting a single function read
+ from proxy and channels. The same change applies to writing to
+ descriptors that are reported to be ready.
+
+- Optimized the loop to report as ready the agent descriptors that
+ have become available after the proxy execution phase.
+
+- Now the proxy will try to asynchronously read from its descriptor
+ after having read from the channel.
+
+- Unified the routine setting the routines setting the read and write
+ descriptors and the timeouts.
+
+- Unified the routine handling the channel events.
+
+- Reintroduced the last ping timestamp to avoid sending more than a
+ single ping every PingTimeout milliseconds.
+
+- The cleanup procedure now waits for the watchdog and house-keeping
+ processes to terminate.
+
+- Reverted the change occurred in 1.5.0-35 about the byte-ordering
+ problem in decompression of PNG and JPEG images.
+
+- More coding about deferred flushes.
+
+- Relevant debug logs are enabled in the main loop, in the proxy and
+ in the channel classes, so use only for testing.
+
+nxcomp-1.5.0-38
+
+- Started the implementation of deferred writes based on the new NX
+ transport.
+
+- Solved a problem with the statistics file affecting the Windows
+ platform. Basically it seems that Cygwin or the stdc++ library
+ don't deal correctly with files that are reopened just after
+ having unlinked them.
+
+- Implemented support for 15 bpp RDP bitmaps.
+
+- Added "rdpcolors" and "rdpcache" to the list of parameters that
+ are ignored in ParseEnviromentOptions().
+
+- This version has lot of logs enabled and some forced cleanups in
+ the case of unexpected conditions. Avoid to use it for long-last-
+ ing test sessions.
+
+nxcomp-1.5.0-37
+
+- Now splitting of images is fully disabled when running with link
+ LAN. Previously the split was not executed by the proxy, but the
+ agent was configured to send the start/end sequence.
+
+- Added the check protecting the proxy from token underflow. In the
+ previous code the check was left out to verify the conditions un-
+ der which this event might be encountered.
+
+- NXTransWrite() and NXTransWriteVector() allocate a context before
+ calling handleRead(), so they can correctly return in the case of
+ a cleanup.
+
+nxcomp-1.5.0-36
+
+- Modified the channel write loop to interleave abort split events
+ between the writes to the X server socket.
+
+nxcomp-1.5.0-35
+
+- Solved a byte-ordering problem in decompression of PNG and JPEG
+ images.
+
+- Changed the handleToken() interface to make explicit if a token
+ must be issued because of a ping.
+
+- Small fix to prevent a warning in Jpeg.cpp when TEST is enabled.
+
+nxcomp-1.5.0-34
+
+- Solved a few problems introduced by the rewrite of the read loops
+ in channels.
+
+- Added definition of NXCollectInputFocusNotify.
+
+nxcomp-1.5.0-33
+
+- Implemented a new control flow system based on tokens exchanged bet-
+ ween the proxies. When the clint side runs out of tokens it stops
+ sending data until a new token is returned. Tokens are sent whenever
+ the data written to the socket exceeds the amount of data set for a
+ scheduled write, so the proxy is free to send a number of smaller
+ frames before running out of tokens.
+
+- The reason the new system is introduced is because the old method
+ had two outstanding problems:
+
+ - It worked very well when the proxies were directly connected,
+ but couldn't reliably detect a link congestion when tunneled
+ over SSH.
+
+ - In the attempt of reducing the data queued to the TCP layer, it
+ didn't leverage all the available bandwidth.
+
+- Moved the final check on the state of the session at the end of
+ execution stage, instead of the beginning. The signals sent by the
+ user to request the statistics could be reset before the proxy had
+ entered the right procedure.
+
+- Added the session type 'unix-rootless', to be used for single apps
+ run with the agent in rootless mode.
+
+nxcomp-1.5.0-32
+
+- Modified the main loop to skip the select when I/O is possible on
+ any of the agent descriptors or the proxy link.
+
+- Now the house-keeping process is allocated in the heap instead of
+ the stack.
+
+nxcomp-1.5.0-31
+
+- The server channel now performs asynchronous reads from the display
+ by interleaving them with data decoded from the remote proxy.
+
+- Improved the handling of the memory-to-memory transport.
+
+nxcomp-1.5.0-30
+
+- Redesigned the handling of the congestion events to work more re-
+ liably and to report the events to the remote peer earlier.
+
+- Increased the size of the shared memory segment when the MIT-SHM
+ extension is activated.
+
+- Solved a bug that prevented the ping timeout to work as expected.
+
+- Ensured that, when entering the main select(), neither the chan-
+ nels or the proxy have pending messages in the read buffers.
+
+- There is now only a single case where we can have pending messa-
+ ges, namely in the handling of the MIT-SHM events.
+
+- Solved a bug introduced by the 1.5.0-29 that made the proxy en-
+ ter the select with a null timeout.
+
+- Renamed LINK_TYPE_AUTO to LINK_TYPE_NONE in NXproto.h.
+
+nxcomp-1.5.0-29
+
+- Now encoding data from the agent descriptors happens in the same
+ context as data is written to the buffer.
+
+- Optimized the handling of the congestion events to avoid running
+ further unneeded loops.
+
+- Added timer handling utilities.
+
+nxcomp-1.5.0-28
+
+- Added code providing information about the reason of the failure
+ encountered connecting to the local X server. This greatly helps
+ when troubleshooting X authorization problems.
+
+- On connection failure a warning message is printed in the session
+ log.
+
+- Removed the warning that was previously printed when the MIT-SHM
+ extension failed to be initialized on Windows. Shared memory has
+ problems on Cygwin and it currently doesn't work in NXWin.
+
+- Reworked the message printed in the case of link failure.
+
+nxcomp-1.5.0-27
+
+- Solved a bug in the connection procedure introduced by 1.5.0-25.
+
+nxcomp-1.5.0-26
+
+- Transformed the errors printed on failure of the fork() creating
+ the children into warnings. This can happen quite often on Win-
+ dows, due to well known Cygwin problems. See also the ChangeLog
+ entry for nxcomp 1.4.0-28. This patch closes the TRSL052278 but
+ it's obviously not a long-term solution.
+
+- Set the sticky bit when creating the '/tmp/.X11-unix' directory.
+
+- Modified the Makefile.in to remove the *.out.* files generated by
+ Valgrind.
+
+- Updated the README files.
+
+nxcomp-1.5.0-25
+
+- Changed the directory where the client for the Mac is searched if
+ it is not found in the system path.
+
+- Modified Auth.cpp to use nxauth also on the Mac.
+
+- Reworked the procedure showing the alert dialog when a timeout is
+ encountered in the initial connection.
+
+- Removed the experimental code from the official 1.5.0 branch.
+
+nxcomp-1.5.0-24
+
+- Implemented more experimental classes.
+
+nxcomp-1.5.0-23
+
+- Added the NX_SIGNAL_FORWARD action to NXTransSignal(). This can
+ be used to let the proxy call the original signal handler of the
+ agent after having blocked the signal.
+
+nxcomp-1.5.0-22
+
+- Ensured that we always have a context, even before creating the
+ transport.
+
+- Suppressed the error message printed when passing the -h option.
+
+- Added the experimental code that is currently under development.
+
+nxcomp-1.5.0-21
+
+- Enabled the fake X cookie authentication. This requires checking
+ the remote proxy version to verify that the server supports the
+ new authorization mechanism.
+
+- It's worth noting that the X client side proxy doesn't care which
+ cookie is sent over the connection. The problem is that clients
+ connecting to 1.4.0 servers don't have a method to force the ser-
+ ver to use the fake cookie. This means that we have to solve the
+ problem by letting the proxy check the remote version so that it
+ can omit to replace the cookie when connecting to older servers.
+
+nxcomp-1.5.0-20
+
+- Added the NXTransCongestion() function. It returns true if proxy
+ is in congestion state.
+
+- Removed an incorrect warning that was printed when calling force()
+ for the memory-to-memory transport. The agent could actually re-
+ quire multiple loops to read all data queued for it.
+
+nxcomp-1.5.0-19
+
+- Small optimization in NXTransReadable() to run a new NXTransConti-
+ nue() loop only after at least RetryTimeout milliseconds are pas-
+ sed since the last call and still no data is available. This cuts
+ the number of unneeded loops to 1/4th of the total, probably more,
+ on faster machines.
+
+- Added NXCollectGrabPointerNotify to NXproto.h.
+
+nxcomp-1.5.0-18
+
+- Minor changes to NXTransContinue().
+
+nxcomp-1.5.0-17
+
+- Moved respawning of a new nxclient instance in the cleanup procedu-
+ re. This ensures that the respawn is executed whatever is the rea-
+ son of the session shutdown.
+
+- Added a method to force closure of a given channel in proxy.
+
+- Removed code handling the special case triggered on Windows by the
+ presence of a NX_SESSION variable in the environment.
+
+nxcomp-1.5.0-16
+
+- Added the NXTransSignal() function to let agents tell to the proxy
+ how to handle the standard POSIX signals. Given the SIGINT signal,
+ for example, the caller can specify any of the following actions.
+
+ NX_SIGNAL_ENABLE: A signal handler will have to be installed by
+ the library, so that it can be intercepted by
+ the proxy.
+
+ NX_SIGNAL_DISABLE: The signal will be handled by the caller and,
+ eventually, forwarded to the proxy by calling
+ NXTransSignal() explicitly.
+
+ NX_SIGNAL_RAISE: The signal must be handled now, as if it had
+ been delivered by the operating system. This
+ function can be called by the agent with the
+ purpose of propagating a signal to the proxy.
+
+ As a rule of thumb, agents should let the proxy handle SIGUSR1 and
+ SIGUSR2, used for producing the NX protocol statistics, and SIGHUP,
+ used for disconnecting the NX transport.
+
+- The following signals are blocked by default upon creation of the
+ NX transport:
+
+ SIGCHLD These signals should be always put under the control
+ SIGUSR1 of the proxy. If agents are intercepting them, agents
+ SIGUSR2 should later call NXTransSignal(..., NX_SIGNAL_RAISE)
+ SIGHUP to forward the signal to the proxy.
+
+ SIGINT These signals should be intercepted by agents. Agents
+ SIGTERM should ensure that NXTransDestroy() is called before
+ exiting, to give the proxy a chance to shut down the
+ NX transport.
+
+ SIGPIPE This signal is blocked by the proxy, but not used to
+ implement any functionality. It can be handled by the
+ NX agent without affecting the proxy.
+
+ SIGALRM This is not blocked by the proxy, but could be used
+ in future.
+
+ SIGVTALRM These signals are not used and should not be used in
+ SIGWINCH future versions of the library.
+ SIGIO
+ SIGTSTP
+ SIGTTIN
+ SIGTTOU
+
+- By calling NXTransSignal(..., NX_SIGNAL_DISABLE) nxcomp will res-
+ tore the signal handler that was saved at the time the proxy hand-
+ ler was installed. This means that you should call the function
+ just after the XOpenDisplay() or any other function used to init-
+ ialize the NX transport.
+
+nxcomp-1.5.0-15
+
+- In NXTransContinue(), if the transport is gone, return immediately,
+ that is without having to wait until the NXTransSelect() timeout.
+
+- Ensure that NXTransCreate() has a jump context, just in the case
+ a subsequent operation would cause a cleanup.
+
+nxcomp-1.5.0-14
+
+- Solved a problem with requests left in the agent's buffer when run-
+ ning the NX transport. The agent could have enqueued data to our
+ side and checked the available events but requests could not be
+ written to the proxy because proxy might not have had a chance to
+ enter a new select. We found that this behaviour was triggered by
+ _XEventsQueued, so now a new loop is forced when agent is calling
+ _X11TransDataReadable. The procedure can be optimized, by avoiding
+ an expensive loop when no critical I/O is pending.
+
+- Added a few additional logs to ClientChannel and ServerChannel.
+
+nxcomp-1.5.0-13
+
+- Added the code handling the special cases of an user not specifying
+ a proxy cookie or the case of the X authorization file not contain-
+ ing a value matching the display. In the first case we'll forward
+ the same cookie that was feeded to the proxy, in the second case we
+ will forward to the X server a random generated cookie, similarly to
+ what SSH does in this same condition.
+
+- Rewritten the command line parser. Removed all the command line
+ options parsed on behalf of nxproxy except:
+
+ -C Specify that nxproxy has to run on the "X client"
+ side, listening for connections and impersonating
+ an X server.\n\
+
+ -S Specify that nxproxy has to run in "X server" mode,
+ thus forwarding the connections to daemons running
+ on the client.\n\
+
+ -V n.n.n Request nxproxy to load the given nxcomp version.
+ This option is only present on Solaris and Linux.
+
+ -v Print version information.
+
+ host:port Put at the end, specifies the host and port of the
+ listening proxy.
+
+ value=name Set the NX option to the provided value.
+
+ Multiple NX options can be specified in the DISPLAY environment or
+ on the command line, by using the nx/nx,name=value notation.
+
+- The above information is printed on the console when incurring in
+ a parse error, together with a list of the available option=value
+ parameters.
+
+- Renamed the 'log' option to 'errors'. This makes sense as the de-
+ fault name for the log file is actually 'errors'.
+
+- Now the "Established X server connection" message is printed to
+ the session log only after the X connection has passed the X auth-
+ entication phase. This means that the NX client should become able
+ to show the details of the session log whenever the session fails
+ due to a cookie problem.
+
+- When selecting the additional services without specifying a port,
+ the client proxy will now automatically forward the connections to
+ the corresponding well-known ports of the CUPS, SMB and HTTP servi-
+ ces. Embedded X keyboard connections will be automatically forward-
+ ed to the same display port used to connect to the X server. The
+ user will still have to specify the port to be used for the media
+ connections as we don't have a suitable well-known port.
+
+- Starting from this version, connections to the keybd port will cre-
+ ate real X connection channels. This is required to let connections
+ leverage the fake authorization cookie.
+
+- By testing the forwarding of keybd connections I found that, when
+ letting X clients connect to the port, it is required to provide
+ the X cookie for the unix display. Adding only the TCP cookie will
+ not work. For example, by creating a cookie as in:
+
+ xauth add localhost:2009 MIT-MAGIC-COOKIE-1 6f...f4
+
+ And running:
+
+ xterm xterm -display localhost:2009
+
+ You will get the following error:
+
+ Xlib: connection to "localhost:2009.0" refused by server
+ Xlib: No protocol specified
+
+ Adding also the unix cookie will fix it:
+
+ xauth add localhost/unix:2009 MIT-MAGIC-COOKIE-1 6f...f4
+
+ This seems to be a Xlib problem, with Xlib trying to get the cookie
+ for the UDS port even if the TCP port was requested by the user.
+
+- Fixed a bug that prevented the nxclient dialog to be displayed when
+ the session was abruptedly shut down.
+
+- Fixed the compilation error on Apple MacOSX due to the sa_restorer
+ field in sigaction. As long as this field is present on Linux we
+ will keep following the safer route and will set it explicitly to
+ NULL.
+
+- Included what needs to be included in Process.cpp to compile with
+ older gcc.
+
+nxcomp-1.5.0-12
+
+- Implemented a replacement for the popen() and pclose() that do not
+ rely on a shell to run the command. They were required on Windows,
+ where we don't ship a suitable shell in the install.
+
+- Removed code forcing the PATH to include the bin directory on
+ Windows.
+
+nxcomp-1.5.0-11
+
+- Forced Auth.cpp on Windows to have the directory containing the
+ nxauth executable in the PATH. This is just for testing, until
+ nxclient is fixed.
+
+- Fixed compilation errors on Cygwin and Sun.
+
+- Removed the NX_FORCE_* stubs.
+
+nxcomp-1.5.0-10
+
+- Modified the memory management policies in ReadBuffer to fit all
+ the available bytes in a single buffer allocation.
+
+- The locateMessage() methods now give hints on the amount of data
+ that has to be read.
+
+- The read loop in channel now doesn't yield in the case of prio-
+ ritized messages. This is experimental.
+
+- Removed the check on isTimeToYield() between encodings of multi-
+ ple messages. This is aimed at reducing the risk of leaving pen-
+ ding messages in channels.
+
+- Modified the channels' read loop to always read all the available
+ data.
+
+- Disabled the log output that was selected when compiling with the
+ configure option --with-info. This leaves space for other log out-
+ put to be selected for more up-to-date scopes.
+
+- Implemented the NXTransReadVector() and the NXTransWriteVector()
+ functions to replace READV() and WRITEV().
+
+- Implemented memory-to-memory communication between the agent and
+ the NX proxy by making use of the NXTransAgent() interface in the
+ nx-X11/lib/X11/Xtranssock.c file.
+
+- Added a check in NXTransSelect() for the EBADF and, on Solaris,
+ the EINVAL errors. It can happen in the X11 code that a descript-
+ or has become invalid before it is removed from the managed set
+
+- Rewritten the signal handling functions to restore the old actions
+ and masks when the NX transport is destroyed.
+
+- Added a NXTransAgent() function to let agents tell the proxy which
+ descriptor must be used for the controlling connection. Setting a
+ controlling connection has the effect of disabling further X client
+ connections and makes the proxy terminate when the channel is shut
+ down.
+
+- Solved a problem with setting the initial timeout in the select().
+
+- Modified the Makefile.in to not include -Wno-deprecated when compi-
+ ling C programs.
+
+nxcomp-1.5.0-9
+
+- Fixed a problem that prevented the 1.5.0-8 to work on the NX server
+ side.
+
+- This version has NX_FORCE_NULL_LISTEN_OPTION and NX_FORCE_NEW_SES-
+ SION_OPTION undefined, so it should work in a way that is compati-
+ ble with the old nxcomp.
+
+nxcomp-1.5.0-8
+
+- The new code comes with a preliminary integration of nxcomp with
+ SSH. It is now possible to create the NX transport by just calling
+ the "switch" command as in the following example:
+
+ NX> 299 Switching connection to: NX options: ...
+
+- Other possible forms for the NX switch command are:
+
+ NX> 299 Switching connection to: NX mode: ...
+
+ NX> 299 Switching connection to: NX mode: ... options: ...
+
+ Or just:
+
+ NX> 299 Switching connection to: NX
+
+ The "mode" parameter is there to provide a way to run both enc-
+ rypted and unencrypted connections. Possible values are "encrypt-
+ ed", "unencrypted" or "default", the latter being an alias for
+ "encrypted". Unfortunately I was not able to test unencrypted
+ connections, so this may or may not work.
+
+- The top-level process can create the NX transport layer by calling
+ NXTransCreate(). The user has to set up a socket pair and pass the
+ higher descriptor to nxcomp. nxcomp will later monitor its end, by
+ reading and writing NX-encoded traffic. The user has to call the
+ NXTransExecute() function as often as it is possible, by letting
+ first NXTransPrepare() combine the sets of NX descriptors with the
+ descriptors that are used inside its process. A custom NXTransSel-
+ ect() is provided to optionally replace the original select(). This
+ function saves the original error code and the number of selected
+ descriptors upon exit, so the user can call it, restore the original
+ values as they were returned by the select() and run the rest of
+ the loop unmodified.
+
+- Future versions of the library should provide appropriate methods
+ for passing data to and from the proxy by means of a memcpy(), so
+ that it will be possible to remove the even minimal TCP overhead.
+
+- Note that integration is far from complete. More work is required
+ especially to manage the shutdown cleanly, in a way that gives to
+ SSH a chance to free its resources, and on adding facilities for
+ handling SSH and NX signals in a single function.
+
+- Rewritten the initialization procedure to make possible to run the
+ proxy in-process in an arbitrary connection manager, like SSH or a
+ HTTP utility. The same functionality can be used to embed nxcomp in
+ the NX agents, so that nxcomp has not to run in a separate process.
+
+- A new state-machine handles the advances through the connection
+ stages until the remote proxy (or the forwarder process ) is auth-
+ enticated, options have been negotiated and the session is started.
+
+- The option "session" is now used to pass the name of the session
+ file to the proxy. The parameter was previously used to pass the
+ literal name of the session, as set by the user, and was ignored
+ by the proxy. By default the session log is the standard error of
+ the process. It is anyway required to set the option when running
+ inside SSH, otherwise output will go to the same file as the SSH
+ output. In the NX client this would be the 'sshlog' file.
+
+- This version can be easily crafted to test the new integration by
+ setting the following define:
+
+ NX_FORCE_NULL_LISTEN_OPTION
+
+ This makes possible to test the nxcomp/nxssh integration by using
+ any production 1.4.0 client. To run this version you are required
+ to use nxproxy 1.5.0-4 and nxssh-1.5.0-6.
+
+- New functions handling enabling and disabling signals, based on
+ sigprocmask().
+
+- Improved error reporting when failures are encountered while ne-
+ gotiating the session.
+
+nxcomp-1.5.0-7
+
+- Caches are saved with a version identifier 1.4.0 so that they are
+ not discarded after upgrading the software to the 1.5.0.
+
+- Made values of T_* enumerations to be all lower case.
+
+- Updated copyright to year 2005.
+
+- Started working on an interface for running nxcomp in-process, as
+ a additional transport layer of nxssh or nxagent.
+
+nxcomp-1.5.0-6
+
+- Modified the configure script and the makefiles to not include the
+ -Wstrict-prototypes and -Wmissing-prototypes compilation flags. The
+ -Wnested-externs and -Wmissing-declarations flags are not included
+ when using GCC 3.
+
+- Removed the initial newline from string "NXPROXY - Version" printed
+ at program startup.
+
+- Made X authentication compatible with 1.4.0 clients. This is a tem-
+ porary solution while code is updated to handle the new X authori-
+ zation scheme.
+
+nxcomp-1.5.0-5
+
+- Added an Auth class to handle the X authentication. The NX server
+ should now use the same proxie cookie that is passed by the client
+ at session startup. The X server side proxy will forward the autho-
+ rization credentials by replacing the fake cookie with the real co-
+ okie, as it is read from the auth file using the xauth utility. The
+ implementation is based on the corresponding code found in the SSH
+ client and comes with the same limitations: only MIT-MAGIC-COOKIE-1
+ cookies are checked and the authorization file is read only once,
+ at the time the instance is initialized. If the auth file changes
+ along the life of the session, the old cookie will still be used.
+ This works with X servers because of an undocumented "feature".
+ See also nx-X11.
+
+nxcomp-1.5.0-4
+
+- Adjusted for alarm(0) returning an inconsistent value in ConnectTo-
+ Remote().
+
+- Small changes to Types.h, Jpeg.cpp and Png.cpp to compile with gcc
+ 3.4.2.
+
+- Cosmetic changes to the ChangeLog file.
+
+nxcomp-1.5.0-3
+
+- Removed test code from ServerChannel.cpp.
+
+- Small cosmetic change in Loop.cpp.
+
+- Tested for compatibility against 1.4.1-8.
+
+nxcomp-1.5.0-2
+
+- Changed VERSION file according to TRCL052336.
+
+nxcomp-1.5.0-1
+
+- Opened the 1.5.0 branch based on 1.4.1-9.
+
+nxcomp-1.4.1-9
+
+- Implemented methods PACK_RDP_PLAIN_64K_COLORS and PACK_RDP_PLAIN_-
+ 16M_COLORS. It seems that 16 bpp plain bitmaps can be seldom recei-
+ ved. This doesn't seem to be the case of 24 bpp bitmaps.
+
+nxcomp-1.4.1-8
+
+- The new code enables use of 16 bpp and 32 bpp RDP bitmaps. This re-
+ quired modifications to the following files:
+
+ Misc.h
+ Misc.cpp
+ Loop.cpp
+ Control.cpp
+ ServerChannel.cpp
+ Unpack.h
+ Unpack.cpp
+
+- Lots of cosmetic changes compared to the original code in 1.4.1-7.
+
+- Merged the 1.4.0 and the 1.4.1 branches by importing changes up to
+ the 1.4.1-7.
+
+nxcomp-1.4.0-30
+
+- Modified the function checking the Unix socket where X connections
+ will be forwarded. The function will not fail if the socket itself
+ doesn't exist yet. This solves the TRCL042203.
+
+- Moved the code checking for the CTRL+ALT+SHIFT+ESC sequence to a
+ separate function. The implementation will now look for a different
+ sequence on the MacOS/X platform. This should solve the TRCL042182.
+
+nxcomp-1.4.0-29
+
+- Solved a bug that could cause proxy to stop reading data from the
+ X channels due to an incorrect calculation of the bytes queued on
+ the proxy link. The bug only affected Linux kernels of the 2.0/2.2
+ series.
+
+- Fixed a problem in the proxy class that could let the select time-
+ out to become zero.
+
+nxcomp-1.4.0-28
+
+- Prevented the main proxy process from quitting when the fork() of
+ any of the children fails. This can actually happen on Windows due
+ to conflicts in reallocating any of the Cygwin DLLs.
+
+- Reworked handling of the priority flag in channels when dealing
+ with the X_InternAtoms and X_AllocColor requests to reduce the time
+ of session startup. Priority is never set in case of X_InternAtom
+ requests and replies as we assume that most clients use the appro-
+ priate Xlib function to pipeline multiple requests in a single net-
+ work operation.
+
+- The timeout after which proxy will abort the peer connection is
+ still 120 seconds. An alert dialog will be shown earlier, after 30
+ seconds instead of 60.
+
+nxcomp-1.4.0-27
+
+- Removed the code setting priority on channels on ButtonPress and
+ Button release events. It seems preferrable to delay the flush and
+ pack more events in a single frame. This makes possible to get the
+ ButtonRelease together with the ButtonPress.
+
+- Fixed error detection in SetNoDelay() where a positive result
+ could produce a 'not supported' message in debug mode.
+
+nxcomp-1.4.0-26
+
+- Reworked the check aimed at detecting the clock drifts. The previ-
+ ous code, introduced in 1.3.2-2, had the undesired side-effect of
+ resetting the proxy timeout. This could cause the proxy to never
+ detect that the other end had been killed.
+
+- The default behaviour of proxy is now to terminate the session at
+ the time an error is encountered. This includes network failures.
+
+- Added a new dialog to be shown to the user whenever the session
+ is terminated abnormally.
+
+nxcomp-1.4.0-25
+
+- Fixed a bug in handling of suppression of errors generated by
+ committing the image splits. Other errors, unrelated to the commit
+ of split, could be suppressed. This might cause the hangup of the
+ session when the suppressed error was matching a reply.
+
+nxcomp-1.4.0-24
+
+- When killing the proxy process -9 the watchdog could remain alive.
+ This caused the SSH link to keep the stderr open, with the effect
+ that former proxy message were not flushed to the session log.
+ Now the watchdog checks every second if the parent is dead, so
+ that file descriptors can be closed as soon as possible.
+
+- SetupDisplaySocket() tried to force the connections to the X
+ server on the UNIX port whenever the display was set to localhost.
+ This patch was intended to fix a bug in nxclient, using the TCP
+ port even when the display was set to a UNIX socket. This beha-
+ viour caused problems when running nxclient on a remote host by
+ means of a ssh -X. Proxy will now adhere to the display setting.
+
+nxcomp-1.4.0-23
+
+- Added the possibility to respawn nxclient at the end of session.
+ This is an useful feature when powering thin-clients where NX
+ is the only application made available.
+
+- This configuration applies system-wide to the local client ins-
+ tallation. By default it is disabled. It can be enabled by the
+ by creating a 'noexit' file in the directory '/usr/NX/share',
+ or at compile time, by setting ENABLE_RESTART_ON_SHUTDOWN to 1.
+
+- Note that the solution is not perfect yet, as there are cases
+ where session could die without going through HandleShutdown(),
+ for example if a decoding error is raised in the communication
+ with the remote peer and the subsequent restore of the proxy
+ link fails to succeed.
+
+nxcomp-1.4.0-22
+
+- Corrected typos in Timestamp.h and Loop.cpp.
+
+nxcomp-1.4.0-21
+
+- Tuned the handling of RenderCompositeGlyphs. Now compression of
+ RENDER shows an average ratio of 8:1. This is a steady increase
+ compared to the 5:1 of the 1.3.2 version. The overall advantage
+ when running clients displaying a large amount of text is 30%.
+
+nxcomp-1.4.0-20
+
+- Added a new message store for the CreatePixmap request.
+
+- Fixed handleCopy() to only send the data part past the offset.
+ This fix can save a significant amount of traffic, especially
+ when sending many small images whose size doesn't reach the
+ threshold set for compressing them.
+
+nxcomp-1.4.0-19
+
+- Added tuning of all the RENDER requests. RenderCompositeGlyphs
+ will require further work.
+
+- Completed porting of the RENDER requests to the new templates.
+
+nxcomp-1.4.0-18
+
+- Tuned the handling of RenderCreatePicture and RenderFreePicture.
+
+- Created a new template system for writing new message encoders.
+
+- Renamed getBits() in EncodeBuffer with diffBits(). The getBits()
+ method now returns the bits actually allocated in the buffer and
+ can be called multiple times.
+
+nxcomp-1.4.0-17
+
+- Provided specific encoding of the remaining RENDER requests and
+ added compression of RenderCreateGlyphSet.
+
+nxcomp-1.4.0-16
+
+- Code underwent through 7 different patch versions but seems to
+ be stable now. It still needs to provide specific encoding for
+ RenderCreatePicture RenderChangePicture and RenderFreePicture.
+ Code will likely include incompatible changes at any new version
+ until the new encoding will stabilize, so be sure that you use
+ the same nxcomp version at both sides.
+
+- Created the infrastructure for compressing extension messages
+ based on the minor opcode. A new class, MinorMessageStore, has
+ to be inherited by stores requiring specific compression, so
+ that all the encoding methods of the parent can be redirected
+ to it. The RENDER extension is now compressed using this new
+ class.
+
+- Managed to maintain the compatibility with caches created by the
+ 1.3.X version. Caches will be saved using the old format if the
+ remote version is not a 1.4.x. When restoring a cache created
+ by the 1.3.x with both proxies being the newest version, RENDER
+ mesages will be discarded without having to discard the whole
+ cache.
+
+- Added a member to the message class to store the identity size.
+ This field is now used instead of the default data offset to
+ manage the message data, that is the part of message after the
+ identity.
+
+- Removed the warnings printed in the session log when passing the
+ 'kbtype' and the 'geometry' options to the proxy. These options
+ are actually used by agents.
+
+nxcomp-1.4.0-15
+
+- Started implementation of the new framework for handling enco-
+ ding of X extensions.
+
+- Renamed the overloaded methods handleEncode() and handleDecode()
+ to handleEncodeCached(), handleEncodeIdentity() and handleDecode-
+ Cached(), handleDecodeIdentity().
+
+- Updated the copyright notice to year 2004.
+
+nxcomp-1.4.0-14
+
+- Added the new channel for HTTP connections. This channel can be
+ used to let applications running on the NX server get access to
+ data and applications made available by a HTTP daemon running at
+ the client side.
+
+ This channel is not used at the moment by nxclient. It can be
+ activated by passing http=1 to the NX server side proxy and the
+ value http=80 to the NX client side.
+
+nxcomp-1.4.0-13
+
+- Quick patch to run on MacOS/X where inet_addr("127.0.0.1")
+ alone seems to fail to return a valid interface.
+
+nxcomp-1.4.0-12
+
+- Few cosmetic changes to logging.
+
+- Removed the debug output.
+
+nxcomp-1.4.0-11
+
+- Removed code used for simulating the new forwarding function-
+ ality without client and server support.
+
+nxcomp-1.4.0-10
+
+- Implemented authentication of the forwarder to the listening
+ proxy. If a session cookie is required, the forwarder must
+ provide the cookie upon connection.
+
+ An authentication phase at the time the forwarder connects
+ to the NX client side is strongly suggested because it is usu-
+ ally this side that sends the authorization cookie. Without
+ such a forwarder authentication, the local peer would reveal
+ the cookie to the first process connecting to the port.
+
+- If no cookie is specified in the switch command, the forward-
+ er is required to skip the authentication phase. This can be
+ useful when running the programs on the command line.
+
+- The implementation required appropriate changes to session
+ negotiation in nxssh. This version requires nxssh-1.4.0-8.
+
+nxcomp-1.4.0-9
+
+- Fixed the startup procedure to correctly negotiate the cookie
+ when the X server side proxy is listening for a forwarder.
+
+- When listening for a local forwarder (that is when the listen
+ option is enabled at X server side), proxy will listen for the
+ forwarder on the localhost interface.
+
+- This version has ports and and other parameters hardcoded for
+ testing purposes and is not intended for normal use.
+
+nxcomp-1.4.0-8
+
+- Slightly modified the text of FAILED_PROXY_CONNECTION alert.
+
+nxcomp-1.4.0-7
+
+- Added provision for leaving a running dialog showing an OK box
+ if proxy is exiting abnormally. This seems to fail to yield the
+ expected results as, at the present moment, client checks if
+ parent has exited.
+
+- Added a Binder class invoked when calling proxy with -B option.
+ It would serve as a replacement of the modifications I'm doing
+ in nxssh. The class is just a framework and the implementation
+ is unfinished.
+
+nxcomp-1.4.0-6
+
+- Added a line in the session log if the connection procedure is
+ aborted due to a signal.
+
+- Fixed ParseHostOption() to let it take in account the proxy
+ port offset when passing 'host:port' as the last parameter at
+ X server side.
+
+nxcomp-1.4.0-5
+
+- Lowered the default port offsets used for CUPS and SMB services
+ to 2000 and 3000. Arbitrary ports can be used by passing the ser-
+ vice's port at proxy startup. By default, anyway, the port is at
+ the same offset of the proxied display. Considering that the NX
+ server uses to start the first session at display 1000, we must
+ lower some of the defaults to avoid interference with the normal
+ X sessions run on the server.
+
+ Session at display ":1000" will have:
+
+ . Forwarding of CUPS connections at 3000.
+
+ . Forwarding of SMB connections at 4000.
+
+ . The listening proxy (at NX server side) at 5000. This
+ port is not used when enabling SSH tunneling
+
+ . The proxied X display (NX agent or proxy) at 7000.
+
+ . Forwarding of multimedia channels at 8000.
+
+ . Forwarding of embedded keyboard connections at 9000.
+
+- Turned off the log output.
+
+nxcomp-1.4.0-4
+
+- Managed to get the X client side proxy connect to the remote
+ peer. This means that some parameters that were implicitly
+ assumed based on the proxied port simulating the X display
+ must be now specified in the NX display string.
+
+- The 'port' parameter now specifies the remote TCP port where
+ the local proxy will connect to the remote peer. In previous
+ versions this parameter also affected the proxied port. This
+ was mainly a side effect, as the remote peer was always con-
+ tacted at port 4000 plus the proxied display offset.
+
+- Added the 'listen' parameter to tell to the proxy that is go-
+ ing to accept the peer connection on which port it will be
+ contacted. By default, similarly to the previous version, the
+ proxy will listen at the proxied display offset.
+
+- A check has been added in order to disallow passing both the
+ 'accept' and the 'connect' parameter at the same time. A simi-
+ lar check affects the 'listen' parameter.
+
+- Renamed the previously unused 'sync' channel as 'cups'.
+
+nxcomp-1.4.0-3
+
+- Preserved the ability of version 1.3.2 to load caches generated
+ by this version.
+
+nxcomp-1.4.0-2
+
+- Small fixes to compile under Solaris 8.
+
+nxcomp-1.4.0-1
+
+- Opened the 1.4.0 branch.
+
+nxcomp-1.3.2-4
+
+- Fixed a problem with shmget(). Code in ServerChannel checked
+ the return value for being greater then 0, while 0 can actual-
+ ly be a valid segment.
+
+- When memory cache is disabled, nxcomp will explicitly set the
+ appropriate control variables dealing with loading and saving
+ of the persistent cache. This can be considered a cosmetic
+ change as nxcomp will disable NX delta compression if memory
+ cache is not available and this has an implicit effect on the
+ ability to load and save such a cache.
+
+nxcomp-1.3.2-3
+
+- Removed inclusion of zlib.h in Png.cpp. Conflicting symbols
+ with zlib.h from nx-X11 could cause compilation to fail.
+
+nxcomp-1.3.2-2
+
+- Added a function to take into account the clock drifts at the
+ time we check the ping from the remote proxy. This can be
+ caused by the user changing the system time or by small adjust-
+ ments introduced by the operating system making the clock go
+ backward. Such events could cause the proxy link to be shut
+ down and reconnected.
+
+- Reduced the length of lines printed in statistics when showing
+ the details of X protocol's opcodes. This is intended to help
+ nxclient to keep the whole statistics in the 'details' window,
+ so that users don't have to use the scrollbars.
+
+nxcomp-1.3.2-1
+
+- Opened the 1.3.2 branch.
+
+nxcomp-1.3.1-5
+
+- It seems that Solaris can return an EINVAL error selecting a
+ shutdown descriptor even before we actually close the socket.
+ We now ignore the condition on Solaris. This is definitely a
+ Solaris bug.
+
+nxcomp-1.3.1-4
+
+- Increased the timeout after which proxy will abort the peer
+ connection to 120 seconds. An alert dialog will now be shown
+ after 60 seconds, instead of the 30 seconds being the default
+ in the previous version. Some users reported the timeout to
+ be too short in the case of temporary network failures.
+
+nxcomp-1.3.1-3
+
+- Some optimizations in the cache house-keeping process. Now it
+ runs at lower system priority in respect to the parent. Any 2
+ iterations through directories and images, it also sleeps for
+ a tiny amount of time. This further decreases the system load.
+
+- Removed the underline characters and added a colon in the
+ title of this ChangeLog to fully comply with format used in
+ the release notices.
+
+nxcomp-1.3.1-2
+
+- Fixed a crash when running both client and server on the SPARC
+ Solaris. An optimization used to avoid byte-swapping when both
+ hosts have the same endianess doesn't work on SPARC if buffer
+ is not aligned to word boundary. A better version of the code
+ should check the CPU, not the OS, as this probably applies to
+ other processors.
+
+nxcomp-1.3.1-1
+
+- Opened the 1.3.1 branch.
+
+nxcomp-1.3.0-50
+
+- Disabled the processor limit in X client side proxy. In previous
+ versions the processor load limit was set to an idle time ratio
+ of 2. This was likely to cause an unwanted slowdown on very old
+ hardware or when running the server as guest OS inside a VMWare
+ virtual machine.
+
+nxcomp-1.3.0-49
+
+- Last minute update on proxy shutdown bug on Cygwin. A stack
+ trace reveals that faillure happens in the static destructor of
+ the BlockCacheSet class. It seems that problem appeared just
+ after having upgraded to the latest version of Cygwin DLL. Now
+ the destructor is skipped at shutdown.
+
+nxcomp-1.3.0-48
+
+- Further fix to overcome the shutdown problem on Windows.
+
+nxcomp-1.3.0-47
+
+- Used T_files::value_type() in Keeper.cpp. The form without the
+ explicit constructor fails to compile with GCC 2.91 on RH 6.2.
+
+- Fixed '==' -> '=' in configure.in to build under RH 6.2 and
+ probably other platforms.
+
+nxcomp-1.3.0-46
+
+- The cleanup procedure now skips deletion of the IO streams
+ under Windows. This is intended to overcome a strange segfault
+ occurring at random time, at the time proxy is being shutdown.
+
+nxcomp-1.3.0-45
+
+- Newer versions of the stdlibc++ do not seem to be able to deal
+ with NULL strings or non printable characters. This caused the
+ standard error stream to get sometimes corrupted in the case
+ TEST and DEBUG logs were enabled in Loop.cpp.
+
+nxcomp-1.3.0-44
+
+- Disabled TEST and DEBUG logs in ServerChannel.
+
+nxcomp-1.3.0-43
+
+- Fixed a crash when unpacking an image in server proxy in the
+ case the unpack state for the given channel had not been
+ previously created.
+
+nxcomp-1.3.0-42
+
+- Small modification to setSchedule() to not account previous
+ data accumulated in the encode buffer. The new code seems to
+ allow better use of the available bandwidth.
+
+nxcomp-1.3.0-41
+
+- Further fix in Tight decompression. Content of the main write
+ buffer was not removed before flushing the unpacked data to
+ the X server link.
+
+nxcomp-1.3.0-40
+
+- Removed the code marked as FIXME in Loop and ServerChannel.
+ Code was used in previous version to override any pack method
+ other than NO_PACK and to disable initialization of the shared
+ memory.
+
+nxcomp-1.3.0-39
+
+- Solved the problem with unpacking of RDP text on big-endian
+ architectures. The width field in PutPackedImage was put as
+ 16 bits and extracted as 32 bits at decoding side.
+
+- Moved the code checking if any of the children has exited in a
+ specific function. This function is now called any time signals
+ are newly enabled. This works even on Solaris.
+
+- Enabled use of shared memory with Tight image decompression.
+
+- Added event NXCollectPropertyNotify to NXproto.h.
+
+nxcomp-1.3.0-38
+
+- Message locks are now checked before splitting a message.
+
+nxcomp-1.3.0-37
+
+- Added a further counter to yield encoding data if the amount
+ of output bytes to be written to channels exceeds a threshold.
+
+- Modified proxy to write data immediately if the encoding loop
+ was interrupted.
+
+nxcomp-1.3.0-36
+
+- Further fix required by Tight decompression. By handling mul-
+ tiple writes in the Tight class we may flush the main write
+ buffer in the wrong order. If Tight decompression is enabled,
+ the buffer is now flushed before decoding the packed image.
+
+- A SIGCHLD is raised any time signals are newly enabled. This
+ allows the main loop to wait() the pid of children that had
+ exited while signals were disabled.
+
+nxcomp-1.3.0-35
+
+- Fixed Tight decompression by handling multiple writes to the
+ channel's transport inside the decompression function.
+
+- Made Tight decompressor in ServerChannel a pointer instead of
+ an instance of the class. Class is allocated the first time it
+ is referenced.
+
+- Rewritten handleUnpack() in ServerChannel to use a switch()
+ on the pack method instead of multiple if().
+
+nxcomp-1.3.0-34
+
+- Disabled RENDER extension when running X agent sessions on
+ the Solaris client. Problems seem to be caused by incompati-
+ bilities in the implementation of RENDER on the Sun X server
+ shipped by default. We did not test NX with the XFree86 X
+ server, but it seems that, when using the Sun X server, even
+ plain X clients don't make full use of the extension.
+
+nxcomp-1.3.0-33
+
+- Added 4 new fields to the X_NXGetControlParameters reply.
+ A dialog should be shown by agent if proxy was not able to
+ negotiate a persistent cache or if a cache was selected but
+ not loaded because incompatible or corrupted.
+
+- Fixed a bug on Solaris where socket() must be recreated if
+ the first connect() fails. Not a problem. The strange thing
+ is that if you reuse the socket(), the subsequent connect()
+ is successful and the program fails as soon as you try to
+ write to it.
+
+nxcomp-1.3.0-32
+
+- Added request X_NXFreeUnpack to free the resources allocated
+ by the remote proxy to unpack images for the given agent's
+ client
+
+- Added an alert at X server side if proxies were not able to
+ negotiate a persistent cache or if a cache was selected but
+ not loaded at X server side (that means that probably cache
+ was not compatible or corrupted). The alert is not enabled
+ at the moment. Before enabling it, we need to find a way to
+ deal with full-screen mode.
+
+- Tuning of MIT-SHM parameters.
+
+nxcomp-1.3.0-31
+
+- Modified the following requests to carry the id of the
+ agent's client:
+
+ - X_NXSetUnpackGeometry
+ - X_NXSetUnpackColormap
+ - X_NXSetUnpackAlpha
+ - X_NXPutPackedImage
+
+ Now each agent's client has its own record pointing to the
+ geometry, colormap and alpha channel that will be used to
+ unpack the image. Code is not finished yet so it is advisable
+ that agents' writers keep using client id 0. Note also that
+ the final solution will require some changes to the way split
+ notifications are sent to agent that I plan to implement in
+ the next versions.
+
+- The X_NXSetUnpackGeometry request has now a message store.
+ This should reduce the overhead to the minimum even in the
+ case of dumb agents not checking if the current geometry
+ matches the image to be unpacked.
+
+nxcomp-1.3.0-30
+
+- Improved handling of short-circuited replies at X client
+ side. Request opcodes are now pushed in the sequence queue
+ to determine if a reply is expected. In this case tainting
+ of reply is skipped to preserve the sequence ordering of
+ any event or error that could have generated by the reply.
+ This seems to solve all the problems reported by people
+ running a NX session in virtual desktop mode nested in an
+ existing session being run in single application mode.
+
+- Modified the cleanup procedure to print the 'Waiting for'
+ message in the session log only at the time all X channels
+ have been actually shut down. This message can be used by
+ the NX server to find out the appropriate time to close
+ the session.
+
+- Added the selected session type to the 'Using pack method'
+ message printed at startup.
+
+- Added the option to close down the proxy link and perform
+ a clean shutdown of the session, re-read the whole session
+ configuration or restart the proxy link when a SIGHUP is
+ delivered to the proxy process. The default behaviour is
+ to close down the session. The new default (the old one was
+ to restart the link) is a required feature to let users run
+ full sessions using single application mode f.e. in a LTSP
+ environment. I noted that when the controlling terminal of
+ the X session is closed, a SIGHUP is delivered to the X
+ server side proxy (probably it is delivered to all the con-
+ nected clients, and, thus for NX, only to the proxy). This
+ signal can be used to terminate the whole session.
+
+nxcomp-1.3.0-29
+
+- Changed defaults to disable image masks when running proxy
+ in single application mode. The new configuration requires
+ changes in NX server and in NX X11 library. Users should
+ experience much better image quality when using NX on slow
+ links. This comes at the cost of slightly worse compression
+ ratios.
+
+nxcomp-1.3.0-28
+
+- Opcode of request was not rewritten by server channel when
+ using link LAN. This caused problems with taint of replies.
+
+nxcomp-1.3.0-27
+
+- Modified the split store and the split procedures in client
+ channel to always return the client id in the notification
+ events. The new field is added to event at byte offset 28
+ to preserve compatibility with previous agent releases. The
+ modification permits to agent to match the commit of alpha
+ channel with the original image, if requests are split by
+ proxy.
+
+- Temporarily set the timeout used to poll MIT-SHM completion
+ events coming from X server to 0. More testing is required.
+
+- Fixed an inconsistent message generated in statistics due to
+ a division by 0.
+
+nxcomp-1.3.0-26
+
+- Modified pending timeout from 1 to 0 milliseconds. This means
+ that both channels and proxy are now immediately restarted to
+ let them consume all data left in their read buffer.
+
+- When querying the X server for MIT-SHM support, size of name
+ in X_QueryExtension request was sent as 32 bits instead of 16.
+ This caused request to fail on big endian architectures.
+
+- Modified the MIT-SHM initialization procedure to always send
+ all the 3 protocol requests also in the case of early failures.
+
+nxcomp-1.3.0-25
+
+- Implemented handling of the new X_NXSetUnpackAlpha message.
+ Performances are very satisfactory. Using most of the current
+ GNOME and KDE applications, caching reaches 90% of the total
+ messages.
+
+- Modified the persistent cache management routines to handle
+ backward compatibility with proxy versions prior of 1.3.0.
+
+- It has been made possible to send both X_NXSetUnpackColormap
+ and X_NXSetUnpackAlpha messages with 0 entries to temporarily
+ disable use of the colormap or the alpha channel and free the
+ resources allocated by the server channel class.
+
+- Added function UnpackAlpha() to Unpack.cpp.
+
+nxcomp-1.3.0-24
+
+- Using WriteBuffer::registerPointer() to track growing of the
+ write buffer in handleFastWrite() functions. This problem
+ existed even in 1.2.2 but never shown up because we didn't
+ have to use the pointer after data had been written to the
+ write buffer. Now, instead, we have to post-process the write
+ buffer to copy data to the shared segment.
+
+- Optimized memory allocations running with link LAN to save a
+ memcpy() any time new data is allocated in the scratch buffer.
+
+- When running with link LAN the scratch buffer is now used only
+ when:
+
+ - A further allocation would case growing of the write buffer
+ (and, thus, a memcpy() of the previous content).
+
+ - When data to added is bigger than the write threshold.
+
+ In previous versions the scratch buffer was used any time the
+ total amount of data to be written (write buffer + scratch
+ buffer) exceeded the scheduled write threshold. This caused
+ small writes to be appended even when a single write could
+ be obtained without reallocating the buffer.
+
+- Preliminary support for transporting the alpha channel in a
+ separate message in case of 32 bits displays using the RENDER
+ extension.
+
+nxcomp-1.3.0-23
+
+- Modified the mask used to open the shared memory segment on
+ OS/X to 0777. We have to better investigate why the previous
+ 0600 mask doesn't work even if the user running the proxy is
+ the same user running the X server.
+
+nxcomp-1.3.0-22
+
+- Implemented MIT-SHM support on LAN connections.
+
+- More MIT-SHM bug fixes.
+
+nxcomp-1.3.0-21
+
+- Better use of the shared segment through an improved
+ algorithm leveraging the offset field of the X_ShmPutImage
+ request. The new algorithm greatly reduces the amount of
+ polls the proxy needs to perform to find if the completion
+ event has arrived.
+
+- Implemented MIT-SHM support for X_PutImage requests.
+
+- Implemented option shmem=value. Use of this option is anyway
+ discouraged. Proxy will allocate the shared memory segment
+ based on the size of the in-memory cache set by the user.
+ Use of MIT-SHM is disabled when user did set a memory cache
+ smaller than 2MB (for example on the embedded client).
+
+- Rewritten post-processing of images in server channel loop.
+
+- Improved error handling to ensure we intercept all MIT-SHM
+ X errors before they reach the NX agent.
+
+- Solved a problem that was causing channels to not reflect
+ shared memory support flags set in control.
+
+nxcomp-1.3.0-20
+
+- Solved a compatibility problem when mixing proxy versions
+ 1.2.2 and 1.3.0.
+
+nxcomp-1.3.0-18
+
+- Rewritten interfaces to shared memory initialization in
+ client and server channel.
+
+- Server channel checks for the completion event until a
+ timeout before reusing the shared memory segment.
+
+- Flush flag was not cleared after the write buffer had been
+ flushed in handleWrite() of both client and server channels.
+ This could lead to multiple fragmented writes, affecting
+ the performances.
+
+- Added -lcygipc to linking on Windows platform.
+
+- Added a check on GCC version to see if -Wnested-externs
+ -Wmissing-declarations are valid options.
+
+nxcomp-1.3.0-17
+
+- Implemented initial support for MIT-SHM extension in the
+ network path between the X server proxy and the real X
+ server. Presently it works only for X_NXPutPackedImages.
+
+- Modified configure.in to compile under FreeBSD.
+
+- Small changes to sources due to FreeBSD support.
+
+nxcomp-1.3.0-16
+
+- Fixed caching of RENDER extension on MacOS/X and Solaris.
+
+- Under Solaris an explicit call to EnableSignal() is needed
+ at the end of the signal handler as raising a signal seems
+ to reset the previous settings.
+
+- Can't find a way to get bytes queued for write on Solaris as
+ both FIONWRITE and TIOCOUTQ don't seem to be available. This
+ means that NX server on Solaris is only able to detect con-
+ gestions on proxy link at the time a write fails with error
+ EAGAIN.
+
+- Starting from this version, render extension messages are not
+ automatically discarded from cache when running agent based X
+ sessions. This is in preparation of render support introduced
+ in this release.
+
+nxcomp-1.3.0-15
+
+- Changed default to force writes if X channels exceed buffer
+ limits. This change was suggested by benchmarks performed on
+ Win32.
+
+- Wrapped IO on cache files in functions performing better error
+ checking.
+
+- General cleanup in handling of socket options for MacOS/X and
+ Solaris.
+
+nxcomp-1.3.0-14
+
+- Corrupted persistent caches were not deleted in case loading
+ of any of the message stores failed. To run further sessions
+ on the same host, user had to delete the cache file manually.
+
+- Improved error handling in JPEG decompression. Now connection
+ is reset in case of failure.
+
+- Before performing JPEG or PNG decompression, image is better
+ checked to verify if loading from disk failed.
+
+- Improved error handling in case of failure loading persistent
+ cache from disk. On MacOS/X istream -> fail() doesn't seem to
+ work properly. This needs further investigation.
+
+- The default installation path of nxclient is searched under
+ MacOS/X at the time nxclient is invoked in dialog mode.
+
+nxcomp-1.3.0-13
+
+- Fixed a (further) compilation problem under Solaris. Now static
+ libraries are first searched under /usr/sfw/lib (in case Sun
+ would decide to include them in future releases).
+
+nxcomp-1.3.0-12
+
+- Fixed parsing of command line when passing option -V.
+
+- Correctly detected ENOPROTOOPT when setting TCP_NODELAY socket
+ option on MacOS/X and Solaris.
+
+nxcomp-1.3.0-11
+
+- Given option in configure to specify what needs to be built
+ statically:
+
+ --with-static-png enable static linking of PNG library
+ --with-static-jpeg enable static linking of JPEG library
+ --with-static-z enable static linking of Z library
+
+nxcomp-1.3.0-10
+
+- Fixed a problem in saving of persistent cache on big-endian
+ machines.
+
+nxcomp-1.3.0-9
+
+- Testing with different settings to check if it's possible to
+ increase the performances under Windows.
+
+- Solved a problem in parsing of options that prevented proxy
+ to connect to a remote session running at port offset 0.
+
+- Fixed two warnings compiling on Solaris.
+
+- Changed configure.in to first check for nx-X11 includes
+ and libraries. Added "/usr/openwin/bin/makedepend" to path
+ searched for the executable.
+
+nxcomp-1.3.0-8
+
+- Small cleanup in configure.in and files modified by Gregorz
+ Kryza to add support for Solaris.
+
+- A new configure script has been generated using autoconf-2.57-3.
+
+nxcomp-1.3.0-7
+
+- Added support for detection of Solaris in configure script.
+ Now Makefile.in uses ranlib instead of ar.
+
+- Small changes in source and header files to support Solaris.
+
+nxcomp-1.3.0-4
+
+- Corrected a bug that could cause priority on proxy and channels
+ to be not taken in account at the time proxy tries to determine
+ if it's time to flush the proxy link.
+
+- Better implementation of abort split notification by X server
+ proxy to its remote peer. The new implementation doesn't
+ need to set a timeout and permits notifications to be received
+ earlier.
+
+- Improved support for 'tainting' XSync() messages coming from
+ X clients in single application mode. Now a X_GetInputFocus
+ is sent to the real X server any n such messages received by
+ proxy.
+
+- Included support for 15 bpp displays. It seems that handling
+ them as 16 bpp it's OK.
diff --git a/nxcomp/COPYING b/nxcomp/COPYING
new file mode 100644
index 000000000..d511905c1
--- /dev/null
+++ b/nxcomp/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/nxcomp/ChangeGC.cpp b/nxcomp/ChangeGC.cpp
new file mode 100644
index 000000000..574651763
--- /dev/null
+++ b/nxcomp/ChangeGC.cpp
@@ -0,0 +1,172 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "ChangeGC.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int ChangeGCStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ChangeGCMessage *changeGC = (ChangeGCMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ changeGC -> gcontext = GetULONG(buffer + 4, bigEndian);
+ changeGC -> value_mask = GetULONG(buffer + 8, bigEndian);
+
+ //
+ // Clear the unused bytes carried in the
+ // payload to increase the effectiveness
+ // of the caching algorithm.
+ //
+
+ if ((int) size > dataOffset)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Removing unused bytes from the "
+ << "data payload.\n" << logofs_flush;
+ #endif
+
+ changeGC -> value_mask &= (1 << 23) - 1;
+
+ unsigned int mask = 0x1;
+ unsigned char *source = (unsigned char *) buffer + CHANGEGC_DATA_OFFSET;
+ unsigned long value = 0;
+
+ for (unsigned int i = 0; i < 23; i++)
+ {
+ if (changeGC -> value_mask & mask)
+ {
+ value = GetULONG(source, bigEndian);
+
+ value &= (0xffffffff >> (32 - CREATEGC_FIELD_WIDTH[i]));
+
+ PutULONG(value, source, bigEndian);
+
+ source += 4;
+ }
+
+ mask <<= 1;
+ }
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int ChangeGCStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ChangeGCMessage *changeGC = (ChangeGCMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ PutULONG(changeGC -> gcontext, buffer + 4, bigEndian);
+ PutULONG(changeGC -> value_mask, buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void ChangeGCStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ ChangeGCMessage *changeGC = (ChangeGCMessage *) message;
+
+ *logofs << name() << ": Identity gcontext " << changeGC -> gcontext
+ << ", mask " << changeGC -> value_mask << ", size "
+ << changeGC -> size_ << ".\n" << logofs_flush;
+ #endif
+}
+
+void ChangeGCStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+/*
+ md5_append(md5_state_, buffer + 4, 8);
+*/
+ md5_append(md5_state_, buffer + 8, 4);
+}
+
+void ChangeGCStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ ChangeGCMessage *changeGC = (ChangeGCMessage *) message;
+ ChangeGCMessage *cachedChangeGC = (ChangeGCMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << changeGC -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(changeGC -> gcontext, clientCache -> gcCache);
+
+ cachedChangeGC -> gcontext = changeGC -> gcontext;
+}
+
+void ChangeGCStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ ChangeGCMessage *changeGC = (ChangeGCMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ changeGC -> gcontext = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << changeGC -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+}
diff --git a/nxcomp/ChangeGC.h b/nxcomp/ChangeGC.h
new file mode 100644
index 000000000..81b808d87
--- /dev/null
+++ b/nxcomp/ChangeGC.h
@@ -0,0 +1,177 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef ChangeGC_H
+#define ChangeGC_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define CHANGEGC_ENABLE_CACHE 1
+#define CHANGEGC_ENABLE_DATA 0
+#define CHANGEGC_ENABLE_SPLIT 0
+#define CHANGEGC_ENABLE_COMPRESS 0
+
+#define CHANGEGC_DATA_LIMIT 144
+#define CHANGEGC_DATA_OFFSET 12
+
+#define CHANGEGC_CACHE_SLOTS 3000
+#define CHANGEGC_CACHE_THRESHOLD 3
+#define CHANGEGC_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class ChangeGCMessage : public Message
+{
+ friend class ChangeGCStore;
+
+ public:
+
+ ChangeGCMessage()
+ {
+ }
+
+ ~ChangeGCMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned int gcontext;
+ unsigned int value_mask;
+};
+
+class ChangeGCStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ ChangeGCStore() : MessageStore()
+ {
+ enableCache = CHANGEGC_ENABLE_CACHE;
+ enableData = CHANGEGC_ENABLE_DATA;
+ enableSplit = CHANGEGC_ENABLE_SPLIT;
+ enableCompress = CHANGEGC_ENABLE_COMPRESS;
+
+ dataLimit = CHANGEGC_DATA_LIMIT;
+ dataOffset = CHANGEGC_DATA_OFFSET;
+
+ cacheSlots = CHANGEGC_CACHE_SLOTS;
+ cacheThreshold = CHANGEGC_CACHE_THRESHOLD;
+ cacheLowerThreshold = CHANGEGC_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~ChangeGCStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "ChangeGC";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_ChangeGC;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(ChangeGCMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new ChangeGCMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new ChangeGCMessage((const ChangeGCMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (ChangeGCMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+};
+
+#endif /* ChangeGC_H */
diff --git a/nxcomp/ChangeGCCompat.cpp b/nxcomp/ChangeGCCompat.cpp
new file mode 100644
index 000000000..ca2973774
--- /dev/null
+++ b/nxcomp/ChangeGCCompat.cpp
@@ -0,0 +1,131 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "ChangeGCCompat.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int ChangeGCCompatStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ChangeGCCompatMessage *changeGC = (ChangeGCCompatMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ changeGC -> gcontext = GetULONG(buffer + 4, bigEndian);
+ changeGC -> value_mask = GetULONG(buffer + 8, bigEndian);
+
+ //
+ // Clear the unused bytes carried in the
+ // payload to increase the effectiveness
+ // of the caching algorithm.
+ //
+
+ if ((int) size > dataOffset)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Removing unused bytes from the "
+ << "data payload.\n" << logofs_flush;
+ #endif
+
+ changeGC -> value_mask &= (1 << 23) - 1;
+
+ unsigned int mask = 0x1;
+ unsigned char *source = (unsigned char *) buffer + CHANGEGC_DATA_OFFSET;
+ unsigned long value = 0;
+
+ for (unsigned int i = 0; i < 23; i++)
+ {
+ if (changeGC -> value_mask & mask)
+ {
+ value = GetULONG(source, bigEndian);
+
+ value &= (0xffffffff >> (32 - CREATEGC_FIELD_WIDTH[i]));
+
+ PutULONG(value, source, bigEndian);
+
+ source += 4;
+ }
+
+ mask <<= 1;
+ }
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int ChangeGCCompatStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ChangeGCCompatMessage *changeGC = (ChangeGCCompatMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ PutULONG(changeGC -> gcontext, buffer + 4, bigEndian);
+ PutULONG(changeGC -> value_mask, buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void ChangeGCCompatStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ ChangeGCCompatMessage *changeGC = (ChangeGCCompatMessage *) message;
+
+ *logofs << name() << ": Identity gcontext " << changeGC -> gcontext
+ << ", mask " << changeGC -> value_mask << ", size "
+ << changeGC -> size_ << ".\n" << logofs_flush;
+ #endif
+}
+
+void ChangeGCCompatStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ md5_append(md5_state_, buffer + 4, 8);
+}
diff --git a/nxcomp/ChangeGCCompat.h b/nxcomp/ChangeGCCompat.h
new file mode 100644
index 000000000..3a7b0c0a9
--- /dev/null
+++ b/nxcomp/ChangeGCCompat.h
@@ -0,0 +1,170 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef ChangeGCCompat_H
+#define ChangeGCCompat_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define CHANGEGC_ENABLE_CACHE 1
+#define CHANGEGC_ENABLE_DATA 0
+#define CHANGEGC_ENABLE_SPLIT 0
+#define CHANGEGC_ENABLE_COMPRESS 0
+
+#define CHANGEGC_DATA_LIMIT 144
+#define CHANGEGC_DATA_OFFSET 12
+
+#define CHANGEGC_CACHE_SLOTS 3000
+#define CHANGEGC_CACHE_THRESHOLD 3
+#define CHANGEGC_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class ChangeGCCompatMessage : public Message
+{
+ friend class ChangeGCCompatStore;
+
+ public:
+
+ ChangeGCCompatMessage()
+ {
+ }
+
+ ~ChangeGCCompatMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned int gcontext;
+ unsigned int value_mask;
+};
+
+class ChangeGCCompatStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ ChangeGCCompatStore() : MessageStore()
+ {
+ enableCache = CHANGEGC_ENABLE_CACHE;
+ enableData = CHANGEGC_ENABLE_DATA;
+ enableSplit = CHANGEGC_ENABLE_SPLIT;
+ enableCompress = CHANGEGC_ENABLE_COMPRESS;
+
+ dataLimit = CHANGEGC_DATA_LIMIT;
+ dataOffset = CHANGEGC_DATA_OFFSET;
+
+ cacheSlots = CHANGEGC_CACHE_SLOTS;
+ cacheThreshold = CHANGEGC_CACHE_THRESHOLD;
+ cacheLowerThreshold = CHANGEGC_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~ChangeGCCompatStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "ChangeGCCompat";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_ChangeGC;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(ChangeGCCompatMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new ChangeGCCompatMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new ChangeGCCompatMessage((const ChangeGCCompatMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (ChangeGCCompatMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* ChangeGCCompat_H */
diff --git a/nxcomp/ChangeProperty.cpp b/nxcomp/ChangeProperty.cpp
new file mode 100644
index 000000000..0270d7274
--- /dev/null
+++ b/nxcomp/ChangeProperty.cpp
@@ -0,0 +1,179 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "ChangeProperty.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int ChangePropertyStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ChangePropertyMessage *changeProperty = (ChangePropertyMessage *) message;
+
+ changeProperty -> mode = *(buffer + 1);
+ changeProperty -> format = *(buffer + 16);
+
+ changeProperty -> window = GetULONG(buffer + 4, bigEndian);
+ changeProperty -> property = GetULONG(buffer + 8, bigEndian);
+ changeProperty -> type = GetULONG(buffer + 12, bigEndian);
+ changeProperty -> length = GetULONG(buffer + 20, bigEndian);
+
+ //
+ // Cleanup the padding bytes.
+ //
+
+ unsigned int uiFormat;
+ unsigned int uiLengthInBytes;
+
+ if ((int) size > CHANGEPROPERTY_DATA_OFFSET)
+ {
+ uiFormat = *(buffer + 16);
+
+ uiLengthInBytes = changeProperty -> length;
+
+ #ifdef DEBUG
+ *logofs << name() << ": length " << uiLengthInBytes
+ << ", format " << uiFormat << ", size "
+ << size << ".\n" << logofs_flush;
+ #endif
+
+ if (uiFormat == 16)
+ {
+ uiLengthInBytes <<= 1;
+ }
+ else if (uiFormat == 32)
+ {
+ uiLengthInBytes <<= 2;
+ }
+
+ unsigned char *end = ((unsigned char *) buffer) + size;
+ unsigned char *pad = ((unsigned char *) buffer) + CHANGEPROPERTY_DATA_OFFSET + uiLengthInBytes;
+
+ CleanData((unsigned char *) pad, end - pad);
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at "
+ << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int ChangePropertyStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ChangePropertyMessage *changeProperty = (ChangePropertyMessage *) message;
+
+ *(buffer + 1) = changeProperty -> mode;
+ *(buffer + 16) = changeProperty -> format;
+
+ PutULONG(changeProperty -> window, buffer + 4, bigEndian);
+ PutULONG(changeProperty -> property, buffer + 8, bigEndian);
+ PutULONG(changeProperty -> type, buffer + 12, bigEndian);
+ PutULONG(changeProperty -> length, buffer + 20, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void ChangePropertyStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ ChangePropertyMessage *changeProperty = (ChangePropertyMessage *) message;
+
+ *logofs << name() << ": Identity mode " << (unsigned int) changeProperty -> mode << ", format "
+ << (unsigned int) changeProperty -> format << ", window " << changeProperty -> window
+ << ", property " << changeProperty -> property << ", type " << changeProperty -> type
+ << ", length " << changeProperty -> length << ", size " << changeProperty -> size_
+ << ".\n" << logofs_flush;
+
+ #endif
+}
+
+void ChangePropertyStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ md5_append(md5_state_, buffer + 1, 1);
+ md5_append(md5_state_, buffer + 16, 1);
+
+ md5_append(md5_state_, buffer + 8, 4);
+ md5_append(md5_state_, buffer + 12, 4);
+ md5_append(md5_state_, buffer + 20, 4);
+}
+
+void ChangePropertyStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ ChangePropertyMessage *changeProperty = (ChangePropertyMessage *) message;
+ ChangePropertyMessage *cachedChangeProperty = (ChangePropertyMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << changeProperty -> window
+ << " as window field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(changeProperty -> window, clientCache -> windowCache);
+
+ cachedChangeProperty -> window = changeProperty -> window;
+}
+
+void ChangePropertyStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ ChangePropertyMessage *changeProperty = (ChangePropertyMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> windowCache);
+
+ changeProperty -> window = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << changeProperty -> window
+ << " as window field.\n" << logofs_flush;
+ #endif
+}
+
+
diff --git a/nxcomp/ChangeProperty.h b/nxcomp/ChangeProperty.h
new file mode 100644
index 000000000..f941fced9
--- /dev/null
+++ b/nxcomp/ChangeProperty.h
@@ -0,0 +1,181 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef ChangeProperty_H
+#define ChangeProperty_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define CHANGEPROPERTY_ENABLE_CACHE 1
+#define CHANGEPROPERTY_ENABLE_DATA 0
+#define CHANGEPROPERTY_ENABLE_SPLIT 0
+#define CHANGEPROPERTY_ENABLE_COMPRESS 0
+
+#define CHANGEPROPERTY_DATA_LIMIT 28688
+#define CHANGEPROPERTY_DATA_OFFSET 24
+
+#define CHANGEPROPERTY_CACHE_SLOTS 2000
+#define CHANGEPROPERTY_CACHE_THRESHOLD 2
+#define CHANGEPROPERTY_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class ChangePropertyMessage : public Message
+{
+ friend class ChangePropertyStore;
+
+ public:
+
+ ChangePropertyMessage()
+ {
+ }
+
+ ~ChangePropertyMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char mode;
+ unsigned char format;
+ unsigned int window;
+ unsigned int property;
+ unsigned int type;
+ unsigned int length;
+};
+
+class ChangePropertyStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ ChangePropertyStore() : MessageStore()
+ {
+ enableCache = CHANGEPROPERTY_ENABLE_CACHE;
+ enableData = CHANGEPROPERTY_ENABLE_DATA;
+ enableSplit = CHANGEPROPERTY_ENABLE_SPLIT;
+ enableCompress = CHANGEPROPERTY_ENABLE_COMPRESS;
+
+ dataLimit = CHANGEPROPERTY_DATA_LIMIT;
+ dataOffset = CHANGEPROPERTY_DATA_OFFSET;
+
+ cacheSlots = CHANGEPROPERTY_CACHE_SLOTS;
+ cacheThreshold = CHANGEPROPERTY_CACHE_THRESHOLD;
+ cacheLowerThreshold = CHANGEPROPERTY_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~ChangePropertyStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "ChangeProperty";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_ChangeProperty;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(ChangePropertyMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new ChangePropertyMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new ChangePropertyMessage((const ChangePropertyMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (ChangePropertyMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* ChangeProperty_H */
diff --git a/nxcomp/Channel.cpp b/nxcomp/Channel.cpp
new file mode 100644
index 000000000..71b556b0d
--- /dev/null
+++ b/nxcomp/Channel.cpp
@@ -0,0 +1,2125 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "Channel.h"
+
+#include "List.h"
+#include "Proxy.h"
+#include "Statistics.h"
+
+#include "StaticCompressor.h"
+
+#include "NXalert.h"
+
+extern Proxy *proxy;
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Log the operations related to splits.
+//
+
+#undef SPLIT
+
+#undef COUNT
+
+#define COUNT_MAJOR_OPCODE 154
+
+#undef MONITOR
+
+#define MONITOR_MAJOR_OPCODE 154
+#define MONITOR_MINOR_OPCODE 23
+
+#undef CLEAR
+
+#define CLEAR_MAJOR_OPCODE 154
+#define CLEAR_MINOR_OPCODE 23
+
+//
+// Define this to know how many messages
+// are allocated and deallocated.
+//
+
+#undef REFERENCES
+
+//
+// Set to the descriptor of the first X
+// channel successfully connected.
+//
+
+int Channel::firstClient_ = -1;
+
+//
+// Port used for font server connections.
+//
+
+int Channel::fontPort_ = -1;
+
+//
+// This is used for reference count.
+//
+
+#ifdef REFERENCES
+
+int Channel::references_ = 0;
+
+#endif
+
+Channel::Channel(Transport *transport, StaticCompressor *compressor)
+
+ : transport_(transport), compressor_(compressor)
+{
+ fd_ = transport_ -> fd();
+
+ finish_ = 0;
+ closing_ = 0;
+ drop_ = 0;
+ congestion_ = 0;
+ priority_ = 0;
+
+ alert_ = 0;
+
+ firstRequest_ = 1;
+ firstReply_ = 1;
+
+ enableCache_ = 1;
+ enableSplit_ = 1;
+ enableSave_ = 1;
+ enableLoad_ = 1;
+
+ //
+ // Must be set by proxy.
+ //
+
+ opcodeStore_ = NULL;
+
+ clientStore_ = NULL;
+ serverStore_ = NULL;
+
+ clientCache_ = NULL;
+ serverCache_ = NULL;
+
+ #ifdef REFERENCES
+ *logofs << "Channel: Created new Channel at "
+ << this << " out of " << ++references_
+ << " allocated references.\n" << logofs_flush;
+ #endif
+}
+
+Channel::~Channel()
+{
+ if (firstClient_ == fd_)
+ {
+ firstClient_ = -1;
+ }
+
+ #ifdef REFERENCES
+ *logofs << "Channel: Deleted Channel at "
+ << this << " out of " << --references_
+ << " allocated references.\n" << logofs_flush;
+ #endif
+}
+
+int Channel::handleEncode(EncodeBuffer &encodeBuffer, ChannelCache *channelCache,
+ MessageStore *store, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size)
+{
+ #ifdef MONITOR
+
+ static float totalMessages = 0;
+ static float totalBits = 0;
+
+ int bits;
+ int diff;
+
+ bits = encodeBuffer.getBits();
+
+ #endif
+
+ //
+ // Check if message can be differentially
+ // encoded using a similar message in the
+ // message store.
+ //
+
+ #ifdef COUNT
+
+ if (*(buffer) == COUNT_MAJOR_OPCODE)
+ {
+ if (*(buffer) < 128)
+ {
+ *logofs << "handleEncode: Handling OPCODE#" << (unsigned int) *(buffer)
+ << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleEncode: Handling OPCODE#" << (unsigned int) *(buffer)
+ << " MINOR#" << (unsigned int) *(buffer + 1) << ".\n"
+ << logofs_flush;
+ }
+ }
+
+ #endif
+
+ #ifdef CLEAR
+
+ if (*(buffer) == CLEAR_MAJOR_OPCODE &&
+ (CLEAR_MINOR_OPCODE == -1 || *(buffer + 1) == CLEAR_MINOR_OPCODE))
+ {
+ *((unsigned char *) buffer) = X_NoOperation;
+
+ *((unsigned char *) buffer + 1) = '\0';
+
+ CleanData((unsigned char *) buffer + 4, size - 4);
+ }
+
+ #endif
+
+ if (handleEncodeCached(encodeBuffer, channelCache,
+ store, buffer, size) == 1)
+ {
+ #ifdef MONITOR
+
+ diff = encodeBuffer.getBits() - bits;
+
+ if (*(buffer) == MONITOR_MAJOR_OPCODE &&
+ (MONITOR_MINOR_OPCODE == -1 || *(buffer + 1) == MONITOR_MINOR_OPCODE))
+ {
+ totalMessages++;
+
+ totalBits += diff;
+
+ *logofs << "handleEncode: Handled cached OPCODE#" << (unsigned int) *(buffer)
+ << " MINOR#" << (unsigned int) *(buffer + 1) << ". " << size
+ << " bytes in, " << diff << " bits (" << ((float) diff) / 8
+ << " bytes) out. Average " << totalBits / totalMessages
+ << "/1.\n" << logofs_flush;
+ }
+
+ #endif
+
+ //
+ // Let the channel update the split store
+ // and notify the agent in the case of a
+ // cache hit.
+ //
+
+ if (store -> enableSplit)
+ {
+ handleSplit(encodeBuffer, store, store -> lastAction,
+ store -> lastHit, opcode, buffer, size);
+ }
+
+ return 1;
+ }
+
+ //
+ // A similar message could not be found in
+ // cache or message must be discarded. Must
+ // transmit the message using the field by
+ // field differential encoding.
+ //
+
+ handleEncodeIdentity(encodeBuffer, channelCache,
+ store, buffer, size, bigEndian_);
+
+ //
+ // Check if message has a distinct data part.
+ //
+
+ if (store -> enableData)
+ {
+ //
+ // If message split was requested by agent then send data
+ // out-of-band, dividing it in small chunks. Until message
+ // is completely transferred, keep in the split store a
+ // dummy version of the message, with data replaced with a
+ // pattern.
+ //
+ // While data is being transferred, agent should have put
+ // the resource (for example its client) asleep. It can
+ // happen, though, that a different client would reference
+ // the same message. We cannot issue a cache hit for images
+ // being split (such images are put in store in 'incomplete'
+ // state), so we need to handle this case.
+ //
+
+ if (store -> enableSplit == 1)
+ {
+ //
+ // Let the channel decide what to do with the
+ // message. If the split can't take place be-
+ // cause the split store is full, the channel
+ // will tell the remote side that the data is
+ // going to follow.
+ //
+
+ if (handleSplit(encodeBuffer, store, store -> lastAction,
+ (store -> lastAction == IS_ADDED ? store -> lastAdded : 0),
+ opcode, buffer, size) == 1)
+ {
+ #ifdef MONITOR
+
+ diff = encodeBuffer.getBits() - bits;
+
+ if (*(buffer) == MONITOR_MAJOR_OPCODE &&
+ (MONITOR_MINOR_OPCODE == -1 || *(buffer + 1) == MONITOR_MINOR_OPCODE))
+ {
+ totalMessages++;
+
+ totalBits += diff;
+
+ *logofs << "handleEncode: Handled split OPCODE#" << (unsigned int) *(buffer)
+ << " MINOR#" << (unsigned int) *(buffer + 1) << ". " << size
+ << " bytes in, " << diff << " bits (" << ((float) diff) / 8
+ << " bytes) out. Average " << totalBits / totalMessages
+ << "/1.\n" << logofs_flush;
+ }
+
+ #endif
+
+ return 0;
+ }
+ }
+
+ //
+ // The split did not take place and we are going
+ // to transfer the data part. Check if the static
+ // compression of the data section is enabled.
+ // This is the case of all messages not having a
+ // special differential encoding or messages that
+ // we want to store in cache in compressed form.
+ //
+
+ unsigned int offset = store -> identitySize(buffer, size);
+
+ if (store -> enableCompress)
+ {
+ unsigned char *data = NULL;
+ unsigned int dataSize = 0;
+
+ int compressed = handleCompress(encodeBuffer, opcode, offset,
+ buffer, size, data, dataSize);
+ if (compressed < 0)
+ {
+ return -1;
+ }
+ else if (compressed > 0)
+ {
+ //
+ // Update the size of the message according
+ // to the result of the data compression.
+ //
+
+ handleUpdate(store, size - offset, dataSize);
+ }
+ }
+ else
+ {
+ handleCopy(encodeBuffer, opcode, offset, buffer, size);
+ }
+ }
+
+ #ifdef MONITOR
+
+ diff = encodeBuffer.getBits() - bits;
+
+ if (*(buffer) == MONITOR_MAJOR_OPCODE &&
+ (MONITOR_MINOR_OPCODE == -1 || *(buffer + 1) == MONITOR_MINOR_OPCODE))
+ {
+ totalMessages++;
+
+ totalBits += diff;
+
+ *logofs << "handleEncode: Handled OPCODE#" << (unsigned int) *(buffer)
+ << " MINOR#" << (unsigned int) *(buffer + 1) << ". " << size
+ << " bytes in, " << diff << " bits (" << ((float) diff) / 8
+ << " bytes) out. Average " << totalBits / totalMessages
+ << "/1.\n" << logofs_flush;
+ }
+
+ #endif
+
+ return 0;
+}
+
+int Channel::handleDecode(DecodeBuffer &decodeBuffer, ChannelCache *channelCache,
+ MessageStore *store, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+{
+ //
+ // Check first if the message is in the
+ // message store.
+ //
+
+ unsigned int split = 0;
+
+ if (handleDecodeCached(decodeBuffer, channelCache,
+ store, buffer, size) == 1)
+ {
+ //
+ // Let the channel update the split store
+ // in the case of a message being cached.
+ //
+
+ if (store -> enableSplit == 1)
+ {
+ if (control -> isProtoStep7() == 1)
+ {
+ #ifdef DEBUG
+ *logofs << "handleDecode: " << store -> name()
+ << ": Checking if the message was split.\n"
+ << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeBoolValue(split);
+ }
+
+ if (split == 1)
+ {
+ handleSplit(decodeBuffer, store, store -> lastAction,
+ store -> lastHit, opcode, buffer, size);
+
+ handleCleanAndNullRequest(opcode, buffer, size);
+ }
+ }
+
+ return 1;
+ }
+
+ //
+ // Decode the full identity.
+ //
+
+ handleDecodeIdentity(decodeBuffer, channelCache, store, buffer,
+ size, bigEndian_, &writeBuffer_);
+
+ //
+ // Check if the message has a distinct
+ // data part.
+ //
+
+ if (store -> enableData)
+ {
+ //
+ // Check if message has been split.
+ //
+
+ if (store -> enableSplit)
+ {
+ #ifdef DEBUG
+ *logofs << "handleDecode: " << store -> name()
+ << ": Checking if the message was split.\n"
+ << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeBoolValue(split);
+
+ if (split == 1)
+ {
+ //
+ // If the message was added to the store,
+ // create the entry without the data part.
+ //
+
+ handleSaveSplit(store, buffer, size);
+
+ handleSplit(decodeBuffer, store, store -> lastAction,
+ (store -> lastAction == IS_ADDED ? store -> lastAdded : 0),
+ opcode, buffer, size);
+
+ handleCleanAndNullRequest(opcode, buffer, size);
+
+ return 0;
+ }
+ }
+
+ //
+ // Decode the data part.
+ //
+
+ unsigned int offset = store -> identitySize(buffer, size);
+
+ if (store -> enableCompress)
+ {
+ const unsigned char *data = NULL;
+ unsigned int dataSize = 0;
+
+ int decompressed = handleDecompress(decodeBuffer, opcode, offset,
+ buffer, size, data, dataSize);
+ if (decompressed < 0)
+ {
+ return -1;
+ }
+ else if (decompressed > 0)
+ {
+ //
+ // The message has been transferred
+ // in compressed format.
+ //
+
+ handleSave(store, buffer, size, data, dataSize);
+
+ if (store -> enableSplit)
+ {
+ if (split == 1)
+ {
+ handleSplit(decodeBuffer, store, store -> lastAction,
+ (store -> lastAction == IS_ADDED ? store -> lastAdded : 0),
+ opcode, buffer, size);
+
+ handleCleanAndNullRequest(opcode, buffer, size);
+ }
+ }
+
+ return 0;
+ }
+ }
+ else
+ {
+ //
+ // Static compression of the data part
+ // was not enabled for this message.
+ //
+
+ handleCopy(decodeBuffer, opcode, offset, buffer, size);
+ }
+ }
+
+ //
+ // The message doesn't have a data part
+ // or the data was not compressed.
+ //
+
+ handleSave(store, buffer, size);
+
+ if (store -> enableSplit)
+ {
+ if (split == 1)
+ {
+ handleSplit(decodeBuffer, store, store -> lastAction,
+ (store -> lastAction == IS_ADDED ? store -> lastAdded : 0),
+ opcode, buffer, size);
+
+ handleCleanAndNullRequest(opcode, buffer, size);
+ }
+ }
+
+ return 0;
+}
+
+int Channel::handleEncodeCached(EncodeBuffer &encodeBuffer, ChannelCache *channelCache,
+ MessageStore *store, const unsigned char *buffer,
+ const unsigned int size)
+{
+ if (control -> LocalDeltaCompression == 0 ||
+ enableCache_ == 0 || store -> enableCache == 0)
+ {
+ if (control -> isProtoStep7() == 1)
+ {
+ encodeBuffer.encodeActionValue(is_discarded,
+ store -> lastActionCache);
+ }
+ else
+ {
+ encodeBuffer.encodeActionValueCompat(is_discarded,
+ store -> lastActionCacheCompat);
+ }
+
+ store -> lastAction = is_discarded;
+
+ return 0;
+ }
+
+ #ifdef DEBUG
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": Going to handle a new message of this class.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Check if the estimated size of cache is greater
+ // than the requested limit. If it is the case make
+ // some room by deleting one or more messages.
+ //
+
+ int position;
+
+ while (mustCleanStore(store) == 1 && canCleanStore(store) == 1)
+ {
+ #ifdef DEBUG
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": Trying to reduce size of message store.\n"
+ << logofs_flush;
+ #endif
+
+ position = store -> clean(use_checksum);
+
+ if (position == nothing)
+ {
+ #ifdef TEST
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": WARNING! No message found to be "
+ << "actually removed.\n" << logofs_flush;
+ #endif
+
+ break;
+ }
+
+ #ifdef DEBUG
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": Message at position " << position
+ << " will be removed.\n" << logofs_flush;
+ #endif
+
+ //
+ // Encode the position of message to
+ // be discarded.
+ //
+
+ store -> lastRemoved = position;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ encodeBuffer.encodeActionValue(is_removed, store -> lastRemoved,
+ store -> lastActionCache);
+ }
+ else
+ {
+ encodeBuffer.encodeActionValueCompat(is_removed,
+ store -> lastActionCacheCompat);
+
+ encodeBuffer.encodePositionValueCompat(store -> lastRemoved,
+ store -> lastRemovedCacheCompat);
+ }
+
+ #ifdef DEBUG
+ *logofs << "handleEncodeCached: " << store -> name() << ": Going to "
+ << "clean up message at position " << position << ".\n"
+ << logofs_flush;
+ #endif
+
+ store -> remove(position, use_checksum, discard_data);
+
+ #ifdef DEBUG
+ *logofs << "handleEncodeCached: " << store -> name() << ": There are "
+ << store -> getSize() << " messages in the store out of "
+ << store -> cacheSlots << " slots.\n" << logofs_flush;
+
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": Size of store is " << store -> getLocalStorageSize()
+ << " bytes locally and " << store -> getRemoteStorageSize()
+ << " bytes remotely.\n" << logofs_flush;
+
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": Size of total cache is " << store -> getLocalTotalStorageSize()
+ << " bytes locally and " << store -> getRemoteTotalStorageSize()
+ << " bytes remotely.\n" << logofs_flush;
+ #endif
+ }
+
+ #ifdef DEBUG
+
+ if (mustCleanStore(store) == 1 && canCleanStore(store) == 0)
+ {
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": Store would need a clean but operation will be delayed.\n"
+ << logofs_flush;
+
+ *logofs << "handleEncodeCached: " << store -> name() << ": There are "
+ << store -> getSize() << " messages in the store out of "
+ << store -> cacheSlots << " slots.\n" << logofs_flush;
+
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": Size of store is " << store -> getLocalStorageSize()
+ << " bytes locally and " << store -> getRemoteStorageSize()
+ << " bytes remotely.\n" << logofs_flush;
+
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": Size of total cache is " << store -> getLocalTotalStorageSize()
+ << " bytes locally and " << store -> getRemoteTotalStorageSize()
+ << " bytes remotely.\n" << logofs_flush;
+ }
+
+ #endif
+
+ //
+ // If 'on the wire' size of message exceeds the
+ // allowed limit then avoid to store it in the
+ // cache.
+ //
+
+ if (store -> validateMessage(buffer, size) == 0)
+ {
+ #ifdef TEST
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": Message with size " << size << " ignored.\n"
+ << logofs_flush;
+ #endif
+
+ if (control -> isProtoStep7() == 1)
+ {
+ encodeBuffer.encodeActionValue(is_discarded,
+ store -> lastActionCache);
+ }
+ else
+ {
+ encodeBuffer.encodeActionValueCompat(is_discarded,
+ store -> lastActionCacheCompat);
+ }
+
+ store -> lastAction = is_discarded;
+
+ return 0;
+ }
+
+ //
+ // Fill the message object with the
+ // received data.
+ //
+
+ Message *message = store -> getTemporary();
+
+ if (message == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": PANIC! Can't allocate memory for "
+ << "a new message.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory for "
+ << "a new message in context [D].\n";
+
+ HandleCleanup();
+ }
+
+ //
+ // As we are at encoding side, it is enough to store the
+ // checksum for the object while data can be erased. Both
+ // the identity and the data will never be sent through
+ // the wire again as long as they are stored in the cache
+ // at the decoding side. The split parameter is always
+ // set to 0 as the data will not be stored in any case.
+ //
+
+ store -> parse(message, 0, buffer, size, use_checksum,
+ discard_data, bigEndian_);
+
+ #ifdef DUMP
+
+ store -> dump(message);
+
+ #endif
+
+ //
+ // Search the object in the message
+ // store. If found get the position.
+ //
+
+ #ifdef DEBUG
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": Searching object of size " << size
+ << " in the cache.\n" << logofs_flush;
+ #endif
+
+ int added;
+ int locked;
+
+ position = store -> findOrAdd(message, use_checksum,
+ discard_data, added, locked);
+
+ if (position == nothing)
+ {
+ #ifdef WARNING
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": WARNING! Can't store object in the cache.\n"
+ << logofs_flush;
+ #endif
+
+ if (control -> isProtoStep7() == 1)
+ {
+ encodeBuffer.encodeActionValue(is_discarded,
+ store -> lastActionCache);
+ }
+ else
+ {
+ encodeBuffer.encodeActionValueCompat(is_discarded,
+ store -> lastActionCacheCompat);
+ }
+
+ store -> lastAction = is_discarded;
+
+ return 0;
+ }
+ else if (locked == 1)
+ {
+ //
+ // We can't issue a cache hit. Encoding identity
+ // differences while message it's being split
+ // would later result in agent to commit a wrong
+ // version of message.
+ //
+
+ #ifdef WARNING
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": WARNING! Message of size " << store -> plainSize(position)
+ << " at position " << position << " is locked.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Message of size " << store -> plainSize(position)
+ << " at position " << position << " is locked.\n";
+
+ if (control -> isProtoStep7() == 1)
+ {
+ encodeBuffer.encodeActionValue(is_discarded,
+ store -> lastActionCache);
+ }
+ else
+ {
+ encodeBuffer.encodeActionValueCompat(is_discarded,
+ store -> lastActionCacheCompat);
+ }
+
+ store -> lastAction = is_discarded;
+
+ return 0;
+ }
+ else if (added == 1)
+ {
+ store -> resetTemporary();
+
+ #ifdef DEBUG
+ *logofs << "handleEncodeCached: " << store -> name() << ": Message of size "
+ << store -> plainSize(position) << " has been stored at position "
+ << position << ".\n" << logofs_flush;
+
+ *logofs << "handleEncodeCached: " << store -> name() << ": There are "
+ << store -> getSize() << " messages in the store out of "
+ << store -> cacheSlots << " slots.\n" << logofs_flush;
+
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": Size of store is " << store -> getLocalStorageSize()
+ << " bytes locally and " << store -> getRemoteStorageSize()
+ << " bytes remotely.\n" << logofs_flush;
+
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": Size of total cache is " << store -> getLocalTotalStorageSize()
+ << " bytes locally and " << store -> getRemoteTotalStorageSize()
+ << " bytes remotely.\n" << logofs_flush;
+ #endif
+
+ //
+ // Inform the decoding side that message
+ // must be inserted in cache and encode
+ // the position where the insertion took
+ // place.
+ //
+
+ store -> lastAction = IS_ADDED;
+
+ store -> lastAdded = position;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ encodeBuffer.encodeActionValue(IS_ADDED, store -> lastAdded,
+ store -> lastActionCache);
+
+ }
+ else
+ {
+ encodeBuffer.encodeActionValueCompat(IS_ADDED,
+ store -> lastActionCacheCompat);
+
+ encodeBuffer.encodePositionValueCompat(store -> lastAdded,
+ store -> lastAddedCacheCompat);
+ }
+
+ return 0;
+ }
+ else
+ {
+ #ifdef DEBUG
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": Cache hit. Found object at position "
+ << position << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Must abort the connection if the
+ // the position is invalid.
+ //
+
+ Message *cachedMessage = store -> get(position);
+
+ //
+ // Increase the rating of the cached
+ // message.
+ //
+
+ store -> touch(cachedMessage);
+
+ #ifdef DEBUG
+ *logofs << "handleEncodeCached: " << store -> name() << ": Hits for "
+ << "object at position " << position << " are now "
+ << store -> getTouches(position) << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Send to the decoding side position
+ // where object can be found in cache.
+ //
+
+ store -> lastAction = IS_HIT;
+
+ store -> lastHit = position;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ encodeBuffer.encodeActionValue(IS_HIT, store -> lastHit,
+ store -> lastActionCache);
+ }
+ else
+ {
+ encodeBuffer.encodeActionValueCompat(IS_HIT,
+ store -> lastActionCacheCompat);
+
+ encodeBuffer.encodePositionValueCompat(store -> lastHit,
+ store -> lastHitCacheCompat);
+ }
+
+ //
+ // Send the field by field differences in
+ // respect to the original message stored
+ // in cache.
+ //
+
+ store -> updateIdentity(encodeBuffer, message, cachedMessage, channelCache);
+
+ return 1;
+ }
+}
+
+void Channel::handleUpdateAdded(MessageStore *store, unsigned int dataSize,
+ unsigned int compressedDataSize)
+{
+ #ifdef TEST
+
+ if (store -> lastAction != IS_ADDED)
+ {
+ #ifdef PANIC
+ *logofs << "handleUpdateAdded: " << store -> name()
+ << ": PANIC! Function called for action '"
+ << store -> lastAction << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Update function called for "
+ << "store '" << store -> name() << "' with "
+ << "action '" << store -> lastAction
+ << "'.\n";
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ #ifdef DEBUG
+ *logofs << "handleUpdateAdded: " << store -> name() << ": Updating "
+ << "object at position " << store -> lastAdded << " of size "
+ << store -> plainSize(store -> lastAdded) << " (" << dataSize
+ << "/" << compressedDataSize << ").\n" << logofs_flush;
+ #endif
+
+ store -> updateData(store -> lastAdded, dataSize, compressedDataSize);
+
+ #ifdef DEBUG
+ *logofs << "handleUpdateAdded: " << store -> name() << ": There are "
+ << store -> getSize() << " messages in the store out of "
+ << store -> cacheSlots << " slots.\n" << logofs_flush;
+
+ *logofs << "handleUpdateAdded: " << store -> name()
+ << ": Size of store is " << store -> getLocalStorageSize()
+ << " bytes locally and " << store -> getRemoteStorageSize()
+ << " bytes remotely.\n" << logofs_flush;
+
+ *logofs << "handleUpdateAdded: " << store -> name()
+ << ": Size of total cache is " << store -> getLocalTotalStorageSize()
+ << " bytes locally and " << store -> getRemoteTotalStorageSize()
+ << " bytes remotely.\n" << logofs_flush;
+ #endif
+}
+
+int Channel::handleDecodeCached(DecodeBuffer &decodeBuffer, ChannelCache *channelCache,
+ MessageStore *store, unsigned char *&buffer,
+ unsigned int &size)
+{
+ //
+ // Create a new message object and
+ // fill it with received data.
+ //
+
+ #ifdef DEBUG
+ *logofs << "handleDecodeCached: " << store -> name()
+ << ": Going to handle a new message of this class.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Decode bits telling how to handle
+ // this message.
+ //
+
+ unsigned char action;
+ unsigned short int position;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ decodeBuffer.decodeActionValue(action, position,
+ store -> lastActionCache);
+ }
+ else
+ {
+ decodeBuffer.decodeActionValueCompat(action,
+ store -> lastActionCacheCompat);
+ }
+
+ //
+ // Clean operations must always come
+ // before any operation on message.
+ //
+
+ while (action == is_removed)
+ {
+ if (control -> isProtoStep7() == 1)
+ {
+ store -> lastRemoved = position;
+ }
+ else
+ {
+ decodeBuffer.decodePositionValueCompat(store -> lastRemoved,
+ store -> lastRemovedCacheCompat);
+ }
+
+ #ifdef DEBUG
+
+ if (store -> get(store -> lastRemoved))
+ {
+ *logofs << "handleDecodeCached: " << store -> name() << ": Cleaning up "
+ << "object at position " << store -> lastRemoved
+ << " of size " << store -> plainSize(store -> lastRemoved)
+ << " (" << store -> plainSize(store -> lastRemoved) << "/"
+ << store -> compressedSize(store -> lastRemoved) << ").\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ //
+ // If the message can't be found we
+ // will abort the connection.
+ //
+
+ store -> remove(store -> lastRemoved, discard_checksum, use_data);
+
+ if (control -> isProtoStep7() == 1)
+ {
+ decodeBuffer.decodeActionValue(action, position,
+ store -> lastActionCache);
+ }
+ else
+ {
+ decodeBuffer.decodeActionValueCompat(action,
+ store -> lastActionCacheCompat);
+ }
+ }
+
+ //
+ // If it's a cache hit, the position
+ // where object can be found follows.
+ //
+
+ if ((T_store_action) action == IS_HIT)
+ {
+ if (control -> isProtoStep7() == 1)
+ {
+ store -> lastHit = position;
+ }
+ else
+ {
+ decodeBuffer.decodePositionValueCompat(store -> lastHit,
+ store -> lastHitCacheCompat);
+ }
+
+ //
+ // Get data from the cache at given position.
+ //
+
+ #ifdef DEBUG
+
+ if (store -> get(store -> lastHit))
+ {
+ *logofs << "handleDecodeCached: " << store -> name() << ": Retrieving "
+ << "object at position " << store -> lastHit
+ << " of size " << store -> plainSize(store -> lastHit)
+ << " (" << store -> plainSize(store -> lastHit) << "/"
+ << store -> compressedSize(store -> lastHit) << ").\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ //
+ // Must abort the connection if the
+ // the position is invalid.
+ //
+
+ Message *message = store -> get(store -> lastHit);
+
+ //
+ // Make room for the outgoing message.
+ //
+
+ size = store -> plainSize(store -> lastHit);
+
+ buffer = writeBuffer_.addMessage(size);
+
+ #ifdef DEBUG
+ *logofs << "handleDecodeCached: " << store -> name()
+ << ": Prepared an outgoing buffer of "
+ << size << " bytes.\n" << logofs_flush;
+ #endif
+
+ //
+ // Decode the variant part. Pass client
+ // or server cache to the message store.
+ //
+
+ store -> updateIdentity(decodeBuffer, message, channelCache);
+
+ //
+ // Write each field in the outgoing buffer.
+ //
+
+ store -> unparse(message, buffer, size, bigEndian_);
+
+ #ifdef DUMP
+
+ store -> dump(message);
+
+ #endif
+
+ store -> lastAction = IS_HIT;
+
+ return 1;
+ }
+ else if ((T_store_action) action == IS_ADDED)
+ {
+ if (control -> isProtoStep7() == 1)
+ {
+ store -> lastAdded = position;
+ }
+ else
+ {
+ decodeBuffer.decodePositionValueCompat(store -> lastAdded,
+ store -> lastAddedCacheCompat);
+ }
+
+ #ifdef DEBUG
+ *logofs << "handleDecodeCached: " << store -> name()
+ << ": Message will be later stored at position "
+ << store -> lastAdded << ".\n" << logofs_flush;
+ #endif
+
+ store -> lastAction = IS_ADDED;
+
+ return 0;
+ }
+ else
+ {
+ #ifdef DEBUG
+ *logofs << "handleDecodeCached: " << store -> name()
+ << ": Message will be later discarded.\n"
+ << logofs_flush;
+ #endif
+
+ store -> lastAction = is_discarded;
+
+ return 0;
+ }
+}
+
+void Channel::handleSaveAdded(MessageStore *store, int split, unsigned char *buffer,
+ unsigned int size, const unsigned char *compressedData,
+ const unsigned int compressedDataSize)
+{
+ #ifdef TEST
+
+ if (store -> lastAction != IS_ADDED)
+ {
+ #ifdef PANIC
+ *logofs << "handleSaveAdded: " << store -> name()
+ << ": PANIC! Function called for action '"
+ << store -> lastAction << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Save function called for "
+ << "store '" << store -> name() << "' with "
+ << "action '" << store -> lastAction
+ << "'.\n";
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ Message *message = store -> getTemporary();
+
+ if (message == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "handleSaveAdded: " << store -> name()
+ << ": PANIC! Can't access temporary storage "
+ << "for message at position " << store -> lastAdded
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't access temporary storage "
+ << "for message at position " << store -> lastAdded
+ << ".\n";
+
+ HandleCleanup();
+ }
+
+ if (compressedData == NULL)
+ {
+ //
+ // If the data part has been split
+ // avoid to copy it into the message.
+ //
+
+ store -> parse(message, split, buffer, size, discard_checksum,
+ use_data, bigEndian_);
+ }
+ else
+ {
+ store -> parse(message, buffer, size, compressedData,
+ compressedDataSize, discard_checksum,
+ use_data, bigEndian_);
+ }
+
+ if (store -> add(message, store -> lastAdded,
+ discard_checksum, use_data) == nothing)
+ {
+ #ifdef PANIC
+ *logofs << "handleSaveAdded: " << store -> name()
+ << ": PANIC! Can't store message in the cache "
+ << "at position " << store -> lastAdded << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't store message of type "
+ << store -> name() << "in the cache at position "
+ << store -> lastAdded << ".\n";
+
+ HandleCleanup();
+ }
+ else
+ {
+ store -> resetTemporary();
+
+ #ifdef DEBUG
+ *logofs << "handleSaveAdded: " << store -> name() << ": Stored "
+ << (compressedData == NULL ? "plain" : "compressed")
+ << " object at position " << store -> lastAdded
+ << " of size " << store -> plainSize(store -> lastAdded)
+ << " (" << store -> plainSize(store -> lastAdded) << "/"
+ << store -> compressedSize(store -> lastAdded) << ").\n"
+ << logofs_flush;
+ #endif
+ }
+
+ #ifdef DEBUG
+ *logofs << "handleSaveAdded: " << store -> name()
+ << ": Size of store is " << store -> getLocalStorageSize()
+ << " bytes locally and " << store -> getRemoteStorageSize()
+ << " bytes remotely.\n" << logofs_flush;
+
+ *logofs << "handleSaveAdded: " << store -> name()
+ << ": Size of total cache is " << store -> getLocalTotalStorageSize()
+ << " bytes locally and " << store -> getRemoteTotalStorageSize()
+ << " bytes remotely.\n" << logofs_flush;
+ #endif
+}
+
+int Channel::handleWait(int timeout)
+{
+ #ifdef TEST
+ *logofs << "handleWait: Going to wait for more data "
+ << "on FD#" << fd_ << " at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ #endif
+
+ T_timestamp startTs = getNewTimestamp();
+
+ T_timestamp nowTs = startTs;
+
+ int readable;
+ int remaining;
+
+ for (;;)
+ {
+ remaining = timeout - diffTimestamp(startTs, nowTs);
+
+ if (transport_ -> blocked() == 1)
+ {
+ #ifdef WARNING
+ *logofs << "handleWait: WARNING! Having to drain with "
+ << "channel " << "for FD#" << fd_ << " blocked.\n"
+ << logofs_flush;
+ #endif
+
+ handleDrain(0, remaining);
+
+ continue;
+ }
+
+ if (remaining <= 0)
+ {
+ #ifdef TEST
+ *logofs << "handleWait: Timeout raised while waiting "
+ << "for more data for FD#" << fd_ << " at "
+ << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ #ifdef TEST
+ *logofs << "handleWait: Waiting " << remaining << " Ms "
+ << "for a new message on FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ readable = transport_ -> wait(remaining);
+
+ if (readable > 0)
+ {
+ #ifdef TEST
+ *logofs << "handleWait: WARNING! Encoding more data "
+ << "for FD#" << fd_ << " at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (proxy -> handleAsyncRead(fd_) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+ }
+ else if (readable == -1)
+ {
+ return -1;
+ }
+
+ nowTs = getNewTimestamp();
+ }
+}
+
+int Channel::handleDrain(int limit, int timeout)
+{
+ #ifdef TEST
+ *logofs << "handleDrain: Going to drain FD#" << fd_
+ << " with a limit of " << limit << " bytes "
+ << "at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ T_timestamp startTs = getNewTimestamp();
+
+ T_timestamp nowTs = startTs;
+
+ int drained;
+ int remaining;
+
+ int result;
+
+ for (;;)
+ {
+ remaining = timeout - diffTimestamp(startTs, nowTs);
+
+ if (remaining <= 0)
+ {
+ #ifdef TEST
+ *logofs << "handleDrain: Timeout raised while draining "
+ << "FD#" << fd_ << " at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ #endif
+
+ result = 0;
+
+ goto ChannelDrainEnd;
+ }
+
+ #ifdef TEST
+ *logofs << "handleDrain: Trying to write to FD#"
+ << fd_ << " with " << remaining << " Ms "
+ << "remaining.\n" << logofs_flush;
+ #endif
+
+ drained = transport_ -> drain(limit, remaining);
+
+ if (drained == 1)
+ {
+ #ifdef TEST
+ *logofs << "handleDrain: Transport for FD#" << fd_
+ << " drained to " << transport_ -> length()
+ << " bytes at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ result = 1;
+
+ goto ChannelDrainEnd;
+ }
+ else if (drained == 0 && transport_ -> readable() > 0)
+ {
+ #ifdef TEST
+ *logofs << "handleDrain: WARNING! Encoding more data "
+ << "for FD#" << fd_ << " at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (proxy -> handleAsyncRead(fd_) < 0)
+ {
+ goto ChannelDrainError;
+ }
+ }
+ else if (drained == -1)
+ {
+ goto ChannelDrainError;
+ }
+
+ nowTs = getNewTimestamp();
+
+ if (diffTimestamp(startTs, nowTs) >= control -> ChannelTimeout)
+ {
+ int seconds = (remaining + control -> LatencyTimeout * 10) / 1000;
+
+ #ifdef WARNING
+ *logofs << "handleDrain: WARNING! Could not drain FD#"
+ << fd_ << " within " << seconds << " seconds.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Can't write to connection on FD#"
+ << fd_ << " since " << seconds << " seconds.\n";
+
+ if (alert_ == 0)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ alert_ = CLOSE_DEAD_X_CONNECTION_CLIENT_ALERT;
+ }
+ else
+ {
+ alert_ = CLOSE_DEAD_X_CONNECTION_SERVER_ALERT;
+ }
+
+ HandleAlert(alert_, 1);
+ }
+ }
+ }
+
+ChannelDrainEnd:
+
+ //
+ // Maybe we drained the channel and are
+ // now out of the congestion state.
+ //
+
+ handleCongestion();
+
+ return result;
+
+ChannelDrainError:
+
+ finish_ = 1;
+
+ return -1;
+}
+
+int Channel::handleCongestion()
+{
+ //
+ // Send a begin congestion control code
+ // if the local end of the channel does
+ // not consume its data.
+ //
+
+ if (isCongested() == 1)
+ {
+ if (congestion_ == 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleCongestion: Sending congestion for FD#"
+ << fd_ << " with length " << transport_ -> length()
+ << " at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ congestion_ = 1;
+
+ //
+ // Use the callback to send the control
+ // code immediately.
+ //
+
+ if (proxy -> handleAsyncCongestion(fd_) < 0)
+ {
+ finish_ = 1;
+
+ return -1;
+ }
+ }
+ }
+ else
+ {
+ //
+ // If the channel was in congestion state
+ // send an end congestion control code.
+ //
+
+ if (congestion_ == 1)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleCongestion: Sending decongestion for FD#"
+ << fd_ << " with length " << transport_ -> length()
+ << " at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ congestion_ = 0;
+
+ if (proxy -> handleAsyncDecongestion(fd_) < 0)
+ {
+ finish_ = 1;
+
+ return -1;
+ }
+ }
+
+ //
+ // Remove the "channel unresponsive"
+ // dialog.
+ //
+
+ if (alert_ != 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleCongestion: Displacing the dialog "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ HandleAlert(DISPLACE_MESSAGE_ALERT, 1);
+ }
+ }
+
+ return 1;
+}
+
+int Channel::handleFlush(T_flush type, int bufferLength, int scratchLength)
+{
+ if (finish_ == 1)
+ {
+ #ifdef TEST
+ *logofs << "handleFlush: Not flushing data for "
+ << "finishing channel for FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ writeBuffer_.fullReset();
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "handleFlush: Flushing " << bufferLength
+ << " + " << scratchLength << " bytes "
+ << "to FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Check if the channel has data available.
+ // Recent Linux kernels are very picky.
+ // They require that we read often or they
+ // assume that the process is non-interact-
+ // ive.
+ //
+
+ int result = 0;
+
+ if (handleAsyncEvents() < 0)
+ {
+ goto ChannelFlushError;
+ }
+
+ //
+ // Write the data in the main buffer first,
+ // followed by the data in the scratch buffer.
+ //
+
+ if (bufferLength > 0)
+ {
+ result = transport_ -> write(write_immediate,
+ writeBuffer_.getData(), bufferLength);
+ }
+
+ if (result >= 0 && scratchLength > 0)
+ {
+ result = transport_ -> write(write_immediate,
+ writeBuffer_.getScratchData(), scratchLength);
+ }
+
+ if (type == flush_if_any)
+ {
+ writeBuffer_.fullReset();
+ }
+ else
+ {
+ writeBuffer_.partialReset();
+ }
+
+ //
+ // If we failed to write to the X connection then
+ // set the finish flag. The caller should continue
+ // to handle all the remaining messages or it will
+ // corrupt the decode buffer. At the real end, an
+ // error will be propagated to the upper layers
+ // which will perform any needed cleanup.
+ //
+
+ if (result < 0)
+ {
+ goto ChannelFlushError;
+ }
+
+ //
+ // Reset transport buffers.
+ //
+
+ transport_ -> partialReset();
+
+ //
+ // Check if the X server has generated
+ // any event in response to our data.
+ //
+
+ if (handleAsyncEvents() < 0)
+ {
+ goto ChannelFlushError;
+ }
+
+ //
+ // Check if the channel has entered in
+ // congestion state and, in this case,
+ // send an immediate congestion control
+ // code to the remote.
+ //
+
+ handleCongestion();
+
+ //
+ // We could optionally drain the output
+ // buffer if this is X11 channel.
+ //
+ // if (isCongested() == 1 && isReliable() == 1)
+ // {
+ // if (handleDrain(0, control -> ChannelTimeout) < 0)
+ // {
+ // goto ChannelFlushError;
+ // }
+ // }
+ //
+
+ return 1;
+
+ChannelFlushError:
+
+ finish_ = 1;
+
+ return -1;
+}
+
+int Channel::handleFlush()
+{
+ #ifdef TEST
+ *logofs << "handleFlush: Flushing "
+ << transport_ -> length() << " bytes to FD#"
+ << fd_ << " with descriptor writable.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Check if there is anything to read
+ // before anf after having written to
+ // the socket.
+ //
+
+ if (handleAsyncEvents() < 0)
+ {
+ goto ChannelFlushError;
+ }
+
+ if (transport_ -> flush() < 0)
+ {
+ #ifdef TEST
+ *logofs << "handleFlush: Failure detected "
+ << "flushing data to FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ goto ChannelFlushError;
+ }
+
+ if (handleAsyncEvents() < 0)
+ {
+ goto ChannelFlushError;
+ }
+
+ //
+ // Reset channel's transport buffers.
+ //
+
+ transport_ -> partialReset();
+
+ //
+ // Check if the channel went out of the
+ // congestion state.
+ //
+
+ handleCongestion();
+
+ return 1;
+
+ChannelFlushError:
+
+ finish_ = 1;
+
+ return -1;
+}
+
+void Channel::handleResetAlert()
+{
+ if (alert_ != 0)
+ {
+ #ifdef TEST
+ *logofs << "handleResetAlert: The channel alert '"
+ << alert_ << "' was displaced.\n"
+ << logofs_flush;
+ #endif
+
+ alert_ = 0;
+ }
+}
+
+int Channel::handleCompress(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned int offset, const unsigned char *buffer,
+ const unsigned int size, unsigned char *&compressedData,
+ unsigned int &compressedDataSize)
+{
+ if (size <= offset)
+ {
+ #ifdef DEBUG
+ *logofs << "handleCompress: Not compressing data for FD#" << fd_
+ << " as offset is " << offset << " with data size "
+ << size << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ #ifdef DEBUG
+ *logofs << "handleCompress: Compressing data for FD#" << fd_
+ << " with data size " << size << " and offset "
+ << offset << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // It is responsibility of the compressor to
+ // mark the buffer as such if the compression
+ // couldn't take place.
+ //
+
+ if (compressor_ -> compressBuffer(buffer + offset, size - offset, compressedData,
+ compressedDataSize, encodeBuffer) <= 0)
+ {
+ #ifdef DEBUG
+ *logofs << "handleCompress: Sent " << size - offset
+ << " bytes of plain data for FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else
+ {
+ #ifdef DEBUG
+ *logofs << "handleCompress: Sent " << compressedDataSize
+ << " bytes of compressed data for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+ }
+}
+
+int Channel::handleDecompress(DecodeBuffer &decodeBuffer, const unsigned char opcode,
+ const unsigned int offset, unsigned char *buffer,
+ const unsigned int size, const unsigned char *&compressedData,
+ unsigned int &compressedDataSize)
+{
+ if (size <= offset)
+ {
+ return 0;
+ }
+
+ int result = compressor_ -> decompressBuffer(buffer + offset, size - offset,
+ compressedData, compressedDataSize,
+ decodeBuffer);
+ if (result < 0)
+ {
+ #ifdef PANIC
+ *logofs << "handleDecompress: PANIC! Failed to decompress "
+ << size - offset << " bytes of data for FD#" << fd_
+ << " with OPCODE#" << (unsigned int) opcode << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Data decompression failed for OPCODE#"
+ << (unsigned int) opcode << ".\n";
+
+ return -1;
+ }
+ else if (result == 0)
+ {
+ #ifdef DEBUG
+ *logofs << "handleDecompress: Received " << size - offset
+ << " bytes of plain data for FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else
+ {
+ #ifdef DEBUG
+ *logofs << "handleDecompress: Received " << compressedDataSize
+ << " bytes of compressed data for FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+ }
+}
+
+int Channel::handleCleanAndNullRequest(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size)
+{
+ #ifdef TEST
+ *logofs << "handleCleanAndNullRequest: Removing the previous data "
+ << "and sending an X_NoOperation " << "for FD#" << fd_
+ << " due to OPCODE#" << (unsigned int) opcode << " ("
+ << DumpOpcode(opcode) << ").\n" << logofs_flush;
+ #endif
+
+ writeBuffer_.removeMessage(size - 4);
+
+ size = 4;
+ opcode = X_NoOperation;
+
+ return 1;
+}
+
+int Channel::handleNullRequest(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size)
+{
+ #ifdef TEST
+ *logofs << "handleNullRequest: Sending an X_NoOperation for FD#"
+ << fd_ << " due to OPCODE#" << (unsigned int) opcode
+ << " (" << DumpOpcode(opcode) << ").\n"
+ << logofs_flush;
+ #endif
+
+ size = 4;
+ buffer = writeBuffer_.addMessage(size);
+ opcode = X_NoOperation;
+
+ return 1;
+}
+
+void Channel::handleSplitStoreError(int resource)
+{
+ if (resource < 0 || resource >= CONNECTIONS_LIMIT)
+ {
+ #ifdef PANIC
+ *logofs << "handleSplitStoreError: PANIC! Resource "
+ << resource << " is out of range with limit "
+ << "set to " << CONNECTIONS_LIMIT << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Resource " << resource
+ << " is out of range with limit set to "
+ << CONNECTIONS_LIMIT << ".\n";
+
+ HandleCleanup();
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "handleSplitStoreError: PANIC! Cannot "
+ << "allocate the split store for resource "
+ << resource << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot allocate the "
+ << "split store for resource " << resource
+ << ".\n";
+
+ HandleCleanup();
+ }
+}
+
+void Channel::handleSplitStoreAlloc(List *list, int resource)
+{
+ if (resource < 0 || resource >= CONNECTIONS_LIMIT)
+ {
+ handleSplitStoreError(resource);
+ }
+
+ if (clientStore_ -> getSplitStore(resource) == NULL)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitStoreAlloc: Allocating a new "
+ << "split store for resource " << resource
+ << ".\n" << logofs_flush;
+ #endif
+
+ SplitStore *splitStore = clientStore_ -> createSplitStore(resource);
+
+ if (splitStore == NULL)
+ {
+ handleSplitStoreError(resource);
+ }
+
+ list -> add(resource);
+ }
+ #if defined(TEST) || defined(SPLIT)
+ else
+ {
+ //
+ // Old proxy versions only use a single
+ // split store.
+ //
+
+ if (resource != 0)
+ {
+ *logofs << "handleSplitStoreAlloc: WARNING! A split "
+ << "store for resource " << resource
+ << " already exists.\n" << logofs_flush;
+ }
+ }
+ #endif
+}
+
+void Channel::handleSplitStoreRemove(List *list, int resource)
+{
+ if (resource < 0 || resource >= CONNECTIONS_LIMIT)
+ {
+ handleSplitStoreError(resource);
+ }
+
+ SplitStore *splitStore = clientStore_ -> getSplitStore(resource);
+
+ if (splitStore != NULL)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitStoreRemove: Deleting the "
+ << "split store for resource " << resource
+ << ".\n" << logofs_flush;
+ #endif
+
+ clientStore_ -> destroySplitStore(resource);
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitStoreRemove: Deleting resource "
+ << resource << " from the list " << ".\n"
+ << logofs_flush;
+ #endif
+
+ list -> remove(resource);
+ }
+ #if defined(TEST) || defined(SPLIT)
+ else
+ {
+ *logofs << "handleSplitStoreRemove: WARNING! A split "
+ << "store for resource " << resource
+ << " does not exist.\n" << logofs_flush;
+ }
+ #endif
+}
+
+Split *Channel::handleSplitCommitRemove(int request, int resource, int position)
+{
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitCommitRemove: SPLIT! Checking split "
+ << "commit with resource " << resource << " request "
+ << request << " and position " << position
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Remove the split from the split queue.
+ //
+
+ CommitStore *commitStore = clientStore_ -> getCommitStore();
+
+ Split *split = commitStore -> pop();
+
+ if (split == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "handleSplitCommitRemove: PANIC! Can't "
+ << "find the split in the commit queue.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't find the "
+ << "split in the commit queue.\n";
+
+ HandleCleanup();
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitCommitRemove: SPLIT! Element from "
+ << "the queue has resource " << split -> getResource()
+ << " request " << split -> getRequest() << " and "
+ << "position " << split -> getPosition()
+ << ".\n" << logofs_flush;
+ #endif
+
+ if ((control -> isProtoStep7() == 1 &&
+ (resource != split -> getResource() ||
+ request != split -> getRequest() ||
+ position != split -> getPosition())) ||
+ (request != split -> getRequest() ||
+ position != split -> getPosition()))
+ {
+ #ifdef PANIC
+ *logofs << "handleSplitCommitRemove: PANIC! The data in "
+ << "the split doesn't match the commit request.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": The data in the split doesn't "
+ << "match the commit request.\n";
+
+ return NULL;
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+
+ commitStore -> dump();
+
+ #endif
+
+ return split;
+}
+
+int Channel::setReferences()
+{
+ #ifdef TEST
+ *logofs << "Channel: Initializing the static "
+ << "members for the base class.\n"
+ << logofs_flush;
+ #endif
+
+ firstClient_ = -1;
+
+ fontPort_ = -1;
+
+ #ifdef REFERENCES
+
+ references_ = 0;
+
+ #endif
+
+ return 1;
+}
+
+int Channel::setOpcodes(OpcodeStore *opcodeStore)
+{
+ opcodeStore_ = opcodeStore;
+
+ #ifdef TEST
+ *logofs << "setOpcodes: Propagated opcodes store to channel "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int Channel::setStores(ClientStore *clientStore, ServerStore *serverStore)
+{
+ clientStore_ = clientStore;
+ serverStore_ = serverStore;
+
+ #ifdef TEST
+ *logofs << "setStores: Propagated message stores to channel "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int Channel::setCaches(ClientCache *clientCache, ServerCache *serverCache)
+{
+ clientCache_ = clientCache;
+ serverCache_ = serverCache;
+
+ #ifdef TEST
+ *logofs << "setCaches: Propagated encode caches to channel "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
diff --git a/nxcomp/Channel.h b/nxcomp/Channel.h
new file mode 100644
index 000000000..68fe1d7e9
--- /dev/null
+++ b/nxcomp/Channel.h
@@ -0,0 +1,656 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef Channel_H
+#define Channel_H
+
+#include "Transport.h"
+
+#include "WriteBuffer.h"
+
+#include "OpcodeStore.h"
+
+#include "ClientStore.h"
+#include "ServerStore.h"
+
+#include "ClientCache.h"
+#include "ServerCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Forward declaration of referenced classes.
+//
+
+class List;
+
+class StaticCompressor;
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Define this to log a line when a channel
+// is created or destroyed.
+//
+
+#undef REFERENCES
+
+//
+// Type of traffic carried by channel.
+//
+
+typedef enum
+{
+ channel_none = -1,
+ channel_x11,
+ channel_cups,
+ channel_smb,
+ channel_media,
+ channel_http,
+ channel_font,
+ channel_slave,
+ channel_last_tag
+
+} T_channel_type;
+
+//
+// Type of notification event to be sent
+// by proxy to the X channel.
+//
+
+typedef enum
+{
+ notify_no_split,
+ notify_start_split,
+ notify_commit_split,
+ notify_end_split,
+ notify_empty_split,
+
+} T_notification_type;
+
+class Channel
+{
+ public:
+
+ //
+ // Maximum number of X connections supported.
+ //
+
+ static const int CONNECTIONS_LIMIT = 256;
+
+ Channel(Transport *transport, StaticCompressor *compressor);
+
+ virtual ~Channel();
+
+ //
+ // Read any X message available on the X
+ // connection and encode it to the encode
+ // buffer.
+ //
+
+ virtual int handleRead(EncodeBuffer &encodeBuffer, const unsigned char *message,
+ unsigned int length) = 0;
+
+ //
+ // Decode any X message encoded in the
+ // proxy message and write it to the X
+ // connection.
+ //
+
+ virtual int handleWrite(const unsigned char *message, unsigned int length) = 0;
+
+ //
+ // Other methods to be implemented in
+ // client, server and generic channel
+ // classes.
+ //
+
+ virtual int handleSplit(EncodeBuffer &encodeBuffer, MessageStore *store,
+ T_store_action action, int position, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size) = 0;
+
+ virtual int handleSplit(DecodeBuffer &decodeBuffer, MessageStore *store,
+ T_store_action action, int position, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size) = 0;
+
+ virtual int handleSplit(EncodeBuffer &encodeBuffer) = 0;
+
+ virtual int handleSplit(DecodeBuffer &decodeBuffer) = 0;
+
+ virtual int handleSplitEvent(EncodeBuffer &encodeBuffer, Split *split) = 0;
+
+ virtual int handleSplitEvent(DecodeBuffer &decodeBuffer) = 0;
+
+ virtual int handleMotion(EncodeBuffer &encodeBuffer) = 0;
+
+ virtual int handleCompletion(EncodeBuffer &encodeBuffer) = 0;
+
+ virtual int handleConfiguration() = 0;
+
+ virtual int handleFinish() = 0;
+
+ //
+ // Interleave reads of the available
+ // events while writing data to the
+ // channel socket.
+ //
+
+ virtual int handleAsyncEvents() = 0;
+
+ //
+ // Handle the channel tear down.
+ //
+
+ int handleClosing()
+ {
+ closing_ = 1;
+
+ return 1;
+ }
+
+ int handleDrop()
+ {
+ drop_ = 1;
+
+ return 1;
+ }
+
+ //
+ // Try to read more data from the socket. In
+ // the meanwhile flush any enqueued data if
+ // the channel is blocked. Return as soon as
+ // more data has been read or the timeout has
+ // been exceeded.
+ //
+
+ int handleWait(int timeout);
+
+ //
+ // Drain the output buffer while handling the
+ // data that may become readable.
+ //
+
+ int handleDrain(int timeout, int limit);
+
+ //
+ // Flush any remaining data in the transport
+ // buffer.
+ //
+
+ int handleFlush();
+
+ //
+ // Called when the loop has replaced or
+ // closed a previous alert.
+ //
+
+ void handleResetAlert();
+
+ //
+ // Initialize all the static members.
+ //
+
+ static int setReferences();
+
+ //
+ // Set pointer to object mapping opcodes
+ // of NX specific messages.
+ //
+
+ int setOpcodes(OpcodeStore *opcodeStore);
+
+ //
+ // Update pointers to message stores in
+ // channels.
+ //
+
+ int setStores(ClientStore *clientStore, ServerStore *serverStore);
+
+ //
+ // The same for channels caches.
+ //
+
+ int setCaches(ClientCache *clientCache, ServerCache *serverCache);
+
+ //
+ // Set the port used for tunneling of the
+ // font server connections.
+ //
+
+ void setPorts(int fontPort)
+ {
+ fontPort_ = fontPort;
+ }
+
+ //
+ // Check if there are pending split
+ // to send to the remote side.
+ //
+
+ virtual int needSplit() const = 0;
+
+ //
+ // Check if there are motion events
+ // to flush.
+ //
+
+ virtual int needMotion() const = 0;
+
+ //
+ // Return the type of traffic carried
+ // by this channel.
+ //
+
+ virtual T_channel_type getType() const = 0;
+
+ //
+ // Check if the channel has been marked
+ // as closing down.
+ //
+
+ int getFinish() const
+ {
+ return finish_;
+ }
+
+ int getClosing()
+ {
+ return closing_;
+ }
+
+ int getDrop()
+ {
+ return drop_;
+ }
+
+ int getCongestion()
+ {
+ return congestion_;
+ }
+
+ protected:
+
+ int handleFlush(T_flush type)
+ {
+ //
+ // We could write the data immediately if there
+ // is already something queued to the low level
+ // TCP buffers.
+ //
+ // if (... || transport_ -> queued() > 0)
+ // {
+ // ...
+ // }
+ //
+
+ if (writeBuffer_.getScratchLength() > 0 ||
+ (type == flush_if_any && writeBuffer_.getLength() > 0) ||
+ writeBuffer_.getLength() >= (unsigned int)
+ control -> TransportFlushBufferSize)
+ {
+ return handleFlush(type, writeBuffer_.getLength(),
+ writeBuffer_.getScratchLength());
+ }
+
+ return 0;
+ }
+
+ //
+ // Actually flush the data to the
+ // channel descriptor.
+ //
+
+ int handleFlush(T_flush type, int bufferLength, int scratchLength);
+
+ //
+ // Handle the congestion changes.
+ //
+
+ int handleCongestion();
+
+ //
+ // Encode and decode X messages.
+ //
+
+ int handleEncode(EncodeBuffer &encodeBuffer, ChannelCache *channelCache,
+ MessageStore *store, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size);
+
+ int handleDecode(DecodeBuffer &decodeBuffer, ChannelCache *channelCache,
+ MessageStore *store, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size);
+
+ //
+ // Encode the message based on its
+ // message store.
+ //
+
+ int handleEncodeCached(EncodeBuffer &encodeBuffer, ChannelCache *channelCache,
+ MessageStore *store, const unsigned char *buffer,
+ const unsigned int size);
+
+ int handleDecodeCached(DecodeBuffer &decodeBuffer, ChannelCache *channelCache,
+ MessageStore *store, unsigned char *&buffer,
+ unsigned int &size);
+
+ int handleEncodeIdentity(EncodeBuffer &encodeBuffer, ChannelCache *channelCache,
+ MessageStore *store, const unsigned char *buffer,
+ const unsigned int size, int bigEndian)
+ {
+ return (store -> encodeIdentity(encodeBuffer, buffer, size,
+ bigEndian, channelCache));
+ }
+
+ int handleDecodeIdentity(DecodeBuffer &decodeBuffer, ChannelCache *channelCache,
+ MessageStore *store, unsigned char *&buffer,
+ unsigned int &size, int bigEndian,
+ WriteBuffer *writeBuffer)
+ {
+ return (store -> decodeIdentity(decodeBuffer, buffer, size, bigEndian,
+ writeBuffer, channelCache));
+ }
+
+ //
+ // Other utility functions used by
+ // the encoding and decoding methods.
+ //
+
+ void handleCopy(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned int offset, const unsigned char *buffer,
+ const unsigned int size)
+ {
+ if (size > offset)
+ {
+ encodeBuffer.encodeMemory(buffer + offset, size - offset);
+ }
+ }
+
+ void handleCopy(DecodeBuffer &decodeBuffer, const unsigned char opcode,
+ const unsigned int offset, unsigned char *buffer,
+ const unsigned int size)
+ {
+ if (size > offset)
+ {
+ memcpy(buffer + offset, decodeBuffer.decodeMemory(size - offset), size - offset);
+ }
+ }
+
+ void handleUpdate(MessageStore *store, const unsigned int dataSize,
+ const unsigned int compressedDataSize)
+ {
+ if (store -> lastAction == IS_ADDED)
+ {
+ handleUpdateAdded(store, dataSize, compressedDataSize);
+ }
+ }
+
+ void handleSave(MessageStore *store, unsigned char *buffer, unsigned int size,
+ const unsigned char *compressedData = NULL,
+ const unsigned int compressedDataSize = 0)
+ {
+ if (store -> lastAction == IS_ADDED)
+ {
+ handleSaveAdded(store, 0, buffer, size, compressedData, compressedDataSize);
+ }
+ }
+
+ void handleSaveSplit(MessageStore *store, unsigned char *buffer,
+ unsigned int size)
+ {
+ if (store -> lastAction == IS_ADDED)
+ {
+ return handleSaveAdded(store, 1, buffer, size, 0, 0);
+ }
+ }
+
+ void handleUpdateAdded(MessageStore *store, const unsigned int dataSize,
+ const unsigned int compressedDataSize);
+
+ void handleSaveAdded(MessageStore *store, int split, unsigned char *buffer,
+ unsigned int size, const unsigned char *compressedData,
+ const unsigned int compressedDataSize);
+
+ //
+ // Compress the data part of a message
+ // using ZLIB or another compressor
+ // and send it over the network.
+ //
+
+ int handleCompress(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned int offset, const unsigned char *buffer,
+ const unsigned int size, unsigned char *&compressedData,
+ unsigned int &compressedDataSize);
+
+ int handleDecompress(DecodeBuffer &decodeBuffer, const unsigned char opcode,
+ const unsigned int offset, unsigned char *buffer,
+ const unsigned int size, const unsigned char *&compressedData,
+ unsigned int &compressedDataSize);
+
+ //
+ // Send an X_NoOperation to the X server.
+ // The second version also removes any
+ // previous data in the write buffer.
+ //
+
+ int handleNullRequest(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size);
+
+ int handleCleanAndNullRequest(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size);
+
+ //
+ // X11 channels are considered to be in
+ // congestion state when there was a
+ // blocking write and, since then, the
+ // local end didn't consume all the data.
+ //
+
+ virtual int isCongested()
+ {
+ return (transport_ -> getType() !=
+ transport_agent && transport_ -> length() >
+ control -> TransportFlushBufferSize);
+ }
+
+ virtual int isReliable()
+ {
+ return 1;
+ }
+
+ //
+ // Determine how to handle allocation
+ // of new messages in the message
+ // stores.
+ //
+
+ int mustCleanStore(MessageStore *store)
+ {
+ return (store -> getRemoteTotalStorageSize() > control ->
+ RemoteTotalStorageSize || store -> getLocalTotalStorageSize() >
+ control -> LocalTotalStorageSize || (store -> getRemoteStorageSize() >
+ (control -> RemoteTotalStorageSize / 100 * store ->
+ cacheThreshold)) || (store -> getLocalStorageSize() >
+ (control -> LocalTotalStorageSize / 100 * store ->
+ cacheThreshold)));
+ }
+
+ int canCleanStore(MessageStore *store)
+ {
+ return ((store -> getSize() > 0 && (store -> getRemoteStorageSize() >
+ (control -> RemoteTotalStorageSize / 100 * store ->
+ cacheLowerThreshold))) || (store -> getLocalStorageSize() >
+ (control -> LocalTotalStorageSize / 100 * store ->
+ cacheLowerThreshold)));
+ }
+
+ protected:
+
+ //
+ // Set up the split stores.
+ //
+
+ void handleSplitStoreError(int resource);
+
+ void handleSplitStoreAlloc(List *list, int resource);
+ void handleSplitStoreRemove(List *list, int resource);
+
+ Split *handleSplitCommitRemove(int request, int resource, int position);
+
+ void validateSize(const char *name, int input, int output,
+ int offset, int size)
+ {
+ if (size < offset || size > control -> MaximumMessageSize ||
+ size != (int) RoundUp4(input) + offset ||
+ output > control -> MaximumMessageSize)
+ {
+ *logofs << "Channel: PANIC! Invalid size " << size
+ << " for " << name << " output with data "
+ << input << "/" << output << "/" << offset
+ << "/" << size << ".\n" << logofs_flush;
+
+ cerr << "Error" << ": Invalid size " << size
+ << " for " << name << " output.\n";
+
+ HandleAbort();
+ }
+ }
+
+ //
+ // Is the X client big endian?
+ //
+
+ int bigEndian() const
+ {
+ return bigEndian_;
+ }
+
+ int bigEndian_;
+
+ //
+ // Other X server's features
+ // saved at session startup.
+ //
+
+ unsigned int imageByteOrder_;
+ unsigned int bitmapBitOrder_;
+ unsigned int scanlineUnit_;
+ unsigned int scanlinePad_;
+
+ int firstRequest_;
+ int firstReply_;
+
+ //
+ // Use this class for IO operations.
+ //
+
+ Transport *transport_;
+
+ //
+ // The static compressor is created by the
+ // proxy and shared among channels.
+ //
+
+ StaticCompressor *compressor_;
+
+ //
+ // Map NX operations to opcodes. Propagated
+ // by proxy to all channels on the same X
+ // server.
+ //
+
+ OpcodeStore *opcodeStore_;
+
+ //
+ // Also stores are shared between channels.
+ //
+
+ ClientStore *clientStore_;
+ ServerStore *serverStore_;
+
+ //
+ // Caches are specific for each channel.
+ //
+
+ ClientCache *clientCache_;
+ ServerCache *serverCache_;
+
+ //
+ // Data going to X connection.
+ //
+
+ WriteBuffer writeBuffer_;
+
+ //
+ // Other data members.
+ //
+
+ int fd_;
+
+ int finish_;
+ int closing_;
+ int drop_;
+ int congestion_;
+ int priority_;
+
+ int alert_;
+
+ //
+ // It will be set to the descriptor of the
+ // first X channel that is successfully con-
+ // nected and will print an info message on
+ // standard error.
+ //
+
+ static int firstClient_;
+
+ //
+ // Port used for font server connections.
+ //
+
+ static int fontPort_;
+
+ //
+ // Track which cache operations have been
+ // enabled by the agent.
+ //
+
+ int enableCache_;
+ int enableSplit_;
+ int enableSave_;
+ int enableLoad_;
+
+ //
+ // Keep track of object creation and
+ // deletion.
+ //
+
+ #ifdef REFERENCES
+
+ static int references_;
+
+ #endif
+};
+
+#endif /* Channel_H */
diff --git a/nxcomp/ChannelCache.cpp b/nxcomp/ChannelCache.cpp
new file mode 100644
index 000000000..eaf8e426b
--- /dev/null
+++ b/nxcomp/ChannelCache.cpp
@@ -0,0 +1,56 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "ChannelCache.h"
+
+const unsigned int CONFIGUREWINDOW_FIELD_WIDTH[7] =
+{
+ 16, // x
+ 16, // y
+ 16, // width
+ 16, // height
+ 16, // border width
+ 29, // sibling window
+ 3 // stack mode
+};
+
+const unsigned int CREATEGC_FIELD_WIDTH[23] =
+{
+ 4, // function
+ 32, // plane mask
+ 32, // foreground
+ 32, // background
+ 16, // line width
+ 2, // line style
+ 2, // cap style
+ 2, // join style
+ 2, // fill style
+ 1, // fill rule
+ 29, // tile
+ 29, // stipple
+ 16, // tile/stipple x origin
+ 16, // tile/stipple y origin
+ 29, // font
+ 1, // subwindow mode
+ 1, // graphics exposures
+ 16, // clip x origin
+ 16, // clip y origin
+ 29, // clip mask
+ 16, // card offset
+ 8, // dashes
+ 1 // arc mode
+};
diff --git a/nxcomp/ChannelCache.h b/nxcomp/ChannelCache.h
new file mode 100644
index 000000000..7b94893b6
--- /dev/null
+++ b/nxcomp/ChannelCache.h
@@ -0,0 +1,60 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef ChannelCache_H
+#define ChannelCache_H
+
+//
+// Elements in array of caches used in TextCompressor.
+//
+
+const unsigned int CLIENT_TEXT_CACHE_SIZE = 9999;
+const unsigned int SERVER_TEXT_CACHE_SIZE = 9999;
+
+//
+// Sizes of optional fields for ConfigureWindow
+// request.
+//
+
+extern const unsigned int CONFIGUREWINDOW_FIELD_WIDTH[7];
+
+//
+// Sizes of optional fields for CreateGC request.
+//
+
+extern const unsigned int CREATEGC_FIELD_WIDTH[23];
+
+//
+// This is just needed to provide a pointer
+// to the base cache class in encoding and
+// decoding procedures of message stores.
+//
+
+class ChannelCache
+{
+ public:
+
+ ChannelCache()
+ {
+ }
+
+ ~ChannelCache()
+ {
+ }
+};
+
+#endif /* ChannelCache_H */
diff --git a/nxcomp/ChannelStore.h b/nxcomp/ChannelStore.h
new file mode 100644
index 000000000..3103fb308
--- /dev/null
+++ b/nxcomp/ChannelStore.h
@@ -0,0 +1,46 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef ChannelStore_H
+#define ChannelStore_H
+
+//
+// One message store for each opcode.
+//
+
+#define CHANNEL_STORE_OPCODE_LIMIT 256
+
+//
+// One split store for each resource.
+//
+
+#define CHANNEL_STORE_RESOURCE_LIMIT 256
+
+class ChannelStore
+{
+ public:
+
+ ChannelStore()
+ {
+ }
+
+ virtual ~ChannelStore()
+ {
+ }
+};
+
+#endif /* ChannelStore_H */
diff --git a/nxcomp/CharCache.cpp b/nxcomp/CharCache.cpp
new file mode 100644
index 000000000..2803a1b1d
--- /dev/null
+++ b/nxcomp/CharCache.cpp
@@ -0,0 +1,61 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "CharCache.h"
+
+int CharCache::lookup(unsigned char value, unsigned int &index)
+{
+ for (unsigned int i = 0; i < length_; i++)
+ if (value == buffer_[i])
+ {
+ index = i;
+ if (i)
+ {
+ unsigned int target = (i >> 1);
+ do
+ {
+ buffer_[i] = buffer_[i - 1];
+ i--;
+ }
+ while (i > target);
+ buffer_[target] = value;
+ }
+ return 1;
+ }
+ insert(value);
+ return 0;
+}
+
+void CharCache::insert(unsigned char value)
+{
+ unsigned int insertionPoint = 0;
+ if (2 >= length_)
+ insertionPoint = length_;
+ else
+ insertionPoint = 2;
+ unsigned int start;
+ if (length_ >= 7)
+ start = 7 - 1;
+ else
+ {
+ start = length_;
+ length_++;
+ }
+ for (unsigned int k = start; k > insertionPoint; k--)
+ buffer_[k] = buffer_[k - 1];
+ buffer_[insertionPoint] = value;
+}
diff --git a/nxcomp/CharCache.h b/nxcomp/CharCache.h
new file mode 100644
index 000000000..53710f181
--- /dev/null
+++ b/nxcomp/CharCache.h
@@ -0,0 +1,83 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef CharCache_H
+#define CharCache_H
+
+//
+// CharCache is a counterpart of IntCache that is
+// optimized for use in compressing text composed
+// of 8-bit characters.
+//
+
+class CharCache
+{
+ public:
+
+ CharCache() : length_(0)
+ {
+ }
+
+ ~CharCache()
+ {
+ }
+
+ unsigned int getSize() const
+ {
+ return (unsigned int) length_;
+ }
+
+ int lookup(unsigned char value, unsigned int &index);
+
+ //
+ // This can be inlined as it is only
+ // called by decodeCachedValue().
+ //
+
+ unsigned int get(unsigned int index)
+ {
+ unsigned char result = buffer_[index];
+
+ if (index != 0)
+ {
+ unsigned int i = index;
+ unsigned int target = (i >> 1);
+
+ do
+ {
+ buffer_[i] = buffer_[i - 1];
+
+ i--;
+ }
+ while (i > target);
+
+ buffer_[target] = result;
+ }
+
+ return (unsigned int) result;
+ }
+
+ void insert(unsigned char value);
+
+ private:
+
+ unsigned char length_;
+
+ unsigned char buffer_[7];
+};
+
+#endif /* CharCache_H */
diff --git a/nxcomp/Children.cpp b/nxcomp/Children.cpp
new file mode 100644
index 000000000..a19b882e8
--- /dev/null
+++ b/nxcomp/Children.cpp
@@ -0,0 +1,1038 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <fcntl.h>
+
+#include "NX.h"
+
+#include "Misc.h"
+
+#include "Types.h"
+#include "Timestamp.h"
+
+#include "Control.h"
+#include "Statistics.h"
+#include "Proxy.h"
+
+#include "Keeper.h"
+#include "Fork.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#define DISPLAY_LENGTH_LIMIT 256
+#define DEFAULT_STRING_LIMIT 512
+
+//
+// These are from the main loop.
+//
+
+extern Keeper *keeper;
+
+extern int (*handler)(int);
+
+extern int useUnixSocket;
+
+extern int lastDialog;
+extern int lastWatchdog;
+extern int lastKeeper;
+
+extern void CleanupListeners();
+extern void CleanupSockets();
+extern void CleanupAgent();
+extern void CleanupGlobal();
+
+extern void InstallSignals();
+
+extern char *GetClientPath();
+
+extern int CheckParent(const char *name, const char *type,
+ int parent);
+
+#ifdef __sun
+extern char **environ;
+#endif
+
+//
+// Close all the unused descriptors and
+// install any signal handler that might
+// have been disabled in the main process.
+//
+
+static void SystemCleanup(const char *name);
+
+//
+// Release all objects allocated in the
+// heap.
+
+static void MemoryCleanup(const char *name);
+
+//
+// Remove 'name' from the environment.
+//
+
+static int UnsetEnv(const char *name);
+
+static int NXTransKeeperHandler(int signal);
+static void NXTransKeeperCheck();
+
+
+//
+// Start a nxclient process in dialog mode.
+//
+
+int NXTransDialog(const char *caption, const char *message,
+ const char *window, const char *type, int local,
+ const char* display)
+{
+ //
+ // Be sure log file is valid.
+ //
+
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ int pid;
+
+ #ifdef TEST
+ *logofs << "NXTransDialog: Going to fork with NX pid '"
+ << getpid() << "'.\n" << logofs_flush;
+ #endif
+
+ pid = Fork();
+
+ if (pid != 0)
+ {
+ if (pid < 0)
+ {
+ #ifdef TEST
+ *logofs << "NXTransDialog: WARNING! Function fork failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Function fork failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "NXTransDialog: Created NX dialog process "
+ << "with pid '" << pid << "'.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ return pid;
+ }
+
+ #ifdef TEST
+ *logofs << "NXTransDialog: Executing child with pid '"
+ << getpid() << "' and parent '" << getppid()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ SystemCleanup("NXTransDialog");
+
+ //
+ // Copy the client command before
+ // freeing up the control class.
+ //
+
+ char command[DEFAULT_STRING_LIMIT];
+
+ if (control != NULL)
+ {
+ strcpy(command, control -> ClientPath);
+ }
+ else
+ {
+ char *path = GetClientPath();
+
+ strcpy(command, path);
+
+ delete [] path;
+ }
+
+ //
+ // Get rid of the unused resources.
+ //
+
+ MemoryCleanup("NXTransDialog");
+
+ #ifdef TEST
+ *logofs << "NXTransDialog: Running external NX dialog with caption '"
+ << caption << "' message '" << message << "' type '"
+ << type << "' local '" << local << "' display '"
+ << display << "'.\n"
+ << logofs_flush;
+ #endif
+
+ int pulldown = (strcmp(type, "pulldown") == 0);
+
+ char parent[DEFAULT_STRING_LIMIT];
+
+ snprintf(parent, DEFAULT_STRING_LIMIT, "%d", getppid());
+
+ parent[DEFAULT_STRING_LIMIT - 1] = '\0';
+
+ UnsetEnv("LD_LIBRARY_PATH");
+
+ for (int i = 0; i < 2; i++)
+ {
+ if (local != 0)
+ {
+ if (pulldown)
+ {
+ execlp(command, command, "--dialog", type, "--caption", caption,
+ "--window", window, "--local", "--parent", parent,
+ "--display", display, NULL);
+ }
+ else
+ {
+ execlp(command, command, "--dialog", type, "--caption", caption,
+ "--message", message, "--local", "--parent", parent,
+ "--display", display, NULL);
+ }
+ }
+ else
+ {
+ if (pulldown)
+ {
+ execlp(command, command, "--dialog", type, "--caption", caption,
+ "--window", window, "--parent", parent,
+ "--display", display, NULL);
+ }
+ else
+ {
+ execlp(command, command, "--dialog", type, "--caption", caption,
+ "--message", message, "--parent", parent,
+ "--display", display, NULL);
+ }
+ }
+
+ #ifdef WARNING
+ *logofs << "NXTransDialog: WARNING! Couldn't start '"
+ << command << "'. " << "Error is " << EGET()
+ << " '" << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Couldn't start '" << command
+ << "'. Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+
+ //
+ // Retry by looking for the default name
+ // in the default NX path.
+ //
+
+ strcpy(command, "nxclient");
+
+ char newPath[DEFAULT_STRING_LIMIT];
+
+ strcpy(newPath, "/usr/NX/bin:/opt/NX/bin:/usr/local/NX/bin:");
+
+ #ifdef __APPLE__
+
+ strcat(newPath, "/Applications/NX Client for OSX.app/Contents/MacOS:");
+
+ #endif
+
+ #ifdef __CYGWIN32__
+
+ strcat(newPath, ".:");
+
+ #endif
+
+ int newLength = strlen(newPath);
+
+ char *oldPath = getenv("PATH");
+
+ strncpy(newPath + newLength, oldPath, DEFAULT_STRING_LIMIT - newLength - 1);
+
+ newPath[DEFAULT_STRING_LIMIT - 1] = '\0';
+
+ #ifdef WARNING
+ *logofs << "NXTransDialog: WARNING! Trying with path '"
+ << newPath << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Trying with path '" << newPath
+ << "'.\n";
+
+ //
+ // Solaris doesn't seem to have
+ // function setenv().
+ //
+
+ #ifdef __sun
+
+ char newEnv[DEFAULT_STRING_LIMIT + 5];
+
+ sprintf(newEnv,"PATH=%s", newPath);
+
+ putenv(newEnv);
+
+ #else
+
+ setenv("PATH", newPath, 1);
+
+ #endif
+ }
+
+ //
+ // Hopefully useless.
+ //
+
+ exit(0);
+}
+
+//
+// Start a nxclient process in dialog mode.
+//
+
+int NXTransClient(const char* display)
+{
+ //
+ // Be sure log file is valid.
+ //
+
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ int pid;
+
+ #ifdef TEST
+ *logofs << "NXTransClient: Going to fork with NX pid '"
+ << getpid() << "'.\n" << logofs_flush;
+ #endif
+
+ pid = Fork();
+
+ if (pid != 0)
+ {
+ if (pid < 0)
+ {
+ #ifdef TEST
+ *logofs << "NXTransClient: WARNING! Function fork failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Function fork failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "NXTransClient: Created NX client process "
+ << "with pid '" << pid << "'.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ return pid;
+ }
+
+ #ifdef TEST
+ *logofs << "NXTransClient: Executing child with pid '"
+ << getpid() << "' and parent '" << getppid()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ SystemCleanup("NXTransClient");
+
+ //
+ // Copy the client command before
+ // freeing up the control class.
+ //
+
+ char command[DEFAULT_STRING_LIMIT];
+
+ if (control != NULL)
+ {
+ strcpy(command, control -> ClientPath);
+ }
+ else
+ {
+ char *path = GetClientPath();
+
+ strcpy(command, path);
+
+ delete [] path;
+ }
+
+ //
+ // Get rid of unused resources.
+ //
+
+ MemoryCleanup("NXTransClient");
+
+ #ifdef TEST
+ *logofs << "NXTransClient: Running external NX client with display '"
+ << display << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // Provide the display in the environment.
+ //
+
+ char newDisplay[DISPLAY_LENGTH_LIMIT];
+
+ #ifdef __sun
+
+ snprintf(newDisplay, DISPLAY_LENGTH_LIMIT - 1, "DISPLAY=%s", display);
+
+ newDisplay[DISPLAY_LENGTH_LIMIT - 1] = '\0';
+
+ putenv(newDisplay);
+
+ #else
+
+ strncpy(newDisplay, display, DISPLAY_LENGTH_LIMIT - 1);
+
+ newDisplay[DISPLAY_LENGTH_LIMIT - 1] = '\0';
+
+ setenv("DISPLAY", newDisplay, 1);
+
+ #endif
+
+ UnsetEnv("LD_LIBRARY_PATH");
+
+ for (int i = 0; i < 2; i++)
+ {
+ execlp(command, command, NULL);
+
+ #ifdef WARNING
+ *logofs << "NXTransClient: WARNING! Couldn't start '"
+ << command << "'. Error is " << EGET() << " '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Couldn't start '" << command
+ << "'. Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+
+ //
+ // Retry by looking for the default name
+ // in the default NX path.
+ //
+
+ strcpy(command, "nxclient");
+
+ char newPath[DEFAULT_STRING_LIMIT];
+
+ strcpy(newPath, "/usr/NX/bin:/opt/NX/bin:/usr/local/NX/bin:");
+
+ #ifdef __APPLE__
+
+ strcat(newPath, "/Applications/NX Client for OSX.app/Contents/MacOS:");
+
+ #endif
+
+ #ifdef __CYGWIN32__
+
+ strcat(newPath, ".:");
+
+ #endif
+
+ int newLength = strlen(newPath);
+
+ char *oldPath = getenv("PATH");
+
+ strncpy(newPath + newLength, oldPath, DEFAULT_STRING_LIMIT - newLength - 1);
+
+ newPath[DEFAULT_STRING_LIMIT - 1] = '\0';
+
+ #ifdef WARNING
+ *logofs << "NXTransClient: WARNING! Trying with path '"
+ << newPath << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Trying with path '" << newPath
+ << "'.\n";
+
+ //
+ // Solaris doesn't seem to have
+ // function setenv().
+ //
+
+ #ifdef __sun
+
+ char newEnv[DEFAULT_STRING_LIMIT + 5];
+
+ sprintf(newEnv,"PATH=%s", newPath);
+
+ putenv(newEnv);
+
+ #else
+
+ setenv("PATH", newPath, 1);
+
+ #endif
+ }
+
+ //
+ // Hopefully useless.
+ //
+
+ exit(0);
+}
+
+//
+// Wait until the timeout is expired.
+// The timeout is expressed in milli-
+// seconds.
+//
+
+int NXTransWatchdog(int timeout)
+{
+ //
+ // Be sure log file is valid.
+ //
+
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ int pid;
+
+ #ifdef TEST
+ *logofs << "NXTransWatchdog: Going to fork with NX pid '"
+ << getpid() << "'.\n" << logofs_flush;
+ #endif
+
+ pid = Fork();
+
+ if (pid != 0)
+ {
+ if (pid < 0)
+ {
+ #ifdef TEST
+ *logofs << "NXTransWatchdog: WARNING! Function fork failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Function fork failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "NXTransWatchdog: Created NX watchdog process "
+ << "with pid '" << pid << "'.\n" << logofs_flush;
+ }
+ #endif
+
+ return pid;
+ }
+
+ int parent = getppid();
+
+ #ifdef TEST
+ *logofs << "NXTransWatchdog: Executing child with pid '"
+ << getpid() << "' and parent '" << parent
+ << "'.\n" << logofs_flush;
+ #endif
+
+ SystemCleanup("NXTransWatchdog");
+
+ //
+ // Get rid of unused resources.
+ //
+
+ MemoryCleanup("NXTransWatchdog");
+
+ //
+ // Run until the timeout is expired
+ // or forever, if no timeout is
+ // provided.
+ //
+
+ T_timestamp startTs = getTimestamp();
+
+ int diffTs = 0;
+
+ for (;;)
+ {
+ //
+ // Complain if the parent is dead.
+ //
+
+ if (CheckParent("NXTransWatchdog", "watchdog", parent) == 0)
+ {
+ #ifdef TEST
+ *logofs << "NXTransWatchdog: Exiting with no parent "
+ << "running.\n" << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+
+ if (timeout > 0)
+ {
+ if (diffTs >= timeout)
+ {
+ #ifdef TEST
+ *logofs << "NXTransWatchdog: Timeout of " << timeout
+ << " Ms raised in watchdog.\n" << logofs_flush;
+ #endif
+
+ //
+ // We will just exit. Our parent should be
+ // monitoring us and detect that the process
+ // is gone.
+ //
+
+ HandleCleanup();
+ }
+ }
+
+ if (timeout > 0)
+ {
+ #ifdef TEST
+ *logofs << "NXTransWatchdog: Waiting for the timeout "
+ << "with " << timeout - diffTs << " Ms to run.\n"
+ << logofs_flush;
+ #endif
+
+ usleep((timeout - diffTs) * 1000);
+
+ diffTs = diffTimestamp(startTs, getNewTimestamp());
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "NXTransWatchdog: Waiting for a signal.\n"
+ << logofs_flush;
+ #endif
+
+ sleep(10);
+ }
+ }
+
+ //
+ // Hopefully useless.
+ //
+
+ exit(0);
+}
+
+int NXTransKeeperHandler(int signal)
+{
+ if (keeper != NULL)
+ {
+ switch (signal)
+ {
+ case SIGTERM:
+ case SIGINT:
+ case SIGHUP:
+ {
+ #ifdef TEST
+ *logofs << "NXTransKeeperHandler: Requesting giveup "
+ << "because of signal " << signal << " ,'"
+ << DumpSignal(signal) << "'.\n"
+ << logofs_flush;
+ #endif
+
+ keeper -> setSignal(signal);
+
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+void NXTransKeeperCheck()
+{
+ if (CheckParent("NXTransKeeper", "keeper",
+ keeper -> getParent()) == 0 || keeper -> getSignal() != 0)
+ {
+ #ifdef TEST
+ *logofs << "NXTransKeeperCheck: Exiting because of signal "
+ << "or no parent running.\n" << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+}
+
+int NXTransKeeper(int caches, int images, const char *root)
+{
+ //
+ // Be sure log file is valid.
+ //
+
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ if (caches == 0 && images == 0)
+ {
+ #ifdef TEST
+ *logofs << "NXTransKeeper: No NX cache house-keeping needed.\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ int pid;
+
+ #ifdef TEST
+ *logofs << "NXTransKeeper: Going to fork with NX pid '"
+ << getpid() << "'.\n" << logofs_flush;
+ #endif
+
+ pid = Fork();
+
+ if (pid != 0)
+ {
+ if (pid < 0)
+ {
+ #ifdef TEST
+ *logofs << "NXTransKeeper: WARNING! Function fork failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Function fork failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "NXTransKeeper: Created NX keeper process "
+ << "with pid '" << pid << "'.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ return pid;
+ }
+
+ int parent = getppid();
+
+ #ifdef TEST
+ *logofs << "NXTransKeeper: Executing child with pid '"
+ << getpid() << "' and parent '" << parent
+ << "'.\n" << logofs_flush;
+ #endif
+
+ SystemCleanup("NXTransKeeper");
+
+ #ifdef TEST
+ *logofs << "NXTransKeeper: Going to run with caches " << caches
+ << " images " << images << " and root " << root
+ << " at " << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Create the house-keeper class.
+ //
+
+ int timeout = control -> KeeperTimeout;
+
+ keeper = new Keeper(caches, images, root, 100, parent);
+
+ handler = NXTransKeeperHandler;
+
+ if (keeper == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "NXTransKeeper: PANIC! Failed to create the keeper object.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to create the keeper object.\n";
+
+ HandleCleanup();
+ }
+
+ //
+ // Get rid of unused resources. Root path
+ // must be copied in keeper's constructor
+ // before control is deleted.
+ //
+
+ MemoryCleanup("NXTransKeeper");
+
+ //
+ // Decrease the priority of this process.
+ //
+ // The following applies to Cygwin: "Cygwin processes can be
+ // set to IDLE_PRIORITY_CLASS, NORMAL_PRIORITY_CLASS, HIGH_-
+ // PRIORITY_CLASS, or REALTIME_PRIORITY_CLASS with the nice
+ // call. If you pass a positive number to nice(), then the
+ // priority level will decrease by one (within the above list
+ // of priorities). A negative number would make it increase
+ // by one. It is not possible to change it by more than one
+ // at a time without making repeated calls".
+ //
+
+ if (nice(5) < 0 && errno != 0)
+ {
+ #ifdef WARNING
+ *logofs << "NXTransKeeper: WARNING! Failed to renice process to +5. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Failed to renice process to +5. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+ }
+
+ //
+ // Delay a bit the first run to give
+ // a boost to the session startup.
+ //
+
+ #ifdef TEST
+ *logofs << "NXTransKeeper: Going to sleep for "
+ << timeout / 20 << " Ms.\n" << logofs_flush;
+ #endif
+
+ usleep(timeout / 20 * 1000);
+
+ NXTransKeeperCheck();
+
+ //
+ // The house keeping of the persistent
+ // caches is performed only once.
+ //
+
+ if (caches != 0)
+ {
+ #ifdef TEST
+ *logofs << "NXTransKeeper: Going to cleanup the NX cache "
+ << "directories at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ keeper -> cleanupCaches();
+
+ #ifdef TEST
+ *logofs << "NXTransKeeper: Completed cleanup of NX cache "
+ << "directories at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "NXTransKeeper: Nothing to do for the "
+ << "persistent caches.\n" << logofs_flush;
+ }
+ #endif
+
+ if (images == 0)
+ {
+ #ifdef TEST
+ *logofs << "NXTransKeeper: Nothing to do for the "
+ << "persistent images.\n" << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+
+ //
+ // Take care of the persisten image cache.
+ // Run a number of iterations and then exit,
+ // so we can keep the memory consumption
+ // low. The parent will check our exit code
+ // and will eventually restart us.
+ //
+
+ for (int iterations = 0; iterations < 100; iterations++)
+ {
+ #ifdef TEST
+ *logofs << "NXTransKeeper: Running iteration " << iterations
+ << " at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ NXTransKeeperCheck();
+
+ #ifdef TEST
+ *logofs << "NXTransKeeper: Going to cleanup the NX images "
+ << "directories at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (keeper -> cleanupImages() < 0)
+ {
+ #ifdef TEST
+ *logofs << "NXTransKeeper: Exiting because of error "
+ << "handling the image cache.\n" << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+
+ #ifdef TEST
+ *logofs << "NXTransKeeper: Completed cleanup of NX images "
+ << "directories at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ NXTransKeeperCheck();
+
+ #ifdef TEST
+ *logofs << "NXTransKeeper: Going to sleep for " << timeout
+ << " Ms.\n" << logofs_flush;
+ #endif
+
+ usleep(timeout * 1000);
+ }
+
+ HandleCleanup(2);
+
+ //
+ // Hopefully useless.
+ //
+
+ exit(0);
+}
+
+void SystemCleanup(const char *name)
+{
+ #ifdef TEST
+ *logofs << name << ": Performing system cleanup in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Reinstall signals that might
+ // have been restored by agents.
+ //
+
+ InstallSignals();
+}
+
+void MemoryCleanup(const char *name)
+{
+ #ifdef TEST
+ *logofs << name << ": Performing memory cleanup in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ DisableSignals();
+
+ //
+ // Prevent deletion of unix socket
+ // and lock file.
+ //
+
+ useUnixSocket = 0;
+
+ //
+ // Don't let cleanup kill other
+ // children.
+ //
+
+ lastDialog = 0;
+ lastWatchdog = 0;
+ lastKeeper = 0;
+
+ CleanupListeners();
+
+ CleanupSockets();
+
+ CleanupGlobal();
+
+ EnableSignals();
+}
+
+int UnsetEnv(const char *name)
+{
+ int result;
+
+ #ifdef __sun
+
+ char **pEnv = environ;
+
+ int nameLen = strlen(name) + 1;
+
+ char *varName = new char[nameLen + 1];
+
+ strcpy(varName, name);
+
+ strcat(varName, "=");
+
+ pEnv = environ;
+
+ while (*pEnv != NULL)
+ {
+ if (!strncmp(varName, *pEnv, nameLen))
+ {
+ break;
+ }
+
+ *pEnv++;
+ }
+
+ while (*pEnv != NULL)
+ {
+ *pEnv = *(pEnv + 1);
+
+ pEnv++;
+ }
+
+ result = 0;
+
+ #else
+
+ #ifdef __APPLE__
+
+ unsetenv(name);
+ result = 0;
+
+ #else
+
+ result = unsetenv(name);
+
+ #endif
+
+ #endif
+
+ return result;
+}
diff --git a/nxcomp/ClearArea.cpp b/nxcomp/ClearArea.cpp
new file mode 100644
index 000000000..223a3b3e1
--- /dev/null
+++ b/nxcomp/ClearArea.cpp
@@ -0,0 +1,113 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "ClearArea.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int ClearAreaStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ClearAreaMessage *clearArea = (ClearAreaMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ clearArea -> exposures = *(buffer + 1);
+
+ clearArea -> window = GetULONG(buffer + 4, bigEndian);
+
+ clearArea -> x = GetUINT(buffer + 8, bigEndian);
+ clearArea -> y = GetUINT(buffer + 10, bigEndian);
+ clearArea -> width = GetUINT(buffer + 12, bigEndian);
+ clearArea -> height = GetUINT(buffer + 14, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int ClearAreaStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ClearAreaMessage *clearArea = (ClearAreaMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ *(buffer + 1) = clearArea -> exposures;
+
+ PutULONG(clearArea -> window, buffer + 4, bigEndian);
+
+ PutUINT(clearArea -> x, buffer + 8, bigEndian);
+ PutUINT(clearArea -> y, buffer + 10, bigEndian);
+ PutUINT(clearArea -> width, buffer + 12, bigEndian);
+ PutUINT(clearArea -> height, buffer + 14, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void ClearAreaStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ ClearAreaMessage *clearArea = (ClearAreaMessage *) message;
+
+ *logofs << name() << ": Identity exposures " << clearArea -> (unsigned int) exposures
+ << ", window " << clearArea -> window << ", x " << clearArea -> x
+ << ", y " << clearArea -> y << ", width " << clearArea -> width
+ << ", height " << clearArea -> height << ", size " << clearArea -> size_
+ << ".\n";
+
+ #endif
+}
+
+void ClearAreaStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ md5_append(md5_state_, buffer + 1, 1);
+ md5_append(md5_state_, buffer + 4, 4);
+ md5_append(md5_state_, buffer + 8, 2);
+ md5_append(md5_state_, buffer + 10, 2);
+ md5_append(md5_state_, buffer + 12, 2);
+ md5_append(md5_state_, buffer + 14, 2);
+}
diff --git a/nxcomp/ClearArea.h b/nxcomp/ClearArea.h
new file mode 100644
index 000000000..039eea44e
--- /dev/null
+++ b/nxcomp/ClearArea.h
@@ -0,0 +1,174 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef ClearArea_H
+#define ClearArea_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define CLEARAREA_ENABLE_CACHE 1
+#define CLEARAREA_ENABLE_DATA 0
+#define CLEARAREA_ENABLE_SPLIT 0
+#define CLEARAREA_ENABLE_COMPRESS 0
+
+#define CLEARAREA_DATA_LIMIT 0
+#define CLEARAREA_DATA_OFFSET 16
+
+#define CLEARAREA_CACHE_SLOTS 3000
+#define CLEARAREA_CACHE_THRESHOLD 5
+#define CLEARAREA_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class ClearAreaMessage : public Message
+{
+ friend class ClearAreaStore;
+
+ public:
+
+ ClearAreaMessage()
+ {
+ }
+
+ ~ClearAreaMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char exposures;
+ unsigned int window;
+ unsigned short x;
+ unsigned short y;
+ unsigned short width;
+ unsigned short height;
+};
+
+class ClearAreaStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ ClearAreaStore() : MessageStore()
+ {
+ enableCache = CLEARAREA_ENABLE_CACHE;
+ enableData = CLEARAREA_ENABLE_DATA;
+ enableSplit = CLEARAREA_ENABLE_SPLIT;
+ enableCompress = CLEARAREA_ENABLE_COMPRESS;
+
+ dataLimit = CLEARAREA_DATA_LIMIT;
+ dataOffset = CLEARAREA_DATA_OFFSET;
+
+ cacheSlots = CLEARAREA_CACHE_SLOTS;
+ cacheThreshold = CLEARAREA_CACHE_THRESHOLD;
+ cacheLowerThreshold = CLEARAREA_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~ClearAreaStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "ClearArea";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_ClearArea;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(ClearAreaMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new ClearAreaMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new ClearAreaMessage((const ClearAreaMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (ClearAreaMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* ClearArea_H */
diff --git a/nxcomp/ClientCache.cpp b/nxcomp/ClientCache.cpp
new file mode 100644
index 000000000..2a82009a3
--- /dev/null
+++ b/nxcomp/ClientCache.cpp
@@ -0,0 +1,389 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "ClientCache.h"
+
+ClientCache::ClientCache() :
+
+ freeGCCache(16), freeDrawableCache(16), freeWindowCache(16),
+
+ cursorCache(16), colormapCache(16), visualCache(16), lastFont(0),
+
+ changePropertyPropertyCache(16), changePropertyTypeCache(16),
+ changePropertyData32Cache(16),
+ changePropertyTextCompressor(textCache, CLIENT_TEXT_CACHE_SIZE),
+
+ configureWindowBitmaskCache(4),
+
+ convertSelectionRequestorCache(16),
+ convertSelectionLastTimestamp(0),
+
+ copyPlaneBitPlaneCache(8),
+
+ createGCBitmaskCache(8),
+
+ createPixmapIdCache(16), createPixmapLastId(0),
+ createPixmapXCache(8), createPixmapYCache(8),
+
+ createWindowBitmaskCache(8),
+
+ fillPolyNumPointsCache(8), fillPolyIndex(0),
+
+ getSelectionOwnerSelectionCache(8),
+
+ grabButtonEventMaskCache(8), grabButtonConfineCache(8),
+ grabButtonModifierCache(8),
+
+ grabKeyboardLastTimestamp(0),
+
+ imageTextLengthCache(8),
+ imageTextLastX(0), imageTextLastY(0),
+ imageTextCacheX(8), imageTextCacheY(8),
+ imageTextTextCompressor(textCache, CLIENT_TEXT_CACHE_SIZE),
+
+ internAtomTextCompressor(textCache, CLIENT_TEXT_CACHE_SIZE),
+
+ openFontTextCompressor(textCache, CLIENT_TEXT_CACHE_SIZE),
+
+ polySegmentCacheX(8), polySegmentCacheY(8), polySegmentCacheIndex(0),
+
+ polyTextLastX(0), polyTextLastY(0), polyTextCacheX(8),
+ polyTextCacheY(8), polyTextFontCache(8),
+ polyTextTextCompressor(textCache, CLIENT_TEXT_CACHE_SIZE),
+
+ putImageWidthCache(8), putImageHeightCache(8), putImageLastX(0),
+ putImageLastY(0), putImageXCache(8), putImageYCache(8),
+
+ getImagePlaneMaskCache(8),
+
+ queryColorsLastPixel(0),
+
+ setClipRectanglesXCache(8), setClipRectanglesYCache(8),
+
+ setDashesLengthCache(8), setDashesOffsetCache(8),
+
+ setSelectionOwnerCache(8), setSelectionOwnerTimestampCache(8),
+
+ translateCoordsSrcCache(8), translateCoordsDstCache(8),
+ translateCoordsXCache(8), translateCoordsYCache(8),
+
+ sendEventMaskCache(16), sendEventLastSequence(0),
+ sendEventIntDataCache(16),
+
+ putPackedImageSrcLengthCache(16), putPackedImageDstLengthCache(16),
+
+ //
+ // RenderExtension requests.
+ //
+
+ renderFreePictureCache(16),
+
+ renderGlyphSetCache(16),
+ renderFreeGlyphSetCache(16),
+
+ renderIdCache(8),
+
+ renderLengthCache(16), renderFormatCache(16),
+ renderValueMaskCache(8), renderNumGlyphsCache(8),
+
+ renderXCache(16), renderYCache(16),
+ renderLastX(0), renderLastY(0),
+
+ renderWidthCache(16), renderHeightCache(16),
+
+ renderLastId(0),
+
+ renderTextCompressor(textCache, CLIENT_TEXT_CACHE_SIZE),
+
+ renderGlyphXCache(16), renderGlyphYCache(16),
+ renderGlyphX(0), renderGlyphY(0),
+
+ renderLastCompositeGlyphsData(0),
+
+ setCacheParametersCache(8),
+
+ lastIdCache(16), lastId(0)
+
+{
+ unsigned int i;
+
+ for (i = 0; i < 3; i++)
+ {
+ allocColorRGBCache[i] = new IntCache(8);
+
+ convertSelectionAtomCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ clearAreaGeomCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 7; i++)
+ {
+ configureWindowAttrCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 6; i++)
+ {
+ copyAreaGeomCache[i] = new IntCache(8);
+ copyPlaneGeomCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 23; i++)
+ {
+ if (CREATEGC_FIELD_WIDTH[i] > 16)
+ {
+ createGCAttrCache[i] = new IntCache(16);
+ }
+ else
+ {
+ createGCAttrCache[i] = new IntCache(CREATEGC_FIELD_WIDTH[i]);
+ }
+ }
+
+ for (i = 0; i < 6; i++)
+ {
+ createWindowGeomCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 15; i++)
+ {
+ createWindowAttrCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 10; i++)
+ {
+ fillPolyXRelCache[i] = new IntCache(8);
+ fillPolyXAbsCache[i] = new IntCache(8);
+ fillPolyYRelCache[i] = new IntCache(8);
+ fillPolyYAbsCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 8; i++)
+ {
+ fillPolyRecentX[i] = 0;
+ fillPolyRecentY[i] = 0;
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ polyFillRectangleCacheX[i] = new IntCache(8);
+ polyFillRectangleCacheY[i] = new IntCache(8);
+ polyFillRectangleCacheWidth[i] = new IntCache(8);
+ polyFillRectangleCacheHeight[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ polyLineCacheX[i] = new IntCache(8);
+ polyLineCacheY[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ polyPointCacheX[i] = new IntCache(8);
+ polyPointCacheY[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ polyRectangleGeomCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ polySegmentLastX[i] = 0;
+ polySegmentLastY[i] = 0;
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ setClipRectanglesGeomCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ polyFillArcCacheX[i] = new IntCache(8);
+ polyFillArcCacheY[i] = new IntCache(8);
+ polyFillArcCacheWidth[i] = new IntCache(8);
+ polyFillArcCacheHeight[i] = new IntCache(8);
+ polyFillArcCacheAngle1[i] = new IntCache(8);
+ polyFillArcCacheAngle2[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ polyArcCacheX[i] = new IntCache(8);
+ polyArcCacheY[i] = new IntCache(8);
+ polyArcCacheWidth[i] = new IntCache(8);
+ polyArcCacheHeight[i] = new IntCache(8);
+ polyArcCacheAngle1[i] = new IntCache(8);
+ polyArcCacheAngle2[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 8; i++)
+ {
+ shapeDataCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 8; i++)
+ {
+ genericRequestDataCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 16; i++)
+ {
+ renderDataCache[i] = new IntCache(16);
+ }
+
+ for (i = 0; i < 16; i++)
+ {
+ renderCompositeGlyphsDataCache[i] = new IntCache(16);
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+ renderCompositeDataCache[i] = new IntCache(16);
+ }
+}
+
+
+ClientCache::~ClientCache()
+{
+ unsigned int i;
+
+ for (i = 0; i < 3; i++)
+ {
+ delete allocColorRGBCache[i];
+ delete convertSelectionAtomCache[i];
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ delete clearAreaGeomCache[i];
+ }
+
+ for (i = 0; i < 7; i++)
+ {
+ delete configureWindowAttrCache[i];
+ }
+
+ for (i = 0; i < 6; i++)
+ {
+ delete copyAreaGeomCache[i];
+ delete copyPlaneGeomCache[i];
+ }
+
+ for (i = 0; i < 23; i++)
+ {
+ delete createGCAttrCache[i];
+ }
+
+ for (i = 0; i < 6; i++)
+ {
+ delete createWindowGeomCache[i];
+ }
+
+ for (i = 0; i < 15; i++)
+ {
+ delete createWindowAttrCache[i];
+ }
+
+ for (i = 0; i < 10; i++)
+ {
+ delete fillPolyXRelCache[i];
+ delete fillPolyXAbsCache[i];
+ delete fillPolyYRelCache[i];
+ delete fillPolyYAbsCache[i];
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ delete polyFillRectangleCacheX[i];
+ delete polyFillRectangleCacheY[i];
+ delete polyFillRectangleCacheWidth[i];
+ delete polyFillRectangleCacheHeight[i];
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ delete polyLineCacheX[i];
+ delete polyLineCacheY[i];
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ delete polyPointCacheX[i];
+ delete polyPointCacheY[i];
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ delete polyRectangleGeomCache[i];
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ delete setClipRectanglesGeomCache[i];
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ delete polyFillArcCacheX[i];
+ delete polyFillArcCacheY[i];
+ delete polyFillArcCacheWidth[i];
+ delete polyFillArcCacheHeight[i];
+ delete polyFillArcCacheAngle1[i];
+ delete polyFillArcCacheAngle2[i];
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ delete polyArcCacheX[i];
+ delete polyArcCacheY[i];
+ delete polyArcCacheWidth[i];
+ delete polyArcCacheHeight[i];
+ delete polyArcCacheAngle1[i];
+ delete polyArcCacheAngle2[i];
+ }
+
+ for (i = 0; i < 8; i++)
+ {
+ delete shapeDataCache[i];
+ }
+
+ for (i = 0; i < 8; i++)
+ {
+ delete genericRequestDataCache[i];
+ }
+
+ for (i = 0; i < 16; i++)
+ {
+ delete renderDataCache[i];
+ }
+
+ for (i = 0; i < 16; i++)
+ {
+ delete renderCompositeGlyphsDataCache[i];
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+ delete renderCompositeDataCache[i];
+ }
+}
diff --git a/nxcomp/ClientCache.h b/nxcomp/ClientCache.h
new file mode 100644
index 000000000..6702e5b66
--- /dev/null
+++ b/nxcomp/ClientCache.h
@@ -0,0 +1,429 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef ClientCache_H
+#define ClientCache_H
+
+#include "Misc.h"
+
+#include "IntCache.h"
+#include "CharCache.h"
+#include "OpcodeCache.h"
+#include "XidCache.h"
+#include "FreeCache.h"
+
+#include "TextCompressor.h"
+
+#include "ChannelCache.h"
+
+class ClientCache : public ChannelCache
+{
+ public:
+
+ ClientCache();
+
+ ~ClientCache();
+
+ //
+ // Opcode prediction caches.
+ //
+
+ OpcodeCache opcodeCache;
+
+ //
+ // GC and drawables caches.
+ //
+
+ XidCache gcCache;
+ FreeCache freeGCCache;
+
+ XidCache drawableCache;
+ FreeCache freeDrawableCache;
+
+ XidCache windowCache;
+ FreeCache freeWindowCache;
+
+ //
+ // General-purpose caches.
+ //
+
+ CharCache textCache[CLIENT_TEXT_CACHE_SIZE];
+ IntCache cursorCache;
+ IntCache colormapCache;
+ IntCache visualCache;
+ CharCache depthCache;
+ CharCache resourceCache;
+ CharCache methodCache;
+
+ unsigned int lastFont;
+
+ //
+ // AllocColor request.
+ //
+
+ IntCache *allocColorRGBCache[3];
+
+ //
+ // ChangeProperty request.
+ //
+
+ CharCache changePropertyFormatCache;
+ IntCache changePropertyPropertyCache;
+ IntCache changePropertyTypeCache;
+ IntCache changePropertyData32Cache;
+ TextCompressor changePropertyTextCompressor;
+
+ //
+ // ClearArea request.
+ //
+
+ IntCache *clearAreaGeomCache[4];
+
+ //
+ // ConfigureWindow request.
+ //
+
+ IntCache configureWindowBitmaskCache;
+ IntCache *configureWindowAttrCache[7];
+
+ //
+ // ConvertSelection request.
+ //
+
+ IntCache convertSelectionRequestorCache;
+ IntCache* convertSelectionAtomCache[3];
+ unsigned int convertSelectionLastTimestamp;
+
+ //
+ // CopyArea request.
+ //
+
+ IntCache *copyAreaGeomCache[6];
+
+ //
+ // CopyPlane request.
+ //
+
+ IntCache *copyPlaneGeomCache[6];
+ IntCache copyPlaneBitPlaneCache;
+
+ //
+ // CreateGC request.
+ //
+
+ IntCache createGCBitmaskCache;
+ IntCache *createGCAttrCache[23];
+
+ //
+ // CreatePixmap request.
+ //
+
+ IntCache createPixmapIdCache;
+ unsigned int createPixmapLastId;
+ IntCache createPixmapXCache;
+ IntCache createPixmapYCache;
+
+ //
+ // CreateWindow request.
+ //
+
+ IntCache *createWindowGeomCache[6];
+ IntCache createWindowBitmaskCache;
+ IntCache *createWindowAttrCache[15];
+
+ //
+ // FillPoly request.
+ //
+
+ IntCache fillPolyNumPointsCache;
+ IntCache *fillPolyXRelCache[10];
+ IntCache *fillPolyXAbsCache[10];
+ IntCache *fillPolyYRelCache[10];
+ IntCache *fillPolyYAbsCache[10];
+ unsigned int fillPolyRecentX[8];
+ unsigned int fillPolyRecentY[8];
+ unsigned int fillPolyIndex;
+
+ //
+ // GetSelectionOwner request.
+ //
+
+ IntCache getSelectionOwnerSelectionCache;
+
+ //
+ // GrabButton request (also used for GrabPointer).
+ //
+
+ IntCache grabButtonEventMaskCache;
+ IntCache grabButtonConfineCache;
+ CharCache grabButtonButtonCache;
+ IntCache grabButtonModifierCache;
+
+ //
+ // GrabKeyboard request.
+ //
+
+ unsigned int grabKeyboardLastTimestamp;
+
+ //
+ // ImageText8/16 request.
+ //
+
+ IntCache imageTextLengthCache;
+ unsigned int imageTextLastX;
+ unsigned int imageTextLastY;
+ IntCache imageTextCacheX;
+ IntCache imageTextCacheY;
+ TextCompressor imageTextTextCompressor;
+
+ //
+ // InternAtom request.
+ //
+
+ TextCompressor internAtomTextCompressor;
+
+ //
+ // OpenFont request.
+ //
+
+ TextCompressor openFontTextCompressor;
+
+ //
+ // PolyFillRectangle request.
+ //
+
+ IntCache *polyFillRectangleCacheX[4];
+ IntCache *polyFillRectangleCacheY[4];
+ IntCache *polyFillRectangleCacheWidth[4];
+ IntCache *polyFillRectangleCacheHeight[4];
+
+ //
+ // PolyLine request.
+ //
+
+ IntCache *polyLineCacheX[2];
+ IntCache *polyLineCacheY[2];
+
+ //
+ // PolyPoint request.
+ //
+
+ IntCache *polyPointCacheX[2];
+ IntCache *polyPointCacheY[2];
+
+ //
+ // PolyRectangle request.
+ //
+
+ IntCache *polyRectangleGeomCache[4];
+
+ //
+ // PolySegment request.
+ //
+
+ IntCache polySegmentCacheX;
+ IntCache polySegmentCacheY;
+ unsigned int polySegmentLastX[2];
+ unsigned int polySegmentLastY[2];
+ unsigned int polySegmentCacheIndex;
+
+ //
+ // PolyText8/16 request.
+ //
+
+ unsigned int polyTextLastX;
+ unsigned int polyTextLastY;
+ IntCache polyTextCacheX;
+ IntCache polyTextCacheY;
+ IntCache polyTextFontCache;
+ CharCache polyTextDeltaCache;
+ TextCompressor polyTextTextCompressor;
+
+ //
+ // PutImage request.
+ //
+
+ IntCache putImageWidthCache;
+ IntCache putImageHeightCache;
+ unsigned int putImageLastX;
+ unsigned int putImageLastY;
+ IntCache putImageXCache;
+ IntCache putImageYCache;
+ CharCache putImageLeftPadCache;
+
+ //
+ // GetImage request.
+ //
+
+ IntCache getImagePlaneMaskCache;
+
+ //
+ // QueryColors request.
+ //
+
+ unsigned int queryColorsLastPixel;
+
+ //
+ // SetClipRectangles request.
+ //
+
+ IntCache setClipRectanglesXCache;
+ IntCache setClipRectanglesYCache;
+ IntCache *setClipRectanglesGeomCache[4];
+
+ //
+ // SetDashes request.
+ //
+
+ IntCache setDashesLengthCache;
+ IntCache setDashesOffsetCache;
+ CharCache setDashesDashCache_[2];
+
+ //
+ // SetSelectionOwner request.
+ //
+
+ IntCache setSelectionOwnerCache;
+ IntCache setSelectionOwnerTimestampCache;
+
+ //
+ // TranslateCoords request.
+ //
+
+ IntCache translateCoordsSrcCache;
+ IntCache translateCoordsDstCache;
+ IntCache translateCoordsXCache;
+ IntCache translateCoordsYCache;
+
+ //
+ // SendEvent request.
+ //
+
+ IntCache sendEventMaskCache;
+ CharCache sendEventCodeCache;
+ CharCache sendEventByteDataCache;
+ unsigned int sendEventLastSequence;
+ IntCache sendEventIntDataCache;
+ CharCache sendEventEventCache;
+
+ //
+ // PolyFillArc request.
+ //
+
+ IntCache *polyFillArcCacheX[2];
+ IntCache *polyFillArcCacheY[2];
+ IntCache *polyFillArcCacheWidth[2];
+ IntCache *polyFillArcCacheHeight[2];
+ IntCache *polyFillArcCacheAngle1[2];
+ IntCache *polyFillArcCacheAngle2[2];
+
+ //
+ // PolyArc request.
+ //
+
+ IntCache *polyArcCacheX[2];
+ IntCache *polyArcCacheY[2];
+ IntCache *polyArcCacheWidth[2];
+ IntCache *polyArcCacheHeight[2];
+ IntCache *polyArcCacheAngle1[2];
+ IntCache *polyArcCacheAngle2[2];
+
+ //
+ // PutPackedImage request.
+ //
+
+ IntCache putPackedImageSrcLengthCache;
+ IntCache putPackedImageDstLengthCache;
+
+ //
+ // Shape extension requests.
+ //
+
+ CharCache shapeOpcodeCache;
+ IntCache *shapeDataCache[8];
+
+ //
+ // Generic requests.
+ //
+
+ CharCache genericRequestOpcodeCache;
+ IntCache *genericRequestDataCache[8];
+
+ //
+ // Render extension requests.
+ //
+
+ OpcodeCache renderOpcodeCache;
+
+ CharCache renderOpCache;
+
+ XidCache renderSrcPictureCache;
+ XidCache renderMaskPictureCache;
+ XidCache renderDstPictureCache;
+ FreeCache renderFreePictureCache;
+
+ IntCache renderGlyphSetCache;
+ FreeCache renderFreeGlyphSetCache;
+
+ IntCache renderIdCache;
+ IntCache renderLengthCache;
+ IntCache renderFormatCache;
+ IntCache renderValueMaskCache;
+ IntCache renderNumGlyphsCache;
+
+ IntCache renderXCache;
+ IntCache renderYCache;
+
+ unsigned int renderLastX;
+ unsigned int renderLastY;
+
+ IntCache renderWidthCache;
+ IntCache renderHeightCache;
+
+ unsigned int renderLastId;
+
+ IntCache *renderDataCache[16];
+
+ TextCompressor renderTextCompressor;
+
+ IntCache renderGlyphXCache;
+ IntCache renderGlyphYCache;
+
+ unsigned int renderGlyphX;
+ unsigned int renderGlyphY;
+
+ IntCache *renderCompositeGlyphsDataCache[16];
+ unsigned int renderLastCompositeGlyphsData;
+
+ IntCache *renderCompositeDataCache[3];
+
+ //
+ // SetCacheParameters request.
+ //
+
+ IntCache setCacheParametersCache;
+
+ //
+ // Encode new XID values based
+ // on the last value encoded.
+ //
+
+ IntCache lastIdCache;
+ unsigned int lastId;
+};
+
+#endif /* ClientCache_H */
diff --git a/nxcomp/ClientChannel.cpp b/nxcomp/ClientChannel.cpp
new file mode 100644
index 000000000..31d6d125a
--- /dev/null
+++ b/nxcomp/ClientChannel.cpp
@@ -0,0 +1,8244 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include <string.h>
+
+#include <X11/X.h>
+#include <X11/Xatom.h>
+
+#include "NXproto.h"
+#include "NXrender.h"
+
+#include "ClientChannel.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "StaticCompressor.h"
+
+#include "Statistics.h"
+#include "Proxy.h"
+
+#include "PutImage.h"
+#include "PutPackedImage.h"
+
+extern Proxy *proxy;
+
+//
+// Set the verbosity level. You also
+// need to define OPCODES in Misc.cpp
+// if you want literals instead of
+// opcodes' numbers.
+//
+
+#define PANIC
+#define WARNING
+#undef OPCODES
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Log the important tracepoints related
+// to writing packets to the peer proxy.
+//
+
+#undef FLUSH
+
+//
+// Log the operations related to splits.
+//
+
+#undef SPLIT
+
+//
+// Define this to trace the invocations
+// of the agent's callbacks.
+//
+
+#undef CALLBACK
+
+//
+// By defining this, a simple procedure is activated at
+// startup which just allocates and deallocates plenty
+// of cache objects. This is used to help determine the
+// current memory requirements.
+//
+
+#undef MEMORY
+
+//
+// Inspects target of common X operations.
+//
+
+#undef TARGETS
+
+#ifdef TARGETS
+
+#include <set>
+#include <map>
+
+typedef set < unsigned int, less<unsigned int> > T_windows;
+typedef set < unsigned int, less<unsigned int> > T_pixmaps;
+typedef map < unsigned int, unsigned int, less<unsigned int> > T_gcontexts;
+
+T_windows windows;
+T_pixmaps pixmaps;
+T_gcontexts gcontexts;
+
+#endif
+
+//
+// Define this to log when a channel
+// is created or destroyed.
+//
+
+#undef REFERENCES
+
+//
+// Here are the static members.
+//
+
+#ifdef REFERENCES
+
+int ClientChannel::references_ = 0;
+
+#endif
+
+ClientChannel::ClientChannel(Transport *transport, StaticCompressor *compressor)
+
+ : Channel(transport, compressor), readBuffer_(transport_, this)
+{
+ //
+ // Sequence number of the next message
+ // being encoded or decoded.
+ //
+
+ clientSequence_ = 0;
+ serverSequence_ = 0;
+
+ //
+ // Current sequence known by NX agent.
+ //
+
+ lastSequence_ = 0;
+
+ //
+ // This is used to test the synchronous
+ // flush in the proxy.
+ //
+
+ lastRequest_ = 0;
+
+ //
+ // Store information about the images
+ // being streamed.
+ //
+
+ splitState_.resource = nothing;
+ splitState_.pending = 0;
+ splitState_.commit = 0;
+ splitState_.mode = split_none;
+
+ //
+ // Disable image streaming if the remote
+ // doesn't support our proxy version.
+ //
+
+ handleSplitEnable();
+
+ //
+ // Number of outstanding tainted replies.
+ //
+
+ taintCounter_ = 0;
+
+ #ifdef MEMORY
+
+ *logofs << "ClientChannel: Created 1 ClientCache and 1 ServerCache. "
+ << "You have 30 seconds to check the allocated size.\n"
+ << logofs_flush;
+
+ sleep(30);
+
+ ClientCache *clientCacheTestArray[100];
+ ServerCache *serverCacheTestArray[100];
+
+ for (int i = 0; i < 100; i++)
+ {
+ clientCacheTestArray[i] = new ClientCache();
+ }
+
+ *logofs << "ClientChannel: Created further 100 ClientCache. "
+ << "You have 30 seconds to check the allocated size.\n"
+ << logofs_flush;
+
+ sleep(30);
+
+ for (int i = 0; i < 100; i++)
+ {
+ serverCacheTestArray[i] = new ServerCache();
+ }
+
+ *logofs << "ClientChannel: Created further 100 ServerCache. "
+ << "You have 30 seconds to check the allocated size.\n"
+ << logofs_flush;
+
+ sleep(30);
+
+ for (int i = 0; i < 100; i++)
+ {
+ delete clientCacheTestArray[i];
+ delete serverCacheTestArray[i];
+ }
+
+ *logofs << "ClientChannel: Deleted 100 ClientCache and 100 ServerCache. "
+ << "You have 30 seconds to check the allocated size.\n"
+ << logofs_flush;
+
+ sleep(30);
+
+ #endif
+
+ #ifdef REFERENCES
+ *logofs << "ClientChannel: Created new object at "
+ << this << " for FD#" << fd_ << " out of "
+ << ++references_ << " allocated channels.\n"
+ << logofs_flush;
+ #endif
+}
+
+ClientChannel::~ClientChannel()
+{
+ #ifdef REFERENCES
+ *logofs << "ClientChannel: Deleted object at "
+ << this << " for FD#" << fd_ << " out of "
+ << --references_ << " allocated channels.\n"
+ << logofs_flush;
+ #endif
+}
+
+//
+// Beginning of handleRead().
+//
+
+int ClientChannel::handleRead(EncodeBuffer &encodeBuffer, const unsigned char *message,
+ unsigned int length)
+{
+ #ifdef TEST
+ *logofs << "handleRead: Called for FD#" << fd_
+ << " with " << encodeBuffer.getLength()
+ << " bytes already encoded.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Pointer to located message and
+ // its size in bytes.
+ //
+
+ const unsigned char *inputMessage;
+ unsigned int inputLength;
+
+ //
+ // Set when message is found in
+ // cache.
+ //
+
+ int hit;
+
+ //
+ // Check if we can borrow the buffer
+ // from the caller.
+ //
+
+ if (message != NULL && length != 0)
+ {
+ readBuffer_.readMessage(message, length);
+ }
+ else
+ {
+ //
+ // Get the data from the transport.
+ //
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleRead: Trying to read from FD#"
+ << fd_ << " at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ int result = readBuffer_.readMessage();
+
+ #ifdef DEBUG
+ *logofs << "handleRead: Read result on FD#" << fd_
+ << " is " << result << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (result < 0)
+ {
+ //
+ // Let the proxy close the channel.
+ //
+
+ return -1;
+ }
+ else if (result == 0)
+ {
+ #if defined(TEST) || defined(INFO)
+
+ *logofs << "handleRead: PANIC! No data read from FD#"
+ << fd_ << " while encoding messages.\n"
+ << logofs_flush;
+
+ HandleCleanup();
+
+ #endif
+
+ return 0;
+ }
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+ *logofs << "handleRead: Encoding messages for FD#" << fd_
+ << " with " << readBuffer_.getLength() << " bytes "
+ << "in the buffer.\n" << logofs_flush;
+ #endif
+
+ //
+ // Extract any complete message which
+ // is available in the buffer.
+ //
+
+ if (proxy -> handleAsyncSwitch(fd_) < 0)
+ {
+ return -1;
+ }
+
+ while ((inputMessage = readBuffer_.getMessage(inputLength)) != NULL)
+ {
+ hit = 0;
+
+ if (firstRequest_)
+ {
+ //
+ // Need to add the length of the first
+ // request as it was not present in
+ // previous versions.
+ //
+
+ if (control -> isProtoStep7() == 1)
+ {
+ encodeBuffer.encodeValue(inputLength, 8);
+ }
+
+ for (unsigned int i = 0; i < inputLength; i++)
+ {
+ encodeBuffer.encodeValue((unsigned int) inputMessage[i], 8);
+ }
+
+ firstRequest_ = 0;
+
+ #if defined(TEST) || defined(OPCODES)
+
+ int bits = encodeBuffer.diffBits();
+
+ *logofs << "handleRead: Handled first request. " << inputLength
+ << " bytes in, " << bits << " bits (" << ((float) bits) / 8
+ << " bytes) out.\n" << logofs_flush;
+ #endif
+
+ priority_++;
+
+ //
+ // Due to the way the loop was implemented
+ // we can't encode multiple messages if we
+ // are encoding the first request.
+ //
+
+ if (control -> isProtoStep7() == 0)
+ {
+ if (proxy -> handleAsyncInit() < 0)
+ {
+ return -1;
+ }
+ }
+ }
+ else
+ {
+ //
+ // First of all we get the opcode.
+ //
+
+ unsigned char inputOpcode = *inputMessage;
+
+ #if defined(TEST) || defined(INFO)
+
+ //
+ // This is used to test the synchronous
+ // flush in the parent proxy.
+ //
+
+ lastRequest_ = inputOpcode;
+
+ #endif
+
+ //
+ // Check if the request is supported by the
+ // remote. If not, only handle it locally and
+ // taint the opcode as a X_NoOperation. Also
+ // try to short-circuit some replies at this
+ // side. XSync requests, for example, weight
+ // for half of the total round-trips.
+ //
+
+ if (handleTaintRequest(inputOpcode, inputMessage,
+ inputLength) < 0)
+ {
+ return -1;
+ }
+
+ encodeBuffer.encodeOpcodeValue(inputOpcode, clientCache_ -> opcodeCache);
+
+ //
+ // Update the current sequence.
+ //
+
+ clientSequence_++;
+ clientSequence_ &= 0xffff;
+
+ #ifdef DEBUG
+ *logofs << "handleRead: Last client sequence number for FD#"
+ << fd_ << " is " << clientSequence_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // If differential compression is disabled
+ // then use the most simple encoding.
+ //
+
+ if (control -> LocalDeltaCompression == 0)
+ {
+ int result = handleFastReadRequest(encodeBuffer, inputOpcode,
+ inputMessage, inputLength);
+ if (result < 0)
+ {
+ return -1;
+ }
+ else if (result > 0)
+ {
+ continue;
+ }
+ }
+
+ //
+ // Go to the message's specific encoding.
+ //
+
+ switch (inputOpcode)
+ {
+ case X_AllocColor:
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29,
+ clientCache_ -> colormapCache);
+ const unsigned char *nextSrc = inputMessage + 8;
+ unsigned int colorData[3];
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ unsigned int value = GetUINT(nextSrc, bigEndian_);
+ encodeBuffer.encodeCachedValue(value, 16,
+ *(clientCache_ -> allocColorRGBCache[i]), 4);
+ colorData[i] = value;
+ nextSrc += 2;
+ }
+
+ sequenceQueue_.push(clientSequence_, inputOpcode,
+ colorData[0], colorData[1], colorData[2]);
+
+ priority_++;
+ }
+ break;
+ case X_ReparentWindow:
+ {
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> windowCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_),
+ clientCache_ -> windowCache);
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 12, bigEndian_), 16, 11);
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 14, bigEndian_), 16, 11);
+ }
+ break;
+ case X_ChangeProperty:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_ChangeProperty);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ unsigned char format = inputMessage[16];
+ encodeBuffer.encodeCachedValue(format, 8,
+ clientCache_ -> changePropertyFormatCache);
+ unsigned int dataLength = GetULONG(inputMessage + 20, bigEndian_);
+ encodeBuffer.encodeValue(dataLength, 32, 6);
+ encodeBuffer.encodeValue(inputMessage[1], 2);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> windowCache);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), 29,
+ clientCache_ -> changePropertyPropertyCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), 29,
+ clientCache_ -> changePropertyTypeCache, 9);
+ const unsigned char *nextSrc = inputMessage + 24;
+ if (format == 8)
+ {
+ if (control -> isProtoStep7() == 1)
+ {
+ encodeBuffer.encodeTextData(nextSrc, dataLength);
+ }
+ else
+ {
+ clientCache_ -> changePropertyTextCompressor.reset();
+ for (unsigned int i = 0; i < dataLength; i++)
+ clientCache_ -> changePropertyTextCompressor.
+ encodeChar(*nextSrc++, encodeBuffer);
+ }
+ }
+ else if (format == 32)
+ {
+ for (unsigned int i = 0; i < dataLength; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 32,
+ clientCache_ -> changePropertyData32Cache);
+ nextSrc += 4;
+ }
+ }
+ else
+ {
+ for (unsigned int i = 0; i < dataLength; i++)
+ {
+ encodeBuffer.encodeValue(GetUINT(nextSrc, bigEndian_), 16);
+ nextSrc += 2;
+ }
+ }
+ }
+ break;
+ case X_SendEvent:
+ {
+ //
+ // TODO: This can be improved. In the worst
+ // cases, it appears to provide a poor 1.6:1
+ // ratio.
+ //
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_SendEvent);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
+ unsigned int window = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (window == 0 || window == 1)
+ {
+ encodeBuffer.encodeBoolValue(1);
+ encodeBuffer.encodeBoolValue(window);
+ }
+ else
+ {
+ encodeBuffer.encodeBoolValue(0);
+ encodeBuffer.encodeXidValue(window, clientCache_ -> windowCache);
+ }
+
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), 32,
+ clientCache_ -> sendEventMaskCache, 9);
+ encodeBuffer.encodeCachedValue(*(inputMessage + 12), 8,
+ clientCache_ -> sendEventCodeCache);
+ encodeBuffer.encodeCachedValue(*(inputMessage + 13), 8,
+ clientCache_ -> sendEventByteDataCache);
+
+ unsigned int newSeq = GetUINT(inputMessage + 14, bigEndian_);
+ unsigned int diffSeq = newSeq - clientCache_ -> sendEventLastSequence;
+ clientCache_ -> sendEventLastSequence = newSeq;
+ encodeBuffer.encodeValue(diffSeq, 16, 4);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 16, bigEndian_), 32,
+ clientCache_ -> sendEventIntDataCache);
+
+ for (unsigned int i = 20; i < 44; i++)
+ {
+ encodeBuffer.encodeCachedValue((unsigned int) inputMessage[i], 8,
+ clientCache_ -> sendEventEventCache);
+ }
+ }
+ break;
+ case X_ChangeWindowAttributes:
+ {
+ encodeBuffer.encodeValue((inputLength - 12) >> 2, 4);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> windowCache);
+ unsigned int bitmask = GetULONG(inputMessage + 8, bigEndian_);
+ encodeBuffer.encodeCachedValue(bitmask, 15,
+ clientCache_ -> createWindowBitmaskCache);
+ const unsigned char *nextSrc = inputMessage + 12;
+ unsigned int mask = 0x1;
+ for (unsigned int j = 0; j < 15; j++)
+ {
+ if (bitmask & mask)
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 32,
+ *clientCache_ -> createWindowAttrCache[j]);
+ nextSrc += 4;
+ }
+ mask <<= 1;
+ }
+ }
+ break;
+ case X_ClearArea:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_ClearArea target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_ClearArea target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_ClearArea target id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_ClearArea);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> windowCache);
+ const unsigned char *nextSrc = inputMessage + 8;
+ for (unsigned int i = 0; i < 4; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16,
+ *clientCache_ -> clearAreaGeomCache[i], 8);
+ nextSrc += 2;
+ }
+ }
+ break;
+ case X_CloseFont:
+ {
+ unsigned int font = GetULONG(inputMessage + 4, bigEndian_);
+ encodeBuffer.encodeValue(font - clientCache_ -> lastFont, 29, 5);
+ clientCache_ -> lastFont = font;
+ }
+ break;
+ case X_ConfigureWindow:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_ConfigureWindow);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> windowCache);
+ unsigned int bitmask = GetUINT(inputMessage + 8, bigEndian_);
+ encodeBuffer.encodeCachedValue(bitmask, 7,
+ clientCache_ -> configureWindowBitmaskCache);
+ unsigned int mask = 0x1;
+ const unsigned char *nextSrc = inputMessage + 12;
+ for (unsigned int i = 0; i < 7; i++)
+ {
+ if (bitmask & mask)
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_),
+ CONFIGUREWINDOW_FIELD_WIDTH[i],
+ *clientCache_ -> configureWindowAttrCache[i], 8);
+ nextSrc += 4;
+ }
+ mask <<= 1;
+ }
+ }
+ break;
+ case X_ConvertSelection:
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29,
+ clientCache_ -> convertSelectionRequestorCache, 9);
+ const unsigned char* nextSrc = inputMessage + 8;
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 29,
+ *(clientCache_ -> convertSelectionAtomCache[i]), 9);
+ nextSrc += 4;
+ }
+ unsigned int timestamp = GetULONG(nextSrc, bigEndian_);
+ encodeBuffer.encodeValue(timestamp -
+ clientCache_ -> convertSelectionLastTimestamp, 32, 4);
+ clientCache_ -> convertSelectionLastTimestamp = timestamp;
+ }
+ break;
+ case X_CopyArea:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_CopyArea source id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_CopyArea source id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_CopyArea source id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ t_id = GetULONG(inputMessage + 8, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_CopyArea target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_CopyArea target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_CopyArea target id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_CopyArea);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
+ bigEndian_), clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 12,
+ bigEndian_), clientCache_ -> gcCache);
+ const unsigned char *nextSrc = inputMessage + 16;
+ for (unsigned int i = 0; i < 6; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16,
+ *clientCache_ -> copyAreaGeomCache[i], 8);
+ nextSrc += 2;
+ }
+ }
+ break;
+ case X_CopyGC:
+ {
+ #ifdef TARGETS
+
+ unsigned int s_g_id = GetULONG(inputMessage + 4, bigEndian_);
+ unsigned int d_g_id = GetULONG(inputMessage + 8, bigEndian_);
+
+ *logofs << "handleRead: X_CopyGC source gcontext id is " << s_g_id
+ << " destination gcontext id is " << d_g_id << ".\n"
+ << logofs_flush;
+
+ #endif
+
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> gcCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
+ bigEndian_), clientCache_ -> gcCache);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12,
+ bigEndian_), 23, clientCache_ -> createGCBitmaskCache);
+ }
+ break;
+ case X_CopyPlane:
+ {
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
+ bigEndian_), clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 12,
+ bigEndian_), clientCache_ -> gcCache);
+ const unsigned char *nextSrc = inputMessage + 16;
+ for (unsigned int i = 0; i < 6; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16,
+ *clientCache_ -> copyPlaneGeomCache[i], 8);
+ nextSrc += 2;
+ }
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 28, bigEndian_), 32,
+ clientCache_ -> copyPlaneBitPlaneCache, 10);
+ }
+ break;
+ case X_CreateGC:
+ {
+ #ifdef TARGETS
+
+ unsigned int g_id = GetULONG(inputMessage + 4, bigEndian_);
+ unsigned int t_id = GetULONG(inputMessage + 8, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_CreateGC id " << g_id
+ << " target id is pixmap " << t_id
+ << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_CreateGC id " << g_id
+ << " target id is window " << t_id
+ << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_CreateGC id " << g_id
+ << " target id is unrecognized.\n"
+ << logofs_flush;
+ }
+
+ gcontexts.insert(T_gcontexts::value_type(g_id, t_id));
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_CreateGC);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ if (control -> isProtoStep7() == 1)
+ {
+ encodeBuffer.encodeNewXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> lastId, clientCache_ -> lastIdCache,
+ clientCache_ -> gcCache,
+ clientCache_ -> freeGCCache);
+ }
+ else
+ {
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> gcCache);
+ }
+
+ const unsigned char *nextSrc = inputMessage + 8;
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
+ bigEndian_), clientCache_ -> drawableCache);
+ nextSrc += 4;
+ unsigned int bitmask = GetULONG(nextSrc, bigEndian_);
+ nextSrc += 4;
+ encodeBuffer.encodeCachedValue(bitmask, 23,
+ clientCache_ -> createGCBitmaskCache);
+ unsigned int mask = 0x1;
+ for (unsigned int i = 0; i < 23; i++)
+ {
+ if (bitmask & mask)
+ {
+ unsigned int value = GetULONG(nextSrc, bigEndian_);
+ nextSrc += 4;
+ unsigned int fieldWidth = CREATEGC_FIELD_WIDTH[i];
+ if (fieldWidth <= 4)
+ {
+ encodeBuffer.encodeValue(value, fieldWidth);
+ }
+ else
+ {
+ encodeBuffer.encodeCachedValue(value, fieldWidth,
+ *clientCache_ -> createGCAttrCache[i]);
+ }
+ }
+ mask <<= 1;
+ }
+ }
+ break;
+ case X_ChangeGC:
+ {
+ #ifdef TARGETS
+
+ unsigned int g_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ T_gcontexts::iterator i = gcontexts.find(g_id);
+
+ if (i != gcontexts.end())
+ {
+ unsigned int t_id = i -> second;
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_ChangeGC gcontext id is " << g_id
+ << " target id is pixmap " << t_id << ".\n"
+ << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_ChangeGC gcontext id is " << g_id
+ << " target id is window " << t_id << ".\n"
+ << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_ChangeGC gcontext is " << g_id
+ << " target id is unrecognized.\n"
+ << logofs_flush;
+ }
+ }
+ else
+ {
+ *logofs << "handleRead: X_ChangeGC gcontext id " << g_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ gcontexts.erase(g_id);
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_ChangeGC);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> gcCache);
+ const unsigned char *nextSrc = inputMessage + 8;
+ unsigned int bitmask = GetULONG(nextSrc, bigEndian_);
+ nextSrc += 4;
+ encodeBuffer.encodeCachedValue(bitmask, 23,
+ clientCache_ -> createGCBitmaskCache);
+ unsigned int mask = 0x1;
+ for (unsigned int i = 0; i < 23; i++)
+ {
+ if (bitmask & mask)
+ {
+ unsigned int value = GetULONG(nextSrc, bigEndian_);
+ nextSrc += 4;
+ unsigned int fieldWidth = CREATEGC_FIELD_WIDTH[i];
+ if (fieldWidth <= 4)
+ {
+ encodeBuffer.encodeValue(value, fieldWidth);
+ }
+ else
+ {
+ encodeBuffer.encodeCachedValue(value, fieldWidth,
+ *clientCache_ -> createGCAttrCache[i]);
+ }
+ }
+ mask <<= 1;
+ }
+ }
+ break;
+ case X_CreatePixmap:
+ {
+ #ifdef TARGETS
+
+ *logofs << "handleRead: X_CreatePixmap depth " << (unsigned) inputMessage[1]
+ << ", pixmap id " << GetULONG(inputMessage + 4, bigEndian_)
+ << ", drawable " << GetULONG(inputMessage + 8, bigEndian_)
+ << ", width " << GetUINT(inputMessage + 12, bigEndian_)
+ << ", height " << GetUINT(inputMessage + 14, bigEndian_)
+ << ", size " << GetUINT(inputMessage + 2, bigEndian_) << 2
+ << ".\n" << logofs_flush;
+
+ unsigned int p_id = GetULONG(inputMessage + 4, bigEndian_);
+ unsigned short p_sx = GetUINT(inputMessage + 12, bigEndian_);
+ unsigned short p_sy = GetUINT(inputMessage + 14, bigEndian_);
+
+ *logofs << "handleRead: X_CreatePixmap id is " << p_id
+ << " width is " << p_sx << " height is " << p_sy
+ << ".\n" << logofs_flush;
+
+ if (p_sx * p_sy <= 64 * 64)
+ {
+ *logofs << "handleRead: X_CreatePixmap id " << p_id << " of size "
+ << p_sx << "x" << p_sy << "=" << p_sx * p_sy
+ << " will be painted at client side.\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_CreatePixmap id " << p_id << " of size "
+ << p_sx << "x" << p_sy << "=" << p_sx * p_sy
+ << " will be painted at server side.\n" << logofs_flush;
+ }
+
+ pixmaps.insert(p_id);
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_CreatePixmap);
+
+ hit = handleEncode(encodeBuffer, clientCache_, messageStore,
+ inputOpcode, inputMessage, inputLength);
+ }
+ break;
+ case X_CreateWindow:
+ {
+ #ifdef TARGETS
+
+ unsigned int w_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ *logofs << "handleRead: X_CreateWindow id is " << w_id
+ << ".\n" << logofs_flush;
+
+ windows.insert(w_id);
+
+ #endif
+
+ unsigned bitmask = GetULONG(inputMessage + 28, bigEndian_);
+ encodeBuffer.encodeCachedValue((unsigned int) inputMessage[1], 8,
+ clientCache_ -> depthCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_),
+ clientCache_ -> windowCache);
+
+ if (control -> isProtoStep7() == 1)
+ {
+ encodeBuffer.encodeNewXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> lastId, clientCache_ -> lastIdCache,
+ clientCache_ -> windowCache,
+ clientCache_ -> freeWindowCache);
+ }
+ else
+ {
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> windowCache);
+ }
+ const unsigned char *nextSrc = inputMessage + 12;
+ for (unsigned int i = 0; i < 6; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16,
+ *clientCache_ -> createWindowGeomCache[i], 8);
+ nextSrc += 2;
+ }
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 24,
+ bigEndian_), 29, clientCache_ -> visualCache);
+ encodeBuffer.encodeCachedValue(bitmask, 15,
+ clientCache_ -> createWindowBitmaskCache);
+ nextSrc = inputMessage + 32;
+ unsigned int mask = 0x1;
+ for (unsigned int j = 0; j < 15; j++)
+ {
+ if (bitmask & mask)
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 32,
+ *clientCache_ -> createWindowAttrCache[j]);
+ nextSrc += 4;
+ }
+ mask <<= 1;
+ }
+ }
+ break;
+ case X_DeleteProperty:
+ {
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> windowCache);
+ encodeBuffer.encodeValue(GetULONG(inputMessage + 8, bigEndian_), 29, 9);
+ }
+ break;
+ case X_FillPoly:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_FillPoly target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_FillPoly target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_FillPoly target id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_FillPoly);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ unsigned int numPoints = ((inputLength - 16) >> 2);
+
+ if (control -> isProtoStep10() == 1)
+ {
+ encodeBuffer.encodeCachedValue(numPoints, 16,
+ clientCache_ -> fillPolyNumPointsCache, 4);
+ }
+ else
+ {
+ encodeBuffer.encodeCachedValue(numPoints, 14,
+ clientCache_ -> fillPolyNumPointsCache, 4);
+ }
+
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_),
+ clientCache_ -> gcCache);
+ encodeBuffer.encodeValue((unsigned int) inputMessage[12], 2);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[13]);
+ int relativeCoordMode = (inputMessage[13] != 0);
+ const unsigned char *nextSrc = inputMessage + 16;
+ unsigned int pointIndex = 0;
+
+ for (unsigned int i = 0; i < numPoints; i++)
+ {
+ if (relativeCoordMode)
+ {
+ encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16,
+ *clientCache_ -> fillPolyXRelCache[pointIndex], 8);
+ nextSrc += 2;
+ encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16,
+ *clientCache_ -> fillPolyYRelCache[pointIndex], 8);
+ nextSrc += 2;
+ }
+ else
+ {
+ unsigned int x = GetUINT(nextSrc, bigEndian_);
+ nextSrc += 2;
+ unsigned int y = GetUINT(nextSrc, bigEndian_);
+ nextSrc += 2;
+ unsigned int j;
+ for (j = 0; j < 8; j++)
+ if ((x == clientCache_ -> fillPolyRecentX[j]) &&
+ (y == clientCache_ -> fillPolyRecentY[j]))
+ break;
+ if (j < 8)
+ {
+ encodeBuffer.encodeBoolValue(1);
+ encodeBuffer.encodeValue(j, 3);
+ }
+ else
+ {
+ encodeBuffer.encodeBoolValue(0);
+ encodeBuffer.encodeCachedValue(x, 16,
+ *clientCache_ -> fillPolyXAbsCache[pointIndex], 8);
+ encodeBuffer.encodeCachedValue(y, 16,
+ *clientCache_ -> fillPolyYAbsCache[pointIndex], 8);
+ clientCache_ -> fillPolyRecentX[clientCache_ -> fillPolyIndex] = x;
+ clientCache_ -> fillPolyRecentY[clientCache_ -> fillPolyIndex] = y;
+ clientCache_ -> fillPolyIndex++;
+ if (clientCache_ -> fillPolyIndex == 8)
+ clientCache_ -> fillPolyIndex = 0;
+ }
+ }
+
+ if (++pointIndex == 10) pointIndex = 0;
+ }
+ }
+ break;
+ case X_FreeColors:
+ {
+ unsigned int numPixels = GetUINT(inputMessage + 2, bigEndian_) - 3;
+ encodeBuffer.encodeValue(numPixels, 16, 4);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29,
+ clientCache_ -> colormapCache);
+ encodeBuffer.encodeValue(GetULONG(inputMessage + 8, bigEndian_), 32, 4);
+ const unsigned char *nextSrc = inputMessage + 12;
+ while (numPixels)
+ {
+ encodeBuffer.encodeValue(GetULONG(nextSrc, bigEndian_), 32, 8);
+ nextSrc += 4;
+ numPixels--;
+ }
+ }
+ break;
+ case X_FreeCursor:
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_),
+ 29, clientCache_ -> cursorCache, 9);
+ }
+ break;
+ case X_FreeGC:
+ {
+ #ifdef TARGETS
+
+ unsigned int g_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ T_gcontexts::iterator i = gcontexts.find(g_id);
+
+ if (i != gcontexts.end())
+ {
+ unsigned int t_id = i -> second;
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_FreeGC gcontext id is " << g_id
+ << " target id is pixmap " << t_id << ".\n"
+ << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_FreeGC gcontext id is " << g_id
+ << " target id is window " << t_id << ".\n"
+ << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_FreeGC gcontext id is " << g_id
+ << " target id is unrecognized.\n"
+ << logofs_flush;
+ }
+ }
+ else
+ {
+ *logofs << "handleRead: X_FreeGC gcontext id " << g_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ gcontexts.erase(g_id);
+
+ #endif
+
+ if (control -> isProtoStep7() == 1)
+ {
+ encodeBuffer.encodeFreeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> freeGCCache);
+ }
+ else
+ {
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> gcCache);
+ }
+ }
+ break;
+ case X_FreePixmap:
+ {
+ #ifdef TARGETS
+
+ unsigned int p_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ *logofs << "handleRead: X_FreePixmap id is " << p_id << ".\n" << logofs_flush;
+
+ pixmaps.erase(p_id);
+
+ #endif
+
+ if (control -> isProtoStep7() == 1)
+ {
+ encodeBuffer.encodeFreeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> freeDrawableCache);
+ }
+ else
+ {
+ unsigned int pixmap = GetULONG(inputMessage + 4, bigEndian_);
+ unsigned int diff = pixmap - clientCache_ -> createPixmapLastId;
+ if (diff == 0)
+ {
+ encodeBuffer.encodeBoolValue(1);
+ }
+ else
+ {
+ encodeBuffer.encodeBoolValue(0);
+ clientCache_ -> createPixmapLastId = pixmap;
+ encodeBuffer.encodeValue(diff, 29, 4);
+ }
+ }
+ }
+ break;
+ case X_GetAtomName:
+ {
+ encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 29, 9);
+
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_GetGeometry:
+ {
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> drawableCache);
+
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_GetInputFocus:
+ {
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_GetModifierMapping:
+ {
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_GetKeyboardMapping:
+ {
+ encodeBuffer.encodeValue((unsigned int) inputMessage[4], 8);
+ encodeBuffer.encodeValue((unsigned int) inputMessage[5], 8);
+
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_GetProperty:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_GetProperty);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ unsigned int property = GetULONG(inputMessage + 8, bigEndian_);
+
+ sequenceQueue_.push(clientSequence_, inputOpcode, property);
+
+ priority_++;
+
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> windowCache);
+ unsigned int property = GetULONG(inputMessage + 8, bigEndian_);
+ encodeBuffer.encodeValue(property, 29, 9);
+ encodeBuffer.encodeValue(GetULONG(inputMessage + 12, bigEndian_), 29, 9);
+ encodeBuffer.encodeValue(GetULONG(inputMessage + 16, bigEndian_), 32, 2);
+ encodeBuffer.encodeValue(GetULONG(inputMessage + 20, bigEndian_), 32, 8);
+
+ sequenceQueue_.push(clientSequence_, inputOpcode, property);
+
+ priority_++;
+ }
+ break;
+ case X_GetSelectionOwner:
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29,
+ clientCache_ -> getSelectionOwnerSelectionCache, 9);
+
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_GrabButton:
+ {
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> windowCache);
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 8, bigEndian_), 16,
+ clientCache_ -> grabButtonEventMaskCache);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[10]);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[11]);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), 29,
+ clientCache_ -> grabButtonConfineCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 16, bigEndian_), 29,
+ clientCache_ -> cursorCache, 9);
+ encodeBuffer.encodeCachedValue(inputMessage[20], 8,
+ clientCache_ -> grabButtonButtonCache);
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 22, bigEndian_), 16,
+ clientCache_ -> grabButtonModifierCache);
+ }
+ break;
+ case X_GrabPointer:
+ {
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> windowCache);
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 8, bigEndian_), 16,
+ clientCache_ -> grabButtonEventMaskCache);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[10]);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[11]);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12,
+ bigEndian_), 29,
+ clientCache_ -> grabButtonConfineCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 16,
+ bigEndian_), 29, clientCache_ -> cursorCache, 9);
+
+ unsigned int timestamp = GetULONG(inputMessage + 20, bigEndian_);
+ encodeBuffer.encodeValue(timestamp -
+ clientCache_ -> grabKeyboardLastTimestamp, 32, 4);
+ clientCache_ -> grabKeyboardLastTimestamp = timestamp;
+
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_GrabKeyboard:
+ {
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> windowCache);
+ unsigned int timestamp = GetULONG(inputMessage + 8, bigEndian_);
+ encodeBuffer.encodeValue(timestamp -
+ clientCache_ -> grabKeyboardLastTimestamp, 32, 4);
+ clientCache_ -> grabKeyboardLastTimestamp = timestamp;
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[12]);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[13]);
+
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_GrabServer:
+ case X_UngrabServer:
+ case X_NoOperation:
+ {
+ }
+ break;
+ case X_PolyText8:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_PolyText8 target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_PolyText8 target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_PolyText8 target id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolyText8);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
+ bigEndian_), clientCache_ -> gcCache);
+ unsigned int x = GetUINT(inputMessage + 12, bigEndian_);
+ int xDiff = x - clientCache_ -> polyTextLastX;
+ clientCache_ -> polyTextLastX = x;
+ encodeBuffer.encodeCachedValue(xDiff, 16,
+ clientCache_ -> polyTextCacheX);
+ unsigned int y = GetUINT(inputMessage + 14, bigEndian_);
+ int yDiff = y - clientCache_ -> polyTextLastY;
+ clientCache_ -> polyTextLastY = y;
+ encodeBuffer.encodeCachedValue(yDiff, 16,
+ clientCache_ -> polyTextCacheY);
+ const unsigned char *end = inputMessage + inputLength - 1;
+ const unsigned char *nextSrc = inputMessage + 16;
+ while (nextSrc < end)
+ {
+ unsigned int textLength = (unsigned int) *nextSrc++;
+ encodeBuffer.encodeBoolValue(1);
+ encodeBuffer.encodeValue(textLength, 8);
+ if (textLength == 255)
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(nextSrc, 1), 29,
+ clientCache_ -> polyTextFontCache);
+ nextSrc += 4;
+ }
+ else
+ {
+ encodeBuffer.encodeCachedValue(*nextSrc++, 8,
+ clientCache_ -> polyTextDeltaCache);
+
+ if (control -> isProtoStep7() == 1)
+ {
+ encodeBuffer.encodeTextData(nextSrc, textLength);
+
+ nextSrc += textLength;
+ }
+ else
+ {
+ clientCache_ -> polyTextTextCompressor.reset();
+ for (unsigned int i = 0; i < textLength; i++)
+ clientCache_ -> polyTextTextCompressor.encodeChar(*nextSrc++, encodeBuffer);
+ }
+ }
+ }
+ encodeBuffer.encodeBoolValue(0);
+ }
+ break;
+ case X_PolyText16:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_PolyText16 target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_PolyText16 target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_PolyText16 target id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolyText16);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
+ bigEndian_), clientCache_ -> gcCache);
+ unsigned int x = GetUINT(inputMessage + 12, bigEndian_);
+ int xDiff = x - clientCache_ -> polyTextLastX;
+ clientCache_ -> polyTextLastX = x;
+ encodeBuffer.encodeCachedValue(xDiff, 16,
+ clientCache_ -> polyTextCacheX);
+ unsigned int y = GetUINT(inputMessage + 14, bigEndian_);
+ int yDiff = y - clientCache_ -> polyTextLastY;
+ clientCache_ -> polyTextLastY = y;
+ encodeBuffer.encodeCachedValue(yDiff, 16,
+ clientCache_ -> polyTextCacheY);
+ const unsigned char *end = inputMessage + inputLength - 1;
+ const unsigned char *nextSrc = inputMessage + 16;
+ while (nextSrc < end)
+ {
+ unsigned int textLength = (unsigned int) *nextSrc++;
+ encodeBuffer.encodeBoolValue(1);
+ encodeBuffer.encodeValue(textLength, 8);
+ if (textLength == 255)
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(nextSrc, 1), 29,
+ clientCache_ -> polyTextFontCache);
+ nextSrc += 4;
+ }
+ else
+ {
+ encodeBuffer.encodeCachedValue(*nextSrc++, 8,
+ clientCache_ -> polyTextDeltaCache);
+
+ if (control -> isProtoStep7() == 1)
+ {
+ encodeBuffer.encodeTextData(nextSrc, textLength * 2);
+
+ nextSrc += textLength * 2;
+ }
+ else
+ {
+ clientCache_ -> polyTextTextCompressor.reset();
+ for (unsigned int i = 0; i < textLength * 2; i++)
+ clientCache_ -> polyTextTextCompressor.encodeChar(*nextSrc++, encodeBuffer);
+ }
+ }
+ }
+ encodeBuffer.encodeBoolValue(0);
+ }
+ break;
+ case X_ImageText8:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_ImageText8 target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_ImageText8 target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_ImageText8 target id "
+ << t_id << " is unrecognized.\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_ImageText8);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ unsigned int textLength = (unsigned int) inputMessage[1];
+ encodeBuffer.encodeCachedValue(textLength, 8,
+ clientCache_ -> imageTextLengthCache, 4);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
+ bigEndian_), clientCache_ -> gcCache);
+ unsigned int x = GetUINT(inputMessage + 12, bigEndian_);
+ int xDiff = x - clientCache_ -> imageTextLastX;
+ clientCache_ -> imageTextLastX = x;
+ encodeBuffer.encodeCachedValue(xDiff, 16,
+ clientCache_ -> imageTextCacheX);
+ unsigned int y = GetUINT(inputMessage + 14, bigEndian_);
+ int yDiff = y - clientCache_ -> imageTextLastY;
+ clientCache_ -> imageTextLastY = y;
+ encodeBuffer.encodeCachedValue(yDiff, 16,
+ clientCache_ -> imageTextCacheY);
+ const unsigned char *nextSrc = inputMessage + 16;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ encodeBuffer.encodeTextData(nextSrc, textLength);
+ }
+ else
+ {
+ clientCache_ -> imageTextTextCompressor.reset();
+ for (unsigned int j = 0; j < textLength; j++)
+ clientCache_ -> imageTextTextCompressor.encodeChar(*nextSrc++, encodeBuffer);
+ }
+ }
+ break;
+ case X_ImageText16:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_ImageText16 target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_ImageText16 target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_ImageText16 target id "
+ << t_id << " is unrecognized.\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_ImageText16);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ unsigned int textLength = (unsigned int) inputMessage[1];
+ encodeBuffer.encodeCachedValue(textLength, 8,
+ clientCache_ -> imageTextLengthCache, 4);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
+ bigEndian_), clientCache_ -> gcCache);
+ unsigned int x = GetUINT(inputMessage + 12, bigEndian_);
+ int xDiff = x - clientCache_ -> imageTextLastX;
+ clientCache_ -> imageTextLastX = x;
+ encodeBuffer.encodeCachedValue(xDiff, 16,
+ clientCache_ -> imageTextCacheX);
+ unsigned int y = GetUINT(inputMessage + 14, bigEndian_);
+ int yDiff = y - clientCache_ -> imageTextLastY;
+ clientCache_ -> imageTextLastY = y;
+ encodeBuffer.encodeCachedValue(yDiff, 16,
+ clientCache_ -> imageTextCacheY);
+ const unsigned char *nextSrc = inputMessage + 16;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ encodeBuffer.encodeTextData(nextSrc, textLength * 2);
+ }
+ else
+ {
+ clientCache_ -> imageTextTextCompressor.reset();
+ for (unsigned int j = 0; j < textLength * 2; j++)
+ clientCache_ -> imageTextTextCompressor.encodeChar(*nextSrc++, encodeBuffer);
+ }
+ }
+ break;
+ case X_InternAtom:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_InternAtom);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ //
+ // Set the priority, also if doing so will
+ // penalize all the well written clients
+ // using XInternAtoms() to pipeline multi-
+ // ple replies.
+ //
+
+ priority_++;
+
+ hit = 1;
+
+ break;
+ }
+
+ unsigned int nameLength = GetUINT(inputMessage + 4, bigEndian_);
+ encodeBuffer.encodeValue(nameLength, 16, 6);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
+ const unsigned char *nextSrc = inputMessage + 8;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ encodeBuffer.encodeTextData(nextSrc, nameLength);
+ }
+ else
+ {
+ clientCache_ -> internAtomTextCompressor.reset();
+ for (unsigned int i = 0; i < nameLength; i++)
+ {
+ clientCache_ -> internAtomTextCompressor.encodeChar(*nextSrc++, encodeBuffer);
+ }
+ }
+
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_ListExtensions:
+ {
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_ListFonts:
+ {
+ unsigned int textLength = GetUINT(inputMessage + 6, bigEndian_);
+ encodeBuffer.encodeValue(textLength, 16, 6);
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 4, bigEndian_), 16, 6);
+ const unsigned char* nextSrc = inputMessage + 8;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ encodeBuffer.encodeTextData(nextSrc, textLength);
+ }
+ else
+ {
+ clientCache_ -> polyTextTextCompressor.reset();
+ for (unsigned int i = 0; i < textLength; i++)
+ {
+ clientCache_ -> polyTextTextCompressor.encodeChar(*nextSrc++, encodeBuffer);
+ }
+ }
+
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_LookupColor:
+ case X_AllocNamedColor:
+ {
+ unsigned int textLength = GetUINT(inputMessage + 8, bigEndian_);
+ encodeBuffer.encodeValue(textLength, 16, 6);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_),
+ 29, clientCache_ -> colormapCache);
+ const unsigned char *nextSrc = inputMessage + 12;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ encodeBuffer.encodeTextData(nextSrc, textLength);
+ }
+ else
+ {
+ clientCache_ -> polyTextTextCompressor.reset();
+ for (unsigned int i = 0; i < textLength; i++)
+ {
+ clientCache_ -> polyTextTextCompressor.encodeChar(*nextSrc++, encodeBuffer);
+ }
+ }
+
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_MapWindow:
+ case X_UnmapWindow:
+ case X_MapSubwindows:
+ case X_GetWindowAttributes:
+ case X_DestroyWindow:
+ case X_DestroySubwindows:
+ case X_QueryPointer:
+ case X_QueryTree:
+ {
+ #ifdef TARGETS
+
+ if (inputOpcode == X_DestroyWindow)
+ {
+ unsigned int w_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ *logofs << "handleRead: X_DestroyWindow id is "
+ << w_id << ".\n" << logofs_flush;
+
+ windows.erase(w_id);
+ }
+
+ #endif
+
+ if (inputOpcode == X_DestroyWindow && control -> isProtoStep7() == 1)
+ {
+ encodeBuffer.encodeFreeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> freeWindowCache);
+ }
+ else
+ {
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> windowCache);
+ }
+
+ if ((inputOpcode == X_QueryPointer) ||
+ (inputOpcode == X_GetWindowAttributes) ||
+ (inputOpcode == X_QueryTree))
+ {
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ }
+ break;
+ case X_OpenFont:
+ {
+ unsigned int nameLength = GetUINT(inputMessage + 8, bigEndian_);
+ encodeBuffer.encodeValue(nameLength, 16, 7);
+ unsigned int font = GetULONG(inputMessage + 4, bigEndian_);
+ encodeBuffer.encodeValue(font - clientCache_ -> lastFont, 29, 5);
+ clientCache_ -> lastFont = font;
+ const unsigned char *nextSrc = inputMessage + 12;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ encodeBuffer.encodeTextData(nextSrc, nameLength);
+ }
+ else
+ {
+ clientCache_ -> openFontTextCompressor.reset();
+ for (; nameLength; nameLength--)
+ {
+ clientCache_ -> openFontTextCompressor.
+ encodeChar(*nextSrc++, encodeBuffer);
+ }
+ }
+ }
+ break;
+ case X_PolyFillRectangle:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_PolyFillRectangle target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_PolyFillRectangle target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_PolyFillRectangle target id "
+ << t_id << " is unrecognized.\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolyFillRectangle);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
+ bigEndian_), clientCache_ -> gcCache);
+
+ unsigned int index = 0;
+ unsigned int lastX = 0, lastY = 0;
+ unsigned int lastWidth = 0, lastHeight = 0;
+
+ //
+ // TODO: Could send the size at the beginning
+ // instead of a bool at each iteration.
+ //
+
+ for (unsigned int i = 12; i < inputLength;)
+ {
+ unsigned int x = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newX = x;
+ x -= lastX;
+ lastX = newX;
+ encodeBuffer.encodeCachedValue(x, 16,
+ *clientCache_ -> polyFillRectangleCacheX[index], 8);
+ i += 2;
+ unsigned int y = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newY = y;
+ y -= lastY;
+ lastY = newY;
+ encodeBuffer.encodeCachedValue(y, 16,
+ *clientCache_ -> polyFillRectangleCacheY[index], 8);
+ i += 2;
+ unsigned int width = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newWidth = width;
+ width -= lastWidth;
+ lastWidth = newWidth;
+ encodeBuffer.encodeCachedValue(width, 16,
+ *clientCache_ -> polyFillRectangleCacheWidth[index], 8);
+ i += 2;
+ unsigned int height = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newHeight = height;
+ height -= lastHeight;
+ lastHeight = newHeight;
+ encodeBuffer.encodeCachedValue(height, 16,
+ *clientCache_ -> polyFillRectangleCacheHeight[index], 8);
+ i += 2;
+
+ if (++index == 4) index = 0;
+
+ encodeBuffer.encodeBoolValue((i < inputLength) ? 1 : 0);
+ }
+ }
+ break;
+ case X_PolyFillArc:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_PolyFillArc target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_PolyFillArc target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_PolyFillArc target id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolyFillArc);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
+ bigEndian_), clientCache_ -> gcCache);
+
+ unsigned int index = 0;
+ unsigned int lastX = 0, lastY = 0;
+ unsigned int lastWidth = 0, lastHeight = 0;
+ unsigned int lastAngle1 = 0, lastAngle2 = 0;
+
+ //
+ // TODO: Could send the size at the beginning
+ // instead of a bool at each iteration.
+ //
+
+ for (unsigned int i = 12; i < inputLength;)
+ {
+ unsigned int x = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newX = x;
+ x -= lastX;
+ lastX = newX;
+ encodeBuffer.encodeCachedValue(x, 16,
+ *clientCache_ -> polyFillArcCacheX[index], 8);
+ i += 2;
+ unsigned int y = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newY = y;
+ y -= lastY;
+ lastY = newY;
+ encodeBuffer.encodeCachedValue(y, 16,
+ *clientCache_ -> polyFillArcCacheY[index], 8);
+ i += 2;
+ unsigned int width = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newWidth = width;
+ width -= lastWidth;
+ lastWidth = newWidth;
+ encodeBuffer.encodeCachedValue(width, 16,
+ *clientCache_ -> polyFillArcCacheWidth[index], 8);
+ i += 2;
+ unsigned int height = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newHeight = height;
+ height -= lastHeight;
+ lastHeight = newHeight;
+ encodeBuffer.encodeCachedValue(height, 16,
+ *clientCache_ -> polyFillArcCacheHeight[index], 8);
+ i += 2;
+ unsigned int angle1 = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newAngle1 = angle1;
+ angle1 -= lastAngle1;
+ lastAngle1 = newAngle1;
+ encodeBuffer.encodeCachedValue(angle1, 16,
+ *clientCache_ -> polyFillArcCacheAngle1[index], 8);
+ i += 2;
+ unsigned int angle2 = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newAngle2 = angle2;
+ angle2 -= lastAngle2;
+ lastAngle2 = newAngle2;
+ encodeBuffer.encodeCachedValue(angle2, 16,
+ *clientCache_ -> polyFillArcCacheAngle2[index], 8);
+ i += 2;
+
+ if (++index == 2) index = 0;
+
+ encodeBuffer.encodeBoolValue((i < inputLength) ? 1 : 0);
+ }
+ }
+ break;
+ case X_PolyArc:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_PolyArc target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_PolyArc target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_PolyArc target id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolyArc);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
+ bigEndian_), clientCache_ -> gcCache);
+
+ unsigned int index = 0;
+ unsigned int lastX = 0, lastY = 0;
+ unsigned int lastWidth = 0, lastHeight = 0;
+ unsigned int lastAngle1 = 0, lastAngle2 = 0;
+
+ //
+ // TODO: Could send the size at the beginning
+ // instead of a bool at each iteration.
+ //
+
+ for (unsigned int i = 12; i < inputLength;)
+ {
+ unsigned int x = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newX = x;
+ x -= lastX;
+ lastX = newX;
+ encodeBuffer.encodeCachedValue(x, 16,
+ *clientCache_ -> polyArcCacheX[index], 8);
+ i += 2;
+ unsigned int y = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newY = y;
+ y -= lastY;
+ lastY = newY;
+ encodeBuffer.encodeCachedValue(y, 16,
+ *clientCache_ -> polyArcCacheY[index], 8);
+ i += 2;
+ unsigned int width = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newWidth = width;
+ width -= lastWidth;
+ lastWidth = newWidth;
+ encodeBuffer.encodeCachedValue(width, 16,
+ *clientCache_ -> polyArcCacheWidth[index], 8);
+ i += 2;
+ unsigned int height = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newHeight = height;
+ height -= lastHeight;
+ lastHeight = newHeight;
+ encodeBuffer.encodeCachedValue(height, 16,
+ *clientCache_ -> polyArcCacheHeight[index], 8);
+ i += 2;
+ unsigned int angle1 = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newAngle1 = angle1;
+ angle1 -= lastAngle1;
+ lastAngle1 = newAngle1;
+ encodeBuffer.encodeCachedValue(angle1, 16,
+ *clientCache_ -> polyArcCacheAngle1[index], 8);
+ i += 2;
+ unsigned int angle2 = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newAngle2 = angle2;
+ angle2 -= lastAngle2;
+ lastAngle2 = newAngle2;
+ encodeBuffer.encodeCachedValue(angle2, 16,
+ *clientCache_ -> polyArcCacheAngle2[index], 8);
+ i += 2;
+
+ if (++index == 2) index = 0;
+
+ encodeBuffer.encodeBoolValue((i < inputLength) ? 1 : 0);
+ }
+ }
+ break;
+ case X_PolyPoint:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_PolyPoint target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_PolyPoint target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_PolyPoint target id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolyPoint);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 2, bigEndian_) - 3, 16, 4);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_),
+ clientCache_ -> gcCache);
+ const unsigned char *nextSrc = inputMessage + 12;
+
+ unsigned int index = 0;
+ unsigned int lastX = 0, lastY = 0;
+
+ for (unsigned int i = 12; i < inputLength; i += 4)
+ {
+ unsigned int x = GetUINT(nextSrc, bigEndian_);
+ nextSrc += 2;
+ unsigned int tmp = x;
+ x -= lastX;
+ lastX = tmp;
+ encodeBuffer.encodeCachedValue(x, 16,
+ *clientCache_ -> polyPointCacheX[index], 8);
+ unsigned int y = GetUINT(nextSrc, bigEndian_);
+ nextSrc += 2;
+ tmp = y;
+ y -= lastY;
+ lastY = tmp;
+ encodeBuffer.encodeCachedValue(y, 16,
+ *clientCache_ -> polyPointCacheY[index], 8);
+
+ if (++index == 2) index = 0;
+ }
+ }
+ break;
+ case X_PolyLine:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_PolyLine target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_PolyLine target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_PolyLine target id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolyLine);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 2, bigEndian_) - 3, 16, 4);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
+ bigEndian_), clientCache_ -> gcCache);
+ const unsigned char *nextSrc = inputMessage + 12;
+
+ unsigned int index = 0;
+ unsigned int lastX = 0, lastY = 0;
+
+ for (unsigned int i = 12; i < inputLength; i += 4)
+ {
+ unsigned int x = GetUINT(nextSrc, bigEndian_);
+ nextSrc += 2;
+ unsigned int tmp = x;
+ x -= lastX;
+ lastX = tmp;
+ encodeBuffer.encodeCachedValue(x, 16,
+ *clientCache_ -> polyLineCacheX[index], 8);
+ unsigned int y = GetUINT(nextSrc, bigEndian_);
+ nextSrc += 2;
+ tmp = y;
+ y -= lastY;
+ lastY = tmp;
+ encodeBuffer.encodeCachedValue(y, 16,
+ *clientCache_ -> polyLineCacheY[index], 8);
+
+ if (++index == 2) index = 0;
+ }
+ }
+ break;
+ case X_PolyRectangle:
+ {
+ encodeBuffer.encodeValue((GetUINT(inputMessage + 2,
+ bigEndian_) - 3) >> 1, 16, 3);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
+ bigEndian_), clientCache_ -> gcCache);
+ const unsigned char *end = inputMessage + inputLength;
+ const unsigned char *nextSrc = inputMessage + 12;
+ while (nextSrc < end)
+ {
+ for (unsigned int i = 0; i < 4; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16,
+ *clientCache_ -> polyRectangleGeomCache[i], 8);
+ nextSrc += 2;
+ }
+ }
+ }
+ break;
+ case X_PolySegment:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_PolySegment target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_PolySegment target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_PolySegment target id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolySegment);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeValue((GetUINT(inputMessage + 2,
+ bigEndian_) - 3) >> 1, 16, 4);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
+ bigEndian_), clientCache_ -> gcCache);
+ const unsigned char *end = inputMessage + inputLength;
+ const unsigned char *nextSrc = inputMessage + 12;
+ // unsigned int index = 0;
+ // unsigned int lastX1, lastY1, lastX2, lastY2;
+ while (nextSrc < end)
+ {
+ unsigned int x = GetUINT(nextSrc, bigEndian_);
+ nextSrc += 2;
+ unsigned int xDiff0 =
+ x - clientCache_ -> polySegmentLastX[0];
+ unsigned int xDiff1 =
+ x - clientCache_ -> polySegmentLastX[1];
+ int xDiff0Abs = (int) xDiff0;
+ if (xDiff0Abs < 0)
+ xDiff0Abs = -xDiff0Abs;
+ int xDiff1Abs = (int) xDiff1;
+ if (xDiff1Abs < 0)
+ xDiff1Abs = -xDiff1Abs;
+
+ unsigned int y = GetUINT(nextSrc, bigEndian_);
+ nextSrc += 2;
+ unsigned int yDiff0 =
+ y - clientCache_ -> polySegmentLastY[0];
+ unsigned int yDiff1 =
+ y - clientCache_ -> polySegmentLastY[1];
+ int yDiff0Abs = (int) yDiff0;
+ if (yDiff0Abs < 0)
+ yDiff0Abs = -yDiff0Abs;
+ int yDiff1Abs = (int) yDiff1;
+ if (yDiff1Abs < 0)
+ yDiff1Abs = -yDiff1Abs;
+
+ int diff0 = xDiff0Abs + yDiff0Abs;
+ int diff1 = xDiff1Abs + yDiff1Abs;
+ if (diff0 < diff1)
+ {
+ encodeBuffer.encodeBoolValue(0);
+ encodeBuffer.encodeCachedValue(xDiff0, 16,
+ clientCache_ -> polySegmentCacheX, 6);
+ encodeBuffer.encodeCachedValue(yDiff0, 16,
+ clientCache_ -> polySegmentCacheY, 6);
+ }
+ else
+ {
+ encodeBuffer.encodeBoolValue(1);
+ encodeBuffer.encodeCachedValue(xDiff1, 16,
+ clientCache_ -> polySegmentCacheX, 6);
+ encodeBuffer.encodeCachedValue(yDiff1, 16,
+ clientCache_ -> polySegmentCacheY, 6);
+ }
+
+ clientCache_ -> polySegmentLastX[clientCache_ -> polySegmentCacheIndex] = x;
+ clientCache_ -> polySegmentLastY[clientCache_ -> polySegmentCacheIndex] = y;
+
+ clientCache_ -> polySegmentCacheIndex =
+ clientCache_ -> polySegmentCacheIndex == 1 ? 0 : 1;
+ }
+ }
+ break;
+ case X_PutImage:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_PutImage target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_PutImage target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_PutImage target id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PutImage);
+
+ hit = handleEncode(encodeBuffer, clientCache_, messageStore,
+ inputOpcode, inputMessage, inputLength);
+ }
+ break;
+ case X_QueryBestSize:
+ {
+ encodeBuffer.encodeValue((unsigned int)inputMessage[1], 2);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> drawableCache);
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 8, bigEndian_), 16, 8);
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 10, bigEndian_), 16, 8);
+
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_QueryColors:
+ {
+ // Differential encoding.
+ encodeBuffer.encodeBoolValue(1);
+
+ unsigned int numColors = ((inputLength - 8) >> 2);
+ encodeBuffer.encodeValue(numColors, 16, 5);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29,
+ clientCache_ -> colormapCache);
+ const unsigned char *nextSrc = inputMessage + 8;
+ unsigned int predictedPixel = clientCache_ -> queryColorsLastPixel;
+ for (unsigned int i = 0; i < numColors; i++)
+ {
+ unsigned int pixel = GetULONG(nextSrc, bigEndian_);
+ nextSrc += 4;
+ if (pixel == predictedPixel)
+ encodeBuffer.encodeBoolValue(1);
+ else
+ {
+ encodeBuffer.encodeBoolValue(0);
+ encodeBuffer.encodeValue(pixel, 32, 9);
+ }
+ if (i == 0)
+ clientCache_ -> queryColorsLastPixel = pixel;
+ predictedPixel = pixel + 1;
+ }
+
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_QueryExtension:
+ {
+ #ifdef TEST
+
+ char data[256];
+
+ int length = GetUINT(inputMessage + 4, bigEndian_);
+
+ if (length > 256)
+ {
+ length = 256;
+ }
+
+ strncpy(data, (char *) inputMessage + 8, length);
+
+ *(data + length) = '\0';
+
+ *logofs << "handleRead: Going to query extension '"
+ << data << "' for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ unsigned int nameLength = GetUINT(inputMessage + 4, bigEndian_);
+ encodeBuffer.encodeValue(nameLength, 16, 6);
+ const unsigned char *nextSrc = inputMessage + 8;
+
+ for (; nameLength; nameLength--)
+ {
+ encodeBuffer.encodeValue((unsigned int) *nextSrc++, 8);
+ }
+
+ unsigned int extension = 0;
+
+ if (strncmp((char *) inputMessage + 8, "SHAPE", 5) == 0)
+ {
+ extension = X_NXInternalShapeExtension;
+ }
+ else if (strncmp((char *) inputMessage + 8, "RENDER", 6) == 0)
+ {
+ extension = X_NXInternalRenderExtension;
+ }
+
+ sequenceQueue_.push(clientSequence_, inputOpcode, extension);
+
+ priority_++;
+ }
+ break;
+ case X_QueryFont:
+ {
+ unsigned int font = GetULONG(inputMessage + 4, bigEndian_);
+ encodeBuffer.encodeValue(font - clientCache_ -> lastFont, 29, 5);
+ clientCache_ -> lastFont = font;
+
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_SetClipRectangles:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_SetClipRectangles);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ unsigned int numRectangles = ((inputLength - 12) >> 3);
+
+ if (control -> isProtoStep9() == 1)
+ {
+ encodeBuffer.encodeValue(numRectangles, 15, 4);
+ }
+ else
+ {
+ encodeBuffer.encodeValue(numRectangles, 13, 4);
+ }
+
+ encodeBuffer.encodeValue((unsigned int) inputMessage[1], 2);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> gcCache);
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 8, bigEndian_), 16,
+ clientCache_ -> setClipRectanglesXCache, 8);
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 10, bigEndian_), 16,
+ clientCache_ -> setClipRectanglesYCache, 8);
+ const unsigned char *nextSrc = inputMessage + 12;
+ for (unsigned int i = 0; i < numRectangles; i++)
+ {
+ for (unsigned int j = 0; j < 4; j++)
+ {
+ encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16,
+ *clientCache_ -> setClipRectanglesGeomCache[j], 8);
+ nextSrc += 2;
+ }
+ }
+ }
+ break;
+ case X_SetDashes:
+ {
+ unsigned int numDashes = GetUINT(inputMessage + 10, bigEndian_);
+ encodeBuffer.encodeCachedValue(numDashes, 16,
+ clientCache_ -> setDashesLengthCache, 5);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> gcCache);
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 8, bigEndian_), 16,
+ clientCache_ -> setDashesOffsetCache, 5);
+ const unsigned char *nextSrc = inputMessage + 12;
+ for (unsigned int i = 0; i < numDashes; i++)
+ encodeBuffer.encodeCachedValue(*nextSrc++, 8,
+ clientCache_ -> setDashesDashCache_[i & 1], 5);
+ }
+ break;
+ case X_SetSelectionOwner:
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29,
+ clientCache_ -> setSelectionOwnerCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), 29,
+ clientCache_ -> getSelectionOwnerSelectionCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), 32,
+ clientCache_ -> setSelectionOwnerTimestampCache, 9);
+ }
+ break;
+ case X_TranslateCoords:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_TranslateCoords source id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_TranslateCoords source id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_TranslateCoords source id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ t_id = GetULONG(inputMessage + 8, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_TranslateCoords target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_TranslateCoords target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_TranslateCoords target id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_TranslateCoords);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29,
+ clientCache_ -> translateCoordsSrcCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), 29,
+ clientCache_ -> translateCoordsDstCache, 9);
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 12, bigEndian_), 16,
+ clientCache_ -> translateCoordsXCache, 8);
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 14, bigEndian_), 16,
+ clientCache_ -> translateCoordsYCache, 8);
+
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_GetImage:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_GetImage source id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_GetImage source id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_GetImage source id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_GetImage);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+
+ hit = 1;
+
+ break;
+ }
+
+ // Format.
+ encodeBuffer.encodeValue((unsigned int) inputMessage[1], 2);
+ // Drawable.
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> drawableCache);
+ // X.
+ unsigned int x = GetUINT(inputMessage + 8, bigEndian_);
+ int xDiff = x - clientCache_ -> putImageLastX;
+ clientCache_ -> putImageLastX = x;
+ encodeBuffer.encodeCachedValue(xDiff, 16,
+ clientCache_ -> putImageXCache, 8);
+ // Y.
+ unsigned int y = GetUINT(inputMessage + 10, bigEndian_);
+ int yDiff = y - clientCache_ -> putImageLastY;
+ clientCache_ -> putImageLastY = y;
+ encodeBuffer.encodeCachedValue(yDiff, 16,
+ clientCache_ -> putImageYCache, 8);
+ // Width.
+ unsigned int width = GetUINT(inputMessage + 12, bigEndian_);
+ encodeBuffer.encodeCachedValue(width, 16,
+ clientCache_ -> putImageWidthCache, 8);
+ // Height.
+ unsigned int height = GetUINT(inputMessage + 14, bigEndian_);
+ encodeBuffer.encodeCachedValue(height, 16,
+ clientCache_ -> putImageHeightCache, 8);
+ // Plane mask.
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 16, bigEndian_), 32,
+ clientCache_ -> getImagePlaneMaskCache, 5);
+
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_GetPointerMapping:
+ {
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_GetKeyboardControl:
+ {
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ default:
+ {
+ if (inputOpcode == opcodeStore_ -> renderExtension)
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_NXInternalRenderExtension);
+
+ hit = handleEncode(encodeBuffer, clientCache_, messageStore,
+ inputOpcode, inputMessage, inputLength);
+ }
+ else if (inputOpcode == opcodeStore_ -> shapeExtension)
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_NXInternalShapeExtension);
+
+ hit = handleEncode(encodeBuffer, clientCache_, messageStore,
+ inputOpcode, inputMessage, inputLength);
+ }
+ else if (inputOpcode == opcodeStore_ -> putPackedImage)
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_NXPutPackedImage target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_NXPutPackedImage target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_NXPutPackedImage target id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ #endif
+
+ #ifdef DEBUG
+ *logofs << "handleRead: Encoding packed image request for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // The field carries the destination data
+ // length. We add the request's size of
+ // the final X_PutImage.
+ //
+
+ unsigned int outputLength = GetULONG(inputMessage + 20, bigEndian_) + 24;
+
+ statistics -> addPackedBytesIn(inputLength);
+
+ statistics -> addPackedBytesOut(outputLength);
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_NXPutPackedImage);
+
+ hit = handleEncode(encodeBuffer, clientCache_, messageStore,
+ inputOpcode, inputMessage, inputLength);
+ }
+ else if (inputOpcode == opcodeStore_ -> setUnpackColormap)
+ {
+ #ifdef DEBUG
+ *logofs << "handleRead: Encoding set unpack colormap request "
+ << "for FD#" << fd_ << " with size " << inputLength
+ << ".\n" << logofs_flush;
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_NXSetUnpackColormap);
+
+ hit = handleEncode(encodeBuffer, clientCache_, messageStore,
+ inputOpcode, inputMessage, inputLength);
+ }
+ else if (inputOpcode == opcodeStore_ -> setUnpackAlpha)
+ {
+ #ifdef DEBUG
+ *logofs << "handleRead: Encoding set unpack alpha request "
+ << "for FD#" << fd_ << " with size " << inputLength
+ << ".\n" << logofs_flush;
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_NXSetUnpackAlpha);
+
+ hit = handleEncode(encodeBuffer, clientCache_, messageStore,
+ inputOpcode, inputMessage, inputLength);
+ }
+ else if (inputOpcode == opcodeStore_ -> setUnpackGeometry)
+ {
+ #ifdef DEBUG
+ *logofs << "handleRead: Encoding set unpack geometry request "
+ << "for FD#" << fd_ << " with size " << inputLength
+ << ".\n" << logofs_flush;
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_NXSetUnpackGeometry);
+
+ hit = handleEncode(encodeBuffer, clientCache_, messageStore,
+ inputOpcode, inputMessage, inputLength);
+ }
+ else if (inputOpcode == opcodeStore_ -> startSplit)
+ {
+ if (handleStartSplitRequest(encodeBuffer, inputOpcode,
+ inputMessage, inputLength) < 0)
+ {
+ return -1;
+ }
+ }
+ else if (inputOpcode == opcodeStore_ -> endSplit)
+ {
+ if (handleEndSplitRequest(encodeBuffer, inputOpcode,
+ inputMessage, inputLength) < 0)
+ {
+ return -1;
+ }
+ }
+ else if (inputOpcode == opcodeStore_ -> commitSplit)
+ {
+ if (handleCommitSplitRequest(encodeBuffer, inputOpcode,
+ inputMessage, inputLength) < 0)
+ {
+ return -1;
+ }
+ }
+ else if (inputOpcode == opcodeStore_ -> abortSplit)
+ {
+ if (handleAbortSplitRequest(encodeBuffer, inputOpcode,
+ inputMessage, inputLength) < 0)
+ {
+ return -1;
+ }
+ }
+ else if (inputOpcode == opcodeStore_ -> finishSplit)
+ {
+ if (handleFinishSplitRequest(encodeBuffer, inputOpcode,
+ inputMessage, inputLength) < 0)
+ {
+ return -1;
+ }
+ }
+ else if (inputOpcode == opcodeStore_ -> freeSplit)
+ {
+ #ifdef DEBUG
+ *logofs << "handleRead: Encoding free split request "
+ << "for FD#" << fd_ << " with size " << inputLength
+ << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(*(inputMessage + 1), 8,
+ clientCache_ -> resourceCache);
+ }
+ else if (inputOpcode == opcodeStore_ -> freeUnpack)
+ {
+ #ifdef DEBUG
+ *logofs << "handleRead: Encoding free unpack request "
+ << "for FD#" << fd_ << " with size " << inputLength
+ << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(*(inputMessage + 1), 8,
+ clientCache_ -> resourceCache);
+ }
+ else if (inputOpcode == opcodeStore_ -> getControlParameters)
+ {
+ #ifdef DEBUG
+ *logofs << "handleRead: Encoding get control parameters "
+ << "request for FD#" << fd_ << " with size "
+ << inputLength << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Add the reply to the write buffer. If found
+ // to contain a message, it it will be flushed
+ // to the X client before leaving the loop.
+ //
+
+ unsigned char *reply = writeBuffer_.addMessage(32);
+
+ *(reply + 0) = X_Reply;
+
+ PutUINT(clientSequence_, reply + 2, bigEndian_);
+
+ PutULONG(0, reply + 4, bigEndian_);
+
+ //
+ // Save the sequence number we used
+ // to auto-generate this reply.
+ //
+
+ lastSequence_ = clientSequence_;
+
+ #ifdef TEST
+ *logofs << "handleRead: Registered " << lastSequence_
+ << " as last auto-generated sequence number.\n"
+ << logofs_flush;
+ #endif
+
+ *(reply + 1) = control -> LinkMode;
+
+ *(reply + 8) = control -> LocalVersionMajor;
+ *(reply + 9) = control -> LocalVersionMinor;
+ *(reply + 10) = control -> LocalVersionPatch;
+
+ *(reply + 11) = control -> RemoteVersionMajor;
+ *(reply + 12) = control -> RemoteVersionMinor;
+ *(reply + 13) = control -> RemoteVersionPatch;
+
+ PutUINT(control -> SplitTimeout, reply + 14, bigEndian_);
+ PutUINT(control -> MotionTimeout, reply + 16, bigEndian_);
+
+ *(reply + 18) = control -> SplitMode;
+
+ PutULONG(control -> SplitDataThreshold, reply + 20, bigEndian_);
+
+ *(reply + 24) = control -> PackMethod;
+ *(reply + 25) = control -> PackQuality;
+
+ *(reply + 26) = control -> LocalDataCompressionLevel;
+ *(reply + 27) = control -> LocalStreamCompressionLevel;
+ *(reply + 28) = control -> LocalDeltaCompression;
+
+ *(reply + 29) = (control -> LocalDeltaCompression == 1 &&
+ control -> PersistentCacheEnableLoad == 1);
+ *(reply + 30) = (control -> LocalDeltaCompression == 1 &&
+ control -> PersistentCacheEnableSave == 1);
+ *(reply + 31) = (control -> LocalDeltaCompression == 1 &&
+ control -> PersistentCacheEnableLoad == 1 &&
+ control -> PersistentCacheName != NULL);
+
+ if (handleFlush(flush_if_any) < 0)
+ {
+ return -1;
+ }
+ }
+ else if (inputOpcode == opcodeStore_ -> getCleanupParameters)
+ {
+ #ifdef WARNING
+ *logofs << "handleRead: WARNING! Encoding fake get cleanup "
+ << "parameters request for FD#" << fd_ << " with size "
+ << inputLength << ".\n" << logofs_flush;
+ #endif
+ }
+ else if (inputOpcode == opcodeStore_ -> getImageParameters)
+ {
+ #ifdef WARNING
+ *logofs << "handleRead: WARNING! Encoding fake get cleanup "
+ << "parameters request for FD#" << fd_ << " with size "
+ << inputLength << ".\n" << logofs_flush;
+ #endif
+ }
+ else if (inputOpcode == opcodeStore_ -> getUnpackParameters)
+ {
+ #ifdef DEBUG
+ *logofs << "handleRead: Encoding get unpack parameters "
+ << "request for FD#" << fd_ << " with size "
+ << inputLength << ".\n" << logofs_flush;
+ #endif
+
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+ }
+ else if (inputOpcode == opcodeStore_ -> getShmemParameters)
+ {
+ if (handleShmemRequest(encodeBuffer, inputOpcode,
+ inputMessage, inputLength) < 0)
+ {
+ return -1;
+ }
+ }
+ else if (inputOpcode == opcodeStore_ -> setExposeParameters)
+ {
+ //
+ // Enable or disable expose events
+ // coming from the real server.
+ //
+
+ encodeBuffer.encodeBoolValue(*(inputMessage + 4));
+ encodeBuffer.encodeBoolValue(*(inputMessage + 5));
+ encodeBuffer.encodeBoolValue(*(inputMessage + 6));
+ }
+ else if (inputOpcode == opcodeStore_ -> setCacheParameters)
+ {
+ if (handleCacheRequest(encodeBuffer, inputOpcode,
+ inputMessage, inputLength) < 0)
+ {
+ return -1;
+ }
+ }
+ else if (inputOpcode == opcodeStore_ -> getFontParameters)
+ {
+ if (handleFontRequest(encodeBuffer, inputOpcode,
+ inputMessage, inputLength) < 0)
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_NXInternalGenericRequest);
+
+ hit = handleEncode(encodeBuffer, clientCache_, messageStore,
+ inputOpcode, inputMessage, inputLength);
+
+ //
+ // Don't flush if the opcode is unrecognized.
+ // We may optionally flush it is an extension
+ // but would penalize the well written clients.
+ //
+ // if (inputOpcode > 127)
+ // {
+ // priority_++;
+ // }
+ //
+ }
+ }
+ } // End of switch on opcode.
+
+ int bits = encodeBuffer.diffBits();
+
+ #if defined(TEST) || defined(OPCODES)
+
+ const char *cacheString = (hit ? "cached " : "");
+
+ *logofs << "handleRead: Handled " << cacheString << "request OPCODE#"
+ << (unsigned int) inputOpcode << " (" << DumpOpcode(inputOpcode)
+ << ")" << " for FD#" << fd_ << " sequence " << clientSequence_
+ << ". " << inputLength << " bytes in, " << bits << " bits ("
+ << ((float) bits) / 8 << " bytes) out.\n" << logofs_flush;
+
+ #endif
+
+ if (hit)
+ {
+ statistics -> addCachedRequest(inputOpcode);
+ }
+
+ statistics -> addRequestBits(inputOpcode, inputLength << 3, bits);
+
+ if (inputOpcode == opcodeStore_ -> renderExtension)
+ {
+ if (hit)
+ {
+ statistics -> addRenderCachedRequest(*(inputMessage + 1));
+ }
+
+ statistics -> addRenderRequestBits(*(inputMessage + 1), inputLength << 3, bits);
+ }
+
+ } // End if (firstRequest_)... else ...
+
+ } // End of while ((inputMessage = readBuffer_.getMessage(inputLength)) != 0) ...
+
+ //
+ // Check if we need to flush because of
+ // prioritized data.
+ //
+
+ if (priority_ > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleRead: WARNING! Requesting flush "
+ << "because of " << priority_ << " prioritized "
+ << "messages for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (proxy -> handleAsyncPriority() < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Reset the priority flag.
+ //
+
+ priority_ = 0;
+ }
+
+ //
+ // Flush if we exceeded the token length.
+ //
+
+ if (proxy -> canAsyncFlush() == 1)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleRead: WARNING! Requesting flush "
+ << "because of token length exceeded.\n"
+ << logofs_flush;
+ #endif
+
+ if (proxy -> handleAsyncFlush() < 0)
+ {
+ return -1;
+ }
+ }
+
+ #if defined(TEST) || defined(INFO)
+
+ if (transport_ -> pending() != 0 ||
+ readBuffer_.checkMessage() != 0)
+ {
+ *logofs << "handleRead: PANIC! Buffer for X descriptor FD#"
+ << fd_ << " has " << transport_ -> pending()
+ << " bytes to read.\n" << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ //
+ // Reset the read buffer.
+ //
+
+ readBuffer_.fullReset();
+
+ return 1;
+}
+
+//
+// End of handleRead().
+//
+
+//
+// Beginning of handleWrite().
+//
+
+int ClientChannel::handleWrite(const unsigned char *message, unsigned int length)
+{
+ #ifdef TEST
+ *logofs << "handleWrite: Called for FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Create the buffer from which to
+ // decode messages.
+ //
+
+ DecodeBuffer decodeBuffer(message, length);
+
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+ *logofs << "handleWrite: Decoding messages for FD#" << fd_
+ << " with " << length << " bytes in the buffer.\n"
+ << logofs_flush;
+ #endif
+
+ if (firstReply_)
+ {
+ #ifdef TEST
+ *logofs << "handleWrite: First reply detected.\n" << logofs_flush;
+ #endif
+
+ unsigned int outputOpcode;
+
+ decodeBuffer.decodeValue(outputOpcode, 8);
+ unsigned int secondByte;
+ decodeBuffer.decodeValue(secondByte, 8);
+ unsigned int major;
+ decodeBuffer.decodeValue(major, 16);
+ unsigned int minor;
+ decodeBuffer.decodeValue(minor, 16);
+ unsigned int extraLength;
+ decodeBuffer.decodeValue(extraLength, 16);
+ unsigned int outputLength = 8 + (extraLength << 2);
+
+ unsigned char *outputMessage = writeBuffer_.addMessage(outputLength);
+ *outputMessage = (unsigned char) outputOpcode;
+ outputMessage[1] = (unsigned char) secondByte;
+ PutUINT(major, outputMessage + 2, bigEndian_);
+ PutUINT(minor, outputMessage + 4, bigEndian_);
+ PutUINT(extraLength, outputMessage + 6, bigEndian_);
+ unsigned char *nextDest = outputMessage + 8;
+ unsigned int cached;
+ decodeBuffer.decodeBoolValue(cached);
+
+ if (cached)
+ {
+ memcpy(nextDest, ServerCache::lastInitReply.getData(), outputLength - 8);
+ }
+ else
+ {
+ for (unsigned i = 8; i < outputLength; i++)
+ {
+ unsigned int nextByte;
+ decodeBuffer.decodeValue(nextByte, 8);
+ *nextDest++ = (unsigned char) nextByte;
+ }
+
+ ServerCache::lastInitReply.set(outputLength - 8, outputMessage + 8);
+ }
+
+ imageByteOrder_ = outputMessage[30];
+ bitmapBitOrder_ = outputMessage[31];
+ scanlineUnit_ = outputMessage[32];
+ scanlinePad_ = outputMessage[33];
+
+ firstReply_ = 0;
+
+ } // End of if (firstReply_)
+
+ //
+ // This was previously in a 'else' block.
+ // Due to the way the first request was
+ // handled, we could not decode multiple
+ // messages in the first frame.
+ //
+
+ { // Start of the decoding block.
+
+ #ifdef DEBUG
+ *logofs << "handleWrite: Starting loop on opcodes.\n"
+ << logofs_flush;
+ #endif
+
+ unsigned char outputOpcode;
+
+ //
+ // NX client needs this line to consider
+ // the initialization phase successfully
+ // completed.
+ //
+
+ if (firstClient_ == -1)
+ {
+ cerr << "Info" << ": Established X client connection.\n" ;
+
+ firstClient_ = fd_;
+ }
+
+ while (decodeBuffer.decodeOpcodeValue(outputOpcode, serverCache_ -> opcodeCache, 1))
+ {
+ #ifdef DEBUG
+ *logofs << "handleWrite: Decoded a new OPCODE#"
+ << (unsigned int) outputOpcode << ".\n"
+ << logofs_flush;
+ #endif
+
+ unsigned char *outputMessage = NULL;
+ unsigned int outputLength = 0;
+
+ //
+ // General-purpose temp variables
+ // for decoding ints and chars.
+ //
+
+ unsigned int value = 0;
+ unsigned char cValue = 0;
+
+ //
+ // Check first if we need to abort any split,
+ // then if this is a reply, finally if it is
+ // en event or error.
+ //
+
+ if (outputOpcode == opcodeStore_ -> splitEvent)
+ {
+ //
+ // It's an abort split, not a normal
+ // burst of proxy data.
+ //
+
+ handleSplitEvent(decodeBuffer);
+
+ continue;
+ }
+ else if (outputOpcode == X_Reply)
+ {
+ #ifdef DEBUG
+ *logofs << "handleWrite: Decoding sequence number of reply.\n"
+ << logofs_flush;
+ #endif
+
+ unsigned int sequenceNum;
+ unsigned int sequenceDiff;
+
+ decodeBuffer.decodeCachedValue(sequenceDiff, 16,
+ serverCache_ -> replySequenceCache, 7);
+
+ sequenceNum = (serverSequence_ + sequenceDiff) & 0xffff;
+
+ serverSequence_ = sequenceNum;
+
+ #ifdef DEBUG
+ *logofs << "handleWrite: Last server sequence number for FD#"
+ << fd_ << " is " << serverSequence_ << " with "
+ << "difference " << sequenceDiff << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // In case of reply we can follow the X server and
+ // override any event's sequence number generated
+ // by this side.
+ //
+
+ #ifdef TEST
+ *logofs << "handleWrite: Updating last event's sequence "
+ << lastSequence_ << " to reply's sequence number "
+ << serverSequence_ << " for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ lastSequence_ = serverSequence_;
+
+ unsigned short int requestSequenceNum;
+ unsigned char requestOpcode;
+
+ #ifdef DEBUG
+
+ requestSequenceNum = 0;
+ requestOpcode = 0;
+
+ *logofs << "handleWrite: Peek of sequence number returns ";
+
+ *logofs << sequenceQueue_.peek(requestSequenceNum, requestOpcode);
+
+ *logofs << " with sequence " << requestSequenceNum << " and opcode "
+ << (unsigned int) requestOpcode << ".\n" << logofs_flush;
+
+ #endif
+
+ if (sequenceQueue_.peek(requestSequenceNum, requestOpcode) == 1 &&
+ (requestSequenceNum == sequenceNum))
+ {
+ unsigned int requestData[3];
+
+ sequenceQueue_.pop(requestSequenceNum, requestOpcode,
+ requestData[0], requestData[1], requestData[2]);
+
+ #ifdef DEBUG
+ *logofs << "handleWrite: Identified reply to OPCODE#"
+ << (unsigned int) requestOpcode << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Is differential encoding disabled?
+ //
+
+ if (control -> RemoteDeltaCompression == 0)
+ {
+ int result = handleFastWriteReply(decodeBuffer, requestOpcode,
+ outputMessage, outputLength);
+ if (result < 0)
+ {
+ return -1;
+ }
+ else if (result > 0)
+ {
+ continue;
+ }
+ }
+
+ switch (requestOpcode)
+ {
+ case X_AllocColor:
+ {
+ outputLength = 32;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ unsigned char *nextDest = outputMessage + 8;
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ decodeBuffer.decodeBoolValue(value);
+ if (value)
+ {
+ PutUINT(requestData[i], nextDest, bigEndian_);
+ }
+ else
+ {
+ decodeBuffer.decodeValue(value, 16, 6);
+ PutUINT(requestData[i] + value, nextDest, bigEndian_);
+ }
+ nextDest += 2;
+ }
+ decodeBuffer.decodeValue(value, 32, 9);
+ PutULONG(value, outputMessage + 16, bigEndian_);
+ }
+ break;
+ case X_GetAtomName:
+ {
+ unsigned int nameLength;
+ decodeBuffer.decodeValue(nameLength, 16, 6);
+ outputLength = RoundUp4(nameLength) + 32;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ PutUINT(nameLength, outputMessage + 8, bigEndian_);
+ unsigned char* nextDest = outputMessage + 32;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ decodeBuffer.decodeTextData(nextDest, nameLength);
+ }
+ else
+ {
+ serverCache_ -> getAtomNameTextCompressor.reset();
+ for (unsigned int i = 0; i < nameLength; i++)
+ {
+ *nextDest++ = serverCache_ -> getAtomNameTextCompressor.
+ decodeChar(decodeBuffer);
+ }
+ }
+ }
+ break;
+ case X_GetGeometry:
+ {
+ outputLength = 32;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ serverCache_ -> depthCache);
+ outputMessage[1] = cValue;
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> getGeometryRootCache, 9);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ unsigned char *nextDest = outputMessage + 12;
+ for (unsigned int i = 0; i < 5; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *serverCache_ -> getGeometryGeomCache[i], 8);
+ PutUINT(value, nextDest, bigEndian_);
+ nextDest += 2;
+ }
+ }
+ break;
+ case X_GetInputFocus:
+ {
+ outputLength = 32;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeValue(value, 2);
+ outputMessage[1] = (unsigned char) value;
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> getInputFocusWindowCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ }
+ break;
+ case X_GetKeyboardMapping:
+ {
+ decodeBuffer.decodeBoolValue(value);
+ if (value)
+ {
+ unsigned int dataLength =
+ ServerCache::getKeyboardMappingLastMap.getLength();
+ outputLength = 32 + dataLength;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ outputMessage[1] =
+ ServerCache::getKeyboardMappingLastKeysymsPerKeycode;
+ memcpy(outputMessage + 32,
+ ServerCache::getKeyboardMappingLastMap.getData(),
+ dataLength);
+ break;
+ }
+ unsigned int numKeycodes;
+ decodeBuffer.decodeValue(numKeycodes, 8);
+ unsigned int keysymsPerKeycode;
+ decodeBuffer.decodeValue(keysymsPerKeycode, 8, 4);
+ ServerCache::getKeyboardMappingLastKeysymsPerKeycode =
+ keysymsPerKeycode;
+ outputLength = 32 + numKeycodes * keysymsPerKeycode * 4;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ outputMessage[1] = (unsigned char) keysymsPerKeycode;
+ unsigned char *nextDest = outputMessage + 32;
+ unsigned char previous = 0;
+ for (unsigned int count = numKeycodes * keysymsPerKeycode;
+ count; --count)
+ {
+ decodeBuffer.decodeBoolValue(value);
+ if (value)
+ PutULONG((unsigned int) NoSymbol, nextDest, bigEndian_);
+ else
+ {
+ unsigned int keysym;
+ decodeBuffer.decodeCachedValue(keysym, 24,
+ serverCache_ -> getKeyboardMappingKeysymCache, 9);
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ serverCache_ -> getKeyboardMappingLastByteCache, 5);
+ previous += cValue;
+ PutULONG((keysym << 8) | previous, nextDest, bigEndian_);
+ }
+ nextDest += 4;
+ }
+ ServerCache::getKeyboardMappingLastMap.set(outputLength - 32,
+ outputMessage + 32);
+ }
+ break;
+ case X_GetModifierMapping:
+ {
+ unsigned int keycodesPerModifier;
+ decodeBuffer.decodeValue(keycodesPerModifier, 8);
+ outputLength = 32 + (keycodesPerModifier << 3);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ outputMessage[1] = (unsigned char) keycodesPerModifier;
+ unsigned char *nextDest = outputMessage + 32;
+ decodeBuffer.decodeBoolValue(value);
+ if (value)
+ {
+ memcpy(outputMessage + 32,
+ ServerCache::getModifierMappingLastMap.getData(),
+ ServerCache::getModifierMappingLastMap.getLength());
+ break;
+ }
+ for (unsigned int count = outputLength - 32; count; count--)
+ {
+ decodeBuffer.decodeBoolValue(value);
+ if (value)
+ *nextDest++ = 0;
+ else
+ {
+ decodeBuffer.decodeValue(value, 8);
+ *nextDest++ = value;
+ }
+ }
+ ServerCache::getModifierMappingLastMap.set(outputLength - 32,
+ outputMessage + 32);
+ }
+ break;
+ case X_GetProperty:
+ {
+ MessageStore *messageStore = serverStore_ ->
+ getReplyStore(X_GetProperty);
+
+ handleDecode(decodeBuffer, serverCache_, messageStore,
+ requestOpcode, outputMessage, outputLength);
+ }
+ break;
+ case X_GetSelectionOwner:
+ {
+ outputLength = 32;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> getSelectionOwnerCache, 9);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ }
+ break;
+ case X_GetWindowAttributes:
+ {
+ outputLength = 44;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeValue(value, 2);
+ outputMessage[1] = (unsigned char) value;
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> visualCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> getWindowAttributesClassCache, 3);
+ PutUINT(value, outputMessage + 12, bigEndian_);
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ serverCache_ -> getWindowAttributesBitGravityCache);
+ outputMessage[14] = cValue;
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ serverCache_ -> getWindowAttributesWinGravityCache);
+ outputMessage[15] = cValue;
+ decodeBuffer.decodeCachedValue(value, 32,
+ serverCache_ -> getWindowAttributesPlanesCache, 9);
+ PutULONG(value, outputMessage + 16, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 32,
+ serverCache_ -> getWindowAttributesPixelCache, 9);
+ PutULONG(value, outputMessage + 20, bigEndian_);
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[24] = (unsigned char) value;
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[25] = (unsigned char) value;
+ decodeBuffer.decodeValue(value, 2);
+ outputMessage[26] = (unsigned char) value;
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[27] = (unsigned char) value;
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> colormapCache, 9);
+ PutULONG(value, outputMessage + 28, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 32,
+ serverCache_ -> getWindowAttributesAllEventsCache);
+ PutULONG(value, outputMessage + 32, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 32,
+ serverCache_ -> getWindowAttributesYourEventsCache);
+ PutULONG(value, outputMessage + 36, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> getWindowAttributesDontPropagateCache);
+ PutUINT(value, outputMessage + 40, bigEndian_);
+ }
+ break;
+ case X_GrabKeyboard:
+ case X_GrabPointer:
+ {
+ outputLength = 32;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeValue(value, 3);
+ outputMessage[1] = (unsigned char) value;
+ }
+ break;
+ case X_InternAtom:
+ {
+ outputLength = 32;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeValue(value, 29, 9);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ }
+ break;
+ case X_ListExtensions:
+ {
+ decodeBuffer.decodeValue(value, 32, 8);
+ outputLength = 32 + (value << 2);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ unsigned int numExtensions;
+ decodeBuffer.decodeValue(numExtensions, 8);
+ outputMessage[1] = (unsigned char) numExtensions;
+ unsigned char *nextDest = outputMessage + 32;
+ for (; numExtensions; numExtensions--)
+ {
+ unsigned int length;
+ decodeBuffer.decodeValue(length, 8);
+ *nextDest++ = (unsigned char) length;
+ for (; length; length--)
+ {
+ decodeBuffer.decodeValue(value, 8);
+ *nextDest++ = value;
+ }
+ }
+ }
+ break;
+ case X_ListFonts:
+ {
+ //
+ // Differential compression can achieve a 12:1 to 14:1
+ // ratio, while the best ZLIB compression can achieve
+ // a mere 4:1 to 5:1. In the first case, though, the
+ // huge amount of data constituting the message would
+ // be stored uncompressed at the remote side. We need
+ // to find a compromise. The solution is to use diffe-
+ // rential compression at startup and ZLIB compression
+ // later on.
+ //
+
+ MessageStore *messageStore = serverStore_ ->
+ getReplyStore(X_ListFonts);
+
+ if (handleDecodeCached(decodeBuffer, serverCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ decodeBuffer.decodeValue(value, 32, 8);
+ outputLength = 32 + (value << 2);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ unsigned int numFonts;
+ decodeBuffer.decodeValue(numFonts, 16, 6);
+ PutUINT(numFonts, outputMessage + 8, bigEndian_);
+
+ // Differential or plain data compression?
+ decodeBuffer.decodeBoolValue(value);
+
+ if (value)
+ {
+ unsigned char* nextDest = outputMessage + 32;
+ for (; numFonts; numFonts--)
+ {
+ unsigned int length;
+ decodeBuffer.decodeValue(length, 8);
+ *nextDest++ = (unsigned char)length;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ decodeBuffer.decodeTextData(nextDest, length);
+
+ nextDest += length;
+ }
+ else
+ {
+ serverCache_ -> getPropertyTextCompressor.reset();
+ for (; length; length--)
+ {
+ *nextDest++ = serverCache_ -> getPropertyTextCompressor.
+ decodeChar(decodeBuffer);
+ }
+ }
+ }
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ else
+ {
+ const unsigned char *compressedData = NULL;
+ unsigned int compressedDataSize = 0;
+
+ int decompressed = handleDecompress(decodeBuffer, requestOpcode, messageStore -> dataOffset,
+ outputMessage, outputLength, compressedData,
+ compressedDataSize);
+ if (decompressed < 0)
+ {
+ return -1;
+ }
+ else if (decompressed > 0)
+ {
+ handleSave(messageStore, outputMessage, outputLength,
+ compressedData, compressedDataSize);
+ }
+ else
+ {
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ }
+ }
+ break;
+ case X_LookupColor:
+ case X_AllocNamedColor:
+ {
+ outputLength = 32;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ unsigned char *nextDest = outputMessage + 8;
+ if (requestOpcode == X_AllocNamedColor)
+ {
+ decodeBuffer.decodeValue(value, 32, 9);
+ PutULONG(value, nextDest, bigEndian_);
+ nextDest += 4;
+ }
+ unsigned int count = 3;
+ do
+ {
+ decodeBuffer.decodeValue(value, 16, 9);
+ PutUINT(value, nextDest, bigEndian_);
+ unsigned int visualColor;
+ decodeBuffer.decodeValue(visualColor, 16, 5);
+ visualColor += value;
+ visualColor &= 0xffff;
+ PutUINT(visualColor, nextDest + 6, bigEndian_);
+ nextDest += 2;
+ }
+ while (--count);
+ }
+ break;
+ case X_QueryBestSize:
+ {
+ outputLength = 32;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeValue(value, 16, 8);
+ PutUINT(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeValue(value, 16, 8);
+ PutUINT(value, outputMessage + 10, bigEndian_);
+ }
+ break;
+ case X_QueryColors:
+ {
+ // Differential or plain data compression?
+ decodeBuffer.decodeBoolValue(value);
+
+ if (value)
+ {
+ decodeBuffer.decodeBoolValue(value);
+ if (value)
+ {
+ unsigned int numColors =
+ serverCache_ -> queryColorsLastReply.getLength() / 6;
+ outputLength = 32 + (numColors << 3);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ PutUINT(numColors, outputMessage + 8, bigEndian_);
+ const unsigned char *nextSrc =
+ serverCache_ -> queryColorsLastReply.getData();
+ unsigned char *nextDest = outputMessage + 32;
+ for (; numColors; numColors--)
+ {
+ for (unsigned int i = 0; i < 6; i++)
+ *nextDest++ = *nextSrc++;
+ nextDest += 2;
+ }
+ }
+ else
+ {
+ unsigned int numColors;
+ decodeBuffer.decodeValue(numColors, 16, 5);
+ outputLength = 32 + (numColors << 3);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ PutUINT(numColors, outputMessage + 8, bigEndian_);
+ unsigned char *nextDest = outputMessage + 32;
+ for (unsigned int c = 0; c < numColors; c++)
+ {
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ decodeBuffer.decodeValue(value, 16);
+ PutUINT(value, nextDest, bigEndian_);
+ nextDest += 2;
+ }
+ }
+ serverCache_ -> queryColorsLastReply.set(numColors * 6,
+ outputMessage + 32);
+ const unsigned char *nextSrc = nextDest - 1;
+ nextDest = outputMessage + 32 + ((numColors - 1) << 3) + 5;
+ for (; numColors > 1; numColors--)
+ {
+ for (unsigned int i = 0; i < 6; i++)
+ *nextDest-- = *nextSrc--;
+ nextDest -= 2;
+ }
+ }
+ }
+ else
+ {
+ // Reply length.
+ unsigned int numColors;
+ decodeBuffer.decodeValue(numColors, 16, 5);
+ outputLength = 32 + (numColors << 3);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ PutUINT(numColors, outputMessage + 8, bigEndian_);
+
+ const unsigned char *compressedData = NULL;
+ unsigned int compressedDataSize = 0;
+
+ int decompressed = handleDecompress(decodeBuffer, requestOpcode, 32,
+ outputMessage, outputLength, compressedData,
+ compressedDataSize);
+ if (decompressed < 0)
+ {
+ return -1;
+ }
+ }
+ }
+ break;
+ case X_QueryExtension:
+ {
+ outputLength = 32;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[8] = (unsigned char) value;
+ decodeBuffer.decodeValue(value, 8);
+ outputMessage[9] = (unsigned char) value;
+ decodeBuffer.decodeValue(value, 8);
+ outputMessage[10] = (unsigned char) value;
+ decodeBuffer.decodeValue(value, 8);
+ outputMessage[11] = (unsigned char) value;
+
+ //
+ // We use a predefined opcode to address
+ // extensions' message stores, while real
+ // opcodes are used for communication with
+ // X server and clients.
+ //
+
+ if (requestData[0] == X_NXInternalShapeExtension)
+ {
+ opcodeStore_ -> shapeExtension = outputMessage[9];
+
+ #ifdef TEST
+ *logofs << "handleWrite: Shape extension opcode for FD#" << fd_
+ << " is " << (unsigned int) opcodeStore_ -> shapeExtension
+ << ".\n" << logofs_flush;
+ #endif
+ }
+ else if (requestData[0] == X_NXInternalRenderExtension)
+ {
+ opcodeStore_ -> renderExtension = outputMessage[9];
+
+ #ifdef TEST
+ *logofs << "handleWrite: Render extension opcode for FD#" << fd_
+ << " is " << (unsigned int) opcodeStore_ -> renderExtension
+ << ".\n" << logofs_flush;
+ #endif
+ }
+ }
+ break;
+ case X_QueryFont:
+ {
+ //
+ // Use differential compression at startup and plain
+ // data compression later. Check X_ListFonts message
+ // for an explaination.
+ //
+
+ MessageStore *messageStore = serverStore_ ->
+ getReplyStore(X_QueryFont);
+
+ if (handleDecodeCached(decodeBuffer, serverCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ // Differential or plain data compression?
+ decodeBuffer.decodeBoolValue(value);
+
+ if (value)
+ {
+ unsigned int numProperties;
+ unsigned int numCharInfos;
+ decodeBuffer.decodeValue(numProperties, 16, 8);
+ decodeBuffer.decodeValue(numCharInfos, 32, 10);
+ outputLength = 60 + numProperties * 8 + numCharInfos * 12;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ PutUINT(numProperties, outputMessage + 46, bigEndian_);
+ PutULONG(numCharInfos, outputMessage + 56, bigEndian_);
+ handleDecodeCharInfo(decodeBuffer, outputMessage + 8);
+ handleDecodeCharInfo(decodeBuffer, outputMessage + 24);
+ decodeBuffer.decodeValue(value, 16, 9);
+ PutUINT(value, outputMessage + 40, bigEndian_);
+ decodeBuffer.decodeValue(value, 16, 9);
+ PutUINT(value, outputMessage + 42, bigEndian_);
+ decodeBuffer.decodeValue(value, 16, 9);
+ PutUINT(value, outputMessage + 44, bigEndian_);
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[48] = (unsigned char) value;
+ decodeBuffer.decodeValue(value, 8);
+ outputMessage[49] = (unsigned char) value;
+ decodeBuffer.decodeValue(value, 8);
+ outputMessage[50] = (unsigned char) value;
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[51] = (unsigned char) value;
+ decodeBuffer.decodeValue(value, 16, 9);
+ PutUINT(value, outputMessage + 52, bigEndian_);
+ decodeBuffer.decodeValue(value, 16, 9);
+ PutUINT(value, outputMessage + 54, bigEndian_);
+ unsigned char *nextDest = outputMessage + 60;
+ decodeBuffer.decodeBoolValue(value);
+
+ int end = 0;
+
+ if (value == 1)
+ {
+ unsigned int index;
+ decodeBuffer.decodeValue(index, 4);
+ unsigned int length;
+ const unsigned char *data;
+ ServerCache::queryFontFontCache.get(index, length, data);
+ memcpy(nextDest, data, length);
+
+ end = 1;
+ }
+
+ if (end == 0)
+ {
+ unsigned char *saveDest = nextDest;
+ unsigned int length = numProperties * 8 + numCharInfos * 12;
+ for (; numProperties; numProperties--)
+ {
+ decodeBuffer.decodeValue(value, 32, 9);
+ PutULONG(value, nextDest, bigEndian_);
+ decodeBuffer.decodeValue(value, 32, 9);
+ PutULONG(value, nextDest + 4, bigEndian_);
+ nextDest += 8;
+ }
+ for (; numCharInfos; numCharInfos--)
+ {
+ handleDecodeCharInfo(decodeBuffer, nextDest);
+
+ nextDest += 12;
+ }
+ ServerCache::queryFontFontCache.set(length, saveDest);
+ }
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ else
+ {
+ // Reply length.
+ unsigned int replyLength;
+ decodeBuffer.decodeValue(replyLength, 32, 16);
+ outputLength = 32 + (replyLength << 2);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+
+ const unsigned char *compressedData = NULL;
+ unsigned int compressedDataSize = 0;
+
+ int decompressed = handleDecompress(decodeBuffer, requestOpcode, messageStore -> dataOffset,
+ outputMessage, outputLength, compressedData,
+ compressedDataSize);
+ if (decompressed < 0)
+ {
+ return -1;
+ }
+ else if (decompressed > 0)
+ {
+ handleSave(messageStore, outputMessage, outputLength,
+ compressedData, compressedDataSize);
+ }
+ else
+ {
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ }
+ }
+ break;
+ case X_QueryPointer:
+ {
+ outputLength = 32;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[1] = (unsigned char) value;
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> queryPointerRootCache, 9);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> queryPointerChildCache, 9);
+ PutULONG(value, outputMessage + 12, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> motionNotifyRootXCache, 8);
+ serverCache_ -> motionNotifyLastRootX += value;
+ PutUINT(serverCache_ -> motionNotifyLastRootX, outputMessage + 16,
+ bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> motionNotifyRootYCache, 8);
+ serverCache_ -> motionNotifyLastRootY += value;
+ PutUINT(serverCache_ -> motionNotifyLastRootY, outputMessage + 18,
+ bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> motionNotifyEventXCache, 8);
+ PutUINT(serverCache_ -> motionNotifyLastRootX + value,
+ outputMessage + 20, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> motionNotifyEventYCache, 8);
+ PutUINT(serverCache_ -> motionNotifyLastRootY + value,
+ outputMessage + 22, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> motionNotifyStateCache);
+ PutUINT(value, outputMessage + 24, bigEndian_);
+ }
+ break;
+ case X_QueryTree:
+ {
+ unsigned int children;
+ decodeBuffer.decodeValue(children, 16, 8);
+
+ outputLength = 32 + (children << 2);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+
+ PutULONG(outputLength, outputMessage + 4, bigEndian_);
+
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> queryTreeWindowCache);
+
+ PutULONG(value, outputMessage + 8, bigEndian_);
+
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> queryTreeWindowCache);
+
+ PutULONG(value, outputMessage + 12, bigEndian_);
+
+ unsigned char *next = outputMessage + 32;
+
+ PutUINT(children, outputMessage + 16, bigEndian_);
+
+ for (unsigned int i = 0; i < children; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> queryTreeWindowCache);
+
+ PutULONG(value, next + (i * 4), bigEndian_);
+ }
+ }
+ break;
+ case X_TranslateCoords:
+ {
+ outputLength = 32;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[1] = (unsigned char) value;
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> translateCoordsChildCache, 9);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> translateCoordsXCache, 8);
+ PutUINT(value, outputMessage + 12, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> translateCoordsYCache, 8);
+ PutUINT(value, outputMessage + 14, bigEndian_);
+ }
+ break;
+ case X_GetImage:
+ {
+ MessageStore *messageStore = serverStore_ ->
+ getReplyStore(X_GetImage);
+
+ if (handleDecodeCached(decodeBuffer, serverCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ // Depth.
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ serverCache_ -> depthCache);
+ // Reply length.
+ unsigned int replyLength;
+ decodeBuffer.decodeValue(replyLength, 32, 9);
+ outputLength = 32 + (replyLength << 2);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ outputMessage[1] = (unsigned char) cValue;
+ // Visual.
+ unsigned int visual;
+ decodeBuffer.decodeCachedValue(visual, 29,
+ serverCache_ -> visualCache);
+ PutULONG(visual, outputMessage + 8, bigEndian_);
+
+ if (control -> isProtoStep8() == 0)
+ {
+ const unsigned char *compressedData = NULL;
+ unsigned int compressedDataSize = 0;
+
+ int decompressed = handleDecompress(decodeBuffer, requestOpcode, messageStore -> dataOffset,
+ outputMessage, outputLength, compressedData,
+ compressedDataSize);
+ if (decompressed < 0)
+ {
+ return -1;
+ }
+ else if (decompressed > 0)
+ {
+ handleSave(messageStore, outputMessage, outputLength,
+ compressedData, compressedDataSize);
+ }
+ else
+ {
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ }
+ else
+ {
+ handleCopy(decodeBuffer, requestOpcode, messageStore ->
+ dataOffset, outputMessage, outputLength);
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ }
+ break;
+ case X_GetPointerMapping:
+ {
+ unsigned int nextByte;
+ decodeBuffer.decodeValue(nextByte, 8, 4);
+ unsigned int replyLength;
+ decodeBuffer.decodeValue(replyLength, 32, 4);
+ outputLength = 32 + (replyLength << 2);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ outputMessage[1] = (unsigned char) nextByte;
+ unsigned char *nextDest = outputMessage + 32;
+ for (unsigned int i = 32; i < outputLength; i++)
+ {
+ decodeBuffer.decodeValue(nextByte, 8, 4);
+ *nextDest++ = (unsigned char) nextByte;
+ }
+ }
+ break;
+ case X_GetKeyboardControl:
+ {
+ unsigned int nextByte;
+ decodeBuffer.decodeValue(nextByte, 8, 2);
+ unsigned int replyLength;
+ decodeBuffer.decodeValue(replyLength, 32, 8);
+ outputLength = 32 + (replyLength << 2);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ outputMessage[1] = (unsigned char) nextByte;
+ unsigned char *nextDest = outputMessage + 8;
+ for (unsigned int i = 8; i < outputLength; i++)
+ {
+ decodeBuffer.decodeValue(nextByte, 8, 4);
+ *nextDest++ = (unsigned char) nextByte;
+ }
+ }
+ break;
+ default:
+ {
+ if (requestOpcode == opcodeStore_ -> getUnpackParameters)
+ {
+ #ifdef TEST
+ *logofs << "handleWrite: Received get unpack parameters reply "
+ << "OPCODE#" << (unsigned int) opcodeStore_ -> getUnpackParameters
+ << ".\n" << logofs_flush;
+ #endif
+
+ outputLength = 32 + PACK_METHOD_LIMIT;
+
+ outputMessage = writeBuffer_.addMessage(outputLength);
+
+ unsigned int method;
+
+ //
+ // Let agent use only the unpack methods
+ // implemented at both sides.
+ //
+
+ for (int i = 0; i < PACK_METHOD_LIMIT; i++)
+ {
+ decodeBuffer.decodeBoolValue(method);
+
+ control -> RemoteUnpackMethods[i] = method;
+
+ *(outputMessage + 32 + i) =
+ (control -> LocalUnpackMethods[i] == 1 &&
+ method == 1);
+ }
+ }
+ else if (requestOpcode == opcodeStore_ -> getShmemParameters)
+ {
+ if (handleShmemReply(decodeBuffer, requestOpcode,
+ outputMessage, outputLength) < 0)
+ {
+ return -1;
+ }
+ }
+ else if (requestOpcode == opcodeStore_ -> getFontParameters)
+ {
+ if (handleFontReply(decodeBuffer, requestOpcode,
+ outputMessage, outputLength) < 0)
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "handleWrite: PANIC! No matching request for "
+ << "reply with sequence number " << sequenceNum
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": No matching request for "
+ << "reply with sequence number " << sequenceNum
+ << ".\n";
+
+ return -1;
+ }
+ }
+ }
+
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << "handleWrite: Handled reply to OPCODE#"
+ << (unsigned) requestOpcode << " (" << DumpOpcode(requestOpcode)
+ << ")" << " for FD#" << fd_ << " with sequence " << serverSequence_
+ << ". Output size is " << outputLength << ".\n" << logofs_flush;
+ #endif
+
+ statistics -> addRepliedRequest(requestOpcode);
+ }
+ else // End of if (sequenceQueue_.peek() && ...)
+ {
+ //
+ // Reply didn't match any request opcode.
+ // Check again if differential encoding
+ // is disabled.
+ //
+
+ #ifdef DEBUG
+ *logofs << "handleWrite: Identified generic reply.\n"
+ << logofs_flush;
+ #endif
+
+ requestOpcode = X_Reply;
+
+ if (control -> RemoteDeltaCompression == 0)
+ {
+ int result = handleFastWriteReply(decodeBuffer, requestOpcode,
+ outputMessage, outputLength);
+ if (result < 0)
+ {
+ return -1;
+ }
+ else if (result > 0)
+ {
+ continue;
+ }
+ }
+
+ //
+ // All replies whose opcode is not pushed in
+ // sequence number queue are cached together.
+ // Among such replies are those to extension
+ // requests.
+ //
+
+ MessageStore *messageStore = serverStore_ ->
+ getReplyStore(X_NXInternalGenericReply);
+
+ handleDecode(decodeBuffer, serverCache_, messageStore,
+ requestOpcode, outputMessage, outputLength);
+
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << "handleWrite: Handled generic reply for FD#" << fd_
+ << " with sequence " << serverSequence_ << ". Output size is "
+ << outputLength << ".\n" << logofs_flush;
+ #endif
+
+ statistics -> addRepliedRequest(requestOpcode);
+
+ } // End of if (sequenceQueue_.peek() && ...) else ...
+
+ //
+ // If any output was produced then write opcode,
+ // sequence number and size to the buffer.
+ //
+
+ if (outputLength > 0)
+ {
+ *outputMessage = outputOpcode;
+
+ PutUINT(serverSequence_, outputMessage + 2, bigEndian_);
+
+ PutULONG((outputLength - 32) >> 2, outputMessage + 4, bigEndian_);
+ }
+
+ } // End of if (outputOpcode == 1)...
+ else
+ {
+ //
+ // It's an event or error.
+ //
+
+ unsigned int sequenceNum;
+ unsigned int sequenceDiff;
+
+ decodeBuffer.decodeCachedValue(sequenceDiff, 16,
+ serverCache_ -> eventSequenceCache, 7);
+
+ sequenceNum = (serverSequence_ + sequenceDiff) & 0xffff;
+
+ serverSequence_ = sequenceNum;
+
+ #ifdef DEBUG
+ *logofs << "handleWrite: Last server sequence number for FD#"
+ << fd_ << " is " << serverSequence_ << " with "
+ << "difference " << sequenceDiff << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Check if this is an error that matches
+ // a sequence number for which we were
+ // expecting a reply.
+ //
+
+ if (outputOpcode == X_Error)
+ {
+ unsigned short int errorSequenceNum;
+ unsigned char errorOpcode;
+
+ if (sequenceQueue_.peek(errorSequenceNum, errorOpcode) &&
+ ((unsigned) errorSequenceNum == serverSequence_))
+ {
+ //
+ // Remove the queued sequence of the reply.
+ //
+
+ #ifdef TEST
+ *logofs << "handleWrite: WARNING! Removing reply to OPCODE#"
+ << (unsigned) errorOpcode << " sequence "
+ << errorSequenceNum << " for FD#" << fd_
+ << " due to error.\n" << logofs_flush;
+ #endif
+
+ sequenceQueue_.pop(errorSequenceNum, errorOpcode);
+
+ //
+ // Send to the client the current sequence
+ // number, not the number that matched the
+ // reply. Because we are generating replies
+ // at our side, Xlib can incur in a sequence
+ // lost if the error comes after the auto-
+ // generated reply.
+ //
+
+ if (control -> SessionMode == session_proxy)
+ {
+ #ifdef TEST
+ *logofs << "handleWrite: Updating last event's sequence "
+ << lastSequence_ << " to X server's error sequence "
+ << "number " << serverSequence_ << " for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ lastSequence_ = serverSequence_;
+ }
+ }
+
+ //
+ // In case of errors always send to client the
+ // original X server's sequence associated to
+ // the failing request.
+ //
+
+ if (control -> SessionMode != session_proxy)
+ {
+ #ifdef TEST
+ *logofs << "handleWrite: Updating last event's sequence "
+ << lastSequence_ << " to X server's error sequence "
+ << "number " << serverSequence_ << " for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ lastSequence_ = serverSequence_;
+ }
+ }
+
+ //
+ // Check if by producing events at client side we
+ // have modified the events' sequence numbering.
+ // In this case taint the original sequence to
+ // comply with the last one known by client.
+ //
+
+/*
+FIXME: Recover the sequence number if the proxy
+ is not connected to an agent.
+*/
+ if (serverSequence_ > lastSequence_ ||
+ control -> SessionMode != session_proxy)
+ {
+ #ifdef DEBUG
+ *logofs << "handleWrite: Updating last event's sequence "
+ << lastSequence_ << " to X server's sequence number "
+ << serverSequence_ << " for FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ lastSequence_ = serverSequence_;
+ }
+ #ifdef DEBUG
+ else if (serverSequence_ < lastSequence_)
+ {
+ //
+ // Use our last auto-generated sequence.
+ //
+
+ *logofs << "handleWrite: Tainting sequence number "
+ << serverSequence_ << " to last event's sequence "
+ << lastSequence_ << " for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ }
+ #endif
+
+ //
+ // Check if remote side used fast encoding.
+ //
+
+ if (control -> RemoteDeltaCompression == 0)
+ {
+ int result = handleFastWriteEvent(decodeBuffer, outputOpcode,
+ outputMessage, outputLength);
+ if (result < 0)
+ {
+ return -1;
+ }
+ else if (result > 0)
+ {
+ continue;
+ }
+ }
+
+ //
+ // Make space for message in the outgoing buffer
+ // and write opcode and sequence number.
+ //
+
+ outputLength = 32;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+
+ *outputMessage = outputOpcode;
+
+ PutUINT(lastSequence_, outputMessage + 2, bigEndian_);
+
+ #ifdef DEBUG
+ *logofs << "handleWrite: Going to handle event or error OPCODE#"
+ << (unsigned int) outputOpcode << " for FD#" << fd_
+ << " sequence " << lastSequence_ << " (real was "
+ << serverSequence_ << ").\n" << logofs_flush;
+ #endif
+
+ switch (outputOpcode)
+ {
+ case X_Error:
+ {
+ unsigned char code;
+ decodeBuffer.decodeCachedValue(code, 8,
+ serverCache_ -> errorCodeCache);
+ outputMessage[1] = code;
+
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << "handleWrite: Handled error ERR_CODE#"
+ << (unsigned int) code << " for FD#" << fd_;
+ #endif
+
+ if ((code != 11) && (code != 8) &&
+ (code != 15) && (code != 1))
+ {
+ decodeBuffer.decodeValue(value, 32, 16);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << " RES_ID#" << value;
+ #endif
+ }
+
+ if (code >= 18)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> errorMinorCache);
+ PutUINT(value, outputMessage + 8, bigEndian_);
+
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << " MIN_OP#" << value;
+ #endif
+ }
+
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ serverCache_ -> errorMajorCache);
+ outputMessage[10] = cValue;
+
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << " MAJ_OP#" << (unsigned int) cValue;
+ #endif
+
+ if (code >= 18)
+ {
+ unsigned char *nextDest = outputMessage + 11;
+ for (unsigned int i = 11; i < 32; i++)
+ {
+ decodeBuffer.decodeValue(value, 8);
+ *nextDest++ = (unsigned char) cValue;
+ }
+ }
+
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << " sequence " << lastSequence_ << " (real was "
+ << serverSequence_ << ") . Size is "
+ << (unsigned int) outputLength << ".\n"
+ << logofs_flush;
+ #endif
+ }
+ break;
+ case ButtonPress:
+ case ButtonRelease:
+ case KeyPress:
+ case KeyRelease:
+ case MotionNotify:
+ case EnterNotify:
+ case LeaveNotify:
+ {
+ if (outputOpcode == MotionNotify)
+ {
+ decodeBuffer.decodeBoolValue(value);
+ }
+ else if (outputOpcode == EnterNotify || outputOpcode == LeaveNotify)
+ {
+ decodeBuffer.decodeValue(value, 3);
+ }
+ else if (outputOpcode == KeyRelease)
+ {
+ decodeBuffer.decodeBoolValue(value);
+ if (value)
+ {
+ value = serverCache_ -> keyPressLastKey;
+ }
+ else
+ {
+ decodeBuffer.decodeValue(value, 8);
+ }
+ }
+ else if (outputOpcode == ButtonPress || outputOpcode == ButtonRelease)
+ {
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ serverCache_ -> buttonCache);
+ value = (unsigned int) cValue;
+ }
+ else
+ {
+ decodeBuffer.decodeValue(value, 8);
+ }
+
+ outputMessage[1] = (unsigned char) value;
+ decodeBuffer.decodeCachedValue(value, 32,
+ serverCache_ -> motionNotifyTimestampCache, 9);
+ serverCache_ -> lastTimestamp += value;
+ PutULONG(serverCache_ -> lastTimestamp, outputMessage + 4,
+ bigEndian_);
+ unsigned char *nextDest = outputMessage + 8;
+ int skipRest = 0;
+ if (outputOpcode == KeyRelease)
+ {
+ decodeBuffer.decodeBoolValue(value);
+ if (value)
+ {
+ for (unsigned int i = 0; i < 23; i++)
+ {
+ *nextDest++ = serverCache_ -> keyPressCache[i];
+ }
+ skipRest = 1;
+ }
+ }
+
+ if (!skipRest)
+ {
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 29,
+ *serverCache_ -> motionNotifyWindowCache[i], 6);
+ PutULONG(value, nextDest, bigEndian_);
+ nextDest += 4;
+ }
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> motionNotifyRootXCache, 6);
+ serverCache_ -> motionNotifyLastRootX += value;
+ PutUINT(serverCache_ -> motionNotifyLastRootX, outputMessage + 20,
+ bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> motionNotifyRootYCache, 6);
+ serverCache_ -> motionNotifyLastRootY += value;
+ PutUINT(serverCache_ -> motionNotifyLastRootY, outputMessage + 22,
+ bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> motionNotifyEventXCache, 6);
+ PutUINT(serverCache_ -> motionNotifyLastRootX + value,
+ outputMessage + 24, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> motionNotifyEventYCache, 6);
+ PutUINT(serverCache_ -> motionNotifyLastRootY + value,
+ outputMessage + 26, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> motionNotifyStateCache);
+ PutUINT(value, outputMessage + 28, bigEndian_);
+ if (outputOpcode == EnterNotify || outputOpcode == LeaveNotify)
+ {
+ decodeBuffer.decodeValue(value, 2);
+ }
+ else
+ {
+ decodeBuffer.decodeBoolValue(value);
+ }
+ outputMessage[30] = (unsigned char) value;
+ if (outputOpcode == EnterNotify || outputOpcode == LeaveNotify)
+ {
+ decodeBuffer.decodeValue(value, 2);
+ outputMessage[31] = (unsigned char) value;
+ }
+ else if (outputOpcode == KeyPress)
+ {
+ serverCache_ -> keyPressLastKey = outputMessage[1];
+ for (unsigned int i = 8; i < 31; i++)
+ {
+ serverCache_ -> keyPressCache[i - 8] = outputMessage[i];
+ }
+ }
+ }
+ }
+ break;
+ case ColormapNotify:
+ {
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> colormapNotifyWindowCache, 8);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> colormapNotifyColormapCache, 8);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[12] = (unsigned char) value;
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[13] = (unsigned char) value;
+ }
+ break;
+ case ConfigureNotify:
+ {
+ unsigned char *nextDest = outputMessage + 4;
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 29,
+ *serverCache_ -> configureNotifyWindowCache[i], 9);
+ PutULONG(value, nextDest, bigEndian_);
+ nextDest += 4;
+ }
+ for (unsigned int j = 0; j < 5; j++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *serverCache_ -> configureNotifyGeomCache[j], 8);
+ PutUINT(value, nextDest, bigEndian_);
+ nextDest += 2;
+ }
+ decodeBuffer.decodeBoolValue(value);
+ *nextDest = value;
+ }
+ break;
+ case CreateNotify:
+ {
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> createNotifyWindowCache, 9);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeValue(value, 29, 5);
+ serverCache_ -> createNotifyLastWindow += value;
+ serverCache_ -> createNotifyLastWindow &= 0x1fffffff;
+ PutULONG(serverCache_ -> createNotifyLastWindow, outputMessage + 8,
+ bigEndian_);
+ unsigned char* nextDest = outputMessage + 12;
+ for (unsigned int i = 0; i < 5; i++)
+ {
+ decodeBuffer.decodeValue(value, 16, 9);
+ PutUINT(value, nextDest, bigEndian_);
+ nextDest += 2;
+ }
+ decodeBuffer.decodeBoolValue(value);
+ *nextDest = (unsigned char) value;
+ }
+ break;
+ case Expose:
+ {
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> exposeWindowCache, 9);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ unsigned char *nextDest = outputMessage + 8;
+ for (unsigned int i = 0; i < 5; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *serverCache_ -> exposeGeomCache[i], 6);
+ PutUINT(value, nextDest, bigEndian_);
+ nextDest += 2;
+ }
+ }
+ break;
+ case FocusIn:
+ case FocusOut:
+ {
+ decodeBuffer.decodeValue(value, 3);
+ outputMessage[1] = (unsigned char) value;
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> focusInWindowCache, 9);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeValue(value, 2);
+ outputMessage[8] = (unsigned char) value;
+ }
+ break;
+ case KeymapNotify:
+ {
+ decodeBuffer.decodeBoolValue(value);
+ if (value)
+ memcpy(outputMessage + 1, ServerCache::lastKeymap.getData(), 31);
+ else
+ {
+ unsigned char *nextDest = outputMessage + 1;
+ for (unsigned int i = 1; i < 32; i++)
+ {
+ decodeBuffer.decodeValue(value, 8);
+ *nextDest++ = (unsigned char) value;
+ }
+ ServerCache::lastKeymap.set(31, outputMessage + 1);
+ }
+ }
+ break;
+ case MapNotify:
+ case UnmapNotify:
+ case DestroyNotify:
+ {
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> mapNotifyEventCache, 9);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> mapNotifyWindowCache, 9);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ if (outputOpcode == MapNotify || outputOpcode == UnmapNotify)
+ {
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[12] = (unsigned char) value;
+ }
+ }
+ break;
+ case NoExpose:
+ {
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> noExposeDrawableCache, 9);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> noExposeMinorCache);
+ PutUINT(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ serverCache_ -> noExposeMajorCache);
+ outputMessage[10] = cValue;
+ }
+ break;
+ case PropertyNotify:
+ {
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> propertyNotifyWindowCache, 9);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> propertyNotifyAtomCache, 9);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeValue(value, 32, 9);
+ serverCache_ -> lastTimestamp += value;
+ PutULONG(serverCache_ -> lastTimestamp, outputMessage + 12,
+ bigEndian_);
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[16] = (unsigned char) value;
+ }
+ break;
+ case ReparentNotify:
+ {
+ unsigned char* nextDest = outputMessage + 4;
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> reparentNotifyWindowCache, 9);
+ PutULONG(value, nextDest, bigEndian_);
+ nextDest += 4;
+ }
+ decodeBuffer.decodeValue(value, 16, 6);
+ PutUINT(value, nextDest, bigEndian_);
+ decodeBuffer.decodeValue(value, 16, 6);
+ PutUINT(value, nextDest + 2, bigEndian_);
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[20] = (unsigned char)value;
+ }
+ break;
+ case SelectionClear:
+ {
+ decodeBuffer.decodeValue(value, 32, 9);
+ serverCache_ -> lastTimestamp += value;
+ PutULONG(serverCache_ -> lastTimestamp, outputMessage + 4,
+ bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> selectionClearWindowCache, 9);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> selectionClearAtomCache, 9);
+ PutULONG(value, outputMessage + 12, bigEndian_);
+ }
+ break;
+ case SelectionRequest:
+ {
+ decodeBuffer.decodeValue(value, 32, 9);
+ serverCache_ -> lastTimestamp += value;
+ PutULONG(serverCache_ -> lastTimestamp, outputMessage + 4,
+ bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> selectionClearWindowCache, 9);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> selectionClearWindowCache, 9);
+ PutULONG(value, outputMessage + 12, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> selectionClearAtomCache, 9);
+ PutULONG(value, outputMessage + 16, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> selectionClearAtomCache, 9);
+ PutULONG(value, outputMessage + 20, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> selectionClearAtomCache, 9);
+ PutULONG(value, outputMessage + 24, bigEndian_);
+ }
+ break;
+ case VisibilityNotify:
+ {
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> visibilityNotifyWindowCache, 9);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeValue(value, 2);
+ outputMessage[8] = (unsigned char) value;
+ }
+ break;
+ default:
+ {
+ #ifdef TEST
+ *logofs << "handleWrite: Using generic event compression "
+ << "for OPCODE#" << (unsigned int) outputOpcode
+ << ".\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(*(outputMessage + 1), 8,
+ serverCache_ -> genericEventCharCache);
+
+ for (unsigned int i = 0; i < 14; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *serverCache_ -> genericEventIntCache[i]);
+
+ PutUINT(value, outputMessage + i * 2 + 4, bigEndian_);
+ }
+ }
+ } // End of switch (outputOpcode)...
+
+ #if defined(TEST) || defined(OPCODES)
+ if (outputOpcode != X_Error)
+ {
+ *logofs << "handleWrite: Handled event OPCODE#"
+ << (unsigned int) outputOpcode << " for FD#"
+ << fd_ << " sequence " << lastSequence_ << " (real was "
+ << serverSequence_ << "). Size is " << outputLength
+ << ".\n" << logofs_flush;
+ }
+ #endif
+
+ //
+ // Check if we need to suppress the error.
+ //
+
+ if (outputOpcode == X_Error &&
+ handleTaintSyncError(*(outputMessage + 10)) > 0)
+ {
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << "handleWrite: WARNING! Suppressed error OPCODE#"
+ << (unsigned int) outputOpcode << " for FD#"
+ << fd_ << " sequence " << lastSequence_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ writeBuffer_.removeMessage(32);
+ }
+
+ } // End of if (outputOpcode == 1)... else ...
+
+ //
+ // Check if we produced enough data. We need to
+ // decode all provided messages. Just update the
+ // finish flag in case of failure.
+ //
+
+ handleFlush(flush_if_needed);
+
+ } // End of while (decodeBuffer.decodeOpcodeValue(outputOpcode, 8, ...
+
+ } // End of the decoding block.
+
+ //
+ // Write any remaining data to the X connection.
+ //
+
+ if (handleFlush(flush_if_any) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+//
+// End of handleWrite().
+//
+
+//
+// Other members.
+//
+
+int ClientChannel::handleSplit(EncodeBuffer &encodeBuffer, MessageStore *store,
+ T_store_action action, int position, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size)
+{
+ #if defined(TEST) || defined(SPLIT)
+
+ if (control -> isProtoStep8() == 1)
+ {
+ *logofs << "handleSplit: PANIC! SPLIT! Split should "
+ << "not be enabled for message " << "OPCODE#"
+ << (unsigned int) store -> opcode() << ".\n"
+ << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ //
+ // Never split the message if connected to
+ // an old proxy version. Also refuse the
+ // split if we it is not introduced by a
+ // start split.
+ //
+
+ if (control -> isProtoStep7() == 0)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Ignoring the split with "
+ << "an old proxy version.\n" << logofs_flush;
+ #endif
+
+ if (action == IS_ADDED || action == is_discarded)
+ {
+ encodeBuffer.encodeBoolValue(0);
+ }
+
+ return 0;
+ }
+ else if (splitState_.resource == nothing || enableSplit_ == 0)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Nothing to do for message "
+ << "OPCODE#" << (unsigned int) store -> opcode()
+ << " of size " << size << " position " << position
+ << " with action [" << DumpAction(action) << "] at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeBoolValue(0);
+
+ return 0;
+ }
+
+ //
+ // It's not advisable to allocate the store at
+ // the time we receive the start-split because
+ // we may process all the splits received and
+ // deallocate the store even before we receive
+ // the end split. Another message for the same
+ // split sequence may then come and we would
+ // have a null split store.
+ //
+
+ handleSplitStoreAlloc(&splitResources_, splitState_.resource);
+
+ //
+ // Check if the split was actually requested by
+ // the agent and if the request was saved in the
+ // message store. The split can also be refused
+ // if the message is smaller than the threshold
+ // or if the split store is already full.
+ //
+
+ if (mustSplitMessage(splitState_.resource) == 0)
+ {
+ if (action == IS_HIT || canSplitMessage(splitState_.mode, size) == 0)
+ {
+ #if defined(TEST) || defined(SPLIT)
+
+ if (splitState_.mode == split_none)
+ {
+ #ifdef PANIC
+ *logofs << "handleSplit: PANIC! SPLIT! Split state has "
+ << "mode 'none'.\n" << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+
+ if (action != IS_HIT && (int) size >=
+ control -> SplitDataThreshold)
+ {
+ #ifdef WARNING
+ *logofs << "handleSplit: WARNING! SPLIT! Split stores have "
+ << clientStore_ -> getSplitTotalSize() << " messages "
+ << "and " << clientStore_ -> getSplitTotalStorageSize()
+ << " allocated bytes.\n" << logofs_flush;
+ #endif
+ }
+
+ #endif
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Message OPCODE#"
+ << (unsigned int) store -> opcode() << " of size " << size
+ << " [not split] with resource " << splitState_.resource
+ << " mode " << splitState_.mode << " position " << position
+ << " and action [" << DumpAction(action) << "] at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeBoolValue(0);
+
+ return 0;
+ }
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Message OPCODE#"
+ << (unsigned int) store -> opcode() << " of size " << size
+ << " [split] with resource " << splitState_.resource
+ << " mode " << splitState_.mode << " position " << position
+ << " and action [" << DumpAction(action) << "] at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeBoolValue(1);
+
+ T_checksum checksum = NULL;
+
+ if (action == IS_ADDED)
+ {
+ checksum = store -> getChecksum(position);
+ }
+ else if (action == is_discarded)
+ {
+ //
+ // Generate the checksum on the fly.
+ //
+
+ checksum = store -> getChecksum(buffer, size, bigEndian_);
+ }
+
+ //
+ // The method must abort the connection
+ // if it can't allocate the split.
+ //
+
+ Split *splitMessage = clientStore_ -> getSplitStore(splitState_.resource) ->
+ add(store, splitState_.resource, splitState_.mode,
+ position, action, checksum, buffer, size);
+
+ //
+ // Send the checksum. By using the checksum,
+ // the remote end will try to locate the
+ // message and load it from disk.
+ //
+
+ if (action == IS_HIT)
+ {
+ splitMessage -> setState(split_loaded);
+ }
+ else if (handleSplitChecksum(encodeBuffer, checksum) == 0)
+ {
+ //
+ // If the checksum is not sent, for example
+ // because loading of messages from disk is
+ // disabled, then mark the split as missed.
+ //
+
+ #ifdef WARNING
+ *logofs << "handleSplit: WARNING! Checksum not sent. "
+ << "Marking the split as [missed].\n"
+ << logofs_flush;
+ #endif
+
+ splitMessage -> setState(split_missed);
+ }
+
+ if (action == is_discarded)
+ {
+ delete [] checksum;
+ }
+
+ //
+ // Check if we are ready to send a new split
+ // for this store.
+ //
+
+ handleSplitPending(splitState_.resource);
+
+ #if defined(TEST) || defined(SPLIT)
+
+ *logofs << "handleSplit: SPLIT! There are " << clientStore_ ->
+ getSplitTotalSize() << " messages and " << clientStore_ ->
+ getSplitTotalStorageSize() << " bytes to send in "
+ << "the split stores.\n" << logofs_flush;
+
+ clientStore_ -> dumpSplitStore(splitState_.resource);
+
+ #endif
+
+ return 1;
+}
+
+int ClientChannel::handleSplit(EncodeBuffer &encodeBuffer)
+{
+ //
+ // Determine the maximum amount of bytes
+ // we can write in this iteration.
+ //
+
+ int total = control -> SplitDataPacketLimit;
+
+ int bytes = total;
+ int splits = 0;
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Handling splits "
+ << "for FD#" << fd_ << " with " << clientStore_ ->
+ getSplitTotalSize() << " elements and " << total
+ << " bytes to write at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (proxy -> handleAsyncSwitch(fd_) < 0)
+ {
+ return -1;
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Looping to find "
+ << "if there is any split to send.\n"
+ << logofs_flush;
+ #endif
+
+ SplitStore *splitStore;
+
+ Split *splitMessage;
+
+ //
+ // Divide the available bandwidth among all the active
+ // split stores by implementing a simple round-robin
+ // mechanism. This can be extended by using an external
+ // function returning the number of bytes to be written
+ // based on the state of the split (splits which didn't
+ // receive yet a confirmation event could be delayed),
+ // the current bitrate, and by letting the agent asso-
+ // ciate a priority to the resource in the start split
+ // operation.
+ //
+
+ splitState_.pending = 0;
+
+ splitResources_.rotate();
+
+ //
+ // Copy the list since elements can be removed
+ // in the middle of the loop.
+ //
+
+ T_list splitList = splitResources_.copyList();
+
+ for (T_list::iterator j = splitList.begin();
+ j != splitList.end(); j++)
+ {
+ int resource = *j;
+
+ #ifdef DEBUG
+ *logofs << "handleSplit: SPLIT! Looping with current "
+ << "resource " << resource << ".\n"
+ << logofs_flush;
+ #endif
+
+ splitStore = clientStore_ -> getSplitStore(resource);
+
+ if (splitStore != NULL)
+ {
+ //
+ // Don't send more than the the packet size
+ // bytes but ensure that we abort any split
+ // found in the disk cache.
+ //
+
+ for (;;)
+ {
+ #if defined(TEST) || defined(SPLIT)
+
+ clientStore_ -> dumpSplitStore(resource);
+
+ #endif
+
+ splitMessage = splitStore -> getFirstSplit();
+
+ if (splitMessage == NULL)
+ {
+ //
+ // We have created the store after a start
+ // split but no message was added yet.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: WARNING! SPLIT! The split store "
+ << "is still empty.\n" << logofs_flush;
+ #endif
+
+ break;
+ }
+
+ //
+ // Splits already aborted can't be in the
+ // split store.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+
+ if (splitMessage -> getState() == split_aborted)
+ {
+ *logofs << "handleSplit: PANIC! SPLIT! Found an "
+ << "aborted split in store [" << resource
+ << "].\n" << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ //
+ // Check if there are more messages in the
+ // store that can be aborted or if we have
+ // exceeded the number of bytes we can send
+ // for this iteration.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Checking closure "
+ << "of the inner loop with " << bytes
+ << " bytes to write and split state ["
+ << DumpState(splitMessage -> getState())
+ << "].\n" << logofs_flush;
+ #endif
+
+ if ((splitMessage -> getMode() == split_sync &&
+ splitMessage -> getState() == split_added) ||
+ (bytes <= 0 && splitMessage ->
+ getState() != split_loaded))
+ {
+ break;
+ }
+
+ //
+ // If the split was loaded at the remote
+ // side abort it immediately.
+ //
+
+ if (splitMessage -> getState() == split_loaded)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Sending more data "
+ << "for store [" << resource << "] with "
+ << "a split to be aborted.\n"
+ << logofs_flush;
+ #endif
+
+ if (handleSplitSend(encodeBuffer, resource, splits, bytes) < 0)
+ {
+ return -1;
+ }
+ }
+ else if (bytes > 0)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Sending more data "
+ << "for store [" << resource << "] with "
+ << bytes << " bytes to send.\n"
+ << logofs_flush;
+ #endif
+
+ if (handleSplitSend(encodeBuffer, resource, splits, bytes) < 0)
+ {
+ return -1;
+ }
+ }
+
+ //
+ // Check if the split store was deleted.
+ //
+
+ splitStore = clientStore_ -> getSplitStore(resource);
+
+ if (splitStore == NULL)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Exiting from the "
+ << "inner loop with split store [" << resource
+ << "] destroyed.\n" << logofs_flush;
+ #endif
+
+ break;
+ }
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Completed handling splits "
+ << "for store [" << resource << "] with " << bytes
+ << " bytes still to send.\n" << logofs_flush;
+ #endif
+
+ //
+ // Check if there is still a split to
+ // send for the store just processed.
+ //
+
+ handleSplitPending(resource);
+ }
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+
+ if (splits == 0)
+ {
+ #ifdef PANIC
+ *logofs << "handleSplit: PANIC! Function called but "
+ << "no split message was sent.\n"
+ << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+
+ *logofs << "handleSplit: SPLIT! Sent " << splits
+ << " splits and " << total - bytes << " bytes for FD#" << fd_
+ << " with " << clientStore_ -> getSplitTotalStorageSize()
+ << " bytes and [" << clientStore_ -> getSplitTotalSize()
+ << "] splits remaining.\n" << logofs_flush;
+
+ *logofs << "handleSplit: SPLIT! The pending split flag is "
+ << splitState_.pending << " with " << clientStore_ ->
+ getSplitTotalSize() << " splits in the split stores.\n"
+ << logofs_flush;
+
+ clientStore_ -> dumpSplitStores();
+
+ #endif
+
+ return 1;
+}
+
+int ClientChannel::handleSplitSend(EncodeBuffer &encodeBuffer, int resource,
+ int &splits, int &bytes)
+{
+ #if defined(TEST) || defined(SPLIT)
+
+ SplitStore *splitStore = clientStore_ -> getSplitStore(resource);
+
+ Split *splitMessage = splitStore -> getFirstSplit();
+
+ if (splitStore -> getResource() != resource ||
+ splitMessage -> getResource() != resource)
+ {
+ #ifdef PANIC
+ *logofs << "handleSplitSend: PANIC! The resource doesn't "
+ << "match the split store.\n" << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+
+ *logofs << "handleSplitSend: SPLIT! Sending message "
+ << "OPCODE#" << (unsigned) opcodeStore_ -> splitData
+ << " for resource " << splitMessage -> getResource()
+ << " with request " << splitMessage -> getRequest()
+ << " position " << splitMessage -> getPosition()
+ << " and " << bytes << " bytes to write.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Use a special opcode to signal the other
+ // side this is part of a split and not a
+ // new message.
+ //
+
+ encodeBuffer.encodeOpcodeValue(opcodeStore_ -> splitData,
+ clientCache_ -> opcodeCache);
+
+ encodeBuffer.encodeCachedValue(resource, 8,
+ clientCache_ -> resourceCache);
+
+ int result = clientStore_ -> getSplitStore(resource) ->
+ send(encodeBuffer, bytes);
+
+ if (result < 0)
+ {
+ #ifdef PANIC
+ *logofs << "handleSplit: PANIC! Error sending splits for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error sending splits for FD#"
+ << fd_ << ".\n";
+
+ return -1;
+ }
+
+ //
+ // Get the bits written and update the
+ // statistics for this special opcode.
+ //
+
+ int bits = encodeBuffer.diffBits();
+
+ #if defined(TEST) || defined(SPLIT)|| defined(OPCODES)
+ *logofs << "handleSplitSend: SPLIT! Handled request OPCODE#"
+ << (unsigned int) opcodeStore_ -> splitData << " ("
+ << DumpOpcode(opcodeStore_ -> splitData) << ")" << " for FD#"
+ << fd_ << " sequence none. 0 bytes in, " << bits << " bits ("
+ << ((float) bits) / 8 << " bytes) out.\n" << logofs_flush;
+ #endif
+
+ statistics -> addRequestBits(opcodeStore_ -> splitData, 0, bits);
+
+ bytes -= bits >> 3;
+
+ splits++;
+
+ if (result == 1)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitSend: SPLIT! Split at the head "
+ << "of the list was completely transferred.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // The split at the head of the list was
+ // completely transferred.
+ //
+
+ handleRestart(sequence_deferred, resource);
+ }
+ #if defined(TEST) || defined(SPLIT)
+ else
+ {
+ *logofs << "handleSplitSend: SPLIT! More data to send "
+ << "for the split at the head of the list.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ return result;
+}
+
+int ClientChannel::handleSplitChecksum(EncodeBuffer &encodeBuffer, T_checksum checksum)
+{
+ //
+ // Send the checksum only if the loading
+ // or the saving of the message to the
+ // persistent image cache is enabled.
+ //
+
+ if ((control -> ImageCacheEnableLoad == 1 ||
+ control -> ImageCacheEnableSave == 1) &&
+ (enableLoad_ == 1 || enableSave_ == 1))
+ {
+ encodeBuffer.encodeBoolValue(1);
+
+ for (unsigned int i = 0; i < MD5_LENGTH; i++)
+ {
+ encodeBuffer.encodeValue((unsigned int) checksum[i], 8);
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitChecksum: SPLIT! Sent checksum "
+ << "[" << DumpChecksum(checksum) << "].\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+ }
+ else
+ {
+ encodeBuffer.encodeBoolValue(0);
+
+ return 0;
+ }
+}
+
+void ClientChannel::handleSplitPending()
+{
+ #if defined(TEST) || defined(SPLIT)
+
+ int previous = splitState_.pending;
+
+ #endif
+
+ if (clientStore_ -> getSplitTotalSize() == 0)
+ {
+ splitState_.pending = 0;
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitPending: SPLIT! Set the pending "
+ << "split flag to " << splitState_.pending
+ << " with split stores empty.\n"
+ << logofs_flush;
+ #endif
+ }
+ else
+ {
+ //
+ // Loop through the stores to find if
+ // there is any split that has become
+ // ready.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitPending: WARNING! SPLIT! Looping to "
+ << "find if there is any split pending.\n"
+ << logofs_flush;
+ #endif
+
+ splitState_.pending = 0;
+
+ T_list &splitList = splitResources_.getList();
+
+ for (T_list::iterator j = splitList.begin();
+ j != splitList.end(); j++)
+ {
+ int resource = *j;
+
+ SplitStore *splitStore = clientStore_ -> getSplitStore(resource);
+
+ if (splitStore != NULL)
+ {
+ #if defined(TEST) || defined(SPLIT)
+
+ clientStore_ -> dumpSplitStore(resource);
+
+ #endif
+
+ Split *splitMessage = splitStore -> getFirstSplit();
+
+ if (splitMessage != NULL && canSendSplit(splitMessage) == 1)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitPending: SPLIT! Found a pending "
+ << "split in store [" << resource << "].\n"
+ << logofs_flush;
+ #endif
+
+ splitState_.pending = 1;
+
+ #if defined(TEST) || defined(SPLIT)
+
+ if (splitMessage -> getState() == split_loaded)
+ {
+ *logofs << "handleSplitPending: PANIC! SPLIT! Found a "
+ << "loaded split in store [" << resource
+ << "].\n" << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ break;
+ }
+ }
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitPending: SPLIT! Set the pending "
+ << "split flag to " << splitState_.pending
+ << " with " << clientStore_ -> getSplitTotalSize()
+ << " splits in the split stores.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+
+ if (splitState_.pending != previous)
+ {
+ *logofs << "handleSplitPending: SPLIT! Pending state "
+ << "changed from " << previous << " to "
+ << splitState_.pending << ".\n"
+ << logofs_flush;
+ }
+
+ #endif
+}
+
+int ClientChannel::handleSplitEvent(EncodeBuffer &encodeBuffer, Split *splitMessage)
+{
+ SplitStore *splitStore;
+
+ int resource = splitMessage -> getResource();
+
+ #if defined(TEST) || defined(INFO)
+
+ splitStore = clientStore_ -> getSplitStore(resource);
+
+ if (splitStore == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "handleSplitEvent: PANIC! The split store can't "
+ << "be NULL handling abort splits.\n"
+ << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+ else if (splitMessage -> getState() != split_loaded)
+ {
+ *logofs << "handleSplitEvent: PANIC! Can't find the split "
+ << "to be aborted.\n" << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ //
+ // Send any split that it is possible to
+ // abort until the store is either empty
+ // or the next split can't be aborted.
+ //
+
+ if (proxy -> handleAsyncSwitch(fd_) < 0)
+ {
+ return -1;
+ }
+
+ while ((splitStore = clientStore_ ->
+ getSplitStore(resource)) != NULL &&
+ (splitMessage = splitStore -> getFirstSplit()) != NULL &&
+ splitMessage -> getState() == split_loaded)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitEvent: SPLIT! Aborting split with "
+ << "checksum [" << DumpChecksum(splitMessage ->
+ getChecksum()) << "] for resource " << resource
+ << " at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ int any = 0;
+
+ if (handleSplitSend(encodeBuffer, resource, any, any) < 0)
+ {
+ return -1;
+ }
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+
+ if ((splitStore = clientStore_ ->
+ getSplitStore(resource)) == NULL)
+ {
+ *logofs << "handleSplitEvent: SPLIT! The split store ["
+ << resource << "] has been destroyed.\n"
+ << logofs_flush;
+ }
+ else if ((splitMessage = splitStore ->
+ getFirstSplit()) == NULL)
+ {
+ *logofs << "handleSplitEvent: SPLIT! The split store ["
+ << resource << "] is empty.\n"
+ << logofs_flush;
+ }
+ else if (splitMessage -> getState() != split_loaded)
+ {
+ *logofs << "handleSplitEvent: SPLIT! The split at the "
+ << "head of store [" << resource << "] doesn't "
+ << "need to be aborted.\n" << logofs_flush;
+ }
+
+ #endif
+
+ return 1;
+}
+
+int ClientChannel::handleSplitEvent(DecodeBuffer &decodeBuffer)
+{
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitEvent: SPLIT! Handling abort "
+ << "split messages for FD#" << fd_ << " at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ if (control -> isProtoStep7() == 0)
+ {
+ #ifdef PANIC
+ *logofs << "handleSplitEvent: PANIC! The split can't "
+ << "be aborted when connected to an old "
+ << "proxy version.\n" << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+
+ //
+ // Decode the information about the
+ // message to be updated.
+ //
+
+ unsigned char resource;
+
+ decodeBuffer.decodeCachedValue(resource, 8,
+ serverCache_ -> resourceCache);
+
+ unsigned int loaded;
+
+ decodeBuffer.decodeBoolValue(loaded);
+
+ unsigned char request;
+ unsigned int size;
+
+ if (loaded == 1)
+ {
+ decodeBuffer.decodeOpcodeValue(request, serverCache_ -> abortOpcodeCache);
+
+ decodeBuffer.decodeValue(size, 32, 14);
+ }
+ else
+ {
+ request = 0;
+ size = 0;
+ }
+
+ unsigned int value;
+
+ md5_byte_t checksum[MD5_LENGTH];
+
+ for (unsigned int i = 0; i < MD5_LENGTH; i++)
+ {
+ decodeBuffer.decodeValue(value, 8);
+
+ checksum[i] = (unsigned char) value;
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitEvent: SPLIT! Checking split "
+ << "with checksum [" << DumpChecksum(checksum)
+ << "] loaded " << loaded << " request " << (unsigned int)
+ request << " compressed size " << size << " at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ Split *splitMessage = handleSplitFind(checksum, resource);
+
+ if (splitMessage != NULL)
+ {
+ if (loaded == 1)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitEvent: SPLIT! Marked split with "
+ << "checksum [" << DumpChecksum(checksum) << "] "
+ << "as [loaded] at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ splitMessage -> setState(split_loaded);
+
+ #if defined(TEST) || defined(SPLIT)
+
+ if (splitMessage -> compressedSize() != (int) size)
+ {
+ *logofs << "handleSplitEvent: WARNING! SPLIT! Updating "
+ << "compressed data size from " << splitMessage ->
+ compressedSize() << " to " << size << ".\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ splitMessage -> compressedSize(size);
+
+ //
+ // The splits to be aborted are checked by the split
+ // store at the time we are going to send a new chunk
+ // of split data. The splits must be strictly handled
+ // in the same order as they were added to the split
+ // store and the split we want to abort here may be
+ // not at the head of the list.
+ //
+
+ if (splitMessage == clientStore_ ->
+ getSplitStore(resource) -> getFirstSplit())
+ {
+ //
+ // We don't need to flush this packet immediately.
+ // The abort can be sent at any time to the remote
+ // proxy. What's important is that we restart the
+ // agent resource as soon as possible.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+
+ T_timestamp startTs = getTimestamp();
+
+ *logofs << "handleSplitEvent: SPLIT! Encoding abort "
+ << "split events for FD#" << fd_ << " with "
+ << "resource " << (unsigned) resource << " at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ if (proxy -> handleAsyncSplit(fd_, splitMessage) < 0)
+ {
+ return -1;
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitEvent: SPLIT! Spent "
+ << diffTimestamp(startTs, getTimestamp()) << " Ms "
+ << "handling abort split events for FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Check if we can clear the pending flag.
+ //
+
+ handleSplitPending();
+ }
+ #if defined(TEST) || defined(SPLIT)
+ else
+ {
+ *logofs << "handleSplitEvent: WARNING! SPLIT! Abort split "
+ << "event not sent because not at the head "
+ << "of the list.\n" << logofs_flush;
+ }
+ #endif
+ }
+ else
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitEvent: SPLIT! Marked split with "
+ << "checksum [" << DumpChecksum(checksum) << "] "
+ << "as [missed] at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ splitMessage -> setState(split_missed);
+
+ //
+ // Check if we can set the pending flag.
+ //
+
+ handleSplitPending(resource);
+ }
+ }
+ else
+ {
+ //
+ // The split report came after the split was already
+ // sent or the split store deleted. If the message
+ // had been loaded from disk by the remote side, we
+ // need to update the compressed size in our message
+ // store or the checksum will not match at the time
+ // we will try to save the message store on disk.
+ //
+
+ if (loaded == 1 && size != 0)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitEvent: WARNING! SPLIT! Can't find "
+ << "the split. Updating in the message store.\n"
+ << logofs_flush;
+ #endif
+
+ MessageStore *store = clientStore_ -> getRequestStore(request);
+
+ if (store != NULL)
+ {
+ store -> updateData(checksum, size);
+ }
+ #if defined(TEST) || defined(SPLIT)
+ else
+ {
+ #ifdef PANIC
+ *logofs << "handleSplitEvent: PANIC! The message store "
+ << "can't be null.\n" << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+ #endif
+ }
+ #if defined(TEST) || defined(SPLIT)
+ else
+ {
+ *logofs << "handleSplitEvent: WARNING! SPLIT! No need to "
+ << "update the store with loaded " << loaded
+ << " and compressed size " << size << ".\n"
+ << logofs_flush;
+ }
+ #endif
+ }
+
+ return 1;
+}
+
+Split *ClientChannel::handleSplitFind(T_checksum checksum, int resource)
+{
+ //
+ // It can be that we handled all the splits,
+ // restarted the resource and deleted the
+ // store before the event could even reach
+ // our side.
+ //
+
+ SplitStore *splitStore = clientStore_ -> getSplitStore(resource);
+
+ if (splitStore != NULL)
+ {
+ Split *splitMessage;
+
+ T_splits *splitList = splitStore -> getSplits();
+
+ for (T_splits::iterator i = splitList -> begin();
+ i != splitList -> end(); i++)
+ {
+ splitMessage = (*i);
+
+ if (splitMessage -> getChecksum() != NULL)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitFind: SPLIT! Comparing with message ["
+ << DumpChecksum(splitMessage -> getChecksum())
+ << "].\n" << logofs_flush;
+ #endif
+
+ if (memcmp(checksum, splitMessage -> getChecksum(), MD5_LENGTH) == 0)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitFind: SPLIT! Located split for "
+ << "checksum [" << DumpChecksum(checksum) << "] "
+ << "in store [" << splitStore -> getResource()
+ << "].\n" << logofs_flush;
+ #endif
+
+ return splitMessage;
+ }
+ }
+ }
+ }
+ #if defined(TEST) || defined(SPLIT)
+ else
+ {
+ *logofs << "handleSplitFind: WARNING! SPLIT! The split store "
+ << "was already deleted.\n" << logofs_flush;
+ }
+ #endif
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitFind: WARNING! SPLIT! Can't find the "
+ << "split for checksum [" << DumpChecksum(checksum)
+ << "].\n" << logofs_flush;
+ #endif
+
+ return NULL;
+}
+
+int ClientChannel::handleRestart(T_sequence_mode mode, int resource)
+{
+ //
+ // The agent must send a start-split message, followed by the
+ // X messages that may be optionally split by the proxy. Usu-
+ // ally, in the middle of a start-split/end-split sequence is
+ // a single PutImage() or PutPackedImage(), that, in turn,
+ // can generate multiple partial requests, like a SetUnpack-
+ // Colormap() and SetUnpackAlpha() followed by the image that
+ // must be transferred. Multiple requests may be also genera-
+ // ted because the maximum size of a X request has been exce-
+ // eded, so that Xlib has divided the single image in multi-
+ // ple sub-image requests. The agent doesn't need to take care
+ // of that, except tracking the result of the split operation.
+ //
+ // By monitoring the notify events sent by the proxy, the
+ // agent will have to implement its own strategy to deal with
+ // its resources (for example its clients). For example:
+ //
+ // - It will issue a new image request and suspend a client
+ // if the image was not entirely sent in the main X oputput
+ // stream.
+ //
+ // - It will choose to commit or discard the messages after
+ // they are recomposed at the remote side. The set of mes-
+ // sages that will have to be committed will include all
+ // messages that were part of the split (the colormap, the
+ // alpha channel).
+ //
+ // - It will restart its own client, in the case it had been
+ // suspended.
+ //
+ // A more useful strategy would be to replace the original im-
+ // age with a tiny 'placeholder' if a split took place, and
+ // synchronize the content of the drawable at later time. This
+ // is generally referred as 'lazy encoding'.
+ //
+ // The agent will be able to identify the original split ope-
+ // ration (the one marked with the start-spit) by the small
+ // integer number (0-255) referred to as the 'resource' field.
+ //
+ // Before the proxy will be able to report the status of the
+ // split, the agent will have to close the sequence by issueing
+ // an end-split. The proxy will then report the result of the
+ // operation, so that the agent will have the option of suspend-
+ // ing the client or marking the drawable as dirty and take
+ // care of synchronizing it at later time.
+ //
+ // One of the following cases may be encountered:
+ //
+ // notify_no_split: All messages were sent in the main out-
+ // put stream, so that no split actually
+ // took place.
+ //
+ // notify_start_split: One or more messages were split, so,
+ // at discrection of the agent, the client
+ // may be suspended until the transferral
+ // is completed.
+ //
+ // notify_commit_split: One of the requests that made up the
+ // split was recomposed. The agent should
+ // either commit the given request or tell
+ // the proxy to discard it.
+ //
+ // notify_end_split: The split was duly completed. The agent
+ // can restart the client.
+ //
+ // notify_empty_split: No more split operation are pending.
+ // The agent can use this information to
+ // implement specific strategies requiring
+ // that all messages have been recomposed
+ // at the remote end, like updating the
+ // drawables that were not synchronized
+ // because of the lazy encoding.
+ //
+ // By checking the split and commit store we can determine if we
+ // need to send a new notification event to the agent. There can
+ // be four different cases:
+ //
+ // - If the split store is not null and not empty, we are still
+ // in the middle of a split.
+ //
+ // - If the commit store is not empty, we completely recomposed
+ // a full message and can send a new commit notify.
+ //
+ // - If the split store has become empty, we recomposed all the
+ // messages added for the given resource, and so will be able
+ // to restart the resource.
+ //
+ // - If no more messages are in the split stores, we can notify
+ // an empty split event to the agent.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleRestart: SPLIT! Handling ["
+ << (mode == sequence_immediate ? "immediate" : "deferred")
+ << "] restart events for resource " << resource << " at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ SplitStore *splitStore = clientStore_ -> getSplitStore(resource);
+
+ if (mode == sequence_immediate)
+ {
+ //
+ // We have received an end-split request. If the store
+ // was not deleted already, we mark the last split added
+ // as the one ending the row for this resource. If the
+ // commit() function returns 0 it means that the split
+ // store is either empty or that we did not add any split
+ // for this resource. This is because when connected to
+ // an old proxy version we only have a single store for
+ // all the resources.
+ //
+ // It can happen that all the split messages that were
+ // originally appended to the list were completely sent
+ // before our client had the chance of ending the split
+ // sequence. In this case the split store will be empty
+ // or already deleted and so we will be able to restart
+ // the resource.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+
+ if (splitStore == NULL)
+ {
+ *logofs << "handleRestart: WARNING! SPLIT! Split store ["
+ << resource << "] was already deleted.\n"
+ << logofs_flush;
+ }
+ else
+ {
+ clientStore_ -> dumpSplitStore(resource);
+ }
+
+ #endif
+
+ if (splitStore == NULL || splitStore -> getSize() == 0)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleRestart: SPLIT! Immediate agent split event "
+ << "TYPE#" << (unsigned) opcodeStore_ -> noSplitNotify
+ << " [no split] with resource " << resource
+ << " at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (handleNotify(notify_no_split, sequence_immediate,
+ resource, nothing, nothing) < 0)
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleRestart: SPLIT! Immediate agent split event "
+ << "TYPE#" << (unsigned) opcodeStore_ -> startSplitNotify
+ << " [start split] with resource " << resource
+ << " at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (handleNotify(notify_start_split, sequence_immediate,
+ resource, nothing, nothing) < 0)
+ {
+ return -1;
+ }
+ }
+ }
+ else
+ {
+ //
+ // We have completely transferred a message
+ // that was put in the split store.
+ //
+ // The id of the resource can be different
+ // than the index of the store if we are
+ // connected to an old proxy.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+
+ if (splitStore == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "handleRestart: PANIC! The split store can't "
+ << "be NULL handling deferred restart events.\n"
+ << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+ else
+ {
+ clientStore_ -> dumpSplitStore(resource);
+ }
+
+ #endif
+
+ CommitStore *commitStore = clientStore_ -> getCommitStore();
+
+ #if defined(TEST) || defined(SPLIT)
+
+ clientStore_ -> dumpCommitStore();
+
+ #endif
+
+ //
+ // Check if there is any commit to notify.
+ //
+
+ Split *split;
+
+ T_splits *commitList = commitStore -> getSplits();
+
+ for (T_splits::iterator i = commitList -> begin();
+ i != commitList -> end(); i++)
+ {
+ split = *i;
+
+ if (split -> getState() != split_notified)
+ {
+ #if defined(TEST) || defined(SPLIT)
+
+ if (split -> getResource() != resource)
+ {
+ #ifdef PANIC
+ *logofs << "handleSplitSend: PANIC! The resource doesn't "
+ << "match the split store.\n" << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ int request = split -> getRequest();
+ int position = split -> getPosition();
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleRestart: SPLIT! Deferred agent split event "
+ << "TYPE#" << (unsigned) opcodeStore_ -> commitSplitNotify
+ << " [commit split] with resource " << resource << " request "
+ << request << " position " << position << " at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ if (handleNotify(notify_commit_split, sequence_deferred,
+ resource, request, position) < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Don't send the notification again.
+ //
+
+ split -> setState(split_notified);
+ }
+ #if defined(TEST) || defined(SPLIT)
+ else
+ {
+ *logofs << "handleRestart: SPLIT! Split for request "
+ << split -> getRequest() << " and position "
+ << split -> getPosition() << " was already "
+ << "notified.\n" << logofs_flush;
+ }
+ #endif
+ }
+
+ //
+ // Don't send the end split if we are still
+ // in the middle of a start-split/end-split
+ // sequence. We'll send a no-split at the
+ // time the end-split is received.
+ //
+
+ if (splitStore -> getSize() == 0 &&
+ splitStore -> getResource() != splitState_.resource)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleRestart: SPLIT! Deferred agent split event "
+ << "TYPE#" << (unsigned) opcodeStore_ -> endSplitNotify
+ << " [end split] with resource " << resource << " at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ if (handleNotify(notify_end_split, sequence_deferred,
+ resource, nothing, nothing) < 0)
+ {
+ return -1;
+ }
+ }
+ #if defined(TEST) || defined(SPLIT)
+ else if (splitStore -> getSize() == 0 &&
+ splitStore -> getResource() == splitState_.resource)
+ {
+ *logofs << "handleRestart: SPLIT! WARNING! The split store "
+ << "for resource " << resource << " was emptied in the "
+ << "split sequence at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ }
+ #endif
+ }
+
+ //
+ // Remove the split store if it's empty.
+ //
+
+ if (splitStore != NULL && splitStore -> getSize() == 0 &&
+ splitStore -> getResource() != splitState_.resource)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleRestart: SPLIT! Removing the split store ["
+ << resource << "] at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ #endif
+
+ handleSplitStoreRemove(&splitResources_, resource);
+
+ if (clientStore_ -> getSplitTotalSize() == 0)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleRestart: SPLIT! Deferred agent split event "
+ << "TYPE#" << (unsigned) opcodeStore_ -> emptySplitNotify
+ << " [empty split] for FD#" << fd_ << " at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ if (handleNotify(notify_empty_split, sequence_deferred,
+ nothing, nothing, nothing) < 0)
+ {
+ return -1;
+ }
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleRestart: SPLIT! There are " << clientStore_ ->
+ getSplitTotalSize() << " messages and " << clientStore_ ->
+ getSplitTotalStorageSize() << " bytes to send in "
+ << "the split stores.\n" << logofs_flush;
+
+ if ((clientStore_ -> getSplitTotalSize() != 0 &&
+ clientStore_ -> getSplitTotalStorageSize() == 0) ||
+ (clientStore_ -> getSplitTotalSize() == 0 &&
+ clientStore_ -> getSplitTotalStorageSize() != 0))
+ {
+ #ifdef PANIC
+ *logofs << "handleRestart: PANIC! Inconsistency detected "
+ << "while handling the split stores.\n"
+ << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+
+ #endif
+ }
+
+ return 1;
+}
+
+int ClientChannel::handleTaintCacheRequest(unsigned char &opcode, const unsigned char *&buffer,
+ unsigned int &size)
+{
+ #ifdef TEST
+ *logofs << "handleTaintCacheRequest: Tainting cache request "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // The save and load flags would affect
+ // the decoding side but the decoding
+ // side doesn't support the request.
+ //
+
+ enableCache_ = *(buffer + 4);
+ enableSplit_ = *(buffer + 5);
+
+ handleSplitEnable();
+
+ #ifdef TEST
+ *logofs << "handleTaintCacheRequest: Set cache parameters to "
+ << "cache " << enableCache_ << " split " << enableSplit_
+ << " load " << enableLoad_ << " save " << enableSave_
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Taint the request to a X_NoOperation.
+ //
+
+ opcode = X_NoOperation;
+
+ return 0;
+}
+
+int ClientChannel::handleTaintFontRequest(unsigned char &opcode, const unsigned char *&buffer,
+ unsigned int &size)
+{
+ //
+ // The remote end doesn't support this
+ // request so generate an empty reply
+ // at the local side.
+ //
+
+ #ifdef TEST
+ *logofs << "handleTaintFontRequest: Suppressing font "
+ << "request for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // The client sequence number has not
+ // been incremented yet in the loop.
+ //
+
+ unsigned int sequence = (clientSequence_ + 1) & 0xffff;
+
+ #ifdef TEST
+ *logofs << "handleTaintFontRequest: Opcode is " << (unsigned) opcode
+ << " expected client sequence is " << sequence
+ << ".\n" << logofs_flush;
+ #endif
+
+ unsigned char *reply = writeBuffer_.addMessage(36);
+
+ *(reply + 0) = X_Reply;
+
+ PutUINT(sequence, reply + 2, bigEndian_);
+
+ PutULONG(1, reply + 4, bigEndian_);
+
+ //
+ // Set the length of the returned
+ // path to 0.
+ //
+
+ *(reply + 32) = 0;
+
+ //
+ // Save the sequence number, not incremented
+ // yet, we used to auto-generate this reply.
+ //
+
+ lastSequence_ = clientSequence_ + 1;
+
+ #ifdef TEST
+ *logofs << "handleTaintFontRequest: Registered " << lastSequence_
+ << " as last auto-generated sequence number.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Taint the request to a X_NoOperation.
+ //
+
+ opcode = X_NoOperation;
+
+ if (handleFlush(flush_if_any) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+int ClientChannel::handleTaintSplitRequest(unsigned char &opcode, const unsigned char *&buffer,
+ unsigned int &size)
+{
+ #ifdef TEST
+
+ if (opcode == opcodeStore_ -> abortSplit)
+ {
+ *logofs << "handleTaintSplitRequest: Tainting abort split "
+ << "request for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ }
+ else if (opcode == opcodeStore_ -> finishSplit)
+ {
+ *logofs << "handleTaintSplitRequest: Tainting finish split "
+ << "request for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleTaintSplitRequest: Tainting free split "
+ << "request for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ //
+ // Taint the request to a X_NoOperation.
+ //
+
+ opcode = X_NoOperation;
+
+ return 1;
+}
+
+int ClientChannel::handleTaintLameRequest(unsigned char &opcode, const unsigned char *&buffer,
+ unsigned int &size)
+{
+ //
+ // Test the efficiency of the encoding
+ // without these RENDER requests.
+ //
+
+ if (opcode == opcodeStore_ -> renderExtension &&
+ (*(buffer + 1) == X_RenderCompositeGlyphs8 ||
+ *(buffer + 1) == X_RenderCompositeGlyphs16 ||
+ *(buffer + 1) == X_RenderCompositeGlyphs32 ||
+ *(buffer + 1) == X_RenderAddGlyphs ||
+ *(buffer + 1) == X_RenderTrapezoids))
+ {
+ #ifdef TEST
+ *logofs << "handleTaintLameRequest: Tainting request "
+ << "OPCODE#" << (unsigned int) opcode << " MINOR#"
+ << (unsigned int) *(buffer + 1) << " for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ opcode = X_NoOperation;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+int ClientChannel::handleTaintSyncRequest(unsigned char &opcode, const unsigned char *&buffer,
+ unsigned int &size)
+{
+ //
+ // Should short-circuit other common replies
+ // whose values could be queried only once.
+ // Examples are X_InterAtom, X_ListExtension
+ // and X_QueryExtension.
+ //
+
+ if (taintCounter_ >= control -> TaintThreshold)
+ {
+ #ifdef DEBUG
+ *logofs << "handleTaintSyncRequest: Reset taint counter after "
+ << taintCounter_ << " replies managed.\n"
+ << logofs_flush;
+ #endif
+
+ taintCounter_ = 0;
+
+ return 0;
+ }
+
+ //
+ // Check if we are rolling the counter.
+ // The client sequence number has not
+ // been incremented yet in the loop.
+ //
+
+ unsigned int sequence = (clientSequence_ + 1) & 0xffff;
+
+ #ifdef DEBUG
+ *logofs << "handleTaintSyncRequest: Opcode is " << (unsigned) opcode
+ << " expected client sequence is " << sequence
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (sequence == 0xffff)
+ {
+ return 0;
+ }
+
+ unsigned short t1;
+ unsigned char t2;
+
+ //
+ // Check if there is a previous reply
+ // pending.
+ //
+
+ if (sequenceQueue_.peek(t1, t2) != 0)
+ {
+ #ifdef DEBUG
+ *logofs << "handleTaintSyncRequest: Skipping taint of reply due to "
+ << "pending request OPCODE#" << t1 << " with sequence "
+ << (unsigned int) t2 << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ #ifdef DEBUG
+ *logofs << "handleTaintSyncRequest: Suppressing get input focus "
+ << "request for FD#" << fd_ << " with sequence "
+ << sequence << ".\n" << logofs_flush;
+ #endif
+
+ unsigned char *reply = writeBuffer_.addMessage(32);
+
+ *(reply + 0) = X_Reply;
+
+ PutUINT(sequence, reply + 2, bigEndian_);
+
+ PutULONG(0, reply + 4, bigEndian_);
+
+ //
+ // Set revert-to to none.
+ //
+
+ *(reply + 1) = 0;
+
+ //
+ // Set focus to none.
+ //
+
+ PutULONG(0, reply + 8, bigEndian_);
+
+ //
+ // Save the sequence number, not incremented
+ // yet, we used to auto-generate this reply.
+ //
+
+ lastSequence_ = clientSequence_ + 1;
+
+ #ifdef TEST
+ *logofs << "handleTaintSyncRequest: Registered " << lastSequence_
+ << " as last auto-generated sequence number.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Taint the request to a X_NoOperation.
+ //
+
+ opcode = X_NoOperation;
+
+ //
+ // We may assume that the client has finished
+ // drawing and flush immediately, even if this
+ // seems to perceively affect the performance.
+ //
+ // priority_++;
+ //
+
+ if (handleFlush(flush_if_any) < 0)
+ {
+ return -1;
+ }
+
+ taintCounter_++;
+
+ return 1;
+}
+
+int ClientChannel::handleTaintSyncError(unsigned char opcode)
+{
+ if (control -> TaintReplies > 0)
+ {
+ //
+ // By enabling short-circuiting of replies
+ // some window managers can get confused
+ // by some otherwise innocuous X errors.
+ //
+
+ if (opcode == X_GrabKey || opcode == X_ReparentWindow ||
+ opcode == X_ConfigureWindow)
+ {
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << "handleTaintSyncError: WARNING! Suppressed error "
+ << "on OPCODE#" << (unsigned int) opcode << " for FD#"
+ << fd_ << " sequence " << lastSequence_ << " (real was "
+ << serverSequence_ << ").\n" << logofs_flush;
+ #endif
+
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int ClientChannel::handleNotify(T_notification_type type, T_sequence_mode mode,
+ int resource, int request, int position)
+{
+ if (finish_ == 1)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleNotify: Discarding notification on "
+ << "channel for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ //
+ // Add a new message to the write buffer.
+ //
+
+ unsigned char *event = writeBuffer_.addMessage(32);
+
+ //
+ // Event is ClientMessage, atom and
+ // window are 0, format is 32.
+ //
+
+ *(event + 0) = ClientMessage;
+
+ PutULONG(0, event + 4, bigEndian_);
+ PutULONG(0, event + 8, bigEndian_);
+
+ *(event + 1) = 32;
+
+ //
+ // If the event follows immediately the request (that is the
+ // sequence mode is 'immediate') then the sequence number is
+ // the one of the last request, else it should be the last
+ // sequence number encoded by peer proxy but, as we are ins-
+ // erting events in the stream, we must ensure that the se-
+ // quence we send is not less than the last sequence we have
+ // auto-generated.
+ //
+
+ if (mode == sequence_immediate)
+ {
+ //
+ // Save the sequence number we used
+ // to auto-generate this event.
+ //
+
+ lastSequence_ = clientSequence_;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleNotify: Registered " << lastSequence_
+ << " as last auto-generated sequence number.\n"
+ << logofs_flush;
+ #endif
+ }
+ else
+ {
+ if (serverSequence_ > lastSequence_)
+ {
+ #ifdef DEBUG
+ *logofs << "handleNotify: Updating last event's sequence "
+ << lastSequence_ << " to X server's sequence number "
+ << serverSequence_ << " for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ lastSequence_ = serverSequence_;
+ }
+ #ifdef DEBUG
+ else if (serverSequence_ < lastSequence_)
+ {
+ //
+ // Use our last auto-generated sequence.
+ //
+
+ *logofs << "handleNotify: Tainting sequence number "
+ << serverSequence_ << " to last event's sequence "
+ << lastSequence_ << " for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ }
+ #endif
+ }
+
+ PutUINT(lastSequence_, event + 2, bigEndian_);
+
+ //
+ // Be sure we set to void the fields that
+ // are not significant for the specific
+ // notification message.
+ //
+
+ PutULONG(nothing, event + 16, bigEndian_);
+ PutULONG(nothing, event + 20, bigEndian_);
+ PutULONG(nothing, event + 24, bigEndian_);
+
+ switch (type)
+ {
+ case notify_no_split:
+ {
+ PutULONG(opcodeStore_ -> noSplitNotify,
+ event + 12, bigEndian_);
+
+ PutULONG(resource, event + 16, bigEndian_);
+
+ break;
+ }
+ case notify_start_split:
+ {
+ PutULONG(opcodeStore_ -> startSplitNotify,
+ event + 12, bigEndian_);
+
+ PutULONG(resource, event + 16, bigEndian_);
+
+ break;
+ }
+ case notify_commit_split:
+ {
+ PutULONG(opcodeStore_ -> commitSplitNotify,
+ event + 12, bigEndian_);
+
+ PutULONG(resource, event + 16, bigEndian_);
+
+ PutULONG(request, event + 20, bigEndian_);
+
+ PutULONG(position, event + 24, bigEndian_);
+
+ break;
+ }
+ case notify_end_split:
+ {
+ PutULONG(opcodeStore_ -> endSplitNotify,
+ event + 12, bigEndian_);
+
+ PutULONG(resource, event + 16, bigEndian_);
+
+ break;
+ }
+ case notify_empty_split:
+ {
+ PutULONG(opcodeStore_ -> emptySplitNotify,
+ event + 12, bigEndian_);
+ break;
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "handleNotify: PANIC! Unrecognized notify "
+ << "TYPE#" << type << ".\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ }
+
+ #if defined(TEST) || defined(INFO) || defined (SPLIT)
+
+ *logofs << "handleNotify: Sending "
+ << (mode == sequence_immediate ? "immediate " : "deferred ")
+ << "agent notify event TYPE#" << GetULONG(event + 12, bigEndian_)
+ << logofs_flush;
+
+ if (resource != nothing)
+ {
+ *logofs << " with resource " << GetULONG(event + 16, bigEndian_)
+ << logofs_flush;
+
+ if (request != nothing && position != nothing)
+ {
+ *logofs << " request " << GetULONG(event + 20, bigEndian_)
+ << " position " << GetULONG(event + 24, bigEndian_)
+ << logofs_flush;
+ }
+ }
+
+ *logofs << ".\n" << logofs_flush;
+
+ #endif
+
+ //
+ // Send the notification now.
+ //
+
+ if (handleFlush(flush_if_any) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+int ClientChannel::handleCommitSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size)
+{
+ //
+ // Get the data of the request to be
+ // committed.
+ //
+
+ unsigned char request = *(buffer + 5);
+
+ MessageStore *store = clientStore_ -> getRequestStore(request);
+
+ if (store == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "handleCommitSplitRequest: PANIC! Can't commit split for "
+ << "request OPCODE#" << (unsigned int) request
+ << ". No message store found.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't commit split for request "
+ << "OPCODE#" << (unsigned int) request
+ << ". No message store found.\n";
+
+ return -1;
+ }
+
+ //
+ // The position in cache of the message
+ // to commit. Encode it as difference in
+ // respect to the last encoded value.
+ //
+
+ unsigned int position = GetULONG(buffer + 8, bigEndian_);
+
+ unsigned char resource = *(buffer + 1);
+ unsigned int commit = *(buffer + 4);
+
+ #if defined(TEST) || defined(SPLIT)
+
+ if (commit == 1)
+ {
+ *logofs << "handleCommitSplitRequest: SPLIT! Committing request "
+ << "OPCODE#" << (unsigned) request << " at position "
+ << position << " for FD#" << fd_ << " with resource "
+ << (unsigned) resource << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleCommitSplitRequest: SPLIT! Discarding request "
+ << "OPCODE#" << (unsigned) request << " at position "
+ << position << " for FD#" << fd_ << " with resource "
+ << (unsigned) resource << ".\n" << logofs_flush;
+ }
+
+ #endif
+
+ encodeBuffer.encodeOpcodeValue(request, clientCache_ -> opcodeCache);
+
+ int diffCommit = position - splitState_.commit;
+
+ splitState_.commit = position;
+
+ encodeBuffer.encodeValue(diffCommit, 32, 5);
+
+ //
+ // Send the resource id and the commit
+ // flag.
+ //
+
+ encodeBuffer.encodeCachedValue(resource, 8,
+ clientCache_ -> resourceCache);
+
+ encodeBuffer.encodeBoolValue(commit);
+
+ //
+ // Remove the split from the split queue.
+ //
+
+ Split *split = handleSplitCommitRemove(request, resource, splitState_.commit);
+
+ if (split == NULL)
+ {
+ return -1;
+ }
+
+ clientStore_ -> getCommitStore() -> update(split);
+
+ //
+ // Free the split.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleCommitSplitRequest: SPLIT! Freeing up the "
+ << "committed split.\n" << logofs_flush;
+ #endif
+
+ delete split;
+
+ return 1;
+}
+
+int ClientChannel::handleAbortSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size)
+{
+ unsigned char resource = *(buffer + 1);
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleAbortSplitRequest: SPLIT! Handling abort split "
+ << "request for FD#"<< fd_ << " and resource "
+ << (unsigned int) resource << ".\n"
+ << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(resource, 8,
+ clientCache_ -> resourceCache);
+
+ SplitStore *splitStore = clientStore_ -> getSplitStore(resource);
+
+ if (splitStore == NULL)
+ {
+ #ifdef WARNING
+ *logofs << "handleAbortSplitRequest: WARNING! SPLIT! The split "
+ << "store [" << (unsigned int) resource << "] "
+ << "is already empty.\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ //
+ // Loop through the messages in the split
+ // store and discard from the memory cache
+ // the messages that are still incomplete.
+ // Then remove the message from the split
+ // store.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+
+ clientStore_ -> dumpSplitStore(resource);
+
+ #endif
+
+ int splits = 0;
+
+ Split *splitMessage;
+
+ for (;;)
+ {
+ splitMessage = splitStore -> getFirstSplit();
+
+ if (splitMessage == NULL)
+ {
+ //
+ // Check if we had created the store
+ // but no message was added yet.
+ //
+
+ #ifdef WARNING
+
+ if (splits == 0)
+ {
+ *logofs << "handleAbortSplitRequest: WARNING! SPLIT! The "
+ << "split store [" << (unsigned int) resource
+ << "] is unexpectedly empty.\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ break;
+ }
+
+ //
+ // Splits already aborted can't be in the
+ // split store.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+
+ if (splitMessage -> getState() == split_aborted)
+ {
+ *logofs << "handleAbortSplitRequest: PANIC! SPLIT! Found an "
+ << "aborted split in store [" << (unsigned int) resource
+ << "].\n" << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ if (splitMessage -> getAction() == IS_HIT)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleAbortSplitRequest: SPLIT! Removing the "
+ << "split from the memory cache.\n"
+ << logofs_flush;
+ #endif
+
+ splitMessage -> getStore() -> remove(splitMessage -> getPosition(),
+ use_checksum, discard_data);
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleAbortSplitRequest: SPLIT! Removing the "
+ << "split from the split store.\n"
+ << logofs_flush;
+ #endif
+
+ splitMessage = splitStore -> pop();
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleAbortSplitRequest: SPLIT! Freeing up the "
+ << "aborted split.\n" << logofs_flush;
+ #endif
+
+ delete splitMessage;
+
+ splits++;
+ }
+
+ //
+ // If the start-split/end-split sequence
+ // was closed, send the notification now,
+ // else wait for the end-split.
+ //
+
+ if (resource != splitState_.resource)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleAbortSplitRequest: SPLIT! Sending the "
+ << "deferred [end split] event.\n"
+ << logofs_flush;
+ #endif
+
+ handleRestart(sequence_deferred, resource);
+ }
+ #if defined(TEST) || defined(SPLIT)
+ else
+ {
+ *logofs << "handleAbortSplitRequest: WARNING! SPLIT! Still "
+ << "waiting for the closure of the split "
+ << "sequence.\n" << logofs_flush;
+ }
+ #endif
+
+ //
+ // Check if there is any other store
+ // having splits to send.
+ //
+
+ handleSplitPending();
+
+ return (splits > 0);
+}
+
+int ClientChannel::handleFinishSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size)
+{
+ unsigned char resource = *(buffer + 1);
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleFinishSplitRequest: SPLIT! Handling finish split "
+ << "request for FD#"<< fd_ << " and resource "
+ << (unsigned int) resource << ".\n"
+ << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(resource, 8,
+ clientCache_ -> resourceCache);
+
+ //
+ // We need to get the protocol statistics
+ // for the finish message we are handling
+ // here because sending a new split will
+ // reset the bits counter.
+ //
+
+ int bits = encodeBuffer.diffBits();
+
+ statistics -> addRequestBits(opcode, size << 3, bits);
+
+ SplitStore *splitStore = clientStore_ -> getSplitStore(resource);
+
+ if (splitStore == NULL)
+ {
+ #ifdef WARNING
+ *logofs << "handleFinishSplitRequest: WARNING! SPLIT! The split "
+ << "store [" << (unsigned int) resource << "] "
+ << "is already empty.\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ //
+ // Send all the split queued for the given
+ // resource until the split store becomes
+ // empty.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+
+ clientStore_ -> dumpSplitStore(resource);
+
+ #endif
+
+ Split *splitMessage;
+
+ int total = MESSAGE_DATA_LIMIT;
+
+ int bytes = total;
+ int splits = 0;
+
+ for (;;)
+ {
+ splitMessage = splitStore -> getFirstSplit();
+
+ if (splitMessage == NULL)
+ {
+ //
+ // We have presumably created the store
+ // after a start split but no message
+ // was added yet.
+ //
+
+ #ifdef WARNING
+ *logofs << "handleFinishSplitRequest: WARNING! SPLIT! The "
+ << "split store [" << (unsigned int) resource
+ << "] is unexpectedly empty.\n"
+ << logofs_flush;
+ #endif
+
+ break;
+ }
+
+ //
+ // Splits already aborted can't be in the
+ // split store.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+
+ if (splitMessage -> getState() == split_aborted)
+ {
+ *logofs << "handleFinishSplitRequest: PANIC! SPLIT! Found an "
+ << "aborted split in store [" << (unsigned int) resource
+ << "].\n" << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ *logofs << "handleFinishSplitRequest: SPLIT! Sending more "
+ << "data for store [" << (unsigned int) resource
+ << "].\n" << logofs_flush;
+ #endif
+
+ if (handleSplitSend(encodeBuffer, resource, splits, bytes) < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Check if the split store was deleted.
+ //
+
+ if (clientStore_ -> getSplitStore(resource) == NULL)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleFinishSplitRequest: SPLIT! Exiting "
+ << "from the finish loop with split store ["
+ << (unsigned int) resource << "] destroyed.\n"
+ << logofs_flush;
+ #endif
+
+ break;
+ }
+ }
+
+ //
+ // Check if there is any other store
+ // having splits to send.
+ //
+
+ handleSplitPending();
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleFinishSplitRequest: SPLIT! Sent " << splits
+ << " splits and " << total - bytes << " bytes for FD#" << fd_
+ << " with " << clientStore_ -> getSplitTotalStorageSize()
+ << " bytes and [" << clientStore_ -> getSplitTotalSize()
+ << "] splits remaining.\n" << logofs_flush;
+ #endif
+
+ return (splits > 0);
+}
+
+int ClientChannel::handleConfiguration()
+{
+ #ifdef TEST
+ *logofs << "ClientChannel: Setting new buffer parameters.\n"
+ << logofs_flush;
+ #endif
+
+ readBuffer_.setSize(control -> ClientInitialReadSize,
+ control -> ClientMaximumBufferSize);
+
+ writeBuffer_.setSize(control -> TransportXBufferSize,
+ control -> TransportXBufferThreshold,
+ control -> TransportMaximumBufferSize);
+
+ transport_ -> setSize(control -> TransportXBufferSize,
+ control -> TransportXBufferThreshold,
+ control -> TransportMaximumBufferSize);
+
+ return 1;
+}
+
+int ClientChannel::handleFinish()
+{
+ #ifdef TEST
+ *logofs << "ClientChannel: Finishing channel for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ congestion_ = 0;
+ priority_ = 0;
+
+ finish_ = 1;
+
+ taintCounter_ = 0;
+
+ splitState_.resource = nothing;
+ splitState_.pending = 0;
+ splitState_.commit = 0;
+ splitState_.mode = split_none;
+
+ transport_ -> finish();
+
+ return 1;
+}
+
+//
+// If differential compression is disabled then use the
+// most simple encoding but handle the image requests
+// and the X_ListExtensions and X_QueryExtension messa-
+// ges (needed to detect the opcode of the shape or the
+// other extensions) in the usual way.
+//
+
+int ClientChannel::handleFastReadRequest(EncodeBuffer &encodeBuffer, const unsigned char &opcode,
+ const unsigned char *&buffer, const unsigned int &size)
+{
+ //
+ // All the NX requests are handled in the
+ // main message loop. The X_PutImage can
+ // be handled here only if the split was
+ // not requested.
+ //
+
+ if ((opcode >= X_NXFirstOpcode && opcode <= X_NXLastOpcode) ||
+ (control -> isProtoStep7() == 1 && opcode == X_PutImage &&
+ splitState_.resource != nothing) || opcode == X_ListExtensions ||
+ opcode == X_QueryExtension)
+ {
+ return 0;
+ }
+
+ #ifdef DEBUG
+ *logofs << "handleFastReadRequest: Encoding raw request OPCODE#"
+ << (unsigned int) opcode << " for FD#" << fd_
+ << " with size " << size << ".\n"
+ << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeMemory(buffer, size);
+
+ //
+ // Put request on the fast track
+ // if it needs a reply.
+ //
+
+ switch (opcode)
+ {
+ case X_GetAtomName:
+ case X_GetGeometry:
+ case X_GetInputFocus:
+ case X_GetModifierMapping:
+ case X_GetKeyboardMapping:
+ case X_GetProperty:
+ case X_GetSelectionOwner:
+ case X_GrabPointer:
+ case X_GrabKeyboard:
+ case X_ListExtensions:
+ case X_ListFonts:
+ case X_LookupColor:
+ case X_AllocNamedColor:
+ case X_QueryPointer:
+ case X_GetWindowAttributes:
+ case X_QueryTree:
+ case X_QueryBestSize:
+ case X_QueryColors:
+ case X_QueryFont:
+ case X_TranslateCoords:
+ case X_GetImage:
+ case X_GetPointerMapping:
+ case X_GetKeyboardControl:
+ case X_InternAtom:
+ case X_AllocColor:
+ {
+ sequenceQueue_.push(clientSequence_, opcode);
+
+ priority_++;
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ int bits = encodeBuffer.diffBits();
+
+ #if defined(TEST) || defined(OPCODES)
+
+ *logofs << "handleFastReadRequest: Handled raw request OPCODE#"
+ << (unsigned int) opcode << " (" << DumpOpcode(opcode) << ")"
+ << " for FD#" << fd_ << " sequence " << clientSequence_
+ << ". " << size << " bytes in, " << bits << " bits ("
+ << ((float) bits) / 8 << " bytes) out.\n" << logofs_flush;
+
+ #endif
+
+ statistics -> addRequestBits(opcode, size << 3, bits);
+
+ if (opcode == opcodeStore_ -> renderExtension)
+ {
+ statistics -> addRenderRequestBits(*(buffer + 1), size << 3, bits);
+ }
+
+ return 1;
+}
+
+int ClientChannel::handleFastWriteReply(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+{
+ if ((opcode >= X_NXFirstOpcode &&
+ opcode <= X_NXLastOpcode) ||
+ opcode == X_ListExtensions ||
+ opcode == X_QueryExtension)
+ {
+ return 0;
+ }
+
+ #ifdef DEBUG
+ *logofs << "handleFastWriteReply: Decoding raw reply OPCODE#"
+ << (unsigned int) opcode << " for FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ buffer = writeBuffer_.addMessage(8);
+
+ #ifndef __sun
+
+ unsigned int *next = (unsigned int *) decodeBuffer.decodeMemory(8);
+
+ *((unsigned int *) buffer) = *next++;
+ *((unsigned int *) (buffer + 4)) = *next;
+
+ #else /* #ifndef __sun */
+
+ memcpy(buffer, decodeBuffer.decodeMemory(8), 8);
+
+ #endif /* #ifndef __sun */
+
+ size = 32 + (GetULONG(buffer + 4, bigEndian_) << 2);
+
+ writeBuffer_.registerPointer(&buffer);
+
+ if (writeBuffer_.getAvailable() < size - 8 ||
+ (int) size >= control -> TransportFlushBufferSize)
+ {
+ #ifdef DEBUG
+ *logofs << "handleFastWriteReply: Using scratch buffer for OPCODE#"
+ << (unsigned int) opcode << " with size " << size << " and "
+ << writeBuffer_.getLength() << " bytes in buffer.\n"
+ << logofs_flush;
+ #endif
+
+ writeBuffer_.removeMessage(8);
+
+ buffer = writeBuffer_.addScratchMessage(((unsigned char *)
+ decodeBuffer.decodeMemory(size - 8)) - 8, size);
+ }
+ else
+ {
+ writeBuffer_.addMessage(size - 8);
+
+ #ifndef __sun
+
+ if (size == 32)
+ {
+ next = (unsigned int *) decodeBuffer.decodeMemory(size - 8);
+
+ for (int i = 8; i < 32; i += sizeof(unsigned int))
+ {
+ *((unsigned int *) (buffer + i)) = *next++;
+ }
+ }
+ else
+ {
+ memcpy(buffer + 8, decodeBuffer.decodeMemory(size - 8), size - 8);
+ }
+
+ #else /* #ifndef __sun */
+
+ memcpy(buffer + 8, decodeBuffer.decodeMemory(size - 8), size - 8);
+
+ #endif /* #ifndef __sun */
+ }
+
+ writeBuffer_.unregisterPointer();
+
+ //
+ // We don't need to write our local sequence
+ // number. Replies are always sent with the
+ // original X server's sequence number.
+ //
+
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << "handleFastWriteReply: Handled raw reply OPCODE#"
+ << (unsigned int) opcode << " for FD#" << fd_ << " with sequence "
+ << serverSequence_ << ". Output size is " << size << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef DEBUG
+ *logofs << "handleFastWriteReply: Length of sequence queue is "
+ << sequenceQueue_.length() << ".\n" << logofs_flush;
+ #endif
+
+ statistics -> addRepliedRequest(opcode);
+
+ handleFlush(flush_if_needed);
+
+ return 1;
+}
+
+int ClientChannel::handleFastWriteEvent(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+{
+ #ifdef DEBUG
+ *logofs << "handleFastWriteEvent: Decoding raw "
+ << (opcode == X_Error ? "error" : "event") << " OPCODE#"
+ << (unsigned int) opcode << " for FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ size = 32;
+
+ buffer = writeBuffer_.addMessage(size);
+
+ #ifndef __sun
+
+ unsigned int *next = (unsigned int *) decodeBuffer.decodeMemory(size);
+
+ for (int i = 0; i < 32; i += sizeof(unsigned int))
+ {
+ *((unsigned int *) (buffer + i)) = *next++;
+ }
+
+ #else /* #ifndef __sun */
+
+ memcpy(buffer, decodeBuffer.decodeMemory(size), size);
+
+ #endif /* #ifndef __sun */
+
+ //
+ // Use our local sequence number.
+ //
+
+ PutUINT(lastSequence_, buffer + 2, bigEndian_);
+
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << "handleFastWriteEvent: Handled raw "
+ << (opcode == X_Error ? "error" : "event") << " OPCODE#"
+ << (unsigned int) opcode << " for FD#" << fd_ << " with sequence "
+ << lastSequence_ << ". Output size is " << size << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Check if we need to suppress the error.
+ //
+
+ if (opcode == X_Error && handleTaintSyncError(*(buffer + 10)) > 0)
+ {
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << "handleFastWriteEvent: WARNING! Suppressed error OPCODE#"
+ << (unsigned int) opcode << " for FD#" << fd_
+ << " with sequence " << lastSequence_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ writeBuffer_.removeMessage(32);
+ }
+
+ handleFlush(flush_if_needed);
+
+ return 1;
+}
+
+int ClientChannel::handleShmemRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size)
+{
+ //
+ // Will push sequence and set
+ // priority according to stage.
+ //
+
+ unsigned int stage = *(buffer + 1);
+
+ #ifdef TEST
+ *logofs << "handleShmemRequest: Encoding shmem request "
+ << "OPCODE#" << (unsigned int) opcode << " for FD#"
+ << fd_ << " with size " << size << " at stage "
+ << stage << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeValue(stage, 2);
+
+ if (stage == 0)
+ {
+ unsigned int enableClient = 0;
+ unsigned int enableServer = 0;
+
+ if (control -> ShmemClient == 1)
+ {
+ enableClient = *(buffer + 4);
+ }
+
+ if (control -> ShmemServer == 1)
+ {
+ enableServer = *(buffer + 5);
+ }
+
+ encodeBuffer.encodeBoolValue(enableClient);
+ encodeBuffer.encodeBoolValue(enableServer);
+
+ unsigned int clientSegment = GetULONG(buffer + 8, bigEndian_);
+ unsigned int serverSegment = GetULONG(buffer + 12, bigEndian_);
+
+ encodeBuffer.encodeValue(clientSegment, 29, 9);
+ encodeBuffer.encodeValue(serverSegment, 29, 9);
+
+ #ifdef TEST
+ *logofs << "handleShmemRequest: Enable client is "
+ << enableClient << " enable server is " << enableServer
+ << " client segment is " << (void *) clientSegment
+ << " server segment is " << (void *) serverSegment
+ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef TEST
+ *logofs << "handleShmemRequest: Size of the shared memory "
+ << "segment will be " << control -> ShmemServerSize
+ << ".\n" << logofs_flush;
+ #endif
+ }
+
+ if (stage != 1)
+ {
+ sequenceQueue_.push(clientSequence_, opcodeStore_ ->
+ getShmemParameters);
+
+ priority_++;
+ }
+
+ return 1;
+}
+
+int ClientChannel::handleShmemReply(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+{
+ #ifdef TEST
+ *logofs << "handleShmemReply: Received shmem parameters "
+ << "reply OPCODE#" << (unsigned int) opcode
+ << ".\n" << logofs_flush;
+ #endif
+
+ size = 32;
+ buffer = writeBuffer_.addMessage(size);
+
+ unsigned int stage;
+
+ decodeBuffer.decodeValue(stage, 2);
+
+ *(buffer + 1) = stage;
+
+ if (stage == 2)
+ {
+ unsigned int clientEnabled;
+ unsigned int serverEnabled;
+
+ decodeBuffer.decodeBoolValue(clientEnabled);
+ decodeBuffer.decodeBoolValue(serverEnabled);
+
+ //
+ // Client support is not implemented
+ // and not useful. It is here only
+ // for compatibility.
+ //
+
+ clientEnabled = 0;
+
+ *(buffer + 8) = clientEnabled;
+ *(buffer + 9) = serverEnabled;
+
+ PutULONG(0, buffer + 12, bigEndian_);
+
+ if (serverEnabled == 1)
+ {
+ #ifdef TEST
+ *logofs << "handleShmemReply: Enabled shared memory "
+ << "support in X server with segment size "
+ << control -> ShmemServerSize << ".\n"
+ << logofs_flush;
+ #endif
+
+ PutULONG(control -> ShmemServerSize, buffer + 16, bigEndian_);
+ }
+ else
+ {
+ PutULONG(0, buffer + 16, bigEndian_);
+ }
+ }
+ else
+ {
+ *(buffer + 8) = 0;
+ *(buffer + 9) = 0;
+
+ PutULONG(0, buffer + 12, bigEndian_);
+ PutULONG(0, buffer + 16, bigEndian_);
+ }
+
+ return 1;
+}
+
+int ClientChannel::handleFontRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size)
+{
+ #ifdef TEST
+ *logofs << "handleFontRequest: Encoding font request "
+ << "OPCODE#" << (unsigned int) opcode << " for FD#"
+ << fd_ << " with size " << size << ".\n"
+ << logofs_flush;
+ #endif
+
+ sequenceQueue_.push(clientSequence_, opcodeStore_ ->
+ getFontParameters);
+
+ return 1;
+}
+
+int ClientChannel::handleFontReply(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+{
+ #ifdef TEST
+ *logofs << "handleFontReply: Received font operation "
+ << "reply OPCODE#" << (unsigned int) opcode
+ << ".\n" << logofs_flush;
+ #endif
+
+ unsigned int length;
+
+ decodeBuffer.decodeValue(length, 8);
+
+ size = 32 + RoundUp4(length + 1);
+ buffer = writeBuffer_.addMessage(size);
+
+ unsigned char *next = buffer + 32;
+
+ *next++ = length;
+
+ decodeBuffer.decodeTextData(next, length);
+
+ #ifdef TEST
+
+ *logofs << "handleFontReply: Received tunneled font server "
+ << "path '";
+
+ for (unsigned int i = 0; i < length; i++)
+ {
+ *logofs << *(buffer + 32 + 1 + i);
+ }
+
+ *logofs << "' for FD#" << fd_ << ".\n" << logofs_flush;
+
+ #endif
+
+ if (fontPort_ == -1)
+ {
+ //
+ // The local side is not going to forward
+ // the font server connections.
+ //
+
+ #ifdef TEST
+ *logofs << "handleFontReply: WARNING! Returning an empty "
+ << "font server path.\n" << logofs_flush;
+ #endif
+
+ writeBuffer_.removeMessage(size);
+
+ size = 36;
+ buffer = writeBuffer_.addMessage(size);
+
+ //
+ // Set the length of the returned
+ // path to 0.
+ //
+
+ *(buffer + 32) = 0;
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "handleFontReply: Returning the received "
+ << "font server path.\n" << logofs_flush;
+ }
+ #endif
+
+ return 1;
+}
+
+int ClientChannel::handleCacheRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size)
+{
+ #ifdef TEST
+ *logofs << "handleCacheRequest: Handling cache request "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ enableCache_ = *(buffer + 4);
+ enableSplit_ = *(buffer + 5);
+ enableSave_ = *(buffer + 6);
+ enableLoad_ = *(buffer + 7);
+
+ handleSplitEnable();
+
+ #ifdef TEST
+ *logofs << "handleCacheRequest: Set cache parameters to "
+ << " cache " << enableCache_ << " split " << enableSplit_
+ << " save " << enableSave_ << " load " << enableLoad_
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Encode all the parameters as a
+ // single unsigned int so we can
+ // use an int cache.
+ //
+
+ unsigned int mask = enableSave_ << 8 | enableLoad_;
+
+ encodeBuffer.encodeCachedValue(mask, 32, clientCache_ ->
+ setCacheParametersCache);
+ return 0;
+}
+
+int ClientChannel::handleStartSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size)
+{
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleStartSplitRequest: SPLIT! Handling start split "
+ << "request for FD#"<< fd_ << ".\n" << logofs_flush;
+ #endif
+
+ if (splitState_.resource != nothing)
+ {
+ #ifdef PANIC
+ *logofs << "handleStartSplitRequest: PANIC! SPLIT! Split requested "
+ << "for resource id " << (unsigned int) *(buffer + 1)
+ << " while handling resource " << splitState_.resource
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Split requested for "
+ << "resource id " << (unsigned int) *(buffer + 1)
+ << " while handling resource " << splitState_.resource
+ << ".\n";
+
+ return -1;
+ }
+ else if (fd_ != firstClient_)
+ {
+ //
+ // It can be that an auxiliary channel is the
+ // first to connect, then comes the agent that
+ // is actually using the NX opcodes.
+ //
+
+ #ifdef WARNING
+ *logofs << "handleStartSplitRequest: WARNING SPLIT! Split requested "
+ << "on FD#" << fd_ << " while expecting FD#" << firstClient_
+ << ".\n" << logofs_flush;
+ #endif
+
+ firstClient_ = fd_;
+ }
+
+ //
+ // Set the agent's resource for which we are
+ // going to split the request.
+ //
+
+ splitState_.resource = *(buffer + 1);
+
+ #if defined(TEST) || defined(SPLIT)
+
+ *logofs << "handleStartSplitRequest: SPLIT! Registered id "
+ << splitState_.resource << " as resource "
+ << "waiting for a split.\n" << logofs_flush;
+
+ if (clientStore_ -> getSplitStore(splitState_.resource) != NULL)
+ {
+ *logofs << "handleStartSplitRequest: WARNING! SPLIT! A split "
+ << "store for resource id " << splitState_.resource
+ << " already exists.\n" << logofs_flush;
+
+ clientStore_ -> dumpSplitStore(splitState_.resource);
+ }
+
+ #endif
+
+ //
+ // Send the selected resource to the remote.
+ //
+
+ if (control -> isProtoStep7() == 1)
+ {
+ encodeBuffer.encodeCachedValue(splitState_.resource, 8,
+ clientCache_ -> resourceCache);
+ }
+
+ splitState_.mode = (T_split_mode) *(buffer + 4);
+
+ if (splitState_.mode != NXSplitModeAsync &&
+ splitState_.mode != NXSplitModeSync)
+ {
+ splitState_.mode = (T_split_mode) control -> SplitMode;
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleStartSplitRequest: SPLIT! Set split "
+ << "mode to '" << splitState_.mode << "' with "
+ << "provided value '" << (unsigned) *(buffer + 4)
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+
+ if (splitState_.mode == NXSplitModeAsync)
+ {
+ *logofs << "handleStartSplitRequest: SPLIT! Selected split "
+ << "mode is [split_async].\n" << logofs_flush;
+ }
+ else if (splitState_.mode == NXSplitModeSync)
+ {
+ *logofs << "handleStartSplitRequest: SPLIT! Selected split "
+ << "mode is [split_sync].\n" << logofs_flush;
+ }
+
+ clientStore_ -> dumpSplitStores();
+
+ #endif
+
+ return 1;
+}
+
+int ClientChannel::handleEndSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size)
+{
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleEndSplitRequest: SPLIT! Handling end split "
+ << "request for FD#"<< fd_ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Verify that the agent resource matches.
+ //
+
+ if (splitState_.resource == nothing)
+ {
+ #ifdef PANIC
+ *logofs << "handleEndSplitRequest: PANIC! SPLIT! Received an end of "
+ << "split for resource id " << (unsigned int) *(buffer + 1)
+ << " without a previous start.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Received an end of split "
+ << "for resource id " << (unsigned int) *(buffer + 1)
+ << " without a previous start.\n";
+
+ return -1;
+ }
+ else if (splitState_.resource != *(buffer + 1))
+ {
+ #ifdef PANIC
+ *logofs << "handleEndSplitRequest: PANIC! SPLIT! Invalid resource id "
+ << (unsigned int) *(buffer + 1) << " received while "
+ << "waiting for resource id " << splitState_.resource
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid resource id "
+ << (unsigned int) *(buffer + 1) << " received while "
+ << "waiting for resource id " << splitState_.resource
+ << ".\n";
+
+ return -1;
+ }
+
+ //
+ // Send the selected resource to the remote.
+ //
+
+ if (control -> isProtoStep7() == 1)
+ {
+ encodeBuffer.encodeCachedValue(splitState_.resource, 8,
+ clientCache_ -> resourceCache);
+ }
+
+ //
+ // Send the split notification events
+ // to the agent.
+ //
+
+ handleRestart(sequence_immediate, splitState_.resource);
+
+ //
+ // Check if we still have splits to send.
+ //
+
+ handleSplitPending();
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleEndSplitRequest: SPLIT! Reset id "
+ << splitState_.resource << " as resource "
+ << "selected for splits.\n" << logofs_flush;
+ #endif
+
+ splitState_.resource = nothing;
+ splitState_.mode = split_none;
+
+ #if defined(TEST) || defined(SPLIT)
+
+ clientStore_ -> dumpSplitStores();
+
+ #endif
+
+ return 1;
+}
+
+void ClientChannel::handleDecodeCharInfo(DecodeBuffer &decodeBuffer, unsigned char *nextDest)
+{
+ unsigned int value;
+
+ decodeBuffer.decodeCachedValue(value, 32,
+ *serverCache_ -> queryFontCharInfoCache[0], 6);
+
+ PutUINT(value & 0xffff, nextDest, bigEndian_);
+ PutUINT(value >> 16, nextDest + 10, bigEndian_);
+
+ nextDest += 2;
+
+ for (unsigned int i = 1; i < 5; i++)
+ {
+ unsigned int value;
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *serverCache_ -> queryFontCharInfoCache[i], 6);
+
+ PutUINT(value, nextDest, bigEndian_);
+
+ nextDest += 2;
+ }
+}
+
+int ClientChannel::setBigEndian(int flag)
+{
+ bigEndian_ = flag;
+
+ return 1;
+}
+
+int ClientChannel::setReferences()
+{
+ #ifdef TEST
+ *logofs << "ClientChannel: Initializing the static "
+ << "members for the client channels.\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef REFERENCES
+
+ references_ = 0;
+
+ #endif
+
+ return 1;
+}
diff --git a/nxcomp/ClientChannel.h b/nxcomp/ClientChannel.h
new file mode 100644
index 000000000..9924bb263
--- /dev/null
+++ b/nxcomp/ClientChannel.h
@@ -0,0 +1,466 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef ClientChannel_H
+#define ClientChannel_H
+
+#include "List.h"
+#include "Channel.h"
+
+#include "SequenceQueue.h"
+
+#include "ClientReadBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// If defined, the client channel will
+// have the chance of suppressing more
+// opcodes for test purposes.
+//
+
+#undef LAME
+
+//
+// Define this to log a line when a
+// channel is created or destroyed.
+//
+
+#undef REFERENCES
+
+//
+// This class implements the X client
+// side compression of the protocol.
+//
+
+class ClientChannel : public Channel
+{
+ public:
+
+ ClientChannel(Transport *transport, StaticCompressor *compressor);
+
+ virtual ~ClientChannel();
+
+ virtual int handleRead(EncodeBuffer &encodeBuffer, const unsigned char *message,
+ unsigned int length);
+
+ virtual int handleWrite(const unsigned char *message, unsigned int length);
+
+ virtual int handleSplit(EncodeBuffer &encodeBuffer, MessageStore *store,
+ T_store_action action, int position, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size);
+
+ virtual int handleSplit(DecodeBuffer &decodeBuffer, MessageStore *store,
+ T_store_action action, int position, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+ {
+ return 0;
+ }
+
+ virtual int handleSplit(EncodeBuffer &encodeBuffer);
+
+ virtual int handleSplit(DecodeBuffer &decodeBuffer)
+ {
+ return 0;
+ }
+
+ virtual int handleSplitEvent(EncodeBuffer &encodeBuffer, Split *split);
+
+ virtual int handleSplitEvent(DecodeBuffer &decodeBuffer);
+
+ virtual int handleMotion(EncodeBuffer &encodeBuffer)
+ {
+ return 0;
+ }
+
+ virtual int handleCompletion(EncodeBuffer &encodeBuffer)
+ {
+ return 0;
+ }
+
+ virtual int handleConfiguration();
+
+ virtual int handleFinish();
+
+ virtual int handleAsyncEvents()
+ {
+ return 0;
+ }
+
+ virtual int needSplit() const
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "needSplit: SPLIT! Returning pending split "
+ << "flag " << splitState_.pending << " with "
+ << clientStore_ -> getSplitTotalSize()
+ << " splits in the split stores.\n"
+ << logofs_flush;
+ #endif
+
+ return splitState_.pending;
+ }
+
+ virtual int needMotion() const
+ {
+ return 0;
+ }
+
+ virtual T_channel_type getType() const
+ {
+ return channel_x11;
+ }
+
+ int setBigEndian(int flag);
+
+ //
+ // Initialize the static members.
+ //
+
+ static int setReferences();
+
+ private:
+
+ int handleFastReadRequest(EncodeBuffer &encodeBuffer, const unsigned char &opcode,
+ const unsigned char *&buffer, const unsigned int &size);
+
+ int handleFastWriteReply(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size);
+
+ int handleFastWriteEvent(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size);
+
+ //
+ // Intercept the request before the opcode
+ // is encoded.
+ //
+
+ int handleTaintRequest(unsigned char &opcode, const unsigned char *&buffer,
+ unsigned int &size)
+ {
+ if (control -> isProtoStep7() == 0)
+ {
+ if (opcode == X_NXFreeSplit || opcode == X_NXAbortSplit ||
+ opcode == X_NXFinishSplit)
+ {
+ return handleTaintSplitRequest(opcode, buffer, size);
+ }
+ else if (opcode == X_NXSetCacheParameters)
+ {
+ return handleTaintCacheRequest(opcode, buffer, size);
+ }
+ else if (opcode == X_NXGetFontParameters)
+ {
+ return handleTaintFontRequest(opcode, buffer, size);
+ }
+ }
+
+ if (control -> TaintReplies > 0 &&
+ opcode == X_GetInputFocus)
+ {
+ return handleTaintSyncRequest(opcode, buffer, size);
+ }
+
+ #ifdef LAME
+
+ return handleTaintLameRequest(opcode, buffer, size);
+
+ #endif
+
+ return 0;
+ }
+
+ int handleTaintCacheRequest(unsigned char &opcode, const unsigned char *&buffer,
+ unsigned int &size);
+
+ int handleTaintFontRequest(unsigned char &opcode, const unsigned char *&buffer,
+ unsigned int &size);
+
+ int handleTaintSplitRequest(unsigned char &opcode, const unsigned char *&buffer,
+ unsigned int &size);
+
+ int handleTaintLameRequest(unsigned char &opcode, const unsigned char *&buffer,
+ unsigned int &size);
+
+ int handleTaintSyncRequest(unsigned char &opcode, const unsigned char *&buffer,
+ unsigned int &size);
+
+ int handleTaintSyncError(unsigned char opcode);
+
+ //
+ // How to handle sequence counter
+ // in notification event.
+ //
+
+ enum T_sequence_mode
+ {
+ sequence_immediate,
+ sequence_deferred
+ };
+
+ //
+ // Send split notifications to the
+ // agent.
+ //
+
+ int handleRestart(T_sequence_mode mode, int resource);
+
+ int handleNotify(T_notification_type type, T_sequence_mode mode,
+ int resource, int request, int position);
+
+ //
+ // Other utility functions used in
+ // handling of the image streaming.
+ //
+
+ int mustSplitMessage(int resource)
+ {
+ return (clientStore_ -> getSplitStore(resource) ->
+ getSize() != 0);
+ }
+
+ int canSplitMessage(T_split_mode mode, unsigned int size)
+ {
+ return ((int) size >= control -> SplitDataThreshold &&
+ (clientStore_ -> getSplitTotalStorageSize() < control ->
+ SplitTotalStorageSize && clientStore_ ->
+ getSplitTotalSize() < control -> SplitTotalSize));
+ }
+
+ int canSendSplit(Split *split)
+ {
+ return (split -> getMode() != split_sync ||
+ split -> getState() == split_missed ||
+ split -> getState() == split_loaded);
+ }
+
+ int handleSplitSend(EncodeBuffer &encodeBuffer, int resource,
+ int &total, int &bytes);
+
+ Split *handleSplitFind(T_checksum checksum, int resource);
+
+ int handleSplitChecksum(EncodeBuffer &encodeBuffer, T_checksum checksum);
+
+ void handleSplitEnable()
+ {
+ if (control -> isProtoStep7() == 0)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitEnable: WARNING! Disabling split "
+ << "with an old proxy version.\n"
+ << logofs_flush;
+ #endif
+
+ enableSplit_ = 0;
+ }
+ }
+
+ void handleSplitPending(int resource)
+ {
+ if (splitState_.pending == 0)
+ {
+ if (clientStore_ -> getSplitStore(resource) != NULL &&
+ clientStore_ -> getSplitStore(resource) ->
+ getFirstSplit() != NULL)
+ {
+ splitState_.pending = canSendSplit(clientStore_ ->
+ getSplitStore(resource) -> getFirstSplit());
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitPending: SPLIT! Set the pending "
+ << "split flag to " << splitState_.pending
+ << " with " << clientStore_ -> getSplitTotalSize()
+ << " splits in the split stores.\n"
+ << logofs_flush;
+ #endif
+ }
+ }
+ }
+
+ //
+ // Scan all the split stores to find
+ // if there is any split to send.
+ //
+
+ void handleSplitPending();
+
+ //
+ // Handle the MIT-SHM initialization
+ // messages exchanged with the remote
+ // proxy.
+ //
+
+ int handleShmemRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size);
+
+ int handleShmemReply(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size);
+
+ //
+ // Query the port used to tunnel
+ // the font server connections.
+ //
+
+ int handleFontRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size);
+
+ int handleFontReply(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size);
+
+ //
+ // Let the agent set the cache
+ // policy for image requests.
+ //
+
+ int handleCacheRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size);
+
+ //
+ // Encode the start and end split
+ // requests.
+ //
+
+ int handleStartSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size);
+
+ int handleEndSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size);
+
+ //
+ // Empty a split store and send the
+ // restart event.
+ //
+
+ int handleAbortSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size);
+
+ //
+ // Force the proxy to finalize all
+ // the pending split operations and
+ // restart a resource.
+ //
+
+ int handleFinishSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size);
+
+ //
+ // Tell the remote peer to send the
+ // split requests to the X server.
+ //
+
+ int handleCommitSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size);
+
+ //
+ // Other utilities.
+ //
+
+ void handleDecodeCharInfo(DecodeBuffer &, unsigned char *);
+
+ //
+ // Own read buffer. It is able to identify
+ // full messages read from the X descriptor.
+ //
+
+ ClientReadBuffer readBuffer_;
+
+ //
+ // Sequence number of last request coming
+ // from the X client or the X server.
+ //
+
+ unsigned int clientSequence_;
+ unsigned int serverSequence_;
+
+ //
+ // Last sequence number known by client. It can
+ // be the real sequence generated by server or
+ // the one of the last auto-generated event.
+ //
+
+ unsigned int lastSequence_;
+
+ //
+ // Used to identify replies based on sequence
+ // number of original request.
+ //
+
+ SequenceQueue sequenceQueue_;
+
+ //
+ // This is used to test the synchronous flush
+ // in the proxy.
+ //
+
+ int lastRequest_;
+
+ //
+ // Current resource id selected as target and
+ // other information related to the image split.
+ // The pending and abort flags are set when we
+ // want the proxy to give us a chance to send
+ // more split data. We also save the position
+ // of the last commit operation performed by
+ // channel so we can differentially encode the
+ // position of next message to commit.
+ //
+
+ typedef struct
+ {
+ int resource;
+ int pending;
+ int commit;
+ T_split_mode mode;
+
+ } T_split_state;
+
+ T_split_state splitState_;
+
+ //
+ // List of agent resources.
+ //
+
+ List splitResources_;
+
+ //
+ // How many sync requests we
+ // have tainted so far.
+ //
+
+ int taintCounter_;
+
+ private:
+
+ //
+ // Keep track of object
+ // creation and deletion.
+ //
+
+ #ifdef REFERENCES
+
+ static int references_;
+
+ #endif
+};
+
+#endif /* ClientChannel_H */
diff --git a/nxcomp/ClientProxy.cpp b/nxcomp/ClientProxy.cpp
new file mode 100644
index 000000000..ef63bb0eb
--- /dev/null
+++ b/nxcomp/ClientProxy.cpp
@@ -0,0 +1,538 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "Socket.h"
+#include "Agent.h"
+
+#include "ClientProxy.h"
+
+#include "ClientChannel.h"
+#include "GenericChannel.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Log the operations related to sending
+// and receiving the control tokens.
+//
+
+#undef TOKEN
+
+ClientProxy::ClientProxy(int proxyFd) : Proxy(proxyFd)
+{
+ fontServerPort_ = NULL;
+
+ #ifdef DEBUG
+ *logofs << "ClientProxy: Created new object at " << this
+ << ".\n" << logofs_flush;
+ #endif
+}
+
+ClientProxy::~ClientProxy()
+{
+ delete [] fontServerPort_;
+
+ #ifdef DEBUG
+ *logofs << "ClientProxy: Deleted object at " << this
+ << ".\n" << logofs_flush;
+ #endif
+}
+
+void ClientProxy::handleDisplayConfiguration(const char *xServerDisplay, int xServerAddrFamily,
+ sockaddr * xServerAddr, unsigned int xServerAddrLength)
+{
+ #ifdef DEBUG
+ *logofs << "ClientProxy: No display configuration to set.\n"
+ << logofs_flush;
+ #endif
+}
+
+void ClientProxy::handlePortConfiguration(int cupsServerPort, int smbServerPort, int mediaServerPort,
+ int httpServerPort, const char *fontServerPort)
+{
+ delete [] fontServerPort_;
+
+ fontServerPort_ = new char[strlen(fontServerPort) + 1];
+
+ strcpy(fontServerPort_, fontServerPort);
+
+ #ifdef DEBUG
+ *logofs << "ClientProxy: Set port configuration to font '"
+ << fontServerPort_ << "'.\n"
+ << logofs_flush;
+ #endif
+}
+
+int ClientProxy::handleNewConnection(T_channel_type type, int clientFd)
+{
+ switch (type)
+ {
+ case channel_x11:
+ {
+ return handleNewXConnection(clientFd);
+ }
+ case channel_cups:
+ {
+ return handleNewGenericConnection(clientFd, channel_cups, "CUPS");
+ }
+ case channel_smb:
+ {
+ return handleNewGenericConnection(clientFd, channel_smb, "SMB");
+ }
+ case channel_media:
+ {
+ return handleNewGenericConnection(clientFd, channel_media, "media");
+ }
+ case channel_http:
+ {
+ return handleNewGenericConnection(clientFd, channel_http, "HTTP");
+ }
+ case channel_slave:
+ {
+ return handleNewSlaveConnection(clientFd);
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "ClientProxy: PANIC! Unsupported channel with type '"
+ << getTypeName(type) << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Unsupported channel with type '"
+ << getTypeName(type) << "'.\n";
+
+ return -1;
+ }
+ }
+}
+
+int ClientProxy::handleNewConnectionFromProxy(T_channel_type type, int channelId)
+{
+ switch (type)
+ {
+ case channel_font:
+ {
+ int port = atoi(fontServerPort_);
+
+ if (port > 0)
+ {
+ //
+ // Connect on the TCP port number.
+ //
+
+ return handleNewGenericConnectionFromProxy(channelId, channel_font, "localhost",
+ port, "font");
+ }
+ else
+ {
+ //
+ // Connect to the Unix path.
+ //
+
+ return handleNewGenericConnectionFromProxy(channelId, channel_font, "localhost",
+ fontServerPort_, "font");
+ }
+ }
+ case channel_slave:
+ {
+ return handleNewSlaveConnectionFromProxy(channelId);
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "ClientProxy: PANIC! Unsupported channel with type '"
+ << getTypeName(type) << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Unsupported channel with type '"
+ << getTypeName(type) << "'.\n";
+
+ return -1;
+ }
+ }
+}
+
+int ClientProxy::handleNewAgentConnection(Agent *agent)
+{
+ int clientFd = agent -> getLocalFd();
+
+ int channelId = allocateChannelMap(clientFd);
+
+ if (channelId == -1)
+ {
+ #ifdef PANIC
+ *logofs << "ClientProxy: PANIC! Maximum mumber of available "
+ << "channels exceeded.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Maximum mumber of available "
+ << "channels exceeded.\n";
+
+ return -1;
+ }
+
+ transports_[channelId] = agent -> getTransport();
+
+ agent_ = channelId;
+
+ return handleNewXConnection(clientFd);
+}
+
+int ClientProxy::handleNewXConnection(int clientFd)
+{
+ int channelId = getChannel(clientFd);
+
+ //
+ // Check if the channel has been
+ // already mapped.
+ //
+
+ if (channelId == -1)
+ {
+ channelId = allocateChannelMap(clientFd);
+
+ if (channelId == -1)
+ {
+ #ifdef PANIC
+ *logofs << "ClientProxy: PANIC! Maximum mumber of available "
+ << "channels exceeded.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Maximum mumber of available "
+ << "channels exceeded.\n";
+
+ return -1;
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "ClientProxy: X client descriptor FD#" << clientFd
+ << " mapped to channel ID#" << channelId << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Turn queuing off for path proxy-to-X-client.
+ //
+
+ if (control -> OptionClientNoDelay == 1)
+ {
+ SetNoDelay(clientFd, control -> OptionClientNoDelay);
+ }
+
+ //
+ // If requested, set the size of the TCP send
+ // and receive buffers.
+ //
+
+ if (control -> OptionClientSendBuffer != -1)
+ {
+ SetSendBuffer(clientFd, control -> OptionClientSendBuffer);
+ }
+
+ if (control -> OptionClientReceiveBuffer != -1)
+ {
+ SetReceiveBuffer(clientFd, control -> OptionClientReceiveBuffer);
+ }
+
+ if (allocateTransport(clientFd, channelId) < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Starting from protocol level 3 client and server
+ // caches are created in proxy and shared between all
+ // channels. If remote proxy has older protocol level
+ // pointers are NULL and channels must create their
+ // own instances.
+ //
+
+ channels_[channelId] = new ClientChannel(transports_[channelId], compressor_);
+
+ if (channels_[channelId] == NULL)
+ {
+ deallocateTransport(channelId);
+
+ return -1;
+ }
+
+ increaseChannels(channelId);
+
+ //
+ // Propagate channel stores and caches to the new
+ // channel.
+ //
+
+ channels_[channelId] -> setOpcodes(opcodeStore_);
+
+ channels_[channelId] -> setStores(clientStore_, serverStore_);
+
+ channels_[channelId] -> setCaches(clientCache_, serverCache_);
+
+ int port = atoi(fontServerPort_);
+
+ if (port > 0 || *fontServerPort_ != '\0')
+ {
+ channels_[channelId] -> setPorts(1);
+ }
+
+ if (handleControl(code_new_x_connection, channelId) < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Let channel configure itself according
+ // to control parameters.
+ //
+
+ channels_[channelId] -> handleConfiguration();
+
+ return 1;
+}
+
+int ClientProxy::handleNewXConnectionFromProxy(int channelId)
+{
+ #ifdef PANIC
+ *logofs << "ClientProxy: PANIC! Can't create a new X channel "
+ << "with ID#" << channelId << " at this side.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't create a new X channel "
+ << "with ID#" << channelId << " at this side.\n";
+
+ return -1;
+}
+
+int ClientProxy::handleLoad(T_load_type type)
+{
+ int channelCount = getChannels(channel_x11);
+
+ if ((channelCount == 0 && type == load_if_first) ||
+ (channelCount > 0 && type == load_if_any))
+ {
+ #ifdef TEST
+ *logofs << "ClientProxy: Going to load content of client store.\n"
+ << logofs_flush;
+ #endif
+
+ int result = handleLoadStores();
+
+ if (result == 1)
+ {
+ if (handleControl(code_load_request) < 0)
+ {
+ return -1;
+ }
+
+ priority_ = 1;
+ }
+ else if (result < 0)
+ {
+ #ifdef WARNING
+ *logofs << "ClientProxy: WARNING! Failed to load content "
+ << "of persistent cache.\n" << logofs_flush;
+ #endif
+
+ //
+ // Don't abort the proxy connection in the case
+ // of a corrupted cache. By not sending the load
+ // message to the remote peer, both sides will
+ // start encoding messages using empty stores.
+ // This behaviour is compatible with old proxy
+ // versions.
+ //
+
+ if (channelCount == 0 && type == load_if_first)
+ {
+ if (handleResetStores() < 0)
+ {
+ #ifdef PANIC
+ *logofs << "ClientProxy: PANIC! Failed to reset message stores.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "ClientProxy: PANIC! Can't load the stores with "
+ << channelCount << " remaining channels.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int ClientProxy::handleSave()
+{
+ //
+ // If no more X channels are remaining
+ // then save content of message stores.
+ //
+
+ int channelCount = getChannels(channel_x11);
+
+ if (channelCount == 0)
+ {
+ int result = handleSaveStores();
+
+ if (result == 1)
+ {
+ if (handleControl(code_save_request) < 0)
+ {
+ return -1;
+ }
+
+ priority_ = 1;
+
+ return 1;
+ }
+ else if (result < 0)
+ {
+ #ifdef PANIC
+ *logofs << "ClientProxy: PANIC! Failed to save stores "
+ << "to persistent cache.\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "ClientProxy: PANIC! Can't save the stores with "
+ << channelCount << " remaining channels.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int ClientProxy::handleAsyncEvents()
+{
+ if (canRead() == 1)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: WARNING! Reading while writing "
+ << "with data available on the proxy link.\n"
+ << logofs_flush;
+ #endif
+
+ if (handleRead() < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+int ClientProxy::handleLoadFromProxy()
+{
+ #ifdef PANIC
+ *logofs << "ClientProxy: PANIC! Invalid load control message "
+ << "received in proxy.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid load control message "
+ << "received in proxy.\n";
+
+ return -1;
+}
+
+int ClientProxy::handleSaveFromProxy()
+{
+ #ifdef PANIC
+ *logofs << "ClientProxy: PANIC! Invalid save control message "
+ << "received in proxy.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid save control message "
+ << "received in proxy.\n";
+
+ return -1;
+}
+
+int ClientProxy::handleSaveAllStores(ostream *cachefs, md5_state_t *md5StateStream,
+ md5_state_t *md5StateClient) const
+{
+ if (clientStore_ -> saveRequestStores(cachefs, md5StateStream, md5StateClient,
+ use_checksum, discard_data) < 0)
+ {
+ return -1;
+ }
+ else if (serverStore_ -> saveReplyStores(cachefs, md5StateStream, md5StateClient,
+ discard_checksum, use_data) < 0)
+ {
+ return -1;
+ }
+ else if (serverStore_ -> saveEventStores(cachefs, md5StateStream, md5StateClient,
+ discard_checksum, use_data) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+int ClientProxy::handleLoadAllStores(istream *cachefs, md5_state_t *md5StateStream) const
+{
+ if (clientStore_ -> loadRequestStores(cachefs, md5StateStream,
+ use_checksum, discard_data) < 0)
+ {
+ return -1;
+ }
+ else if (serverStore_ -> loadReplyStores(cachefs, md5StateStream,
+ discard_checksum, use_data) < 0)
+ {
+ return -1;
+ }
+ else if (serverStore_ -> loadEventStores(cachefs, md5StateStream,
+ discard_checksum, use_data) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
diff --git a/nxcomp/ClientProxy.h b/nxcomp/ClientProxy.h
new file mode 100644
index 000000000..2b669ba2d
--- /dev/null
+++ b/nxcomp/ClientProxy.h
@@ -0,0 +1,108 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef ClientProxy_H
+#define ClientProxy_H
+
+#include "Proxy.h"
+
+//
+// Set the verbosity level.
+//
+
+#undef TEST
+#undef DEBUG
+
+class ClientProxy : public Proxy
+{
+ public:
+
+ ClientProxy(int proxyFD);
+
+ virtual ~ClientProxy();
+
+ virtual void handleDisplayConfiguration(const char *xServerDisplay, int xServerAddrFamily,
+ sockaddr *xServerAddr, unsigned int xServerAddrLength);
+
+ virtual void handlePortConfiguration(int cupsServerPort, int smbServerPort, int mediaServerPort,
+ int httpServerPort, const char *fontServerPort);
+
+ protected:
+
+ //
+ // Create a new channel.
+ //
+
+ virtual int handleNewConnection(T_channel_type type, int clientFd);
+
+ virtual int handleNewConnectionFromProxy(T_channel_type type, int channelId);
+
+ virtual int handleNewAgentConnection(Agent *agent);
+
+ virtual int handleNewXConnection(int clientFd);
+
+ virtual int handleNewXConnectionFromProxy(int channelId);
+
+ //
+ // Implement persistence according
+ // to our proxy mode.
+ //
+
+ virtual int handleLoad(T_load_type type);
+ virtual int handleSave();
+
+ virtual int handleAsyncEvents();
+
+ virtual int handleLoadFromProxy();
+ virtual int handleSaveFromProxy();
+
+ virtual int handleSaveAllStores(ostream *cachefs, md5_state_t *md5StateStream,
+ md5_state_t *md5StateClient) const;
+
+ virtual int handleLoadAllStores(istream *cachefs, md5_state_t *md5StateStream) const;
+
+ //
+ // Utility function used to realize
+ // a new connection.
+ //
+
+ protected:
+
+ virtual int checkLocalChannelMap(int channelId)
+ {
+ if (control -> isProtoStep7() == 1)
+ {
+ return ((channelId & control -> ChannelMask) != 0);
+ }
+ else
+ {
+ return 1;
+ }
+ }
+
+ //
+ // Ports where to forward extended services'
+ // TCP connections.
+ //
+
+ private:
+
+ char *fontServerPort_;
+};
+
+
+#endif /* ClientProxy_H */
diff --git a/nxcomp/ClientReadBuffer.cpp b/nxcomp/ClientReadBuffer.cpp
new file mode 100644
index 000000000..b32033b17
--- /dev/null
+++ b/nxcomp/ClientReadBuffer.cpp
@@ -0,0 +1,166 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "ClientReadBuffer.h"
+
+#include "ClientChannel.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+unsigned int ClientReadBuffer::suggestedLength(unsigned int pendingLength)
+{
+ //
+ // Even if the pending data is not
+ // enough to make a complete message,
+ // resize the buffer to accomodate
+ // it all.
+ //
+
+ unsigned int readLength = pendingLength;
+
+ if (pendingLength < remaining_)
+ {
+ readLength = remaining_;
+ }
+
+ return readLength;
+}
+
+int ClientReadBuffer::locateMessage(const unsigned char *start,
+ const unsigned char *end,
+ unsigned int &controlLength,
+ unsigned int &dataLength,
+ unsigned int &trailerLength)
+{
+ unsigned int size = end - start;
+
+ #ifdef TEST
+ *logofs << "ClientReadBuffer: Locating message for FD#"
+ << transport_ -> fd() << " with " << size
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ if (firstMessage_)
+ {
+ if (size < 12)
+ {
+ remaining_ = 12 - size;
+
+ #ifdef TEST
+ *logofs << "ClientReadBuffer: No message was located "
+ << "with remaining " << remaining_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ if (*start == 0x42)
+ {
+ bigEndian_ = 1;
+ }
+ else
+ {
+ bigEndian_ = 0;
+ }
+
+ channel_ -> setBigEndian(bigEndian_);
+
+ dataLength = 12 + RoundUp4(GetUINT(start + 6, bigEndian_)) +
+ RoundUp4(GetUINT(start + 8, bigEndian_));
+
+ //
+ // Send the data immediately if this is unlikely
+ // to be a X connection attempt.
+ //
+
+ if (dataLength > 4096)
+ {
+ #ifdef WARNING
+ *logofs << "ClientReadBuffer: WARNING! Flushing suspicious X "
+ << "connection with first request of " << dataLength
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ dataLength = size;
+ }
+ }
+ else
+ {
+ if (size < 4)
+ {
+ remaining_ = 4 - size;
+
+ #ifdef TEST
+ *logofs << "ClientReadBuffer: No message was located "
+ << "with remaining " << remaining_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ dataLength = (GetUINT(start + 2, bigEndian_) << 2);
+
+ if (dataLength < 4)
+ {
+ #ifdef TEST
+ *logofs << "ClientReadBuffer: WARNING! Assuming length 4 "
+ << "for suspicious message of length " << dataLength
+ << ".\n" << logofs_flush;
+ #endif
+
+ dataLength = 4;
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "ClientReadBuffer: Length of the next message is "
+ << dataLength << ".\n" << logofs_flush;
+ #endif
+
+ if (size < dataLength)
+ {
+ remaining_ = dataLength - size;
+
+ #ifdef TEST
+ *logofs << "ClientReadBuffer: No message was located "
+ << "with remaining " << remaining_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ firstMessage_ = 0;
+
+ controlLength = 0;
+ trailerLength = 0;
+
+ remaining_ = 0;
+
+ #ifdef TEST
+ *logofs << "ClientReadBuffer: Located message with "
+ << "remaining " << remaining_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
diff --git a/nxcomp/ClientReadBuffer.h b/nxcomp/ClientReadBuffer.h
new file mode 100644
index 000000000..6dee630ac
--- /dev/null
+++ b/nxcomp/ClientReadBuffer.h
@@ -0,0 +1,57 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef ClientReadBuffer_H
+#define ClientReadBuffer_H
+
+#include "Control.h"
+#include "ReadBuffer.h"
+
+class ClientChannel;
+
+class ClientReadBuffer : public ReadBuffer
+{
+ public:
+
+ ClientReadBuffer(Transport *transport, ClientChannel *channel)
+
+ : ReadBuffer(transport), firstMessage_(1), channel_(channel)
+ {
+ }
+
+ virtual ~ClientReadBuffer()
+ {
+ }
+
+ protected:
+
+ virtual unsigned int suggestedLength(unsigned int pendingLength);
+
+ virtual int locateMessage(const unsigned char *start,
+ const unsigned char *end,
+ unsigned int &controlLength,
+ unsigned int &dataLength,
+ unsigned int &trailerLength);
+
+ int bigEndian_;
+
+ int firstMessage_;
+
+ ClientChannel *channel_;
+};
+
+#endif /* ClientReadBuffer_H */
diff --git a/nxcomp/ClientStore.cpp b/nxcomp/ClientStore.cpp
new file mode 100644
index 000000000..be0e892b4
--- /dev/null
+++ b/nxcomp/ClientStore.cpp
@@ -0,0 +1,228 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "ClientStore.h"
+
+//
+// Cached request classes.
+//
+
+#include "ChangeProperty.h"
+#include "SendEvent.h"
+#include "CreateGC.h"
+#include "ChangeGC.h"
+#include "CreatePixmap.h"
+#include "SetClipRectangles.h"
+#include "CopyArea.h"
+#include "PolyLine.h"
+#include "PolySegment.h"
+#include "PolyFillRectangle.h"
+#include "PutImage.h"
+#include "TranslateCoords.h"
+#include "GetImage.h"
+#include "ClearArea.h"
+#include "ConfigureWindow.h"
+#include "ShapeExtension.h"
+#include "RenderExtension.h"
+#include "PolyText8.h"
+#include "PolyText16.h"
+#include "ImageText8.h"
+#include "ImageText16.h"
+#include "PolyPoint.h"
+#include "PolyFillArc.h"
+#include "PolyArc.h"
+#include "FillPoly.h"
+#include "InternAtom.h"
+#include "GetProperty.h"
+#include "SetUnpackGeometry.h"
+#include "SetUnpackColormap.h"
+#include "SetUnpackAlpha.h"
+#include "PutPackedImage.h"
+#include "GenericRequest.h"
+
+#include "ChangeGCCompat.h"
+#include "CreatePixmapCompat.h"
+#include "SetUnpackColormapCompat.h"
+#include "SetUnpackAlphaCompat.h"
+
+//
+// Set the verbosity level.
+//
+
+#define WARNING
+#define PANIC
+#undef TEST
+
+ClientStore::ClientStore(StaticCompressor *compressor)
+
+ : compressor_(compressor)
+{
+ if (logofs == NULL)
+ {
+ logofs = &cout;
+ }
+
+ for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++)
+ {
+ requests_[i] = NULL;
+ }
+
+ requests_[X_ChangeProperty] = new ChangePropertyStore();
+ requests_[X_SendEvent] = new SendEventStore();
+ requests_[X_CreateGC] = new CreateGCStore();
+ requests_[X_SetClipRectangles] = new SetClipRectanglesStore();
+ requests_[X_CopyArea] = new CopyAreaStore();
+ requests_[X_PolyLine] = new PolyLineStore();
+ requests_[X_PolySegment] = new PolySegmentStore();
+ requests_[X_PolyFillRectangle] = new PolyFillRectangleStore();
+ requests_[X_PutImage] = new PutImageStore(compressor);
+ requests_[X_TranslateCoords] = new TranslateCoordsStore();
+ requests_[X_GetImage] = new GetImageStore();
+ requests_[X_ClearArea] = new ClearAreaStore();
+ requests_[X_ConfigureWindow] = new ConfigureWindowStore();
+ requests_[X_PolyText8] = new PolyText8Store();
+ requests_[X_PolyText16] = new PolyText16Store();
+ requests_[X_ImageText8] = new ImageText8Store();
+ requests_[X_ImageText16] = new ImageText16Store();
+ requests_[X_PolyPoint] = new PolyPointStore();
+ requests_[X_PolyFillArc] = new PolyFillArcStore();
+ requests_[X_PolyArc] = new PolyArcStore();
+ requests_[X_FillPoly] = new FillPolyStore();
+ requests_[X_InternAtom] = new InternAtomStore();
+ requests_[X_GetProperty] = new GetPropertyStore();
+
+ requests_[X_NXInternalShapeExtension] = new ShapeExtensionStore(compressor);
+ requests_[X_NXInternalGenericRequest] = new GenericRequestStore(compressor);
+ requests_[X_NXInternalRenderExtension] = new RenderExtensionStore(compressor);
+ requests_[X_NXSetUnpackGeometry] = new SetUnpackGeometryStore(compressor);
+ requests_[X_NXPutPackedImage] = new PutPackedImageStore(compressor);
+
+ if (control -> isProtoStep7() == 1)
+ {
+ requests_[X_ChangeGC] = new ChangeGCStore();
+ requests_[X_CreatePixmap] = new CreatePixmapStore();
+ requests_[X_NXSetUnpackColormap] = new SetUnpackColormapStore(compressor);
+ requests_[X_NXSetUnpackAlpha] = new SetUnpackAlphaStore(compressor);
+ }
+ else
+ {
+ requests_[X_ChangeGC] = new ChangeGCCompatStore();
+ requests_[X_CreatePixmap] = new CreatePixmapCompatStore();
+ requests_[X_NXSetUnpackColormap] = new SetUnpackColormapCompatStore(compressor);
+ requests_[X_NXSetUnpackAlpha] = new SetUnpackAlphaCompatStore(compressor);
+ }
+
+ for (int i = 0; i < CHANNEL_STORE_RESOURCE_LIMIT; i++)
+ {
+ splits_[i] = NULL;
+ }
+
+ commits_ = new CommitStore(compressor);
+}
+
+ClientStore::~ClientStore()
+{
+ if (logofs == NULL)
+ {
+ logofs = &cout;
+ }
+
+ for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++)
+ {
+ delete requests_[i];
+ }
+
+ for (int i = 0; i < CHANNEL_STORE_RESOURCE_LIMIT; i++)
+ {
+ delete splits_[i];
+ }
+
+ delete commits_;
+}
+
+int ClientStore::saveRequestStores(ostream *cachefs, md5_state_t *md5StateStream,
+ md5_state_t *md5StateClient, T_checksum_action checksumAction,
+ T_data_action dataAction) const
+{
+ for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++)
+ {
+ if (requests_[i] != NULL &&
+ requests_[i] -> saveStore(cachefs, md5StateStream, md5StateClient,
+ checksumAction, dataAction,
+ storeBigEndian()) < 0)
+ {
+ #ifdef WARNING
+ *logofs << "ClientStore: WARNING! Error saving request store "
+ << "for OPCODE#" << (unsigned int) i << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Error saving request store "
+ << "for opcode '" << (unsigned int) i << "'.\n";
+
+ return -1;
+ }
+ }
+
+ return 1;
+}
+
+int ClientStore::loadRequestStores(istream *cachefs, md5_state_t *md5StateStream,
+ T_checksum_action checksumAction, T_data_action dataAction) const
+{
+ for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++)
+ {
+ if (requests_[i] != NULL &&
+ requests_[i] -> loadStore(cachefs, md5StateStream,
+ checksumAction, dataAction,
+ storeBigEndian()) < 0)
+ {
+ #ifdef WARNING
+ *logofs << "ClientStore: WARNING! Error loading request store "
+ << "for OPCODE#" << (unsigned int) i << ".\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ }
+
+ return 1;
+}
+
+void ClientStore::dumpSplitStores() const
+{
+ for (int i = 0; i < CHANNEL_STORE_RESOURCE_LIMIT; i++)
+ {
+ if (splits_[i] != NULL)
+ {
+ splits_[i] -> dump();
+ }
+ }
+
+ if ((getSplitTotalSize() != 0 && getSplitTotalStorageSize() == 0) ||
+ (getSplitTotalSize() == 0 && getSplitTotalStorageSize() != 0))
+ {
+ #ifdef PANIC
+ *logofs << "ClientStore: PANIC! Inconsistency detected "
+ << "while handling the split stores.\n"
+ << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+}
diff --git a/nxcomp/ClientStore.h b/nxcomp/ClientStore.h
new file mode 100644
index 000000000..54a68a309
--- /dev/null
+++ b/nxcomp/ClientStore.h
@@ -0,0 +1,135 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef ClientStore_H
+#define ClientStore_H
+
+#include "Message.h"
+#include "Split.h"
+
+#include "ChannelStore.h"
+
+class StaticCompressor;
+
+class ClientStore : public ChannelStore
+{
+ public:
+
+ ClientStore(StaticCompressor *compressor);
+
+ virtual ~ClientStore();
+
+ //
+ // Get the store based on the index.
+ //
+
+ MessageStore *getRequestStore(unsigned char opcode) const
+ {
+ return requests_[opcode];
+ }
+
+ SplitStore *getSplitStore(int resource) const
+ {
+ return splits_[resource];
+ }
+
+ int getSplitTotalSize() const
+ {
+ return SplitStore::getTotalSize();
+ }
+
+ int getSplitTotalStorageSize() const
+ {
+ return SplitStore::getTotalStorageSize();
+ }
+
+ CommitStore *getCommitStore() const
+ {
+ return commits_;
+ }
+
+ int getCommitSize() const
+ {
+ return commits_ -> getSize();
+ }
+
+ void dumpSplitStore(int resource) const
+ {
+ splits_[resource] -> dump();
+ }
+
+ void dumpCommitStore() const
+ {
+ commits_ -> dump();
+ }
+
+ void dumpSplitStores() const;
+
+ SplitStore *createSplitStore(int resource)
+ {
+ splits_[resource] = new SplitStore(compressor_, commits_, resource);
+
+ return splits_[resource];
+ }
+
+ void destroySplitStore(int resource)
+ {
+ delete splits_[resource];
+
+ splits_[resource] = NULL;
+ }
+
+ //
+ // Actually save the message store
+ // to disk according to proxy mode.
+ //
+
+ int saveRequestStores(ostream *cachefs, md5_state_t *md5StateStream,
+ md5_state_t *md5StateClient, T_checksum_action checksumAction,
+ T_data_action dataAction) const;
+
+ int loadRequestStores(istream *cachefs, md5_state_t *md5StateStream,
+ T_checksum_action checksumAction, T_data_action dataAction) const;
+
+ private:
+
+ //
+ // A client store contains requests.
+ //
+
+ MessageStore *requests_[CHANNEL_STORE_OPCODE_LIMIT];
+
+ //
+ // Client messages being split.
+ //
+
+ SplitStore *splits_[CHANNEL_STORE_RESOURCE_LIMIT];
+
+ //
+ // Messages having been recomposed.
+ //
+
+ CommitStore *commits_;
+
+ //
+ // Passed forward to the other stores.
+ //
+
+ StaticCompressor *compressor_;
+};
+
+#endif /* ClientStore_H */
diff --git a/nxcomp/Colormap.cpp b/nxcomp/Colormap.cpp
new file mode 100644
index 000000000..5702beca9
--- /dev/null
+++ b/nxcomp/Colormap.cpp
@@ -0,0 +1,94 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "Misc.h"
+#include "Unpack.h"
+#include "Colormap.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+int UnpackColormap(unsigned char method, unsigned char *src_data, int src_size,
+ unsigned char *dst_data, int dst_size)
+{
+ if (*src_data == 0)
+ {
+ if (dst_size != src_size - 1)
+ {
+ #ifdef TEST
+ *logofs << "UnpackColormap: PANIC! Invalid destination size "
+ << dst_size << " with source " << src_size
+ << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "UnpackColormap: Expanding " << src_size - 1
+ << " bytes of plain colormap data.\n" << logofs_flush;
+ #endif
+
+ memcpy(dst_data, src_data + 1, src_size - 1);
+
+ return 1;
+ }
+
+ unsigned int check_size = dst_size;
+
+ int result = ZDecompress(&unpackStream, dst_data, &check_size,
+ src_data + 1, src_size - 1);
+
+ if (result != Z_OK)
+ {
+ #ifdef PANIC
+ *logofs << "UnpackColormap: PANIC! Failure decompressing colormap data. "
+ << "Error is '" << zError(result) << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decompressing colormap data. "
+ << "Error is '" << zError(result) << "'.\n";
+
+ return -1;
+ }
+ else if (check_size != (unsigned int) dst_size)
+ {
+ #ifdef PANIC
+ *logofs << "UnpackColormap: PANIC! Size mismatch in colormap data. "
+ << "Resulting size is " << check_size << " with "
+ << "expected size " << dst_size << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Size mismatch in colormap data. "
+ << "Resulting size is " << check_size << " with "
+ << "expected size " << dst_size << ".\n";
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "UnpackColormap: Decompressed " << src_size - 1
+ << " bytes to " << dst_size << " bytes of colormap data.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
diff --git a/nxcomp/Colormap.h b/nxcomp/Colormap.h
new file mode 100644
index 000000000..e0056f86c
--- /dev/null
+++ b/nxcomp/Colormap.h
@@ -0,0 +1,24 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef Colormap_H
+#define Colormap_H
+
+int UnpackColormap(unsigned char method, unsigned char *src_data, int src_size,
+ unsigned char *dst_data, int dst_size);
+
+#endif /* Colormap_H */
diff --git a/nxcomp/ConfigureWindow.cpp b/nxcomp/ConfigureWindow.cpp
new file mode 100644
index 000000000..995ab1831
--- /dev/null
+++ b/nxcomp/ConfigureWindow.cpp
@@ -0,0 +1,130 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "ConfigureWindow.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int ConfigureWindowStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ConfigureWindowMessage *configureWindow = (ConfigureWindowMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ configureWindow -> window = GetULONG(buffer + 4, bigEndian);
+
+ configureWindow -> value_mask = GetUINT(buffer + 8, bigEndian);
+
+ //
+ // To increase effectiveness of the caching algorithm
+ // we remove the unused bytes carried in the data part.
+ //
+
+ if ((int) size > dataOffset)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Removing unused bytes from the data payload.\n" << logofs_flush;
+ #endif
+
+ configureWindow -> value_mask &= (1 << 7) - 1;
+
+ unsigned int mask = 0x1;
+ unsigned char *source = (unsigned char *) buffer + CONFIGUREWINDOW_DATA_OFFSET;
+ unsigned long value = 0;
+
+ for (unsigned int i = 0; i < 7; i++)
+ {
+ if (configureWindow -> value_mask & mask)
+ {
+ value = GetULONG(source, bigEndian);
+
+ value &= (1 << CONFIGUREWINDOW_FIELD_WIDTH[i]) - 1;
+
+ PutULONG(value, source, bigEndian);
+
+ source += 4;
+ }
+ mask <<= 1;
+ }
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int ConfigureWindowStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ConfigureWindowMessage *configureWindow = (ConfigureWindowMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ PutULONG(configureWindow -> window, buffer + 4, bigEndian);
+
+ PutUINT(configureWindow -> value_mask, buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void ConfigureWindowStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ ConfigureWindowMessage *configureWindow = (ConfigureWindowMessage *) message;
+
+ *logofs << "ConfigureWindow: window " << configureWindow -> window
+ << ", value_mask " << configureWindow -> value_mask
+ << ", size " << configureWindow -> size_ << ".\n";
+
+ #endif
+}
+
+void ConfigureWindowStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ md5_append(md5_state_, buffer + 4, 4);
+ md5_append(md5_state_, buffer + 8, 2);
+}
diff --git a/nxcomp/ConfigureWindow.h b/nxcomp/ConfigureWindow.h
new file mode 100644
index 000000000..bb511b0d4
--- /dev/null
+++ b/nxcomp/ConfigureWindow.h
@@ -0,0 +1,170 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef ConfigureWindow_H
+#define ConfigureWindow_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define CONFIGUREWINDOW_ENABLE_CACHE 1
+#define CONFIGUREWINDOW_ENABLE_DATA 0
+#define CONFIGUREWINDOW_ENABLE_SPLIT 0
+#define CONFIGUREWINDOW_ENABLE_COMPRESS 0
+
+#define CONFIGUREWINDOW_DATA_LIMIT 32
+#define CONFIGUREWINDOW_DATA_OFFSET 12
+
+#define CONFIGUREWINDOW_CACHE_SLOTS 3000
+#define CONFIGUREWINDOW_CACHE_THRESHOLD 5
+#define CONFIGUREWINDOW_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class ConfigureWindowMessage : public Message
+{
+ friend class ConfigureWindowStore;
+
+ public:
+
+ ConfigureWindowMessage()
+ {
+ }
+
+ ~ConfigureWindowMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned int window;
+ unsigned short value_mask;
+};
+
+class ConfigureWindowStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ ConfigureWindowStore() : MessageStore()
+ {
+ enableCache = CONFIGUREWINDOW_ENABLE_CACHE;
+ enableData = CONFIGUREWINDOW_ENABLE_DATA;
+ enableSplit = CONFIGUREWINDOW_ENABLE_SPLIT;
+ enableCompress = CONFIGUREWINDOW_ENABLE_COMPRESS;
+
+ dataLimit = CONFIGUREWINDOW_DATA_LIMIT;
+ dataOffset = CONFIGUREWINDOW_DATA_OFFSET;
+
+ cacheSlots = CONFIGUREWINDOW_CACHE_SLOTS;
+ cacheThreshold = CONFIGUREWINDOW_CACHE_THRESHOLD;
+ cacheLowerThreshold = CONFIGUREWINDOW_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~ConfigureWindowStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "ConfigureWindow";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_ConfigureWindow;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(ConfigureWindowMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new ConfigureWindowMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new ConfigureWindowMessage((const ConfigureWindowMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (ConfigureWindowMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* ConfigureWindow_H */
diff --git a/nxcomp/Control.cpp b/nxcomp/Control.cpp
new file mode 100644
index 000000000..ce99567d7
--- /dev/null
+++ b/nxcomp/Control.cpp
@@ -0,0 +1,896 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "NXpack.h"
+
+#include "Control.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Flush immediately on prioritized messages.
+//
+
+#define FLUSH_PRIORITY 0
+
+//
+// Maximum number of bytes sent for each token.
+//
+
+#define TOKEN_SIZE 1536
+
+//
+// Maximum number of tokens that can be spent
+// by the client proxy before having to block
+// waiting for a token reply.
+//
+
+#define TOKEN_LIMIT 24
+
+//
+// By default assume the proxy is running as a
+// standalone program.
+//
+
+#define LINK_ENCRYPTED 0
+
+//
+// Maximum number of pids the proxy will record
+// and kill at shutdown.
+//
+
+#define KILL_LIMIT 16
+
+//
+// Allocate on the NX client side channels whose
+// ids are a multiple of 8 (starting from 0). All
+// the other ids can be used to allocate channels
+// at the NX server side (X client side).
+//
+
+#define CHANNEL_MASK 0x07
+
+//
+// Kill session if control parameters cannot be
+// negotiated before this timeout.
+//
+
+#define INIT_TIMEOUT 60000
+
+//
+// Enter the congestion state if the remote does
+// not reply to a ping within the given amount
+// of time.
+//
+
+#define PING_TIMEOUT 5000
+
+//
+// Only send one motion event any N milliseconds.
+//
+
+#define MOTION_TIMEOUT 0
+
+
+//
+// Force an update of the congestion counter if
+// the proxy is idle for this time.
+//
+
+#define IDLE_TIMEOUT 50
+
+//
+// Close X connection if can't write before this
+// timeout.
+//
+
+#define CHANNEL_TIMEOUT 10000
+
+//
+// Warn user (or close proxy connection) if don't
+// receive any data before this timeout.
+//
+
+#define PROXY_TIMEOUT 120000
+
+//
+// How many milliseconds to wait for the shared
+// memory completion event to become available.
+//
+
+#define SHMEM_TIMEOUT 200
+//
+// Before closing down the proxy, wait for the
+// given amount of miliseconds to let all the
+// running applications to close down their
+// connections.
+//
+// A null timeout will cause the proxy to wait
+// indefinitely, until the watchdog process is
+// killed. This is usually the way the proxy is
+// started by the NX server. If on the other
+// hand a timeout is given and there no channel
+// is remaining, the proxy will be closed down
+// using a small timeout, presently of 500 ms.
+//
+
+#define CLEANUP_TIMEOUT 3000
+
+//
+// Wait this amount of milliseconds after any
+// iteration of the house-keeping process.
+//
+
+#define KEEPER_TIMEOUT 60000
+
+//
+// In case of timeout, select can return control
+// to program earlier or later of this amount of
+// ms. Consider this when calculating if timeout
+// is elapsed.
+//
+
+#define LATENCY_TIMEOUT 1
+
+//
+// Control memory allocation in transport
+// and other classes.
+//
+
+#define TRANSPORT_X_BUFFER_SIZE 131072
+#define TRANSPORT_PROXY_BUFFER_SIZE 65536
+#define TRANSPORT_GENERIC_BUFFER_SIZE 16384
+
+#define TRANSPORT_X_BUFFER_THRESHOLD 262144
+#define TRANSPORT_PROXY_BUFFER_THRESHOLD 131072
+#define TRANSPORT_GENERIC_BUFFER_THRESHOLD 32768
+
+//
+// Never allow buffers to exceed this limit.
+//
+
+#define TRANSPORT_MAXIMUM_BUFFER_SIZE 393216
+
+//
+// Immediately flush the accumulated data to
+// the X server if the write buffer exceeds
+// this size.
+//
+
+#define TRANSPORT_FLUSH_BUFFER_SIZE 16384
+
+//
+// Defaults used for socket options.
+//
+
+#define OPTION_PROXY_KEEP_ALIVE 0
+#define OPTION_PROXY_LOW_DELAY 1
+#define OPTION_PROXY_CLIENT_NO_DELAY 1
+#define OPTION_PROXY_SERVER_NO_DELAY 1
+#define OPTION_CLIENT_NO_DELAY 1
+#define OPTION_SERVER_NO_DELAY 1
+
+#define OPTION_PROXY_RECEIVE_BUFFER -1
+#define OPTION_CLIENT_RECEIVE_BUFFER -1
+#define OPTION_SERVER_RECEIVE_BUFFER -1
+
+#define OPTION_PROXY_SEND_BUFFER -1
+#define OPTION_CLIENT_SEND_BUFFER -1
+#define OPTION_SERVER_SEND_BUFFER -1
+
+#define OPTION_PROXY_RETRY_CONNECT 30
+#define OPTION_PROXY_RETRY_ACCEPT 3
+#define OPTION_SERVER_RETRY_CONNECT 3
+
+//
+// Defaults used for cache persistence.
+//
+
+#define PERSISTENT_CACHE_THRESHOLD 102400
+
+#define PERSISTENT_CACHE_ENABLE_LOAD 1
+#define PERSISTENT_CACHE_ENABLE_SAVE 1
+
+#define PERSISTENT_CACHE_CHECK_ON_SHUTDOWN 0
+
+#define PERSISTENT_CACHE_LOAD_PACKED 1
+#define PERSISTENT_CACHE_LOAD_RENDER 1
+
+#define PERSISTENT_CACHE_DISK_LIMIT 33554432
+
+//
+// Defaults used for image cache.
+//
+
+#define IMAGE_CACHE_ENABLE_LOAD 0
+#define IMAGE_CACHE_ENABLE_SAVE 0
+
+#define IMAGE_CACHE_DISK_LIMIT 33554432
+
+//
+// Suggested defaults for read length parameters
+// used by read buffer classes.
+//
+
+#define CLIENT_INITIAL_READ_SIZE 8192
+#define CLIENT_MAXIMUM_BUFFER_SIZE 262144
+
+#define SERVER_INITIAL_READ_SIZE 8192
+#define SERVER_MAXIMUM_BUFFER_SIZE 65536
+
+#define PROXY_INITIAL_READ_SIZE 65536
+#define PROXY_MAXIMUM_BUFFER_SIZE 262144 + 1024
+
+#define GENERIC_INITIAL_READ_SIZE 8192
+#define GENERIC_MAXIMUM_BUFFER_SIZE 8192
+
+//
+// Calculate bitrate in given time frames.
+// Values are in milliseconds.
+//
+
+#define SHORT_BITRATE_TIME_FRAME 5000
+#define LONG_BITRATE_TIME_FRAME 30000
+
+//
+// Bandwidth control. A value of 0 means no
+// limit. Values are stored internally in
+// bytes per second.
+//
+
+#define CLIENT_BITRATE_LIMIT 0
+#define SERVER_BITRATE_LIMIT 0
+
+//
+// Default values for cache control. We limit
+// the maximum size of a request to 262144 but
+// we need to consider the replies, whose size
+// may be up to 4MB.
+//
+
+#define MINIMUM_MESSAGE_SIZE 4
+#define MAXIMUM_MESSAGE_SIZE 4194304
+#define MAXIMUM_REQUEST_SIZE 262144
+
+#define CLIENT_TOTAL_STORAGE_SIZE 8388608
+#define SERVER_TOTAL_STORAGE_SIZE 8388608
+
+#define STORE_TIME_LIMIT 3600
+
+#define STORE_HITS_LOAD_BONUS 10
+#define STORE_HITS_ADD_BONUS 20
+#define STORE_HITS_LIMIT 100
+
+#define STORE_HITS_TOUCH 1
+#define STORE_HITS_UNTOUCH 2
+
+//
+// Default parameters for message splitting.
+//
+
+#define SPLIT_MODE 1
+#define SPLIT_TIMEOUT 50
+#define SPLIT_TOTAL_SIZE 128
+#define SPLIT_TOTAL_STORAGE_SIZE 1048576
+#define SPLIT_DATA_THRESHOLD 65536
+#define SPLIT_DATA_PACKET_LIMIT 24576
+
+//
+// Agent related parameters.
+//
+
+#define PACK_METHOD 63
+#define PACK_QUALITY 9
+#define HIDE_RENDER 0
+#define TAINT_REPLIES 1
+#define TAINT_THRESHOLD 8
+
+//
+// In current version only X server support is
+// implemented. Note that use of shared memory
+// is negotiated according to options provided
+// by the user.
+//
+
+#define SHMEM_CLIENT 0
+#define SHMEM_SERVER 1
+
+//
+// Default size of shared memory segments used
+// in MIT-SHM support.
+//
+
+#define SHMEM_CLIENT_SIZE 0
+#define SHMEM_SERVER_SIZE 2097152
+
+//
+// What do we do at the end of session? If this
+// flag is set, we launch a new client letting
+// the user run a new NX session.
+//
+
+#define ENABLE_RESTART_ON_SHUTDOWN 0
+
+//
+// Do we produce a core dump on fatal errors?
+//
+
+#define ENABLE_CORE_DUMP_ON_ABORT 0
+
+//
+// Reopen the log file if it exceeds this size.
+//
+
+#define FILE_SIZE_LIMIT 60000000
+
+//
+// Check periodically if we need to truncate the
+// log file. By default check every minute.
+//
+
+#define FILE_SIZE_CHECK_TIMEOUT 60000
+
+//
+// Set defaults for control. They should be what
+// you get in case of 'local' connection.
+//
+
+Control::Control()
+{
+ ProxyMode = proxy_undefined;
+ ProxyStage = stage_undefined;
+ SessionMode = session_undefined;
+ FlushPolicy = policy_undefined;
+ LinkMode = link_undefined;
+
+ LinkEncrypted = LINK_ENCRYPTED;
+ FlushPriority = FLUSH_PRIORITY;
+
+ TokenSize = TOKEN_SIZE;
+ TokenLimit = TOKEN_LIMIT;
+
+ ChannelMask = CHANNEL_MASK;
+
+ InitTimeout = INIT_TIMEOUT;
+ PingTimeout = PING_TIMEOUT;
+ MotionTimeout = MOTION_TIMEOUT;
+ IdleTimeout = IDLE_TIMEOUT;
+
+ ChannelTimeout = CHANNEL_TIMEOUT;
+ ProxyTimeout = PROXY_TIMEOUT;
+ ShmemTimeout = SHMEM_TIMEOUT;
+
+ CleanupTimeout = CLEANUP_TIMEOUT;
+ KeeperTimeout = KEEPER_TIMEOUT;
+ LatencyTimeout = LATENCY_TIMEOUT;
+
+ FileSizeLimit = FILE_SIZE_LIMIT;
+ FileSizeCheckTimeout = FILE_SIZE_CHECK_TIMEOUT;
+
+ EnableRestartOnShutdown = ENABLE_RESTART_ON_SHUTDOWN;
+
+ KillDaemonOnShutdownLimit = KILL_LIMIT;
+
+ KillDaemonOnShutdown = new int[KillDaemonOnShutdownLimit];
+
+ for (int i = 0; i < KILL_LIMIT; i++)
+ {
+ KillDaemonOnShutdown[i] = -1;
+ }
+
+ KillDaemonOnShutdownNumber = 0;
+
+ EnableCoreDumpOnAbort = ENABLE_CORE_DUMP_ON_ABORT;
+
+ //
+ // Collect statistics by default.
+ //
+
+ EnableStatistics = 1;
+
+ //
+ // Memory restrictions if any.
+ //
+
+ LocalMemoryLevel = -1;
+
+ //
+ // Compression must be negotiated between proxies.
+ //
+
+ LocalDeltaCompression = -1;
+ RemoteDeltaCompression = -1;
+
+ LocalDataCompression = -1;
+ LocalStreamCompression = -1;
+
+ RemoteDataCompression = -1;
+ RemoteStreamCompression = -1;
+
+ LocalDataCompressionLevel = -1;
+ LocalDataCompressionThreshold = -1;
+ LocalStreamCompressionLevel = -1;
+
+ RemoteDataCompressionLevel = -1;
+ RemoteStreamCompressionLevel = -1;
+
+ //
+ // Transport buffers' allocation parameters.
+ //
+
+ TransportXBufferSize = TRANSPORT_X_BUFFER_SIZE;
+ TransportProxyBufferSize = TRANSPORT_PROXY_BUFFER_SIZE;
+ TransportGenericBufferSize = TRANSPORT_GENERIC_BUFFER_SIZE;
+
+ TransportXBufferThreshold = TRANSPORT_X_BUFFER_THRESHOLD;
+ TransportProxyBufferThreshold = TRANSPORT_PROXY_BUFFER_THRESHOLD;
+ TransportGenericBufferThreshold = TRANSPORT_GENERIC_BUFFER_THRESHOLD;
+
+ TransportMaximumBufferSize = TRANSPORT_MAXIMUM_BUFFER_SIZE;
+
+ //
+ // Flush the write buffer if it exceeds
+ // this size.
+ //
+
+ TransportFlushBufferSize = TRANSPORT_FLUSH_BUFFER_SIZE;
+
+ //
+ // Socket options.
+ //
+
+ OptionProxyKeepAlive = OPTION_PROXY_KEEP_ALIVE;
+ OptionProxyLowDelay = OPTION_PROXY_LOW_DELAY;
+ OptionProxyClientNoDelay = OPTION_PROXY_CLIENT_NO_DELAY;
+ OptionProxyServerNoDelay = OPTION_PROXY_SERVER_NO_DELAY;
+ OptionClientNoDelay = OPTION_CLIENT_NO_DELAY;
+ OptionServerNoDelay = OPTION_SERVER_NO_DELAY;
+
+ OptionProxyReceiveBuffer = OPTION_PROXY_RECEIVE_BUFFER;
+ OptionClientReceiveBuffer = OPTION_CLIENT_RECEIVE_BUFFER;
+ OptionServerReceiveBuffer = OPTION_SERVER_RECEIVE_BUFFER;
+
+ OptionProxySendBuffer = OPTION_PROXY_SEND_BUFFER;
+ OptionClientSendBuffer = OPTION_CLIENT_SEND_BUFFER;
+ OptionServerSendBuffer = OPTION_SERVER_SEND_BUFFER;
+
+ OptionProxyRetryAccept = OPTION_PROXY_RETRY_ACCEPT;
+ OptionProxyRetryConnect = OPTION_PROXY_RETRY_CONNECT;
+ OptionServerRetryConnect = OPTION_SERVER_RETRY_CONNECT;
+
+ //
+ // Base NX directories.
+ //
+
+ HomePath = NULL;
+ RootPath = NULL;
+ SystemPath = NULL;
+ TempPath = NULL;
+ ClientPath = NULL;
+
+ //
+ // Set defaults for handling persistent cache.
+ //
+
+ PersistentCachePath = NULL;
+ PersistentCacheName = NULL;
+
+ PersistentCacheThreshold = PERSISTENT_CACHE_THRESHOLD;
+
+ PersistentCacheEnableLoad = PERSISTENT_CACHE_ENABLE_LOAD;
+ PersistentCacheEnableSave = PERSISTENT_CACHE_ENABLE_SAVE;
+
+ PersistentCacheCheckOnShutdown = PERSISTENT_CACHE_CHECK_ON_SHUTDOWN;
+
+ PersistentCacheLoadPacked = PERSISTENT_CACHE_LOAD_PACKED;
+ PersistentCacheLoadRender = PERSISTENT_CACHE_LOAD_RENDER;
+
+ PersistentCacheDiskLimit = PERSISTENT_CACHE_DISK_LIMIT;
+
+ //
+ // Set defaults for image cache.
+ //
+
+ ImageCachePath = NULL;
+
+ ImageCacheEnableLoad = IMAGE_CACHE_ENABLE_LOAD;
+ ImageCacheEnableSave = IMAGE_CACHE_ENABLE_SAVE;
+
+ ImageCacheDiskLimit = IMAGE_CACHE_DISK_LIMIT;
+
+ //
+ // Set defaults for the read buffers.
+ //
+
+ ClientInitialReadSize = CLIENT_INITIAL_READ_SIZE;
+ ClientMaximumBufferSize = CLIENT_MAXIMUM_BUFFER_SIZE;
+
+ ServerInitialReadSize = SERVER_INITIAL_READ_SIZE;
+ ServerMaximumBufferSize = SERVER_MAXIMUM_BUFFER_SIZE;
+
+ ProxyInitialReadSize = PROXY_INITIAL_READ_SIZE;
+ ProxyMaximumBufferSize = PROXY_MAXIMUM_BUFFER_SIZE;
+
+ GenericInitialReadSize = GENERIC_INITIAL_READ_SIZE;
+ GenericMaximumBufferSize = GENERIC_MAXIMUM_BUFFER_SIZE;
+
+ ShortBitrateTimeFrame = SHORT_BITRATE_TIME_FRAME;
+ LongBitrateTimeFrame = LONG_BITRATE_TIME_FRAME;
+
+ //
+ // Bandwidth control.
+ //
+
+ LocalBitrateLimit = -1;
+
+ ClientBitrateLimit = CLIENT_BITRATE_LIMIT;
+ ServerBitrateLimit = SERVER_BITRATE_LIMIT;
+
+ //
+ // Default parameters for message handling.
+ //
+
+ ClientTotalStorageSize = CLIENT_TOTAL_STORAGE_SIZE;
+ ServerTotalStorageSize = SERVER_TOTAL_STORAGE_SIZE;
+
+ LocalTotalStorageSize = -1;
+ RemoteTotalStorageSize = -1;
+
+ StoreTimeLimit = STORE_TIME_LIMIT;
+
+ StoreHitsLoadBonus = STORE_HITS_LOAD_BONUS;
+ StoreHitsAddBonus = STORE_HITS_ADD_BONUS;
+ StoreHitsLimit = STORE_HITS_LIMIT;
+
+ StoreHitsTouch = STORE_HITS_TOUCH;
+ StoreHitsUntouch = STORE_HITS_UNTOUCH;
+
+ MinimumMessageSize = MINIMUM_MESSAGE_SIZE;
+ MaximumMessageSize = MAXIMUM_MESSAGE_SIZE;
+ MaximumRequestSize = MAXIMUM_REQUEST_SIZE;
+
+ SplitMode = SPLIT_MODE;
+ SplitTimeout = SPLIT_TIMEOUT;
+ SplitTotalSize = SPLIT_TOTAL_SIZE;
+ SplitTotalStorageSize = SPLIT_TOTAL_STORAGE_SIZE;
+ SplitDataThreshold = SPLIT_DATA_THRESHOLD;
+ SplitDataPacketLimit = SPLIT_DATA_PACKET_LIMIT;
+
+ PackMethod = PACK_METHOD;
+ PackQuality = PACK_QUALITY;
+ HideRender = HIDE_RENDER;
+ TaintReplies = TAINT_REPLIES;
+ TaintThreshold = TAINT_THRESHOLD;
+
+ ShmemClient = SHMEM_CLIENT;
+ ShmemServer = SHMEM_SERVER;
+
+ ShmemClientSize = SHMEM_CLIENT_SIZE;
+ ShmemServerSize = SHMEM_SERVER_SIZE;
+
+ //
+ // Get local version number from compile time
+ // settings. Version of remote proxy will be
+ // checked at connection time.
+ //
+
+ RemoteVersionMajor = -1;
+ RemoteVersionMinor = -1;
+ RemoteVersionPatch = -1;
+
+ CompatVersionMajor = -1;
+ CompatVersionMinor = -1;
+ CompatVersionPatch = -1;
+
+ char version[32];
+
+ strcpy(version, VERSION);
+
+ char *value;
+
+ value = strtok(version, ".");
+
+ for (int i = 0; value != NULL && i < 3; i++)
+ {
+ switch (i)
+ {
+ case 0:
+
+ LocalVersionMajor = atoi(value);
+
+ break;
+
+ case 1:
+
+ LocalVersionMinor = atoi(value);
+
+ break;
+
+ case 2:
+
+ LocalVersionPatch = atoi(value);
+
+ break;
+ }
+
+ value = strtok(NULL, ".");
+ }
+
+ #ifdef TEST
+ *logofs << "Control: Major version is " << LocalVersionMajor
+ << " minor is " << LocalVersionMinor << " patch is "
+ << LocalVersionPatch << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Initialize local implemented methods later
+ // and negotiate remote methods at connection
+ // time.
+ //
+
+ LocalUnpackMethods = NULL;
+ RemoteUnpackMethods = NULL;
+
+ //
+ // Set to 1 those methods which are implemented.
+ //
+
+ setLocalUnpackMethods();
+
+ //
+ // Set the protocol version at the
+ // time the session is negotiated.
+ //
+
+ protoStep6_ = 0;
+ protoStep7_ = 0;
+ protoStep8_ = 0;
+ protoStep9_ = 0;
+ protoStep10_ = 0;
+}
+
+Control::~Control()
+{
+ if (KillDaemonOnShutdown != NULL)
+ {
+ delete [] KillDaemonOnShutdown;
+ }
+
+ if (HomePath != NULL)
+ {
+ delete [] HomePath;
+ }
+
+ if (RootPath != NULL)
+ {
+ delete [] RootPath;
+ }
+
+ if (SystemPath != NULL)
+ {
+ delete [] SystemPath;
+ }
+
+ if (TempPath != NULL)
+ {
+ delete [] TempPath;
+ }
+
+ if (ClientPath != NULL)
+ {
+ delete [] ClientPath;
+ }
+
+ if (PersistentCachePath != NULL)
+ {
+ delete [] PersistentCachePath;
+ }
+
+ if (PersistentCacheName != NULL)
+ {
+ delete [] PersistentCacheName;
+ }
+
+ if (LocalUnpackMethods != NULL)
+ {
+ delete [] LocalUnpackMethods;
+ }
+
+ if (RemoteUnpackMethods != NULL)
+ {
+ delete [] RemoteUnpackMethods;
+ }
+
+ if (ImageCachePath != NULL)
+ {
+ delete [] ImageCachePath;
+ }
+}
+
+//
+// Set the protocol step based on the
+// remote version.
+//
+
+void Control::setProtoStep(int step)
+{
+ switch (step)
+ {
+ case 6:
+ {
+ protoStep6_ = 1;
+ protoStep7_ = 0;
+ protoStep8_ = 0;
+ protoStep9_ = 0;
+ protoStep10_ = 0;
+
+ break;
+ }
+ case 7:
+ {
+ protoStep6_ = 1;
+ protoStep7_ = 1;
+ protoStep8_ = 0;
+ protoStep9_ = 0;
+ protoStep10_ = 0;
+
+ break;
+ }
+ case 8:
+ {
+ protoStep6_ = 1;
+ protoStep7_ = 1;
+ protoStep8_ = 1;
+ protoStep9_ = 0;
+ protoStep10_ = 0;
+
+ break;
+ }
+ case 9:
+ {
+ protoStep6_ = 1;
+ protoStep7_ = 1;
+ protoStep8_ = 1;
+ protoStep9_ = 1;
+ protoStep10_ = 0;
+
+ break;
+ }
+ case 10:
+ {
+ protoStep6_ = 1;
+ protoStep7_ = 1;
+ protoStep8_ = 1;
+ protoStep9_ = 1;
+ protoStep10_ = 1;
+
+ break;
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "Control: PANIC! Invalid protocol step "
+ << "with value " << step << ".\n"
+ << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+ }
+}
+
+int Control::getProtoStep()
+{
+ if (protoStep10_ == 1)
+ {
+ return 10;
+ }
+ else if (protoStep9_ == 1)
+ {
+ return 9;
+ }
+ else if (protoStep8_ == 1)
+ {
+ return 8;
+ }
+ else if (protoStep7_ == 1)
+ {
+ return 7;
+ }
+ else if (protoStep6_ == 1)
+ {
+ return 6;
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Control: PANIC! Can't identify the "
+ << "protocol step.\n" << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+}
+
+//
+// Set here the pack/unpack methods that are
+// implemented by this NX proxy.
+//
+
+void Control::setLocalUnpackMethods()
+{
+ LocalUnpackMethods = new unsigned char[PACK_METHOD_LIMIT];
+ RemoteUnpackMethods = new unsigned char[PACK_METHOD_LIMIT];
+
+ for (int i = 0; i < PACK_METHOD_LIMIT; i++)
+ {
+ LocalUnpackMethods[i] = 0;
+ RemoteUnpackMethods[i] = 0;
+ }
+
+ LocalUnpackMethods[NO_PACK] = 1;
+
+ LocalUnpackMethods[PACK_MASKED_8_COLORS] = 1;
+ LocalUnpackMethods[PACK_MASKED_64_COLORS] = 1;
+ LocalUnpackMethods[PACK_MASKED_256_COLORS] = 1;
+ LocalUnpackMethods[PACK_MASKED_512_COLORS] = 1;
+ LocalUnpackMethods[PACK_MASKED_4K_COLORS] = 1;
+ LocalUnpackMethods[PACK_MASKED_32K_COLORS] = 1;
+ LocalUnpackMethods[PACK_MASKED_64K_COLORS] = 1;
+ LocalUnpackMethods[PACK_MASKED_256K_COLORS] = 1;
+ LocalUnpackMethods[PACK_MASKED_2M_COLORS] = 1;
+ LocalUnpackMethods[PACK_MASKED_16M_COLORS] = 1;
+
+ LocalUnpackMethods[PACK_RAW_8_BITS] = 1;
+ LocalUnpackMethods[PACK_RAW_16_BITS] = 1;
+ LocalUnpackMethods[PACK_RAW_24_BITS] = 1;
+
+ LocalUnpackMethods[PACK_COLORMAP_256_COLORS] = 1;
+
+ LocalUnpackMethods[PACK_JPEG_8_COLORS] = 1;
+ LocalUnpackMethods[PACK_JPEG_64_COLORS] = 1;
+ LocalUnpackMethods[PACK_JPEG_256_COLORS] = 1;
+ LocalUnpackMethods[PACK_JPEG_512_COLORS] = 1;
+ LocalUnpackMethods[PACK_JPEG_4K_COLORS] = 1;
+ LocalUnpackMethods[PACK_JPEG_32K_COLORS] = 1;
+ LocalUnpackMethods[PACK_JPEG_64K_COLORS] = 1;
+ LocalUnpackMethods[PACK_JPEG_256K_COLORS] = 1;
+ LocalUnpackMethods[PACK_JPEG_2M_COLORS] = 1;
+ LocalUnpackMethods[PACK_JPEG_16M_COLORS] = 1;
+
+ LocalUnpackMethods[PACK_PNG_8_COLORS] = 1;
+ LocalUnpackMethods[PACK_PNG_64_COLORS] = 1;
+ LocalUnpackMethods[PACK_PNG_256_COLORS] = 1;
+ LocalUnpackMethods[PACK_PNG_512_COLORS] = 1;
+ LocalUnpackMethods[PACK_PNG_4K_COLORS] = 1;
+ LocalUnpackMethods[PACK_PNG_32K_COLORS] = 1;
+ LocalUnpackMethods[PACK_PNG_64K_COLORS] = 1;
+ LocalUnpackMethods[PACK_PNG_256K_COLORS] = 1;
+ LocalUnpackMethods[PACK_PNG_2M_COLORS] = 1;
+ LocalUnpackMethods[PACK_PNG_16M_COLORS] = 1;
+
+ LocalUnpackMethods[PACK_RGB_16M_COLORS] = 1;
+ LocalUnpackMethods[PACK_RLE_16M_COLORS] = 1;
+
+ LocalUnpackMethods[PACK_ALPHA] = 1;
+ LocalUnpackMethods[PACK_COLORMAP] = 1;
+
+ LocalUnpackMethods[PACK_BITMAP_16M_COLORS] = 1;
+}
diff --git a/nxcomp/Control.h b/nxcomp/Control.h
new file mode 100644
index 000000000..c21477544
--- /dev/null
+++ b/nxcomp/Control.h
@@ -0,0 +1,747 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef Control_H
+#define Control_H
+
+#include "NXpack.h"
+
+#include "Misc.h"
+#include "Types.h"
+#include "Timestamp.h"
+#include "Statistics.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// This is the mode proxy is running.
+//
+
+typedef enum
+{
+ proxy_undefined = -1,
+ proxy_client,
+ proxy_server,
+ proxy_last_tag
+}
+T_proxy_mode;
+
+//
+// Handle advances in the connection
+// procedure.
+//
+
+typedef enum
+{
+ stage_undefined,
+ stage_initializing,
+ stage_connecting,
+ stage_connected,
+ stage_waiting_forwarder_version,
+ stage_waiting_forwarder_options,
+ stage_sending_forwarder_options,
+ stage_waiting_proxy_version,
+ stage_waiting_proxy_options,
+ stage_sending_proxy_options,
+ stage_waiting_proxy_caches,
+ stage_sending_proxy_caches,
+ stage_operational,
+ stage_terminating,
+ stage_terminated
+}
+T_proxy_stage;
+
+//
+// Hint about whether or not the proxy is
+// connected to a NX agen.
+//
+
+typedef enum
+{
+ session_undefined = -1,
+ session_agent,
+ session_shadow,
+ session_proxy,
+ session_last_tag
+}
+T_session_mode;
+
+//
+// Set how data will be written to the peer
+// socket.
+//
+
+typedef enum
+{
+ policy_undefined = -1,
+ policy_immediate,
+ policy_deferred
+}
+T_flush_policy;
+
+//
+// Link mode, after negotiation, will be set to
+// any of the values defined in the NXproto.h.
+//
+
+#define link_undefined -1;
+
+//
+// This class collects functioning parameters,
+// to be configurable at run-time. They are for
+// the most part regarding timeouts, transport
+// and message stores handling.
+//
+
+class Control
+{
+ public:
+
+ //
+ // Does proxy run in client mode or server mode?
+ // As soon as we'll have gone through parsing of
+ // the command line options the current mode will
+ // be propagated to the control class.
+ //
+
+ T_proxy_mode ProxyMode;
+
+ //
+ // Goes from initializing to operational.
+ //
+
+ T_proxy_stage ProxyStage;
+
+ //
+ // Hint about type of session currently running.
+ //
+
+ T_session_mode SessionMode;
+
+ //
+ // Either immediate or defferred flushes.
+ //
+
+ T_flush_policy FlushPolicy;
+
+ //
+ // If set, the channels will try to flush the
+ // encoded data whenever there is a prioritized
+ // message. Depending on the flush policy, this
+ // may determine an immediate flush or an event
+ // being generated telling to the agent that it
+ // should flush the proxy link.
+ //
+
+ int FlushPriority;
+
+ //
+ // Id corresponding to link speed negotiated
+ // between proxies.
+ //
+
+ int LinkMode;
+
+ //
+ // Set if the proxy is connected to a program
+ // providing the encryption of the point to
+ // point communication.
+ //
+
+ int LinkEncrypted;
+
+ //
+ // Maximum number of bytes sent for each token.
+ //
+
+ int TokenSize;
+
+ //
+ // Maximum number of tokens that can be spent
+ // by the client proxy before having to block
+ // waiting for a reply.
+ //
+
+ int TokenLimit;
+
+ //
+ // Bitmask used to determine the distribution
+ // of channel ids between the client and server
+ // proxies.
+ //
+
+ int ChannelMask;
+
+ //
+ // Kill session if control parameters cannot
+ // be negotiated before this timeout.
+ //
+
+ int InitTimeout;
+
+ //
+ // Enter the congestion state if the remote does
+ // not reply to the ping within the given amount
+ // of time.
+ //
+
+ int PingTimeout;
+
+ //
+ // Enqueue motion notify events in server channel.
+ //
+
+ int MotionTimeout;
+
+ //
+ // Force an update of the congestion counter if
+ // the proxy is idle for this time.
+ //
+
+ int IdleTimeout;
+
+ //
+ // Close the connection if can't write before
+ // this timeout.
+ //
+
+ int ChannelTimeout;
+
+ //
+ // Close connection if can't write before
+ // this timeout.
+ //
+
+ int ProxyTimeout;
+
+ //
+ // How many milliseconds to wait for the shared
+ // memory completion event to become available.
+ //
+
+ int ShmemTimeout;
+
+ //
+ // Wait for applications to complete at the time
+ // proxy is shut down.
+ //
+
+ int CleanupTimeout;
+
+ //
+ // Wait this amount of milliseconds before any
+ // iteration of the house-keeping process.
+ //
+
+ int KeeperTimeout;
+
+ //
+ // Adjust timeout calculations.
+ //
+
+ int LatencyTimeout;
+
+ //
+ // Maximum allowed size of log files.
+ //
+
+ int FileSizeLimit;
+ int FileSizeCheckTimeout;
+
+ //
+ // What do we do at the end of session? If
+ // this flag is set we launch a new client
+ // letting the user run a new NX session.
+ //
+
+ int EnableRestartOnShutdown;
+
+ //
+ // The client can request the proxy to kill
+ // a number of processes before exiting.
+ //
+
+ int *KillDaemonOnShutdown;
+ int KillDaemonOnShutdownNumber;
+ int KillDaemonOnShutdownLimit;
+
+ //
+ // Do we generate a core dump and exit in
+ // case of program errors?
+ //
+
+ int EnableCoreDumpOnAbort;
+
+ //
+ // Is statistic output enabled?
+ //
+
+ int EnableStatistics;
+
+ //
+ // Version number of local and remote proxy.
+ //
+
+ int LocalVersionMajor;
+ int LocalVersionMinor;
+ int LocalVersionPatch;
+
+ int RemoteVersionMajor;
+ int RemoteVersionMinor;
+ int RemoteVersionPatch;
+
+ int CompatVersionMajor;
+ int CompatVersionMinor;
+ int CompatVersionPatch;
+
+ //
+ // Which unpack methods are implemented in proxy?
+ //
+
+ unsigned char *LocalUnpackMethods;
+ unsigned char *RemoteUnpackMethods;
+
+ //
+ // Memory restriction imposed by user.
+ //
+
+ int LocalMemoryLevel;
+
+ //
+ // Use or not differential compression
+ // and caching of X protocol messages.
+ //
+
+ int LocalDeltaCompression;
+ int RemoteDeltaCompression;
+
+ //
+ // Compression of images and replies.
+ //
+
+ int LocalDataCompression;
+ int LocalDataCompressionLevel;
+
+ int RemoteDataCompression;
+ int RemoteDataCompressionLevel;
+
+ //
+ // Minimum packet size to be compressed.
+ //
+
+ int LocalDataCompressionThreshold;
+
+ //
+ // Compress or not data flowing through the proxy
+ // link. Level should be one of the ZLIB level as
+ // Z_DEFAULT_COMPRESSION or Z_BEST_COMPRESSION.
+ //
+
+ int LocalStreamCompression;
+ int LocalStreamCompressionLevel;
+
+ int RemoteStreamCompression;
+ int RemoteStreamCompressionLevel;
+
+ //
+ // Size of read operations in read buffer classes.
+ //
+
+ int ClientInitialReadSize;
+ int ClientMaximumBufferSize;
+
+ int ServerInitialReadSize;
+ int ServerMaximumBufferSize;
+
+ int ProxyInitialReadSize;
+ int ProxyMaximumBufferSize;
+
+ int GenericInitialReadSize;
+ int GenericMaximumBufferSize;
+
+ //
+ // Set initial size and resize policy of
+ // transport buffers. If maximum size is
+ // exceeded, print a warning.
+ //
+
+ int TransportXBufferSize;
+ int TransportProxyBufferSize;
+ int TransportGenericBufferSize;
+
+ int TransportXBufferThreshold;
+ int TransportProxyBufferThreshold;
+ int TransportGenericBufferThreshold;
+
+ int TransportMaximumBufferSize;
+
+ //
+ // Flush the data produced for the channel
+ // connection if it exceeds this size.
+ //
+
+ int TransportFlushBufferSize;
+
+ //
+ // Socket options.
+ //
+
+ int OptionProxyKeepAlive;
+ int OptionProxyLowDelay;
+ int OptionProxyClientNoDelay;
+ int OptionProxyServerNoDelay;
+ int OptionClientNoDelay;
+ int OptionServerNoDelay;
+
+ int OptionProxyReceiveBuffer;
+ int OptionClientReceiveBuffer;
+ int OptionServerReceiveBuffer;
+
+ int OptionProxySendBuffer;
+ int OptionClientSendBuffer;
+ int OptionServerSendBuffer;
+
+ int OptionProxyRetryAccept;
+ int OptionProxyRetryConnect;
+ int OptionServerRetryConnect;
+
+ //
+ // Calculate current bitrate on proxy link
+ // using these observation periods. Value
+ // is in milliseconds.
+ //
+
+ int ShortBitrateTimeFrame;
+ int LongBitrateTimeFrame;
+
+ //
+ // Limit the bandwidth usage of the proxy
+ // link.
+ //
+
+ int LocalBitrateLimit;
+
+ int ClientBitrateLimit;
+ int ServerBitrateLimit;
+
+ //
+ // This is the limit imposed by user on
+ // total cache size.
+ //
+
+ int ClientTotalStorageSize;
+ int ServerTotalStorageSize;
+
+ int LocalTotalStorageSize;
+ int RemoteTotalStorageSize;
+
+ //
+ // Discard messages in store older than
+ // this amount of seconds.
+ //
+
+ int StoreTimeLimit;
+
+ //
+ // Any new message in store starts with
+ // this amount of hits.
+ //
+
+ int StoreHitsAddBonus;
+
+ //
+ // Unless it is loaded from persistent
+ // cache.
+ //
+
+ int StoreHitsLoadBonus;
+
+ //
+ // Stop increasing hits at this threshold.
+ //
+
+ int StoreHitsLimit;
+
+ //
+ // Give a special weight to messages put or
+ // taken from cache during startup time.
+ //
+
+ int StoreHitsStartup;
+
+ //
+ // Weight of touch and untoch operations.
+ //
+
+ int StoreHitsTouch;
+ int StoreHitsUntouch;
+
+ //
+ // Directives on size of messages to cache.
+ //
+
+ int MinimumMessageSize;
+ int MaximumMessageSize;
+
+ //
+ // Maximum size of a single X request.
+ //
+
+ int MaximumRequestSize;
+
+ //
+ // Currently selected streaming mode.
+ //
+
+ int SplitMode;
+
+ //
+ // Send new split data any given amount of
+ // milliseconds.
+ //
+
+ int SplitTimeout;
+
+ //
+ // Maximum number of distinct messages and
+ // maximum size in bytes of the temporary
+ // storage.
+ //
+
+ int SplitTotalSize;
+ int SplitTotalStorageSize;
+
+ //
+ // Don't split messages smaller that this
+ // threshold and send no more than the
+ // given amount of bytes in a single data
+ // shot when streaming the split messages.
+ //
+
+ int SplitDataThreshold;
+ int SplitDataPacketLimit;
+
+ //
+ // Agent related parameters. These values apply
+ // to the agent which, at startup, must query
+ // the user's settings.
+ //
+
+ int PackMethod;
+ int PackQuality;
+ int HideRender;
+ int TaintReplies;
+ int TaintThreshold;
+
+ //
+ // Do we allow shared memory image support in
+ // client and or server?
+ //
+
+ int ShmemClient;
+ int ShmemServer;
+
+ //
+ // Default size of shared memory segments used
+ // in MIT-SHM support.
+ //
+
+ int ShmemClientSize;
+ int ShmemServerSize;
+
+ //
+ // The user's home directory.
+ //
+
+ char *HomePath;
+
+ //
+ // The ".nx" directory, usually in
+ // the user's home.
+ //
+
+ char *RootPath;
+
+ //
+ // Usually the /usr/NX" directory.
+ //
+
+ char *SystemPath;
+
+ //
+ // Usually the "/tmp" directory.
+ //
+
+ char *TempPath;
+
+ //
+ // The complete path to the client.
+ //
+
+ char *ClientPath;
+
+ //
+ // String containing path of cache
+ // file selected for load or save.
+ //
+
+ char *PersistentCachePath;
+
+ //
+ // Name of selected cache file.
+ //
+
+ char *PersistentCacheName;
+
+ //
+ // Minimum size of cache in memory
+ // to proceed to its storage on disk.
+ //
+
+ int PersistentCacheThreshold;
+
+ //
+ // Is persistent cache enabled?
+ //
+
+ int PersistentCacheEnableLoad;
+ int PersistentCacheEnableSave;
+
+ //
+ // This is used just for test because
+ // it requires that client and server
+ // reside on the same machine.
+ //
+
+ int PersistentCacheCheckOnShutdown;
+
+ //
+ // Load packed image and render extension
+ // message stores. This currently depends
+ // on the type of session.
+ //
+
+ int PersistentCacheLoadPacked;
+ int PersistentCacheLoadRender;
+
+ //
+ // Maximum disk consumption of message
+ // caches on disk.
+ //
+
+ int PersistentCacheDiskLimit;
+
+ //
+ // String containing the base path
+ // of image cache files.
+ //
+
+ char *ImageCachePath;
+
+ //
+ // Is image cache enabled?
+ //
+
+ int ImageCacheEnableLoad;
+ int ImageCacheEnableSave;
+
+ //
+ // Maximum disk consumption of image
+ // caches on disk.
+ //
+
+ int ImageCacheDiskLimit;
+
+ //
+ // Only constructor, destructor
+ // and a few utility functions.
+ //
+
+ Control();
+
+ ~Control();
+
+ //
+ // Should not leverage control to find channel
+ // stores' size limits. As most of values in
+ // control, this info must be moved elsewhere.
+ //
+
+ int getUpperStorageSize() const
+ {
+ return (ClientTotalStorageSize >
+ ServerTotalStorageSize ?
+ ClientTotalStorageSize :
+ ServerTotalStorageSize);
+ }
+
+ int getLowerStorageSize() const
+ {
+ return (ClientTotalStorageSize <
+ ServerTotalStorageSize ?
+ ClientTotalStorageSize :
+ ServerTotalStorageSize);
+ }
+
+ void setProtoStep(int step);
+
+ int getProtoStep();
+
+ int isProtoStep7()
+ {
+ return protoStep7_;
+ }
+
+ int isProtoStep8()
+ {
+ return protoStep8_;
+ }
+
+ int isProtoStep9()
+ {
+ return protoStep9_;
+ }
+
+ int isProtoStep10()
+ {
+ return protoStep10_;
+ }
+
+ private:
+
+ //
+ // Look in Control.cpp.
+ //
+
+ void setLocalUnpackMethods();
+
+ //
+ // Manage the encoding according
+ // to the protocol version.
+ //
+
+ int protoStep6_;
+ int protoStep7_;
+ int protoStep8_;
+ int protoStep9_;
+ int protoStep10_;
+};
+
+#endif /* Control_H */
diff --git a/nxcomp/CopyArea.cpp b/nxcomp/CopyArea.cpp
new file mode 100644
index 000000000..e384ce13c
--- /dev/null
+++ b/nxcomp/CopyArea.cpp
@@ -0,0 +1,187 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "CopyArea.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+int CopyAreaStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ CopyAreaMessage *copyArea = (CopyAreaMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ copyArea -> src_drawable = GetULONG(buffer + 4, bigEndian);
+ copyArea -> dst_drawable = GetULONG(buffer + 8, bigEndian);
+ copyArea -> gcontext = GetULONG(buffer + 12, bigEndian);
+
+ copyArea -> src_x = GetUINT(buffer + 16, bigEndian);
+ copyArea -> src_y = GetUINT(buffer + 18, bigEndian);
+ copyArea -> dst_x = GetUINT(buffer + 20, bigEndian);
+ copyArea -> dst_y = GetUINT(buffer + 22, bigEndian);
+
+ copyArea -> width = GetUINT(buffer + 24, bigEndian);
+ copyArea -> height = GetUINT(buffer + 26, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int CopyAreaStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ CopyAreaMessage *copyArea = (CopyAreaMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ PutULONG(copyArea -> src_drawable, buffer + 4, bigEndian);
+ PutULONG(copyArea -> dst_drawable, buffer + 8, bigEndian);
+ PutULONG(copyArea -> gcontext, buffer + 12, bigEndian);
+
+ PutUINT(copyArea -> src_x, buffer + 16, bigEndian);
+ PutUINT(copyArea -> src_y, buffer + 18, bigEndian);
+ PutUINT(copyArea -> dst_x, buffer + 20, bigEndian);
+ PutUINT(copyArea -> dst_y, buffer + 22, bigEndian);
+
+ PutUINT(copyArea -> width, buffer + 24, bigEndian);
+ PutUINT(copyArea -> height, buffer + 26, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void CopyAreaStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ CopyAreaMessage *copyArea = (CopyAreaMessage *) message;
+
+ *logofs << name() << ": Identity src_drawable " << copyArea -> src_drawable
+ << ", dst_drawable " << copyArea -> dst_drawable << ", gcontext " << copyArea -> gcontext
+ << ", src_x " << copyArea -> src_x << ", src_y " << copyArea -> src_y
+ << ", dst_x " << copyArea -> dst_x << ", dst_y " << copyArea -> dst_y
+ << ", width " << copyArea -> width << ", height " << copyArea -> height
+ << ", size " << copyArea -> size_ << ".\n";
+
+ #endif
+}
+
+void CopyAreaStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ md5_append(md5_state_, buffer + 16, 12);
+}
+
+void CopyAreaStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ CopyAreaMessage *copyArea = (CopyAreaMessage *) message;
+ CopyAreaMessage *cachedCopyArea = (CopyAreaMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << copyArea -> src_drawable
+ << " as " << "src_drawable" << " field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(copyArea -> src_drawable, clientCache -> drawableCache);
+
+ cachedCopyArea -> src_drawable = copyArea -> src_drawable;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << copyArea -> dst_drawable
+ << " as " << "dst_drawable" << " field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(copyArea -> dst_drawable, clientCache -> drawableCache);
+
+ cachedCopyArea -> dst_drawable = copyArea -> dst_drawable;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << copyArea -> gcontext
+ << " as " << "gcontext" << " field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(copyArea -> gcontext, clientCache -> gcCache);
+
+ cachedCopyArea -> gcontext = copyArea -> gcontext;
+}
+
+void CopyAreaStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ CopyAreaMessage *copyArea = (CopyAreaMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ copyArea -> src_drawable = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << copyArea -> src_drawable
+ << " as " << "src_drawable" << " field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ copyArea -> dst_drawable = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << copyArea -> dst_drawable
+ << " as " << "dst_drawable" << " field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ copyArea -> gcontext = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << copyArea -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+}
+
+
diff --git a/nxcomp/CopyArea.h b/nxcomp/CopyArea.h
new file mode 100644
index 000000000..a811f3801
--- /dev/null
+++ b/nxcomp/CopyArea.h
@@ -0,0 +1,184 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef CopyArea_H
+#define CopyArea_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define COPYAREA_ENABLE_CACHE 1
+#define COPYAREA_ENABLE_DATA 0
+#define COPYAREA_ENABLE_SPLIT 0
+#define COPYAREA_ENABLE_COMPRESS 0
+
+#define COPYAREA_DATA_LIMIT 0
+#define COPYAREA_DATA_OFFSET 28
+
+#define COPYAREA_CACHE_SLOTS 3000
+#define COPYAREA_CACHE_THRESHOLD 5
+#define COPYAREA_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class CopyAreaMessage : public Message
+{
+ friend class CopyAreaStore;
+
+ public:
+
+ CopyAreaMessage()
+ {
+ }
+
+ ~CopyAreaMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned int src_drawable;
+ unsigned int dst_drawable;
+ unsigned int gcontext;
+ unsigned short src_x;
+ unsigned short src_y;
+ unsigned short dst_x;
+ unsigned short dst_y;
+ unsigned short width;
+ unsigned short height;
+};
+
+class CopyAreaStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ CopyAreaStore() : MessageStore()
+ {
+ enableCache = COPYAREA_ENABLE_CACHE;
+ enableData = COPYAREA_ENABLE_DATA;
+ enableSplit = COPYAREA_ENABLE_SPLIT;
+ enableCompress = COPYAREA_ENABLE_COMPRESS;
+
+ dataLimit = COPYAREA_DATA_LIMIT;
+ dataOffset = COPYAREA_DATA_OFFSET;
+
+ cacheSlots = COPYAREA_CACHE_SLOTS;
+ cacheThreshold = COPYAREA_CACHE_THRESHOLD;
+ cacheLowerThreshold = COPYAREA_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~CopyAreaStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "CopyArea";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_CopyArea;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(CopyAreaMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new CopyAreaMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new CopyAreaMessage((const CopyAreaMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (CopyAreaMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* CopyArea_H */
diff --git a/nxcomp/CreateGC.cpp b/nxcomp/CreateGC.cpp
new file mode 100644
index 000000000..f1c10e69b
--- /dev/null
+++ b/nxcomp/CreateGC.cpp
@@ -0,0 +1,226 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "CreateGC.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int CreateGCStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ CreateGCMessage *createGC = (CreateGCMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ createGC -> gcontext = GetULONG(buffer + 4, bigEndian);
+ createGC -> drawable = GetULONG(buffer + 8, bigEndian);
+ createGC -> value_mask = GetULONG(buffer + 12, bigEndian);
+
+ //
+ // Clear the unused bytes carried in the
+ // payload to increase the effectiveness
+ // of the caching algorithm.
+ //
+
+ if ((int) size > dataOffset)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Removing unused bytes from the "
+ << "data payload.\n" << logofs_flush;
+ #endif
+
+ createGC -> value_mask &= (1 << 23) - 1;
+
+ unsigned int mask = 0x1;
+ unsigned char *source = (unsigned char *) buffer + CREATEGC_DATA_OFFSET;
+ unsigned long value = 0;
+
+ for (unsigned int i = 0; i < 23; i++)
+ {
+ if (createGC -> value_mask & mask)
+ {
+ value = GetULONG(source, bigEndian);
+
+ value &= (0xffffffff >> (32 - CREATEGC_FIELD_WIDTH[i]));
+
+ PutULONG(value, source, bigEndian);
+
+ source += 4;
+ }
+
+ mask <<= 1;
+ }
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int CreateGCStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ CreateGCMessage *createGC = (CreateGCMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ PutULONG(createGC -> gcontext, buffer + 4, bigEndian);
+ PutULONG(createGC -> drawable, buffer + 8, bigEndian);
+ PutULONG(createGC -> value_mask, buffer + 12, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void CreateGCStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ CreateGCMessage *createGC = (CreateGCMessage *) message;
+
+ *logofs << name() << ": Identity gcontext " << createGC -> gcontext << ", drawable "
+ << createGC -> drawable << ", value_mask " << createGC -> value_mask
+ << ", size " << createGC -> size_ << ".\n" << logofs_flush;
+ #endif
+}
+
+void CreateGCStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ //
+ // This didn't include the drawable
+ // in previous versions.
+ //
+
+ md5_append(md5_state_, buffer + 8, 8);
+}
+
+void CreateGCStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ CreateGCMessage *createGC = (CreateGCMessage *) message;
+ CreateGCMessage *cachedCreateGC = (CreateGCMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << createGC -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeNewXidValue(createGC -> gcontext, clientCache -> lastId,
+ clientCache -> lastIdCache, clientCache -> gcCache,
+ clientCache -> freeGCCache);
+
+ cachedCreateGC -> gcontext = createGC -> gcontext;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << createGC -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(createGC -> drawable, clientCache -> drawableCache);
+
+ cachedCreateGC -> drawable = createGC -> drawable;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << createGC -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(createGC -> gcontext, clientCache -> gcCache);
+
+ cachedCreateGC -> gcontext = createGC -> gcontext;
+ }
+}
+
+void CreateGCStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ CreateGCMessage *createGC = (CreateGCMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ decodeBuffer.decodeNewXidValue(value, clientCache -> lastId,
+ clientCache -> lastIdCache, clientCache -> gcCache,
+ clientCache -> freeGCCache);
+
+ createGC -> gcontext = value;
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded value " << createGC -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ createGC -> drawable = value;
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded value " << createGC -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ createGC -> gcontext = value;
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded value " << createGC -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+ }
+}
diff --git a/nxcomp/CreateGC.h b/nxcomp/CreateGC.h
new file mode 100644
index 000000000..b77f13c47
--- /dev/null
+++ b/nxcomp/CreateGC.h
@@ -0,0 +1,178 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef CreateGC_H
+#define CreateGC_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define CREATEGC_ENABLE_CACHE 1
+#define CREATEGC_ENABLE_DATA 0
+#define CREATEGC_ENABLE_SPLIT 0
+#define CREATEGC_ENABLE_COMPRESS 0
+
+#define CREATEGC_DATA_LIMIT 144
+#define CREATEGC_DATA_OFFSET 16
+
+#define CREATEGC_CACHE_SLOTS 2000
+#define CREATEGC_CACHE_THRESHOLD 2
+#define CREATEGC_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class CreateGCMessage : public Message
+{
+ friend class CreateGCStore;
+
+ public:
+
+ CreateGCMessage()
+ {
+ }
+
+ ~CreateGCMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned int gcontext;
+ unsigned int drawable;
+ unsigned int value_mask;
+};
+
+class CreateGCStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ CreateGCStore() : MessageStore()
+ {
+ enableCache = CREATEGC_ENABLE_CACHE;
+ enableData = CREATEGC_ENABLE_DATA;
+ enableSplit = CREATEGC_ENABLE_SPLIT;
+ enableCompress = CREATEGC_ENABLE_COMPRESS;
+
+ dataLimit = CREATEGC_DATA_LIMIT;
+ dataOffset = CREATEGC_DATA_OFFSET;
+
+ cacheSlots = CREATEGC_CACHE_SLOTS;
+ cacheThreshold = CREATEGC_CACHE_THRESHOLD;
+ cacheLowerThreshold = CREATEGC_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~CreateGCStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "CreateGC";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_CreateGC;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(CreateGCMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new CreateGCMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new CreateGCMessage((const CreateGCMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (CreateGCMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* CreateGC_H */
diff --git a/nxcomp/CreatePixmap.cpp b/nxcomp/CreatePixmap.cpp
new file mode 100644
index 000000000..403786747
--- /dev/null
+++ b/nxcomp/CreatePixmap.cpp
@@ -0,0 +1,268 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "CreatePixmap.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "WriteBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Constructors and destructors.
+//
+
+CreatePixmapStore::CreatePixmapStore()
+
+ : MessageStore()
+{
+ enableCache = CREATEPIXMAP_ENABLE_CACHE;
+ enableData = CREATEPIXMAP_ENABLE_DATA;
+ enableSplit = CREATEPIXMAP_ENABLE_SPLIT;
+ enableCompress = CREATEPIXMAP_ENABLE_COMPRESS;
+
+ dataLimit = CREATEPIXMAP_DATA_LIMIT;
+ dataOffset = CREATEPIXMAP_DATA_OFFSET;
+
+ cacheSlots = CREATEPIXMAP_CACHE_SLOTS;
+ cacheThreshold = CREATEPIXMAP_CACHE_THRESHOLD;
+ cacheLowerThreshold = CREATEPIXMAP_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+CreatePixmapStore::~CreatePixmapStore()
+{
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int CreatePixmapStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue(*(buffer + 1), 8,
+ clientCache -> depthCache);
+
+ encodeBuffer.encodeNewXidValue(GetULONG(buffer + 4, bigEndian),
+ clientCache -> lastId, clientCache -> lastIdCache,
+ clientCache -> drawableCache,
+ clientCache -> freeDrawableCache);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian),
+ clientCache -> windowCache);
+
+ encodeBuffer.encodeCachedValue(GetUINT(buffer + 12, bigEndian), 16,
+ clientCache -> createPixmapXCache, 8);
+
+ encodeBuffer.encodeCachedValue(GetUINT(buffer + 14, bigEndian), 16,
+ clientCache -> createPixmapYCache, 8);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int CreatePixmapStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned char cValue;
+ unsigned int value;
+
+ size = 16;
+
+ buffer = writeBuffer -> addMessage(size);
+
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> depthCache);
+
+ *(buffer + 1) = cValue;
+
+ decodeBuffer.decodeNewXidValue(value,
+ clientCache -> lastId, clientCache -> lastIdCache,
+ clientCache -> drawableCache,
+ clientCache -> freeDrawableCache);
+
+ PutULONG(value, buffer + 4, bigEndian);
+
+ decodeBuffer.decodeXidValue(value,
+ clientCache -> windowCache);
+
+ PutULONG(value, buffer + 8, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> createPixmapXCache, 8);
+
+ PutUINT(value, buffer + 12, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> createPixmapYCache, 8);
+
+ PutUINT(value, buffer + 14, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int CreatePixmapStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ CreatePixmapMessage *createPixmap = (CreatePixmapMessage *) message;
+
+ createPixmap -> depth = *(buffer + 1);
+
+ createPixmap -> id = GetULONG(buffer + 4, bigEndian);
+ createPixmap -> drawable = GetULONG(buffer + 8, bigEndian);
+
+ createPixmap -> width = GetUINT(buffer + 12, bigEndian);
+ createPixmap -> height = GetUINT(buffer + 14, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Size is "
+ << createPixmap -> size_ << " identity is "
+ << createPixmap -> i_size_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int CreatePixmapStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ CreatePixmapMessage *createPixmap = (CreatePixmapMessage *) message;
+
+ *(buffer + 1) = createPixmap -> depth;
+
+ PutULONG(createPixmap -> id, buffer + 4, bigEndian);
+ PutULONG(createPixmap -> drawable, buffer + 8, bigEndian);
+
+ PutUINT(createPixmap -> width, buffer + 12, bigEndian);
+ PutUINT(createPixmap -> height, buffer + 14, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Size is "
+ << createPixmap -> size_ << " identity is "
+ << createPixmap -> i_size_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void CreatePixmapStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ #ifdef WARNING
+ *logofs << name() << ": WARNING! Dump of identity not implemented.\n"
+ << logofs_flush;
+ #endif
+
+ #endif
+}
+
+void CreatePixmapStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ md5_append(md5_state_, buffer + 1, 1);
+ md5_append(md5_state_, buffer + 8, 8);
+}
+
+void CreatePixmapStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ CreatePixmapMessage *createPixmap = (CreatePixmapMessage *) message;
+ CreatePixmapMessage *cachedCreatePixmap = (CreatePixmapMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeNewXidValue(createPixmap -> id,
+ clientCache -> lastId, clientCache -> lastIdCache,
+ clientCache -> drawableCache,
+ clientCache -> freeDrawableCache);
+
+ cachedCreatePixmap -> id = createPixmap -> id;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Size is "
+ << createPixmap -> size_ << " identity is "
+ << createPixmap -> i_size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+
+void CreatePixmapStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ CreatePixmapMessage *createPixmap = (CreatePixmapMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeNewXidValue(createPixmap -> id,
+ clientCache -> lastId, clientCache -> lastIdCache,
+ clientCache -> drawableCache,
+ clientCache -> freeDrawableCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Size is "
+ << createPixmap -> size_ << " identity is "
+ << createPixmap -> i_size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
diff --git a/nxcomp/CreatePixmap.h b/nxcomp/CreatePixmap.h
new file mode 100644
index 000000000..1d742e452
--- /dev/null
+++ b/nxcomp/CreatePixmap.h
@@ -0,0 +1,154 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef CreatePixmap_H
+#define CreatePixmap_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define CREATEPIXMAP_ENABLE_CACHE 1
+#define CREATEPIXMAP_ENABLE_DATA 0
+#define CREATEPIXMAP_ENABLE_SPLIT 0
+#define CREATEPIXMAP_ENABLE_COMPRESS 0
+
+#define CREATEPIXMAP_DATA_LIMIT 16
+#define CREATEPIXMAP_DATA_OFFSET 16
+
+#define CREATEPIXMAP_CACHE_SLOTS 1000
+#define CREATEPIXMAP_CACHE_THRESHOLD 2
+#define CREATEPIXMAP_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class CreatePixmapMessage : public Message
+{
+ friend class CreatePixmapStore;
+
+ public:
+
+ CreatePixmapMessage()
+ {
+ }
+
+ ~CreatePixmapMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char depth;
+
+ unsigned int id;
+ unsigned int drawable;
+
+ unsigned short width;
+ unsigned short height;
+};
+
+class CreatePixmapStore : public MessageStore
+{
+ public:
+
+ CreatePixmapStore();
+
+ virtual ~CreatePixmapStore();
+
+ virtual const char *name() const
+ {
+ return "CreatePixmap";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_CreatePixmap;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(CreatePixmapMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ protected:
+
+ virtual Message *create() const
+ {
+ return new CreatePixmapMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new CreatePixmapMessage((const CreatePixmapMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (CreatePixmapMessage *) message;
+ }
+
+ virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const;
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* CreatePixmap_H */
diff --git a/nxcomp/CreatePixmapCompat.cpp b/nxcomp/CreatePixmapCompat.cpp
new file mode 100644
index 000000000..6ea346ee1
--- /dev/null
+++ b/nxcomp/CreatePixmapCompat.cpp
@@ -0,0 +1,272 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "CreatePixmapCompat.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "WriteBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Constructors and destructors.
+//
+
+CreatePixmapCompatStore::CreatePixmapCompatStore()
+
+ : MessageStore()
+{
+ enableCache = CREATEPIXMAP_ENABLE_CACHE;
+ enableData = CREATEPIXMAP_ENABLE_DATA;
+ enableSplit = CREATEPIXMAP_ENABLE_SPLIT;
+ enableCompress = CREATEPIXMAP_ENABLE_COMPRESS;
+
+ dataLimit = CREATEPIXMAP_DATA_LIMIT;
+ dataOffset = CREATEPIXMAP_DATA_OFFSET;
+
+ cacheSlots = CREATEPIXMAP_CACHE_SLOTS;
+ cacheThreshold = CREATEPIXMAP_CACHE_THRESHOLD;
+ cacheLowerThreshold = CREATEPIXMAP_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+CreatePixmapCompatStore::~CreatePixmapCompatStore()
+{
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int CreatePixmapCompatStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue(*(buffer + 1), 8,
+ clientCache -> depthCache);
+
+ encodeBuffer.encodeDiffCachedValue(GetULONG(buffer + 4, bigEndian),
+ clientCache -> createPixmapLastId, 29,
+ clientCache -> createPixmapIdCache, 4);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian),
+ clientCache -> drawableCache);
+
+ encodeBuffer.encodeCachedValue(GetUINT(buffer + 12, bigEndian), 16,
+ clientCache -> createPixmapXCache, 8);
+
+ encodeBuffer.encodeCachedValue(GetUINT(buffer + 14, bigEndian), 16,
+ clientCache -> createPixmapYCache, 8);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int CreatePixmapCompatStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned char cValue;
+ unsigned int value;
+
+ size = 16;
+
+ buffer = writeBuffer -> addMessage(size);
+
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> depthCache);
+
+ *(buffer + 1) = cValue;
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> createPixmapLastId, 29,
+ clientCache -> createPixmapIdCache, 4);
+
+ PutULONG(value, buffer + 4, bigEndian);
+
+ decodeBuffer.decodeXidValue(value,
+ clientCache -> drawableCache);
+
+ PutULONG(value, buffer + 8, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> createPixmapXCache, 8);
+
+ PutUINT(value, buffer + 12, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> createPixmapYCache, 8);
+
+ PutUINT(value, buffer + 14, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int CreatePixmapCompatStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ CreatePixmapCompatMessage *createPixmap = (CreatePixmapCompatMessage *) message;
+
+ createPixmap -> depth = *(buffer + 1);
+
+ createPixmap -> id = GetULONG(buffer + 4, bigEndian);
+ createPixmap -> drawable = GetULONG(buffer + 8, bigEndian);
+
+ createPixmap -> width = GetUINT(buffer + 12, bigEndian);
+ createPixmap -> height = GetUINT(buffer + 14, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Size is "
+ << createPixmap -> size_ << " identity is "
+ << createPixmap -> i_size_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int CreatePixmapCompatStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ CreatePixmapCompatMessage *createPixmap = (CreatePixmapCompatMessage *) message;
+
+ *(buffer + 1) = createPixmap -> depth;
+
+ PutULONG(createPixmap -> id, buffer + 4, bigEndian);
+ PutULONG(createPixmap -> drawable, buffer + 8, bigEndian);
+
+ PutUINT(createPixmap -> width, buffer + 12, bigEndian);
+ PutUINT(createPixmap -> height, buffer + 14, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Size is "
+ << createPixmap -> size_ << " identity is "
+ << createPixmap -> i_size_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void CreatePixmapCompatStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ #ifdef WARNING
+ *logofs << name() << ": WARNING! Dump of identity not implemented.\n"
+ << logofs_flush;
+ #endif
+
+ #endif
+}
+
+void CreatePixmapCompatStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ md5_append(md5_state_, buffer + 1, 1);
+ md5_append(md5_state_, buffer + 8, 8);
+}
+
+void CreatePixmapCompatStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ CreatePixmapCompatMessage *createPixmap = (CreatePixmapCompatMessage *) message;
+ CreatePixmapCompatMessage *cachedCreatePixmap = (CreatePixmapCompatMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeDiffCachedValue(createPixmap -> id,
+ clientCache -> createPixmapLastId, 29,
+ clientCache -> createPixmapIdCache, 4);
+
+ cachedCreatePixmap -> id = createPixmap -> id;
+
+ encodeBuffer.encodeXidValue(createPixmap -> drawable,
+ clientCache -> drawableCache);
+
+ cachedCreatePixmap -> drawable = createPixmap -> drawable;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Size is "
+ << createPixmap -> size_ << " identity is "
+ << createPixmap -> i_size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+
+void CreatePixmapCompatStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ CreatePixmapCompatMessage *createPixmap = (CreatePixmapCompatMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeDiffCachedValue(createPixmap -> id,
+ clientCache -> createPixmapLastId, 29,
+ clientCache -> createPixmapIdCache, 4);
+
+ decodeBuffer.decodeXidValue(createPixmap -> drawable,
+ clientCache -> drawableCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Size is "
+ << createPixmap -> size_ << " identity is "
+ << createPixmap -> i_size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
diff --git a/nxcomp/CreatePixmapCompat.h b/nxcomp/CreatePixmapCompat.h
new file mode 100644
index 000000000..e8cf8d99f
--- /dev/null
+++ b/nxcomp/CreatePixmapCompat.h
@@ -0,0 +1,154 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef CreatePixmapCompat_H
+#define CreatePixmapCompat_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define CREATEPIXMAP_ENABLE_CACHE 1
+#define CREATEPIXMAP_ENABLE_DATA 0
+#define CREATEPIXMAP_ENABLE_SPLIT 0
+#define CREATEPIXMAP_ENABLE_COMPRESS 0
+
+#define CREATEPIXMAP_DATA_LIMIT 16
+#define CREATEPIXMAP_DATA_OFFSET 16
+
+#define CREATEPIXMAP_CACHE_SLOTS 1000
+#define CREATEPIXMAP_CACHE_THRESHOLD 2
+#define CREATEPIXMAP_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class CreatePixmapCompatMessage : public Message
+{
+ friend class CreatePixmapCompatStore;
+
+ public:
+
+ CreatePixmapCompatMessage()
+ {
+ }
+
+ ~CreatePixmapCompatMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char depth;
+
+ unsigned int id;
+ unsigned int drawable;
+
+ unsigned short width;
+ unsigned short height;
+};
+
+class CreatePixmapCompatStore : public MessageStore
+{
+ public:
+
+ CreatePixmapCompatStore();
+
+ virtual ~CreatePixmapCompatStore();
+
+ virtual const char *name() const
+ {
+ return "CreatePixmapCompat";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_CreatePixmap;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(CreatePixmapCompatMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ protected:
+
+ virtual Message *create() const
+ {
+ return new CreatePixmapCompatMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new CreatePixmapCompatMessage((const CreatePixmapCompatMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (CreatePixmapCompatMessage *) message;
+ }
+
+ virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const;
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* CreatePixmapCompat_H */
diff --git a/nxcomp/DecodeBuffer.cpp b/nxcomp/DecodeBuffer.cpp
new file mode 100644
index 000000000..077bfdfc0
--- /dev/null
+++ b/nxcomp/DecodeBuffer.cpp
@@ -0,0 +1,692 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "Misc.h"
+#include "Control.h"
+
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+DecodeBuffer::DecodeBuffer(const unsigned char *data, unsigned int length)
+
+ : buffer_(data), end_(buffer_ + length), nextSrc_(buffer_), srcMask_(0x80)
+{
+ if (control -> isProtoStep7() == 1)
+ {
+ end_ = buffer_ + length - DECODE_BUFFER_POSTFIX_SIZE;
+ }
+}
+
+int DecodeBuffer::decodeValue(unsigned int &value, unsigned int numBits,
+ unsigned int blockSize, int endOkay)
+{
+ #ifdef DUMP
+ *logofs << "DecodeBuffer: Decoding " << numBits
+ << " bits value with block " << blockSize
+ << " and " << (nextSrc_ - buffer_)
+ << " bytes in buffer.\n" << logofs_flush;
+ #endif
+
+ unsigned int result = 0;
+ unsigned int destMask = 0x1;
+ unsigned int bitsRead = 0;
+
+ if (blockSize == 0)
+ blockSize = numBits;
+
+ unsigned char nextSrcChar = *nextSrc_;
+ unsigned int numBlocks = 1;
+
+ do
+ {
+ if (numBlocks == 4)
+ {
+ blockSize = numBits;
+ }
+
+ unsigned int bitsToRead = (blockSize > numBits - bitsRead ?
+ numBits - bitsRead : blockSize);
+ unsigned int count = 0;
+ unsigned char lastBit;
+
+ do
+ {
+ if (nextSrc_ >= end_)
+ {
+ if (!endOkay)
+ {
+ #ifdef PANIC
+ *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [A] "
+ << "in decodeValue() nextSrc_ = " << (nextSrc_ - buffer_)
+ << " end_ = " << (end_ - buffer_) << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Label "context" is just used to identify
+ // the routine which detected the problem in
+ // present source file.
+ //
+
+ cerr << "Error" << ": Failure decoding data in context [A].\n";
+
+ HandleAbort();
+ }
+
+ #ifdef PANIC
+ *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [B] "
+ << "in decodeValue() nextSrc_ = " << (nextSrc_ - buffer_)
+ << " end_ = " << (end_ - buffer_) << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decoding data in context [B].\n";
+
+ HandleAbort();
+ }
+
+ lastBit = (nextSrcChar & srcMask_);
+
+ if (lastBit)
+ result |= destMask;
+
+ srcMask_ >>= 1;
+
+ if (srcMask_ == 0)
+ {
+ srcMask_ = 0x80;
+ nextSrc_++;
+ nextSrcChar = *nextSrc_;
+ }
+
+ destMask <<= 1;
+ }
+ while (bitsToRead > ++count);
+
+ bitsRead += bitsToRead;
+
+ if (bitsRead < numBits)
+ {
+ if (nextSrc_ >= end_)
+ {
+ if (!endOkay)
+ {
+ #ifdef PANIC
+ *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [C] "
+ << "in decodeValue() nextSrc_ = " << (nextSrc_ - buffer_)
+ << " end_ = " << (end_ - buffer_) << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decoding data in context [C].\n";
+
+ HandleAbort();
+ }
+
+ #ifdef PANIC
+ *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [D] "
+ << "in decodeValue() nextSrc_ = " << (nextSrc_ - buffer_)
+ << " end_ = " << (end_ - buffer_) << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decoding data in context [D].\n";
+
+ HandleAbort();
+ }
+
+ unsigned char moreData = (nextSrcChar & srcMask_);
+
+ srcMask_ >>= 1;
+
+ if (srcMask_ == 0)
+ {
+ srcMask_ = 0x80;
+ nextSrc_++;
+ nextSrcChar = *nextSrc_;
+ }
+
+ if (!moreData)
+ {
+ if (lastBit)
+ {
+ do
+ {
+ result |= destMask;
+ destMask <<= 1;
+ }
+ while (numBits > ++bitsRead);
+ }
+ else
+ bitsRead = numBits;
+ }
+ }
+
+ blockSize >>= 1;
+
+ if (blockSize < 2)
+ blockSize = 2;
+
+ numBlocks++;
+ }
+ while (numBits > bitsRead);
+
+ value = result;
+
+ return 1;
+}
+
+int DecodeBuffer::decodeCachedValue(unsigned int &value, unsigned int numBits,
+ IntCache &cache, unsigned int blockSize,
+ int endOkay)
+{
+ #ifdef DUMP
+ *logofs << "DecodeBuffer: Decoding " << numBits
+ << " bits cached value with block " << blockSize
+ << " and " << (nextSrc_ - buffer_)
+ << " bytes in buffer.\n" << logofs_flush;
+ #endif
+
+ if (nextSrc_ >= end_)
+ {
+ #ifdef PANIC
+ *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [E] "
+ << "in decodeValue() nextSrc_ = " << (nextSrc_ - buffer_)
+ << " end_ = " << (end_ - buffer_) << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decoding data in context [E].\n";
+
+ HandleAbort();
+ }
+
+ unsigned int index = 0;
+ unsigned char nextSrcChar = *nextSrc_;
+
+ while (!(nextSrcChar & srcMask_))
+ {
+ index++;
+ srcMask_ >>= 1;
+ if (srcMask_ == 0)
+ {
+ srcMask_ = 0x80;
+ nextSrc_++;
+ if (nextSrc_ >= end_)
+ {
+ if (!endOkay)
+ {
+ #ifdef PANIC
+ *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [F] "
+ << "in decodeCachedValue() nextSrc_ = "
+ << (nextSrc_ - buffer_) << " end_ = "
+ << (end_ - buffer_) << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decoding data in context [F].\n";
+
+ HandleAbort();
+ }
+
+ #ifdef PANIC
+ *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [G] "
+ << "in decodeValue() nextSrc_ = " << (nextSrc_ - buffer_)
+ << " end_ = " << (end_ - buffer_) << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decoding data in context [G].\n";
+
+ HandleAbort();
+ }
+
+ nextSrcChar = *nextSrc_;
+ }
+ }
+
+ srcMask_ >>= 1;
+
+ if (srcMask_ == 0)
+ {
+ srcMask_ = 0x80;
+ nextSrc_++;
+ }
+
+ if (index == 2)
+ {
+ if (control -> isProtoStep8() == 1)
+ {
+ blockSize = cache.getBlockSize(blockSize);
+
+ if (decodeValue(value, numBits, blockSize, endOkay))
+ {
+ cache.insert(value, IntMask[numBits]);
+
+ return 1;
+ }
+
+ #ifdef PANIC
+ *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [H] "
+ << "in decodeCacheValue() with no value found.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decoding data in context [H].\n";
+
+ HandleAbort();
+ }
+ else
+ {
+ unsigned int sameDiff;
+
+ decodeBoolValue(sameDiff);
+
+ if (sameDiff)
+ {
+ value = cache.getLastDiff(IntMask[numBits]);
+
+ cache.insert(value, IntMask[numBits]);
+
+ return 1;
+ }
+ else
+ {
+ blockSize = cache.getBlockSize(blockSize);
+
+ if (decodeValue(value, numBits, blockSize, endOkay))
+ {
+ cache.insert(value, IntMask[numBits]);
+
+ return 1;
+ }
+
+ #ifdef PANIC
+ *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [H] "
+ << "in decodeCacheValue() with no value found.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decoding data in context [H].\n";
+
+ HandleAbort();
+ }
+ }
+ }
+ else
+ {
+ if (index > 2)
+ {
+ index--;
+ }
+
+ if (index > cache.getSize())
+ {
+ #ifdef PANIC
+ *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [I] "
+ << "in decodeCachedValue() index = " << index
+ << " cache size = " << cache.getSize() << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decoding data in context [I].\n";
+
+ HandleAbort();
+ }
+
+ value = cache.get(index);
+
+ return 1;
+ }
+}
+
+int DecodeBuffer::decodeCachedValue(unsigned char &value, unsigned int numBits,
+ CharCache &cache, unsigned int blockSize,
+ int endOkay)
+{
+ #ifdef DUMP
+ *logofs << "DecodeBuffer: Decoding " << numBits
+ << " bits char cached value with block " << blockSize
+ << " and " << nextSrc_ - buffer_ << " bytes read out of "
+ << end_ - buffer_ << ".\n" << logofs_flush;
+ #endif
+
+ if (nextSrc_ >= end_)
+ {
+ #ifdef TEST
+ *logofs << "DecodeBuffer: End of buffer reached in context [J] with "
+ << nextSrc_ - buffer_ << " bytes read out of "
+ << end_ - buffer_ << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ unsigned int index = 0;
+ unsigned char nextSrcChar = *nextSrc_;
+
+ while (!(nextSrcChar & srcMask_))
+ {
+ index++;
+ srcMask_ >>= 1;
+
+ if (srcMask_ == 0)
+ {
+ srcMask_ = 0x80;
+ nextSrc_++;
+
+ if (nextSrc_ >= end_)
+ {
+ if (!endOkay)
+ {
+ #ifdef PANIC
+ *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [K] "
+ << "in decodeCachedValue() nextSrc_ "
+ << (nextSrc_ - buffer_) << " end_ " << (end_ - buffer_)
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decoding data in context [K].\n";
+
+ HandleAbort();
+ }
+
+ #ifdef TEST
+ *logofs << "DecodeBuffer: End of buffer reached in context [L] with "
+ << nextSrc_ - buffer_ << " bytes read out of "
+ << end_ - buffer_ << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ nextSrcChar = *nextSrc_;
+ }
+ }
+
+ srcMask_ >>= 1;
+
+ if (srcMask_ == 0)
+ {
+ srcMask_ = 0x80;
+ nextSrc_++;
+ }
+
+ if (index == 2)
+ {
+ unsigned int temp;
+
+ if (decodeValue(temp, numBits, blockSize, endOkay))
+ {
+ value = (unsigned char) temp;
+
+ cache.insert(value);
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [M] "
+ << "in decodeValue() with index = 2.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decoding data in context [M].\n";
+
+ HandleAbort();
+ }
+ }
+ else
+ {
+ if (index > 2)
+ {
+ index--;
+ }
+
+ if (index > cache.getSize())
+ {
+ #ifdef PANIC
+ *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [N] "
+ << "in decodeCachedValue() " << "index = " << index
+ << " cache size = " << cache.getSize() << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decoding data in context [N].\n";
+
+ HandleAbort();
+ }
+
+ value = cache.get(index);
+ }
+
+ return 1;
+}
+
+//
+// Simply returns a pointer to the correct spot in
+// the internal buffer. If the caller needs this
+// data to last beyond the lifetime of the internal
+// buffer, it must copy the data in its own memory.
+//
+
+const unsigned char *DecodeBuffer::decodeMemory(unsigned int numBytes)
+{
+ #ifdef DUMP
+ *logofs << "DecodeBuffer: Decoding " << numBytes
+ << " bytes of memory with " << (nextSrc_ - buffer_)
+ << " bytes in buffer.\n" << logofs_flush;
+ #endif
+
+ const unsigned char *result;
+
+ //
+ // Force ourselves to a byte boundary.
+ // Is up to application to ensure data
+ // is word alligned when needed.
+ //
+
+ if (srcMask_ != 0x80)
+ {
+ srcMask_ = 0x80;
+ nextSrc_++;
+ }
+
+ result = nextSrc_;
+
+ if (numBytes > DECODE_BUFFER_OVERFLOW_SIZE)
+ {
+ #ifdef PANIC
+ *logofs << "DecodeBuffer: PANIC! Can't decode a buffer of "
+ << numBytes << " bytes with limit set to "
+ << DECODE_BUFFER_OVERFLOW_SIZE << ".\n"
+ << logofs_flush;
+
+ *logofs << "DecodeBuffer: PANIC! Assuming failure decoding "
+ << "data in context [O].\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Should never decode buffer of size "
+ << "greater than " << DECODE_BUFFER_OVERFLOW_SIZE
+ << " bytes.\n";
+
+ cerr << "Error" << ": Assuming failure decoding data in "
+ << "context [O].\n";
+
+ HandleAbort();
+ }
+ else if (end_ - nextSrc_ < (int) numBytes)
+ {
+ #ifdef PANIC
+ *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [P] "
+ << "in decodeMemory() " << "with length " << numBytes
+ << " and " << (end_ - nextSrc_)
+ << " bytes remaining.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decoding data in context [P].\n";
+
+ HandleAbort();
+ }
+
+ nextSrc_ += numBytes;
+
+ return result;
+}
+
+void DecodeBuffer::decodeActionValue(unsigned char &value, unsigned short &position,
+ ActionCache &cache)
+{
+ unsigned int t;
+
+ decodeCachedValue(t, 15, *(cache.base_[cache.slot_]));
+
+ cache.last_ += t;
+ cache.last_ &= 0x7fff;
+
+ value = cache.last_ >> 13;
+
+ position = cache.last_ & 0x1fff;
+
+ #ifdef DEBUG
+ *logofs << "DecodeBuffer: Decoded value "
+ << (unsigned) value << " and position "
+ << position << " with base " << cache.slot_
+ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef DEBUG
+ *logofs << "DecodeBuffer: Action block prediction is "
+ << (*(cache.base_[cache.slot_])).getBlockSize(15)
+ << ".\n" << logofs_flush;
+ #endif
+
+ cache.slot_ = (cache.last_ & 0xff);
+}
+
+void DecodeBuffer::decodeNewXidValue(unsigned int &value, unsigned int &lastId,
+ IntCache &lastIdCache, IntCache &cache,
+ FreeCache &freeCache)
+{
+ decodeCachedValue(value, 29, lastIdCache);
+
+ lastId += (value + 1);
+ lastId &= 0x1fffffff;
+
+ value = lastId;
+
+ cache.push(value, 0x1fffffff);
+
+ freeCache.push(value, 0x1fffffff);
+}
+
+void DecodeBuffer::decodeNewXidValue(unsigned int &value, unsigned int &lastId,
+ IntCache &lastIdCache, XidCache &cache,
+ FreeCache &freeCache)
+{
+ decodeCachedValue(value, 29, lastIdCache);
+
+ #ifdef DEBUG
+ *logofs << "DecodeBuffer: Decoded new Xid difference "
+ << value << ".\n" << logofs_flush;
+ #endif
+
+ lastId += (value + 1);
+ lastId &= 0x1fffffff;
+
+ value = lastId;
+
+ unsigned int t = (value - cache.last_);
+
+ cache.last_ = value;
+
+ #ifdef DEBUG
+ *logofs << "DecodeBuffer: Decoded new Xid " << value
+ << " with base " << cache.slot_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ cache.slot_ = (value & 0xff);
+
+ cache.base_[cache.slot_] -> push(t, 0x1fffffff);
+
+ freeCache.push(value, 0x1fffffff);
+}
+
+void DecodeBuffer::decodeXidValue(unsigned int &value, XidCache &cache)
+{
+ unsigned int t;
+
+ decodeCachedValue(t, 29, *(cache.base_[cache.slot_]));
+
+ cache.last_ += t;
+ cache.last_ &= 0x1fffffff;
+
+ value = cache.last_;
+
+ #ifdef DEBUG
+ *logofs << "DecodeBuffer: Decoded Xid " << value
+ << " with base " << cache.slot_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ cache.slot_ = (value & 0xff);
+
+ #ifdef DEBUG
+ *logofs << "DecodeBuffer: Xid block prediction is "
+ << (*(cache.base_[cache.slot_])).getBlockSize(29)
+ << ".\n" << logofs_flush;
+ #endif
+}
+
+void DecodeBuffer::decodeFreeXidValue(unsigned int &value, FreeCache &cache)
+{
+ decodeCachedValue(value, 29, cache);
+}
+
+void DecodeBuffer::decodePositionValueCompat(short int &value, PositionCacheCompat &cache)
+{
+ unsigned int t;
+
+ decodeCachedValue(t, 13, *(cache.base_[cache.slot_]));
+
+ cache.last_ += t;
+ cache.last_ &= 0x1fff;
+
+ value = cache.last_;
+
+ #ifdef DEBUG
+ *logofs << "DecodeBuffer: Decoded position "
+ << value << " with base " << cache.slot_
+ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef DEBUG
+ *logofs << "DecodeBuffer: Position block prediction is "
+ << (*(cache.base_[cache.slot_])).getBlockSize(13)
+ << ".\n" << logofs_flush;
+ #endif
+
+ cache.slot_ = (value & 0x1f);
+}
diff --git a/nxcomp/DecodeBuffer.h b/nxcomp/DecodeBuffer.h
new file mode 100644
index 000000000..9345f4e23
--- /dev/null
+++ b/nxcomp/DecodeBuffer.h
@@ -0,0 +1,142 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef DecodeBuffer_H
+#define DecodeBuffer_H
+
+#include <string.h>
+
+#include "IntCache.h"
+#include "CharCache.h"
+#include "XidCache.h"
+#include "FreeCache.h"
+#include "OpcodeCache.h"
+#include "ActionCache.h"
+
+#include "ActionCacheCompat.h"
+#include "PositionCacheCompat.h"
+
+#define DECODE_BUFFER_OVERFLOW_SIZE 4194304
+
+#define DECODE_BUFFER_POSTFIX_SIZE 1
+
+class DecodeBuffer
+{
+ public:
+
+ DecodeBuffer(const unsigned char *data, unsigned int length);
+
+ ~DecodeBuffer()
+ {
+ }
+
+ int decodeValue(unsigned int &value, unsigned int numBits,
+ unsigned int blockSize = 0, int endOkay = 0);
+
+ int decodeCachedValue(unsigned int &value, unsigned int numBits,
+ IntCache &cache, unsigned int blockSize = 0,
+ int endOkay = 0);
+
+ int decodeCachedValue(unsigned char &value, unsigned int numBits,
+ CharCache &cache, unsigned int blockSize = 0,
+ int endOkay = 0);
+
+ void decodeDiffCachedValue(unsigned int &value, unsigned int &previous,
+ unsigned int numBits, IntCache &cache,
+ unsigned int blockSize = 0)
+ {
+ decodeCachedValue(value, numBits, cache, blockSize);
+
+ previous += (value + 1);
+ previous &= (0xffffffff >> (32 - numBits));
+
+ value = previous;
+ }
+
+ void decodeBoolValue(unsigned int &value)
+ {
+ decodeValue(value, 1);
+ }
+
+ int decodeOpcodeValue(unsigned char &value, OpcodeCache &cache, int endOkay = 0)
+ {
+ int result = decodeCachedValue(value, 8, cache.base_[cache.slot_], 8, endOkay);
+
+ if (result == 1)
+ {
+ cache.slot_ = value;
+ }
+
+ return result;
+ }
+
+ void decodeActionValue(unsigned char &value, unsigned short &position,
+ ActionCache &cache);
+
+ void decodeNewXidValue(unsigned int &value, unsigned int &lastId,
+ IntCache &lastIdCache, IntCache &cache,
+ FreeCache &freeCache);
+
+ void decodeNewXidValue(unsigned int &value, unsigned int &lastId,
+ IntCache &lastIdCache, XidCache &cache,
+ FreeCache &freeCache);
+
+ void decodeXidValue(unsigned int &value, XidCache &cache);
+
+ void decodeFreeXidValue(unsigned int &value, FreeCache &cache);
+
+ void decodeActionValueCompat(unsigned char &value, ActionCacheCompat &cache)
+ {
+ decodeCachedValue(value, 2, cache.base_[cache.slot_]);
+
+ cache.slot_ = value;
+ }
+
+ void decodePositionValueCompat(short int &value, PositionCacheCompat &cache);
+
+ void decodeTextData(unsigned char *buffer, unsigned int numBytes)
+ {
+ decodeMemory(buffer, numBytes);
+ }
+
+ void decodeIntData(unsigned char *buffer, unsigned int numBytes)
+ {
+ decodeMemory(buffer, numBytes);
+ }
+
+ void decodeLongData(unsigned char *buffer, unsigned int numBytes)
+ {
+ decodeMemory(buffer, numBytes);
+ }
+
+ const unsigned char *decodeMemory(unsigned int numBytes);
+
+ void decodeMemory(unsigned char *buffer, unsigned int numBytes)
+ {
+ memcpy(buffer, decodeMemory(numBytes), numBytes);
+ }
+
+ private:
+
+ const unsigned char *buffer_;
+ const unsigned char *end_;
+ const unsigned char *nextSrc_;
+
+ unsigned char srcMask_;
+};
+
+#endif /* DecodeBuffer_H */
diff --git a/nxcomp/EncodeBuffer.cpp b/nxcomp/EncodeBuffer.cpp
new file mode 100644
index 000000000..466a1d7a0
--- /dev/null
+++ b/nxcomp/EncodeBuffer.cpp
@@ -0,0 +1,660 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "Misc.h"
+#include "Control.h"
+
+#include "EncodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+#define ADVANCE_DEST \
+\
+if (destShift_ == 0) \
+{ \
+ destShift_ = 7; nextDest_++; *nextDest_ = 0; \
+} \
+else \
+{ \
+ destShift_--; \
+}
+
+EncodeBuffer::EncodeBuffer()
+{
+ size_ = ENCODE_BUFFER_DEFAULT_SIZE;
+
+ buffer_ = new unsigned char[size_ + ENCODE_BUFFER_PREFIX_SIZE +
+ ENCODE_BUFFER_POSTFIX_SIZE] + ENCODE_BUFFER_PREFIX_SIZE;
+ end_ = buffer_ + size_;
+
+ nextDest_ = buffer_;
+ *nextDest_ = 0;
+ destShift_ = 7;
+
+ lastBits_ = 0;
+
+ initialSize_ = ENCODE_BUFFER_DEFAULT_SIZE;
+ thresholdSize_ = ENCODE_BUFFER_DEFAULT_SIZE << 1;
+ maximumSize_ = ENCODE_BUFFER_DEFAULT_SIZE << 4;
+}
+
+EncodeBuffer::~EncodeBuffer()
+{
+ delete [] (buffer_ - ENCODE_BUFFER_PREFIX_SIZE);
+}
+
+void EncodeBuffer::setSize(unsigned int initialSize, unsigned int thresholdSize,
+ unsigned int maximumSize)
+{
+ initialSize_ = initialSize;
+ thresholdSize_ = thresholdSize;
+ maximumSize_ = maximumSize;
+
+ #ifdef TEST
+ *logofs << "EncodeBuffer: Set buffer sizes to "
+ << initialSize_ << "/" << thresholdSize_
+ << "/" << maximumSize_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+
+void EncodeBuffer::fullReset()
+{
+ if (size_ > initialSize_)
+ {
+ delete [] (buffer_ - ENCODE_BUFFER_PREFIX_SIZE);
+
+ size_ = initialSize_;
+
+ buffer_ = new unsigned char[size_ + ENCODE_BUFFER_PREFIX_SIZE +
+ ENCODE_BUFFER_POSTFIX_SIZE] + ENCODE_BUFFER_PREFIX_SIZE;
+ }
+
+ end_ = buffer_ + size_;
+
+ nextDest_ = buffer_;
+ *nextDest_ = 0;
+ destShift_ = 7;
+
+ lastBits_ = 0;
+}
+
+void EncodeBuffer::encodeValue(unsigned int value, unsigned int numBits,
+ unsigned int blockSize)
+{
+ #ifdef DUMP
+ *logofs << "EncodeBuffer: Encoding " << numBits
+ << " bits value with block " << blockSize
+ << " and " << (nextDest_ - buffer_)
+ << " bytes in buffer.\n" << logofs_flush;
+ #endif
+
+ value &= IntMask[numBits];
+
+ unsigned int srcMask = 0x1;
+ unsigned int bitsWritten = 0;
+
+ if (blockSize == 0)
+ blockSize = numBits;
+
+ if (end_ - nextDest_ < 8)
+ {
+ growBuffer();
+ }
+
+ unsigned int numBlocks = 1;
+
+ do
+ {
+ if (numBlocks == 4)
+ blockSize = numBits;
+
+ unsigned int bitsToWrite = (blockSize > numBits - bitsWritten ?
+ numBits - bitsWritten : blockSize);
+ unsigned int count = 0;
+ unsigned int lastBit;
+
+ do
+ {
+ lastBit = (value & srcMask);
+ if (lastBit)
+ *nextDest_ |= (1 << destShift_);
+ ADVANCE_DEST;
+ srcMask <<= 1;
+ }
+ while (bitsToWrite > ++count);
+
+ bitsWritten += bitsToWrite;
+
+ if (bitsWritten < numBits)
+ {
+ unsigned int tmpMask = srcMask;
+ unsigned int i = bitsWritten;
+
+ if (lastBit)
+ {
+ do
+ {
+ unsigned int nextBit = (value & tmpMask);
+
+ if (!nextBit)
+ break;
+
+ tmpMask <<= 1;
+ }
+ while (numBits > ++i);
+ }
+ else
+ {
+ do
+ {
+ unsigned int nextBit = (value & tmpMask);
+
+ if (nextBit)
+ break;
+
+ tmpMask <<= 1;
+ }
+ while (numBits > ++i);
+ }
+
+ if (i < numBits)
+ *nextDest_ |= (1 << destShift_);
+ else
+ bitsWritten = numBits;
+
+ ADVANCE_DEST;
+ }
+ blockSize >>= 1;
+
+ if (blockSize < 2)
+ blockSize = 2;
+
+ numBlocks++;
+ }
+ while (numBits > bitsWritten);
+}
+
+void EncodeBuffer::encodeCachedValue(unsigned int value, unsigned int numBits,
+ IntCache &cache, unsigned int blockSize)
+{
+ #ifdef DUMP
+ *logofs << "EncodeBuffer: Encoding " << numBits
+ << " bits cached value with block " << blockSize
+ << " and " << (nextDest_ - buffer_)
+ << " bytes in buffer.\n" << logofs_flush;
+ #endif
+
+ value &= IntMask[numBits];
+
+ if (end_ - nextDest_ < 8)
+ {
+ growBuffer();
+ }
+
+ blockSize = cache.getBlockSize(blockSize);
+
+ unsigned int index;
+ unsigned int sameDiff;
+
+ #ifdef DUMP
+
+ diffBits();
+
+ #endif
+
+ if (cache.lookup(value, index, IntMask[numBits], sameDiff))
+ {
+ if (index > 1)
+ index++;
+
+ while (destShift_ < index)
+ {
+ index -= destShift_;
+ index--;
+ destShift_ = 7;
+ nextDest_++;
+ *nextDest_ = 0;
+ }
+
+ destShift_ -= index;
+ *nextDest_ |= (1 << destShift_);
+ ADVANCE_DEST;
+
+ #ifdef DUMP
+ *logofs << "EncodeBuffer: Encoded cached int using "
+ << diffBits() << " bits out of " << numBits
+ << ".\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ ADVANCE_DEST;
+ ADVANCE_DEST;
+ *nextDest_ |= (1 << destShift_);
+ ADVANCE_DEST;
+
+ //
+ // The attempt is very seldom successful.
+ // Avoid to encode the additional bool.
+ //
+
+ if (control -> isProtoStep8() == 1)
+ {
+ #ifdef DUMP
+ *logofs << "EncodeBuffer: Encoded missed int using "
+ << diffBits() << " bits out of " << numBits
+ << ".\n" << logofs_flush;
+ #endif
+
+ encodeValue(value, numBits, blockSize);
+ }
+ else
+ {
+ if (sameDiff)
+ {
+ #ifdef DUMP
+ *logofs << "EncodeBuffer: Matched difference with block size "
+ << cache.getBlockSize(blockSize) << ".\n"
+ << logofs_flush;
+ #endif
+
+ encodeBoolValue(1);
+ }
+ else
+ {
+ #ifdef DUMP
+ *logofs << "EncodeBuffer: Missed difference with block size "
+ << cache.getBlockSize(blockSize) << ".\n"
+ << logofs_flush;
+ #endif
+
+ encodeBoolValue(0);
+
+ encodeValue(value, numBits, blockSize);
+ }
+
+ #ifdef DUMP
+ *logofs << "EncodeBuffer: Encoded missed int using "
+ << diffBits() << " bits out of " << numBits
+ << ".\n" << logofs_flush;
+ #endif
+ }
+ }
+}
+
+void EncodeBuffer::encodeCachedValue(unsigned char value, unsigned int numBits,
+ CharCache &cache, unsigned int blockSize)
+{
+ #ifdef DUMP
+ *logofs << "EncodeBuffer: Encoding " << numBits
+ << " bits char cached value with block " << blockSize
+ << " and " << (nextDest_ - buffer_)
+ << " bytes in buffer.\n" << logofs_flush;
+ #endif
+
+ value &= IntMask[numBits];
+
+ if (end_ - nextDest_ < 8)
+ {
+ growBuffer();
+ }
+
+ unsigned int index;
+
+ #ifdef DUMP
+
+ diffBits();
+
+ #endif
+
+ if (cache.lookup(value, index))
+ {
+ if (index > 1)
+ index++;
+
+ while (destShift_ < index)
+ {
+ index -= destShift_;
+ index--;
+ destShift_ = 7;
+ nextDest_++;
+ *nextDest_ = 0;
+ }
+
+ destShift_ -= index;
+ *nextDest_ |= (1 << destShift_);
+ ADVANCE_DEST;
+
+ #ifdef DUMP
+ *logofs << "EncodeBuffer: Encoded cached char using "
+ << diffBits() << " bits out of " << numBits
+ << ".\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ ADVANCE_DEST;
+ ADVANCE_DEST;
+ *nextDest_ |= (1 << destShift_);
+ ADVANCE_DEST;
+
+ encodeValue(value, numBits, blockSize);
+
+ #ifdef DUMP
+ *logofs << "EncodeBuffer: Encoded missed char using "
+ << diffBits() << " bits out of " << numBits
+ << ".\n" << logofs_flush;
+ #endif
+ }
+}
+
+void EncodeBuffer::encodeMemory(const unsigned char *buffer, unsigned int numBytes)
+{
+ #ifdef DUMP
+ *logofs << "EncodeBuffer: Encoding " << numBytes
+ << " bytes of memory with " << (nextDest_ - buffer_)
+ << " bytes in buffer.\n" << logofs_flush;
+ #endif
+
+ if (numBytes > ENCODE_BUFFER_OVERFLOW_SIZE)
+ {
+ #ifdef PANIC
+ *logofs << "EncodeBuffer: PANIC! Should never encode buffer "
+ << "of size greater than " << ENCODE_BUFFER_OVERFLOW_SIZE
+ << " bytes.\n" << logofs_flush;
+
+ *logofs << "EncodeBuffer: PANIC! Assuming failure encoding data "
+ << "in context [A].\n" << logofs_flush;
+ #endif
+
+ //
+ // Label "context" is just used to identify
+ // the routine which detected the problem in
+ // present source file.
+ //
+
+ cerr << "Error" << ": Should never encode buffer of size "
+ << "greater than " << ENCODE_BUFFER_OVERFLOW_SIZE
+ << " bytes.\n";
+
+ cerr << "Error" << ": Assuming failure encoding data "
+ << "in context [A].\n" ;
+
+ HandleAbort();
+ }
+
+ alignBuffer();
+
+ if (end_ - nextDest_ < (int) numBytes)
+ {
+ growBuffer(numBytes);
+ }
+
+ memcpy(nextDest_, buffer, numBytes);
+
+ nextDest_ += numBytes;
+
+ if (nextDest_ == end_)
+ {
+ growBuffer();
+ }
+ else if (nextDest_ > end_)
+ {
+ #ifdef PANIC
+ *logofs << "EncodeBuffer: PANIC! Assertion failed. Error [B] "
+ << "in encodeMemory() nextDest_ " << (nextDest_ - buffer)
+ << " end_ " << (end_ - buffer) << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Label "context" is just used to identify
+ // the routine which detected the problem in
+ // present source file.
+ //
+
+ cerr << "Error" << ": Failure encoding raw data "
+ << "in context [B].\n" ;
+
+ HandleAbort();
+ }
+
+ *nextDest_ = 0;
+}
+
+unsigned int EncodeBuffer::getLength() const
+{
+ unsigned int length = nextDest_ - buffer_;
+
+ if (destShift_ != 7)
+ {
+ length++;
+ }
+
+ if (length > 0 && control -> isProtoStep7() == 1)
+ {
+ return length + ENCODE_BUFFER_POSTFIX_SIZE;
+ }
+
+ return length;
+}
+
+unsigned int EncodeBuffer::diffBits()
+{
+ unsigned int bits = ((nextDest_ - buffer_) << 3);
+
+ bits += (7 - destShift_);
+
+ unsigned int diff = bits - lastBits_;
+
+ lastBits_ = bits;
+
+ return diff;
+}
+
+void EncodeBuffer::growBuffer(unsigned int numBytes)
+{
+ if (numBytes == 0)
+ {
+ numBytes = initialSize_;
+ }
+
+ unsigned int bytesInBuffer = nextDest_ - buffer_;
+
+ unsigned int newSize = thresholdSize_;
+
+ while (newSize < bytesInBuffer + numBytes)
+ {
+ newSize <<= 1;
+
+ if (newSize > maximumSize_)
+ {
+ newSize = bytesInBuffer + numBytes + initialSize_;
+ }
+ }
+
+ unsigned char *newBuffer;
+
+ newBuffer = new unsigned char[newSize + ENCODE_BUFFER_PREFIX_SIZE +
+ ENCODE_BUFFER_POSTFIX_SIZE] + ENCODE_BUFFER_PREFIX_SIZE;
+
+ if (newBuffer == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "EncodeBuffer: PANIC! Error in context [C] "
+ << "growing buffer to accomodate " << numBytes
+ << " bytes .\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error in context [C] "
+ << "growing encode buffer to accomodate "
+ << numBytes << " bytes.\n";
+
+ HandleAbort();
+ }
+
+ #ifdef TEST
+ if (newSize >= maximumSize_)
+ {
+ *logofs << "EncodeBuffer: WARNING! Buffer grown to reach "
+ << "size of " << newSize << " bytes.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ //
+ // Prefix should not contain any valid data.
+ // It is proxy that will fill it with control
+ // messages and data length at the time a new
+ // frame is written to socket.
+ //
+
+ memcpy(newBuffer, buffer_, bytesInBuffer + 1);
+
+ newBuffer[bytesInBuffer + 1] = 0;
+
+ delete [] (buffer_ - ENCODE_BUFFER_PREFIX_SIZE);
+
+ buffer_ = newBuffer;
+ size_ = newSize;
+ end_ = buffer_ + size_;
+
+ nextDest_ = buffer_ + bytesInBuffer;
+}
+
+void EncodeBuffer::alignBuffer()
+{
+ if (destShift_ != 7)
+ {
+ destShift_ = 7;
+ nextDest_++;
+
+ if (nextDest_ >= end_)
+ {
+ growBuffer();
+ }
+
+ *nextDest_ = 0;
+ }
+}
+
+void EncodeBuffer::encodeActionValue(unsigned char value, unsigned short position,
+ ActionCache &cache)
+{
+ unsigned int v = (value << 13) | position;
+
+ unsigned int t = (v - cache.last_);
+
+ encodeCachedValue(t, 15, *(cache.base_[cache.slot_]));
+
+ cache.last_ = v;
+
+ #ifdef DEBUG
+ *logofs << "EncodeBuffer: Encoded value "
+ << (unsigned) value << " and position "
+ << position << " with base " << cache.slot_
+ << ".\n" << logofs_flush;
+ #endif
+
+ cache.slot_ = (cache.last_ & 0xff);
+}
+
+void EncodeBuffer::encodeNewXidValue(unsigned int value, unsigned int &lastId,
+ IntCache &lastIdCache, IntCache &cache,
+ FreeCache &freeCache)
+{
+ encodeCachedValue((value - 1) - lastId, 29, lastIdCache);
+
+ lastId = value;
+
+ cache.push(value, 0x1fffffff);
+
+ freeCache.push(value, 0x1fffffff);
+}
+
+void EncodeBuffer::encodeNewXidValue(unsigned int value, unsigned int &lastId,
+ IntCache &lastIdCache, XidCache &cache,
+ FreeCache &freeCache)
+{
+ encodeCachedValue((value - 1) - lastId, 29, lastIdCache);
+
+ lastId = value;
+
+ unsigned int t = (value - cache.last_);
+
+ cache.last_ = value;
+
+ #ifdef DEBUG
+ *logofs << "EncodeBuffer: Encoded new Xid " << value
+ << " with base " << cache.slot_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ cache.slot_ = (value & 0xff);
+
+ cache.base_[cache.slot_] -> push(t, 0x1fffffff);
+
+ freeCache.push(value, IntMask[29]);
+}
+
+void EncodeBuffer::encodeXidValue(unsigned int value, XidCache &cache)
+{
+ unsigned int t = (value - cache.last_);
+
+ encodeCachedValue(t, 29, *(cache.base_[cache.slot_]));
+
+ cache.last_ = value;
+
+ #ifdef DEBUG
+ *logofs << "EncodeBuffer: Encoded Xid " << value
+ << " with base " << cache.slot_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ cache.slot_ = (value & 0xff);
+}
+
+void EncodeBuffer::encodeFreeXidValue(unsigned int value, FreeCache &cache)
+{
+ encodeCachedValue(value, 29, cache);
+}
+
+void EncodeBuffer::encodePositionValueCompat(short int value, PositionCacheCompat &cache)
+{
+ unsigned int t = (value - cache.last_);
+
+ encodeCachedValue(t, 13, *(cache.base_[cache.slot_]));
+
+ cache.last_ = value;
+
+ #ifdef DEBUG
+ *logofs << "EncodeBuffer: Encoded position "
+ << value << " with base " << cache.slot_
+ << ".\n" << logofs_flush;
+ #endif
+
+ cache.slot_ = (value & 0x1f);
+}
diff --git a/nxcomp/EncodeBuffer.h b/nxcomp/EncodeBuffer.h
new file mode 100644
index 000000000..9f5ac5352
--- /dev/null
+++ b/nxcomp/EncodeBuffer.h
@@ -0,0 +1,187 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef EncodeBuffer_H
+#define EncodeBuffer_H
+
+#include "IntCache.h"
+#include "CharCache.h"
+#include "XidCache.h"
+#include "FreeCache.h"
+#include "OpcodeCache.h"
+#include "ActionCache.h"
+
+#include "ActionCacheCompat.h"
+#include "PositionCacheCompat.h"
+
+#define ENCODE_BUFFER_DEFAULT_SIZE 16384
+
+//
+// This should match the maximum size of
+// a single message added to write buffer
+// (see WriteBuffer.h).
+//
+
+#define ENCODE_BUFFER_OVERFLOW_SIZE 4194304
+
+//
+// Adjust for the control messages and the
+// frame length added by the proxy.
+//
+
+#define ENCODE_BUFFER_PREFIX_SIZE 64
+
+//
+// The encode routines may write one byte
+// past the nominal end of the encode buffer.
+// This additional byte is included in the
+// payload. This is actually a harmless bug.
+//
+
+#define ENCODE_BUFFER_POSTFIX_SIZE 1
+
+class EncodeBuffer
+{
+ public:
+
+ EncodeBuffer();
+
+ ~EncodeBuffer();
+
+ void setSize(unsigned int initialSize, unsigned int thresholdSize,
+ unsigned int maximumSize);
+
+ void encodeValue(unsigned int value, unsigned int numBits,
+ unsigned int blockSize = 0);
+
+ void encodeCachedValue(unsigned int value, unsigned int numBits,
+ IntCache &cache, unsigned int blockSize = 0);
+
+ void encodeCachedValue(unsigned char value, unsigned int numBits,
+ CharCache &cache, unsigned int blockSize = 0);
+
+ void encodeDiffCachedValue(const unsigned int value, unsigned int &previous,
+ unsigned int numBits, IntCache &cache,
+ unsigned int blockSize = 0)
+ {
+ encodeCachedValue((value - 1) - previous, numBits, cache, blockSize);
+
+ previous = value;
+ }
+
+ void encodeBoolValue(unsigned int value)
+ {
+ encodeValue(value, 1);
+ }
+
+ void encodeOpcodeValue(unsigned char value, OpcodeCache &cache)
+ {
+ encodeCachedValue(value, 8, cache.base_[cache.slot_], 8);
+
+ cache.slot_ = value;
+ }
+
+ void encodeActionValue(unsigned char value, ActionCache &cache)
+ {
+ unsigned short position = 0;
+
+ encodeActionValue(value, position, cache);
+ }
+
+ void encodeActionValue(unsigned char value, unsigned short position,
+ ActionCache &cache);
+
+ void encodeNewXidValue(unsigned int value, unsigned int &lastId,
+ IntCache &lastIdCache, IntCache &cache,
+ FreeCache &freeCache);
+
+ void encodeNewXidValue(unsigned int value, unsigned int &lastId,
+ IntCache &lastIdCache, XidCache &cache,
+ FreeCache &freeCache);
+
+ void encodeXidValue(unsigned int value, XidCache &cache);
+
+ void encodeFreeXidValue(unsigned int value, FreeCache &cache);
+
+ void encodeActionValueCompat(unsigned char value, ActionCacheCompat &cache)
+ {
+ encodeCachedValue(value, 2, cache.base_[cache.slot_]);
+
+ cache.slot_ = value;
+ }
+
+ void encodePositionValueCompat(short int value, PositionCacheCompat &cache);
+
+ void encodeTextData(const unsigned char *buffer, unsigned int numBytes)
+ {
+ encodeMemory(buffer, numBytes);
+ }
+
+ void encodeIntData(const unsigned char *buffer, unsigned int numBytes)
+ {
+ encodeMemory(buffer, numBytes);
+ }
+
+ void encodeLongData(const unsigned char *buffer, unsigned int numBytes)
+ {
+ encodeMemory(buffer, numBytes);
+ }
+
+ void encodeMemory(const unsigned char *buffer, unsigned int numBytes);
+
+ unsigned char *getData()
+ {
+ return buffer_;
+ }
+
+ unsigned int getLength() const;
+
+ unsigned int getBits() const
+ {
+ return ((nextDest_ - buffer_) << 3) + (7 - destShift_);
+ }
+
+ unsigned int diffBits();
+
+ void fullReset();
+
+ private:
+
+ void growBuffer(unsigned int numBytes = 0);
+
+ void alignBuffer();
+
+ unsigned int size_;
+ unsigned char *buffer_;
+
+ //
+ // This points to the first byte
+ // just beyond end of the buffer.
+ //
+
+ const unsigned char *end_;
+
+ unsigned char *nextDest_;
+ unsigned int destShift_;
+ unsigned int lastBits_;
+
+ unsigned int initialSize_;
+ unsigned int thresholdSize_;
+ unsigned int maximumSize_;
+};
+
+#endif /* EncodeBuffer_H */
diff --git a/nxcomp/FillPoly.cpp b/nxcomp/FillPoly.cpp
new file mode 100644
index 000000000..37df3772b
--- /dev/null
+++ b/nxcomp/FillPoly.cpp
@@ -0,0 +1,227 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "FillPoly.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int FillPolyStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ FillPolyMessage *fillPoly = (FillPolyMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ fillPoly -> drawable = GetULONG(buffer + 4, bigEndian);
+ fillPoly -> gcontext = GetULONG(buffer + 8, bigEndian);
+
+ fillPoly -> shape = *(buffer + 12);
+ fillPoly -> mode = *(buffer + 13);
+
+ if (control -> isProtoStep8() == 1 &&
+ size >= (unsigned int) dataOffset)
+ {
+ fillPoly -> x_origin = GetUINT(buffer + 16, bigEndian);
+ fillPoly -> y_origin = GetUINT(buffer + 18, bigEndian);
+ }
+ else
+ {
+ fillPoly -> x_origin = 0;
+ fillPoly -> y_origin = 0;
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int FillPolyStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ FillPolyMessage *fillPoly = (FillPolyMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ PutULONG(fillPoly -> drawable, buffer + 4, bigEndian);
+ PutULONG(fillPoly -> gcontext, buffer + 8, bigEndian);
+
+ *(buffer + 12) = fillPoly -> shape;
+ *(buffer + 13) = fillPoly -> mode;
+
+ if (control -> isProtoStep8() == 1 &&
+ size >= (unsigned int) dataOffset)
+ {
+ PutUINT(fillPoly -> x_origin, buffer + 16, bigEndian);
+ PutUINT(fillPoly -> y_origin, buffer + 18, bigEndian);
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void FillPolyStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ FillPolyMessage *fillPoly = (FillPolyMessage *) message;
+
+ *logofs << name() << ": Identity drawable " << fillPoly -> drawable
+ << ", gcontext " << fillPoly -> gcontext << ", shape "
+ << fillPoly -> shape << ", mode " << fillPoly -> mode
+ << fillPoly -> size_ << ", x_origin " << fillPoly -> x_origin
+ << ", y_origin " << fillPoly -> y_origin << ".\n"
+ << logofs_flush;
+ #endif
+}
+
+void FillPolyStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ //
+ // Fields shape, mode.
+ //
+
+ md5_append(md5_state_, buffer + 12, 2);
+}
+
+void FillPolyStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ FillPolyMessage *fillPoly = (FillPolyMessage *) message;
+ FillPolyMessage *cachedFillPoly = (FillPolyMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << fillPoly -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(fillPoly -> drawable, clientCache -> drawableCache);
+
+ cachedFillPoly -> drawable = fillPoly -> drawable;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << fillPoly -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(fillPoly -> gcontext, clientCache -> gcCache);
+
+ cachedFillPoly -> gcontext = fillPoly -> gcontext;
+
+ if (control -> isProtoStep8() == 1 &&
+ fillPoly -> size_ >= dataOffset)
+ {
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << fillPoly -> x_origin
+ << " as x_origin field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(fillPoly -> x_origin, 16,
+ *clientCache -> fillPolyXAbsCache[0], 8);
+
+ cachedFillPoly -> x_origin = fillPoly -> x_origin;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << fillPoly -> y_origin
+ << " as y_origin field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(fillPoly -> y_origin, 16,
+ *clientCache -> fillPolyYAbsCache[0], 8);
+
+ cachedFillPoly -> y_origin = fillPoly -> y_origin;
+ }
+}
+
+void FillPolyStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ FillPolyMessage *fillPoly = (FillPolyMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeXidValue(fillPoly -> drawable, clientCache -> drawableCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded value " << fillPoly -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(fillPoly -> gcontext, clientCache -> gcCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded value " << fillPoly -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ if (control -> isProtoStep8() == 1 &&
+ fillPoly -> size_ >= dataOffset)
+ {
+ unsigned int value;
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache -> fillPolyXAbsCache[0], 8);
+
+ fillPoly -> x_origin = value;
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded value " << fillPoly -> x_origin
+ << " as x_origin field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache -> fillPolyYAbsCache[0], 8);
+
+ fillPoly -> y_origin = value;
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded value " << fillPoly -> y_origin
+ << " as y_origin field.\n" << logofs_flush;
+ #endif
+ }
+}
+
+
diff --git a/nxcomp/FillPoly.h b/nxcomp/FillPoly.h
new file mode 100644
index 000000000..f33968494
--- /dev/null
+++ b/nxcomp/FillPoly.h
@@ -0,0 +1,198 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef FillPoly_H
+#define FillPoly_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define FILLPOLY_ENABLE_CACHE 1
+#define FILLPOLY_ENABLE_DATA 0
+#define FILLPOLY_ENABLE_SPLIT 0
+#define FILLPOLY_ENABLE_COMPRESS 0
+
+#define FILLPOLY_DATA_LIMIT 512
+#define FILLPOLY_DATA_OFFSET 16
+
+#define FILLPOLY_CACHE_SLOTS 2000
+#define FILLPOLY_CACHE_THRESHOLD 3
+#define FILLPOLY_CACHE_LOWER_THRESHOLD 1
+
+#define FILLPOLY_DATA_OFFSET_IF_PROTO_STEP_8 20
+
+//
+// The message class.
+//
+
+class FillPolyMessage : public Message
+{
+ friend class FillPolyStore;
+
+ public:
+
+ FillPolyMessage()
+ {
+ }
+
+ ~FillPolyMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char shape;
+ unsigned char mode;
+ unsigned int drawable;
+ unsigned int gcontext;
+
+ unsigned short x_origin;
+ unsigned short y_origin;
+};
+
+class FillPolyStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ FillPolyStore() : MessageStore()
+ {
+ enableCache = FILLPOLY_ENABLE_CACHE;
+ enableData = FILLPOLY_ENABLE_DATA;
+ enableSplit = FILLPOLY_ENABLE_SPLIT;
+ enableCompress = FILLPOLY_ENABLE_COMPRESS;
+
+ dataLimit = FILLPOLY_DATA_LIMIT;
+ dataOffset = FILLPOLY_DATA_OFFSET;
+
+ if (control -> isProtoStep8() == 1)
+ {
+ dataOffset = FILLPOLY_DATA_OFFSET_IF_PROTO_STEP_8;
+ }
+
+ cacheSlots = FILLPOLY_CACHE_SLOTS;
+ cacheThreshold = FILLPOLY_CACHE_THRESHOLD;
+ cacheLowerThreshold = FILLPOLY_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~FillPolyStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "FillPoly";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_FillPoly;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(FillPolyMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new FillPolyMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new FillPolyMessage((const FillPolyMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (FillPolyMessage *) message;
+ }
+
+ virtual int identitySize(const unsigned char *buffer, unsigned int size)
+ {
+ unsigned int offset = (control -> isProtoStep8() == 1 ?
+ FILLPOLY_DATA_OFFSET_IF_PROTO_STEP_8 :
+ FILLPOLY_DATA_OFFSET);
+
+ return (size >= offset ? offset : size);
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* FillPoly_H */
diff --git a/nxcomp/Fork.cpp b/nxcomp/Fork.cpp
new file mode 100644
index 000000000..48faa2992
--- /dev/null
+++ b/nxcomp/Fork.cpp
@@ -0,0 +1,98 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include <unistd.h>
+
+#include "Fork.h"
+#include "Misc.h"
+#include "Timestamp.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Only on Cygwin, retry n times waiting a
+// given amount of milliseconds after each
+// attempt.
+//
+
+#define RETRY_LIMIT 30
+#define RETRY_TIMEOUT 1000
+
+int Fork()
+{
+ #ifdef __CYGWIN32__
+
+ int limit = RETRY_LIMIT;
+ int timeout = RETRY_TIMEOUT;
+
+ #else
+
+ int limit = 1;
+ int timeout = 0;
+
+ #endif
+
+ int pid = 0;
+
+ for (int i = 0; i < limit; i++)
+ {
+ #ifdef TEST
+ *logofs << "Fork: Trying at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // It could optionally try again only if the
+ // error code is 11, 'Resource temporarily
+ // unavailable'.
+ //
+
+ if ((pid = fork()) >= 0)
+ {
+ break;
+ }
+ else if (i < limit - 1)
+ {
+ #ifdef WARNING
+ *logofs << "Fork: WARNING! Function fork failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'. Retrying...\n" << logofs_flush;
+ #endif
+
+ usleep(timeout * 1000);
+ }
+ }
+
+ #ifdef TEST
+
+ if (pid <= 0)
+ {
+ *logofs << "Fork: Returning at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ }
+
+ #endif
+
+ return pid;
+}
diff --git a/nxcomp/Fork.h b/nxcomp/Fork.h
new file mode 100644
index 000000000..9df9f4041
--- /dev/null
+++ b/nxcomp/Fork.h
@@ -0,0 +1,23 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+//
+// Try again if the fork() fails, as it can happen
+// often on Cygwin.
+//
+
+extern int Fork();
diff --git a/nxcomp/FreeCache.h b/nxcomp/FreeCache.h
new file mode 100644
index 000000000..01fa42cd8
--- /dev/null
+++ b/nxcomp/FreeCache.h
@@ -0,0 +1,34 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef FreeCache_H
+#define FreeCache_H
+
+#include "IntCache.h"
+
+class FreeCache : public IntCache
+{
+ public:
+
+ FreeCache(unsigned int size)
+
+ : IntCache(size)
+ {
+ }
+};
+
+#endif /* FreeCache_H */
diff --git a/nxcomp/GenericChannel.cpp b/nxcomp/GenericChannel.cpp
new file mode 100644
index 000000000..641ad36d4
--- /dev/null
+++ b/nxcomp/GenericChannel.cpp
@@ -0,0 +1,483 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "GenericChannel.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "StaticCompressor.h"
+
+#include "Statistics.h"
+#include "Proxy.h"
+
+extern Proxy *proxy;
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Log the important tracepoints related
+// to writing packets to the peer proxy.
+//
+
+#undef FLUSH
+
+//
+// Define this to log when a channel
+// is created or destroyed.
+//
+
+#undef REFERENCES
+
+//
+// Here are the static members.
+//
+
+#ifdef REFERENCES
+
+int GenericChannel::references_ = 0;
+
+#endif
+
+GenericChannel::GenericChannel(Transport *transport, StaticCompressor *compressor)
+
+ : Channel(transport, compressor), readBuffer_(transport_, this)
+{
+ #ifdef REFERENCES
+ *logofs << "GenericChannel: Created new object at "
+ << this << " for FD#" << fd_ << " out of "
+ << ++references_ << " allocated channels.\n"
+ << logofs_flush;
+ #endif
+}
+
+GenericChannel::~GenericChannel()
+{
+ #ifdef REFERENCES
+ *logofs << "GenericChannel: Deleted object at "
+ << this << " for FD#" << fd_ << " out of "
+ << --references_ << " allocated channels.\n"
+ << logofs_flush;
+ #endif
+}
+
+//
+// Beginning of handleRead().
+//
+
+int GenericChannel::handleRead(EncodeBuffer &encodeBuffer, const unsigned char *message,
+ unsigned int length)
+{
+ #ifdef TEST
+ *logofs << "handleRead: Called for FD#" << fd_
+ << " with " << encodeBuffer.getLength()
+ << " bytes already encoded.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Pointer to located message and
+ // its size in bytes.
+ //
+
+ const unsigned char *inputMessage;
+ unsigned int inputLength;
+
+ //
+ // Tag message as generic data in compression
+ // routine. Opcode is not actually transferred
+ // over the network.
+ //
+
+ unsigned char inputOpcode = X_NXInternalGenericData;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleRead: Trying to read from FD#"
+ << fd_ << " at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ int result = readBuffer_.readMessage();
+
+ #ifdef DEBUG
+ *logofs << "handleRead: Read result on FD#" << fd_
+ << " is " << result << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (result < 0)
+ {
+ //
+ // Let the proxy close the channel.
+ //
+
+ return -1;
+ }
+ else if (result == 0)
+ {
+ #if defined(TEST) || defined(INFO)
+
+ *logofs << "handleRead: PANIC! No data read from FD#"
+ << fd_ << " while encoding messages.\n"
+ << logofs_flush;
+
+ HandleCleanup();
+
+ #endif
+
+ return 0;
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+ *logofs << "handleRead: Encoding messages for FD#" << fd_
+ << " with " << readBuffer_.getLength() << " bytes "
+ << "in the buffer.\n" << logofs_flush;
+ #endif
+
+ //
+ // Divide the available data in multiple
+ // messages and encode them one by one.
+ //
+
+ if (proxy -> handleAsyncSwitch(fd_) < 0)
+ {
+ return -1;
+ }
+
+ while ((inputMessage = readBuffer_.getMessage(inputLength)) != NULL)
+ {
+ encodeBuffer.encodeValue(inputLength, 32, 14);
+
+ if (isCompressed() == 1)
+ {
+ unsigned int compressedDataSize = 0;
+ unsigned char *compressedData = NULL;
+
+ if (handleCompress(encodeBuffer, inputOpcode, 0,
+ inputMessage, inputLength, compressedData,
+ compressedDataSize) < 0)
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ encodeBuffer.encodeMemory(inputMessage, inputLength);
+ }
+
+ int bits = encodeBuffer.diffBits();
+
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << "handleRead: Handled generic data for FD#" << fd_
+ << ". " << inputLength << " bytes in, " << bits << " bits ("
+ << ((float) bits) / 8 << " bytes) out.\n" << logofs_flush;
+ #endif
+
+ addProtocolBits(inputLength << 3, bits);
+
+ if (isPrioritized() == 1)
+ {
+ priority_++;
+ }
+
+ } // End of while ((inputMessage = readBuffer_.getMessage(inputLength)) != NULL) ...
+
+ //
+ // All data has been read from the read buffer.
+ // We still need to mark the end of the encode
+ // buffer just before sending the frame. This
+ // allows us to accomodate multiple reads in
+ // a single frame.
+ //
+
+ if (priority_ > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleRead: WARNING! Requesting flush "
+ << "because of " << priority_ << " prioritized "
+ << "messages for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (proxy -> handleAsyncPriority() < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Reset the priority flag.
+ //
+
+ priority_ = 0;
+ }
+
+ //
+ // Flush if we produced enough data.
+ //
+
+ if (proxy -> canAsyncFlush() == 1)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleRead: WARNING! Requesting flush "
+ << "because of enough data or timeout on the "
+ << "proxy link.\n" << logofs_flush;
+ #endif
+
+ if (proxy -> handleAsyncFlush() < 0)
+ {
+ return -1;
+ }
+ }
+
+ #if defined(TEST) || defined(INFO)
+
+ if (transport_ -> pending() != 0 ||
+ readBuffer_.checkMessage() != 0)
+ {
+ *logofs << "handleRead: PANIC! Buffer for X descriptor FD#"
+ << fd_ << " has " << transport_ -> pending()
+ << " bytes to read.\n" << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ //
+ // Reset the read buffer.
+ //
+
+ readBuffer_.fullReset();
+
+ return 1;
+}
+
+//
+// End of handleRead().
+//
+
+//
+// Beginning of handleWrite().
+//
+
+int GenericChannel::handleWrite(const unsigned char *message, unsigned int length)
+{
+ #ifdef TEST
+ *logofs << "handleWrite: Called for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Create the buffer from which to
+ // decode messages.
+ //
+
+ DecodeBuffer decodeBuffer(message, length);
+
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+ *logofs << "handleWrite: Decoding messages for FD#" << fd_
+ << " with " << length << " bytes in the buffer.\n"
+ << logofs_flush;
+ #endif
+
+ unsigned char *outputMessage;
+ unsigned int outputLength;
+
+ //
+ // Tag message as generic data
+ // in decompression.
+ //
+
+ unsigned char outputOpcode = X_NXInternalGenericData;
+
+ for (;;)
+ {
+ decodeBuffer.decodeValue(outputLength, 32, 14);
+
+ if (outputLength == 0)
+ {
+ break;
+ }
+
+ if (isCompressed() == 1)
+ {
+ if (writeBuffer_.getAvailable() < outputLength ||
+ (int) outputLength >= control -> TransportFlushBufferSize)
+ {
+ #ifdef DEBUG
+ *logofs << "handleWrite: Using scratch buffer for "
+ << "generic data with size " << outputLength << " and "
+ << writeBuffer_.getLength() << " bytes in buffer.\n"
+ << logofs_flush;
+ #endif
+
+ outputMessage = writeBuffer_.addScratchMessage(outputLength);
+ }
+ else
+ {
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ }
+
+ const unsigned char *compressedData = NULL;
+ unsigned int compressedDataSize = 0;
+
+ int decompressed = handleDecompress(decodeBuffer, outputOpcode, 0,
+ outputMessage, outputLength, compressedData,
+ compressedDataSize);
+ if (decompressed < 0)
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ #ifdef DEBUG
+ *logofs << "handleWrite: Using scratch buffer for "
+ << "generic data with size " << outputLength << " and "
+ << writeBuffer_.getLength() << " bytes in buffer.\n"
+ << logofs_flush;
+ #endif
+
+ writeBuffer_.addScratchMessage((unsigned char *)
+ decodeBuffer.decodeMemory(outputLength), outputLength);
+ }
+
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << "handleWrite: Handled generic data for FD#" << fd_
+ << ". " << outputLength << " bytes out.\n"
+ << logofs_flush;
+ #endif
+
+ handleFlush(flush_if_needed);
+ }
+
+ //
+ // Write any remaining data to socket.
+ //
+
+ if (handleFlush(flush_if_any) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+//
+// End of handleWrite().
+//
+
+//
+// Other members.
+//
+
+int GenericChannel::handleCompletion(EncodeBuffer &encodeBuffer)
+{
+ //
+ // Add the bits telling to the remote
+ // that all data in the frame has been
+ // encoded.
+ //
+
+ if (encodeBuffer.getLength() > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleCompletion: Writing completion bits with "
+ << encodeBuffer.getLength() << " bytes encoded "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeValue(0, 32, 14);
+
+ return 1;
+ }
+ #if defined(TEST) || defined(INFO)
+ else
+ {
+ *logofs << "handleCompletion: PANIC! No completion to write "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+
+ HandleCleanup();
+ }
+ #endif
+
+ return 0;
+}
+
+int GenericChannel::handleConfiguration()
+{
+ #ifdef TEST
+ *logofs << "GenericChannel: Setting new buffer parameters.\n"
+ << logofs_flush;
+ #endif
+
+ readBuffer_.setSize(control -> GenericInitialReadSize,
+ control -> GenericMaximumBufferSize);
+
+ writeBuffer_.setSize(control -> TransportGenericBufferSize,
+ control -> TransportGenericBufferThreshold,
+ control -> TransportMaximumBufferSize);
+
+ transport_ -> setSize(control -> TransportGenericBufferSize,
+ control -> TransportGenericBufferThreshold,
+ control -> TransportMaximumBufferSize);
+
+ return 1;
+}
+
+int GenericChannel::handleFinish()
+{
+ #ifdef TEST
+ *logofs << "GenericChannel: Finishing channel for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ congestion_ = 0;
+ priority_ = 0;
+
+ finish_ = 1;
+
+ transport_ -> fullReset();
+
+ return 1;
+}
+
+int GenericChannel::setReferences()
+{
+ #ifdef TEST
+ *logofs << "GenericChannel: Initializing the static "
+ << "members for the generic channels.\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef REFERENCES
+
+ references_ = 0;
+
+ #endif
+
+ return 1;
+}
diff --git a/nxcomp/GenericChannel.h b/nxcomp/GenericChannel.h
new file mode 100644
index 000000000..ba4f1e7e8
--- /dev/null
+++ b/nxcomp/GenericChannel.h
@@ -0,0 +1,448 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef GenericChannel_H
+#define GenericChannel_H
+
+#include "Channel.h"
+
+#include "Statistics.h"
+
+#include "GenericReadBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#undef TEST
+#undef DEBUG
+
+//
+// Define this to log a line when a channel
+// is created or destroyed.
+//
+
+#undef REFERENCES
+
+//
+// This class implements the client
+// side compression of X protocol.
+//
+
+class GenericChannel : public Channel
+{
+ public:
+
+ GenericChannel(Transport *transport, StaticCompressor *compressor);
+
+ virtual ~GenericChannel();
+
+ virtual int handleRead(EncodeBuffer &encodeBuffer, const unsigned char *message,
+ unsigned int length);
+
+ virtual int handleWrite(const unsigned char *message, unsigned int length);
+
+
+ virtual int handleSplit(EncodeBuffer &encodeBuffer, MessageStore *store,
+ T_store_action action, int position, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size)
+ {
+ return 0;
+ }
+
+ virtual int handleSplit(DecodeBuffer &decodeBuffer, MessageStore *store,
+ T_store_action action, int position, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+ {
+ return 0;
+ }
+
+
+ virtual int handleSplit(EncodeBuffer &encodeBuffer)
+ {
+ return 0;
+ }
+
+ virtual int handleSplit(DecodeBuffer &decodeBuffer)
+ {
+ return 0;
+ }
+
+ virtual int handleSplitEvent(EncodeBuffer &encodeBuffer, Split *split)
+ {
+ return 0;
+ }
+
+ virtual int handleSplitEvent(DecodeBuffer &decodeBuffer)
+ {
+ return 0;
+ }
+
+ virtual int handleMotion(EncodeBuffer &encodeBuffer)
+ {
+ return 0;
+ }
+
+ virtual int handleCompletion(EncodeBuffer &encodeBuffer);
+
+ virtual int handleConfiguration();
+
+ virtual int handleFinish();
+
+ virtual int handleAsyncEvents()
+ {
+ return 0;
+ }
+
+ virtual int needSplit() const
+ {
+ return 0;
+ }
+
+ virtual int needMotion() const
+ {
+ return 0;
+ }
+
+ virtual T_channel_type getType() const = 0;
+
+ //
+ // Initialize the static members.
+ //
+
+ static int setReferences();
+
+ protected:
+
+ //
+ // Generic channels are considered to be
+ // in congestion state as soon as the
+ // socket is blocked for write.
+ //
+
+ virtual int isCongested()
+ {
+ return (transport_ -> blocked() == 1);
+ }
+
+ virtual int isReliable()
+ {
+ return 0;
+ }
+
+ //
+ // Model generic channels' encoding and
+ // decoding policy.
+ //
+
+ virtual int isCompressed() = 0;
+
+ //
+ // Return true if the channel contains
+ // time sensitive data.
+ //
+
+ virtual int isPrioritized() = 0;
+
+ //
+ // Record the protocol bits for the
+ // specific service.
+ //
+
+ virtual void addProtocolBits(unsigned int bitsIn, unsigned int bitsOut) = 0;
+
+ //
+ // Channel's own read buffer.
+ //
+
+ GenericReadBuffer readBuffer_;
+
+ private:
+
+ //
+ // Keep track of object's creation
+ // and deletion.
+ //
+
+ #ifdef REFERENCES
+
+ static int references_;
+
+ #endif
+};
+
+class CupsChannel : public GenericChannel
+{
+ public:
+
+ CupsChannel(Transport *transport, StaticCompressor *compressor)
+
+ : GenericChannel(transport, compressor)
+ {
+ }
+
+ virtual ~CupsChannel()
+ {
+ }
+
+ protected:
+
+ virtual T_channel_type getType() const
+ {
+ return channel_cups;
+ }
+
+ virtual int isCompressed()
+ {
+ if (control -> isProtoStep8() == 0)
+ {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ virtual int isPrioritized()
+ {
+ return 0;
+ }
+
+ virtual void addProtocolBits(unsigned int bitsIn,
+ unsigned int bitsOut)
+ {
+ statistics -> addCupsBits(bitsIn, bitsOut);
+ }
+};
+
+class SmbChannel : public GenericChannel
+{
+ public:
+
+ SmbChannel(Transport *transport, StaticCompressor *compressor)
+
+ : GenericChannel(transport, compressor)
+ {
+ }
+
+ virtual ~SmbChannel()
+ {
+ }
+
+ protected:
+
+ virtual T_channel_type getType() const
+ {
+ return channel_smb;
+ }
+
+ virtual int isCompressed()
+ {
+ if (control -> isProtoStep8() == 0)
+ {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ virtual int isPrioritized()
+ {
+ return 0;
+ }
+
+ virtual void addProtocolBits(unsigned int bitsIn,
+ unsigned int bitsOut)
+ {
+ statistics -> addSmbBits(bitsIn, bitsOut);
+ }
+};
+
+class MediaChannel : public GenericChannel
+{
+ public:
+
+ MediaChannel(Transport *transport, StaticCompressor *compressor)
+
+ : GenericChannel(transport, compressor)
+ {
+ }
+
+ virtual ~MediaChannel()
+ {
+ }
+
+ protected:
+
+ virtual T_channel_type getType() const
+ {
+ return channel_media;
+ }
+
+ //
+ // Don't try to compress the media data.
+ //
+
+ virtual int isCompressed()
+ {
+ return 0;
+ }
+
+ //
+ // Reduce the latency of media channels
+ // by setting them as prioritized, even
+ // if this will take away bandwidth from
+ // the X channels.
+ //
+
+ virtual int isPrioritized()
+ {
+ return 1;
+ }
+
+ virtual void addProtocolBits(unsigned int bitsIn,
+ unsigned int bitsOut)
+ {
+ statistics -> addMediaBits(bitsIn, bitsOut);
+ }
+};
+
+class HttpChannel : public GenericChannel
+{
+ public:
+
+ HttpChannel(Transport *transport, StaticCompressor *compressor)
+
+ : GenericChannel(transport, compressor)
+ {
+ }
+
+ virtual ~HttpChannel()
+ {
+ }
+
+ protected:
+
+ virtual T_channel_type getType() const
+ {
+ return channel_http;
+ }
+
+ virtual int isCompressed()
+ {
+ if (control -> isProtoStep8() == 0)
+ {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ virtual int isPrioritized()
+ {
+ return 0;
+ }
+
+ virtual void addProtocolBits(unsigned int bitsIn,
+ unsigned int bitsOut)
+ {
+ statistics -> addHttpBits(bitsIn, bitsOut);
+ }
+};
+
+class FontChannel : public GenericChannel
+{
+ public:
+
+ FontChannel(Transport *transport, StaticCompressor *compressor)
+
+ : GenericChannel(transport, compressor)
+ {
+ }
+
+ virtual ~FontChannel()
+ {
+ }
+
+ protected:
+
+ virtual T_channel_type getType() const
+ {
+ return channel_font;
+ }
+
+ virtual int isCompressed()
+ {
+ if (control -> isProtoStep8() == 0)
+ {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ virtual int isPrioritized()
+ {
+ return 1;
+ }
+
+ virtual void addProtocolBits(unsigned int bitsIn,
+ unsigned int bitsOut)
+ {
+ statistics -> addFontBits(bitsIn, bitsOut);
+ }
+};
+
+class SlaveChannel : public GenericChannel
+{
+ public:
+
+ SlaveChannel(Transport *transport, StaticCompressor *compressor)
+
+ : GenericChannel(transport, compressor)
+ {
+ }
+
+ virtual ~SlaveChannel()
+ {
+ }
+
+ protected:
+
+ virtual T_channel_type getType() const
+ {
+ return channel_slave;
+ }
+
+ virtual int isCompressed()
+ {
+ return 0;
+ }
+
+ virtual int isPrioritized()
+ {
+ return 0;
+ }
+
+ virtual void addProtocolBits(unsigned int bitsIn,
+ unsigned int bitsOut)
+ {
+ statistics -> addSlaveBits(bitsIn, bitsOut);
+ }
+};
+
+#endif /* GenericChannel_H */
diff --git a/nxcomp/GenericReadBuffer.cpp b/nxcomp/GenericReadBuffer.cpp
new file mode 100644
index 000000000..b7b6d93f4
--- /dev/null
+++ b/nxcomp/GenericReadBuffer.cpp
@@ -0,0 +1,70 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "GenericReadBuffer.h"
+
+#include "GenericChannel.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+unsigned int GenericReadBuffer::suggestedLength(unsigned int pendingLength)
+{
+ //
+ // Always read the initial read size.
+ //
+
+ return 0;
+}
+
+int GenericReadBuffer::locateMessage(const unsigned char *start,
+ const unsigned char *end,
+ unsigned int &controlLength,
+ unsigned int &dataLength,
+ unsigned int &trailerLength)
+{
+ //
+ // We don't care about the endianess
+ // in generic channels.
+ //
+
+ unsigned int size = end - start;
+
+ #ifdef TEST
+ *logofs << "GenericReadBuffer: Locating message for FD#"
+ << transport_ -> fd() << " with " << size
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ if (size == 0)
+ {
+ remaining_ = 1;
+
+ return 0;
+ }
+
+ dataLength = size;
+
+ controlLength = 0;
+ trailerLength = 0;
+
+ remaining_ = 0;
+
+ return 1;
+}
diff --git a/nxcomp/GenericReadBuffer.h b/nxcomp/GenericReadBuffer.h
new file mode 100644
index 000000000..6b1fdd1fa
--- /dev/null
+++ b/nxcomp/GenericReadBuffer.h
@@ -0,0 +1,53 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef GenericReadBuffer_H
+#define GenericReadBuffer_H
+
+#include "ReadBuffer.h"
+#include "Control.h"
+
+class GenericChannel;
+
+class GenericReadBuffer : public ReadBuffer
+{
+ public:
+
+ GenericReadBuffer(Transport *transport, GenericChannel *channel)
+
+ : ReadBuffer(transport), channel_(channel)
+ {
+ }
+
+ virtual ~GenericReadBuffer()
+ {
+ }
+
+ protected:
+
+ virtual unsigned int suggestedLength(unsigned int pendingLength);
+
+ virtual int locateMessage(const unsigned char *start,
+ const unsigned char *end,
+ unsigned int &controlLength,
+ unsigned int &dataLength,
+ unsigned int &trailerLength);
+
+ GenericChannel *channel_;
+};
+
+#endif /* GenericReadBuffer_H */
diff --git a/nxcomp/GenericReply.cpp b/nxcomp/GenericReply.cpp
new file mode 100644
index 000000000..9daccc566
--- /dev/null
+++ b/nxcomp/GenericReply.cpp
@@ -0,0 +1,293 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "GenericReply.h"
+
+#include "ServerCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "WriteBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Constructors and destructors.
+//
+
+GenericReplyStore::GenericReplyStore(StaticCompressor *compressor)
+
+ : MessageStore(compressor)
+{
+ enableCache = GENERICREPLY_ENABLE_CACHE;
+ enableData = GENERICREPLY_ENABLE_DATA;
+ enableSplit = GENERICREPLY_ENABLE_SPLIT;
+ enableCompress = GENERICREPLY_ENABLE_COMPRESS;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ enableCompress = GENERICREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7;
+ }
+
+ dataLimit = GENERICREPLY_DATA_LIMIT;
+ dataOffset = GENERICREPLY_DATA_OFFSET;
+
+ cacheSlots = GENERICREPLY_CACHE_SLOTS;
+ cacheThreshold = GENERICREPLY_CACHE_THRESHOLD;
+ cacheLowerThreshold = GENERICREPLY_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+GenericReplyStore::~GenericReplyStore()
+{
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int GenericReplyStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ ServerCache *serverCache = (ServerCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding full message identity.\n"
+ << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeValue(GetULONG(buffer + 4, bigEndian), 32, 15);
+
+ encodeBuffer.encodeCachedValue(*(buffer + 1), 8,
+ serverCache -> genericReplyCharCache);
+
+ for (unsigned int i = 0; i < 6; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + i * 4 + 8, bigEndian), 32,
+ *serverCache -> genericReplyIntCache[i]);
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoded full message identity.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int GenericReplyStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const
+{
+ ServerCache *serverCache = (ServerCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding full message identity.\n"
+ << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeValue(size, 32, 15);
+
+ size = 32 + (size << 2);
+
+ buffer = writeBuffer -> addMessage(size);
+
+ decodeBuffer.decodeCachedValue(*(buffer + 1), 8,
+ serverCache -> genericReplyCharCache);
+
+ unsigned int value;
+
+ for (unsigned int i = 0; i < 6; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 32,
+ *serverCache -> genericReplyIntCache[i]);
+
+ PutULONG(value, buffer + i * 4 + 8, bigEndian);
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded full message identity.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int GenericReplyStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ GenericReplyMessage *genericReply = (GenericReplyMessage *) message;
+
+ genericReply -> byte_data = *(buffer + 1);
+
+ for (int i = 0; i < 12; i++)
+ {
+ genericReply -> short_data[i] = GetUINT(buffer + i * 2 + 8, bigEndian);
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at "
+ << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int GenericReplyStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ GenericReplyMessage *genericReply = (GenericReplyMessage *) message;
+
+ *(buffer + 1) = genericReply -> byte_data;
+
+ for (int i = 0; i < 12; i++)
+ {
+ PutUINT(genericReply -> short_data[i], buffer + i * 2 + 8, bigEndian);
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at "
+ << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void GenericReplyStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ GenericReplyMessage *genericReply = (GenericReplyMessage *) message;
+
+ *logofs << name() << ": Identity byte_data "
+ << (unsigned) genericReply -> byte_data;
+
+ for (int i = 0; i < 12; i++)
+ {
+ *logofs << ", short_data[" << i << "]"
+ << (unsigned) genericReply -> short_data[i];
+ }
+
+ *logofs << ", size " << genericReply -> size_ << ".\n";
+
+ #endif
+}
+
+void GenericReplyStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+}
+
+void GenericReplyStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ //
+ // Encode the variant part.
+ //
+
+ GenericReplyMessage *genericReply = (GenericReplyMessage *) message;
+ GenericReplyMessage *cachedGenericReply = (GenericReplyMessage *) cachedMessage;
+
+ ServerCache *serverCache = (ServerCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value "
+ << (unsigned int) genericReply -> byte_data
+ << " as byte_data field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(genericReply -> byte_data, 8,
+ serverCache -> genericReplyCharCache);
+
+ cachedGenericReply -> byte_data = genericReply -> byte_data;
+
+ for (unsigned int i = 0; i < 12; i++)
+ {
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << genericReply -> short_data[i]
+ << " as short_data[" << i << "] field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(genericReply -> short_data[i], 16,
+ *serverCache -> genericReplyIntCache[i]);
+
+ cachedGenericReply -> short_data[i] = genericReply -> short_data[i];
+ }
+}
+
+void GenericReplyStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ //
+ // Decode the variant part.
+ //
+
+ GenericReplyMessage *genericReply = (GenericReplyMessage *) message;
+
+ ServerCache *serverCache = (ServerCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(genericReply -> byte_data, 8,
+ serverCache -> genericReplyCharCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded value "
+ << (unsigned int) genericReply -> byte_data
+ << " as byte_data field.\n" << logofs_flush;
+ #endif
+
+ unsigned int value;
+
+ for (unsigned int i = 0; i < 12; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *serverCache -> genericReplyIntCache[i]);
+
+ genericReply -> short_data[i] = (unsigned short) value;
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded value " << genericReply -> short_data[i]
+ << " as short_data[" << i << "] field.\n" << logofs_flush;
+ #endif
+ }
+}
diff --git a/nxcomp/GenericReply.h b/nxcomp/GenericReply.h
new file mode 100644
index 000000000..de97b86d0
--- /dev/null
+++ b/nxcomp/GenericReply.h
@@ -0,0 +1,154 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef GenericReply_H
+#define GenericReply_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define GENERICREPLY_ENABLE_CACHE 1
+#define GENERICREPLY_ENABLE_DATA 1
+#define GENERICREPLY_ENABLE_SPLIT 0
+#define GENERICREPLY_ENABLE_COMPRESS 1
+
+#define GENERICREPLY_DATA_LIMIT 1048576 - 32
+#define GENERICREPLY_DATA_OFFSET 32
+
+#define GENERICREPLY_CACHE_SLOTS 400
+#define GENERICREPLY_CACHE_THRESHOLD 5
+#define GENERICREPLY_CACHE_LOWER_THRESHOLD 1
+
+#define GENERICREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7 0
+
+//
+// The message class.
+//
+
+class GenericReplyMessage : public Message
+{
+ friend class GenericReplyStore;
+
+ public:
+
+ GenericReplyMessage()
+ {
+ }
+
+ ~GenericReplyMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute the
+ // 'identity' part of the message. Starting
+ // from protocol level 3 we use short data
+ // to increase cache efficiency.
+ //
+
+ private:
+
+ unsigned char byte_data;
+ unsigned int int_data[6];
+ unsigned short short_data[12];
+};
+
+class GenericReplyStore : public MessageStore
+{
+ public:
+
+ GenericReplyStore(StaticCompressor *compressor);
+
+ virtual ~GenericReplyStore();
+
+ virtual const char *name() const
+ {
+ return "GenericReply";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_NXInternalGenericReply;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(GenericReplyMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ protected:
+
+ virtual Message *create() const
+ {
+ return new GenericReplyMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new GenericReplyMessage((const GenericReplyMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (GenericReplyMessage *) message;
+ }
+
+ virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const;
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* GenericReply_H */
diff --git a/nxcomp/GenericRequest.cpp b/nxcomp/GenericRequest.cpp
new file mode 100644
index 000000000..40077291b
--- /dev/null
+++ b/nxcomp/GenericRequest.cpp
@@ -0,0 +1,327 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "GenericRequest.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "WriteBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Constructors and destructors.
+//
+
+GenericRequestStore::GenericRequestStore(StaticCompressor *compressor)
+
+ : MessageStore(compressor)
+{
+ enableCache = GENERICREQUEST_ENABLE_CACHE;
+ enableData = GENERICREQUEST_ENABLE_DATA;
+ enableSplit = GENERICREQUEST_ENABLE_SPLIT;
+ enableCompress = GENERICREQUEST_ENABLE_COMPRESS;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ enableCompress = GENERICREQUEST_ENABLE_COMPRESS_IF_PROTO_STEP_7;
+
+ enableCompress = 0;
+ }
+
+ dataLimit = GENERICREQUEST_DATA_LIMIT;
+ dataOffset = GENERICREQUEST_DATA_OFFSET;
+
+ cacheSlots = GENERICREQUEST_CACHE_SLOTS;
+ cacheThreshold = GENERICREQUEST_CACHE_THRESHOLD;
+ cacheLowerThreshold = GENERICREQUEST_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+GenericRequestStore::~GenericRequestStore()
+{
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int GenericRequestStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding full message identity.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeValue(size >> 2, 16, 10);
+
+ encodeBuffer.encodeCachedValue(*(buffer + 1), 8,
+ clientCache -> genericRequestOpcodeCache);
+
+ for (unsigned int i = 0; i < 8 && (i * 2 + 4) < size; i++)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding data[" << i << "] "
+ << "at position " << i * 2 + 4 << " with value "
+ << GetUINT(buffer + (i * 2) + 4, bigEndian)
+ << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(GetUINT(buffer + (i * 2) + 4, bigEndian), 16,
+ *clientCache -> genericRequestDataCache[i]);
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int GenericRequestStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding full message identity.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeValue(size, 16, 10);
+
+ size <<= 2;
+
+ buffer = writeBuffer -> addMessage(size);
+
+ decodeBuffer.decodeCachedValue(*(buffer + 1), 8,
+ clientCache -> genericRequestOpcodeCache);
+
+ unsigned int value;
+
+ for (unsigned int i = 0; i < 8 && (i * 2 + 4) < size; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache -> genericRequestDataCache[i]);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding data[" << i << "] "
+ << "at position " << i * 2 + 4 << " with value "
+ << value << ".\n" << logofs_flush;
+ #endif
+
+ PutUINT(value, buffer + 4 + (i * 2), bigEndian);
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int GenericRequestStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ GenericRequestMessage *genericRequest = (GenericRequestMessage *) message;
+
+ genericRequest -> opcode = *(buffer + 1);
+
+ for (unsigned int i = 0; i < 8; i++)
+ {
+ if ((i * 2 + 4) < size)
+ {
+ genericRequest -> data[i] = GetUINT(buffer + i * 2 + 4, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed data[" << i << "] "
+ << "with value " << genericRequest -> data[i]
+ << ".\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ genericRequest -> data[i] = 0;
+ }
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int GenericRequestStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ GenericRequestMessage *genericRequest = (GenericRequestMessage *) message;
+
+ *(buffer + 1) = genericRequest -> opcode;
+
+ for (unsigned int i = 0; i < 8 && (i * 2 + 4) < size; i++)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed data[" << i << "] "
+ << "with value " << genericRequest -> data[i]
+ << ".\n" << logofs_flush;
+ #endif
+
+ PutUINT(genericRequest -> data[i], buffer + i * 2 + 4, bigEndian);
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void GenericRequestStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ GenericRequestMessage *genericRequest = (GenericRequestMessage *) message;
+
+ *logofs << name() << ": Identity opcode " << (unsigned) genericRequest -> opcode;
+
+ for (int i = 0; i < 8; i++)
+ {
+ *logofs << ", data[" << i << "] " << genericRequest -> data[i];
+ }
+
+ *logofs << ", size " << genericRequest -> size_ << ".\n" << logofs_flush;
+
+ #endif
+}
+
+void GenericRequestStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ //
+ // As data offset can be beyond the real end of
+ // the message, we need to include the message's
+ // size or we will match any message whose size
+ // is less or equal to the data offset.
+ //
+
+ md5_append(md5_state_, buffer + 2, 2);
+}
+
+void GenericRequestStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ //
+ // Encode the variant part.
+ //
+
+ GenericRequestMessage *genericRequest = (GenericRequestMessage *) message;
+ GenericRequestMessage *cachedGenericRequest = (GenericRequestMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Updating value "
+ << (unsigned) genericRequest -> opcode
+ << " as opcode field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue((unsigned int) genericRequest -> opcode, 8,
+ clientCache -> genericRequestOpcodeCache);
+
+ cachedGenericRequest -> opcode = genericRequest -> opcode;
+
+ for (int i = 0; i < 8 && (i * 2 + 4) < genericRequest -> size_; i++)
+ {
+ #ifdef TEST
+ *logofs << name() << ": Updating data[" << i << "] "
+ << "with value " << genericRequest -> data[i]
+ << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue((unsigned int) genericRequest -> data[i], 16,
+ *clientCache -> genericRequestDataCache[i]);
+
+ cachedGenericRequest -> data[i] = genericRequest -> data[i];
+ }
+}
+
+void GenericRequestStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ GenericRequestMessage *genericRequest = (GenericRequestMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(genericRequest -> opcode, 8,
+ clientCache -> genericRequestOpcodeCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Updated value "
+ << (unsigned) genericRequest -> opcode
+ << " as opcode field.\n" << logofs_flush;
+ #endif
+
+ unsigned int value;
+
+ for (int i = 0; i < 8 && (i * 2 + 4) < genericRequest -> size_; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache -> genericRequestDataCache[i]);
+
+ genericRequest -> data[i] = (unsigned short) value;
+
+ #ifdef TEST
+ *logofs << name() << ": Updated data[" << i << "] "
+ << "with value " << genericRequest -> data[i]
+ << ".\n" << logofs_flush;
+ #endif
+ }
+}
diff --git a/nxcomp/GenericRequest.h b/nxcomp/GenericRequest.h
new file mode 100644
index 000000000..3175fc2ba
--- /dev/null
+++ b/nxcomp/GenericRequest.h
@@ -0,0 +1,153 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef GenericRequest_H
+#define GenericRequest_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define GENERICREQUEST_ENABLE_CACHE 1
+#define GENERICREQUEST_ENABLE_DATA 1
+#define GENERICREQUEST_ENABLE_SPLIT 0
+#define GENERICREQUEST_ENABLE_COMPRESS 1
+
+#define GENERICREQUEST_DATA_LIMIT 262144 - 20
+#define GENERICREQUEST_DATA_OFFSET 20
+
+#define GENERICREQUEST_CACHE_SLOTS 400
+#define GENERICREQUEST_CACHE_THRESHOLD 5
+#define GENERICREQUEST_CACHE_LOWER_THRESHOLD 1
+
+#define GENERICREQUEST_ENABLE_COMPRESS_IF_PROTO_STEP_7 0
+
+//
+// The message class.
+//
+
+class GenericRequestMessage : public Message
+{
+ friend class GenericRequestStore;
+
+ public:
+
+ GenericRequestMessage()
+ {
+ }
+
+ ~GenericRequestMessage()
+ {
+ }
+
+ //
+ // Note that we consider for this message a data offset
+ // of 4 (or 20 starting from protocol 3). Bytes from 9
+ // to 20, if present, are taken as part of identity and
+ // encoded through an array of int caches.
+ //
+
+ private:
+
+ unsigned char opcode;
+ unsigned short data[8];
+};
+
+class GenericRequestStore : public MessageStore
+{
+ public:
+
+ GenericRequestStore(StaticCompressor *compressor);
+
+ virtual ~GenericRequestStore();
+
+ virtual const char *name() const
+ {
+ return "GenericRequest";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_NXInternalGenericRequest;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(GenericRequestMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new GenericRequestMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new GenericRequestMessage((const GenericRequestMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (GenericRequestMessage *) message;
+ }
+
+ virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const;
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* GenericRequest_H */
diff --git a/nxcomp/GetImage.cpp b/nxcomp/GetImage.cpp
new file mode 100644
index 000000000..6be574dbb
--- /dev/null
+++ b/nxcomp/GetImage.cpp
@@ -0,0 +1,165 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "GetImage.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int GetImageStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ GetImageMessage *getImage = (GetImageMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ getImage -> format = *(buffer + 1);
+
+ #ifdef TEST
+ if (getImage -> format != 1 && getImage -> format != 2)
+ {
+ *logofs << name() << ": WARNING! Dirty value " << getImage -> format
+ << " for field format.\n" << logofs_flush;
+ }
+ #endif
+
+ getImage -> drawable = GetULONG(buffer + 4, bigEndian);
+
+ getImage -> x = GetUINT(buffer + 8, bigEndian);
+ getImage -> y = GetUINT(buffer + 10, bigEndian);
+ getImage -> width = GetUINT(buffer + 12, bigEndian);
+ getImage -> height = GetUINT(buffer + 14, bigEndian);
+
+ getImage -> plane_mask = GetULONG(buffer + 16, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int GetImageStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ GetImageMessage *getImage = (GetImageMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ *(buffer + 1) = getImage -> format;
+
+ PutULONG(getImage -> drawable, buffer + 4, bigEndian);
+
+ PutUINT(getImage -> x, buffer + 8, bigEndian);
+ PutUINT(getImage -> y, buffer + 10, bigEndian);
+ PutUINT(getImage -> width, buffer + 12, bigEndian);
+ PutUINT(getImage -> height, buffer + 14, bigEndian);
+
+ PutULONG(getImage -> plane_mask, buffer + 16, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void GetImageStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ GetImageMessage *getImage = (GetImageMessage *) message;
+
+ *logofs << name() << ": Identity format " << (unsigned) getImage -> format
+ << ", drawable " << getImage -> drawable << ", x " << getImage -> x
+ << ", y " << getImage -> y << ", width " << getImage -> width
+ << ", height " << getImage -> height << ", plane_mask "
+ << getImage -> plane_mask << ", size " << getImage -> size_
+ << ".\n" << logofs_flush;
+
+ #endif
+}
+
+void GetImageStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ md5_append(md5_state_, buffer + 1, 1);
+ md5_append(md5_state_, buffer + 8, 2);
+ md5_append(md5_state_, buffer + 10, 2);
+ md5_append(md5_state_, buffer + 12, 2);
+ md5_append(md5_state_, buffer + 14, 2);
+ md5_append(md5_state_, buffer + 16, 4);
+}
+
+void GetImageStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ GetImageMessage *getImage = (GetImageMessage *) message;
+ GetImageMessage *cachedGetImage = (GetImageMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << getImage -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(getImage -> drawable, clientCache -> drawableCache);
+
+ cachedGetImage -> drawable = getImage -> drawable;
+}
+
+void GetImageStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ GetImageMessage *getImage = (GetImageMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ getImage -> drawable = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << getImage -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+}
+
diff --git a/nxcomp/GetImage.h b/nxcomp/GetImage.h
new file mode 100644
index 000000000..f48fb9079
--- /dev/null
+++ b/nxcomp/GetImage.h
@@ -0,0 +1,182 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef GetImage_H
+#define GetImage_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define GETIMAGE_ENABLE_CACHE 1
+#define GETIMAGE_ENABLE_DATA 0
+#define GETIMAGE_ENABLE_SPLIT 0
+#define GETIMAGE_ENABLE_COMPRESS 0
+
+#define GETIMAGE_DATA_LIMIT 0
+#define GETIMAGE_DATA_OFFSET 20
+
+#define GETIMAGE_CACHE_SLOTS 200
+#define GETIMAGE_CACHE_THRESHOLD 1
+#define GETIMAGE_CACHE_LOWER_THRESHOLD 0
+
+//
+// The message class.
+//
+
+class GetImageMessage : public Message
+{
+ friend class GetImageStore;
+
+ public:
+
+ GetImageMessage()
+ {
+ }
+
+ ~GetImageMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char format;
+ unsigned int drawable;
+ unsigned short int x;
+ unsigned short int y;
+ unsigned short int width;
+ unsigned short int height;
+ unsigned int plane_mask;
+};
+
+class GetImageStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ GetImageStore() : MessageStore()
+ {
+ enableCache = GETIMAGE_ENABLE_CACHE;
+ enableData = GETIMAGE_ENABLE_DATA;
+ enableSplit = GETIMAGE_ENABLE_SPLIT;
+ enableCompress = GETIMAGE_ENABLE_COMPRESS;
+
+ dataLimit = GETIMAGE_DATA_LIMIT;
+ dataOffset = GETIMAGE_DATA_OFFSET;
+
+ cacheSlots = GETIMAGE_CACHE_SLOTS;
+ cacheThreshold = GETIMAGE_CACHE_THRESHOLD;
+ cacheLowerThreshold = GETIMAGE_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~GetImageStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "GetImage";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_GetImage;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(GetImageMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new GetImageMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new GetImageMessage((const GetImageMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (GetImageMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* GetImage_H */
diff --git a/nxcomp/GetImageReply.cpp b/nxcomp/GetImageReply.cpp
new file mode 100644
index 000000000..d4bc21b5a
--- /dev/null
+++ b/nxcomp/GetImageReply.cpp
@@ -0,0 +1,185 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "GetImageReply.h"
+
+#include "ServerCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Constructors and destructors.
+//
+
+GetImageReplyStore::GetImageReplyStore(StaticCompressor *compressor)
+
+ : MessageStore(compressor)
+{
+ enableCache = GETIMAGEREPLY_ENABLE_CACHE;
+ enableData = GETIMAGEREPLY_ENABLE_DATA;
+ enableSplit = GETIMAGEREPLY_ENABLE_SPLIT;
+ enableCompress = GETIMAGEREPLY_ENABLE_COMPRESS;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ enableCompress = GETIMAGEREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7;
+ }
+
+ dataLimit = GETIMAGEREPLY_DATA_LIMIT;
+ dataOffset = GETIMAGEREPLY_DATA_OFFSET;
+
+ cacheSlots = GETIMAGEREPLY_CACHE_SLOTS;
+ cacheThreshold = GETIMAGEREPLY_CACHE_THRESHOLD;
+ cacheLowerThreshold = GETIMAGEREPLY_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+GetImageReplyStore::~GetImageReplyStore()
+{
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int GetImageReplyStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ GetImageReplyMessage *getImageReply = (GetImageReplyMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ getImageReply -> depth = *(buffer + 1);
+
+ getImageReply -> visual = GetULONG(buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at "
+ << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int GetImageReplyStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ GetImageReplyMessage *getImageReply = (GetImageReplyMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ *(buffer + 1) = getImageReply -> depth;
+
+ PutULONG(getImageReply -> visual, buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at "
+ << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void GetImageReplyStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ GetImageReplyMessage *getImageReply = (GetImageReplyMessage *) message;
+
+ *logofs << name() << ": Identity depth " << (unsigned) getImageReply -> depth
+ << ", visual " << getImageReply -> visual << ", size "
+ << getImageReply -> size_ << ".\n";
+
+ #endif
+}
+
+void GetImageReplyStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ //
+ // Field depth.
+ //
+
+ md5_append(md5_state_, buffer + 1, 1);
+}
+
+void GetImageReplyStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ //
+ // Encode the variant part.
+ //
+
+ GetImageReplyMessage *getImageReply = (GetImageReplyMessage *) message;
+
+ ServerCache *serverCache = (ServerCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << getImageReply -> visual
+ << " as visual field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(getImageReply -> visual, 29,
+ serverCache -> visualCache);
+}
+
+void GetImageReplyStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ //
+ // Decode the variant part.
+ //
+
+ GetImageReplyMessage *getImageReply = (GetImageReplyMessage *) message;
+
+ ServerCache *serverCache = (ServerCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(getImageReply -> visual, 29,
+ serverCache -> visualCache);
+}
diff --git a/nxcomp/GetImageReply.h b/nxcomp/GetImageReply.h
new file mode 100644
index 000000000..ee7b0bfa3
--- /dev/null
+++ b/nxcomp/GetImageReply.h
@@ -0,0 +1,143 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef GetImageReply_H
+#define GetImageReply_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define GETIMAGEREPLY_ENABLE_CACHE 1
+#define GETIMAGEREPLY_ENABLE_DATA 1
+#define GETIMAGEREPLY_ENABLE_SPLIT 0
+#define GETIMAGEREPLY_ENABLE_COMPRESS 1
+
+#define GETIMAGEREPLY_DATA_LIMIT 1048576 - 32
+#define GETIMAGEREPLY_DATA_OFFSET 32
+
+#define GETIMAGEREPLY_CACHE_SLOTS 1000
+#define GETIMAGEREPLY_CACHE_THRESHOLD 20
+#define GETIMAGEREPLY_CACHE_LOWER_THRESHOLD 2
+
+#define GETIMAGEREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7 0
+
+//
+// The message class.
+//
+
+class GetImageReplyMessage : public Message
+{
+ friend class GetImageReplyStore;
+
+ public:
+
+ GetImageReplyMessage()
+ {
+ }
+
+ ~GetImageReplyMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char depth;
+ unsigned int visual;
+};
+
+class GetImageReplyStore : public MessageStore
+{
+ public:
+
+ GetImageReplyStore(StaticCompressor *compressor);
+
+ virtual ~GetImageReplyStore();
+
+ virtual const char *name() const
+ {
+ return "GetImageReply";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_GetImage;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(GetImageReplyMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ protected:
+
+ virtual Message *create() const
+ {
+ return new GetImageReplyMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new GetImageReplyMessage((const GetImageReplyMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (GetImageReplyMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* GetImageReply_H */
diff --git a/nxcomp/GetProperty.cpp b/nxcomp/GetProperty.cpp
new file mode 100644
index 000000000..d358f8a6f
--- /dev/null
+++ b/nxcomp/GetProperty.cpp
@@ -0,0 +1,107 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "GetProperty.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int GetPropertyStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ GetPropertyMessage *getProperty = (GetPropertyMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ getProperty -> property_delete = *(buffer + 1);
+
+ getProperty -> window = GetULONG(buffer + 4, bigEndian);
+ getProperty -> property = GetULONG(buffer + 8, bigEndian);
+ getProperty -> type = GetULONG(buffer + 12, bigEndian);
+ getProperty -> long_offset = GetULONG(buffer + 16, bigEndian);
+ getProperty -> long_length = GetULONG(buffer + 20, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int GetPropertyStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+
+ GetPropertyMessage *getProperty = (GetPropertyMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ *(buffer + 1) = getProperty -> property_delete;
+
+ PutULONG(getProperty -> window, buffer + 4, bigEndian);
+ PutULONG(getProperty -> property, buffer + 8, bigEndian);
+ PutULONG(getProperty -> type, buffer + 12, bigEndian);
+ PutULONG(getProperty -> long_offset, buffer + 16, bigEndian);
+ PutULONG(getProperty -> long_length, buffer + 20, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void GetPropertyStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ GetPropertyMessage *getProperty = (GetPropertyMessage *) message;
+
+ *logofs << name() << ": Identity property_delete " << (unsigned int) getProperty -> property_delete
+ << ", window " << getProperty -> window << ", property " << getProperty -> property
+ << ", type " << getProperty -> type << ", long-offset " << getProperty -> long_offset
+ << ", long-length " << getProperty -> long_length << ".\n" << logofs_flush;
+
+ #endif
+}
+
+void GetPropertyStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ md5_append(md5_state_, buffer + 1, 1);
+ md5_append(md5_state_, buffer + 4, 20);
+}
diff --git a/nxcomp/GetProperty.h b/nxcomp/GetProperty.h
new file mode 100644
index 000000000..a73425354
--- /dev/null
+++ b/nxcomp/GetProperty.h
@@ -0,0 +1,175 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef GetProperty_H
+#define GetProperty_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define GETPROPERTY_ENABLE_CACHE 1
+#define GETPROPERTY_ENABLE_DATA 0
+#define GETPROPERTY_ENABLE_SPLIT 0
+#define GETPROPERTY_ENABLE_COMPRESS 0
+
+#define GETPROPERTY_DATA_LIMIT 0
+#define GETPROPERTY_DATA_OFFSET 24
+
+#define GETPROPERTY_CACHE_SLOTS 2000
+#define GETPROPERTY_CACHE_THRESHOLD 2
+#define GETPROPERTY_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class GetPropertyMessage : public Message
+{
+ friend class GetPropertyStore;
+
+ public:
+
+ GetPropertyMessage()
+ {
+ }
+
+ ~GetPropertyMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char property_delete;
+ unsigned long window;
+ unsigned long property;
+ unsigned long type;
+ unsigned long long_offset;
+ unsigned long long_length;
+};
+
+class GetPropertyStore : public MessageStore
+{
+
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ GetPropertyStore() : MessageStore()
+ {
+ enableCache = GETPROPERTY_ENABLE_CACHE;
+ enableData = GETPROPERTY_ENABLE_DATA;
+ enableSplit = GETPROPERTY_ENABLE_SPLIT;
+ enableCompress = GETPROPERTY_ENABLE_COMPRESS;
+
+ dataLimit = GETPROPERTY_DATA_LIMIT;
+ dataOffset = GETPROPERTY_DATA_OFFSET;
+
+ cacheSlots = GETPROPERTY_CACHE_SLOTS;
+ cacheThreshold = GETPROPERTY_CACHE_THRESHOLD;
+ cacheLowerThreshold = GETPROPERTY_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~GetPropertyStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "GetProperty";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_GetProperty;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(GetPropertyMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ protected:
+
+ virtual Message *create() const
+ {
+ return new GetPropertyMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new GetPropertyMessage((const GetPropertyMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (GetPropertyMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* GetProperty_H */
diff --git a/nxcomp/GetPropertyReply.cpp b/nxcomp/GetPropertyReply.cpp
new file mode 100644
index 000000000..223131803
--- /dev/null
+++ b/nxcomp/GetPropertyReply.cpp
@@ -0,0 +1,295 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "GetPropertyReply.h"
+
+#include "ServerCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "WriteBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Constructors and destructors.
+//
+
+GetPropertyReplyStore::GetPropertyReplyStore(StaticCompressor *compressor)
+
+ : MessageStore(compressor)
+{
+ enableCache = GETPROPERTYREPLY_ENABLE_CACHE;
+ enableData = GETPROPERTYREPLY_ENABLE_DATA;
+ enableSplit = GETPROPERTYREPLY_ENABLE_SPLIT;
+ enableCompress = GETPROPERTYREPLY_ENABLE_COMPRESS;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ enableCompress = GETPROPERTYREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7;
+ }
+
+ dataLimit = GETPROPERTYREPLY_DATA_LIMIT;
+ dataOffset = GETPROPERTYREPLY_DATA_OFFSET;
+
+ cacheSlots = GETPROPERTYREPLY_CACHE_SLOTS;
+ cacheThreshold = GETPROPERTYREPLY_CACHE_THRESHOLD;
+ cacheLowerThreshold = GETPROPERTYREPLY_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+GetPropertyReplyStore::~GetPropertyReplyStore()
+{
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int GetPropertyReplyStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ ServerCache *serverCache = (ServerCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding full message identity.\n"
+ << logofs_flush;
+ #endif
+
+ unsigned char format = (unsigned int) *(buffer + 1);
+
+ encodeBuffer.encodeCachedValue(format, 8,
+ serverCache -> getPropertyFormatCache);
+
+ unsigned int numBytes = GetULONG(buffer + 16, bigEndian);
+
+ encodeBuffer.encodeValue(numBytes, 32, 9);
+
+ if (format == 16)
+ {
+ numBytes <<= 1;
+ }
+ else if (format == 32)
+ {
+ numBytes <<= 2;
+ }
+
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + 8, bigEndian), 29,
+ serverCache -> getPropertyTypeCache, 9);
+
+ encodeBuffer.encodeValue(GetULONG(buffer + 12, bigEndian), 32, 9);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoded full message identity.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int GetPropertyReplyStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const
+{
+ ServerCache *serverCache = (ServerCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding full message identity.\n"
+ << logofs_flush;
+ #endif
+
+ unsigned char format;
+
+ decodeBuffer.decodeCachedValue(format, 8,
+ serverCache -> getPropertyFormatCache);
+
+ unsigned int length;
+
+ decodeBuffer.decodeValue(length, 32, 9);
+
+ unsigned int numBytes = length;
+
+ if (format == 16)
+ {
+ numBytes <<= 1;
+ }
+ else if (format == 32)
+ {
+ numBytes <<= 2;
+ }
+
+ size = 32 + RoundUp4(numBytes);
+
+ buffer = writeBuffer -> addMessage(size);
+
+ *(buffer + 1) = format;
+
+ PutULONG(length, buffer + 16, bigEndian);
+
+ unsigned int value;
+
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache -> getPropertyTypeCache, 9);
+
+ PutULONG(value, buffer + 8, bigEndian);
+
+ decodeBuffer.decodeValue(value, 32, 9);
+
+ PutULONG(value, buffer + 12, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded full message identity.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int GetPropertyReplyStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ GetPropertyReplyMessage *getPropertyReply = (GetPropertyReplyMessage *) message;
+
+ getPropertyReply -> format = *(buffer + 1);
+
+ getPropertyReply -> type = GetULONG(buffer + 8, bigEndian);
+ getPropertyReply -> after = GetULONG(buffer + 12, bigEndian);
+ getPropertyReply -> items = GetULONG(buffer + 16, bigEndian);
+
+ //
+ // Cleanup the padding bytes.
+ //
+
+ unsigned int uiLengthInBytes;
+ unsigned int uiFormat;
+
+ if ((int) size > GETPROPERTYREPLY_DATA_OFFSET)
+ {
+ uiLengthInBytes = getPropertyReply -> items;
+
+ uiFormat = *(buffer + 1);
+
+ #ifdef DEBUG
+ *logofs << name() << ": length " << uiLengthInBytes
+ << ", format " << uiFormat << ", size "
+ << size << ".\n" << logofs_flush;
+ #endif
+
+ if (uiFormat == 16)
+ {
+ uiLengthInBytes <<= 1;
+ }
+ else if (uiFormat == 32)
+ {
+ uiLengthInBytes <<= 2;
+ }
+
+ unsigned char *end = ((unsigned char *) buffer) + size;
+ unsigned char *pad = ((unsigned char *) buffer) + GETPROPERTYREPLY_DATA_OFFSET + uiLengthInBytes;
+
+ CleanData((unsigned char *) pad, end - pad);
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at "
+ << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int GetPropertyReplyStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ GetPropertyReplyMessage *getPropertyReply = (GetPropertyReplyMessage *) message;
+
+ *(buffer + 1) = getPropertyReply -> format;
+
+ PutULONG(getPropertyReply -> type, buffer + 8, bigEndian);
+ PutULONG(getPropertyReply -> after, buffer + 12, bigEndian);
+ PutULONG(getPropertyReply -> items, buffer + 16, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at "
+ << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void GetPropertyReplyStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ GetPropertyReplyMessage *getPropertyReply = (GetPropertyReplyMessage *) message;
+
+ *logofs << name() << ": Identity format "
+ << (unsigned) getPropertyReply -> format << ", type "
+ << getPropertyReply -> type << ", after " << getPropertyReply -> after
+ << ", items " << getPropertyReply -> items << ", size "
+ << getPropertyReply -> size_ << ".\n";
+
+ #endif
+}
+
+void GetPropertyReplyStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ //
+ // Fields format, type, after, items.
+ //
+
+ md5_append(md5_state_, buffer + 1, 1);
+ md5_append(md5_state_, buffer + 8, 12);
+}
+
+void GetPropertyReplyStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+}
+
+void GetPropertyReplyStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+}
diff --git a/nxcomp/GetPropertyReply.h b/nxcomp/GetPropertyReply.h
new file mode 100644
index 000000000..0f6b19508
--- /dev/null
+++ b/nxcomp/GetPropertyReply.h
@@ -0,0 +1,153 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef GetPropertyReply_H
+#define GetPropertyReply_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define GETPROPERTYREPLY_ENABLE_CACHE 1
+#define GETPROPERTYREPLY_ENABLE_DATA 1
+#define GETPROPERTYREPLY_ENABLE_SPLIT 0
+#define GETPROPERTYREPLY_ENABLE_COMPRESS 1
+
+#define GETPROPERTYREPLY_DATA_LIMIT 1048576 - 32
+#define GETPROPERTYREPLY_DATA_OFFSET 32
+
+#define GETPROPERTYREPLY_CACHE_SLOTS 400
+#define GETPROPERTYREPLY_CACHE_THRESHOLD 5
+#define GETPROPERTYREPLY_CACHE_LOWER_THRESHOLD 1
+
+#define GETPROPERTYREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7 0
+
+//
+// The message class.
+//
+
+class GetPropertyReplyMessage : public Message
+{
+ friend class GetPropertyReplyStore;
+
+ public:
+
+ GetPropertyReplyMessage()
+ {
+ }
+
+ ~GetPropertyReplyMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char format;
+ unsigned int type;
+ unsigned int after;
+ unsigned int items;
+};
+
+class GetPropertyReplyStore : public MessageStore
+{
+ public:
+
+ GetPropertyReplyStore(StaticCompressor *compressor);
+
+ virtual ~GetPropertyReplyStore();
+
+ virtual const char *name() const
+ {
+ return "GetPropertyReply";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_GetProperty;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(GetPropertyReplyMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ protected:
+
+ virtual Message *create() const
+ {
+ return new GetPropertyReplyMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new GetPropertyReplyMessage((const GetPropertyReplyMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (GetPropertyReplyMessage *) message;
+ }
+
+ virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const;
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* GetPropertyReply_H */
diff --git a/nxcomp/ImageText16.cpp b/nxcomp/ImageText16.cpp
new file mode 100644
index 000000000..569fdbe13
--- /dev/null
+++ b/nxcomp/ImageText16.cpp
@@ -0,0 +1,219 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "ImageText16.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int ImageText16Store::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ImageText16Message *imageText16 = (ImageText16Message *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ imageText16 -> len = *(buffer + 1);
+
+ imageText16 -> drawable = GetULONG(buffer + 4, bigEndian);
+ imageText16 -> gcontext = GetULONG(buffer + 8, bigEndian);
+
+ imageText16 -> x = GetUINT(buffer + 12, bigEndian);
+ imageText16 -> y = GetUINT(buffer + 14, bigEndian);
+
+ if ((int) size > dataOffset)
+ {
+ int pad = (size - dataOffset) - (imageText16 -> len * 2);
+
+ if (pad > 0)
+ {
+ CleanData((unsigned char *) buffer + size - pad, pad);
+ }
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int ImageText16Store::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ImageText16Message *imageText16 = (ImageText16Message *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ *(buffer + 1) = imageText16 -> len;
+
+ PutULONG(imageText16 -> drawable, buffer + 4, bigEndian);
+ PutULONG(imageText16 -> gcontext, buffer + 8, bigEndian);
+
+ PutUINT(imageText16 -> x, buffer + 12, bigEndian);
+ PutUINT(imageText16 -> y, buffer + 14, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void ImageText16Store::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ ImageText16Message *imageText16 = (ImageText16Message *) message;
+
+ *logofs << name() << ": Identity len " << (unsigned int) imageText16 -> len
+ << " drawable " << imageText16 -> drawable << ", gcontext "
+ << imageText16 -> gcontext << ", x " << imageText16 -> x << ", y "
+ << imageText16 -> y << ", size " << imageText16 -> size_ << ".\n";
+
+ #endif
+}
+
+void ImageText16Store::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ md5_append(md5_state_, buffer + 1, 1);
+}
+
+void ImageText16Store::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ ImageText16Message *imageText16 = (ImageText16Message *) message;
+ ImageText16Message *cachedImageText16 = (ImageText16Message *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << imageText16 -> drawable
+ << " as " << "drawable" << " field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(imageText16 -> drawable, clientCache -> drawableCache);
+
+ cachedImageText16 -> drawable = imageText16 -> drawable;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << imageText16 -> gcontext
+ << " as " << "gcontext" << " field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(imageText16 -> gcontext, clientCache -> gcCache);
+
+ cachedImageText16 -> gcontext = imageText16 -> gcontext;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << imageText16 -> x
+ << " as " << "x" << " field.\n" << logofs_flush;
+ #endif
+
+ unsigned short int diff_x = imageText16 -> x - cachedImageText16 -> x;
+
+ encodeBuffer.encodeCachedValue(diff_x, 16,
+ clientCache -> imageTextCacheX);
+
+ cachedImageText16 -> x = imageText16 -> x;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << imageText16 -> y
+ << " as " << "y" << " field.\n" << logofs_flush;
+ #endif
+
+ unsigned short int diff_y = imageText16 -> y - cachedImageText16 -> y;
+
+ encodeBuffer.encodeCachedValue(diff_y, 16,
+ clientCache -> imageTextCacheY);
+
+ cachedImageText16 -> y = imageText16 -> y;
+}
+
+void ImageText16Store::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ ImageText16Message *imageText16 = (ImageText16Message *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ imageText16 -> drawable = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << imageText16 -> drawable
+ << " as " << "drawable" << " field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ imageText16 -> gcontext = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << imageText16 -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> imageTextCacheX);
+
+ imageText16 -> x += value;
+ imageText16 -> x &= 0xffff;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << imageText16 -> x
+ << " as x field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> imageTextCacheY);
+
+ imageText16 -> y += value;
+ imageText16 -> y &= 0xffff;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << imageText16 -> y
+ << " as y field.\n" << logofs_flush;
+ #endif
+}
+
+
diff --git a/nxcomp/ImageText16.h b/nxcomp/ImageText16.h
new file mode 100644
index 000000000..0e116a4fe
--- /dev/null
+++ b/nxcomp/ImageText16.h
@@ -0,0 +1,182 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef ImageText16_H
+#define ImageText16_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define IMAGETEXT16_ENABLE_CACHE 1
+#define IMAGETEXT16_ENABLE_DATA 0
+#define IMAGETEXT16_ENABLE_SPLIT 0
+#define IMAGETEXT16_ENABLE_COMPRESS 0
+
+#define IMAGETEXT16_DATA_LIMIT 512
+#define IMAGETEXT16_DATA_OFFSET 16
+
+#define IMAGETEXT16_CACHE_SLOTS 3000
+#define IMAGETEXT16_CACHE_THRESHOLD 5
+#define IMAGETEXT16_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class ImageText16Message : public Message
+{
+ friend class ImageText16Store;
+
+ public:
+
+ ImageText16Message()
+ {
+ }
+
+ ~ImageText16Message()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char len;
+
+ unsigned int drawable;
+ unsigned int gcontext;
+
+ unsigned short x;
+ unsigned short y;
+};
+
+class ImageText16Store : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ ImageText16Store() : MessageStore()
+ {
+ enableCache = IMAGETEXT16_ENABLE_CACHE;
+ enableData = IMAGETEXT16_ENABLE_DATA;
+ enableSplit = IMAGETEXT16_ENABLE_SPLIT;
+ enableCompress = IMAGETEXT16_ENABLE_COMPRESS;
+
+ dataLimit = IMAGETEXT16_DATA_LIMIT;
+ dataOffset = IMAGETEXT16_DATA_OFFSET;
+
+ cacheSlots = IMAGETEXT16_CACHE_SLOTS;
+ cacheThreshold = IMAGETEXT16_CACHE_THRESHOLD;
+ cacheLowerThreshold = IMAGETEXT16_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~ImageText16Store()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "ImageText16";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_ImageText16;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(ImageText16Message);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new ImageText16Message();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new ImageText16Message((const ImageText16Message &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (ImageText16Message *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* ImageText16_H */
diff --git a/nxcomp/ImageText8.cpp b/nxcomp/ImageText8.cpp
new file mode 100644
index 000000000..161977677
--- /dev/null
+++ b/nxcomp/ImageText8.cpp
@@ -0,0 +1,219 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "ImageText8.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int ImageText8Store::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ImageText8Message *imageText8 = (ImageText8Message *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ imageText8 -> len = *(buffer + 1);
+
+ imageText8 -> drawable = GetULONG(buffer + 4, bigEndian);
+ imageText8 -> gcontext = GetULONG(buffer + 8, bigEndian);
+
+ imageText8 -> x = GetUINT(buffer + 12, bigEndian);
+ imageText8 -> y = GetUINT(buffer + 14, bigEndian);
+
+ if ((int) size > dataOffset)
+ {
+ int pad = (size - dataOffset) - imageText8 -> len;
+
+ if (pad > 0)
+ {
+ CleanData((unsigned char *) buffer + size - pad, pad);
+ }
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int ImageText8Store::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ImageText8Message *imageText8 = (ImageText8Message *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ *(buffer + 1) = imageText8 -> len;
+
+ PutULONG(imageText8 -> drawable, buffer + 4, bigEndian);
+ PutULONG(imageText8 -> gcontext, buffer + 8, bigEndian);
+
+ PutUINT(imageText8 -> x, buffer + 12, bigEndian);
+ PutUINT(imageText8 -> y, buffer + 14, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void ImageText8Store::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ ImageText8Message *imageText8 = (ImageText8Message *) message;
+
+ *logofs << name() << ": Identity len " << (unsigned int) imageText8 -> len
+ << " drawable " << imageText8 -> drawable << ", gcontext "
+ << imageText8 -> gcontext << ", x " << imageText8 -> x << ", y "
+ << imageText8 -> y << ", size " << imageText8 -> size_ << ".\n";
+
+ #endif
+}
+
+void ImageText8Store::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ md5_append(md5_state_, buffer + 1, 1);
+}
+
+void ImageText8Store::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ ImageText8Message *imageText8 = (ImageText8Message *) message;
+ ImageText8Message *cachedImageText8 = (ImageText8Message *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << imageText8 -> drawable
+ << " as " << "drawable" << " field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(imageText8 -> drawable, clientCache -> drawableCache);
+
+ cachedImageText8 -> drawable = imageText8 -> drawable;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << imageText8 -> gcontext
+ << " as " << "gcontext" << " field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(imageText8 -> gcontext, clientCache -> gcCache);
+
+ cachedImageText8 -> gcontext = imageText8 -> gcontext;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << imageText8 -> x
+ << " as " << "x" << " field.\n" << logofs_flush;
+ #endif
+
+ unsigned short int diff_x = imageText8 -> x - cachedImageText8 -> x;
+
+ encodeBuffer.encodeCachedValue(diff_x, 16,
+ clientCache -> imageTextCacheX);
+
+ cachedImageText8 -> x = imageText8 -> x;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << imageText8 -> y
+ << " as " << "y" << " field.\n" << logofs_flush;
+ #endif
+
+ unsigned short int diff_y = imageText8 -> y - cachedImageText8 -> y;
+
+ encodeBuffer.encodeCachedValue(diff_y, 16,
+ clientCache -> imageTextCacheY);
+
+ cachedImageText8 -> y = imageText8 -> y;
+}
+
+void ImageText8Store::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ ImageText8Message *imageText8 = (ImageText8Message *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ imageText8 -> drawable = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << imageText8 -> drawable
+ << " as " << "drawable" << " field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ imageText8 -> gcontext = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << imageText8 -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> imageTextCacheX);
+
+ imageText8 -> x += value;
+ imageText8 -> x &= 0xffff;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << imageText8 -> x
+ << " as x field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> imageTextCacheY);
+
+ imageText8 -> y += value;
+ imageText8 -> y &= 0xffff;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << imageText8 -> y
+ << " as y field.\n" << logofs_flush;
+ #endif
+}
+
+
diff --git a/nxcomp/ImageText8.h b/nxcomp/ImageText8.h
new file mode 100644
index 000000000..c56502f90
--- /dev/null
+++ b/nxcomp/ImageText8.h
@@ -0,0 +1,182 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef ImageText8_H
+#define ImageText8_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define IMAGETEXT8_ENABLE_CACHE 1
+#define IMAGETEXT8_ENABLE_DATA 0
+#define IMAGETEXT8_ENABLE_SPLIT 0
+#define IMAGETEXT8_ENABLE_COMPRESS 0
+
+#define IMAGETEXT8_DATA_LIMIT 256
+#define IMAGETEXT8_DATA_OFFSET 16
+
+#define IMAGETEXT8_CACHE_SLOTS 3000
+#define IMAGETEXT8_CACHE_THRESHOLD 5
+#define IMAGETEXT8_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class ImageText8Message : public Message
+{
+ friend class ImageText8Store;
+
+ public:
+
+ ImageText8Message()
+ {
+ }
+
+ ~ImageText8Message()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char len;
+
+ unsigned int drawable;
+ unsigned int gcontext;
+
+ unsigned short x;
+ unsigned short y;
+};
+
+class ImageText8Store : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ ImageText8Store() : MessageStore()
+ {
+ enableCache = IMAGETEXT8_ENABLE_CACHE;
+ enableData = IMAGETEXT8_ENABLE_DATA;
+ enableSplit = IMAGETEXT8_ENABLE_SPLIT;
+ enableCompress = IMAGETEXT8_ENABLE_COMPRESS;
+
+ dataLimit = IMAGETEXT8_DATA_LIMIT;
+ dataOffset = IMAGETEXT8_DATA_OFFSET;
+
+ cacheSlots = IMAGETEXT8_CACHE_SLOTS;
+ cacheThreshold = IMAGETEXT8_CACHE_THRESHOLD;
+ cacheLowerThreshold = IMAGETEXT8_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~ImageText8Store()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "ImageText8";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_ImageText8;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(ImageText8Message);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new ImageText8Message();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new ImageText8Message((const ImageText8Message &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (ImageText8Message *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* ImageText8_H */
diff --git a/nxcomp/IntCache.cpp b/nxcomp/IntCache.cpp
new file mode 100644
index 000000000..8262d5605
--- /dev/null
+++ b/nxcomp/IntCache.cpp
@@ -0,0 +1,218 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include <string.h>
+
+#include "Misc.h"
+#include "IntCache.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+IntCache::IntCache(unsigned int size)
+
+ : size_(size), length_(0), buffer_(new unsigned int[size]),
+ lastDiff_(0), lastValueInserted_(0), predictedBlockSize_(0)
+{
+}
+
+int IntCache::lookup(unsigned int &value, unsigned int &index,
+ unsigned int mask, unsigned int &sameDiff)
+{
+ for (unsigned int i = 0; i < length_; i++)
+ {
+ if (value == buffer_[i])
+ {
+ index = i;
+ if (i)
+ {
+ unsigned int target = (i >> 1);
+ do
+ {
+ buffer_[i] = buffer_[i - 1];
+ i--;
+ }
+ while (i > target);
+ buffer_[target] = value;
+ }
+ return 1;
+ }
+ }
+ unsigned int insertionPoint;
+ if (2 >= length_)
+ insertionPoint = length_;
+ else
+ insertionPoint = 2;
+ unsigned int start;
+ if (length_ >= size_)
+ start = size_ - 1;
+ else
+ {
+ start = length_;
+ length_++;
+ }
+ for (unsigned int k = start; k > insertionPoint; k--)
+ buffer_[k] = buffer_[k - 1];
+ buffer_[insertionPoint] = value;
+ unsigned int diff = value - lastValueInserted_;
+
+ lastValueInserted_ = (value & mask);
+ value = (diff & mask);
+ sameDiff = (value == lastDiff_);
+ if (!sameDiff)
+ {
+ lastDiff_ = value;
+
+ unsigned int lastChangeIndex = 0;
+ unsigned int lastBitIsOne = (lastDiff_ & 0x1);
+ unsigned int j = 1;
+ for (unsigned int nextMask = 0x2; nextMask & mask; nextMask <<= 1)
+ {
+ unsigned int nextBitIsOne = (lastDiff_ & nextMask);
+ if (nextBitIsOne)
+ {
+ if (!lastBitIsOne)
+ {
+ lastChangeIndex = j;
+ lastBitIsOne = nextBitIsOne;
+ }
+ }
+ else
+ {
+ if (lastBitIsOne)
+ {
+ lastChangeIndex = j;
+ lastBitIsOne = nextBitIsOne;
+ }
+ }
+ j++;
+ }
+ predictedBlockSize_ = lastChangeIndex + 1;
+ if (predictedBlockSize_ < 2)
+ predictedBlockSize_ = 2;
+ }
+ return 0;
+}
+
+void IntCache::insert(unsigned int &value, unsigned int mask)
+{
+ unsigned int insertionPoint;
+ if (2 >= length_)
+ insertionPoint = length_;
+ else
+ insertionPoint = 2;
+ unsigned int start;
+ if (length_ >= size_)
+ start = size_ - 1;
+ else
+ {
+ start = length_;
+ length_++;
+ }
+ for (unsigned int k = start; k > insertionPoint; k--)
+ buffer_[k] = buffer_[k - 1];
+ if (lastDiff_ != value)
+ {
+ lastDiff_ = value;
+ unsigned int lastChangeIndex = 0;
+ unsigned int lastBitIsOne = (lastDiff_ & 0x1);
+ unsigned int j = 1;
+ for (unsigned int nextMask = 0x2; nextMask & mask; nextMask <<= 1)
+ {
+ unsigned int nextBitIsOne = (lastDiff_ & nextMask);
+ if (nextBitIsOne)
+ {
+ if (!lastBitIsOne)
+ {
+ lastChangeIndex = j;
+ lastBitIsOne = nextBitIsOne;
+ }
+ }
+ else
+ {
+ if (lastBitIsOne)
+ {
+ lastChangeIndex = j;
+ lastBitIsOne = nextBitIsOne;
+ }
+ }
+ j++;
+ }
+ predictedBlockSize_ = lastChangeIndex + 1;
+ if (predictedBlockSize_ < 2)
+ predictedBlockSize_ = 2;
+ }
+ lastValueInserted_ += value;
+ lastValueInserted_ &= mask;
+ buffer_[insertionPoint] = lastValueInserted_;
+ value = lastValueInserted_;
+}
+
+void IntCache::push(unsigned int &value, unsigned int mask)
+{
+ //
+ // Using a memmove() appears to be slower.
+ //
+ // memmove((char *) &buffer_[1], (char *) &buffer_[0],
+ // sizeof(unsigned int) * (size_ - 1));
+ //
+ // if (length_ < size_)
+ // {
+ // length_++;
+ // }
+ //
+
+ unsigned int start;
+
+ if (length_ >= size_)
+ {
+ start = size_ - 1;
+ }
+ else
+ {
+ start = length_;
+
+ length_++;
+ }
+
+ for (unsigned int k = start; k > 0; k--)
+ {
+ buffer_[k] = buffer_[k - 1];
+ }
+
+ value &= mask;
+
+ buffer_[0] = value;
+}
+
+void IntCache::dump()
+{
+ #ifdef DUMP
+
+ *logofs << "IntCache: Dumping content of cache at "
+ << (void *) this << ":\n" << logofs_flush;
+
+ for (unsigned int i = 0; i < length_; i++)
+ {
+ *logofs << "IntCache: [" << i << "][" << buffer_[i] << "]\n";
+ }
+
+ #endif
+}
diff --git a/nxcomp/IntCache.h b/nxcomp/IntCache.h
new file mode 100644
index 000000000..15cc8ea53
--- /dev/null
+++ b/nxcomp/IntCache.h
@@ -0,0 +1,111 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef IntCache_H
+#define IntCache_H
+
+class IntCache
+{
+ public:
+
+ IntCache(unsigned int size);
+
+ ~IntCache()
+ {
+ delete [] buffer_;
+ }
+
+ unsigned int getSize() const
+ {
+ return length_;
+ }
+
+ int lookup(unsigned int &value, unsigned int &index,
+ unsigned int mask, unsigned int &sameDiff);
+
+ //
+ // This can be inlined as it is only
+ // called by decodeCachedValue().
+ //
+
+ unsigned int get(unsigned int index)
+ {
+ unsigned int result = buffer_[index];
+
+ if (index != 0)
+ {
+ //
+ // Using a memmove() appears to be slower.
+ //
+ // unsigned int target = index >> 1;
+ //
+ // memmove((char *) &buffer_[target + 1], (char *) &buffer_[target],
+ // sizeof(unsigned int) * (index - target));
+ //
+ // buffer_[target] = result;
+ //
+
+ unsigned int i = index;
+
+ unsigned int target = (i >> 1);
+
+ do
+ {
+ buffer_[i] = buffer_[i - 1];
+
+ i--;
+ }
+ while (i > target);
+
+ buffer_[target] = result;
+ }
+
+ return result;
+ }
+
+ void insert(unsigned int &value, unsigned int mask);
+
+ void push(unsigned int &value, unsigned int mask);
+
+ void dump();
+
+ unsigned int getLastDiff(unsigned int mask) const
+ {
+ return lastDiff_;
+ }
+
+ unsigned int getBlockSize(unsigned int bits) const
+ {
+ if (bits > 0)
+ {
+ return (predictedBlockSize_ < bits ? predictedBlockSize_ : bits);
+ }
+
+ return predictedBlockSize_;
+ }
+
+ private:
+
+ unsigned int size_;
+ unsigned int length_;
+ unsigned int *buffer_;
+ unsigned int lastDiff_;
+ unsigned int lastValueInserted_;
+ unsigned int predictedBlockSize_;
+};
+
+#endif /* IntCache_H */
diff --git a/nxcomp/InternAtom.cpp b/nxcomp/InternAtom.cpp
new file mode 100644
index 000000000..d90c8c058
--- /dev/null
+++ b/nxcomp/InternAtom.cpp
@@ -0,0 +1,119 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "InternAtom.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int InternAtomStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ InternAtomMessage *internAtom = (InternAtomMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ internAtom -> only_if_exists = *(buffer + 1);
+ internAtom -> name_length = GetUINT(buffer + 4, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Clean up padding bytes.
+ //
+
+ if ((int) size > dataOffset)
+ {
+ unsigned char *end = ((unsigned char *) buffer) + size;
+
+ for (unsigned char *pad = ((unsigned char *) buffer) + 8 +
+ internAtom -> name_length; pad < end; pad++)
+ {
+ *pad = 0;
+ }
+ }
+
+ return 1;
+}
+
+int InternAtomStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ InternAtomMessage *internAtom = (InternAtomMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ *(buffer + 1) = internAtom -> only_if_exists;
+
+ PutUINT(internAtom -> name_length, buffer + 4, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void InternAtomStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ InternAtomMessage *internAtom = (InternAtomMessage *) message;
+
+ *logofs << name() << ": Identity only_if_exists "
+ << (unsigned int) internAtom -> only_if_exists
+ << ", name_length " << internAtom -> name_length
+ << ", name '";
+
+ for (int i = 0; i < internAtom -> name_length; i++)
+ {
+ *logofs << internAtom -> data_[i];
+ }
+
+ *logofs << "'.\n" << logofs_flush;
+
+ #endif
+}
+
+void InternAtomStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ md5_append(md5_state_, buffer + 1, 1);
+ md5_append(md5_state_, buffer + 4, 2);
+}
diff --git a/nxcomp/InternAtom.h b/nxcomp/InternAtom.h
new file mode 100644
index 000000000..f7f366dce
--- /dev/null
+++ b/nxcomp/InternAtom.h
@@ -0,0 +1,170 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef InternAtom_H
+#define InternAtom_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define INTERNATOM_ENABLE_CACHE 1
+#define INTERNATOM_ENABLE_DATA 0
+#define INTERNATOM_ENABLE_SPLIT 0
+#define INTERNATOM_ENABLE_COMPRESS 0
+
+#define INTERNATOM_DATA_LIMIT 80
+#define INTERNATOM_DATA_OFFSET 8
+
+#define INTERNATOM_CACHE_SLOTS 2000
+#define INTERNATOM_CACHE_THRESHOLD 2
+#define INTERNATOM_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class InternAtomMessage : public Message
+{
+ friend class InternAtomStore;
+
+ public:
+
+ InternAtomMessage()
+ {
+ }
+
+ ~InternAtomMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char only_if_exists;
+ unsigned short name_length;
+};
+
+class InternAtomStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ InternAtomStore() : MessageStore()
+ {
+ enableCache = INTERNATOM_ENABLE_CACHE;
+ enableData = INTERNATOM_ENABLE_DATA;
+ enableSplit = INTERNATOM_ENABLE_SPLIT;
+ enableCompress = INTERNATOM_ENABLE_COMPRESS;
+
+ dataLimit = INTERNATOM_DATA_LIMIT;
+ dataOffset = INTERNATOM_DATA_OFFSET;
+
+ cacheSlots = INTERNATOM_CACHE_SLOTS;
+ cacheThreshold = INTERNATOM_CACHE_THRESHOLD;
+ cacheLowerThreshold = INTERNATOM_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~InternAtomStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "InternAtom";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_InternAtom;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(InternAtomMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ protected:
+
+ virtual Message *create() const
+ {
+ return new InternAtomMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new InternAtomMessage((const InternAtomMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (InternAtomMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* InternAtom_H */
diff --git a/nxcomp/Jpeg.cpp b/nxcomp/Jpeg.cpp
new file mode 100644
index 000000000..b3973227c
--- /dev/null
+++ b/nxcomp/Jpeg.cpp
@@ -0,0 +1,875 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include <X11/Xmd.h>
+
+#include <unistd.h>
+#include <setjmp.h>
+#include <zlib.h>
+
+#ifdef __cplusplus
+
+extern "C"
+{
+ #include <stdio.h>
+ #include <jpeglib.h>
+}
+
+#else
+
+#include <stdio.h>
+#include <jpeglib.h>
+
+#endif
+
+#include "Misc.h"
+#include "Jpeg.h"
+#include "Unpack.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#define RGB24_TO_PIXEL(bpp,r,g,b) \
+ ((((CARD##bpp)(r) & 0xff) * srcRedMax + 127) / 255 \
+ << srcRedShift | \
+ (((CARD##bpp)(g) & 0xff) * srcGreenMax + 127) / 255 \
+ << srcGreenShift | \
+ (((CARD##bpp)(b) & 0xff) * srcBlueMax + 127) / 255 \
+ << srcBlueShift)
+
+#define RGB24_TO_PIXEL32(r,g,b) \
+ (((CARD32)(r) & 0xff) << srcRedShift | \
+ ((CARD32)(g) & 0xff) << srcGreenShift | \
+ ((CARD32)(b) & 0xff) << srcBlueShift)
+
+//
+// Functions from Unpack.cpp
+//
+
+extern int Unpack32To32(const T_colormask *colormask, const unsigned int *data,
+ unsigned int *out, unsigned int *end);
+
+extern int Unpack24To24(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+extern int Unpack16To16(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+//
+// Local functions used for the jpeg decompression.
+//
+
+static void JpegSetSrcManager(j_decompress_ptr cinfo, CARD8 *compressedData, int compressedLen);
+static void JpegInitSource(j_decompress_ptr cinfo);
+static void JpegTermSource(j_decompress_ptr cinfo);
+static void JpegSkipInputData(j_decompress_ptr cinfo, long num_bytes);
+
+static boolean JpegFillInputBuffer(j_decompress_ptr cinfo);
+
+static int DecompressJpeg16(unsigned char *compressedData, int compressedLen,
+ unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder);
+
+static int DecompressJpeg24(unsigned char *compressedData, int compressedLen,
+ unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder);
+
+static int DecompressJpeg32(unsigned char *compressedData, int compressedLen,
+ unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder);
+
+void UnpackJpegErrorHandler(j_common_ptr cinfo);
+
+//
+// Colormap stuff.
+//
+
+CARD16 srcRedMax, srcGreenMax, srcBlueMax;
+CARD8 srcRedShift, srcGreenShift, srcBlueShift;
+
+//
+// Error handler.
+//
+
+static bool jpegError;
+
+jmp_buf UnpackJpegContext;
+
+void UnpackJpegErrorHandler(j_common_ptr cinfo)
+{
+ #ifdef PANIC
+ *logofs << "UnpackJpegErrorHandler: PANIC! Detected error in JPEG decompression.\n"
+ << logofs_flush;
+
+ *logofs << "UnpackJpegErrorHandler: PANIC! Trying to revert to the previous context.\n"
+ << logofs_flush;
+ #endif
+
+ jpegError = 1;
+
+ longjmp(UnpackJpegContext, 1);
+}
+
+//
+// Attributes used for the jpeg decompression.
+//
+
+static struct jpeg_source_mgr jpegSrcManager;
+static JOCTET *jpegBufferPtr;
+static size_t jpegBufferLen;
+
+static char *tmpBuf;
+static int tmpBufSize = 0;
+
+int UnpackJpeg(T_geometry *geometry, unsigned char method, unsigned char *srcData,
+ int srcSize, int dstBpp, int dstWidth, int dstHeight,
+ unsigned char *dstData, int dstSize)
+{
+ int byteOrder = geometry -> image_byte_order;
+
+ //
+ // Check if data is coming from a failed unsplit.
+ //
+
+ if (srcSize < 2 || (srcData[0] == SPLIT_PATTERN &&
+ srcData[1] == SPLIT_PATTERN))
+ {
+ #ifdef WARNING
+ *logofs << "UnpackJpeg: WARNING! Skipping unpack of dummy data.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ #ifdef DEBUG
+ *logofs << "UnpackJpeg: Decompression. Source size "
+ << srcSize << " bits per plane " << dstBpp
+ << ".\n" << logofs_flush;
+ #endif
+
+ srcRedShift = ffs(geometry -> red_mask) - 1;
+ srcGreenShift = ffs(geometry -> green_mask) - 1;
+ srcBlueShift = ffs(geometry -> blue_mask) - 1;
+
+ #ifdef DEBUG
+ *logofs << "UnpackJpeg: Red shift " << (int) srcRedShift
+ << " green shift " << (int) srcGreenShift << " blue shift "
+ << (int) srcBlueShift << ".\n" << logofs_flush;
+ #endif
+
+ srcRedMax = geometry -> red_mask >> srcRedShift;
+ srcGreenMax = geometry -> green_mask >> srcGreenShift;
+ srcBlueMax = geometry -> blue_mask >> srcBlueShift;
+
+ #ifdef DEBUG
+ *logofs << "UnpackJpeg: Red mask " << (void *) geometry -> red_mask
+ << " green mask " << (void *) geometry -> green_mask
+ << " blue mask " << (void *) geometry -> blue_mask
+ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef DEBUG
+ *logofs << "UnpackJpeg: Red max " << srcRedMax << " green max "
+ << srcGreenMax << " blue max " << srcBlueMax
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Make enough space in the temporary
+ // buffer to have one complete row of
+ // the image with 3 bytes for a pixel.
+ //
+
+ tmpBufSize = dstWidth * 3;
+ tmpBuf = new char[tmpBufSize];
+
+ if (tmpBuf == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "UnpackJpeg: PANIC! Cannot allocate "
+ << dstWidth * 3 << " bytes for Jpeg "
+ << "decompressed data.\n" << logofs_flush;
+ #endif
+
+ delete [] tmpBuf;
+
+ return -1;
+ }
+
+ int result = 1;
+
+ switch(dstBpp)
+ {
+ case 8:
+ {
+ //
+ // Simply move the data from srcData to dstData
+ // taking into consideration the correct padding.
+ //
+
+ int row;
+
+ unsigned char * dstBuff = dstData;
+ unsigned char * srcBuff = srcData;
+
+ for (row = 0; row < dstHeight; row++)
+ {
+ memcpy(dstBuff, srcBuff, dstWidth);
+
+ dstBuff += RoundUp4(dstWidth);
+ srcBuff += dstWidth;
+ }
+
+ break;
+ }
+ case 16:
+ {
+ result = DecompressJpeg16(srcData, srcSize, dstWidth,
+ dstHeight, dstData, byteOrder);
+ break;
+ }
+ case 24:
+ {
+ result = DecompressJpeg24(srcData, srcSize, dstWidth,
+ dstHeight, dstData, byteOrder);
+ break;
+ }
+ case 32:
+ {
+ result = DecompressJpeg32(srcData, srcSize, dstWidth,
+ dstHeight, dstData, byteOrder);
+ break;
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "UnpackJpeg: PANIC! Failed to decode Jpeg image. "
+ << " Unsupported Bpp value " << dstBpp
+ << " for the Jpeg compression"
+ << ".\n" << logofs_flush;
+ #endif
+
+ delete [] tmpBuf;
+
+ result = -1;
+ }
+ }
+
+ #ifdef DEBUG
+ *logofs << "UnpackJpeg: Decompression finished with result "
+ << result << ".\n" << logofs_flush;
+ #endif
+
+ if (result == -1)
+ {
+ delete [] tmpBuf;
+
+ #ifdef PANIC
+ *logofs << "UnpackJpeg: PANIC! Failed to decode Jpeg image using "
+ << dstBpp << " Bpp destination.\n" << logofs_flush;
+ #endif
+
+ return result;
+ }
+
+ //
+ // Apply the correction for the brightness.
+ //
+
+ int maskMethod;
+
+ switch(method)
+ {
+ case PACK_JPEG_8_COLORS:
+ {
+ maskMethod = MASK_8_COLORS;
+
+ break;
+ }
+ case PACK_JPEG_64_COLORS:
+ {
+ maskMethod = MASK_64_COLORS;
+
+ break;
+ }
+ case PACK_JPEG_256_COLORS:
+ {
+ maskMethod = MASK_256_COLORS;
+
+ break;
+ }
+ case PACK_JPEG_512_COLORS:
+ {
+ maskMethod = MASK_512_COLORS;
+
+ break;
+ }
+ case PACK_JPEG_4K_COLORS:
+ {
+ maskMethod = MASK_4K_COLORS;
+
+ break;
+ }
+ case PACK_JPEG_32K_COLORS:
+ {
+ maskMethod = MASK_32K_COLORS;
+
+ break;
+ }
+ case PACK_JPEG_64K_COLORS:
+ {
+ maskMethod = MASK_64K_COLORS;
+
+ break;
+ }
+ case PACK_JPEG_256K_COLORS:
+ {
+ maskMethod = MASK_256K_COLORS;
+
+ break;
+ }
+ case PACK_JPEG_2M_COLORS:
+ {
+ maskMethod = MASK_2M_COLORS;
+
+ break;
+ }
+ case PACK_JPEG_16M_COLORS:
+ {
+ maskMethod = MASK_16M_COLORS;
+
+ break;
+ }
+ default:
+ {
+ delete [] tmpBuf;
+
+ return -1;
+ }
+ }
+
+ const T_colormask *colorMask = MethodColorMask(maskMethod);
+
+ unsigned char *dstBuff = dstData;
+
+ switch (dstBpp)
+ {
+ case 16:
+ {
+ Unpack16To16(colorMask, dstBuff, dstBuff, dstBuff + dstSize);
+
+ break;
+ }
+ case 24:
+ {
+ break;
+ }
+ case 32:
+ {
+ Unpack32To32(colorMask, (unsigned int *) dstBuff, (unsigned int *) dstBuff,
+ (unsigned int *) (dstBuff + dstSize));
+ break;
+ }
+ default:
+ {
+ delete [] tmpBuf;
+
+ return -1;
+ }
+ }
+
+ delete [] tmpBuf;
+
+ return 1;
+}
+
+//
+// Functions that actually do the Jpeg decompression.
+//
+
+int DecompressJpeg16(unsigned char *compressedData, int compressedLen,
+ unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder)
+{
+ struct jpeg_decompress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ unsigned char *data;
+ JSAMPROW rowPointer[1];
+
+ unsigned int dx = 0;
+ unsigned int dy = 0;
+
+ #ifdef DEBUG
+ *logofs << "DecompressJpeg16: Decompressing with length "
+ << compressedLen << " width " << w << " height "
+ << h << ".\n" << logofs_flush;
+ #endif
+
+ jpegError = 0;
+
+ cinfo.err = jpeg_std_error(&jerr);
+
+ jerr.error_exit = UnpackJpegErrorHandler;
+
+ if (setjmp(UnpackJpegContext) == 1)
+ {
+ #ifdef TEST
+ *logofs << "DecompressJpeg16: Out of the long jump with error '"
+ << jpegError << "'.\n" << logofs_flush;
+ #endif
+
+ goto AbortDecompressJpeg16;
+ }
+
+ jpeg_create_decompress(&cinfo);
+
+ if (jpegError) goto AbortDecompressJpeg16;
+
+ JpegSetSrcManager(&cinfo, compressedData, compressedLen);
+
+ jpeg_read_header(&cinfo, 1);
+
+ if (jpegError) goto AbortDecompressJpeg16;
+
+ cinfo.out_color_space = JCS_RGB;
+
+ jpeg_start_decompress(&cinfo);
+
+ if (jpegError) goto AbortDecompressJpeg16;
+
+ if (cinfo.output_width != w ||
+ cinfo.output_height != h ||
+ cinfo.output_components != 3)
+ {
+ #ifdef PANIC
+ *logofs << "DecompressJpeg16: PANIC! Wrong JPEG data received.\n"
+ << logofs_flush;
+ #endif
+
+ jpeg_destroy_decompress(&cinfo);
+
+ return -1;
+ }
+
+ //
+ // PixelPtr points to dstBuf which is
+ // already padded correctly for the final
+ // image to put
+ //
+
+ data = dstBuf;
+
+ rowPointer[0] = (JSAMPROW) tmpBuf;
+
+ unsigned long pixel;
+
+ while (cinfo.output_scanline < cinfo.output_height)
+ {
+ jpeg_read_scanlines(&cinfo, rowPointer, 1);
+
+ if (jpegError) goto AbortDecompressJpeg16;
+
+ for (dx = 0; dx < w; dx++)
+ {
+ pixel = RGB24_TO_PIXEL(16, tmpBuf[dx * 3], tmpBuf[dx * 3 + 1],
+ tmpBuf[dx * 3 + 2]);
+
+ //
+ // Follow the server byte order when arranging data.
+ //
+
+ if (byteOrder == LSBFirst)
+ {
+ data[0] = (unsigned char) (pixel & 0xff);
+ data[1] = (unsigned char) ((pixel >> 8) & 0xff);
+ }
+ else
+ {
+ data[1] = (unsigned char) (pixel & 0xff);
+ data[0] = (unsigned char) ((pixel >> 8) & 0xff);
+ }
+
+ data += 2;
+ }
+
+ //
+ // Move data at the beginning of the
+ // next line.
+ //
+
+ data = data + (RoundUp4(w * 2) - w * 2);
+
+ dy++;
+ }
+
+ AbortDecompressJpeg16:
+
+ if (jpegError == 0)
+ {
+ jpeg_finish_decompress(&cinfo);
+ }
+
+ jpeg_destroy_decompress(&cinfo);
+
+ if (jpegError == 1)
+ {
+ #ifdef PANIC
+ *logofs << "DecompressJpeg16: Failed to decompress JPEG image.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "DecompressJpeg16: Decompression finished with "
+ << dy << " lines handled.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int DecompressJpeg24(unsigned char *compressedData, int compressedLen,
+ unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder)
+{
+ struct jpeg_decompress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ CARD8 *pixelPtr = NULL;
+ JSAMPROW rowPointer[1];
+
+ unsigned int dx = 0;
+ unsigned int dy = 0;
+
+ #ifdef TEST
+ *logofs << "DecompressJpeg24: Decompressing with length "
+ << compressedLen << " width " << w << " height "
+ << h << ".\n" << logofs_flush;
+ #endif
+
+ jpegError = 0;
+
+ cinfo.err = jpeg_std_error(&jerr);
+
+ jerr.error_exit = UnpackJpegErrorHandler;
+
+ if (setjmp(UnpackJpegContext) == 1)
+ {
+ #ifdef TEST
+ *logofs << "DecompressJpeg24: Out of the long jump with error '"
+ << jpegError << "'.\n" << logofs_flush;
+ #endif
+
+ goto AbortDecompressJpeg24;
+ }
+
+ jpeg_create_decompress(&cinfo);
+
+ if (jpegError) goto AbortDecompressJpeg24;
+
+ JpegSetSrcManager(&cinfo, compressedData, compressedLen);
+
+ jpeg_read_header(&cinfo, 1);
+
+ if (jpegError) goto AbortDecompressJpeg24;
+
+ cinfo.out_color_space = JCS_RGB;
+
+ jpeg_start_decompress(&cinfo);
+
+ if (jpegError) goto AbortDecompressJpeg24;
+
+ if (cinfo.output_width != w ||
+ cinfo.output_height != h ||
+ cinfo.output_components != 3)
+ {
+ #ifdef PANIC
+ *logofs << "DecompressJpeg24: PANIC! Wrong JPEG data received.\n"
+ << logofs_flush;
+ #endif
+
+ jpeg_destroy_decompress(&cinfo);
+
+ return -1;
+ }
+
+ //
+ // PixelPtr points to dstBuf which is
+ // already padded correctly for the final
+ // image to put.
+ //
+
+ pixelPtr = (CARD8 *) dstBuf;
+
+ rowPointer[0] = (JSAMPROW) tmpBuf;
+
+ while (cinfo.output_scanline < cinfo.output_height)
+ {
+ jpeg_read_scanlines(&cinfo, rowPointer, 1);
+
+ if (jpegError) goto AbortDecompressJpeg24;
+
+ for (dx = 0; dx < w; dx++)
+ {
+ //
+ // Follow the server byte order when arranging data.
+ //
+
+ if (byteOrder == LSBFirst)
+ {
+ pixelPtr[0] = tmpBuf[dx * 3];
+ pixelPtr[1] = tmpBuf[dx * 3 + 1];
+ pixelPtr[2] = tmpBuf[dx * 3 + 2];
+ }
+ else
+ {
+ pixelPtr[2] = tmpBuf[dx * 3];
+ pixelPtr[1] = tmpBuf[dx * 3 + 1];
+ pixelPtr[0] = tmpBuf[dx * 3 + 2];
+ }
+
+ pixelPtr += 3;
+ }
+
+ //
+ // Go to the next line.
+ //
+
+ pixelPtr = (CARD8 *) (((char *) pixelPtr) + (RoundUp4(w * 3) - w * 3));
+
+ dy++;
+ }
+
+ AbortDecompressJpeg24:
+
+ if (jpegError == 0)
+ {
+ jpeg_finish_decompress(&cinfo);
+ }
+
+ jpeg_destroy_decompress(&cinfo);
+
+ if (jpegError == 1)
+ {
+ #ifdef PANIC
+ *logofs << "DecompressJpeg24: Failed to decompress JPEG image.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "DecompressJpeg24: Decompression finished with "
+ << dy << " lines handled.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int DecompressJpeg32(unsigned char *compressedData, int compressedLen,
+ unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder)
+{
+ struct jpeg_decompress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ unsigned char *data;
+ JSAMPROW rowPointer[1];
+
+ unsigned int dx = 0;
+ unsigned int dy = 0;
+
+ #ifdef TEST
+ *logofs << "DecompressJpeg32: Decompressing with length "
+ << compressedLen << " width " << w << " height "
+ << h << ".\n" << logofs_flush;
+ #endif
+
+ jpegError = 0;
+
+ cinfo.err = jpeg_std_error(&jerr);
+
+ jerr.error_exit = UnpackJpegErrorHandler;
+
+ if (setjmp(UnpackJpegContext) == 1)
+ {
+ #ifdef TEST
+ *logofs << "DecompressJpeg32: Out of the long jump with error '"
+ << jpegError << "'.\n" << logofs_flush;
+ #endif
+
+ goto AbortDecompressJpeg32;
+ }
+
+ jpeg_create_decompress(&cinfo);
+
+ if (jpegError) goto AbortDecompressJpeg32;
+
+ JpegSetSrcManager(&cinfo, compressedData, compressedLen);
+
+ jpeg_read_header(&cinfo, 1);
+
+ if (jpegError) goto AbortDecompressJpeg32;
+
+ cinfo.out_color_space = JCS_RGB;
+
+ jpeg_start_decompress(&cinfo);
+
+ if (jpegError) goto AbortDecompressJpeg32;
+
+ if (cinfo.output_width != w ||
+ cinfo.output_height != h ||
+ cinfo.output_components != 3)
+ {
+ #ifdef PANIC
+ *logofs << "DecompressJpeg32 : PANIC! Wrong JPEG data received.\n"
+ << logofs_flush;
+ #endif
+
+ jpeg_destroy_decompress(&cinfo);
+
+ return -1;
+ }
+
+ //
+ // PixelPtr points to dstBuf which is
+ // already padded correctly for the final
+ // image to put
+ //
+
+ data = dstBuf;
+
+ rowPointer[0] = (JSAMPROW) tmpBuf;
+
+ unsigned long pixel;
+
+ int i;
+
+ while (cinfo.output_scanline < cinfo.output_height)
+ {
+ jpeg_read_scanlines(&cinfo, rowPointer, 1);
+
+ if (jpegError) goto AbortDecompressJpeg32;
+
+ for (dx = 0; dx < w; dx++)
+ {
+ pixel = RGB24_TO_PIXEL(32, tmpBuf[dx * 3], tmpBuf[dx * 3 + 1],
+ tmpBuf[dx * 3 + 2]);
+
+ //
+ // Follow the server byte order when arranging data.
+ //
+
+ if (byteOrder == LSBFirst)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ data[i] = (unsigned char)(pixel & 0xff);
+ pixel >>= 8;
+ }
+ }
+ else
+ {
+ for (i = 3; i >= 0; i--)
+ {
+ data[i] = (unsigned char) (pixel & 0xff);
+ pixel >>= 8;
+ }
+ }
+
+ data += 4;
+ }
+
+ dy++;
+ }
+
+ AbortDecompressJpeg32:
+
+ if (jpegError == 0)
+ {
+ jpeg_finish_decompress(&cinfo);
+ }
+
+ jpeg_destroy_decompress(&cinfo);
+
+ if (jpegError == 1)
+ {
+ #ifdef PANIC
+ *logofs << "DecompressJpeg32: Failed to decompress JPEG image.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "DecompressJpeg32: Decompression finished with "
+ << dy << " lines handled.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+static void JpegInitSource(j_decompress_ptr cinfo)
+{
+ jpegError = 0;
+}
+
+static boolean JpegFillInputBuffer(j_decompress_ptr cinfo)
+{
+ jpegError = 1;
+
+ jpegSrcManager.bytes_in_buffer = jpegBufferLen;
+ jpegSrcManager.next_input_byte = (JOCTET *)jpegBufferPtr;
+
+ return 1;
+}
+
+static void JpegSkipInputData(j_decompress_ptr cinfo, long num_bytes)
+{
+ if (num_bytes < 0 || (unsigned long) num_bytes > jpegSrcManager.bytes_in_buffer)
+ {
+ jpegError = 1;
+
+ jpegSrcManager.bytes_in_buffer = jpegBufferLen;
+ jpegSrcManager.next_input_byte = (JOCTET *)jpegBufferPtr;
+ }
+ else
+ {
+ jpegSrcManager.next_input_byte += (size_t) num_bytes;
+ jpegSrcManager.bytes_in_buffer -= (size_t) num_bytes;
+ }
+}
+
+static void JpegTermSource(j_decompress_ptr cinfo)
+{
+}
+
+static void JpegSetSrcManager(j_decompress_ptr cinfo,
+ CARD8 *compressedData,
+ int compressedLen)
+{
+ jpegBufferPtr = (JOCTET *) compressedData;
+ jpegBufferLen = (size_t) compressedLen;
+
+ jpegSrcManager.init_source = JpegInitSource;
+ jpegSrcManager.fill_input_buffer = JpegFillInputBuffer;
+ jpegSrcManager.skip_input_data = JpegSkipInputData;
+ jpegSrcManager.resync_to_restart = jpeg_resync_to_restart;
+ jpegSrcManager.term_source = JpegTermSource;
+ jpegSrcManager.next_input_byte = jpegBufferPtr;
+ jpegSrcManager.bytes_in_buffer = jpegBufferLen;
+
+ cinfo->src = &jpegSrcManager;
+}
diff --git a/nxcomp/Jpeg.h b/nxcomp/Jpeg.h
new file mode 100644
index 000000000..f3743d07a
--- /dev/null
+++ b/nxcomp/Jpeg.h
@@ -0,0 +1,28 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef Jpeg_H
+#define Jpeg_H
+
+#include "Misc.h"
+#include "Unpack.h"
+
+int UnpackJpeg(T_geometry *geometry, unsigned char method, unsigned char *srcData,
+ int srcSize, int dstBpp, int dstWidth, int dstHeight,
+ unsigned char *dstData, int dstSize);
+
+#endif /* Jpeg_H */
diff --git a/nxcomp/Keeper.cpp b/nxcomp/Keeper.cpp
new file mode 100644
index 000000000..fd9b79f45
--- /dev/null
+++ b/nxcomp/Keeper.cpp
@@ -0,0 +1,600 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
+#include "Keeper.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Remove the directory if it's empty
+// since more than 30 * 24 h.
+//
+
+#define EMPTY_DIR_TIME 2592000
+
+//
+// Sleep once any ONCE entries.
+//
+
+#define ONCE 2
+
+//
+// Define this to trace messages while
+// they are allocated and deallocated.
+//
+
+#undef REFERENCES
+
+//
+// This is used for reference count.
+//
+
+#ifdef REFERENCES
+
+int File::references_ = 0;
+
+#endif
+
+bool T_older::operator () (File *a, File *b) const
+{
+ return a -> compare(b);
+}
+
+File::File()
+{
+ name_ = NULL;
+
+ size_ = 0;
+ time_ = 0;
+
+ #ifdef REFERENCES
+
+ references_++;
+
+ *logofs << "Keeper: Created new File at "
+ << this << " out of " << references_
+ << " allocated references.\n"
+ << logofs_flush;
+
+ #endif
+}
+
+//
+// TODO: This class can leak industrial amounts of memory.
+// I'm 100% sure that the desctructor is called and that
+// also the string pointed in the File structure is dele-
+// ted. Everything is logged, but still the memory is not
+// freed. This is less a problem on Windows, where the me-
+// mory occupation remains almost constant. Obviously the
+// problem lies in the STL allocators of the GNU libstc++.
+//
+
+File::~File()
+{
+ #ifdef TEST
+ *logofs << "Keeper: Deleting name for File at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ delete [] name_;
+
+ #ifdef REFERENCES
+
+ *logofs << "Keeper: Deleted File at "
+ << this << " out of " << references_
+ << " allocated references.\n"
+ << logofs_flush;
+
+ references_--;
+
+ #endif
+}
+
+bool File::compare(File *b) const
+{
+ if (this -> time_ == b -> time_)
+ {
+ return (this -> size_ < b -> size_);
+ }
+
+ return (this -> time_ < b -> time_);
+}
+
+Keeper::Keeper(int caches, int images, const char *root,
+ int sleep, int parent)
+{
+ caches_ = caches;
+ images_ = images;
+ sleep_ = sleep;
+ parent_ = parent;
+
+ root_ = new char[strlen(root) + 1];
+
+ strcpy(root_, root);
+
+ total_ = 0;
+ signal_ = 0;
+
+ files_ = new T_files;
+}
+
+Keeper::~Keeper()
+{
+ empty();
+
+ delete files_;
+
+ delete [] root_;
+}
+
+int Keeper::cleanupCaches()
+{
+ #ifdef TEST
+ *logofs << "Keeper: Looking for cache directories in '"
+ << root_ << "'.\n" << logofs_flush;
+ #endif
+
+ DIR *rootDir = opendir(root_);
+
+ if (rootDir != NULL)
+ {
+ dirent *dirEntry;
+
+ struct stat fileStat;
+
+ int baseSize = strlen(root_);
+
+ int s = 0;
+
+ while (((dirEntry = readdir(rootDir)) != NULL))
+ {
+ if (s++ % ONCE == 0) usleep(sleep_ * 1000);
+
+ if (signal_ != 0) break;
+
+ if (strcmp(dirEntry -> d_name, "cache") == 0 ||
+ strncmp(dirEntry -> d_name, "cache-", 6) == 0)
+ {
+ char *dirName = new char[baseSize + strlen(dirEntry -> d_name) + 2];
+
+ if (dirName == NULL)
+ {
+ #ifdef WARNING
+ *logofs << "Keeper: WARNING! Can't check directory entry '"
+ << dirEntry -> d_name << "'.\n" << logofs_flush;
+ #endif
+
+ delete [] dirName;
+
+ continue;
+ }
+
+ strcpy(dirName, root_);
+ strcpy(dirName + baseSize, "/");
+ strcpy(dirName + baseSize + 1, dirEntry -> d_name);
+
+ #ifdef TEST
+ *logofs << "Keeper: Checking directory '" << dirName
+ << "'.\n" << logofs_flush;
+ #endif
+
+ if (stat(dirName, &fileStat) == 0 &&
+ S_ISDIR(fileStat.st_mode) != 0)
+ {
+ //
+ // Add to repository all the "C-" and
+ // "S-" files in the given directory.
+ //
+
+ collect(dirName);
+ }
+
+ delete [] dirName;
+ }
+ }
+
+ closedir(rootDir);
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "Keeper: WARNING! Can't open NX root directory '"
+ << root_ << "'. Error is " << EGET() << " '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Can't open NX root directory '"
+ << root_ << "'. Error is " << EGET() << " '"
+ << ESTR() << "'.\n";
+ }
+
+ //
+ // Remove older files.
+ //
+
+ cleanup(caches_);
+
+ //
+ // Empty the repository.
+ //
+
+ empty();
+
+ return 1;
+}
+
+int Keeper::cleanupImages()
+{
+ #ifdef TEST
+ *logofs << "Keeper: Looking for image directory in '"
+ << root_ << "'.\n" << logofs_flush;
+ #endif
+
+ char *imagesPath = new char[strlen(root_) + strlen("/images") + 1];
+
+ if (imagesPath == NULL)
+ {
+ return -1;
+ }
+
+ strcpy(imagesPath, root_);
+ strcat(imagesPath, "/images");
+
+ //
+ // Check if the cache directory does exist.
+ //
+
+ struct stat dirStat;
+
+ if (stat(imagesPath, &dirStat) == -1)
+ {
+ #ifdef WARNING
+ *logofs << "Keeper: WARNING! Can't stat NX images cache directory '"
+ << imagesPath << ". Error is " << EGET() << " '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Can't stat NX images cache directory '"
+ << imagesPath << ". Error is " << EGET() << " '"
+ << ESTR() << "'.\n";
+
+ delete [] imagesPath;
+
+ return -1;
+ }
+
+ //
+ // Check any of the 16 directories in the
+ // images root path.
+ //
+
+ char *digitPath = new char[strlen(imagesPath) + 5];
+
+ strcpy(digitPath, imagesPath);
+
+ for (char digit = 0; digit < 16; digit++)
+ {
+ //
+ // Give up if we received a signal or
+ // our parent is gone.
+ //
+
+ if (signal_ != 0)
+ {
+ #ifdef TEST
+ *logofs << "Keeper: Signal detected. Aborting.\n"
+ << logofs_flush;
+ #endif
+
+ goto KeeperCleanupImagesAbort;
+ }
+ else if (parent_ != getppid() || parent_ == 1)
+ {
+ #ifdef WARNING
+ *logofs << "Keeper: WARNING! Parent process appears "
+ << "to be dead. Returning.\n"
+ << logofs_flush;
+ #endif
+
+ goto KeeperCleanupImagesAbort;
+
+ return 0;
+ }
+
+ sprintf(digitPath + strlen(imagesPath), "/I-%01X", digit);
+
+ //
+ // Add to the repository all the files
+ // in the given directory.
+ //
+
+ collect(digitPath);
+ }
+
+ delete [] imagesPath;
+ delete [] digitPath;
+
+ //
+ // Remove the oldest files.
+ //
+
+ cleanup(images_);
+
+ //
+ // Empty the repository.
+ //
+
+ empty();
+
+ return 1;
+
+KeeperCleanupImagesAbort:
+
+ delete [] imagesPath;
+ delete [] digitPath;
+
+ empty();
+
+ return 0;
+}
+
+int Keeper::collect(const char *path)
+{
+ #ifdef TEST
+ *logofs << "Keeper: Looking for files in directory '"
+ << path << "'.\n" << logofs_flush;
+ #endif
+
+ DIR *cacheDir = opendir(path);
+
+ if (cacheDir != NULL)
+ {
+ File *file;
+
+ dirent *dirEntry;
+
+ struct stat fileStat;
+
+ int baseSize = strlen(path);
+ int fileSize = baseSize + 3 + MD5_LENGTH * 2 + 1;
+
+ int n = 0;
+ int s = 0;
+
+ while (((dirEntry = readdir(cacheDir)) != NULL))
+ {
+ if (s++ % ONCE == 0) usleep(sleep_ * 1000);
+
+ if (signal_ != 0) break;
+
+ if (strcmp(dirEntry -> d_name, ".") == 0 ||
+ strcmp(dirEntry -> d_name, "..") == 0)
+ {
+ continue;
+ }
+
+ n++;
+
+ if (strlen(dirEntry -> d_name) == (MD5_LENGTH * 2 + 2) &&
+ (strncmp(dirEntry -> d_name, "I-", 2) == 0 ||
+ strncmp(dirEntry -> d_name, "S-", 2) == 0 ||
+ strncmp(dirEntry -> d_name, "C-", 2) == 0))
+ {
+ file = new File();
+
+ char *fileName = new char[fileSize];
+
+ if (file == NULL || fileName == NULL)
+ {
+ #ifdef WARNING
+ *logofs << "Keeper: WARNING! Can't add file '"
+ << dirEntry -> d_name << "' to repository.\n"
+ << logofs_flush;
+ #endif
+
+ delete [] fileName;
+
+ delete file;
+
+ continue;
+ }
+
+ strcpy(fileName, path);
+ strcpy(fileName + baseSize, "/");
+ strcpy(fileName + baseSize + 1, dirEntry -> d_name);
+
+ file -> name_ = fileName;
+
+ #ifdef DEBUG
+ *logofs << "Keeper: Adding file '" << file -> name_
+ << "'.\n" << logofs_flush;
+ #endif
+
+ if (stat(file -> name_, &fileStat) == -1)
+ {
+ #ifdef WARNING
+ *logofs << "Keeper: WARNING! Can't stat NX file '"
+ << file -> name_ << ". Error is " << EGET()
+ << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ delete file;
+
+ continue;
+ }
+
+ file -> size_ = fileStat.st_size;
+ file -> time_ = fileStat.st_mtime;
+
+ files_ -> insert(T_files::value_type(file));
+
+ total_ += file -> size_;
+ }
+ }
+
+ closedir(cacheDir);
+
+ if (n == 0)
+ {
+ time_t now = time(NULL);
+
+ if (now > 0 && stat(path, &fileStat) == 0)
+ {
+ #ifdef TEST
+ *logofs << "Keeper: Empty NX subdirectory '" << path
+ << "' unused since " << now - fileStat.st_mtime
+ << " S.\n" << logofs_flush;
+ #endif
+
+ if (now - fileStat.st_mtime > EMPTY_DIR_TIME)
+ {
+ #ifdef TEST
+ *logofs << "Keeper: Removing empty NX subdirectory '"
+ << path << "'.\n" << logofs_flush;
+ #endif
+
+ rmdir(path);
+ }
+ }
+ }
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "Keeper: WARNING! Can't open NX subdirectory '"
+ << path << ". Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Can't open NX subdirectory '"
+ << path << ". Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+ }
+
+ return 1;
+}
+
+int Keeper::cleanup(int threshold)
+{
+ #ifdef TEST
+ *logofs << "Keeper: Deleting the oldest files with "
+ << files_ -> size() << " elements threshold "
+ << threshold << " and size " << total_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // At least some versions of the standard
+ // library don't allow erasing an element
+ // while looping. This is not the most ef-
+ // ficient way to release the objects, but
+ // it's better than making a copy of the
+ // container.
+ //
+
+ while (total_ > threshold && files_ -> size() > 0)
+ {
+ T_files::iterator i = files_ -> begin();
+
+ File *file = *i;
+
+ #ifdef TEST
+ *logofs << "Keeper: Removing '" << file -> name_
+ << "' with time " << file -> time_ << " and size "
+ << file -> size_ << ".\n" << logofs_flush;
+ #endif
+
+ unlink(file -> name_);
+
+ total_ -= file -> size_;
+
+ #ifdef DEBUG
+ *logofs << "Keeper: Going to delete the file at "
+ << file << " while cleaning up.\n"
+ << logofs_flush;
+ #endif
+
+ delete file;
+
+ #ifdef DEBUG
+ *logofs << "Keeper: Going to erase the element "
+ << "while cleaning up.\n"
+ << logofs_flush;
+ #endif
+
+ files_ -> erase(i);
+ }
+
+ #ifdef TEST
+ *logofs << "Keeper: Bytes in repository are "
+ << total_ << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void Keeper::empty()
+{
+ #ifdef TEST
+ *logofs << "Keeper: Getting rid of files in repository.\n"
+ << logofs_flush;
+ #endif
+
+ while (files_ -> size() > 0)
+ {
+ T_files::iterator i = files_ -> begin();
+
+ File *file = *i;
+
+ #ifdef DEBUG
+ *logofs << "Keeper: Going to delete the file at "
+ << file << " while emptying.\n"
+ << logofs_flush;
+ #endif
+
+ delete file;
+
+ #ifdef DEBUG
+ *logofs << "Keeper: Going to erase the element "
+ << "while emptying.\n"
+ << logofs_flush;
+ #endif
+
+ files_ -> erase(i);
+ }
+
+ total_ = 0;
+
+ #ifdef TEST
+ *logofs << "Keeper: Bytes in repository are "
+ << total_ << ".\n" << logofs_flush;
+ #endif
+}
diff --git a/nxcomp/Keeper.h b/nxcomp/Keeper.h
new file mode 100644
index 000000000..c357b2116
--- /dev/null
+++ b/nxcomp/Keeper.h
@@ -0,0 +1,191 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef Keeper_H
+#define Keeper_H
+
+#include "Misc.h"
+#include "Types.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Define this to check how many file
+// nodes are allocated and deallocated.
+//
+
+#undef REFERENCES
+
+class Keeper;
+
+class File
+{
+ friend class Keeper;
+
+ public:
+
+ File();
+
+ ~File();
+
+ //
+ // Allow sort by time and size. If time
+ // is the same, keep the bigger element.
+ //
+
+ bool compare(File *b) const;
+
+ private:
+
+ char *name_;
+
+ int size_;
+ time_t time_;
+
+ #ifdef REFERENCES
+
+ static int references_;
+
+ #endif
+};
+
+class Keeper
+{
+ public:
+
+ Keeper(int caches, int images, const char *root,
+ int sleep, int parent);
+
+ ~Keeper();
+
+ //
+ // Call this just once.
+ //
+
+ int cleanupCaches();
+
+ //
+ // Call this at any given interval.
+ //
+
+ int cleanupImages();
+
+ //
+ // Call this if it's time to exit.
+ //
+
+ void setSignal(int signal)
+ {
+ signal_ = signal;
+ }
+
+ int getSignal()
+ {
+ return signal_;
+ }
+
+ int getParent()
+ {
+ return parent_;
+ }
+
+ private:
+
+ //
+ // Get a list of files in directory.
+ //
+
+ int collect(const char *path);
+
+ //
+ // Sort the collected files according to
+ // last modification time and delete the
+ // older ones until disk size is below
+ // the threshold.
+ //
+
+ int cleanup(int threshold);
+
+ //
+ // Empty the files repository.
+ //
+
+ void empty();
+
+ //
+ // Size in bytes of total allowed
+ // storage for persistent caches.
+ //
+
+ int caches_;
+
+ //
+ // Size in bytes of total allowed
+ // storage for images cache.
+ //
+
+ int images_;
+
+ //
+ // Path of the NX root directory.
+ //
+
+ char *root_;
+
+ //
+ // The little delay to be introduced
+ // before reading a new entry.
+ //
+
+ int sleep_;
+
+ //
+ // Total size of files in repository.
+ //
+
+ int total_;
+
+ //
+ // The parent process, so we can exit
+ // if it is gone.
+ //
+
+ int parent_;
+
+ //
+ // Set if we need to give up because
+ // of a signal.
+ //
+
+ int signal_;
+
+ //
+ // Repository where to collect files.
+ //
+
+ T_files *files_;
+};
+
+#endif /* Keeper_H */
+
diff --git a/nxcomp/LICENSE b/nxcomp/LICENSE
new file mode 100644
index 000000000..2b3203474
--- /dev/null
+++ b/nxcomp/LICENSE
@@ -0,0 +1,37 @@
+Copyright (c) 2001, 2010 NoMachine - http://www.nomachine.com/.
+
+NXCOMP library and NX extensions to X are copyright of NoMachine.
+Redistribution and use of this software is allowed according to the
+following terms:
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License Version 2, and
+not any other version, as published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTA-
+BILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, you can request a copy to NoMachine
+or write to the Free Software Foundation, Inc., 59 Temple Place, Suite
+330, Boston, MA 02111-1307 USA
+
+Parts of this software are derived from DXPC project. These copyright
+notices apply to original DXPC code:
+
+Redistribution and use in source and binary forms are permitted provi-
+ded that the above copyright notice and this paragraph are duplicated
+in all such forms.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+Copyright (c) 1995,1996 Brian Pane
+Copyright (c) 1996,1997 Zachary Vonler and Brian Pane
+Copyright (c) 1999 Kevin Vigor and Brian Pane
+Copyright (c) 2000,2006 Gian Filippo Pinzari and Brian Pane
+
+All rights reserved.
diff --git a/nxcomp/List.cpp b/nxcomp/List.cpp
new file mode 100644
index 000000000..1ba104b59
--- /dev/null
+++ b/nxcomp/List.cpp
@@ -0,0 +1,100 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "List.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Define this to know how many instances
+// are allocated and deallocated.
+//
+
+#undef REFERENCES
+
+#ifdef REFERENCES
+
+int List::references_ = 0;
+
+#endif
+
+List::List()
+{
+ #ifdef REFERENCES
+
+ references_++;
+
+ *logofs << "List: Created new List at "
+ << this << " out of " << references_
+ << " allocated references.\n" << logofs_flush;
+ #endif
+}
+
+List::~List()
+{
+ #ifdef REFERENCES
+
+ references_--;
+
+ *logofs << "List: Deleted List at "
+ << this << " out of " << references_
+ << " allocated references.\n" << logofs_flush;
+ #endif
+}
+
+void List::remove(int value)
+{
+ for (T_list::iterator i = list_.begin(); i != list_.end(); i++)
+ {
+ if (*i == value)
+ {
+ list_.erase(i);
+
+ return;
+ }
+ }
+
+ #ifdef PANIC
+ *logofs << "List: PANIC! Should not try to remove "
+ << "an element not found in the list.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Should not try to remove "
+ << "an element not found in the list.\n";
+
+ HandleAbort();
+}
+
+void List::rotate()
+{
+ if (list_.size() > 1)
+ {
+ int value = *(list_.begin());
+
+ list_.pop_front();
+
+ list_.push_back(value);
+ }
+}
diff --git a/nxcomp/List.h b/nxcomp/List.h
new file mode 100644
index 000000000..b5e41ae11
--- /dev/null
+++ b/nxcomp/List.h
@@ -0,0 +1,87 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef List_H
+#define List_H
+
+#include "Misc.h"
+#include "Types.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Define this to log when lists are
+// allocated and deallocated.
+//
+
+#undef REFERENCES
+
+class List
+{
+ public:
+
+ List();
+
+ ~List();
+
+ int getSize()
+ {
+ return list_.size();
+ }
+
+ T_list &getList()
+ {
+ return list_;
+ }
+
+ T_list copyList()
+ {
+ return list_;
+ }
+
+ void add(int value)
+ {
+ list_.push_back(value);
+ }
+
+ void remove(int value);
+
+ void rotate();
+
+ private:
+
+ //
+ // The list container.
+ //
+
+ T_list list_;
+
+ #ifdef REFERENCES
+
+ static int references_;
+
+ #endif
+};
+
+#endif /* List_H */
diff --git a/nxcomp/ListFontsReply.cpp b/nxcomp/ListFontsReply.cpp
new file mode 100644
index 000000000..5bace82f2
--- /dev/null
+++ b/nxcomp/ListFontsReply.cpp
@@ -0,0 +1,204 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "ListFontsReply.h"
+
+#include "ServerCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef DUMP
+#undef TEST
+#undef DEBUG
+
+ListFontsReplyStore::ListFontsReplyStore(StaticCompressor *compressor)
+
+ : MessageStore(compressor)
+{
+ enableCache = LISTFONTSREPLY_ENABLE_CACHE;
+ enableData = LISTFONTSREPLY_ENABLE_DATA;
+ enableSplit = LISTFONTSREPLY_ENABLE_SPLIT;
+ enableCompress = LISTFONTSREPLY_ENABLE_COMPRESS;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ enableCompress = LISTFONTSREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7;
+ }
+
+ dataLimit = LISTFONTSREPLY_DATA_LIMIT;
+ dataOffset = LISTFONTSREPLY_DATA_OFFSET;
+
+ cacheSlots = LISTFONTSREPLY_CACHE_SLOTS;
+ cacheThreshold = LISTFONTSREPLY_CACHE_THRESHOLD;
+ cacheLowerThreshold = LISTFONTSREPLY_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+ListFontsReplyStore::~ListFontsReplyStore()
+{
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int ListFontsReplyStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ListFontsReplyMessage *listFontsReply = (ListFontsReplyMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ listFontsReply -> number_of_names = GetUINT(buffer + 8, bigEndian);
+
+ //
+ // Clean up padding bytes.
+ //
+
+ if ((int) size > dataOffset)
+ {
+ unsigned int current;
+ unsigned int length;
+ unsigned int nstringInNames;
+
+ unsigned char *end = NULL;
+ unsigned char *pad = NULL;
+
+ #ifdef DUMP
+
+ *logofs << "\n" << logofs_flush;
+
+ *logofs << "Number of STRING8 " << listFontsReply -> number_of_names << ".\n" << logofs_flush;
+
+ *logofs << "Size " << size << ".\n" << logofs_flush;
+
+ DumpHexData(buffer, size);
+
+ *logofs << "\n" << logofs_flush;
+
+ #endif
+
+ length = LISTFONTSREPLY_DATA_OFFSET;
+
+ for (nstringInNames = 0;
+ nstringInNames < listFontsReply -> number_of_names &&
+ listFontsReply -> number_of_names > 0;
+ nstringInNames++)
+ {
+ //
+ // Start with offset LISTFONTSREPLY_DATA_OFFSET 32.
+ //
+
+ current = buffer[length];
+
+ length += current + 1;
+
+ #ifdef DUMP
+ *logofs << "\nString number : " << nstringInNames << " Current length : "
+ << current << "\n" << logofs_flush;
+ #endif
+ }
+
+ #ifdef DUMP
+ *logofs << "\nFinal length " << length << "\n" << logofs_flush;
+ #endif
+
+ end = ((unsigned char *) buffer) + size;
+
+ for (pad = ((unsigned char *) buffer) + length; pad < end; pad++)
+ {
+ *pad = 0;
+
+ #ifdef DUMP
+ *logofs << "\nPadding ." << "\n" << logofs_flush;
+ #endif
+ }
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int ListFontsReplyStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ListFontsReplyMessage *listFontsReply = (ListFontsReplyMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ PutUINT(listFontsReply -> number_of_names, buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at "
+ << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void ListFontsReplyStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ ListFontsReplyMessage *listFontsReply = (ListFontsReplyMessage *) message;
+
+ *logofs << name() << ": Identity number_of_names "
+ << listFontsReply -> number_of_names << ", size "
+ << listFontsReply -> size_ << ".\n";
+
+ #endif
+}
+
+void ListFontsReplyStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ //
+ // Field number_of_names.
+ //
+
+ md5_append(md5_state_, buffer + 8, 2);
+}
diff --git a/nxcomp/ListFontsReply.h b/nxcomp/ListFontsReply.h
new file mode 100644
index 000000000..078fd7ebc
--- /dev/null
+++ b/nxcomp/ListFontsReply.h
@@ -0,0 +1,139 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef ListFontsReply_H
+#define ListFontsReply_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define LISTFONTSREPLY_ENABLE_CACHE 1
+#define LISTFONTSREPLY_ENABLE_DATA 1
+#define LISTFONTSREPLY_ENABLE_SPLIT 0
+#define LISTFONTSREPLY_ENABLE_COMPRESS 1
+
+#define LISTFONTSREPLY_DATA_LIMIT 1048576 - 32
+#define LISTFONTSREPLY_DATA_OFFSET 32
+
+#define LISTFONTSREPLY_CACHE_SLOTS 200
+#define LISTFONTSREPLY_CACHE_THRESHOLD 20
+#define LISTFONTSREPLY_CACHE_LOWER_THRESHOLD 5
+
+#define LISTFONTSREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7 0
+
+//
+// The message class.
+//
+
+class ListFontsReplyMessage : public Message
+{
+ friend class ListFontsReplyStore;
+
+ public:
+
+ ListFontsReplyMessage()
+ {
+ }
+
+ ~ListFontsReplyMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned short int number_of_names;
+};
+
+class ListFontsReplyStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ ListFontsReplyStore(StaticCompressor *compressor);
+
+ virtual ~ListFontsReplyStore();
+
+ virtual const char *name() const
+ {
+ return "ListFontsReply";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_ListFonts;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(ListFontsReplyMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ protected:
+
+ virtual Message *create() const
+ {
+ return new ListFontsReplyMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new ListFontsReplyMessage((const ListFontsReplyMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (ListFontsReplyMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* ListFontsReply_H */
diff --git a/nxcomp/Loop.cpp b/nxcomp/Loop.cpp
new file mode 100644
index 000000000..92b6fc28f
--- /dev/null
+++ b/nxcomp/Loop.cpp
@@ -0,0 +1,16683 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <errno.h>
+#include <signal.h>
+#include <setjmp.h>
+
+#include <math.h>
+#include <ctype.h>
+#include <string.h>
+#include <dirent.h>
+#include <pwd.h>
+
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/resource.h>
+#include <sys/utsname.h>
+
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "Misc.h"
+
+#ifdef __sun
+#include <strings.h>
+#endif
+
+//
+// MacOSX 10.4 defines socklen_t. This is
+// intended to ensure compatibility with
+// older versions.
+//
+
+#ifdef __APPLE__
+#include <AvailabilityMacros.h>
+#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_3
+typedef int socklen_t;
+#endif
+#endif
+
+#ifdef _AIX
+#include <strings.h>
+#include <sys/select.h>
+#endif
+
+#ifndef __CYGWIN32__
+#include <netinet/tcp.h>
+#include <sys/un.h>
+#endif
+
+//
+// NX include files.
+//
+
+#include "NX.h"
+#include "NXalert.h"
+
+#include "Misc.h"
+#include "Control.h"
+#include "Socket.h"
+#include "Statistics.h"
+#include "Auth.h"
+#include "Keeper.h"
+#include "Agent.h"
+
+#include "ClientProxy.h"
+#include "ServerProxy.h"
+
+#include "Message.h"
+
+//
+// System specific defines.
+//
+
+#if defined(__EMX__) || defined(__CYGWIN32__)
+
+struct sockaddr_un
+{
+ u_short sun_family;
+ char sun_path[108];
+};
+
+#endif
+
+//
+// HP-UX hides this define.
+//
+
+#if defined(hpux) && !defined(RLIM_INFINITY)
+
+#define RLIM_INFINITY 0x7fffffff
+
+#endif
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Enable log output in signal handler.
+// This is likely to hang the proxy at
+// random, at least on Linux.
+//
+
+#undef UNSAFE
+
+//
+// Let all logs go to the standard error.
+// This is useful to interleave the Xlib
+// log output with the proxy output in a
+// single file.
+//
+
+#undef MIXED
+
+//
+// Define this to check if the client and
+// server caches match at shutdown. This
+// is a test facility as it requires that
+// both proxies are running on the same
+// host.
+//
+
+#undef MATCH
+
+//
+// If defined, reduce the size of the log
+// file and be sure it never exceeds the
+// limit.
+//
+
+#undef QUOTA
+
+//
+// If defined, force very strict limits for
+// the proxy tokens and force the proxy to
+// enter often in congestion state.
+//
+
+#undef STRICT
+
+//
+// Print a line in the log if the time we
+// spent inside the select or handling the
+// messages exceeded a given time value.
+//
+
+#undef TIME
+
+//
+// This can be useful when testing the forwarding
+// of the SSHD connection by nxssh to the agent.
+// The debug output will go to a well known file
+// that will be opened also by nxssh when BINDER
+// is enabled there.
+//
+
+#undef BINDER
+
+//
+// Define this to override the limits on
+// the core dump size.
+//
+
+#define COREDUMPS
+
+//
+// Upper limit of pre-allocated buffers
+// for string parameters.
+//
+
+#define DEFAULT_STRING_LENGTH 256
+
+//
+// Maximum length of remote options data
+// passed by peer proxy at startup.
+//
+
+#define DEFAULT_REMOTE_OPTIONS_LENGTH 512
+
+//
+// Maximum length of NX display string.
+//
+
+#define DEFAULT_DISPLAY_OPTIONS_LENGTH 1024
+
+//
+// Maximum number of cache file names to
+// send to the server side.
+//
+
+#define DEFAULT_REMOTE_CACHE_ENTRIES 100
+
+//
+// Maximum length of remote options string
+// that can be received from the peer proxy.
+//
+
+#define MAXIMUM_REMOTE_OPTIONS_LENGTH 4096
+
+//
+// Macro is true if we determined our proxy
+// mode.
+//
+
+#define WE_SET_PROXY_MODE (control -> ProxyMode != proxy_undefined)
+
+//
+// Macro is true if our side is the one that
+// should connect to remote.
+//
+
+#define WE_INITIATE_CONNECTION (*connectHost != '\0')
+
+//
+// Is true if we must provide our credentials
+// to the remote peer.
+//
+
+#define WE_PROVIDE_CREDENTIALS (control -> ProxyMode == proxy_server)
+
+//
+// Is true if we listen for a local forwarder
+// that will tunnel the traffic through a SSH
+// or HTTP link.
+//
+
+#define WE_LISTEN_FORWARDER (control -> ProxyMode == proxy_server && \
+ listenPort != -1)
+
+//
+// You must define FLUSH in Misc.h if
+// you want an immediate flush of the
+// log output.
+//
+
+ostream *logofs = NULL;
+
+//
+// Other stream destriptors used for
+// logging.
+//
+
+ostream *statofs = NULL;
+ostream *errofs = NULL;
+
+//
+// Save standard error's rdbuf here
+// and restore it when exiting.
+//
+
+static streambuf *errsbuf = NULL;
+
+//
+// Allow faults to be recovered by
+// jumping back into the main loop.
+//
+
+jmp_buf context;
+
+//
+// Provide operational parameters.
+//
+
+Control *control = NULL;
+
+//
+// Collect and print statistics.
+//
+
+Statistics *statistics = NULL;
+
+//
+// Keep data for X11 authentication.
+//
+
+Auth *auth = NULL;
+
+//
+// This class makes the hard work.
+//
+
+Proxy *proxy = NULL;
+
+//
+// Used to handle memory-to-memory
+// transport to the X agent.
+//
+
+Agent *agent = NULL;
+
+//
+// The image cache house-keeping class.
+//
+
+Keeper *keeper = NULL;
+
+//
+// Callback set by the child process
+// to be notified about signals.
+//
+
+int (*handler)(int) = NULL;
+
+//
+// Signal handling functions.
+//
+
+void DisableSignals();
+void EnableSignals();
+void InstallSignals();
+
+static void RestoreSignals();
+static void HandleSignal(int signal);
+
+//
+// Signal handling utilities.
+//
+
+static void InstallSignal(int signal, int action);
+static void RestoreSignal(int signal);
+
+static int HandleChildren();
+
+static int HandleChild(int child);
+static int CheckChild(int pid, int status);
+static int WaitChild(int child, const char *label, int force);
+
+int CheckParent(const char *name, const char *type, int parent);
+
+void RegisterChild(int child);
+
+static int CheckAbort();
+
+//
+// Timer handling utilities.
+//
+
+void SetTimer(int timeout);
+void ResetTimer();
+
+static void HandleTimer(int signal);
+
+//
+// Kill or check a running child.
+//
+
+static int KillProcess(int pid, const char *label, int signal, int wait);
+static int CheckProcess(int pid, const char *label);
+
+//
+// Macros used to test the pid of a child.
+//
+
+#define IsFailed(pid) ((pid) < 0)
+#define IsRunning(pid) ((pid) > 1)
+#define IsNotRunning(pid) ((pid) == 0)
+#define IsRestarting(pid) ((pid) == 1)
+
+#define SetNotRunning(pid) ((pid) = 0)
+#define SetRestarting(pid) ((pid) = 1)
+
+//
+// Start or restart the house-keeper process.
+//
+
+static int StartKeeper();
+
+//
+// Cleanup functions.
+//
+
+void CleanupConnections();
+void CleanupListeners();
+void CleanupSockets();
+void CleanupGlobal();
+
+static void CleanupChildren();
+static void CleanupLocal();
+static void CleanupKeeper();
+static void CleanupStreams();
+
+//
+// Loop forever until the connections
+// to the peer proxy is dropped.
+//
+
+static void WaitCleanup();
+
+//
+// Initialization functions.
+//
+
+static int InitBeforeNegotiation();
+static int SetupProxyConnection();
+static int InitAfterNegotiation();
+static int SetupProxyInstance();
+static int SetupAuthInstance();
+static int SetupAgentInstance();
+
+static int SetupTcpSocket();
+static int SetupUnixSocket();
+static int SetupServiceSockets();
+static int SetupDisplaySocket(int &xServerAddrFamily, sockaddr *&xServerAddr,
+ unsigned int &xServerAddrLength);
+
+//
+// Setup a listening socket and accept
+// a new connection.
+//
+
+static int ListenConnection(int port, const char *label);
+static int AcceptConnection(int fd, int domain, const char *label);
+
+//
+// Other convenience functions.
+//
+
+static int WaitForRemote(int portNum);
+static int ConnectToRemote(const char *const hostName, int portNum);
+
+static int SendProxyOptions(int fd);
+static int SendProxyCaches(int fd);
+static int ReadProxyVersion(int fd);
+static int ReadProxyOptions(int fd);
+static int ReadProxyCaches(int fd);
+static int ReadForwarderVersion(int fd);
+static int ReadForwarderOptions(int fd);
+
+static int ReadRemoteData(int fd, char *buffer, int size, char stop);
+static int WriteLocalData(int fd, const char *buffer, int size);
+
+static void PrintVersionInfo();
+static void PrintProcessInfo();
+static void PrintConnectionInfo();
+static void PrintUsageInfo(const char *option, const int error);
+static void PrintOptionIgnored(const char *type, const char *name, const char *value);
+
+//
+// This is not static to avoid a warning.
+//
+
+void PrintCopyrightInfo();
+
+static const char *GetOptions(const char *options);
+static const char *GetArg(int &argi, int argc, const char **argv);
+static int CheckArg(const char *type, const char *name, const char *value);
+static int ParseArg(const char *type, const char *name, const char *value);
+static int ValidateArg(const char *type, const char *name, const char *value);
+static int LowercaseArg(const char *type, const char *name, char *value);
+static int CheckSignal(int signal);
+
+extern "C"
+{
+ int ParseCommandLineOptions(int argc, const char **argv);
+ int ParseEnvironmentOptions(const char *env, int force);
+ int ParseBindOptions(char **host, int *port);
+}
+
+static int ParseFileOptions(const char *file);
+static int ParseRemoteOptions(char *opts);
+static int ParseForwarderOptions(char *opts);
+
+//
+// These functions are used to parse literal
+// values provided by the user and set the
+// control parameters accordingly.
+//
+
+static int ParseLinkOption(const char *opt);
+static int ParseBitrateOption(const char *opt);
+static int ParseCacheOption(const char *opt);
+static int ParseShmemOption(const char *opt);
+static int ParseImagesOption(const char *opt);
+static int ParsePackOption(const char *opt);
+
+//
+// Set host and port where NX proxy is supposed
+// to be listening in case such parameters are
+// given on the command line.
+//
+
+static int ParseHostOption(const char *opt, char *host, int &port);
+
+//
+// Translate a font server port specification
+// to the corresponding Unix socket path.
+//
+
+static int ParseFontPath(char *path);
+
+//
+// Determine the interface where to listen for
+// the remote proxy connection or the local
+// forwarder.
+//
+
+static int ParseListenOption(int &interface);
+
+//
+// Translate a pack method id in a literal.
+//
+
+static int ParsePackMethod(const int method, const int quality);
+
+//
+// Try to increase the size of the allowed
+// core dumps.
+//
+
+static int SetCore();
+
+//
+// Set the proxy mode to either client or
+// server.
+//
+
+static int SetMode(int mode);
+
+//
+// Determine the path of the NX_* directories
+// from the environment.
+//
+
+static int SetDirectories();
+
+//
+// Set the defaults used for the log file and
+// statistics.
+//
+
+static int SetLogs();
+
+//
+// Check if local and remote protocol versions
+// are compatible and, eventually, downgrade
+// local version to the minimum level that is
+// known to work.
+//
+
+static int SetVersion();
+
+//
+// Setup the listening TCP ports used for the
+// additional channels according to user's
+// wishes.
+//
+
+static int SetPorts();
+
+//
+// Set the maximum number of open descriptors.
+//
+
+static int SetDescriptors();
+
+//
+// Set the path used for choosing the cache.
+// It must be selected after determining the
+// session type.
+//
+
+static int SetCaches();
+
+//
+// Initialize, one after the other, all the
+// configuration parameters.
+//
+
+static int SetParameters();
+
+//
+// Set the specific configuration parameter.
+//
+
+static int SetSession();
+static int SetStorage();
+static int SetShmem();
+static int SetPack();
+static int SetImages();
+static int SetLimits();
+
+//
+// Set up the control parameters based on
+// the link speed negotiated between the
+// proxies.
+//
+
+static int SetLink();
+
+static int SetLinkModem();
+static int SetLinkIsdn();
+static int SetLinkAdsl();
+static int SetLinkWan();
+static int SetLinkLan();
+
+//
+// Adjust the compression parameters.
+//
+
+static int SetCompression();
+
+static int SetCompressionModem();
+static int SetCompressionIsdn();
+static int SetCompressionAdsl();
+static int SetCompressionWan();
+static int SetCompressionLan();
+
+//
+// Determine the NX paths based on the
+// user's parameters or the environment.
+//
+
+char *GetClientPath();
+
+static char *GetSystemPath();
+static char *GetHomePath();
+static char *GetTempPath();
+static char *GetRootPath();
+static char *GetCachePath();
+static char *GetImagesPath();
+static char *GetSessionPath();
+static char *GetLastCache(char *list, const char *path);
+
+static int OpenLogFile(char *name, ostream *&stream);
+static int ReopenLogFile(char *name, ostream *&stream, int limit);
+
+//
+// Perform operations on the managed
+// descriptors in the main loop.
+//
+
+static void handleCheckSessionInLoop();
+static void handleCheckBitrateInLoop();
+
+#if defined(TEST) || defined(INFO)
+static void handleCheckSelectInLoop(int &setFDs, fd_set &readSet,
+ fd_set &writeSet, T_timestamp selectTs);
+static void handleCheckResultInLoop(int &resultFDs, int &errorFDs, int &setFDs, fd_set &readSet,
+ fd_set &writeSet, struct timeval &selectTs,
+ struct timeval &startTs);
+static void handleCheckStateInLoop(int &setFDs);
+#endif
+
+static void handleCheckSessionInConnect();
+
+static inline void handleSetReadInLoop(fd_set &readSet, int &setFDs, struct timeval &selectTs);
+static inline void handleSetWriteInLoop(fd_set &writeSet, int &setFDs, struct timeval &selectTs);
+static inline void handleSetListenersInLoop(fd_set &writeSet, int &setFDs);
+static inline void handleSetAgentInLoop(int &setFDs, fd_set &readSet, fd_set &writeSet,
+ struct timeval &selectTs);
+
+static void handleAlertInLoop();
+static void handleStatisticsInLoop();
+
+static inline void handleAgentInLoop(int &resultFDs, int &errorFDs, int &setFDs, fd_set &readSet,
+ fd_set &writeSet, struct timeval &selectTs);
+static inline void handleAgentLateInLoop(int &resultFDs, int &errorFDs, int &setFDs, fd_set &readSet,
+ fd_set &writeSet, struct timeval &selectTs);
+
+static inline void handleReadableInLoop(int &resultFDs, fd_set &readSet);
+static inline void handleWritableInLoop(int &resultFDs, fd_set &writeSet);
+
+static inline void handleRotateInLoop();
+static inline void handleEventsInLoop();
+static inline void handleFlushInLoop();
+
+//
+// Manage the proxy link during the negotiation
+// phase.
+//
+
+static void handleNegotiationInLoop(int &setFDs, fd_set &readSet,
+ fd_set &writeSet, T_timestamp &selectTs);
+
+//
+// Print the 'terminating' messages in the
+// session log.
+//
+
+static inline void handleTerminatingInLoop();
+static inline void handleTerminatedInLoop();
+
+//
+// Monitor the size of the log file.
+//
+
+static void handleLogReopenInLoop(T_timestamp &logsTs, T_timestamp &nowTs);
+
+//
+// Directory where the NX binaries and libraries reside.
+//
+
+static char systemDir[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// Directory used for temporary files.
+//
+
+static char tempDir[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// Actually the full path to the client.
+//
+
+static char clientDir[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// User's home directory.
+//
+
+static char homeDir[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// Root of directory structure to be created by proxy.
+//
+
+static char rootDir[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// Root of statistics and log files to be created by proxy.
+//
+
+static char sessionDir[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// Log files for errors and statistics. Error log is
+// the place where we print also debug informations.
+// Both files are closed, deleted and reopened as
+// their size exceed the limit set in control class.
+// The session log is not reopened, as it is used by
+// the NX client and server to track the advance of
+// the session.
+//
+
+static char errorsFileName[DEFAULT_STRING_LENGTH] = { 0 };
+static char statsFileName[DEFAULT_STRING_LENGTH] = { 0 };
+static char sessionFileName[DEFAULT_STRING_LENGTH] = { 0 };
+static char optionsFileName[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// String literal representing selected link speed
+// parameter. Value is translated in control values
+// used by proxies to stay synchronized.
+//
+
+static char linkSpeedName[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// String literal representing selected
+// cache size.
+//
+
+static char cacheSizeName[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// String literal representing selected
+// shared memory segment size.
+//
+
+static char shsegSizeName[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// String literal of images cache size.
+//
+
+static char imagesSizeName[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// String literal for bandwidth limit.
+//
+
+static char bitrateLimitName[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// String literal for image packing method.
+//
+
+static char packMethodName[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// Product name provided by the server or client.
+//
+
+static char productName[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// Its corresponding value from NXpack.h.
+//
+
+static int packMethod = -1;
+static int packQuality = -1;
+
+//
+// String literal for session type. Persistent caches
+// are searched in directory whose name matches this
+// parameter.
+//
+
+static char sessionType[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// Unique id assigned to session. It is used as
+// name of directory where all files are placed.
+//
+
+static char sessionId[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// Set if we already parsed the options.
+//
+
+static int parsedOptions = 0;
+static int parsedCommand = 0;
+
+//
+// Buffer data received from the remote proxy at
+// session negotiation.
+//
+
+static char remoteData[MAXIMUM_REMOTE_OPTIONS_LENGTH] = { 0 };
+static int remotePosition = 0;
+
+//
+// Main loop file descriptors.
+//
+
+static int tcpFD = -1;
+static int unixFD = -1;
+static int cupsFD = -1;
+static int auxFD = -1;
+static int smbFD = -1;
+static int mediaFD = -1;
+static int httpFD = -1;
+static int fontFD = -1;
+static int slaveFD = -1;
+static int proxyFD = -1;
+
+//
+// Used for internal communication
+// with the X agent.
+//
+
+static int agentFD[2] = { -1, -1 };
+
+//
+// Flags determining which protocols and
+// ports are forwarded.
+//
+
+int useUnixSocket = 1;
+
+static int useTcpSocket = 1;
+static int useCupsSocket = 0;
+static int useAuxSocket = 0;
+static int useSmbSocket = 0;
+static int useMediaSocket = 0;
+static int useHttpSocket = 0;
+static int useFontSocket = 0;
+static int useSlaveSocket = 0;
+static int useAgentSocket = 0;
+
+//
+// Set if the launchd service is running
+// and its socket must be used as X socket.
+//
+
+static int useLaunchdSocket = 0;
+
+//
+// Set by user if he/she wants to modify
+// the default TCP_NODELAY option as set
+// in control.
+//
+
+static int useNoDelay = -1;
+
+//
+// Set if user wants to override default
+// flush timeout set according to link.
+//
+
+static int usePolicy = -1;
+
+//
+// Set if user wants to hide the RENDER
+// extension or wants to short-circuit
+// some simple replies at client side.
+//
+
+static int useRender = -1;
+static int useTaint = -1;
+
+//
+// Set if the user wants to reduce the
+// nominal size of the token messages
+// exchanged between the proxies.
+//
+
+static int useStrict = -1;
+
+//
+// Set if the proxy is running as part
+// of SSH on the client.
+//
+
+static int useEncryption = -1;
+
+//
+// Name of Unix socket created by the client proxy to
+// accept client connections. File must be unlinked
+// by cleanup function.
+//
+
+static char unixSocketName[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// Other parameters.
+//
+
+static char connectHost[DEFAULT_STRING_LENGTH] = { 0 };
+static char acceptHost[DEFAULT_STRING_LENGTH] = { 0 };
+static char listenHost[DEFAULT_STRING_LENGTH] = { 0 };
+static char displayHost[DEFAULT_STRING_LENGTH] = { 0 };
+static char authCookie[DEFAULT_STRING_LENGTH] = { 0 };
+
+static int proxyPort = DEFAULT_NX_PROXY_PORT;
+static int xPort = DEFAULT_NX_X_PORT;
+
+//
+// Used to setup the connection the real
+// X display socket.
+//
+
+static int xServerAddrFamily = -1;
+static sockaddr *xServerAddr = NULL;
+static unsigned int xServerAddrLength = 0;
+
+//
+// The port where the local proxy will await
+// the peer connection or where the remote
+// proxy will be contacted.
+//
+
+static int listenPort = -1;
+static int connectPort = -1;
+
+//
+// Helper channels are disabled by default.
+//
+
+static int cupsPort = -1;
+static int auxPort = -1;
+static int smbPort = -1;
+static int mediaPort = -1;
+static int httpPort = -1;
+static int slavePort = -1;
+
+//
+// Can be either a port number or a Unix
+// socket.
+//
+
+static char fontPort[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// Host and port where the existing proxy
+// is running.
+//
+
+static char bindHost[DEFAULT_STRING_LENGTH] = { 0 };
+static int bindPort = -1;
+
+//
+// Pointers to the callback functions and
+// parameter set by the agent
+//
+
+static void (*flushCallback)(void *, int) = NULL;
+static void *flushParameter = NULL;
+
+static void (*statisticsCallback)(void *, int) = NULL;
+static void *statisticsParameter = NULL;
+
+//
+// State variables shared between the init
+// function and the main loop.
+//
+
+T_timestamp initTs;
+T_timestamp startTs;
+T_timestamp logsTs;
+T_timestamp nowTs;
+
+int diffTs;
+
+//
+// This is set to the main proxy process id.
+//
+
+int lastProxy = 0;
+
+//
+// Set to last dialog process launched by proxy.
+//
+
+int lastDialog = 0;
+
+//
+// Set to watchdog process launched by proxy.
+//
+
+int lastWatchdog = 0;
+
+//
+// Set if a cache house-keeper process is running.
+//
+
+int lastKeeper = 0;
+
+//
+// Let an inner routine register the pid of a slave
+// process.
+//
+
+static int lastChild = 0;
+
+//
+// Exit code of the last child process exited.
+//
+
+static int lastStatus = 0;
+
+//
+// Set if shutdown was requested through a signal.
+//
+
+static int lastKill = 0;
+
+//
+// Set if the agent confirmed the destruction of
+// the NX transport.
+//
+
+static int lastDestroy = 0;
+
+//
+// This is set to the code and local flag of the
+// last requested alert.
+//
+
+static struct
+{
+ int code;
+ int local;
+
+} lastAlert;
+
+//
+// Manage the current signal masks.
+//
+
+static struct
+{
+ sigset_t saved;
+
+ int blocked;
+ int installed;
+
+ int enabled[32];
+ int forward[32];
+
+ struct sigaction action[32];
+
+} lastMasks;
+
+//
+// Manage the current timer.
+//
+
+static struct
+{
+ struct sigaction action;
+ struct itimerval value;
+ struct timeval start;
+ struct timeval next;
+
+} lastTimer;
+
+//
+// This is set to last signal received in handler.
+//
+
+static int lastSignal = 0;
+
+//
+// Set to the last time bytes readable were queried
+// by the agent.
+//
+
+static T_timestamp lastReadableTs = nullTimestamp();
+
+//
+// Here are interfaces declared in NX.h.
+//
+
+int NXTransProxy(int fd, int mode, const char* options)
+{
+ //
+ // Let the log temporarily go to the standard
+ // error. Be also sure we have a jump context,
+ // in the case any subsequent operation will
+ // cause a cleanup.
+ //
+
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ if (setjmp(context) == 1)
+ {
+ #ifdef TEST
+ *logofs << "NXTransProxy: Out of the long jump with pid '"
+ << lastProxy << "'.\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ //
+ // Check if have already performed a parsing of
+ // parameters, as in the case we are running as
+ // a stand-alone process. If needed create the
+ // parameters repository
+ //
+
+ if (control == NULL)
+ {
+ control = new Control();
+ }
+
+ lastProxy = getpid();
+
+ #ifdef TEST
+ *logofs << "NXTransProxy: Main process started with pid '"
+ << lastProxy << "'.\n" << logofs_flush;
+ #endif
+
+ SetMode(mode);
+
+ if (mode == NX_MODE_CLIENT)
+ {
+ if (fd != NX_FD_ANY)
+ {
+ #ifdef TEST
+
+ *logofs << "NXTransProxy: Agent descriptor for X client connections is FD#"
+ << fd << ".\n" << logofs_flush;
+
+ *logofs << "NXTransProxy: Disabling listening on further X client connections.\n"
+ << logofs_flush;
+
+ #endif
+
+ useTcpSocket = 0;
+ useUnixSocket = 0;
+ useAgentSocket = 1;
+
+ agentFD[1] = fd;
+ }
+ }
+ else if (mode == NX_MODE_SERVER)
+ {
+ if (fd != NX_FD_ANY)
+ {
+ #ifdef TEST
+ *logofs << "NXTransProxy: PANIC! Agent descriptor for X server connections "
+ << "not supported yet.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Agent descriptor for X server connections "
+ << "not supported yet.\n";
+
+ return -1;
+ }
+ }
+
+ const char *env = GetOptions(options);
+
+ if (ParseEnvironmentOptions(env, 0) < 0)
+ {
+ cerr << "Error" << ": Parsing of NX transport options failed.\n";
+
+ return -1;
+ }
+
+ //
+ // Set the path of the NX directories.
+ //
+
+ SetDirectories();
+
+ //
+ // Open the log files.
+ //
+
+ SetLogs();
+
+ #ifdef TEST
+ *logofs << "NXTransProxy: Going to run the NX transport loop.\n"
+ << logofs_flush;
+ #endif
+
+ WaitCleanup();
+
+ //
+ // This function should never return.
+ //
+
+ exit(0);
+}
+
+void NXTransExit(int code)
+{
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ static int recurse;
+
+ if (++recurse > 1)
+ {
+ #ifdef TEST
+ *logofs << "NXTransExit: Aborting process with pid '"
+ << getpid() << "' due to recursion through "
+ << "exit.\n" << logofs_flush;
+ #endif
+
+ abort();
+ }
+
+ #ifdef TEST
+ *logofs << "NXTransExit: Process with pid '"
+ << getpid() << "' called exit with code '"
+ << code << "'.\n" << logofs_flush;
+ #endif
+
+ if (control != NULL)
+ {
+ //
+ // Be sure that there we can detect the
+ // termination of the watchdog.
+ //
+
+ EnableSignals();
+
+ //
+ // Close the NX transport if it was not
+ // shut down already.
+ //
+
+ NXTransDestroy(NX_FD_ANY);
+ }
+
+ exit(code);
+}
+
+int NXTransParseCommandLine(int argc, const char **argv)
+{
+ return ParseCommandLineOptions(argc, argv);
+}
+
+int NXTransParseEnvironment(const char *env, int force)
+{
+ return ParseEnvironmentOptions(env, force);
+}
+
+void NXTransCleanup()
+{
+ HandleCleanup();
+}
+
+//
+// Check the parameters for subsequent
+// initialization of the NX transport.
+//
+
+int NXTransCreate(int fd, int mode, const char* options)
+{
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ //
+ // Be sure we have a jump context, in the
+ // case a subsequent operation will cause
+ // a cleanup.
+ //
+
+ if (setjmp(context) == 1)
+ {
+ return -1;
+ }
+
+ //
+ // Create the parameters repository
+ //
+
+ if (control != NULL)
+ {
+ #ifdef PANIC
+ *logofs << "NXTransCreate: PANIC! The NX transport seems "
+ << "to be already running.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": The NX transport seems "
+ << "to be already running.\n";
+
+ return -1;
+ }
+
+ control = new Control();
+
+ if (control == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Error creating the NX transport.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error creating the NX transport.\n";
+
+ return -1;
+ }
+
+ lastProxy = getpid();
+
+ #ifdef TEST
+ *logofs << "NXTransCreate: Caller process running with pid '"
+ << lastProxy << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // Set the local proxy mode an parse the
+ // display NX options.
+ //
+
+ SetMode(mode);
+
+ const char *env = GetOptions(options);
+
+ if (ParseEnvironmentOptions(env, 0) < 0)
+ {
+ cerr << "Error" << ": Parsing of NX transport options failed.\n";
+
+ return -1;
+ }
+
+ //
+ // Set the path of the NX directories.
+ //
+
+ SetDirectories();
+
+ //
+ // Open the log files.
+ //
+
+ SetLogs();
+
+ //
+ // Use the provided descriptor.
+ //
+
+ proxyFD = fd;
+
+ #ifdef TEST
+ *logofs << "NXTransCreate: Called with NX proxy descriptor '"
+ << proxyFD << "'.\n" << logofs_flush;
+ #endif
+
+ #ifdef TEST
+ *logofs << "NXTransCreate: Creation of the NX transport completed.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+//
+// Tell the proxy to use the descriptor as the internal
+// connection to the X client side NX agent. This will
+// have the side effect of disabling listening for add-
+// itional X client connections.
+//
+
+int NXTransAgent(int fd[2])
+{
+ //
+ // Be sure we have a jump context, in the
+ // case a subsequent operation will cause
+ // a cleanup.
+ //
+
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ if (setjmp(context) == 1)
+ {
+ return -1;
+ }
+
+ if (control == NULL)
+ {
+ cerr << "Error" << ": Can't set the NX agent without a NX transport.\n";
+
+ return -1;
+ }
+ else if (control -> ProxyMode != proxy_client)
+ {
+ #ifdef PANIC
+ *logofs << "NXTransAgent: Invalid mode while setting the NX agent.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid mode while setting the NX agent.\n\n";
+
+ return -1;
+ }
+
+ useTcpSocket = 0;
+ useUnixSocket = 0;
+ useAgentSocket = 1;
+
+ agentFD[0] = fd[0];
+ agentFD[1] = fd[1];
+
+ #ifdef TEST
+
+ *logofs << "NXTransAgent: Internal descriptors for agent are FD#"
+ << agentFD[0] << " and FD#" << agentFD[1] << ".\n"
+ << logofs_flush;
+
+ *logofs << "NXTransAgent: Disabling listening for further X client "
+ << "connections.\n" << logofs_flush;
+
+ #endif
+
+ agent = new Agent(agentFD);
+
+ if (agent == NULL || agent -> isValid() != 1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Error creating the NX memory transport .\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error creating the NX memory transport.\n";
+
+ HandleCleanup();
+ }
+
+ #ifdef TEST
+ *logofs << "NXTransAgent: Enabling memory-to-memory transport.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int NXTransClose(int fd)
+{
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ /*
+ * Only handle the proxy connection. The X
+ * transport will take care of closing its
+ * end of the socket pair.
+ */
+
+ if (control != NULL && ((agent != NULL &&
+ (fd == agentFD[0] || fd == NX_FD_ANY)) ||
+ (fd == proxyFD || fd == NX_FD_ANY)))
+ {
+ if (proxy != NULL)
+ {
+ #ifdef TEST
+ *logofs << "NXTransClose: Closing down all the X connections.\n"
+ << logofs_flush;
+ #endif
+
+ CleanupConnections();
+ }
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "NXTransClose: The NX transport is not running.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ return 1;
+}
+
+//
+// Close down the transport and free the
+// allocated NX resources.
+//
+
+int NXTransDestroy(int fd)
+{
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ if (control != NULL && ((agent != NULL &&
+ (fd == agentFD[0] || fd == NX_FD_ANY)) ||
+ (fd == proxyFD || fd == NX_FD_ANY)))
+ {
+ //
+ // Shut down the X connections and
+ // wait the cleanup to complete.
+ //
+
+ if (proxy != NULL)
+ {
+ #ifdef TEST
+ *logofs << "NXTransDestroy: Closing down all the X connections.\n"
+ << logofs_flush;
+ #endif
+
+ CleanupConnections();
+ }
+
+ #ifdef TEST
+ *logofs << "NXTransDestroy: Waiting for the NX transport to terminate.\n"
+ << logofs_flush;
+ #endif
+
+ lastDestroy = 1;
+
+ WaitCleanup();
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "NXTransDestroy: The NX transport is not running.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ return 1;
+}
+
+//
+// Assume that the NX transport is valid
+// as long as the control class has not
+// been destroyed.
+//
+
+int NXTransRunning(int fd)
+{
+ return (control != NULL);
+}
+
+int NXTransContinue(struct timeval *selectTs)
+{
+ if (control != NULL)
+ {
+ //
+ // If no timeout is provided use
+ // the default.
+ //
+
+ T_timestamp newTs;
+
+ if (selectTs == NULL)
+ {
+ setTimestamp(newTs, control -> PingTimeout);
+
+ selectTs = &newTs;
+ }
+
+ //
+ // Use empty masks and only get the
+ // descriptors set by the proxy.
+ //
+
+ fd_set readSet;
+ fd_set writeSet;
+
+ int setFDs;
+ int errorFDs;
+ int resultFDs;
+
+ setFDs = 0;
+
+ FD_ZERO(&readSet);
+ FD_ZERO(&writeSet);
+
+ //
+ // Run a new loop. If the transport
+ // is gone avoid sleeping until the
+ // timeout.
+ //
+
+ if (NXTransPrepare(&setFDs, &readSet, &writeSet, selectTs) != 0)
+ {
+ NXTransSelect(&resultFDs, &errorFDs, &setFDs, &readSet, &writeSet, selectTs);
+
+ NXTransExecute(&resultFDs, &errorFDs, &setFDs, &readSet, &writeSet, selectTs);
+ }
+ }
+
+ return (control != NULL);
+}
+
+int NXTransSignal(int signal, int action)
+{
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ if (control == NULL)
+ {
+ return 0;
+ }
+
+ if (action == NX_SIGNAL_RAISE)
+ {
+ #ifdef TEST
+ *logofs << "NXTransSignal: Raising signal '" << DumpSignal(signal)
+ << "' in the proxy handler.\n" << logofs_flush;
+ #endif
+
+ HandleSignal(signal);
+
+ return 1;
+ }
+ else if (signal == NX_SIGNAL_ANY)
+ {
+ #ifdef TEST
+ *logofs << "NXTransSignal: Setting action of all signals to '"
+ << action << "'.\n" << logofs_flush;
+ #endif
+
+ for (int i = 0; i < 32; i++)
+ {
+ if (CheckSignal(i) == 1)
+ {
+ NXTransSignal(i, action);
+ }
+ }
+
+ return 1;
+ }
+ else if (CheckSignal(signal) == 1)
+ {
+ #ifdef TEST
+ *logofs << "NXTransSignal: Setting action of signal '"
+ << DumpSignal(signal) << "' to '" << action
+ << "'.\n" << logofs_flush;
+ #endif
+
+ if (action == NX_SIGNAL_ENABLE ||
+ action == NX_SIGNAL_FORWARD)
+ {
+ InstallSignal(signal, action);
+
+ return 1;
+ }
+ else if (action == NX_SIGNAL_DISABLE)
+ {
+ RestoreSignal(signal);
+
+ return 1;
+ }
+ }
+
+ #ifdef WARNING
+ *logofs << "NXTransSignal: WARNING! Unable to perform action '"
+ << action << "' on signal '" << DumpSignal(signal)
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Unable to perform action '" << action
+ << "' on signal '" << DumpSignal(signal)
+ << "'.\n";
+
+ return -1;
+}
+
+int NXTransCongestion(int fd)
+{
+ if (control != NULL && proxy != NULL)
+ {
+ #ifdef DUMP
+
+ int congestion = proxy -> getCongestion(proxyFD);
+
+ *logofs << "NXTransCongestion: Returning " << congestion
+ << " as current congestion level.\n" << logofs_flush;
+
+ return congestion;
+
+ #endif
+
+ return (proxy -> getCongestion(proxyFD));
+ }
+
+ return 0;
+}
+
+int NXTransHandler(int fd, int type, void (*handler)(void *parameter,
+ int reason), void *parameter)
+{
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ switch (type)
+ {
+ case NX_HANDLER_FLUSH:
+ {
+ flushCallback = handler;
+ flushParameter = parameter;
+
+ break;
+ }
+ case NX_HANDLER_STATISTICS:
+ {
+ //
+ // Reporting of statistics by the agent
+ // still needs to be implemented.
+ //
+
+ statisticsCallback = handler;
+ statisticsParameter = parameter;
+
+ break;
+ }
+ default:
+ {
+ #ifdef TEST
+ *logofs << "NXTransHandler: WARNING! Failed to set "
+ << "the NX callback for event '" << type << "' to '"
+ << (void *) handler << "' and parameter '"
+ << parameter << "'.\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "NXTransHandler: Set the NX "
+ << "callback for event '" << type << "' to '"
+ << (void *) handler << "' and parameter '"
+ << parameter << "'.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int NXTransRead(int fd, char *data, int size)
+{
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ if (control != NULL && agent != NULL &&
+ fd == agentFD[0])
+ {
+ #ifdef DUMP
+ *logofs << "NXTransRead: Dequeuing " << size << " bytes "
+ << "from FD#" << agentFD[0] << ".\n" << logofs_flush;
+ #endif
+
+ int result = agent -> dequeueData(data, size);
+
+ #ifdef DUMP
+
+ if (result < 0 && EGET() == EAGAIN)
+ {
+ *logofs << "NXTransRead: WARNING! Dequeuing from FD#"
+ << agentFD[0] << " would block.\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "NXTransRead: Dequeued " << result << " bytes "
+ << "to FD#" << agentFD[0] << ".\n" << logofs_flush;
+ }
+
+ #endif
+
+ return result;
+ }
+ else
+ {
+ #ifdef DUMP
+ *logofs << "NXTransRead: Reading " << size << " bytes "
+ << "from FD#" << fd << ".\n" << logofs_flush;
+ #endif
+
+ return read(fd, data, size);
+ }
+}
+
+int NXTransReadVector(int fd, struct iovec *iovdata, int iovsize)
+{
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ if (control != NULL && agent != NULL &&
+ fd == agentFD[0])
+ {
+ #if defined(DUMP)
+
+ if (control -> ProxyStage >= stage_operational &&
+ agent -> localReadable() > 0)
+ {
+ *logofs << "NXTransReadVector: WARNING! Agent has data readable.\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ char *base;
+
+ int length;
+ int result;
+
+ struct iovec *vector = iovdata;
+ int count = iovsize;
+
+ ESET(0);
+
+ int i = 0;
+ int total = 0;
+
+ for (; i < count; i++, vector++)
+ {
+ length = vector -> iov_len;
+ base = (char *) vector -> iov_base;
+
+ while (length > 0)
+ {
+ #ifdef DUMP
+ *logofs << "NXTransReadVector: Dequeuing " << length
+ << " bytes " << "from FD#" << agentFD[0] << ".\n"
+ << logofs_flush;
+ #endif
+
+ result = agent -> dequeueData(base, length);
+
+ #ifdef DUMP
+
+ if (result < 0 && EGET() == EAGAIN)
+ {
+ *logofs << "NXTransReadVector: WARNING! Dequeuing from FD#"
+ << agentFD[0] << " would block.\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "NXTransReadVector: Dequeued " << result
+ << " bytes " << "from FD#" << agentFD[0] << ".\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ if (result < 0 && total == 0)
+ {
+ return result;
+ }
+ else if (result <= 0)
+ {
+ return total;
+ }
+
+ ESET(0);
+
+ length -= result;
+ total += result;
+ base += result;
+ }
+ }
+
+ return total;
+ }
+ else
+ {
+ #ifdef DUMP
+ *logofs << "NXTransReadVector: Reading vector with "
+ << iovsize << " elements from FD#" << fd << ".\n"
+ << logofs_flush;
+ #endif
+
+ return readv(fd, iovdata, iovsize);
+ }
+}
+
+int NXTransReadable(int fd, int *readable)
+{
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ if (control == NULL || agent == NULL ||
+ fd != agentFD[0])
+ {
+ #ifdef DUMP
+
+ int result = GetBytesReadable(fd, readable);
+
+ if (result == -1)
+ {
+ *logofs << "NXTransReadable: Error detected on FD#"
+ << fd << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "NXTransReadable: Returning " << *readable
+ << " bytes as readable from FD#" << fd
+ << ".\n" << logofs_flush;
+ }
+
+ return result;
+
+ #else
+
+ return GetBytesReadable(fd, readable);
+
+ #endif
+ }
+
+ int result = agent -> dequeuableData();
+
+ switch (result)
+ {
+ case 0:
+ {
+ //
+ // The client might have enqueued data to our side
+ // and is now checking for the available events. As
+ // _XEventsQueued() may omit to call _XSelect(), we
+ // handle here the new data that is coming from the
+ // proxy to avoid spinning through this function
+ // again.
+ //
+
+ if (proxy != NULL && proxy -> canRead() == 1)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "NXTransReadable: WARNING! Trying to "
+ << "read to generate new agent data.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Set the context as the function
+ // can cause a cleanup.
+ //
+
+ if (setjmp(context) == 1)
+ {
+ return -1;
+ }
+
+ if (proxy -> handleRead() < 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "NXTransReadable: Failure reading "
+ << "messages from proxy FD#" << proxyFD
+ << ".\n" << logofs_flush;
+ #endif
+
+ HandleShutdown();
+ }
+
+ //
+ // Call again the routine. By reading
+ // new control messages from the proxy
+ // the agent channel may be gone.
+ //
+
+ return NXTransReadable(fd, readable);
+ }
+
+ #ifdef DUMP
+ *logofs << "NXTransReadable: Returning " << 0
+ << " bytes as readable from FD#" << fd
+ << " with result 0.\n" << logofs_flush;
+ #endif
+
+ *readable = 0;
+
+ return 0;
+ }
+ case -1:
+ {
+ #ifdef DUMP
+ *logofs << "NXTransReadable: Returning " << 0
+ << " bytes as readable from FD#" << fd
+ << " with result -1.\n" << logofs_flush;
+ #endif
+
+ *readable = 0;
+
+ return -1;
+ }
+ default:
+ {
+ #ifdef DUMP
+ *logofs << "NXTransReadable: Returning " << result
+ << " bytes as readable from FD#" << fd
+ << " with result 0.\n" << logofs_flush;
+ #endif
+
+ *readable = result;
+
+ return 0;
+ }
+ }
+}
+
+int NXTransWrite(int fd, char *data, int size)
+{
+ //
+ // Be sure we have a valid log file.
+ //
+
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ if (control != NULL && agent != NULL &&
+ fd == agentFD[0])
+ {
+ int result;
+
+ if (proxy != NULL)
+ {
+ if (proxy -> canRead(agentFD[1]) == 0)
+ {
+ #if defined(DUMP) || defined(TEST)
+ *logofs << "NXTransWrite: WARNING! Delayed enqueuing to FD#"
+ << agentFD[0] << " with proxy unable to read.\n"
+ << logofs_flush;
+ #endif
+
+ ESET(EAGAIN);
+
+ return -1;
+ }
+
+ //
+ // Set the context as the function
+ // can cause a cleanup.
+ //
+
+ if (setjmp(context) == 1)
+ {
+ return -1;
+ }
+
+ //
+ // Don't enqueue the data to the transport
+ // but let the channel borrow the buffer.
+ //
+
+ #ifdef DUMP
+ *logofs << "NXTransWrite: Letting the channel borrow "
+ << size << " bytes from FD#" << agentFD[0]
+ << ".\n" << logofs_flush;
+ #endif
+
+ result = proxy -> handleRead(agentFD[1], data, size);
+
+ if (result == 1)
+ {
+ result = size;
+ }
+ else
+ {
+ if (result == 0)
+ {
+ ESET(EAGAIN);
+ }
+ else
+ {
+ ESET(EPIPE);
+ }
+
+ result = -1;
+ }
+ }
+ else
+ {
+ //
+ // We don't have a proxy connection, yet.
+ // Enqueue the data to the agent transport.
+ //
+
+ #ifdef DUMP
+ *logofs << "NXTransWrite: Enqueuing " << size << " bytes "
+ << "to FD#" << agentFD[0] << ".\n" << logofs_flush;
+ #endif
+
+ result = agent -> enqueueData(data, size);
+ }
+
+ #ifdef DUMP
+
+ if (result < 0)
+ {
+ if (EGET() == EAGAIN)
+ {
+ *logofs << "NXTransWrite: WARNING! Enqueuing to FD#"
+ << agentFD[0] << " would block.\n"
+ << logofs_flush;
+ }
+ else
+ {
+ *logofs << "NXTransWrite: WARNING! Error enqueuing to FD#"
+ << agentFD[0] << ".\n" << logofs_flush;
+ }
+ }
+ else
+ {
+ *logofs << "NXTransWrite: Enqueued " << result << " bytes "
+ << "to FD#" << agentFD[0] << ".\n" << logofs_flush;
+ }
+
+ #endif
+
+ return result;
+ }
+ else
+ {
+ #ifdef DUMP
+ *logofs << "NXTransWrite: Writing " << size << " bytes "
+ << "to FD#" << fd << ".\n" << logofs_flush;
+ #endif
+
+ return write(fd, data, size);
+ }
+}
+
+int NXTransWriteVector(int fd, struct iovec *iovdata, int iovsize)
+{
+ //
+ // Be sure we have a valid log file and a
+ // jump context because we will later call
+ // functions that can perform a cleanup.
+ //
+
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ int result = 0;
+
+ if (control != NULL && agent != NULL &&
+ fd == agentFD[0])
+ {
+ //
+ // See the comment in NXTransWrite().
+ //
+
+ if (proxy != NULL)
+ {
+ if (proxy -> canRead(agentFD[1]) == 0)
+ {
+ #if defined(DUMP) || defined(TEST)
+ *logofs << "NXTransWriteVector: WARNING! Delayed enqueuing to FD#"
+ << agentFD[0] << " with proxy unable to read.\n"
+ << logofs_flush;
+ #endif
+
+ ESET(EAGAIN);
+
+ return -1;
+ }
+ }
+
+ //
+ // Set the context as the function
+ // can cause a cleanup.
+ //
+
+ if (setjmp(context) == 1)
+ {
+ return -1;
+ }
+
+ char *base;
+
+ int length;
+
+ struct iovec *vector = iovdata;
+ int count = iovsize;
+
+ ESET(0);
+
+ int i = 0;
+ int total = 0;
+
+ for (; i < count; i++, vector++)
+ {
+ length = vector -> iov_len;
+ base = (char *) vector -> iov_base;
+
+ while (length > 0)
+ {
+ if (proxy != NULL)
+ {
+ //
+ // Don't enqueue the data to the transport
+ // but let the channel borrow the buffer.
+ //
+
+ #ifdef DUMP
+ *logofs << "NXTransWriteVector: Letting the channel borrow "
+ << length << " bytes from FD#" << agentFD[0]
+ << ".\n" << logofs_flush;
+ #endif
+
+ result = proxy -> handleRead(agentFD[1], base, length);
+
+ if (result == 1)
+ {
+ result = length;
+ }
+ else
+ {
+ if (result == 0)
+ {
+ ESET(EAGAIN);
+ }
+ else
+ {
+ ESET(EPIPE);
+ }
+
+ result = -1;
+ }
+ }
+ else
+ {
+ //
+ // We don't have a proxy connection, yet.
+ // Enqueue the data to the agent transport.
+ //
+
+ #ifdef DUMP
+ *logofs << "NXTransWriteVector: Enqueuing " << length
+ << " bytes " << "to FD#" << agentFD[0] << ".\n"
+ << logofs_flush;
+ #endif
+
+ result = agent -> enqueueData(base, length);
+ }
+
+ #ifdef DUMP
+
+ if (result < 0)
+ {
+ if (EGET() == EAGAIN)
+ {
+ *logofs << "NXTransWriteVector: WARNING! Enqueuing to FD#"
+ << agentFD[0] << " would block.\n"
+ << logofs_flush;
+ }
+ else
+ {
+ *logofs << "NXTransWriteVector: WARNING! Error enqueuing to FD#"
+ << agentFD[0] << ".\n" << logofs_flush;
+ }
+ }
+ else
+ {
+ *logofs << "NXTransWriteVector: Enqueued " << result
+ << " bytes " << "to FD#" << agentFD[0] << ".\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ if (result < 0 && total == 0)
+ {
+ return result;
+ }
+ else if (result <= 0)
+ {
+ return total;
+ }
+
+ ESET(0);
+
+ length -= result;
+ total += result;
+ base += result;
+ }
+ }
+
+ return total;
+ }
+ else
+ {
+ #ifdef DUMP
+ *logofs << "NXTransWriteVector: Writing vector with "
+ << iovsize << " elements to FD#" << fd << ".\n"
+ << logofs_flush;
+ #endif
+
+ return writev(fd, iovdata, iovsize);
+ }
+}
+
+int NXTransPolicy(int fd, int type)
+{
+ if (control != NULL)
+ {
+ if (usePolicy == -1)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "NXTransPolicy: Setting flush policy on "
+ << "proxy FD#" << proxyFD << " to '"
+ << DumpPolicy(type == NX_POLICY_DEFERRED ?
+ policy_deferred : policy_immediate)
+ << "'.\n" << logofs_flush;
+ #endif
+
+ control -> FlushPolicy = (type == NX_POLICY_DEFERRED ?
+ policy_deferred : policy_immediate);
+
+ if (proxy != NULL)
+ {
+ proxy -> handleFlush();
+ }
+
+ return 1;
+ }
+ else
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "NXTransPolicy: Ignoring the agent "
+ << "setting with user policy set to '"
+ << DumpPolicy(control -> FlushPolicy)
+ << "'.\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+int NXTransFlushable(int fd)
+{
+ if (proxy == NULL || agent == NULL ||
+ fd != agentFD[0])
+ {
+ #ifdef DUMP
+ *logofs << "NXTransFlushable: Returning 0 bytes as "
+ << "flushable for unrecognized FD#" << fd
+ << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else
+ {
+ #if defined(DUMP) || defined(INFO)
+ *logofs << "NXTransFlushable: Returning " << proxy ->
+ getFlushable(proxyFD) << " as bytes flushable on "
+ << "proxy FD#" << proxyFD << ".\n"
+ << logofs_flush;
+ #endif
+
+ return proxy -> getFlushable(proxyFD);
+ }
+}
+
+int NXTransFlush(int fd)
+{
+ if (proxy != NULL)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "NXTransFlush: Requesting an immediate flush of "
+ << "proxy FD#" << proxyFD << ".\n"
+ << logofs_flush;
+ #endif
+
+ return proxy -> handleFlush();
+ }
+
+ return 0;
+}
+
+int NXTransChannel(int fd, int channelFd, int type)
+{
+ if (proxy != NULL)
+ {
+ //
+ // Set the context as the function
+ // can cause a cleanup.
+ //
+
+ if (setjmp(context) == 1)
+ {
+ return -1;
+ }
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "NXTransChannel: Going to create a new channel "
+ << "with type '" << type << "' on FD#" << channelFd
+ << ".\n" << logofs_flush;
+ #endif
+
+ int result = -1;
+
+ switch (type)
+ {
+ case NX_CHANNEL_X11:
+ {
+ if (useUnixSocket == 1 || useTcpSocket == 1 ||
+ useAgentSocket == 1 || useAuxSocket == 1)
+ {
+ result = proxy -> handleNewConnection(channel_x11, channelFd);
+ }
+
+ break;
+ }
+ case NX_CHANNEL_CUPS:
+ {
+ if (useCupsSocket == 1)
+ {
+ result = proxy -> handleNewConnection(channel_cups, channelFd);
+ }
+
+ break;
+ }
+ case NX_CHANNEL_SMB:
+ {
+ if (useSmbSocket == 1)
+ {
+ result = proxy -> handleNewConnection(channel_smb, channelFd);
+ }
+
+ break;
+ }
+ case NX_CHANNEL_MEDIA:
+ {
+ if (useMediaSocket == 1)
+ {
+ result = proxy -> handleNewConnection(channel_media, channelFd);
+ }
+
+ break;
+ }
+ case NX_CHANNEL_HTTP:
+ {
+ if (useHttpSocket == 1)
+ {
+ result = proxy -> handleNewConnection(channel_http, channelFd);
+ }
+
+ break;
+ }
+ case NX_CHANNEL_FONT:
+ {
+ if (useFontSocket == 1)
+ {
+ result = proxy -> handleNewConnection(channel_font, channelFd);
+ }
+
+ break;
+ }
+ case NX_CHANNEL_SLAVE:
+ {
+ if (useSlaveSocket == 1)
+ {
+ result = proxy -> handleNewConnection(channel_slave, channelFd);
+ }
+
+ break;
+ }
+ default:
+ {
+ #ifdef WARNING
+ *logofs << "NXTransChannel: WARNING! Unrecognized channel "
+ << "type '" << type << "'.\n" << logofs_flush;
+ #endif
+
+ break;
+ }
+ }
+
+ #ifdef WARNING
+
+ if (result != 1)
+ {
+ *logofs << "NXTransChannel: WARNING! Could not create the "
+ << "new channel with type '" << type << "' on FD#"
+ << channelFd << ".\n" << logofs_flush;
+ }
+
+ #endif
+
+ return result;
+ }
+
+ return 0;
+}
+
+const char *NXTransFile(int type)
+{
+ char *name = NULL;
+
+ switch (type)
+ {
+ case NX_FILE_SESSION:
+ {
+ name = sessionFileName;
+
+ break;
+ }
+ case NX_FILE_ERRORS:
+ {
+ name = errorsFileName;
+
+ break;
+ }
+ case NX_FILE_OPTIONS:
+ {
+ name = optionsFileName;
+
+ break;
+ }
+ case NX_FILE_STATS:
+ {
+ name = statsFileName;
+
+ break;
+ }
+ }
+
+ if (name != NULL && *name != '\0')
+ {
+ return name;
+ }
+
+ return NULL;
+}
+
+long NXTransTime()
+{
+ static T_timestamp last = getTimestamp();
+
+ T_timestamp now = getTimestamp();
+
+ long diff = diffTimestamp(last, now);
+
+ last = now;
+
+ return diff;
+}
+
+int NXTransAlert(int code, int local)
+{
+ if (proxy != NULL)
+ {
+ #if defined(DUMP) || defined(INFO)
+ *logofs << "NXTransAlert: Requesting a NX dialog with code "
+ << code << " and local " << local << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (local == 0)
+ {
+ //
+ // Set the context as the function
+ // can cause a cleanup.
+ //
+
+ if (setjmp(context) == 1)
+ {
+ return -1;
+ }
+
+ proxy -> handleAlert(code);
+ }
+ else
+ {
+ //
+ // Show the alert at the next loop.
+ //
+
+ HandleAlert(code, local);
+ }
+
+ return 1;
+ }
+ #if defined(DUMP) || defined(INFO)
+ else
+ {
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ *logofs << "NXTransAlert: Can't request an alert without "
+ << "a valid NX transport.\n" << logofs_flush;
+ }
+ #endif
+
+ return 0;
+}
+
+//
+// Prepare the file sets and the timeout
+// for a later execution of the select().
+//
+
+int NXTransPrepare(int *setFDs, fd_set *readSet,
+ fd_set *writeSet, struct timeval *selectTs)
+{
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ //
+ // Control is NULL if the NX transport was
+ // reset or was never created. If control
+ // is valid then prepare to jump back when
+ // the transport is destroyed.
+ //
+
+ if (control == NULL || setjmp(context) == 1)
+ {
+ return 0;
+ }
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "\nNXTransPrepare: Going to prepare the NX transport.\n"
+ << logofs_flush;
+ #endif
+
+ if (control -> ProxyStage < stage_operational)
+ {
+ handleNegotiationInLoop(*setFDs, *readSet, *writeSet, *selectTs);
+ }
+ else
+ {
+ #if defined(TEST) || defined(INFO)
+
+ if (isTimestamp(*selectTs) == 0)
+ {
+ *logofs << "Loop: WARNING! Preparing the select with requested "
+ << "timeout of " << selectTs -> tv_sec << " S and "
+ << (double) selectTs -> tv_usec / 1000 << " Ms.\n"
+ << logofs_flush;
+ }
+ else
+ {
+ *logofs << "Loop: Preparing the select with requested "
+ << "timeout of " << selectTs -> tv_sec << " S and "
+ << (double) selectTs -> tv_usec / 1000 << " Ms.\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ //
+ // Set descriptors of listening sockets.
+ //
+
+ handleSetListenersInLoop(*readSet, *setFDs);
+
+ //
+ // Set descriptors of both proxy and X
+ // connections.
+ //
+
+ handleSetReadInLoop(*readSet, *setFDs, *selectTs);
+
+ //
+ // Find out which file descriptors have
+ // data to write.
+ //
+
+ handleSetWriteInLoop(*writeSet, *setFDs, *selectTs);
+ }
+
+ //
+ // Prepare the masks for handling the memory-
+ // to-memory transport. This is required even
+ // during session negotiation.
+ //
+
+ if (agent != NULL)
+ {
+ handleSetAgentInLoop(*setFDs, *readSet, *writeSet, *selectTs);
+ }
+
+ //
+ // Register time spent handling messages.
+ //
+
+ nowTs = getNewTimestamp();
+
+ diffTs = diffTimestamp(startTs, nowTs);
+
+ #ifdef TEST
+ *logofs << "Loop: Mark - 0 - at " << strMsTimestamp()
+ << " with " << diffTs << " Ms elapsed.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // TODO: Should add the read time in two
+ // parts otherwise the limits are checked
+ // before the counters are updated with
+ // time spent in the last loop.
+ //
+
+ if (control -> ProxyStage >= stage_operational)
+ {
+ statistics -> addReadTime(diffTs);
+ }
+
+ startTs = nowTs;
+
+ #ifdef DEBUG
+ *logofs << "Loop: New timestamp is " << strMsTimestamp(startTs)
+ << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+//
+// Let's say that we call select() to find out
+// if any of the handled descriptors has data,
+// but actually things are a bit more complex
+// than that.
+//
+
+int NXTransSelect(int *resultFDs, int *errorFDs, int *setFDs, fd_set *readSet,
+ fd_set *writeSet, struct timeval *selectTs)
+{
+ #ifdef TIME
+
+ static T_timestamp lastTs;
+
+ #endif
+
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ //
+ // Control is NULL if the NX transport was
+ // reset or never created. If control is
+ // valid then prepare for jumping back in
+ // the case of an error.
+ //
+
+ if (control == NULL || setjmp(context) == 1)
+ {
+ *resultFDs = select(*setFDs, readSet, writeSet, NULL, selectTs);
+
+ *errorFDs = errno;
+
+ return 0;
+ }
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "\nNXTransSelect: Going to select the NX descriptors.\n"
+ << logofs_flush;
+ #endif
+
+ #if defined(TEST) || defined(INFO)
+
+ handleCheckSelectInLoop(*setFDs, *readSet, *writeSet, *selectTs);
+
+ #endif
+
+ #ifdef TIME
+
+ diffTs = diffTimestamp(lastTs, getNewTimestamp());
+
+ if (diffTs > 20)
+ {
+ *logofs << "Loop: TIME! Spent " << diffTs
+ << " Ms handling messages for proxy FD#"
+ << proxyFD << ".\n" << logofs_flush;
+ }
+
+ lastTs = getNewTimestamp();
+
+ #endif
+
+ #if defined(TEST) || defined(INFO)
+
+ if (isTimestamp(*selectTs) == 0)
+ {
+ *logofs << "Loop: WARNING! Executing the select with requested "
+ << "timeout of " << selectTs -> tv_sec << " S and "
+ << (double) selectTs -> tv_usec / 1000 << " Ms.\n"
+ << logofs_flush;
+ }
+ else if (proxy != NULL && proxy -> getFlushable(proxyFD) > 0)
+ {
+ *logofs << "Loop: WARNING! Proxy FD#" << proxyFD
+ << " has " << proxy -> getFlushable(proxyFD)
+ << " bytes to write but timeout is "
+ << selectTs -> tv_sec << " S and "
+ << selectTs -> tv_usec / 1000 << " Ms.\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ //
+ // Wait for the selected sockets
+ // or the timeout.
+ //
+
+ ESET(0);
+
+ *resultFDs = select(*setFDs, readSet, writeSet, NULL, selectTs);
+
+ *errorFDs = EGET();
+
+ #ifdef TIME
+
+ diffTs = diffTimestamp(lastTs, getNewTimestamp());
+
+ if (diffTs > 100)
+ {
+ *logofs << "Loop: TIME! Spent " << diffTs
+ << " Ms waiting for new data for proxy FD#"
+ << proxyFD << ".\n" << logofs_flush;
+ }
+
+ lastTs = getNewTimestamp();
+
+ #endif
+
+ //
+ // Check the result of the select.
+ //
+
+ #if defined(TEST) || defined(INFO)
+
+ handleCheckResultInLoop(*resultFDs, *errorFDs, *setFDs, *readSet, *writeSet, *selectTs, startTs);
+
+ #endif
+
+ //
+ // Get time spent in select. The accouting is done
+ // in milliseconds. This is a real problem on fast
+ // machines where each loop is unlikely to take
+ // more than 500 us, so consider that the results
+ // can be inaccurate.
+ //
+
+ nowTs = getNewTimestamp();
+
+ diffTs = diffTimestamp(startTs, nowTs);
+
+ #ifdef TEST
+ *logofs << "Loop: Out of select after " << diffTs << " Ms "
+ << "at " << strMsTimestamp(nowTs) << " with result "
+ << *resultFDs << ".\n" << logofs_flush;
+ #endif
+
+ startTs = nowTs;
+
+ #ifdef DEBUG
+ *logofs << "Loop: New timestamp is " << strMsTimestamp(startTs)
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (control -> ProxyStage >= stage_operational)
+ {
+ statistics -> addIdleTime(diffTs);
+ }
+
+ if (*resultFDs < 0)
+ {
+ //
+ // Check if the call was interrupted or if any of the
+ // managed descriptors has become invalid. This can
+ // happen to the X11 code, before the descriptor is
+ // removed from the managed set.
+ //
+
+ #ifdef __sun
+
+ if (*errorFDs == EINTR || *errorFDs == EBADF ||
+ *errorFDs == EINVAL)
+
+ #else
+
+ if (*errorFDs == EINTR || *errorFDs == EBADF)
+
+ #endif
+
+ {
+ #ifdef TEST
+
+ if (*errorFDs == EINTR)
+ {
+ *logofs << "Loop: Select failed due to EINTR error.\n"
+ << logofs_flush;
+ }
+ else
+ {
+ *logofs << "Loop: WARNING! Call to select failed. Error is "
+ << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ }
+
+ #endif
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to select failed. Error is "
+ << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to select failed. Error is "
+ << EGET() << " '" << ESTR() << "'.\n";
+
+ HandleCleanup();
+ }
+ }
+
+ return 1;
+}
+
+//
+// Perform the required actions on all
+// the descriptors having I/O pending.
+//
+
+int NXTransExecute(int *resultFDs, int *errorFDs, int *setFDs, fd_set *readSet,
+ fd_set *writeSet, struct timeval *selectTs)
+{
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ //
+ // Control is NULL if the NX transport was
+ // reset or never created. If control is
+ // valid then prepare for jumping back in
+ // the case of an error.
+ //
+
+ if (control == NULL || setjmp(context) == 1)
+ {
+ return 0;
+ }
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "\nNXTransExecute: Going to execute I/O on the NX descriptors.\n"
+ << logofs_flush;
+ #endif
+
+ if (control -> ProxyStage >= stage_operational)
+ {
+ //
+ // Check if I/O is possible on the proxy and
+ // local agent descriptors.
+ //
+
+ if (agent != NULL)
+ {
+ handleAgentInLoop(*resultFDs, *errorFDs, *setFDs, *readSet, *writeSet, *selectTs);
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Mark - 1 - at " << strMsTimestamp()
+ << " with " << diffTimestamp(startTs, getTimestamp())
+ << " Ms elapsed.\n" << logofs_flush;
+ #endif
+
+ //
+ // Rotate the channel that will be handled
+ // first.
+ //
+
+ handleRotateInLoop();
+
+ //
+ // Flush any data on newly writable sockets.
+ //
+
+ handleWritableInLoop(*resultFDs, *writeSet);
+
+ #ifdef TEST
+ *logofs << "Loop: Mark - 2 - at " << strMsTimestamp()
+ << " with " << diffTimestamp(startTs, getTimestamp())
+ << " Ms elapsed.\n" << logofs_flush;
+ #endif
+
+ //
+ // Check if any socket has become readable.
+ //
+
+ handleReadableInLoop(*resultFDs, *readSet);
+
+ #ifdef TEST
+ *logofs << "Loop: Mark - 3 - at " << strMsTimestamp()
+ << " with " << diffTimestamp(startTs, getTimestamp())
+ << " Ms elapsed.\n" << logofs_flush;
+ #endif
+
+ //
+ // Handle the scheduled events on channels.
+ //
+ // - Restart, if possible, any client that was
+ // put to sleep.
+ //
+ // - Check if there are pointer motion events to
+ // flush. This applies only to X server side.
+ //
+ // - Check if any channel has exited the conges-
+ // tion state.
+ //
+ // - Check if there are images that are currently
+ // being streamed.
+ //
+
+ handleEventsInLoop();
+
+ #ifdef TEST
+ *logofs << "Loop: Mark - 4 - at " << strMsTimestamp()
+ << " with " << diffTimestamp(startTs, getTimestamp())
+ << " Ms elapsed.\n" << logofs_flush;
+ #endif
+
+ //
+ // Check if user sent a signal to produce
+ // statistics.
+ //
+
+ handleStatisticsInLoop();
+
+ //
+ // We may have flushed the proxy link or
+ // handled data coming from the remote.
+ // Post-process the masks and set the
+ // selected agent descriptors as ready.
+ //
+
+ if (agent != NULL)
+ {
+ handleAgentLateInLoop(*resultFDs, *errorFDs, *setFDs, *readSet, *writeSet, *selectTs);
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Mark - 5 - at " << strMsTimestamp()
+ << " with " << diffTimestamp(startTs, getTimestamp())
+ << " Ms elapsed.\n" << logofs_flush;
+ #endif
+
+ //
+ // Check if there is any data to flush.
+ // Agents should flush the proxy link
+ // explicitly.
+ //
+
+ handleFlushInLoop();
+
+ #ifdef TEST
+ *logofs << "Loop: Mark - 6 - at " << strMsTimestamp()
+ << " with " << diffTimestamp(startTs, getTimestamp())
+ << " Ms elapsed.\n" << logofs_flush;
+ #endif
+ }
+
+ //
+ // Check if we have an alert to show.
+ //
+
+ handleAlertInLoop();
+
+ if (control -> ProxyStage >= stage_operational)
+ {
+ //
+ // Check if it's time to give up.
+ //
+
+ handleCheckSessionInLoop();
+
+ //
+ // Check if local proxy is consuming
+ // too many resources.
+ //
+
+ handleCheckBitrateInLoop();
+
+ //
+ // Check coherency of internal state.
+ //
+
+ #if defined(TEST) || defined(INFO)
+
+ handleCheckStateInLoop(*setFDs);
+
+ #endif
+
+ #ifdef TEST
+ *logofs << "Loop: Mark - 7 - at " << strMsTimestamp()
+ << " with " << diffTimestamp(startTs, getTimestamp())
+ << " Ms elapsed.\n" << logofs_flush;
+ #endif
+ }
+
+ //
+ // Truncate the logs if needed.
+ //
+
+ handleLogReopenInLoop(logsTs, nowTs);
+
+ return 1;
+}
+
+//
+// Initialize the connection parameters and
+// prepare for negotiating the link with the
+// remote proxy.
+//
+
+int InitBeforeNegotiation()
+{
+ //
+ // Disable limits on core dumps.
+ //
+
+ SetCore();
+
+ //
+ // Install the signal handlers.
+ //
+
+ InstallSignals();
+
+ //
+ // Track how much time we spent in initialization.
+ //
+
+ nowTs = getNewTimestamp();
+
+ startTs = nowTs;
+ initTs = nowTs;
+
+ #ifdef TEST
+ *logofs << "Loop: INIT! Taking mark for initialization at "
+ << strMsTimestamp(initTs) << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // If not explicitly specified, determine if local
+ // mode is client or server according to parameters
+ // provided so far.
+ //
+
+ if (WE_SET_PROXY_MODE == 0)
+ {
+ cerr << "Error" << ": Please specify either the -C or -S option.\n";
+
+ HandleCleanup();
+ }
+
+ //
+ // Start a watchdog. If initialization cannot
+ // be completed before timeout, then clean up
+ // everything and exit.
+ //
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Starting watchdog process with timeout of "
+ << control -> InitTimeout / 1000 << " seconds.\n"
+ << logofs_flush;
+ #endif
+
+ lastWatchdog = NXTransWatchdog(control -> InitTimeout);
+
+ if (IsFailed(lastWatchdog))
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't start the NX watchdog process.\n"
+ << logofs_flush;
+ #endif
+
+ SetNotRunning(lastWatchdog);
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Watchdog started with pid '"
+ << lastWatchdog << "'.\n" << logofs_flush;
+ }
+ #endif
+ }
+
+ //
+ // Print preliminary info.
+ //
+
+ PrintProcessInfo();
+
+ //
+ // Set cups, multimedia and other
+ // auxiliary ports.
+ //
+
+ SetPorts();
+
+ //
+ // Increase the number of maximum open
+ // file descriptors for this process.
+ //
+
+ SetDescriptors();
+
+ //
+ // Set local endianess.
+ //
+
+ unsigned int test = 1;
+
+ setHostBigEndian(*((unsigned char *) (&test)) == 0);
+
+ #ifdef TEST
+ *logofs << "Loop: Local host is "
+ << (hostBigEndian() ? "big endian" : "little endian")
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ //
+ // Listen on sockets that mimic an X display to
+ // which X clients will be able to connect (e.g.
+ // unix:8 and/or localhost:8).
+ //
+
+ if (useTcpSocket == 1)
+ {
+ SetupTcpSocket();
+ }
+
+ if (useUnixSocket == 1)
+ {
+ SetupUnixSocket();
+ }
+ }
+ else
+ {
+ //
+ // Don't listen for X connections.
+ //
+
+ useUnixSocket = 0;
+ useTcpSocket = 0;
+ useAgentSocket = 0;
+
+ //
+ // Get ready to open the local display.
+ //
+
+ SetupDisplaySocket(xServerAddrFamily, xServerAddr, xServerAddrLength);
+ }
+
+ //
+ // If we are the NX server-side proxy we need to
+ // complete our initializazion. We will mandate
+ // our parameters at the time the NX client will
+ // connect.
+ //
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ SetParameters();
+ }
+
+ return 1;
+}
+
+int SetupProxyConnection()
+{
+ if (proxyFD == -1)
+ {
+ if (WE_INITIATE_CONNECTION)
+ {
+ if (connectPort < 0)
+ {
+ connectPort = DEFAULT_NX_PROXY_PORT_OFFSET + proxyPort;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Going to connect to " << connectHost
+ << ":" << connectPort << ".\n" << logofs_flush;
+ #endif
+
+ proxyFD = ConnectToRemote(connectHost, connectPort);
+
+ #ifdef TEST
+ *logofs << "Loop: Connected to remote proxy on FD#"
+ << proxyFD << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Info" << ": Connection to remote proxy '" << connectHost
+ << ":" << connectPort << "' established.\n";
+ }
+ else
+ {
+ if (listenPort < 0)
+ {
+ listenPort = DEFAULT_NX_PROXY_PORT_OFFSET + proxyPort;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Going to wait for connection on port "
+ << listenPort << ".\n" << logofs_flush;
+ #endif
+
+ proxyFD = WaitForRemote(listenPort);
+
+ #ifdef TEST
+
+ if (WE_LISTEN_FORWARDER)
+ {
+ *logofs << "Loop: Connected to remote forwarder on FD#"
+ << proxyFD << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "Loop: Connected to remote proxy on FD#"
+ << proxyFD << ".\n" << logofs_flush;
+ }
+
+ #endif
+ }
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Using the inherited connection on FD#"
+ << proxyFD << ".\n" << logofs_flush;
+ }
+ #endif
+
+ //
+ // Set TCP_NODELAY on proxy descriptor
+ // to reduce startup time. Option will
+ // later be disabled if needed.
+ //
+
+ SetNoDelay(proxyFD, 1);
+
+ //
+ // We need non-blocking input since the
+ // negotiation phase.
+ //
+
+ SetNonBlocking(proxyFD, 1);
+
+ return 1;
+}
+
+//
+// Create the required proxy and channel classes
+// and get ready for handling the encoded traffic.
+//
+
+int InitAfterNegotiation()
+{
+ #ifdef TEST
+ *logofs << "Loop: Connection with remote proxy completed.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Info" << ": Connection with remote proxy completed.\n"
+ << logofs_flush;
+
+ //
+ // If we are the server proxy we completed
+ // our initializazion phase according to
+ // the values provided by the client side.
+ //
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ SetParameters();
+ }
+
+ //
+ // Set up the listeners for the additional
+ // services.
+ //
+
+ SetupServiceSockets();
+
+ //
+ // Create the proxy class and the statistics
+ // repository and pass all the configuration
+ // data we negotiated with the remote peer.
+ //
+
+ SetupProxyInstance();
+
+ //
+ // We completed both parsing of user's parameters
+ // and handlshaking with remote proxy. Now print
+ // a brief summary including the most significant
+ // control values.
+ //
+
+ PrintConnectionInfo();
+
+ //
+ // Cancel the initialization watchdog.
+ //
+
+ if (IsRunning(lastWatchdog))
+ {
+ KillProcess(lastWatchdog, "watchdog", SIGTERM, 1);
+
+ SetNotRunning(lastWatchdog);
+
+ lastSignal = 0;
+ }
+
+ //
+ // Start the house-keeper process. It will
+ // remove the oldest persistent caches, if
+ // the amount of storage exceed the limits
+ // set by the user.
+ //
+
+ StartKeeper();
+
+ //
+ // Set the log size check timestamp.
+ //
+
+ nowTs = getNewTimestamp();
+
+ logsTs = nowTs;
+
+ //
+ // TODO: Due to the way the new NX transport is working,
+ // the accounting of time spent handling messages must
+ // be rewritten. In particular, at the moment it only
+ // shows the time spent encoding and decoding messages
+ // in the main loop, after executing a select. It doesn't
+ // take into account the time spent in the NXTrans* calls
+ // where messages can be encoded and decoded implicitly,
+ // on demand of the agent. When the agent transport is
+ // in use, these calls constitute the vast majority of
+ // the encoding activity. The result is that the number
+ // of KB encoded per second shown by the proxy statistics
+ // is actually much lower than the real throughput gene-
+ // rated by the proxy.
+ //
+
+ #ifdef TEST
+ *logofs << "Loop: INIT! Completed initialization at "
+ << strMsTimestamp(nowTs) << " with "
+ << diffTimestamp(initTs, nowTs) << " Ms "
+ << "since the init mark.\n" << logofs_flush;
+ #endif
+
+ initTs = getNewTimestamp();
+
+ //
+ // We can now start handling binary data from
+ // our peer proxy.
+ //
+
+ if (agent == NULL)
+ {
+ cerr << "Session" << ": Session started at '"
+ << strTimestamp() << "'.\n";
+ }
+
+ return 1;
+}
+
+int SetMode(int mode)
+{
+ //
+ // Set the local proxy mode.
+ //
+
+ if (control -> ProxyMode == proxy_undefined)
+ {
+ if (mode == NX_MODE_CLIENT)
+ {
+ #ifdef TEST
+ *logofs << "Loop: INIT! Initializing with mode "
+ << "NX_MODE_CLIENT at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ #endif
+
+ control -> ProxyMode = proxy_client;
+ }
+ else if (mode == NX_MODE_SERVER)
+ {
+ #ifdef TEST
+ *logofs << "Loop: INIT! Initializing with mode "
+ << "NX_MODE_SERVER at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ #endif
+
+ control -> ProxyMode = proxy_server;
+ }
+ else
+ {
+ cerr << "Error" << ": Please specify either "
+ << "the -C or -S option.\n";
+
+ HandleCleanup();
+ }
+ }
+
+ return 1;
+}
+
+int SetupProxyInstance()
+{
+ if (control -> ProxyMode == proxy_client)
+ {
+ proxy = new ClientProxy(proxyFD);
+ }
+ else
+ {
+ proxy = new ServerProxy(proxyFD);
+ }
+
+ if (proxy == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Error creating the NX proxy.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error creating the NX proxy.\n";
+
+ HandleCleanup();
+ }
+
+ //
+ // Create the statistics repository.
+ //
+
+ statistics = new Statistics(proxy);
+
+ if (statistics == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Error creating the NX statistics.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error creating the NX statistics.\n";
+
+ HandleCleanup();
+ }
+
+ //
+ // If user gave us a proxy cookie than create the
+ // X11 authorization repository and find the real
+ // cookie to be used for our X display.
+ //
+
+ SetupAuthInstance();
+
+ //
+ // Reset the static members in channels.
+ //
+
+ proxy -> handleChannelConfiguration();
+
+ //
+ // Inform the proxies about the ports where they
+ // will have to forward the network connections.
+ //
+
+ proxy -> handleDisplayConfiguration(displayHost, xServerAddrFamily,
+ xServerAddr, xServerAddrLength);
+
+ proxy -> handlePortConfiguration(cupsPort, smbPort, mediaPort,
+ httpPort, fontPort);
+
+ //
+ // We handed over the sockaddr structure we
+ // created when we set up the display socket
+ // to the proxy.
+ //
+
+ xServerAddr = NULL;
+
+ //
+ // Set socket options on proxy link, then propagate link
+ // configuration to proxy. This includes translating some
+ // control parameters in 'local' and 'remote'. Finally
+ // adjust cache parameters according to pack method and
+ // session type selected by user.
+ //
+
+ if (proxy -> handleSocketConfiguration() < 0 ||
+ proxy -> handleLinkConfiguration() < 0 ||
+ proxy -> handleCacheConfiguration() < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Error configuring the NX transport.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error configuring the NX transport.\n";
+
+ HandleCleanup();
+ }
+
+ //
+ // Load the message stores from the persistent
+ // cache.
+ //
+
+ proxy -> handleLoad(load_if_first);
+
+ //
+ // Inform the proxy that from now on it can
+ // start handling the encoded data.
+ //
+
+ proxy -> setOperational();
+
+ //
+ // Are we going to use an internal IPC connection
+ // with an agent? In this case create the channel
+ // by using the socket descriptor provided by the
+ // caller at the proxy initialization.
+ //
+
+ SetupAgentInstance();
+
+ //
+ // Check if we need to verify the existence of
+ // a matching client cache at shutdown.
+ //
+
+ #ifdef MATCH
+
+ control -> PersistentCacheCheckOnShutdown = 1;
+
+ #endif
+
+ //
+ // Flush any data produced so far.
+ //
+
+ proxy -> handleFlush();
+
+ #if defined(TEST) || defined(INFO)
+
+ if (proxy -> getFlushable(proxyFD) > 0)
+ {
+ *logofs << "Loop: WARNING! Proxy FD#" << proxyFD << " has data "
+ << "to flush after setup of the NX transport.\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ return 1;
+}
+
+int SetupAuthInstance()
+{
+ //
+ // If user gave us a proxy cookie, then create the
+ // X11 authorization repository and find the real
+ // cookie to be used for our X display.
+ //
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ if (authCookie != NULL && *authCookie != '\0')
+ {
+ if (useLaunchdSocket == 1)
+ {
+ //
+ // If we are going to retrieve the X11 autho-
+ // rization through the launchd service, make
+ // a connection to its socket to trigger the
+ // X server starting.
+ //
+
+ sockaddr_un launchdAddrUnix;
+
+ unsigned int launchdAddrLength = sizeof(sockaddr_un);
+
+ int launchdAddrFamily = AF_UNIX;
+
+ launchdAddrUnix.sun_family = AF_UNIX;
+
+ const int launchdAddrNameLength = 108;
+
+ int success = -1;
+
+ strncpy(launchdAddrUnix.sun_path, displayHost, launchdAddrNameLength);
+
+ *(launchdAddrUnix.sun_path + launchdAddrNameLength - 1) = '\0';
+
+ #ifdef TEST
+ *logofs << "Loop: Connecting to launchd service "
+ << "on Unix port '" << displayHost << "'.\n" << logofs_flush;
+ #endif
+
+ int launchdFd = socket(launchdAddrFamily, SOCK_STREAM, PF_UNSPEC);
+
+ if (launchdFd < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to socket failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+ else if ((success = connect(launchdFd, (sockaddr *) &launchdAddrUnix, launchdAddrLength)) < 0)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Connection to launchd service "
+ << "on Unix port '" << displayHost << "' failed "
+ << "with error " << EGET() << ", '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ if (launchdFd >= 0)
+ {
+ close(launchdFd);
+ }
+
+ //
+ // The real cookie will not be available
+ // until the X server starts. Query for the
+ // cookie in a loop, unless the connection
+ // to the launchd service failed.
+ //
+
+ int attempts = (success < 0 ? 1 : 10);
+
+ for (int i = 0; i < attempts; i++)
+ {
+ delete auth;
+
+ auth = new Auth(displayHost, authCookie);
+
+ if (auth != NULL && auth -> isFake() == 1)
+ {
+ usleep(200000);
+
+ continue;
+ }
+
+ break;
+ }
+ }
+ else
+ {
+ auth = new Auth(displayHost, authCookie);
+ }
+
+ if (auth == NULL || auth -> isValid() != 1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Error creating the X authorization.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error creating the X authorization.\n";
+
+ HandleCleanup();
+ }
+ else if (auth -> isFake() == 1)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Could not retrieve the X server "
+ << "authentication cookie.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Failed to read data from the X "
+ << "auth command.\n";
+
+ cerr << "Warning" << ": Generated a fake cookie for X "
+ << "authentication.\n";
+ }
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Loop: No proxy cookie was provided for "
+ << "authentication.\n" << logofs_flush;
+ #endif
+
+ cerr << "Info" << ": No proxy cookie was provided for "
+ << "authentication.\n";
+
+ #ifdef TEST
+ *logofs << "Loop: Forwarding the real X authorization "
+ << "cookie.\n" << logofs_flush;
+ #endif
+
+ cerr << "Info" << ": Forwarding the real X authorization "
+ << "cookie.\n";
+ }
+ }
+
+ return 1;
+}
+
+int SetupAgentInstance()
+{
+ if (control -> ProxyMode == proxy_client &&
+ useAgentSocket == 1)
+ {
+ //
+ // This will temporarily disable signals to safely
+ // load the cache, then will send a control packet
+ // to the remote end, telling that cache has to be
+ // loaded, so it's important that proxy is already
+ // set in operational state.
+ //
+
+ int result;
+
+ if (agent != NULL)
+ {
+ result = proxy -> handleNewAgentConnection(agent);
+ }
+ else
+ {
+ result = proxy -> handleNewConnection(channel_x11, agentFD[1]);
+ }
+
+ if (result < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Error creating the NX agent connection.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error creating the NX agent connection.\n";
+
+ HandleCleanup();
+ }
+ }
+
+ return 1;
+}
+
+int SetupTcpSocket()
+{
+ //
+ // Open TCP socket emulating local display.
+ //
+
+ tcpFD = socket(AF_INET, SOCK_STREAM, PF_UNSPEC);
+
+ if (tcpFD == -1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to socket failed for TCP socket"
+ << ". Error is " << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to socket failed for TCP socket"
+ << ". Error is " << EGET() << " '" << ESTR() << "'.\n";
+
+ HandleCleanup();
+ }
+ else if (SetReuseAddress(tcpFD) < 0)
+ {
+ HandleCleanup();
+ }
+
+ unsigned int proxyPortTCP = X_TCP_PORT + proxyPort;
+
+ sockaddr_in tcpAddr;
+
+ tcpAddr.sin_family = AF_INET;
+ tcpAddr.sin_port = htons(proxyPortTCP);
+ tcpAddr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ if (bind(tcpFD, (sockaddr *) &tcpAddr, sizeof(tcpAddr)) == -1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to bind failed for TCP port "
+ << proxyPortTCP << ". Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to bind failed for TCP port "
+ << proxyPortTCP << ". Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+
+ HandleCleanup();
+ }
+
+ if (listen(tcpFD, 8) == -1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to listen failed for TCP port "
+ << proxyPortTCP << ". Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to listen failed for TCP port "
+ << proxyPortTCP << ". Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+
+ HandleCleanup();
+ }
+
+ return 1;
+}
+
+int SetupUnixSocket()
+{
+ //
+ // Open UNIX domain socket for display.
+ //
+
+ unixFD = socket(AF_UNIX, SOCK_STREAM, PF_UNSPEC);
+
+ if (unixFD == -1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to socket failed for UNIX domain"
+ << ". Error is " << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to socket failed for UNIX domain"
+ << ". Error is " << EGET() << " '" << ESTR() << "'.\n";
+
+ HandleCleanup();
+ }
+
+ sockaddr_un unixAddr;
+ unixAddr.sun_family = AF_UNIX;
+
+ char dirName[DEFAULT_STRING_LENGTH];
+
+ snprintf(dirName, DEFAULT_STRING_LENGTH - 1, "%s/.X11-unix",
+ control -> TempPath);
+
+ *(dirName + DEFAULT_STRING_LENGTH - 1) = '\0';
+
+ struct stat dirStat;
+
+ if ((stat(dirName, &dirStat) == -1) && (EGET() == ENOENT))
+ {
+ mkdir(dirName, (0777 | S_ISVTX));
+ chmod(dirName, (0777 | S_ISVTX));
+ }
+
+ snprintf(unixSocketName, DEFAULT_STRING_LENGTH - 1, "%s/X%d",
+ dirName, proxyPort);
+
+ strncpy(unixAddr.sun_path, unixSocketName, 108);
+
+ #ifdef TEST
+ *logofs << "Loop: Assuming Unix socket with name '"
+ << unixAddr.sun_path << "'.\n"
+ << logofs_flush;
+ #endif
+
+ *(unixAddr.sun_path + 107) = '\0';
+
+ if (bind(unixFD, (sockaddr *) &unixAddr, sizeof(unixAddr)) == -1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to bind failed for UNIX domain socket "
+ << unixSocketName << ". Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to bind failed for UNIX domain socket "
+ << unixSocketName << ". Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+
+ HandleCleanup();
+ }
+
+ if (listen(unixFD, 8) == -1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to listen failed for UNIX domain socket "
+ << unixSocketName << ". Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to listen failed for UNIX domain socket "
+ << unixSocketName << ". Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+
+ HandleCleanup();
+ }
+
+ //
+ // Let any local user to gain access to socket.
+ //
+
+ chmod(unixSocketName, 0777);
+
+ return 1;
+}
+
+//
+// The following is a dumb copy-paste. The
+// nxcompsh library should offer a better
+// implementation.
+//
+
+int SetupDisplaySocket(int &xServerAddrFamily, sockaddr *&xServerAddr,
+ unsigned int &xServerAddrLength)
+{
+ xServerAddrFamily = AF_INET;
+ xServerAddr = NULL;
+ xServerAddrLength = 0;
+
+ char *display;
+
+ if (*displayHost == '\0')
+ {
+ //
+ // Assume DISPLAY as the X server to which
+ // we will forward the proxied connections.
+ // This means that NX parameters have been
+ // passed through other means.
+ //
+
+ display = getenv("DISPLAY");
+
+ if (display == NULL || *display == '\0')
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Host X server DISPLAY is not set.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Host X server DISPLAY is not set.\n";
+
+ HandleCleanup();
+ }
+ else if (strncasecmp(display, "nx/nx,", 6) == 0 ||
+ strncasecmp(display, "nx,", 3) == 0 ||
+ strncasecmp(display, "nx:", 3) == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! NX transport on host X server '"
+ << display << "' not supported.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": NX transport on host X server '"
+ << display << "' not supported.\n";
+
+ cerr << "Error" << ": Please run the local proxy specifying "
+ << "the host X server to connect to.\n";
+
+ HandleCleanup();
+ }
+ else if (strlen(display) >= DEFAULT_STRING_LENGTH)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Host X server DISPLAY cannot exceed "
+ << DEFAULT_STRING_LENGTH << " characters.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Host X server DISPLAY cannot exceed "
+ << DEFAULT_STRING_LENGTH << " characters.\n";
+
+ HandleCleanup();
+ }
+
+ strcpy(displayHost, display);
+ }
+
+ display = new char[strlen(displayHost) + 1];
+
+ if (display == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Out of memory handling DISPLAY variable.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Out of memory handling DISPLAY variable.\n";
+
+ HandleCleanup();
+ }
+
+ strcpy(display, displayHost);
+
+ #ifdef __APPLE__
+
+ if (strncasecmp(display, "/tmp/launch", 11) == 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Using launchd service on socket '"
+ << display << "'.\n" << logofs_flush;
+ #endif
+
+ useLaunchdSocket = 1;
+ }
+
+ #endif
+
+ char *separator = rindex(display, ':');
+
+ if ((separator == NULL) || !isdigit(*(separator + 1)))
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid display '" << display << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid display '" << display << "'.\n";
+
+ HandleCleanup();
+ }
+
+ *separator = '\0';
+
+ xPort = atoi(separator + 1);
+
+ #ifdef TEST
+ *logofs << "Loop: Using local X display '" << displayHost
+ << "' with host '" << display << "' and port '"
+ << xPort << "'.\n" << logofs_flush;
+ #endif
+
+ #ifdef __APPLE__
+
+ if (separator == display || strcmp(display, "unix") == 0 ||
+ useLaunchdSocket == 1)
+
+ #else
+
+ if (separator == display || strcmp(display, "unix") == 0)
+
+ #endif
+ {
+ //
+ // UNIX domain port.
+ //
+
+ #ifdef TEST
+ *logofs << "Loop: Using real X server on UNIX domain socket.\n"
+ << logofs_flush;
+ #endif
+
+ sockaddr_un *xServerAddrUNIX = new sockaddr_un;
+
+ xServerAddrFamily = AF_UNIX;
+ xServerAddrUNIX -> sun_family = AF_UNIX;
+
+ //
+ // The scope of this function is to fill either the sockaddr_un
+ // (when the display is set to the Unix Domain socket) or the
+ // sockaddr_in structure (when connecting by TCP) only once, so
+ // that the structure will be later used at the time the server
+ // proxy will try to forward the connection to the X server. We
+ // don't need to verify that the socket does exist at the pre-
+ // sent moment. The method that forwards the connection will
+ // perform the required checks and will retry, if needed. Anyway
+ // we need to select the name of the socket, so we check if the
+ // well-known directory exists and take that as an indication of
+ // where the socket will be created.
+ //
+
+ struct stat statInfo;
+
+ char unixSocketDir[DEFAULT_STRING_LENGTH];
+
+ snprintf(unixSocketDir, DEFAULT_STRING_LENGTH - 1, "%s/.X11-unix",
+ control -> TempPath);
+
+ #ifdef __APPLE__
+
+ if (useLaunchdSocket == 1)
+ {
+ char *slash = rindex(display, '/');
+
+ if (slash != NULL)
+ {
+ *slash = '\0';
+ }
+
+ snprintf(unixSocketDir, DEFAULT_STRING_LENGTH - 1, "%s", display);
+ }
+
+ #endif
+
+ *(unixSocketDir + DEFAULT_STRING_LENGTH - 1) = '\0';
+
+ #ifdef TEST
+ *logofs << "Loop: Assuming X socket in directory '"
+ << unixSocketDir << "'.\n" << logofs_flush;
+ #endif
+
+ if (stat(unixSocketDir, &statInfo) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't determine the location of "
+ << "the X display socket.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't determine the location of "
+ << "the X display socket.\n";
+
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Error " << EGET() << " '" << ESTR()
+ << "' checking '" << unixSocketDir << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error " << EGET() << " '" << ESTR()
+ << "' checking '" << unixSocketDir << "'.\n";
+
+ HandleCleanup();
+ }
+
+ sprintf(unixSocketName, "%s/X%d", unixSocketDir, xPort);
+
+ #ifdef __APPLE__
+
+ if (useLaunchdSocket == 1)
+ {
+ strncpy(unixSocketName, displayHost, DEFAULT_STRING_LENGTH - 1);
+ }
+
+ #endif
+
+ #ifdef TEST
+ *logofs << "Loop: Assuming X socket name '" << unixSocketName
+ << "'.\n" << logofs_flush;
+ #endif
+
+ strcpy(xServerAddrUNIX -> sun_path, unixSocketName);
+
+ xServerAddr = (sockaddr *) xServerAddrUNIX;
+ xServerAddrLength = sizeof(sockaddr_un);
+ }
+ else
+ {
+ //
+ // TCP port.
+ //
+
+ #ifdef TEST
+ *logofs << "Loop: Using real X server on TCP port.\n"
+ << logofs_flush;
+ #endif
+
+ xServerAddrFamily = AF_INET;
+
+ int xServerIPAddr = GetHostAddress(display);
+
+ if (xServerIPAddr == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Unknown display host '" << display
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Unknown display host '" << display
+ << "'.\n";
+
+ HandleCleanup();
+ }
+
+ sockaddr_in *xServerAddrTCP = new sockaddr_in;
+
+ xServerAddrTCP -> sin_family = AF_INET;
+ xServerAddrTCP -> sin_port = htons(X_TCP_PORT + xPort);
+ xServerAddrTCP -> sin_addr.s_addr = xServerIPAddr;
+
+ xServerAddr = (sockaddr *) xServerAddrTCP;
+ xServerAddrLength = sizeof(sockaddr_in);
+ }
+
+ delete [] display;
+
+ return 1;
+}
+
+int SetupServiceSockets()
+{
+ if (control -> ProxyMode == proxy_client)
+ {
+ if (useCupsSocket)
+ {
+ if ((cupsFD = ListenConnection(cupsPort, "CUPS")) < 0)
+ {
+ useCupsSocket = 0;
+ }
+ }
+
+ if (useAuxSocket)
+ {
+ if ((auxFD = ListenConnection(auxPort, "auxiliary X11")) < 0)
+ {
+ useAuxSocket = 0;
+ }
+ }
+
+ if (useSmbSocket)
+ {
+ if ((smbFD = ListenConnection(smbPort, "SMB")) < 0)
+ {
+ useSmbSocket = 0;
+ }
+ }
+
+ if (useMediaSocket)
+ {
+ if ((mediaFD = ListenConnection(mediaPort, "media")) < 0)
+ {
+ useMediaSocket = 0;
+ }
+ }
+
+ if (useHttpSocket)
+ {
+ if ((httpFD = ListenConnection(httpPort, "http")) < 0)
+ {
+ useHttpSocket = 0;
+ }
+ }
+
+ useFontSocket = 0;
+ }
+ else
+ {
+ //
+ // Disable the font server connections if
+ // they are not supported by the remote
+ // proxy.
+ //
+
+ if (useFontSocket)
+ {
+ if (control -> isProtoStep7() == 1)
+ {
+ int port = atoi(fontPort);
+
+ if ((fontFD = ListenConnection(port, "font")) < 0)
+ {
+ useFontSocket = 0;
+ }
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Font server connections not supported "
+ << "by the remote proxy.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Font server connections not supported "
+ << "by the remote proxy.\n";
+
+ useFontSocket = 0;
+ }
+ }
+
+ useCupsSocket = 0;
+ useAuxSocket = 0;
+ useSmbSocket = 0;
+ useMediaSocket = 0;
+ useHttpSocket = 0;
+ }
+
+ //
+ // Slave channels can be originated
+ // by both sides.
+ //
+
+ if (useSlaveSocket)
+ {
+ if (control -> isProtoStep7() == 1)
+ {
+ if ((slaveFD = ListenConnection(slavePort, "slave")) < 0)
+ {
+ useSlaveSocket = 0;
+ }
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Slave connections not supported "
+ << "by the remote proxy.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Slave connections not supported "
+ << "by the remote proxy.\n";
+
+ useSlaveSocket = 0;
+ }
+ }
+
+ return 1;
+}
+
+int ListenConnection(int port, const char *label)
+{
+ sockaddr_in tcpAddr;
+
+ unsigned int portTCP = port;
+
+ int newFD = socket(AF_INET, SOCK_STREAM, PF_UNSPEC);
+
+ if (newFD == -1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to socket failed for " << label
+ << " TCP socket. Error is " << EGET() << " '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to socket failed for " << label
+ << " TCP socket. Error is " << EGET() << " '"
+ << ESTR() << "'.\n";
+
+ goto SetupSocketError;
+ }
+ else if (SetReuseAddress(newFD) < 0)
+ {
+ goto SetupSocketError;
+ }
+
+ tcpAddr.sin_family = AF_INET;
+ tcpAddr.sin_port = htons(portTCP);
+ tcpAddr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ if (bind(newFD, (sockaddr *) &tcpAddr, sizeof(tcpAddr)) == -1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to bind failed for " << label
+ << " TCP port " << port << ". Error is " << EGET()
+ << " '" << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to bind failed for " << label
+ << " TCP port " << port << ". Error is " << EGET()
+ << " '" << ESTR() << "'.\n";
+
+ goto SetupSocketError;
+ }
+
+ if (listen(newFD, 8) == -1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to bind failed for " << label
+ << " TCP port " << port << ". Error is " << EGET()
+ << " '" << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to bind failed for " << label
+ << " TCP port " << port << ". Error is " << EGET()
+ << " '" << ESTR() << "'.\n";
+
+ goto SetupSocketError;
+ }
+
+ return newFD;
+
+SetupSocketError:
+
+ if (newFD != -1)
+ {
+ close(newFD);
+ }
+
+ //
+ // May optionally return. The session would
+ // continue without the service. The problem
+ // with this approach is that it would make
+ // harder to track problems with allocation
+ // of ports in clients and server.
+ //
+
+ HandleCleanup();
+}
+
+static int AcceptConnection(int fd, int domain, const char *label)
+{
+ struct sockaddr newAddr;
+
+ socklen_t addrLen = sizeof(newAddr);
+
+ #ifdef TEST
+
+ if (domain == AF_UNIX)
+ {
+ *logofs << "Loop: Going to accept new Unix " << label
+ << " connection on FD#" << fd << ".\n"
+ << logofs_flush;
+ }
+ else
+ {
+ *logofs << "Loop: Going to accept new TCP " << label
+ << " connection on FD#" << fd << ".\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ int newFD = accept(fd, &newAddr, &addrLen);
+
+ if (newFD < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to accept failed for "
+ << label << " connection. Error is " << EGET()
+ << " '" << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to accept failed for "
+ << label << " connection. Error is " << EGET()
+ << " '" << ESTR() << "'.\n";
+ }
+
+ return newFD;
+}
+
+void HandleShutdown()
+{
+ if (proxy -> getShutdown() == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! No shutdown of proxy link "
+ << "performed by remote proxy.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Close the socket before showing the alert.
+ // It seems that the closure of the socket can
+ // sometimes take several seconds, even after
+ // the connection is broken. The result is that
+ // the dialog can be shown long after the user
+ // has gone after the failed session. Note that
+ // disabling the linger timeout does not seem
+ // to make any difference.
+ //
+
+ CleanupSockets();
+
+ cerr << "Error" << ": Connection with remote peer broken.\n";
+
+ #ifdef TEST
+ *logofs << "Loop: Bytes received so far are "
+ << (unsigned long long) statistics -> getBytesIn()
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Please check the state of your "
+ << "network and retry.\n";
+
+ handleTerminatingInLoop();
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Showing the proxy abort dialog.\n"
+ << logofs_flush;
+ #endif
+
+ HandleAlert(ABORT_PROXY_CONNECTION_ALERT, 1);
+
+ handleAlertInLoop();
+ }
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Finalized the remote proxy shutdown.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ HandleCleanup();
+}
+
+
+void WaitCleanup()
+{
+ T_timestamp selectTs;
+
+ while (NXTransRunning(NX_FD_ANY))
+ {
+ setTimestamp(selectTs, control -> PingTimeout);
+
+ NXTransContinue(&selectTs);
+ }
+}
+
+int KillProcess(int pid, const char *label, int signal, int wait)
+{
+ if (pid > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Killing the " << label << " process '"
+ << pid << "' from process with pid '" << getpid()
+ << "' with signal '" << DumpSignal(signal)
+ << "'.\n" << logofs_flush;
+ #endif
+
+ signal = (signal == 0 ? SIGTERM : signal);
+
+ if (kill(pid, signal) < 0 && EGET() != ESRCH)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Couldn't kill the " << label
+ << " process with pid '" << pid << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Couldn't kill the " << label
+ << " process with pid '" << pid << "'.\n";
+ }
+
+ if (wait == 1)
+ {
+ WaitChild(pid, label, 1);
+ }
+
+ return 1;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Loop: No " << label << " process "
+ << "to kill with pid '" << pid
+ << "'.\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+}
+
+int CheckProcess(int pid, const char *label)
+{
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Checking the " << label << " process '"
+ << pid << "' from process with pid '" << getpid()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ if (kill(pid, SIGCONT) < 0 && EGET() == ESRCH)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! The " << label << " process "
+ << "with pid '" << pid << "' has exited.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": The " << label << " process "
+ << "with pid '" << pid << "' has exited.\n";
+
+ return 0;
+ }
+
+ return 1;
+}
+
+int StartKeeper()
+{
+ #if defined(TEST) || defined(INFO)
+
+ if (IsRunning(lastKeeper) == 1 ||
+ IsRestarting(lastKeeper) == 1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! The house-keeping process is "
+ << "alreay running with pid '" << lastKeeper
+ << "'.\n" << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ //
+ // Don't care harvesting the persistent caches if
+ // the memory cache is not enabled. If the memory
+ // cache is not enabled neither we produced per-
+ // sistent caches. The user can still delete any
+ // persistent cache produced by the previous runs
+ // by using the client GUI.
+ //
+ // TODO: At the moment the user doesn't have a way
+ // to specify the amount of disk space to use for
+ // the persistent caches, but only the amount of
+ // space to use for images.
+ //
+
+ if (control -> LocalTotalStorageSize > 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Starting the house-keeping process with "
+ << "storage size " << control -> PersistentCacheDiskLimit
+ << ".\n" << logofs_flush;
+ #endif
+
+ lastKeeper = NXTransKeeper(control -> PersistentCacheDiskLimit,
+ 0, control -> RootPath);
+
+ if (IsFailed(lastKeeper))
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Failed to start the NX keeper process.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Failed to start the NX keeper process.\n";
+
+ SetNotRunning(lastKeeper);
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Keeper started with pid '"
+ << lastKeeper << "'.\n" << logofs_flush;
+ }
+ #endif
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Nothing to do for the keeper process "
+ << "with persistent cache not enabled.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ return 1;
+}
+
+void HandleCleanup(int code)
+{
+ #ifdef TEST
+ *logofs << "Loop: Going to clean up system resources "
+ << "in process '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ handleTerminatedInLoop();
+
+ //
+ // Suspend any signal while cleaning up.
+ //
+
+ DisableSignals();
+
+ if (getpid() == lastProxy)
+ {
+ //
+ // Terminate all the children.
+ //
+
+ CleanupChildren();
+
+ //
+ // Close all listeners.
+ //
+
+ CleanupListeners();
+
+ //
+ // Close all sockets.
+ //
+
+ CleanupSockets();
+
+ //
+ // Release the global objects.
+ //
+
+ CleanupGlobal();
+
+ //
+ // Restore the original signal handlers.
+ //
+
+ RestoreSignals();
+ }
+
+ //
+ // This is our last chance to print a message. If this
+ // is the process which created the transport we will
+ // jump back into the loop, letting the caller find out
+ // that the connection is broken, otherwise we assume
+ // that this is a child of the proxy and so we will
+ // safely exit.
+ //
+
+ #ifdef TEST
+
+ if (getpid() == lastProxy)
+ {
+ *logofs << "Loop: Reverting to loop context in process with "
+ << "pid '" << getpid() << "' at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "Loop: Exiting from child process with pid '"
+ << getpid() << "' at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ }
+
+ #endif
+
+ if (getpid() == lastProxy)
+ {
+ //
+ // Reset all values to their default.
+ //
+
+ CleanupLocal();
+
+ CleanupStreams();
+
+ longjmp(context, 1);
+ }
+ else
+ {
+ //
+ // Give a last chance to the process
+ // to cleanup the ancillary classes.
+ //
+
+ CleanupKeeper();
+
+ CleanupStreams();
+
+ exit(code);
+ }
+}
+
+void CleanupKeeper()
+{
+ if (keeper != NULL)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Freeing up keeper in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ delete keeper;
+
+ keeper = NULL;
+ }
+}
+
+void CleanupStreams()
+{
+ //
+ // TODO: The cleanup procedure skips deletion of
+ // the I/O streams under Windows. This is intended
+ // to avoid a strange segfault occurring randomly,
+ // at the time the proxy is being shut down.
+ //
+
+ #ifndef __CYGWIN32__
+
+ #ifdef TEST
+ *logofs << "Loop: Freeing up streams in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ if (logofs != NULL && logofs != &cerr &&
+ *errorsFileName != '\0')
+ {
+ *logofs << flush;
+
+ delete logofs;
+
+ //
+ // Let the log go again to the standard
+ // error.
+ //
+
+ logofs = &cerr;
+ }
+
+ if (statofs != NULL && statofs != &cerr &&
+ *statsFileName != '\0')
+ {
+ *statofs << flush;
+
+ delete statofs;
+
+ statofs = NULL;
+ }
+
+ if (errofs != NULL)
+ {
+ *errofs << flush;
+
+ if (errofs == &cerr)
+ {
+ errofs = NULL;
+ }
+ else if (errsbuf != NULL)
+ {
+ cerr.rdbuf(errsbuf);
+
+ errsbuf = NULL;
+
+ delete errofs;
+ }
+
+ errofs = NULL;
+ }
+
+ #endif /* #ifndef __CYGWIN32__ */
+
+ //
+ // Reset these as they can't be reset
+ // in CleanupLocal().
+ //
+
+ *sessionFileName = '\0';
+ *errorsFileName = '\0';
+ *optionsFileName = '\0';
+ *statsFileName = '\0';
+}
+
+void CleanupChildren()
+{
+ //
+ // Remove any watchdog.
+ //
+
+ if (IsRunning(lastWatchdog))
+ {
+ KillProcess(lastWatchdog, "watchdog", SIGTERM, 1);
+
+ SetNotRunning(lastWatchdog);
+
+ lastSignal = 0;
+ }
+
+ //
+ // Kill the cache house-keeping process.
+ //
+
+ if (IsRunning(lastKeeper))
+ {
+ KillProcess(lastKeeper, "house-keeping", SIGTERM, 1);
+
+ SetNotRunning(lastKeeper);
+ }
+
+ //
+ // Let any running dialog to continue until it is
+ // closed by the user. In general this is the exp-
+ // ected behaviour, as for example when we are
+ // exiting because the link was abrouptedly shut
+ // down.
+ //
+
+ if (IsRunning(lastDialog))
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: WARNING! Leaving the dialog process '"
+ << lastDialog << "' running in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ SetNotRunning(lastDialog);
+ }
+
+ //
+ // Give user a chance to start a new session.
+ //
+
+ if (control -> EnableRestartOnShutdown == 1)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Respawning the NX client "
+ << "on display '" << displayHost << "'.\n"
+ << logofs_flush;
+ #endif
+
+ NXTransClient(displayHost);
+ }
+
+ for (int i = 0; i < control -> KillDaemonOnShutdownNumber; i++)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Killing the NX daemon with "
+ << "pid '" << control -> KillDaemonOnShutdown[i]
+ << "'.\n" << logofs_flush;
+ #endif
+
+ KillProcess(control -> KillDaemonOnShutdown[i], "daemon", SIGTERM, 0);
+ }
+}
+
+void CleanupGlobal()
+{
+ if (proxy != NULL)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Freeing up proxy in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ delete proxy;
+
+ proxy = NULL;
+ }
+
+ if (agent != NULL)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Freeing up agent in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ delete agent;
+
+ agent = NULL;
+ }
+
+ if (auth != NULL)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Freeing up auth data in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ delete auth;
+
+ auth = NULL;
+ }
+
+ if (statistics != NULL)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Freeing up statistics in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ delete statistics;
+
+ statistics = NULL;
+ }
+
+ if (control != NULL)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Freeing up control in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ delete control;
+
+ control = NULL;
+ }
+}
+
+void CleanupConnections()
+{
+ if (proxy -> getChannels(channel_x11) != 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Closing any remaining X connections.\n"
+ << logofs_flush;
+ #endif
+
+ proxy -> handleCloseAllXConnections();
+
+ #ifdef TEST
+ *logofs << "Loop: Closing any remaining listener.\n"
+ << logofs_flush;
+ #endif
+
+ proxy -> handleCloseAllListeners();
+ }
+
+ proxy -> handleFinish();
+}
+
+void CleanupListeners()
+{
+ if (useTcpSocket == 1)
+ {
+ if (tcpFD != -1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Closing TCP listener in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ close(tcpFD);
+
+ tcpFD = -1;
+ }
+
+ useTcpSocket = 0;
+ }
+
+ if (useUnixSocket == 1)
+ {
+ if (unixFD != -1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Closing UNIX listener in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ close(unixFD);
+
+ unixFD = -1;
+ }
+
+ if (*unixSocketName != '\0')
+ {
+ #ifdef TEST
+ *logofs << "Loop: Going to remove the Unix domain socket '"
+ << unixSocketName << "' in process " << "with pid '"
+ << getpid() << "'.\n" << logofs_flush;
+ #endif
+
+ unlink(unixSocketName);
+ }
+
+ useUnixSocket = 0;
+ }
+
+ if (useAgentSocket == 1)
+ {
+ //
+ // There is no listener for the
+ // agent descriptor.
+ //
+
+ useAgentSocket = 0;
+ }
+
+ if (useCupsSocket == 1)
+ {
+ if (cupsFD != -1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Closing CUPS listener in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ close(cupsFD);
+
+ cupsFD = -1;
+ }
+
+ useCupsSocket = 0;
+ }
+
+ if (useAuxSocket == 1)
+ {
+ if (auxFD != -1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Closing auxiliary X11 listener "
+ << "in process " << "with pid '" << getpid()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ close(auxFD);
+
+ auxFD = -1;
+ }
+
+ useAuxSocket = 0;
+ }
+
+ if (useSmbSocket == 1)
+ {
+ if (smbFD != -1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Closing SMB listener in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ close(smbFD);
+
+ smbFD = -1;
+ }
+
+ useSmbSocket = 0;
+ }
+
+ if (useMediaSocket == 1)
+ {
+ if (mediaFD != -1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Closing multimedia listener in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ close(mediaFD);
+
+ mediaFD = -1;
+ }
+
+ useMediaSocket = 0;
+ }
+
+ if (useHttpSocket == 1)
+ {
+ if (httpFD != -1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Closing http listener in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ close(httpFD);
+
+ httpFD = -1;
+ }
+
+ useHttpSocket = 0;
+ }
+
+ if (useFontSocket == 1)
+ {
+ if (fontFD != -1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Closing font server listener in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ close(fontFD);
+
+ fontFD = -1;
+ }
+
+ useFontSocket = 0;
+ }
+
+ if (useSlaveSocket == 1)
+ {
+ if (slaveFD != -1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Closing slave listener in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ close(slaveFD);
+
+ slaveFD = -1;
+ }
+
+ useSlaveSocket = 0;
+ }
+}
+
+void CleanupSockets()
+{
+ if (proxyFD != -1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Closing proxy FD in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ close(proxyFD);
+
+ proxyFD = -1;
+ }
+
+ if (agentFD[1] != -1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Closing agent FD in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ close(agentFD[1]);
+
+ agentFD[0] = -1;
+ agentFD[1] = -1;
+ }
+}
+
+void CleanupLocal()
+{
+ *homeDir = '\0';
+ *rootDir = '\0';
+ *tempDir = '\0';
+ *systemDir = '\0';
+ *sessionDir = '\0';
+
+ *linkSpeedName = '\0';
+ *cacheSizeName = '\0';
+ *shsegSizeName = '\0';
+ *imagesSizeName = '\0';
+ *bitrateLimitName = '\0';
+ *packMethodName = '\0';
+ *productName = '\0';
+
+ packMethod = -1;
+ packQuality = -1;
+
+ *sessionType = '\0';
+ *sessionId = '\0';
+
+ parsedOptions = 0;
+ parsedCommand = 0;
+
+ *remoteData = '\0';
+ remotePosition = 0;
+
+ tcpFD = -1;
+ unixFD = -1;
+ cupsFD = -1;
+ auxFD = -1;
+ smbFD = -1;
+ mediaFD = -1;
+ httpFD = -1;
+ fontFD = -1;
+ slaveFD = -1;
+ proxyFD = -1;
+
+ agentFD[0] = -1;
+ agentFD[1] = -1;
+
+ useUnixSocket = 1;
+ useTcpSocket = 1;
+ useCupsSocket = 0;
+ useAuxSocket = 0;
+ useSmbSocket = 0;
+ useMediaSocket = 0;
+ useHttpSocket = 0;
+ useFontSocket = 0;
+ useSlaveSocket = 0;
+ useAgentSocket = 0;
+
+ useNoDelay = -1;
+ usePolicy = -1;
+ useRender = -1;
+ useTaint = -1;
+
+ *unixSocketName = '\0';
+
+ *connectHost = '\0';
+ *acceptHost = '\0';
+ *listenHost = '\0';
+ *displayHost = '\0';
+ *authCookie = '\0';
+
+ proxyPort = DEFAULT_NX_PROXY_PORT;
+ xPort = DEFAULT_NX_X_PORT;
+
+ xServerAddrFamily = -1;
+ xServerAddrLength = 0;
+
+ delete xServerAddr;
+
+ xServerAddr = NULL;
+
+ listenPort = -1;
+ connectPort = -1;
+
+ cupsPort = -1;
+ auxPort = -1;
+ smbPort = -1;
+ mediaPort = -1;
+ httpPort = -1;
+ slavePort = -1;
+
+ *fontPort = '\0';
+
+ *bindHost = '\0';
+ bindPort = -1;
+
+ initTs = nullTimestamp();
+ startTs = nullTimestamp();
+ logsTs = nullTimestamp();
+ nowTs = nullTimestamp();
+
+ diffTs = 0;
+
+ lastProxy = 0;
+ lastDialog = 0;
+ lastWatchdog = 0;
+ lastKeeper = 0;
+ lastStatus = 0;
+ lastKill = 0;
+ lastDestroy = 0;
+
+ lastReadableTs = nullTimestamp();
+
+ lastAlert.code = 0;
+ lastAlert.local = 0;
+
+ lastMasks.blocked = 0;
+ lastMasks.installed = 0;
+
+ memset(&lastMasks.saved, 0, sizeof(sigset_t));
+
+ for (int i = 0; i < 32; i++)
+ {
+ lastMasks.enabled[i] = 0;
+ lastMasks.forward[i] = 0;
+
+ memset(&lastMasks.action[i], 0, sizeof(struct sigaction));
+ }
+
+ lastSignal = 0;
+
+ memset(&lastTimer.action, 0, sizeof(struct sigaction));
+ memset(&lastTimer.value, 0, sizeof(struct itimerval));
+
+ lastTimer.start = nullTimestamp();
+ lastTimer.next = nullTimestamp();
+}
+
+int CheckAbort()
+{
+ if (lastSignal != 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Aborting the procedure due to signal '"
+ << lastSignal << "', '" << DumpSignal(lastSignal)
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Info" << ": Aborting the procedure due to signal '"
+ << lastSignal << "'.\n";
+
+ lastSignal = 0;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+void HandleAbort()
+{
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ *logofs << flush;
+
+ handleTerminatingInLoop();
+
+ if (lastSignal == SIGHUP)
+ {
+ lastSignal = 0;
+ }
+
+ //
+ // The current default is to just quit the program.
+ // Code has not been updated to deal with the new
+ // NX transport loop.
+ //
+
+ if (control -> EnableCoreDumpOnAbort == 1)
+ {
+ if (agent != NULL)
+ {
+ cerr << "Session" << ": Terminating session at '"
+ << strTimestamp() << "'.\n";
+ }
+
+ cerr << "Error" << ": Generating a core file to help "
+ << "the investigations.\n";
+
+ cerr << "Session" << ": Session terminated at '"
+ << strTimestamp() << "'.\n";
+
+ cerr << flush;
+
+ signal(SIGABRT, SIG_DFL);
+
+ raise(SIGABRT);
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Showing the proxy abort dialog.\n"
+ << logofs_flush;
+ #endif
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ //
+ // Close the socket before showing the alert.
+ // It seems that the closure of the socket can
+ // sometimes take several seconds, even after
+ // the connection is broken.
+ //
+
+ CleanupSockets();
+
+ if (lastKill == 0)
+ {
+ HandleAlert(ABORT_PROXY_CONNECTION_ALERT, 1);
+ }
+ else
+ {
+ HandleAlert(ABORT_PROXY_SHUTDOWN_ALERT, 1);
+ }
+
+ handleAlertInLoop();
+ }
+
+ HandleCleanup();
+}
+
+void HandleAlert(int code, int local)
+{
+ if (lastAlert.code == 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Requesting an alert dialog with code "
+ << code << " and local " << local << ".\n"
+ << logofs_flush;
+ #endif
+
+ lastAlert.code = code;
+ lastAlert.local = local;
+ }
+ #if defined(TEST) || defined(INFO)
+ else
+ {
+ *logofs << "Loop: WARNING! Alert dialog already requested "
+ << "with code " << lastAlert.code << ".\n"
+ << logofs_flush;
+ }
+ #endif
+
+ return;
+}
+
+void FlushCallback(int length)
+{
+ if (flushCallback != NULL)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Reporting a flush request at "
+ << strMsTimestamp() << " with " << length
+ << " bytes written.\n" << logofs_flush;
+ #endif
+
+ (*flushCallback)(flushParameter, length);
+ }
+ #if defined(TEST) || defined(INFO)
+ else if (control -> ProxyMode == proxy_client)
+ {
+ *logofs << "Loop: WARNING! Can't find a flush "
+ << "callback in process with pid '" << getpid()
+ << "'.\n" << logofs_flush;
+ }
+ #endif
+}
+
+void KeeperCallback()
+{
+ if (IsRunning(lastKeeper) == 0)
+ {
+ //
+ // Let the house-keeping process take care
+ // of the persistent image cache.
+ //
+
+ if (control -> ImageCacheEnableLoad == 1 ||
+ control -> ImageCacheEnableSave == 1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Starting the house-keeping process with "
+ << "image storage size " << control -> ImageCacheDiskLimit
+ << ".\n" << logofs_flush;
+ #endif
+
+ lastKeeper = NXTransKeeper(0, control -> ImageCacheDiskLimit,
+ control -> RootPath);
+
+ if (IsFailed(lastKeeper))
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Can't start the NX keeper process.\n"
+ << logofs_flush;
+ #endif
+
+ SetNotRunning(lastKeeper);
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Keeper started with pid '"
+ << lastKeeper << "'.\n" << logofs_flush;
+ }
+ #endif
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Nothing to do for the keeper process "
+ << "with image cache not enabled.\n"
+ << logofs_flush;
+ }
+ #endif
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Nothing to do with the keeper process "
+ << "already running.\n" << logofs_flush;
+ }
+ #endif
+}
+
+void InstallSignals()
+{
+ #ifdef TEST
+ *logofs << "Loop: Installing signals in process with pid '"
+ << getpid() << "'.\n" << logofs_flush;
+ #endif
+
+ for (int i = 0; i < 32; i++)
+ {
+ if (CheckSignal(i) == 1 &&
+ lastMasks.enabled[i] == 0)
+ {
+ InstallSignal(i, NX_SIGNAL_ENABLE);
+ }
+ }
+
+ lastMasks.installed = 1;
+}
+
+void RestoreSignals()
+{
+ #ifdef TEST
+ *logofs << "Loop: Restoring signals in process with pid '"
+ << getpid() << "'.\n" << logofs_flush;
+ #endif
+
+ if (lastMasks.installed == 1)
+ {
+ //
+ // Need to keep monitoring the children.
+ //
+
+ for (int i = 0; i < 32; i++)
+ {
+ if (lastMasks.enabled[i] == 1)
+ {
+ RestoreSignal(i);
+ }
+ }
+ }
+
+ lastMasks.installed = 0;
+
+ if (lastMasks.blocked == 1)
+ {
+ EnableSignals();
+ }
+}
+
+void DisableSignals()
+{
+ if (lastMasks.blocked == 0)
+ {
+ sigset_t newMask;
+
+ sigemptyset(&newMask);
+
+ //
+ // Block also the other signals that may be
+ // installed by the agent, that are those
+ // signals for which the function returns 2.
+ //
+
+ for (int i = 0; i < 32; i++)
+ {
+ if (CheckSignal(i) > 0)
+ {
+ #ifdef DUMP
+ *logofs << "Loop: Disabling signal " << i << " '"
+ << DumpSignal(i) << "' in process with pid '"
+ << getpid() << "'.\n" << logofs_flush;
+ #endif
+
+ sigaddset(&newMask, i);
+ }
+ }
+
+ sigprocmask(SIG_BLOCK, &newMask, &lastMasks.saved);
+
+ lastMasks.blocked++;
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: WARNING! Signals were already blocked in "
+ << "process with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ }
+ #endif
+}
+
+void EnableSignals()
+{
+ if (lastMasks.blocked == 1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Enabling signals in process with pid '"
+ << getpid() << "'.\n" << logofs_flush;
+ #endif
+
+ sigprocmask(SIG_SETMASK, &lastMasks.saved, NULL);
+
+ lastMasks.blocked = 0;
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Signals were not blocked in "
+ << "process with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Signals were not blocked in "
+ << "process with pid '" << getpid() << "'.\n";
+ }
+}
+
+void InstallSignal(int signal, int action)
+{
+ if (lastMasks.enabled[signal] == 1)
+ {
+ if (action == NX_SIGNAL_FORWARD)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Forwarding handler for signal " << signal
+ << " '" << DumpSignal(signal) << "' in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ lastMasks.forward[signal] = 1;
+
+ return;
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Reinstalling handler for signal " << signal
+ << " '" << DumpSignal(signal) << "' in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ }
+ #endif
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Installing handler for signal " << signal
+ << " '" << DumpSignal(signal) << "' in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ if (signal == SIGALRM && isTimestamp(lastTimer.start))
+ {
+ ResetTimer();
+ }
+
+ struct sigaction newAction;
+
+ newAction.sa_handler = HandleSignal;
+
+ //
+ // This field doesn't exist on most OSes except
+ // Linux. We keep setting the field to NULL to
+ // avoid side-effects in the case the field is
+ // a value return.
+ //
+
+ #if defined(__linux__)
+
+ newAction.sa_restorer = NULL;
+
+ #endif
+
+ sigemptyset(&(newAction.sa_mask));
+
+ if (signal == SIGCHLD)
+ {
+ newAction.sa_flags = SA_NOCLDSTOP;
+ }
+ else
+ {
+ newAction.sa_flags = 0;
+ }
+
+ sigaction(signal, &newAction, &lastMasks.action[signal]);
+
+ lastMasks.enabled[signal] = 1;
+
+ if (action == NX_SIGNAL_FORWARD)
+ {
+ lastMasks.forward[signal] = 1;
+ }
+}
+
+void RestoreSignal(int signal)
+{
+ if (lastMasks.enabled[signal] == 0)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Signal '" << DumpSignal(signal)
+ << " not installed in process with pid '"
+ << getpid() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Signal '" << DumpSignal(signal)
+ << " not installed in process with pid '"
+ << getpid() << "'.\n";
+
+ return;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Restoring handler for signal " << signal
+ << " '" << DumpSignal(signal) << "' in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ if (signal == SIGALRM && isTimestamp(lastTimer.start))
+ {
+ ResetTimer();
+ }
+
+ sigaction(signal, &lastMasks.action[signal], NULL);
+
+ lastMasks.enabled[signal] = 0;
+ lastMasks.forward[signal] = 0;
+}
+
+void HandleSignal(int signal)
+{
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ #if defined(UNSAFE) && (defined(TEST) || defined(INFO))
+
+ if (lastSignal != 0)
+ {
+ *logofs << "Loop: WARNING! Last signal is '" << lastSignal
+ << "', '" << DumpSignal(signal) << "' and not zero "
+ << "in process with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ }
+
+ *logofs << "Loop: Signal '" << signal << "', '"
+ << DumpSignal(signal) << "' received in process "
+ << "with pid '" << getpid() << "'.\n" << logofs_flush;
+
+ #endif
+
+ if (getpid() != lastProxy && handler != NULL)
+ {
+ #if defined(UNSAFE) && (defined(TEST) || defined(INFO))
+ *logofs << "Loop: Calling slave handler in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ if ((*handler)(signal) == 0)
+ {
+ return;
+ }
+ }
+
+ switch (signal)
+ {
+ case SIGUSR1:
+ {
+ if (proxy != NULL && lastSignal == 0)
+ {
+ lastSignal = SIGUSR1;
+ }
+
+ break;
+ }
+ case SIGUSR2:
+ {
+ if (proxy != NULL && lastSignal == 0)
+ {
+ lastSignal = SIGUSR2;
+ }
+
+ break;
+ }
+ case SIGPIPE:
+ {
+ //
+ // It can happen that SIGPIPE is delivered
+ // to the proxy even in the case some other
+ // descriptor is unexpectedly closed.
+ //
+ // if (agentFD[1] != -1)
+ // {
+ // cerr << "Info" << ": Received signal 'SIGPIPE'. "
+ // << "Closing agent conection.\n";
+ //
+ // shutdown(agentFD[1], SHUT_RDWR);
+ // }
+ //
+
+ break;
+ }
+ case SIGALRM:
+ {
+ //
+ // Nothing to do. Just wake up the
+ // process on blocking operations.
+ //
+
+ break;
+ }
+ case SIGCHLD:
+ {
+ //
+ // Check if any of our children has exited.
+ //
+
+ if (HandleChildren() != 0)
+ {
+ signal = 0;
+ }
+
+ //
+ // Don't save this signal or it will override
+ // any previous signal sent by child before
+ // exiting.
+ //
+
+ break;
+ }
+
+ #ifdef __CYGWIN32__
+
+ case 12:
+ {
+ //
+ // Nothing to do. This signal is what is delivered
+ // by the Cygwin library when trying use a shared
+ // memory function if the daemon is not running.
+ //
+
+ #ifdef TEST
+ *logofs << "Loop: WARNING! Received signal '12' in "
+ << "process with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+
+ *logofs << "Loop: WARNING! Please check that the "
+ << "cygserver daemon is running.\n"
+ << logofs_flush;
+ #endif
+
+ break;
+ }
+
+ #endif
+
+ default:
+ {
+ //
+ // Register the signal so we can handle it
+ // inside the main loop. We will probably
+ // dispose any resource and exit.
+ //
+
+ if (getpid() == lastProxy)
+ {
+ #if defined(UNSAFE) && defined(TEST)
+ *logofs << "Loop: Registering end of session request "
+ << "due to signal '" << signal << "', '"
+ << DumpSignal(signal) << "'.\n"
+ << logofs_flush;
+ #endif
+
+ lastSignal = signal;
+ }
+ else
+ {
+ //
+ // This is a child, so exit immediately.
+ //
+
+ HandleCleanup();
+ }
+ }
+ }
+
+ if (signal != 0 && lastMasks.forward[signal] == 1)
+ {
+ if (lastMasks.action[signal].sa_handler != NULL &&
+ lastMasks.action[signal].sa_handler != HandleSignal)
+ {
+ #if defined(UNSAFE) && defined(TEST)
+ *logofs << "Loop: Forwarding signal '" << signal << "', '"
+ << DumpSignal(signal) << "' to previous handler.\n"
+ << logofs_flush;
+ #endif
+
+ lastMasks.action[signal].sa_handler(signal);
+ }
+ #ifdef WARNING
+ else if (lastMasks.action[signal].sa_handler == NULL)
+ {
+ *logofs << "Loop: WARNING! Parent requested to forward "
+ << "signal '" << signal << "', '" << DumpSignal(signal)
+ << "' but didn't set a handler.\n" << logofs_flush;
+ }
+ #endif
+ }
+}
+
+int HandleChildren()
+{
+ //
+ // Try to waitpid() for each child because the
+ // call might have return ECHILD and so we may
+ // have lost any of the processes.
+ //
+
+ if (IsRunning(lastDialog) && HandleChild(lastDialog) == 1)
+ {
+ #if defined(UNSAFE) && defined(TEST)
+ *logofs << "Loop: Resetting pid of last dialog process "
+ << "in handler.\n" << logofs_flush;
+ #endif
+
+ SetNotRunning(lastDialog);
+
+ if (proxy != NULL)
+ {
+ proxy -> handleResetAlert();
+ }
+
+ return 1;
+ }
+
+ if (IsRunning(lastWatchdog) && HandleChild(lastWatchdog) == 1)
+ {
+ #if defined(UNSAFE) && defined(TEST)
+ *logofs << "Loop: Watchdog is gone. Setting the last "
+ << "signal to SIGHUP.\n" << logofs_flush;
+ #endif
+
+ lastSignal = SIGHUP;
+
+ #if defined(UNSAFE) && defined(TEST)
+ *logofs << "Loop: Resetting pid of last watchdog process "
+ << "in handler.\n" << logofs_flush;
+ #endif
+
+ SetNotRunning(lastWatchdog);
+
+ return 1;
+ }
+
+ //
+ // The house-keeping process exits after a
+ // number of iterations to keep the memory
+ // pollution low. It is restarted on demand
+ // by the lower layers, using the callback
+ // function.
+ //
+
+ if (IsRunning(lastKeeper) && HandleChild(lastKeeper) == 1)
+ {
+ #if defined(UNSAFE) && defined(TEST)
+ *logofs << "Loop: Resetting pid of last house-keeping "
+ << "process in handler.\n" << logofs_flush;
+ #endif
+
+ SetNotRunning(lastKeeper);
+
+ return 1;
+ }
+
+ //
+ // The pid will be checked by the code
+ // that registered the child.
+ //
+
+ if (IsRunning(lastChild))
+ {
+ #if defined(UNSAFE) && defined(TEST)
+ *logofs << "Loop: Resetting pid of last child process "
+ << "in handler.\n" << logofs_flush;
+ #endif
+
+ SetNotRunning(lastChild);
+
+ return 1;
+ }
+
+ //
+ // This can actually happen either because we
+ // reset the pid of the child process as soon
+ // as we kill it, or because of a child process
+ // of our parent.
+ //
+
+ #if defined(UNSAFE) && (defined(TEST) || defined(INFO))
+ *logofs << "Loop: Ignoring signal received for the "
+ << "unregistered child.\n" << logofs_flush;
+ #endif
+
+ return 0;
+}
+
+int HandleChild(int child)
+{
+ int pid;
+
+ int status = 0;
+ int options = WNOHANG | WUNTRACED;
+
+ while ((pid = waitpid(child, &status, options)) &&
+ pid == -1 && EGET() == EINTR);
+
+ return CheckChild(pid, status);
+}
+
+int WaitChild(int child, const char* label, int force)
+{
+ int pid;
+
+ int status = 0;
+ int options = WUNTRACED;
+
+ for (;;)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Waiting for the " << label
+ << " process '" << child << "' to die.\n"
+ << logofs_flush;
+ #endif
+
+ pid = waitpid(child, &status, options);
+
+ if (pid == -1 && EGET() == EINTR)
+ {
+ if (force == 0)
+ {
+ return 0;
+ }
+
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Ignoring signal while "
+ << "waiting for the " << label << " process '"
+ << child << "' to die.\n"
+ << logofs_flush;
+ #endif
+
+ continue;
+ }
+
+ break;
+ }
+
+ return (EGET() == ECHILD ? 1 : CheckChild(pid, status));
+}
+
+int CheckChild(int pid, int status)
+{
+ lastStatus = 0;
+
+ if (pid > 0)
+ {
+ if (WIFSTOPPED(status))
+ {
+ #if defined(UNSAFE) && defined(TEST)
+ *logofs << "Loop: Child process '" << pid << "' was stopped "
+ << "with signal " << (WSTOPSIG(status)) << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else
+ {
+ if (WIFEXITED(status))
+ {
+ #if defined(UNSAFE) && defined(TEST)
+ *logofs << "Loop: Child process '" << pid << "' exited "
+ << "with status '" << (WEXITSTATUS(status))
+ << "'.\n" << logofs_flush;
+ #endif
+
+ lastStatus = WEXITSTATUS(status);
+ }
+ else if (WIFSIGNALED(status))
+ {
+ if (CheckSignal(WTERMSIG(status)) != 1)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Child process '" << pid
+ << "' died because of signal " << (WTERMSIG(status))
+ << ", '" << DumpSignal(WTERMSIG(status)) << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Child process '" << pid
+ << "' died because of signal " << (WTERMSIG(status))
+ << ", '" << DumpSignal(WTERMSIG(status)) << "'.\n";
+ }
+ #if defined(UNSAFE) && defined(TEST)
+ else
+ {
+ *logofs << "Loop: Child process '" << pid
+ << "' died because of signal " << (WTERMSIG(status))
+ << ", '" << DumpSignal(WTERMSIG(status)) << "'.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ lastStatus = 1;
+ }
+
+ return 1;
+ }
+ }
+ else if (pid < 0)
+ {
+ if (EGET() != ECHILD)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to waitpid failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to waitpid failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+
+ HandleCleanup();
+ }
+
+ //
+ // This can happen when the waitpid() is
+ // blocking, as the SIGCHLD is received
+ // within the call.
+ //
+
+ #ifdef TEST
+ *logofs << "Loop: No more children processes running.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+ }
+
+ return 0;
+}
+
+void RegisterChild(int child)
+{
+ #if defined(TEST) || defined(INFO)
+
+ if (IsNotRunning(lastChild))
+ {
+ *logofs << "Loop: Registering child process '" << child
+ << "' in process with pid '" << getpid()
+ << "'.\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "Loop: WARNING! Overriding registered child '"
+ << lastChild << "' with new child '" << child
+ << "' in process with pid '" << getpid()
+ << "'.\n" << logofs_flush;
+ }
+
+ #endif
+
+ lastChild = child;
+}
+
+int CheckParent(const char *name, const char *type, int parent)
+{
+ if (parent != getppid() || parent == 1)
+ {
+ #ifdef WARNING
+ *logofs << name << ": WARNING! Parent process appears "
+ << "to be dead. Exiting " << type << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Parent process appears "
+ << "to be dead. Exiting " << type << ".\n";
+
+ return 0;
+ }
+
+ return 1;
+}
+
+void HandleTimer(int signal)
+{
+ if (signal == SIGALRM)
+ {
+ if (isTimestamp(lastTimer.start))
+ {
+ #if defined(UNSAFE) && defined(TEST)
+ *logofs << "Loop: Timer expired at " << strMsTimestamp()
+ << " in process with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ if (proxy != NULL)
+ {
+ proxy -> handleTimer();
+ }
+
+ ResetTimer();
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Inconsistent timer state "
+ << " in process with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Inconsistent timer state "
+ << " in process with pid '" << getpid() << "'.\n";
+ }
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Inconsistent signal '"
+ << signal << "', '" << DumpSignal(signal)
+ << "' received in process with pid '"
+ << getpid() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Inconsistent signal '"
+ << signal << "', '" << DumpSignal(signal)
+ << "' received in process with pid '"
+ << getpid() << "'.\n";
+ }
+}
+
+void SetTimer(int value)
+{
+ getNewTimestamp();
+
+ if (isTimestamp(lastTimer.start))
+ {
+ int diffTs = diffTimestamp(lastTimer.start, getTimestamp());
+
+ if (diffTs > lastTimer.next.tv_usec / 1000 * 2)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Timer missed to expire at "
+ << strMsTimestamp() << " in process with pid '"
+ << getpid() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Timer missed to expire at "
+ << strMsTimestamp() << " in process with pid '"
+ << getpid() << "'.\n";
+
+ HandleTimer(SIGALRM);
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Loop: Timer already running at "
+ << strMsTimestamp() << " in process with pid '"
+ << getpid() << "'.\n" << logofs_flush;
+ #endif
+
+ return;
+ }
+ }
+
+ //
+ // Save the former handler.
+ //
+
+ struct sigaction action;
+
+ action.sa_handler = HandleTimer;
+
+ #if defined(__linux__)
+
+ action.sa_restorer = NULL;
+
+ #endif
+
+ sigemptyset(&action.sa_mask);
+
+ action.sa_flags = 0;
+
+ sigaction(SIGALRM, &action, &lastTimer.action);
+
+ //
+ // Start the timer.
+ //
+
+ lastTimer.next = getTimestamp(value);
+
+ struct itimerval timer;
+
+ timer.it_interval = lastTimer.next;
+ timer.it_value = lastTimer.next;
+
+ #ifdef TEST
+ *logofs << "Loop: Timer set to " << lastTimer.next.tv_sec
+ << " S and " << lastTimer.next.tv_usec / 1000
+ << " Ms at " << strMsTimestamp() << " in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ if (setitimer(ITIMER_REAL, &timer, &lastTimer.value) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to setitimer failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to setitimer failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+
+ lastTimer.next = nullTimestamp();
+
+ return;
+ }
+
+ lastTimer.start = getTimestamp();
+}
+
+void ResetTimer()
+{
+ if (isTimestamp(lastTimer.start) == 0)
+ {
+ #if defined(UNSAFE) && defined(TEST)
+ *logofs << "Loop: Timer not running in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ return;
+ }
+
+ #if defined(UNSAFE) && defined(TEST)
+ *logofs << "Loop: Timer reset at " << strMsTimestamp()
+ << " in process with pid '" << getpid()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // Restore the old signal mask and timer.
+ //
+
+ if (setitimer(ITIMER_REAL, &lastTimer.value, NULL) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to setitimer failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to setitimer failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+ }
+
+ if (sigaction(SIGALRM, &lastTimer.action, NULL) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to sigaction failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to sigaction failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+ }
+
+ lastTimer.start = lastTimer.next = nullTimestamp();
+}
+
+//
+// Open TCP socket to listen for remote proxy and
+// block until remote connects. If successful close
+// the listening socket and return FD on which the
+// other party is connected.
+//
+
+int WaitForRemote(int portNum)
+{
+ char hostLabel[DEFAULT_STRING_LENGTH] = { 0 };
+
+ int retryAccept = -1;
+ int listenIPAddr = -1;
+
+ int proxyFD = -1;
+ int newFD = -1;
+
+ //
+ // Get IP address of host to be awaited.
+ //
+
+ int acceptIPAddr = 0;
+
+ if (*acceptHost != '\0')
+ {
+ acceptIPAddr = GetHostAddress(acceptHost);
+
+ if (acceptIPAddr == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Cannot accept connections from unknown host '"
+ << acceptHost << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot accept connections from unknown host '"
+ << acceptHost << "'.\n";
+
+ goto WaitForRemoteError;
+ }
+ }
+
+ proxyFD = socket(AF_INET, SOCK_STREAM, PF_UNSPEC);
+
+ if (proxyFD == -1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to socket failed for TCP socket. "
+ << "Error is " << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to socket failed for TCP socket. "
+ << "Error is " << EGET() << " '" << ESTR() << "'.\n";
+
+ goto WaitForRemoteError;
+ }
+ else if (SetReuseAddress(proxyFD) < 0)
+ {
+ goto WaitForRemoteError;
+ }
+
+ listenIPAddr = 0;
+
+ ParseListenOption(listenIPAddr);
+
+ sockaddr_in tcpAddr;
+
+ tcpAddr.sin_family = AF_INET;
+ tcpAddr.sin_port = htons(portNum);
+
+ //
+ // Quick patch to run on MacOS/X where inet_addr("127.0.0.1")
+ // alone seems to fail to return a valid interface. It probably
+ // just needs a htonl() or something like that.
+ //
+ // TODO: We have to give another look at inet_addr("127.0.0.1")
+ // on the Mac.
+ //
+
+ #ifdef __APPLE__
+
+ tcpAddr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ #else
+
+ tcpAddr.sin_addr.s_addr = listenIPAddr;
+
+ #endif
+
+ if (bind(proxyFD, (sockaddr *) &tcpAddr, sizeof(tcpAddr)) == -1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to bind failed for TCP port "
+ << portNum << ". Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to bind failed for TCP port "
+ << portNum << ". Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+
+ goto WaitForRemoteError;
+ }
+
+ if (listen(proxyFD, 4) == -1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to listen failed for TCP port "
+ << portNum << ". Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to listen failed for TCP port "
+ << portNum << ". Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+
+ goto WaitForRemoteError;
+ }
+
+ if (*acceptHost != '\0')
+ {
+ strcat(hostLabel, "'");
+ strcat(hostLabel, acceptHost);
+ strcat(hostLabel, "'");
+ }
+ else
+ {
+ strcpy(hostLabel, "any host");
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Waiting for connection from "
+ << hostLabel << " on port '" << portNum
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Info" << ": Waiting for connection from "
+ << hostLabel << " on port '" << portNum
+ << "'.\n";
+
+ //
+ // How many times to loop waiting for connections
+ // from the selected host? Each loop wait for at
+ // most 20 seconds so a default value of 3 gives
+ // a timeout of 1 minute.
+ //
+ // TODO: Handling of timeouts and retry attempts
+ // must be rewritten.
+ //
+
+ retryAccept = control -> OptionProxyRetryAccept;
+
+ for (;;)
+ {
+ fd_set readSet;
+
+ FD_ZERO(&readSet);
+ FD_SET(proxyFD, &readSet);
+
+ T_timestamp selectTs;
+
+ selectTs.tv_sec = 20;
+ selectTs.tv_usec = 0;
+
+ int result = select(proxyFD + 1, &readSet, NULL, NULL, &selectTs);
+
+ getNewTimestamp();
+
+ if (result == -1)
+ {
+ if (EGET() == EINTR)
+ {
+ if (CheckAbort() != 0)
+ {
+ goto WaitForRemoteError;
+ }
+
+ continue;
+ }
+
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to select failed. Error is "
+ << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to select failed. Error is "
+ << EGET() << " '" << ESTR() << "'.\n";
+
+ goto WaitForRemoteError;
+ }
+ else if (result > 0 && FD_ISSET(proxyFD, &readSet))
+ {
+ sockaddr_in newAddr;
+
+ size_t addrLen = sizeof(sockaddr_in);
+
+ newFD = accept(proxyFD, (sockaddr *) &newAddr, (socklen_t *) &addrLen);
+
+ if (newFD == -1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to accept failed. Error is "
+ << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to accept failed. Error is "
+ << EGET() << " '" << ESTR() << "'.\n";
+
+ goto WaitForRemoteError;
+ }
+
+ char *connectedHost = inet_ntoa(newAddr.sin_addr);
+
+ if (*acceptHost == '\0' || (int) newAddr.sin_addr.s_addr == acceptIPAddr)
+ {
+ #ifdef TEST
+
+ unsigned int connectedPort = ntohs(newAddr.sin_port);
+
+ *logofs << "Loop: Accepted connection from '" << connectedHost
+ << "' with port '" << connectedPort << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Info" << ": Accepted connection from '"
+ << connectedHost << "'.\n";
+
+ break;
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Loop: WARNING! Refusing connection from '" << connectedHost
+ << "' on port '" << portNum << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Refusing connection from '"
+ << connectedHost << "'.\n";
+ }
+
+ //
+ // Not the best way to elude a DOS attack...
+ //
+
+ sleep(5);
+
+ close(newFD);
+ }
+
+ if (--retryAccept == 0)
+ {
+ if (*acceptHost == '\0')
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Connection with remote host "
+ << "could not be established.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Connection with remote host "
+ << "could not be established.\n";
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Connection with remote host '"
+ << acceptHost << "' could not be established.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Connection with remote host '"
+ << acceptHost << "' could not be established.\n";
+ }
+
+ goto WaitForRemoteError;
+ }
+ else
+ {
+ handleCheckSessionInConnect();
+ }
+ }
+
+ close(proxyFD);
+
+ return newFD;
+
+WaitForRemoteError:
+
+ close(proxyFD);
+
+ HandleCleanup();
+}
+
+//
+// Connect to remote proxy. If successful
+// return FD of connection, else return -1.
+//
+
+int ConnectToRemote(const char *const hostName, int portNum)
+{
+ int proxyFD = -1;
+
+ int remoteIPAddr = GetHostAddress(hostName);
+
+ if (remoteIPAddr == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Unknown remote host '"
+ << hostName << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Unknown remote host '"
+ << hostName << "'.\n";
+
+ HandleCleanup();
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Connecting to remote host '"
+ << hostName << ":" << portNum << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Info" << ": Connecting to remote host '"
+ << hostName << ":" << portNum << "'.\n"
+ << logofs_flush;
+
+ //
+ // How many times we retry to connect to remote
+ // host in case of failure?
+ //
+
+ int retryConnect = control -> OptionProxyRetryConnect;
+
+ //
+ // Show an alert after 20 seconds and use the
+ // same timeout to interrupt the connect. The
+ // retry timeout is incremental, starting from
+ // 100 miliseconds up to 1 second.
+ //
+
+ int alertTimeout = 20000;
+ int connectTimeout = 20000;
+ int retryTimeout = 100;
+
+ T_timestamp lastRetry = getNewTimestamp();
+
+ sockaddr_in addr;
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(portNum);
+ addr.sin_addr.s_addr = remoteIPAddr;
+
+ for (;;)
+ {
+ proxyFD = socket(AF_INET, SOCK_STREAM, PF_UNSPEC);
+
+ if (proxyFD == -1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to socket failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to socket failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+
+ goto ConnectToRemoteError;
+ }
+ else if (SetReuseAddress(proxyFD) < 0)
+ {
+ goto ConnectToRemoteError;
+ }
+
+ //
+ // Ensure operation is timed out
+ // if there is a network problem.
+ //
+
+ #ifdef DEBUG
+ *logofs << "Loop: Timer set to " << connectTimeout / 1000
+ << " S " << "with retry set to " << retryConnect
+ << " in process with pid '" << getpid()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ SetTimer(connectTimeout);
+
+ int result = connect(proxyFD, (sockaddr *) &addr, sizeof(sockaddr_in));
+
+ int reason = EGET();
+
+ ResetTimer();
+
+ if (result < 0)
+ {
+ close(proxyFD);
+
+ if (CheckAbort() != 0)
+ {
+ goto ConnectToRemoteError;
+ }
+ else if (--retryConnect == 0)
+ {
+ ESET(reason);
+
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Connection to '" << hostName
+ << ":" << portNum << "' failed. Error is "
+ << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Connection to '" << hostName
+ << ":" << portNum << "' failed. Error is "
+ << EGET() << " '" << ESTR() << "'.\n";
+
+ goto ConnectToRemoteError;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Loop: Sleeping " << retryTimeout
+ << " Ms before retrying.\n"
+ << logofs_flush;
+ #endif
+
+ usleep(retryTimeout * 1000);
+
+ retryTimeout <<= 1;
+
+ if (retryTimeout > 1000 * 1000)
+ {
+ retryTimeout = 1000 * 1000;
+ }
+ }
+
+ //
+ // Check if it is time to show an alert dialog.
+ //
+
+ if (diffTimestamp(lastRetry, getNewTimestamp()) >=
+ (alertTimeout - control -> LatencyTimeout))
+ {
+ if (IsNotRunning(lastDialog))
+ {
+ handleCheckSessionInConnect();
+
+ //
+ // Wait for the dialog process to die
+ // unless a signal is received.
+ //
+
+ while (IsRunning(lastDialog))
+ {
+ WaitChild(lastDialog, "dialog", 0);
+
+ if (CheckAbort() != 0)
+ {
+ //
+ // The client ignores the TERM signal
+ // on Windows.
+ //
+
+ #ifdef __CYGWIN32__
+
+ KillProcess(lastDialog, "dialog", SIGKILL, 1);
+
+ #else
+
+ KillProcess(lastDialog, "dialog", SIGTERM, 1);
+
+ #endif
+
+ goto ConnectToRemoteError;
+ }
+ }
+
+ lastRetry = getTimestamp();
+ }
+ }
+ #ifdef TEST
+ {
+ *logofs << "Loop: Not showing the dialog with "
+ << (diffTimestamp(lastRetry, getTimestamp()) / 1000)
+ << " seconds elapsed.\n" << logofs_flush;
+ }
+ #endif
+
+ ESET(reason);
+
+ #ifdef TEST
+ *logofs << "Loop: Connection to '" << hostName
+ << ":" << portNum << "' failed with error '"
+ << ESTR() << "'. Retrying.\n"
+ << logofs_flush;
+ #endif
+ }
+ else
+ {
+ //
+ // Connection was successful.
+ //
+
+ break;
+ }
+ }
+
+ return proxyFD;
+
+ConnectToRemoteError:
+
+ if (proxyFD != -1)
+ {
+ close(proxyFD);
+ }
+
+ HandleCleanup();
+}
+
+//
+// Make a string of options for the remote
+// proxy and write it to the descriptor.
+// The string includes the local version.
+//
+
+int SendProxyOptions(int fd)
+{
+ char options[DEFAULT_REMOTE_OPTIONS_LENGTH];
+
+ //
+ // Send the "compatibility" version first, then our
+ // actual version. Old proxies will take the first
+ // value and ignore the second.
+ //
+
+ sprintf(options, "NXPROXY-1.5.0-%i.%i.%i", control -> LocalVersionMajor,
+ control -> LocalVersionMinor, control -> LocalVersionPatch);
+
+ //
+ // If you want to send options from proxy
+ // initiating the connection use something
+ // like this:
+ //
+ // if (WE_PROVIDE_CREDENTIALS)
+ // {
+ // sprintf(options + strlen(options), "%s=%s", option, value);
+ // }
+ //
+ // If you want to send options according to
+ // local proxy mode use something like this:
+ //
+ // if (control -> ProxyMode == proxy_client)
+ // {
+ // sprintf(options + strlen(options), "%s=%s", option, value);
+ // }
+ //
+
+ //
+ // Send the authorization cookie if any. We assume
+ // user can choose to not provide any auth cookie
+ // and allow any connection to be accepted.
+ //
+
+ if (WE_PROVIDE_CREDENTIALS && *authCookie != '\0')
+ {
+ sprintf(options + strlen(options), " cookie=%s,", authCookie);
+ }
+ else
+ {
+ sprintf(options + strlen(options), " ");
+ }
+
+ //
+ // Now link characteristics and compression
+ // options. Delta compression, as well as
+ // preferred pack method, are imposed by
+ // client proxy.
+ //
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ sprintf(options + strlen(options), "link=%s,pack=%s,cache=%s,",
+ linkSpeedName, packMethodName, cacheSizeName);
+
+ if (*bitrateLimitName != '\0')
+ {
+ sprintf(options + strlen(options), "limit=%s,",
+ bitrateLimitName);
+ }
+
+ //
+ // Let the user disable the render extension
+ // and let the X client proxy know if it can
+ // short-circuit the X replies. Also pass
+ // along the session type to ensure that the
+ // remote proxy gets the right value.
+ //
+
+ sprintf(options + strlen(options), "render=%d,taint=%d,",
+ (control -> HideRender == 0),
+ control -> TaintReplies);
+
+ if (*sessionType != '\0')
+ {
+ sprintf(options + strlen(options), "type=%s,", sessionType);
+ }
+ else
+ {
+ sprintf(options + strlen(options), "type=default,");
+ }
+
+ //
+ // Add the 'strict' option, if needed.
+ //
+
+ if (control -> isProtoStep7() == 1 &&
+ useStrict != -1)
+ {
+ sprintf(options + strlen(options), "strict=%d,", useStrict);
+ }
+
+ //
+ // Tell the remote the size of the shared
+ // memory segment.
+ //
+
+ if (control -> isProtoStep7() == 1 &&
+ *shsegSizeName != '\0')
+ {
+ sprintf(options + strlen(options), "shseg=%s,", shsegSizeName);
+ }
+
+ //
+ // Send image cache parameters.
+ //
+
+ sprintf(options + strlen(options), "images=%s,", imagesSizeName);
+
+ sprintf(options + strlen(options), "delta=%d,stream=%d,data=%d ",
+ control -> LocalDeltaCompression,
+ control -> LocalStreamCompressionLevel,
+ control -> LocalDataCompressionLevel);
+ }
+ else
+ {
+ //
+ // If no special compression level was selected,
+ // server side will use compression levels set
+ // by client.
+ //
+
+ if (control -> LocalStreamCompressionLevel < 0)
+ {
+ sprintf(options + strlen(options), "stream=default,");
+ }
+ else
+ {
+ sprintf(options + strlen(options), "stream=%d,",
+ control -> LocalStreamCompressionLevel);
+ }
+
+ if (control -> LocalDataCompressionLevel < 0)
+ {
+ sprintf(options + strlen(options), "data=default ");
+ }
+ else
+ {
+ sprintf(options + strlen(options), "data=%d ",
+ control -> LocalDataCompressionLevel);
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Sending remote options '"
+ << options << "'.\n" << logofs_flush;
+ #endif
+
+ return WriteLocalData(fd, options, strlen(options));
+}
+
+int ReadProxyVersion(int fd)
+{
+ #ifdef TEST
+ *logofs << "Loop: Going to read the remote proxy version "
+ << "from FD#" << fd << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Read until the first space in string.
+ // We expect the remote version number.
+ //
+
+ char options[DEFAULT_REMOTE_OPTIONS_LENGTH];
+
+ int result = ReadRemoteData(fd, options, sizeof(options), ' ');
+
+ if (result <= 0)
+ {
+ if (result < 0)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ HandleAlert(ABORT_PROXY_NEGOTIATION_ALERT, 1);
+ }
+
+ handleAlertInLoop();
+ }
+
+ return result;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Received remote version string '"
+ << options << "' from FD#" << fd << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (strncmp(options, "NXPROXY-", strlen("NXPROXY-")) != 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Parse error in remote options string '"
+ << options << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Parse error in remote options string '"
+ << options << "'.\n";
+
+ return -1;
+ }
+
+ //
+ // Try to determine if this is a pre-2.0.0
+ // version advertising itself as compatible
+ // with the 1.2.2.
+ //
+
+ int major = -1;
+ int minor = -1;
+ int patch = -1;
+
+ sscanf(options, "NXPROXY-%i.%i.%i-%i.%i.%i", &(control -> RemoteVersionMajor),
+ &(control -> RemoteVersionMinor), &(control -> RemoteVersionPatch),
+ &major, &minor, &patch);
+
+ if (control -> RemoteVersionMajor == 1 &&
+ control -> RemoteVersionMinor == 2 &&
+ control -> RemoteVersionPatch == 2 &&
+ major != -1 && minor != -1 && patch != -1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Read trailing remote version '" << major
+ << "." << minor << "." << patch << "'.\n"
+ << logofs_flush;
+ #endif
+
+ control -> CompatVersionMajor = major;
+ control -> CompatVersionMinor = minor;
+ control -> CompatVersionPatch = patch;
+
+ control -> RemoteVersionMajor = major;
+ control -> RemoteVersionMinor = minor;
+ control -> RemoteVersionPatch = patch;
+ }
+ else
+ {
+ //
+ // We read the remote version at the first
+ // round. If the second version is missing,
+ // we will retain the values read before.
+ //
+
+ sscanf(options, "NXPROXY-%i.%i.%i-%i.%i.%i", &(control -> CompatVersionMajor),
+ &(control -> CompatVersionMinor), &(control -> CompatVersionPatch),
+ &(control -> RemoteVersionMajor), &(control -> RemoteVersionMinor),
+ &(control -> RemoteVersionPatch));
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Identified remote version '" << control -> RemoteVersionMajor
+ << "." << control -> RemoteVersionMinor << "." << control -> RemoteVersionPatch
+ << "'.\n" << logofs_flush;
+
+ *logofs << "Loop: Remote compatibility version '" << control -> CompatVersionMajor
+ << "." << control -> CompatVersionMinor << "." << control -> CompatVersionPatch
+ << "'.\n" << logofs_flush;
+
+ *logofs << "Loop: Local version '" << control -> LocalVersionMajor
+ << "." << control -> LocalVersionMinor << "." << control -> LocalVersionPatch
+ << "'.\n" << logofs_flush;
+ #endif
+
+ if (SetVersion() < 0)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ HandleAlert(WRONG_PROXY_VERSION_ALERT, 1);
+ }
+
+ handleAlertInLoop();
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int ReadProxyOptions(int fd)
+{
+ #ifdef TEST
+ *logofs << "Loop: Going to read the remote proxy options "
+ << "from FD#" << fd << ".\n" << logofs_flush;
+ #endif
+
+ char options[DEFAULT_REMOTE_OPTIONS_LENGTH];
+
+ int result = ReadRemoteData(fd, options, sizeof(options), ' ');
+
+ if (result <= 0)
+ {
+ return result;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Received remote options string '"
+ << options << "' from FD#" << fd << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Get the remote options, delimited by a space character.
+ // Note that there will be a further initialization phase
+ // at the time proxies negotiate cache file to restore.
+ //
+
+ if (ParseRemoteOptions(options) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Couldn't negotiate a valid "
+ << "session with remote NX proxy.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Couldn't negotiate a valid "
+ << "session with remote NX proxy.\n";
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int SendProxyCaches(int fd)
+{
+ #ifdef TEST
+ *logofs << "Loop: Synchronizing local and remote caches.\n"
+ << logofs_flush;
+ #endif
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ //
+ // Prepare a list of caches matching this
+ // session type and send it to the remote.
+ //
+
+ #ifdef TEST
+ *logofs << "Loop: Going to send the list of local caches.\n"
+ << logofs_flush;
+ #endif
+
+ SetCaches();
+
+ int entries = DEFAULT_REMOTE_CACHE_ENTRIES;
+
+ const char prefix = 'C';
+
+ if (control -> LocalDeltaCompression == 0 ||
+ control -> PersistentCacheEnableLoad == 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Writing an empty list to FD#" << fd
+ << ".\n" << logofs_flush;
+ #endif
+
+ return WriteLocalData(fd, "cachelist=none ", strlen("cachelist=none "));
+ }
+
+ int count = 0;
+
+ #ifdef TEST
+ *logofs << "Loop: Looking for cache files in directory '"
+ << control -> PersistentCachePath << "'.\n" << logofs_flush;
+ #endif
+
+ DIR *cacheDir = opendir(control -> PersistentCachePath);
+
+ if (cacheDir != NULL)
+ {
+ dirent *dirEntry;
+
+ int prologue = 0;
+
+ while (((dirEntry = readdir(cacheDir)) != NULL) && (count < entries))
+ {
+ if (*dirEntry -> d_name == prefix &&
+ strlen(dirEntry -> d_name) == (MD5_LENGTH * 2 + 2))
+ {
+ if (prologue == 0)
+ {
+ WriteLocalData(fd, "cachelist=", strlen("cachelist="));
+
+ prologue = 1;
+ }
+ else
+ {
+ WriteLocalData(fd, ",", strlen(","));
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Writing entry '" << control -> PersistentCachePath
+ << "/" << dirEntry -> d_name << "' to FD#" << fd
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Write cache file name to the socket,
+ // including leading 'C-' or 'S-'.
+ //
+
+ WriteLocalData(fd, dirEntry -> d_name, MD5_LENGTH * 2 + 2);
+
+ count++;
+ }
+ }
+
+ closedir(cacheDir);
+ }
+
+ if (count == 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Writing an empty list to FD#" << fd
+ << ".\n" << logofs_flush;
+ #endif
+
+ return WriteLocalData(fd, "cachelist=none ", strlen("cachelist=none "));
+ }
+ else
+ {
+ return WriteLocalData(fd, " ", 1);
+ }
+ }
+ else
+ {
+ //
+ // Send back the selected cache name.
+ //
+
+ #ifdef TEST
+ *logofs << "Loop: Going to send the selected cache.\n"
+ << logofs_flush;
+ #endif
+
+ char buffer[DEFAULT_STRING_LENGTH];
+
+ if (control -> PersistentCacheName != NULL)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Name of selected cache file is '"
+ << control -> PersistentCacheName << "'.\n"
+ << logofs_flush;
+ #endif
+
+ sprintf(buffer, "cachefile=%s%s ",
+ *(control -> PersistentCacheName) == 'C' ? "S-" : "C-",
+ control -> PersistentCacheName + 2);
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Loop: No valid cache file was selected.\n"
+ << logofs_flush;
+ #endif
+
+ sprintf(buffer, "cachefile=none ");
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Sending string '" << buffer
+ << "' as selected cache file.\n"
+ << logofs_flush;
+ #endif
+
+ return WriteLocalData(fd, buffer, strlen(buffer));
+ }
+}
+
+int ReadProxyCaches(int fd)
+{
+ if (control -> ProxyMode == proxy_client)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Going to receive the selected proxy cache.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // We will read the name of cache plus the stop character.
+ //
+
+ char buffer[DEFAULT_STRING_LENGTH];
+
+ //
+ // Leave space for a trailing null.
+ //
+
+ int result = ReadRemoteData(fd, buffer, sizeof("cachefile=") + MD5_LENGTH * 2 + 3, ' ');
+
+ if (result <= 0)
+ {
+ return result;
+ }
+
+ char *cacheName = strstr(buffer, "cachefile=");
+
+ if (cacheName == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid cache file option '"
+ << buffer << "' provided by remote proxy.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid cache file option '"
+ << buffer << "' provided by remote proxy.\n";
+
+ HandleCleanup();
+ }
+
+ cacheName += strlen("cachefile=");
+
+ if (control -> PersistentCacheName != NULL)
+ {
+ delete [] control -> PersistentCacheName;
+ }
+
+ control -> PersistentCacheName = NULL;
+
+ if (strncasecmp(cacheName, "none", strlen("none")) == 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: No cache file selected by remote proxy.\n"
+ << logofs_flush;
+ #endif
+ }
+ else if (strlen(cacheName) != MD5_LENGTH * 2 + 3 ||
+ *(cacheName + MD5_LENGTH * 2 + 2) != ' ')
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid cache file name '"
+ << cacheName << "' provided by remote proxy.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid cache file name '"
+ << cacheName << "' provided by remote proxy.\n";
+
+ HandleCleanup();
+ }
+ else
+ {
+ //
+ // It is "C-" + 32 + "\0".
+ //
+
+ control -> PersistentCacheName = new char[MD5_LENGTH * 2 + 3];
+
+ *(cacheName + MD5_LENGTH * 2 + 2) = '\0';
+
+ strcpy(control -> PersistentCacheName, cacheName);
+
+ #ifdef TEST
+ *logofs << "Loop: Cache file '" << control -> PersistentCacheName
+ << "' selected by remote proxy.\n" << logofs_flush;
+ #endif
+ }
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Loop: Going to receive the list of remote caches.\n"
+ << logofs_flush;
+ #endif
+
+ SetCaches();
+
+ int size = ((MD5_LENGTH * 2 + 2) + strlen(",")) * DEFAULT_REMOTE_CACHE_ENTRIES +
+ strlen("cachelist=") + strlen(" ") + 1;
+
+ char *buffer = new char[size];
+
+ int result = ReadRemoteData(fd, buffer, size - 1, ' ');
+
+ if (result <= 0)
+ {
+ delete [] buffer;
+
+ return result;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Read list of caches from remote side as '"
+ << buffer << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // Prepare the buffer. What we want is a list
+ // like "cache1,cache2,cache2" terminated by
+ // null.
+ //
+
+ *(buffer + strlen(buffer) - 1) = '\0';
+
+ if (strncasecmp(buffer, "cachelist=", strlen("cachelist=")) != 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: Wrong format for list of cache files "
+ << "read from FD#" << fd << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Wrong format for list of cache files.\n";
+
+ delete [] buffer;
+
+ return -1;
+ }
+
+ control -> PersistentCacheName = GetLastCache(buffer, control -> PersistentCachePath);
+
+ //
+ // Get rid of list of caches.
+ //
+
+ delete [] buffer;
+ }
+
+ return 1;
+}
+
+int ReadForwarderVersion(int fd)
+{
+ #ifdef TEST
+ *logofs << "Loop: Going to negotiate the forwarder version.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Check if we actually expect the session cookie.
+ //
+
+ if (*authCookie == '\0')
+ {
+ #ifdef TEST
+ *logofs << "Loop: No authentication cookie required "
+ << "from FD#" << fd << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+ }
+
+ char options[DEFAULT_REMOTE_OPTIONS_LENGTH];
+
+ int result = ReadRemoteData(fd, options, sizeof(options), ' ');
+
+ if (result <= 0)
+ {
+ return result;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Received forwarder version string '" << options
+ << "' from FD#" << fd << ".\n" << logofs_flush;
+ #endif
+
+ if (strncmp(options, "NXSSH-", strlen("NXSSH-")) != 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Parse error in forwarder options string '"
+ << options << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Parse error in forwarder options string '"
+ << options << "'.\n";
+
+ return -1;
+ }
+
+ //
+ // Accept whatever forwarder version.
+ //
+
+ sscanf(options, "NXSSH-%i.%i.%i", &(control -> RemoteVersionMajor),
+ &(control -> RemoteVersionMinor), &(control -> RemoteVersionPatch));
+
+ #ifdef TEST
+ *logofs << "Loop: Read forwarder version '" << control -> RemoteVersionMajor
+ << "." << control -> RemoteVersionMinor << "." << control -> RemoteVersionPatch
+ << "'.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int ReadForwarderOptions(int fd)
+{
+ //
+ // Get the forwarder cookie.
+ //
+
+ if (*authCookie == '\0')
+ {
+ #ifdef TEST
+ *logofs << "Loop: No authentication cookie required "
+ << "from FD#" << fd << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+ }
+
+ char options[DEFAULT_REMOTE_OPTIONS_LENGTH];
+
+ int result = ReadRemoteData(fd, options, sizeof(options), ' ');
+
+ if (result <= 0)
+ {
+ return result;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Received forwarder options string '"
+ << options << "' from FD#" << fd << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (ParseForwarderOptions(options) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Couldn't negotiate a valid "
+ << "cookie with the NX forwarder.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Couldn't negotiate a valid "
+ << "cookie with the NX forwarder.\n";
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int ReadRemoteData(int fd, char *buffer, int size, char stop)
+{
+ #ifdef TEST
+ *logofs << "Loop: Going to read remote data from FD#"
+ << fd << ".\n" << logofs_flush;
+ #endif
+
+ if (size >= MAXIMUM_REMOTE_OPTIONS_LENGTH)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Maximum remote options buffer "
+ << "limit exceeded.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Maximum remote options buffer "
+ << "limit exceeded.\n";
+
+ HandleCleanup();
+ }
+
+ while (remotePosition < (size - 1))
+ {
+ int result = read(fd, remoteData + remotePosition, 1);
+
+ getNewTimestamp();
+
+ if (result <= 0)
+ {
+ if (result == -1)
+ {
+ if (EGET() == EAGAIN)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Reading data from FD#" << fd
+ << " would block.\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else if (EGET() == EINTR)
+ {
+ if (CheckAbort() != 0)
+ {
+ return -1;
+ }
+
+ continue;
+ }
+ }
+
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! The remote NX proxy closed "
+ << "the connection.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": The remote NX proxy closed "
+ << "the connection.\n";
+
+ return -1;
+ }
+ else if (*(remoteData + remotePosition) == stop)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Read stop character from FD#"
+ << fd << ".\n" << logofs_flush;
+ #endif
+
+ remotePosition++;
+
+ //
+ // Copy the fake terminating null
+ // in the buffer.
+ //
+
+ *(remoteData + remotePosition) = '\0';
+
+ memcpy(buffer, remoteData, remotePosition + 1);
+
+ #ifdef TEST
+ *logofs << "Loop: Remote string '" << remoteData
+ << "' read from FD#" << fd << ".\n"
+ << logofs_flush;
+ #endif
+
+ int t = remotePosition;
+
+ remotePosition = 0;
+
+ return t;
+ }
+ else
+ {
+ //
+ // Make sure string received
+ // from far end is printable.
+ //
+
+ if (isgraph(*(remoteData + remotePosition)) == 0)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Non printable character decimal '"
+ << (unsigned int) *(remoteData + remotePosition)
+ << "' received in remote data from FD#"
+ << fd << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Non printable character decimal '"
+ << (unsigned int) *(remoteData + remotePosition)
+ << "' received in remote data from FD#"
+ << fd << ".\n" << logofs_flush;
+
+ *(remoteData + remotePosition) = ' ';
+ }
+
+ #ifdef DEBUG
+ *logofs << "Loop: Read a further character "
+ << "from FD#" << fd << ".\n"
+ << logofs_flush;
+ #endif
+
+ remotePosition++;
+ }
+ }
+
+ *(remoteData + remotePosition) = '\0';
+
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Stop character missing "
+ << "from FD#" << fd << " after " << remotePosition
+ << " characters read in string '" << remoteData
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Stop character missing "
+ << "from FD#" << fd << " after " << remotePosition
+ << " characters read in string '" << remoteData
+ << "'.\n";
+
+ memcpy(buffer, remoteData, remotePosition);
+
+ remotePosition = 0;
+
+ return -1;
+}
+
+int WriteLocalData(int fd, const char *buffer, int size)
+{
+ int position = 0;
+
+ while (position < size)
+ {
+ int result = write(fd, buffer + position, size - position);
+
+ getNewTimestamp();
+
+ if (result <= 0)
+ {
+ if (result < 0 && EGET() == EINTR)
+ {
+ continue;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Error writing data to FD#"
+ << fd << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ position += result;
+ }
+
+ return position;
+}
+
+//
+// Parse the string passed by calling process in
+// the environment. This is not necessarily the
+// content of DISPLAY variable, but can be the
+// parameters passed when creating the process
+// or thread.
+//
+
+int ParseEnvironmentOptions(const char *env, int force)
+{
+ //
+ // Be sure log file is valid.
+ //
+
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ //
+ // Be sure we have a parameters repository
+ // and a context to jump into because this
+ // can be called before creating the proxy.
+ //
+
+ if (control == NULL)
+ {
+ control = new Control();
+ }
+
+ if (setjmp(context) == 1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Out of the long jump while parsing "
+ << "the environment options.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ if (force == 0 && parsedOptions == 1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Skipping a further parse of environment "
+ << "options string '" << (env != NULL ? env : "")
+ << "'.\n" << logofs_flush;
+ #endif
+
+ return 1;
+ }
+
+ if (env == NULL || *env == '\0')
+ {
+ #ifdef TEST
+ *logofs << "Loop: Nothing to do with empty environment "
+ << "options string '" << (env != NULL ? env : "")
+ << "'.\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Going to parse the environment options "
+ << "string '" << env << "'.\n"
+ << logofs_flush;
+ #endif
+
+ parsedOptions = 1;
+
+ //
+ // Copy the string passed as parameter
+ // because we need to modify it.
+ //
+
+ char opts[DEFAULT_DISPLAY_OPTIONS_LENGTH];
+
+ #ifdef VALGRIND
+
+ memset(opts, '\0', DEFAULT_DISPLAY_OPTIONS_LENGTH);
+
+ #endif
+
+ if (strlen(env) >= DEFAULT_DISPLAY_OPTIONS_LENGTH)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Environment options string '" << env
+ << "' exceeds length of " << DEFAULT_DISPLAY_OPTIONS_LENGTH
+ << " characters.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Environment options string '" << env
+ << "' exceeds length of " << DEFAULT_DISPLAY_OPTIONS_LENGTH
+ << " characters.\n";
+
+ return -1;
+ }
+
+ strcpy(opts, env);
+
+ char *nextOpts = opts;
+
+ //
+ // Ensure that DISPLAY environment variable
+ // (roughly) follows the X convention for
+ // transport notation.
+ //
+
+ if (strncasecmp(opts, "nx/nx,:", 7) == 0 ||
+ strncasecmp(opts, "nx,:", 4) == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Parse error in options string '"
+ << opts << "' at 'nx,:'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Parse error in options string '"
+ << opts << "' at 'nx,:'.\n";
+
+ return -1;
+ }
+ else if (strncasecmp(opts, "nx/nx,", 6) == 0)
+ {
+ nextOpts += 6;
+ }
+ else if (strncasecmp(opts, "nx,", 3) == 0)
+ {
+ nextOpts += 3;
+ }
+ else if (strncasecmp(opts, "nx:", 3) == 0)
+ {
+ nextOpts += 3;
+ }
+ else if (force == 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Ignoring host X server display string '"
+ << opts << "'.\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ //
+ // Save here the name of the options file and
+ // parse it after all the other options.
+ //
+
+ char fileOptions[DEFAULT_STRING_LENGTH] = { 0 };
+
+ //
+ // The options string is intended to be a series
+ // of name/value tuples in the form name=value
+ // separated by the ',' character ended by a ':'
+ // followed by remote NX proxy port.
+ //
+
+ char *name;
+ char *value;
+
+ value = rindex(nextOpts, ':');
+
+ if (value != NULL)
+ {
+ char *check = value + 1;
+
+ if (*check == '\0' || isdigit(*check) == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't identify NX port in string '"
+ << value << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't identify NX port in string '"
+ << value << "'.\n";
+
+ return -1;
+ }
+
+ proxyPort = atoi(check);
+
+ //
+ // Get rid of the port specification.
+ //
+
+ *value = '\0';
+ }
+ else if (proxyPort == DEFAULT_NX_PROXY_PORT && force == 0)
+ {
+ //
+ // Complain only if user didn't specify
+ // the port on the command line.
+ //
+
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't identify NX port in string '"
+ << opts << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't identify NX port in string '"
+ << opts << "'.\n";
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Parsing options string '"
+ << nextOpts << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // Now all the other optional parameters.
+ //
+
+ name = strtok(nextOpts, "=");
+
+ while (name)
+ {
+ value = strtok(NULL, ",");
+
+ if (CheckArg("environment", name, value) < 0)
+ {
+ return -1;
+ }
+
+ if (strcasecmp(name, "options") == 0)
+ {
+ strncpy(fileOptions, value, DEFAULT_STRING_LENGTH - 1);
+ }
+ else if (strcasecmp(name, "display") == 0)
+ {
+ strncpy(displayHost, value, DEFAULT_STRING_LENGTH - 1);
+ }
+ else if (strcasecmp(name, "link") == 0)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ PrintOptionIgnored("local", name, value);
+ }
+ else if (ParseLinkOption(value) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't identify 'link' option in string '"
+ << value << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't identify 'link' option in string '"
+ << value << "'.\n";
+
+ return -1;
+ }
+ }
+ else if (strcasecmp(name, "limit") == 0)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ PrintOptionIgnored("local", name, value);
+ }
+ else if (ParseBitrateOption(value) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't identify option 'limit' in string '"
+ << value << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't identify option 'limit' in string '"
+ << value << "'.\n";
+
+ return -1;
+ }
+ }
+ else if (strcasecmp(name, "type") == 0)
+ {
+ //
+ // Type of session, for example "desktop",
+ // "application", "windows", etc.
+ //
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ PrintOptionIgnored("local", name, value);
+ }
+ else
+ {
+ if (strcasecmp(value, "default") == 0)
+ {
+ *sessionType = '\0';
+ }
+ else
+ {
+ strncpy(sessionType, value, DEFAULT_STRING_LENGTH - 1);
+ }
+ }
+ }
+ else if (strcasecmp(name, "listen") == 0)
+ {
+ if (*connectHost != '\0')
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't handle 'listen' and 'connect' parameters "
+ << "at the same time.\n" << logofs_flush;
+
+ *logofs << "Loop: PANIC! Refusing 'listen' parameter with 'connect' being '"
+ << connectHost << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't handle 'listen' and 'connect' parameters "
+ << "at the same time.\n";
+
+ cerr << "Error" << ": Refusing 'listen' parameter with 'connect' being '"
+ << connectHost << "'.\n";
+
+ return -1;
+ }
+
+ listenPort = ValidateArg("local", name, value);
+ }
+ else if (strcasecmp(name, "accept") == 0)
+ {
+ if (*connectHost != '\0')
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't handle 'accept' and 'connect' parameters "
+ << "at the same time.\n" << logofs_flush;
+
+ *logofs << "Loop: PANIC! Refusing 'accept' parameter with 'connect' being '"
+ << connectHost << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't handle 'accept' and 'connect' parameters "
+ << "at the same time.\n";
+
+ cerr << "Error" << ": Refusing 'accept' parameter with 'connect' being '"
+ << connectHost << "'.\n";
+
+ return -1;
+ }
+
+ strncpy(acceptHost, value, DEFAULT_STRING_LENGTH - 1);
+ }
+ else if (strcasecmp(name, "connect") == 0)
+ {
+ if (*acceptHost != '\0')
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't handle 'connect' and 'accept' parameters "
+ << "at the same time.\n" << logofs_flush;
+
+ *logofs << "Loop: PANIC! Refusing 'connect' parameter with 'accept' being '"
+ << acceptHost << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't handle 'connect' and 'accept' parameters "
+ << "at the same time.\n";
+
+ cerr << "Error" << ": Refusing 'connect' parameter with 'accept' being '"
+ << acceptHost << "'.\n";
+
+ return -1;
+ }
+
+ strncpy(connectHost, value, DEFAULT_STRING_LENGTH - 1);
+ }
+ else if (strcasecmp(name, "port") == 0)
+ {
+ connectPort = ValidateArg("local", name, value);
+ }
+ else if (strcasecmp(name, "retry") == 0)
+ {
+ control -> OptionProxyRetryConnect = ValidateArg("local", name, value);
+ control -> OptionServerRetryConnect = ValidateArg("local", name, value);
+ }
+ else if (strcasecmp(name, "session") == 0)
+ {
+ strncpy(sessionFileName, value, DEFAULT_STRING_LENGTH - 1);
+ }
+ else if (strcasecmp(name, "errors") == 0)
+ {
+ //
+ // The old name of the parameter was 'log'
+ // but the default name for the file is
+ // 'errors' so it is more logical to use
+ // the same name.
+ //
+
+ strncpy(errorsFileName, value, DEFAULT_STRING_LENGTH - 1);
+ }
+ else if (strcasecmp(name, "root") == 0)
+ {
+ strncpy(rootDir, value, DEFAULT_STRING_LENGTH - 1);
+ }
+ else if (strcasecmp(name, "id") == 0)
+ {
+ strncpy(sessionId, value, DEFAULT_STRING_LENGTH - 1);
+ }
+ else if (strcasecmp(name, "stats") == 0)
+ {
+ control -> EnableStatistics = 1;
+
+ strncpy(statsFileName, value, DEFAULT_STRING_LENGTH - 1);
+ }
+ else if (strcasecmp(name, "cookie") == 0)
+ {
+ LowercaseArg("local", name, value);
+
+ strncpy(authCookie, value, DEFAULT_STRING_LENGTH - 1);
+ }
+ else if (strcasecmp(name, "nodelay") == 0)
+ {
+ useNoDelay = ValidateArg("local", name, value);
+ }
+ else if (strcasecmp(name, "policy") == 0)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ PrintOptionIgnored("local", name, value);
+ }
+ else
+ {
+ usePolicy = ValidateArg("local", name, value);
+ }
+ }
+ else if (strcasecmp(name, "render") == 0)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ PrintOptionIgnored("local", name, value);
+ }
+ else
+ {
+ useRender = ValidateArg("local", name, value);
+ }
+ }
+ else if (strcasecmp(name, "taint") == 0)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ PrintOptionIgnored("local", name, value);
+ }
+ else
+ {
+ useTaint = ValidateArg("local", name, value);
+ }
+ }
+ else if (strcasecmp(name, "delta") == 0)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ PrintOptionIgnored("local", name, value);
+ }
+ else
+ {
+ control -> LocalDeltaCompression = ValidateArg("local", name, value);
+ }
+ }
+ else if (strcasecmp(name, "data") == 0)
+ {
+ control -> LocalDataCompressionLevel = ValidateArg("local", name, value);
+
+ if (control -> LocalDataCompressionLevel == 0)
+ {
+ control -> LocalDataCompression = 0;
+ }
+ else
+ {
+ control -> LocalDataCompression = 1;
+ }
+ }
+ else if (strcasecmp(name, "stream") == 0)
+ {
+ control -> LocalStreamCompressionLevel = ValidateArg("local", name, value);
+
+ if (control -> LocalStreamCompressionLevel == 0)
+ {
+ control -> LocalStreamCompression = 0;
+ }
+ else
+ {
+ control -> LocalStreamCompression = 1;
+ }
+ }
+ else if (strcasecmp(name, "memory") == 0)
+ {
+ control -> LocalMemoryLevel = ValidateArg("local", name, value);
+ }
+ else if (strcasecmp(name, "cache") == 0)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ PrintOptionIgnored("local", name, value);
+ }
+ else if (ParseCacheOption(value) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't identify cache size for string '"
+ << value << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't identify cache size for string '"
+ << value << "'.\n";
+
+ return -1;
+ }
+ }
+ else if (strcasecmp(name, "images") == 0)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ PrintOptionIgnored("local", name, value);
+ }
+ else if (ParseImagesOption(value) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't identify images cache size for string '"
+ << value << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't identify images cache size for string '"
+ << value << "'.\n";
+
+ return -1;
+ }
+ }
+ else if (strcasecmp(name, "shseg") == 0)
+ {
+ //
+ // The 'shmem' option is used by the agent, together
+ // with 'shpix' literal. We make the 'shseg' option
+ // specific to the proxy and use it to determine the
+ // size of the shared memory segment, or otherwise 0,
+ // if the use of the shared memory extension should
+ // not be enabled on the real X server.
+ //
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ PrintOptionIgnored("local", name, value);
+ }
+ else if (ParseShmemOption(value) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't identify size of shared memory "
+ << "segment in string '" << value << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't identify size of shared memory "
+ << "segment in string '" << value << "'.\n";
+
+ return -1;
+ }
+ }
+ else if (strcasecmp(name, "load") == 0)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ PrintOptionIgnored("local", name, value);
+ }
+ else
+ {
+ control -> PersistentCacheEnableLoad = ValidateArg("local", name, value);
+
+ if (control -> PersistentCacheEnableLoad > 0)
+ {
+ control -> PersistentCacheEnableLoad = 1;
+ }
+ else
+ {
+ if (control -> PersistentCacheName != NULL)
+ {
+ delete [] control -> PersistentCacheName;
+ }
+
+ control -> PersistentCacheName = NULL;
+
+ control -> PersistentCacheEnableLoad = 0;
+ }
+ }
+ }
+ else if (strcasecmp(name, "save") == 0)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ PrintOptionIgnored("local", name, value);
+ }
+ else
+ {
+ control -> PersistentCacheEnableSave = ValidateArg("local", name, value);
+
+ if (control -> PersistentCacheEnableSave > 0)
+ {
+ control -> PersistentCacheEnableSave = 1;
+ }
+ else
+ {
+ if (control -> PersistentCacheName != NULL)
+ {
+ delete [] control -> PersistentCacheName;
+ }
+
+ control -> PersistentCacheName = NULL;
+
+ control -> PersistentCacheEnableSave = 0;
+ }
+ }
+ }
+ else if (strcasecmp(name, "cups") == 0)
+ {
+ cupsPort = ValidateArg("local", name, value);
+ }
+ else if (strcasecmp(name, "sync") == 0)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! No 'sync' channel in current version. "
+ << "Assuming 'cups' channel.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": No 'sync' channel in current version. "
+ << "Assuming 'cups' channel.\n";
+
+ cupsPort = ValidateArg("local", name, value);
+ }
+ else if (strcasecmp(name, "keybd") == 0 ||
+ strcasecmp(name, "aux") == 0)
+ {
+ auxPort = ValidateArg("local", name, value);
+ }
+ else if (strcasecmp(name, "samba") == 0 ||
+ strcasecmp(name, "smb") == 0)
+ {
+ smbPort = ValidateArg("local", name, value);
+ }
+ else if (strcasecmp(name, "media") == 0)
+ {
+ mediaPort = ValidateArg("local", name, value);
+ }
+ else if (strcasecmp(name, "http") == 0)
+ {
+ httpPort = ValidateArg("local", name, value);
+ }
+ else if (strcasecmp(name, "font") == 0)
+ {
+ strncpy(fontPort, value, DEFAULT_STRING_LENGTH - 1);
+ }
+ else if (strcasecmp(name, "slave") == 0)
+ {
+ slavePort = ValidateArg("local", name, value);
+ }
+ else if (strcasecmp(name, "mask") == 0)
+ {
+ control -> ChannelMask = ValidateArg("local", name, value);
+ }
+ else if (strcasecmp(name, "timeout") == 0)
+ {
+ int timeout = ValidateArg("local", name, value);
+
+ if (timeout == 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Disabling timeout on broken "
+ << "proxy connection.\n" << logofs_flush;
+ #endif
+
+ control -> ProxyTimeout = 0;
+ }
+ else
+ {
+ control -> ProxyTimeout = timeout * 1000;
+ }
+ }
+ else if (strcasecmp(name, "cleanup") == 0)
+ {
+ int cleanup = ValidateArg("local", name, value);
+
+ if (cleanup == 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Disabling grace timeout on "
+ << "proxy shutdown.\n" << logofs_flush;
+ #endif
+
+ control -> CleanupTimeout = 0;
+ }
+ else
+ {
+ control -> CleanupTimeout = cleanup * 1000;
+ }
+ }
+ else if (strcasecmp(name, "pack") == 0)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ PrintOptionIgnored("local", name, value);
+ }
+ else if (ParsePackOption(value) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't identify pack method for string '"
+ << value << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't identify pack method for string '"
+ << value << "'.\n";
+
+ return -1;
+ }
+ }
+ else if (strcasecmp(name, "core") == 0)
+ {
+ control -> EnableCoreDumpOnAbort = ValidateArg("local", name, value);
+ }
+ else if (strcasecmp(name, "kill") == 0)
+ {
+ if (control -> KillDaemonOnShutdownNumber <
+ control -> KillDaemonOnShutdownLimit)
+ {
+ #ifdef TEST
+ *logofs << "Loop: WARNING! Adding process with pid '"
+ << ValidateArg("local", name, value) << " to the "
+ << "daemons to kill at shutdown.\n"
+ << logofs_flush;
+ #endif
+
+ control -> KillDaemonOnShutdown[control ->
+ KillDaemonOnShutdownNumber] =
+ ValidateArg("local", name, value);
+
+ control -> KillDaemonOnShutdownNumber++;
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Number of daemons to kill "
+ << "at shutdown exceeded.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Number of daemons to kill "
+ << "at shutdown exceeded.\n";
+ }
+ }
+ else if (strcasecmp(name, "strict") == 0)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ PrintOptionIgnored("local", name, value);
+ }
+ else
+ {
+ useStrict = ValidateArg("local", name, value);
+ }
+ }
+ else if (strcasecmp(name, "encryption") == 0)
+ {
+ useEncryption = ValidateArg("local", name, value);
+ }
+ else if (strcasecmp(name, "product") == 0)
+ {
+ strncpy(productName, value, DEFAULT_STRING_LENGTH - 1);
+ }
+ else if (strcasecmp(name, "rootless") == 0 ||
+ strcasecmp(name, "geometry") == 0 ||
+ strcasecmp(name, "resize") == 0 ||
+ strcasecmp(name, "fullscreen") == 0 ||
+ strcasecmp(name, "keyboard") == 0 ||
+ strcasecmp(name, "clipboard") == 0 ||
+ strcasecmp(name, "streaming") == 0 ||
+ strcasecmp(name, "backingstore") == 0)
+ {
+ #ifdef DEBUG
+ *logofs << "Loop: Ignoring agent option '" << name
+ << "' with value '" << value << "'.\n"
+ << logofs_flush;
+ #endif
+ }
+ else if (strcasecmp(name, "composite") == 0 ||
+ strcasecmp(name, "shmem") == 0 ||
+ strcasecmp(name, "shpix") == 0 ||
+ strcasecmp(name, "kbtype") == 0 ||
+ strcasecmp(name, "client") == 0 ||
+ strcasecmp(name, "shadow") == 0 ||
+ strcasecmp(name, "shadowuid") == 0 ||
+ strcasecmp(name, "shadowmode") == 0 ||
+ strcasecmp(name, "clients") == 0)
+ {
+ #ifdef DEBUG
+ *logofs << "Loop: Ignoring agent option '" << name
+ << "' with value '" << value << "'.\n"
+ << logofs_flush;
+ #endif
+ }
+ else if (strcasecmp(name, "defer") == 0 ||
+ strcasecmp(name, "tile") == 0 ||
+ strcasecmp(name, "menu") == 0)
+ {
+ #ifdef DEBUG
+ *logofs << "Loop: Ignoring agent option '" << name
+ << "' with value '" << value << "'.\n"
+ << logofs_flush;
+ #endif
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Ignoring unknown option '"
+ << name << "' with value '" << value << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Ignoring unknown option '"
+ << name << "' with value '" << value << "'.\n";
+ }
+
+ name = strtok(NULL, "=");
+
+ } // End of while (name) ...
+
+ #ifdef TEST
+ *logofs << "Loop: Completed parsing of string '"
+ << env << "'.\n" << logofs_flush;
+ #endif
+
+ if (*fileOptions != '\0')
+ {
+ if (strcmp(fileOptions, optionsFileName) != 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Reading options from '" << fileOptions
+ << "'.\n" << logofs_flush;
+ #endif
+
+ if (ParseFileOptions(fileOptions) < 0)
+ {
+ return -1;
+ }
+ }
+ #ifdef WARNING
+ else
+ {
+ *logofs << "Loop: WARNING! Name of the options file "
+ << "specified multiple times. Not parsing "
+ << "again.\n" << logofs_flush;
+ }
+ #endif
+
+ if (*optionsFileName == '\0')
+ {
+ strncpy(optionsFileName, value, DEFAULT_STRING_LENGTH - 1);
+
+ #ifdef TEST
+ *logofs << "Loop: Assuming name of options file '"
+ << optionsFileName << "'.\n"
+ << logofs_flush;
+ #endif
+ }
+ }
+
+ //
+ // If port where proxy is acting as an X server
+ // was not specified assume the same port where
+ // proxy is listening for the remote peer.
+ //
+
+ if (xPort == DEFAULT_NX_X_PORT)
+ {
+ xPort = proxyPort;
+ }
+
+ return 1;
+}
+
+//
+// Parse the command line options passed by user when
+// running proxy in stand alone mode. Note that passing
+// parameters this way is strongly discouraged. These
+// command line switch can change (and they do often).
+// Please, use the form "option=value" instead and set
+// the DISPLAY environment variable.
+//
+
+int ParseCommandLineOptions(int argc, const char **argv)
+{
+ //
+ // Be sure log file is valid.
+ //
+
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ if (setjmp(context) == 1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Out of the long jump while parsing "
+ << "the command line options.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ //
+ // Be sure we have a parameters repository
+ //
+
+ if (control == NULL)
+ {
+ control = new Control();
+ }
+
+ if (parsedCommand == 1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Skipping a further parse of command line options.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Going to parse the command line options.\n"
+ << logofs_flush;
+ #endif
+
+ parsedCommand = 1;
+
+ //
+ // Print out arguments.
+ //
+
+ #ifdef TEST
+
+ *logofs << "Loop: Argc is " << argc << ".\n" << logofs_flush;
+
+ for (int argi = 0; argi < argc; argi++)
+ {
+ *logofs << "Loop: Argv[" << argi << "] is " << argv[argi]
+ << ".\n" << logofs_flush;
+ }
+
+ #endif
+
+ //
+ // Shall use getopt here.
+ //
+
+ for (int argi = 1; argi < argc; argi++)
+ {
+ const char *nextArg = argv[argi];
+
+ if (*nextArg == '-')
+ {
+ switch (*(nextArg + 1))
+ {
+ case 'h':
+ {
+ PrintUsageInfo(nextArg, 0);
+
+ return -1;
+ }
+ case 'C':
+ {
+ //
+ // Start proxy in CLIENT mode.
+ //
+
+ if (WE_SET_PROXY_MODE == 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Setting local proxy mode to proxy_client.\n"
+ << logofs_flush;
+ #endif
+
+ control -> ProxyMode = proxy_client;
+ }
+ else if (control -> ProxyMode != proxy_client)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't redefine local proxy to "
+ << "client mode.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't redefine local proxy to "
+ << "client mode.\n";
+
+ return -1;
+ }
+
+ break;
+ }
+ case 'S':
+ {
+ //
+ // Start proxy in SERVER mode.
+ //
+
+ if (WE_SET_PROXY_MODE == 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Setting local proxy mode to proxy_server.\n"
+ << logofs_flush;
+ #endif
+
+ control -> ProxyMode = proxy_server;
+ }
+ else if (control -> ProxyMode != proxy_server)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't redefine local proxy to "
+ << "server mode.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't redefine local proxy to "
+ << "server mode.\n";
+
+ return -1;
+ }
+
+ break;
+ }
+ case 'v':
+ {
+ PrintVersionInfo();
+
+ return -1;
+ }
+ default:
+ {
+ PrintUsageInfo(nextArg, 1);
+
+ //
+ // Function GetArg() is not used anymore.
+ // Add a dummy call to avoid the warning.
+ //
+
+ if (0)
+ {
+ GetArg(argi, argc, argv);
+ }
+
+ return -1;
+ }
+ }
+ }
+ else
+ {
+ if (nextArg)
+ {
+ //
+ // Try to parse the option as a remote host:port
+ // specification as in 'localhost:8'. Such a
+ // parameter can be specified at the end of the
+ // command line at the connecting side.
+ //
+
+ if (ParseHostOption(nextArg, connectHost, connectPort) > 0)
+ {
+ //
+ // Assume port is at a proxied display offset.
+ //
+
+ proxyPort = connectPort;
+
+ connectPort += DEFAULT_NX_PROXY_PORT_OFFSET;
+ }
+ else if (ParseEnvironmentOptions(nextArg, 1) < 0)
+ {
+ return -1;
+ }
+ }
+ }
+ }
+
+ return 1;
+}
+
+//
+// Set the variable to the values of host and
+// port where this proxy is going to hook to
+// an existing proxy.
+//
+
+int ParseBindOptions(char **host, int *port)
+{
+ if (*bindHost != '\0')
+ {
+ *host = bindHost;
+ *port = bindPort;
+
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+//
+// Read options from file and merge with environment.
+//
+
+int ParseFileOptions(const char *file)
+{
+ char *fileName;
+
+ if (*file != '/' && *file != '.')
+ {
+ char *filePath = GetSessionPath();
+
+ if (filePath == NULL)
+ {
+ cerr << "Error" << ": Cannot determine directory for NX option file.\n";
+
+ HandleCleanup();
+ }
+
+ fileName = new char[strlen(filePath) + strlen("/") +
+ strlen(file) + 1];
+
+ strcpy(fileName, filePath);
+
+ strcat(fileName, "/");
+ strcat(fileName, file);
+
+ delete [] filePath;
+ }
+ else
+ {
+ fileName = new char[strlen(file) + 1];
+
+ strcpy(fileName, file);
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Going to read options from file '"
+ << fileName << "'.\n" << logofs_flush;
+ #endif
+
+ FILE *filePtr = fopen(fileName, "r");
+
+ if (filePtr == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't open options file '" << fileName
+ << "'. Error is " << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't open options file '" << fileName
+ << "'. Error is " << EGET() << " '" << ESTR() << "'.\n";
+
+ delete [] fileName;
+
+ return -1;
+ }
+
+ char options[DEFAULT_DISPLAY_OPTIONS_LENGTH];
+
+ #ifdef VALGRIND
+
+ memset(options, '\0', DEFAULT_DISPLAY_OPTIONS_LENGTH);
+
+ #endif
+
+ if (fgets(options, DEFAULT_DISPLAY_OPTIONS_LENGTH, filePtr) == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't read options from file '" << fileName
+ << "'. Error is " << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't read options from file '" << fileName
+ << "'. Error is " << EGET() << " '" << ESTR() << "'.\n";
+
+ fclose(filePtr);
+
+ delete [] fileName;
+
+ return -1;
+ }
+
+ fclose(filePtr);
+
+ //
+ // Purge the newline and the other non-
+ // printable characters in the string.
+ //
+
+ char *next = options;
+
+ while (*next != '\0')
+ {
+ if (isprint(*next) == 0)
+ {
+ *next = '\0';
+ }
+
+ next++;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Read options '" << options << "' from file '"
+ << fileName << "'.\n" << logofs_flush;
+ #endif
+
+ if (ParseEnvironmentOptions(options, 1) < 0)
+ {
+ delete [] fileName;
+
+ return -1;
+ }
+
+ delete [] fileName;
+
+ return 1;
+}
+
+//
+// Parse the option string passed from the
+// remote proxy at startup.
+//
+
+int ParseRemoteOptions(char *opts)
+{
+ #ifdef TEST
+ *logofs << "Loop: Going to parse the remote options "
+ << "string '" << opts << "'.\n"
+ << logofs_flush;
+ #endif
+
+ char *name;
+ char *value;
+
+ //
+ // The options string is intended to be a series
+ // of name/value tuples in the form name=value
+ // separated by the ',' character.
+ //
+
+ int hasCookie = 0;
+ int hasLink = 0;
+ int hasPack = 0;
+ int hasCache = 0;
+ int hasImages = 0;
+ int hasDelta = 0;
+ int hasStream = 0;
+ int hasData = 0;
+ int hasLimit = 0;
+ int hasRender = 0;
+ int hasTaint = 0;
+ int hasType = 0;
+ int hasStrict = 0;
+ int hasShseg = 0;
+
+ //
+ // Get rid of the terminating space.
+ //
+
+ if (*(opts + strlen(opts) - 1) == ' ')
+ {
+ *(opts + strlen(opts) - 1) = '\0';
+ }
+
+ name = strtok(opts, "=");
+
+ while (name)
+ {
+ value = strtok(NULL, ",");
+
+ if (CheckArg("remote", name, value) < 0)
+ {
+ return -1;
+ }
+
+ if (strcasecmp(name, "cookie") == 0)
+ {
+ if (WE_PROVIDE_CREDENTIALS)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Ignoring remote option 'cookie' "
+ << "with value '" << value << "' when initiating "
+ << "connection.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Ignoring remote option 'cookie' "
+ << "with value '" << value << "' when initiating "
+ << "connection.\n";
+ }
+ else if (strncasecmp(authCookie, value, strlen(authCookie)) != 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Authentication cookie '" << value
+ << "' doesn't match '" << authCookie << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Authentication cookie '" << value
+ << "' doesn't match '" << authCookie << "'.\n";
+
+ return -1;
+ }
+
+ hasCookie = 1;
+ }
+ else if (strcasecmp(name, "link") == 0)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ PrintOptionIgnored("remote", name, value);
+ }
+ else
+ {
+ if (*linkSpeedName != '\0' && strcasecmp(linkSpeedName, value) != 0)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Overriding option 'link' "
+ << "with new value '" << value << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Overriding option 'link' "
+ << "with new value '" << value << "'.\n";
+ }
+
+ if (ParseLinkOption(value) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't identify remote 'link' "
+ << "option in string '" << value << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't identify remote 'link' "
+ << "option in string '" << value << "'.\n";
+
+ return -1;
+ }
+ }
+
+ hasLink = 1;
+ }
+ else if (strcasecmp(name, "pack") == 0)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ PrintOptionIgnored("remote", name, value);
+ }
+ else
+ {
+ if (*packMethodName != '\0' && strcasecmp(packMethodName, value) != 0)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Overriding option 'pack' "
+ << "with remote value '" << value << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Overriding option 'pack' "
+ << "with remote value '" << value << "'.\n";
+ }
+
+ if (ParsePackOption(value) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid pack option '"
+ << value << "' requested by remote.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid pack option '"
+ << value << "' requested by remote.\n";
+
+ return -1;
+ }
+ }
+
+ hasPack = 1;
+ }
+ else if (strcasecmp(name, "cache") == 0)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ PrintOptionIgnored("remote", name, value);
+ }
+ else
+ {
+ //
+ // Cache size is sent as a hint of how much memory
+ // the remote proxy is going to consume. A very low
+ // powered thin client could choose to refuse the
+ // connection.
+ //
+
+ if (ParseCacheOption(value) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't identify remote 'cache' "
+ << "option in string '" << value << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't identify remote 'cache' "
+ << "option in string '" << value << "'.\n";
+
+ return -1;
+ }
+ }
+
+ hasCache = 1;
+ }
+ else if (strcasecmp(name, "images") == 0)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ PrintOptionIgnored("remote", name, value);
+ }
+ else
+ {
+ //
+ // Images cache size is sent as a hint.
+ // There is no obbligation for the local
+ // proxy to use the persistent cache.
+ //
+
+ if (ParseImagesOption(value) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't identify remote 'images' "
+ << "option in string '" << value << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't identify remote 'images' "
+ << "option in string '" << value << "'.\n";
+
+ return -1;
+ }
+ }
+
+ hasImages = 1;
+ }
+ else if (strcasecmp(name, "limit") == 0)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ PrintOptionIgnored("remote", name, value);
+ }
+ else
+ {
+ if (*bitrateLimitName != '\0' &&
+ strcasecmp(bitrateLimitName, value) != 0)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Overriding option 'limit' "
+ << "with new value '" << value << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Overriding option 'limit' "
+ << "with new value '" << value << "'.\n";
+ }
+
+ if (ParseBitrateOption(value) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't identify 'limit' "
+ << "option in string '" << value << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't identify 'limit' "
+ << "option in string '" << value << "'.\n";
+
+ return -1;
+ }
+ }
+
+ hasLimit = 1;
+ }
+ else if (strcasecmp(name, "render") == 0)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ PrintOptionIgnored("remote", name, value);
+ }
+ else
+ {
+ useRender = ValidateArg("remote", name, value);
+ }
+
+ hasRender = 1;
+ }
+ else if (strcasecmp(name, "taint") == 0)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ PrintOptionIgnored("remote", name, value);
+ }
+ else
+ {
+ useTaint = ValidateArg("remote", name, value);
+ }
+
+ hasTaint = 1;
+ }
+ else if (strcasecmp(name, "type") == 0)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ PrintOptionIgnored("remote", name, value);
+ }
+ else
+ {
+ if (strcasecmp(value, "default") == 0)
+ {
+ *sessionType = '\0';
+ }
+ else
+ {
+ strncpy(sessionType, value, DEFAULT_STRING_LENGTH - 1);
+ }
+ }
+
+ hasType = 1;
+ }
+ else if (strcasecmp(name, "strict") == 0)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ PrintOptionIgnored("remote", name, value);
+ }
+ else
+ {
+ useStrict = ValidateArg("remote", name, value);
+ }
+
+ hasStrict = 1;
+ }
+ else if (strcasecmp(name, "shseg") == 0)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ PrintOptionIgnored("remote", name, value);
+ }
+ else if (ParseShmemOption(value) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't identify size of shared memory "
+ << "segment in string '" << value << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't identify size of shared memory "
+ << "segment in string '" << value << "'.\n";
+
+ return -1;
+ }
+
+ hasShseg = 1;
+ }
+ else if (strcasecmp(name, "delta") == 0)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ PrintOptionIgnored("remote", name, value);
+ }
+ else
+ {
+ control -> RemoteDeltaCompression = ValidateArg("remote", name, value);
+
+ //
+ // Follow for delta compression the
+ // same settings as the client proxy.
+ //
+
+ control -> LocalDeltaCompression = control -> RemoteDeltaCompression;
+ }
+
+ hasDelta = 1;
+ }
+ else if (strcasecmp(name, "stream") == 0)
+ {
+ //
+ // If remote side didn't choose its own
+ // stream compression level then assume
+ // local settings.
+ //
+
+ if (strcasecmp(value, "default") == 0)
+ {
+ //
+ // This applies only at client side.
+ //
+
+ control -> RemoteStreamCompression =
+ control -> LocalStreamCompression;
+
+ control -> RemoteStreamCompressionLevel =
+ control -> LocalStreamCompressionLevel;
+ }
+ else
+ {
+ control -> RemoteStreamCompressionLevel = ValidateArg("remote", name, value);
+
+ if (control -> RemoteStreamCompressionLevel > 0)
+ {
+ control -> RemoteStreamCompression = 1;
+ }
+ else
+ {
+ control -> RemoteStreamCompression = 0;
+ }
+
+ if (control -> LocalStreamCompressionLevel < 0)
+ {
+ control -> LocalStreamCompressionLevel = ValidateArg("remote", name, value);
+
+ if (control -> LocalStreamCompressionLevel > 0)
+ {
+ control -> LocalStreamCompression = 1;
+ }
+ else
+ {
+ control -> LocalStreamCompression = 0;
+ }
+ }
+ }
+
+ hasStream = 1;
+ }
+ else if (strcasecmp(name, "data") == 0)
+ {
+ //
+ // Apply the same to data compression level.
+ //
+
+ if (strcasecmp(value, "default") == 0)
+ {
+ control -> RemoteDataCompression =
+ control -> LocalDataCompression;
+
+ control -> RemoteDataCompressionLevel =
+ control -> LocalDataCompressionLevel;
+ }
+ else
+ {
+ control -> RemoteDataCompressionLevel = ValidateArg("remote", name, value);
+
+ if (control -> RemoteDataCompressionLevel > 0)
+ {
+ control -> RemoteDataCompression = 1;
+ }
+ else
+ {
+ control -> RemoteDataCompression = 0;
+ }
+
+ if (control -> LocalDataCompressionLevel < 0)
+ {
+ control -> LocalDataCompressionLevel = ValidateArg("remote", name, value);
+
+ if (control -> LocalDataCompressionLevel > 0)
+ {
+ control -> LocalDataCompression = 1;
+ }
+ else
+ {
+ control -> LocalDataCompression = 0;
+ }
+ }
+ }
+
+ hasData = 1;
+ }
+ else if (strcasecmp(name, "flush") == 0)
+ {
+ //
+ // This option has no effect in recent
+ // versions.
+ //
+
+ #ifdef DEBUG
+ *logofs << "Loop: Ignoring obsolete remote option '"
+ << name << "' with value '" << value
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Ignoring unknown remote option '"
+ << name << "' with value '" << value << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Ignoring unknown remote option '"
+ << name << "' with value '" << value << "'.\n";
+ }
+
+ name = strtok(NULL, "=");
+
+ } // End of while (name) ...
+
+ //
+ // If we are client side, we need remote 'stream'
+ // and 'data' options. If we are server, we need
+ // all the above plus 'link' and some others.
+ //
+
+ char missing[DEFAULT_STRING_LENGTH];
+
+ *missing = '\0';
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ if (hasStream == 0)
+ {
+ strcpy(missing, "stream");
+ }
+ else if (hasData == 0)
+ {
+ strcpy(missing, "data");
+ }
+ }
+ else
+ {
+ //
+ // Don't complain if the optional 'flush',
+ // 'render' and 'taint' options are not
+ // provided.
+ //
+
+ if (hasLink == 0)
+ {
+ strcpy(missing, "link");
+ }
+ else if (hasCache == 0)
+ {
+ strcpy(missing, "cache");
+ }
+ else if (hasPack == 0)
+ {
+ strcpy(missing, "pack");
+ }
+ else if (hasDelta == 0)
+ {
+ strcpy(missing, "delta");
+ }
+ else if (hasStream == 0)
+ {
+ strcpy(missing, "stream");
+ }
+ else if (hasData == 0)
+ {
+ strcpy(missing, "data");
+ }
+ else if (hasType == 0)
+ {
+ strcpy(missing, "type");
+ }
+ else if (hasImages == 0)
+ {
+ strcpy(missing, "images");
+ }
+ }
+
+ if (WE_PROVIDE_CREDENTIALS == 0)
+ {
+ //
+ // Can be that user doesn't have requested to
+ // check the authorization cookie provided by
+ // the connecting peer.
+ //
+
+ if (hasCookie == 0 && *authCookie != '\0')
+ {
+ strcpy(missing, "cookie");
+ }
+ }
+
+ if (*missing != '\0')
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! The remote peer didn't specify the option '"
+ << missing << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": The remote peer didn't specify the option '"
+ << missing << "'.\n";
+
+ return -1;
+ }
+
+ return 1;
+}
+
+//
+// Parse the cookie provided by the NX proxy
+// connection forwarder.
+//
+
+int ParseForwarderOptions(char *opts)
+{
+ #ifdef TEST
+ *logofs << "Loop: Going to parse the forwarder options "
+ << "string '" << opts << "'.\n"
+ << logofs_flush;
+ #endif
+
+ char *name;
+ char *value;
+
+ int hasCookie = 0;
+
+ //
+ // Get rid of the terminating space.
+ //
+
+ if (*(opts + strlen(opts) - 1) == ' ')
+ {
+ *(opts + strlen(opts) - 1) = '\0';
+ }
+
+ name = strtok(opts, "=");
+
+ while (name)
+ {
+ value = strtok(NULL, ",");
+
+ if (CheckArg("forwarder", name, value) < 0)
+ {
+ return -1;
+ }
+
+ if (strcasecmp(name, "cookie") == 0)
+ {
+ if (strncasecmp(authCookie, value, strlen(authCookie)) != 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! The NX forwarder cookie '" << value
+ << "' doesn't match '" << authCookie << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": The NX forwarder cookie '" << value
+ << "' doesn't match '" << authCookie << "'.\n";
+
+ return -1;
+ }
+
+ hasCookie = 1;
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Ignoring unknown forwarder option '"
+ << name << "' with value '" << value << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Ignoring unknown forwarder option '"
+ << name << "' with value '" << value << "'.\n";
+ }
+
+ name = strtok(NULL, "=");
+
+ } // End of while (name) ...
+
+ if (hasCookie == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! The NX forwarder didn't provide "
+ << "the authentication cookie.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": The NX forwarder didn't provide "
+ << "the authentication cookie.\n";
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int SetCore()
+{
+ #ifdef COREDUMPS
+
+ rlimit rlim;
+
+ if (getrlimit(RLIMIT_CORE, &rlim))
+ {
+ #ifdef TEST
+ *logofs << "Cannot read RLIMIT_CORE. Error is '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ if (rlim.rlim_cur < rlim.rlim_max)
+ {
+ rlim.rlim_cur = rlim.rlim_max;
+
+ if (setrlimit(RLIMIT_CORE, &rlim))
+ {
+ #ifdef TEST
+ *logofs << "Loop: Cannot read RLIMIT_CORE. Error is '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ return -2;
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Set RLIMIT_CORE to "<< rlim.rlim_max
+ << ".\n" << logofs_flush;
+ #endif
+
+ #endif // #ifdef COREDUMPS
+
+ return 1;
+}
+
+char *GetLastCache(char *listBuffer, const char *searchPath)
+{
+ if (listBuffer == NULL || searchPath == NULL ||
+ strncmp(listBuffer, "cachelist=", strlen("cachelist=")) != 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Invalid parameters '" << listBuffer << "' and '"
+ << (searchPath != NULL ? searchPath : "")
+ << "'. Can't select any cache.\n" << logofs_flush;
+ #endif
+
+ return NULL;
+ }
+
+ char *selectedName = new char[MD5_LENGTH * 2 + 3];
+
+ *selectedName = '\0';
+
+ const char *localPrefix;
+ const char *remotePrefix;
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ localPrefix = "C-";
+ remotePrefix = "S-";
+ }
+ else
+ {
+ localPrefix = "S-";
+ remotePrefix = "C-";
+ }
+
+ //
+ // Get rid of prefix.
+ //
+
+ listBuffer += strlen("cachelist=");
+
+ char *fileName;
+
+ fileName = strtok(listBuffer, ",");
+
+ //
+ // It is "/path/to/file" + "/" + "C-" + 32 + "\0".
+ //
+
+ char fullPath[strlen(searchPath) + MD5_LENGTH * 2 + 4];
+
+ time_t selectedTime = 0;
+
+ struct stat fileStat;
+
+ while (fileName)
+ {
+ if (strncmp(fileName, "none", strlen("none")) == 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: No cache files seem to be available.\n"
+ << logofs_flush;
+ #endif
+
+ delete [] selectedName;
+
+ return NULL;
+ }
+ else if (strlen(fileName) != MD5_LENGTH * 2 + 2 ||
+ strncmp(fileName, remotePrefix, 2) != 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Bad cache file name '"
+ << fileName << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Bad cache file name '"
+ << fileName << "'.\n";
+
+ delete [] selectedName;
+
+ HandleCleanup();
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Parsing remote cache name '"
+ << fileName << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // Prefix, received as "S-", becomes
+ // "C-" and viceversa.
+ //
+
+ *fileName = *localPrefix;
+
+ strcpy(fullPath, searchPath);
+ strcat(fullPath, "/");
+ strcat(fullPath, fileName);
+
+ if (stat(fullPath, &fileStat) == 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Found a matching cache '"
+ << fullPath << "'.\n" << logofs_flush;
+ #endif
+
+ if (fileStat.st_mtime >= selectedTime)
+ {
+ strcpy(selectedName, fileName);
+
+ selectedTime = fileStat.st_mtime;
+ }
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Can't get stats of file '"
+ << fullPath << "'.\n" << logofs_flush;
+ }
+ #endif
+
+ fileName = strtok(NULL, ",");
+ }
+
+ if (*selectedName != '\0')
+ {
+ return selectedName;
+ }
+ else
+ {
+ delete [] selectedName;
+
+ return NULL;
+ }
+}
+
+char *GetTempPath()
+{
+ if (*tempDir == '\0')
+ {
+ //
+ // Check the NX_TEMP environment, first,
+ // then the TEMP variable.
+ //
+
+ const char *tempEnv = getenv("NX_TEMP");
+
+ if (tempEnv == NULL || *tempEnv == '\0')
+ {
+ #ifdef TEST
+ *logofs << "Loop: WARNING! No environment for NX_TEMP.\n"
+ << logofs_flush;
+ #endif
+
+ tempEnv = getenv("TEMP");
+
+ if (tempEnv == NULL || *tempEnv == '\0')
+ {
+ #ifdef TEST
+ *logofs << "Loop: WARNING! No environment for TEMP.\n"
+ << logofs_flush;
+ #endif
+
+ tempEnv = "/tmp";
+ }
+ }
+
+ if (strlen(tempEnv) > DEFAULT_STRING_LENGTH - 1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid value for the NX "
+ << "temporary directory '" << tempEnv
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid value for the NX "
+ << "temporary directory '" << tempEnv
+ << "'.\n";
+
+ HandleCleanup();
+ }
+
+ strcpy(tempDir, tempEnv);
+
+ #ifdef TEST
+ *logofs << "Loop: Assuming temporary NX directory '"
+ << tempDir << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ char *tempPath = new char[strlen(tempDir) + 1];
+
+ if (tempPath == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't allocate memory "
+ << "for the temp path.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory "
+ << "for the temp path.\n";
+
+ HandleCleanup();
+ }
+
+ strcpy(tempPath, tempDir);
+
+ return tempPath;
+}
+
+char *GetClientPath()
+{
+ if (*clientDir == '\0')
+ {
+ //
+ // Check the NX_CLIENT environment.
+ //
+
+ const char *clientEnv = getenv("NX_CLIENT");
+
+ if (clientEnv == NULL || *clientEnv == '\0')
+ {
+ #ifdef TEST
+ *logofs << "Loop: WARNING! No environment for NX_CLIENT.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Try to guess the location of the client.
+ //
+
+ clientEnv = "/usr/NX/bin/nxclient";
+
+ #ifdef __APPLE__
+
+ clientEnv = "/Applications/NX Client for OSX.app/Contents/MacOS/nxclient";
+
+ #endif
+
+ #ifdef __CYGWIN32__
+
+ clientEnv = "C:\\Program Files\\NX Client for Windows\\nxclient";
+
+ #endif
+ }
+
+ if (strlen(clientEnv) > DEFAULT_STRING_LENGTH - 1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid value for the NX "
+ << "client directory '" << clientEnv
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid value for the NX "
+ << "client directory '" << clientEnv
+ << "'.\n";
+
+ HandleCleanup();
+ }
+
+ strcpy(clientDir, clientEnv);
+
+ #ifdef TEST
+ *logofs << "Loop: Assuming NX client location '"
+ << clientDir << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ char *clientPath = new char[strlen(clientDir) + 1];
+
+ if (clientPath == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't allocate memory "
+ << "for the client path.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory "
+ << "for the client path.\n";
+
+ HandleCleanup();
+ }
+
+ strcpy(clientPath, clientDir);
+
+ return clientPath;
+}
+
+char *GetSystemPath()
+{
+ if (*systemDir == '\0')
+ {
+ //
+ // Check the NX_SYSTEM environment.
+ //
+
+ const char *systemEnv = getenv("NX_SYSTEM");
+
+ if (systemEnv == NULL || *systemEnv == '\0')
+ {
+ #ifdef TEST
+ *logofs << "Loop: WARNING! No environment for NX_SYSTEM.\n"
+ << logofs_flush;
+ #endif
+
+ systemEnv = "/usr/NX";
+ }
+
+ if (strlen(systemEnv) > DEFAULT_STRING_LENGTH - 1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid value for the NX "
+ << "system directory '" << systemEnv
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid value for the NX "
+ << "system directory '" << systemEnv
+ << "'.\n";
+
+ HandleCleanup();
+ }
+
+ strcpy(systemDir, systemEnv);
+
+ #ifdef TEST
+ *logofs << "Loop: Assuming system NX directory '"
+ << systemDir << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ char *systemPath = new char[strlen(systemDir) + 1];
+
+ if (systemPath == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't allocate memory "
+ << "for the system path.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory "
+ << "for the system path.\n";
+
+ HandleCleanup();
+ }
+
+ strcpy(systemPath, systemDir);
+
+ return systemPath;
+}
+
+char *GetHomePath()
+{
+ if (*homeDir == '\0')
+ {
+ //
+ // Check the NX_HOME environment.
+ //
+
+ const char *homeEnv = getenv("NX_HOME");
+
+ if (homeEnv == NULL || *homeEnv == '\0')
+ {
+ #ifdef TEST
+ *logofs << "Loop: WARNING! No environment for NX_HOME.\n"
+ << logofs_flush;
+ #endif
+
+ homeEnv = getenv("HOME");
+
+ if (homeEnv == NULL || *homeEnv == '\0')
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! No environment for HOME.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": No environment for HOME.\n";
+
+ HandleCleanup();
+ }
+ }
+
+ if (strlen(homeEnv) > DEFAULT_STRING_LENGTH - 1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid value for the NX "
+ << "home directory '" << homeEnv
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid value for the NX "
+ << "home directory '" << homeEnv
+ << "'.\n";
+
+ HandleCleanup();
+ }
+
+ strcpy(homeDir, homeEnv);
+
+ #ifdef TEST
+ *logofs << "Loop: Assuming NX user's home directory '"
+ << homeDir << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ char *homePath = new char[strlen(homeDir) + 1];
+
+ if (homePath == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't allocate memory "
+ << "for the home path.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory "
+ << "for the home path.\n";
+
+ HandleCleanup();
+ }
+
+ strcpy(homePath, homeDir);
+
+ return homePath;
+}
+
+char *GetRootPath()
+{
+ if (*rootDir == '\0')
+ {
+ //
+ // Check the NX_ROOT environment.
+ //
+
+ const char *rootEnv = getenv("NX_ROOT");
+
+ if (rootEnv == NULL || *rootEnv == '\0')
+ {
+ #ifdef TEST
+ *logofs << "Loop: WARNING! No environment for NX_ROOT.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // We will determine the root NX directory
+ // based on the NX_HOME or HOME directory
+ // settings.
+ //
+
+ const char *homeEnv = GetHomePath();
+
+ if (strlen(homeEnv) > DEFAULT_STRING_LENGTH -
+ strlen("/.nx") - 1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid value for the NX "
+ << "home directory '" << homeEnv
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid value for the NX "
+ << "home directory '" << homeEnv
+ << "'.\n";
+
+ HandleCleanup();
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Assuming NX root directory in "
+ << "the user's home '" << homeEnv
+ << "'.\n" << logofs_flush;
+ #endif
+
+ strcpy(rootDir, homeEnv);
+ strcat(rootDir, "/.nx");
+
+ delete [] homeEnv;
+
+ //
+ // Create the NX root directory.
+ //
+
+ struct stat dirStat;
+
+ if ((stat(rootDir, &dirStat) == -1) && (EGET() == ENOENT))
+ {
+ if (mkdir(rootDir, 0700) < 0 && (EGET() != EEXIST))
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't create directory '"
+ << rootDir << ". Error is " << EGET() << " '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't create directory '"
+ << rootDir << ". Error is " << EGET() << " '"
+ << ESTR() << "'.\n";
+
+ HandleCleanup();
+ }
+ }
+ }
+ else
+ {
+ if (strlen(rootEnv) > DEFAULT_STRING_LENGTH - 1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid value for the NX "
+ << "root directory '" << rootEnv
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid value for the NX "
+ << "root directory '" << rootEnv
+ << "'.\n";
+
+ HandleCleanup();
+ }
+
+ strcpy(rootDir, rootEnv);
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Assuming NX root directory '"
+ << rootDir << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ char *rootPath = new char[strlen(rootDir) + 1];
+
+ if (rootPath == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't allocate memory "
+ << "for the root path.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory "
+ << "for the root path.\n";
+
+ HandleCleanup();
+ }
+
+ strcpy(rootPath, rootDir);
+
+ return rootPath;
+}
+
+char *GetCachePath()
+{
+ char *rootPath = GetRootPath();
+
+ char *cachePath;
+
+ if (*sessionType != '\0')
+ {
+ cachePath = new char[strlen(rootPath) + strlen("/cache-") +
+ strlen(sessionType) + 1];
+ }
+ else
+ {
+ cachePath = new char[strlen(rootPath) + strlen("/cache") + 1];
+ }
+
+ strcpy(cachePath, rootPath);
+
+ if (*sessionType != '\0')
+ {
+ strcat(cachePath, "/cache-");
+
+ strcat(cachePath, sessionType);
+ }
+ else
+ {
+ strcat(cachePath, "/cache");
+ }
+
+ //
+ // Create the cache directory if needed.
+ //
+
+ struct stat dirStat;
+
+ if ((stat(cachePath, &dirStat) == -1) && (EGET() == ENOENT))
+ {
+ if (mkdir(cachePath, 0700) < 0 && (EGET() != EEXIST))
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't create directory '" << cachePath
+ << ". Error is " << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't create directory '" << cachePath
+ << ". Error is " << EGET() << " '" << ESTR() << "'.\n";
+
+ delete [] rootPath;
+ delete [] cachePath;
+
+ return NULL;
+ }
+ }
+
+ delete [] rootPath;
+
+ return cachePath;
+}
+
+char *GetImagesPath()
+{
+ char *rootPath = GetRootPath();
+
+ char *imagesPath = new char[strlen(rootPath) + strlen("/images") + 1];
+
+ strcpy(imagesPath, rootPath);
+
+ strcat(imagesPath, "/images");
+
+ //
+ // Create the cache directory if needed.
+ //
+
+ struct stat dirStat;
+
+ if ((stat(imagesPath, &dirStat) == -1) && (EGET() == ENOENT))
+ {
+ if (mkdir(imagesPath, 0700) < 0 && (EGET() != EEXIST))
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't create directory '" << imagesPath
+ << ". Error is " << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't create directory '" << imagesPath
+ << ". Error is " << EGET() << " '" << ESTR() << "'.\n";
+
+ delete [] rootPath;
+ delete [] imagesPath;
+
+ return NULL;
+ }
+ }
+
+ //
+ // Create 16 directories in the path to
+ // hold the images whose name begins with
+ // the corresponding hexadecimal digit.
+ //
+
+ char *digitPath = new char[strlen(imagesPath) + 5];
+
+ strcpy(digitPath, imagesPath);
+
+ //
+ // Image paths have format "[path][/I-c][\0]",
+ // where c is the first digit of the checksum.
+ //
+
+ for (char digit = 0; digit < 16; digit++)
+ {
+ sprintf(digitPath + strlen(imagesPath), "/I-%01X", digit);
+
+ if ((stat(digitPath, &dirStat) == -1) && (EGET() == ENOENT))
+ {
+ if (mkdir(digitPath, 0700) < 0 && (EGET() != EEXIST))
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't create directory '" << digitPath
+ << ". Error is " << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't create directory '" << digitPath
+ << ". Error is " << EGET() << " '" << ESTR() << "'.\n";
+
+ delete [] rootPath;
+ delete [] imagesPath;
+ delete [] digitPath;
+
+ return NULL;
+ }
+ }
+ }
+
+ delete [] rootPath;
+ delete [] digitPath;
+
+ return imagesPath;
+}
+
+char *GetSessionPath()
+{
+ if (*sessionDir == '\0')
+ {
+ char *rootPath = GetRootPath();
+
+ strcpy(sessionDir, rootPath);
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ strcat(sessionDir, "/C-");
+ }
+ else
+ {
+ strcat(sessionDir, "/S-");
+ }
+
+ if (*sessionId == '\0')
+ {
+ char port[DEFAULT_STRING_LENGTH];
+
+ sprintf(port, "%d", proxyPort);
+
+ strcpy(sessionId, port);
+ }
+
+ strcat(sessionDir, sessionId);
+
+ struct stat dirStat;
+
+ if ((stat(sessionDir, &dirStat) == -1) && (EGET() == ENOENT))
+ {
+ if (mkdir(sessionDir, 0700) < 0 && (EGET() != EEXIST))
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't create directory '" << sessionDir
+ << ". Error is " << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't create directory '" << sessionDir
+ << ". Error is " << EGET() << " '" << ESTR() << "'.\n";
+
+ delete [] rootPath;
+
+ return NULL;
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Root of NX session is '" << sessionDir
+ << "'.\n" << logofs_flush;
+ #endif
+
+ delete [] rootPath;
+ }
+
+ char *sessionPath = new char[strlen(sessionDir) + 1];
+
+ strcpy(sessionPath, sessionDir);
+
+ return sessionPath;
+}
+
+//
+// Identify requested link characteristics
+// and set control parameters accordingly.
+//
+
+int ParseLinkOption(const char *opt)
+{
+ //
+ // Normalize the user input.
+ //
+
+ if (strcasecmp(opt, "modem") == 0 ||
+ strcasecmp(opt, "33k") == 0 ||
+ strcasecmp(opt, "56k") == 0)
+ {
+ strcpy(linkSpeedName, "MODEM");
+ }
+ else if (strcasecmp(opt, "isdn") == 0 ||
+ strcasecmp(opt, "64k") == 0 ||
+ strcasecmp(opt, "128k") == 0)
+ {
+ strcpy(linkSpeedName, "ISDN");
+ }
+ else if (strcasecmp(opt, "adsl") == 0 ||
+ strcasecmp(opt, "256k") == 0 ||
+ strcasecmp(opt, "640k") == 0)
+ {
+ strcpy(linkSpeedName, "ADSL");
+ }
+ else if (strcasecmp(opt, "wan") == 0 ||
+ strcasecmp(opt, "1m") == 0 ||
+ strcasecmp(opt, "2m") == 0 ||
+ strcasecmp(opt, "34m") == 0)
+ {
+ strcpy(linkSpeedName, "WAN");
+ }
+ else if (strcasecmp(opt, "lan") == 0 ||
+ strcasecmp(opt, "10m") == 0 ||
+ strcasecmp(opt, "100m") == 0 ||
+ strcasecmp(opt, "local") == 0)
+ {
+ strcpy(linkSpeedName, "LAN");
+ }
+
+ if (strcasecmp(linkSpeedName, "modem") != 0 &&
+ strcasecmp(linkSpeedName, "isdn") != 0 &&
+ strcasecmp(linkSpeedName, "adsl") != 0 &&
+ strcasecmp(linkSpeedName, "wan") != 0 &&
+ strcasecmp(linkSpeedName, "lan") != 0)
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+int ParsePackOption(const char *opt)
+{
+ #ifdef DEBUG
+ *logofs << "Loop: Pack method is " << packMethod
+ << " quality is " << packQuality << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef DEBUG
+ *logofs << "Loop: Parsing pack method '" << opt
+ << "'.\n" << logofs_flush;
+ #endif
+
+ if (strcasecmp(opt, "0") == 0 ||
+ strcasecmp(opt, "none") == 0 ||
+ strcasecmp(opt, "nopack") == 0 ||
+ strcasecmp(opt, "no-pack") == 0)
+ {
+ packMethod = PACK_NONE;
+ }
+ else if (strcasecmp(opt, "8") == 0)
+ {
+ packMethod = PACK_MASKED_8_COLORS;
+ }
+ else if (strcasecmp(opt, "64") == 0)
+ {
+ packMethod = PACK_MASKED_64_COLORS;
+ }
+ else if (strcasecmp(opt, "256") == 0)
+ {
+ packMethod = PACK_MASKED_256_COLORS;
+ }
+ else if (strcasecmp(opt, "512") == 0)
+ {
+ packMethod = PACK_MASKED_512_COLORS;
+ }
+ else if (strcasecmp(opt, "4k") == 0)
+ {
+ packMethod = PACK_MASKED_4K_COLORS;
+ }
+ else if (strcasecmp(opt, "32k") == 0)
+ {
+ packMethod = PACK_MASKED_32K_COLORS;
+ }
+ else if (strcasecmp(opt, "64k") == 0)
+ {
+ packMethod = PACK_MASKED_64K_COLORS;
+ }
+ else if (strcasecmp(opt, "256k") == 0)
+ {
+ packMethod = PACK_MASKED_256K_COLORS;
+ }
+ else if (strcasecmp(opt, "2m") == 0)
+ {
+ packMethod = PACK_MASKED_2M_COLORS;
+ }
+ else if (strcasecmp(opt, "16m") == 0)
+ {
+ packMethod = PACK_MASKED_16M_COLORS;
+ }
+ else if (strncasecmp(opt, "8-jpeg", strlen("8-jpeg")) == 0)
+ {
+ packMethod = PACK_JPEG_8_COLORS;
+ }
+ else if (strncasecmp(opt, "64-jpeg", strlen("64-jpeg")) == 0)
+ {
+ packMethod = PACK_JPEG_64_COLORS;
+ }
+ else if (strncasecmp(opt, "256-jpeg", strlen("256-jpeg")) == 0)
+ {
+ packMethod = PACK_JPEG_256_COLORS;
+ }
+ else if (strncasecmp(opt, "512-jpeg", strlen("512-jpeg")) == 0)
+ {
+ packMethod = PACK_JPEG_512_COLORS;
+ }
+ else if (strncasecmp(opt, "4k-jpeg", strlen("4k-jpeg")) == 0)
+ {
+ packMethod = PACK_JPEG_4K_COLORS;
+ }
+ else if (strncasecmp(opt, "32k-jpeg", strlen("32k-jpeg")) == 0)
+ {
+ packMethod = PACK_JPEG_32K_COLORS;
+ }
+ else if (strncasecmp(opt, "64k-jpeg", strlen("64k-jpeg")) == 0)
+ {
+ packMethod = PACK_JPEG_64K_COLORS;
+ }
+ else if (strncasecmp(opt, "256k-jpeg", strlen("256k-jpeg")) == 0)
+ {
+ packMethod = PACK_JPEG_256K_COLORS;
+ }
+ else if (strncasecmp(opt, "2m-jpeg", strlen("2m-jpeg")) == 0)
+ {
+ packMethod = PACK_JPEG_2M_COLORS;
+ }
+ else if (strncasecmp(opt, "16m-jpeg", strlen("16m-jpeg")) == 0)
+ {
+ packMethod = PACK_JPEG_16M_COLORS;
+ }
+ else if (strncasecmp(opt, "8-png", strlen("8-png")) == 0)
+ {
+ packMethod = PACK_PNG_8_COLORS;
+ }
+ else if (strncasecmp(opt, "64-png", strlen("64-png")) == 0)
+ {
+ packMethod = PACK_PNG_64_COLORS;
+ }
+ else if (strncasecmp(opt, "256-png", strlen("256-png")) == 0)
+ {
+ packMethod = PACK_PNG_256_COLORS;
+ }
+ else if (strncasecmp(opt, "512-png", strlen("512-png")) == 0)
+ {
+ packMethod = PACK_PNG_512_COLORS;
+ }
+ else if (strncasecmp(opt, "4k-png", strlen("4k-png")) == 0)
+ {
+ packMethod = PACK_PNG_4K_COLORS;
+ }
+ else if (strncasecmp(opt, "32k-png", strlen("32k-png")) == 0)
+ {
+ packMethod = PACK_PNG_32K_COLORS;
+ }
+ else if (strncasecmp(opt, "64k-png", strlen("64k-png")) == 0)
+ {
+ packMethod = PACK_PNG_64K_COLORS;
+ }
+ else if (strncasecmp(opt, "256k-png", strlen("256k-png")) == 0)
+ {
+ packMethod = PACK_PNG_256K_COLORS;
+ }
+ else if (strncasecmp(opt, "2m-png", strlen("2m-png")) == 0)
+ {
+ packMethod = PACK_PNG_2M_COLORS;
+ }
+ else if (strncasecmp(opt, "16m-png", strlen("16m-png")) == 0)
+ {
+ packMethod = PACK_PNG_16M_COLORS;
+ }
+ else if (strncasecmp(opt, "16m-rgb", strlen("16m-rgb")) == 0 ||
+ strncasecmp(opt, "rgb", strlen("rgb")) == 0)
+ {
+ packMethod = PACK_RGB_16M_COLORS;
+ }
+ else if (strncasecmp(opt, "16m-rle", strlen("16m-rle")) == 0 ||
+ strncasecmp(opt, "rle", strlen("rle")) == 0)
+ {
+ packMethod = PACK_RLE_16M_COLORS;
+ }
+ else if (strncasecmp(opt, "16m-bitmap", strlen("16m-bitmap")) == 0 ||
+ strncasecmp(opt, "bitmap", strlen("bitmap")) == 0)
+ {
+ packMethod = PACK_BITMAP_16M_COLORS;
+ }
+ else if (strncasecmp(opt, "lossy", strlen("lossy")) == 0)
+ {
+ packMethod = PACK_LOSSY;
+ }
+ else if (strncasecmp(opt, "lossless", strlen("lossless")) == 0)
+ {
+ packMethod = PACK_LOSSLESS;
+ }
+ else if (strncasecmp(opt, "adaptive", strlen("adaptive")) == 0)
+ {
+ packMethod = PACK_ADAPTIVE;
+ }
+ else
+ {
+ return -1;
+ }
+
+ if (packMethod == PACK_NONE)
+ {
+ strcpy(packMethodName, "none");
+ }
+ else
+ {
+ strcpy(packMethodName, opt);
+ }
+
+ if (packMethod == PACK_RGB_16M_COLORS ||
+ packMethod == PACK_RLE_16M_COLORS ||
+ packMethod == PACK_BITMAP_16M_COLORS ||
+ (packMethod >= PACK_JPEG_8_COLORS &&
+ packMethod <= PACK_JPEG_16M_COLORS) ||
+ (packMethod >= PACK_PNG_8_COLORS &&
+ packMethod <= PACK_PNG_16M_COLORS) ||
+ packMethod == PACK_LOSSY ||
+ packMethod == PACK_LOSSLESS ||
+ packMethod == PACK_ADAPTIVE)
+ {
+ const char *dash = rindex(opt, '-');
+
+ if (dash != NULL && strlen(dash) == 2 &&
+ *(dash + 1) >= '0' && *(dash + 1) <= '9')
+ {
+ packQuality = atoi(dash + 1);
+
+ #ifdef DEBUG
+ *logofs << "Loop: Using pack quality '"
+ << packQuality << "'.\n" << logofs_flush;
+ #endif
+ }
+ }
+ else
+ {
+ packQuality = 0;
+ }
+
+ return 1;
+}
+
+int ParsePackMethod(const int method, const int quality)
+{
+ switch (method)
+ {
+ case PACK_NONE:
+ {
+ strcpy(packMethodName, "none");
+
+ break;
+ }
+ case PACK_MASKED_8_COLORS:
+ {
+ strcpy(packMethodName, "8");
+
+ break;
+ }
+ case PACK_MASKED_64_COLORS:
+ {
+ strcpy(packMethodName, "64");
+
+ break;
+ }
+ case PACK_MASKED_256_COLORS:
+ {
+ strcpy(packMethodName, "256");
+
+ break;
+ }
+ case PACK_MASKED_512_COLORS:
+ {
+ strcpy(packMethodName, "512");
+
+ break;
+ }
+ case PACK_MASKED_4K_COLORS:
+ {
+ strcpy(packMethodName, "4k");
+
+ break;
+ }
+ case PACK_MASKED_32K_COLORS:
+ {
+ strcpy(packMethodName, "32k");
+
+ break;
+ }
+ case PACK_MASKED_64K_COLORS:
+ {
+ strcpy(packMethodName, "64k");
+
+ break;
+ }
+ case PACK_MASKED_256K_COLORS:
+ {
+ strcpy(packMethodName, "256k");
+
+ break;
+ }
+ case PACK_MASKED_2M_COLORS:
+ {
+ strcpy(packMethodName, "2m");
+
+ break;
+ }
+ case PACK_MASKED_16M_COLORS:
+ {
+ strcpy(packMethodName, "16m");
+
+ break;
+ }
+ case PACK_JPEG_8_COLORS:
+ {
+ strcpy(packMethodName, "8-jpeg");
+
+ break;
+ }
+ case PACK_JPEG_64_COLORS:
+ {
+ strcpy(packMethodName, "64-jpeg");
+
+ break;
+ }
+ case PACK_JPEG_256_COLORS:
+ {
+ strcpy(packMethodName, "256-jpeg");
+
+ break;
+ }
+ case PACK_JPEG_512_COLORS:
+ {
+ strcpy(packMethodName, "512-jpeg");
+
+ break;
+ }
+ case PACK_JPEG_4K_COLORS:
+ {
+ strcpy(packMethodName, "4k-jpeg");
+
+ break;
+ }
+ case PACK_JPEG_32K_COLORS:
+ {
+ strcpy(packMethodName, "32k-jpeg");
+
+ break;
+ }
+ case PACK_JPEG_64K_COLORS:
+ {
+ strcpy(packMethodName, "64k-jpeg");
+
+ break;
+ }
+ case PACK_JPEG_256K_COLORS:
+ {
+ strcpy(packMethodName, "256k-jpeg");
+
+ break;
+ }
+ case PACK_JPEG_2M_COLORS:
+ {
+ strcpy(packMethodName, "2m-jpeg");
+
+ break;
+ }
+ case PACK_JPEG_16M_COLORS:
+ {
+ strcpy(packMethodName, "16m-jpeg");
+
+ break;
+ }
+ case PACK_PNG_8_COLORS:
+ {
+ strcpy(packMethodName, "8-png");
+
+ break;
+ }
+ case PACK_PNG_64_COLORS:
+ {
+ strcpy(packMethodName, "64-png");
+
+ break;
+ }
+ case PACK_PNG_256_COLORS:
+ {
+ strcpy(packMethodName, "256-png");
+
+ break;
+ }
+ case PACK_PNG_512_COLORS:
+ {
+ strcpy(packMethodName, "512-png");
+
+ break;
+ }
+ case PACK_PNG_4K_COLORS:
+ {
+ strcpy(packMethodName, "4k-png");
+
+ break;
+ }
+ case PACK_PNG_32K_COLORS:
+ {
+ strcpy(packMethodName, "32k-png");
+
+ break;
+ }
+ case PACK_PNG_64K_COLORS:
+ {
+ strcpy(packMethodName, "64k-png");
+
+ break;
+ }
+ case PACK_PNG_256K_COLORS:
+ {
+ strcpy(packMethodName, "256k-png");
+
+ break;
+ }
+ case PACK_PNG_2M_COLORS:
+ {
+ strcpy(packMethodName, "2m-png");
+
+ break;
+ }
+ case PACK_PNG_16M_COLORS:
+ {
+ strcpy(packMethodName, "16m-png");
+
+ break;
+ }
+ case PACK_RGB_16M_COLORS:
+ {
+ strcpy(packMethodName, "16m-rgb");
+
+ break;
+ }
+ case PACK_RLE_16M_COLORS:
+ {
+ strcpy(packMethodName, "16m-rle");
+
+ break;
+ }
+ case PACK_BITMAP_16M_COLORS:
+ {
+ strcpy(packMethodName, "16m-bitmap");
+
+ break;
+ }
+ case PACK_LOSSY:
+ {
+ strcpy(packMethodName, "lossy");
+
+ break;
+ }
+ case PACK_LOSSLESS:
+ {
+ strcpy(packMethodName, "lossless");
+
+ break;
+ }
+ case PACK_ADAPTIVE:
+ {
+ strcpy(packMethodName, "adaptive");
+
+ break;
+ }
+ default:
+ {
+ return -1;
+ }
+ }
+
+ if (quality < 0 || quality > 9)
+ {
+ return -1;
+ }
+
+ if (packMethod == PACK_RGB_16M_COLORS ||
+ packMethod == PACK_RLE_16M_COLORS ||
+ packMethod == PACK_BITMAP_16M_COLORS ||
+ (packMethod >= PACK_JPEG_8_COLORS &&
+ packMethod <= PACK_JPEG_16M_COLORS) ||
+ (packMethod >= PACK_PNG_8_COLORS &&
+ packMethod <= PACK_PNG_16M_COLORS) ||
+ packMethod == PACK_LOSSY ||
+ packMethod == PACK_LOSSLESS ||
+ packMethod == PACK_ADAPTIVE)
+ {
+ sprintf(packMethodName + strlen(packMethodName),
+ "-%d", quality);
+ }
+
+ packMethod = method;
+ packQuality = quality;
+
+ control -> PackMethod = packMethod;
+ control -> PackQuality = packQuality;
+
+ return 1;
+}
+
+int SetDirectories()
+{
+ //
+ // Determine the location of the user's NX
+ // directory and the other relevant paths.
+ // The functions below will check the pa-
+ // rameters passed to the program and will
+ // query the environment, if needed.
+ //
+
+ control -> HomePath = GetHomePath();
+ control -> RootPath = GetRootPath();
+ control -> SystemPath = GetSystemPath();
+ control -> TempPath = GetTempPath();
+ control -> ClientPath = GetClientPath();
+
+ return 1;
+}
+
+int SetLogs()
+{
+ //
+ // So far we used stderr (or stdout under
+ // WIN32). Now use the files selected by
+ // the user.
+ //
+
+ if (*statsFileName == '\0')
+ {
+ strcpy(statsFileName, "stats");
+
+ #ifdef TEST
+ *logofs << "Loop: Assuming default statistics file '"
+ << statsFileName << "'.\n" << logofs_flush;
+ #endif
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Name selected for statistics is '"
+ << statsFileName << "'.\n" << logofs_flush;
+ }
+ #endif
+
+ if (OpenLogFile(statsFileName, statofs) < 0)
+ {
+ HandleCleanup();
+ }
+
+ #ifndef MIXED
+
+ if (*errorsFileName == '\0')
+ {
+ strcpy(errorsFileName, "errors");
+
+ #ifdef TEST
+ *logofs << "Loop: Assuming default log file name '"
+ << errorsFileName << "'.\n" << logofs_flush;
+ #endif
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Name selected for log file is '"
+ << errorsFileName << "'.\n" << logofs_flush;
+ }
+ #endif
+
+ //
+ // Share the bebug output with the nxssh binder
+ // process. The file must be made writable by
+ // everybody because the nxssh process is run by
+ // nxserver as the nx user.
+ //
+
+ #ifdef BINDER
+
+ strcpy(errorsFileName, "/tmp/errors");
+
+ ostream *tmpfs = new ofstream(errorsFileName, ios::out);
+
+ delete tmpfs;
+
+ chmod(errorsFileName, S_IRUSR | S_IWUSR | S_IRGRP |
+ S_IWGRP | S_IROTH | S_IWOTH);
+
+ #endif
+
+ if (OpenLogFile(errorsFileName, logofs) < 0)
+ {
+ HandleCleanup();
+ }
+
+ //
+ // By default the session log is the standard error
+ // of the process. It is anyway required to set the
+ // option when running inside SSH, otherwise the
+ // output will go to the same file as the SSH log,
+ // depending where the NX client has redirected the
+ // output.
+ //
+
+ if (*sessionFileName != '\0')
+ {
+ #ifdef TEST
+ *logofs << "Loop: Name selected for session file is '"
+ << sessionFileName << "'.\n" << logofs_flush;
+ #endif
+
+ if (errofs != NULL)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Unexpected value for stream errofs.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Unexpected value for stream errofs.\n";
+ }
+
+ if (errsbuf != NULL)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Unexpected value for buffer errsbuf.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Unexpected value for buffer errsbuf.\n";
+ }
+
+ errofs = NULL;
+ errsbuf = NULL;
+
+ if (OpenLogFile(sessionFileName, errofs) < 0)
+ {
+ HandleCleanup();
+ }
+
+ //
+ // Redirect the standard error to the file.
+ //
+
+ errsbuf = cerr.rdbuf(errofs -> rdbuf());
+ }
+
+ #endif
+
+ return 1;
+}
+
+int SetPorts()
+{
+ //
+ // Depending on the proxy side, we need to determine on which
+ // port to listen for the given protocol or to which port we
+ // will have to forward the connection. Three possibilities
+ // are given for each supported protocol:
+ //
+ // Port <= 0: Disable port forwarding.
+ // Port == 1: Use the default port.
+ // Port > 1: Use the specified port.
+ //
+ // At the connectiong side the user should always explicitly
+ // set the ports where the connections will be forwarded. This
+ // is both for security reasons and because, when running both
+ // proxies on the same host, there is a concrete possibility
+ // that, by using the default ports, the connection will be
+ // forwarded to the same port where the peer proxy is listen-
+ // ing, causing a loop.
+ //
+
+ if (cupsPort <= 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Disabling cups connections.\n"
+ << logofs_flush;
+ #endif
+
+ cupsPort = 0;
+
+ useCupsSocket = 0;
+ }
+ else
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ if (cupsPort == 1)
+ {
+ cupsPort = DEFAULT_NX_CUPS_PORT_OFFSET + proxyPort;
+ }
+
+ useCupsSocket = 1;
+ }
+ else
+ {
+ if (cupsPort == 1)
+ {
+ //
+ // Use the well-known 631/tcp port of the
+ // Internet Printing Protocol.
+ //
+
+ cupsPort = 631;
+ }
+
+ useCupsSocket = 0;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Using cups port '" << cupsPort
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ if (auxPort <= 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Disabling auxiliary X11 connections.\n"
+ << logofs_flush;
+ #endif
+
+ auxPort = 0;
+
+ useAuxSocket = 0;
+ }
+ else
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ if (auxPort == 1)
+ {
+ auxPort = DEFAULT_NX_AUX_PORT_OFFSET + proxyPort;
+ }
+
+ useAuxSocket = 1;
+ }
+ else
+ {
+ //
+ // Auxiliary X connections are always forwarded
+ // to the display where the session is running.
+ // The only value accepted is 1.
+ //
+
+ if (auxPort != 1)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Overriding auxiliary X11 "
+ << "port with new value '" << 1 << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Overriding auxiliary X11 "
+ << "port with new value '" << 1 << "'.\n";
+
+ auxPort = 1;
+ }
+
+ useAuxSocket = 0;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Using auxiliary X11 port '" << auxPort
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ if (smbPort <= 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Disabling SMB connections.\n"
+ << logofs_flush;
+ #endif
+
+ smbPort = 0;
+
+ useSmbSocket = 0;
+ }
+ else
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ if (smbPort == 1)
+ {
+ smbPort = DEFAULT_NX_SMB_PORT_OFFSET + proxyPort;
+ }
+
+ useSmbSocket = 1;
+ }
+ else
+ {
+ if (smbPort == 1)
+ {
+ //
+ // Assume the 139/tcp port used for SMB
+ // over NetBIOS over TCP.
+ //
+
+ smbPort = 139;
+ }
+
+ useSmbSocket = 0;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Using SMB port '" << smbPort
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ if (mediaPort <= 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Disabling multimedia connections.\n"
+ << logofs_flush;
+ #endif
+
+ mediaPort = 0;
+
+ useMediaSocket = 0;
+ }
+ else
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ if (mediaPort == 1)
+ {
+ mediaPort = DEFAULT_NX_MEDIA_PORT_OFFSET + proxyPort;
+ }
+
+ useMediaSocket = 1;
+ }
+ else
+ {
+ if (mediaPort == 1)
+ {
+ //
+ // We don't have a well-known port to
+ // be used for media connections.
+ //
+
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! No port specified for multimedia connections.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": No port specified for multimedia connections.\n";
+
+ HandleCleanup();
+ }
+
+ useMediaSocket = 0;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Using multimedia port '" << mediaPort
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ if (httpPort <= 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Disabling HTTP connections.\n"
+ << logofs_flush;
+ #endif
+
+ httpPort = 0;
+
+ useHttpSocket = 0;
+ }
+ else
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ if (httpPort == 1)
+ {
+ httpPort = DEFAULT_NX_HTTP_PORT_OFFSET + proxyPort;
+ }
+
+ useHttpSocket = 1;
+ }
+ else
+ {
+ if (httpPort == 1)
+ {
+ //
+ // Use the well-known 80/tcp port.
+ //
+
+ httpPort = 80;
+ }
+
+ useHttpSocket = 0;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Using HTTP port '" << httpPort
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ if (ParseFontPath(fontPort) <= 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Disabling font server connections.\n"
+ << logofs_flush;
+ #endif
+
+ *fontPort = '\0';
+
+ useFontSocket = 0;
+ }
+ else
+ {
+ //
+ // We don't know yet if the remote proxy supports
+ // the font server connections. If needed, we will
+ // disable the font server connections at later
+ // time.
+ //
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ useFontSocket = 1;
+ }
+ else
+ {
+ useFontSocket = 0;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Using font server port '" << fontPort
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ if (slavePort <= 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Disabling slave connections.\n"
+ << logofs_flush;
+ #endif
+
+ slavePort = 0;
+
+ useSlaveSocket = 0;
+ }
+ else
+ {
+ //
+ // File transfer connections can
+ // be originated by both sides.
+ //
+
+ if (slavePort == 1)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ slavePort = DEFAULT_NX_SLAVE_PORT_CLIENT_OFFSET + proxyPort;
+ }
+ else
+ {
+ slavePort = DEFAULT_NX_SLAVE_PORT_SERVER_OFFSET + proxyPort;
+ }
+ }
+
+ useSlaveSocket = 1;
+
+ #ifdef TEST
+ *logofs << "Loop: Using slave port '" << slavePort
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ return 1;
+}
+
+int SetDescriptors()
+{
+ unsigned int limit = 0;
+
+ #ifdef RLIMIT_NOFILE
+
+ rlimit limits;
+
+ if (getrlimit(RLIMIT_NOFILE, &limits) == 0)
+ {
+ if (limits.rlim_max == RLIM_INFINITY)
+ {
+ limit = 0;
+ }
+ else
+ {
+ limit = (unsigned int) limits.rlim_max;
+ }
+ }
+
+ #endif
+
+ #ifdef _SC_OPEN_MAX
+
+ if (limit == 0)
+ {
+ limit = sysconf(_SC_OPEN_MAX);
+ }
+
+ #endif
+
+ #ifdef FD_SETSIZE
+
+ if (limit > FD_SETSIZE)
+ {
+ limit = FD_SETSIZE;
+ }
+
+ #endif
+
+ #ifdef RLIMIT_NOFILE
+
+ if (limits.rlim_cur < limit)
+ {
+ limits.rlim_cur = limit;
+
+ setrlimit(RLIMIT_NOFILE, &limits);
+ }
+
+ #endif
+
+ if (limit == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Cannot determine number of available "
+ << "file descriptors.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot determine number of available "
+ << "file descriptors.\n";
+
+ return -1;
+ }
+
+ return 1;
+}
+
+//
+// Find the directory containing the caches
+// matching the session type.
+//
+
+int SetCaches()
+{
+ if ((control -> PersistentCachePath = GetCachePath()) == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Error getting or creating the cache path.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error getting or creating the cache path.\n";
+
+ HandleCleanup();
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Path of cache files is '" << control -> PersistentCachePath
+ << "'.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+//
+// Initialize all configuration parameters.
+//
+
+int SetParameters()
+{
+ //
+ // Find out the type of session.
+ //
+
+ SetSession();
+
+ //
+ // Initialize the network and compression
+ // parameters according to the settings
+ // suggested by the user.
+ //
+
+ SetLink();
+
+ //
+ // Set compression according to link speed.
+ //
+
+ SetCompression();
+
+ //
+ // Be sure that we have a literal for current
+ // cache size. Value will reflect control's
+ // default unless we already parsed a 'cache'
+ // option. Server side has no control on size
+ // of cache but is informed at session nego-
+ // tiation about how much memory is going to
+ // be used.
+ //
+
+ SetStorage();
+
+ //
+ // Set size of shared memory segments.
+ //
+
+ SetShmem();
+
+ //
+ // Make adjustments to cache based
+ // on the pack method.
+ //
+
+ SetPack();
+
+ //
+ // Set disk-based image cache.
+ //
+
+ SetImages();
+
+ //
+ // Set CPU and bandwidth limits.
+ //
+
+ SetLimits();
+
+ return 1;
+}
+
+//
+// According to session literal determine
+// the type of traffic that is going to be
+// transported. Literals should be better
+// standardized in future NX versions.
+//
+
+int SetSession()
+{
+ if (strncmp(sessionType, "agent", strlen("agent")) == 0 ||
+ strncmp(sessionType, "desktop", strlen("desktop")) == 0 ||
+ strncmp(sessionType, "rootless", strlen("rootless")) == 0 ||
+ strncmp(sessionType, "console", strlen("console")) == 0 ||
+ strncmp(sessionType, "default", strlen("default")) == 0 ||
+ strncmp(sessionType, "gnome", strlen("gnome")) == 0 ||
+ strncmp(sessionType, "kde", strlen("kde")) == 0 ||
+ strncmp(sessionType, "cde", strlen("cde")) == 0 ||
+ strncmp(sessionType, "xdm", strlen("xdm")) == 0)
+ {
+ control -> SessionMode = session_agent;
+ }
+ else if (strncmp(sessionType, "win", strlen("win")) == 0 ||
+ strncmp(sessionType, "vnc", strlen("vnc")) == 0)
+ {
+ control -> SessionMode = session_agent;
+ }
+ else if (strncmp(sessionType, "shadow", strlen("shadow")) == 0)
+ {
+ control -> SessionMode = session_shadow;
+ }
+ else if (strncmp(sessionType, "proxy", strlen("proxy")) == 0 ||
+ strncmp(sessionType, "application", strlen("application")) == 0 ||
+ strncmp(sessionType, "raw", strlen("raw")) == 0)
+ {
+ control -> SessionMode = session_proxy;
+ }
+ else
+ {
+ //
+ // If the session type is not passed or
+ // it is not among the recognized strings,
+ // we assume that the proxy is connected
+ // to the agent.
+ //
+
+ if (*sessionType != '\0' &&
+ (control -> isProtoStep8() == 1 ||
+ strncmp(sessionType, "unix-", strlen("unix-")) != 0))
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Unrecognized session type '"
+ << sessionType << "'. Assuming agent session.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Unrecognized session type '"
+ << sessionType << "'. Assuming agent session.\n";
+ }
+
+ control -> SessionMode = session_agent;
+ }
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Assuming session type '"
+ << DumpSession(control -> SessionMode) << "' with "
+ << "string '" << sessionType << "'.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // By default the policy is immediate. Agents
+ // will set a different policy, if they like.
+ // Anyway we need to check if the user has
+ // provided a custom flush policy.
+ //
+
+ if (usePolicy != -1)
+ {
+ if (usePolicy > 0)
+ {
+ control -> FlushPolicy = policy_deferred;
+ }
+ else
+ {
+ control -> FlushPolicy = policy_immediate;
+ }
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: WARNING! Forcing flush policy to '"
+ << DumpPolicy(control -> FlushPolicy)
+ << ".\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ control -> FlushPolicy = policy_immediate;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Setting initial flush policy to '"
+ << DumpPolicy(control -> FlushPolicy)
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ //
+ // Check if the proxy library is run inside
+ // another program providing encryption, as
+ // it is the case of the SSH client.
+ //
+
+ if (useEncryption != -1)
+ {
+ if (useEncryption > 0)
+ {
+ control -> LinkEncrypted = 1;
+ }
+ else
+ {
+ control -> LinkEncrypted = 0;
+ }
+ }
+
+ if (control -> LinkEncrypted == 1)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Proxy running as part of an "
+ << "encrypting client.\n"
+ << logofs_flush;
+ #endif
+ }
+ else
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Assuming proxy running as a "
+ << "standalone program.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ //
+ // Check if the system administrator has
+ // enabled the respawn of the client at
+ // the end of session.
+ //
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ struct stat fileStat;
+
+ char fileName[DEFAULT_STRING_LENGTH];
+
+ snprintf(fileName, DEFAULT_STRING_LENGTH - 1,
+ "%s/share/noexit", control -> SystemPath);
+
+ *(fileName + DEFAULT_STRING_LENGTH - 1) = '\0';
+
+ if (stat(fileName, &fileStat) == 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Enabling respawn of client at session shutdown.\n"
+ << logofs_flush;
+ #endif
+
+ control -> EnableRestartOnShutdown = 1;
+ }
+ }
+
+ return 1;
+}
+
+int SetStorage()
+{
+ //
+ // If differential compression is disabled
+ // we don't need a cache at all.
+ //
+
+ if (control -> LocalDeltaCompression == 0)
+ {
+ control -> ClientTotalStorageSize = 0;
+ control -> ServerTotalStorageSize = 0;
+ }
+
+ //
+ // Set a a cache size literal.
+ //
+
+ int size = control -> getUpperStorageSize();
+
+ if (size / 1024 > 0)
+ {
+ sprintf(cacheSizeName, "%dk", size / 1024);
+ }
+ else
+ {
+ sprintf(cacheSizeName, "%d", size);
+ }
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ control -> LocalTotalStorageSize =
+ control -> ClientTotalStorageSize;
+
+ control -> RemoteTotalStorageSize =
+ control -> ServerTotalStorageSize;
+ }
+ else
+ {
+ control -> LocalTotalStorageSize =
+ control -> ServerTotalStorageSize;
+
+ control -> RemoteTotalStorageSize =
+ control -> ClientTotalStorageSize;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Loop: Storage size limit is "
+ << control -> ClientTotalStorageSize
+ << " at client and "
+ << control -> ServerTotalStorageSize
+ << " at server.\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef DEBUG
+ *logofs << "Loop: Storage local limit set to "
+ << control -> LocalTotalStorageSize
+ << " remote limit set to "
+ << control -> RemoteTotalStorageSize
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Never reserve for split store more than
+ // half the memory available for messages.
+ //
+
+ if (size > 0 && control ->
+ SplitTotalStorageSize > size / 2)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Reducing size of split store to "
+ << size / 2 << " bytes.\n"
+ << logofs_flush;
+ #endif
+
+ control -> SplitTotalStorageSize = size / 2;
+ }
+
+ //
+ // Don't load render from persistent
+ // cache if extension is hidden or
+ // not supported by agent.
+ //
+
+ if (control -> HideRender == 1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Not loading render extension "
+ << "from persistent cache.\n"
+ << logofs_flush;
+ #endif
+
+ control -> PersistentCacheLoadRender = 0;
+ }
+
+ return 1;
+}
+
+int SetShmem()
+{
+ //
+ // If not set, adjust the size of the shared
+ // memory segment according to size of the
+ // message cache.
+ //
+
+ if (*shsegSizeName == '\0')
+ {
+ int size = control -> getUpperStorageSize();
+
+ const int mega = 1048576;
+
+ if (size > 0)
+ {
+ if (size <= 1 * mega)
+ {
+ size = 0;
+ }
+ else if (size <= 2 * mega)
+ {
+ size = 524288;
+ }
+ else if (size < 4 * mega)
+ {
+ size = 1048576;
+ }
+ else
+ {
+ size = size / 4;
+ }
+
+ if (size > 4194304)
+ {
+ size = 4194304;
+ }
+
+ control -> ShmemClientSize = size;
+ control -> ShmemServerSize = size;
+ }
+ else
+ {
+ //
+ // The delta compression is disabled.
+ // Use a default segment size of 2 MB.
+ //
+
+ control -> ShmemServerSize = 2 * mega;
+ }
+ }
+
+ //
+ // Client side shared memory support is
+ // not useful and not implemented.
+ //
+
+ if (control -> ShmemServerSize >= 524288)
+ {
+ control -> ShmemServer = 1;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Set initial shared memory size "
+ << "to " << control -> ShmemServerSize
+ << " bytes.\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Disabled use of the shared memory "
+ << "extension.\n" << logofs_flush;
+ #endif
+
+ control -> ShmemServer = 0;
+ }
+
+ return 1;
+}
+
+//
+// Adjust the pack method according to the
+// type of the session.
+//
+
+int SetPack()
+{
+ #ifdef TEST
+ *logofs << "Loop: Setting pack with initial method "
+ << packMethod << " and quality " << packQuality
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Check if this is a proxy session and, in
+ // this case, set the pack method to none.
+ // Packed images are not supported by plain
+ // X applications.
+ //
+
+ if (control -> SessionMode == session_proxy)
+ {
+ #ifdef TEST
+ *logofs << "Loop: WARNING! Disabling pack with proxy session.\n"
+ << logofs_flush;
+ #endif
+
+ packMethod = PACK_NONE;
+ }
+
+ //
+ // Adjust the internal settings according
+ // to the newly selected pack method.
+ //
+
+ ParsePackMethod(packMethod, packQuality);
+
+ //
+ // Don't load messages from persistent
+ // cache if packed images are disabled.
+ //
+
+ if (control -> PackMethod == PACK_NONE)
+ {
+ control -> PersistentCacheLoadPacked = 0;
+
+ #ifdef TEST
+ *logofs << "Loop: Not loading packed images "
+ << "from persistent cache.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ return 1;
+}
+
+//
+// Set the disk-based image cache parameters
+// according to the user's wishes.
+//
+
+int SetImages()
+{
+ //
+ // Be sure we disable the image cache if we
+ // are connecting to plain X clients.
+ //
+
+ if (control -> SessionMode == session_proxy)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Disabling image cache with "
+ << "session '" << DumpSession(control ->
+ SessionMode) << "'.\n" << logofs_flush;
+ #endif
+
+ sprintf(imagesSizeName, "0");
+
+ control -> ImageCacheEnableLoad = 0;
+ control -> ImageCacheEnableSave = 0;
+
+ return 1;
+ }
+
+ int size = control -> ImageCacheDiskLimit;
+
+ if (size / 1024 > 0)
+ {
+ sprintf(imagesSizeName, "%dk", size / 1024);
+ }
+ else
+ {
+ sprintf(imagesSizeName, "%d", size);
+ }
+
+ if (size > 0)
+ {
+ control -> ImageCacheEnableLoad = 1;
+ control -> ImageCacheEnableSave = 1;
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ if ((control -> ImageCachePath = GetImagesPath()) == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Error getting or creating image cache path.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error getting or creating image cache path.\n";
+
+ HandleCleanup();
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Path of image cache files is '" << control -> ImageCachePath
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Loop: Disabling the persistent image cache.\n"
+ << logofs_flush;
+ #endif
+
+ control -> ImageCacheEnableLoad = 0;
+ control -> ImageCacheEnableSave = 0;
+ }
+
+ return 1;
+}
+
+int SetVersion()
+{
+ //
+ // Normalize the different proxy versions.
+ //
+
+ int local = (control -> LocalVersionMajor << 24) |
+ (control -> LocalVersionMinor << 16) |
+ control -> LocalVersionPatch;
+
+ int remote = (control -> RemoteVersionMajor << 24) |
+ (control -> RemoteVersionMinor << 16) |
+ control -> RemoteVersionPatch;
+
+ int major = -1;
+ int minor = -1;
+ int patch = -1;
+
+ if (control -> RemoteVersionMajor <= 1)
+ {
+ //
+ // The remote proxy uses a different
+ // logic to determine the version so
+ // we default to the compatibility
+ // version.
+ //
+
+ major = control -> CompatVersionMajor;
+ minor = control -> CompatVersionMinor;
+ patch = control -> CompatVersionPatch;
+
+ #ifdef TEST
+ *logofs << "Loop: Using compatibility version '"
+ << major << "." << minor << "." << patch
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+ else if (control -> LocalVersionMajor >
+ control -> RemoteVersionMajor)
+ {
+ //
+ // We use a more recent version. Let's
+ // negotiate the version based on the
+ // version supported by the remote.
+ //
+
+ major = control -> RemoteVersionMajor;
+ minor = control -> RemoteVersionMinor;
+ patch = control -> RemoteVersionPatch;
+
+ #ifdef TEST
+ *logofs << "Loop: Using remote version '"
+ << major << "." << minor << "." << patch
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ //
+ // We support a major version that is
+ // equal or older than the remote. We
+ // assume the smaller version between
+ // the two, including the minor and
+ // the patch numbers.
+ //
+
+ if (local > remote)
+ {
+ major = control -> RemoteVersionMajor;
+ minor = control -> RemoteVersionMinor;
+ patch = control -> RemoteVersionPatch;
+
+ #ifdef TEST
+ *logofs << "Loop: Using remote version '"
+ << major << "." << minor << "." << patch
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ major = control -> LocalVersionMajor;
+ minor = control -> LocalVersionMinor;
+ patch = control -> LocalVersionPatch;
+
+ #ifdef TEST
+ *logofs << "Loop: Using local version '"
+ << major << "." << minor << "." << patch
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+ }
+
+ //
+ // Handle the 1.5.0 versions. The protocol
+ // step 6 is the minimum supported version.
+ //
+
+ int step = 0;
+
+ if (major == 1)
+ {
+ if (minor == 5)
+ {
+ step = 6;
+ }
+ }
+ else if (major == 2)
+ {
+ step = 7;
+ }
+ else if (major == 3)
+ {
+ if (minor >= 2)
+ {
+ step = 10;
+ }
+ else if (minor > 0 || patch > 0)
+ {
+ step = 9;
+ }
+ else
+ {
+ step = 8;
+ }
+ }
+ else if (major > 3)
+ {
+ step = 10;
+ }
+
+ if (step == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Incompatible remote version "
+ << control -> RemoteVersionMajor << "." << control -> RemoteVersionMinor
+ << "." << control -> RemoteVersionPatch << " with local version "
+ << control -> LocalVersionMajor << "." << control -> LocalVersionMinor
+ << "." << control -> LocalVersionPatch << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Incompatible remote version "
+ << control -> RemoteVersionMajor << "." << control -> RemoteVersionMinor
+ << "." << control -> RemoteVersionPatch << " with local version "
+ << control -> LocalVersionMajor << "." << control -> LocalVersionMinor
+ << "." << control -> LocalVersionPatch << ".\n";
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Using NX protocol step "
+ << step << ".\n" << logofs_flush;
+ #endif
+
+ control -> setProtoStep(step);
+
+ //
+ // Ignore the differences in patch version
+ // and print a warning if the local version
+ // is different or obsolete compared to
+ // the remote.
+ //
+
+ local &= 0xffff0000;
+ remote &= 0xffff0000;
+
+ if (local != remote)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Connected to remote version "
+ << control -> RemoteVersionMajor << "." << control -> RemoteVersionMinor
+ << "." << control -> RemoteVersionPatch << " with local version "
+ << control -> LocalVersionMajor << "." << control -> LocalVersionMinor
+ << "." << control -> LocalVersionPatch << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Connected to remote version "
+ << control -> RemoteVersionMajor << "." << control -> RemoteVersionMinor
+ << "." << control -> RemoteVersionPatch << " with local version "
+ << control -> LocalVersionMajor << "." << control -> LocalVersionMinor
+ << "." << control -> LocalVersionPatch << ".\n" << logofs_flush;
+ }
+
+ if (local < remote)
+ {
+ cerr << "Warning" << ": Consider checking http://www.nomachine.com/ for updates.\n";
+ }
+
+ //
+ // Now that we are aware of the remote
+ // version, let's adjust the options to
+ // be compatible with the remote proxy.
+ //
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ if (control -> isProtoStep8() == 0)
+ {
+ if (strncmp(sessionType, "shadow", strlen("shadow")) == 0 ||
+ strncmp(sessionType, "application", strlen("application")) == 0 ||
+ strncmp(sessionType, "console", strlen("console")) == 0 ||
+ strncmp(sessionType, "default", strlen("default")) == 0 ||
+ strncmp(sessionType, "gnome", strlen("gnome")) == 0 ||
+ strncmp(sessionType, "kde", strlen("kde")) == 0 ||
+ strncmp(sessionType, "cde", strlen("cde")) == 0 ||
+ strncmp(sessionType, "xdm", strlen("xdm")) == 0)
+
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: WARNING! Prepending 'unix-' to the "
+ << "name of the session.\n" << logofs_flush;
+ #endif
+
+ char buffer[DEFAULT_STRING_LENGTH];
+
+ snprintf(buffer, DEFAULT_STRING_LENGTH - 1, "unix-%s", sessionType);
+
+ strcpy(sessionType, buffer);
+ }
+ }
+
+ //
+ // Check if the remote is able to handle
+ // the selected pack method.
+ //
+
+ if (control -> isProtoStep8() == 0)
+ {
+ if (packMethod == PACK_ADAPTIVE || packMethod == PACK_LOSSY)
+ {
+ #ifdef TEST
+ *logofs << "Loop: WARNING! Assuming a lossy encoding with "
+ << "an old proxy version.\n" << logofs_flush;
+ #endif
+
+ packMethod = PACK_JPEG_16M_COLORS;
+ }
+ else if (packMethod == PACK_LOSSLESS)
+ {
+ #ifdef TEST
+ *logofs << "Loop: WARNING! Assuming a lossless encoding with "
+ << "an old proxy version.\n" << logofs_flush;
+ #endif
+
+ if (control -> isProtoStep7() == 1)
+ {
+ packMethod = PACK_RLE_16M_COLORS;
+ }
+ else
+ {
+ packMethod = PACK_PNG_16M_COLORS;
+ }
+ }
+ }
+
+ //
+ // If the remote doesn't support the
+ // selected method use something that
+ // is compatible.
+ //
+
+ if ((packMethod == PACK_RGB_16M_COLORS ||
+ packMethod == PACK_RLE_16M_COLORS ||
+ packMethod == PACK_BITMAP_16M_COLORS) &&
+ control -> isProtoStep7() == 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: WARNING! Setting the pack method to '"
+ << PACK_PNG_16M_COLORS << "' with '" << packMethod
+ << "' unsupported.\n" << logofs_flush;
+ #endif
+
+ packMethod = PACK_PNG_16M_COLORS;
+ packQuality = 9;
+ }
+ else if (packMethod == PACK_BITMAP_16M_COLORS &&
+ control -> isProtoStep8() == 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: WARNING! Setting the pack method to '"
+ << PACK_RLE_16M_COLORS << "' with '" << packMethod
+ << "' unsupported.\n" << logofs_flush;
+ #endif
+
+ packMethod = PACK_RLE_16M_COLORS;
+ packQuality = 9;
+ }
+
+ //
+ // Update the pack method name.
+ //
+
+ ParsePackMethod(packMethod, packQuality);
+ }
+
+ //
+ // At the moment the image cache is not used by the
+ // agent but we need to take care of the compatibi-
+ // lity with old versions. Proxy versions older than
+ // the 3.0.0 assume that it is enabled and will send
+ // specific bits as part of the encoding. Conversely,
+ // it is advisable to disable the cache right now.
+ // By not enabling the image cache, the house-keep-
+ // ing process will only take care of cleaning up
+ // the "cache-" directories.
+ //
+
+ if (control -> isProtoStep8() == 1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Disabling image cache with protocol "
+ << "step '" << control -> getProtoStep()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ sprintf(imagesSizeName, "0");
+
+ control -> ImageCacheEnableLoad = 0;
+ control -> ImageCacheEnableSave = 0;
+ }
+
+ return 1;
+}
+
+//
+// Identify the requested link settings
+// and update the control parameters
+// accordingly.
+//
+
+int SetLink()
+{
+ #ifdef TEST
+ *logofs << "Loop: Setting link with initial value "
+ << linkSpeedName << ".\n" << logofs_flush;
+ #endif
+
+ if (*linkSpeedName == '\0')
+ {
+ strcpy(linkSpeedName, "lan");
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Link speed is " << linkSpeedName
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (strcasecmp(linkSpeedName, "modem") == 0)
+ {
+ SetLinkModem();
+ }
+ else if (strcasecmp(linkSpeedName, "isdn") == 0)
+ {
+ SetLinkIsdn();
+ }
+ else if (strcasecmp(linkSpeedName, "adsl") == 0)
+ {
+ SetLinkAdsl();
+ }
+ else if (strcasecmp(linkSpeedName, "wan") == 0)
+ {
+ SetLinkWan();
+ }
+ else if (strcasecmp(linkSpeedName, "lan") == 0)
+ {
+ SetLinkLan();
+ }
+ else
+ {
+ return -1;
+ }
+
+ //
+ // Set TCP_NODELAY according to the user's
+ // wishes.
+ //
+
+ if (useNoDelay != -1)
+ {
+ control -> OptionProxyClientNoDelay = useNoDelay;
+ control -> OptionProxyServerNoDelay = useNoDelay;
+ }
+
+ //
+ // Select the image compression method.
+ //
+
+ if (packMethod == -1)
+ {
+ packMethod = control -> PackMethod;
+ }
+
+ if (packQuality == -1)
+ {
+ packQuality = control -> PackQuality;
+ }
+
+ if (ParsePackMethod(packMethod, packQuality) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Unrecognized pack method id "
+ << packMethod << " with quality " << packQuality
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Unrecognized pack method id "
+ << packMethod << " with quality " << packQuality
+ << ".\n";
+
+ HandleCleanup();
+ }
+
+ //
+ // Check if the user disabled the ability
+ // to generate simple replies at the client
+ // side.
+ //
+
+ if (control -> SessionMode == session_proxy)
+ {
+ if (useTaint != -1)
+ {
+ control -> TaintReplies = (useTaint == 1);
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Forcing taint of replies "
+ << "with a proxy session.\n"
+ << logofs_flush;
+ #endif
+
+ control -> TaintReplies = 1;
+ }
+ }
+ else
+ {
+ //
+ // There is no need to taint the
+ // replies if we have an agent.
+ //
+
+ control -> TaintReplies = 0;
+ }
+
+ //
+ // Be sure that the requests needing a reply
+ // are flushed immediately. Normal X clients
+ // use so many replies to make the queuing
+ // completely useless.
+ //
+
+ if (control -> SessionMode == session_proxy)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Forcing flush on priority "
+ << "with a proxy session.\n"
+ << logofs_flush;
+ #endif
+
+ control -> FlushPriority = 1;
+ }
+
+ return 1;
+}
+
+//
+// Parameters for MODEM 28.8/33.6/56 Kbps.
+//
+
+int SetLinkModem()
+{
+ #ifdef TEST
+ *logofs << "Loop: Setting parameters for MODEM.\n"
+ << logofs_flush;
+ #endif
+
+ control -> LinkMode = LINK_TYPE_MODEM;
+
+ control -> TokenSize = 256;
+ control -> TokenLimit = 24;
+
+ control -> SplitMode = 1;
+ control -> SplitTotalSize = 128;
+ control -> SplitTotalStorageSize = 1048576;
+
+ control -> SplitTimeout = 50;
+ control -> MotionTimeout = 50;
+ control -> IdleTimeout = 50;
+
+ control -> PackMethod = PACK_ADAPTIVE;
+ control -> PackQuality = 3;
+
+ return 1;
+}
+
+//
+// Parameters for ISDN 64/128 Kbps.
+//
+
+int SetLinkIsdn()
+{
+ #ifdef TEST
+ *logofs << "Loop: Setting parameters for ISDN.\n"
+ << logofs_flush;
+ #endif
+
+ control -> LinkMode = LINK_TYPE_ISDN;
+
+ control -> TokenSize = 384;
+ control -> TokenLimit = 24;
+
+ control -> SplitMode = 1;
+ control -> SplitTotalSize = 128;
+ control -> SplitTotalStorageSize = 1048576;
+
+ control -> SplitTimeout = 50;
+ control -> MotionTimeout = 20;
+ control -> IdleTimeout = 50;
+
+ control -> PackMethod = PACK_ADAPTIVE;
+ control -> PackQuality = 5;
+
+ return 1;
+}
+
+//
+// Parameters for ADSL 256 Kbps.
+//
+
+int SetLinkAdsl()
+{
+ #ifdef TEST
+ *logofs << "Loop: Setting parameters for ADSL.\n"
+ << logofs_flush;
+ #endif
+
+ control -> LinkMode = LINK_TYPE_ADSL;
+
+ control -> TokenSize = 512;
+ control -> TokenLimit = 24;
+
+ control -> SplitMode = 1;
+ control -> SplitTotalSize = 128;
+ control -> SplitTotalStorageSize = 1048576;
+
+ control -> SplitTimeout = 50;
+ control -> MotionTimeout = 10;
+ control -> IdleTimeout = 50;
+
+ control -> PackMethod = PACK_ADAPTIVE;
+ control -> PackQuality = 7;
+
+ return 1;
+}
+
+//
+// Parameters for XDSL/FDDI/ATM 1/2/34 Mbps WAN.
+//
+
+int SetLinkWan()
+{
+ #ifdef TEST
+ *logofs << "Loop: Setting parameters for WAN.\n"
+ << logofs_flush;
+ #endif
+
+ control -> LinkMode = LINK_TYPE_WAN;
+
+ control -> TokenSize = 768;
+ control -> TokenLimit = 24;
+
+ control -> SplitMode = 1;
+ control -> SplitTotalSize = 128;
+ control -> SplitTotalStorageSize = 1048576;
+
+ control -> SplitTimeout = 50;
+ control -> MotionTimeout = 5;
+ control -> IdleTimeout = 50;
+
+ control -> PackMethod = PACK_ADAPTIVE;
+ control -> PackQuality = 9;
+
+ return 1;
+}
+
+//
+// Parameters for LAN 10/100 Mbps.
+//
+
+int SetLinkLan()
+{
+ #ifdef TEST
+ *logofs << "Loop: Setting parameters for LAN.\n"
+ << logofs_flush;
+ #endif
+
+ control -> LinkMode = LINK_TYPE_LAN;
+
+ control -> TokenSize = 1536;
+ control -> TokenLimit = 24;
+
+ control -> SplitMode = 1;
+ control -> SplitTotalSize = 128;
+ control -> SplitTotalStorageSize = 1048576;
+
+ control -> SplitTimeout = 50;
+ control -> MotionTimeout = 0;
+ control -> IdleTimeout = 50;
+
+ control -> PackMethod = PACK_ADAPTIVE;
+ control -> PackQuality = 9;
+
+ return 1;
+}
+
+//
+// Identify the requested link type and set
+// the control parameters accordingly.
+//
+
+int SetCompression()
+{
+ if (strcasecmp(linkSpeedName, "modem") == 0)
+ {
+ SetCompressionModem();
+ }
+ else if (strcasecmp(linkSpeedName, "isdn") == 0)
+ {
+ SetCompressionIsdn();
+ }
+ else if (strcasecmp(linkSpeedName, "adsl") == 0)
+ {
+ SetCompressionAdsl();
+ }
+ else if (strcasecmp(linkSpeedName, "wan") == 0)
+ {
+ SetCompressionWan();
+ }
+ else if (strcasecmp(linkSpeedName, "lan") == 0)
+ {
+ SetCompressionLan();
+ }
+ else
+ {
+ return -1;
+ }
+
+ if (control -> LocalDeltaCompression < 0)
+ {
+ control -> LocalDeltaCompression = 1;
+ }
+
+ //
+ // If we didn't set remote delta compression
+ // (as it should always be the case at client
+ // side) assume value of local side.
+ //
+
+ if (control -> RemoteDeltaCompression < 0)
+ {
+ control -> RemoteDeltaCompression =
+ control -> LocalDeltaCompression;
+ }
+
+ //
+ // If we didn't set remote compression levels
+ // assume values of local side.
+ //
+
+ if (control -> RemoteStreamCompression < 0)
+ {
+ control -> RemoteStreamCompressionLevel =
+ control -> LocalStreamCompressionLevel;
+
+ if (control -> RemoteStreamCompressionLevel > 0)
+ {
+ control -> RemoteStreamCompression = 1;
+ }
+ else
+ {
+ control -> RemoteStreamCompression = 0;
+ }
+ }
+
+ if (control -> RemoteDataCompression < 0)
+ {
+ control -> RemoteDataCompressionLevel =
+ control -> LocalDataCompressionLevel;
+
+ if (control -> RemoteDataCompressionLevel > 0)
+ {
+ control -> RemoteDataCompression = 1;
+ }
+ else
+ {
+ control -> RemoteDataCompression = 0;
+ }
+ }
+
+ return 1;
+}
+
+//
+// Compression for MODEM.
+//
+
+int SetCompressionModem()
+{
+ if (control -> LocalDataCompression < 0)
+ {
+ control -> LocalDataCompression = 1;
+ control -> LocalDataCompressionLevel = 1;
+ }
+
+ if (control -> LocalDataCompressionThreshold < 0)
+ {
+ control -> LocalDataCompressionThreshold = 32;
+ }
+
+ if (control -> LocalStreamCompression < 0)
+ {
+ control -> LocalStreamCompression = 1;
+ control -> LocalStreamCompressionLevel = 9;
+ }
+
+ return 1;
+}
+
+//
+// Compression for ISDN.
+//
+
+int SetCompressionIsdn()
+{
+ if (control -> LocalDataCompression < 0)
+ {
+ control -> LocalDataCompression = 1;
+ control -> LocalDataCompressionLevel = 1;
+ }
+
+ if (control -> LocalDataCompressionThreshold < 0)
+ {
+ control -> LocalDataCompressionThreshold = 32;
+ }
+
+ if (control -> LocalStreamCompression < 0)
+ {
+ control -> LocalStreamCompression = 1;
+ control -> LocalStreamCompressionLevel = 6;
+ }
+
+ return 1;
+}
+
+//
+// Compression for ADSL.
+//
+
+int SetCompressionAdsl()
+{
+ if (control -> LocalDataCompression < 0)
+ {
+ control -> LocalDataCompression = 1;
+ control -> LocalDataCompressionLevel = 1;
+ }
+
+ if (control -> LocalDataCompressionThreshold < 0)
+ {
+ control -> LocalDataCompressionThreshold = 32;
+ }
+
+ if (control -> LocalStreamCompression < 0)
+ {
+ control -> LocalStreamCompression = 1;
+ control -> LocalStreamCompressionLevel = 4;
+ }
+
+ return 1;
+}
+
+//
+// Compression for WAN.
+//
+
+int SetCompressionWan()
+{
+ if (control -> LocalDataCompression < 0)
+ {
+ control -> LocalDataCompression = 1;
+ control -> LocalDataCompressionLevel = 1;
+ }
+
+ if (control -> LocalDataCompressionThreshold < 0)
+ {
+ control -> LocalDataCompressionThreshold = 32;
+ }
+
+ if (control -> LocalStreamCompression < 0)
+ {
+ control -> LocalStreamCompression = 1;
+ control -> LocalStreamCompressionLevel = 1;
+ }
+
+ return 1;
+}
+
+//
+// Compression for LAN.
+//
+
+int SetCompressionLan()
+{
+ //
+ // Disable delta compression if not
+ // explicitly enabled.
+ //
+
+ if (control -> LocalDeltaCompression < 0)
+ {
+ control -> LocalDeltaCompression = 0;
+ }
+
+ if (control -> LocalDataCompression < 0)
+ {
+ control -> LocalDataCompression = 0;
+ control -> LocalDataCompressionLevel = 0;
+ }
+
+ if (control -> LocalDataCompressionThreshold < 0)
+ {
+ control -> LocalDataCompressionThreshold = 0;
+ }
+
+ if (control -> LocalStreamCompression < 0)
+ {
+ control -> LocalStreamCompression = 0;
+ control -> LocalStreamCompressionLevel = 0;
+ }
+
+ return 1;
+}
+
+int SetLimits()
+{
+ //
+ // Check if the user requested strict
+ // control flow parameters.
+ //
+
+ if (useStrict == 1)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: LIMIT! Decreasing the token limit "
+ << "to " << control -> TokenLimit / 2
+ << " with option 'strict'.\n"
+ << logofs_flush;
+ #endif
+
+ control -> TokenLimit /= 2;
+ }
+
+ #ifdef STRICT
+
+ control -> TokenLimit = 1;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: WARNING! LIMIT! Setting the token limit "
+ << "to " << control -> TokenLimit
+ << " to simulate the proxy congestion.\n"
+ << logofs_flush;
+ #endif
+
+ #endif
+
+ //
+ // Reduce the size of the log file.
+ //
+
+ #ifdef QUOTA
+
+ control -> FileSizeLimit = 8388608;
+
+ #endif
+
+ //
+ // Check the bitrate limits.
+ //
+
+ if (control -> LocalBitrateLimit == -1)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ control -> LocalBitrateLimit =
+ control -> ClientBitrateLimit;
+ }
+ else
+ {
+ control -> LocalBitrateLimit =
+ control -> ServerBitrateLimit;
+ }
+ }
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: LIMIT! Setting client bitrate limit "
+ << "to " << control -> ClientBitrateLimit
+ << " server bitrate limit to " << control ->
+ ServerBitrateLimit << " with local limit "
+ << control -> LocalBitrateLimit << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+//
+// These functions are used to parse literal
+// values provided by the user and set the
+// control parameters accordingly.
+//
+
+int ParseCacheOption(const char *opt)
+{
+ int size = ParseArg("", "cache", opt);
+
+ if (size < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid value '"
+ << opt << "' for option 'cache'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid value '"
+ << opt << "' for option 'cache'.\n";
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Setting size of cache to "
+ << size << " bytes.\n" << logofs_flush;
+ #endif
+
+ control -> ClientTotalStorageSize = size;
+ control -> ServerTotalStorageSize = size;
+
+ strcpy(cacheSizeName, opt);
+
+ if (size == 0)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Disabling NX delta compression.\n"
+ << logofs_flush;
+ #endif
+
+ control -> LocalDeltaCompression = 0;
+
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Disabling use of NX persistent cache.\n"
+ << logofs_flush;
+ #endif
+
+ control -> PersistentCacheEnableLoad = 0;
+ control -> PersistentCacheEnableSave = 0;
+ }
+
+ return 1;
+}
+
+int ParseImagesOption(const char *opt)
+{
+ int size = ParseArg("", "images", opt);
+
+ if (size < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid value '"
+ << opt << "' for option 'images'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid value '"
+ << opt << "' for option 'images'.\n";
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Setting size of images cache to "
+ << size << " bytes.\n" << logofs_flush;
+ #endif
+
+ control -> ImageCacheDiskLimit = size;
+
+ strcpy(imagesSizeName, opt);
+
+ return 1;
+}
+
+int ParseShmemOption(const char *opt)
+{
+ int size = ParseArg("", "shseg", opt);
+
+ if (size < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid value '"
+ << opt << "' for option 'shseg'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid value '"
+ << opt << "' for option 'shseg'.\n";
+
+ return -1;
+ }
+
+ control -> ShmemClientSize = size;
+ control -> ShmemServerSize = size;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Set shared memory size to "
+ << control -> ShmemServerSize << " bytes.\n"
+ << logofs_flush;
+ #endif
+
+ strcpy(shsegSizeName, opt);
+
+ return 1;
+}
+
+int ParseBitrateOption(const char *opt)
+{
+ int bitrate = ParseArg("", "limit", opt);
+
+ if (bitrate < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid value '"
+ << opt << "' for option 'limit'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid value '"
+ << opt << "' for option 'limit'.\n";
+
+ return -1;
+ }
+
+ strcpy(bitrateLimitName, opt);
+
+ if (bitrate == 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Disabling bitrate limit on proxy link.\n"
+ << logofs_flush;
+ #endif
+
+ control -> LocalBitrateLimit = 0;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Loop: Setting bitrate to " << bitrate
+ << " bits per second.\n" << logofs_flush;
+ #endif
+
+ //
+ // Internal representation is in bytes
+ // per second.
+ //
+
+ control -> LocalBitrateLimit = bitrate >> 3;
+ }
+
+ return 1;
+}
+
+int ParseHostOption(const char *opt, char *host, int &port)
+{
+ #ifdef TEST
+ *logofs << "Loop: Trying to parse options string '" << opt
+ << "' as a remote NX host.\n" << logofs_flush;
+ #endif
+
+ if (opt == NULL || *opt == '\0')
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! No host parameter provided.\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else if (strlen(opt) >= DEFAULT_STRING_LENGTH)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Host parameter exceeds length of "
+ << DEFAULT_STRING_LENGTH << " characters.\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ //
+ // Look for a host name followed
+ // by a colon followed by port.
+ //
+
+ int newPort = port;
+
+ const char *separator = rindex(opt, ':');
+
+ if (separator != NULL)
+ {
+ const char *check = separator + 1;
+
+ while (*check != '\0' && *check != ',' &&
+ *check != '=' && isdigit(*check) != 0)
+ {
+ check++;
+ }
+
+ newPort = atoi(separator + 1);
+
+ if (newPort < 0 || *check != '\0')
+ {
+ #ifdef TEST
+ *logofs << "Loop: Can't identify remote NX port in string '"
+ << separator << "'.\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ }
+ else if (newPort < 0)
+ {
+ //
+ // Complain if port was not passed
+ // by other means.
+ //
+
+ #ifdef TEST
+ *logofs << "Loop: Can't identify remote NX port in string '"
+ << opt << "'.\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else
+ {
+ separator = opt + strlen(opt);
+ }
+
+ char newHost[DEFAULT_STRING_LENGTH] = { 0 };
+
+ strncpy(newHost, opt, strlen(opt) - strlen(separator));
+
+ *(newHost + strlen(opt) - strlen(separator)) = '\0';
+
+ const char *check = newHost;
+
+ while (*check != '\0' && *check != ',' &&
+ *check != '=')
+ {
+ check++;
+ }
+
+ if (*check != '\0')
+ {
+ #ifdef TEST
+ *logofs << "Loop: Can't identify remote NX host in string '"
+ << newHost << "'.\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else if (*acceptHost != '\0')
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't manage to connect and accept connections "
+ << "at the same time.\n" << logofs_flush;
+
+ *logofs << "Loop: PANIC! Refusing remote NX host with string '"
+ << opt << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't manage to connect and accept connections "
+ << "at the same time.\n";
+
+ cerr << "Error" << ": Refusing remote NX host with string '"
+ << opt << "'.\n";
+
+ return -1;
+ }
+
+ if (*host != '\0' && strcmp(host, newHost) != 0)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Overriding remote NX host '"
+ << host << "' with new value '" << newHost
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ strcpy(host, newHost);
+
+ if (port != -1 && port != newPort)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Overriding remote NX port '"
+ << port << "' with new value '" << newPort
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Parsed options string '" << opt
+ << "' with host '" << newHost << "' and port '"
+ << newPort << "'.\n" << logofs_flush;
+ #endif
+
+ port = newPort;
+
+ return 1;
+}
+
+int ParseFontPath(char *path)
+{
+ char oldPath[DEFAULT_STRING_LENGTH];
+
+ strcpy(oldPath, path);
+
+ if (path == NULL || *path == '\0' || strcmp(path, "0") == 0)
+ {
+ return 0;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Parsing font server option '" << path
+ << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // Convert the value to our default port.
+ //
+
+ if (strcmp(fontPort, "1") == 0)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ snprintf(fontPort, DEFAULT_STRING_LENGTH - 1, "%d",
+ DEFAULT_NX_FONT_PORT_OFFSET + proxyPort);
+ }
+ else
+ {
+ //
+ // Let the client use the well-known
+ // "unix/:7100" font path.
+ //
+
+ snprintf(fontPort, DEFAULT_STRING_LENGTH - 1, "unix/:7100");
+ }
+ }
+
+ //
+ // Check if a simple numaric value was given.
+ //
+
+ if (atoi(path) > 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Assuming numeric TCP port '" << atoi(path)
+ << "' for font server.\n" << logofs_flush;
+ #endif
+
+ return 1;
+ }
+
+ //
+ // Let's assume that a port specification "unix/:7100"
+ // corresponds to "$TEMP/.font-unix/fs7100" and a port
+ // "unix/:-1" corresponds to "$TEMP/.font-unix/fs-1".
+ //
+
+ if (strncmp("unix/:", path, 6) == 0)
+ {
+ snprintf(path, DEFAULT_STRING_LENGTH - 1, "%s/.font-unix/fs%s",
+ control -> TempPath, oldPath + 6);
+
+ *(path + DEFAULT_STRING_LENGTH - 1) = '\0';
+
+ #ifdef TEST
+ *logofs << "Loop: Assuming Unix socket '" << path
+ << "' for font server.\n" << logofs_flush;
+ #endif
+ }
+ else if (strncmp("tcp/:", path, 5) == 0)
+ {
+ snprintf(path, DEFAULT_STRING_LENGTH - 1, "%d", atoi(oldPath + 5));
+
+ *(path + DEFAULT_STRING_LENGTH - 1) = '\0';
+
+ if (atoi(path) <= 0)
+ {
+ goto ParseFontPathError;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Assuming TCP port '" << atoi(path)
+ << "' for font server.\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ //
+ // Accept an absolute file path as
+ // a valid Unix socket.
+ //
+
+ if (*path != '/')
+ {
+ goto ParseFontPathError;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Assuming Unix socket '" << path
+ << "' for font server.\n" << logofs_flush;
+ #endif
+ }
+
+ return 1;
+
+ParseFontPathError:
+
+ #ifdef TEST
+ *logofs << "Loop: Unable to determine the font server "
+ << "port in string '" << path << "'.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+}
+
+int ParseListenOption(int &address)
+{
+ if (*listenHost == '\0')
+ {
+ //
+ // On the X client side listen on any address.
+ // On the X server side listen to the forwarder
+ // on localhost.
+ //
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ address = (int) inet_addr("127.0.0.1");
+ }
+ else
+ {
+ address = htonl(INADDR_ANY);
+ }
+ }
+ else
+ {
+ address = inet_addr(listenHost);
+ }
+
+ return 1;
+}
+
+int OpenLogFile(char *name, ostream *&stream)
+{
+ if (name == NULL || *name == '\0')
+ {
+ #ifdef TEST
+ *logofs << "Loop: WARNING! No name provided for output. Using standard error.\n"
+ << logofs_flush;
+ #endif
+
+ if (stream == NULL)
+ {
+ stream = &cerr;
+ }
+
+ return 1;
+ }
+
+ if (stream == NULL || stream == &cerr)
+ {
+ if (*name != '/' && *name != '.')
+ {
+ char *filePath = GetSessionPath();
+
+ if (filePath == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Cannot determine directory of NX session file.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot determine directory of NX session file.\n";
+
+ return -1;
+ }
+
+ if (strlen(filePath) + strlen("/") +
+ strlen(name) + 1 > DEFAULT_STRING_LENGTH)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Full name of NX file '" << name
+ << " would exceed length of " << DEFAULT_STRING_LENGTH
+ << " characters.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Full name of NX file '" << name
+ << " would exceed length of " << DEFAULT_STRING_LENGTH
+ << " characters.\n";
+
+ return -1;
+ }
+
+ char *file = new char[strlen(filePath) + strlen("/") +
+ strlen(name) + 1];
+
+ //
+ // Transform name in a fully qualified name.
+ //
+
+ strcpy(file, filePath);
+ strcat(file, "/");
+ strcat(file, name);
+
+ strcpy(name, file);
+
+ delete [] filePath;
+ delete [] file;
+ }
+
+ mode_t fileMode = umask(0077);
+
+ for (;;)
+ {
+ if ((stream = new ofstream(name, ios::app)) != NULL)
+ {
+ break;
+ }
+
+ usleep(200000);
+ }
+
+ umask(fileMode);
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Bad stream provided for output.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Bad stream provided for output.\n";
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int ReopenLogFile(char *name, ostream *&stream, int limit)
+{
+ if (*name != '\0' && limit >= 0)
+ {
+ struct stat fileStat;
+
+ if (limit > 0)
+ {
+ //
+ // This is used for the log file, if the
+ // size exceeds the limit.
+ //
+
+ if (stat(name, &fileStat) != 0)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Can't get stats of file '"
+ << name << "'. Error is " << EGET()
+ << " '" << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else if (fileStat.st_size < (long) limit)
+ {
+ return 0;
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Deleting file '" << name
+ << "' with size " << fileStat.st_size
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Create a new stream over the previous
+ // file. Trying to delete the file fails
+ // to work on recent Cygwin installs.
+ //
+
+ *stream << flush;
+
+ delete stream;
+
+ mode_t fileMode = umask(0077);
+
+ for (;;)
+ {
+ if ((stream = new ofstream(name, ios::out)) != NULL)
+ {
+ break;
+ }
+
+ usleep(200000);
+ }
+
+ umask(fileMode);
+
+ #ifdef TEST
+ *logofs << "Loop: Reopened file '" << name
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ return 1;
+}
+
+void PrintProcessInfo()
+{
+ if (agent == NULL)
+ {
+ cerr << "\nNXPROXY - Version " << control -> LocalVersionMajor
+ << "." << control -> LocalVersionMinor << "."
+ << control -> LocalVersionPatch << "\n\n";
+
+ cerr << "Copyright (C) 2001, 2010 NoMachine.\n"
+ << "See http://www.nomachine.com/ for more information.\n\n";
+ }
+
+ //
+ // People get confused by the fact that client
+ // mode is running on NX server and viceversa.
+ // Let's adopt an user-friendly naming conven-
+ // tion here.
+ //
+
+ cerr << "Info: Proxy running in "
+ << (control -> ProxyMode == proxy_client ? "server" : "client")
+ << " mode with pid '" << getpid() << "'.\n";
+
+ if (agent == NULL)
+ {
+ cerr << "Session" << ": Starting session at '"
+ << strTimestamp() << "'.\n";
+ }
+
+ #ifdef TEST
+
+ if (*errorsFileName != '\0')
+ {
+ cerr << "Info" << ": Using errors file '" << errorsFileName << "'.\n";
+ }
+
+ if (*statsFileName != '\0')
+ {
+ cerr << "Info" << ": Using stats file '" << statsFileName << "'.\n";
+ }
+
+ #endif
+}
+
+void PrintConnectionInfo()
+{
+ cerr << "Info" << ": Using "
+ << linkSpeedName << " link parameters "
+ << control -> TokenSize
+ << "/" << control -> TokenLimit
+ << "/" << control -> FlushPolicy + 1
+ << "/" << control -> FlushPriority
+ << ".\n";
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ cerr << "Info" << ": Using agent parameters "
+ << control -> PingTimeout
+ << "/" << control -> MotionTimeout
+ << "/" << control -> IdleTimeout
+ << "/" << control -> TaintReplies
+ << "/" << control -> HideRender
+ << ".\n";
+ }
+
+ if (control -> LocalDeltaCompression == 1)
+ {
+ cerr << "Info" << ": Using cache parameters "
+ << control -> MinimumMessageSize
+ << "/" << control -> MaximumMessageSize / 1024 << "KB"
+ << "/" << control -> ClientTotalStorageSize / 1024 << "KB"
+ << "/" << control -> ServerTotalStorageSize / 1024 << "KB"
+ << ".\n";
+ }
+
+ if (control -> ImageCacheEnableLoad == 1 ||
+ control -> ImageCacheEnableSave == 1)
+ {
+ cerr << "Info" << ": Using image streaming parameters "
+ << control -> SplitTimeout
+ << "/" << control -> SplitTotalSize
+ << "/" << control -> SplitTotalStorageSize / 1024 << "KB"
+ << "/" << control -> SplitDataThreshold
+ << "/" << control -> SplitDataPacketLimit
+ << ".\n";
+
+ cerr << "Info" << ": Using image cache parameters "
+ << control -> ImageCacheEnableLoad
+ << "/" << control -> ImageCacheEnableSave
+ << "/" << control -> ImageCacheDiskLimit / 1024 << "KB"
+ << ".\n";
+ }
+
+ cerr << "Info" << ": Using pack method '"
+ << packMethodName << "' with session '"
+ << sessionType << "'.\n";
+
+ if (*productName != '\0')
+ {
+ cerr << "Info" << ": Using product '" << productName
+ << "'.\n" << logofs_flush;
+ }
+
+ if (control -> LocalDeltaCompression == 0)
+ {
+ cerr << "Info" << ": Not using NX delta compression.\n";
+ }
+
+ if (control -> LocalDataCompression == 1 ||
+ control -> RemoteDataCompression == 1)
+ {
+ cerr << "Info" << ": Using ZLIB data compression "
+ << control -> LocalDataCompressionLevel
+ << "/" << control -> RemoteDataCompressionLevel
+ << "/" << control -> LocalDataCompressionThreshold
+ << ".\n";
+ }
+ else
+ {
+ cerr << "Info" << ": Not using ZLIB data compression.\n";
+ }
+
+ if (control -> LocalStreamCompression == 1 ||
+ control -> RemoteStreamCompression == 1)
+ {
+ cerr << "Info" << ": Using ZLIB stream compression "
+ << control -> LocalStreamCompressionLevel
+ << "/" << control -> RemoteStreamCompressionLevel
+ << ".\n";
+ }
+ else
+ {
+ cerr << "Info" << ": Not using ZLIB stream compression.\n";
+ }
+
+ if (control -> LocalBitrateLimit > 0)
+ {
+ cerr << "Info" << ": Using bandwidth limit of "
+ << bitrateLimitName << " bits per second.\n";
+ }
+
+ if (control -> PersistentCacheName != NULL)
+ {
+ cerr << "Info" << ": Using cache file '"
+ << control -> PersistentCachePath << "/"
+ << control -> PersistentCacheName << "'.\n";
+ }
+ else
+ {
+ if (control -> PersistentCacheEnableLoad == 0 ||
+ control -> LocalDeltaCompression == 0)
+ {
+ cerr << "Info" << ": Not using a persistent cache.\n";
+ }
+ else
+ {
+ cerr << "Info" << ": No suitable cache file found.\n";
+ }
+ }
+
+ if (control -> ProxyMode == proxy_client &&
+ (useUnixSocket > 0 || useTcpSocket > 0 ||
+ useAgentSocket > 0))
+ {
+ cerr << "Info" << ": Listening to X11 connections "
+ << "on display ':" << xPort << "'.\n";
+ }
+ else if (control -> ProxyMode == proxy_server)
+ {
+ cerr << "Info" << ": Forwarding X11 connections "
+ << "to display '" << displayHost << "'.\n";
+ }
+
+ if (control -> ProxyMode == proxy_client &&
+ useCupsSocket > 0 && cupsPort > 0)
+ {
+ cerr << "Info" << ": Listening to CUPS connections "
+ << "on port '" << cupsPort << "'.\n";
+ }
+ else if (control -> ProxyMode == proxy_server &&
+ cupsPort > 0)
+ {
+ cerr << "Info" << ": Forwarding CUPS connections "
+ << "to port '" << cupsPort << "'.\n";
+ }
+
+ if (control -> ProxyMode == proxy_client &&
+ useAuxSocket > 0 && auxPort > 0)
+ {
+ cerr << "Info" << ": Listening to auxiliary X11 connections "
+ << "on port '" << auxPort << "'.\n";
+ }
+ else if (control -> ProxyMode == proxy_server &&
+ auxPort > 0)
+ {
+ cerr << "Info" << ": Forwarding auxiliary X11 connections "
+ << "to display '" << displayHost << "'.\n";
+ }
+
+ if (control -> ProxyMode == proxy_client &&
+ useSmbSocket > 0 && smbPort > 0)
+ {
+ cerr << "Info" << ": Listening to SMB connections "
+ << "on port '" << smbPort << "'.\n";
+ }
+ else if (control -> ProxyMode == proxy_server &&
+ smbPort > 0)
+ {
+ cerr << "Info" << ": Forwarding SMB connections "
+ << "to port '" << smbPort << "'.\n";
+ }
+
+ if (control -> ProxyMode == proxy_client &&
+ useMediaSocket > 0 && mediaPort > 0)
+ {
+ cerr << "Info" << ": Listening to multimedia connections "
+ << "on port '" << mediaPort << "'.\n";
+ }
+ else if (control -> ProxyMode == proxy_server &&
+ mediaPort > 0)
+ {
+ cerr << "Info" << ": Forwarding multimedia connections "
+ << "to port '" << mediaPort << "'.\n";
+ }
+
+ if (control -> ProxyMode == proxy_client &&
+ useHttpSocket > 0 && httpPort > 0)
+ {
+ cerr << "Info" << ": Listening to HTTP connections "
+ << "on port '" << httpPort << "'.\n";
+ }
+ else if (control -> ProxyMode == proxy_server &&
+ httpPort > 0)
+ {
+ cerr << "Info" << ": Forwarding HTTP connections "
+ << "to port '" << httpPort << "'.\n";
+ }
+
+ if (control -> ProxyMode == proxy_server &&
+ useFontSocket > 0 && *fontPort != '\0')
+ {
+ cerr << "Info" << ": Listening to font server connections "
+ << "on port '" << fontPort << "'.\n";
+ }
+ else if (control -> ProxyMode == proxy_client &&
+ *fontPort != '\0')
+ {
+ cerr << "Info" << ": Forwarding font server connections "
+ << "to port '" << fontPort << "'.\n";
+ }
+
+ if (useSlaveSocket > 0 && slavePort > 0)
+ {
+ cerr << "Info" << ": Listening to slave connections "
+ << "on port '" << slavePort << "'.\n";
+ }
+}
+
+void PrintVersionInfo()
+{
+ cerr << "NXPROXY - " << "Version "
+ << control -> LocalVersionMajor << "."
+ << control -> LocalVersionMinor << "."
+ << control -> LocalVersionPatch;
+
+ cerr << endl;
+}
+
+void PrintCopyrightInfo()
+{
+ cerr << endl;
+
+ PrintVersionInfo();
+
+ cerr << endl;
+
+ cerr << GetCopyrightInfo();
+
+ //
+ // Print third party's copyright info.
+ //
+
+ cerr << endl;
+
+ cerr << GetOtherCopyrightInfo();
+
+ cerr << endl;
+}
+
+void PrintOptionIgnored(const char *type, const char *name, const char *value)
+{
+ if (control -> ProxyMode == proxy_server)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Ignoring " << type
+ << " option '" << name << "' with value '"
+ << value << "' at " << "NX client side.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Ignoring " << type
+ << " option '" << name << "' with value '"
+ << value << "' at " << "NX client side.\n";
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Ignoring " << type
+ << " option '" << name << "' with value '"
+ << value << "' at " << "NX server side.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Ignoring " << type
+ << " option '" << name << "' with value '"
+ << value << "' at " << "NX server side.\n";
+ }
+}
+
+const char *GetOptions(const char *options)
+{
+ if (options != NULL)
+ {
+ if (strncasecmp(options, "nx/nx,", 6) != 0 &&
+ strncasecmp(options, "nx,", 3) != 0 &&
+ strncasecmp(options, "nx:", 3) != 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: PANIC! Display options string '" << options
+ << "' must start with 'nx' or 'nx/nx' prefix.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Display options string '" << options
+ << "' must start with 'nx' or 'nx/nx' prefix.\n";
+
+ HandleCleanup();
+ }
+ }
+ else
+ {
+ options = getenv("DISPLAY");
+ }
+
+ return options;
+}
+
+const char *GetArg(int &argi, int argc, const char **argv)
+{
+ //
+ // Skip "-" and flag character.
+ //
+
+ const char *arg = argv[argi] + 2;
+
+ if (*arg == 0)
+ {
+ if (argi + 1 == argc)
+ {
+ return NULL;
+ }
+ else
+ {
+ argi++;
+
+ return (*argv[argi] == '-' ? NULL : argv[argi]);
+ }
+ }
+ else
+ {
+ return (*arg == '-' ? NULL : arg);
+ }
+}
+
+int CheckArg(const char *type, const char *name, const char *value)
+{
+ #ifdef TEST
+ *logofs << "Loop: Parsing " << type << " option '" << name
+ << "' with value '" << (value ? value : "(null)")
+ << "'.\n" << logofs_flush;
+ #endif
+
+ if (value == NULL || strstr(value, "=") != NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Error in " << type << " option '"
+ << name << "'. No value found.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error in " << type << " option '"
+ << name << "'. No value found.\n";
+
+ return -1;
+ }
+ else if (strstr(name, ",") != NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Parse error at " << type << " option '"
+ << name << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Parse error at " << type << " option '"
+ << name << "'.\n";
+
+ return -1;
+ }
+ else if (strlen(value) >= DEFAULT_STRING_LENGTH)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Value '" << value << "' of "
+ << type << " option '" << name << "' exceeds length of "
+ << DEFAULT_STRING_LENGTH << " characters.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Value '" << value << "' of "
+ << type << " option '" << name << "' exceeds length of "
+ << DEFAULT_STRING_LENGTH << " characters.\n";
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int ParseArg(const char *type, const char *name, const char *value)
+{
+ if (strcasecmp(value, "0") == 0)
+ {
+ return 0;
+ }
+
+ //
+ // Find the base factor.
+ //
+
+ double base;
+
+ const char *id = value + strlen(value) - 1;
+
+ if (strcasecmp(id, "g") == 0)
+ {
+ base = 1024 * 1024 * 1024;
+ }
+ else if (strcasecmp(id, "m") == 0)
+ {
+ base = 1024 * 1024;
+ }
+ else if (strcasecmp(id, "k") == 0)
+ {
+ base = 1024;
+ }
+ else if (strcasecmp(id, "b") == 0 || isdigit(*id) == 1)
+ {
+ base = 1;
+ }
+ else
+ {
+ return -1;
+ }
+
+ char *string = new char[strlen(value)];
+
+ strncpy(string, value, strlen(value) - 1);
+
+ *(string + (strlen(value) - 1)) = '\0';
+
+ #ifdef TEST
+
+ *logofs << "Loop: Parsing integer option '" << name
+ << "' from string '" << string << "' with base set to ";
+
+ switch (tolower(*id))
+ {
+ case 'k':
+ case 'm':
+ case 'g':
+ {
+ *logofs << (char) toupper(*id);
+ }
+ break;
+ }
+
+ *logofs << ".\n" << logofs_flush;
+
+ #endif
+
+ double result = atof(string) * base;
+
+ if (result < 0 || result > (((unsigned) -1) >> 1))
+ {
+ delete [] string;
+
+ return -1;
+ }
+
+ delete [] string;
+
+ #ifdef TEST
+ *logofs << "Loop: Integer option parsed to '"
+ << (int) result << "'.\n" << logofs_flush;
+ #endif
+
+ return (int) result;
+}
+
+int ValidateArg(const char *type, const char *name, const char *value)
+{
+ int number = atoi(value);
+
+ if (number < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid " << type
+ << " option '" << name << "' with value '"
+ << value << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid " << type
+ << " option '" << name << "' with value '"
+ << value << "'.\n";
+
+ HandleCleanup();
+ }
+
+ return number;
+}
+
+int LowercaseArg(const char *type, const char *name, char *value)
+{
+ char *next = value;
+
+ while (*next != '\0')
+ {
+ *next = tolower(*next);
+
+ next++;
+ }
+
+ return 1;
+}
+
+int CheckSignal(int signal)
+{
+ //
+ // Return 1 if the signal needs to be handled
+ // by the proxy, 2 if the signal just needs to
+ // be blocked to avoid interrupting a system
+ // call.
+ //
+
+ switch (signal)
+ {
+ case SIGCHLD:
+ case SIGUSR1:
+ case SIGUSR2:
+ case SIGHUP:
+ case SIGINT:
+ case SIGTERM:
+ case SIGPIPE:
+ case SIGALRM:
+ {
+ return 1;
+ }
+ case SIGVTALRM:
+ case SIGWINCH:
+ case SIGIO:
+ case SIGTSTP:
+ case SIGTTIN:
+ case SIGTTOU:
+ {
+ return 2;
+ }
+ default:
+ {
+ #ifdef __CYGWIN32__
+
+ //
+ // This signal can be raised by the Cygwin
+ // library.
+ //
+
+ if (signal == 12)
+ {
+ return 1;
+ }
+
+ #endif
+
+ return 0;
+ }
+ }
+}
+
+static void PrintUsageInfo(const char *option, int error)
+{
+ if (error == 1)
+ {
+ cerr << "Error" << ": Invalid command line option '" << option << "'.\n";
+ }
+
+ cerr << GetUsageInfo();
+
+ if (error == 1)
+ {
+ cerr << "Error" << ": NX transport initialization failed.\n";
+ }
+}
+
+static void handleCheckSessionInLoop()
+{
+ //
+ // Check if we completed the shutdown procedure
+ // and the remote confirmed the shutdown. The
+ // tear down should be always initiated by the
+ // agent, but the X server side may unilateral-
+ // ly shut down the link without our permission.
+ //
+
+ if (proxy -> getShutdown() > 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: End of NX transport requested "
+ << "by remote.\n" << logofs_flush;
+ #endif
+
+ handleTerminatingInLoop();
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Bytes received so far are "
+ << (unsigned long long) statistics -> getBytesIn()
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (statistics -> getBytesIn() < 1024)
+ {
+ cerr << "Info" << ": Your session was closed before reaching "
+ << "a usable state.\n";
+ cerr << "Info" << ": This can be due to the local X server "
+ << "refusing access to the client.\n";
+ cerr << "Info" << ": Please check authorization provided "
+ << "by the remote X application.\n";
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Shutting down the NX transport.\n"
+ << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+ else if (proxy -> handlePing() < 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Failure handling the ping for "
+ << "proxy FD#" << proxyFD << ".\n"
+ << logofs_flush;
+ #endif
+
+ HandleShutdown();
+ }
+
+ //
+ // Check if the watchdog has exited and we didn't
+ // get the SIGCHLD. This can happen if the parent
+ // has overridden our signal handlers.
+ //
+
+ if (IsRunning(lastWatchdog) && CheckProcess(lastWatchdog, "watchdog") == 0)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Watchdog is gone unnoticed. "
+ << "Setting the last signal to SIGTERM.\n"
+ << logofs_flush;
+ #endif
+
+ lastSignal = SIGTERM;
+
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Resetting pid of last "
+ << "watchdog process.\n" << logofs_flush;
+ #endif
+
+ SetNotRunning(lastWatchdog);
+ }
+
+ //
+ // Let the client proxy find out if the agent's
+ // channel is gone. This is the normal shutdown
+ // procedure in the case of an internal connect-
+ // ion to the agent.
+ //
+
+ int cleanup = 0;
+
+ if (control -> ProxyMode == proxy_client &&
+ agent != NULL && proxy -> getType(agentFD[1]) ==
+ channel_none && lastKill == 0 && lastDestroy == 1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: End of NX transport requested "
+ << "by agent.\n" << logofs_flush;
+ #endif
+
+ #ifdef TEST
+ *logofs << "Loop: Bytes sent so far are "
+ << (unsigned long long) statistics -> getBytesOut()
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (statistics -> getBytesOut() < 1024)
+ {
+ cerr << "Info" << ": Your session has died before reaching "
+ << "an usable state.\n";
+ cerr << "Info" << ": This can be due to the remote X server "
+ << "refusing access to the client.\n";
+ cerr << "Info" << ": Please check the authorization provided "
+ << "by your X application.\n";
+ }
+
+ cleanup = 1;
+ }
+
+ //
+ // Check if the user requested the end of the
+ // session by sending a signal to the proxy.
+ // All signals are handled in the main loop
+ // so we need to reset the value to get ready
+ // for the next iteration.
+ //
+
+ int signal = 0;
+
+ if (lastSignal != 0)
+ {
+ switch (lastSignal)
+ {
+ case SIGCHLD:
+ case SIGUSR1:
+ case SIGUSR2:
+ {
+ break;
+ }
+ default:
+ {
+ signal = lastSignal;
+
+ cleanup = 1;
+
+ break;
+ }
+ }
+
+ lastSignal = 0;
+ }
+
+ if (cleanup == 1)
+ {
+ //
+ // The first time termination signal is received
+ // disable all further connections, close down any
+ // X channel and wait for a second signal.
+ //
+
+ if (lastKill == 0)
+ {
+ //
+ // Don't print a message if cleanup is
+ // due to normal termination of agent.
+ //
+
+ if (signal != 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: End of NX transport requested by signal '"
+ << signal << "' '" << DumpSignal(signal)
+ << "'.\n" << logofs_flush;
+ #endif
+
+ handleTerminatingInLoop();
+ }
+
+ //
+ // Disable any further connection.
+ //
+
+ CleanupListeners();
+
+ //
+ // Close all the remaining X channels and
+ // let proxies save their persistent cache
+ // on disk.
+ //
+
+ CleanupConnections();
+
+ //
+ // We'll need to wait for the X channels
+ // to be shut down before waiting for the
+ // cleanup signal.
+ //
+
+ lastKill = 1;
+ }
+ else if (lastKill == 2)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Shutting down the NX transport.\n"
+ << logofs_flush;
+ #endif
+
+ proxy -> handleShutdown();
+
+ HandleCleanup();
+ }
+ }
+
+ if (lastKill == 1 && proxy -> getChannels(channel_x11) == 0)
+ {
+ //
+ // Save the message stores to the
+ // persistent cache.
+ //
+
+ proxy -> handleSave();
+
+ //
+ // Run a watchdog process so we can finally
+ // give up at the time the watchdog exits.
+ //
+
+ if (IsNotRunning(lastWatchdog))
+ {
+ int timeout = control -> CleanupTimeout;
+
+ if (timeout > 0)
+ {
+ if (proxy -> getChannels() == 0)
+ {
+ timeout = 500;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Starting watchdog process with timeout "
+ << "of " << timeout << " Ms.\n"
+ << logofs_flush;
+ #endif
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Starting watchdog process without "
+ << "a timeout.\n" << logofs_flush;
+ }
+ #endif
+
+ lastWatchdog = NXTransWatchdog(timeout);
+
+ if (IsFailed(lastWatchdog))
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't start the NX watchdog "
+ << "process in shutdown.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't start the NX watchdog "
+ << "process in shutdown.\n";
+
+ HandleCleanup();
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Watchdog started with pid '"
+ << lastWatchdog << "'.\n" << logofs_flush;
+ }
+ #endif
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Previous watchdog detected "
+ << "in shutdown with pid '" << lastWatchdog
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Previous watchdog detected "
+ << "in shutdown with pid '" << lastWatchdog
+ << "'.\n";
+
+ HandleCleanup();
+ }
+
+ if (control -> CleanupTimeout > 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Waiting the cleanup timeout to complete.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Info" << ": Waiting the cleanup timeout to complete.\n";
+ }
+ else
+ {
+ //
+ // The NX server will kill the watchdog
+ // process after having shut down the
+ // service channels.
+ //
+
+ cerr << "Info" << ": Watchdog running with pid '" << lastWatchdog
+ << "'.\n";
+
+ #ifdef TEST
+ *logofs << "Loop: Waiting the watchdog process to complete.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Info" << ": Waiting the watchdog process to complete.\n";
+ }
+
+ lastKill = 2;
+ }
+}
+
+static void handleCheckBitrateInLoop()
+{
+ static long int slept = 0;
+
+ #ifdef TEST
+ *logofs << "Loop: Bitrate is " << statistics -> getBitrateInShortFrame()
+ << " B/s and " << statistics -> getBitrateInLongFrame()
+ << " B/s in " << control -> ShortBitrateTimeFrame / 1000
+ << "/" << control -> LongBitrateTimeFrame / 1000
+ << " seconds timeframes.\n" << logofs_flush;
+ #endif
+
+ //
+ // This can be improved. We may not jump out
+ // of the select often enough to guarantee
+ // the necessary accuracy.
+ //
+
+ if (control -> LocalBitrateLimit > 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Calculating bandwidth usage with limit "
+ << control -> LocalBitrateLimit << ".\n"
+ << logofs_flush;
+ #endif
+
+ int reference = (statistics -> getBitrateInLongFrame() +
+ statistics -> getBitrateInShortFrame()) / 2;
+
+ if (reference > control -> LocalBitrateLimit)
+ {
+ double ratio = ((double) reference) /
+ ((double) control -> LocalBitrateLimit);
+
+ if (ratio > 1.2)
+ {
+ ratio = 1.2;
+ }
+
+ slept += (unsigned int) (pow(50000, ratio) / 1000);
+
+ if (slept > 2000)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Sleeping due to "
+ << "reference bitrate of " << reference
+ << " B/s.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Sleeping due to "
+ << "reference bitrate of " << reference
+ << " B/s.\n";
+
+ slept %= 2000;
+ }
+
+ T_timestamp idleTs = getNewTimestamp();
+
+ usleep((unsigned int) pow(50000, ratio));
+
+ int diffTs = diffTimestamp(idleTs, getNewTimestamp());
+
+ statistics -> addIdleTime(diffTs);
+
+ statistics -> subReadTime(diffTs);
+ }
+ }
+}
+
+#if defined(TEST) || defined(INFO)
+
+static void handleCheckStateInLoop(int &setFDs)
+{
+ int fdLength;
+ int fdPending;
+ int fdSplits;
+
+ for (int j = 0; j < setFDs; j++)
+ {
+ if (j != proxyFD)
+ {
+ fdPending = proxy -> getPending(j);
+
+ if (fdPending > 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Buffer for descriptor FD#"
+ << j << " has pending bytes to read.\n"
+ << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+
+ fdLength = proxy -> getLength(j);
+
+ if (fdLength > 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: WARNING! Buffer for descriptor FD#"
+ << j << " has " << fdLength << " bytes to write.\n"
+ << logofs_flush;
+ #endif
+ }
+ }
+ }
+
+ fdPending = proxy -> getPending(proxyFD);
+
+ if (fdPending > 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Buffer for proxy descriptor FD#"
+ << proxyFD << " has pending bytes to read.\n"
+ << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+
+ fdLength = proxy -> getFlushable(proxyFD);
+
+ if (fdLength > 0)
+ {
+ if (control -> FlushPolicy == policy_immediate &&
+ proxy -> getBlocked(proxyFD) == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Buffer for proxy descriptor FD#"
+ << proxyFD << " has " << fdLength << " bytes "
+ << "to write with policy 'immediate'.\n"
+ << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Loop: WARNING! Buffer for proxy descriptor FD#"
+ << proxyFD << " has " << fdLength << " bytes "
+ << "to write.\n" << logofs_flush;
+ #endif
+ }
+ }
+
+ fdSplits = proxy -> getSplitSize();
+
+ if (fdSplits > 0)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Proxy descriptor FD#" << proxyFD
+ << " has " << fdSplits << " splits to send.\n"
+ << logofs_flush;
+ #endif
+ }
+}
+
+static void handleCheckSelectInLoop(int &setFDs, fd_set &readSet,
+ fd_set &writeSet, T_timestamp selectTs)
+{
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Maximum descriptors is ["
+ << setFDs << "] at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ #endif
+
+ int i;
+
+ if (setFDs > 0)
+ {
+ i = 0;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Selected for read are ";
+ #endif
+
+ for (int j = 0; j < setFDs; j++)
+ {
+ if (FD_ISSET(j, &readSet))
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "[" << j << "]" << logofs_flush;
+ #endif
+
+ i++;
+ }
+ }
+
+ if (i > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << ".\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "[none].\n" << logofs_flush;
+ #endif
+ }
+
+ i = 0;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Selected for write are ";
+ #endif
+
+ for (int j = 0; j < setFDs; j++)
+ {
+ if (FD_ISSET(j, &writeSet))
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "[" << j << "]" << logofs_flush;
+ #endif
+
+ i++;
+ }
+ }
+
+ if (i > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << ".\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "[none].\n" << logofs_flush;
+ #endif
+ }
+ }
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Select timeout is "
+ << selectTs.tv_sec << " S and "
+ << (double) selectTs.tv_usec / 1000
+ << " Ms.\n" << logofs_flush;
+ #endif
+}
+
+static void handleCheckResultInLoop(int &resultFDs, int &errorFDs, int &setFDs, fd_set &readSet,
+ fd_set &writeSet, struct timeval &selectTs,
+ struct timeval &startTs)
+{
+ int diffTs = diffTimestamp(startTs, getNewTimestamp());
+
+ #if defined(TEST) || defined(INFO)
+
+ if (diffTs >= (control -> PingTimeout -
+ (control -> LatencyTimeout * 4)))
+ {
+ *logofs << "Loop: Select result is [" << resultFDs
+ << "] at " << strMsTimestamp() << " with no "
+ << "communication within " << diffTs
+ << " Ms.\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "Loop: Select result is [" << resultFDs
+ << "] error is [" << errorFDs << "] at "
+ << strMsTimestamp() << " after " << diffTs
+ << " Ms.\n" << logofs_flush;
+ }
+
+ #endif
+
+ int i;
+
+ if (resultFDs > 0)
+ {
+ i = 0;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Selected for read are ";
+ #endif
+
+ for (int j = 0; j < setFDs; j++)
+ {
+ if (FD_ISSET(j, &readSet))
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "[" << j << "]" << logofs_flush;
+ #endif
+
+ i++;
+ }
+ }
+
+ if (i > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << ".\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "[none].\n" << logofs_flush;
+ #endif
+ }
+
+ i = 0;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Selected for write are ";
+ #endif
+
+ for (int j = 0; j < setFDs; j++)
+ {
+ if (FD_ISSET(j, &writeSet))
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "[" << j << "]" << logofs_flush;
+ #endif
+
+ i++;
+ }
+ }
+
+ if (i > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << ".\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "[none].\n" << logofs_flush;
+ #endif
+ }
+ }
+}
+
+#endif
+
+static void handleCheckSessionInConnect()
+{
+ #ifdef TEST
+ *logofs << "Loop: Going to check session in connect.\n"
+ << logofs_flush;
+ #endif
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ HandleAlert(FAILED_PROXY_CONNECTION_CLIENT_ALERT, 1);
+ }
+ else if (IsNotRunning(lastDialog))
+ {
+ HandleAlert(FAILED_PROXY_CONNECTION_SERVER_ALERT, 1);
+ }
+
+ handleAlertInLoop();
+}
+
+static void handleStatisticsInLoop()
+{
+ if (lastSignal == 0)
+ {
+ return;
+ }
+
+ int mode = NO_STATS;
+
+ if (control -> EnableStatistics == 1)
+ {
+ if (lastSignal == SIGUSR1)
+ {
+ //
+ // Print overall statistics.
+ //
+
+ mode = TOTAL_STATS;
+ }
+ else if (lastSignal == SIGUSR2)
+ {
+ //
+ // Print partial statistics.
+ //
+
+ mode = PARTIAL_STATS;
+ }
+
+ if (mode == TOTAL_STATS || mode == PARTIAL_STATS)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Going to request proxy statistics "
+ << "with signal '" << DumpSignal(lastSignal)
+ << "'.\n" << logofs_flush;
+ #endif
+
+ if (proxy != NULL)
+ {
+ if (ReopenLogFile(statsFileName, statofs, 0) < 0)
+ {
+ HandleCleanup();
+ }
+
+ proxy -> handleStatistics(mode, statofs);
+ }
+ }
+ }
+}
+
+static void handleNegotiationInLoop(int &setFDs, fd_set &readSet,
+ fd_set &writeSet, T_timestamp &selectTs)
+{
+ int yield = 0;
+
+ while (yield == 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Going to run a new negotiation loop "
+ << "with stage " << control -> ProxyStage
+ << " at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ switch (control -> ProxyStage)
+ {
+ case stage_undefined:
+ {
+ #ifdef TEST
+ *logofs << "Loop: Handling negotiation with '"
+ << "stage_undefined" << "'.\n"
+ << logofs_flush;
+ #endif
+
+ control -> ProxyStage = stage_initializing;
+
+ break;
+ }
+ case stage_initializing:
+ {
+ #ifdef TEST
+ *logofs << "Loop: Handling negotiation with '"
+ << "stage_initializing" << "'.\n"
+ << logofs_flush;
+ #endif
+
+ InitBeforeNegotiation();
+
+ control -> ProxyStage = stage_connecting;
+
+ break;
+ }
+ case stage_connecting:
+ {
+ #ifdef TEST
+ *logofs << "Loop: Handling negotiation with '"
+ << "stage_connecting" << "'.\n"
+ << logofs_flush;
+ #endif
+
+ SetupProxyConnection();
+
+ control -> ProxyStage = stage_connected;
+
+ break;
+ }
+ case stage_connected:
+ {
+ #ifdef TEST
+ *logofs << "Loop: Handling negotiation with '"
+ << "stage_connected" << "'.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Server side proxy must always be the one that
+ // sends its version and options first, so, in
+ // some way, client side can be the the one that
+ // has the last word on the matter.
+ //
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ //
+ // Check if we have been listening for a
+ // forwarder. In this case it will have to
+ // authenticate itself.
+ //
+
+ if (WE_LISTEN_FORWARDER)
+ {
+ control -> ProxyStage = stage_waiting_forwarder_version;
+
+ break;
+ }
+
+ control -> ProxyStage = stage_sending_proxy_options;
+ }
+ else
+ {
+ //
+ // The X client side is the side that has to wait
+ // for the authorization cookie and any remote
+ // option.
+ //
+
+ control -> ProxyStage = stage_waiting_proxy_version;
+ }
+
+ break;
+ }
+ case stage_sending_proxy_options:
+ {
+ #ifdef TEST
+ *logofs << "Loop: Handling negotiation with '"
+ << "stage_sending_proxy_options" << "'.\n"
+ << logofs_flush;
+ #endif
+
+ if (SendProxyOptions(proxyFD) < 0)
+ {
+ goto handleNegotiationInLoopError;
+ }
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ control -> ProxyStage = stage_waiting_proxy_version;
+ }
+ else
+ {
+ control -> ProxyStage = stage_sending_proxy_caches;
+ }
+
+ break;
+ }
+ case stage_waiting_forwarder_version:
+ {
+ #ifdef TEST
+ *logofs << "Loop: Handling negotiation with '"
+ << "stage_waiting_forwarder_version" << "'.\n"
+ << logofs_flush;
+ #endif
+
+ int result = ReadForwarderVersion(proxyFD);
+
+ if (result == 0)
+ {
+ yield = 1;
+ }
+ else if (result == 1)
+ {
+ control -> ProxyStage = stage_waiting_forwarder_options;
+ }
+ else
+ {
+ goto handleNegotiationInLoopError;
+ }
+
+ break;
+ }
+ case stage_waiting_forwarder_options:
+ {
+ #ifdef TEST
+ *logofs << "Loop: Handling negotiation with '"
+ << "stage_waiting_forwarder_options" << "'.\n"
+ << logofs_flush;
+ #endif
+
+ int result = ReadForwarderOptions(proxyFD);
+
+ if (result == 0)
+ {
+ yield = 1;
+ }
+ else if (result == 1)
+ {
+ control -> ProxyStage = stage_sending_proxy_options;
+ }
+ else
+ {
+ goto handleNegotiationInLoopError;
+ }
+
+ break;
+ }
+ case stage_waiting_proxy_version:
+ {
+ #ifdef TEST
+ *logofs << "Loop: Handling negotiation with '"
+ << "stage_waiting_proxy_version" << "'.\n"
+ << logofs_flush;
+ #endif
+
+ int result = ReadProxyVersion(proxyFD);
+
+ if (result == 0)
+ {
+ yield = 1;
+ }
+ else if (result == 1)
+ {
+ control -> ProxyStage = stage_waiting_proxy_options;
+ }
+ else
+ {
+ goto handleNegotiationInLoopError;
+ }
+
+ break;
+ }
+ case stage_waiting_proxy_options:
+ {
+ #ifdef TEST
+ *logofs << "Loop: Handling negotiation with '"
+ << "stage_waiting_proxy_options" << "'.\n"
+ << logofs_flush;
+ #endif
+
+ int result = ReadProxyOptions(proxyFD);
+
+ if (result == 0)
+ {
+ yield = 1;
+ }
+ else if (result == 1)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ control -> ProxyStage = stage_waiting_proxy_caches;
+ }
+ else
+ {
+ control -> ProxyStage = stage_sending_proxy_options;
+ }
+ }
+ else
+ {
+ goto handleNegotiationInLoopError;
+ }
+
+ break;
+ }
+ case stage_sending_proxy_caches:
+ {
+ #ifdef TEST
+ *logofs << "Loop: Handling negotiation with '"
+ << "stage_sending_proxy_caches" << "'.\n"
+ << logofs_flush;
+ #endif
+
+ if (SendProxyCaches(proxyFD) < 0)
+ {
+ goto handleNegotiationInLoopError;
+ }
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ control -> ProxyStage = stage_operational;
+ }
+ else
+ {
+ control -> ProxyStage = stage_waiting_proxy_caches;
+ }
+
+ break;
+ }
+ case stage_waiting_proxy_caches:
+ {
+ #ifdef TEST
+ *logofs << "Loop: Handling negotiation with '"
+ << "stage_waiting_proxy_caches" << "'.\n"
+ << logofs_flush;
+ #endif
+
+ int result = ReadProxyCaches(proxyFD);
+
+ if (result == 0)
+ {
+ yield = 1;
+ }
+ else if (result == 1)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ control -> ProxyStage = stage_sending_proxy_caches;
+ }
+ else
+ {
+ control -> ProxyStage = stage_operational;
+ }
+ }
+ else
+ {
+ goto handleNegotiationInLoopError;
+ }
+
+ break;
+ }
+ case stage_operational:
+ {
+ #ifdef TEST
+ *logofs << "Loop: Handling negotiation with '"
+ << "stage_operational" << "'.\n"
+ << logofs_flush;
+ #endif
+
+ InitAfterNegotiation();
+
+ yield = 1;
+
+ break;
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Unmanaged case '" << control -> ProxyStage
+ << "' while handling negotiation.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Unmanaged case '" << control -> ProxyStage
+ << "' while handling negotiation.\n";
+
+ HandleCleanup();
+ }
+ }
+ }
+
+ //
+ // Check if the user requested the end of
+ // the session.
+ //
+
+ if (CheckAbort() != 0)
+ {
+ HandleCleanup();
+ }
+
+ //
+ // Select the proxy descriptor so that we
+ // can proceed negotiating the session.
+ //
+
+ FD_SET(proxyFD, &readSet);
+
+ if (proxyFD >= setFDs)
+ {
+ setFDs = proxyFD + 1;
+ }
+
+ setMinTimestamp(selectTs, control -> PingTimeout);
+
+ #ifdef TEST
+ *logofs << "Loop: Selected proxy FD#" << proxyFD << " in negotiation "
+ << "phase with timeout of " << selectTs.tv_sec << " S and "
+ << selectTs.tv_usec << " Ms.\n" << logofs_flush;
+ #endif
+
+ return;
+
+handleNegotiationInLoopError:
+
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Failure negotiating the session in stage '"
+ << control -> ProxyStage << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure negotiating the session in stage '"
+ << control -> ProxyStage << "'.\n";
+
+
+ if (control -> ProxyMode == proxy_server &&
+ control -> ProxyStage == stage_waiting_proxy_version)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Wrong version or invalid session "
+ << "authentication cookie.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Wrong version or invalid session "
+ << "authentication cookie.\n";
+ }
+
+ handleTerminatingInLoop();
+
+ HandleCleanup();
+}
+
+static void handleTerminatingInLoop()
+{
+ if (getpid() == lastProxy)
+ {
+ if (control -> ProxyStage < stage_terminating)
+ {
+ if (agent == NULL)
+ {
+ cerr << "Session" << ": Terminating session at '"
+ << strTimestamp() << "'.\n";
+ }
+
+ control -> ProxyStage = stage_terminating;
+ }
+ }
+}
+
+static void handleTerminatedInLoop()
+{
+ if (getpid() == lastProxy)
+ {
+ if (control -> ProxyStage < stage_terminated)
+ {
+ if (agent == NULL)
+ {
+ cerr << "Session" << ": Session terminated at '"
+ << strTimestamp() << "'.\n";
+ }
+
+ control -> ProxyStage = stage_terminated;
+ }
+ }
+}
+
+static void handleAlertInLoop()
+{
+ if (lastAlert.code == 0)
+ {
+ return;
+ }
+
+ if (lastAlert.local == 0 &&
+ (lastAlert.code > LAST_PROTO_STEP_6_ALERT &&
+ control -> isProtoStep7() == 0))
+ {
+ //
+ // The remote proxy would be unable
+ // to handle the alert.
+ //
+
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Ignoring unsupported alert "
+ << "with code '" << lastAlert.code << "'.\n"
+ << logofs_flush;
+ #endif
+ }
+ else if (lastAlert.local == 0)
+ {
+ if (proxy != NULL)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Requesting a remote alert with code '"
+ << lastAlert.code << "'.\n" << logofs_flush;
+ #endif
+
+ if (proxy -> handleAlert(lastAlert.code) < 0)
+ {
+ HandleShutdown();
+ }
+ }
+ }
+ else
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Handling a local alert with code '"
+ << lastAlert.code << "'.\n" << logofs_flush;
+ #endif
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ //
+ // If we are at X client side and server
+ // proxy is not responding, we don't have
+ // any possibility to interact with user.
+ //
+
+ if (lastAlert.code != CLOSE_DEAD_PROXY_CONNECTION_CLIENT_ALERT &&
+ lastAlert.code != RESTART_DEAD_PROXY_CONNECTION_CLIENT_ALERT &&
+ lastAlert.code != FAILED_PROXY_CONNECTION_CLIENT_ALERT)
+ {
+ //
+ // Let the server proxy show the dialog.
+ //
+
+ if (proxy != NULL &&
+ proxy -> handleAlert(lastAlert.code) < 0)
+ {
+ HandleShutdown();
+ }
+ }
+ }
+ else
+ {
+ char caption[DEFAULT_STRING_LENGTH];
+
+ strcpy(caption, ALERT_CAPTION_PREFIX);
+
+ int length = strlen(sessionId);
+
+ //
+ // Get rid of the trailing MD5 from session id.
+ //
+
+ if (length > (MD5_LENGTH * 2 + 1) &&
+ *(sessionId + (length - (MD5_LENGTH * 2 + 1))) == '-')
+ {
+ strncat(caption, sessionId, length - (MD5_LENGTH * 2 + 1));
+ }
+ else
+ {
+ strcat(caption, sessionId);
+ }
+
+ //
+ // Use the display to which we are forwarding
+ // the remote X connections.
+ //
+
+ char *display = displayHost;
+
+ int replace = 1;
+ int local = 1;
+
+ const char *message;
+ const char *type;
+
+ switch (lastAlert.code)
+ {
+ case CLOSE_DEAD_X_CONNECTION_CLIENT_ALERT:
+ {
+ message = CLOSE_DEAD_X_CONNECTION_CLIENT_ALERT_STRING;
+ type = CLOSE_DEAD_X_CONNECTION_CLIENT_ALERT_TYPE;
+
+ break;
+ }
+ case CLOSE_DEAD_X_CONNECTION_SERVER_ALERT:
+ {
+ message = CLOSE_DEAD_X_CONNECTION_SERVER_ALERT_STRING;
+ type = CLOSE_DEAD_X_CONNECTION_SERVER_ALERT_TYPE;
+
+ break;
+ }
+ case CLOSE_DEAD_PROXY_CONNECTION_SERVER_ALERT:
+ {
+ message = CLOSE_DEAD_PROXY_CONNECTION_SERVER_ALERT_STRING;
+ type = CLOSE_DEAD_PROXY_CONNECTION_SERVER_ALERT_TYPE;
+
+ break;
+ }
+ case RESTART_DEAD_PROXY_CONNECTION_SERVER_ALERT:
+ {
+ message = RESTART_DEAD_PROXY_CONNECTION_SERVER_ALERT_STRING;
+ type = RESTART_DEAD_PROXY_CONNECTION_SERVER_ALERT_TYPE;
+
+ break;
+ }
+ case CLOSE_UNRESPONSIVE_X_SERVER_ALERT:
+ {
+ message = CLOSE_UNRESPONSIVE_X_SERVER_ALERT_STRING;
+ type = CLOSE_UNRESPONSIVE_X_SERVER_ALERT_TYPE;
+
+ break;
+ }
+ case WRONG_PROXY_VERSION_ALERT:
+ {
+ message = WRONG_PROXY_VERSION_ALERT_STRING;
+ type = WRONG_PROXY_VERSION_ALERT_TYPE;
+
+ break;
+ }
+ case FAILED_PROXY_CONNECTION_SERVER_ALERT:
+ {
+ message = FAILED_PROXY_CONNECTION_SERVER_ALERT_STRING;
+ type = FAILED_PROXY_CONNECTION_SERVER_ALERT_TYPE;
+
+ break;
+ }
+ case MISSING_PROXY_CACHE_ALERT:
+ {
+ message = MISSING_PROXY_CACHE_ALERT_STRING;
+ type = MISSING_PROXY_CACHE_ALERT_TYPE;
+
+ break;
+ }
+ case ABORT_PROXY_CONNECTION_ALERT:
+ {
+ message = ABORT_PROXY_CONNECTION_ALERT_STRING;
+ type = ABORT_PROXY_CONNECTION_ALERT_TYPE;
+
+ break;
+ }
+ case DISPLACE_MESSAGE_ALERT:
+ {
+ message = DISPLACE_MESSAGE_ALERT_STRING;
+ type = DISPLACE_MESSAGE_ALERT_TYPE;
+
+ break;
+ }
+ case GREETING_MESSAGE_ALERT:
+ {
+ message = GREETING_MESSAGE_ALERT_STRING;
+ type = GREETING_MESSAGE_ALERT_TYPE;
+
+ break;
+ }
+ case START_RESUME_SESSION_ALERT:
+ {
+ message = START_RESUME_SESSION_ALERT_STRING;
+ type = START_RESUME_SESSION_ALERT_TYPE;
+
+ break;
+ }
+ case FAILED_RESUME_DISPLAY_ALERT:
+ {
+ message = FAILED_RESUME_DISPLAY_ALERT_STRING;
+ type = FAILED_RESUME_DISPLAY_ALERT_TYPE;
+
+ break;
+ }
+ case FAILED_RESUME_DISPLAY_BROKEN_ALERT:
+ {
+ message = FAILED_RESUME_DISPLAY_BROKEN_STRING;
+ type = FAILED_RESUME_DISPLAY_BROKEN_TYPE;
+
+ break;
+ }
+ case FAILED_RESUME_VISUALS_ALERT:
+ {
+ message = FAILED_RESUME_VISUALS_ALERT_STRING;
+ type = FAILED_RESUME_VISUALS_ALERT_TYPE;
+
+ break;
+ }
+ case FAILED_RESUME_COLORMAPS_ALERT:
+ {
+ message = FAILED_RESUME_COLORMAPS_ALERT_STRING;
+ type = FAILED_RESUME_COLORMAPS_ALERT_TYPE;
+
+ break;
+ }
+ case FAILED_RESUME_PIXMAPS_ALERT:
+ {
+ message = FAILED_RESUME_PIXMAPS_ALERT_STRING;
+ type = FAILED_RESUME_PIXMAPS_ALERT_TYPE;
+
+ break;
+ }
+ case FAILED_RESUME_DEPTHS_ALERT:
+ {
+ message = FAILED_RESUME_DEPTHS_ALERT_STRING;
+ type = FAILED_RESUME_DEPTHS_ALERT_TYPE;
+
+ break;
+ }
+ case FAILED_RESUME_RENDER_ALERT:
+ {
+ message = FAILED_RESUME_RENDER_ALERT_STRING;
+ type = FAILED_RESUME_RENDER_ALERT_TYPE;
+
+ break;
+ }
+ case FAILED_RESUME_FONTS_ALERT:
+ {
+ message = FAILED_RESUME_FONTS_ALERT_STRING;
+ type = FAILED_RESUME_FONTS_ALERT_TYPE;
+
+ break;
+ }
+ case INTERNAL_ERROR_ALERT:
+ {
+ message = INTERNAL_ERROR_ALERT_STRING;
+ type = INTERNAL_ERROR_ALERT_TYPE;
+
+ break;
+ }
+ case ABORT_PROXY_NEGOTIATION_ALERT:
+ {
+ message = ABORT_PROXY_NEGOTIATION_ALERT_STRING;
+ type = ABORT_PROXY_NEGOTIATION_ALERT_TYPE;
+
+ break;
+ }
+ case ABORT_PROXY_SHUTDOWN_ALERT:
+ {
+ message = ABORT_PROXY_SHUTDOWN_ALERT_STRING;
+ type = ABORT_PROXY_SHUTDOWN_ALERT_TYPE;
+
+ break;
+ }
+ case FAILED_XDMCP_CONNECTION_ALERT:
+ {
+ message = FAILED_XDMCP_CONNECTION_ALERT_STRING;
+ type = FAILED_XDMCP_CONNECTION_ALERT_TYPE;
+
+ break;
+ }
+ default:
+ {
+ if (lastAlert.code > LAST_PROTO_STEP_7_ALERT)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! An unrecognized alert type '"
+ << lastAlert.code << "' was requested.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": An unrecognized alert type '"
+ << lastAlert.code << "' was requested.\n";
+ }
+ #ifdef WARNING
+ else
+ {
+ *logofs << "Loop: WARNING! Ignoring obsolete alert type '"
+ << lastAlert.code << "'.\n" << logofs_flush;
+ }
+ #endif
+
+ message = NULL;
+ type = NULL;
+
+ replace = 0;
+
+ break;
+ }
+ }
+
+ if (replace == 1 && IsRunning(lastDialog))
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Killing the previous dialog with pid '"
+ << lastDialog << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // The client ignores the TERM signal
+ // on Windows.
+ //
+
+ #ifdef __CYGWIN32__
+
+ KillProcess(lastDialog, "dialog", SIGKILL, 0);
+
+ #else
+
+ KillProcess(lastDialog, "dialog", SIGTERM, 0);
+
+ #endif
+
+ SetNotRunning(lastDialog);
+
+ if (proxy != NULL)
+ {
+ proxy -> handleResetAlert();
+ }
+ }
+
+ if (message != NULL && type != NULL)
+ {
+ lastDialog = NXTransDialog(caption, message, 0, type, local, display);
+
+ if (IsFailed(lastDialog))
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't start the NX dialog process.\n"
+ << logofs_flush;
+ #endif
+
+ SetNotRunning(lastDialog);
+ }
+ #if defined(TEST) || defined(INFO)
+ else
+ {
+ *logofs << "Loop: Dialog started with pid '"
+ << lastDialog << "'.\n" << logofs_flush;
+ }
+ #endif
+ }
+ #if defined(TEST) || defined(INFO)
+ else
+ {
+ *logofs << "Loop: No new dialog required for code '"
+ << lastAlert.code << "'.\n" << logofs_flush;
+ }
+ #endif
+ }
+ }
+
+ //
+ // Reset state.
+ //
+
+ lastAlert.code = 0;
+ lastAlert.local = 0;
+}
+
+static inline void handleSetAgentInLoop(int &setFDs, fd_set &readSet,
+ fd_set &writeSet, struct timeval &selectTs)
+{
+ #ifdef TEST
+ *logofs << "Loop: Preparing the masks for the agent descriptors.\n"
+ << logofs_flush;
+ #endif
+
+ agent -> saveChannelState();
+
+ agent -> saveReadMask(&readSet);
+ agent -> saveWriteMask(&writeSet);
+
+ if (control -> ProxyStage >= stage_operational)
+ {
+ if (agent -> remoteCanRead(&readSet) ||
+ agent -> remoteCanWrite(&writeSet) ||
+ agent -> localCanRead() ||
+ agent -> proxyCanRead())
+ {
+ #ifdef TEST
+ *logofs << "Loop: Setting a null timeout with agent descriptors ready.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Force a null timeout so we'll bail out
+ // of the select immediately. We will ac-
+ // comodate the result code later.
+ //
+
+ selectTs.tv_sec = 0;
+ selectTs.tv_usec = 0;
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Clearing the read and write agent descriptors.\n"
+ << logofs_flush;
+ #endif
+
+ agent -> clearReadMask(&readSet);
+ agent -> clearWriteMask(&writeSet);
+}
+
+static inline void handleAgentInLoop(int &resultFDs, int &errorFDs, int &setFDs, fd_set &readSet,
+ fd_set &writeSet, struct timeval &selectTs)
+{
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Setting proxy and local agent descriptors.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Check if I/O is possible on the local
+ // agent or the proxy descriptor.
+ //
+
+ if (resultFDs >= 0)
+ {
+ //
+ // Save if the proxy can read from the
+ // the agent descriptor.
+ //
+
+ agent -> saveChannelState();
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Values were resultFDs " << resultFDs
+ << " errorFDs " << errorFDs << " setFDs "
+ << setFDs << ".\n" << logofs_flush;
+ #endif
+
+ if (agent -> localCanRead() == 1)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Setting agent descriptor FD#" << agent ->
+ getLocalFd() << " as ready to read.\n"
+ << logofs_flush;
+ #endif
+
+ agent -> setLocalRead(&readSet, &resultFDs);
+ }
+
+ #if defined(TEST) || defined(INFO)
+
+ if (agent -> proxyCanRead(&readSet) == 0 &&
+ agent -> proxyCanRead() == 1)
+ {
+ *logofs << "Loop: WARNING! Can read from proxy FD#"
+ << proxyFD << " but the descriptor "
+ << "is not selected.\n" << logofs_flush;
+ }
+
+ if (agent -> proxyCanRead(&readSet) == 1)
+ {
+ *logofs << "Loop: Setting proxy descriptor FD#" << agent ->
+ getProxyFd() << " as ready to read.\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Values are now resultFDs " << resultFDs
+ << " errorFDs " << errorFDs << " setFDs "
+ << setFDs << ".\n" << logofs_flush;
+ #endif
+ }
+}
+
+static inline void handleAgentLateInLoop(int &resultFDs, int &errorFDs, int &setFDs, fd_set &readSet,
+ fd_set &writeSet, struct timeval &selectTs)
+{
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Setting remote agent descriptors.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // We reset the masks before calling our select.
+ // We now set the descriptors that are ready but
+ // only if they were set in the original mask.
+ // We do this after having executed our loop as
+ // we may have produced more data and the agent
+ // descriptors may have become readable or writ-
+ // able in the meanwhile.
+ //
+
+ if (resultFDs >= 0)
+ {
+ //
+ // Save if the proxy can read from the
+ // the agent descriptor.
+ //
+
+ agent -> saveChannelState();
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Values were resultFDs " << resultFDs
+ << " errorFDs " << errorFDs << " setFDs "
+ << setFDs << ".\n" << logofs_flush;
+ #endif
+
+ if (agent -> remoteCanRead(agent ->
+ getSavedReadMask()) == 1)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Setting agent descriptor FD#" << agent ->
+ getRemoteFd() << " as ready to read.\n"
+ << logofs_flush;
+ #endif
+
+ agent -> setRemoteRead(&readSet, &resultFDs);
+ }
+
+ if (agent -> remoteCanWrite(agent ->
+ getSavedWriteMask()) == 1)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Setting agent descriptor FD#" << agent ->
+ getRemoteFd() << " as ready to write.\n"
+ << logofs_flush;
+ #endif
+
+ agent -> setRemoteWrite(&writeSet, &resultFDs);
+ }
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Values are now resultFDs " << resultFDs
+ << " errorFDs " << errorFDs << " setFDs "
+ << setFDs << ".\n" << logofs_flush;
+ #endif
+ }
+}
+
+static inline void handleReadableInLoop(int &resultFDs, fd_set &readSet)
+{
+ if (resultFDs > 0)
+ {
+ T_channel_type type = channel_none;
+
+ const char *label = NULL;
+ int domain = -1;
+ int fd = -1;
+
+ if (tcpFD != -1 && FD_ISSET(tcpFD, &readSet))
+ {
+ type = channel_x11;
+ label = "X";
+ domain = AF_INET;
+ fd = tcpFD;
+
+ resultFDs--;
+ }
+
+ if (unixFD != -1 && FD_ISSET(unixFD, &readSet))
+ {
+ type = channel_x11;
+ label = "X";
+ domain = AF_UNIX;
+ fd = unixFD;
+
+ resultFDs--;
+ }
+
+ if (cupsFD != -1 && FD_ISSET(cupsFD, &readSet))
+ {
+ type = channel_cups;
+ label = "CUPS";
+ domain = AF_INET;
+ fd = cupsFD;
+
+ resultFDs--;
+ }
+
+ if (auxFD != -1 && FD_ISSET(auxFD, &readSet))
+ {
+ //
+ // Starting from version 1.5.0 we create real X
+ // connections for the keyboard channel, so they
+ // can use the fake authorization cookie. This
+ // means that there is not such a thing like a
+ // channel_aux anymore.
+ //
+
+ type = channel_x11;
+ label = "auxiliary X11";
+ domain = AF_INET;
+ fd = auxFD;
+
+ resultFDs--;
+ }
+
+ if (smbFD != -1 && FD_ISSET(smbFD, &readSet))
+ {
+ type = channel_smb;
+ label = "SMB";
+ domain = AF_INET;
+ fd = smbFD;
+
+ resultFDs--;
+ }
+
+ if (mediaFD != -1 && FD_ISSET(mediaFD, &readSet))
+ {
+ type = channel_media;
+ label = "media";
+ domain = AF_INET;
+ fd = mediaFD;
+
+ resultFDs--;
+ }
+
+ if (httpFD != -1 && FD_ISSET(httpFD, &readSet))
+ {
+ type = channel_http;
+ label = "HTTP";
+ domain = AF_INET;
+ fd = httpFD;
+
+ resultFDs--;
+ }
+
+ if (fontFD != -1 && FD_ISSET(fontFD, &readSet))
+ {
+ type = channel_font;
+ label = "font server";
+ domain = AF_INET;
+ fd = fontFD;
+
+ resultFDs--;
+ }
+
+ if (slaveFD != -1 && FD_ISSET(slaveFD, &readSet))
+ {
+ type = channel_slave;
+ label = "slave";
+ domain = AF_INET;
+ fd = slaveFD;
+
+ resultFDs--;
+ }
+
+ if (type != channel_none)
+ {
+ int newFD = AcceptConnection(fd, domain, label);
+
+ if (newFD != -1)
+ {
+ if (proxy -> handleNewConnection(type, newFD) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Error creating new " << label
+ << " connection.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error creating new " << label
+ << " connection.\n";
+
+ close(newFD);
+
+ //
+ // Don't kill the proxy in the case of an error.
+ //
+ // HandleCleanup();
+ //
+ }
+ else if (proxy -> getReadable(newFD) > 0)
+ {
+ //
+ // Add the descriptor, so we can try
+ // to read immediately.
+ //
+
+ #ifdef TEST
+ *logofs << "Loop: Trying to read immediately "
+ << "from descriptor FD#" << newFD
+ << ".\n" << logofs_flush;
+ #endif
+
+ FD_SET(newFD, &readSet);
+
+ resultFDs++;
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Nothing to read immediately "
+ << "from descriptor FD#" << newFD
+ << ".\n" << logofs_flush;
+ }
+ #endif
+ }
+ }
+ }
+
+ #ifdef DEBUG
+ *logofs << "Loop: Going to check the readable descriptors.\n"
+ << logofs_flush;
+ #endif
+
+ if (proxy -> handleRead(resultFDs, readSet) < 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Failure reading from descriptors "
+ << "for proxy FD#" << proxyFD << ".\n"
+ << logofs_flush;
+ #endif
+
+ HandleShutdown();
+ }
+}
+
+static inline void handleWritableInLoop(int &resultFDs, fd_set &writeSet)
+{
+ #ifdef DEBUG
+ *logofs << "Loop: Going to check the writable descriptors.\n"
+ << logofs_flush;
+ #endif
+
+ if (resultFDs > 0 && proxy -> handleFlush(resultFDs, writeSet) < 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Failure writing to descriptors "
+ << "for proxy FD#" << proxyFD << ".\n"
+ << logofs_flush;
+ #endif
+
+ HandleShutdown();
+ }
+}
+
+static inline void handleFlushInLoop()
+{
+ #ifdef DEBUG
+ *logofs << "Loop: Going to flush any data to the proxy.\n"
+ << logofs_flush;
+ #endif
+
+ if (agent == NULL || control ->
+ FlushPolicy == policy_immediate)
+ {
+ #if defined(TEST) || defined(INFO)
+
+ if (usePolicy == -1 && control ->
+ ProxyMode == proxy_client)
+ {
+ *logofs << "Loop: WARNING! Flushing the proxy link "
+ << "on behalf of the agent.\n" << logofs_flush;
+ }
+
+ #endif
+
+ if (proxy -> handleFlush() < 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Failure flushing the proxy FD#"
+ << proxyFD << ".\n" << logofs_flush;
+ #endif
+
+ HandleShutdown();
+ }
+ }
+}
+
+static inline void handleRotateInLoop()
+{
+ #ifdef DEBUG
+ *logofs << "Loop: Going to rotate channels "
+ << "for proxy FD#" << proxyFD << ".\n"
+ << logofs_flush;
+ #endif
+
+ proxy -> handleRotate();
+}
+
+static inline void handleEventsInLoop()
+{
+ #ifdef DEBUG
+ *logofs << "Loop: Going to check channel events "
+ << "for proxy FD#" << proxyFD << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (proxy -> handleEvents() < 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Failure handling channel events "
+ << "for proxy FD#" << proxyFD << ".\n"
+ << logofs_flush;
+ #endif
+
+ HandleShutdown();
+ }
+}
+
+static void handleLogReopenInLoop(T_timestamp &logsTs, T_timestamp &nowTs)
+{
+ //
+ // If need to limit the size of the
+ // log file, check the size at each
+ // loop.
+ //
+
+ #ifndef QUOTA
+
+ if (diffTimestamp(logsTs, nowTs) > control -> FileSizeCheckTimeout)
+
+ #endif
+ {
+ #ifdef DEBUG
+ *logofs << "Loop: Checking size of log file '"
+ << errorsFileName << "'.\n" << logofs_flush;
+ #endif
+
+ #ifndef MIXED
+
+ if (ReopenLogFile(errorsFileName, logofs, control -> FileSizeLimit) < 0)
+ {
+ HandleShutdown();
+ }
+
+ #endif
+
+ //
+ // Reset to current timestamp.
+ //
+
+ logsTs = nowTs;
+ }
+}
+
+static inline void handleSetReadInLoop(fd_set &readSet, int &setFDs, struct timeval &selectTs)
+{
+ proxy -> setReadDescriptors(&readSet, setFDs, selectTs);
+}
+
+static inline void handleSetWriteInLoop(fd_set &writeSet, int &setFDs, struct timeval &selectTs)
+{
+ proxy -> setWriteDescriptors(&writeSet, setFDs, selectTs);
+}
+
+static void handleSetListenersInLoop(fd_set &readSet, int &setFDs)
+{
+ //
+ // Set descriptors of listening sockets.
+ //
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ if (useTcpSocket == 1)
+ {
+ FD_SET(tcpFD, &readSet);
+
+ if (tcpFD >= setFDs)
+ {
+ setFDs = tcpFD + 1;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Loop: Selected listener tcpFD " << tcpFD
+ << " with setFDs " << setFDs << ".\n"
+ << logofs_flush;
+ #endif
+ }
+
+ if (useUnixSocket == 1)
+ {
+ FD_SET(unixFD, &readSet);
+
+ if (unixFD >= setFDs)
+ {
+ setFDs = unixFD + 1;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Loop: Selected listener unixFD " << unixFD
+ << " with setFDs " << setFDs << ".\n"
+ << logofs_flush;
+ #endif
+ }
+
+ if (useCupsSocket == 1)
+ {
+ FD_SET(cupsFD, &readSet);
+
+ if (cupsFD >= setFDs)
+ {
+ setFDs = cupsFD + 1;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Loop: Selected listener cupsFD " << cupsFD
+ << " with setFDs " << setFDs << ".\n"
+ << logofs_flush;
+ #endif
+ }
+
+ if (useAuxSocket == 1)
+ {
+ FD_SET(auxFD, &readSet);
+
+ if (auxFD >= setFDs)
+ {
+ setFDs = auxFD + 1;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Loop: Selected listener auxFD " << auxFD
+ << " with setFDs " << setFDs << ".\n"
+ << logofs_flush;
+ #endif
+ }
+
+ if (useSmbSocket == 1)
+ {
+ FD_SET(smbFD, &readSet);
+
+ if (smbFD >= setFDs)
+ {
+ setFDs = smbFD + 1;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Loop: Selected listener smbFD " << smbFD
+ << " with setFDs " << setFDs << ".\n"
+ << logofs_flush;
+ #endif
+ }
+
+ if (useMediaSocket == 1)
+ {
+ FD_SET(mediaFD, &readSet);
+
+ if (mediaFD >= setFDs)
+ {
+ setFDs = mediaFD + 1;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Loop: Selected listener mediaFD " << mediaFD
+ << " with setFDs " << setFDs << ".\n"
+ << logofs_flush;
+ #endif
+ }
+
+ if (useHttpSocket == 1)
+ {
+ FD_SET(httpFD, &readSet);
+
+ if (httpFD >= setFDs)
+ {
+ setFDs = httpFD + 1;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Loop: Selected listener httpFD " << httpFD
+ << " with setFDs " << setFDs << ".\n"
+ << logofs_flush;
+ #endif
+ }
+ }
+ else
+ {
+ if (useFontSocket == 1)
+ {
+ FD_SET(fontFD, &readSet);
+
+ if (fontFD >= setFDs)
+ {
+ setFDs = fontFD + 1;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Loop: Selected listener fontFD " << fontFD
+ << " with setFDs " << setFDs << ".\n"
+ << logofs_flush;
+ #endif
+ }
+ }
+
+ if (useSlaveSocket == 1)
+ {
+ FD_SET(slaveFD, &readSet);
+
+ if (slaveFD >= setFDs)
+ {
+ setFDs = slaveFD + 1;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Loop: Selected listener slaveFD " << slaveFD
+ << " with setFDs " << setFDs << ".\n"
+ << logofs_flush;
+ #endif
+ }
+}
diff --git a/nxcomp/MD5.c b/nxcomp/MD5.c
new file mode 100644
index 000000000..e49d3cf58
--- /dev/null
+++ b/nxcomp/MD5.c
@@ -0,0 +1,399 @@
+/*
+ Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.c is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
+ either statically or dynamically; added missing #include <string.h>
+ in library.
+ 2002-03-11 lpd Corrected argument list for main(), and added int return
+ type, in test program and T value program.
+ 2002-02-21 lpd Added missing #include <stdio.h> in test program.
+ 2000-07-03 lpd Patched to eliminate warnings about "constant is
+ unsigned in ANSI C, signed in traditional"; made test program
+ self-checking.
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+ 1999-05-03 lpd Original version.
+ */
+
+#include "MD5.h"
+
+#include <string.h>
+
+/*
+ * Try to determine the CPU endianess
+ * at compile time.
+ */
+
+#if defined(__linux) || defined(__CYGWIN32__)
+
+#include <endian.h>
+
+#if (__BYTE_ORDER == __LITTLE_ENDIAN)
+#define ARCH_IS_BIG_ENDIAN 0
+#else
+#define ARCH_IS_BIG_ENDIAN 1
+#endif
+
+#endif /* #if defined(__linux) || defined(__CYGWIN32__) */
+
+#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
+#ifdef ARCH_IS_BIG_ENDIAN
+# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
+#else
+# define BYTE_ORDER 0
+#endif
+
+#define T_MASK ((md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3 0x242070db
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6 0x4787c62a
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9 0x698098d8
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13 0x6b901122
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16 0x49b40821
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19 0x265e5a51
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22 0x02441453
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25 0x21e1cde6
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28 0x455a14ed
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31 0x676f02d9
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35 0x6d9d6122
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38 0x4bdecfa9
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41 0x289b7ec6
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44 0x04881d05
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47 0x1fa27cf8
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50 0x432aff97
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53 0x655b59c3
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57 0x6fa87e4f
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60 0x4e0811a1
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63 0x2ad7d2bb
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+ md5_word_t
+ a = pms->abcd[0], b = pms->abcd[1],
+ c = pms->abcd[2], d = pms->abcd[3];
+ md5_word_t t;
+#if BYTE_ORDER > 0
+ /* Define storage only for big-endian CPUs. */
+ md5_word_t X[16];
+#else
+ /* Define storage for little-endian or both types of CPUs. */
+ md5_word_t xbuf[16];
+ const md5_word_t *X;
+#endif
+
+ {
+#if BYTE_ORDER == 0
+ /*
+ * Determine dynamically whether this is a big-endian or
+ * little-endian machine, since we can use a more efficient
+ * algorithm on the latter.
+ */
+ static const int w = 1;
+
+ if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
+#endif
+#if BYTE_ORDER <= 0 /* little-endian */
+ {
+ /*
+ * On little-endian machines, we can process properly aligned
+ * data without copying it.
+ */
+ if (!((data - (const md5_byte_t *)0) & 3)) {
+ /* data are properly aligned */
+ X = (const md5_word_t *)data;
+ } else {
+ /* not aligned */
+ memcpy(xbuf, data, 64);
+ X = xbuf;
+ }
+ }
+#endif
+#if BYTE_ORDER == 0
+ else /* dynamic big-endian */
+#endif
+#if BYTE_ORDER >= 0 /* big-endian */
+ {
+ /*
+ * On big-endian machines, we must arrange the bytes in the
+ * right order.
+ */
+ const md5_byte_t *xp = data;
+ int i;
+
+# if BYTE_ORDER == 0
+ X = xbuf; /* (dynamic only) */
+# else
+# define xbuf X /* (static only) */
+# endif
+ for (i = 0; i < 16; ++i, xp += 4)
+ xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+ }
+#endif
+ }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+ /* Round 1. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + F(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 7, T1);
+ SET(d, a, b, c, 1, 12, T2);
+ SET(c, d, a, b, 2, 17, T3);
+ SET(b, c, d, a, 3, 22, T4);
+ SET(a, b, c, d, 4, 7, T5);
+ SET(d, a, b, c, 5, 12, T6);
+ SET(c, d, a, b, 6, 17, T7);
+ SET(b, c, d, a, 7, 22, T8);
+ SET(a, b, c, d, 8, 7, T9);
+ SET(d, a, b, c, 9, 12, T10);
+ SET(c, d, a, b, 10, 17, T11);
+ SET(b, c, d, a, 11, 22, T12);
+ SET(a, b, c, d, 12, 7, T13);
+ SET(d, a, b, c, 13, 12, T14);
+ SET(c, d, a, b, 14, 17, T15);
+ SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+ /* Round 2. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + G(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 1, 5, T17);
+ SET(d, a, b, c, 6, 9, T18);
+ SET(c, d, a, b, 11, 14, T19);
+ SET(b, c, d, a, 0, 20, T20);
+ SET(a, b, c, d, 5, 5, T21);
+ SET(d, a, b, c, 10, 9, T22);
+ SET(c, d, a, b, 15, 14, T23);
+ SET(b, c, d, a, 4, 20, T24);
+ SET(a, b, c, d, 9, 5, T25);
+ SET(d, a, b, c, 14, 9, T26);
+ SET(c, d, a, b, 3, 14, T27);
+ SET(b, c, d, a, 8, 20, T28);
+ SET(a, b, c, d, 13, 5, T29);
+ SET(d, a, b, c, 2, 9, T30);
+ SET(c, d, a, b, 7, 14, T31);
+ SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+ /* Round 3. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + H(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 5, 4, T33);
+ SET(d, a, b, c, 8, 11, T34);
+ SET(c, d, a, b, 11, 16, T35);
+ SET(b, c, d, a, 14, 23, T36);
+ SET(a, b, c, d, 1, 4, T37);
+ SET(d, a, b, c, 4, 11, T38);
+ SET(c, d, a, b, 7, 16, T39);
+ SET(b, c, d, a, 10, 23, T40);
+ SET(a, b, c, d, 13, 4, T41);
+ SET(d, a, b, c, 0, 11, T42);
+ SET(c, d, a, b, 3, 16, T43);
+ SET(b, c, d, a, 6, 23, T44);
+ SET(a, b, c, d, 9, 4, T45);
+ SET(d, a, b, c, 12, 11, T46);
+ SET(c, d, a, b, 15, 16, T47);
+ SET(b, c, d, a, 2, 23, T48);
+#undef SET
+
+ /* Round 4. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + I(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 6, T49);
+ SET(d, a, b, c, 7, 10, T50);
+ SET(c, d, a, b, 14, 15, T51);
+ SET(b, c, d, a, 5, 21, T52);
+ SET(a, b, c, d, 12, 6, T53);
+ SET(d, a, b, c, 3, 10, T54);
+ SET(c, d, a, b, 10, 15, T55);
+ SET(b, c, d, a, 1, 21, T56);
+ SET(a, b, c, d, 8, 6, T57);
+ SET(d, a, b, c, 15, 10, T58);
+ SET(c, d, a, b, 6, 15, T59);
+ SET(b, c, d, a, 13, 21, T60);
+ SET(a, b, c, d, 4, 6, T61);
+ SET(d, a, b, c, 11, 10, T62);
+ SET(c, d, a, b, 2, 15, T63);
+ SET(b, c, d, a, 9, 21, T64);
+#undef SET
+
+ /* Then perform the following additions. (That is increment each
+ of the four registers by the value it had before this block
+ was started.) */
+ pms->abcd[0] += a;
+ pms->abcd[1] += b;
+ pms->abcd[2] += c;
+ pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+ pms->count[0] = pms->count[1] = 0;
+ pms->abcd[0] = 0x67452301;
+ pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
+ pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+ pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+ const md5_byte_t *p = data;
+ int left = nbytes;
+ int offset = (pms->count[0] >> 3) & 63;
+ md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+ if (nbytes <= 0)
+ return;
+
+ /* Update the message length. */
+ pms->count[1] += nbytes >> 29;
+ pms->count[0] += nbits;
+ if (pms->count[0] < nbits)
+ pms->count[1]++;
+
+ /* Process an initial partial block. */
+ if (offset) {
+ int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+ memcpy(pms->buf + offset, p, copy);
+ if (offset + copy < 64)
+ return;
+ p += copy;
+ left -= copy;
+ md5_process(pms, pms->buf);
+ }
+
+ /* Process full blocks. */
+ for (; left >= 64; p += 64, left -= 64)
+ md5_process(pms, p);
+
+ /* Process a final partial block. */
+ if (left)
+ memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+ static const md5_byte_t pad[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ md5_byte_t data[8];
+ int i;
+
+ /* Save the length before padding. */
+ for (i = 0; i < 8; ++i)
+ data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+ /* Pad to 56 bytes mod 64. */
+ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+ /* Append the length. */
+ md5_append(pms, data, 8);
+ for (i = 0; i < 16; ++i)
+ digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
diff --git a/nxcomp/MD5.h b/nxcomp/MD5.h
new file mode 100644
index 000000000..698c995d8
--- /dev/null
+++ b/nxcomp/MD5.h
@@ -0,0 +1,91 @@
+/*
+ Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.h is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Removed support for non-ANSI compilers; removed
+ references to Ghostscript; clarified derivation from RFC 1321;
+ now handles byte order either statically or dynamically.
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+ added conditionalization for C++ compilation from Martin
+ Purschke <purschke@bnl.gov>.
+ 1999-05-03 lpd Original version.
+ */
+
+#ifndef md5_INCLUDED
+# define md5_INCLUDED
+
+/*
+ * This package supports both compile-time and run-time determination of CPU
+ * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
+ * defined as non-zero, the code will be compiled to run only on big-endian
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
+ * run on either big- or little-endian CPUs, but will run slightly less
+ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
+ */
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+ md5_word_t count[2]; /* message length in bits, lsw first */
+ md5_word_t abcd[4]; /* digest buffer */
+ md5_byte_t buf[64]; /* accumulate block */
+} md5_state_t;
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* Initialize the algorithm. */
+void md5_init(md5_state_t *pms);
+
+/* Append a string to the message. */
+void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+
+/* Finish the message and return the digest. */
+void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
+
+#endif /* md5_INCLUDED */
diff --git a/nxcomp/Makefile.in b/nxcomp/Makefile.in
new file mode 100644
index 000000000..434118b4e
--- /dev/null
+++ b/nxcomp/Makefile.in
@@ -0,0 +1,279 @@
+############################################################################
+# #
+# Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. #
+# #
+# NXCOMP, NX protocol compression and NX extensions to this software #
+# are copyright of NoMachine. Redistribution and use of the present #
+# software is allowed according to terms specified in the file LICENSE #
+# which comes in the source distribution. #
+# #
+# Check http://www.nomachine.com/licensing.html for applicability. #
+# #
+# NX and NoMachine are trademarks of Medialogic S.p.A. #
+# #
+# All rights reserved. #
+# #
+############################################################################
+
+#
+# Get these values from the configure script. The
+# version printed by the program should be derived
+# from the CHANGELOG. For example we may use the
+# following command:
+#
+# head -n 3 CHANGELOG | grep 'nxcomp-' | cut -d '-' -f 2-3
+#
+
+VERSION=@VERSION@
+LIBVERSION=@LIBVERSION@
+
+#
+# We would really like to enable all warnings, -Wredundant-decls,
+# though, gives a warning caused by pthread.h and unistd.h and
+# GCC 3.4 was changed in a way that it now complains about some
+# of the -W directives we used before (-Wmissing-declarations,
+# -Wnested-externs, -Wstrict-prototypes and -Wmissing-prototypes).
+#
+
+CXX = @CXX@
+CXXFLAGS = @CXXFLAGS@ @X_CFLAGS@ @DEFS@ \
+ -Wall -Wpointer-arith
+CXXINCLUDES =
+CXXDEFINES =
+
+#
+# C programs have their own CFLAGS.
+#
+
+CC = @CC@
+CCFLAGS = @CFLAGS@ @X_CFLAGS@ @DEFS@ \
+ -Wall -Wpointer-arith
+CCINCLUDES =
+CCDEFINES =
+
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@
+
+#
+# Other autoconfigured settings, not used at the moment.
+#
+
+srcdir = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+bindir = @bindir@
+man1dir = @mandir@/man1
+VPATH = @srcdir@
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+
+#
+# This should be autodetected.
+#
+
+MAKEDEPEND = @MAKEDEPEND@
+DEPENDINCLUDES = -I/usr/include/c++ -I/usr/include/g++ -I/usr/include/g++-3
+
+.SUFFIXES: .cpp.c
+
+.cpp.o:
+ $(CXX) -c $(CXXFLAGS) $(CXXINCLUDES) $(CXXDEFINES) $<
+.c.o:
+ $(CC) -c $(CCFLAGS) $(CCINCLUDES) $(CCDEFINES) $<
+
+LIBRARY = Xcomp
+
+LIBNAME = lib$(LIBRARY)
+LIBFULL = lib$(LIBRARY).so.$(VERSION)
+LIBLOAD = lib$(LIBRARY).so.$(LIBVERSION)
+LIBSHARED = lib$(LIBRARY).so
+LIBARCHIVE = lib$(LIBRARY).a
+
+LIBCYGSHARED = cyg$(LIBRARY).dll
+LIBCYGARCHIVE = lib$(LIBRARY).dll.a
+
+all: depend @ALL@
+
+MSRC =
+
+CSRC = MD5.c \
+ Pack.c \
+ Vars.c
+
+CXXSRC = Loop.cpp \
+ Children.cpp \
+ Control.cpp \
+ Misc.cpp \
+ Socket.cpp \
+ Fork.cpp \
+ Pipe.cpp \
+ List.cpp \
+ Keeper.cpp \
+ Timestamp.cpp \
+ Transport.cpp \
+ Statistics.cpp \
+ Auth.cpp \
+ Agent.cpp \
+ Proxy.cpp \
+ Channel.cpp \
+ Message.cpp \
+ Split.cpp \
+ ClientProxy.cpp \
+ ServerProxy.cpp \
+ OpcodeStore.cpp \
+ ClientStore.cpp \
+ ServerStore.cpp \
+ ChannelCache.cpp \
+ ClientCache.cpp \
+ ServerCache.cpp \
+ ClientChannel.cpp \
+ ServerChannel.cpp \
+ GenericChannel.cpp \
+ ReadBuffer.cpp \
+ ProxyReadBuffer.cpp \
+ ClientReadBuffer.cpp \
+ ServerReadBuffer.cpp \
+ GenericReadBuffer.cpp \
+ EncodeBuffer.cpp \
+ DecodeBuffer.cpp \
+ WriteBuffer.cpp \
+ SequenceQueue.cpp \
+ IntCache.cpp \
+ CharCache.cpp \
+ XidCache.cpp \
+ ActionCache.cpp \
+ BlockCache.cpp \
+ BlockCacheSet.cpp \
+ StaticCompressor.cpp \
+ TextCompressor.cpp \
+ Unpack.cpp \
+ Alpha.cpp \
+ Colormap.cpp \
+ Jpeg.cpp \
+ Pgn.cpp \
+ Bitmap.cpp \
+ Rgb.cpp \
+ Rle.cpp \
+ Z.cpp \
+ ChangeProperty.cpp \
+ SendEvent.cpp \
+ ChangeGC.cpp \
+ CreateGC.cpp \
+ CreatePixmap.cpp \
+ SetClipRectangles.cpp \
+ CopyArea.cpp \
+ PolyLine.cpp \
+ PolySegment.cpp \
+ PolyFillRectangle.cpp \
+ PutImage.cpp \
+ TranslateCoords.cpp \
+ GetImage.cpp \
+ ClearArea.cpp \
+ ConfigureWindow.cpp \
+ PolyText8.cpp \
+ PolyText16.cpp \
+ ImageText8.cpp \
+ ImageText16.cpp \
+ PolyPoint.cpp \
+ PolyFillArc.cpp \
+ PolyArc.cpp \
+ FillPoly.cpp \
+ InternAtom.cpp \
+ GetProperty.cpp \
+ SetUnpackGeometry.cpp \
+ SetUnpackColormap.cpp \
+ SetUnpackAlpha.cpp \
+ PutPackedImage.cpp \
+ ShapeExtension.cpp \
+ RenderExtension.cpp \
+ GenericRequest.cpp \
+ QueryFontReply.cpp \
+ ListFontsReply.cpp \
+ GetImageReply.cpp \
+ GetPropertyReply.cpp \
+ GenericReply.cpp \
+ RenderGenericRequest.cpp \
+ RenderCreatePicture.cpp \
+ RenderChangePicture.cpp \
+ RenderFreePicture.cpp \
+ RenderPictureClip.cpp \
+ RenderPictureTransform.cpp \
+ RenderPictureFilter.cpp \
+ RenderCreateGlyphSet.cpp \
+ RenderFreeGlyphSet.cpp \
+ RenderAddGlyphs.cpp \
+ RenderComposite.cpp \
+ RenderCompositeGlyphs.cpp \
+ RenderFillRectangles.cpp \
+ RenderTrapezoids.cpp \
+ RenderTriangles.cpp \
+ PositionCacheCompat.cpp \
+ ChangeGCCompat.cpp \
+ CreatePixmapCompat.cpp \
+ SetUnpackColormapCompat.cpp \
+ SetUnpackAlphaCompat.cpp \
+ RenderCreatePictureCompat.cpp \
+ RenderFreePictureCompat.cpp \
+ RenderPictureClipCompat.cpp \
+ RenderCreateGlyphSetCompat.cpp \
+ RenderCompositeCompat.cpp \
+ RenderCompositeGlyphsCompat.cpp
+
+MOBJ = $(MSRC:.c=.o)
+COBJ = $(CSRC:.c=.o)
+CXXOBJ = $(CXXSRC:.cpp=.o)
+
+$(LIBFULL): $(CXXOBJ) $(COBJ)
+ $(CXX) -o $@ $(LDFLAGS) $(CXXOBJ) $(COBJ) $(LIBS)
+
+$(LIBLOAD): $(LIBFULL)
+ rm -f $(LIBLOAD)
+ ln -s $(LIBFULL) $(LIBLOAD)
+
+$(LIBSHARED): $(LIBFULL)
+ rm -f $(LIBSHARED)
+ ln -s $(LIBFULL) $(LIBSHARED)
+
+$(LIBARCHIVE): $(CXXOBJ) $(COBJ)
+ rm -f $(LIBARCHIVE)
+ ar clq $(LIBARCHIVE) $(CXXOBJ) $(COBJ)
+ ranlib $(LIBARCHIVE)
+
+$(LIBCYGSHARED): $(LIBARCHIVE)
+ $(CC) -shared -o $(LIBCYGSHARED) \
+ -Wl,--out-implib=$(LIBCYGARCHIVE) \
+ -Wl,--export-all-symbols \
+ -Wl,--enable-auto-import \
+ -Wl,--whole-archive $(LIBARCHIVE) \
+ -Wl,--no-whole-archive $(LIBS) \
+ $(LDFLAGS)
+
+$(LIBCYGARCHIVE): $(LIBCYGSHARED)
+
+depends: depend.status
+
+depend: depend.status
+
+depend.status:
+ if [ -x $(MAKEDEPEND) ] ; then \
+ $(MAKEDEPEND) $(CXXINCLUDES) $(CCINCLUDES) \
+ $(DEPENDINCLUDES) -f Makefile $(MSRC) $(CSRC) \
+ $(CXXSRC) 2>/dev/null; \
+ fi
+ touch depend.status
+
+install: install.bin install.man
+
+install.bin:
+
+install.man:
+
+clean:
+ -rm -f *~ *.o *.bak *.orig *.rej st?????? core core.* *.out.* \
+ @ALL@
+
+distclean: clean
+ -rm -rf autom4te.cache config.status config.log \
+ config.cache depend.status Makefile tags
diff --git a/nxcomp/Message.cpp b/nxcomp/Message.cpp
new file mode 100644
index 000000000..72d4fff3d
--- /dev/null
+++ b/nxcomp/Message.cpp
@@ -0,0 +1,2345 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <algorithm>
+
+#include "Misc.h"
+
+//
+// We need channel's cache data.
+//
+
+#include "Message.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level. You also
+// need to define DUMP in Misc.cpp
+// if DUMP is defined here.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Define this to log when messages
+// are allocated and deallocated.
+//
+
+#undef REFERENCES
+
+//
+// Keep track of how many bytes are
+// occupied by cache.
+//
+
+int MessageStore::totalLocalStorageSize_ = 0;
+int MessageStore::totalRemoteStorageSize_ = 0;
+
+//
+// These are used for reference count.
+//
+
+#ifdef REFERENCES
+
+int Message::references_ = 0;
+int MessageStore::references_ = 0;
+
+#endif
+
+//
+// Here are the methods to handle cached messages.
+//
+
+MessageStore::MessageStore(StaticCompressor *compressor)
+
+ : compressor_(compressor)
+{
+ //
+ // Public members.
+ //
+
+ enableCache = MESSAGE_ENABLE_CACHE;
+ enableData = MESSAGE_ENABLE_DATA;
+ enableSplit = MESSAGE_ENABLE_SPLIT;
+ enableCompress = MESSAGE_ENABLE_COMPRESS;
+
+ dataLimit = MESSAGE_DATA_LIMIT;
+ dataOffset = MESSAGE_DATA_OFFSET;
+
+ cacheSlots = MESSAGE_CACHE_SLOTS;
+ cacheThreshold = MESSAGE_CACHE_THRESHOLD;
+ cacheLowerThreshold = MESSAGE_CACHE_LOWER_THRESHOLD;
+
+ #ifdef TEST
+ *logofs << "MessageStore: Static compressor is at "
+ << compressor_ << ".\n" << logofs_flush;
+ #endif
+
+ md5_state_ = new md5_state_t();
+
+ #ifdef DEBUG
+ *logofs << "MessageStore: Created MD5 state for object at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ lastAdded = cacheSlots;
+ lastHit = 0;
+ lastRemoved = 0;
+ lastRated = nothing;
+ lastAction = is_discarded;
+
+ //
+ // This is used only for compatibility
+ // with older proxies.
+ //
+
+ if (control -> isProtoStep7() == 1)
+ {
+ lastResize = -1;
+ }
+ else
+ {
+ lastResize = 0;
+ }
+
+ //
+ // Private members.
+ //
+
+ localStorageSize_ = 0;
+ remoteStorageSize_ = 0;
+
+ #ifdef TEST
+ *logofs << "MessageStore: Size of total cache is "
+ << totalLocalStorageSize_ << " bytes at local side and "
+ << totalRemoteStorageSize_ << " bytes at remote side.\n"
+ << logofs_flush;
+ #endif
+
+ messages_ = new T_messages();
+ checksums_ = new T_checksums();
+
+ temporary_ = NULL;
+
+ #ifdef REFERENCES
+
+ references_++;
+
+ *logofs << "MessageStore: Created new store at "
+ << this << "out of " << references_
+ << " allocated stores.\n" << logofs_flush;
+
+ #endif
+}
+
+MessageStore::~MessageStore()
+{
+ //
+ // The virtual destructor of specialized class
+ // must get rid of both messages in container
+ // and temporary.
+ //
+
+ #ifdef DEBUG
+ *logofs << "MessageStore: Deleting MD5 state for object at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ delete md5_state_;
+
+ delete messages_;
+ delete checksums_;
+
+ //
+ // Update the static members tracking
+ // size of total memory allocated for
+ // all stores.
+ //
+
+ totalLocalStorageSize_ -= localStorageSize_;
+ totalRemoteStorageSize_ -= remoteStorageSize_;
+
+ #ifdef TEST
+ *logofs << "MessageStore: Size of total cache is "
+ << totalLocalStorageSize_ << " bytes at local side and "
+ << totalRemoteStorageSize_ << " bytes at remote side.\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef REFERENCES
+
+ references_--;
+
+ *logofs << "MessageStore: Deleted store at "
+ << this << " out of " << references_
+ << " allocated stores.\n" << logofs_flush;
+
+ #endif
+}
+
+//
+// Here are the methods to parse and cache
+// messages in the message stores.
+//
+
+int MessageStore::parse(Message *message, int split, const unsigned char *buffer,
+ unsigned int size, T_checksum_action checksumAction,
+ T_data_action dataAction, int bigEndian)
+{
+ //
+ // Save the message size as received on the link.
+ // This information will be used to create an ap-
+ // propriate buffer at the time the message will
+ // be unparsed.
+ //
+
+ message -> size_ = size;
+ message -> i_size_ = identitySize(buffer, size);
+ message -> c_size_ = 0;
+
+ validateSize(size);
+
+ if (checksumAction == use_checksum)
+ {
+ beginChecksum(message);
+
+ parseIdentity(message, buffer, size, bigEndian);
+
+ identityChecksum(message, buffer, size, bigEndian);
+
+ parseData(message, split, buffer, size, checksumAction, dataAction, bigEndian);
+
+ endChecksum(message);
+ }
+ else
+ {
+ parseIdentity(message, buffer, size, bigEndian);
+
+ parseData(message, split, buffer, size, checksumAction, dataAction, bigEndian);
+ }
+
+ return 1;
+}
+
+int MessageStore::parse(Message *message, const unsigned char *buffer,
+ unsigned int size, const unsigned char *compressedData,
+ const unsigned int compressedDataSize,
+ T_checksum_action checksumAction,
+ T_data_action dataAction, int bigEndian)
+{
+ int offset = identitySize(buffer, size);
+
+ message -> size_ = size;
+ message -> i_size_ = offset;
+ message -> c_size_ = compressedDataSize + offset;
+
+ validateSize(message -> size_ - offset, compressedDataSize);
+
+ if (checksumAction == use_checksum)
+ {
+ beginChecksum(message);
+
+ parseIdentity(message, buffer, size, bigEndian);
+
+ identityChecksum(message, buffer, size, bigEndian);
+
+ parseData(message, buffer, size, compressedData, compressedDataSize,
+ checksumAction, dataAction, bigEndian);
+
+ endChecksum(message);
+ }
+ else
+ {
+ parseIdentity(message, buffer, size, bigEndian);
+
+ parseData(message, buffer, size, compressedData, compressedDataSize,
+ checksumAction, dataAction, bigEndian);
+ }
+
+ return 1;
+}
+
+int MessageStore::parseData(Message *message, int split, const unsigned char *buffer,
+ unsigned int size, T_checksum_action checksumAction,
+ T_data_action dataAction, int bigEndian)
+{
+ if ((int) size > message -> i_size_)
+ {
+ unsigned int dataSize = size - message -> i_size_;
+
+ if (checksumAction == use_checksum)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Calculating checksum of object at "
+ << message << " with data size " << dataSize
+ << ".\n" << logofs_flush;
+ #endif
+
+ dataChecksum(message, buffer, size, bigEndian);
+ }
+
+ if (dataAction == discard_data)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Discarded " << dataSize
+ << " bytes of plain data. Real size is "
+ << message -> size_ << " compressed size is "
+ << message -> c_size_ << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+ }
+
+ //
+ // Accept anyway data beyond the
+ // expected limit.
+ //
+
+ #ifdef TEST
+
+ if (dataSize > (unsigned int) dataLimit)
+ {
+ *logofs << name() << ": WARNING! Data is " << dataSize
+ << " bytes. Ignoring the established limit.\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ if (dataSize != message -> data_.size())
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Data will be resized from "
+ << message -> data_.size() << " to hold a plain buffer of "
+ << dataSize << " bytes.\n" << logofs_flush;
+ #endif
+
+ message -> data_.clear();
+
+ message -> data_.resize(dataSize);
+ }
+
+ if (split == 0)
+ {
+ memcpy(message -> data_.begin(), buffer + message -> i_size_, dataSize);
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << name() << ": Not copied " << dataSize
+ << " bytes of fake data for the split message.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed " << dataSize
+ << " bytes of plain data. Real size is "
+ << message -> size_ << " compressed size is "
+ << message -> c_size_ << ".\n" << logofs_flush;
+ #endif
+ }
+
+ return 1;
+}
+
+//
+// Store the data part in compressed format.
+//
+
+int MessageStore::parseData(Message *message, const unsigned char *buffer,
+ unsigned int size, const unsigned char *compressedData,
+ const unsigned int compressedDataSize,
+ T_checksum_action checksumAction,
+ T_data_action dataAction, int bigEndian)
+{
+ if ((int) size > message -> i_size_)
+ {
+ unsigned int dataSize = size - message -> i_size_;
+
+ if (checksumAction == use_checksum)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Calculating checksum of object at "
+ << message << " with data size " << dataSize
+ << ".\n" << logofs_flush;
+ #endif
+
+ dataChecksum(message, buffer, size, bigEndian);
+ }
+
+ if (dataAction == discard_data)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Discarded " << dataSize
+ << " bytes of compressed data. Real size is "
+ << message -> size_ << " compressed size is "
+ << message -> c_size_ << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+ }
+
+ #ifdef WARNING
+ if (dataSize > (unsigned int) dataLimit)
+ {
+ *logofs << name() << ": WARNING! Data is " << dataSize
+ << " bytes. Ignoring the established limit!\n"
+ << logofs_flush;
+ }
+ #endif
+
+ dataSize = compressedDataSize;
+
+ if (dataSize != message -> data_.size())
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Data will be resized from "
+ << message -> data_.size() << " to hold a compressed buffer of "
+ << dataSize << " bytes.\n" << logofs_flush;
+ #endif
+
+ message -> data_.clear();
+
+ message -> data_.resize(compressedDataSize);
+ }
+
+ memcpy(message -> data_.begin(), compressedData, compressedDataSize);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed " << dataSize
+ << " bytes of compressed data. Real size is "
+ << message -> size_ << " compressed size is "
+ << message -> c_size_ << ".\n" << logofs_flush;
+ #endif
+ }
+
+ return 1;
+}
+
+int MessageStore::unparseData(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian)
+{
+ //
+ // Copy data, if any, to the buffer.
+ //
+
+ if ((int) size > message -> i_size_)
+ {
+ //
+ // Check if message has been stored
+ // in compressed format.
+ //
+
+ if (message -> c_size_ == 0)
+ {
+ memcpy(buffer + message -> i_size_, message -> data_.begin(), size - message -> i_size_);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed " << message -> size_ - message -> i_size_
+ << " bytes of data to a buffer of " << message -> size_ - message -> i_size_
+ << ".\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Using static compressor at " << (void *) compressor_
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (compressor_ ->
+ decompressBuffer(buffer + message -> i_size_,
+ size - message -> i_size_,
+ message -> data_.begin(),
+ message -> c_size_ - message -> i_size_) < 0)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Data decompression failed.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Data decompression failed.\n";
+
+ return -1;
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed " << message -> c_size_ - message -> i_size_
+ << " bytes of compressed data to a buffer of "
+ << message -> size_ - message -> i_size_ << ".\n" << logofs_flush;
+ #endif
+ }
+ }
+
+ //
+ // We could write size to the buffer but this
+ // is something the channel class is doing by
+ // itself.
+ //
+ // PutUINT(size >> 2, buffer + 2, bigEndian);
+ //
+
+ return 1;
+}
+
+void MessageStore::dumpData(const Message *message) const
+{
+ #ifdef DUMP
+
+ *logofs << name() << ": Dumping enumerated data:\n" << logofs_flush;
+
+ DumpData(message -> data_.begin(), message -> data_.size());
+
+ #endif
+
+ #ifdef DUMP
+
+ *logofs << name() << ": Dumping checksum data:\n" << logofs_flush;
+
+ DumpData(message -> md5_digest_, MD5_LENGTH);
+
+ #endif
+}
+
+T_checksum MessageStore::getChecksum(const unsigned char *buffer,
+ unsigned int size, int bigEndian)
+{
+ Message *message = getTemporary();
+
+ message -> size_ = size;
+ message -> i_size_ = identitySize(buffer, size);
+ message -> c_size_ = 0;
+
+ validateSize(size);
+
+ beginChecksum(message);
+
+ //
+ // We don't need to extract the identity
+ // data from the buffer.
+ //
+ // parseIdentity(message, buffer, size, bigEndian);
+ //
+
+ identityChecksum(message, buffer, size, bigEndian);
+
+ parseData(message, 0, buffer, size, use_checksum, discard_data, bigEndian);
+
+ endChecksum(message);
+
+ //
+ // The caller will have to explicitly
+ // deallocated the memory after use.
+ //
+
+ T_checksum checksum = new md5_byte_t[MD5_LENGTH];
+
+ memcpy(checksum, message -> md5_digest_, MD5_LENGTH);
+
+ return checksum;
+}
+
+int MessageStore::clean(T_checksum_action checksumAction)
+{
+ int position = lastRemoved + 1;
+
+ if (position >= cacheSlots)
+ {
+ position = 0;
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Searching a message to remove "
+ << "starting at position " << position
+ << " with " << checksums_ -> size()
+ << " elements in cache.\n"
+ << logofs_flush;
+ #endif
+
+ while (position != lastRemoved)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Examining position "
+ << position << ".\n" << logofs_flush;
+ #endif
+
+ if ((*messages_)[position] != NULL)
+ {
+ if (getRating((*messages_)[position], rating_for_clean) == 0)
+ {
+ break;
+ }
+ else
+ {
+ untouch((*messages_)[position]);
+ }
+ }
+
+ if (++position == cacheSlots)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Rolled position at "
+ << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ position = 0;
+ }
+ }
+
+ //
+ // If no message is a good candidate,
+ // then try the object at the next slot
+ // in respect to last element removed.
+ //
+
+ if (position == lastRemoved)
+ {
+ position = lastRemoved + 1;
+
+ if (position >= cacheSlots)
+ {
+ position = 0;
+ }
+
+ if ((*messages_)[position] == NULL ||
+ (*messages_)[position] -> locks_ != 0)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": WARNING! No message found "
+ << "to be actually removed.\n"
+ << logofs_flush;
+ #endif
+
+ return nothing;
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": WARNING! Assuming object "
+ << "at position " << position << ".\n"
+ << logofs_flush;
+ #endif
+ }
+
+ return position;
+}
+
+//
+// This is the insertion method used at local side
+// side. Cache at remote side side will be kept in
+// sync by telling the to other party where to
+// store the message.
+//
+
+int MessageStore::findOrAdd(Message *message, T_checksum_action checksumAction,
+ T_data_action dataAction, int &added, int &locked)
+{
+ if (checksumAction != use_checksum)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Internal error in context [A]. "
+ << "Cannot find or add message to repository "
+ << "without using checksum.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Internal error in context [A]. "
+ << "Cannot find or add message to repository "
+ << "without using checksum.\n";
+
+ HandleAbort();
+ }
+
+ //
+ // Set added to true only if message
+ // is inserted in cache.
+ //
+
+ added = 0;
+ locked = 0;
+
+ //
+ // First of all figure out where to
+ // store this object.
+ //
+
+ #ifdef DEBUG
+ *logofs << name() << ": Searching an empty slot "
+ << "with last rated " << lastRated << " and "
+ << "last added " << lastAdded << ".\n"
+ << logofs_flush;
+ #endif
+
+ int position = lastRated;
+
+ if (position == nothing)
+ {
+ position = lastAdded + 1;
+
+ if (position >= cacheSlots)
+ {
+ position = 0;
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Searching an empty slot "
+ << "starting at position " << position
+ << " with " << checksums_ -> size()
+ << " elements in cache.\n"
+ << logofs_flush;
+ #endif
+
+ while (position != lastAdded)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Examining position "
+ << position << ".\n" << logofs_flush;
+ #endif
+
+ if ((*messages_)[position] == NULL)
+ {
+ break;
+ }
+ else if (getRating((*messages_)[position], rating_for_insert) == 0)
+ {
+ break;
+ }
+ else
+ {
+ untouch((*messages_)[position]);
+ }
+
+ if (++position == cacheSlots)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Rolled position at "
+ << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ position = 0;
+ }
+ }
+ }
+ #ifdef DEBUG
+ else
+ {
+ *logofs << name() << ": Using last rated position "
+ << position << ".\n" << logofs_flush;
+ }
+ #endif
+
+ //
+ // If we made an extensive check but did not
+ // find neither a free slot or a message to
+ // replace, assume slot at next position in
+ // respect to last added. This can happen if
+ // all objects in repository have got an hit
+ // recently.
+ //
+
+ if (position == lastAdded)
+ {
+ position = lastAdded + 1;
+
+ if (position >= cacheSlots)
+ {
+ position = 0;
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": WARNING! Assuming slot "
+ << "at position " << position << ".\n"
+ << logofs_flush;
+ #endif
+ }
+ #ifdef DEBUG
+ else
+ {
+ *logofs << name() << ": Found candidate slot "
+ << "at position " << position << ".\n"
+ << logofs_flush;
+ }
+ #endif
+
+ //
+ // Save the search result so if the message
+ // is found in cache, we can use the slot
+ // at next run.
+ //
+
+ lastRated = position;
+
+ if ((*messages_)[position] != NULL &&
+ (*messages_)[position] -> locks_ != 0)
+ {
+ #ifdef WARNING
+ *logofs << name() << ": WARNING! Insertion at position "
+ << position << " would replace a locked message. "
+ << "Forcing channel to discard the message.\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef TEST
+ *logofs << name() << ": Invalidating rating of object "
+ << "at position " << position << ".\n"
+ << logofs_flush;
+ #endif
+
+ return (lastRated = nothing);
+ }
+
+ if (checksumAction == use_checksum)
+ {
+ T_checksum checksum = getChecksum(message);
+
+ #ifdef TEST
+ *logofs << name() << ": Searching checksum ["
+ << DumpChecksum(checksum) << "] in repository.\n"
+ << logofs_flush;
+
+ #endif
+
+ pair<T_checksums::iterator, bool> result;
+
+ result = checksums_ -> insert(T_checksums::value_type(checksum, position));
+
+ //
+ // Message was found in cache or
+ // insertion couldn't take place.
+ //
+
+ if (result.second == 0)
+ {
+ if (result.first == checksums_ -> end())
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Failed to insert object "
+ << "in the cache.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to insert object of type "
+ << name() << " in the cache.\n";
+
+ return nothing;
+ }
+
+ //
+ // Message is in cache.
+ //
+
+ #ifdef TEST
+ *logofs << name() << ": Object is already in cache "
+ << "at position " << (result.first) -> second
+ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef DEBUG
+
+ printStorageSize();
+
+ #endif
+
+ //
+ // Message is locked, probably because
+ // it has not completely recomposed at
+ // remote side after a split.
+ //
+
+ if ((*messages_)[(result.first) -> second] -> locks_ != 0)
+ {
+ #ifdef TEST
+ *logofs << name() << ": WARNING! Object at position "
+ << (result.first) -> second << " is locked.\n"
+ << logofs_flush;
+ #endif
+
+ locked = 1;
+ }
+
+ //
+ // Object got a hit, so prevent
+ // its removal.
+ //
+
+ if (lastRated == (result.first) -> second)
+ {
+ #ifdef TEST
+ *logofs << name() << ": Resetting rating of object "
+ << "at position " << (result.first) -> second
+ << ".\n" << logofs_flush;
+ #endif
+
+ lastRated = nothing;
+ }
+
+ return (result.first) -> second;
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Could not find message in cache.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ //
+ // Message not found in hash table (or insertion
+ // of checksum in hash table was not requested).
+ // Message was added to cache.
+ //
+
+ added = 1;
+
+ //
+ // Log data about the missed message.
+ //
+
+ #ifdef TEST
+
+ if (opcode() == X_PutImage || opcode() == X_NXPutPackedImage)
+ {
+ #ifdef WARNING
+ *logofs << name() << ": WARNING! Dumping identity of "
+ << "missed image object of type " << name()
+ << ".\n" << logofs_flush;
+ #endif
+
+ dumpIdentity(message);
+ }
+
+ #endif
+
+ if ((*messages_)[position] != NULL)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": The message replaces "
+ << "the old one at position " << position
+ << ".\n" << logofs_flush;
+ #endif
+
+ remove(position, checksumAction, dataAction);
+ }
+
+ (*messages_)[position] = message;
+
+ //
+ // We used the slot. Perform a new
+ // search at next run.
+ //
+
+ lastRated = nothing;
+
+ #ifdef TEST
+ *logofs << name() << ": Stored message object of size "
+ << plainSize(position) << " (" << message -> size_
+ << "/" << message -> c_size_ << ") at position "
+ << position << ".\n" << logofs_flush;
+ #endif
+
+ unsigned int localSize;
+ unsigned int remoteSize;
+
+ storageSize(message, localSize, remoteSize);
+
+ localStorageSize_ += localSize;
+ remoteStorageSize_ += remoteSize;
+
+ totalLocalStorageSize_ += localSize;
+ totalRemoteStorageSize_ += remoteSize;
+
+ #ifdef DEBUG
+
+ printStorageSize();
+
+ #endif
+
+ //
+ // Set hits and timestamp at insertion in cache.
+ //
+
+ message -> hits_ = control -> StoreHitsAddBonus;
+ message -> last_ = (getTimestamp()).tv_sec;
+
+ message -> locks_ = 0;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Set last hit of object at "
+ << strMsTimestamp() << " with a bonus of "
+ << message -> hits_ << ".\n" << logofs_flush;
+ #endif
+
+ return position;
+}
+
+//
+// Add a parsed message to repository. It is normally used
+// at decoding side or at encoding side when we load store
+// from disk. To handle messages coming from network, the
+// encoding side uses the optimized method findOrAdd().
+//
+
+int MessageStore::add(Message *message, const int position,
+ T_checksum_action checksumAction, T_data_action dataAction)
+{
+ if (position < 0 || position >= cacheSlots)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Cannot add a message "
+ << "at non existing position " << position
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot add a message "
+ << "at non existing position " << position
+ << ".\n";
+
+ HandleAbort();
+ }
+
+ if ((*messages_)[position] != NULL)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": The message will replace "
+ << "the old one at position " << position
+ << ".\n" << logofs_flush;
+ #endif
+
+ remove(position, checksumAction, dataAction);
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Inserting object in repository at position "
+ << position << ".\n" << logofs_flush;
+ #endif
+
+ (*messages_)[position] = message;
+
+ //
+ // Get the object's checksum value
+ // and insert it in the table.
+ //
+
+ if (checksumAction == use_checksum)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Inserting object's checksum in repository.\n";
+ #endif
+
+ T_checksum checksum = getChecksum(message);
+
+ checksums_ -> insert(T_checksums::value_type(checksum, position));
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Stored message object of size "
+ << plainSize(position) << " (" << message -> size_
+ << "/" << message -> c_size_ << ") at position "
+ << position << ".\n" << logofs_flush;
+ #endif
+
+ unsigned int localSize;
+ unsigned int remoteSize;
+
+ storageSize(message, localSize, remoteSize);
+
+ localStorageSize_ += localSize;
+ remoteStorageSize_ += remoteSize;
+
+ totalLocalStorageSize_ += localSize;
+ totalRemoteStorageSize_ += remoteSize;
+
+ #ifdef DEBUG
+
+ printStorageSize();
+
+ #endif
+
+ //
+ // Set hits and timestamp at insertion in cache.
+ //
+
+ message -> hits_ = control -> StoreHitsAddBonus;
+ message -> last_ = (getTimestamp()).tv_sec;
+
+ message -> locks_ = 0;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Set last hit of object at "
+ << strMsTimestamp() << " with a bonus of "
+ << message -> hits_ << ".\n" << logofs_flush;
+ #endif
+
+ return position;
+}
+
+//
+// The following functions don't modify data,
+// so they are supposed to be called only at
+// the encoding side.
+//
+
+void MessageStore::updateData(const int position, unsigned int dataSize,
+ unsigned int compressedDataSize)
+{
+ Message *message = (*messages_)[position];
+
+ validateSize(dataSize, compressedDataSize);
+
+ if (compressedDataSize != 0)
+ {
+ unsigned int localSize;
+ unsigned int remoteSize;
+
+ storageSize(message, localSize, remoteSize);
+
+ localStorageSize_ -= localSize;
+ remoteStorageSize_ -= remoteSize;
+
+ totalLocalStorageSize_ -= localSize;
+ totalRemoteStorageSize_ -= remoteSize;
+
+ message -> c_size_ = compressedDataSize + message -> i_size_;
+
+ #ifdef TEST
+
+ if (message -> size_ != (int) (dataSize + message -> i_size_))
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Size of object looks "
+ << message -> size_ << " bytes while it "
+ << "should be " << dataSize + message -> i_size_
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Size of object looks "
+ << message -> size_ << " bytes while it "
+ << "should be " << dataSize + message -> i_size_
+ << ".\n";
+
+ HandleAbort();
+ }
+
+ #endif
+
+ storageSize(message, localSize, remoteSize);
+
+ localStorageSize_ += localSize;
+ remoteStorageSize_ += remoteSize;
+
+ totalLocalStorageSize_ += localSize;
+ totalRemoteStorageSize_ += remoteSize;
+
+ #ifdef DEBUG
+
+ printStorageSize();
+
+ #endif
+ }
+}
+
+void MessageStore::updateData(const T_checksum checksum, unsigned int compressedDataSize)
+{
+ #ifdef TEST
+ *logofs << name() << ": Searching checksum ["
+ << DumpChecksum(checksum) << "] in repository.\n"
+ << logofs_flush;
+ #endif
+
+ T_checksums::iterator found = checksums_ -> find(checksum);
+
+ if (found != checksums_ -> end())
+ {
+ Message *message = (*messages_)[found -> second];
+
+ #ifdef TEST
+ *logofs << name() << ": Message found in cache at "
+ << "position " << found -> second << " with size "
+ << message -> size_ << " and compressed size "
+ << message -> c_size_ << ".\n" << logofs_flush;
+ #endif
+
+ updateData(found -> second, message -> size_ -
+ message -> i_size_, compressedDataSize);
+ }
+ #ifdef TEST
+ else if (checksums_ -> size() > 0)
+ {
+ *logofs << name() << ": WARNING! Can't locate the "
+ << "checksum [" << DumpChecksum(checksum)
+ << "] for the update.\n" << logofs_flush;
+ }
+ #endif
+}
+
+//
+// This function replaces the data part of the message
+// and updates the information about its size. Split
+// messages are advertised to the decoding side with
+// their uncompressed size, data is then compressed
+// before sending the first chunk. This function is
+// called by the decoding side after the split message
+// is fully recomposed to replace the dummy data and
+// set the real size.
+//
+
+void MessageStore::updateData(const int position, const unsigned char *newData,
+ unsigned int dataSize, unsigned int compressedDataSize)
+{
+ Message *message = (*messages_)[position];
+
+ validateSize(dataSize, compressedDataSize);
+
+ #ifdef TEST
+
+ if (message -> size_ != (int) (dataSize + message -> i_size_))
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Data of object looks "
+ << dataSize << " bytes while it " << "should be "
+ << message -> size_ - message -> i_size_
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Data of object looks "
+ << dataSize << " bytes while it " << "should be "
+ << message -> size_ - message -> i_size_
+ << ".\n";
+
+ HandleAbort();
+ }
+
+ #endif
+
+ //
+ // A compressed data size of 0 means that
+ // message's data was not compressed.
+ //
+
+ if (compressedDataSize != 0)
+ {
+ unsigned int localSize;
+ unsigned int remoteSize;
+
+ storageSize(message, localSize, remoteSize);
+
+ localStorageSize_ -= localSize;
+ remoteStorageSize_ -= remoteSize;
+
+ totalLocalStorageSize_ -= localSize;
+ totalRemoteStorageSize_ -= remoteSize;
+
+ if (message -> c_size_ != (int) compressedDataSize +
+ message -> i_size_)
+ {
+ #ifdef TEST
+ *logofs << name() << ": Resizing data of message at "
+ << "position " << position << " from " << message ->
+ c_size_ << " to " << compressedDataSize +
+ message -> i_size_ << " bytes.\n"
+ << logofs_flush;
+ #endif
+
+ message -> data_.clear();
+
+ message -> data_.resize(compressedDataSize);
+ }
+
+ memcpy(message -> data_.begin(), newData, compressedDataSize);
+
+ #ifdef TEST
+ *logofs << name() << ": Data of message at position "
+ << position << " has size " << message -> data_.size()
+ << " and capacity " << message -> data_.capacity()
+ << ".\n" << logofs_flush;
+ #endif
+
+ message -> c_size_ = compressedDataSize + message -> i_size_;
+
+ storageSize(message, localSize, remoteSize);
+
+ localStorageSize_ += localSize;
+ remoteStorageSize_ += remoteSize;
+
+ totalLocalStorageSize_ += localSize;
+ totalRemoteStorageSize_ += remoteSize;
+
+ #ifdef DEBUG
+
+ printStorageSize();
+
+ #endif
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << name() << ": No changes to data size for message "
+ << "at position " << position << ".\n" << logofs_flush;
+ #endif
+
+ memcpy(message -> data_.begin(), newData, dataSize);
+ }
+}
+
+int MessageStore::remove(const int position, T_checksum_action checksumAction,
+ T_data_action dataAction)
+{
+ Message *message;
+
+ if (position < 0 || position >= cacheSlots ||
+ (message = (*messages_)[position]) == NULL)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Cannot remove "
+ << "a non existing message at position "
+ << position << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot remove "
+ << "a non existing message at position "
+ << position << ".\n";
+
+ HandleAbort();
+ }
+
+ #if defined(TEST) || defined(INFO)
+
+ if (opcode() == X_PutImage || opcode() == X_NXPutPackedImage)
+ {
+ #ifdef WARNING
+ *logofs << name() << ": WARNING! Discarding image object "
+ << "of type " << name() << " at position "
+ << position << ".\n" << logofs_flush;
+ #endif
+ }
+
+ #endif
+
+ //
+ // The checksum is only stored at the encoding
+ // side.
+ //
+
+ if (checksumAction == use_checksum)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Removing checksum for object at "
+ << "position " << position << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // TODO: If we had stored the iterator and
+ // not the pointer to the message, we could
+ // have removed the message without having
+ // to look up the checksum.
+ //
+
+ T_checksum checksum = getChecksum(message);
+
+ #ifdef TEST
+ *logofs << name() << ": Searching checksum ["
+ << DumpChecksum(checksum) << "] in repository.\n"
+ << logofs_flush;
+ #endif
+
+ T_checksums::iterator found = checksums_ -> find(checksum);
+
+ if (found == checksums_ -> end())
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! No checksum found for "
+ << "object at position " << position << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": No checksum found for "
+ << "object at position " << position << ".\n";
+
+ HandleAbort();
+ }
+
+ #ifdef TEST
+
+ else if (position != found -> second)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Value of position for object "
+ << "doesn't match position " << position << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Value of position for object "
+ << "doesn't match position " << position << ".\n";
+
+ HandleAbort();
+ }
+
+ #endif
+
+ checksums_ -> erase(found);
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Removing message at position "
+ << position << " of size " << plainSize(position)
+ << " (" << message -> size_ << "/" << message -> c_size_
+ << ").\n" << logofs_flush;
+ #endif
+
+ unsigned int localSize;
+ unsigned int remoteSize;
+
+ storageSize(message, localSize, remoteSize);
+
+ localStorageSize_ -= localSize;
+ remoteStorageSize_ -= remoteSize;
+
+ totalLocalStorageSize_ -= localSize;
+ totalRemoteStorageSize_ -= remoteSize;
+
+ recycle(message);
+
+ (*messages_)[position] = NULL;
+
+ #ifdef DEBUG
+
+ printStorageSize();
+
+ #endif
+
+ return position;
+}
+
+//
+// This should only be called at encoding side.
+// The decoding side can't rely on the counter
+// as it is decremented by the encoding side
+// every time the repository is searched for a
+// message to be removed.
+//
+
+int MessageStore::getRating(Message *message, T_rating type) const
+{
+ if (message -> locks_ != 0)
+ {
+ #ifdef TEST
+ *logofs << name() << ": Rate set to -1 as locks of object are "
+ << (int) message -> locks_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ else if ((type == rating_for_clean ||
+ (int) checksums_ -> size() == cacheSlots) &&
+ message -> hits_ <= control -> StoreHitsLoadBonus)
+ {
+ //
+ // We don't have any free slot or we exceeded the
+ // available storage size. This is likely to happen
+ // after having loaded objects from persistent cache.
+ // It's not a bad idea to discard some messages that
+ // were restored but never referenced.
+ //
+
+ #ifdef TEST
+
+ if (type == rating_for_clean)
+ {
+ *logofs << name() << ": Rate set to 0 with hits "
+ << message -> hits_ << " as maximum storage size "
+ << "was exceeded.\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << name() << ": Rate set to 0 with hits "
+ << message -> hits_ << " as there are no available "
+ << "slots in store.\n" << logofs_flush;
+ }
+
+ #endif
+
+ return 0;
+ }
+ else if (type == rating_for_clean &&
+ (getTimestamp()).tv_sec - message -> last_ >=
+ control -> StoreTimeLimit)
+ {
+ #ifdef TEST
+ *logofs << name() << ": Rate set to 0 as last hit of object was "
+ << (getTimestamp()).tv_sec - message -> last_
+ << " seconds ago with limit set to " << control ->
+ StoreTimeLimit << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else
+ {
+ #ifdef TEST
+ if (message -> hits_ < 0)
+ {
+ *logofs << name() << ": PANIC! Rate of object shouldn't be "
+ << message -> hits_ << ".\n" << logofs_flush;
+
+ cerr << "Error" << ": Rate of object of type " << name()
+ << " shouldn't be " << message -> hits_ << ".\n";
+
+ HandleAbort();
+ }
+ #endif
+
+ #ifdef TEST
+ *logofs << name() << ": Rate of object is " << message -> hits_
+ << " with last hit " << (getTimestamp()).tv_sec -
+ message -> last_ << " seconds ago.\n"
+ << logofs_flush;
+ #endif
+
+ return message -> hits_;
+ }
+}
+
+int MessageStore::touch(Message *message) const
+{
+ message -> last_ = (getTimestamp()).tv_sec;
+
+ message -> hits_ += control -> StoreHitsTouch;
+
+ if (message -> hits_ > control -> StoreHitsLimit)
+ {
+ message -> hits_ = control -> StoreHitsLimit;
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Increased hits of object to "
+ << message -> hits_ << " at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ #endif
+
+ return message -> hits_;
+}
+
+int MessageStore::untouch(Message *message) const
+{
+ message -> hits_ -= control -> StoreHitsUntouch;
+
+ if (message -> hits_ < 0)
+ {
+ message -> hits_ = 0;
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Decreased hits of object to "
+ << message -> hits_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return message -> hits_;
+}
+
+int MessageStore::lock(const int position) const
+{
+ Message *message = (*messages_)[position];
+
+ if (message == NULL)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Can't lock the null "
+ << "object at position " << position
+ << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Increasing locks of object to "
+ << (int) message -> locks_ + 1 << ".\n"
+ << logofs_flush;
+ #endif
+
+ return ++(message -> locks_);
+}
+
+int MessageStore::unlock(const int position) const
+{
+ Message *message = (*messages_)[position];
+
+ if (message == NULL)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Can't unlock the null "
+ << "object at position " << position
+ << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decreasing locks of object to "
+ << (int) message -> locks_ - 1 << ".\n"
+ << logofs_flush;
+ #endif
+
+ return --(message -> locks_);
+}
+
+int MessageStore::saveStore(ostream *cachefs, md5_state_t *md5StateStream,
+ md5_state_t *md5StateClient, T_checksum_action checksumAction,
+ T_data_action dataAction, int bigEndian)
+{
+ Message *message;
+
+ #ifdef TEST
+ *logofs << name() << ": Opcode of this store is "
+ << (unsigned int) opcode() << " default size of "
+ << "identity is " << dataOffset << ".\n"
+ << logofs_flush;
+ #endif
+
+ unsigned char *identityBuffer = new unsigned char[dataOffset];
+ unsigned char *sizeBuffer = new unsigned char[4 * 2];
+ unsigned char *positionBuffer = new unsigned char[4];
+ unsigned char *opcodeBuffer = new unsigned char[4];
+
+ #ifdef DUMP
+
+ char *md5ClientDump = new char[dataOffset * 2 + 128];
+
+ #endif
+
+ unsigned char value;
+
+ int offset;
+
+ int failed = 0;
+
+ for (int position = 0; position < cacheSlots; position++)
+ {
+ message = (*messages_)[position];
+
+ //
+ // Don't save split messages.
+ //
+
+ if (message != NULL && message -> locks_ == 0)
+ {
+ //
+ // Use the total size if offset is
+ // beyond the real end of message.
+ //
+
+ offset = dataOffset;
+
+ if (offset > message -> size_)
+ {
+ offset = message -> size_;
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Going to save message at position "
+ << position << ".\n" << logofs_flush;
+ #endif
+
+ value = 1;
+
+ PutULONG(position, positionBuffer, bigEndian);
+ PutULONG(opcode(), opcodeBuffer, bigEndian);
+
+ md5_append(md5StateClient, positionBuffer, 4);
+ md5_append(md5StateClient, opcodeBuffer, 4);
+
+ #ifdef DUMP
+
+ *logofs << "Name=" << name() << logofs_flush;
+
+ sprintf(md5ClientDump," Pos=%d Op=%d\n", position, opcode());
+
+ *logofs << md5ClientDump << logofs_flush;
+
+ #endif
+
+ if (PutData(cachefs, &value, 1) < 0)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": PANIC! Failure writing " << 1
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ failed = 1;
+
+ break;
+ }
+
+ md5_append(md5StateStream, &value, 1);
+
+ PutULONG(message -> size_, sizeBuffer, bigEndian);
+ PutULONG(message -> c_size_, sizeBuffer + 4, bigEndian);
+
+ //
+ // Note that the identity size is not saved with
+ // the message and will be determined from the
+ // data read when restoring the identity.
+ //
+
+ if (PutData(cachefs, sizeBuffer, 4 * 2) < 0)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": PANIC! Failure writing " << 4 * 2
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ failed = 1;
+
+ break;
+ }
+
+ md5_append(md5StateStream, sizeBuffer, 4 * 2);
+ md5_append(md5StateClient, sizeBuffer, 4 * 2);
+
+ #ifdef DUMP
+
+ sprintf(md5ClientDump, "size = %d c_size = %d\n",
+ message -> size_, message -> c_size_);
+
+ *logofs << md5ClientDump << logofs_flush;
+
+ #endif
+
+ //
+ // Prepare a clean buffer for unparse.
+ //
+
+ CleanData(identityBuffer, offset);
+
+ unparseIdentity(message, identityBuffer, offset, bigEndian);
+
+ if (PutData(cachefs, identityBuffer, offset) < 0)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": PANIC! Failure writing " << offset
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ failed = 1;
+
+ break;
+ }
+
+ md5_append(md5StateStream, identityBuffer, offset);
+ md5_append(md5StateClient, identityBuffer, offset);
+
+ #ifdef DUMP
+
+ for (int i = 0; i < offset; i++)
+ {
+ sprintf(md5ClientDump + (i * 2), "%02X", identityBuffer[i]);
+ }
+
+ *logofs << "Identity = " << md5ClientDump << "\n" << logofs_flush;
+
+ #endif
+
+ //
+ // Set the real identity size before
+ // saving the data.
+ //
+
+ offset = message -> i_size_;
+
+ if (offset > message -> size_)
+ {
+ offset = message -> size_;
+ }
+
+ if (checksumAction == use_checksum)
+ {
+ if (PutData(cachefs, message -> md5_digest_, MD5_LENGTH) < 0)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": PANIC! Failure writing " << MD5_LENGTH
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ failed = 1;
+
+ break;
+ }
+
+ md5_append(md5StateStream, message -> md5_digest_, MD5_LENGTH);
+ }
+ else if (dataAction == use_data)
+ {
+ int dataSize = (message -> c_size_ == 0 ?
+ message -> size_ - offset :
+ message -> c_size_ - offset);
+ if (dataSize > 0)
+ {
+ if (PutData(cachefs, message -> data_.begin(), dataSize) < 0)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": PANIC! Failure writing " << dataSize
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ failed = 1;
+
+ break;
+ }
+
+ md5_append(md5StateStream, message -> data_.begin(), dataSize);
+ }
+ }
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << name() << ": Not saving message at position "
+ << position << ".\n" << logofs_flush;
+ #endif
+
+ value = 0;
+
+ if (PutData(cachefs, &value, 1) < 0)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": PANIC! Failure writing " << 1
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ failed = 1;
+
+ break;
+ }
+
+ md5_append(md5StateStream, &value, 1);
+ }
+ }
+
+ if (failed == 1)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Write to persistent cache file failed.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Write to persistent cache file failed.\n";
+ }
+
+ delete [] identityBuffer;
+ delete [] sizeBuffer;
+ delete [] positionBuffer;
+ delete [] opcodeBuffer;
+
+ #ifdef DUMP
+
+ delete [] md5ClientDump;
+
+ #endif
+
+ return (failed == 0 ? 1 : -1);
+}
+
+int MessageStore::loadStore(istream *cachefs, md5_state_t *md5StateStream,
+ T_checksum_action checksumAction, T_data_action dataAction,
+ int bigEndian)
+{
+ Message *message;
+
+ #ifdef TEST
+ *logofs << name() << ": Opcode of this store is "
+ << (unsigned int) opcode() << " default size of "
+ << "identity is " << dataOffset << " slots are "
+ << cacheSlots << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // If packed images or the render extension has been
+ // disabled we don't need to restore these messages
+ // in the cache. Encoding of RENDER in 1.4.0 is also
+ // changed so we want to skip messages saved using
+ // the old format. We want to restore all the other
+ // messages so we'll need to skip these one by one.
+ //
+
+ int skip = 0;
+
+ if ((opcode() == X_NXPutPackedImage &&
+ control -> PersistentCacheLoadPacked == 0) ||
+ (opcode() == X_NXInternalRenderExtension &&
+ control -> PersistentCacheLoadRender == 0))
+ {
+ #ifdef TEST
+ *logofs << name() << ": All messages for OPCODE#"
+ << (unsigned int) opcode() << " will be discarded.\n"
+ << logofs_flush;
+ #endif
+
+ skip = 1;
+ }
+
+ unsigned char *identityBuffer = new unsigned char[dataOffset];
+ unsigned char *sizeBuffer = new unsigned char[4 * 2];
+
+ unsigned char value;
+
+ int offset;
+
+ int failed = 0;
+
+ for (int position = 0; position < cacheSlots; position++)
+ {
+ if (GetData(cachefs, &value, 1) < 0)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": PANIC! Failure reading " << 1
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ failed = 1;
+
+ break;
+ }
+
+ md5_append(md5StateStream, &value, 1);
+
+ if (value == 1)
+ {
+ #ifdef TEST
+ *logofs << name() << ": Going to load message at position "
+ << position << ".\n" << logofs_flush;
+ #endif
+
+ if (GetData(cachefs, sizeBuffer, 4 * 2) < 0)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": PANIC! Failure reading " << 4 * 2
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ failed = 1;
+
+ break;
+ }
+
+ md5_append(md5StateStream, sizeBuffer, 4 * 2);
+
+ message = getTemporary();
+
+ if (message == NULL)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Can't access temporary storage "
+ << "for message in context [B].\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't access temporary storage "
+ << "for message in context [B].\n";
+
+ failed = 1;
+
+ break;
+ }
+
+ message -> size_ = GetULONG(sizeBuffer, bigEndian);
+ message -> c_size_ = GetULONG(sizeBuffer + 4, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Size is " << message -> size_
+ << " compressed size is " << message -> c_size_
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Use the total size if offset is
+ // beyond the real end of message.
+ //
+
+ offset = dataOffset;
+
+ if (offset > message -> size_)
+ {
+ offset = message -> size_;
+ }
+
+ if (GetData(cachefs, identityBuffer, offset) < 0)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": PANIC! Failure reading " << offset
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ failed = 1;
+
+ break;
+ }
+
+ md5_append(md5StateStream, identityBuffer, offset);
+
+ //
+ // Get the real identity size based on the value
+ // reported by the message store. The dataOffset
+ // value is guaranteed to be greater or equal to
+ // the maximum identity size of the messages in
+ // the major store.
+ //
+
+ offset = identitySize(identityBuffer, offset);
+
+ if (offset > message -> size_)
+ {
+ offset = message -> size_;
+ }
+
+ message -> i_size_ = offset;
+
+ //
+ // Get identity of message from the buffer we just
+ // created. Don't calculate neither checksum nor
+ // data, restore them from stream. Don't pass the
+ // message's size but the default size of identity.
+ //
+
+ parseIdentity(message, identityBuffer, offset, bigEndian);
+
+ if (checksumAction == use_checksum)
+ {
+ if (message -> md5_digest_ == NULL)
+ {
+ message -> md5_digest_ = new md5_byte_t[MD5_LENGTH];
+ }
+
+ if (GetData(cachefs, message -> md5_digest_, MD5_LENGTH) < 0)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": PANIC! Failure reading " << MD5_LENGTH
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ failed = 1;
+
+ break;
+ }
+
+ //
+ // Add message's checksum to checksum that will
+ // be saved together with this cache. Checksum
+ // will be verified when cache file is restored
+ // to ensure file is not corrupted.
+ //
+
+ md5_append(md5StateStream, message -> md5_digest_, MD5_LENGTH);
+
+ if (skip == 1)
+ {
+ #ifdef TEST
+ *logofs << name() << ": Discarding message for OPCODE#"
+ << (unsigned int) opcode() << ".\n"
+ << logofs_flush;
+ #endif
+
+ continue;
+ }
+ }
+ else if (dataAction == use_data)
+ {
+ //
+ // Restore the data part.
+ //
+
+ int dataSize = (message -> c_size_ == 0 ?
+ message -> size_ - offset :
+ message -> c_size_ - offset);
+
+ if (dataSize < 0 || dataSize > control -> MaximumMessageSize)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Bad data size "
+ << dataSize << " loading persistent cache.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Bad data size " << dataSize
+ << " loading persistent cache.\n";
+
+ failed = 1;
+
+ break;
+ }
+ else if (dataSize > 0)
+ {
+ //
+ // If need to skip the message let anyway
+ // it to be part of the calculated MD5.
+ //
+
+ if (skip == 1)
+ {
+ unsigned char *dummy = new unsigned char[dataSize];
+
+ if (dummy == NULL)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Can't allocate dummy buffer "
+ << "of size " << dataSize << " loading cache.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate dummy buffer "
+ << "of size " << dataSize << " loading cache.\n";
+
+ failed = 1;
+
+ break;
+ }
+
+ if (GetData(cachefs, dummy, dataSize) < 0)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": PANIC! Failure reading " << dataSize
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ failed = 1;
+
+ break;
+ }
+
+ md5_append(md5StateStream, dummy, dataSize);
+
+ delete [] dummy;
+
+ #ifdef TEST
+ *logofs << name() << ": Discarding message for OPCODE#"
+ << (unsigned int) opcode() << ".\n"
+ << logofs_flush;
+ #endif
+
+ continue;
+ }
+ else
+ {
+ message -> data_.clear();
+
+ message -> data_.resize(dataSize);
+
+ if (GetData(cachefs, message -> data_.begin(), dataSize) < 0)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": PANIC! Failure reading " << dataSize
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ failed = 1;
+
+ break;
+ }
+
+ //
+ // Add message's data to cache checksum.
+ //
+
+ md5_append(md5StateStream, message -> data_.begin(), dataSize);
+ }
+ }
+ else
+ {
+ //
+ // We are here if data part is zero.
+ //
+
+ if (skip == 1)
+ {
+ #ifdef TEST
+ *logofs << name() << ": Discarding message for OPCODE#"
+ << (unsigned int) opcode() << ".\n"
+ << logofs_flush;
+ #endif
+
+ continue;
+ }
+ }
+ }
+
+ int added;
+
+ added = add(message, position, checksumAction, dataAction);
+
+ if (added != position)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Can't store message "
+ << "in the cache at position " << position
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't store message "
+ << "in the cache at position " << position
+ << ".\n";
+
+ failed = 1;
+
+ break;
+ }
+ else
+ {
+ //
+ // Replace default value of hits set by add
+ // function. Messages read from cache start
+ // with a lower bonus than fresh messages
+ // inserted.
+ //
+
+ message -> hits_ = control -> StoreHitsLoadBonus;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Updated last hit of object at "
+ << strMsTimestamp() << " with a bonus of "
+ << message -> hits_ << ".\n" << logofs_flush;
+ #endif
+
+ resetTemporary();
+ }
+ }
+ else if ((*messages_)[position] != NULL)
+ {
+ #ifdef TEST
+ *logofs << name() << ": Going to remove message at position "
+ << position << ".\n" << logofs_flush;
+ #endif
+
+ int removed;
+
+ removed = remove(position, checksumAction, dataAction);
+
+ if (removed != position)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Can't remove message from cache "
+ << "at position " << position << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't remove message from cache "
+ << "at position " << position << ".\n";
+
+ failed = 1;
+
+ break;
+ }
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << name() << ": Not loading message at position "
+ << position << ".\n" << logofs_flush;
+ }
+ #endif
+ }
+
+ #ifdef WARNING
+
+ if (failed == 1)
+ {
+ *logofs << name() << ": WARNING! Read from persistent cache file failed.\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ delete [] identityBuffer;
+ delete [] sizeBuffer;
+
+ return (failed == 0 ? 1 : -1);
+}
+
+void MessageStore::storageSize(const Message *message, unsigned int &local,
+ unsigned int &remote) const
+{
+ local = remote = storage();
+
+ //
+ // Encoding side includes 48 bytes for
+ // the map of checksums and 24 bytes
+ // of adjustment for total overhead.
+ //
+
+ local += MD5_LENGTH + 48 + 24;
+
+ //
+ // At decoding side we include size of
+ // data part and 24 bytes of adjustment
+ // for total overhead.
+ //
+
+ if (message -> c_size_ == 0)
+ {
+ remote += message -> size_ + 24;
+ }
+ else
+ {
+ remote += message -> c_size_ + 24;
+ }
+
+ //
+ // Check if we are the encoding or the
+ // decoding side and, if needed, swap
+ // the values.
+ //
+
+ if (message -> md5_digest_ == NULL)
+ {
+ unsigned int t = local;
+
+ local = remote;
+
+ remote = t;
+ }
+}
+
+void MessageStore::printStorageSize()
+{
+ #ifdef TEST
+
+ *logofs << name() << ": There are "
+ << checksums_ -> size() << " checksums in this store "
+ << "out of " << cacheSlots << " slots.\n"
+ << logofs_flush;
+
+ *logofs << name() << ": Size of this store is "
+ << localStorageSize_ << " bytes at local side and "
+ << remoteStorageSize_ << " bytes at remote side.\n"
+ << logofs_flush;
+
+ *logofs << name() << ": Size of total cache is "
+ << totalLocalStorageSize_ << " bytes at local side and "
+ << totalRemoteStorageSize_ << " bytes at remote side.\n"
+ << logofs_flush;
+
+ #endif
+}
diff --git a/nxcomp/Message.h b/nxcomp/Message.h
new file mode 100644
index 000000000..dcfff7cb6
--- /dev/null
+++ b/nxcomp/Message.h
@@ -0,0 +1,1102 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef Message_H
+#define Message_H
+
+#include <X11/Xproto.h>
+
+#include "NXproto.h"
+
+#include "Misc.h"
+#include "Control.h"
+
+#include "Types.h"
+#include "Timestamp.h"
+
+#include "ActionCache.h"
+
+#include "ActionCacheCompat.h"
+#include "PositionCacheCompat.h"
+
+#include "StaticCompressor.h"
+
+//
+// Forward class declarations.
+//
+
+class ChannelCache;
+
+class EncodeBuffer;
+class DecodeBuffer;
+
+class WriteBuffer;
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Define this to know how many messages
+// are allocated and deallocated.
+//
+
+#undef REFERENCES
+
+//
+// Set default values. We limit the maximum
+// size of a request to 262144 but we need to
+// consider the replies, whose size may be up
+// to 4MB.
+//
+
+#define MESSAGE_ENABLE_CACHE 0
+#define MESSAGE_ENABLE_DATA 0
+#define MESSAGE_ENABLE_SPLIT 0
+#define MESSAGE_ENABLE_COMPRESS 0
+
+#define MESSAGE_DATA_LIMIT 4194304 - 4
+#define MESSAGE_DATA_OFFSET 4
+
+#define MESSAGE_CACHE_SLOTS 6000
+#define MESSAGE_CACHE_THRESHOLD 50
+#define MESSAGE_CACHE_LOWER_THRESHOLD 5
+
+//
+// Base message class.
+//
+
+class Message
+{
+ friend class MessageStore;
+ friend class RenderExtensionStore;
+
+ public:
+
+ Message()
+ {
+ hits_ = 0;
+ last_ = 0;
+ locks_ = 0;
+
+ size_ = 0;
+ c_size_ = 0;
+
+ md5_digest_ = NULL;
+
+ #ifdef REFERENCES
+
+ references_++;
+
+ *logofs << "Message: Created new message at "
+ << this << " out of " << references_
+ << " allocated messages.\n"
+ << logofs_flush;
+
+ #endif
+ }
+
+ Message(const Message &message)
+ {
+ size_ = message.size_;
+ c_size_ = message.c_size_;
+ i_size_ = message.i_size_;
+
+ hits_ = message.hits_;
+ last_ = message.last_;
+ locks_ = message.locks_;
+
+ data_ = message.data_;
+
+ #ifdef REFERENCES
+
+ references_++;
+
+ *logofs << "Message: Creating new copied message at "
+ << this << " out of " << references_
+ << " allocated messages.\n"
+ << logofs_flush;
+ #endif
+
+ if (message.md5_digest_ != NULL)
+ {
+ md5_digest_ = new md5_byte_t[MD5_LENGTH];
+
+ memcpy(md5_digest_, message.md5_digest_, MD5_LENGTH);
+
+ #ifdef DEBUG
+ *logofs << "Message: Created MD5 digest for object at "
+ << this << ".\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ md5_digest_ = NULL;
+ }
+ }
+
+ ~Message()
+ {
+ #ifdef DEBUG
+ if (md5_digest_ != NULL)
+ {
+ *logofs << "Message: Deleted MD5 digest for object at "
+ << this << ".\n" << logofs_flush;
+ }
+ #endif
+
+ delete [] md5_digest_;
+
+ #ifdef REFERENCES
+
+ references_--;
+
+ *logofs << "Message: Deleted message at "
+ << this << " out of " << references_
+ << " allocated messages.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ //
+ // This is the original message size
+ // including the data part regardless
+ // data is still stored in the object.
+ //
+
+ int size_;
+
+ //
+ // This is the size of the identity.
+ //
+
+ int i_size_;
+
+ //
+ // This is the size, including identity,
+ // after message has been 'updated' to
+ // reflect storage of data in compressed
+ // format.
+ //
+
+ int c_size_;
+
+ protected:
+
+ //
+ // This is the data part.
+ //
+
+ T_data data_;
+
+ //
+ // Time of last hit.
+ //
+
+ time_t last_;
+
+ //
+ // This is the number of cache hits
+ // registered for the object.
+ //
+
+ short int hits_;
+
+ //
+ // This is used to mark messages
+ // that have been split.
+ //
+
+ short int locks_;
+
+ //
+ // This is the MD5 checksum.
+ //
+
+ md5_byte_t *md5_digest_;
+
+ //
+ // Keep a reference counter
+ // of allocated objects.
+ //
+
+ #ifdef REFERENCES
+
+ static int references_;
+
+ #endif
+};
+
+//
+// Repository of messages.
+//
+
+class MessageStore
+{
+ public:
+
+ //
+ // Enable or disable cache of messages in store.
+ //
+
+ int enableCache;
+
+ //
+ // Does message have a distinct data part.
+ //
+
+ int enableData;
+
+ //
+ // Enable or disable split of data part.
+ //
+
+ int enableSplit;
+
+ //
+ // Enable or disable compression of data part.
+ //
+
+ int enableCompress;
+
+ //
+ // Set starting point of data part in the message.
+ //
+
+ int dataOffset;
+
+ //
+ // Set maximum size for the data part of each message.
+ //
+
+ int dataLimit;
+
+ //
+ // Set maximum elements in cache.
+ //
+
+ int cacheSlots;
+
+ //
+ // Set the percentage of total cache memory which
+ // a given type of message is allowed to occupy.
+ // When threshold is exceeded store is cleaned to
+ // make room for a new message of the same type.
+ //
+
+ int cacheThreshold;
+
+ //
+ // Don't clean the store if percentage of cache
+ // memory occupied by messages of this type is
+ // below the threshold.
+ //
+
+ int cacheLowerThreshold;
+
+ //
+ // Last operation performed on cache.
+ //
+
+ T_store_action lastAction;
+
+ //
+ // Position of last element stored in cache.
+ //
+
+ short int lastAdded;
+
+ //
+ // Positions of last element found in cache.
+ //
+
+ short int lastHit;
+
+ //
+ // Position of last element erased.
+ //
+
+ short int lastRemoved;
+
+ //
+ // Used to encode the the action to
+ // perform on the store and the slot
+ // involved.
+ //
+
+ ActionCache lastActionCache;
+
+ //
+ // Used in old protocol versions.
+ //
+
+ ActionCacheCompat lastActionCacheCompat;
+
+ PositionCacheCompat lastAddedCacheCompat;
+ PositionCacheCompat lastHitCacheCompat;
+ PositionCacheCompat lastRemovedCacheCompat;
+
+ //
+ // Position in cache where next insertion
+ // is going to take place.
+ //
+
+ short int lastRated;
+
+ //
+ // Size of data part of last split message
+ // once compressed. This is used only for
+ // compatibility with older proxies.
+ //
+
+ int lastResize;
+
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ MessageStore(StaticCompressor *compressor = NULL);
+
+ virtual ~MessageStore();
+
+ virtual const char *name() const = 0;
+
+ virtual unsigned char opcode() const = 0;
+
+ virtual unsigned int storage() const = 0;
+
+ //
+ // These are members that must be specialized.
+ //
+
+ public:
+
+ virtual Message *create() const = 0;
+
+ virtual Message *create(const Message &message) const = 0;
+
+ virtual void destroy(Message *message) const = 0;
+
+ void validateSize(int size)
+ {
+ if (size < control -> MinimumMessageSize ||
+ size > control -> MaximumMessageSize)
+ {
+ *logofs << name() << ": PANIC! Invalid size " << size
+ << " for message.\n" << logofs_flush;
+
+ cerr << "Error" << ": Invalid size " << size
+ << " for message opcode " << opcode() << ".\n";
+
+ HandleAbort();
+ }
+ }
+
+ void validateSize(int dataSize, int compressedDataSize)
+ {
+ if (dataSize < 0 || dataSize > control ->
+ MaximumMessageSize - 4 || compressedDataSize < 0 ||
+ compressedDataSize >= dataSize)
+ {
+ *logofs << name() << ": PANIC! Invalid data size "
+ << dataSize << " and compressed data size "
+ << compressedDataSize << " for message.\n"
+ << logofs_flush;
+
+ cerr << "Error" << ": Invalid data size "
+ << dataSize << " and compressed data size "
+ << compressedDataSize << " for message "
+ << "opcode " << (unsigned) opcode() << ".\n";
+
+ HandleAbort();
+ }
+ }
+
+ //
+ // Determine if the message can be stored
+ // in the cache.
+ //
+
+ virtual int validateMessage(const unsigned char *buffer, int size)
+ {
+ return (size >= control -> MinimumMessageSize &&
+ size <= control -> MaximumMessageSize);
+ }
+
+ //
+ // Get data offset based on major and minor
+ // opcode of the message.
+ //
+
+ virtual int identitySize(const unsigned char *buffer, unsigned int size)
+ {
+ return dataOffset;
+ }
+
+ //
+ // Encode identity and data using the
+ // specific message encoding.
+ //
+ // Some messages do not implement these
+ // methods because the encoding is done
+ // directly in the channel loop. Should
+ // move the encoding methods in in the
+ // message classes.
+ //
+
+ virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+ {
+ return 1;
+ }
+
+ virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const
+ {
+ return 1;
+ }
+
+ //
+ // Encode differences between message
+ // in cache and the one to be encoded.
+ //
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage, ChannelCache *channelCache) const
+ {
+ }
+
+ //
+ // Decode differences and update the
+ // cached version of the same message.
+ //
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+ {
+ }
+
+ //
+ // Post process the message information
+ // contained in the store by either up-
+ // dating the size record or the actual
+ // data part once the message has been
+ // completely sent to our peer.
+ //
+
+ void updateData(const int position, unsigned int dataSize,
+ unsigned int compressedDataSize);
+
+ void updateData(const T_checksum checksum, unsigned int compressedDataSize);
+
+ void updateData(const int position, const unsigned char *newData,
+ unsigned int dataSize, unsigned int compressedDataSize);
+
+ //
+ // These members, used internally
+ // in the message store class, are
+ // mandatory.
+ //
+
+ protected:
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const = 0;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const = 0;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const = 0;
+
+ virtual void dumpIdentity(const Message *message) const = 0;
+
+ //
+ // Design should preserve these from being
+ // virtual.
+ //
+
+ int parseData(Message *message, int split, const unsigned char *buffer,
+ unsigned int size, T_checksum_action checksumAction,
+ T_data_action dataAction, int bigEndian);
+
+ int parseData(Message *message, const unsigned char *buffer,
+ unsigned int size, const unsigned char *compressedData,
+ const unsigned int compressedDataSize, T_checksum_action checksumAction,
+ T_data_action dataAction, int bigEndian);
+
+ int unparseData(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian);
+
+ //
+ // Manage efficient allocation of messages
+ // in the heap.
+ //
+
+ void recycle(Message *message)
+ {
+ #ifdef TEST
+
+ if (message == NULL)
+ {
+ *logofs << name() << ": PANIC! Cannot recycle a null message.\n"
+ << logofs_flush;
+
+ cerr << "Error" << ": Cannot recycle a null message.\n";
+
+ HandleAbort();
+ }
+
+ #endif
+
+ if (temporary_ == NULL)
+ {
+ //
+ // Be careful when reusing the message as
+ // it can contain valid data that must be
+ // explicitly deallocated if not needed.
+ // Note also that you cannot count on the
+ // initialization made in costructor.
+ //
+
+ temporary_ = message;
+ }
+ else
+ {
+ destroy(message);
+ }
+ }
+
+ void beginChecksum(Message *message)
+ {
+ if (message -> md5_digest_ == NULL)
+ {
+ message -> md5_digest_ = new md5_byte_t[MD5_LENGTH];
+
+ #ifdef DEBUG
+ *logofs << name() << ": Created MD5 digest structure "
+ << "for object at " << message << ".\n"
+ << logofs_flush;
+ #endif
+ }
+ #ifdef DEBUG
+ else
+ {
+ *logofs << name() << ": Using existing MD5 digest structure "
+ << "for object at " << message << ".\n"
+ << logofs_flush;
+ }
+ #endif
+
+ #ifdef DEBUG
+ *logofs << name() << ": Prepared MD5 digest for object at "
+ << message << ".\n" << logofs_flush;
+ #endif
+
+ md5_init(md5_state_);
+ }
+
+ void endChecksum(Message *message)
+ {
+ md5_finish(md5_state_, message -> md5_digest_);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Calculated checksum for object at "
+ << message << ".\n" << logofs_flush;
+ #endif
+ }
+
+ void dataChecksum(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian)
+ {
+ //
+ // Messages that have a data part starting
+ // at an offset possibly beyond the end of
+ // the message, must include the size in
+ // the identity checksum.
+ //
+
+ if ((int) size > message -> i_size_)
+ {
+ md5_append(md5_state_, buffer + message -> i_size_,
+ size - message -> i_size_);
+ }
+ }
+
+ //
+ // Repository handling methods.
+ //
+
+ public:
+
+ //
+ // Extract identity and data from buffer.
+ // The size field will be updated at the
+ // time of data parsing.
+ //
+
+ int parse(Message *message, int split, const unsigned char *buffer, unsigned int size,
+ T_checksum_action checksumAction, T_data_action dataAction, int bigEndian);
+
+ int parse(Message *message, const unsigned char *buffer, unsigned int size,
+ const unsigned char *compressedData, const unsigned int compressedDataSize,
+ T_checksum_action checksumAction, T_data_action dataAction, int bigEndian);
+
+ //
+ // From identity and data write the
+ // final message to the raw buffer.
+ //
+
+ int unparse(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian)
+ {
+ return (unparseData(message, buffer, size, bigEndian) &&
+ unparseIdentity(message, buffer, size, bigEndian));
+ }
+
+ void dump(const Message *message) const
+ {
+ dumpIdentity(message);
+
+ dumpData(message);
+ }
+
+ void dumpData(const Message *message) const;
+
+ //
+ // This returns the original message size as it
+ // was received on the link. It takes in account
+ // the data part, regardless data is still stored
+ // in the message object. This information will
+ // be used at the time message is unparsed.
+ //
+
+ int plainSize(const int position) const
+ {
+ return (*messages_)[position] -> size_;
+ }
+
+ //
+ // This returns either the size of identity plus
+ // the compressed data part or 0 if message is
+ // stored in uncompressed format.
+ //
+
+ int compressedSize(const int position) const
+ {
+ return (*messages_)[position] -> c_size_;
+ }
+
+ //
+ // Returns a pointer to message
+ // given its position in cache.
+ //
+
+ Message *get(const int position) const
+ {
+ if (position < 0 || position >= cacheSlots)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Requested position "
+ << position << " is not inside the "
+ << "container.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Requested position "
+ << position << " is not inside the"
+ << "container.\n";
+
+ HandleAbort();
+ }
+ else if ((*messages_)[position] == NULL)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Message at position "
+ << position << " is NULL.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Message at position "
+ << position << " is NULL.\n";
+
+ HandleAbort();
+ }
+
+ return (*messages_)[position];
+ }
+
+ //
+ // This is the method called at encoding
+ // side to add a message to cache.
+ //
+
+ int findOrAdd(Message *message, T_checksum_action checksumAction,
+ T_data_action dataAction, int &added, int &locked);
+
+ //
+ // Utility interfaces to message insertion
+ // and deletion.
+ //
+
+ int add(Message *message, const int position,
+ T_checksum_action checksumAction, T_data_action dataAction);
+
+ int remove(const int position, T_checksum_action checksumAction,
+ T_data_action dataAction);
+
+ //
+ // Make space in the repository by remove
+ // the first suitable message object.
+ //
+
+ int clean(T_checksum_action checksumAction);
+
+ //
+ // Increase or decrease the "rating" of
+ // the message object.
+ //
+
+ int touch(Message *message) const;
+ int untouch(Message *message) const;
+
+ int getTouches(const int position) const
+ {
+ Message *message = (*messages_)[position];
+
+ if (message == NULL)
+ {
+ return 0;
+ }
+
+ return message -> hits_;
+ }
+
+ //
+ // Gives a 'weight' to the cached message. A zero
+ // value means object can be safely removed. A value
+ // greater than zero means it is advisable to retain
+ // the object. A negative result means it is mandato-
+ // ry to keep object in cache.
+ //
+
+ int getRating(Message *message, T_rating type) const;
+
+ //
+ // Increase or decrease locks of message at given
+ // position. A locked message will not be removed
+ // from the message store until the lock counter
+ // is zero.
+ //
+
+ int lock(const int position) const;
+ int unlock(const int position) const;
+
+ int getLocks(const int position) const
+ {
+ Message *message = (*messages_)[position];
+
+ if (message == NULL)
+ {
+ return 0;
+ }
+
+ return message -> locks_;
+ }
+
+ T_checksum const getChecksum(const int position) const
+ {
+ return getChecksum(get(position));
+ }
+
+ T_checksum const getChecksum(const Message *message) const
+ {
+ if (message -> md5_digest_ == NULL)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Checksum not initialized "
+ << "for object at " << message << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Checksum not initialized "
+ << "for object at " << message << ".\n";
+
+ HandleAbort();
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Got checksum for object at "
+ << message << ".\n" << logofs_flush;
+ #endif
+
+ return message -> md5_digest_;
+ }
+
+ //
+ // Calculate the checksum on the fly based the
+ // opcode in the buffer. Useful in the case a
+ // message was not processed or was not stored
+ // in the cache. The returned checksum must be
+ // explicitly deallocated by the caller, after
+ // use.
+ //
+
+ T_checksum getChecksum(const unsigned char *buffer,
+ unsigned int size, int bigEndian);
+
+ const unsigned char *getData(const Message *message) const
+ {
+ return message -> data_.begin();
+ }
+
+ int plainSize(const Message *message) const
+ {
+ return message -> size_;
+ }
+
+ int identitySize(Message *message)
+ {
+ return message -> i_size_;
+ }
+
+ int compressedSize(const Message *message) const
+ {
+ return message -> c_size_;
+ }
+
+ Message *getTemporary()
+ {
+ if (temporary_ == NULL)
+ {
+ temporary_ = create();
+ }
+
+ return temporary_;
+ }
+
+ void resetTemporary()
+ {
+ temporary_ = NULL;
+ }
+
+ //
+ // On side where we don't have checksums, we
+ // count how many messages are in the array.
+ // This is obviously expensive and should be
+ // only performed when reporting statistics.
+ //
+
+ int getSize() const
+ {
+ int size = checksums_ -> size();
+
+ if (size == 0)
+ {
+ for (int i = 0; i < cacheSlots; i++)
+ {
+ if ((*messages_)[i] != NULL)
+ {
+ size++;
+ }
+ }
+ }
+
+ return size;
+ }
+
+ int getLocalStorageSize() const
+ {
+ return localStorageSize_;
+ }
+
+ int getRemoteStorageSize() const
+ {
+ return remoteStorageSize_;
+ }
+
+ int getLocalTotalStorageSize() const
+ {
+ return totalLocalStorageSize_;
+ }
+
+ int getRemoteTotalStorageSize() const
+ {
+ return totalRemoteStorageSize_;
+ }
+
+ static int getCumulativeTotalStorageSize()
+ {
+ return (totalLocalStorageSize_ > totalRemoteStorageSize_ ?
+ totalLocalStorageSize_ : totalRemoteStorageSize_);
+ }
+
+ int saveStore(ostream *cachefs, md5_state_t *md5_state_stream,
+ md5_state_t *md5_state_client, T_checksum_action checksumAction,
+ T_data_action dataAction, int bigEndian);
+
+ int loadStore(istream *cachefs, md5_state_t *md5_state_stream,
+ T_checksum_action checksumAction, T_data_action dataAction,
+ int bigEndian);
+
+ protected:
+
+ //
+ // Estimate the memory requirements of given
+ // instance of message. Size includes memory
+ // allocated from heap to store checksum and
+ // data.
+ //
+
+ void storageSize(const Message *message, unsigned int &local,
+ unsigned int &remote) const;
+
+ //
+ // Just used for debug.
+ //
+
+ void printStorageSize();
+
+ //
+ // Repositories where to save cached messages.
+ // First is a vector of pointers, the second
+ // is a hash table used for fast lookups.
+ //
+
+ T_messages *messages_;
+ T_checksums *checksums_;
+
+ //
+ // A message object to be used as a temporary.
+ // Reuse the temporary object if possible, if
+ // not, create a new instance.
+ //
+
+ Message *temporary_;
+
+ //
+ // Used to calculate message's checksum.
+ //
+
+ md5_state_t *md5_state_;
+
+ private:
+
+ //
+ // Used to compress data payload.
+ //
+
+ StaticCompressor *compressor_;
+
+ //
+ // Keep track of how many bytes
+ // are taken by cache.
+ //
+
+ int localStorageSize_;
+ int remoteStorageSize_;
+
+ static int totalLocalStorageSize_;
+ static int totalRemoteStorageSize_;
+
+ //
+ // Used to track object allocation and deallocation.
+ //
+
+ #ifdef REFERENCES
+
+ static int references_;
+
+ #endif
+};
+
+//
+// This is an ancillary class of the message
+// store, used to encode extensions based on
+// the minor opcode.
+//
+
+class MinorMessageStore
+{
+ public:
+
+ virtual ~MinorMessageStore()
+ {
+ }
+
+ virtual const char *name() const = 0;
+
+ virtual int identitySize(const unsigned char *buffer, unsigned int size) = 0;
+
+ virtual int encodeMessage(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+ {
+ return 1;
+ }
+
+ virtual int decodeMessage(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, unsigned char type, int bigEndian,
+ WriteBuffer *writeBuffer, ChannelCache *channelCache) const
+ {
+ return 1;
+ }
+
+ virtual void encodeData(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+ {
+ }
+
+ virtual void decodeData(DecodeBuffer &decodeBuffer, unsigned char *buffer,
+ unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+ {
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const = 0;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const = 0;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+ {
+ }
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+ {
+ }
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, md5_state_t *md5_state,
+ int bigEndian) const = 0;
+};
+
+#endif /* Message_H */
+
diff --git a/nxcomp/Misc.cpp b/nxcomp/Misc.cpp
new file mode 100644
index 000000000..2c72259e3
--- /dev/null
+++ b/nxcomp/Misc.cpp
@@ -0,0 +1,1893 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <errno.h>
+#include <string.h>
+
+#include "NXproto.h"
+
+#include "MD5.h"
+
+#include "Misc.h"
+#include "Proxy.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#define OPCODES
+#undef TEST
+#undef DEBUG
+
+//
+// TCP port offset applied to any NX port specification.
+//
+
+const int DEFAULT_NX_PROXY_PORT_OFFSET = 4000;
+
+//
+// Default TCP port used by client proxy to listen to
+// X clients and by server proxy to connect to remote.
+//
+
+const int DEFAULT_NX_PROXY_PORT = 8;
+
+//
+// Default X display number that client proxy imitates.
+//
+
+const int DEFAULT_NX_X_PORT = 8;
+
+//
+// Default ports used for listening for cups, samba, http,
+// multimedia and auxiliary X connections. Arbitrary ports
+// can be used by passing the service's port at the proxy
+// startup. By default ports are determined by adding the
+// offset below to the offset of the proxied display. For
+// example, if the proxy is impersonating the display :8,
+// SMB tunnels can be created by connecting to port 3008.
+//
+// Considering that the NX server uses to start the first
+// session at display offset 1000, we must lower the CUPS
+// and SMB ports to avoid interference with normal X ses-
+// sions run on the server.
+//
+// Font server connections are used to let the X server on
+// the client connect to a font server on the NX server.
+//
+// Slave channels can be originated by both sides so we need
+// different offsets in the case the user runs both proxies
+// on the same host.
+//
+
+const int DEFAULT_NX_CUPS_PORT_OFFSET = 2000;
+const int DEFAULT_NX_SMB_PORT_OFFSET = 3000;
+const int DEFAULT_NX_MEDIA_PORT_OFFSET = 7000;
+const int DEFAULT_NX_AUX_PORT_OFFSET = 8000;
+const int DEFAULT_NX_HTTP_PORT_OFFSET = 9000;
+const int DEFAULT_NX_FONT_PORT_OFFSET = 10000;
+
+const int DEFAULT_NX_SLAVE_PORT_CLIENT_OFFSET = 11000;
+const int DEFAULT_NX_SLAVE_PORT_SERVER_OFFSET = 12000;
+
+//
+// Usage info and copyright.
+//
+
+static const char UsageInfo[] =
+"\n\
+ Usage: nxproxy [OPTIONS] host:port\n\
+\n\
+ -C Specify that nxproxy has to run on the 'X client'\n\
+ side, listening for connections and impersonating\n\
+ an X server.\n\
+\n\
+ -S Specify that nxproxy has to run in 'X server' mode,\n\
+ thus forwarding the connections to daemons running\n\
+ on the client.\n\
+\n\
+ -h Print this message.\n\
+\n\
+ -v Print version information.\n\
+\n\
+ host:port Put at the end, specifies the host and port of the\n\
+ listening proxy.\n\
+\n\
+ name=value Set the NX option to the provided value.\n\
+\n\
+ Multiple NX options can be specified in the DISPLAY environment\n\
+ or on the command line, by using the nx/nx,option=value notation.\n\
+\n\
+ Options:\n\
+\n\
+ link=s An indication of the link speed that is going to be\n\
+ used between the proxies. Usually the compression\n\
+ and the other link parameters depend on this setting.\n\
+ The value can be either 'modem', 'isdn', 'adsl',\n\
+ 'wan', 'lan', 'local' or a bandwidth specification,\n\
+ like for example '56k', '1m', '100m', etc.\n\
+\n\
+ type=s Type of session, for example 'windows', 'unix-kde'.\n\
+ 'unix-application', etc.\n\
+\n\
+ display=s Specify the real display where X connections have\n\
+ to be forwarded by the proxy running on the client.\n\
+\n\
+ listen=n Local port used for accepting the proxy connection.\n\
+\n\
+ accept=s Name or IP of host that can connect to the proxy.\n\
+\n\
+ connect=s Name or IP of host that the proxy will connect to.\n\
+\n\
+ port=n Remote port used for the connection.\n\
+\n\
+ retry=n Number of connection atempts.\n\
+\n\
+ root=s The root directory for the session. Usually is the\n\
+ C-* or S-* in the .nx directory in the user's home,\n\
+ with '*' being the virtual display.\n\
+\n\
+ session=s Name of the session file. The default is the name\n\
+ 'session' in the session directory.\n\
+\n\
+ errors=s Name of the log file used by the proxy. The default\n\
+ is the name 'errors' in the session directory.\n\
+\n\
+ stats=s Name of the file where are written the proxy stat-\n\
+ istics. The default is a file 'stats' in the session\n\
+ directory. The proxy replaces the data in the file\n\
+ whenever it receives a SIGUSR1 or SIGUSR2 signal:\n\
+\n\
+ SIGUSR1 Gives total statistics, i.e. statistics\n\
+ collected since the beginning of the\n\
+ session.\n\
+\n\
+ SIGUSR2 Gives partial statistics, i.e. statist-\n\
+ ics collected since the last time this\n\
+ signal was received.\n\
+\n\
+ cookie=s Use the provided cookie for authenticating to the\n\
+ remote proxy. The same cookie is used as the fake\n\
+ value used for the X authorization. The fake cookie\n\
+ is replaced on the X server side with the real cookie\n\
+ to be used for the display, so that the real cookie\n\
+ doesn't have to travel over the net. When not using\n\
+ a proxy cookie, any host will be able to connect to\n\
+ the proxy. See also the 'accept' parameter.\n\
+\n\
+ nodelay=b A boolean indicating if TCP_NODELAY has to be set\n\
+ on the proxy link. Old Linux kernels had problems\n\
+ with handling TCP_NODELAY on PPP links.\n\
+\n\
+ policy=b Let or not the agent decide when it is the best time\n\
+ to flush the proxy link. If set to 0, the proxy will\n\
+ flush any encoded data immediately. The option has\n\
+ only effect on the X client side proxy.\n\
+\n\
+ render=b Enable or disable use of the RENDER extension.\n\
+\n\
+ taint=b Try to suppress trivial sources of X roundtrips by\n\
+ generating the reply on the X client side.\n\
+\n\
+ delta=b Enable X differential compression.\n\
+\n\
+ data=n Enable or disable the ZLIB data compression. It is\n\
+ possible to specify a value between 0 and 9. Usual-\n\
+ ly the value is chosen automatically based on the\n\
+ requested link setting.\n\
+\n\
+ stream=n Enable or disable the ZLIB stream compression. The\n\
+ value, between 0 and 9, is usually determined accor-\n\
+ ding to the requested link setting.\n\
+\n\
+ limit=n Specify a bitrate limit allowed for this session.\n\
+\n\
+ memory=n Trigger memory optimizations used to keep small the\n\
+ size of X buffers. This is useful on embedded plat-\n\
+ forms, or where memory is scarce.\n\
+\n\
+ cache=n Size of the in-memory X message cache. Setting the\n\
+ value to 0 will disable the memory cache as well\n\
+ as the NX differential compression.\n\
+\n\
+ images=n Size of the persistent image cache.\n\
+\n\
+ shseg=n Enable the use of the MIT-SHM extension between the\n\
+ NX client proxy and the real X server. A value greater\n\
+ than 1 is assumed to be the size of requested shared\n\
+ memory segment. By default, the size of the segment is\n\
+ determined based on the size of the in-memory cache.\n\
+\n\
+ load=b Enable loading a persistent X message cache at the\n\
+ proxy startup.\n\
+\n\
+ save=b Enable saving a persistent X message cache at the\n\
+ end of session.\n\
+\n\
+ cups=n Enable or disable forwarding of CUPS connections,\n\
+ by listening on the optional port 'n'.\n\
+\n\
+ aux=n Enable or disable forwarding of the auxiliary X chan-\n\
+ nel used for controlling the keyboard. The 'keybd=n'\n\
+ form is accepted for backward compatibility.\n\
+\n\
+ smb=n Enable or disable forwarding of SMB connections. The\n\
+ 'samba=n' form is accepted for backward compatibility.\n\
+\n\
+ media=n Enable forwarding of audio connections.\n\
+\n\
+ http=n Enable forwarding of HTTP connections.\n\
+\n\
+ font=n Enable forwarding of reversed connections to a font\n\
+ server running on the NX server.\n\
+\n\
+ file=n Enable forwarding of file transfer connections.\n\
+\n\
+ mask=n Determine the distribution of channel ids between the\n\
+ proxies. By default, channels whose ids are multiple\n\
+ of 8 (starting from 0) are reserved for the NX client\n\
+ side. All the other channels can be allocated by the\n\
+ NX server side.\n\
+\n\
+ timeout=t Specify the keep-alive timeout used by proxies to\n\
+ determine if there is a network problem preventing\n\
+ communication with the remote peer. A value of 0\n\
+ disables the check.\n\
+\n\
+ cleanup=t Specify the number of seconds the proxy has to wait\n\
+ at session shutdown before closing all channels.\n\
+ The feature is used by the NX server to ensure that\n\
+ services are disconnected before shutting down the\n\
+ link.\n\
+\n\
+ pack=s Determine the method used to compress images.\n\
+\n\
+ product=s The product id of the client or server. The value is\n\
+ ignored by the proxy, but the client or server can\n\
+ provide it to facilitate the support.\n\
+\n\
+ core=b Enable production of core dumps when aborting the\n\
+ proxy connection.\n\
+\n\
+ options=s Specify an additional file containing options that\n\
+ has to be merged with option read from the command\n\
+ line or the environment.\n\
+\n\
+ kill=n Add the given process to the list of daemons that\n\
+ must be terminated at session shutdown. Multiple\n\
+ 'kill=n' options can be specified. The proxy will\n\
+ send them a SIGTERM signal just before exiting.\n\
+\n\
+ strict=b Optimize for responsiveness, rather than for the best\n\
+ use of all the available bandwidth.\n\
+\n\
+ encryption=b Should be set to 1 if the proxy is running as part of\n\
+ a program providing encryption of the point to point\n\
+ communication.\n\
+\n\
+rootless=b\n\
+geometry=s\n\
+resize=b\n\
+fullscreen=b\n\
+keyboard=s\n\
+clipboard=n\n\
+streaming=n\n\
+backingstore=n\n\
+composite=n\n\
+shmem=b\n\
+shpix=b\n\
+kbtype=s\n\
+client=s\n\
+shadow=n\n\
+shadowuid=n\n\
+shadowmode=s\n\
+defer=n\n\
+tile=s\n\
+menu=n These options are interpreted by the NX agent. They\n\
+ are ignored by the proxy.\n\
+\n\
+ Environment:\n\
+\n\
+ NX_ROOT The root NX directory is the place where the session\n\
+ directory and the cache files are created. This is\n\
+ usually overridden by passing the 'root=' option. By\n\
+ default, the root NX directory is assumed to be the\n\
+ directory '.nx' in the user's home.\n\
+\n\
+ NX_SYSTEM The directory where NX programs and libraries reside.\n\
+ If not set, the value is assumed to be '/usr/NX'.\n\
+ Programs, libraries and data files are respectedly\n\
+ searched in the 'bin', 'lib' and 'share' subdirecto-\n\
+ ries.\n\
+\n\
+ NX_HOME The NX user's home directory. If NX_ROOT is not set\n\
+ or invalid, the user's NX directory is created here.\n\
+\n\
+ NX_TEMP The directory where the X11 Unix Domain Sockets and\n\
+ all temporary files are to be created.\n\
+\n\
+ NX_CLIENT The full path to the nxclient executable. If the va-\n\
+ riable is not set, the nxclient executable will be\n\
+ run assuming that the program is in the system path.\n\
+ This can be useful on platforms like Windows and the\n\
+ Mac where nxclient is located in a different direct-\n\
+ ory compared to the other programs, to make easier\n\
+ for the user to execute the program from the shell.\n\
+\n\
+ Shell environment:\n\
+\n\
+ HOME The variable is checked in the case NX_HOME is not\n\
+ set, null or invalid.\n\
+\n\
+ TEMP The variable is checked whenever the NX_TEMP direct-\n\
+ ory is not set, null or invalid.\n\
+\n\
+ PATH The path where all executables are searched, except\n\
+ nxclient. If NX_CLIENT is not set, also the client\n\
+ executable is searched in the system path.\n\
+\n\
+ LD_LIBRARY_PATH\n\
+ System-wide library search order. This should be set\n\
+ by the program invoking the proxy.\n\
+\n\
+ DISPLAY On the X server side, the DISPLAY variable indicates\n\
+ the location of the X11 server. When nxcomp is used\n\
+ as a transport library, the DISPLAY may represent a\n\
+ NX transport specification and options can passed in\n\
+ the form nx/nx,option=value...\n\
+\n\
+ XAUTHORITY This is the file containing the X11 authorization\n\
+ cookie. If not set, the file is assumed to be in\n\
+ the user's home (either NX_HOME or HOME).\n\
+\n\
+";
+
+const char *GetUsageInfo()
+{
+ return UsageInfo;
+}
+
+static const char CopyrightInfo[] =
+"\
+Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/.\n\
+\n\
+NXCOMP, NX protocol compression and NX extensions to this software \n\
+are copyright of NoMachine. Redistribution and use of the present\n\
+software is allowed according to terms specified in the file LICENSE\n\
+which comes in the source distribution.\n\
+\n\
+Check http://www.nomachine.com/licensing.html for applicability.\n\
+\n\
+NX and NoMachine are trademarks of NoMachine S.r.l.\n\
+\n\
+All rights reserved.\n\
+";
+
+const char *GetCopyrightInfo()
+{
+ return CopyrightInfo;
+}
+
+static const char OtherCopyrightInfo[] =
+"\
+NX protocol compression is derived from DXPC project.\n\
+\n\
+Copyright (c) 1995,1996 Brian Pane\n\
+Copyright (c) 1996,1997 Zachary Vonler and Brian Pane\n\
+Copyright (c) 1999 Kevin Vigor and Brian Pane\n\
+Copyright (c) 2000,2003 Gian Filippo Pinzari and Brian Pane\n\
+\n\
+All rights reserved.\n\
+";
+
+const char *GetOtherCopyrightInfo()
+{
+ return OtherCopyrightInfo;
+}
+
+int _hostBigEndian = 0;
+int _storeBigEndian = 0;
+
+const unsigned int IntMask[33] =
+{
+ 0x00000000,
+ 0x00000001,
+ 0x00000003,
+ 0x00000007,
+ 0x0000000f,
+ 0x0000001f,
+ 0x0000003f,
+ 0x0000007f,
+ 0x000000ff,
+ 0x000001ff,
+ 0x000003ff,
+ 0x000007ff,
+ 0x00000fff,
+ 0x00001fff,
+ 0x00003fff,
+ 0x00007fff,
+ 0x0000ffff,
+ 0x0001ffff,
+ 0x0003ffff,
+ 0x0007ffff,
+ 0x000fffff,
+ 0x001fffff,
+ 0x003fffff,
+ 0x007fffff,
+ 0x00ffffff,
+ 0x01ffffff,
+ 0x03ffffff,
+ 0x07ffffff,
+ 0x0fffffff,
+ 0x1fffffff,
+ 0x3fffffff,
+ 0x7fffffff,
+ 0xffffffff
+};
+
+unsigned int GetUINT(unsigned const char *buffer, int bigEndian)
+{
+ //
+ // It doesn't work on SPARCs if the buffer
+ // is not aligned to the word boundary. We
+ // should check the CPU, not the OS as this
+ // surely applies to other architectures.
+ //
+
+ #ifndef __sun
+
+ if (_hostBigEndian == bigEndian)
+ {
+ return *((unsigned short *) buffer);
+ }
+
+ #else
+
+ if (_hostBigEndian == bigEndian && ((unsigned int) buffer) & 0x1 == 0)
+ {
+ return *((unsigned short *) buffer);
+ }
+
+ #endif
+
+ unsigned int result;
+
+ if (bigEndian)
+ {
+ result = *buffer;
+
+ result <<= 8;
+
+ result += buffer[1];
+ }
+ else
+ {
+ result = buffer[1];
+
+ result <<= 8;
+
+ result += *buffer;
+ }
+
+ return result;
+}
+
+unsigned int GetULONG(unsigned const char *buffer, int bigEndian)
+{
+ //
+ // It doesn't work on SPARCs if the buffer
+ // is not aligned to word the boundary.
+ //
+
+ #ifndef __sun
+
+ if (_hostBigEndian == bigEndian)
+ {
+ return *((unsigned int *) buffer);
+ }
+
+ #else
+
+ if (_hostBigEndian == bigEndian && ((unsigned int) buffer) & 0x3 == 0)
+ {
+ return *((unsigned int *) buffer);
+ }
+
+ #endif
+
+ const unsigned char *next = (bigEndian ? buffer : buffer + 3);
+
+ unsigned int result = 0;
+
+ for (int i = 0; i < 4; i++)
+ {
+ result <<= 8;
+
+ result += *next;
+
+ if (bigEndian)
+ {
+ next++;
+ }
+ else
+ {
+ next--;
+ }
+ }
+
+ return result;
+}
+
+void PutUINT(unsigned int value, unsigned char *buffer, int bigEndian)
+{
+ if (_hostBigEndian == bigEndian)
+ {
+ *((unsigned short *) buffer) = value;
+
+ return;
+ }
+
+ if (bigEndian)
+ {
+ buffer[1] = (unsigned char) (value & 0xff);
+
+ value >>= 8;
+
+ *buffer = (unsigned char) value;
+ }
+ else
+ {
+ *buffer = (unsigned char) (value & 0xff);
+
+ value >>= 8;
+
+ buffer[1] = (unsigned char) value;
+ }
+}
+
+void PutULONG(unsigned int value, unsigned char *buffer, int bigEndian)
+{
+ if (_hostBigEndian == bigEndian)
+ {
+ *((unsigned int *) buffer) = value;
+
+ return;
+ }
+
+ if (bigEndian)
+ {
+ buffer += 3;
+
+ for (int i = 4; i > 0; i--)
+ {
+ *buffer-- = (unsigned char) (value & 0xff);
+
+ value >>= 8;
+ }
+ }
+ else
+ {
+ for (int i = 4; i > 0; i--)
+ {
+ *buffer++ = (unsigned char) (value & 0xff);
+
+ value >>= 8;
+ }
+ }
+}
+
+int CheckData(istream *fs)
+{
+ if (fs == NULL || fs -> fail())
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+int CheckData(ostream *fs)
+{
+ if (fs == NULL || fs -> fail())
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+int PutData(ostream *fs, const unsigned char *buffer, int size)
+{
+ fs -> write((char *) buffer, size);
+
+ #ifdef DEBUG
+ *logofs << "PutData: Written " << size << " bytes with eof "
+ << fs -> eof() << " fail " << fs -> fail() << " and bad "
+ << fs -> bad() << ".\n" << logofs_flush;
+ #endif
+
+ if (fs -> fail())
+ {
+ return -1;
+ }
+
+ return size;
+}
+
+int GetData(istream *fs, unsigned char *buffer, int size)
+{
+ fs -> read((char *) buffer, size);
+
+ #ifdef DEBUG
+ *logofs << "GetData: Read " << size << " bytes with eof "
+ << fs -> eof() << " fail " << fs -> fail()
+ << " and bad " << fs -> bad() << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef __APPLE__
+
+ if (fs -> bad())
+ {
+ return -1;
+ }
+
+ #else
+
+ if (fs -> fail())
+ {
+ return -1;
+ }
+
+ #endif
+
+ return size;
+}
+
+int FlushData(ostream *fs)
+{
+ fs -> flush();
+
+ if (fs -> fail())
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+unsigned int RoundUp2(unsigned int x)
+{
+ unsigned int y = x / 2;
+
+ y *= 2;
+
+ if (y != x)
+ {
+ y += 2;
+ }
+
+ return y;
+}
+
+unsigned int RoundUp4(unsigned int x)
+{
+ unsigned int y = x / 4;
+
+ y *= 4;
+
+ if (y != x)
+ {
+ y += 4;
+ }
+
+ return y;
+}
+
+unsigned int RoundUp8(unsigned int x)
+{
+ unsigned int y = x / 8;
+
+ y *= 8;
+
+ if (y != x)
+ {
+ y += 8;
+ }
+
+ return y;
+}
+
+const char *DumpSignal(int signal)
+{
+ switch (signal)
+ {
+ case SIGCHLD:
+ {
+ return "SIGCHLD";
+ }
+ case SIGUSR1:
+ {
+ return "SIGUSR1";
+ }
+ case SIGUSR2:
+ {
+ return "SIGUSR2";
+ }
+ case SIGHUP:
+ {
+ return "SIGHUP";
+ }
+ case SIGINT:
+ {
+ return "SIGINT";
+ }
+ case SIGTERM:
+ {
+ return "SIGTERM";
+ }
+ case SIGPIPE:
+ {
+ return "SIGPIPE";
+ }
+ case SIGALRM:
+ {
+ return "SIGALRM";
+ }
+ case SIGVTALRM:
+ {
+ return "SIGVTALRM";
+ }
+ case SIGWINCH:
+ {
+ return "SIGWINCH";
+ }
+ case SIGIO:
+ {
+ return "SIGIO";
+ }
+ case SIGTSTP:
+ {
+ return "SIGTSTP";
+ }
+ case SIGTTIN:
+ {
+ return "SIGTTIN";
+ }
+ case SIGTTOU:
+ {
+ return "SIGTTOU";
+ }
+ case SIGSEGV:
+ {
+ return "SIGSEGV";
+ }
+ case SIGABRT:
+ {
+ return "SIGABRT";
+ }
+ default:
+ {
+ return "Unknown";
+ }
+ }
+}
+
+const char *DumpPolicy(int type)
+{
+ switch ((T_flush_policy) type)
+ {
+ case policy_immediate:
+ {
+ return "immediate";
+ }
+ case policy_deferred:
+ {
+ return "deferred";
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "Misc: PANIC! Unknown policy type '"
+ << type << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Unknown policy type '"
+ << type << "'.\n";
+
+ HandleCleanup();
+ }
+ }
+}
+
+const char *DumpAction(int type)
+{
+ T_store_action action = (T_store_action) type;
+
+ if (action == IS_HIT)
+ {
+ return "is_hit";
+ }
+ else if (action == IS_ADDED)
+ {
+ return "is_added";
+ }
+ else if (action == is_discarded)
+ {
+ return "is_discarded";
+ }
+ else if (action == is_removed)
+ {
+ return "is_removed";
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Misc: PANIC! Unknown store action '"
+ << type << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Unknown store action '"
+ << type << "'.\n";
+
+ HandleCleanup();
+ }
+}
+
+const char *DumpState(int type)
+{
+ switch ((T_split_state) type)
+ {
+ case split_added:
+ {
+ return "split_added";
+ }
+ case split_missed:
+ {
+ return "split_missed";
+ }
+ case split_loaded:
+ {
+ return "split_loaded";
+ }
+ case split_aborted:
+ {
+ return "split_aborted";
+ }
+ case split_notified:
+ {
+ return "split_notified";
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "Misc: PANIC! Unknown split state '"
+ << type << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Unknown split state '"
+ << type << "'.\n";
+
+ HandleCleanup();
+ }
+ }
+}
+
+const char *DumpControl(int code)
+{
+ switch ((T_proxy_code) code)
+ {
+ case code_new_x_connection:
+ {
+ return "code_new_x_connection";
+ }
+ case code_new_cups_connection:
+ {
+ return "code_new_cups_connection";
+ }
+ case code_new_aux_connection:
+ {
+ return "code_new_aux_connection";
+ }
+ case code_new_smb_connection:
+ {
+ return "code_new_smb_connection";
+ }
+ case code_new_media_connection:
+ {
+ return "code_new_media_connection";
+ }
+ case code_switch_connection:
+ {
+ return "code_switch_connection";
+ }
+ case code_drop_connection:
+ {
+ return "code_drop_connection";
+ }
+ case code_finish_connection:
+ {
+ return "code_finish_connection";
+ }
+ case code_begin_congestion:
+ {
+ return "code_begin_congestion";
+ }
+ case code_end_congestion:
+ {
+ return "code_end_congestion";
+ }
+ case code_alert_request:
+ {
+ return "code_alert_request";
+ }
+ case code_alert_reply:
+ {
+ return "code_alert_reply";
+ }
+ case code_reset_request:
+ {
+ return "code_reset_request";
+ }
+ case code_reset_reply:
+ {
+ return "code_reset_reply";
+ }
+ case code_load_request:
+ {
+ return "code_load_request";
+ }
+ case code_load_reply:
+ {
+ return "code_load_reply";
+ }
+ case code_save_request:
+ {
+ return "code_save_request";
+ }
+ case code_save_reply:
+ {
+ return "code_save_reply";
+ }
+ case code_shutdown_request:
+ {
+ return "code_shutdown_request";
+ }
+ case code_shutdown_reply:
+ {
+ return "code_shutdown_reply";
+ }
+ case code_control_token_request:
+ {
+ return "code_control_token_request";
+ }
+ case code_control_token_reply:
+ {
+ return "code_control_token_reply";
+ }
+ case code_configuration_request:
+ {
+ return "code_configuration_request";
+ }
+ case code_configuration_reply:
+ {
+ return "code_configuration_reply";
+ }
+ case code_statistics_request:
+ {
+ return "code_statistics_request";
+ }
+ case code_statistics_reply:
+ {
+ return "code_statistics_reply";
+ }
+ case code_new_http_connection:
+ {
+ return "code_new_http_connection";
+ }
+ case code_sync_request:
+ {
+ return "code_sync_request";
+ }
+ case code_sync_reply:
+ {
+ return "code_sync_reply";
+ }
+ case code_new_font_connection:
+ {
+ return "code_new_font_connection";
+ }
+ case code_new_slave_connection:
+ {
+ return "code_new_slave_connection";
+ }
+ case code_finish_listeners:
+ {
+ return "code_finish_listeners";
+ }
+ case code_split_token_request:
+ {
+ return "code_split_token_request";
+ }
+ case code_split_token_reply:
+ {
+ return "code_split_token_reply";
+ }
+ case code_data_token_request:
+ {
+ return "code_data_token_request";
+ }
+ case code_data_token_reply:
+ {
+ return "code_data_token_reply";
+ }
+ default:
+ {
+ #ifdef WARNING
+ *logofs << "Misc: WARNING! Unknown control code '"
+ << code << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Unknown control code '"
+ << code << "'.\n";
+
+ return "unknown";
+ }
+ }
+}
+
+const char *DumpSession(int code)
+{
+ switch ((T_session_mode) code)
+ {
+ case session_agent:
+ {
+ return "session_agent";
+ }
+ case session_shadow:
+ {
+ return "session_shadow";
+ }
+ case session_proxy:
+ {
+ return "session_proxy";
+ }
+ default:
+ {
+ #ifdef WARNING
+ *logofs << "Misc: WARNING! Unknown session type '"
+ << code << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Unknown session type '"
+ << code << "'.\n";
+
+ return "unknown";
+ }
+ }
+}
+
+const char *DumpToken(int type)
+{
+ switch ((T_token_type) type)
+ {
+ case token_control:
+ {
+ return "token_control";
+ }
+ case token_split:
+ {
+ return "token_split";
+ }
+ case token_data:
+ {
+ return "token_data";
+ }
+ default:
+ {
+ #ifdef WARNING
+ *logofs << "Misc: WARNING! Unknown token type '"
+ << type << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Unknown token type '"
+ << type << "'.\n";
+
+ return "unknown";
+ }
+ }
+}
+
+//
+// Always include this in code as it is generally
+// needed to test channels and split store.
+//
+
+const char *DumpChecksum(const void *checksum)
+{
+ static char string[MD5_LENGTH * 2 + 1];
+
+ if (checksum != NULL)
+ {
+ for (unsigned int i = 0; i < MD5_LENGTH; i++)
+ {
+ sprintf(string + (i * 2), "%02X", ((unsigned char *) checksum)[i]);
+ }
+ }
+ else
+ {
+ strcpy(string, "null");
+ }
+
+ return string;
+}
+
+//
+// Define OPCODES here and in the channel
+// if you want to log the opcode literal.
+//
+
+#ifdef OPCODES
+
+const char *DumpOpcode(const int &opcode)
+{
+ switch (opcode)
+ {
+ case X_CreateWindow:
+ {
+ return "X_CreateWindow";
+ }
+ case X_ChangeWindowAttributes:
+ {
+ return "X_ChangeWindowAttributes";
+ }
+ case X_GetWindowAttributes:
+ {
+ return "X_GetWindowAttributes";
+ }
+ case X_DestroyWindow:
+ {
+ return "X_DestroyWindow";
+ }
+ case X_DestroySubwindows:
+ {
+ return "X_DestroySubwindows";
+ }
+ case X_ChangeSaveSet:
+ {
+ return "X_ChangeSaveSet";
+ }
+ case X_ReparentWindow:
+ {
+ return "X_ReparentWindow";
+ }
+ case X_MapWindow:
+ {
+ return "X_MapWindow";
+ }
+ case X_MapSubwindows:
+ {
+ return "X_MapSubwindows";
+ }
+ case X_UnmapWindow:
+ {
+ return "X_UnmapWindow";
+ }
+ case X_UnmapSubwindows:
+ {
+ return "X_UnmapSubwindows";
+ }
+ case X_ConfigureWindow:
+ {
+ return "X_ConfigureWindow";
+ }
+ case X_CirculateWindow:
+ {
+ return "X_CirculateWindow";
+ }
+ case X_GetGeometry:
+ {
+ return "X_GetGeometry";
+ }
+ case X_QueryTree:
+ {
+ return "X_QueryTree";
+ }
+ case X_InternAtom:
+ {
+ return "X_InternAtom";
+ }
+ case X_GetAtomName:
+ {
+ return "X_GetAtomName";
+ }
+ case X_ChangeProperty:
+ {
+ return "X_ChangeProperty";
+ }
+ case X_DeleteProperty:
+ {
+ return "X_DeleteProperty";
+ }
+ case X_GetProperty:
+ {
+ return "X_GetProperty";
+ }
+ case X_ListProperties:
+ {
+ return "X_ListProperties";
+ }
+ case X_SetSelectionOwner:
+ {
+ return "X_SetSelectionOwner";
+ }
+ case X_GetSelectionOwner:
+ {
+ return "X_GetSelectionOwner";
+ }
+ case X_ConvertSelection:
+ {
+ return "X_ConvertSelection";
+ }
+ case X_SendEvent:
+ {
+ return "X_SendEvent";
+ }
+ case X_GrabPointer:
+ {
+ return "X_GrabPointer";
+ }
+ case X_UngrabPointer:
+ {
+ return "X_UngrabPointer";
+ }
+ case X_GrabButton:
+ {
+ return "X_GrabButton";
+ }
+ case X_UngrabButton:
+ {
+ return "X_UngrabButton";
+ }
+ case X_ChangeActivePointerGrab:
+ {
+ return "X_ChangeActivePointerGrab";
+ }
+ case X_GrabKeyboard:
+ {
+ return "X_GrabKeyboard";
+ }
+ case X_UngrabKeyboard:
+ {
+ return "X_UngrabKeyboard";
+ }
+ case X_GrabKey:
+ {
+ return "X_GrabKey";
+ }
+ case X_UngrabKey:
+ {
+ return "X_UngrabKey";
+ }
+ case X_AllowEvents:
+ {
+ return "X_AllowEvents";
+ }
+ case X_GrabServer:
+ {
+ return "X_GrabServer";
+ }
+ case X_UngrabServer:
+ {
+ return "X_UngrabServer";
+ }
+ case X_QueryPointer:
+ {
+ return "X_QueryPointer";
+ }
+ case X_GetMotionEvents:
+ {
+ return "X_GetMotionEvents";
+ }
+ case X_TranslateCoords:
+ {
+ return "X_TranslateCoords";
+ }
+ case X_WarpPointer:
+ {
+ return "X_WarpPointer";
+ }
+ case X_SetInputFocus:
+ {
+ return "X_SetInputFocus";
+ }
+ case X_GetInputFocus:
+ {
+ return "X_GetInputFocus";
+ }
+ case X_QueryKeymap:
+ {
+ return "X_QueryKeymap";
+ }
+ case X_OpenFont:
+ {
+ return "X_OpenFont";
+ }
+ case X_CloseFont:
+ {
+ return "X_CloseFont";
+ }
+ case X_QueryFont:
+ {
+ return "X_QueryFont";
+ }
+ case X_QueryTextExtents:
+ {
+ return "X_QueryTextExtents";
+ }
+ case X_ListFonts:
+ {
+ return "X_ListFonts";
+ }
+ case X_ListFontsWithInfo:
+ {
+ return "X_ListFontsWithInfo";
+ }
+ case X_SetFontPath:
+ {
+ return "X_SetFontPath";
+ }
+ case X_GetFontPath:
+ {
+ return "X_GetFontPath";
+ }
+ case X_CreatePixmap:
+ {
+ return "X_CreatePixmap";
+ }
+ case X_FreePixmap:
+ {
+ return "X_FreePixmap";
+ }
+ case X_CreateGC:
+ {
+ return "X_CreateGC";
+ }
+ case X_ChangeGC:
+ {
+ return "X_ChangeGC";
+ }
+ case X_CopyGC:
+ {
+ return "X_CopyGC";
+ }
+ case X_SetDashes:
+ {
+ return "X_SetDashes";
+ }
+ case X_SetClipRectangles:
+ {
+ return "X_SetClipRectangles";
+ }
+ case X_FreeGC:
+ {
+ return "X_FreeGC";
+ }
+ case X_ClearArea:
+ {
+ return "X_ClearArea";
+ }
+ case X_CopyArea:
+ {
+ return "X_CopyArea";
+ }
+ case X_CopyPlane:
+ {
+ return "X_CopyPlane";
+ }
+ case X_PolyPoint:
+ {
+ return "X_PolyPoint";
+ }
+ case X_PolyLine:
+ {
+ return "X_PolyLine";
+ }
+ case X_PolySegment:
+ {
+ return "X_PolySegment";
+ }
+ case X_PolyRectangle:
+ {
+ return "X_PolyRectangle";
+ }
+ case X_PolyArc:
+ {
+ return "X_PolyArc";
+ }
+ case X_FillPoly:
+ {
+ return "X_FillPoly";
+ }
+ case X_PolyFillRectangle:
+ {
+ return "X_PolyFillRectangle";
+ }
+ case X_PolyFillArc:
+ {
+ return "X_PolyFillArc";
+ }
+ case X_PutImage:
+ {
+ return "X_PutImage";
+ }
+ case X_GetImage:
+ {
+ return "X_GetImage";
+ }
+ case X_PolyText8:
+ {
+ return "X_PolyText8";
+ }
+ case X_PolyText16:
+ {
+ return "X_PolyText16";
+ }
+ case X_ImageText8:
+ {
+ return "X_ImageText8";
+ }
+ case X_ImageText16:
+ {
+ return "X_ImageText16";
+ }
+ case X_CreateColormap:
+ {
+ return "X_CreateColormap";
+ }
+ case X_FreeColormap:
+ {
+ return "X_FreeColormap";
+ }
+ case X_CopyColormapAndFree:
+ {
+ return "X_CopyColormapAndFree";
+ }
+ case X_InstallColormap:
+ {
+ return "X_InstallColormap";
+ }
+ case X_UninstallColormap:
+ {
+ return "X_UninstallColormap";
+ }
+ case X_ListInstalledColormaps:
+ {
+ return "X_ListInstalledColormaps";
+ }
+ case X_AllocColor:
+ {
+ return "X_AllocColor";
+ }
+ case X_AllocNamedColor:
+ {
+ return "X_AllocNamedColor";
+ }
+ case X_AllocColorCells:
+ {
+ return "X_AllocColorCells";
+ }
+ case X_AllocColorPlanes:
+ {
+ return "X_AllocColorPlanes";
+ }
+ case X_FreeColors:
+ {
+ return "X_FreeColors";
+ }
+ case X_StoreColors:
+ {
+ return "X_StoreColors";
+ }
+ case X_StoreNamedColor:
+ {
+ return "X_StoreNamedColor";
+ }
+ case X_QueryColors:
+ {
+ return "X_QueryColors";
+ }
+ case X_LookupColor:
+ {
+ return "X_LookupColor";
+ }
+ case X_CreateCursor:
+ {
+ return "X_CreateCursor";
+ }
+ case X_CreateGlyphCursor:
+ {
+ return "X_CreateGlyphCursor";
+ }
+ case X_FreeCursor:
+ {
+ return "X_FreeCursor";
+ }
+ case X_RecolorCursor:
+ {
+ return "X_RecolorCursor";
+ }
+ case X_QueryBestSize:
+ {
+ return "X_QueryBestSize";
+ }
+ case X_QueryExtension:
+ {
+ return "X_QueryExtension";
+ }
+ case X_ListExtensions:
+ {
+ return "X_ListExtensions";
+ }
+ case X_ChangeKeyboardMapping:
+ {
+ return "X_ChangeKeyboardMapping";
+ }
+ case X_GetKeyboardMapping:
+ {
+ return "X_GetKeyboardMapping";
+ }
+ case X_ChangeKeyboardControl:
+ {
+ return "X_ChangeKeyboardControl";
+ }
+ case X_GetKeyboardControl:
+ {
+ return "X_GetKeyboardControl";
+ }
+ case X_Bell:
+ {
+ return "X_Bell";
+ }
+ case X_ChangePointerControl:
+ {
+ return "X_ChangePointerControl";
+ }
+ case X_GetPointerControl:
+ {
+ return "X_GetPointerControl";
+ }
+ case X_SetScreenSaver:
+ {
+ return "X_SetScreenSaver";
+ }
+ case X_GetScreenSaver:
+ {
+ return "X_GetScreenSaver";
+ }
+ case X_ChangeHosts:
+ {
+ return "X_ChangeHosts";
+ }
+ case X_ListHosts:
+ {
+ return "X_ListHosts";
+ }
+ case X_SetAccessControl:
+ {
+ return "X_SetAccessControl";
+ }
+ case X_SetCloseDownMode:
+ {
+ return "X_SetCloseDownMode";
+ }
+ case X_KillClient:
+ {
+ return "X_KillClient";
+ }
+ case X_RotateProperties:
+ {
+ return "X_RotateProperties";
+ }
+ case X_ForceScreenSaver:
+ {
+ return "X_ForceScreenSaver";
+ }
+ case X_SetPointerMapping:
+ {
+ return "X_SetPointerMapping";
+ }
+ case X_GetPointerMapping:
+ {
+ return "X_GetPointerMapping";
+ }
+ case X_SetModifierMapping:
+ {
+ return "X_SetModifierMapping";
+ }
+ case X_GetModifierMapping:
+ {
+ return "X_GetModifierMapping";
+ }
+ case X_NoOperation:
+ {
+ return "X_NoOperation";
+ }
+ case X_NXInternalGenericData:
+ {
+ return "X_NXInternalGenericData";
+ }
+ //
+ // case X_NXInternalGenericReply:
+ // {
+ // return "X_NXInternalGenericReply";
+ // }
+ //
+ case X_NXInternalGenericRequest:
+ {
+ return "X_NXInternalGenericRequest";
+ }
+ case X_NXInternalShapeExtension:
+ {
+ return "X_NXInternalShapeExtension";
+ }
+ case X_NXGetControlParameters:
+ {
+ return "X_NXGetControlParameters";
+ }
+ case X_NXGetCleanupParameters:
+ {
+ return "X_NXGetCleanupParameters";
+ }
+ case X_NXGetImageParameters:
+ {
+ return "X_NXGetImageParameters";
+ }
+ case X_NXGetUnpackParameters:
+ {
+ return "X_NXGetUnpackParameters";
+ }
+ case X_NXGetShmemParameters:
+ {
+ return "X_NXGetShmemParameters";
+ }
+ case X_NXGetFontParameters:
+ {
+ return "X_NXGetFontParameters";
+ }
+ case X_NXSetExposeParameters:
+ {
+ return "X_NXSetExposeParameters";
+ }
+ case X_NXSetCacheParameters:
+ {
+ return "X_NXSetCacheParameters";
+ }
+ case X_NXStartSplit:
+ {
+ return "X_NXStartSplit";
+ }
+ case X_NXEndSplit:
+ {
+ return "X_NXEndSplit";
+ }
+ case X_NXSplitData:
+ {
+ return "X_NXSplitData";
+ }
+ case X_NXSplitEvent:
+ {
+ return "X_NXSplitEvent";
+ }
+ case X_NXCommitSplit:
+ {
+ return "X_NXCommitSplit";
+ }
+ case X_NXFinishSplit:
+ {
+ return "X_NXFinishSplit";
+ }
+ case X_NXAbortSplit:
+ {
+ return "X_NXAbortSplit";
+ }
+ case X_NXFreeSplit:
+ {
+ return "X_NXFreeSplit";
+ }
+ case X_NXSetUnpackGeometry:
+ {
+ return "X_NXSetUnpackGeometry";
+ }
+ case X_NXSetUnpackColormap:
+ {
+ return "X_NXSetUnpackColormap";
+ }
+ case X_NXSetUnpackAlpha:
+ {
+ return "X_NXSetUnpackAlpha";
+ }
+ case X_NXPutPackedImage:
+ {
+ return "X_NXPutPackedImage";
+ }
+ case X_NXFreeUnpack:
+ {
+ return "X_NXFreeUnpack";
+ }
+ default:
+ {
+ if (opcode > 127)
+ {
+ return "Extension";
+ }
+ else
+ {
+ return "?";
+ }
+ }
+ }
+}
+
+#else /* #ifdef OPCODES */
+
+const char *DumpOpcode(const int &opcode)
+{
+ return "?";
+}
+
+#endif /* #ifdef OPCODES */
+
+void DumpData(const unsigned char *buffer, unsigned int size)
+{
+ if (buffer != NULL)
+ {
+ unsigned int i = 0;
+
+ while (i < size)
+ {
+ *logofs << "[" << i << "]\t";
+
+ for (unsigned int ii = 0; i < size && ii < 8; i++, ii++)
+ {
+ *logofs << (unsigned int) (buffer[i]) << "\t";
+ }
+
+ *logofs << "\n" << logofs_flush;
+ }
+ }
+}
+
+void DumpChecksum(const unsigned char *buffer, unsigned int size)
+{
+ if (buffer != NULL)
+ {
+ md5_byte_t md5_digest[MD5_LENGTH];
+
+ md5_state_t md5_state;
+
+ md5_init(&md5_state);
+
+ md5_append(&md5_state, buffer, size);
+
+ md5_finish(&md5_state, md5_digest);
+
+ char md5_string[MD5_LENGTH * 2 + 1];
+
+ for (unsigned int i = 0; i < MD5_LENGTH; i++)
+ {
+ sprintf(md5_string + (i * 2), "%02X", md5_digest[i]);
+ }
+
+ *logofs << "[" << md5_string << "]" << logofs_flush;
+ }
+}
+
+void DumpBlockChecksums(const unsigned char *buffer,
+ unsigned int size, unsigned int block)
+{
+ for (unsigned int i = 0; i < (size / block); i++)
+ {
+ *logofs << "[" << i * block << "]";
+
+ DumpChecksum(buffer + (i * block), block);
+
+ *logofs << "\n";
+ }
+
+ if (size % block > 0)
+ {
+ *logofs << "[" << size / block * block << "]";
+
+ DumpChecksum(buffer + (size / block * block), size % block);
+
+ *logofs << "\n";
+ }
+}
+
+void DumpHexData(const unsigned char *buffer, unsigned int size)
+{
+ char message [65536];
+ char ascii [17];
+
+ unsigned int index = 0;
+ unsigned int linescan = 0;
+ unsigned int index_ascii = 0;
+
+ sprintf (message,"\n#### Start Dump Buffer of [%.5d] Bytes ####\n\n",size);
+
+ *logofs << message << logofs_flush;
+
+ //
+ // "Index 0 1 2 3 4 5 6 7 8 9 a b c d e f Ascii "
+ // "----- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ----------------"
+ // "00000 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................"
+ //
+
+ sprintf (message,"Index 0 1 2 3 4 5 6 7 8 9 a b c d e f Ascii \n");
+ *logofs << message << logofs_flush;
+ sprintf (message,"----- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ----------------\n");
+ *logofs << message << logofs_flush;
+
+ index = 0;
+
+ while (index < size)
+ {
+ memset (ascii, ' ', sizeof(ascii));
+
+ ascii[16] = '\0';
+
+ sprintf (message,"%.5d ", index);
+
+ for (index_ascii = 0, linescan = index;
+ ((index < (linescan + 16)) && (index < size));
+ index++, index_ascii++)
+ {
+ if (isprint(buffer [index]))
+ {
+ ascii[index_ascii] = buffer [index];
+ }
+ else
+ {
+ ascii[index_ascii] = '.';
+ }
+
+ sprintf (&message [strlen (message)],"%.2x ", (unsigned char) buffer [index]);
+ }
+
+ for (linescan = index_ascii; linescan < 16; linescan++)
+ {
+ strcat (&message [strlen (message)], " ");
+ }
+
+ sprintf (&message [strlen (message)]," %s\n", ascii);
+
+ *logofs << message << logofs_flush;
+ }
+
+ sprintf (message,"\n#### End Dump Buffer ####\n\n");
+
+ *logofs << message << logofs_flush;
+}
diff --git a/nxcomp/Misc.h b/nxcomp/Misc.h
new file mode 100644
index 000000000..200831757
--- /dev/null
+++ b/nxcomp/Misc.h
@@ -0,0 +1,262 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef Misc_H
+#define Misc_H
+
+#include <iostream>
+#include <fstream>
+
+#include <errno.h>
+#include <string.h>
+
+#ifdef __sun
+
+#include <strings.h>
+
+#endif
+
+using namespace std;
+
+//
+// This is MD5 length.
+//
+
+#define MD5_LENGTH 16
+
+//
+// Error handling macros.
+//
+
+#define ESET(e) (errno = (e))
+#define EGET() (errno)
+#define ESTR() strerror(errno)
+
+//
+// TCP port offset applied to NX port specification.
+//
+
+extern const int DEFAULT_NX_PROXY_PORT_OFFSET;
+
+//
+// Default TCP port used by client proxy to listen
+// to X clients and by server proxy to connect to
+// remote.
+//
+
+extern const int DEFAULT_NX_PROXY_PORT;
+
+//
+// Default X display number that client
+// proxy imitates.
+//
+
+extern const int DEFAULT_NX_X_PORT;
+
+//
+// Establish the port offsets for the additional
+// services.
+//
+
+extern const int DEFAULT_NX_CUPS_PORT_OFFSET;
+extern const int DEFAULT_NX_SMB_PORT_OFFSET;
+extern const int DEFAULT_NX_MEDIA_PORT_OFFSET;
+extern const int DEFAULT_NX_AUX_PORT_OFFSET;
+extern const int DEFAULT_NX_HTTP_PORT_OFFSET;
+extern const int DEFAULT_NX_FONT_PORT_OFFSET;
+
+//
+// Slave channels can be originated by both sides
+// so they need to have different port offsets
+// in the case the user runs both proxies on the
+// same host.
+//
+
+extern const int DEFAULT_NX_SLAVE_PORT_CLIENT_OFFSET;
+extern const int DEFAULT_NX_SLAVE_PORT_SERVER_OFFSET;
+
+//
+// Return strings containing various info.
+//
+
+const char *GetUsageInfo();
+const char *GetCopyrightInfo();
+const char *GetOtherCopyrightInfo();
+
+//
+// Define this if you want immediate flush of
+// the log output.
+//
+
+#define FLUSH_LOGOFS
+
+//
+// Global objects providing shared functions.
+//
+
+class Auth;
+class Control;
+class Statistics;
+
+extern Auth *auth;
+extern Control *control;
+extern Statistics *statistics;
+
+//
+// Log file.
+//
+
+extern ostream *logofs;
+
+//
+// Cleanup code.
+//
+
+void HandleAbort() __attribute__((noreturn));
+void HandleShutdown() __attribute__((noreturn));
+
+extern "C"
+{
+ void HandleCleanup(int code = 0) __attribute__((noreturn));
+}
+
+//
+// Manage signal handlers.
+//
+
+void DisableSignals();
+void EnableSignals();
+
+//
+// Manage timers.
+//
+
+void SetTimer(int value);
+void ResetTimer();
+
+//
+// Show a dialog asking the user if he/she
+// wants to close the current session. Look
+// in the alerts file for the known critical
+// events.
+//
+
+void HandleAlert(int code, int local);
+
+//
+// Run the callback registered by the proxy
+// or the agent.
+//
+
+void KeeperCallback();
+void FlushCallback(int length);
+
+//
+// Return the string literal corresponding
+// the value.
+//
+
+const char *DumpSignal(int signal);
+const char *DumpPolicy(int type);
+const char *DumpControl(int code);
+const char *DumpSession(int code);
+const char *DumpAction(int type);
+const char *DumpState(int type);
+const char *DumpToken(int type);
+
+//
+// Print out content of buffer to log file.
+// You need to define DUMP or OPCODES in
+// the source to have these compiled.
+//
+
+const char *DumpOpcode(const int &opcode);
+const char *DumpChecksum(const void *checksum);
+
+void DumpData(const unsigned char *data, unsigned int length);
+void DumpHexData(const unsigned char *data, unsigned int length);
+void DumpChecksum(const unsigned char *data, unsigned int length);
+void DumpBlockChecksums(const unsigned char *data, unsigned int length,
+ unsigned int block);
+
+//
+// Defines logofs_flush as an empty string to
+// avoid calling the corresponding ostream's
+// flush() function.
+//
+
+#ifdef FLUSH_LOGOFS
+
+#define logofs_flush "" ; logofs -> flush()
+
+#else
+
+#define logofs_flush ""
+
+#endif
+
+//
+// Is the host where local proxy is running
+// big-endian?
+//
+
+extern int _hostBigEndian;
+extern int _storeBigEndian;
+
+inline void setHostBigEndian(int flag)
+{
+ _hostBigEndian = flag;
+}
+
+inline int hostBigEndian()
+{
+ return _hostBigEndian;
+}
+
+inline int storeBigEndian()
+{
+ return _storeBigEndian;
+}
+
+extern const unsigned int IntMask[33];
+
+unsigned int GetUINT(unsigned const char *buffer, int bigEndian);
+unsigned int GetULONG(unsigned const char *buffer, int bigEndian);
+void PutUINT(unsigned int value, unsigned char *buffer, int bigEndian);
+void PutULONG(unsigned int value, unsigned char *buffer, int bigEndian);
+
+inline void CleanData(unsigned char *buffer, int size)
+{
+ unsigned char *end = buffer + size;
+
+ while (buffer < end)
+ {
+ *buffer++ = 0x00;
+ }
+}
+
+int CheckData(istream *fs);
+int CheckData(ostream *fs);
+int PutData(ostream *fs, const unsigned char *buffer, int size);
+int GetData(istream *fs, unsigned char *buffer, int size);
+int FlushData(ostream *fs);
+
+unsigned int RoundUp2(unsigned int x);
+unsigned int RoundUp4(unsigned int x);
+unsigned int RoundUp8(unsigned int x);
+
+#endif /* Misc_H */
diff --git a/nxcomp/NX.h b/nxcomp/NX.h
new file mode 100644
index 000000000..d98af79bb
--- /dev/null
+++ b/nxcomp/NX.h
@@ -0,0 +1,449 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef NX_H
+#define NX_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <unistd.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/select.h>
+#include <sys/uio.h>
+
+#define NX_FD_ANY -1
+
+#define NX_MODE_ANY -1
+#define NX_MODE_CLIENT 1
+#define NX_MODE_SERVER 2
+
+#define NX_DISPLAY_ANY NULL
+
+#define NX_SIGNAL_ANY -1
+#define NX_SIGNAL_ENABLE 1
+#define NX_SIGNAL_DISABLE 2
+#define NX_SIGNAL_RAISE 3
+#define NX_SIGNAL_FORWARD 4
+
+#define NX_POLICY_IMMEDIATE 1
+#define NX_POLICY_DEFERRED 2
+
+#define NX_ALERT_REMOTE 0
+#define NX_ALERT_LOCAL 1
+
+#define NX_HANDLER_FLUSH 0
+#define NX_HANDLER_STATISTICS 1
+
+#define NX_STATISTICS_PARTIAL 0
+#define NX_STATISTICS_TOTAL 1
+
+#define NX_CHANNEL_X11 0
+#define NX_CHANNEL_CUPS 1
+#define NX_CHANNEL_SMB 2
+#define NX_CHANNEL_MEDIA 3
+#define NX_CHANNEL_HTTP 4
+#define NX_CHANNEL_FONT 5
+#define NX_CHANNEL_SLAVE 6
+
+#define NX_FILE_SESSION 0
+#define NX_FILE_ERRORS 1
+#define NX_FILE_OPTIONS 2
+#define NX_FILE_STATS 3
+
+/*
+ * The following are the new interfaces to the NX transport. The
+ * NX proxy software is now intended to be run as a library of a
+ * higher level communication manager (nxssh, nxhttp, nxrtp, etc,
+ * not only nxproxy). This is a work-in-progress, so expect these
+ * interfaces to change in future. At the present moment, as an
+ * example, there is no provision for creating and managing mul-
+ * tiple proxy connections.
+ */
+
+/*
+ * Attach a NX transport to the provided descriptor. This should be
+ * done after having created a pair of connected sockets.
+ */
+
+extern int NXTransCreate(int fd, int mode, const char *options);
+
+/*
+ * Tell the proxy to use the second descriptor as its own end of
+ * the internal connection to the NX agent. The NX agent will use
+ * the first descriptor. Setting an agent connection will have the
+ * effect of disabling further X client connections and, if it is
+ * possible, will trigger the use of the memory-to-memory transport.
+ */
+
+extern int NXTransAgent(int fd[2]);
+
+/*
+ * Prepare the file sets and the timeout for a later execution of
+ * the select(). The masks and the timeout must persist across all
+ * the calls, so if you don't need any of the values, it is requi-
+ * red that you create empty masks and a default timeout. To save
+ * a check at each run, all the functions below assume that valid
+ * pointers are passed.
+ */
+
+extern int NXTransPrepare(int *maxfds, fd_set *readfds,
+ fd_set *writefds, struct timeval *timeout);
+
+/*
+ * Call select() to find out the descriptors in the sets having
+ * pending data.
+ */
+
+extern int NXTransSelect(int *result, int *error, int *maxfds, fd_set *readfds,
+ fd_set *writefds, struct timeval *timeout);
+
+/*
+ * Perform the required I/O on all the NX descriptors having pen-
+ * ding data. This can include reading and writing to the NX chan-
+ * nels, encoding and decoding the proxy data or managing any of
+ * the other NX resources.
+ */
+
+extern int NXTransExecute(int *result, int *error, int *maxfds, fd_set *readfds,
+ fd_set *writefds, struct timeval *timeout);
+
+/*
+ * Run an empty loop, giving to the NX transport a chance to check
+ * its descriptors.
+ */
+
+extern int NXTransContinue(struct timeval *timeout);
+
+/*
+ * Perform I/O on the given descriptors. If memory-to-memory trans-
+ * port has been activated and the descriptor is recognized as a
+ * valid agent connection, then the functions will read and write
+ * the data directly to the proxy buffer, otherwise the correspond-
+ * ing network operation will be performed.
+ */
+
+extern int NXTransRead(int fd, char *data, int size);
+extern int NXTransWrite(int fd, char *data, int size);
+extern int NXTransReadable(int fd, int *readable);
+
+extern int NXTransReadVector(int fd, struct iovec *iovdata, int iovsize);
+extern int NXTransWriteVector(int fd, struct iovec *iovdata, int iovsize);
+
+extern int NXTransClose(int fd);
+
+/*
+ * Return true if the NX transport is running. The fd parameter can
+ * be either the local descriptor attached to the NX transport or
+ * NX_FD_ANY.
+ */
+
+extern int NXTransRunning(int fd);
+
+/*
+ * Close down the NX transport and free all the allocated resources.
+ * The fd parameter can be either the local descriptor or NX_FD_ANY.
+ * This must be explicitly called by the agent before the proxy can
+ * start the tear down procedure.
+ */
+
+extern int NXTransDestroy(int fd);
+
+/*
+ * Tell to the proxy how to handle the standard POSIX signals. For
+ * example, given the SIGINT signal, the caller can specify any of
+ * the following actions:
+ *
+ * NX_SIGNAL_ENABLE: A signal handler will have to be installed by
+ * the library, so that it can be intercepted by
+ * the proxy.
+ *
+ * NX_SIGNAL_DISABLE: The signal will be handled by the caller and,
+ * eventually, forwarded to the proxy by calling
+ * NXTransSignal() explicitly.
+ *
+ * NX_SIGNAL_RAISE: The signal must be handled now, as if it had
+ * been delivered by the operating system. This
+ * function can be called by the agent with the
+ * purpose of propagating a signal to the proxy.
+ *
+ * NX_SIGNAL_FORWARD: A signal handler will have to be installed by
+ * the library but the library will have to call
+ * the original signal handler when the signal
+ * is received.
+ *
+ * As a rule of thumb, agents should let the proxy handle SIGUSR1
+ * and SIGUSR2, used for producing the NX protocol statistics, and
+ * SIGHUP, used for disconnecting the NX transport.
+ *
+ * The following signals are blocked by default upon creation of the
+ * NX transport:
+ *
+ * SIGCHLD These signals should be always put under the control
+ * SIGUSR1 of the proxy. If agents are intercepting them, agents
+ * SIGUSR2 should later call NXTransSignal(..., NX_SIGNAL_RAISE)
+ * SIGHUP to forward the signal to the proxy. As an alternative
+ * they can specify a NX_SIGNAL_FORWARD action, so they,
+ * in turn, can be notified about the signal. This can
+ * be especially useful for SIGCHLD.
+ *
+ * SIGINT These signals should be intercepted by agents. Agents
+ * SIGTERM should ensure that NXTransDestroy() is called before
+ * exiting, to give the proxy a chance to shut down the
+ * NX transport.
+ *
+ * SIGPIPE This signal is blocked by the proxy, but not used to
+ * implement any functionality. It can be handled by the
+ * NX agent without affecting the proxy.
+ *
+ * SIGALRM This is now used by the proxy and agents should not
+ * redefine it. Agents can use the signal to implement
+ * their own timers but should not interleave calls to
+ * the NX transport and should restore the old handler
+ * when the timeout is raised.
+ *
+ * SIGVTALRM These signals are not used but may be used in future
+ * SIGWINCH versions of the library.
+ * SIGIO
+ * SIGTSTP
+ * SIGTTIN
+ * SIGTTOU
+ *
+ * By calling NXTransSignal(..., NX_SIGNAL_DISABLE) nxcomp will res-
+ * tore the signal handler that was saved at the time the proxy hand-
+ * ler was installed. This means that you should call the function
+ * just after the XOpenDisplay() or any other function used to init-
+ * ialize the NX transport.
+ */
+
+extern int NXTransSignal(int signal, int action);
+
+/*
+ * Return a value between 0 and 9 indicating the congestion level
+ * based on the tokens still available. A value of 9 means that
+ * the link is congested and no further data can be sent.
+ */
+
+extern int NXTransCongestion(int fd);
+
+/*
+ * Let the application to be notified by the proxy when an event oc-
+ * curs. The parameter, as set at the time the handler is installed,
+ * is passed each time to the callback function. The parameter is
+ * presumably the display pointer, given that at the present moment
+ * the NX transport doesn't have access to the display structure and
+ * so wouldn't be able to determine the display to pass to the call-
+ * back function.
+ *
+ * NX_HANDLER_FLUSH: The handler function is called when some
+ * more data has been written to the proxy
+ * link.
+ *
+ * The data is the number of bytes written.
+ *
+ * NX_HANDLER_STATISTICS: This handler is called to let the agent
+ * include arbitrary data in the transport
+ * statistics. The parameter, in this case,
+ * is a pointer to a pointer to a null term-
+ * inated string. The pointer is set at the
+ * time the handler is registered. The point-
+ * ed string will have to be filled by the
+ * agent with its statistics data.
+ *
+ * The data can be NX_STATISTICS_PARTIAL or NX_STATISTICS_TOTAL. The
+ * agent can refer to the value by using the NXStatisticsPartial and
+ * NXStatisticsTotal constants defined in NXvars.h.
+ *
+ * Note that these interfaces are used by Xlib and nxcompext. Agents
+ * should never call these interfaces directly, but use the nxcompext
+ * wrapper.
+ */
+
+extern int NXTransHandler(int fd, int type, void (*handler)(void *parameter,
+ int reason), void *parameter);
+
+/*
+ * Set the policy to be used by the NX transport to write data to the
+ * proxy link:
+ *
+ * NX_POLICY_IMMEDIATE: When set to immediate, the proxy will try to
+ * write the data just after having encoded it.
+ *
+ * NX_POLICY_DEFERRED: When policy is set to deferred, data will be
+ * accumulated in a buffer and written to the
+ * remote proxy when NXTransFlush() is called by
+ * the agent.
+ */
+
+extern int NXTransPolicy(int fd, int type);
+
+/*
+ * Query the number of bytes that have been accumulated for a deferred
+ * flush.
+ */
+
+extern int NXTransFlushable(int fd);
+
+/*
+ * Tell to the NX transport to write all the accumulated data to the
+ * remote proxy.
+ */
+
+extern int NXTransFlush(int fd);
+
+/*
+ * Create a new channel of the given type. It returns 1 on success,
+ * 0 if the NX transport is not running, or -1 in the case of error.
+ * On success, the descriptor provided by the caller can be later
+ * used for the subsequent I/O. The type parameter not only tells to
+ * the proxy the remote port where the channel has to be connected,
+ * but also gives a hint about the type of data that will be carried
+ * by the channel, so that the proxy can try to optimize the traffic
+ * on the proxy link.
+ *
+ * NX_CHANNEL_X: The channel will carry X traffic and it
+ * will be connected to the remote X display.
+ *
+ * NX_CHANNEL_CUPS: The channel will carry CUPS/IPP protocol.
+ *
+ * NX_CHANNEL_SMB: The channel will carry SMB/CIFS protocol.
+ *
+ * NX_CHANNEL_MEDIA: The channel will transport audio or other
+ * multimedia data.
+ *
+ * NX_CHANNEL_HTTP: The channel will carry HTTP protocol.
+ *
+ * NX_CHANNEL_FONT: The channel will forward a X font server
+ * connection.
+ *
+ * Only a proxy running at the NX server/X client side will be able
+ * to create a X, CUPS, SMB, MEDIA and HTTP channel. A proxy running
+ * at the NX client/X server side can create font server connections.
+ * The channel creation will also fail if the remote end has not been
+ * set up to forward the connection.
+ *
+ * To create a new channel the agent will have to set up a socketpair
+ * and pass to the proxy one of the socket descriptors.
+ *
+ * Example:
+ *
+ * #include <sys/types.h>
+ * #include <sys/socket.h>
+ *
+ * int fds[2];
+ *
+ * if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) < 0)
+ * {
+ * ...
+ * }
+ * else
+ * {
+ * //
+ * // Use fds[0] locally and let the
+ * // proxy use fds[1].
+ * //
+ *
+ * if (NXTransChannel(NX_FD_ANY, fds[1], NX_CHANNEL_X) <= 0)
+ * {
+ * ...
+ * }
+ *
+ * //
+ * // The agent can now use fds[0] in
+ * // read(), write() and select()
+ * // system calls.
+ * //
+ *
+ * ...
+ * }
+ *
+ * Note that all the I/O on the descriptor should be non-blocking, to
+ * give a chance to the NX transport to run in the background and handle
+ * the data that will be fed to the agent's side of the socketpair. This
+ * will happen automatically, as long as the agent uses the XSelect()
+ * version of the select() function (as it is normal whenever performing
+ * Xlib I/O). In all the other cases, like presumably in the agent's main
+ * loop, the agent will have to loop through NXTransPrepare(), NXTrans-
+ * Select() and NXTransExecute() functions explicitly, adding to the sets
+ * the descriptors that are awaited by the agent. Please check the imple-
+ * mentation of _XSelect() in nx-X11/lib/X11/XlibInt.c for an example.
+ */
+
+extern int NXTransChannel(int fd, int channelfd, int type);
+
+/*
+ * Return the name of the files used by the proxy for the current session.
+ *
+ * The type parameter can be:
+ *
+ * NX_FILE_SESSION: Usually the file 'session' in the user's session
+ * directory.
+ *
+ * NX_FILE_ERRORS: The file used for the diagnostic output. Usually
+ * the file 'errors' in the session directory.
+ *
+ * NX_FILE_OPTIONS: The file containing the NX options, if any.
+ *
+ * NX_FILE_STATS: The file used for the statistics output.
+ *
+ * The returned string is allocated in static memory. The caller should
+ * copy the string upon returning from the function, without freeing the
+ * pointer.
+ */
+
+extern const char *NXTransFile(int type);
+
+/*
+ * Return the time in milliseconds elapsed since the last call to this
+ * same function.
+ */
+
+extern long NXTransTime(void);
+
+/*
+ * Other interfaces to the internal transport functions.
+ */
+
+extern int NXTransProxy(int fd, int mode, const char *display);
+
+extern int NXTransClient(const char *display);
+
+extern int NXTransDialog(const char *caption, const char *message,
+ const char *window, const char *type, int local,
+ const char *display);
+
+extern int NXTransAlert(int code, int local);
+
+extern int NXTransWatchdog(int timeout);
+
+extern int NXTransKeeper(int caches, int images, const char *root);
+
+extern void NXTransExit(int code) __attribute__((noreturn));
+
+extern int NXTransParseCommandLine(int argc, const char **argv);
+extern int NXTransParseEnvironment(const char *env, int force);
+
+extern void NXTransCleanup(void) __attribute__((noreturn));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NX_H */
diff --git a/nxcomp/NXalert.h b/nxcomp/NXalert.h
new file mode 100644
index 000000000..2f01a3071
--- /dev/null
+++ b/nxcomp/NXalert.h
@@ -0,0 +1,268 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef NXalert_H
+#define NXalert_H
+
+#define ALERT_CAPTION_PREFIX "NX - "
+
+#define INTERNAL_ERROR_ALERT 1
+#define INTERNAL_ERROR_ALERT_TYPE "error"
+#define INTERNAL_ERROR_ALERT_STRING \
+"\
+An unrecoverable internal error was detected.\n\
+Press OK to terminate the current session.\n\
+"
+
+#define CLOSE_DEAD_X_CONNECTION_CLIENT_ALERT 2
+#define CLOSE_DEAD_X_CONNECTION_CLIENT_ALERT_TYPE "yesno"
+#define CLOSE_DEAD_X_CONNECTION_CLIENT_ALERT_STRING \
+"\
+One of the applications currently in use is not responding.\n\
+Do you want to terminate the current session?\n\
+"
+
+#define CLOSE_DEAD_X_CONNECTION_SERVER_ALERT 3
+#define CLOSE_DEAD_X_CONNECTION_SERVER_ALERT_TYPE "yesno"
+#define CLOSE_DEAD_X_CONNECTION_SERVER_ALERT_STRING \
+"\
+One of the applications did not behave correctly and caused\n\
+the X server to stop responding in a timely fashion. Do you\n\
+want to terminate the current session?\n\
+"
+
+#define CLOSE_DEAD_PROXY_CONNECTION_CLIENT_ALERT 4
+#define CLOSE_DEAD_PROXY_CONNECTION_CLIENT_ALERT_TYPE NULL
+#define CLOSE_DEAD_PROXY_CONNECTION_CLIENT_ALERT_STRING NULL
+
+#define CLOSE_DEAD_PROXY_CONNECTION_SERVER_ALERT 5
+#define CLOSE_DEAD_PROXY_CONNECTION_SERVER_ALERT_TYPE "yesno"
+#define CLOSE_DEAD_PROXY_CONNECTION_SERVER_ALERT_STRING \
+"\
+No response received from the remote server.\n\
+Do you want to terminate the current session?\n\
+"
+
+#define RESTART_DEAD_PROXY_CONNECTION_CLIENT_ALERT 6
+#define RESTART_DEAD_PROXY_CONNECTION_CLIENT_ALERT_TYPE NULL
+#define RESTART_DEAD_PROXY_CONNECTION_CLIENT_ALERT_STRING NULL
+
+#define RESTART_DEAD_PROXY_CONNECTION_SERVER_ALERT 7
+#define RESTART_DEAD_PROXY_CONNECTION_SERVER_ALERT_TYPE "yesno"
+#define RESTART_DEAD_PROXY_CONNECTION_SERVER_ALERT_STRING \
+"\
+Connection with remote server was shut down. NX will try\n\
+to establish a new server connection. Session could have\n\
+been left in a unusable state. Do you want to terminate\n\
+the session?\n\
+"
+
+#define CLOSE_UNRESPONSIVE_X_SERVER_ALERT 8
+#define CLOSE_UNRESPONSIVE_X_SERVER_ALERT_TYPE "panic"
+#define CLOSE_UNRESPONSIVE_X_SERVER_ALERT_STRING \
+"\
+You pressed the key sequence CTRL+ALT+SHIFT+ESC.\n\
+This is probably because your X server has become\n\
+unresponsive. Session will be terminated in 30\n\
+seconds unless you abort the procedure by pressing\n\
+the Cancel button.\n\
+"
+
+#define WRONG_PROXY_VERSION_ALERT 9
+#define WRONG_PROXY_VERSION_ALERT_TYPE "ok"
+#define WRONG_PROXY_VERSION_ALERT_STRING \
+"\
+Local NX libraries version " VERSION " do not match the NX\n\
+version of the remote server. Please check the error\n\
+log on the server to find out which client version you\n\
+need to install to be able to access this server.\n\
+"
+
+#define FAILED_PROXY_CONNECTION_CLIENT_ALERT 10
+#define FAILED_PROXY_CONNECTION_CLIENT_ALERT_TYPE NULL
+#define FAILED_PROXY_CONNECTION_CLIENT_ALERT_STRING NULL
+
+#define FAILED_PROXY_CONNECTION_SERVER_ALERT 11
+#define FAILED_PROXY_CONNECTION_SERVER_ALERT_TYPE "yesno"
+#define FAILED_PROXY_CONNECTION_SERVER_ALERT_STRING \
+"\
+Could not yet establish the connection to the remote\n\
+proxy. Do you want to terminate the current session?\n\
+"
+
+#define MISSING_PROXY_CACHE_ALERT 12
+#define MISSING_PROXY_CACHE_ALERT_TYPE "ok"
+#define MISSING_PROXY_CACHE_ALERT_STRING \
+"\
+NX was unable to negotiate a cache for this session.\n\
+This may happen if this is the first time you run a\n\
+session on this server or if cache was corrupted or\n\
+produced by an incompatible NX version.\n\
+"
+
+#define ABORT_PROXY_CONNECTION_ALERT 13
+#define ABORT_PROXY_CONNECTION_ALERT_TYPE "ok"
+#define ABORT_PROXY_CONNECTION_ALERT_STRING \
+"\
+The connection with the remote server was shut down.\n\
+Please check the state of your network connection.\n\
+"
+
+/*
+ * The one below is a special alert, used to close
+ * a previous alert that is running on the given
+ * side. This can be used to get rid of a message
+ * that has ceased to hold true.
+ */
+
+#define DISPLACE_MESSAGE_ALERT 14
+#define DISPLACE_MESSAGE_ALERT_TYPE NULL
+#define DISPLACE_MESSAGE_ALERT_STRING NULL
+
+/*
+ * These are the other alert messages that were
+ * added in the 1.5.0 release. The first is never
+ * shown and is intended just for testing.
+ */
+
+#define GREETING_MESSAGE_ALERT 15
+#define GREETING_MESSAGE_ALERT_TYPE "ok"
+#define GREETING_MESSAGE_ALERT_STRING \
+"\
+Welcome to NX from the NoMachine team. We really\n\
+hope you will enjoy this wonderful software as much\n\
+as we had fun making it ;-).\n\
+"
+
+/*
+ * These alerts are intended to notify the user
+ * of the reason why the agent failed to resume
+ * the session.
+ */
+
+#define START_RESUME_SESSION_ALERT 16
+#define START_RESUME_SESSION_ALERT_TYPE "ok"
+#define START_RESUME_SESSION_ALERT_STRING \
+"\
+You appear to run your NX session across a slow network\n\
+connection. Resuming the session may require some time.\n\
+Please wait.\
+"
+
+#define FAILED_RESUME_DISPLAY_ALERT 17
+#define FAILED_RESUME_DISPLAY_ALERT_TYPE "error"
+#define FAILED_RESUME_DISPLAY_ALERT_STRING \
+"\
+Failed to open the display. Can't resume the NX\n\
+session on this display.\n\
+"
+
+#define FAILED_RESUME_DISPLAY_BROKEN_ALERT 18
+#define FAILED_RESUME_DISPLAY_BROKEN_TYPE "error"
+#define FAILED_RESUME_DISPLAY_BROKEN_STRING \
+"\
+The display connection was broken while trying to\n\
+resume the session. Please, check your network\n\
+connection and try again.\n\
+"
+
+#define FAILED_RESUME_VISUALS_ALERT 19
+#define FAILED_RESUME_VISUALS_ALERT_TYPE "error"
+#define FAILED_RESUME_VISUALS_ALERT_STRING \
+"\
+Failed to restore all the required visuals.\n\
+Can't resume the NX session on this display.\n\
+"
+
+#define FAILED_RESUME_COLORMAPS_ALERT 20
+#define FAILED_RESUME_COLORMAPS_ALERT_TYPE "error"
+#define FAILED_RESUME_COLORMAPS_ALERT_STRING \
+"\
+The number of available colormaps is different\n\
+on the new display. Can't resume the NX session\n\
+on this display.\n\
+"
+
+#define FAILED_RESUME_PIXMAPS_ALERT 21
+#define FAILED_RESUME_PIXMAPS_ALERT_TYPE "error"
+#define FAILED_RESUME_PIXMAPS_ALERT_STRING \
+"\
+Failed to restore all the required pixmap formats.\n\
+Can't resume the NX session on this display.\n\
+"
+
+#define FAILED_RESUME_DEPTHS_ALERT 22
+#define FAILED_RESUME_DEPTHS_ALERT_TYPE "error"
+#define FAILED_RESUME_DEPTHS_ALERT_STRING \
+"\
+Failed to restore all the required screen depths.\n\
+Can't resume the NX session on this display.\n\
+"
+
+#define FAILED_RESUME_RENDER_ALERT 23
+#define FAILED_RESUME_RENDER_ALERT_TYPE "error"
+#define FAILED_RESUME_RENDER_ALERT_STRING \
+"\
+The render extension is missing or an incompatible\n\
+version was detected on your X server. Can't resume\n\
+the NX session on this display.\n\
+"
+
+#define FAILED_RESUME_FONTS_ALERT 24
+#define FAILED_RESUME_FONTS_ALERT_TYPE "error"
+#define FAILED_RESUME_FONTS_ALERT_STRING \
+"\
+One or more of the fonts that are in use by the\n\
+session are missing. Can't resume the NX session\n\
+on this display.\n\
+"
+
+#define ABORT_PROXY_NEGOTIATION_ALERT 62
+#define ABORT_PROXY_NEGOTIATION_ALERT_TYPE "ok"
+#define ABORT_PROXY_NEGOTIATION_ALERT_STRING \
+"\
+The remote proxy closed the connection while negotiating\n\
+the session. This may be due to the wrong authentication\n\
+credentials passed to the server.\n\
+"
+
+#define ABORT_PROXY_SHUTDOWN_ALERT 64
+#define ABORT_PROXY_SHUTDOWN_ALERT_TYPE "ok"
+#define ABORT_PROXY_SHUTDOWN_ALERT_STRING \
+"\
+No response received from the remote proxy while\n\
+waiting for the session shutdown.\n\
+"
+
+#define FAILED_XDMCP_CONNECTION_ALERT 65
+#define FAILED_XDMCP_CONNECTION_ALERT_TYPE "ok"
+#define FAILED_XDMCP_CONNECTION_ALERT_STRING \
+"\
+The XDM host that was contacted by the NX server doesn't\n\
+seem to be able to start the session. Please check your\n\
+server configuration.\n\
+"
+
+/*
+ * Used to handle the backward compatibility.
+ * Update the numbers if you add a new alert.
+ */
+
+#define LAST_PROTO_STEP_6_ALERT 63
+#define LAST_PROTO_STEP_7_ALERT 65
+
+#endif /* NXalert_H */
diff --git a/nxcomp/NXmitshm.h b/nxcomp/NXmitshm.h
new file mode 100644
index 000000000..aa79ec8ae
--- /dev/null
+++ b/nxcomp/NXmitshm.h
@@ -0,0 +1,48 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef NXmitshm_H
+#define NXmitshm_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Import opcodes from <X11/extensions/XShm.h>
+ * to get rid of weird dependencies from other
+ * headers of X environment.
+ */
+
+#define X_ShmQueryVersion 0
+#define X_ShmAttach 1
+#define X_ShmDetach 2
+#define X_ShmPutImage 3
+#define X_ShmGetImage 4
+#define X_ShmCreatePixmap 5
+
+#define ShmCompletion 0
+#define ShmNumberEvents (ShmCompletion + 1)
+
+#define BadShmSeg 0
+#define ShmNumberErrors (BadShmSeg + 1)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NXmitshm_H */
diff --git a/nxcomp/NXpack.h b/nxcomp/NXpack.h
new file mode 100644
index 000000000..de1c0f0d9
--- /dev/null
+++ b/nxcomp/NXpack.h
@@ -0,0 +1,133 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef NXpack_H
+#define NXpack_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MASK_METHOD_LIMIT 10
+
+#define NO_MASK 0
+
+#define MASK_8_COLORS 1
+#define MASK_64_COLORS 2
+#define MASK_256_COLORS 3
+#define MASK_512_COLORS 4
+#define MASK_4K_COLORS 5
+#define MASK_32K_COLORS 6
+#define MASK_64K_COLORS 7
+#define MASK_256K_COLORS 8
+#define MASK_2M_COLORS 9
+#define MASK_16M_COLORS 10
+
+#define PACK_METHOD_LIMIT 128
+
+#define NO_PACK 0
+
+#define PACK_MASKED_8_COLORS 1
+#define PACK_MASKED_64_COLORS 2
+#define PACK_MASKED_256_COLORS 3
+#define PACK_MASKED_512_COLORS 4
+#define PACK_MASKED_4K_COLORS 5
+#define PACK_MASKED_32K_COLORS 6
+#define PACK_MASKED_64K_COLORS 7
+#define PACK_MASKED_256K_COLORS 8
+#define PACK_MASKED_2M_COLORS 9
+#define PACK_MASKED_16M_COLORS 10
+
+#define PACK_RAW_8_BITS 3
+#define PACK_RAW_16_BITS 7
+#define PACK_RAW_24_BITS 10
+
+#define PACK_COLORMAP_256_COLORS 11
+
+#define PACK_JPEG_8_COLORS 26
+#define PACK_JPEG_64_COLORS 27
+#define PACK_JPEG_256_COLORS 28
+#define PACK_JPEG_512_COLORS 29
+#define PACK_JPEG_4K_COLORS 30
+#define PACK_JPEG_32K_COLORS 31
+#define PACK_JPEG_64K_COLORS 32
+#define PACK_JPEG_256K_COLORS 33
+#define PACK_JPEG_2M_COLORS 34
+#define PACK_JPEG_16M_COLORS 35
+
+#define PACK_PNG_8_COLORS 37
+#define PACK_PNG_64_COLORS 38
+#define PACK_PNG_256_COLORS 39
+#define PACK_PNG_512_COLORS 40
+#define PACK_PNG_4K_COLORS 41
+#define PACK_PNG_32K_COLORS 42
+#define PACK_PNG_64K_COLORS 43
+#define PACK_PNG_256K_COLORS 44
+#define PACK_PNG_2M_COLORS 45
+#define PACK_PNG_16M_COLORS 46
+
+#define PACK_RGB_16M_COLORS 63
+#define PACK_RLE_16M_COLORS 64
+
+#define PACK_ALPHA 65
+#define PACK_COLORMAP 66
+
+#define PACK_BITMAP_16M_COLORS 67
+
+/*
+ * Not really pack methods. These values
+ * allow dynamic selection of the pack
+ * method by the agent.
+ */
+
+#define PACK_NONE 0
+#define PACK_LOSSY 253
+#define PACK_LOSSLESS 254
+#define PACK_ADAPTIVE 255
+
+/*
+ * Reduce the number of colors in the
+ * image by applying a mask.
+ */
+
+typedef struct
+{
+ unsigned int color_mask;
+ unsigned int correction_mask;
+ unsigned int white_threshold;
+ unsigned int black_threshold;
+
+} ColorMask;
+
+extern const ColorMask Mask8TrueColor;
+extern const ColorMask Mask64TrueColor;
+extern const ColorMask Mask512TrueColor;
+extern const ColorMask Mask4KTrueColor;
+extern const ColorMask Mask32KTrueColor;
+extern const ColorMask Mask256KTrueColor;
+extern const ColorMask Mask2MTrueColor;
+extern const ColorMask Mask16MTrueColor;
+
+const ColorMask *MethodColorMask(unsigned int method);
+
+int MethodBitsPerPixel(unsigned int method);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NXpack_H */
diff --git a/nxcomp/NXproto.h b/nxcomp/NXproto.h
new file mode 100644
index 000000000..ce25cc4c7
--- /dev/null
+++ b/nxcomp/NXproto.h
@@ -0,0 +1,439 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef NXproto_H
+#define NXproto_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <X11/X.h>
+#include <X11/Xmd.h>
+#include <X11/Xproto.h>
+
+/*
+ * Force the size to match the wire protocol.
+ */
+
+#define Drawable CARD32
+#define GContext CARD32
+
+#define sz_xNXGetControlParametersReq 4
+#define sz_xNXGetCleanupParametersReq 4
+#define sz_xNXGetImageParametersReq 4
+#define sz_xNXGetUnpackParametersReq 8
+#define sz_xNXGetShmemParametersReq 16
+#define sz_xNXGetFontParametersReq 4
+#define sz_xNXSetExposeParametersReq 8
+#define sz_xNXSetCacheParametersReq 8
+#define sz_xNXStartSplitReq 8
+#define sz_xNXEndSplitReq 4
+#define sz_xNXCommitSplitReq 12
+#define sz_xNXSetUnpackGeometryReq 24
+#define sz_xNXSetUnpackColormapReq 16
+#define sz_xNXSetUnpackAlphaReq 16
+#define sz_xNXPutPackedImageReq 40
+#define sz_xNXFreeUnpackReq 4
+#define sz_xNXFinishSplitReq 4
+#define sz_xNXAbortSplitReq 4
+#define sz_xNXFreeSplitReq 4
+
+#define sz_xGetControlParametersReply 32
+#define sz_xGetCleanupParametersReply 32
+#define sz_xGetImageParametersReply 32
+#define sz_xGetUnpackParametersReply 32
+#define sz_xGetShmemParametersReply 32
+
+#define LINK_TYPE_LIMIT 5
+
+#define LINK_TYPE_NONE 0
+#define LINK_TYPE_MODEM 1
+#define LINK_TYPE_ISDN 2
+#define LINK_TYPE_ADSL 3
+#define LINK_TYPE_WAN 4
+#define LINK_TYPE_LAN 5
+
+/*
+ * NX Replies.
+ */
+
+/*
+ * The following reply has 4 new boolean
+ * fields in the last protocol version.
+ */
+
+typedef struct _NXGetControlParametersReply {
+ BYTE type; /* Is X_Reply. */
+ CARD8 linkType;
+ CARD16 sequenceNumber B16;
+ CARD32 length B32; /* Is 0. */
+ CARD8 localMajor;
+ CARD8 localMinor;
+ CARD8 localPatch;
+ CARD8 remoteMajor;
+ CARD8 remoteMinor;
+ CARD8 remotePatch;
+ CARD16 splitTimeout B16;
+ CARD16 motionTimeout B16;
+ CARD8 splitMode;
+ CARD8 pad1;
+ CARD32 splitSize B32;
+ CARD8 packMethod;
+ CARD8 packQuality;
+ CARD8 dataLevel;
+ CARD8 streamLevel;
+ CARD8 deltaLevel;
+ CARD8 loadCache;
+ CARD8 saveCache;
+ CARD8 startupCache;
+} xNXGetControlParametersReply;
+
+typedef struct _NXGetCleanupParametersReply {
+ BYTE type; /* Is X_Reply. */
+ BYTE pad;
+ CARD16 sequenceNumber B16;
+ CARD32 length B32; /* Is 0. */
+ BOOL cleanGet;
+ BOOL cleanAlloc;
+ BOOL cleanFlush;
+ BOOL cleanSend;
+ BOOL cleanImages;
+ BYTE pad1, pad2, pad3;
+ CARD32 pad4 B32;
+ CARD32 pad5 B32;
+ CARD32 pad6 B32;
+ CARD32 pad7 B32;
+} xNXGetCleanupParametersReply;
+
+typedef struct _NXGetImageParametersReply {
+ BYTE type; /* Is X_Reply. */
+ BYTE pad;
+ CARD16 sequenceNumber B16;
+ CARD32 length B32; /* Is 0. */
+ BOOL imageSplit;
+ BOOL imageMask;
+ BOOL imageFrame;
+ CARD8 imageMaskMethod;
+ CARD8 imageSplitMethod;
+ BYTE pad1, pad2, pad3;
+ CARD32 pad4 B32;
+ CARD32 pad5 B32;
+ CARD32 pad6 B32;
+ CARD32 pad7 B32;
+} xNXGetImageParametersReply;
+
+/*
+ * Data is made of PACK_METHOD_LIMIT values of
+ * type BOOL telling which unpack capabilities
+ * are implemented in proxy.
+ */
+
+typedef struct _NXGetUnpackParametersReply {
+ BYTE type; /* Is X_Reply. */
+ BYTE pad;
+ CARD16 sequenceNumber B16;
+ CARD32 length B32; /* Is PACK_METHOD_LIMIT / 4 from NXpack.h. */
+ CARD8 entries; /* Is PACK_METHOD_LIMIT. */
+ BYTE pad1, pad2, pad3;
+ CARD32 pad4 B32;
+ CARD32 pad5 B32;
+ CARD32 pad6 B32;
+ CARD32 pad7 B32;
+ CARD32 pad8 B32;
+} xNXGetUnpackParametersReply;
+
+typedef struct _NXGetShmemParametersReply {
+ BYTE type; /* Is X_Reply. */
+ CARD8 stage; /* As in the corresponding request. */
+ CARD16 sequenceNumber B16;
+ CARD32 length B32; /* Is 0. */
+ BOOL clientEnabled; /* SHM on path agent to proxy. */
+ BOOL serverEnabled; /* SHM on path proxy to X server. */
+ BYTE pad1, pad2; /* Previous values can be checked */
+ CARD32 clientSize B32; /* at end of stage 2. */
+ CARD32 serverSize B32;
+ CARD32 pad3 B32;
+ CARD32 pad4 B32;
+ CARD32 pad5 B32;
+} xNXGetShmemParametersReply;
+
+typedef struct _NXGetFontParametersReply {
+ BYTE type; /* Is X_Reply. */
+ BYTE pad1;
+ CARD16 sequenceNumber B16;
+ CARD32 length B32; /* Is length of path string + 1 / 4. */
+ CARD32 pad2 B32;
+ CARD32 pad3 B32;
+ CARD32 pad4 B32;
+ CARD32 pad5 B32;
+ CARD32 pad6 B32;
+ CARD32 pad7 B32;
+} xNXGetFontParametersReply;
+
+/*
+ * NX Requests.
+ */
+
+typedef struct _NXGetControlParametersReq {
+ CARD8 reqType;
+ BYTE pad;
+ CARD16 length B16;
+} xNXGetControlParametersReq;
+
+typedef struct _NXGetCleanupParametersReq {
+ CARD8 reqType;
+ BYTE pad;
+ CARD16 length B16;
+} xNXGetCleanupParametersReq;
+
+typedef struct _NXGetImageParametersReq {
+ CARD8 reqType;
+ BYTE pad;
+ CARD16 length B16;
+} xNXGetImageParametersReq;
+
+typedef struct _NXGetUnpackParametersReq {
+ CARD8 reqType;
+ BYTE pad;
+ CARD16 length B16;
+ CARD8 entries;
+ BYTE pad1, pad2, pad3;
+} xNXGetUnpackParametersReq;
+
+typedef struct _NXGetShmemParametersReq {
+ CARD8 reqType;
+ CARD8 stage; /* It is between 0 and 2. */
+ CARD16 length B16;
+ BOOL enableClient; /* X client side support is */
+ BOOL enableServer; /* not implemented yet. */
+ BYTE pad1, pad2;
+ CARD32 clientSegment; /* XID identifying the shared */
+ CARD32 serverSegment; /* memory segments. */
+} xNXGetShmemParametersReq;
+
+typedef struct _NXGetFontParametersReq {
+ CARD8 reqType;
+ CARD8 pad;
+ CARD16 length B16;
+} xNXGetFontParametersReq;
+
+/*
+ * The available split modes.
+ */
+
+#define NXSplitModeDefault 0
+#define NXSplitModeAsync 1
+#define NXSplitModeSync 2
+
+typedef struct _NXStartSplitReq {
+ CARD8 reqType;
+ CARD8 resource;
+ CARD16 length B16;
+ CARD8 mode;
+ BYTE pad1, pad2, pad3;
+} xNXStartSplitReq;
+
+typedef struct _NXEndSplitReq {
+ CARD8 reqType;
+ CARD8 resource;
+ CARD16 length B16;
+} xNXEndSplitReq;
+
+typedef struct _NXCommitSplitReq {
+ CARD8 reqType;
+ CARD8 resource;
+ CARD16 length B16;
+ CARD8 propagate;
+ CARD8 request;
+ BYTE pad1, pad2;
+ CARD32 position B32;
+} xNXCommitSplitReq;
+
+typedef struct _NXFinishSplitReq {
+ CARD8 reqType;
+ CARD8 resource;
+ CARD16 length B16;
+} xNXFinishSplitReq;
+
+typedef struct _NXAbortSplitReq {
+ CARD8 reqType;
+ CARD8 resource;
+ CARD16 length B16;
+} xNXAbortSplitReq;
+
+typedef struct _NXFreeSplitReq {
+ CARD8 reqType;
+ CARD8 resource;
+ CARD16 length B16;
+} xNXFreeSplitReq;
+
+typedef struct _NXSetExposeParametersReq {
+ CARD8 reqType;
+ BYTE pad;
+ CARD16 length B16;
+ BOOL expose;
+ BOOL graphicsExpose;
+ BOOL noExpose;
+ BYTE pad1;
+} xNXSetExposeParametersReq;
+
+typedef struct _NXSetCacheParametersReq {
+ CARD8 reqType;
+ BYTE pad;
+ CARD16 length B16;
+ BOOL enableCache;
+ BOOL enableSplit;
+ BOOL enableSave;
+ BOOL enableLoad;
+} xNXSetCacheParametersReq;
+
+typedef struct _NXSetUnpackGeometryReq {
+ CARD8 reqType;
+ CARD8 resource;
+ CARD16 length B16;
+ CARD8 depth1Bpp;
+ CARD8 depth4Bpp;
+ CARD8 depth8Bpp;
+ CARD8 depth16Bpp;
+ CARD8 depth24Bpp;
+ CARD8 depth32Bpp;
+ BYTE pad1, pad2;
+ CARD32 redMask B32;
+ CARD32 greenMask B32;
+ CARD32 blueMask B32;
+} xNXSetUnpackGeometryReq;
+
+typedef struct _NXSetUnpackColormapReq {
+ CARD8 reqType;
+ CARD8 resource;
+ CARD16 length B16;
+ CARD8 method;
+ BYTE pad1, pad2, pad3;
+ CARD32 srcLength B32;
+ CARD32 dstLength B32;
+} xNXSetUnpackColormapReq;
+
+typedef struct _NXSetUnpackAlphaReq {
+ CARD8 reqType;
+ CARD8 resource;
+ CARD16 length B16;
+ CARD8 method;
+ BYTE pad1, pad2, pad3;
+ CARD32 srcLength B32;
+ CARD32 dstLength B32;
+} xNXSetUnpackAlphaReq;
+
+typedef struct _NXPutPackedImageReq {
+ CARD8 reqType;
+ CARD8 resource;
+ CARD16 length B16;
+ Drawable drawable B32;
+ GContext gc B32;
+ CARD8 method;
+ CARD8 format;
+ CARD8 srcDepth;
+ CARD8 dstDepth;
+ CARD32 srcLength B32;
+ CARD32 dstLength B32;
+ INT16 srcX B16, srcY B16;
+ CARD16 srcWidth B16, srcHeight B16;
+ INT16 dstX B16, dstY B16;
+ CARD16 dstWidth B16, dstHeight B16;
+} xNXPutPackedImageReq;
+
+typedef struct _NXFreeUnpackReq {
+ CARD8 reqType;
+ CARD8 resource;
+ CARD16 length B16;
+} xNXFreeUnpackReq;
+
+/*
+ * The X_NXSplitData and X_NXSplitEvent opcodes
+ * are used internally and are ignored if coming
+ * from the agent.
+ */
+
+#define X_NXInternalGenericData 0
+#define X_NXInternalGenericReply 1
+#define X_NXInternalGenericRequest 255
+
+#define X_NXInternalShapeExtension 128
+#define X_NXInternalRenderExtension 129
+
+#define X_NXFirstOpcode 230
+#define X_NXLastOpcode 252
+
+#define X_NXGetControlParameters 230
+#define X_NXGetCleanupParameters 231
+#define X_NXGetImageParameters 232
+#define X_NXGetUnpackParameters 233
+#define X_NXStartSplit 234
+#define X_NXEndSplit 235
+#define X_NXSplitData 236
+#define X_NXCommitSplit 237
+#define X_NXSetExposeParameters 240
+#define X_NXSetUnpackGeometry 241
+#define X_NXSetUnpackColormap 242
+#define X_NXPutPackedImage 243
+#define X_NXSplitEvent 244
+#define X_NXGetShmemParameters 245
+#define X_NXSetUnpackAlpha 246
+#define X_NXFreeUnpack 247
+#define X_NXFinishSplit 248
+#define X_NXAbortSplit 249
+#define X_NXFreeSplit 250
+#define X_NXGetFontParameters 251
+#define X_NXSetCacheParameters 252
+
+/*
+ * The following events are received by the agent
+ * in the form of a ClientMessage with the value
+ * 0 in fields atom and window. The format is
+ * always 32. Event specific data starts at byte
+ * offset 12.
+ *
+ * These events are sent by the NX transport to
+ * notify the agent about the result of a split
+ * operation.
+ */
+
+#define NXNoSplitNotify 1
+#define NXStartSplitNotify 2
+#define NXCommitSplitNotify 3
+#define NXEndSplitNotify 4
+#define NXEmptySplitNotify 5
+
+/*
+ * Notifications of collect events. These events
+ * don't come from the NX transport but are put
+ * back in client's event queue by NXlib.
+ */
+
+#define NXCollectImageNotify 8
+#define NXCollectPropertyNotify 9
+#define NXCollectGrabPointerNotify 10
+#define NXCollectInputFocusNotify 11
+
+#undef Drawable
+#undef GContext
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NXproto_H */
diff --git a/nxcomp/NXrender.h b/nxcomp/NXrender.h
new file mode 100644
index 000000000..bc359f273
--- /dev/null
+++ b/nxcomp/NXrender.h
@@ -0,0 +1,70 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef NXrender_H
+#define NXrender_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Import this from <X11/extensions/render.h>
+ * to compile under old XFree86 distributions
+ * when render extension was not present yet.
+ */
+
+#define X_RenderQueryVersion 0
+#define X_RenderQueryPictFormats 1
+#define X_RenderQueryPictIndexValues 2
+#define X_RenderQueryDithers 3
+#define X_RenderCreatePicture 4
+#define X_RenderChangePicture 5
+#define X_RenderSetPictureClipRectangles 6
+#define X_RenderFreePicture 7
+#define X_RenderComposite 8
+#define X_RenderScale 9
+#define X_RenderTrapezoids 10
+#define X_RenderTriangles 11
+#define X_RenderTriStrip 12
+#define X_RenderTriFan 13
+#define X_RenderColorTrapezoids 14
+#define X_RenderColorTriangles 15
+#define X_RenderTransform 16
+#define X_RenderCreateGlyphSet 17
+#define X_RenderReferenceGlyphSet 18
+#define X_RenderFreeGlyphSet 19
+#define X_RenderAddGlyphs 20
+#define X_RenderAddGlyphsFromPicture 21
+#define X_RenderFreeGlyphs 22
+#define X_RenderCompositeGlyphs8 23
+#define X_RenderCompositeGlyphs16 24
+#define X_RenderCompositeGlyphs32 25
+#define X_RenderFillRectangles 26
+/* 0.5 */
+#define X_RenderCreateCursor 27
+/* 0.6 */
+#define X_RenderSetPictureTransform 28
+#define X_RenderQueryFilters 29
+#define X_RenderSetPictureFilter 30
+#define X_RenderCreateAnimCursor 31
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NXrender_H */
diff --git a/nxcomp/NXvars.h b/nxcomp/NXvars.h
new file mode 100644
index 000000000..3bee6d3ed
--- /dev/null
+++ b/nxcomp/NXvars.h
@@ -0,0 +1,193 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef NXvars_H
+#define NXvars_H
+
+/*
+ * This can be included by the proxy or another
+ * layer that doesn't use Xlib.
+ */
+
+#if !defined(_XLIB_H_) && !defined(_XKBSRV_H_)
+
+#define NeedFunctionPrototypes 1
+
+#define Display void
+
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Display flush policies.
+ */
+
+#define NXPolicyImmediate 1
+#define NXPolicyDeferred 2
+
+/*
+ * Type of flush.
+ */
+
+#define NXFlushBuffer 0
+#define NXFlushLink 1
+
+/*
+ * Type of statistics.
+ */
+
+#define NXStatisticsPartial 0
+#define NXStatisticsTotal 1
+
+/*
+ * Reason why the display is blocking.
+ */
+
+#define NXBlockRead 1
+#define NXBlockWrite 2
+
+/*
+ * Set if the client is interested in ignoring
+ * the display error and continue with the exe-
+ * cution of the program. By default the usual
+ * Xlib behaviour is gotten, and the library
+ * will call an exit().
+ */
+
+extern int _NXHandleDisplayError;
+
+/*
+ * The function below is called whenever Xlib is
+ * going to perform an I/O operation. The funct-
+ * ion can be redefined to include additional
+ * checks aimed at detecting if the display needs
+ * to be closed, for example because of an event
+ * or a signal mandating the end of the session.
+ * In this way the client program can regain the
+ * control before Xlib blocks waiting for input
+ * from the network.
+ */
+
+typedef int (*NXDisplayErrorPredicate)(
+#if NeedFunctionPrototypes
+ Display* /* display */,
+ int /* reason */
+#endif
+);
+
+extern NXDisplayErrorPredicate _NXDisplayErrorFunction;
+
+/*
+ * This is called when Xlib is going to block
+ * waiting for the display to become readable or
+ * writable. The client can use the hook to run
+ * any arbitrary operation that may require some
+ * time to complete. The user should not try to
+ * read or write to the display inside the call-
+ * back routine.
+ */
+
+typedef void (*NXDisplayBlockHandler)(
+#if NeedFunctionPrototypes
+ Display* /* display */,
+ int /* reason */
+#endif
+);
+
+extern NXDisplayBlockHandler _NXDisplayBlockFunction;
+
+/*
+ * Used to notify the program when more data
+ * is written to the socket.
+ */
+
+typedef void (*NXDisplayWriteHandler)(
+#if NeedFunctionPrototypes
+ Display* /* display */,
+ int /* length */
+#endif
+);
+
+extern NXDisplayWriteHandler _NXDisplayWriteFunction;
+
+/*
+ * This callback is used to notify the agent
+ * that the proxy link has been flushed.
+ */
+
+typedef void (*NXDisplayFlushHandler)(
+#if NeedFunctionPrototypes
+ Display* /* display */,
+ int /* length */
+#endif
+);
+
+extern NXDisplayFlushHandler _NXDisplayFlushFunction;
+
+/*
+ * Used by the NX transport to get an arbitrary
+ * string to add to its protocol statistics.
+ */
+
+typedef void (*NXDisplayStatisticsHandler)(
+#if NeedFunctionPrototypes
+ Display* /* display */,
+ char* /* buffer */,
+ int /* size */
+#endif
+);
+
+extern NXDisplayStatisticsHandler _NXDisplayStatisticsFunction;
+
+/*
+ * Let users redefine the function printing an
+ * error message in the case of a out-of-order
+ * sequence number.
+ */
+
+typedef void (*NXLostSequenceHandler)(
+#if NeedFunctionPrototypes
+ Display* /* display */,
+ unsigned long /* newseq */,
+ unsigned long /* lastseq */,
+ unsigned int /* type */
+#endif
+);
+
+extern NXLostSequenceHandler _NXLostSequenceFunction;
+
+/*
+ * Let the X server run the children processes
+ * (as for example the keyboard initialization
+ * utilities) by using the native system libra-
+ * ries, instead of the libraries shipped with
+ * the NX environment. If set, the Popen() in
+ * the X server will remove the LD_LIBRARY_PATH
+ * setting from the environment before calling
+ * the execl() function in the child process.
+ */
+
+extern int _NXUnsetLibraryPath;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NXvars_H */
diff --git a/nxcomp/OpcodeCache.h b/nxcomp/OpcodeCache.h
new file mode 100644
index 000000000..529f8eee1
--- /dev/null
+++ b/nxcomp/OpcodeCache.h
@@ -0,0 +1,45 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef OpcodeCache_H
+#define OpcodeCache_H
+
+#include "CharCache.h"
+
+class OpcodeCache
+{
+ friend class EncodeBuffer;
+ friend class DecodeBuffer;
+
+ public:
+
+ OpcodeCache()
+ {
+ slot_ = 0;
+ }
+
+ ~OpcodeCache()
+ {
+ }
+
+ private:
+
+ CharCache base_[256];
+ unsigned char slot_;
+};
+
+#endif /* OpcodeCache_H */
diff --git a/nxcomp/OpcodeStore.cpp b/nxcomp/OpcodeStore.cpp
new file mode 100644
index 000000000..909744821
--- /dev/null
+++ b/nxcomp/OpcodeStore.cpp
@@ -0,0 +1,76 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "OpcodeStore.h"
+
+OpcodeStore::OpcodeStore()
+{
+ //
+ // Assign values of the specific
+ // NX opcodes.
+ //
+
+ getControlParameters = X_NXGetControlParameters;
+ getCleanupParameters = X_NXGetCleanupParameters;
+ getImageParameters = X_NXGetImageParameters;
+ getUnpackParameters = X_NXGetUnpackParameters;
+ getShmemParameters = X_NXGetShmemParameters;
+ getFontParameters = X_NXGetFontParameters;
+
+ startSplit = X_NXStartSplit;
+ endSplit = X_NXEndSplit;
+ commitSplit = X_NXCommitSplit;
+ finishSplit = X_NXFinishSplit;
+ abortSplit = X_NXAbortSplit;
+
+ splitData = X_NXSplitData;
+ splitEvent = X_NXSplitEvent;
+
+ setCacheParameters = X_NXSetCacheParameters;
+ setExposeParameters = X_NXSetExposeParameters;
+
+ setUnpackGeometry = X_NXSetUnpackGeometry;
+ setUnpackColormap = X_NXSetUnpackColormap;
+ setUnpackAlpha = X_NXSetUnpackAlpha;
+
+ putPackedImage = X_NXPutPackedImage;
+
+ freeUnpack = X_NXFreeUnpack;
+ freeSplit = X_NXFreeSplit;
+
+ //
+ // These values must be fetched
+ // from the X server.
+ //
+
+ shapeExtension = 0;
+ renderExtension = 0;
+
+ //
+ // Events sent as ClientMessage.
+ //
+
+ noSplitNotify = NXNoSplitNotify;
+ startSplitNotify = NXStartSplitNotify;
+ commitSplitNotify = NXCommitSplitNotify;
+ endSplitNotify = NXEndSplitNotify;
+ emptySplitNotify = NXEmptySplitNotify;
+}
+
+OpcodeStore::~OpcodeStore()
+{
+}
diff --git a/nxcomp/OpcodeStore.h b/nxcomp/OpcodeStore.h
new file mode 100644
index 000000000..f6626dfef
--- /dev/null
+++ b/nxcomp/OpcodeStore.h
@@ -0,0 +1,83 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef OpcodeStore_H
+#define OpcodeStore_H
+
+#include "NXproto.h"
+
+class OpcodeStore
+{
+ public:
+
+ OpcodeStore();
+
+ ~OpcodeStore();
+
+ //
+ // Map NX protocol messages. At the moment mapping is hard-
+ // coded. Opcodes should be instead agreed with the proxied
+ // X server (by excluding all opcodes used for extensions)
+ // and exported by the proxy class to channels.
+ //
+ // Some toolkits query the server only once for extensions'
+ // opcodes and share the same settings across all channels.
+ // This could be a problem as channels needed to monitor the
+ // traffic to find out the extensions' opcodes themselves,
+ // so it is important that proxy passes an instance of this
+ // class to new channels.
+ //
+
+ unsigned char getControlParameters;
+ unsigned char getCleanupParameters;
+ unsigned char getImageParameters;
+ unsigned char getUnpackParameters;
+ unsigned char getShmemParameters;
+ unsigned char getFontParameters;
+
+ unsigned char startSplit;
+ unsigned char endSplit;
+ unsigned char commitSplit;
+ unsigned char finishSplit;
+ unsigned char abortSplit;
+
+ unsigned char splitData;
+ unsigned char splitEvent;
+
+ unsigned char setCacheParameters;
+ unsigned char setExposeParameters;
+
+ unsigned char setUnpackGeometry;
+ unsigned char setUnpackColormap;
+ unsigned char setUnpackAlpha;
+
+ unsigned char putPackedImage;
+
+ unsigned char freeUnpack;
+ unsigned char freeSplit;
+
+ unsigned char shapeExtension;
+ unsigned char renderExtension;
+
+ unsigned char noSplitNotify;
+ unsigned char startSplitNotify;
+ unsigned char commitSplitNotify;
+ unsigned char endSplitNotify;
+ unsigned char emptySplitNotify;
+};
+
+#endif /* OpcodeStore_H */
diff --git a/nxcomp/Pack.c b/nxcomp/Pack.c
new file mode 100644
index 000000000..59a54cfbf
--- /dev/null
+++ b/nxcomp/Pack.c
@@ -0,0 +1,172 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+
+#include "NXpack.h"
+
+const ColorMask Mask8TrueColor = { 128, 63, 240, 7 };
+const ColorMask Mask64TrueColor = { 192, 7, 240, 4 };
+const ColorMask Mask256TrueColor = { 255, 0, 255, 0 };
+const ColorMask Mask512TrueColor = { 224, 5, 240, 4 };
+const ColorMask Mask4KTrueColor = { 240, 4, 240, 2 };
+const ColorMask Mask32KTrueColor = { 248, 3, 248, 2 };
+const ColorMask Mask64KTrueColor = { 255, 0, 255, 0 };
+const ColorMask Mask256KTrueColor = { 252, 1, 252, 1 };
+const ColorMask Mask2MTrueColor = { 255, 0, 254, 1 };
+const ColorMask Mask16MTrueColor = { 255, 0, 255, 0 };
+
+const ColorMask *MethodColorMask(unsigned int method)
+{
+ switch (method)
+ {
+ case MASK_8_COLORS:
+ {
+ return &Mask8TrueColor;
+ }
+ case MASK_64_COLORS:
+ {
+ return &Mask64TrueColor;
+ }
+ case MASK_256_COLORS:
+ {
+ return &Mask256TrueColor;
+ }
+ case MASK_512_COLORS:
+ {
+ return &Mask512TrueColor;
+ }
+ case MASK_4K_COLORS:
+ {
+ return &Mask4KTrueColor;
+ }
+ case MASK_32K_COLORS:
+ {
+ return &Mask32KTrueColor;
+ }
+ case MASK_64K_COLORS:
+ {
+ return &Mask64KTrueColor;
+ }
+ case MASK_256K_COLORS:
+ {
+ return &Mask256KTrueColor;
+ }
+ case MASK_2M_COLORS:
+ {
+ return &Mask2MTrueColor;
+ }
+ case MASK_16M_COLORS:
+ {
+ return &Mask16MTrueColor;
+ }
+ default:
+ {
+ return NULL;
+ }
+ }
+}
+
+int MethodBitsPerPixel(unsigned int method)
+{
+ switch (method)
+ {
+ case PACK_MASKED_8_COLORS:
+ case PACK_JPEG_8_COLORS:
+ case PACK_PNG_8_COLORS:
+ {
+ return 8;
+ }
+ case PACK_MASKED_64_COLORS:
+ case PACK_JPEG_64_COLORS:
+ case PACK_PNG_64_COLORS:
+ {
+ return 8;
+ }
+ case PACK_MASKED_256_COLORS:
+ case PACK_JPEG_256_COLORS:
+ case PACK_PNG_256_COLORS:
+ {
+ return 8;
+ }
+ case PACK_MASKED_512_COLORS:
+ case PACK_JPEG_512_COLORS:
+ case PACK_PNG_512_COLORS:
+ {
+ return 16;
+ }
+ case PACK_MASKED_4K_COLORS:
+ case PACK_JPEG_4K_COLORS:
+ case PACK_PNG_4K_COLORS:
+ {
+ return 16;
+ }
+ case PACK_MASKED_32K_COLORS:
+ case PACK_JPEG_32K_COLORS:
+ case PACK_PNG_32K_COLORS:
+ {
+ return 16;
+ }
+ case PACK_MASKED_64K_COLORS:
+ case PACK_JPEG_64K_COLORS:
+ case PACK_PNG_64K_COLORS:
+ {
+ return 16;
+ }
+ case PACK_MASKED_256K_COLORS:
+ case PACK_JPEG_256K_COLORS:
+ case PACK_PNG_256K_COLORS:
+ {
+ return 24;
+ }
+ case PACK_MASKED_2M_COLORS:
+ case PACK_JPEG_2M_COLORS:
+ case PACK_PNG_2M_COLORS:
+ {
+ return 24;
+ }
+ case PACK_MASKED_16M_COLORS:
+ case PACK_JPEG_16M_COLORS:
+ case PACK_PNG_16M_COLORS:
+ {
+ return 24;
+ }
+ case PACK_BITMAP_16M_COLORS:
+ case PACK_RGB_16M_COLORS:
+ case PACK_RLE_16M_COLORS:
+ {
+ return 24;
+ }
+ default:
+ {
+ return 0;
+ }
+ }
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/nxcomp/Pgn.cpp b/nxcomp/Pgn.cpp
new file mode 100644
index 000000000..a68373441
--- /dev/null
+++ b/nxcomp/Pgn.cpp
@@ -0,0 +1,794 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+//
+// This file obviously supports PNG
+// decompression. It was renamed to
+// avoid name clashes on Windows.
+//
+
+#include <X11/Xmd.h>
+
+#include <unistd.h>
+#include <stdio.h>
+#include <png.h>
+
+#include "Unpack.h"
+#include "Pgn.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#define RGB24_TO_PIXEL(bpp,r,g,b) \
+ ((((CARD##bpp)(r) & 0xFF) * srcRedMax2 + 127) / 255 \
+ << srcRedShift2 | \
+ (((CARD##bpp)(g) & 0xFF) * srcGreenMax2 + 127) / 255 \
+ << srcGreenShift2 | \
+ (((CARD##bpp)(b) & 0xFF) * srcBlueMax2 + 127) / 255 \
+ << srcBlueShift2)
+
+#define RGB24_TO_PIXEL32(r,g,b) \
+ (((CARD32)(r) & 0xFF) << srcRedShift2 | \
+ ((CARD32)(g) & 0xFF) << srcGreenShift2 | \
+ ((CARD32)(b) & 0xFF) << srcBlueShift2)
+
+//
+// Functions from Unpack.cpp
+//
+
+extern int Unpack32To32(const T_colormask *colormask, const unsigned int *data,
+ unsigned int *out, unsigned int *end);
+
+extern int Unpack24To24(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+extern int Unpack16To16(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+//
+// Local functions used for the png decompression.
+//
+
+static int DecompressPng16(unsigned char *compressedData, int compressedLen,
+ unsigned int w, unsigned int h, unsigned char *dstBuf,
+ int byteOrder);
+
+static int DecompressPng24(unsigned char *compressedData, int compressedLen,
+ unsigned int w, unsigned int h, unsigned char *dstBuf,
+ int byteOrder);
+
+static int DecompressPng32(unsigned char *compressedData, int compressedLen,
+ unsigned int w, unsigned int h, unsigned char *dstBuf,
+ int byteOrder);
+
+static void PngReadData(png_structp png_ptr, png_bytep data, png_size_t length);
+
+//
+// Colormap stuff.
+//
+
+CARD16 srcRedMax2, srcGreenMax2, srcBlueMax2;
+CARD8 srcRedShift2, srcGreenShift2, srcBlueShift2;
+
+//
+// Attributes used for the png decompression.
+//
+
+static char *tmpBuf;
+static int tmpBufSize = 0;
+static int streamPos;
+
+int UnpackPng(T_geometry *geometry, unsigned char method, unsigned char *srcData,
+ int srcSize, int dstBpp, int dstWidth, int dstHeight,
+ unsigned char *dstData, int dstSize)
+{
+ int byteOrder = geometry -> image_byte_order;
+
+ //
+ // Check if data is coming from a failed unsplit.
+ //
+
+ if (srcSize < 2 || (srcData[0] == SPLIT_PATTERN &&
+ srcData[1] == SPLIT_PATTERN))
+ {
+ #ifdef WARNING
+ *logofs << "UnpackPng: WARNING! Skipping unpack of dummy data.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ #ifdef DEBUG
+ *logofs << "UnpackPng: Decompressing image with "
+ << "srcSize " << srcSize << " and bpp "
+ << dstBpp << ".\n" << logofs_flush;
+ #endif
+
+ srcRedShift2 = ffs(geometry -> red_mask) - 1;
+ srcGreenShift2 = ffs(geometry -> green_mask) - 1;
+ srcBlueShift2 = ffs(geometry -> blue_mask) - 1;
+ srcRedMax2 = geometry -> red_mask >> srcRedShift2;
+ srcGreenMax2 = geometry -> green_mask >> srcGreenShift2;
+ srcBlueMax2 = geometry -> blue_mask >> srcBlueShift2;
+
+ //
+ // Make enough space in the temporary
+ // buffer to have one complete row of
+ // the image with 3 bytes per pixel.
+ //
+
+ tmpBufSize = dstWidth * 3;
+ tmpBuf = new char [tmpBufSize];
+
+ if (tmpBuf == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "UnpackPng: PANIC! Cannot allocate "
+ << dstWidth * 3 << " bytes for PNG "
+ << "decompressed data.\n" << logofs_flush;
+ #endif
+
+ delete [] tmpBuf;
+
+ return -1;
+ }
+
+ int result = 1;
+
+ switch (dstBpp)
+ {
+ case 8:
+ {
+ //
+ // Simply move the data from srcData to dstData
+ // taking into consideration the correct padding.
+ //
+
+ int row;
+
+ unsigned char * dstBuff = dstData;
+ unsigned char * srcBuff = srcData;
+
+ for (row = 0; row < dstHeight; row++)
+ {
+ memcpy(dstBuff, srcBuff, dstWidth );
+
+ dstBuff += RoundUp4(dstWidth);
+ srcBuff += dstWidth;
+ }
+ }
+ case 16:
+ {
+ result = DecompressPng16(srcData, srcSize, dstWidth,
+ dstHeight, dstData, byteOrder);
+ break;
+ }
+ case 24:
+ {
+ result = DecompressPng24(srcData, srcSize, dstWidth,
+ dstHeight, dstData, byteOrder);
+ break;
+ }
+ case 32:
+ {
+ result = DecompressPng32(srcData, srcSize, dstWidth,
+ dstHeight, dstData, byteOrder);
+ break;
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "UnpackPng: PANIC! Error in PNG compression. "
+ << " Unsupported Bpp value " << dstBpp
+ << " for the PNG compression"
+ << ".\n" << logofs_flush;
+ #endif
+
+ delete [] tmpBuf;
+
+ result = -1;
+ }
+ }
+
+ if (result == -1)
+ {
+ delete [] tmpBuf;
+
+ return result;
+ }
+
+ //
+ // Apply the correction for the brightness
+ //
+
+ int maskMethod;
+
+ switch (method)
+ {
+ case PACK_PNG_8_COLORS:
+ {
+ maskMethod = MASK_8_COLORS;
+
+ break;
+ }
+ case PACK_PNG_64_COLORS:
+ {
+ maskMethod = MASK_64_COLORS;
+
+ break;
+ }
+ case PACK_PNG_256_COLORS:
+ {
+ maskMethod = MASK_256_COLORS;
+
+ break;
+ }
+ case PACK_PNG_512_COLORS:
+ {
+ maskMethod = MASK_512_COLORS;
+
+ break;
+ }
+ case PACK_PNG_4K_COLORS:
+ {
+ maskMethod = MASK_4K_COLORS;
+
+ break;
+ }
+ case PACK_PNG_32K_COLORS:
+ {
+ maskMethod = MASK_32K_COLORS;
+
+ break;
+ }
+ case PACK_PNG_64K_COLORS:
+ {
+ maskMethod = MASK_64K_COLORS;
+
+ break;
+ }
+ case PACK_PNG_256K_COLORS:
+ {
+ maskMethod = MASK_256K_COLORS;
+
+ break;
+ }
+ case PACK_PNG_2M_COLORS:
+ {
+ maskMethod = MASK_2M_COLORS;
+
+ break;
+ }
+ case PACK_PNG_16M_COLORS:
+ {
+ maskMethod = MASK_16M_COLORS;
+
+ break;
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng16: PANIC! "
+ << " No matching decompression method.\n"
+ << logofs_flush;
+ #endif
+
+ delete [] tmpBuf;
+
+ return -1;
+ }
+ }
+
+ const T_colormask *colorMask = MethodColorMask(maskMethod);
+
+ unsigned char *dstBuff = dstData;
+
+ switch (dstBpp)
+ {
+ case 16:
+ {
+ Unpack16To16(colorMask, dstBuff, dstBuff, dstBuff + dstSize);
+
+ break;
+ }
+ case 24:
+ {
+ break;
+ }
+ case 32:
+ {
+ Unpack32To32(colorMask, (unsigned int *)dstBuff, (unsigned int *)dstBuff,
+ (unsigned int *)(dstBuff + dstSize));
+ break;
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng16: PANIC! "
+ << " No matching destination bits per plane.\n"
+ << logofs_flush;
+ #endif
+
+ delete [] tmpBuf;
+
+ return -1;
+ }
+ }
+
+ delete [] tmpBuf;
+
+ return 1;
+}
+
+
+//
+// Functions that actually do
+// the PNG decompression.
+//
+
+int DecompressPng16(unsigned char *compressedData, int compressedLen,
+ unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder)
+{
+ unsigned char *data;
+ unsigned int dx, dy;
+
+ png_structp pngPtr;
+ png_infop infoPtr;
+ png_bytep rowPointers;
+
+
+ streamPos = 0;
+
+ pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+
+ if (!pngPtr)
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng16: PANIC! "
+ << " Failed png_create_read_struct operation"
+ << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ infoPtr = png_create_info_struct(pngPtr);
+
+ if (!infoPtr)
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng16: PANIC! "
+ << "Failed png_create_info_struct operation"
+ << ".\n" << logofs_flush;
+ #endif
+
+ png_destroy_read_struct(&pngPtr, NULL, NULL);
+
+ return -1;
+ }
+
+ if (setjmp(png_jmpbuf(pngPtr)))
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng16: PANIC! "
+ << "Error during IO initialization"
+ << ".\n" << logofs_flush;
+ #endif
+
+ png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
+
+ return -1;
+ }
+
+ png_set_read_fn(pngPtr, (void *)compressedData, PngReadData);
+
+ if (setjmp(png_jmpbuf(pngPtr)))
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng16: PANIC! "
+ << "Error during read of PNG header"
+ << ".\n" << logofs_flush;
+ #endif
+
+ png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
+
+ return -1;
+ }
+
+ png_read_info(pngPtr, infoPtr);
+
+ if (infoPtr -> color_type == PNG_COLOR_TYPE_PALETTE)
+ {
+ png_set_expand(pngPtr);
+ }
+
+ //
+ // data points to dstBuf which is
+ // already padded correctly for the final
+ // image to put
+ //
+
+ data = dstBuf;
+ rowPointers = (png_byte *) tmpBuf;
+
+ //
+ // We use setjmp() to save our context.
+ // The PNG library will call longjmp()
+ // in case of error.
+ //
+
+ if (setjmp(png_jmpbuf(pngPtr)))
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng16: PANIC! "
+ << "Error during read of PNG rows"
+ << ".\n" << logofs_flush;
+ #endif
+
+ png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
+
+ return -1;
+ }
+
+ unsigned long pixel;
+
+ for (dy = 0; dy < h; dy++)
+ {
+ png_read_row(pngPtr, rowPointers, NULL);
+
+ for (dx = 0; dx < w; dx++)
+ {
+ pixel = RGB24_TO_PIXEL(16, tmpBuf[dx*3], tmpBuf[dx*3+1], tmpBuf[dx*3+2]);
+
+ //
+ // Follow the server byte order when arranging data.
+ //
+
+ if (byteOrder == LSBFirst)
+ {
+ data[0] = (unsigned char) (pixel & 0xff);
+ data[1] = (unsigned char) ((pixel >> 8) & 0xff);
+ }
+ else
+ {
+ data[1] = (unsigned char) (pixel & 0xff);
+ data[0] = (unsigned char) ((pixel >> 8) & 0xff);
+ }
+
+ data += 2;
+ }
+
+ //
+ // Move pixelPtr at the beginning of the
+ // next line.
+ //
+
+ data = data + (RoundUp4(w * 2) - w * 2);
+ }
+
+ png_destroy_read_struct(&pngPtr, &infoPtr,NULL);
+
+ #ifdef DEBUG
+ *logofs << "DecompressPng16: Decompression finished."
+ << dy << " lines handled.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int DecompressPng24(unsigned char *compressedData, int compressedLen,
+ unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder)
+{
+ static CARD8 *pixelPtr = NULL;
+ unsigned int dx, dy;
+
+ png_structp pngPtr;
+ png_infop infoPtr;
+ png_bytep rowPointers;
+
+
+ streamPos = 0;
+
+ pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+
+ if (!pngPtr)
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng24: PANIC! "
+ << "Failed png_create_read_struct operation"
+ << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ infoPtr = png_create_info_struct(pngPtr);
+
+ if (!infoPtr)
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng24: PANIC! "
+ << "Failed png_create_info_struct operation"
+ << ".\n" << logofs_flush;
+ #endif
+
+ png_destroy_read_struct(&pngPtr, NULL, NULL);
+
+ return -1;
+ }
+
+ if (setjmp(png_jmpbuf(pngPtr)))
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng24: PANIC! "
+ << "Error during IO initialization"
+ << ".\n" << logofs_flush;
+ #endif
+
+ png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
+
+ return -1;
+ }
+
+ png_set_read_fn(pngPtr, (void *)compressedData, PngReadData);
+
+ if (setjmp(png_jmpbuf(pngPtr)))
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng24: PANIC! "
+ << "Error during read of PNG header"
+ << ".\n" << logofs_flush;
+ #endif
+
+ png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
+
+ return -1;
+ }
+
+ png_read_info( pngPtr, infoPtr ) ;
+
+ if (infoPtr -> color_type == PNG_COLOR_TYPE_PALETTE)
+ {
+ png_set_expand(pngPtr);
+ }
+
+ //
+ // PixelPtr points to dstBuf which is
+ // already padded correctly for the final
+ // image to put
+ //
+
+ pixelPtr = (CARD8 *) dstBuf;
+
+ rowPointers = (png_byte *)tmpBuf;
+
+ if (setjmp(png_jmpbuf(pngPtr)))
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng24: PANIC! "
+ << "Error during read of PNG rows"
+ << ".\n" << logofs_flush;
+ #endif
+
+ png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
+
+ return -1;
+ }
+
+ for (dy = 0; dy < h; dy++)
+ {
+ png_read_row(pngPtr, rowPointers, NULL);
+
+ for (dx = 0; dx < w; dx++)
+ {
+ //
+ // Follow the server byte order when arranging data.
+ //
+
+ if (byteOrder == LSBFirst)
+ {
+ pixelPtr[0] = tmpBuf[dx * 3];
+ pixelPtr[1] = tmpBuf[dx * 3 + 1];
+ pixelPtr[2] = tmpBuf[dx * 3 + 2];
+ }
+ else
+ {
+ pixelPtr[2] = tmpBuf[dx * 3];
+ pixelPtr[1] = tmpBuf[dx * 3 + 1];
+ pixelPtr[0] = tmpBuf[dx * 3 + 2];
+ }
+
+ pixelPtr += 3;
+ }
+
+ //
+ // Go to the next line.
+ //
+
+ pixelPtr = (CARD8 *) (((char *) pixelPtr) + (RoundUp4(w * 3) - w * 3));
+ }
+
+ png_destroy_read_struct(&pngPtr, &infoPtr,NULL);
+
+ #ifdef DEBUG
+ *logofs << "DecompressPng24: Decompression finished."
+ << dy << " lines handled.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int DecompressPng32(unsigned char *compressedData, int compressedLen,
+ unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder)
+{
+ unsigned char *data;
+
+ unsigned int dx, dy;
+
+ png_structp pngPtr;
+ png_infop infoPtr;
+ png_bytep rowPointers;
+
+ streamPos = 0;
+
+ pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+
+ if (!pngPtr)
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng32: PANIC! "
+ << "Failed png_create_read_struct operation"
+ << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ infoPtr = png_create_info_struct(pngPtr);
+
+ if (!infoPtr)
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng32: PANIC! "
+ << "Failed png_create_info_struct operation."
+ << ".\n" << logofs_flush;
+ #endif
+
+ png_destroy_read_struct(&pngPtr, NULL, NULL);
+
+ return -1;
+ }
+
+ if (setjmp(png_jmpbuf(pngPtr)))
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng32: PANIC! "
+ << "Error during IO initialization"
+ << ".\n" << logofs_flush;
+ #endif
+
+ png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
+
+ return -1;
+ }
+
+ png_set_read_fn(pngPtr, (void *)compressedData, PngReadData);
+
+ if (setjmp(png_jmpbuf(pngPtr)))
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng32: PANIC! "
+ << "Error during read of PNG header"
+ << ".\n" << logofs_flush;
+ #endif
+
+ png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
+
+ return -1;
+ }
+
+ png_read_info(pngPtr, infoPtr) ;
+
+
+ if (infoPtr -> color_type == PNG_COLOR_TYPE_PALETTE)
+ {
+ png_set_expand(pngPtr);
+ }
+
+ //
+ // data points to dstBuf which is
+ // already padded correctly for the final
+ // image to put
+ //
+
+ data = dstBuf;
+
+ rowPointers = (png_byte *) tmpBuf;
+
+ if (setjmp(png_jmpbuf(pngPtr)))
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng32: PANIC! "
+ << "Error during read of PNG rows"
+ << ".\n" << logofs_flush;
+ #endif
+
+ png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
+
+ return -1;
+ }
+
+ unsigned long pixel;
+
+ int i;
+
+ for (dy = 0; dy < h; dy++)
+ {
+ png_read_row(pngPtr, rowPointers, NULL);
+
+ for (dx = 0; dx < w; dx++)
+ {
+ pixel = RGB24_TO_PIXEL(32, tmpBuf[dx * 3], tmpBuf[dx * 3 + 1],
+ tmpBuf[dx * 3 + 2]);
+
+ //
+ // Follow the server byte order when arranging data.
+ //
+
+ if (byteOrder == LSBFirst)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ data[i] = (unsigned char)(pixel & 0xff);
+ pixel >>= 8;
+ }
+ }
+ else
+ {
+ for (i = 3; i >= 0; i--)
+ {
+ data[i] = (unsigned char) (pixel & 0xff);
+ pixel >>= 8;
+ }
+ }
+
+ data += 4;
+ }
+ }
+
+ png_destroy_read_struct(&pngPtr, &infoPtr,NULL);
+
+ #ifdef DEBUG
+ *logofs << "DecompressPng32: Decompression finished."
+ << dy << " lines handled.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+static void PngReadData(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+ memcpy((char *) data, (char *) png_get_io_ptr(png_ptr) + streamPos, length);
+
+ streamPos += length;
+}
diff --git a/nxcomp/Pgn.h b/nxcomp/Pgn.h
new file mode 100644
index 000000000..ddf9b75d1
--- /dev/null
+++ b/nxcomp/Pgn.h
@@ -0,0 +1,34 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef Pgn_H
+#define Pgn_H
+
+//
+// This file obviously supports PNG
+// decompression. It was renamed to
+// avoid name clashes on Windows.
+//
+
+#include "Misc.h"
+#include "Unpack.h"
+
+int UnpackPng(T_geometry *geometry, unsigned char method, unsigned char *srcData,
+ int srcSize, int dstBpp, int dstWidth, int dstHeight,
+ unsigned char *dstData, int dstSize);
+
+#endif /* Pgn_H */
diff --git a/nxcomp/Pipe.cpp b/nxcomp/Pipe.cpp
new file mode 100644
index 000000000..7238d0c73
--- /dev/null
+++ b/nxcomp/Pipe.cpp
@@ -0,0 +1,412 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "Pipe.h"
+#include "Misc.h"
+#include "Fork.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+extern void RegisterChild(int child);
+
+static int Psplit(const char *command, char *parameters[], int limit);
+
+//
+// These are slightly modified versions of popen(3) and pclose(3)
+// that don't rely on a shell to be available on the system, so
+// that they can also work on Windows. As an additional benefit,
+// these functions give up all privileges before running the com-
+// mand. Code is taken from the X distribution and, in turn, is
+// based on libc from FreeBSD 2.2.
+//
+
+static struct pid
+{
+ struct pid *next;
+ FILE *fp;
+ int self;
+
+} *pidlist;
+
+//
+// A very unsofisticated attempt to parse the command line and
+// split each parameter in distinct strings. This is not going
+// to work when dealing with parameters containing spaces, even
+// if they are enclosed in quotes.
+//
+
+int Psplit(const char *command, char *parameters[], int limit)
+{
+ char *line;
+ char *value;
+
+ int number;
+
+ //
+ // Preapare the list of parameters.
+ //
+
+ for (number = 0; number < limit; number++)
+ {
+ parameters[number] = NULL;
+ }
+
+ //
+ // Copy the command to get rid of the
+ // const qualifier.
+ //
+
+ line = new char[strlen(command) + 1];
+
+ if (line == NULL)
+ {
+ goto PsplitError;
+ }
+
+ strcpy(line, command);
+
+ number = 0;
+
+ value = strtok(line, " ");
+
+ while (value != NULL && number < limit)
+ {
+ #ifdef DEBUG
+ *logofs << "Psplit: Got parameter '" << value
+ << "'.\n" << logofs_flush;
+ #endif
+
+ parameters[number] = new char[strlen(value) + 1];
+
+ if (parameters[number] == NULL)
+ {
+ goto PsplitError;
+ }
+
+ strcpy(parameters[number], value);
+
+ number++;
+
+ //
+ // If this is the first parameter, then
+ // copy it in the second position and
+ // use it as the name of the command.
+ //
+
+ if (number == 1)
+ {
+ parameters[number] = new char[strlen(value) + 1];
+
+ if (parameters[number] == NULL)
+ {
+ goto PsplitError;
+ }
+
+ strcpy(parameters[number], value);
+
+ number++;
+ }
+
+ value = strtok(NULL, " ");
+ }
+
+ //
+ // Needs at least to have the command itself and
+ // the first argument, being again the name of
+ // the command.
+ //
+
+ if (number < 2)
+ {
+ goto PsplitError;
+ }
+
+ return number;
+
+PsplitError:
+
+ #ifdef PANIC
+ *logofs << "Psplit: PANIC! Can't split command line '"
+ << command << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't split command line '"
+ << command << "'.\n";
+
+ delete [] line;
+
+ return -1;
+}
+
+FILE *Popen(char * const parameters[], const char *type)
+{
+ FILE *iop;
+
+ struct pid *cur;
+ int pdes[2], pid;
+
+ if (parameters == NULL || type == NULL)
+ {
+ return NULL;
+ }
+
+ if ((*type != 'r' && *type != 'w') || type[1])
+ {
+ return NULL;
+ }
+
+ if ((cur = (struct pid *) malloc(sizeof(struct pid))) == NULL)
+ {
+ return NULL;
+ }
+
+ if (pipe(pdes) < 0)
+ {
+ free(cur);
+
+ return NULL;
+ }
+
+ //
+ // Block all signals until command is exited.
+ // We need to gather information about the
+ // child in Pclose().
+ //
+
+ DisableSignals();
+
+ switch (pid = Fork())
+ {
+ case -1:
+ {
+ //
+ // Error.
+ //
+
+ #ifdef PANIC
+ *logofs << "Popen: PANIC! Function fork failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Function fork failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+
+ close(pdes[0]);
+ close(pdes[1]);
+
+ free(cur);
+
+ return NULL;
+ }
+ case 0:
+ {
+ //
+ // Child.
+ //
+
+ setgid(getgid());
+ setuid(getuid());
+
+ if (*type == 'r')
+ {
+ if (pdes[1] != 1)
+ {
+ //
+ // Set up stdout.
+ //
+
+ dup2(pdes[1], 1);
+ close(pdes[1]);
+ }
+
+ close(pdes[0]);
+ }
+ else
+ {
+ if (pdes[0] != 0)
+ {
+ //
+ // Set up stdin.
+ //
+
+ dup2(pdes[0], 0);
+ close(pdes[0]);
+ }
+
+ close(pdes[1]);
+ }
+
+ execvp(parameters[0], parameters + 1);
+
+ exit(127);
+ }
+ }
+
+ //
+ // Parent. Save data about the child.
+ //
+
+ RegisterChild(pid);
+
+ if (*type == 'r')
+ {
+ iop = fdopen(pdes[0], type);
+
+ close(pdes[1]);
+ }
+ else
+ {
+ iop = fdopen(pdes[1], type);
+
+ close(pdes[0]);
+ }
+
+ cur -> fp = iop;
+ cur -> self = pid;
+ cur -> next = pidlist;
+
+ pidlist = cur;
+
+ #ifdef TEST
+ *logofs << "Popen: Executing ";
+
+ for (int i = 0; i < 256 && parameters[i] != NULL; i++)
+ {
+ *logofs << "[" << parameters[i] << "]";
+ }
+
+ *logofs << " with descriptor " << fileno(iop)
+ << ".\n" << logofs_flush;
+ #endif
+
+ return iop;
+}
+
+FILE *Popen(const char *command, const char *type)
+{
+ char *parameters[256];
+
+ if (Psplit(command, parameters, 256) > 0)
+ {
+ FILE *file = Popen(parameters, type);
+
+ for (int i = 0; i < 256; i++)
+ {
+ delete [] parameters[i];
+ }
+
+ return file;
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Popen: PANIC! Failed to parse command '"
+ << command << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to parse command '"
+ << command << "'.\n";
+
+ return NULL;
+ }
+}
+
+int Pclose(FILE *iop)
+{
+ struct pid *cur, *last;
+
+ int pstat;
+ int pid;
+
+ #ifdef TEST
+ *logofs << "Pclose: Closing command with output "
+ << "on descriptor " << fileno(iop) << ".\n"
+ << logofs_flush;
+ #endif
+
+ fclose((FILE *) iop);
+
+ for (last = NULL, cur = pidlist; cur; last = cur, cur = cur -> next)
+ {
+ if (cur -> fp == iop)
+ {
+ break;
+ }
+ }
+
+ if (cur == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Pclose: PANIC! Failed to find the process "
+ << "for descriptor " << fileno(iop) << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to find the process "
+ << "for descriptor " << fileno(iop) << ".\n";
+
+ return -1;
+ }
+
+ do
+ {
+ #ifdef TEST
+ *logofs << "Pclose: Going to wait for process "
+ << "with pid '" << cur -> self << "'.\n"
+ << logofs_flush;
+ #endif
+
+ pid = waitpid(cur -> self, &pstat, 0);
+ }
+ while (pid == -1 && errno == EINTR);
+
+ if (last == NULL)
+ {
+ pidlist = cur -> next;
+ }
+ else
+ {
+ last -> next = cur -> next;
+ }
+
+ free(cur);
+
+ //
+ // Child has finished and we called the
+ // waitpid(). We can enable signals again.
+ //
+
+ EnableSignals();
+
+ return (pid == -1 ? -1 : pstat);
+}
diff --git a/nxcomp/Pipe.h b/nxcomp/Pipe.h
new file mode 100644
index 000000000..b4563a967
--- /dev/null
+++ b/nxcomp/Pipe.h
@@ -0,0 +1,27 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+//
+// These are slightly modified versions of popen(3) and pclose(3)
+// that don't rely on a shell to be available on the system, so
+// that they can also work on Windows.
+//
+
+extern FILE *Popen(char * const parameters[], const char *type);
+extern FILE *Popen(const char *command, const char *type);
+
+extern int Pclose(FILE *file);
diff --git a/nxcomp/PolyArc.cpp b/nxcomp/PolyArc.cpp
new file mode 100644
index 000000000..e572fa454
--- /dev/null
+++ b/nxcomp/PolyArc.cpp
@@ -0,0 +1,150 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "PolyArc.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int PolyArcStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolyArcMessage *polyArc = (PolyArcMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ polyArc -> drawable = GetULONG(buffer + 4, bigEndian);
+ polyArc -> gcontext = GetULONG(buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int PolyArcStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolyArcMessage *polyArc = (PolyArcMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ PutULONG(polyArc -> drawable, buffer + 4, bigEndian);
+ PutULONG(polyArc -> gcontext, buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void PolyArcStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ PolyArcMessage *polyArc = (PolyArcMessage *) message;
+
+ *logofs << name() << ": Identity drawable " << polyArc -> drawable
+ << ", gcontext " << polyArc -> gcontext
+ << ", size " << polyArc -> size_ << ".\n" << logofs_flush;
+ #endif
+}
+
+void PolyArcStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+}
+
+void PolyArcStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ PolyArcMessage *polyArc = (PolyArcMessage *) message;
+ PolyArcMessage *cachedPolyArc = (PolyArcMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyArc -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polyArc -> drawable, clientCache -> drawableCache);
+
+ cachedPolyArc -> drawable = polyArc -> drawable;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyArc -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polyArc -> gcontext, clientCache -> gcCache);
+
+ cachedPolyArc -> gcontext = polyArc -> gcontext;
+}
+
+void PolyArcStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ PolyArcMessage *polyArc = (PolyArcMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ polyArc -> drawable = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyArc -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ polyArc -> gcontext = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyArc -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+}
+
+
diff --git a/nxcomp/PolyArc.h b/nxcomp/PolyArc.h
new file mode 100644
index 000000000..50d2fd9a9
--- /dev/null
+++ b/nxcomp/PolyArc.h
@@ -0,0 +1,177 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef PolyArc_H
+#define PolyArc_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define POLYARC_ENABLE_CACHE 1
+#define POLYARC_ENABLE_DATA 0
+#define POLYARC_ENABLE_SPLIT 0
+#define POLYARC_ENABLE_COMPRESS 0
+
+#define POLYARC_DATA_LIMIT 1980
+#define POLYARC_DATA_OFFSET 12
+
+#define POLYARC_CACHE_SLOTS 2000
+#define POLYARC_CACHE_THRESHOLD 2
+#define POLYARC_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class PolyArcMessage : public Message
+{
+ friend class PolyArcStore;
+
+ public:
+
+ PolyArcMessage()
+ {
+ }
+
+ ~PolyArcMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned int drawable;
+ unsigned int gcontext;
+};
+
+class PolyArcStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ PolyArcStore() : MessageStore()
+ {
+ enableCache = POLYARC_ENABLE_CACHE;
+ enableData = POLYARC_ENABLE_DATA;
+ enableSplit = POLYARC_ENABLE_SPLIT;
+ enableCompress = POLYARC_ENABLE_COMPRESS;
+
+ dataLimit = POLYARC_DATA_LIMIT;
+ dataOffset = POLYARC_DATA_OFFSET;
+
+ cacheSlots = POLYARC_CACHE_SLOTS;
+ cacheThreshold = POLYARC_CACHE_THRESHOLD;
+ cacheLowerThreshold = POLYARC_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~PolyArcStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "PolyArc";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_PolyArc;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(PolyArcMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new PolyArcMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new PolyArcMessage((const PolyArcMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (PolyArcMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* PolyArc_H */
diff --git a/nxcomp/PolyFillArc.cpp b/nxcomp/PolyFillArc.cpp
new file mode 100644
index 000000000..2733eb62a
--- /dev/null
+++ b/nxcomp/PolyFillArc.cpp
@@ -0,0 +1,150 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "PolyFillArc.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int PolyFillArcStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolyFillArcMessage *polyFillArc = (PolyFillArcMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ polyFillArc -> drawable = GetULONG(buffer + 4, bigEndian);
+ polyFillArc -> gcontext = GetULONG(buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int PolyFillArcStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolyFillArcMessage *polyFillArc = (PolyFillArcMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ PutULONG(polyFillArc -> drawable, buffer + 4, bigEndian);
+ PutULONG(polyFillArc -> gcontext, buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void PolyFillArcStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ PolyFillArcMessage *polyFillArc = (PolyFillArcMessage *) message;
+
+ *logofs << name() << ": Identity drawable " << polyFillArc -> drawable
+ << ", gcontext " << polyFillArc -> gcontext
+ << ", size " << polyFillArc -> size_ << ".\n" << logofs_flush;
+ #endif
+}
+
+void PolyFillArcStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+}
+
+void PolyFillArcStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ PolyFillArcMessage *polyFillArc = (PolyFillArcMessage *) message;
+ PolyFillArcMessage *cachedPolyFillArc = (PolyFillArcMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyFillArc -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polyFillArc -> drawable, clientCache -> drawableCache);
+
+ cachedPolyFillArc -> drawable = polyFillArc -> drawable;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyFillArc -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polyFillArc -> gcontext, clientCache -> gcCache);
+
+ cachedPolyFillArc -> gcontext = polyFillArc -> gcontext;
+}
+
+void PolyFillArcStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ PolyFillArcMessage *polyFillArc = (PolyFillArcMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ polyFillArc -> drawable = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyFillArc -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ polyFillArc -> gcontext = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyFillArc -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+}
+
+
diff --git a/nxcomp/PolyFillArc.h b/nxcomp/PolyFillArc.h
new file mode 100644
index 000000000..721f5ac97
--- /dev/null
+++ b/nxcomp/PolyFillArc.h
@@ -0,0 +1,177 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef PolyFillArc_H
+#define PolyFillArc_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define POLYFILLARC_ENABLE_CACHE 1
+#define POLYFILLARC_ENABLE_DATA 0
+#define POLYFILLARC_ENABLE_SPLIT 0
+#define POLYFILLARC_ENABLE_COMPRESS 0
+
+#define POLYFILLARC_DATA_LIMIT 6144
+#define POLYFILLARC_DATA_OFFSET 12
+
+#define POLYFILLARC_CACHE_SLOTS 2000
+#define POLYFILLARC_CACHE_THRESHOLD 2
+#define POLYFILLARC_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class PolyFillArcMessage : public Message
+{
+ friend class PolyFillArcStore;
+
+ public:
+
+ PolyFillArcMessage()
+ {
+ }
+
+ ~PolyFillArcMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned int drawable;
+ unsigned int gcontext;
+};
+
+class PolyFillArcStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ PolyFillArcStore() : MessageStore()
+ {
+ enableCache = POLYFILLARC_ENABLE_CACHE;
+ enableData = POLYFILLARC_ENABLE_DATA;
+ enableSplit = POLYFILLARC_ENABLE_SPLIT;
+ enableCompress = POLYFILLARC_ENABLE_COMPRESS;
+
+ dataLimit = POLYFILLARC_DATA_LIMIT;
+ dataOffset = POLYFILLARC_DATA_OFFSET;
+
+ cacheSlots = POLYFILLARC_CACHE_SLOTS;
+ cacheThreshold = POLYFILLARC_CACHE_THRESHOLD;
+ cacheLowerThreshold = POLYFILLARC_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~PolyFillArcStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "PolyFillArc";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_PolyFillArc;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(PolyFillArcMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new PolyFillArcMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new PolyFillArcMessage((const PolyFillArcMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (PolyFillArcMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* PolyFillArc_H */
diff --git a/nxcomp/PolyFillRectangle.cpp b/nxcomp/PolyFillRectangle.cpp
new file mode 100644
index 000000000..e1e6b7876
--- /dev/null
+++ b/nxcomp/PolyFillRectangle.cpp
@@ -0,0 +1,148 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "PolyFillRectangle.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int PolyFillRectangleStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolyFillRectangleMessage *polyFillRectangle = (PolyFillRectangleMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ polyFillRectangle -> drawable = GetULONG(buffer + 4, bigEndian);
+ polyFillRectangle -> gcontext = GetULONG(buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int PolyFillRectangleStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolyFillRectangleMessage *polyFillRectangle = (PolyFillRectangleMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ PutULONG(polyFillRectangle -> drawable, buffer + 4, bigEndian);
+ PutULONG(polyFillRectangle -> gcontext, buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void PolyFillRectangleStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ PolyFillRectangleMessage *polyFillRectangle = (PolyFillRectangleMessage *) message;
+
+ *logofs << name() << ": Identity drawable " << polyFillRectangle -> drawable
+ << ", gcontext " << polyFillRectangle -> gcontext
+ << ", size " << polyFillRectangle -> size_ << ".\n" << logofs_flush;
+ #endif
+}
+
+void PolyFillRectangleStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+}
+
+void PolyFillRectangleStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ PolyFillRectangleMessage *polyFillRectangle = (PolyFillRectangleMessage *) message;
+ PolyFillRectangleMessage *cachedPolyFillRectangle = (PolyFillRectangleMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding value " << polyFillRectangle -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polyFillRectangle -> drawable, clientCache -> drawableCache);
+
+ cachedPolyFillRectangle -> drawable = polyFillRectangle -> drawable;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding value " << polyFillRectangle -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polyFillRectangle -> gcontext, clientCache -> gcCache);
+
+ cachedPolyFillRectangle -> gcontext = polyFillRectangle -> gcontext;
+}
+
+void PolyFillRectangleStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ PolyFillRectangleMessage *polyFillRectangle = (PolyFillRectangleMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ polyFillRectangle -> drawable = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyFillRectangle -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ polyFillRectangle -> gcontext = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyFillRectangle -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+}
diff --git a/nxcomp/PolyFillRectangle.h b/nxcomp/PolyFillRectangle.h
new file mode 100644
index 000000000..bf94c4818
--- /dev/null
+++ b/nxcomp/PolyFillRectangle.h
@@ -0,0 +1,177 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef PolyFillRectangle_H
+#define PolyFillRectangle_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define POLYFILLRECTANGLE_ENABLE_CACHE 1
+#define POLYFILLRECTANGLE_ENABLE_DATA 0
+#define POLYFILLRECTANGLE_ENABLE_SPLIT 0
+#define POLYFILLRECTANGLE_ENABLE_COMPRESS 0
+
+#define POLYFILLRECTANGLE_DATA_LIMIT 2048
+#define POLYFILLRECTANGLE_DATA_OFFSET 12
+
+#define POLYFILLRECTANGLE_CACHE_SLOTS 4000
+#define POLYFILLRECTANGLE_CACHE_THRESHOLD 5
+#define POLYFILLRECTANGLE_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class PolyFillRectangleMessage : public Message
+{
+ friend class PolyFillRectangleStore;
+
+ public:
+
+ PolyFillRectangleMessage()
+ {
+ }
+
+ ~PolyFillRectangleMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned int drawable;
+ unsigned int gcontext;
+};
+
+class PolyFillRectangleStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ PolyFillRectangleStore() : MessageStore()
+ {
+ enableCache = POLYFILLRECTANGLE_ENABLE_CACHE;
+ enableData = POLYFILLRECTANGLE_ENABLE_DATA;
+ enableSplit = POLYFILLRECTANGLE_ENABLE_SPLIT;
+ enableCompress = POLYFILLRECTANGLE_ENABLE_COMPRESS;
+
+ dataLimit = POLYFILLRECTANGLE_DATA_LIMIT;
+ dataOffset = POLYFILLRECTANGLE_DATA_OFFSET;
+
+ cacheSlots = POLYFILLRECTANGLE_CACHE_SLOTS;
+ cacheThreshold = POLYFILLRECTANGLE_CACHE_THRESHOLD;
+ cacheLowerThreshold = POLYFILLRECTANGLE_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~PolyFillRectangleStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "PolyFillRectangle";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_PolyFillRectangle;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(PolyFillRectangleMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new PolyFillRectangleMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new PolyFillRectangleMessage((const PolyFillRectangleMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (PolyFillRectangleMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* PolyFillRectangle_H */
diff --git a/nxcomp/PolyLine.cpp b/nxcomp/PolyLine.cpp
new file mode 100644
index 000000000..4d285e0b1
--- /dev/null
+++ b/nxcomp/PolyLine.cpp
@@ -0,0 +1,170 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "PolyLine.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int PolyLineStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolyLineMessage *polyLine = (PolyLineMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ polyLine -> mode = *(buffer + 1);
+
+ polyLine -> drawable = GetULONG(buffer + 4, bigEndian);
+ polyLine -> gcontext = GetULONG(buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int PolyLineStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolyLineMessage *polyLine = (PolyLineMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ *(buffer + 1) = polyLine -> mode;
+
+ PutULONG(polyLine -> drawable, buffer + 4, bigEndian);
+ PutULONG(polyLine -> gcontext, buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void PolyLineStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ PolyLineMessage *polyLine = (PolyLineMessage *) message;
+
+ *logofs << name() << ": Identity drawable " << polyLine -> drawable
+ << ", gcontext " << polyLine -> gcontext
+ << ", size " << polyLine -> size_ << ".\n" << logofs_flush;
+ #endif
+}
+
+void PolyLineStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ if (control -> isProtoStep8() == 1)
+ {
+ md5_append(md5_state_, buffer + 1, 1);
+ }
+}
+
+void PolyLineStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ PolyLineMessage *polyLine = (PolyLineMessage *) message;
+ PolyLineMessage *cachedPolyLine = (PolyLineMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ if (control -> isProtoStep8() == 0)
+ {
+ encodeBuffer.encodeBoolValue((unsigned int) polyLine -> mode);
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyLine -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polyLine -> drawable, clientCache -> drawableCache);
+
+ cachedPolyLine -> drawable = polyLine -> drawable;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyLine -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polyLine -> gcontext, clientCache -> gcCache);
+
+ cachedPolyLine -> gcontext = polyLine -> gcontext;
+}
+
+void PolyLineStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ PolyLineMessage *polyLine = (PolyLineMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ if (control -> isProtoStep8() == 0)
+ {
+ decodeBuffer.decodeBoolValue(value);
+
+ polyLine -> mode = value;
+ }
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ polyLine -> drawable = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyLine -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ polyLine -> gcontext = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyLine -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+}
+
+
diff --git a/nxcomp/PolyLine.h b/nxcomp/PolyLine.h
new file mode 100644
index 000000000..39447e659
--- /dev/null
+++ b/nxcomp/PolyLine.h
@@ -0,0 +1,178 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef PolyLine_H
+#define PolyLine_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define POLYLINE_ENABLE_CACHE 1
+#define POLYLINE_ENABLE_DATA 0
+#define POLYLINE_ENABLE_SPLIT 0
+#define POLYLINE_ENABLE_COMPRESS 0
+
+#define POLYLINE_DATA_LIMIT 144
+#define POLYLINE_DATA_OFFSET 12
+
+#define POLYLINE_CACHE_SLOTS 3000
+#define POLYLINE_CACHE_THRESHOLD 3
+#define POLYLINE_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class PolyLineMessage : public Message
+{
+ friend class PolyLineStore;
+
+ public:
+
+ PolyLineMessage()
+ {
+ }
+
+ ~PolyLineMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char mode;
+ unsigned int drawable;
+ unsigned int gcontext;
+};
+
+class PolyLineStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ PolyLineStore() : MessageStore()
+ {
+ enableCache = POLYLINE_ENABLE_CACHE;
+ enableData = POLYLINE_ENABLE_DATA;
+ enableSplit = POLYLINE_ENABLE_SPLIT;
+ enableCompress = POLYLINE_ENABLE_COMPRESS;
+
+ dataLimit = POLYLINE_DATA_LIMIT;
+ dataOffset = POLYLINE_DATA_OFFSET;
+
+ cacheSlots = POLYLINE_CACHE_SLOTS;
+ cacheThreshold = POLYLINE_CACHE_THRESHOLD;
+ cacheLowerThreshold = POLYLINE_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~PolyLineStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "PolyLine";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_PolyLine;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(PolyLineMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new PolyLineMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new PolyLineMessage((const PolyLineMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (PolyLineMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* PolyLine_H */
diff --git a/nxcomp/PolyPoint.cpp b/nxcomp/PolyPoint.cpp
new file mode 100644
index 000000000..847300f78
--- /dev/null
+++ b/nxcomp/PolyPoint.cpp
@@ -0,0 +1,170 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "PolyPoint.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int PolyPointStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolyPointMessage *polyPoint = (PolyPointMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ polyPoint -> mode = *(buffer + 1);
+
+ polyPoint -> drawable = GetULONG(buffer + 4, bigEndian);
+ polyPoint -> gcontext = GetULONG(buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int PolyPointStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolyPointMessage *polyPoint = (PolyPointMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ *(buffer + 1) = polyPoint -> mode;
+
+ PutULONG(polyPoint -> drawable, buffer + 4, bigEndian);
+ PutULONG(polyPoint -> gcontext, buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void PolyPointStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ PolyPointMessage *polyPoint = (PolyPointMessage *) message;
+
+ *logofs << name() << ": Identity drawable " << polyPoint -> drawable
+ << ", gcontext " << polyPoint -> gcontext
+ << ", size " << polyPoint -> size_ << ".\n" << logofs_flush;
+ #endif
+}
+
+void PolyPointStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ if (control -> isProtoStep8() == 1)
+ {
+ md5_append(md5_state_, buffer + 1, 1);
+ }
+}
+
+void PolyPointStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ PolyPointMessage *polyPoint = (PolyPointMessage *) message;
+ PolyPointMessage *cachedPolyPoint = (PolyPointMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ if (control -> isProtoStep8() == 0)
+ {
+ encodeBuffer.encodeBoolValue((unsigned int) polyPoint -> mode);
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyPoint -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polyPoint -> drawable, clientCache -> drawableCache);
+
+ cachedPolyPoint -> drawable = polyPoint -> drawable;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyPoint -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polyPoint -> gcontext, clientCache -> gcCache);
+
+ cachedPolyPoint -> gcontext = polyPoint -> gcontext;
+}
+
+void PolyPointStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ PolyPointMessage *polyPoint = (PolyPointMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ if (control -> isProtoStep8() == 0)
+ {
+ decodeBuffer.decodeBoolValue(value);
+
+ polyPoint -> mode = value;
+ }
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ polyPoint -> drawable = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyPoint -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ polyPoint -> gcontext = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyPoint -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+}
+
+
diff --git a/nxcomp/PolyPoint.h b/nxcomp/PolyPoint.h
new file mode 100644
index 000000000..e090ff9fa
--- /dev/null
+++ b/nxcomp/PolyPoint.h
@@ -0,0 +1,178 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef PolyPoint_H
+#define PolyPoint_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define POLYPOINT_ENABLE_CACHE 1
+#define POLYPOINT_ENABLE_DATA 0
+#define POLYPOINT_ENABLE_SPLIT 0
+#define POLYPOINT_ENABLE_COMPRESS 0
+
+#define POLYPOINT_DATA_LIMIT 3200
+#define POLYPOINT_DATA_OFFSET 12
+
+#define POLYPOINT_CACHE_SLOTS 3000
+#define POLYPOINT_CACHE_THRESHOLD 3
+#define POLYPOINT_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class PolyPointMessage : public Message
+{
+ friend class PolyPointStore;
+
+ public:
+
+ PolyPointMessage()
+ {
+ }
+
+ ~PolyPointMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char mode;
+ unsigned int drawable;
+ unsigned int gcontext;
+};
+
+class PolyPointStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ PolyPointStore() : MessageStore()
+ {
+ enableCache = POLYPOINT_ENABLE_CACHE;
+ enableData = POLYPOINT_ENABLE_DATA;
+ enableSplit = POLYPOINT_ENABLE_SPLIT;
+ enableCompress = POLYPOINT_ENABLE_COMPRESS;
+
+ dataLimit = POLYPOINT_DATA_LIMIT;
+ dataOffset = POLYPOINT_DATA_OFFSET;
+
+ cacheSlots = POLYPOINT_CACHE_SLOTS;
+ cacheThreshold = POLYPOINT_CACHE_THRESHOLD;
+ cacheLowerThreshold = POLYPOINT_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~PolyPointStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "PolyPoint";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_PolyPoint;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(PolyPointMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new PolyPointMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new PolyPointMessage((const PolyPointMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (PolyPointMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* PolyPoint_H */
diff --git a/nxcomp/PolySegment.cpp b/nxcomp/PolySegment.cpp
new file mode 100644
index 000000000..e9259d958
--- /dev/null
+++ b/nxcomp/PolySegment.cpp
@@ -0,0 +1,150 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "PolySegment.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int PolySegmentStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolySegmentMessage *polySegment = (PolySegmentMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ polySegment -> drawable = GetULONG(buffer + 4, bigEndian);
+ polySegment -> gcontext = GetULONG(buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int PolySegmentStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolySegmentMessage *polySegment = (PolySegmentMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ PutULONG(polySegment -> drawable, buffer + 4, bigEndian);
+ PutULONG(polySegment -> gcontext, buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void PolySegmentStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ PolySegmentMessage *polySegment = (PolySegmentMessage *) message;
+
+ *logofs << name() << ": Identity drawable " << polySegment -> drawable
+ << ", gcontext " << polySegment -> gcontext
+ << ", size " << polySegment -> size_ << ".\n" << logofs_flush;
+ #endif
+}
+
+void PolySegmentStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+}
+
+void PolySegmentStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ PolySegmentMessage *polySegment = (PolySegmentMessage *) message;
+ PolySegmentMessage *cachedPolySegment = (PolySegmentMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polySegment -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polySegment -> drawable, clientCache -> drawableCache);
+
+ cachedPolySegment -> drawable = polySegment -> drawable;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polySegment -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polySegment -> gcontext, clientCache -> gcCache);
+
+ cachedPolySegment -> gcontext = polySegment -> gcontext;
+}
+
+void PolySegmentStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ PolySegmentMessage *polySegment = (PolySegmentMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ polySegment -> drawable = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polySegment -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ polySegment -> gcontext = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polySegment -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+}
+
+
diff --git a/nxcomp/PolySegment.h b/nxcomp/PolySegment.h
new file mode 100644
index 000000000..a74865827
--- /dev/null
+++ b/nxcomp/PolySegment.h
@@ -0,0 +1,177 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef PolySegment_H
+#define PolySegment_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define POLYSEGMENT_ENABLE_CACHE 1
+#define POLYSEGMENT_ENABLE_DATA 0
+#define POLYSEGMENT_ENABLE_SPLIT 0
+#define POLYSEGMENT_ENABLE_COMPRESS 0
+
+#define POLYSEGMENT_DATA_LIMIT 8192
+#define POLYSEGMENT_DATA_OFFSET 12
+
+#define POLYSEGMENT_CACHE_SLOTS 3000
+#define POLYSEGMENT_CACHE_THRESHOLD 5
+#define POLYSEGMENT_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class PolySegmentMessage : public Message
+{
+ friend class PolySegmentStore;
+
+ public:
+
+ PolySegmentMessage()
+ {
+ }
+
+ ~PolySegmentMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned int drawable;
+ unsigned int gcontext;
+};
+
+class PolySegmentStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ PolySegmentStore() : MessageStore()
+ {
+ enableCache = POLYSEGMENT_ENABLE_CACHE;
+ enableData = POLYSEGMENT_ENABLE_DATA;
+ enableSplit = POLYSEGMENT_ENABLE_SPLIT;
+ enableCompress = POLYSEGMENT_ENABLE_COMPRESS;
+
+ dataLimit = POLYSEGMENT_DATA_LIMIT;
+ dataOffset = POLYSEGMENT_DATA_OFFSET;
+
+ cacheSlots = POLYSEGMENT_CACHE_SLOTS;
+ cacheThreshold = POLYSEGMENT_CACHE_THRESHOLD;
+ cacheLowerThreshold = POLYSEGMENT_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~PolySegmentStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "PolySegment";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_PolySegment;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(PolySegmentMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new PolySegmentMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new PolySegmentMessage((const PolySegmentMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (PolySegmentMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* PolySegment_H */
diff --git a/nxcomp/PolyText16.cpp b/nxcomp/PolyText16.cpp
new file mode 100644
index 000000000..d90b093ec
--- /dev/null
+++ b/nxcomp/PolyText16.cpp
@@ -0,0 +1,300 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "PolyText16.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int PolyText16Store::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolyText16Message *polyText16 = (PolyText16Message *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ polyText16 -> drawable = GetULONG(buffer + 4, bigEndian);
+ polyText16 -> gcontext = GetULONG(buffer + 8, bigEndian);
+
+ polyText16 -> x = GetUINT(buffer + 12, bigEndian);
+ polyText16 -> y = GetUINT(buffer + 14, bigEndian);
+
+ //
+ // Clean up padding bytes.
+ //
+
+ #ifdef DUMP
+
+ DumpData(buffer, size);
+
+ *logofs << "\n" << logofs_flush;
+
+ #endif
+
+ if ((int) size > dataOffset)
+ {
+ int current;
+ int length;
+ int delta;
+ int item;
+
+ unsigned int nitem;
+
+ unsigned char *pad = NULL;
+ unsigned char *end = NULL;
+
+ delta = 1;
+ nitem = 0;
+
+ #ifdef DUMP
+ *logofs << name() << " Size " << size << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Data is a list of TextItem where element
+ // can be a string or a font shift.
+ //
+
+ current = POLYTEXT16_DATA_OFFSET;
+ length = POLYTEXT16_DATA_OFFSET;
+
+ do
+ {
+ #ifdef DUMP
+ *logofs << name() << " Current " << current << ".\n" << logofs_flush;
+ #endif
+
+ item = GetUINT(buffer + length , bigEndian);
+
+ if (item < 255)
+ {
+ //
+ // Text element. Number represents
+ // the 'Length of CHAR2B string'
+ // field.
+ //
+
+ length += ((item * 2) + delta + 1);
+
+ nitem++;
+ }
+ else if (item == 255)
+ {
+ //
+ // Element is a font shift.
+ //
+
+ length += 5;
+
+ nitem++;
+ }
+
+ #ifdef DUMP
+ *logofs << name() << " Item " << item << ".\n" << logofs_flush;
+ #endif
+
+ current += length;
+ }
+ while(current < (int) size && item != 0);
+
+ #ifdef DUMP
+ *logofs << name() << " Final length " << length << ".\n" << logofs_flush;
+ #endif
+
+ end = ((unsigned char *) buffer) + size;
+
+ pad = ((unsigned char *) buffer) + length;
+
+ for (; pad < end && nitem >= 1; pad++)
+ {
+ #ifdef DUMP
+ *logofs << name() << " Padding " << " .\n" << logofs_flush;
+ #endif
+
+ *pad = 0;
+ }
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int PolyText16Store::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolyText16Message *polyText16 = (PolyText16Message *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ PutULONG(polyText16 -> drawable, buffer + 4, bigEndian);
+ PutULONG(polyText16 -> gcontext, buffer + 8, bigEndian);
+
+ PutUINT(polyText16 -> x, buffer + 12, bigEndian);
+ PutUINT(polyText16 -> y, buffer + 14, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void PolyText16Store::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ PolyText16Message *polyText16 = (PolyText16Message *) message;
+
+ *logofs << name() << ": Identity drawable " << polyText16 -> drawable
+ << ", gcontext " << polyText16 -> gcontext << ", x " << polyText16 -> x
+ << ", y " << polyText16 -> y << ", size " << polyText16 -> size_
+ << ".\n";
+
+ #endif
+}
+
+void PolyText16Store::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+}
+
+void PolyText16Store::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ PolyText16Message *polyText16 = (PolyText16Message *) message;
+ PolyText16Message *cachedPolyText16 = (PolyText16Message *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyText16 -> drawable
+ << " as " << "drawable" << " field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polyText16 -> drawable, clientCache -> drawableCache);
+
+ cachedPolyText16 -> drawable = polyText16 -> drawable;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyText16 -> gcontext
+ << " as " << "gcontext" << " field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polyText16 -> gcontext, clientCache -> gcCache);
+
+ cachedPolyText16 -> gcontext = polyText16 -> gcontext;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyText16 -> x
+ << " as " << "x" << " field.\n" << logofs_flush;
+ #endif
+
+ unsigned short int diff_x = polyText16 -> x - cachedPolyText16 -> x;
+
+ encodeBuffer.encodeCachedValue(diff_x, 16,
+ clientCache -> polyTextCacheX);
+
+ cachedPolyText16 -> x = polyText16 -> x;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyText16 -> y
+ << " as " << "y" << " field.\n" << logofs_flush;
+ #endif
+
+ unsigned short int diff_y = polyText16 -> y - cachedPolyText16 -> y;
+
+ encodeBuffer.encodeCachedValue(diff_y, 16,
+ clientCache -> polyTextCacheY);
+
+ cachedPolyText16 -> y = polyText16 -> y;
+}
+
+void PolyText16Store::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ PolyText16Message *polyText16 = (PolyText16Message *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ polyText16 -> drawable = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyText16 -> drawable
+ << " as " << "drawable" << " field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ polyText16 -> gcontext = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyText16 -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> polyTextCacheX);
+
+ polyText16 -> x += value;
+ polyText16 -> x &= 0xffff;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyText16 -> x
+ << " as x field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> polyTextCacheY);
+
+ polyText16 -> y += value;
+ polyText16 -> y &= 0xffff;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyText16 -> y
+ << " as y field.\n" << logofs_flush;
+ #endif
+}
+
+
diff --git a/nxcomp/PolyText16.h b/nxcomp/PolyText16.h
new file mode 100644
index 000000000..cda6cceed
--- /dev/null
+++ b/nxcomp/PolyText16.h
@@ -0,0 +1,180 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef PolyText16_H
+#define PolyText16_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define POLYTEXT16_ENABLE_CACHE 1
+#define POLYTEXT16_ENABLE_DATA 0
+#define POLYTEXT16_ENABLE_SPLIT 0
+#define POLYTEXT16_ENABLE_COMPRESS 0
+
+#define POLYTEXT16_DATA_LIMIT 420
+#define POLYTEXT16_DATA_OFFSET 16
+
+#define POLYTEXT16_CACHE_SLOTS 3000
+#define POLYTEXT16_CACHE_THRESHOLD 4
+#define POLYTEXT16_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class PolyText16Message : public Message
+{
+ friend class PolyText16Store;
+
+ public:
+
+ PolyText16Message()
+ {
+ }
+
+ ~PolyText16Message()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned int drawable;
+ unsigned int gcontext;
+
+ unsigned short x;
+ unsigned short y;
+};
+
+class PolyText16Store : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ PolyText16Store() : MessageStore()
+ {
+ enableCache = POLYTEXT16_ENABLE_CACHE;
+ enableData = POLYTEXT16_ENABLE_DATA;
+ enableSplit = POLYTEXT16_ENABLE_SPLIT;
+ enableCompress = POLYTEXT16_ENABLE_COMPRESS;
+
+ dataLimit = POLYTEXT16_DATA_LIMIT;
+ dataOffset = POLYTEXT16_DATA_OFFSET;
+
+ cacheSlots = POLYTEXT16_CACHE_SLOTS;
+ cacheThreshold = POLYTEXT16_CACHE_THRESHOLD;
+ cacheLowerThreshold = POLYTEXT16_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~PolyText16Store()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "PolyText16";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_PolyText16;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(PolyText16Message);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new PolyText16Message();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new PolyText16Message((const PolyText16Message &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (PolyText16Message *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* PolyText16_H */
diff --git a/nxcomp/PolyText8.cpp b/nxcomp/PolyText8.cpp
new file mode 100644
index 000000000..15752721d
--- /dev/null
+++ b/nxcomp/PolyText8.cpp
@@ -0,0 +1,298 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "PolyText8.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int PolyText8Store::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolyText8Message *polyText8 = (PolyText8Message *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ polyText8 -> drawable = GetULONG(buffer + 4, bigEndian);
+ polyText8 -> gcontext = GetULONG(buffer + 8, bigEndian);
+
+ polyText8 -> x = GetUINT(buffer + 12, bigEndian);
+ polyText8 -> y = GetUINT(buffer + 14, bigEndian);
+
+ //
+ // Clean up padding bytes.
+ //
+
+ #ifdef DUMP
+
+ DumpData(buffer, size);
+
+ *logofs << "\n\n" << logofs_flush;
+
+ #endif
+
+ if ((int) size > dataOffset)
+ {
+ int length;
+ int current;
+ int delta;
+ int item;
+
+ unsigned int nitem;
+
+ unsigned char *pad = NULL;
+ unsigned char *end = NULL;
+
+ delta = 1;
+ nitem = 0;
+
+ #ifdef DUMP
+ *logofs << name() << " Size " << size << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Data is a list of TextItem where element
+ // can be a string or a font shift.
+ //
+
+ current = POLYTEXT8_DATA_OFFSET;
+ length = POLYTEXT8_DATA_OFFSET;
+
+ do
+ {
+ #ifdef DUMP
+ *logofs << name() << " Current " << current << ".\n" << logofs_flush;
+ #endif
+
+ item = GetUINT(buffer + length , bigEndian);
+
+ if (item < 255)
+ {
+ //
+ // Text element. Number represents
+ // the 'Length of string' field.
+ //
+
+ length += (item + delta + 1);
+
+ nitem++;
+ }
+ else if (item == 255)
+ {
+ //
+ // Element is a font shift.
+ //
+
+ length += 5;
+
+ nitem++;
+ }
+
+ #ifdef DUMP
+ *logofs << name() << " Item " << item << ".\n" << logofs_flush;
+ #endif
+
+ current += length;
+ }
+ while(current < (int) size && item != 0);
+
+
+ #ifdef DUMP
+ *logofs << name() << " Final length " << length << ".\n" << logofs_flush;
+ #endif
+
+ end = ((unsigned char *) buffer) + size;
+
+ pad = ((unsigned char *) buffer) + length;
+
+ for (; pad < end && nitem >= 1; pad++)
+ {
+ #ifdef DUMP
+ *logofs << name() << " Padding " << " .\n" << logofs_flush;
+ #endif
+
+ *pad = 0;
+ }
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int PolyText8Store::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolyText8Message *polyText8 = (PolyText8Message *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ PutULONG(polyText8 -> drawable, buffer + 4, bigEndian);
+ PutULONG(polyText8 -> gcontext, buffer + 8, bigEndian);
+
+ PutUINT(polyText8 -> x, buffer + 12, bigEndian);
+ PutUINT(polyText8 -> y, buffer + 14, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void PolyText8Store::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ PolyText8Message *polyText8 = (PolyText8Message *) message;
+
+ *logofs << name() << ": Identity drawable " << polyText8 -> drawable
+ << ", gcontext " << polyText8 -> gcontext << ", x " << polyText8 -> x
+ << ", y " << polyText8 -> y << ", size " << polyText8 -> size_
+ << ".\n";
+
+ #endif
+}
+
+void PolyText8Store::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+}
+
+void PolyText8Store::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ PolyText8Message *polyText8 = (PolyText8Message *) message;
+ PolyText8Message *cachedPolyText8 = (PolyText8Message *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyText8 -> drawable
+ << " as " << "drawable" << " field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polyText8 -> drawable, clientCache -> drawableCache);
+
+ cachedPolyText8 -> drawable = polyText8 -> drawable;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyText8 -> gcontext
+ << " as " << "gcontext" << " field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polyText8 -> gcontext, clientCache -> gcCache);
+
+ cachedPolyText8 -> gcontext = polyText8 -> gcontext;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyText8 -> x
+ << " as " << "x" << " field.\n" << logofs_flush;
+ #endif
+
+ unsigned short int diff_x = polyText8 -> x - cachedPolyText8 -> x;
+
+ encodeBuffer.encodeCachedValue(diff_x, 16,
+ clientCache -> polyTextCacheX);
+
+ cachedPolyText8 -> x = polyText8 -> x;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyText8 -> y
+ << " as " << "y" << " field.\n" << logofs_flush;
+ #endif
+
+ unsigned short int diff_y = polyText8 -> y - cachedPolyText8 -> y;
+
+ encodeBuffer.encodeCachedValue(diff_y, 16,
+ clientCache -> polyTextCacheY);
+
+ cachedPolyText8 -> y = polyText8 -> y;
+}
+
+void PolyText8Store::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ PolyText8Message *polyText8 = (PolyText8Message *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ polyText8 -> drawable = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyText8 -> drawable
+ << " as " << "drawable" << " field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ polyText8 -> gcontext = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyText8 -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> polyTextCacheX);
+
+ polyText8 -> x += value;
+ polyText8 -> x &= 0xffff;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyText8 -> x
+ << " as x field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> polyTextCacheY);
+
+ polyText8 -> y += value;
+ polyText8 -> y &= 0xffff;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyText8 -> y
+ << " as y field.\n" << logofs_flush;
+ #endif
+}
diff --git a/nxcomp/PolyText8.h b/nxcomp/PolyText8.h
new file mode 100644
index 000000000..eac5aab7c
--- /dev/null
+++ b/nxcomp/PolyText8.h
@@ -0,0 +1,180 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef PolyText8_H
+#define PolyText8_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define POLYTEXT8_ENABLE_CACHE 1
+#define POLYTEXT8_ENABLE_DATA 0
+#define POLYTEXT8_ENABLE_SPLIT 0
+#define POLYTEXT8_ENABLE_COMPRESS 0
+
+#define POLYTEXT8_DATA_LIMIT 380
+#define POLYTEXT8_DATA_OFFSET 16
+
+#define POLYTEXT8_CACHE_SLOTS 3000
+#define POLYTEXT8_CACHE_THRESHOLD 5
+#define POLYTEXT8_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class PolyText8Message : public Message
+{
+ friend class PolyText8Store;
+
+ public:
+
+ PolyText8Message()
+ {
+ }
+
+ ~PolyText8Message()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned int drawable;
+ unsigned int gcontext;
+
+ unsigned short x;
+ unsigned short y;
+};
+
+class PolyText8Store : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ PolyText8Store() : MessageStore()
+ {
+ enableCache = POLYTEXT8_ENABLE_CACHE;
+ enableData = POLYTEXT8_ENABLE_DATA;
+ enableSplit = POLYTEXT8_ENABLE_SPLIT;
+ enableCompress = POLYTEXT8_ENABLE_COMPRESS;
+
+ dataLimit = POLYTEXT8_DATA_LIMIT;
+ dataOffset = POLYTEXT8_DATA_OFFSET;
+
+ cacheSlots = POLYTEXT8_CACHE_SLOTS;
+ cacheThreshold = POLYTEXT8_CACHE_THRESHOLD;
+ cacheLowerThreshold = POLYTEXT8_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~PolyText8Store()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "PolyText8";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_PolyText8;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(PolyText8Message);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new PolyText8Message();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new PolyText8Message((const PolyText8Message &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (PolyText8Message *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* PolyText8_H */
diff --git a/nxcomp/PositionCacheCompat.cpp b/nxcomp/PositionCacheCompat.cpp
new file mode 100644
index 000000000..4a6a2cd63
--- /dev/null
+++ b/nxcomp/PositionCacheCompat.cpp
@@ -0,0 +1,45 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "Control.h"
+
+#include "PositionCacheCompat.h"
+
+PositionCacheCompat::PositionCacheCompat()
+{
+ if (control -> isProtoStep7() == 0)
+ {
+ for (int i = 0; i < 32; i++)
+ {
+ base_[i] = new IntCache(8);
+ }
+
+ slot_ = 0;
+ last_ = 0;
+ }
+}
+
+PositionCacheCompat::~PositionCacheCompat()
+{
+ if (control -> isProtoStep7() == 0)
+ {
+ for (int i = 0; i < 32; i++)
+ {
+ delete base_[i];
+ }
+ }
+}
diff --git a/nxcomp/PositionCacheCompat.h b/nxcomp/PositionCacheCompat.h
new file mode 100644
index 000000000..983e45382
--- /dev/null
+++ b/nxcomp/PositionCacheCompat.h
@@ -0,0 +1,41 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef PositionCacheCompat_H
+#define PositionCacheCompat_H
+
+#include "IntCache.h"
+
+class PositionCacheCompat
+{
+ friend class EncodeBuffer;
+ friend class DecodeBuffer;
+
+ public:
+
+ PositionCacheCompat();
+ ~PositionCacheCompat();
+
+ private:
+
+ IntCache *base_[32];
+
+ unsigned int slot_;
+ short int last_;
+};
+
+#endif /* PositionCacheCompat_H */
diff --git a/nxcomp/Proxy.cpp b/nxcomp/Proxy.cpp
new file mode 100644
index 000000000..3b4df7eb6
--- /dev/null
+++ b/nxcomp/Proxy.cpp
@@ -0,0 +1,6450 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "Misc.h"
+
+#if defined(__CYGWIN32__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__sun)
+#include <netinet/in_systm.h>
+#endif
+
+#ifndef __CYGWIN32__
+#include <sys/un.h>
+#endif
+
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+#if defined(__EMX__ ) || defined(__CYGWIN32__)
+
+struct sockaddr_un
+{
+ u_short sun_family;
+ char sun_path[108];
+};
+
+#endif
+
+#include "NXalert.h"
+#include "NXvars.h"
+
+#include "Proxy.h"
+
+#include "Socket.h"
+#include "Channel.h"
+#include "Statistics.h"
+
+#include "ClientChannel.h"
+#include "ServerChannel.h"
+#include "GenericChannel.h"
+
+//
+// We need to adjust some values related
+// to these messages at the time the mes-
+// sage stores are reconfigured.
+//
+
+#include "PutImage.h"
+#include "ChangeGC.h"
+#include "PolyFillRectangle.h"
+#include "PutPackedImage.h"
+
+//
+// This is from the main loop.
+//
+
+extern void CleanupListeners();
+
+//
+// Default size of string buffers.
+//
+
+#define DEFAULT_STRING_LENGTH 512
+
+//
+// Set the verbosity level. You also need
+// to define DUMP in Misc.cpp if DUMP is
+// defined here.
+//
+
+#define WARNING
+#define PANIC
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Log the important tracepoints related
+// to writing packets to the peer proxy.
+//
+
+#undef FLUSH
+
+//
+// Log the operations related to splits.
+//
+
+#undef SPLIT
+
+//
+// Log the operations related to sending
+// and receiving the control tokens.
+//
+
+#undef TOKEN
+
+//
+// Log the operations related to setting
+// the token limits.
+//
+
+#undef LIMIT
+
+//
+// Log a warning if no data is written by
+// the proxy within a timeout.
+//
+
+#undef TIME
+
+//
+// Log the operation related to generating
+// the ping message at idle time.
+//
+
+#undef PING
+
+Proxy::Proxy(int fd)
+
+ : transport_(new ProxyTransport(fd)), fd_(fd), readBuffer_(transport_)
+{
+ for (int channelId = 0;
+ channelId < CONNECTIONS_LIMIT;
+ channelId++)
+ {
+ channels_[channelId] = NULL;
+ transports_[channelId] = NULL;
+ congestions_[channelId] = 0;
+
+ fdMap_[channelId] = nothing;
+ channelMap_[channelId] = nothing;
+ }
+
+ inputChannel_ = nothing;
+ outputChannel_ = nothing;
+
+ controlLength_ = 0;
+
+ operation_ = operation_in_negotiation;
+
+ draining_ = 0;
+ priority_ = 0;
+ finish_ = 0;
+ shutdown_ = 0;
+ congestion_ = 0;
+
+ timer_ = 0;
+ alert_ = 0;
+
+ agent_ = nothing;
+
+ //
+ // Set null timeouts. This will require
+ // a new link configuration.
+ //
+
+ timeouts_.split = 0;
+ timeouts_.motion = 0;
+
+ timeouts_.readTs = getTimestamp();
+ timeouts_.writeTs = getTimestamp();
+
+ timeouts_.loopTs = getTimestamp();
+ timeouts_.pingTs = getTimestamp();
+ timeouts_.alertTs = nullTimestamp();
+ timeouts_.loadTs = nullTimestamp();
+
+ timeouts_.splitTs = nullTimestamp();
+ timeouts_.motionTs = nullTimestamp();
+
+ //
+ // Initialize the token counters. This
+ // will require a new link configuration.
+ //
+
+ for (int i = token_control; i <= token_data; i++)
+ {
+ tokens_[i].size = 0;
+ tokens_[i].limit = 0;
+
+ tokens_[i].bytes = 0;
+ tokens_[i].remaining = 0;
+ }
+
+ tokens_[token_control].request = code_control_token_request;
+ tokens_[token_control].reply = code_control_token_reply;
+ tokens_[token_control].type = token_control;
+
+ tokens_[token_split].request = code_split_token_request;
+ tokens_[token_split].reply = code_split_token_reply;
+ tokens_[token_split].type = token_split;
+
+ tokens_[token_data].request = code_data_token_request;
+ tokens_[token_data].reply = code_data_token_reply;
+ tokens_[token_data].type = token_data;
+
+ currentStatistics_ = NULL;
+
+ //
+ // Create compressor and decompressor
+ // for image and data payload.
+ //
+
+ compressor_ = new StaticCompressor(control -> LocalDataCompressionLevel,
+ control -> LocalDataCompressionThreshold);
+
+ //
+ // Create object storing NX specific
+ // opcodes.
+ //
+
+ opcodeStore_ = new OpcodeStore();
+
+ //
+ // Create the message stores.
+ //
+
+ clientStore_ = new ClientStore(compressor_);
+ serverStore_ = new ServerStore(compressor_);
+
+ //
+ // Older proxies will refuse to store
+ // messages bigger than 262144 bytes.
+ //
+
+ if (control -> isProtoStep7() == 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: WARNING! Limiting the maximum "
+ << "message size to " << 262144 << ".\n"
+ << logofs_flush;
+ #endif
+
+ control -> MaximumMessageSize = 262144;
+ }
+
+ clientCache_ = new ClientCache();
+ serverCache_ = new ServerCache();
+
+ if (clientCache_ == NULL || serverCache_ == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Failed to create the channel cache.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to create the channel cache.\n";
+
+ HandleCleanup();
+ }
+
+ //
+ // Prepare for image decompression.
+ //
+
+ UnpackInit();
+
+ #ifdef DEBUG
+ *logofs << "Proxy: Created new object at " << this
+ << ".\n" << logofs_flush;
+ #endif
+}
+
+Proxy::~Proxy()
+{
+ for (int channelId = 0;
+ channelId < CONNECTIONS_LIMIT;
+ channelId++)
+ {
+ if (channels_[channelId] != NULL)
+ {
+ deallocateTransport(channelId);
+
+ delete channels_[channelId];
+ channels_[channelId] = NULL;
+ }
+ }
+
+ delete transport_;
+ delete compressor_;
+
+ //
+ // Delete storage shared among channels.
+ //
+
+ delete opcodeStore_;
+
+ delete clientStore_;
+ delete serverStore_;
+
+ delete clientCache_;
+ delete serverCache_;
+
+ //
+ // Get rid of the image decompression
+ // resources.
+ //
+
+ UnpackDestroy();
+
+ #ifdef DEBUG
+ *logofs << "Proxy: Deleted proxy object at " << this
+ << ".\n" << logofs_flush;
+ #endif
+}
+
+int Proxy::setOperational()
+{
+ #ifdef TEST
+ *logofs << "Proxy: Entering operational mode.\n"
+ << logofs_flush;
+ #endif
+
+ operation_ = operation_in_messages;
+
+ return 1;
+}
+
+int Proxy::setReadDescriptors(fd_set *fdSet, int &fdMax, T_timestamp &tsMax)
+{
+ //
+ // Set the initial timeout to the time of
+ // the next ping. If the congestion count
+ // is greater than zero, anyway, use a
+ // shorter timeout to force a congestion
+ // update.
+ //
+
+ if (agent_ != nothing && congestions_[agent_] == 0 &&
+ statistics -> getCongestionInFrame() >= 1 &&
+ tokens_[token_control].remaining >=
+ (tokens_[token_control].limit - 1))
+ {
+ setMinTimestamp(tsMax, control -> IdleTimeout);
+
+ #ifdef TEST
+ *logofs << "Proxy: Initial timeout is " << tsMax.tv_sec
+ << " S and " << (double) tsMax.tv_usec /
+ 1000 << " Ms with congestion "
+ << statistics -> getCongestionInFrame()
+ << ".\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ setMinTimestamp(tsMax, control -> PingTimeout);
+
+ #ifdef TEST
+ *logofs << "Proxy: Initial timeout is " << tsMax.tv_sec
+ << " S and " << (double) tsMax.tv_usec /
+ 1000 << " Ms.\n" << logofs_flush;
+ #endif
+ }
+
+ int fd = -1;
+
+ if (isTimeToRead() == 1)
+ {
+ //
+ // If we don't have split tokens available
+ // don't set the timeout.
+ //
+
+ if (tokens_[token_split].remaining > 0 &&
+ isTimestamp(timeouts_.splitTs) == 1)
+ {
+ int diffTs = getTimeToNextSplit();
+
+ #if defined(TEST) || defined(INFO) || \
+ defined(FLUSH) || defined(SPLIT)
+
+ if (diffTimestamp(timeouts_.splitTs,
+ getTimestamp()) > timeouts_.split)
+ {
+ *logofs << "Proxy: FLUSH! SPLIT! WARNING! Running with "
+ << diffTimestamp(timeouts_.splitTs, getTimestamp())
+ << " Ms elapsed since the last split.\n"
+ << logofs_flush;
+ }
+
+ *logofs << "Proxy: FLUSH! SPLIT! Requesting timeout of "
+ << diffTs << " Ms as there are splits to send.\n"
+ << logofs_flush;
+
+ #endif
+
+ setMinTimestamp(tsMax, diffTs);
+ }
+ #if defined(TEST) || defined(INFO)
+ else if (isTimestamp(timeouts_.splitTs) == 1)
+ {
+ *logofs << "Proxy: WARNING! Not requesting a split "
+ << "timeout with " << tokens_[token_split].remaining
+ << " split tokens remaining.\n" << logofs_flush;
+ }
+ #endif
+
+ //
+ // Loop through the valid channels and set
+ // the descriptors selected for read and
+ // the timeout.
+ //
+
+ T_list &channelList = activeChannels_.getList();
+
+ for (T_list::iterator j = channelList.begin();
+ j != channelList.end(); j++)
+ {
+ int channelId = *j;
+
+ if (channels_[channelId] == NULL)
+ {
+ continue;
+ }
+
+ fd = getFd(channelId);
+
+ if (channels_[channelId] -> getFinish() == 0 &&
+ (channels_[channelId] -> getType() == channel_x11 ||
+ tokens_[token_data].remaining > 0) &&
+ congestions_[channelId] == 0)
+ {
+ FD_SET(fd, fdSet);
+
+ if (fd >= fdMax)
+ {
+ fdMax = fd + 1;
+ }
+
+ #ifdef TEST
+ *logofs << "Proxy: Descriptor FD#" << fd
+ << " selected for read with buffer length "
+ << transports_[channelId] -> length()
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Wakeup the proxy if there are motion
+ // events to flush.
+ //
+
+ if (isTimestamp(timeouts_.motionTs) == 1)
+ {
+ int diffTs = getTimeToNextMotion();
+
+ #if defined(TEST) || defined(INFO)
+
+ if (diffTimestamp(timeouts_.motionTs,
+ getTimestamp()) > timeouts_.motion)
+ {
+ *logofs << "Proxy: FLUSH! WARNING! Running with "
+ << diffTimestamp(timeouts_.motionTs, getTimestamp())
+ << " Ms elapsed since the last motion.\n"
+ << logofs_flush;
+ }
+
+ *logofs << "Proxy: FLUSH! Requesting timeout of "
+ << diffTs << " Ms as FD#" << fd << " has motion "
+ << "events to send.\n" << logofs_flush;
+
+ #endif
+
+ setMinTimestamp(tsMax, diffTs);
+ }
+ }
+ #if defined(TEST) || defined(INFO)
+ else
+ {
+ if (channels_[channelId] -> getType() != channel_x11 &&
+ tokens_[token_data].remaining <= 0)
+ {
+ *logofs << "Proxy: WARNING! Descriptor FD#" << fd
+ << " not selected for read with "
+ << tokens_[token_data].remaining << " data "
+ << "tokens remaining.\n" << logofs_flush;
+ }
+ }
+ #endif
+ }
+ }
+ #if defined(TEST) || defined(INFO)
+ else
+ {
+ *logofs << "Proxy: WARNING! Disabled reading from channels.\n"
+ << logofs_flush;
+
+ *logofs << "Proxy: WARNING! Congestion is " << congestion_
+ << " pending " << transport_ -> pending() << " blocked "
+ << transport_ -> blocked() << " length " << transport_ ->
+ length() << ".\n" << logofs_flush;
+ }
+ #endif
+
+ //
+ // Include the proxy descriptor.
+ //
+
+ FD_SET(fd_, fdSet);
+
+ if (fd_ >= fdMax)
+ {
+ fdMax = fd_ + 1;
+ }
+
+ #ifdef TEST
+ *logofs << "Proxy: Proxy descriptor FD#" << fd_
+ << " selected for read with buffer length "
+ << transport_ -> length() << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+//
+// Add to the mask the file descriptors of all
+// X connections to write to.
+//
+
+int Proxy::setWriteDescriptors(fd_set *fdSet, int &fdMax, T_timestamp &tsMax)
+{
+ int fd = -1;
+
+ T_list &channelList = activeChannels_.getList();
+
+ for (T_list::iterator j = channelList.begin();
+ j != channelList.end(); j++)
+ {
+ int channelId = *j;
+
+ if (channels_[channelId] != NULL)
+ {
+ fd = getFd(channelId);
+
+ if (transports_[channelId] -> length() > 0)
+ {
+ FD_SET(fd, fdSet);
+
+ #ifdef TEST
+ *logofs << "Proxy: Descriptor FD#" << fd << " selected "
+ << "for write with blocked " << transports_[channelId] ->
+ blocked() << " and length " << transports_[channelId] ->
+ length() << ".\n" << logofs_flush;
+ #endif
+
+ if (fd >= fdMax)
+ {
+ fdMax = fd + 1;
+ }
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Proxy: Descriptor FD#" << fd << " not selected "
+ << "for write with blocked " << transports_[channelId] ->
+ blocked() << " and length " << transports_[channelId] ->
+ length() << ".\n" << logofs_flush;
+ }
+ #endif
+
+ #if defined(TEST) || defined(INFO)
+
+ if (transports_[channelId] -> getType() !=
+ transport_agent && transports_[channelId] ->
+ length() > 0 && transports_[channelId] ->
+ blocked() != 1)
+ {
+ *logofs << "Proxy: PANIC! Descriptor FD#" << fd
+ << " has data to write but blocked flag is "
+ << transports_[channelId] -> blocked()
+ << ".\n" << logofs_flush;
+
+ cerr << "Error" << ": Descriptor FD#" << fd
+ << " has data to write but blocked flag is "
+ << transports_[channelId] -> blocked()
+ << ".\n";
+
+ HandleCleanup();
+ }
+
+ #endif
+ }
+ }
+
+ //
+ // Check if the proxy transport has data
+ // from a previous blocking write.
+ //
+
+ if (transport_ -> blocked() == 1)
+ {
+ FD_SET(fd_, fdSet);
+
+ #ifdef TEST
+ *logofs << "Proxy: Proxy descriptor FD#"
+ << fd_ << " selected for write. Blocked is "
+ << transport_ -> blocked() << " length is "
+ << transport_ -> length() << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (fd_ >= fdMax)
+ {
+ fdMax = fd_ + 1;
+ }
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Proxy: Proxy descriptor FD#"
+ << fd_ << " not selected for write. Blocked is "
+ << transport_ -> blocked() << " length is "
+ << transport_ -> length() << ".\n"
+ << logofs_flush;
+ }
+ #endif
+
+ //
+ // We are entering the main select. Save
+ // the timestamp of the last loop so that
+ // we can detect the clock drifts.
+ //
+
+ timeouts_.loopTs = getTimestamp();
+
+ return 1;
+}
+
+int Proxy::getChannels(T_channel_type type)
+{
+ int channels = 0;
+
+ T_list &channelList = activeChannels_.getList();
+
+ for (T_list::iterator j = channelList.begin();
+ j != channelList.end(); j++)
+ {
+ int channelId = *j;
+
+ if (channels_[channelId] != NULL &&
+ (type == channel_none ||
+ type == channels_[channelId] ->
+ getType()))
+ {
+ channels++;
+ }
+ }
+
+ return channels;
+}
+
+T_channel_type Proxy::getType(int fd)
+{
+ int channelId = getChannel(fd);
+
+ if (channelId < 0 || channels_[channelId] == NULL)
+ {
+ return channel_none;
+ }
+
+ return channels_[channelId] -> getType();
+}
+
+const char *Proxy::getTypeName(T_channel_type type)
+{
+ switch (type)
+ {
+ case channel_x11:
+ {
+ return "X";
+ }
+ case channel_cups:
+ {
+ return "CUPS";
+ }
+ case channel_smb:
+ {
+ return "SMB";
+ }
+ case channel_media:
+ {
+ return "media";
+ }
+ case channel_http:
+ {
+ return "HTTP";
+ }
+ case channel_font:
+ {
+ return "font";
+ }
+ case channel_slave:
+ {
+ return "slave";
+ }
+ default:
+ {
+ return "unknown";
+ }
+ }
+}
+
+const char *Proxy::getComputerName()
+{
+ //
+ // Strangely enough, under some Windows OSes SMB
+ // service doesn't bind to localhost. Fall back
+ // to localhost if can't find computer name in
+ // the environment. In future we should try to
+ // bind to localhost and then try the other IPs.
+ //
+
+ const char *hostname = NULL;
+
+ #ifdef __CYGWIN32__
+
+ hostname = getenv("COMPUTERNAME");
+
+ #endif
+
+ if (hostname == NULL)
+ {
+ hostname = "localhost";
+ }
+
+ return hostname;
+}
+
+//
+// Handle data from channels selected for read.
+//
+
+int Proxy::handleRead(int &resultFds, fd_set &readSet)
+{
+ #ifdef DEBUG
+ *logofs << "Proxy: Checking descriptors selected for read.\n"
+ << logofs_flush;
+ #endif
+
+ T_list &channelList = activeChannels_.getList();
+
+ for (T_list::iterator j = channelList.begin();
+ j != channelList.end(); j++)
+ {
+ #ifdef DEBUG
+ *logofs << "Proxy: Looping with current channel "
+ << *j << ".\n" << logofs_flush;
+ #endif
+
+ int fd = getFd(*j);
+
+ if (fd >= 0 && resultFds > 0 && FD_ISSET(fd, &readSet))
+ {
+ #ifdef DEBUG
+ *logofs << "Proxy: Going to read messages from FD#"
+ << fd << ".\n" << logofs_flush;
+ #endif
+
+ int result = handleRead(fd);
+
+ if (result < 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Failure reading messages from FD#"
+ << fd << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Proxy: Clearing the read descriptor "
+ << "for FD#" << fd << ".\n" << logofs_flush;
+ #endif
+
+ FD_CLR(fd, &readSet);
+
+ resultFds--;
+ }
+ }
+
+ if (resultFds > 0 && FD_ISSET(fd_, &readSet))
+ {
+ #ifdef DEBUG
+ *logofs << "Proxy: Going to read messages from "
+ << "proxy FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (handleRead() < 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Failure reading from proxy FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Proxy: Clearing the read descriptor "
+ << "for proxy FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ FD_CLR(fd_, &readSet);
+
+ resultFds--;
+ }
+
+ return 1;
+}
+
+//
+// Perform flush on descriptors selected for write.
+//
+
+int Proxy::handleFlush(int &resultFds, fd_set &writeSet)
+{
+ #ifdef DEBUG
+ *logofs << "Proxy: Checking descriptors selected for write.\n"
+ << logofs_flush;
+ #endif
+
+ if (resultFds > 0 && FD_ISSET(fd_, &writeSet))
+ {
+ #ifdef TEST
+ *logofs << "Proxy: FLUSH! Proxy descriptor FD#" << fd_
+ << " reported to be writable.\n"
+ << logofs_flush;
+ #endif
+
+ if (handleFlush() < 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Failure flushing the writable "
+ << "proxy FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Proxy: Clearing the write descriptor "
+ << "for proxy FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ FD_CLR(fd_, &writeSet);
+
+ resultFds--;
+ }
+
+ T_list &channelList = activeChannels_.getList();
+
+ for (T_list::iterator j = channelList.begin();
+ resultFds > 0 && j != channelList.end(); j++)
+ {
+ #ifdef DEBUG
+ *logofs << "Proxy: Looping with current channel "
+ << *j << ".\n" << logofs_flush;
+ #endif
+
+ int fd = getFd(*j);
+
+ if (fd >= 0 && FD_ISSET(fd, &writeSet))
+ {
+ #ifdef TEST
+ *logofs << "Proxy: X descriptor FD#" << fd
+ << " reported to be writable.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // It can happen that, in handling reads, we
+ // have destroyed the buffer associated to a
+ // closed socket, so don't complain about
+ // the errors.
+ //
+
+ handleFlush(fd);
+
+ //
+ // Clear the descriptor from the mask so
+ // we don't confuse the agent if it's
+ // not checking only its own descriptors.
+ //
+
+ #ifdef DEBUG
+ *logofs << "Proxy: Clearing the write descriptor "
+ << "for FD#" << fd << ".\n"
+ << logofs_flush;
+ #endif
+
+ FD_CLR(fd, &writeSet);
+
+ resultFds--;
+ }
+ }
+
+ return 1;
+}
+
+int Proxy::handleRead()
+{
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Decoding data from proxy FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Decode all the available messages from
+ // the remote proxy until is not possible
+ // to read more.
+ //
+
+ for (;;)
+ {
+ int result = readBuffer_.readMessage();
+
+ #if defined(TEST) || defined(DEBUG) || defined(INFO)
+ *logofs << "Proxy: Read result on proxy FD#" << fd_
+ << " is " << result << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (result < 0)
+ {
+ if (shutdown_ == 0)
+ {
+ if (finish_ == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Failure reading from the "
+ << "peer proxy on FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure reading from the "
+ << "peer proxy.\n";
+ }
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Proxy: Closure of the proxy link detected "
+ << "after clean shutdown.\n" << logofs_flush;
+ }
+ #endif
+
+ priority_ = 0;
+ finish_ = 1;
+ congestion_ = 0;
+
+ return -1;
+ }
+ else if (result == 0)
+ {
+ #if defined(TEST) || defined(DEBUG) || defined(INFO)
+ *logofs << "Proxy: No data read from proxy FD#"
+ << fd_ << "\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ //
+ // We read some data from the remote. If we set
+ // the congestion flag because we couldn't read
+ // before the timeout and have tokens available,
+ // then reset the congestion flag.
+ //
+
+ if (congestion_ == 1 &&
+ tokens_[token_control].remaining > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Exiting congestion due to "
+ << "proxy data with " << tokens_[token_control].remaining
+ << " tokens.\n" << logofs_flush;
+ #endif
+
+ congestion_ = 0;
+ }
+
+ //
+ // Set the timestamp of the last read
+ // operation from the remote proxy and
+ // enable again showing the 'no data
+ // received' dialog at the next timeout.
+ //
+
+ timeouts_.readTs = getTimestamp();
+
+ if (alert_ != 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Displacing the dialog "
+ << "for proxy FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ HandleAlert(DISPLACE_MESSAGE_ALERT, 1);
+ }
+
+ timeouts_.alertTs = nullTimestamp();
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Getting messages from proxy FD#" << fd_
+ << " with " << readBuffer_.getLength() << " bytes "
+ << "in the read buffer.\n" << logofs_flush;
+ #endif
+
+ unsigned int controlLength;
+ unsigned int dataLength;
+
+ const unsigned char *message;
+
+ while ((message = readBuffer_.getMessage(controlLength, dataLength)) != NULL)
+ {
+ statistics -> addFrameIn();
+
+ if (controlLength == 3 && *message == 0 &&
+ *(message + 1) < code_last_tag)
+ {
+ if (handleControlFromProxy(message) < 0)
+ {
+ return -1;
+ }
+ }
+ else if (operation_ == operation_in_messages)
+ {
+ int channelId = inputChannel_;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Identified message of " << dataLength
+ << " bytes for FD#" << getFd(channelId) << " channel ID#"
+ << channelId << ".\n" << logofs_flush;
+ #endif
+
+ if (channelId >= 0 && channelId < CONNECTIONS_LIMIT &&
+ channels_[channelId] != NULL)
+ {
+ int finish = channels_[channelId] -> getFinish();
+
+ #ifdef WARNING
+
+ if (finish == 1)
+ {
+ *logofs << "Proxy: WARNING! Handling data for finishing "
+ << "FD#" << getFd(channelId) << " channel ID#"
+ << channelId << ".\n" << logofs_flush;
+ }
+
+ #endif
+
+ //
+ // We need to decode all the data to preserve
+ // the consistency of the cache, so can't re-
+ // turn as soon as the first error is encount-
+ // ered. Check if this is the first time that
+ // the failure is detected.
+ //
+
+ int result = channels_[channelId] -> handleWrite(message, dataLength);
+
+ if (result < 0 && finish == 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Failed to write proxy data to FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << ".\n" << logofs_flush;
+ #endif
+
+ if (handleFinish(channelId) < 0)
+ {
+ return -1;
+ }
+ }
+
+ //
+ // Check if we have splits or motion
+ // events to send.
+ //
+
+ setSplitTimeout(channelId);
+ setMotionTimeout(channelId);
+ }
+ #ifdef WARNING
+ else
+ {
+ *logofs << "Proxy: WARNING! Received data for "
+ << "invalid channel ID#" << channelId
+ << ".\n" << logofs_flush;
+ }
+ #endif
+ }
+ else if (operation_ == operation_in_statistics)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Received statistics data from remote proxy.\n"
+ << logofs_flush;
+ #endif
+
+ if (handleStatisticsFromProxy(message, dataLength) < 0)
+ {
+ return -1;
+ }
+
+ operation_ = operation_in_messages;
+ }
+ else if (operation_ == operation_in_negotiation)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Received new negotiation data from remote proxy.\n"
+ << logofs_flush;
+ #endif
+
+ if (handleNegotiationFromProxy(message, dataLength) < 0)
+ {
+ return -1;
+ }
+ }
+
+ //
+ // if (controlLength == 3 && *message == 0 && ...) ...
+ // else if (operation_ == operation_in_statistics) ...
+ // else if (operation_ == operation_in_messages) ...
+ // else if (operation_ == operation_in_negotiation) ...
+ // else ...
+ //
+
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Unrecognized message received on proxy FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Unrecognized message received on proxy FD#"
+ << fd_ << ".\n";
+
+ return -1;
+ }
+
+ } // while ((message = readBuffer_.getMessage(controlLength, dataLength)) != NULL) ...
+
+ //
+ // Reset the read buffer.
+ //
+
+ readBuffer_.fullReset();
+
+ //
+ // Give up if no data is readable.
+ //
+
+ if (transport_ -> readable() == 0)
+ {
+ break;
+ }
+
+ } // End of for (;;) ...
+
+ return 1;
+}
+
+int Proxy::handleControlFromProxy(const unsigned char *message)
+{
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Received message '" << DumpControl(*(message + 1))
+ << "' at " << strMsTimestamp() << " with data ID#"
+ << (int) *(message + 2) << ".\n" << logofs_flush;
+ #endif
+
+ T_channel_type channelType = channel_none;
+
+ switch (*(message + 1))
+ {
+ case code_switch_connection:
+ {
+ int channelId = *(message + 2);
+
+ //
+ // If channel is invalid further messages will
+ // be ignored. The acknowledged shutdown of
+ // channels should prevent this.
+ //
+
+ inputChannel_ = channelId;
+
+ break;
+ }
+ case code_begin_congestion:
+ {
+ //
+ // Set the congestion state for the
+ // channel reported by the remote.
+ //
+
+ int channelId = *(message + 2);
+
+ if (channels_[channelId] != NULL)
+ {
+ congestions_[channelId] = 1;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Received a begin congestion "
+ << "for channel id ID#" << channelId
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (channelId == agent_ && congestions_[agent_] != 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Forcing an update of the congestion "
+ << "counter with agent congested.\n"
+ << logofs_flush;
+ #endif
+
+ statistics -> updateCongestion(-tokens_[token_control].remaining,
+ tokens_[token_control].limit);
+ }
+ }
+ #ifdef WARNING
+ else
+ {
+ *logofs << "Proxy: WARNING! Received a begin congestion "
+ << "for invalid channel id ID#" << channelId
+ << ".\n" << logofs_flush;
+ }
+ #endif
+
+ break;
+ }
+ case code_end_congestion:
+ {
+ //
+ // Attend again to the channel.
+ //
+
+ int channelId = *(message + 2);
+
+ if (channels_[channelId] != NULL)
+ {
+ congestions_[channelId] = 0;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Received an end congestion "
+ << "for channel id ID#" << channelId
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (channelId == agent_ && congestions_[agent_] != 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Forcing an update of the congestion "
+ << "counter with agent decongested.\n"
+ << logofs_flush;
+ #endif
+
+ statistics -> updateCongestion(tokens_[token_control].remaining,
+ tokens_[token_control].limit);
+ }
+ }
+ #ifdef WARNING
+ else
+ {
+ *logofs << "Proxy: WARNING! Received an end congestion "
+ << "for invalid channel id ID#" << channelId
+ << ".\n" << logofs_flush;
+ }
+ #endif
+
+ break;
+ }
+ case code_control_token_request:
+ {
+ T_proxy_token &token = tokens_[token_control];
+
+ if (handleTokenFromProxy(token, *(message + 2)) < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ case code_split_token_request:
+ {
+ T_proxy_token &token = tokens_[token_split];
+
+ if (handleTokenFromProxy(token, *(message + 2)) < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ case code_data_token_request:
+ {
+ T_proxy_token &token = tokens_[token_data];
+
+ if (handleTokenFromProxy(token, *(message + 2)) < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ case code_control_token_reply:
+ {
+ T_proxy_token &token = tokens_[token_control];
+
+ if (handleTokenReplyFromProxy(token, *(message + 2)) < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ case code_split_token_reply:
+ {
+ T_proxy_token &token = tokens_[token_split];
+
+ if (handleTokenReplyFromProxy(token, *(message + 2)) < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ case code_data_token_reply:
+ {
+ T_proxy_token &token = tokens_[token_data];
+
+ if (handleTokenReplyFromProxy(token, *(message + 2)) < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ case code_new_x_connection:
+ {
+ //
+ // Opening the channel is handled later.
+ //
+
+ channelType = channel_x11;
+
+ break;
+ }
+ case code_new_cups_connection:
+ {
+ channelType = channel_cups;
+
+ break;
+ }
+ case code_new_aux_connection:
+ {
+ //
+ // Starting from version 1.5.0 we create real X
+ // connections for the keyboard channel. We need
+ // to refuse old auxiliary X connections because
+ // they would be unable to leverage the new fake
+ // authorization cookie.
+ //
+
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Can't open outdated auxiliary X "
+ << "channel for code " << *(message + 1) << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Can't open outdated auxiliary X "
+ << "channel for code " << *(message + 1) << ".\n";
+
+ if (handleControl(code_drop_connection, *(message + 2)) < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ case code_new_smb_connection:
+ {
+ channelType = channel_smb;
+
+ break;
+ }
+ case code_new_media_connection:
+ {
+ channelType = channel_media;
+
+ break;
+ }
+ case code_new_http_connection:
+ {
+ channelType = channel_http;
+
+ break;
+ }
+ case code_new_font_connection:
+ {
+ channelType = channel_font;
+
+ break;
+ }
+ case code_new_slave_connection:
+ {
+ channelType = channel_slave;
+
+ break;
+ }
+ case code_drop_connection:
+ {
+ int channelId = *(message + 2);
+
+ if (channelId >= 0 && channelId < CONNECTIONS_LIMIT &&
+ channels_[channelId] != NULL)
+ {
+ handleDropFromProxy(channelId);
+ }
+ #ifdef WARNING
+ else
+ {
+ *logofs << "Proxy: WARNING! Received a drop message "
+ << "for invalid channel id ID#" << channelId
+ << ".\n" << logofs_flush;
+ }
+ #endif
+
+ break;
+ }
+ case code_finish_connection:
+ {
+ int channelId = *(message + 2);
+
+ if (channelId >= 0 && channelId < CONNECTIONS_LIMIT &&
+ channels_[channelId] != NULL)
+ {
+ //
+ // Force the finish state on the channel.
+ // We can receive this message while in
+ // the read loop, so we only mark the
+ // channel for deletion.
+ //
+
+ #ifdef TEST
+ *logofs << "Proxy: Received a finish message for FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << ".\n" << logofs_flush;
+ #endif
+
+ handleFinishFromProxy(channelId);
+ }
+ #ifdef WARNING
+ else
+ {
+ *logofs << "Proxy: WARNING! Received a finish message "
+ << "for invalid channel id ID#" << channelId
+ << ".\n" << logofs_flush;
+ }
+ #endif
+
+ break;
+ }
+ case code_finish_listeners:
+ {
+ //
+ // This is from the main loop.
+ //
+
+ #ifdef TEST
+ *logofs << "Proxy: Closing down all local listeners.\n"
+ << logofs_flush;
+ #endif
+
+ CleanupListeners();
+
+ finish_ = 1;
+
+ break;
+ }
+ case code_reset_request:
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Proxy reset not supported "
+ << "in this version.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Proxy reset not supported "
+ << "in this version.\n";
+
+ HandleCleanup();
+ }
+ case code_shutdown_request:
+ {
+ //
+ // Time to rest in peace.
+ //
+
+ shutdown_ = 1;
+
+ break;
+ }
+ case code_load_request:
+ {
+ if (handleLoadFromProxy() < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ case code_save_request:
+ {
+ //
+ // Don't abort the connection
+ // if can't write to disk.
+ //
+
+ handleSaveFromProxy();
+
+ break;
+ }
+ case code_statistics_request:
+ {
+ int type = *(message + 2);
+
+ if (handleStatisticsFromProxy(type) < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ case code_statistics_reply:
+ {
+ operation_ = operation_in_statistics;
+
+ break;
+ }
+ case code_alert_request:
+ {
+ HandleAlert(*(message + 2), 1);
+
+ break;
+ }
+ case code_sync_request:
+ {
+ int channelId = *(message + 2);
+
+ if (handleSyncFromProxy(channelId) < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ case code_sync_reply:
+ {
+ //
+ // We are not the one that issued
+ // the request.
+ //
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: PANIC! Received an unexpected "
+ << "synchronization reply.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Received an unexpected "
+ << "synchronization reply.\n";
+
+ HandleCleanup();
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Received bad control message number "
+ << (unsigned int) *(message + 1) << " with attribute "
+ << (unsigned int) *(message + 2) << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Received bad control message number "
+ << (unsigned int) *(message + 1) << " with attribute "
+ << (unsigned int) *(message + 2) << ".\n";
+
+ HandleCleanup();
+ }
+
+ } // End of switch (*(message + 1)) ...
+
+ if (channelType == channel_none)
+ {
+ return 1;
+ }
+
+ //
+ // Handle the channel allocation that we
+ // left from the main switch case.
+ //
+
+ int channelId = *(message + 2);
+
+ //
+ // Check if the channel has been dropped.
+ //
+
+ if (channels_[channelId] != NULL &&
+ (channels_[channelId] -> getDrop() == 1 ||
+ channels_[channelId] -> getClosing() == 1))
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Dropping the descriptor FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << ".\n" << logofs_flush;
+ #endif
+
+ handleDrop(channelId);
+ }
+
+ //
+ // Check if the channel is in the valid
+ // range.
+ //
+
+ int result = checkChannelMap(channelId);
+
+ if (result >= 0)
+ {
+ result = handleNewConnectionFromProxy(channelType, channelId);
+ }
+
+ if (result < 0)
+ {
+ //
+ // Realization of new channel failed.
+ // Send channel shutdown message to
+ // the peer proxy.
+ //
+
+ if (handleControl(code_drop_connection, channelId) < 0)
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ int fd = getFd(channelId);
+
+ if (getReadable(fd) > 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Trying to read immediately "
+ << "from descriptor FD#" << fd << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (handleRead(fd) < 0)
+ {
+ return -1;
+ }
+ }
+ #ifdef TEST
+ *logofs << "Proxy: Nothing to read immediately "
+ << "from descriptor FD#" << fd << ".\n"
+ << logofs_flush;
+ #endif
+ }
+
+ return 1;
+}
+
+int Proxy::handleRead(int fd, const char *data, int size)
+{
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Handling data for connection on FD#"
+ << fd << ".\n" << logofs_flush;
+ #endif
+
+ if (canRead(fd) == 0)
+ {
+ #if defined(TEST) || defined(INFO)
+
+ if (getChannel(fd) < 0)
+ {
+ *logofs << "Proxy: PANIC! Can't read from invalid FD#"
+ << fd << ".\n" << logofs_flush;
+
+ HandleCleanup();
+ }
+ else
+ {
+ *logofs << "Proxy: WARNING! Read method called for FD#"
+ << fd << " but operation is not possible.\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ return 0;
+ }
+
+ int channelId = getChannel(fd);
+
+ //
+ // Let the channel object read all the new data from
+ // its file descriptor, isolate messages, compress
+ // those messages, and append the compressed form to
+ // the encode buffer.
+ //
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Reading messages from FD#" << fd
+ << " channel ID#" << channelId << ".\n"
+ << logofs_flush;
+ #endif
+
+ int result = channels_[channelId] -> handleRead(encodeBuffer_, (const unsigned char *) data,
+ (unsigned int) size);
+
+ //
+ // Even in the case of a failure, write the produced
+ // data to the proxy connection. To keep the stores
+ // synchronized, the remote side needs to decode any
+ // message encoded by this side, also if the X socket
+ // was closed in the meanwhile. If this is the case,
+ // the decompressed output will be silently discarded.
+ //
+
+ if (result < 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Failed to read data from connection FD#"
+ << fd << " channel ID#" << channelId << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (handleFinish(channelId) < 0)
+ {
+ return -1;
+ }
+ }
+
+ //
+ // Check if there are new splits or
+ // motion events to send.
+ //
+
+ setSplitTimeout(channelId);
+ setMotionTimeout(channelId);
+
+ return 1;
+}
+
+int Proxy::handleEvents()
+{
+ #ifdef TEST
+ *logofs << "Proxy: Going to check the events on channels.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Check if we can safely write to the
+ // proxy link.
+ //
+
+ int read = isTimeToRead();
+
+ //
+ // Loop on channels and send the pending
+ // events. We must copy the list because
+ // channels can be removed in the middle
+ // of the loop.
+ //
+
+ T_list channelList = activeChannels_.copyList();
+
+ for (T_list::iterator j = channelList.begin();
+ j != channelList.end(); j++)
+ {
+ int channelId = *j;
+
+ if (channels_[channelId] == NULL)
+ {
+ continue;
+ }
+
+ //
+ // Check if we need to drop the channel.
+ //
+
+ if (channels_[channelId] -> getDrop() == 1 ||
+ channels_[channelId] -> getClosing() == 1)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Dropping the descriptor FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << ".\n" << logofs_flush;
+ #endif
+
+ if (handleDrop(channelId) < 0)
+ {
+ return -1;
+ }
+
+ continue;
+ }
+ else if (channels_[channelId] -> getFinish() == 1)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Skipping finishing "
+ << "descriptor FD#" << getFd(channelId)
+ << " channel ID#" << channelId << ".\n"
+ << logofs_flush;
+ #endif
+
+ continue;
+ }
+
+ //
+ // If the proxy link or the channel is
+ // in congestion state, don't handle
+ // the further events.
+ //
+
+ if (read == 0 || congestions_[channelId] == 1)
+ {
+ #ifdef TEST
+
+ if (read == 0)
+ {
+ *logofs << "Proxy: Can't handle events for FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << " with proxy not available.\n"
+ << logofs_flush;
+ }
+ else
+ {
+ *logofs << "Proxy: Can't handle events for FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << " with channel congested.\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ continue;
+ }
+
+ //
+ // Handle the timeouts on the channel
+ // operations.
+ //
+
+ int result = 0;
+
+ //
+ // Handle the motion events.
+ //
+
+ if (result >= 0 && channels_[channelId] -> needMotion() == 1)
+ {
+ if (isTimeToMotion() == 1)
+ {
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+
+ *logofs << "Proxy: FLUSH! Motion timeout expired after "
+ << diffTimestamp(timeouts_.motionTs, getTimestamp())
+ << " Ms.\n" << logofs_flush;
+
+ #endif
+
+ result = channels_[channelId] -> handleMotion(encodeBuffer_);
+
+ #ifdef TEST
+
+ if (result < 0)
+ {
+ *logofs << "Proxy: Failed to handle motion events for FD#"
+ << getFd(channelId) << " channel ID#" << channelId
+ << ".\n" << logofs_flush;
+ }
+
+ #endif
+
+ timeouts_.motionTs = nullTimestamp();
+
+ setMotionTimeout(channelId);
+ }
+ #if defined(TEST) || defined(INFO)
+ else if (isTimestamp(timeouts_.motionTs) == 1)
+ {
+ *logofs << "Proxy: Running with "
+ << diffTimestamp(timeouts_.motionTs, getTimestamp())
+ << " Ms elapsed since the last motion.\n"
+ << logofs_flush;
+ }
+ #endif
+ }
+
+ if (result >= 0 && channels_[channelId] -> needSplit() == 1)
+ {
+ //
+ // Check if it is time to send more splits
+ // and how many bytes are going to be sent.
+ //
+
+ if (isTimeToSplit() == 1)
+ {
+ #if defined(TEST) || defined(INFO) || defined(SPLIT)
+ *logofs << "Proxy: SPLIT! Split timeout expired after "
+ << diffTimestamp(timeouts_.splitTs, getTimestamp())
+ << " Ms.\n" << logofs_flush;
+ #endif
+
+ #if defined(TEST) || defined(INFO) || defined(SPLIT)
+
+ *logofs << "Proxy: SPLIT! Encoding splits for FD#"
+ << getFd(channelId) << " at " << strMsTimestamp()
+ << " with " << clientStore_ -> getSplitTotalStorageSize()
+ << " total bytes and " << control -> SplitDataPacketLimit
+ << " bytes " << "to write.\n"
+ << logofs_flush;
+
+ #endif
+
+ result = channels_[channelId] -> handleSplit(encodeBuffer_);
+
+ #ifdef TEST
+
+ if (result < 0)
+ {
+ *logofs << "Proxy: Failed to handle splits for FD#"
+ << getFd(channelId) << " channel ID#" << channelId
+ << ".\n" << logofs_flush;
+ }
+
+ #endif
+
+ timeouts_.splitTs = nullTimestamp();
+
+ setSplitTimeout(channelId);
+ }
+ #if defined(TEST) || defined(INFO) || defined(SPLIT)
+ else if (channels_[channelId] -> needSplit() == 1 &&
+ isTimestamp(timeouts_.splitTs) == 0)
+ {
+ *logofs << "Proxy: SPLIT! WARNING! Channel for FD#"
+ << getFd(channelId) << " has split to send but "
+ << "there is no timeout.\n" << logofs_flush;
+ }
+ else if (isTimestamp(timeouts_.splitTs) == 1)
+ {
+ *logofs << "Proxy: SPLIT! Running with "
+ << diffTimestamp(timeouts_.splitTs, getTimestamp())
+ << " Ms elapsed since the last split.\n"
+ << logofs_flush;
+ }
+ #endif
+ }
+
+ if (result < 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Error handling events for FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << ".\n" << logofs_flush;
+ #endif
+
+ if (handleFinish(channelId) < 0)
+ {
+ return -1;
+ }
+ }
+ }
+
+ return 1;
+}
+
+int Proxy::handleFrame(T_frame_type type)
+{
+ //
+ // Write any outstanding control message, followed by the
+ // content of the encode buffer, to the proxy transport.
+ //
+ // This code assumes that the encode buffer data is at an
+ // offset several bytes from start of the buffer, so that
+ // the length header and any necessary control bytes can
+ // be inserted in front of the data already in the buffer.
+ // This is the easiest way to encapsulate header and data
+ // together in a single frame.
+ //
+ // The way framing is implemented is very efficient but
+ // inherently limited and does not allow for getting the
+ // best performance, especially when running over a fast
+ // link. Framing should be rewritten to include the length
+ // of the packets in a fixed size header and, possibly,
+ // to incapsulate the control messages and the channel's
+ // data in a pseudo X protocol message, so that the proxy
+ // itself would be treated like any other channel.
+ //
+
+ #if defined(TEST) || defined(INFO)
+
+ if (congestion_ == 1)
+ {
+ //
+ // This can happen because there may be control
+ // messages to send, like a proxy shutdown mes-
+ // sage or a statistics request. All the other
+ // cases should be considered an error.
+ //
+
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Data is to be sent while "
+ << "congestion is " << congestion_ << ".\n"
+ << logofs_flush;
+ #endif
+ }
+
+ #endif
+
+ //
+ // Check if there is any data available on
+ // the socket. Recent Linux kernels are very
+ // picky. They require that we read often or
+ // they assume that the process is non-inter-
+ // active.
+ //
+
+ if (handleAsyncEvents() < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Check if this is a ping, not a data frame.
+ //
+
+ if (type == frame_ping)
+ {
+ if (handleToken(frame_ping) < 0)
+ {
+ return -1;
+ }
+ }
+
+ unsigned int dataLength = encodeBuffer_.getLength();
+
+ #ifdef DEBUG
+ *logofs << "Proxy: Data length is " << dataLength
+ << " control length is " << controlLength_
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (dataLength > 0)
+ {
+ //
+ // If this is a generic channel we need
+ // to add the completion bits. Data can
+ // also have been encoded because of a
+ // statistics request, even if no output
+ // channel was currently selected.
+ //
+
+ if (outputChannel_ != -1)
+ {
+ #if defined(TEST) || defined(INFO)
+
+ if (channels_[outputChannel_] == NULL)
+ {
+ *logofs << "Proxy: PANIC! A new frame was requested "
+ << "but the channel is invalid.\n"
+ << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ channels_[outputChannel_] -> handleCompletion(encodeBuffer_);
+
+ dataLength = encodeBuffer_.getLength();
+ }
+ }
+ else if (controlLength_ == 0)
+ {
+ #if defined(TEST) || defined(INFO)
+
+ *logofs << "Proxy: PANIC! A new frame was requested "
+ << "but there is no data to write.\n"
+ << logofs_flush;
+
+ HandleCleanup();
+
+ #endif
+
+ return 0;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Proxy: Data length is now " << dataLength
+ << " control length is " << controlLength_
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Check if this frame needs to carry a new
+ // token request.
+ //
+
+ if (type == frame_data)
+ {
+ if (handleToken(frame_data) < 0)
+ {
+ return -1;
+ }
+ }
+
+ #ifdef DEBUG
+ *logofs << "Proxy: Adding a new frame for the remote proxy.\n"
+ << logofs_flush;
+ #endif
+
+ unsigned char temp[5];
+
+ unsigned int lengthLength = 0;
+ unsigned int shift = dataLength;
+
+ while (shift)
+ {
+ temp[lengthLength++] = (unsigned char) (shift & 0x7f);
+
+ shift >>= 7;
+ }
+
+ unsigned char *data = encodeBuffer_.getData();
+
+ unsigned char *outputMessage = data - (controlLength_ + lengthLength);
+
+ unsigned char *nextDest = outputMessage;
+
+ for (int i = 0; i < controlLength_; i++)
+ {
+ *nextDest++ = controlCodes_[i];
+ }
+
+ for (int j = lengthLength - 1; j > 0; j--)
+ {
+ *nextDest++ = (temp[j] | 0x80);
+ }
+
+ if (lengthLength)
+ {
+ *nextDest++ = temp[0];
+ }
+
+ unsigned int outputLength = dataLength + controlLength_ + lengthLength;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Produced plain output for " << dataLength << "+"
+ << controlLength_ << "+" << lengthLength << " out of "
+ << outputLength << " bytes.\n" << logofs_flush;
+ #endif
+
+ #if defined(TEST) || defined(INFO) || defined(FLUSH) || defined(TIME)
+
+ T_timestamp nowTs = getTimestamp();
+
+ *logofs << "Proxy: FLUSH! Immediate with blocked " << transport_ ->
+ blocked() << " length " << transport_ -> length()
+ << " new " << outputLength << " flushable " << transport_ ->
+ flushable() << " tokens " << tokens_[token_control].remaining
+ << " after " << diffTimestamp(timeouts_.writeTs, nowTs)
+ << " Ms.\n" << logofs_flush;
+
+ *logofs << "Proxy: FLUSH! Immediate flush to proxy FD#" << fd_
+ << " of " << outputLength << " bytes at " << strMsTimestamp()
+ << " with priority " << priority_ << ".\n" << logofs_flush;
+
+ *logofs << "Proxy: FLUSH! Current bitrate is "
+ << statistics -> getBitrateInShortFrame() << " with "
+ << statistics -> getBitrateInLongFrame() << " in the "
+ << "long frame and top " << statistics ->
+ getTopBitrate() << ".\n" << logofs_flush;
+ #endif
+
+ statistics -> addWriteOut();
+
+ int result = transport_ -> write(write_immediate, outputMessage, outputLength);
+
+ #ifdef TIME
+
+ if (diffTimestamp(timeouts_.writeTs, nowTs) > 50)
+ {
+ *logofs << "Proxy: WARNING! TIME! Data written to proxy FD#"
+ << fd_ << " at " << strMsTimestamp() << " after "
+ << diffTimestamp(timeouts_.writeTs, nowTs)
+ << " Ms.\n" << logofs_flush;
+ }
+
+ #endif
+
+ #ifdef DUMP
+ *logofs << "Proxy: Sent " << outputLength << " bytes of data "
+ << "with checksum ";
+
+ DumpChecksum(outputMessage, outputLength);
+
+ *logofs << " on proxy FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef DUMP
+ *logofs << "Proxy: Partial checksums are:\n";
+
+ DumpBlockChecksums(outputMessage, outputLength, 256);
+
+ *logofs << logofs_flush;
+ #endif
+
+ //
+ // Clean up the encode buffer and
+ // bring it to the initial size.
+ //
+
+ encodeBuffer_.fullReset();
+
+ //
+ // Close the connection if we got
+ // an error.
+ //
+
+ if (result < 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Failed write to proxy FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ //
+ // Account for the data frame and the
+ // framing overhead.
+ //
+
+ if (dataLength > 0)
+ {
+ statistics -> addFrameOut();
+ }
+
+ statistics -> addFramingBits((controlLength_ + lengthLength) << 3);
+
+ controlLength_ = 0;
+
+ //
+ // Reset all buffers, counters and the
+ // priority flag.
+ //
+
+ handleResetFlush();
+
+ //
+ // Check if more data became available
+ // after writing.
+ //
+
+ if (handleAsyncEvents() < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Drain the proxy link if we are in
+ // congestion state.
+ //
+ // if (needDrain() == 1 && draining_ == 0)
+ // {
+ // if (handleDrain() < 0)
+ // {
+ // return -1;
+ // }
+ // }
+ //
+
+ return result;
+}
+
+int Proxy::handleFlush()
+{
+ //
+ // We can have data in the encode buffer or
+ // control bytes to send. In the case make
+ // up a new frame.
+ //
+
+ if (encodeBuffer_.getLength() + controlLength_ > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Flushing data in the encode buffer.\n"
+ << logofs_flush;
+ #endif
+
+ priority_ = 1;
+
+ if (handleFrame(frame_data) < 0)
+ {
+ return -1;
+ }
+ }
+
+ //
+ // Check if we have something to write.
+ //
+
+ if (transport_ -> length() + transport_ -> flushable() == 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Nothing else to flush for proxy FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ #if defined(TEST) || defined(INFO)
+
+ if (transport_ -> blocked() == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Proxy descriptor FD#" << fd_
+ << " has data to flush but the transport "
+ << "is not blocked.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Proxy descriptor FD#" << fd_
+ << " has data to flush but the transport "
+ << "is not blocked.\n";
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+ *logofs << "Proxy: FLUSH! Deferred with blocked " << transport_ ->
+ blocked() << " length " << transport_ -> length()
+ << " flushable " << transport_ -> flushable() << " tokens "
+ << tokens_[token_control].remaining << ".\n"
+ << logofs_flush;
+
+ *logofs << "Proxy: FLUSH! Deferred flush to proxy FD#" << fd_
+ << " of " << transport_ -> length() + transport_ ->
+ flushable() << " bytes at " << strMsTimestamp()
+ << " with priority " << priority_ << ".\n"
+ << logofs_flush;
+
+ *logofs << "Proxy: FLUSH! Current bitrate is "
+ << statistics -> getBitrateInShortFrame() << " with "
+ << statistics -> getBitrateInLongFrame() << " in the "
+ << "long frame and top " << statistics ->
+ getTopBitrate() << ".\n" << logofs_flush;
+ #endif
+
+ statistics -> addWriteOut();
+
+ int result = transport_ -> flush();
+
+ if (result < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Reset the counters and update the
+ // timestamp of the last write.
+ //
+
+ handleResetFlush();
+
+ return result;
+}
+
+int Proxy::handleDrain()
+{
+ //
+ // If the proxy is run in the same process
+ // as SSH, we can't block or the program
+ // would not have a chance to read or write
+ // its data.
+ //
+
+ if (control -> LinkEncrypted == 1)
+ {
+ return 0;
+ }
+
+ if (needDrain() == 0 || draining_ == 1)
+ {
+ #if defined(TEST) || defined(INFO)
+
+ if (draining_ == 1)
+ {
+ *logofs << "Proxy: WARNING! Already draining proxy FD#"
+ << fd_ << " at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ }
+ else
+ {
+ *logofs << "Proxy: WARNING! No need to drain proxy FD#"
+ << fd_ << " with congestion " << congestion_
+ << " length " << transport_ -> length()
+ << " and blocked " << transport_ -> blocked()
+ << ".\n" << logofs_flush;
+ }
+
+ #endif
+
+ return 0;
+ }
+
+ draining_ = 1;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Going to drain the proxy FD#" << fd_
+ << " at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ int timeout = control -> PingTimeout / 2;
+
+ T_timestamp startTs = getNewTimestamp();
+
+ T_timestamp nowTs = startTs;
+
+ int remaining;
+ int result;
+
+ //
+ // Keep draining the proxy socket while
+ // reading the incoming messages until
+ // the timeout is expired.
+ //
+
+ for (;;)
+ {
+ remaining = timeout - diffTimestamp(startTs, nowTs);
+
+ if (remaining <= 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Timeout raised while draining "
+ << "FD#" << fd_ << " at " << strMsTimestamp()
+ << " after " << diffTimestamp(startTs, nowTs)
+ << " Ms.\n" << logofs_flush;
+ #endif
+
+ result = 0;
+
+ goto ProxyDrainEnd;
+ }
+
+ if (transport_ -> length() > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Trying to write to FD#" << fd_
+ << " at " << strMsTimestamp() << " with length "
+ << transport_ -> length() << " and "
+ << remaining << " Ms remaining.\n"
+ << logofs_flush;
+ #endif
+
+ result = transport_ -> drain(0, remaining);
+
+ if (result == -1)
+ {
+ result = -1;
+
+ goto ProxyDrainEnd;
+ }
+ else if (result == 0 && transport_ -> readable() > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Decoding more data from proxy FD#"
+ << fd_ << " at " << strMsTimestamp() << " with "
+ << transport_ -> length() << " bytes to write and "
+ << transport_ -> readable() << " readable.\n"
+ << logofs_flush;
+ #endif
+
+ if (handleRead() < 0)
+ {
+ result = -1;
+
+ goto ProxyDrainEnd;
+ }
+ }
+ #if defined(TEST) || defined(INFO)
+ else if (result == 1)
+ {
+ *logofs << "Proxy: Transport for proxy FD#" << fd_
+ << " drained down to " << transport_ -> length()
+ << " bytes.\n" << logofs_flush;
+ }
+ #endif
+ }
+ else
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Waiting for more data from proxy "
+ << "FD#" << fd_ << " at " << strMsTimestamp()
+ << " with " << remaining << " Ms remaining.\n"
+ << logofs_flush;
+ #endif
+
+
+ result = transport_ -> wait(remaining);
+
+ if (result == -1)
+ {
+ result = -1;
+
+ goto ProxyDrainEnd;
+ }
+ else if (result > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Decoding more data from proxy FD#"
+ << fd_ << " at " << strMsTimestamp() << " with "
+ << transport_ -> readable() << " bytes readable.\n"
+ << logofs_flush;
+ #endif
+
+ if (handleRead() < 0)
+ {
+ result = -1;
+
+ goto ProxyDrainEnd;
+ }
+ }
+ }
+
+ //
+ // Check if we finally got the tokens
+ // that would allow us to come out of
+ // the congestion state.
+ //
+
+ if (needDrain() == 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Got decongestion for proxy FD#"
+ << fd_ << " at " << strMsTimestamp() << " after "
+ << diffTimestamp(startTs, getTimestamp())
+ << " Ms.\n" << logofs_flush;
+ #endif
+
+ result = 1;
+
+ goto ProxyDrainEnd;
+ }
+
+ nowTs = getNewTimestamp();
+ }
+
+ProxyDrainEnd:
+
+ draining_ = 0;
+
+ return result;
+}
+
+int Proxy::handleFlush(int fd)
+{
+ int channelId = getChannel(fd);
+
+ if (channelId < 0 || channels_[channelId] == NULL)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: WARNING! Skipping flush on invalid "
+ << "descriptor FD#" << fd << " channel ID#"
+ << channelId << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else if (channels_[channelId] -> getFinish() == 1)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Skipping flush on finishing "
+ << "descriptor FD#" << fd << " channel ID#"
+ << channelId << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ #ifdef TEST
+ *logofs << "Proxy: Going to flush FD#" << fd
+ << " with blocked " << transports_[channelId] -> blocked()
+ << " length " << transports_[channelId] -> length()
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (channels_[channelId] -> handleFlush() < 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Failed to flush data to FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << ".\n" << logofs_flush;
+ #endif
+
+ handleFinish(channelId);
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int Proxy::handleStatistics(int type, ostream *stream)
+{
+ if (stream == NULL || control -> EnableStatistics == 0)
+ {
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Cannot produce statistics "
+ << " for proxy FD#" << fd_ << ". Invalid settings "
+ << "for statistics or stream.\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else if (currentStatistics_ != NULL)
+ {
+ //
+ // Need to update the stream pointer as the
+ // previous one could have been destroyed.
+ //
+
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Replacing stream while producing "
+ << "statistics in stream at " << currentStatistics_
+ << " for proxy FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+ }
+
+ currentStatistics_ = stream;
+
+ //
+ // Get statistics of remote peer.
+ //
+
+ if (handleControl(code_statistics_request, type) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+int Proxy::handleStatisticsFromProxy(int type)
+{
+ if (needFlush() == 1)
+ {
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+ *logofs << "Proxy: WARNING! Data for the previous "
+ << "channel ID#" << outputChannel_
+ << " flushed in statistics.\n"
+ << logofs_flush;
+ #endif
+
+ if (handleFrame(frame_data) < 0)
+ {
+ return -1;
+ }
+ }
+
+ if (control -> EnableStatistics == 1)
+ {
+ //
+ // Allocate a buffer for the output.
+ //
+
+ char *buffer = new char[STATISTICS_LENGTH];
+
+ *buffer = '\0';
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Producing "
+ << (type == TOTAL_STATS ? "total" : "partial")
+ << " client statistics for proxy FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ statistics -> getClientProtocolStats(type, buffer);
+
+ statistics -> getClientOverallStats(type, buffer);
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Producing "
+ << (type == TOTAL_STATS ? "total" : "partial")
+ << " server statistics for proxy FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ statistics -> getServerProtocolStats(type, buffer);
+ }
+
+ if (type == PARTIAL_STATS)
+ {
+ statistics -> resetPartialStats();
+ }
+
+ unsigned int length = strlen((char *) buffer) + 1;
+
+ encodeBuffer_.encodeValue(type, 8);
+
+ encodeBuffer_.encodeValue(length, 32);
+
+ #ifdef TEST
+ *logofs << "Proxy: Encoding " << length
+ << " bytes of statistics data for proxy FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer_.encodeMemory((unsigned char *) buffer, length);
+
+ //
+ // Account statistics data as framing bits.
+ //
+
+ statistics -> addFramingBits(length << 3);
+
+ delete [] buffer;
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Got statistics request "
+ << "but local statistics are disabled.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Got statistics request "
+ << "but local statistics are disabled.\n";
+
+ type = NO_STATS;
+
+ encodeBuffer_.encodeValue(type, 8);
+
+ #ifdef TEST
+ *logofs << "Proxy: Sending error code to remote proxy on FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+ }
+
+ //
+ // The next write will flush the statistics
+ // data and the control message.
+ //
+
+ if (handleControl(code_statistics_reply, type) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+int Proxy::handleStatisticsFromProxy(const unsigned char *message, unsigned int length)
+{
+ if (currentStatistics_ == NULL)
+ {
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Unexpected statistics data received "
+ << "from remote proxy on FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Unexpected statistics data received "
+ << "from remote proxy.\n";
+
+ return 0;
+ }
+
+ //
+ // Allocate the decode buffer and at least
+ // the 'type' field to see if there was an
+ // error.
+ //
+
+ DecodeBuffer decodeBuffer(message, length);
+
+ unsigned int type;
+
+ decodeBuffer.decodeValue(type, 8);
+
+ if (type == NO_STATS)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Couldn't get statistics from remote "
+ << "proxy on FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Couldn't get statistics from remote proxy.\n";
+ }
+ else if (type != TOTAL_STATS && type != PARTIAL_STATS)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Cannot produce statistics "
+ << "with qualifier '" << type << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot produce statistics "
+ << "with qualifier '" << type << "'.\n";
+
+ return -1;
+ }
+ else
+ {
+ unsigned int size;
+
+ decodeBuffer.decodeValue(size, 32);
+
+ char *buffer = new char[STATISTICS_LENGTH];
+
+ *buffer = '\0';
+
+ if (control -> EnableStatistics == 1)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Finalizing "
+ << (type == TOTAL_STATS ? "total" : "partial")
+ << " client statistics for proxy FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ statistics -> getClientCacheStats(type, buffer);
+
+ #ifdef TEST
+ *logofs << "Proxy: Decoding " << size
+ << " bytes of statistics data for proxy FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ strncat(buffer, (char *) decodeBuffer.decodeMemory(size), size);
+
+ statistics -> getClientProtocolStats(type, buffer);
+
+ statistics -> getClientOverallStats(type, buffer);
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Finalizing "
+ << (type == TOTAL_STATS ? "total" : "partial")
+ << " server statistics for proxy FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ statistics -> getServerCacheStats(type, buffer);
+
+ statistics -> getServerProtocolStats(type, buffer);
+
+ #ifdef TEST
+ *logofs << "Proxy: Decoding " << size
+ << " bytes of statistics data for proxy FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ strncat(buffer, (char *) decodeBuffer.decodeMemory(size), size);
+ }
+
+ if (type == PARTIAL_STATS)
+ {
+ statistics -> resetPartialStats();
+ }
+
+ *currentStatistics_ << buffer;
+
+ //
+ // Mark the end of text to help external parsing.
+ //
+
+ *currentStatistics_ << '\4';
+
+ *currentStatistics_ << flush;
+ }
+ else
+ {
+ //
+ // It can be that statistics were enabled at the time
+ // we issued the request (otherwise we could not have
+ // set the stream), but now they have been disabled
+ // by user. We must decode statistics data if we want
+ // to keep the connection.
+ //
+
+ #ifdef TEST
+ *logofs << "Proxy: Discarding " << size
+ << " bytes of statistics data for proxy FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ strncat(buffer, (char *) decodeBuffer.decodeMemory(size), size);
+ }
+
+ delete [] buffer;
+ }
+
+ currentStatistics_ = NULL;
+
+ return 1;
+}
+
+int Proxy::handleNegotiation(const unsigned char *message, unsigned int length)
+{
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Writing data during proxy "
+ << "negotiation is not implemented.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Writing data during proxy "
+ << "negotiation is not implemented.\n";
+
+ return -1;
+}
+
+int Proxy::handleNegotiationFromProxy(const unsigned char *message, unsigned int length)
+{
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Reading data during proxy "
+ << "negotiation is not implemented.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Reading data during proxy "
+ << "negotiation is not implemented.\n";
+
+ return -1;
+}
+
+int Proxy::handleAlert(int alert)
+{
+ if (handleControl(code_alert_request, alert) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+int Proxy::handleCloseConnection(int clientFd)
+{
+ int channelId = getChannel(clientFd);
+
+ if (channels_[channelId] != NULL &&
+ channels_[channelId] -> getFinish() == 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Closing down the channel for FD#"
+ << clientFd << ".\n" << logofs_flush;
+ #endif
+
+ if (handleFinish(channelId) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+int Proxy::handleCloseAllXConnections()
+{
+ #ifdef TEST
+ *logofs << "Proxy: Closing down any remaining X channel.\n"
+ << logofs_flush;
+ #endif
+
+ T_list &channelList = activeChannels_.getList();
+
+ for (T_list::iterator j = channelList.begin();
+ j != channelList.end(); j++)
+ {
+ int channelId = *j;
+
+ if (channels_[channelId] != NULL &&
+ channels_[channelId] -> getType() == channel_x11 &&
+ channels_[channelId] -> getFinish() == 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Closing down the channel for FD#"
+ << getFd(channelId) << ".\n" << logofs_flush;
+ #endif
+
+ if (handleFinish(channelId) < 0)
+ {
+ return -1;
+ }
+ }
+ }
+
+ return 1;
+}
+
+int Proxy::handleCloseAllListeners()
+{
+ if (control -> isProtoStep7() == 1)
+ {
+ if (finish_ == 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Closing down all remote listeners.\n"
+ << logofs_flush;
+ #endif
+
+ if (handleControl(code_finish_listeners) < 0)
+ {
+ return -1;
+ }
+
+ finish_ = 1;
+ }
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Proxy: WARNING! Not sending unsupported "
+ << "'code_finish_listeners' message.\n"
+ << logofs_flush;
+ #endif
+
+ finish_ = 1;
+ }
+
+ return 1;
+}
+
+void Proxy::handleResetAlert()
+{
+ if (alert_ != 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: The proxy alert '" << alert_
+ << "' was displaced.\n" << logofs_flush;
+ #endif
+
+ alert_ = 0;
+ }
+
+ T_list &channelList = activeChannels_.getList();
+
+ for (T_list::iterator j = channelList.begin();
+ j != channelList.end(); j++)
+ {
+ int channelId = *j;
+
+ if (channels_[channelId] != NULL)
+ {
+ channels_[channelId] -> handleResetAlert();
+ }
+ }
+}
+
+int Proxy::handleFinish(int channelId)
+{
+ //
+ // Send any outstanding encoded data and
+ // do any finalization needed on the
+ // channel.
+ //
+
+ if (needFlush(channelId) == 1)
+ {
+ if (channels_[channelId] -> getFinish() == 1)
+ {
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! The finishing channel ID#"
+ << channelId << " has data to flush.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+ *logofs << "Proxy: WARNING! Flushing data for the "
+ << "finishing channel ID#" << channelId
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (handleFrame(frame_data) < 0)
+ {
+ return -1;
+ }
+ }
+
+ //
+ // Reset the congestion state and the
+ // timeouts, if needed.
+ //
+
+ congestions_[channelId] = 0;
+
+ setSplitTimeout(channelId);
+ setMotionTimeout(channelId);
+
+ if (channels_[channelId] -> getFinish() == 0)
+ {
+ channels_[channelId] -> handleFinish();
+
+ //
+ // Force a failure in the case somebody
+ // would try to read from the channel.
+ //
+
+ shutdown(getFd(channelId), SHUT_RD);
+
+ //
+ // If the failure was not originated by
+ // the remote, send a channel shutdown
+ // message.
+ //
+
+ if (channels_[channelId] -> getClosing() == 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Finishing channel for FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << " because of failure.\n"
+ << logofs_flush;
+ #endif
+
+ if (handleControl(code_finish_connection, channelId) < 0)
+ {
+ return -1;
+ }
+ }
+ }
+
+ return 1;
+}
+
+int Proxy::handleFinishFromProxy(int channelId)
+{
+ //
+ // Check if this channel has pending
+ // data to send.
+ //
+
+ if (needFlush(channelId) == 1)
+ {
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+ *logofs << "Proxy: WARNING! Flushing data for the "
+ << "finishing channel ID#" << channelId
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (handleFrame(frame_data) < 0)
+ {
+ return -1;
+ }
+ }
+
+ //
+ // Mark the channel. We will free its
+ // resources at the next loop and will
+ // send the drop message to the remote.
+ //
+
+ if (channels_[channelId] -> getClosing() == 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Marking channel for FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << " as closing.\n"
+ << logofs_flush;
+ #endif
+
+ channels_[channelId] -> handleClosing();
+ }
+
+ if (channels_[channelId] -> getFinish() == 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Finishing channel for FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << " because of proxy.\n"
+ << logofs_flush;
+ #endif
+
+ channels_[channelId] -> handleFinish();
+ }
+
+ if (handleFinish(channelId) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+int Proxy::handleDropFromProxy(int channelId)
+{
+ //
+ // Only mark the channel.
+ //
+
+ #ifdef TEST
+ *logofs << "Proxy: Marking channel for FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << " as being dropped.\n"
+ << logofs_flush;
+ #endif
+
+ if (channels_[channelId] -> getDrop() == 0)
+ {
+ channels_[channelId] -> handleDrop();
+ }
+
+ return 1;
+}
+
+//
+// Close the channel and deallocate all its
+// resources.
+//
+
+int Proxy::handleDrop(int channelId)
+{
+ //
+ // Check if this channel has pending
+ // data to send.
+ //
+
+ if (needFlush(channelId) == 1)
+ {
+ if (channels_[channelId] -> getFinish() == 1)
+ {
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! The dropping channel ID#"
+ << channelId << " has data to flush.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+ *logofs << "Proxy: WARNING! Flushing data for the "
+ << "dropping channel ID#" << channelId
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (handleFrame(frame_data) < 0)
+ {
+ return -1;
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "Proxy: Dropping channel for FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << ".\n" << logofs_flush;
+ #endif
+
+ if (channels_[channelId] -> getFinish() == 0)
+ {
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! The channel for FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << " was not marked as "
+ << "finishing.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": The channel for FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << " was not marked as "
+ << "finishing.\n";
+
+ channels_[channelId] -> handleFinish();
+ }
+
+ //
+ // Send the channel shutdown message
+ // to the peer proxy.
+ //
+
+ if (channels_[channelId] -> getClosing() == 1)
+ {
+ if (handleControl(code_drop_connection, channelId) < 0)
+ {
+ return -1;
+ }
+ }
+
+ //
+ // Get rid of the channel.
+ //
+
+ if (channels_[channelId] -> getType() != channel_x11)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Closed connection to "
+ << getTypeName(channels_[channelId] -> getType())
+ << " server.\n" << logofs_flush;
+ #endif
+
+ cerr << "Info" << ": Closed connection to "
+ << getTypeName(channels_[channelId] -> getType())
+ << " server.\n";
+ }
+
+ delete channels_[channelId];
+ channels_[channelId] = NULL;
+
+ cleanupChannelMap(channelId);
+
+ //
+ // Get rid of the transport.
+ //
+
+ deallocateTransport(channelId);
+
+ congestions_[channelId] = 0;
+
+ decreaseChannels(channelId);
+
+ //
+ // Check if the channel was the
+ // one currently selected for
+ // output.
+ //
+
+ if (outputChannel_ == channelId)
+ {
+ outputChannel_ = -1;
+ }
+
+ return 1;
+}
+
+//
+// Send an empty message to the remote peer
+// to verify if the link is alive and let
+// the remote proxy detect a congestion.
+//
+
+int Proxy::handlePing()
+{
+ T_timestamp nowTs = getTimestamp();
+
+ #if defined(DEBUG) || defined(PING)
+
+ *logofs << "Proxy: Checking ping at "
+ << strMsTimestamp(nowTs) << logofs_flush;
+
+ *logofs << " with last loop at "
+ << strMsTimestamp(timeouts_.loopTs) << ".\n"
+ << logofs_flush;
+
+ *logofs << "Proxy: Last bytes in at "
+ << strMsTimestamp(timeouts_.readTs) << logofs_flush;
+
+ *logofs << " last bytes out at "
+ << strMsTimestamp(timeouts_.writeTs) << ".\n"
+ << logofs_flush;
+
+ *logofs << "Proxy: Last ping at "
+ << strMsTimestamp(timeouts_.pingTs) << ".\n"
+ << logofs_flush;
+
+ #endif
+
+ //
+ // Be sure we take into account any clock drift. This
+ // can be caused by the user changing the system timer
+ // or by small adjustments introduced by the operating
+ // system making the clock go backward.
+ //
+
+ if (checkDiffTimestamp(timeouts_.loopTs, nowTs) == 0)
+ {
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Detected drift in system "
+ << "timer. Resetting to current time.\n"
+ << logofs_flush;
+ #endif
+
+ timeouts_.pingTs = nowTs;
+ timeouts_.readTs = nowTs;
+ timeouts_.writeTs = nowTs;
+ }
+
+ //
+ // Check timestamp of last read from remote proxy. It can
+ // happen that we stayed in the main loop long enough to
+ // have idle timeout expired, for example if the proxy was
+ // stopped and restarted or because of an extremely high
+ // load of the system. In this case we don't complain if
+ // there is something new to read from the remote.
+ //
+
+ int diffIn = diffTimestamp(timeouts_.readTs, nowTs);
+
+ if (diffIn >= (control -> PingTimeout * 2) -
+ control -> LatencyTimeout)
+ {
+ //
+ // Force a read to detect whether the remote proxy
+ // aborted the connection.
+ //
+
+ int result = handleRead();
+
+ if (result < 0)
+ {
+ #if defined(TEST) || defined(INFO) || defined(PING)
+ *logofs << "Proxy: WARNING! Detected shutdown waiting "
+ << "for the ping after " << diffIn / 1000
+ << " seconds.\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ else if (result > 0)
+ {
+ diffIn = diffTimestamp(timeouts_.readTs, nowTs);
+
+ if (handleFlush() < 0)
+ {
+ return -1;
+ }
+ }
+ }
+
+ if (diffIn >= (control -> PingTimeout * 2) -
+ control -> LatencyTimeout)
+ {
+ #if defined(TEST) || defined(INFO) || defined(PING)
+ *logofs << "Proxy: Detected congestion at "
+ << strMsTimestamp() << " with " << diffIn / 1000
+ << " seconds since the last read.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // There are two types of proxy congestion. The first,
+ // affecting the ability of the proxy to write the
+ // encoded data to the network, is controlled by the
+ // congestion_ flag. The flag is raised when no data
+ // is received from the remote proxy within a timeout.
+ // On the X client side, the flag is also raised when
+ // the proxy runs out of tokens.
+ //
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ //
+ // At X server side we must return to read data
+ // from the channels after a while, because we
+ // need to give a chance to the channel to read
+ // the key sequence CTRL+ALT+SHIFT+ESC.
+ //
+
+ if (congestion_ == 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Forcibly entering congestion due to "
+ << "timeout with " << tokens_[token_control].remaining
+ << " tokens.\n" << logofs_flush;
+ #endif
+
+ congestion_ = 1;
+ }
+ else
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Forcibly exiting congestion due to "
+ << "timeout with " << tokens_[token_control].remaining
+ << " tokens.\n" << logofs_flush;
+ #endif
+
+ congestion_ = 0;
+ }
+ }
+ else
+ {
+ #if defined(TEST) || defined(INFO)
+
+ if (congestion_ == 0)
+ {
+ *logofs << "Proxy: Entering congestion due to timeout "
+ << "with " << tokens_[token_control].remaining
+ << " tokens.\n" << logofs_flush;
+ }
+
+ #endif
+
+ congestion_ = 1;
+ }
+
+ if (control -> ProxyTimeout > 0 &&
+ diffIn >= (control -> ProxyTimeout -
+ control -> LatencyTimeout))
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! No data received from "
+ << "remote proxy on FD#" << fd_ << " within "
+ << (diffIn + control -> LatencyTimeout) / 1000
+ << " seconds.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": No data received from remote "
+ << "proxy within " << (diffIn + control ->
+ LatencyTimeout) / 1000 << " seconds.\n";
+
+ HandleAbort();
+ }
+ else
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: WARNING! No data received from "
+ << "remote proxy on FD#" << fd_ << " since "
+ << diffIn << " Ms.\n" << logofs_flush;
+ #endif
+
+ if (control -> ProxyTimeout > 0 &&
+ isTimestamp(timeouts_.alertTs) == 0 &&
+ diffIn >= (control -> ProxyTimeout -
+ control -> LatencyTimeout) / 4)
+ {
+ //
+ // If we are in the middle of a shutdown
+ // procedure but the remote is not resp-
+ // onding, force the closure of the link.
+ //
+
+ if (finish_ != 0)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! No response received from "
+ << "the remote proxy on FD#" << fd_ << " while "
+ << "waiting for the shutdown.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": No response received from remote "
+ << "proxy while waiting for the shutdown.\n";
+
+ HandleAbort();
+ }
+ else
+ {
+ cerr << "Warning" << ": No data received from remote "
+ << "proxy within " << (diffIn + control ->
+ LatencyTimeout) / 1000 << " seconds.\n";
+
+ if (alert_ == 0)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ alert_ = CLOSE_DEAD_PROXY_CONNECTION_CLIENT_ALERT;
+ }
+ else
+ {
+ alert_ = CLOSE_DEAD_PROXY_CONNECTION_SERVER_ALERT;
+ }
+
+ HandleAlert(alert_, 1);
+ }
+
+ timeouts_.alertTs = nowTs;
+ }
+ }
+ }
+ }
+
+ //
+ // Check if we need to update the congestion
+ // counter.
+ //
+
+ int diffOut = diffTimestamp(timeouts_.writeTs, nowTs);
+
+ if (agent_ != nothing && congestions_[agent_] == 0 &&
+ statistics -> getCongestionInFrame() >= 1 &&
+ diffOut >= (control -> IdleTimeout -
+ control -> LatencyTimeout * 5))
+ {
+ #if defined(TEST) || defined(INFO) || defined(PING)
+ *logofs << "Proxy: Forcing an update of the "
+ << "congestion counter after timeout.\n"
+ << logofs_flush;
+ #endif
+
+ statistics -> updateCongestion(tokens_[token_control].remaining,
+ tokens_[token_control].limit);
+ }
+
+ //
+ // Send a new token if we didn't send any data to
+ // the remote for longer than the ping timeout.
+ // The client side sends a token, the server side
+ // responds with a token reply.
+ //
+ // VMWare virtual machines can have the system
+ // timer deadly broken. Try to send a ping regard-
+ // less we are the client or the server proxy to
+ // force a write by the remote.
+ //
+
+ if (control -> ProxyMode == proxy_client ||
+ diffIn >= (control -> PingTimeout * 4) -
+ control -> LatencyTimeout)
+ {
+ //
+ // We need to send a new ping even if we didn't
+ // receive anything from the remote within the
+ // ping timeout. The server side will respond
+ // to our ping, so we use the ping to force the
+ // remote end to send some data.
+ //
+
+ if (diffIn >= (control -> PingTimeout -
+ control -> LatencyTimeout * 5) ||
+ diffOut >= (control -> PingTimeout -
+ control -> LatencyTimeout * 5))
+ {
+ int diffPing = diffTimestamp(timeouts_.pingTs, nowTs);
+
+ if (diffPing < 0 || diffPing >= (control -> PingTimeout -
+ control -> LatencyTimeout * 5))
+ {
+ #if defined(TEST) || defined(INFO) || defined(PING)
+ *logofs << "Proxy: Sending a new ping at " << strMsTimestamp()
+ << " with " << tokens_[token_control].remaining
+ << " tokens and elapsed in " << diffIn << " out "
+ << diffOut << " ping " << diffPing
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (handleFrame(frame_ping) < 0)
+ {
+ return -1;
+ }
+
+ timeouts_.pingTs = nowTs;
+ }
+ #if defined(TEST) || defined(INFO) || defined(PING)
+ else
+ {
+ *logofs << "Proxy: Not sending a new ping with "
+ << "elapsed in " << diffIn << " out "
+ << diffOut << " ping " << diffPing
+ << ".\n" << logofs_flush;
+ }
+ #endif
+ }
+ }
+
+ return 1;
+}
+
+int Proxy::handleSyncFromProxy(int channelId)
+{
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: WARNING! Received a synchronization "
+ << "request from the remote proxy.\n"
+ << logofs_flush;
+ #endif
+
+ if (handleControl(code_sync_reply, channelId) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+int Proxy::handleResetStores()
+{
+ //
+ // Recreate the message stores.
+ //
+
+ delete clientStore_;
+ delete serverStore_;
+
+ clientStore_ = new ClientStore(compressor_);
+ serverStore_ = new ServerStore(compressor_);
+
+ timeouts_.loadTs = nullTimestamp();
+
+ //
+ // Replace message stores in channels.
+ //
+
+ T_list &channelList = activeChannels_.getList();
+
+ for (T_list::iterator j = channelList.begin();
+ j != channelList.end(); j++)
+ {
+ int channelId = *j;
+
+ if (channels_[channelId] != NULL)
+ {
+ if (channels_[channelId] -> setStores(clientStore_, serverStore_) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Failed to replace message stores in "
+ << "channel for FD#" << getFd(channelId) << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to replace message stores in "
+ << "channel for FD#" << getFd(channelId) << ".\n";
+
+ return -1;
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Proxy: Replaced message stores in channel "
+ << "for FD#" << getFd(channelId) << ".\n"
+ << logofs_flush;
+ }
+ #endif
+ }
+ }
+
+ return 1;
+}
+
+int Proxy::handleResetPersistentCache()
+{
+ char *fullName = new char[strlen(control -> PersistentCachePath) +
+ strlen(control -> PersistentCacheName) + 2];
+
+ strcpy(fullName, control -> PersistentCachePath);
+ strcat(fullName, "/");
+ strcat(fullName, control -> PersistentCacheName);
+
+ #ifdef TEST
+ *logofs << "Proxy: Going to remove persistent cache file '"
+ << fullName << "'\n" << logofs_flush;
+ #endif
+
+ unlink(fullName);
+
+ delete [] fullName;
+
+ delete [] control -> PersistentCacheName;
+
+ control -> PersistentCacheName = NULL;
+
+ return 1;
+}
+
+void Proxy::handleResetFlush()
+{
+ #ifdef TEST
+ *logofs << "Proxy: Going to reset flush counters "
+ << "for proxy FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Reset the proxy priority flag.
+ //
+
+ priority_ = 0;
+
+ //
+ // Restore buffers to their initial
+ // size.
+ //
+
+ transport_ -> partialReset();
+
+ //
+ // Update the timestamp of the last
+ // write operation performed on the
+ // socket.
+ //
+
+ timeouts_.writeTs = getTimestamp();
+}
+
+int Proxy::handleFinish()
+{
+ //
+ // Reset the timestamps to give the proxy
+ // another chance to show the 'no response'
+ // dialog if the shutdown message doesn't
+ // come in time.
+ //
+
+ timeouts_.readTs = getTimestamp();
+
+ timeouts_.alertTs = nullTimestamp();
+
+ finish_ = 1;
+
+ return 1;
+}
+
+int Proxy::handleShutdown()
+{
+ //
+ // Send shutdown message to remote proxy.
+ //
+
+ shutdown_ = 1;
+
+ handleControl(code_shutdown_request);
+
+ #ifdef TEST
+ *logofs << "Proxy: Starting shutdown procedure "
+ << "for proxy FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Ensure that all the data accumulated
+ // in the transport buffer is flushed
+ // to the network layer.
+ //
+
+ for (int i = 0; i < 100; i++)
+ {
+ if (canFlush() == 1)
+ {
+ handleFlush();
+ }
+ else
+ {
+ break;
+ }
+
+ usleep(100000);
+ }
+
+ //
+ // Now wait for the network layers to
+ // consume all the data.
+ //
+
+ for (int i = 0; i < 100; i++)
+ {
+ if (transport_ -> queued() <= 0)
+ {
+ break;
+ }
+
+ usleep(100000);
+ }
+
+ //
+ // Give time to the remote end to read
+ // the shutdown message and close the
+ // connection.
+ //
+
+ transport_ -> wait(10000);
+
+ #ifdef TEST
+ *logofs << "Proxy: Ending shutdown procedure "
+ << "for proxy FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int Proxy::handleChannelConfiguration()
+{
+ if (activeChannels_.getSize() == 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Going to initialize the static "
+ << "members in channels for proxy FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ Channel::setReferences();
+
+ ClientChannel::setReferences();
+ ServerChannel::setReferences();
+
+ GenericChannel::setReferences();
+ }
+
+ return 1;
+}
+
+int Proxy::handleSocketConfiguration()
+{
+ //
+ // Set linger mode on proxy to correctly
+ // get shutdown notification.
+ //
+
+ SetLingerTimeout(fd_, 30);
+
+ //
+ // Set keep-alive on socket so that if remote link
+ // terminates abnormally (as killed hard or because
+ // of a power-off) process will get a SIGPIPE. In
+ // practice this is useless as proxies already ping
+ // each other every few seconds.
+ //
+
+ if (control -> OptionProxyKeepAlive == 1)
+ {
+ SetKeepAlive(fd_);
+ }
+
+ //
+ // Set 'priority' flag at TCP layer for path
+ // proxy-to-proxy. Look at IPTOS_LOWDELAY in
+ // man 7 ip.
+ //
+
+ if (control -> OptionProxyLowDelay == 1)
+ {
+ SetLowDelay(fd_);
+ }
+
+ //
+ // Update size of TCP send and receive buffers.
+ //
+
+ if (control -> OptionProxySendBuffer != -1)
+ {
+ SetSendBuffer(fd_, control -> OptionProxySendBuffer);
+ }
+
+ if (control -> OptionProxyReceiveBuffer != -1)
+ {
+ SetReceiveBuffer(fd_, control -> OptionProxyReceiveBuffer);
+ }
+
+ //
+ // Update TCP_NODELAY settings. Note that on old Linux
+ // kernels turning off the Nagle algorithm didn't work
+ // when proxy was run through a PPP link. Trying to do
+ // so caused the kernel to stop delivering data to us
+ // if a serious network congestion was encountered.
+ //
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ if (control -> OptionProxyClientNoDelay != -1)
+ {
+ SetNoDelay(fd_, control -> OptionProxyClientNoDelay);
+ }
+ }
+ else
+ {
+ if (control -> OptionProxyServerNoDelay != -1)
+ {
+ SetNoDelay(fd_, control -> OptionProxyServerNoDelay);
+ }
+ }
+
+ return 1;
+}
+
+int Proxy::handleLinkConfiguration()
+{
+ #ifdef TEST
+ *logofs << "Proxy: Propagating parameters to "
+ << "channels' read buffers.\n"
+ << logofs_flush;
+ #endif
+
+ T_list &channelList = activeChannels_.getList();
+
+ for (T_list::iterator j = channelList.begin();
+ j != channelList.end(); j++)
+ {
+ int channelId = *j;
+
+ if (channels_[channelId] != NULL)
+ {
+ channels_[channelId] -> handleConfiguration();
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "Proxy: Propagating parameters to "
+ << "proxy buffers.\n"
+ << logofs_flush;
+ #endif
+
+ readBuffer_.setSize(control -> ProxyInitialReadSize,
+ control -> ProxyMaximumBufferSize);
+
+ encodeBuffer_.setSize(control -> TransportProxyBufferSize,
+ control -> TransportProxyBufferThreshold,
+ control -> TransportMaximumBufferSize);
+
+ transport_ -> setSize(control -> TransportProxyBufferSize,
+ control -> TransportProxyBufferThreshold,
+ control -> TransportMaximumBufferSize);
+
+ #ifdef TEST
+ *logofs << "Proxy: Configuring the proxy timeouts.\n"
+ << logofs_flush;
+ #endif
+
+ timeouts_.split = control -> SplitTimeout;
+ timeouts_.motion = control -> MotionTimeout;
+
+ #ifdef TEST
+ *logofs << "Proxy: Configuring the proxy tokens.\n"
+ << logofs_flush;
+ #endif
+
+ tokens_[token_control].size = control -> TokenSize;
+ tokens_[token_control].limit = control -> TokenLimit;
+
+ if (tokens_[token_control].limit < 1)
+ {
+ tokens_[token_control].limit = 1;
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(LIMIT)
+ *logofs << "Proxy: TOKEN! LIMIT! Setting token ["
+ << DumpToken(token_control) << "] size to "
+ << tokens_[token_control].size << " and limit to "
+ << tokens_[token_control].limit << ".\n"
+ << logofs_flush;
+ #endif
+
+ tokens_[token_split].size = control -> TokenSize;
+ tokens_[token_split].limit = control -> TokenLimit / 2;
+
+ if (tokens_[token_split].limit < 1)
+ {
+ tokens_[token_split].limit = 1;
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(LIMIT)
+ *logofs << "Proxy: TOKEN! LIMIT! Setting token ["
+ << DumpToken(token_split) << "] size to "
+ << tokens_[token_split].size << " and limit to "
+ << tokens_[token_split].limit << ".\n"
+ << logofs_flush;
+ #endif
+
+ tokens_[token_data].size = control -> TokenSize;
+ tokens_[token_data].limit = control -> TokenLimit / 4;
+
+ if (tokens_[token_data].limit < 1)
+ {
+ tokens_[token_data].limit = 1;
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(LIMIT)
+ *logofs << "Proxy: TOKEN! LIMIT! Setting token ["
+ << DumpToken(token_data) << "] size to "
+ << tokens_[token_data].size << " and limit to "
+ << tokens_[token_data].limit << ".\n"
+ << logofs_flush;
+ #endif
+
+ for (int i = token_control; i <= token_data; i++)
+ {
+ tokens_[i].remaining = tokens_[i].limit;
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(LIMIT)
+ *logofs << "Proxy: LIMIT! Using client bitrate "
+ << "limit " << control -> ClientBitrateLimit
+ << " server bitrate limit " << control ->
+ ServerBitrateLimit << " with local limit "
+ << control -> LocalBitrateLimit << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Set the other parameters based on
+ // the token size.
+ //
+
+ int base = control -> TokenSize;
+
+ control -> SplitDataThreshold = base * 4;
+ control -> SplitDataPacketLimit = base / 2;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: LIMIT! Setting split data threshold "
+ << "to " << control -> SplitDataThreshold
+ << " split packet limit to " << control ->
+ SplitDataPacketLimit << " with base "
+ << base << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Set the number of bytes read from the
+ // data channels at each loop. This will
+ // basically determine the maximum band-
+ // width available for the generic chan-
+ // nels.
+ //
+
+ control -> GenericInitialReadSize = base / 2;
+ control -> GenericMaximumBufferSize = base / 2;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: LIMIT! Setting generic channel "
+ << "initial read size to " << control ->
+ GenericInitialReadSize << " maximum read "
+ << "size to " << control -> GenericMaximumBufferSize
+ << " with base " << base << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int Proxy::handleCacheConfiguration()
+{
+ #ifdef TEST
+ *logofs << "Proxy: Configuring cache according to pack parameters.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Further adjust the cache parameters. If
+ // packing of the images is enabled, reduce
+ // the size available for plain images.
+ //
+
+ if (control -> SessionMode == session_agent)
+ {
+ if (control -> PackMethod != NO_PACK)
+ {
+ clientStore_ -> getRequestStore(X_PutImage) ->
+ cacheThreshold = PUTIMAGE_CACHE_THRESHOLD_IF_PACKED;
+
+ clientStore_ -> getRequestStore(X_PutImage) ->
+ cacheLowerThreshold = PUTIMAGE_CACHE_LOWER_THRESHOLD_IF_PACKED;
+ }
+ }
+
+ //
+ // If this is a shadow session increase the
+ // size of the image cache.
+ //
+
+ if (control -> SessionMode == session_shadow)
+ {
+ if (control -> PackMethod != NO_PACK)
+ {
+ clientStore_ -> getRequestStore(X_NXPutPackedImage) ->
+ cacheThreshold = PUTPACKEDIMAGE_CACHE_THRESHOLD_IF_PACKED_SHADOW;
+
+ clientStore_ -> getRequestStore(X_NXPutPackedImage) ->
+ cacheLowerThreshold = PUTPACKEDIMAGE_CACHE_LOWER_THRESHOLD_IF_PACKED_SHADOW;
+ }
+ else
+ {
+ clientStore_ -> getRequestStore(X_PutImage) ->
+ cacheThreshold = PUTIMAGE_CACHE_THRESHOLD_IF_SHADOW;
+
+ clientStore_ -> getRequestStore(X_PutImage) ->
+ cacheLowerThreshold = PUTIMAGE_CACHE_LOWER_THRESHOLD_IF_SHADOW;
+ }
+ }
+
+ return 1;
+}
+
+int Proxy::handleSaveStores()
+{
+ //
+ // Save content of stores on disk.
+ //
+
+ char *cacheToAdopt = NULL;
+
+ if (control -> PersistentCacheEnableSave)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Going to save content of client store.\n"
+ << logofs_flush;
+ #endif
+
+ cacheToAdopt = handleSaveAllStores(control -> PersistentCachePath);
+ }
+ #ifdef TEST
+ else
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ *logofs << "Proxy: Saving persistent cache to disk disabled.\n"
+ << logofs_flush;
+ }
+ else
+ {
+ *logofs << "Proxy: PANIC! Protocol violation in command save.\n"
+ << logofs_flush;
+
+ cerr << "Error" << ": Protocol violation in command save.\n";
+
+ HandleCleanup();
+ }
+ }
+ #endif
+
+ if (cacheToAdopt != NULL)
+ {
+ //
+ // Do we have a cache already?
+ //
+
+ if (control -> PersistentCacheName != NULL)
+ {
+ //
+ // Check if old and new cache are the same.
+ // In this case don't remove the old cache.
+ //
+
+ if (strcasecmp(control -> PersistentCacheName, cacheToAdopt) != 0)
+ {
+ handleResetPersistentCache();
+ }
+
+ delete [] control -> PersistentCacheName;
+ }
+
+ #ifdef TEST
+ *logofs << "Proxy: Setting current persistent cache file to '"
+ << cacheToAdopt << "'\n" << logofs_flush;
+ #endif
+
+ control -> PersistentCacheName = cacheToAdopt;
+
+ return 1;
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Proxy: No cache file produced from message stores.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ //
+ // It can be that we didn't generate a new cache
+ // because store was too small or persistent cache
+ // was disabled. This is not an error.
+ //
+
+ return 0;
+}
+
+int Proxy::handleLoadStores()
+{
+ //
+ // Restore the content of the client store
+ // from disk if a valid cache was negotiated
+ // at session startup.
+ //
+
+ if (control -> PersistentCacheEnableLoad == 1 &&
+ control -> PersistentCachePath != NULL &&
+ control -> PersistentCacheName != NULL)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Going to load content of client store.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Returns the same string passed as name of
+ // the cache, or NULL if it was not possible
+ // to load the cache from disk.
+ //
+
+ if (handleLoadAllStores(control -> PersistentCachePath,
+ control -> PersistentCacheName) == NULL)
+ {
+ //
+ // The corrupted cache should have been
+ // removed from disk. Get rid of the
+ // reference so we don't try to delete
+ // it once again.
+ //
+
+ if (control -> PersistentCacheName != NULL)
+ {
+ delete [] control -> PersistentCacheName;
+ }
+
+ control -> PersistentCacheName = NULL;
+
+ return -1;
+ }
+
+ //
+ // Set timestamp of last time cache
+ // was loaded from data on disk.
+ //
+
+ timeouts_.loadTs = getTimestamp();
+
+ return 1;
+ }
+ #ifdef TEST
+ else
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ *logofs << "Proxy: Loading of cache disabled or no cache file selected.\n"
+ << logofs_flush;
+ }
+ else
+ {
+ *logofs << "Proxy: PANIC! Protocol violation in command load.\n"
+ << logofs_flush;
+
+ cerr << "Error" << ": Protocol violation in command load.\n";
+
+ HandleCleanup();
+ }
+ }
+ #endif
+
+ return 0;
+}
+
+int Proxy::handleControl(T_proxy_code code, int data)
+{
+ //
+ // Send the given control messages
+ // to the remote proxy.
+ //
+
+ #if defined(TEST) || defined(INFO)
+
+ if (data != -1)
+ {
+ if (code == code_control_token_reply ||
+ code == code_split_token_reply ||
+ code == code_data_token_reply)
+ {
+ *logofs << "Proxy: TOKEN! Sending message '" << DumpControl(code)
+ << "' at " << strMsTimestamp() << " with count "
+ << data << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "Proxy: Sending message '" << DumpControl(code)
+ << "' at " << strMsTimestamp() << " with data ID#"
+ << data << ".\n" << logofs_flush;
+ }
+ }
+ else
+ {
+ *logofs << "Proxy: Sending message '" << DumpControl(code)
+ << "' at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ //
+ // Add the control message and see if the
+ // data has to be flushed immediately.
+ //
+
+ if (addControlCodes(code, data) < 0)
+ {
+ return -1;
+ }
+
+ switch (code)
+ {
+ //
+ // Append the first data read from the opened
+ // channel to the control code.
+ //
+
+ case code_new_x_connection:
+ case code_new_cups_connection:
+ case code_new_aux_connection:
+ case code_new_smb_connection:
+ case code_new_media_connection:
+ case code_new_http_connection:
+ case code_new_font_connection:
+ case code_new_slave_connection:
+
+ //
+ // Do we send the token reply immediately?
+ // The control messages are put at the begin-
+ // ning of the of the encode buffer, so we may
+ // reply to multiple tokens before having the
+ // chance of handling the actual frame data.
+ // On the other hand, the sooner we reply, the
+ // sooner the remote proxy is restarted.
+ //
+
+ case code_control_token_reply:
+ case code_split_token_reply:
+ case code_data_token_reply:
+ {
+ break;
+ }
+
+ //
+ // Also send the congestion control codes
+ // immediately.
+ //
+ // case code_begin_congestion:
+ // case code_end_congestion:
+ //
+
+ default:
+ {
+ priority_ = 1;
+
+ break;
+ }
+ }
+
+ if (priority_ == 1)
+ {
+ if (handleFrame(frame_data) < 0)
+ {
+ return -1;
+ }
+ }
+
+ return 1;
+}
+
+int Proxy::handleSwitch(int channelId)
+{
+ //
+ // If data is for a different channel than last
+ // selected for output, prepend to the data the
+ // new channel id.
+ //
+
+ #ifdef DEBUG
+ *logofs << "Proxy: Requested a switch with "
+ << "current channel ID#" << outputChannel_
+ << " new channel ID#" << channelId << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (channelId != outputChannel_)
+ {
+ if (needFlush() == 1)
+ {
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+ *logofs << "Proxy: WARNING! Flushing data for the "
+ << "previous channel ID#" << outputChannel_
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (handleFrame(frame_data) < 0)
+ {
+ return -1;
+ }
+ }
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Sending message '"
+ << DumpControl(code_switch_connection) << "' at "
+ << strMsTimestamp() << " with FD#" << getFd(channelId)
+ << " channel ID#" << channelId << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (addControlCodes(code_switch_connection, channelId) < 0)
+ {
+ return -1;
+ }
+
+ outputChannel_ = channelId;
+ }
+
+ return 1;
+}
+
+int Proxy::addTokenCodes(T_proxy_token &token)
+{
+ #if defined(TEST) || defined(INFO) || defined(TOKEN)
+ *logofs << "Proxy: TOKEN! Sending token ["
+ << DumpToken(token.type) << "] with "
+ << token.bytes << " bytes accumulated size "
+ << token.size << " and " << token.remaining
+ << " available.\n" << logofs_flush;
+ #endif
+
+ //
+ // Give a 'weight' to the token. The tokens
+ // remaining can become negative if we sent
+ // a packet that was exceptionally big.
+ //
+
+ int count = 0;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ count = token.bytes / token.size;
+
+ if (count > 255)
+ {
+ count = 255;
+ }
+ }
+
+ //
+ // Force a count of 1, for example
+ // if this is a ping.
+ //
+
+ if (count < 1)
+ {
+ count = 1;
+
+ token.bytes = 0;
+ }
+ else
+ {
+ //
+ // Let the next token account for the
+ // remaining bytes.
+ //
+
+ token.bytes %= token.size;
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(TOKEN)
+ *logofs << "Proxy: Sending message '"
+ << DumpControl(token.request) << "' at "
+ << strMsTimestamp() << " with count " << count
+ << ".\n" << logofs_flush;
+ #endif
+
+ controlCodes_[controlLength_++] = 0;
+ controlCodes_[controlLength_++] = (unsigned char) token.request;
+ controlCodes_[controlLength_++] = (unsigned char) count;
+
+ statistics -> addFrameOut();
+
+ token.remaining -= count;
+
+ return 1;
+}
+
+int Proxy::handleToken(T_frame_type type)
+{
+ #if defined(TEST) || defined(INFO) || defined(TOKEN)
+ *logofs << "Proxy: TOKEN! Checking tokens with "
+ << "frame type [";
+
+ *logofs << (type == frame_ping ? "frame_ping" : "frame_data");
+
+ *logofs << "] with stream ratio " << statistics ->
+ getStreamRatio() << ".\n" << logofs_flush;
+ #endif
+
+ if (type == frame_data)
+ {
+ if (control -> isProtoStep7() == 1)
+ {
+ //
+ // Send a distinct token for each data type.
+ // We don't want to slow down the sending of
+ // the X events, X replies and split confir-
+ // mation events on the X server side, so
+ // take care only of the generic data token.
+ //
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ statistics -> updateControlToken(tokens_[token_control].bytes);
+
+ if (tokens_[token_control].bytes > tokens_[token_control].size)
+ {
+ if (addTokenCodes(tokens_[token_control]) < 0)
+ {
+ return -1;
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(TOKEN)
+
+ T_proxy_token &token = tokens_[token_control];
+
+ *logofs << "Proxy: TOKEN! Token class ["
+ << DumpToken(token.type) << "] has now "
+ << token.bytes << " bytes accumulated and "
+ << token.remaining << " tokens remaining.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ statistics -> updateSplitToken(tokens_[token_split].bytes);
+
+ if (tokens_[token_split].bytes > tokens_[token_split].size)
+ {
+ if (addTokenCodes(tokens_[token_split]) < 0)
+ {
+ return -1;
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(TOKEN)
+
+ T_proxy_token &token = tokens_[token_split];
+
+ *logofs << "Proxy: TOKEN! Token class ["
+ << DumpToken(token.type) << "] has now "
+ << token.bytes << " bytes accumulated and "
+ << token.remaining << " tokens remaining.\n"
+ << logofs_flush;
+ #endif
+ }
+ }
+
+ statistics -> updateDataToken(tokens_[token_data].bytes);
+
+ if (tokens_[token_data].bytes > tokens_[token_data].size)
+ {
+ if (addTokenCodes(tokens_[token_data]) < 0)
+ {
+ return -1;
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(TOKEN)
+
+ T_proxy_token &token = tokens_[token_data];
+
+ *logofs << "Proxy: TOKEN! Token class ["
+ << DumpToken(token.type) << "] has now "
+ << token.bytes << " bytes accumulated and "
+ << token.remaining << " tokens remaining.\n"
+ << logofs_flush;
+ #endif
+ }
+ }
+ else
+ {
+ //
+ // Sum everything to the control token.
+ //
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ statistics -> updateControlToken(tokens_[token_control].bytes);
+ statistics -> updateSplitToken(tokens_[token_control].bytes);
+ statistics -> updateDataToken(tokens_[token_control].bytes);
+
+ if (tokens_[token_control].bytes > tokens_[token_control].size)
+ {
+ if (addTokenCodes(tokens_[token_control]) < 0)
+ {
+ return -1;
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(TOKEN)
+
+ T_proxy_token &token = tokens_[token_control];
+
+ *logofs << "Proxy: TOKEN! Token class ["
+ << DumpToken(token.type) << "] has now "
+ << token.bytes << " bytes accumulated and "
+ << token.remaining << " tokens remaining.\n"
+ << logofs_flush;
+ #endif
+ }
+ }
+ }
+ }
+ else
+ {
+ if (addTokenCodes(tokens_[token_control]) < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Reset all counters on a ping.
+ //
+
+ tokens_[token_control].bytes = 0;
+ tokens_[token_split].bytes = 0;
+ tokens_[token_data].bytes = 0;
+
+ #if defined(TEST) || defined(INFO) || defined(TOKEN)
+
+ T_proxy_token &token = tokens_[token_control];
+
+ *logofs << "Proxy: TOKEN! Token class ["
+ << DumpToken(token.type) << "] has now "
+ << token.bytes << " bytes accumulated and "
+ << token.remaining << " tokens remaining.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ //
+ // Check if we have entered in
+ // congestion state.
+ //
+
+ if (congestion_ == 0 &&
+ tokens_[token_control].remaining <= 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Entering congestion with "
+ << tokens_[token_control].remaining
+ << " tokens remaining.\n" << logofs_flush;
+ #endif
+
+ congestion_ = 1;
+ }
+
+ statistics -> updateCongestion(tokens_[token_control].remaining,
+ tokens_[token_control].limit);
+
+ return 1;
+}
+
+int Proxy::handleTokenFromProxy(T_proxy_token &token, int count)
+{
+ #if defined(TEST) || defined(INFO) || defined(TOKEN)
+ *logofs << "Proxy: TOKEN! Received token ["
+ << DumpToken(token.type) << "] request at "
+ << strMsTimestamp() << " with count "
+ << count << ".\n" << logofs_flush;
+ #endif
+
+ if (control -> isProtoStep7() == 0)
+ {
+ if (control -> ProxyMode == proxy_client ||
+ token.request != code_control_token_request)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Invalid token request received from remote.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid token request received from remote.\n";
+
+ HandleCleanup();
+ }
+ }
+
+ //
+ // Add our token reply.
+ //
+
+ if (handleControl(token.reply, count) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+int Proxy::handleTokenReplyFromProxy(T_proxy_token &token, int count)
+{
+ #if defined(TEST) || defined(INFO) || defined(TOKEN)
+ *logofs << "Proxy: TOKEN! Received token ["
+ << DumpToken(token.type) << "] reply at "
+ << strMsTimestamp() << " with count " << count
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Increment the available tokens.
+ //
+
+ if (control -> isProtoStep7() == 0)
+ {
+ if (token.reply != code_control_token_reply)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Invalid token reply received from remote.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid token reply received from remote.\n";
+
+ HandleCleanup();
+ }
+
+ count = 1;
+ }
+
+ token.remaining += count;
+
+ if (token.remaining > token.limit)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Token overflow handling messages.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Token overflow handling messages.\n";
+
+ HandleCleanup();
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(TOKEN)
+ *logofs << "Proxy: TOKEN! Token class ["
+ << DumpToken(token.type) << "] has now " << token.bytes
+ << " bytes accumulated and " << token.remaining
+ << " tokens remaining.\n" << logofs_flush;
+ #endif
+
+ //
+ // Check if we can jump out of the
+ // congestion state.
+ //
+
+ if (congestion_ == 1 &&
+ tokens_[token_control].remaining > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Exiting congestion with "
+ << tokens_[token_control].remaining
+ << " tokens remaining.\n" << logofs_flush;
+ #endif
+
+ congestion_ = 0;
+ }
+
+ statistics -> updateCongestion(tokens_[token_control].remaining,
+ tokens_[token_control].limit);
+
+ return 1;
+}
+
+void Proxy::handleFailOnSave(const char *fullName, const char *failContext) const
+{
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Error saving stores to cache file "
+ << "in context [" << failContext << "].\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Error saving stores to cache file "
+ << "in context [" << failContext << "].\n";
+
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Removing invalid cache '"
+ << fullName << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Removing invalid cache '"
+ << fullName << "'.\n";
+
+ unlink(fullName);
+}
+
+void Proxy::handleFailOnLoad(const char *fullName, const char *failContext) const
+{
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Error loading stores from cache file "
+ << "in context [" << failContext << "].\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Error loading stores from cache file "
+ << "in context [" << failContext << "].\n";
+
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Removing invalid cache '"
+ << fullName << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Removing invalid cache '"
+ << fullName << "'.\n";
+
+ unlink(fullName);
+}
+
+int Proxy::handleSaveVersion(unsigned char *buffer, int &major,
+ int &minor, int &patch) const
+{
+ if (control -> isProtoStep8() == 1)
+ {
+ major = 3;
+ minor = 0;
+ patch = 0;
+ }
+ else if (control -> isProtoStep7() == 1)
+ {
+ major = 2;
+ minor = 0;
+ patch = 0;
+ }
+ else
+ {
+ major = 1;
+ minor = 4;
+ patch = 0;
+ }
+
+ *(buffer + 0) = major;
+ *(buffer + 1) = minor;
+
+ PutUINT(patch, buffer + 2, storeBigEndian());
+
+ return 1;
+}
+
+int Proxy::handleLoadVersion(const unsigned char *buffer, int &major,
+ int &minor, int &patch) const
+{
+ major = *(buffer + 0);
+ minor = *(buffer + 1);
+
+ patch = GetUINT(buffer + 2, storeBigEndian());
+
+ //
+ // Force the proxy to discard the
+ // incompatible caches.
+ //
+
+ if (control -> isProtoStep8() == 1)
+ {
+ if (major < 3)
+ {
+ return -1;
+ }
+ }
+ else if (control -> isProtoStep7() == 1)
+ {
+ if (major < 2)
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ if (major != 1 && minor != 4)
+ {
+ return -1;
+ }
+ }
+
+ return 1;
+}
+
+char *Proxy::handleSaveAllStores(const char *savePath) const
+{
+ int cumulativeSize = MessageStore::getCumulativeTotalStorageSize();
+
+ if (cumulativeSize < control -> PersistentCacheThreshold)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Cache not saved as size is "
+ << cumulativeSize << " with threshold set to "
+ << control -> PersistentCacheThreshold
+ << ".\n" << logofs_flush;
+ #endif
+
+ return NULL;
+ }
+ else if (savePath == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! No name provided for save path.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": No name provided for save path.\n";
+
+ return NULL;
+ }
+
+ #ifdef TEST
+ *logofs << "Proxy: Going to save content of message stores.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Our parent process is likely going to terminate.
+ // Until we finish saving cache we must ignore its
+ // SIGIPE.
+ //
+
+ DisableSignals();
+
+ ofstream *cachefs = NULL;
+
+ md5_state_t *md5StateStream = NULL;
+ md5_byte_t *md5DigestStream = NULL;
+
+ md5_state_t *md5StateClient = NULL;
+ md5_byte_t *md5DigestClient = NULL;
+
+ char *tempName = NULL;
+
+ char md5String[MD5_LENGTH * 2 + 2];
+
+ char fullName[strlen(savePath) + MD5_LENGTH * 2 + 4];
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ tempName = tempnam(savePath, "Z-C-");
+ }
+ else
+ {
+ tempName = tempnam(savePath, "Z-S-");
+ }
+
+ //
+ // Change the mask to make the file only
+ // readable by the user, then restore the
+ // old mask.
+ //
+
+ mode_t fileMode = umask(0077);
+
+ cachefs = new ofstream(tempName, ios::out | ios::binary);
+
+ umask(fileMode);
+
+ if (tempName == NULL || cachefs == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Can't create temporary file in '"
+ << savePath << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't create temporary file in '"
+ << savePath << "'.\n";
+
+ if (tempName != NULL)
+ {
+ free(tempName);
+ }
+
+ if (cachefs != NULL)
+ {
+ delete cachefs;
+ }
+
+ EnableSignals();
+
+ return NULL;
+ }
+
+ md5StateStream = new md5_state_t();
+ md5DigestStream = new md5_byte_t[MD5_LENGTH];
+
+ md5_init(md5StateStream);
+
+ //
+ // First write the proxy version.
+ //
+
+ unsigned char version[4];
+
+ int major;
+ int minor;
+ int patch;
+
+ handleSaveVersion(version, major, minor, patch);
+
+ #ifdef TEST
+ *logofs << "Proxy: Saving cache using version '"
+ << major << "." << minor << "." << patch
+ << "'.\n" << logofs_flush;
+ #endif
+
+ if (PutData(cachefs, version, 4) < 0)
+ {
+ handleFailOnSave(tempName, "A");
+
+ delete cachefs;
+
+ delete md5StateStream;
+ delete [] md5DigestStream;
+
+ free(tempName);
+
+ EnableSignals();
+
+ return NULL;
+ }
+
+ //
+ // Make space for the calculated MD5 so we
+ // can later rewind the file and write it
+ // at this position.
+ //
+
+ if (PutData(cachefs, md5DigestStream, MD5_LENGTH) < 0)
+ {
+ handleFailOnSave(tempName, "B");
+
+ delete cachefs;
+
+ delete md5StateStream;
+ delete [] md5DigestStream;
+
+ free(tempName);
+
+ EnableSignals();
+
+ return NULL;
+ }
+
+ md5StateClient = new md5_state_t();
+ md5DigestClient = new md5_byte_t[MD5_LENGTH];
+
+ md5_init(md5StateClient);
+
+ #ifdef DUMP
+
+ ofstream *cacheDump = NULL;
+
+ ofstream *tempfs = (ofstream*) logofs;
+
+ char cacheDumpName[DEFAULT_STRING_LENGTH];
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ snprintf(cacheDumpName, DEFAULT_STRING_LENGTH - 1,
+ "%s/client-cache-dump", control -> TempPath);
+ }
+ else
+ {
+ snprintf(cacheDumpName, DEFAULT_STRING_LENGTH - 1,
+ "%s/server-cache-dump", control -> TempPath);
+ }
+
+ *(cacheDumpName + DEFAULT_STRING_LENGTH - 1) = '\0';
+
+ mode_t fileMode = umask(0077);
+
+ cacheDump = new ofstream(cacheDumpName, ios::out);
+
+ umask(fileMode);
+
+ logofs = cacheDump;
+
+ #endif
+
+ //
+ // Use the virtual method of the concrete proxy class.
+ //
+
+ int allSaved = handleSaveAllStores(cachefs, md5StateStream, md5StateClient);
+
+ #ifdef DUMP
+
+ logofs = tempfs;
+
+ delete cacheDump;
+
+ #endif
+
+ if (allSaved == 0)
+ {
+ handleFailOnSave(tempName, "C");
+
+ delete cachefs;
+
+ delete md5StateStream;
+ delete [] md5DigestStream;
+
+ delete md5StateClient;
+ delete [] md5DigestClient;
+
+ free(tempName);
+
+ EnableSignals();
+
+ return NULL;
+ }
+
+ md5_finish(md5StateClient, md5DigestClient);
+
+ for (unsigned int i = 0; i < MD5_LENGTH; i++)
+ {
+ sprintf(md5String + (i * 2), "%02X", md5DigestClient[i]);
+ }
+
+ strcpy(fullName, (control -> ProxyMode == proxy_client) ? "C-" : "S-");
+
+ strcat(fullName, md5String);
+
+ md5_append(md5StateStream, (const md5_byte_t *) fullName, strlen(fullName));
+ md5_finish(md5StateStream, md5DigestStream);
+
+ //
+ // Go to the beginning of file plus
+ // the integer where we wrote our
+ // proxy version.
+ //
+
+ cachefs -> seekp(4);
+
+ if (PutData(cachefs, md5DigestStream, MD5_LENGTH) < 0)
+ {
+ handleFailOnSave(tempName, "D");
+
+ delete cachefs;
+
+ delete md5StateStream;
+ delete [] md5DigestStream;
+
+ delete md5StateClient;
+ delete [] md5DigestClient;
+
+ free(tempName);
+
+ EnableSignals();
+
+ return NULL;
+ }
+
+ delete cachefs;
+
+ //
+ // Copy the resulting cache name without
+ // the path so that we can return it to
+ // the caller.
+ //
+
+ char *cacheName = new char[MD5_LENGTH * 2 + 4];
+
+ strcpy(cacheName, fullName);
+
+ //
+ // Add the path to the full name and move
+ // the cache into the path.
+ //
+
+ strcpy(fullName, savePath);
+ strcat(fullName, (control -> ProxyMode == proxy_client) ? "/C-" : "/S-");
+ strcat(fullName, md5String);
+
+ #ifdef TEST
+ *logofs << "Proxy: Renaming cache file from '"
+ << tempName << "' to '" << fullName
+ << "'.\n" << logofs_flush;
+ #endif
+
+ rename(tempName, fullName);
+
+ delete md5StateStream;
+ delete [] md5DigestStream;
+
+ delete md5StateClient;
+ delete [] md5DigestClient;
+
+ free(tempName);
+
+ //
+ // Restore the original handlers.
+ //
+
+ EnableSignals();
+
+ #ifdef TEST
+ *logofs << "Proxy: Successfully saved cache file '"
+ << cacheName << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // This must be enabled only for test
+ // because it requires that client and
+ // server reside on the same machine.
+ //
+
+ if (control -> PersistentCacheCheckOnShutdown == 1 &&
+ control -> ProxyMode == proxy_server)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: MATCH! Checking if the file '"
+ << fullName << "' matches a client cache.\n"
+ << logofs_flush;
+ #endif
+
+ strcpy(fullName, savePath);
+ strcat(fullName, "/C-");
+ strcat(fullName, md5String);
+
+ struct stat fileStat;
+
+ if (stat(fullName, &fileStat) != 0)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Can't find a client cache "
+ << "with name '" << fullName << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't find a client cache "
+ << "with name '" << fullName << "'.\n";
+
+ HandleShutdown();
+ }
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: MATCH! Client cache '" << fullName
+ << "' matches the local cache.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ return cacheName;
+}
+
+const char *Proxy::handleLoadAllStores(const char *loadPath, const char *loadName) const
+{
+ #ifdef TEST
+ *logofs << "Proxy: Going to load content of message stores.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Until we finish loading cache we
+ // must at least ignore any SIGIPE.
+ //
+
+ DisableSignals();
+
+ if (loadPath == NULL || loadName == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! No path or no file name provided for cache to restore.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": No path or no file name provided for cache to restore.\n";
+
+ EnableSignals();
+
+ return NULL;
+ }
+ else if (strlen(loadName) != MD5_LENGTH * 2 + 2)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Bad file name provided for cache to restore.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Bad file name provided for cache to restore.\n";
+
+ EnableSignals();
+
+ return NULL;
+ }
+
+ istream *cachefs = NULL;
+ char md5String[(MD5_LENGTH * 2) + 2];
+ md5_byte_t md5FromFile[MD5_LENGTH];
+
+ char *cacheName = new char[strlen(loadPath) + strlen(loadName) + 3];
+
+ strcpy(cacheName, loadPath);
+ strcat(cacheName, "/");
+ strcat(cacheName, loadName);
+
+ #ifdef TEST
+ *logofs << "Proxy: Name of cache file is '"
+ << cacheName << "'.\n" << logofs_flush;
+ #endif
+
+ cachefs = new ifstream(cacheName, ios::in | ios::binary);
+
+ unsigned char version[4];
+
+ if (cachefs == NULL || GetData(cachefs, version, 4) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Can't read cache file '"
+ << cacheName << "'.\n" << logofs_flush;;
+ #endif
+
+ handleFailOnLoad(cacheName, "A");
+
+ delete cachefs;
+
+ delete [] cacheName;
+
+ EnableSignals();
+
+ return NULL;
+ }
+
+ int major;
+ int minor;
+ int patch;
+
+ if (handleLoadVersion(version, major, minor, patch) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: WARNING! Incompatible version '"
+ << major << "." << minor << "." << patch
+ << "' in cache file '" << cacheName
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Incompatible version '"
+ << major << "." << minor << "." << patch
+ << "' in cache file '" << cacheName
+ << "'.\n" << logofs_flush;
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ handleFailOnLoad(cacheName, "B");
+ }
+ else
+ {
+ //
+ // Simply remove the cache file.
+ //
+
+ unlink(cacheName);
+ }
+
+ delete cachefs;
+
+ delete [] cacheName;
+
+ EnableSignals();
+
+ return NULL;
+ }
+
+ #ifdef TEST
+ *logofs << "Proxy: Reading from cache file version '"
+ << major << "." << minor << "." << patch
+ << "'.\n" << logofs_flush;
+ #endif
+
+ if (GetData(cachefs, md5FromFile, MD5_LENGTH) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! No checksum in cache file '"
+ << loadName << "'.\n" << logofs_flush;
+ #endif
+
+ handleFailOnLoad(cacheName, "C");
+
+ delete cachefs;
+
+ delete [] cacheName;
+
+ EnableSignals();
+
+ return NULL;
+ }
+
+ md5_state_t *md5StateStream = NULL;
+ md5_byte_t *md5DigestStream = NULL;
+
+ md5StateStream = new md5_state_t();
+ md5DigestStream = new md5_byte_t[MD5_LENGTH];
+
+ md5_init(md5StateStream);
+
+ //
+ // Use the virtual method of the proxy class.
+ //
+
+ if (handleLoadAllStores(cachefs, md5StateStream) < 0)
+ {
+ handleFailOnLoad(cacheName, "D");
+
+ delete cachefs;
+
+ delete md5StateStream;
+ delete [] md5DigestStream;
+
+ delete [] cacheName;
+
+ EnableSignals();
+
+ return NULL;
+ }
+
+ md5_append(md5StateStream, (const md5_byte_t *) loadName, strlen(loadName));
+ md5_finish(md5StateStream, md5DigestStream);
+
+ for (int i = 0; i < MD5_LENGTH; i++)
+ {
+ if (md5DigestStream[i] != md5FromFile[i])
+ {
+ #ifdef PANIC
+
+ *logofs << "Proxy: PANIC! Bad checksum for cache file '"
+ << cacheName << "'.\n" << logofs_flush;
+
+ for (unsigned int i = 0; i < MD5_LENGTH; i++)
+ {
+ sprintf(md5String + (i * 2), "%02X", md5FromFile[i]);
+ }
+
+ *logofs << "Proxy: PANIC! Saved checksum is '"
+ << md5String << "'.\n" << logofs_flush;
+
+ for (unsigned int i = 0; i < MD5_LENGTH; i++)
+ {
+ sprintf(md5String + (i * 2),"%02X", md5DigestStream[i]);
+ }
+
+ *logofs << "Proxy: PANIC! Calculated checksum is '"
+ << md5String << "'.\n" << logofs_flush;
+
+ #endif
+
+ handleFailOnLoad(cacheName, "E");
+
+ delete cachefs;
+
+ delete md5StateStream;
+ delete [] md5DigestStream;
+
+ delete [] cacheName;
+
+ EnableSignals();
+
+ return NULL;
+ }
+ }
+
+ delete cachefs;
+
+ delete md5StateStream;
+ delete [] md5DigestStream;
+
+ delete [] cacheName;
+
+ //
+ // Restore the original handlers.
+ //
+
+ EnableSignals();
+
+ #ifdef TEST
+ *logofs << "Proxy: Successfully loaded cache file '"
+ << loadName << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // Return the string provided by caller.
+ //
+
+ return loadName;
+}
+
+int Proxy::allocateChannelMap(int fd)
+{
+ //
+ // We assume that the fd is lower than
+ // the maximum allowed number. This is
+ // checked at the time the connection
+ // is accepted.
+ //
+
+ if (fd < 0 || fd >= CONNECTIONS_LIMIT)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Internal error allocating "
+ << "new channel with FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Internal error allocating "
+ << "new channel with FD#" << fd_ << ".\n";
+
+ HandleCleanup();
+ }
+
+ for (int channelId = 0;
+ channelId < CONNECTIONS_LIMIT;
+ channelId++)
+ {
+ if (checkLocalChannelMap(channelId) == 1 &&
+ fdMap_[channelId] == -1)
+ {
+ fdMap_[channelId] = fd;
+ channelMap_[fd] = channelId;
+
+ #ifdef TEST
+ *logofs << "Proxy: Allocated new channel ID#"
+ << channelId << " with FD#" << fd << ".\n"
+ << logofs_flush;
+ #endif
+
+ return channelId;
+ }
+ }
+
+ //
+ // No available channel is remaining.
+ //
+
+ #ifdef TEST
+ *logofs << "Proxy: WARNING! Can't allocate a new channel "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+}
+
+int Proxy::checkChannelMap(int channelId)
+{
+ //
+ // To be acceptable, the channel id must
+ // be an id that is not possible to use
+ // to allocate channels at this side.
+ //
+
+ if (checkLocalChannelMap(channelId) == 1)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Can't open a new channel "
+ << "with invalid ID#" << channelId << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't open a new channel "
+ << "with invalid ID#" << channelId << ".\n";
+
+ return -1;
+ }
+ else if (channels_[channelId] != NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Can't open a new channel "
+ << "over an existing ID#" << channelId
+ << " with FD#" << getFd(channelId)
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't open a new channel "
+ << "over an existing ID#" << channelId
+ << " with FD#" << getFd(channelId)
+ << ".\n";
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int Proxy::assignChannelMap(int channelId, int fd)
+{
+ //
+ // We assume that the fd is lower than
+ // the maximum allowed number. This is
+ // checked at the time the connection
+ // is accepted.
+ //
+
+ if (channelId < 0 || channelId >= CONNECTIONS_LIMIT ||
+ fd < 0 || fd >= CONNECTIONS_LIMIT)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Internal error assigning "
+ << "new channel with FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Internal error assigning "
+ << "new channel with FD#" << fd_ << ".\n";
+
+ HandleCleanup();
+ }
+
+ fdMap_[channelId] = fd;
+ channelMap_[fd] = channelId;
+
+ return 1;
+}
+
+void Proxy::cleanupChannelMap(int channelId)
+{
+ int fd = fdMap_[channelId];
+
+ if (fd != -1)
+ {
+ fdMap_[channelId] = -1;
+ channelMap_[fd] = -1;
+ }
+}
+
+int Proxy::addControlCodes(T_proxy_code code, int data)
+{
+ //
+ // Flush the encode buffer plus all the outstanding
+ // control codes if there is not enough space for
+ // the new control message. We need to ensure that
+ // there are further bytes available, in the case
+ // we will need to add more token control messages.
+ //
+
+ if (controlLength_ + 3 > CONTROL_CODES_THRESHOLD)
+ {
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Flushing control messages "
+ << "while sending code '" << DumpControl(code)
+ << "'.\n" << logofs_flush;
+ #endif
+
+ if (handleFlush() < 0)
+ {
+ return -1;
+ }
+ }
+
+ controlCodes_[controlLength_++] = 0;
+ controlCodes_[controlLength_++] = (unsigned char) code;
+ controlCodes_[controlLength_++] = (unsigned char) (data == -1 ? 0 : data);
+
+ //
+ // Account for the control frame.
+ //
+
+ statistics -> addFrameOut();
+
+ return 1;
+}
+
+void Proxy::setSplitTimeout(int channelId)
+{
+ int needed = channels_[channelId] -> needSplit();
+
+ if (needed != isTimestamp(timeouts_.splitTs))
+ {
+ if (needed == 1)
+ {
+ #if defined(TEST) || defined(INFO) || defined(SPLIT)
+ *logofs << "Proxy: SPLIT! Allocating split timestamp at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ timeouts_.splitTs = getTimestamp();
+ }
+ else
+ {
+ T_list &channelList = activeChannels_.getList();
+
+ for (T_list::iterator j = channelList.begin();
+ j != channelList.end(); j++)
+ {
+ int channelId = *j;
+
+ if (channels_[channelId] != NULL &&
+ channels_[channelId] -> needSplit() == 1)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: SPLIT! Channel for FD#"
+ << getFd(channelId) << " still needs splits.\n"
+ << logofs_flush;
+ #endif
+
+ return;
+ }
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(SPLIT)
+ *logofs << "Proxy: SPLIT! Resetting split timestamp at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ timeouts_.splitTs = nullTimestamp();
+ }
+ }
+}
+
+void Proxy::setMotionTimeout(int channelId)
+{
+ int needed = channels_[channelId] -> needMotion();
+
+ if (needed != isTimestamp(timeouts_.motionTs))
+ {
+ if (channels_[channelId] -> needMotion() == 1)
+ {
+ #if defined(TEST) || defined(INFO) || defined(SPLIT)
+ *logofs << "Proxy: Allocating motion timestamp at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ timeouts_.motionTs = getTimestamp();
+ }
+ else
+ {
+ T_list &channelList = activeChannels_.getList();
+
+ for (T_list::iterator j = channelList.begin();
+ j != channelList.end(); j++)
+ {
+ int channelId = *j;
+
+ if (channels_[channelId] != NULL &&
+ channels_[channelId] -> needMotion() == 1)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: SPLIT! Channel for FD#"
+ << getFd(channelId) << " still needs motions.\n"
+ << logofs_flush;
+ #endif
+
+ return;
+ }
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(SPLIT)
+ *logofs << "Proxy: Resetting motion timestamp at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ timeouts_.motionTs = nullTimestamp();
+ }
+ }
+}
+
+void Proxy::increaseChannels(int channelId)
+{
+ #ifdef TEST
+ *logofs << "Proxy: Adding channel " << channelId
+ << " to the list of active channels.\n"
+ << logofs_flush;
+ #endif
+
+ activeChannels_.add(channelId);
+
+ #ifdef TEST
+ *logofs << "Proxy: There are " << activeChannels_.getSize()
+ << " allocated channels for proxy FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+}
+
+void Proxy::decreaseChannels(int channelId)
+{
+ #ifdef TEST
+ *logofs << "Proxy: Removing channel " << channelId
+ << " from the list of active channels.\n"
+ << logofs_flush;
+ #endif
+
+ activeChannels_.remove(channelId);
+
+ #ifdef TEST
+ *logofs << "Proxy: There are " << activeChannels_.getSize()
+ << " allocated channels for proxy FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+}
+
+int Proxy::allocateTransport(int channelFd, int channelId)
+{
+ if (transports_[channelId] == NULL)
+ {
+ transports_[channelId] = new Transport(channelFd);
+
+ if (transports_[channelId] == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Can't allocate transport for "
+ << "channel id " << channelId << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate transport for "
+ << "channel id " << channelId << ".\n";
+
+ return -1;
+ }
+ }
+ else if (transports_[channelId] ->
+ getType() != transport_agent)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Transport for channel id "
+ << channelId << " should be null.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Transport for channel id "
+ << channelId << " should be null.\n";
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int Proxy::deallocateTransport(int channelId)
+{
+ //
+ // Transport for the agent connection
+ // is passed from the outside when
+ // creating the channel.
+ //
+
+ if (transports_[channelId] ->
+ getType() != transport_agent)
+ {
+ delete transports_[channelId];
+ }
+
+ transports_[channelId] = NULL;
+
+ return 1;
+}
+
+int Proxy::handleNewGenericConnection(int clientFd, T_channel_type type, const char *label)
+{
+ int channelId = allocateChannelMap(clientFd);
+
+ if (channelId == -1)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Maximum mumber of available "
+ << "channels exceeded.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Maximum mumber of available "
+ << "channels exceeded.\n";
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "Proxy: Channel for " << label << " descriptor "
+ << "FD#" << clientFd << " mapped to ID#"
+ << channelId << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Turn queuing off for path server-to-proxy.
+ //
+
+ SetNoDelay(clientFd, 1);
+
+ if (allocateTransport(clientFd, channelId) < 0)
+ {
+ return -1;
+ }
+
+ switch (type)
+ {
+ case channel_cups:
+ {
+ channels_[channelId] = new CupsChannel(transports_[channelId], compressor_);
+
+ break;
+ }
+ case channel_smb:
+ {
+ channels_[channelId] = new SmbChannel(transports_[channelId], compressor_);
+
+ break;
+ }
+ case channel_media:
+ {
+ channels_[channelId] = new MediaChannel(transports_[channelId], compressor_);
+
+ break;
+ }
+ case channel_http:
+ {
+ channels_[channelId] = new HttpChannel(transports_[channelId], compressor_);
+
+ break;
+ }
+ case channel_font:
+ {
+ channels_[channelId] = new FontChannel(transports_[channelId], compressor_);
+
+ break;
+ }
+ default:
+ {
+ channels_[channelId] = new SlaveChannel(transports_[channelId], compressor_);
+
+ break;
+ }
+ }
+
+ if (channels_[channelId] == NULL)
+ {
+ deallocateTransport(channelId);
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "Proxy: Accepted new connection to "
+ << label << " server.\n" << logofs_flush;
+ #endif
+
+ cerr << "Info" << ": Accepted new connection to "
+ << label << " server.\n";
+
+ increaseChannels(channelId);
+
+ switch (type)
+ {
+ case channel_cups:
+ {
+ if (handleControl(code_new_cups_connection, channelId) < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ case channel_smb:
+ {
+ if (handleControl(code_new_smb_connection, channelId) < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ case channel_media:
+ {
+ if (handleControl(code_new_media_connection, channelId) < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ case channel_http:
+ {
+ if (handleControl(code_new_http_connection, channelId) < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ case channel_font:
+ {
+ if (handleControl(code_new_font_connection, channelId) < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ default:
+ {
+ if (handleControl(code_new_slave_connection, channelId) < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ }
+
+ channels_[channelId] -> handleConfiguration();
+
+ return 1;
+}
+
+int Proxy::handleNewSlaveConnection(int clientFd)
+{
+ if (control -> isProtoStep7() == 1)
+ {
+ return handleNewGenericConnection(clientFd, channel_slave, "slave");
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Proxy: WARNING! Not sending unsupported "
+ << "'code_new_slave_connection' message.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+}
+
+int Proxy::handleNewGenericConnectionFromProxy(int channelId, T_channel_type type,
+ const char *hostname, int port, const char *label)
+{
+ if (port <= 0)
+ {
+ //
+ // This happens when user has disabled
+ // forwarding of the specific service.
+ //
+
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Refusing attempted connection "
+ << "to " << label << " server.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Refusing attempted connection "
+ << "to " << label << " server.\n";
+
+ return -1;
+ }
+
+ const char *serverHost = hostname;
+ int serverAddrFamily = AF_INET;
+ sockaddr *serverAddr = NULL;
+ unsigned int serverAddrLength = 0;
+
+ #ifdef TEST
+ *logofs << "Proxy: Connecting to " << label
+ << " server '" << serverHost << "' on TCP port '"
+ << port << "'.\n" << logofs_flush;
+ #endif
+
+ int serverIPAddr = GetHostAddress(serverHost);
+
+ if (serverIPAddr == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Unknown " << label
+ << " server host '" << serverHost << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Unknown " << label
+ << " server host '" << serverHost
+ << "'.\n";
+
+ return -1;
+ }
+
+ sockaddr_in *serverAddrTCP = new sockaddr_in;
+
+ serverAddrTCP -> sin_family = AF_INET;
+ serverAddrTCP -> sin_port = htons(port);
+ serverAddrTCP -> sin_addr.s_addr = serverIPAddr;
+
+ serverAddr = (sockaddr *) serverAddrTCP;
+ serverAddrLength = sizeof(sockaddr_in);
+
+ //
+ // Connect to the requested server.
+ //
+
+ int serverFd = socket(serverAddrFamily, SOCK_STREAM, PF_UNSPEC);
+
+ if (serverFd < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Call to socket failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to socket failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+
+ delete serverAddrTCP;
+
+ return -1;
+ }
+ else if (connect(serverFd, serverAddr, serverAddrLength) < 0)
+ {
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Connection to " << label
+ << " server '" << serverHost << ":" << port
+ << "' failed with error '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Connection to " << label
+ << " server '" << serverHost << ":" << port
+ << "' failed with error '" << ESTR() << "'.\n";
+
+ close(serverFd);
+
+ delete serverAddrTCP;
+
+ return -1;
+ }
+
+ delete serverAddrTCP;
+
+ if (handlePostConnectionFromProxy(channelId, serverFd, type, label) < 0)
+ {
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "Proxy: Forwarded new connection to "
+ << label << " server on port '" << port
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Info" << ": Forwarded new connection to "
+ << label << " server on port '" << port
+ << "'.\n";
+
+ return 1;
+}
+
+int Proxy::handleNewGenericConnectionFromProxy(int channelId, T_channel_type type,
+ const char *hostname, const char *path, const char *label)
+{
+ if (path == NULL || *path == '\0' )
+ {
+ //
+ // This happens when user has disabled
+ // forwarding of the specific service.
+ //
+
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Refusing attempted connection "
+ << "to " << label << " server.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Refusing attempted connection "
+ << "to " << label << " server.\n";
+
+ return -1;
+ }
+
+ sockaddr_un serverAddrUnix;
+
+ unsigned int serverAddrLength = sizeof(sockaddr_un);
+
+ int serverAddrFamily = AF_UNIX;
+
+ serverAddrUnix.sun_family = AF_UNIX;
+
+ const int serverAddrNameLength = 108;
+
+ strncpy(serverAddrUnix.sun_path, path, serverAddrNameLength);
+
+ *(serverAddrUnix.sun_path + serverAddrNameLength - 1) = '\0';
+
+ #ifdef TEST
+ *logofs << "Proxy: Connecting to " << label << " server "
+ << "on Unix port '" << path << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // Connect to the requested server.
+ //
+
+ int serverFd = socket(serverAddrFamily, SOCK_STREAM, PF_UNSPEC);
+
+ if (serverFd < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Call to socket failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to socket failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+
+ return -1;
+ }
+ else if (connect(serverFd, (sockaddr *) &serverAddrUnix, serverAddrLength) < 0)
+ {
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Connection to " << label
+ << " server on Unix port '" << path << "' failed "
+ << "with error " << EGET() << ", '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Connection to " << label
+ << " server on Unix port '" << path << "' failed "
+ << "with error " << EGET() << ", '" << ESTR() << "'.\n";
+
+ close(serverFd);
+
+ return -1;
+ }
+
+ if (handlePostConnectionFromProxy(channelId, serverFd, type, label) < 0)
+ {
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "Proxy: Forwarded new connection to "
+ << label << " server on Unix port '" << path
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Info" << ": Forwarded new connection to "
+ << label << " server on Unix port '" << path
+ << "'.\n";
+
+ return 1;
+}
+
+int Proxy::handleNewSlaveConnectionFromProxy(int channelId)
+{
+ //
+ // Implementation is incomplete. Opening a
+ // slave channel should let the proxy fork
+ // a new client and pass to it the channel
+ // descriptors. For now we make the channel
+ // fail immediately.
+ //
+ // #include <fcntl.h>
+ // #include <sys/types.h>
+ // #include <sys/stat.h>
+ //
+ // char *slaveServer = "/dev/null";
+ //
+ // #ifdef TEST
+ // *logofs << "Proxy: Opening file '" << slaveServer
+ // << "'.\n" << logofs_flush;
+ // #endif
+ //
+ // int serverFd = open(slaveServer, O_RDWR);
+ //
+ // if (handlePostConnectionFromProxy(channelId, serverFd, channel_slave, "slave") < 0)
+ // {
+ // return -1;
+ // }
+ //
+
+ #ifdef WARNING
+ *logofs << "Proxy: Refusing new slave connection for "
+ << "channel ID#" << channelId << "\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Refusing new slave connection for "
+ << "channel ID#" << channelId << "\n";
+
+ return -1;
+}
+
+int Proxy::handlePostConnectionFromProxy(int channelId, int serverFd,
+ T_channel_type type, const char *label)
+{
+ //
+ // Turn queuing off for path proxy-to-server.
+ //
+
+ SetNoDelay(serverFd, 1);
+
+ assignChannelMap(channelId, serverFd);
+
+ #ifdef TEST
+ *logofs << "Proxy: Descriptor FD#" << serverFd
+ << " mapped to channel ID#" << channelId << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (allocateTransport(serverFd, channelId) < 0)
+ {
+ return -1;
+ }
+
+ switch (type)
+ {
+ case channel_cups:
+ {
+ channels_[channelId] = new CupsChannel(transports_[channelId], compressor_);
+ break;
+ }
+ case channel_smb:
+ {
+ channels_[channelId] = new SmbChannel(transports_[channelId], compressor_);
+
+ break;
+ }
+ case channel_media:
+ {
+ channels_[channelId] = new MediaChannel(transports_[channelId], compressor_);
+
+ break;
+ }
+ case channel_http:
+ {
+ channels_[channelId] = new HttpChannel(transports_[channelId], compressor_);
+
+ break;
+ }
+ case channel_font:
+ {
+ channels_[channelId] = new FontChannel(transports_[channelId], compressor_);
+
+ break;
+ }
+ default:
+ {
+ channels_[channelId] = new SlaveChannel(transports_[channelId], compressor_);
+
+ break;
+ }
+ }
+
+ if (channels_[channelId] == NULL)
+ {
+ deallocateTransport(channelId);
+
+ return -1;
+ }
+
+ increaseChannels(channelId);
+
+ channels_[channelId] -> handleConfiguration();
+
+ return 1;
+}
diff --git a/nxcomp/Proxy.h b/nxcomp/Proxy.h
new file mode 100644
index 000000000..3d6c62c54
--- /dev/null
+++ b/nxcomp/Proxy.h
@@ -0,0 +1,1263 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef Proxy_H
+#define Proxy_H
+
+#include <sys/types.h>
+
+#ifdef _AIX
+#include <sys/select.h>
+#endif
+
+#include "Misc.h"
+#include "Timestamp.h"
+
+#include "List.h"
+#include "Channel.h"
+#include "Transport.h"
+#include "EncodeBuffer.h"
+#include "ProxyReadBuffer.h"
+
+//
+// Forward declaration as we
+// need a pointer.
+//
+
+class Agent;
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Log the important tracepoints related
+// to writing packets to the peer proxy.
+//
+
+#undef FLUSH
+
+//
+// Codes used for control messages in
+// proxy-to-proxy protocol.
+//
+// The following codes are currently
+// unused.
+//
+// code_alert_reply,
+// code_reset_request,
+// code_reset_reply,
+// code_load_reply,
+// code_save_reply,
+// code_shutdown_reply,
+// code_configuration_request,
+// code_configuration_reply.
+//
+// These are for compatibility with
+// old versions.
+//
+// code_sync_request,
+// code_sync_reply,
+//
+// The code_new_aux_connection should not
+// be used anymore. Auxiliary X connections
+// are treated as normal X channels since
+// version 1.5.0.
+//
+
+typedef enum
+{
+ code_new_x_connection,
+ code_new_cups_connection,
+ code_new_aux_connection,
+ code_new_smb_connection,
+ code_new_media_connection,
+ code_switch_connection,
+ code_drop_connection,
+ code_finish_connection,
+ code_begin_congestion,
+ code_end_congestion,
+ code_alert_request,
+ code_alert_reply,
+ code_reset_request,
+ code_reset_reply,
+ code_load_request,
+ code_load_reply,
+ code_save_request,
+ code_save_reply,
+ code_shutdown_request,
+ code_shutdown_reply,
+ code_control_token_request,
+ code_control_token_reply,
+ code_configuration_request,
+ code_configuration_reply,
+ code_statistics_request,
+ code_statistics_reply,
+ code_new_http_connection,
+ code_sync_request,
+ code_sync_reply,
+ code_new_font_connection,
+ code_new_slave_connection,
+ code_finish_listeners,
+ code_split_token_request,
+ code_split_token_reply,
+ code_data_token_request,
+ code_data_token_reply,
+ code_last_tag
+
+} T_proxy_code;
+
+typedef enum
+{
+ operation_in_negotiation,
+ operation_in_messages,
+ operation_in_configuration,
+ operation_in_statistics,
+ operation_last_tag
+
+} T_proxy_operation;
+
+typedef enum
+{
+ frame_ping,
+ frame_data,
+
+} T_frame_type;
+
+typedef enum
+{
+ token_control,
+ token_split,
+ token_data
+
+} T_token_type;
+
+typedef enum
+{
+ load_if_any,
+ load_if_first
+
+} T_load_type;
+
+class Proxy
+{
+ public:
+
+ //
+ // Maximum number of supported channels.
+ //
+
+ static const int CONNECTIONS_LIMIT = 256;
+
+ //
+ // Numboer of token types.
+ //
+
+ static const int TOKEN_TYPES = 3;
+
+ //
+ // Lenght of buffer we use to add our
+ // control messages plus the length of
+ // the data frame.
+ //
+
+ static const int CONTROL_CODES_LENGTH = ENCODE_BUFFER_PREFIX_SIZE - 5;
+
+ static const int CONTROL_CODES_THRESHOLD = CONTROL_CODES_LENGTH - 9;
+
+ Proxy(int fd);
+
+ virtual ~Proxy();
+
+ //
+ // Inform the proxy that the negotiation phase is
+ // completed and that it can start handling binary
+ // messages.
+ //
+
+ int setOperational();
+
+ int getOperational()
+ {
+ return (operation_ != operation_in_negotiation);
+ }
+
+ int setReadDescriptors(fd_set *fdSet, int &fdMax, T_timestamp &tsMax);
+
+ int setWriteDescriptors(fd_set *fdSet, int &fdMax, T_timestamp &tsMax);
+
+ //
+ // Perform the operation on the proxy
+ // link or its own channels.
+ //
+
+ int handleRead(int &resultFds, fd_set &fdSet);
+
+ int handleFlush(int &resultFds, fd_set &fdSet);
+
+ int handleRead();
+
+ int handleRead(int fd, const char *data = NULL, int size = 0);
+
+ int handleEvents();
+
+ int handleFlush();
+
+ int handleFlush(int fd);
+
+ int handlePing();
+
+ int handleFinish();
+
+ int handleShutdown();
+
+ int handleStatistics(int type, ostream *statofs);
+
+ int handleAlert(int alert);
+
+ int handleRotate()
+ {
+ activeChannels_.rotate();
+
+ return 1;
+ }
+
+ int handleChannelConfiguration();
+
+ int handleSocketConfiguration();
+
+ int handleLinkConfiguration();
+
+ int handleCacheConfiguration();
+
+ //
+ // These must be called just after initialization to
+ // tell to the proxy where the network connections
+ // have to be forwarded.
+ //
+
+ virtual void handleDisplayConfiguration(const char *xServerDisplay, int xServerAddrFamily,
+ sockaddr * xServerAddr, unsigned int xServerAddrLength) = 0;
+
+ virtual void handlePortConfiguration(int cupsServerPort, int smbServerPort, int mediaServerPort,
+ int httpServerPort, const char *fontServerPort) = 0;
+
+ //
+ // Create new tunneled channels.
+ //
+
+ virtual int handleNewConnection(T_channel_type type, int clientFd) = 0;
+
+ virtual int handleNewConnectionFromProxy(T_channel_type type, int channelId) = 0;
+
+ virtual int handleNewAgentConnection(Agent *agent) = 0;
+
+ virtual int handleNewXConnection(int clientFd) = 0;
+
+ virtual int handleNewXConnectionFromProxy(int channelId) = 0;
+
+ int handleNewGenericConnection(int clientFd, T_channel_type type, const char *label);
+
+ int handleNewGenericConnectionFromProxy(int channelId, T_channel_type type,
+ const char *hostname, int port, const char *label);
+
+ int handleNewGenericConnectionFromProxy(int channelId, T_channel_type type,
+ const char *hostname, const char *path, const char *label);
+
+ int handleNewSlaveConnection(int clientFd);
+
+ int handleNewSlaveConnectionFromProxy(int channelId);
+
+ //
+ // Force closure of channels.
+ //
+
+ int handleCloseConnection(int clientFd);
+
+ int handleCloseAllXConnections();
+
+ int handleCloseAllListeners();
+
+ //
+ // Called when the loop has replaced
+ // or closed a previous alert.
+ //
+
+ void handleResetAlert();
+
+ //
+ // Handle the persistent cache.
+ //
+
+ virtual int handleLoad(T_load_type type) = 0;
+
+ virtual int handleSave() = 0;
+
+ protected:
+
+ //
+ // Timeout related data:
+ //
+ // flush
+ // split
+ // motion
+ //
+ // Timeouts in milliseconds after which the
+ // proxy will have to perform the operation.
+ //
+ // readTs, writeTs
+ //
+ // Timestamp of last packet received or sent
+ // to remote proxy. Used to detect lost con-
+ // nection.
+ //
+ // loopTs
+ //
+ // Timestamp of last loop completed by the
+ // proxy
+ //
+ // pingTs
+ //
+ // Timestamp of last ping request sent to the
+ // remote peer.
+ //
+ // alertTs
+ //
+ // Timestamp of last 'no data received' alert
+ // dialog shown to the user.
+ //
+ // loadTs
+ //
+ // Were message stores populated from data on
+ // disk.
+ //
+ // splitTs
+ // motionTs
+ //
+ // Timestamps of the last operation of this
+ // kind handled by the proxy.
+ //
+
+ typedef struct
+ {
+ int split;
+ int motion;
+
+ T_timestamp readTs;
+ T_timestamp writeTs;
+
+ T_timestamp loopTs;
+ T_timestamp pingTs;
+ T_timestamp alertTs;
+ T_timestamp loadTs;
+
+ T_timestamp splitTs;
+ T_timestamp motionTs;
+
+ } T_proxy_timeouts;
+
+ //
+ // Bytes accumulated so far while waiting
+ // to send the next token, number of tokens
+ // remaining for each token type and other
+ // token related information.
+ //
+
+ typedef struct
+ {
+ int size;
+ int limit;
+
+ int bytes;
+ int remaining;
+
+ T_proxy_code request;
+ T_proxy_code reply;
+
+ T_token_type type;
+
+ } T_proxy_token;
+
+ int handlePostConnectionFromProxy(int channelId, int serverFd,
+ T_channel_type type, const char *label);
+
+ int handleDrain();
+
+ int handleFrame(T_frame_type type);
+
+ int handleFinish(int channelId);
+
+ int handleDrop(int channelId);
+
+ int handleFinishFromProxy(int channelId);
+
+ int handleDropFromProxy(int channelId);
+
+ int handleStatisticsFromProxy(int type);
+
+ int handleStatisticsFromProxy(const unsigned char *message, unsigned int length);
+
+ int handleNegotiation(const unsigned char *message, unsigned int length);
+
+ int handleNegotiationFromProxy(const unsigned char *message, unsigned int length);
+
+ int handleToken(T_frame_type type);
+
+ int handleTokenFromProxy(T_proxy_token &token, int count);
+
+ int handleTokenReplyFromProxy(T_proxy_token &token, int count);
+
+ int handleSyncFromProxy(int channelId);
+
+ int handleSwitch(int channelId);
+
+ int handleControl(T_proxy_code code, int data = -1);
+
+ int handleControlFromProxy(const unsigned char *message);
+
+ //
+ // Interleave reads of the X server
+ // events while writing data to the
+ // remote proxy.
+ //
+
+ virtual int handleAsyncEvents() = 0;
+
+ //
+ // Timer related functions.
+ //
+
+ protected:
+
+ void setTimer(int value)
+ {
+ SetTimer(value);
+ }
+
+ void resetTimer()
+ {
+ ResetTimer();
+
+ timer_ = 0;
+ }
+
+ public:
+
+ void handleTimer()
+ {
+ timer_ = 1;
+ }
+
+ int getTimer()
+ {
+ return timer_;
+ }
+
+ //
+ // Can the channel consume data and the
+ // proxy produce more output?
+ //
+
+ int canRead(int fd) const
+ {
+ return (isTimeToRead() == 1 &&
+ isTimeToRead(getChannel(fd)) == 1);
+ }
+
+ //
+ // Can the proxy read more data from its
+ // peer?
+ //
+
+ int canRead() const
+ {
+ return (transport_ -> readable() != 0);
+ }
+
+ int canFlush() const
+ {
+ return (encodeBuffer_.getLength() +
+ controlLength_ + transport_ -> length() +
+ transport_ -> flushable() > 0);
+ }
+
+ int needFlush(int channelId) const
+ {
+ return (outputChannel_ == channelId &&
+ encodeBuffer_.getLength() > 0);
+ }
+
+ int needFlush() const
+ {
+ return (encodeBuffer_.getLength() > 0);
+ }
+
+ int shouldFlush() const
+ {
+ if ((int) ((encodeBuffer_.getLength() +
+ controlLength_) / statistics ->
+ getStreamRatio()) >= control -> TokenSize)
+ {
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+ *logofs << "Proxy: FLUSH! Requesting a flush with "
+ << (encodeBuffer_.getLength() + controlLength_)
+ << " bytes and stream ratio " << (double) statistics ->
+ getStreamRatio() << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+ *logofs << "Proxy: Not requesting a flush with "
+ << (encodeBuffer_.getLength() + controlLength_)
+ << " bytes and stream ratio " << (double) statistics ->
+ getStreamRatio() << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ int needDrain() const
+ {
+ return (congestion_ == 1 || transport_ -> length() >
+ control -> TransportProxyBufferThreshold);
+ }
+
+ int getFd() const
+ {
+ return fd_;
+ }
+
+ int getFlushable(int fd) const
+ {
+ if (fd == fd_)
+ {
+ return (encodeBuffer_.getLength() + controlLength_ +
+ transport_ -> flushable());
+ }
+
+ return 0;
+ }
+
+ int getSplitSize()
+ {
+ return (clientStore_ != NULL ? clientStore_ ->
+ getSplitTotalSize() : 0);
+ }
+
+ int getSplitStorageSize()
+ {
+ return (clientStore_ != NULL ? clientStore_ ->
+ getSplitTotalStorageSize() : 0);
+ }
+
+ //
+ // Returns the number of active channels
+ // that currently managed by this proxy.
+ //
+
+ int getChannels(T_channel_type type = channel_none);
+
+ //
+ // If descriptor corresponds to a valid
+ // channel, returns the type of traffic
+ // carried by it.
+ //
+
+ T_channel_type getType(int fd);
+
+ //
+ // Given a channel type, returns the
+ // literal name.
+ //
+
+ const char *getTypeName(T_channel_type type);
+
+ //
+ // Get a convenient name for 'localhost'.
+ //
+
+ const char *getComputerName();
+
+ //
+ // Set if we have initiated the shutdown
+ // procedure and if the shutdown request
+ // has been received from the remote.
+ //
+
+ int getFinish() const
+ {
+ return finish_;
+ }
+
+ int getShutdown() const
+ {
+ return shutdown_;
+ }
+
+ //
+ // Interfaces to the transport buffers.
+ //
+
+ int getLength(int fd) const
+ {
+ if (fd == fd_)
+ {
+ return transport_ -> length();
+ }
+
+ int channelId = getChannel(fd);
+
+ if (channelId < 0 || channels_[channelId] == NULL)
+ {
+ return 0;
+ }
+
+ return transports_[channelId] -> length();
+ }
+
+ int getPending(int fd) const
+ {
+ if (fd == fd_)
+ {
+ return transport_ -> pending();
+ }
+
+ int channelId = getChannel(fd);
+
+ if (channelId < 0 || channels_[channelId] == NULL)
+ {
+ return 0;
+ }
+
+ return transports_[channelId] -> pending();
+ }
+
+ //
+ // Check if the proxy or the given channel
+ // has data in the buffer because of a
+ // blocking write.
+ //
+
+ int getBlocked(int fd) const
+ {
+ if (fd == fd_)
+ {
+ return transport_ -> blocked();
+ }
+
+ int channelId = getChannel(fd);
+
+ if (channelId < 0 || channels_[channelId] == NULL)
+ {
+ return 0;
+ }
+
+ return transports_[channelId] -> blocked();
+ }
+
+ //
+ // Check if the proxy or the given channel has
+ // data to read.
+ //
+
+ int getReadable(int fd) const
+ {
+ if (fd == fd_)
+ {
+ return transport_ -> readable();
+ }
+
+ int channelId = getChannel(fd);
+
+ if (channelId < 0 || channels_[channelId] == NULL)
+ {
+ return 0;
+ }
+
+ return transports_[channelId] -> readable();
+ }
+
+ //
+ // Return a vale between 0 and 9 in the case
+ // of the proxy descriptor.
+ //
+
+ int getCongestion(int fd) const
+ {
+ if (fd == fd_)
+ {
+ return (agent_ != nothing && congestions_[agent_] == 1 ? 9 :
+ (int) statistics -> getCongestionInFrame());
+ }
+
+ int channelId = getChannel(fd);
+
+ if (channelId < 0 || channels_[channelId] == NULL)
+ {
+ return 0;
+ }
+
+ return channels_[channelId] -> getCongestion();
+ }
+
+ //
+ // Let the statistics class get info
+ // from the message stores.
+ //
+
+ const ClientStore * const getClientStore() const
+ {
+ return clientStore_;
+ }
+
+ const ServerStore * const getServerStore() const
+ {
+ return serverStore_;
+ }
+
+ //
+ // These can be called asynchronously by
+ // channels during their read or write
+ // loop.
+ //
+
+ int handleAsyncRead(int fd)
+ {
+ return handleRead(fd);
+ }
+
+ int handleAsyncCongestion(int fd)
+ {
+ int channelId = getChannel(fd);
+
+ return handleControl(code_begin_congestion, channelId);
+ }
+
+ int handleAsyncDecongestion(int fd)
+ {
+ int channelId = getChannel(fd);
+
+ return handleControl(code_end_congestion, channelId);
+ }
+
+ int handleAsyncSplit(int fd, Split *split)
+ {
+ return channels_[getChannel(fd)] ->
+ handleSplitEvent(encodeBuffer_, split);
+ }
+
+ int handleAsyncInit()
+ {
+ return handleFlush();
+ }
+
+ int handleAsyncPriority()
+ {
+ if (control -> FlushPriority == 1)
+ {
+ return handleFlush();
+ }
+
+ return 0;
+ }
+
+ int canAsyncFlush() const
+ {
+ return shouldFlush();
+ }
+
+ int handleAsyncFlush()
+ {
+ return handleFlush();
+ }
+
+ int handleAsyncSwitch(int fd)
+ {
+ return handleSwitch(getChannel(fd));
+ }
+
+ int handleAsyncKeeperCallback()
+ {
+ KeeperCallback();
+
+ return 1;
+ }
+
+ //
+ // Internal interfaces used to verify the
+ // availability of channels and the proxy
+ // link.
+ //
+
+ protected:
+
+ int isTimeToRead() const
+ {
+ if (congestion_ == 0 && transport_ ->
+ blocked() == 0)
+ {
+ return 1;
+ }
+ else
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Can't read from channels "
+ << "with congestion " << congestion_
+ << " and blocked " << transport_ ->
+ blocked() << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ }
+
+ int isTimeToRead(int channelId) const
+ {
+ if (channelId >= 0 && channelId < CONNECTIONS_LIMIT &&
+ channels_[channelId] != NULL &&
+ congestions_[channelId] == 0)
+ {
+ if (channels_[channelId] -> getType() == channel_x11 ||
+ tokens_[token_data].remaining > 0 ||
+ channels_[channelId] ->
+ getFinish() == 1)
+ {
+ return 1;
+ }
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Can't read from generic "
+ << "descriptor FD#" << getFd(channelId)
+ << " channel ID#" << channelId << " with "
+ << tokens_[token_data].remaining
+ << " data tokens remaining.\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ #if defined(TEST) || defined(INFO)
+
+ if (channelId < 0 || channelId >= CONNECTIONS_LIMIT ||
+ channels_[channelId] == NULL)
+ {
+ *logofs << "Proxy: WARNING! No valid channel for ID#"
+ << channelId << ".\n" << logofs_flush;
+ }
+ else if (channels_[channelId] -> getFinish())
+ {
+ *logofs << "Proxy: Can't read from finishing "
+ << "descriptor FD#" << getFd(channelId)
+ << " channel ID#" << channelId << ".\n"
+ << logofs_flush;
+ }
+ else if (congestions_[channelId] == 1)
+ {
+ *logofs << "Proxy: Can't read from congested "
+ << "descriptor FD#" << getFd(channelId)
+ << " channel ID#" << channelId << ".\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ return 0;
+ }
+
+ //
+ // Handle the flush and split timeouts.
+ // All these functions should round up
+ // to the value of the latency timeout
+ // to save a further loop.
+ //
+
+ protected:
+
+ int isTimeToSplit() const
+ {
+ if (isTimestamp(timeouts_.splitTs) &&
+ getTimeToNextSplit() <= control ->
+ LatencyTimeout)
+ {
+ if (tokens_[token_split].remaining > 0)
+ {
+ return 1;
+ }
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: WARNING! Can't encode splits "
+ << "with " << tokens_[token_split].remaining
+ << " split tokens remaining.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ return 0;
+ }
+
+ int isTimeToMotion() const
+ {
+ return (isTimestamp(timeouts_.motionTs) &&
+ getTimeToNextMotion() <= control ->
+ LatencyTimeout);
+ }
+
+ int getTimeToNextSplit() const
+ {
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+
+ if (isTimestamp(timeouts_.splitTs) == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! No split timeout was set "
+ << "for proxy FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": No split timeout was set "
+ << "for proxy FD#" << fd_ << ".\n";
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ int diffTs = timeouts_.split -
+ diffTimestamp(timeouts_.splitTs,
+ getTimestamp());
+
+ return (diffTs > 0 ? diffTs : 0);
+ }
+
+ int getTimeToNextMotion() const
+ {
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+
+ if (isTimestamp(timeouts_.motionTs) == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! No motion timeout was set "
+ << "for proxy FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": No motion timeout was set "
+ << "for proxy FD#" << fd_ << ".\n";
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ int diffTs = timeouts_.motion -
+ diffTimestamp(timeouts_.motionTs,
+ getTimestamp());
+
+ return (diffTs > 0 ? diffTs : 0);
+ }
+
+ protected:
+
+ //
+ // Implement persistence of cache on disk.
+ //
+
+ virtual int handleLoadFromProxy() = 0;
+ virtual int handleSaveFromProxy() = 0;
+
+ int handleLoadStores();
+ int handleSaveStores();
+
+ char *handleSaveAllStores(const char *savePath) const;
+
+ virtual int handleSaveAllStores(ostream *cachefs, md5_state_t *md5StateStream,
+ md5_state_t *md5StateClient) const = 0;
+
+ int handleSaveVersion(unsigned char *buffer, int &major, int &minor, int &patch) const;
+
+ void handleFailOnSave(const char *fullName, const char *failContext) const;
+
+ const char *handleLoadAllStores(const char *loadPath, const char *loadName) const;
+
+ virtual int handleLoadAllStores(istream *cachefs, md5_state_t *md5StateStream) const = 0;
+
+ int handleLoadVersion(const unsigned char *buffer, int &major, int &minor, int &patch) const;
+
+ void handleFailOnLoad(const char *fullName, const char *failContext) const;
+
+ //
+ // Prepare for a new persistent cache.
+ //
+
+ int handleResetPersistentCache();
+
+ //
+ // Reset the stores in the case of a
+ // failure loading the cache.
+ //
+
+ int handleResetStores();
+
+ //
+ // Reset the transport buffer and the
+ // other counters.
+ //
+
+ void handleResetFlush();
+
+ //
+ // Utility functions used to map file
+ // descriptors to channel ids.
+ //
+
+ protected:
+
+ int allocateChannelMap(int fd);
+ int checkChannelMap(int channelId);
+ int assignChannelMap(int channelId, int fd);
+
+ void cleanupChannelMap(int channelId);
+
+ virtual int checkLocalChannelMap(int channelId) = 0;
+
+ int addControlCodes(T_proxy_code code, int data);
+ int addTokenCodes(T_proxy_token &token);
+
+ int getFd(int channelId) const
+ {
+ if (channelId >= 0 && channelId < CONNECTIONS_LIMIT)
+ {
+ return fdMap_[channelId];
+ }
+
+ return -1;
+ }
+
+ int getChannel(int fd) const
+ {
+ if (fd >= 0 && fd < CONNECTIONS_LIMIT)
+ {
+ return channelMap_[fd];
+ }
+
+ return -1;
+ }
+
+ protected:
+
+ void setSplitTimeout(int channelId);
+ void setMotionTimeout(int channelId);
+
+ void increaseChannels(int channelId);
+ void decreaseChannels(int channelId);
+
+ int allocateTransport(int channelFd, int channelId);
+ int deallocateTransport(int channelId);
+
+ //
+ // The proxy has its own transport.
+ //
+
+ ProxyTransport *transport_;
+
+ //
+ // The static compressor is shared among
+ // channels and all the message stores.
+ //
+
+ StaticCompressor *compressor_;
+
+ //
+ // Map NX specific opcodes.
+ //
+
+ OpcodeStore *opcodeStore_;
+
+ //
+ // Stores are shared between channels.
+ //
+
+ ClientStore *clientStore_;
+ ServerStore *serverStore_;
+
+ //
+ // Client and server caches are shared
+ // between channels.
+ //
+
+ ClientCache *clientCache_;
+ ServerCache *serverCache_;
+
+ //
+ // The proxy's file descriptor.
+ //
+
+ int fd_;
+
+ //
+ // Channels currently selected for I/O
+ // operations.
+ //
+
+ int inputChannel_;
+ int outputChannel_;
+
+ //
+ // List of active channels.
+ //
+
+ List activeChannels_;
+
+ //
+ // Used to read data sent from peer proxy.
+ //
+
+ ProxyReadBuffer readBuffer_;
+
+ //
+ // Used to send data to peer proxy.
+ //
+
+ EncodeBuffer encodeBuffer_;
+
+ //
+ // Control messages' array.
+ //
+
+ int controlLength_;
+
+ unsigned char controlCodes_[CONTROL_CODES_LENGTH];
+
+ //
+ // Table of channel classes taking
+ // care of open X connections.
+ //
+
+ Channel *channels_[CONNECTIONS_LIMIT];
+
+ //
+ // Table of open sockets.
+ //
+
+ Transport *transports_[CONNECTIONS_LIMIT];
+
+ //
+ // Timeout related data.
+ //
+
+ T_proxy_timeouts timeouts_;
+
+ //
+ // Proxy can be decoding messages,
+ // handling a link reconfiguration,
+ // or decoding statistics.
+ //
+
+ int operation_;
+
+ //
+ // True if we are currently draining
+ // the proxy link.
+ //
+
+ int draining_;
+
+ //
+ // Force flush because of prioritized
+ // control messages to send.
+ //
+
+ int priority_;
+
+ //
+ // Set if we have initiated the close
+ // down procedure.
+ //
+
+ int finish_;
+
+ //
+ // Remote peer requested the shutdown.
+ //
+
+ int shutdown_;
+
+ //
+ // We are in the middle of a network
+ // congestion in the path to remote
+ // proxy.
+ //
+
+ int congestion_;
+
+ //
+ // Channels at the remote end that
+ // are not consuming their data.
+ //
+
+ int congestions_[CONNECTIONS_LIMIT];
+
+ //
+ // Is the timer expired?
+ //
+
+ int timer_;
+
+ //
+ // Did the proxy request an alert?
+ //
+
+ int alert_;
+
+ //
+ // The channel id of the agent.
+ //
+
+ int agent_;
+
+ //
+ // Token related data.
+ //
+
+ T_proxy_token tokens_[TOKEN_TYPES];
+
+ //
+ // Pointer to stream descriptor where
+ // proxy is producing statistics.
+ //
+
+ ostream *currentStatistics_;
+
+ private:
+
+ //
+ // Map channel ids to file descriptors.
+ //
+
+ int channelMap_[CONNECTIONS_LIMIT];
+ int fdMap_[CONNECTIONS_LIMIT];
+};
+
+#endif /* Proxy_H */
diff --git a/nxcomp/ProxyReadBuffer.cpp b/nxcomp/ProxyReadBuffer.cpp
new file mode 100644
index 000000000..b0de14921
--- /dev/null
+++ b/nxcomp/ProxyReadBuffer.cpp
@@ -0,0 +1,199 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "ProxyReadBuffer.h"
+
+#include "Transport.h"
+
+//
+// Set the verbosity level. You also
+// need to define DUMP in Misc.cpp
+// if DUMP is defined here.
+//
+
+#define WARNING
+#define PANIC
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+unsigned int ProxyReadBuffer::suggestedLength(unsigned int pendingLength)
+{
+ //
+ // Always read all the data that
+ // is available.
+ //
+
+ int readable = transport_ -> readable();
+
+ unsigned int readLength = (readable == -1 ? 0 : (unsigned int) readable);
+
+ if (readLength < pendingLength)
+ {
+ readLength = pendingLength;
+ }
+
+ //
+ // Even if the readable data is not
+ // enough to make a complete message,
+ // resize the buffer to accomodate
+ // it all.
+ //
+
+ if (pendingLength < remaining_)
+ {
+ readLength = remaining_;
+ }
+
+ return readLength;
+}
+
+int ProxyReadBuffer::locateMessage(const unsigned char *start,
+ const unsigned char *end,
+ unsigned int &controlLength,
+ unsigned int &dataLength,
+ unsigned int &trailerLength)
+{
+ unsigned int lengthLength = 0;
+ const unsigned char *nextSrc = start;
+ unsigned char next;
+
+ dataLength = 0;
+
+ #ifdef TEST
+ *logofs << "ProxyReadBuffer: Locating message for FD#"
+ << transport_ -> fd() << " with " << end - start
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ //
+ // Use something like the following if
+ // you are looking for errors.
+ //
+
+ #ifdef DUMP
+ if (control -> ProxyMode == proxy_server && start < end &&
+ transport_ -> fd() == 6 || transport_ -> fd() == 11)
+ {
+ *logofs << "ProxyReadBuffer: Partial checksums are:\n";
+
+ DumpBlockChecksums(start, end - start, 256);
+
+ *logofs << logofs_flush;
+ }
+ #endif
+
+ do
+ {
+ if (nextSrc >= end)
+ {
+ remaining_ = 1;
+
+ #ifdef TEST
+ *logofs << "ProxyReadBuffer: No message was located "
+ << "with remaining " << remaining_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ next = *nextSrc++;
+
+ dataLength <<= 7;
+ dataLength |= (unsigned int) (next & 0x7f);
+
+ lengthLength++;
+ }
+ while (next & 0x80);
+
+ unsigned int totalLength;
+
+ if (dataLength == 0)
+ {
+ trailerLength = 0;
+ controlLength = 3;
+ totalLength = controlLength;
+ }
+ else
+ {
+ trailerLength = lengthLength;
+ controlLength = 0;
+ totalLength = dataLength + trailerLength;
+ }
+
+ if (start + totalLength > end)
+ {
+ //
+ // When having to decompress a ZLIB stream,
+ // a single byte can be enough to complete
+ // the frame.
+ //
+
+ if (control -> RemoteStreamCompression == 0)
+ {
+ remaining_ = totalLength - (end - start);
+ }
+ else
+ {
+ remaining_ = 1;
+ }
+
+ #ifdef TEST
+ *logofs << "ProxyReadBuffer: No message was located "
+ << "with remaining " << remaining_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else
+ {
+ #ifdef DUMP
+ *logofs << "ProxyReadBuffer: Received " << totalLength << " bytes of data "
+ << "with checksum ";
+
+ DumpChecksum(start, totalLength);
+
+ *logofs << " on proxy FD#" << transport_ -> fd() << ".\n" << logofs_flush;
+ #endif
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "ProxyReadBuffer: Produced plain input for " << dataLength
+ << "+" << trailerLength << "+" << controlLength << " bytes out of "
+ << totalLength << " bytes.\n" << logofs_flush;
+ #endif
+
+ #ifdef DUMP
+ *logofs << "ProxyReadBuffer: Partial checksums are:\n";
+
+ DumpBlockChecksums(start, totalLength, 256);
+
+ *logofs << logofs_flush;
+ #endif
+
+ remaining_ = 0;
+
+ #ifdef TEST
+ *logofs << "ProxyReadBuffer: Located message with "
+ << "remaining " << remaining_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+ }
+}
diff --git a/nxcomp/ProxyReadBuffer.h b/nxcomp/ProxyReadBuffer.h
new file mode 100644
index 000000000..b87b215bf
--- /dev/null
+++ b/nxcomp/ProxyReadBuffer.h
@@ -0,0 +1,49 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef ProxyReadBuffer_H
+#define ProxyReadBuffer_H
+
+#include "ReadBuffer.h"
+#include "Control.h"
+
+class ProxyReadBuffer : public ReadBuffer
+{
+ public:
+
+ ProxyReadBuffer(Transport *transport)
+
+ : ReadBuffer(transport)
+ {
+ }
+
+ virtual ~ProxyReadBuffer()
+ {
+ }
+
+ protected:
+
+ virtual unsigned int suggestedLength(unsigned int pendingLength);
+
+ virtual int locateMessage(const unsigned char *start,
+ const unsigned char *end,
+ unsigned int &controlLength,
+ unsigned int &dataLength,
+ unsigned int &trailerLength);
+};
+
+#endif /* ProxyReadBuffer_H */
diff --git a/nxcomp/PutImage.cpp b/nxcomp/PutImage.cpp
new file mode 100644
index 000000000..325a4aa66
--- /dev/null
+++ b/nxcomp/PutImage.cpp
@@ -0,0 +1,405 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "PutImage.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "WriteBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Constructors and destructors.
+//
+
+PutImageStore::PutImageStore(StaticCompressor *compressor)
+
+ : MessageStore(compressor)
+{
+ enableCache = PUTIMAGE_ENABLE_CACHE;
+ enableData = PUTIMAGE_ENABLE_DATA;
+ enableSplit = PUTIMAGE_ENABLE_SPLIT;
+ enableCompress = PUTIMAGE_ENABLE_COMPRESS;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ enableCompress = PUTIMAGE_ENABLE_COMPRESS_IF_PROTO_STEP_7;
+ }
+
+ dataLimit = PUTIMAGE_DATA_LIMIT;
+ dataOffset = PUTIMAGE_DATA_OFFSET;
+
+ cacheSlots = PUTIMAGE_CACHE_SLOTS;
+ cacheThreshold = PUTIMAGE_CACHE_THRESHOLD;
+ cacheLowerThreshold = PUTIMAGE_CACHE_LOWER_THRESHOLD;
+
+ if (control -> isProtoStep8() == 1)
+ {
+ enableSplit = PUTIMAGE_ENABLE_SPLIT_IF_PROTO_STEP_8;
+ }
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+PutImageStore::~PutImageStore()
+{
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int PutImageStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding full message identity.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeValue(GetUINT(buffer + 2, bigEndian), 16, 8);
+
+ encodeBuffer.encodeValue((unsigned int) buffer[1], 2);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 4, bigEndian),
+ clientCache -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian),
+ clientCache -> gcCache);
+
+ unsigned int width = GetUINT(buffer + 12, bigEndian);
+ encodeBuffer.encodeCachedValue(width, 16,
+ clientCache -> putImageWidthCache, 8);
+
+ unsigned int height = GetUINT(buffer + 14, bigEndian);
+ encodeBuffer.encodeCachedValue(height, 16,
+ clientCache -> putImageHeightCache, 8);
+
+ unsigned int x = GetUINT(buffer + 16, bigEndian);
+ int xDiff = x - clientCache -> putImageLastX;
+ clientCache -> putImageLastX = x;
+ encodeBuffer.encodeCachedValue(xDiff, 16,
+ clientCache -> putImageXCache, 8);
+
+ unsigned int y = GetUINT(buffer + 18, bigEndian);
+ int yDiff = y - clientCache -> putImageLastY;
+ clientCache -> putImageLastY = y;
+ encodeBuffer.encodeCachedValue(yDiff, 16,
+ clientCache -> putImageYCache, 8);
+
+ encodeBuffer.encodeCachedValue(buffer[20], 8,
+ clientCache -> putImageLeftPadCache);
+
+ encodeBuffer.encodeCachedValue(buffer[21], 8,
+ clientCache -> depthCache);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int PutImageStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding full message identity.\n" << logofs_flush;
+ #endif
+
+ unsigned int value;
+ unsigned char cValue;
+
+ decodeBuffer.decodeValue(value, 16, 8);
+
+ size = (value << 2);
+
+ buffer = writeBuffer -> addMessage(size);
+
+ decodeBuffer.decodeValue(value, 2);
+ buffer[1] = (unsigned char) value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+ PutULONG(value, buffer + 4, bigEndian);
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+ PutULONG(value, buffer + 8, bigEndian);
+
+ unsigned int width;
+ decodeBuffer.decodeCachedValue(width, 16,
+ clientCache -> putImageWidthCache, 8);
+ PutUINT(width, buffer + 12, bigEndian);
+
+ unsigned int height;
+ decodeBuffer.decodeCachedValue(height, 16,
+ clientCache -> putImageHeightCache, 8);
+ PutUINT(height, buffer + 14, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageXCache, 8);
+ clientCache -> putImageLastX += value;
+ clientCache -> putImageLastX &= 0xffff;
+ PutUINT(clientCache -> putImageLastX, buffer + 16, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageYCache, 8);
+ clientCache -> putImageLastY += value;
+ clientCache -> putImageLastY &= 0xffff;
+ PutUINT(clientCache -> putImageLastY, buffer + 18, bigEndian);
+
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> putImageLeftPadCache);
+ buffer[20] = cValue;
+
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> depthCache);
+ buffer[21] = cValue;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int PutImageStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PutImageMessage *putImage = (PutImageMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ putImage -> format = *(buffer + 1);
+ putImage -> depth = *(buffer + 21);
+ putImage -> left_pad = *(buffer + 20);
+
+ putImage -> width = GetUINT(buffer + 12, bigEndian);
+ putImage -> height = GetUINT(buffer + 14, bigEndian);
+ putImage -> pos_x = GetUINT(buffer + 16, bigEndian);
+ putImage -> pos_y = GetUINT(buffer + 18, bigEndian);
+
+ putImage -> drawable = GetULONG(buffer + 4, bigEndian);
+ putImage -> gcontext = GetULONG(buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int PutImageStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PutImageMessage *putImage = (PutImageMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ *(buffer + 1) = putImage -> format;
+
+ PutULONG(putImage -> drawable, buffer + 4, bigEndian);
+ PutULONG(putImage -> gcontext, buffer + 8, bigEndian);
+
+ PutUINT(putImage -> width, buffer + 12, bigEndian);
+ PutUINT(putImage -> height, buffer + 14, bigEndian);
+ PutUINT(putImage -> pos_x, buffer + 16, bigEndian);
+ PutUINT(putImage -> pos_y, buffer + 18, bigEndian);
+
+ *(buffer + 20) = (unsigned char) putImage -> left_pad;
+ *(buffer + 21) = (unsigned char) putImage -> depth;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at "
+ << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void PutImageStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ PutImageMessage *putImage = (PutImageMessage *) message;
+
+ *logofs << name() << ": Identity format " << (unsigned) putImage -> format
+ << ", depth " << (unsigned) putImage -> depth << ", left_pad "
+ << (unsigned) putImage -> left_pad << ", width " << putImage -> width
+ << ", height " << putImage -> height << ", pos_x " << putImage -> pos_x
+ << ", pos_y " << putImage -> pos_y << ", drawable " << putImage -> drawable
+ << ", gcontext " << putImage -> gcontext << ", size " << putImage -> size_
+ << ".\n" << logofs_flush;
+
+ #endif
+}
+
+void PutImageStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ //
+ // Fields format, width, height, left_pad, depth.
+ //
+
+ md5_append(md5_state_, buffer + 1, 1);
+ md5_append(md5_state_, buffer + 12, 4);
+ md5_append(md5_state_, buffer + 20, 2);
+}
+
+void PutImageStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ //
+ // Encode the variant part.
+ //
+
+ PutImageMessage *putImage = (PutImageMessage *) message;
+ PutImageMessage *cachedPutImage = (PutImageMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << putImage -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(putImage -> drawable, clientCache -> drawableCache);
+
+ cachedPutImage -> drawable = putImage -> drawable;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << putImage -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(putImage -> gcontext, clientCache -> gcCache);
+
+ cachedPutImage -> gcontext = putImage -> gcontext;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << putImage -> pos_x
+ << " as " << "pos_x" << " field.\n" << logofs_flush;
+ #endif
+
+ unsigned short int diff_x = putImage -> pos_x - cachedPutImage -> pos_x;
+
+ encodeBuffer.encodeCachedValue(diff_x, 16,
+ clientCache -> putImageXCache, 8);
+
+ cachedPutImage -> pos_x = putImage -> pos_x;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << putImage -> pos_y
+ << " as " << "pos_y" << " field.\n" << logofs_flush;
+ #endif
+
+ unsigned short int diff_y = putImage -> pos_y - cachedPutImage -> pos_y;
+
+ encodeBuffer.encodeCachedValue(diff_y, 16,
+ clientCache -> putImageYCache, 8);
+
+ cachedPutImage -> pos_y = putImage -> pos_y;
+}
+
+void PutImageStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ //
+ // Decode the variant part.
+ //
+
+ PutImageMessage *putImage = (PutImageMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ putImage -> drawable = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << putImage -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ putImage -> gcontext = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << putImage -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageXCache, 8);
+
+ putImage -> pos_x += value;
+ putImage -> pos_x &= 0xffff;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << putImage -> pos_x
+ << " as pos_x field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageYCache, 8);
+
+ putImage -> pos_y += value;
+ putImage -> pos_y &= 0xffff;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << putImage -> pos_y
+ << " as pos_y field.\n" << logofs_flush;
+ #endif
+}
diff --git a/nxcomp/PutImage.h b/nxcomp/PutImage.h
new file mode 100644
index 000000000..e9535cb29
--- /dev/null
+++ b/nxcomp/PutImage.h
@@ -0,0 +1,166 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef PutImage_H
+#define PutImage_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define PUTIMAGE_ENABLE_CACHE 1
+#define PUTIMAGE_ENABLE_DATA 1
+#define PUTIMAGE_ENABLE_SPLIT 1
+#define PUTIMAGE_ENABLE_COMPRESS 1
+
+#define PUTIMAGE_DATA_LIMIT 262144 - 24
+#define PUTIMAGE_DATA_OFFSET 24
+
+#define PUTIMAGE_CACHE_SLOTS 6000
+#define PUTIMAGE_CACHE_THRESHOLD 70
+#define PUTIMAGE_CACHE_LOWER_THRESHOLD 50
+
+#define PUTIMAGE_ENABLE_COMPRESS_IF_PROTO_STEP_7 0
+
+#define PUTIMAGE_CACHE_THRESHOLD_IF_PACKED 10
+#define PUTIMAGE_CACHE_LOWER_THRESHOLD_IF_PACKED 5
+
+#define PUTIMAGE_CACHE_THRESHOLD_IF_SHADOW 97
+#define PUTIMAGE_CACHE_LOWER_THRESHOLD_IF_SHADOW 90
+
+#define PUTIMAGE_ENABLE_SPLIT_IF_PROTO_STEP_8 0
+
+//
+// The message class.
+//
+
+class PutImageMessage : public Message
+{
+ friend class PutImageStore;
+
+ public:
+
+ PutImageMessage()
+ {
+ }
+
+ ~PutImageMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char format;
+ unsigned char depth;
+ unsigned char left_pad;
+ unsigned short width;
+ unsigned short height;
+ unsigned int drawable;
+ unsigned int gcontext;
+ unsigned short pos_x;
+ unsigned short pos_y;
+};
+
+class PutImageStore : public MessageStore
+{
+ public:
+
+ PutImageStore(StaticCompressor *compressor);
+
+ virtual ~PutImageStore();
+
+ virtual const char *name() const
+ {
+ return "PutImage";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_PutImage;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(PutImageMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ protected:
+
+ virtual Message *create() const
+ {
+ return new PutImageMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new PutImageMessage((const PutImageMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (PutImageMessage *) message;
+ }
+
+ virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const;
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* PutImage_H */
diff --git a/nxcomp/PutPackedImage.cpp b/nxcomp/PutPackedImage.cpp
new file mode 100644
index 000000000..eae16a16f
--- /dev/null
+++ b/nxcomp/PutPackedImage.cpp
@@ -0,0 +1,595 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "PutPackedImage.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "WriteBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Constructors and destructors.
+//
+
+PutPackedImageStore::PutPackedImageStore(StaticCompressor *compressor)
+
+ : MessageStore(compressor)
+{
+ enableCache = PUTPACKEDIMAGE_ENABLE_CACHE;
+ enableData = PUTPACKEDIMAGE_ENABLE_DATA;
+ enableSplit = PUTPACKEDIMAGE_ENABLE_SPLIT;
+ enableCompress = PUTPACKEDIMAGE_ENABLE_COMPRESS;
+
+ dataLimit = PUTPACKEDIMAGE_DATA_LIMIT;
+ dataOffset = PUTPACKEDIMAGE_DATA_OFFSET;
+
+ cacheSlots = PUTPACKEDIMAGE_CACHE_SLOTS;
+ cacheThreshold = PUTPACKEDIMAGE_CACHE_THRESHOLD;
+ cacheLowerThreshold = PUTPACKEDIMAGE_CACHE_LOWER_THRESHOLD;
+
+ if (control -> isProtoStep8() == 1)
+ {
+ enableSplit = PUTPACKEDIMAGE_ENABLE_SPLIT_IF_PROTO_STEP_8;
+ }
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+PutPackedImageStore::~PutPackedImageStore()
+{
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int PutPackedImageStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding full message identity.\n" << logofs_flush;
+ #endif
+
+ // Client.
+ encodeBuffer.encodeCachedValue(*(buffer + 1), 8,
+ clientCache -> resourceCache);
+
+ // Size.
+ encodeBuffer.encodeValue(GetUINT(buffer + 2, bigEndian), 16, 10);
+
+ // Drawable.
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 4, bigEndian),
+ clientCache -> drawableCache);
+ // GC.
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian),
+ clientCache -> gcCache);
+ // Method.
+ encodeBuffer.encodeCachedValue(*(buffer + 12), 8,
+ clientCache -> methodCache);
+ // Format.
+ encodeBuffer.encodeValue(*(buffer + 13), 2);
+
+ // SrcDepth.
+ encodeBuffer.encodeCachedValue(*(buffer + 14), 8,
+ clientCache -> depthCache);
+ // DstDepth.
+ encodeBuffer.encodeCachedValue(*(buffer + 15), 8,
+ clientCache -> depthCache);
+ // SrcLength.
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + 16, bigEndian), 24,
+ clientCache -> putPackedImageSrcLengthCache);
+ // DstLength.
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + 20, bigEndian), 24,
+ clientCache -> putPackedImageDstLengthCache);
+ // SrcX.
+ unsigned int x = GetUINT(buffer + 24, bigEndian);
+ int xDiff = x - clientCache -> putImageLastX;
+ clientCache -> putImageLastX = x;
+ encodeBuffer.encodeCachedValue(xDiff, 16,
+ clientCache -> putImageXCache, 8);
+ // SrcY.
+ unsigned int y = GetUINT(buffer + 26, bigEndian);
+ int yDiff = y - clientCache -> putImageLastY;
+ clientCache -> putImageLastY = y;
+ encodeBuffer.encodeCachedValue(yDiff, 16,
+ clientCache -> putImageYCache, 8);
+ // SrcWidth.
+ encodeBuffer.encodeCachedValue(GetUINT(buffer + 28, bigEndian), 16,
+ clientCache -> putImageWidthCache, 8);
+ // SrcHeight.
+ encodeBuffer.encodeCachedValue(GetUINT(buffer + 30, bigEndian), 16,
+ clientCache -> putImageHeightCache, 8);
+ // DstX.
+ x = GetUINT(buffer + 32, bigEndian);
+ xDiff = x - clientCache -> putImageLastX;
+ clientCache -> putImageLastX = x;
+ encodeBuffer.encodeCachedValue(xDiff, 16,
+ clientCache -> putImageXCache, 8);
+ // DstY.
+ y = GetUINT(buffer + 34, bigEndian);
+ yDiff = y - clientCache -> putImageLastY;
+ clientCache -> putImageLastY = y;
+ encodeBuffer.encodeCachedValue(yDiff, 16,
+ clientCache -> putImageYCache, 8);
+ // DstWidth.
+ encodeBuffer.encodeCachedValue(GetUINT(buffer + 36, bigEndian), 16,
+ clientCache -> putImageWidthCache, 8);
+ // DstHeight.
+ encodeBuffer.encodeCachedValue(GetUINT(buffer + 38, bigEndian), 16,
+ clientCache -> putImageHeightCache, 8);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int PutPackedImageStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding full message identity.\n" << logofs_flush;
+ #endif
+
+ unsigned int value;
+ unsigned char cValue;
+
+ // Client.
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> resourceCache);
+
+ // Size.
+ decodeBuffer.decodeValue(size, 16, 10);
+
+ size <<= 2;
+
+ buffer = writeBuffer -> addMessage(size);
+
+ *(buffer + 1) = cValue;
+
+ // Drawable.
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+ PutULONG(value, buffer + 4, bigEndian);
+
+ // GC.
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+ PutULONG(value, buffer + 8, bigEndian);
+
+ // Method.
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> methodCache);
+ *(buffer + 12) = cValue;
+
+ // Format.
+ decodeBuffer.decodeValue(value, 2);
+ *(buffer + 13) = value;
+
+ // SrcDepth.
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> depthCache);
+ *(buffer + 14) = cValue;
+
+ // DstDepth.
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> depthCache);
+ *(buffer + 15) = cValue;
+
+ // SrcLength.
+ decodeBuffer.decodeCachedValue(value, 24,
+ clientCache -> putPackedImageSrcLengthCache);
+ PutULONG(value, buffer + 16, bigEndian);
+
+ // DstLength.
+ decodeBuffer.decodeCachedValue(value, 24,
+ clientCache -> putPackedImageDstLengthCache);
+ PutULONG(value, buffer + 20, bigEndian);
+
+ // SrcX.
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageXCache, 8);
+ clientCache -> putImageLastX += value;
+ clientCache -> putImageLastX &= 0xffff;
+ PutUINT(clientCache -> putImageLastX, buffer + 24, bigEndian);
+
+ // SrcY.
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageYCache, 8);
+ clientCache -> putImageLastY += value;
+ clientCache -> putImageLastY &= 0xffff;
+ PutUINT(clientCache -> putImageLastY, buffer + 26, bigEndian);
+
+ // SrcWidth.
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageWidthCache, 8);
+ PutUINT(value, buffer + 28, bigEndian);
+
+ // SrcHeight.
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageHeightCache, 8);
+ PutUINT(value, buffer + 30, bigEndian);
+
+ // DstX.
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageXCache, 8);
+ clientCache -> putImageLastX += value;
+ clientCache -> putImageLastX &= 0xffff;
+ PutUINT(clientCache -> putImageLastX, buffer + 32, bigEndian);
+
+ // DstY.
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageYCache, 8);
+ clientCache -> putImageLastY += value;
+ clientCache -> putImageLastY &= 0xffff;
+ PutUINT(clientCache -> putImageLastY, buffer + 34, bigEndian);
+
+ // DstWidth.
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageWidthCache, 8);
+ PutUINT(value, buffer + 36, bigEndian);
+
+ // DstHeight.
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageHeightCache, 8);
+ PutUINT(value, buffer + 38, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int PutPackedImageStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PutPackedImageMessage *putPackedImage = (PutPackedImageMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ putPackedImage -> client = *(buffer + 1);
+
+ putPackedImage -> drawable = GetULONG(buffer + 4, bigEndian);
+ putPackedImage -> gcontext = GetULONG(buffer + 8, bigEndian);
+
+ putPackedImage -> method = *(buffer + 12);
+
+ putPackedImage -> format = *(buffer + 13);
+ putPackedImage -> src_depth = *(buffer + 14);
+ putPackedImage -> dst_depth = *(buffer + 15);
+
+ putPackedImage -> src_length = GetULONG(buffer + 16, bigEndian);
+ putPackedImage -> dst_length = GetULONG(buffer + 20, bigEndian);
+
+ putPackedImage -> src_x = GetUINT(buffer + 24, bigEndian);
+ putPackedImage -> src_y = GetUINT(buffer + 26, bigEndian);
+ putPackedImage -> src_width = GetUINT(buffer + 28, bigEndian);
+ putPackedImage -> src_height = GetUINT(buffer + 30, bigEndian);
+
+ putPackedImage -> dst_x = GetUINT(buffer + 32, bigEndian);
+ putPackedImage -> dst_y = GetUINT(buffer + 34, bigEndian);
+ putPackedImage -> dst_width = GetUINT(buffer + 36, bigEndian);
+ putPackedImage -> dst_height = GetUINT(buffer + 38, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at "
+ << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int PutPackedImageStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PutPackedImageMessage *putPackedImage = (PutPackedImageMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ *(buffer + 1) = putPackedImage -> client;
+
+ PutULONG(putPackedImage -> drawable, buffer + 4, bigEndian);
+ PutULONG(putPackedImage -> gcontext, buffer + 8, bigEndian);
+
+ *(buffer + 12) = putPackedImage -> method;
+
+ *(buffer + 13) = putPackedImage -> format;
+ *(buffer + 14) = putPackedImage -> src_depth;
+ *(buffer + 15) = putPackedImage -> dst_depth;
+
+ PutULONG(putPackedImage -> src_length, buffer + 16, bigEndian);
+ PutULONG(putPackedImage -> dst_length, buffer + 20, bigEndian);
+
+ PutUINT(putPackedImage -> src_x, buffer + 24, bigEndian);
+ PutUINT(putPackedImage -> src_y, buffer + 26, bigEndian);
+ PutUINT(putPackedImage -> src_width, buffer + 28, bigEndian);
+ PutUINT(putPackedImage -> src_height, buffer + 30, bigEndian);
+
+ PutUINT(putPackedImage -> dst_x, buffer + 32, bigEndian);
+ PutUINT(putPackedImage -> dst_y, buffer + 34, bigEndian);
+ PutUINT(putPackedImage -> dst_width, buffer + 36, bigEndian);
+ PutUINT(putPackedImage -> dst_height, buffer + 38, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at "
+ << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void PutPackedImageStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ PutPackedImageMessage *putPackedImage = (PutPackedImageMessage *) message;
+
+ *logofs << name() << ": Identity format "
+
+ << "drawable " << putPackedImage -> drawable << ", "
+ << "gcontext " << putPackedImage -> gcontext << ", "
+
+ << "format " << (unsigned int) putPackedImage -> format << ", "
+ << "method " << (unsigned int) putPackedImage -> method << ", "
+
+ << "src_depth " << (unsigned int) putPackedImage -> src_depth << ", "
+ << "dst_depth " << (unsigned int) putPackedImage -> dst_depth << ", "
+
+ << "src_length " << putPackedImage -> src_length << ", "
+ << "dst_length " << putPackedImage -> dst_length << ", "
+
+ << "src_x " << putPackedImage -> src_x << ", "
+ << "src_y " << putPackedImage -> src_y << ", "
+ << "src_width " << putPackedImage -> src_width << ", "
+ << "src_height " << putPackedImage -> src_height << ", "
+
+ << "dst_x " << putPackedImage -> dst_x << ", "
+ << "dst_y " << putPackedImage -> dst_y << ", "
+ << "dst_width " << putPackedImage -> dst_width << ", "
+ << "dst_height " << putPackedImage -> dst_height << ", "
+
+ << "size " << putPackedImage -> size_ << ".\n"
+ << logofs_flush;
+
+ #endif
+}
+
+void PutPackedImageStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ //
+ // Fields method, format, src_depth, dst_depth,
+ // src_length, dst_length, src_x, src_y, src_width,
+ // src_height.
+ //
+ //
+ // TODO: We should better investigate the effect of
+ // having fields src_x and src_y in identity instead
+ // of keeping them in differences.
+ //
+
+ md5_append(md5_state_, buffer + 12, 20);
+}
+
+void PutPackedImageStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ //
+ // Encode the variant part.
+ //
+
+ PutPackedImageMessage *putPackedImage = (PutPackedImageMessage *) message;
+ PutPackedImageMessage *cachedPutPackedImage = (PutPackedImageMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value "
+ << (unsigned int) putPackedImage -> client
+ << " as client field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(putPackedImage -> client, 8,
+ clientCache -> resourceCache);
+
+ cachedPutPackedImage -> client = putPackedImage -> client;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << putPackedImage -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(putPackedImage -> drawable, clientCache -> drawableCache);
+
+ cachedPutPackedImage -> drawable = putPackedImage -> drawable;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << putPackedImage -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(putPackedImage -> gcontext, clientCache -> gcCache);
+
+ cachedPutPackedImage -> gcontext = putPackedImage -> gcontext;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << putPackedImage -> dst_x
+ << " as " << "dst_x" << " field.\n" << logofs_flush;
+ #endif
+
+ unsigned short int diff_x = putPackedImage -> dst_x - cachedPutPackedImage -> dst_x;
+
+ encodeBuffer.encodeCachedValue(diff_x, 16,
+ clientCache -> putImageXCache, 8);
+
+ cachedPutPackedImage -> dst_x = putPackedImage -> dst_x;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << putPackedImage -> dst_y
+ << " as " << "dst_y" << " field.\n" << logofs_flush;
+ #endif
+
+ unsigned short int diff_y = putPackedImage -> dst_y - cachedPutPackedImage -> dst_y;
+
+ encodeBuffer.encodeCachedValue(diff_y, 16,
+ clientCache -> putImageYCache, 8);
+
+ cachedPutPackedImage -> dst_y = putPackedImage -> dst_y;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << putPackedImage -> dst_width
+ << " as " << "dst_width" << " field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(putPackedImage -> dst_width, 16,
+ clientCache -> putImageWidthCache, 8);
+
+ cachedPutPackedImage -> dst_width = putPackedImage -> dst_width;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << putPackedImage -> dst_height
+ << " as " << "dst_height" << " field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(putPackedImage -> dst_height, 16,
+ clientCache -> putImageHeightCache, 8);
+
+ cachedPutPackedImage -> dst_height = putPackedImage -> dst_height;
+}
+
+void PutPackedImageStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ //
+ // Decode the variant part.
+ //
+
+ PutPackedImageMessage *putPackedImage = (PutPackedImageMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeCachedValue(putPackedImage -> client, 8,
+ clientCache -> resourceCache);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value "
+ << (unsigned int) putPackedImage -> client
+ << " as client field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ putPackedImage -> drawable = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << putPackedImage -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ putPackedImage -> gcontext = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << putPackedImage -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageXCache, 8);
+
+ putPackedImage -> dst_x += value;
+ putPackedImage -> dst_x &= 0xffff;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << putPackedImage -> dst_x
+ << " as dst_x field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageYCache, 8);
+
+ putPackedImage -> dst_y += value;
+ putPackedImage -> dst_y &= 0xffff;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << putPackedImage -> dst_y
+ << " as dst_y field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageWidthCache, 8);
+
+ putPackedImage -> dst_width = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << putPackedImage -> dst_width
+ << " as dst_width field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageHeightCache, 8);
+
+ putPackedImage -> dst_height = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << putPackedImage -> dst_height
+ << " as dst_height field.\n" << logofs_flush;
+ #endif
+}
+
diff --git a/nxcomp/PutPackedImage.h b/nxcomp/PutPackedImage.h
new file mode 100644
index 000000000..5287a5b57
--- /dev/null
+++ b/nxcomp/PutPackedImage.h
@@ -0,0 +1,211 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef PutPackedImage_H
+#define PutPackedImage_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define PUTPACKEDIMAGE_ENABLE_CACHE 1
+#define PUTPACKEDIMAGE_ENABLE_DATA 1
+#define PUTPACKEDIMAGE_ENABLE_SPLIT 1
+#define PUTPACKEDIMAGE_ENABLE_COMPRESS 0
+
+//
+// We can't exceed lenght of 262144 bytes.
+//
+
+#define PUTPACKEDIMAGE_DATA_LIMIT 262144 - 40
+#define PUTPACKEDIMAGE_DATA_OFFSET 40
+
+#define PUTPACKEDIMAGE_CACHE_SLOTS 6000
+#define PUTPACKEDIMAGE_CACHE_THRESHOLD 70
+#define PUTPACKEDIMAGE_CACHE_LOWER_THRESHOLD 50
+
+#define PUTPACKEDIMAGE_CACHE_THRESHOLD_IF_PACKED_SHADOW 97
+#define PUTPACKEDIMAGE_CACHE_LOWER_THRESHOLD_IF_PACKED_SHADOW 90
+
+#define PUTPACKEDIMAGE_ENABLE_SPLIT_IF_PROTO_STEP_8 0
+
+//
+// The message class.
+//
+
+class PutPackedImageMessage : public Message
+{
+ friend class PutPackedImageStore;
+
+ public:
+
+ PutPackedImageMessage()
+ {
+ }
+
+ ~PutPackedImageMessage()
+ {
+ }
+
+ //
+ // Here are the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char client;
+
+ unsigned int drawable;
+ unsigned int gcontext;
+
+ unsigned char format;
+ unsigned char method;
+
+ unsigned char src_depth;
+ unsigned char dst_depth;
+
+ unsigned int src_length;
+ unsigned int dst_length;
+
+ short int src_x;
+ short int src_y;
+ unsigned short src_width;
+ unsigned short src_height;
+
+ short int dst_x;
+ short int dst_y;
+ unsigned short dst_width;
+ unsigned short dst_height;
+};
+
+class PutPackedImageStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ PutPackedImageStore(StaticCompressor *compressor);
+
+ virtual ~PutPackedImageStore();
+
+ virtual const char *name() const
+ {
+ return "PutPackedImage";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_NXPutPackedImage;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(PutPackedImageMessage);
+ }
+
+ //
+ // Very special of this class.
+ //
+
+ int getPackedSize(const int position) const
+ {
+ PutPackedImageMessage *message = (PutPackedImageMessage *) (*messages_)[position];
+
+ if (message == NULL)
+ {
+ return 0;
+ }
+
+ return dataOffset + message -> src_length;
+ }
+
+ int getUnpackedSize(const int position) const
+ {
+ PutPackedImageMessage *message = (PutPackedImageMessage *) (*messages_)[position];
+
+ if (message == NULL)
+ {
+ return 0;
+ }
+
+ return dataOffset + message -> dst_length;
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ protected:
+
+ virtual Message *create() const
+ {
+ return new PutPackedImageMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new PutPackedImageMessage((const PutPackedImageMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (PutPackedImageMessage *) message;
+ }
+
+ virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const;
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* PutPackedImage_H */
diff --git a/nxcomp/QueryFontReply.cpp b/nxcomp/QueryFontReply.cpp
new file mode 100644
index 000000000..2d42587f7
--- /dev/null
+++ b/nxcomp/QueryFontReply.cpp
@@ -0,0 +1,145 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "QueryFontReply.h"
+
+#include "ServerCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+QueryFontReplyStore::QueryFontReplyStore(StaticCompressor *compressor)
+
+ : MessageStore(compressor)
+{
+ enableCache = QUERYFONTREPLY_ENABLE_CACHE;
+ enableData = QUERYFONTREPLY_ENABLE_DATA;
+ enableSplit = QUERYFONTREPLY_ENABLE_SPLIT;
+ enableCompress = QUERYFONTREPLY_ENABLE_COMPRESS;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ enableCompress = QUERYFONTREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7;
+ }
+
+ dataLimit = QUERYFONTREPLY_DATA_LIMIT;
+ dataOffset = QUERYFONTREPLY_DATA_OFFSET;
+
+ cacheSlots = QUERYFONTREPLY_CACHE_SLOTS;
+ cacheThreshold = QUERYFONTREPLY_CACHE_THRESHOLD;
+ cacheLowerThreshold = QUERYFONTREPLY_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+QueryFontReplyStore::~QueryFontReplyStore()
+{
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int QueryFontReplyStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ //
+ // Clear the padding bytes.
+ //
+
+ unsigned char *pad = (unsigned char *) buffer;
+
+ if (size >= 24)
+ {
+ PutULONG(0, pad + 20, bigEndian);
+ }
+
+ if (size >= 40)
+ {
+ PutULONG(0, pad + 36, bigEndian);
+ }
+
+ //
+ // TODO: This doesn't work. Probably these
+ // padding bytes are not padding anymore.
+ // This is to be investigated.
+ //
+ // pad += 60;
+ //
+ // while (pad + 16 <= (buffer + size))
+ // {
+ // PutULONG(0, pad + 12, bigEndian);
+ //
+ // pad += 16;
+ // }
+ //
+
+ #ifdef DEBUG
+ *logofs << name() << ": Cleaned padding bytes of "
+ << "message at " << message << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int QueryFontReplyStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ return 1;
+}
+
+void QueryFontReplyStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ QueryFontReplyMessage *queryFontReply = (QueryFontReplyMessage *) message;
+
+ *logofs << name() << ": Identity size " << queryFontReply -> size_ << ".\n";
+
+ #endif
+}
+
+void QueryFontReplyStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+}
diff --git a/nxcomp/QueryFontReply.h b/nxcomp/QueryFontReply.h
new file mode 100644
index 000000000..537da4e63
--- /dev/null
+++ b/nxcomp/QueryFontReply.h
@@ -0,0 +1,129 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef QueryFontReply_H
+#define QueryFontReply_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+#define QUERYFONTREPLY_ENABLE_CACHE 1
+#define QUERYFONTREPLY_ENABLE_DATA 1
+#define QUERYFONTREPLY_ENABLE_SPLIT 0
+#define QUERYFONTREPLY_ENABLE_COMPRESS 1
+
+#define QUERYFONTREPLY_DATA_LIMIT 1048576 - 32
+#define QUERYFONTREPLY_DATA_OFFSET 8
+
+#define QUERYFONTREPLY_CACHE_SLOTS 200
+#define QUERYFONTREPLY_CACHE_THRESHOLD 20
+#define QUERYFONTREPLY_CACHE_LOWER_THRESHOLD 5
+
+#define QUERYFONTREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7 0
+
+//
+// The message class.
+//
+
+class QueryFontReplyMessage : public Message
+{
+ friend class QueryFontReplyStore;
+
+ public:
+
+ QueryFontReplyMessage()
+ {
+ }
+
+ ~QueryFontReplyMessage()
+ {
+ }
+};
+
+class QueryFontReplyStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ QueryFontReplyStore(StaticCompressor *compressor);
+
+ virtual ~QueryFontReplyStore();
+
+ virtual const char *name() const
+ {
+ return "QueryFontReply";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_QueryFont;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(QueryFontReplyMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ protected:
+
+ virtual Message *create() const
+ {
+ return new QueryFontReplyMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new QueryFontReplyMessage((const QueryFontReplyMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (QueryFontReplyMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* QueryFontReply_H */
diff --git a/nxcomp/README b/nxcomp/README
new file mode 100644
index 000000000..f35cce6b5
--- /dev/null
+++ b/nxcomp/README
@@ -0,0 +1,21 @@
+README
+------
+
+Building
+--------
+
+1. To compile:
+
+ > tar zxvf nxcomp-X.Y.Z-N.tar.gz
+ > cd nxcomp
+ > ./configure
+ > make
+
+ You'll have to run gmake under Solaris.
+
+2. The 'make install' target is not currently supported
+ in the Makefile, but it should be simple to fix.
+
+You need at least nxproxy and nxagent packages to enjoy this code. Check the
+NoMachine website at http://www.nomachine.com to get the latest release.
+
diff --git a/nxcomp/README-IPAQ b/nxcomp/README-IPAQ
new file mode 100644
index 000000000..f9418635c
--- /dev/null
+++ b/nxcomp/README-IPAQ
@@ -0,0 +1,21 @@
+README-IPAQ
+-----------
+
+1. Install a cross-compiler for ARM. You can find detailed
+ informations at:
+
+ http://www.ailis.de/~k/knowledge/crosscompiling/toolchain.php
+
+ There are also binaries needed to install the cross-compiler.
+
+2. Configure and compile libXcomp using:
+
+ $ ./configure --with-ipaq
+ $ make
+
+ After compilation type:
+
+ $ arm-linux-strip libXcomp.*
+
+3. Remember that you also need nxproxy to actually run your NX X
+ session.
diff --git a/nxcomp/ReadBuffer.cpp b/nxcomp/ReadBuffer.cpp
new file mode 100644
index 000000000..13122e278
--- /dev/null
+++ b/nxcomp/ReadBuffer.cpp
@@ -0,0 +1,627 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "ReadBuffer.h"
+
+#include "Transport.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+ReadBuffer::ReadBuffer(Transport *transport)
+
+ : transport_(transport)
+{
+ //
+ // The read buffer will grow until
+ // reaching the maximum buffer size
+ // and then will remain stable at
+ // that size.
+ //
+
+ initialReadSize_ = READ_BUFFER_DEFAULT_SIZE;
+ maximumBufferSize_ = READ_BUFFER_DEFAULT_SIZE;
+
+ size_ = 0;
+ buffer_ = NULL;
+
+ owner_ = 1;
+ length_ = 0;
+ start_ = 0;
+
+ remaining_ = 0;
+}
+
+ReadBuffer::~ReadBuffer()
+{
+ if (owner_ == 1)
+ {
+ delete [] buffer_;
+ }
+}
+
+void ReadBuffer::readMessage(const unsigned char *message, unsigned int length)
+{
+ //
+ // To be here we must be the real owner
+ // of the buffer and there must not be
+ // pending bytes in the transport.
+ //
+
+ #ifdef TEST
+
+ if (owner_ == 0)
+ {
+ *logofs << "ReadBuffer: PANIC! Class for FD#"
+ << transport_ -> fd() << " doesn't "
+ << "appear to be the owner of the buffer "
+ << "while borrowing from the caller.\n"
+ << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ //
+ // Be sure that any outstanding data from
+ // the transport is appended to our own
+ // byffer.
+ //
+
+ if (transport_ -> pending() != 0)
+ {
+ #ifdef WARNING
+ *logofs << "ReadBuffer: WARNING! Class for FD#"
+ << transport_ -> fd() << " has pending "
+ << "data in the transport while "
+ << "borrowing from the caller.\n"
+ << logofs_flush;
+ #endif
+
+ readMessage();
+
+ if (owner_ == 0)
+ {
+ convertBuffer();
+ }
+ }
+
+ //
+ // Can't borrow the buffer if there is data
+ // from a partial message. In this case add
+ // the new data to the end of our buffer.
+ //
+
+ if (length_ == 0)
+ {
+ #ifdef TEST
+ *logofs << "ReadBuffer: Borrowing " << length
+ << " bytes from the caller for FD#"
+ << transport_ -> fd() << " with "
+ << length_ << " bytes in the buffer.\n"
+ << logofs_flush;
+ #endif
+
+ delete [] buffer_;
+
+ buffer_ = (unsigned char *) message;
+ size_ = length;
+
+ length_ = length;
+
+ owner_ = 0;
+ start_ = 0;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "ReadBuffer: Appending " << length
+ << " bytes from the caller for FD#"
+ << transport_ -> fd() << " with "
+ << length_ << " bytes in the buffer.\n"
+ << logofs_flush;
+ #endif
+
+ appendBuffer(message, length);
+ }
+}
+
+int ReadBuffer::readMessage()
+{
+ int pendingLength = transport_ -> pending();
+
+ if (pendingLength > 0)
+ {
+ //
+ // Can't move the data in the borrowed buffer,
+ // so use the tansport buffer only if we don't
+ // have any partial message. This can happen
+ // with the proxy where we need to deflate the
+ // stream.
+ //
+
+ if (length_ == 0)
+ {
+ unsigned char *newBuffer;
+
+ length_ = transport_ -> getPending(newBuffer);
+
+ if (newBuffer == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "ReadBuffer: PANIC! Failed to borrow "
+ << length_ << " bytes of memory for buffer "
+ << "in context [A].\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to borrow memory for "
+ << "read buffer in context [A].\n";
+
+ HandleCleanup();
+ }
+
+ delete [] buffer_;
+
+ buffer_ = newBuffer;
+ size_ = length_;
+
+ owner_ = 0;
+ start_ = 0;
+
+ #ifdef TEST
+ *logofs << "ReadBuffer: Borrowed " << length_
+ << " pending bytes for FD#" << transport_ ->
+ fd() << ".\n" << logofs_flush;
+ #endif
+
+ return length_;
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "ReadBuffer: WARNING! Cannot borrow "
+ << pendingLength << " bytes for FD#"
+ << transport_ -> fd() << " with "
+ << length_ << " bytes in the buffer.\n"
+ << logofs_flush;
+ }
+ #endif
+ }
+
+ unsigned int readLength = suggestedLength(pendingLength);
+
+ #ifdef DEBUG
+ *logofs << "ReadBuffer: Requested " << readLength
+ << " bytes for FD#" << transport_ -> fd()
+ << " with readable " << transport_ -> readable()
+ << " remaining " << remaining_ << " pending "
+ << transport_ -> pending() << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (readLength < initialReadSize_)
+ {
+ readLength = initialReadSize_;
+ }
+
+ #ifdef DEBUG
+ *logofs << "ReadBuffer: Buffer size is " << size_
+ << " length " << length_ << " and start "
+ << start_ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // We can't use the transport buffer
+ // to read our own data in it.
+ //
+
+ #ifdef TEST
+
+ if (owner_ == 0)
+ {
+ *logofs << "ReadBuffer: PANIC! Class for FD#"
+ << transport_ -> fd() << " doesn't "
+ << "appear to be the owner of the buffer "
+ << "while reading.\n" << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ //
+ // Be sure that we have enough space
+ // to store all the requested data.
+ //
+
+ if (buffer_ == NULL || length_ + readLength > size_)
+ {
+ unsigned int newSize = length_ + readLength;
+
+ #ifdef TEST
+ *logofs << "ReadBuffer: Resizing buffer for FD#"
+ << transport_ -> fd() << " in read from "
+ << size_ << " to " << newSize << " bytes.\n"
+ << logofs_flush;
+ #endif
+
+ unsigned char *newBuffer = allocateBuffer(newSize);
+
+ memcpy(newBuffer, buffer_ + start_, length_);
+
+ delete [] buffer_;
+
+ buffer_ = newBuffer;
+ size_ = newSize;
+
+ transport_ -> pendingReset();
+
+ owner_ = 1;
+ }
+ else if (start_ != 0 && length_ != 0)
+ {
+ //
+ // If any bytes are left due to a partial
+ // message, shift them to the beginning
+ // of the buffer.
+ //
+
+ #ifdef TEST
+ *logofs << "ReadBuffer: Moving " << length_
+ << " bytes of data " << "at beginning of "
+ << "the buffer for FD#" << transport_ -> fd()
+ << ".\n" << logofs_flush;
+ #endif
+
+ memmove(buffer_, buffer_ + start_, length_);
+ }
+
+ start_ = 0;
+
+ #ifdef DEBUG
+ *logofs << "ReadBuffer: Buffer size is now " << size_
+ << " length is " << length_ << " and start is "
+ << start_ << ".\n" << logofs_flush;
+ #endif
+
+ unsigned char *readData = buffer_ + length_;
+
+ #ifdef DEBUG
+ *logofs << "ReadBuffer: Going to read " << readLength
+ << " bytes from FD#" << transport_ -> fd() << ".\n"
+ << logofs_flush;
+ #endif
+
+ int bytesRead = transport_ -> read(readData, readLength);
+
+ if (bytesRead > 0)
+ {
+ #ifdef TEST
+ *logofs << "ReadBuffer: Read " << bytesRead
+ << " bytes from FD#" << transport_ -> fd()
+ << ".\n" << logofs_flush;
+ #endif
+
+ length_ += bytesRead;
+ }
+ else if (bytesRead < 0)
+ {
+ //
+ // Check if there is more data pending than the
+ // size of the provided buffer. After reading
+ // the requested amount, in fact, the transport
+ // may have decompressed the data and produced
+ // more input. This trick allows us to always
+ // borrow the buffer from the transport, even
+ // when the partial read would have prevented
+ // that.
+ //
+
+ if (transport_ -> pending() > 0)
+ {
+ #ifdef TEST
+ *logofs << "ReadBuffer: WARNING! Trying to read some "
+ << "more with " << transport_ -> pending()
+ << " bytes pending for FD#" << transport_ ->
+ fd() << ".\n" << logofs_flush;
+ #endif
+
+ return readMessage();
+ }
+
+ #ifdef TEST
+ *logofs << "ReadBuffer: Error detected reading "
+ << "from FD#" << transport_ -> fd()
+ << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "ReadBuffer: No data read from FD#"
+ << transport_ -> fd() << " with remaining "
+ << remaining_ << ".\n" << logofs_flush;
+ }
+ #endif
+
+ return bytesRead;
+}
+
+const unsigned char *ReadBuffer::getMessage(unsigned int &controlLength,
+ unsigned int &dataLength)
+{
+ #ifdef TEST
+
+ if (transport_ -> pending() > 0)
+ {
+ *logofs << "ReadBuffer: PANIC! The transport "
+ << "appears to have data pending.\n"
+ << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ if (length_ == 0)
+ {
+ #ifdef DEBUG
+ *logofs << "ReadBuffer: No message can be located "
+ << "for FD#" << transport_ -> fd() << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (owner_ == 0)
+ {
+ buffer_ = NULL;
+ size_ = 0;
+
+ transport_ -> pendingReset();
+
+ owner_ = 1;
+ start_ = 0;
+ }
+
+ return NULL;
+ }
+
+ unsigned int trailerLength;
+
+ #ifdef DEBUG
+ *logofs << "ReadBuffer: Going to locate message with "
+ << "start at " << start_ << " and length "
+ << length_ << " for FD#" << transport_ -> fd()
+ << ".\n" << logofs_flush;
+ #endif
+
+ int located = locateMessage(buffer_ + start_, buffer_ + start_ + length_,
+ controlLength, dataLength, trailerLength);
+
+ if (located == 0)
+ {
+ //
+ // No more complete messages are in
+ // the buffer.
+ //
+
+ #ifdef DEBUG
+ *logofs << "ReadBuffer: No message was located "
+ << "for FD#" << transport_ -> fd()
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (owner_ == 0)
+ {
+ //
+ // Must move the remaining bytes in
+ // our own buffer.
+ //
+
+ convertBuffer();
+ }
+
+ return NULL;
+ }
+ else
+ {
+ const unsigned char *result = buffer_ + start_;
+
+ if (dataLength > 0)
+ {
+ //
+ // Message contains data, so go to the
+ // first byte of payload.
+ //
+
+ result += trailerLength;
+
+ start_ += (dataLength + trailerLength);
+ length_ -= (dataLength + trailerLength);
+ }
+ else
+ {
+ //
+ // It is a control message.
+ //
+
+ start_ += (controlLength + trailerLength);
+ length_ -= (controlLength + trailerLength);
+ }
+
+ #ifdef DEBUG
+ *logofs << "ReadBuffer: Located message for FD#"
+ << transport_ -> fd() << " with control length "
+ << controlLength << " and data length "
+ << dataLength << ".\n" << logofs_flush;
+ #endif
+
+ remaining_ = 0;
+
+ return result;
+ }
+}
+
+int ReadBuffer::setSize(int initialReadSize, int maximumBufferSize)
+{
+ initialReadSize_ = initialReadSize;
+ maximumBufferSize_ = maximumBufferSize;
+
+ #ifdef TEST
+ *logofs << "ReadBuffer: WARNING! Set buffer parameters to "
+ << initialReadSize_ << "/" << maximumBufferSize_
+ << " for object at "<< this << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void ReadBuffer::fullReset()
+{
+ #ifdef TEST
+
+ if (owner_ == 0)
+ {
+ *logofs << "ReadBuffer: PANIC! Class for FD#"
+ << transport_ -> fd() << " doesn't "
+ << "appear to be the owner of the buffer "
+ << "in reset.\n" << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ if (length_ == 0 && size_ > maximumBufferSize_)
+ {
+ #ifdef TEST
+ *logofs << "ReadBuffer: Resizing buffer for FD#"
+ << transport_ -> fd() << " in reset from "
+ << size_ << " to " << maximumBufferSize_
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ delete [] buffer_;
+
+ int newSize = maximumBufferSize_;
+
+ unsigned char *newBuffer = allocateBuffer(newSize);
+
+ buffer_ = newBuffer;
+ size_ = newSize;
+
+ transport_ -> pendingReset();
+
+ owner_ = 1;
+ start_ = 0;
+ }
+}
+
+unsigned char *ReadBuffer::allocateBuffer(unsigned int newSize)
+{
+ unsigned char *newBuffer = new unsigned char[newSize];
+
+ if (newBuffer == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "ReadBuffer: PANIC! Can't allocate "
+ << newSize << " bytes of memory for buffer "
+ << "in context [B].\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory for "
+ << "read buffer in context [B].\n";
+
+ HandleCleanup();
+ }
+
+ #ifdef VALGRIND
+
+ memset(newBuffer, '\0', newSize);
+
+ #endif
+
+ return newBuffer;
+}
+
+void ReadBuffer::appendBuffer(const unsigned char *message, unsigned int length)
+{
+ if (start_ + length_ + length > size_)
+ {
+ unsigned int newSize = length_ + length + initialReadSize_;
+
+ #ifdef TEST
+ *logofs << "ReadBuffer: WARNING! Resizing buffer "
+ << "for FD#" << transport_ -> fd()
+ << " from " << size_ << " to " << newSize
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ unsigned char *newBuffer = allocateBuffer(newSize);
+
+ memcpy(newBuffer, buffer_ + start_, length_);
+
+ delete [] buffer_;
+
+ buffer_ = newBuffer;
+ size_ = newSize;
+
+ start_ = 0;
+ }
+
+ memcpy(buffer_ + start_ + length_, message, length);
+
+ length_ += length;
+
+ transport_ -> pendingReset();
+
+ owner_ = 1;
+}
+
+void ReadBuffer::convertBuffer()
+{
+ unsigned int newSize = length_ + initialReadSize_;
+
+ #ifdef TEST
+ *logofs << "ReadBuffer: WARNING! Converting "
+ << length_ << " bytes to own buffer "
+ << "for FD#" << transport_ -> fd()
+ << " with new size " << newSize
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ unsigned char *newBuffer = allocateBuffer(newSize);
+
+ memcpy(newBuffer, buffer_ + start_, length_);
+
+ buffer_ = newBuffer;
+ size_ = newSize;
+
+ transport_ -> pendingReset();
+
+ owner_ = 1;
+ start_ = 0;
+}
diff --git a/nxcomp/ReadBuffer.h b/nxcomp/ReadBuffer.h
new file mode 100644
index 000000000..20130120a
--- /dev/null
+++ b/nxcomp/ReadBuffer.h
@@ -0,0 +1,120 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef ReadBuffer_H
+#define ReadBuffer_H
+
+#include "Misc.h"
+#include "Timestamp.h"
+#include "Transport.h"
+
+#define READ_BUFFER_DEFAULT_SIZE 8192
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+class ReadBuffer
+{
+ public:
+
+ ReadBuffer(Transport *transport);
+
+ virtual ~ReadBuffer();
+
+ int readMessage();
+
+ void readMessage(const unsigned char *message, unsigned int length);
+
+ const unsigned char *getMessage(unsigned int &dataLength)
+ {
+ unsigned int controlLength;
+
+ return getMessage(controlLength, dataLength);
+ }
+
+ const unsigned char *getMessage(unsigned int &controlLength, unsigned int &dataLength);
+
+ unsigned int getLength() const
+ {
+ return length_;
+ }
+
+ unsigned int getRemaining() const
+ {
+ return remaining_;
+ }
+
+ int setSize(int initialReadSize, int initialbufferSize);
+
+ void fullReset();
+
+ //
+ // Check if there is a complete
+ // message in the buffer.
+ //
+
+ int checkMessage()
+ {
+ if (length_ == 0)
+ {
+ return 0;
+ }
+ else
+ {
+ unsigned int controlLength;
+ unsigned int dataLength;
+ unsigned int trailerLength;
+
+ return (locateMessage(buffer_ + start_, buffer_ + start_ + length_,
+ controlLength, dataLength, trailerLength));
+ }
+ }
+
+ protected:
+
+ virtual unsigned int suggestedLength(unsigned int pendingLength) = 0;
+
+ virtual int locateMessage(const unsigned char *start,
+ const unsigned char *end,
+ unsigned int &controlLength,
+ unsigned int &dataLength,
+ unsigned int &trailerLength) = 0;
+
+ unsigned char *allocateBuffer(unsigned int newSize);
+
+ void appendBuffer(const unsigned char *message, unsigned int length);
+
+ void convertBuffer();
+
+ Transport *transport_;
+
+ unsigned char *buffer_;
+
+ unsigned int length_;
+ unsigned int size_;
+ unsigned int start_;
+ unsigned int remaining_;
+
+ int owner_;
+
+ unsigned int initialReadSize_;
+ unsigned int maximumBufferSize_;
+};
+
+#endif /* ReadBuffer_H */
diff --git a/nxcomp/RenderAddGlyphs.cpp b/nxcomp/RenderAddGlyphs.cpp
new file mode 100644
index 000000000..1d53ec0f8
--- /dev/null
+++ b/nxcomp/RenderAddGlyphs.cpp
@@ -0,0 +1,221 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderAddGlyphs.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_SIZE
+
+MESSAGE_BEGIN_DECODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(size, 16,
+ clientCache -> renderLengthCache, 5);
+
+ size = MESSAGE_OFFSET + (size << 2);
+
+ buffer = writeBuffer -> addMessage(size);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_SIZE
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + 4, bigEndian), 29,
+ clientCache -> renderGlyphSetCache);
+
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + 8, bigEndian), 32,
+ clientCache -> renderNumGlyphsCache, 8);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache -> renderGlyphSetCache);
+
+ PutULONG(value, buffer + 4, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 32,
+ clientCache -> renderNumGlyphsCache, 8);
+
+ PutULONG(value, buffer + 8, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_ENCODE_DATA
+{
+ encodeCharData(encodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_DATA
+
+MESSAGE_BEGIN_DECODE_DATA
+{
+ decodeCharData(decodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_DATA
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.add_glyphs.type = *(buffer + 1);
+
+ renderExtension -> data.add_glyphs.set_id = GetULONG(buffer + 4, bigEndian);
+ renderExtension -> data.add_glyphs.num_elm = GetULONG(buffer + 8, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.add_glyphs.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.add_glyphs.type;
+
+ PutULONG(renderExtension -> data.add_glyphs.set_id, buffer + 4, bigEndian);
+ PutULONG(renderExtension -> data.add_glyphs.num_elm, buffer + 8, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.add_glyphs.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ md5_append(md5_state, buffer + 1, 3);
+ md5_append(md5_state, buffer + 8, 4);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue(renderExtension -> data.add_glyphs.set_id, 29,
+ clientCache -> renderGlyphSetCache);
+
+ cachedRenderExtension -> data.add_glyphs.set_id =
+ renderExtension -> data.add_glyphs.set_id;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.add_glyphs.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(renderExtension -> data.add_glyphs.set_id, 29,
+ clientCache -> renderGlyphSetCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.add_glyphs.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/RenderAddGlyphs.h b/nxcomp/RenderAddGlyphs.h
new file mode 100644
index 000000000..918a70c8d
--- /dev/null
+++ b/nxcomp/RenderAddGlyphs.h
@@ -0,0 +1,80 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderAddGlyphs_H
+#define RenderAddGlyphs_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderAddGlyphs"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderAddGlyphsStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 12
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 1
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 1
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return MESSAGE_OFFSET;
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderAddGlyphs_H */
diff --git a/nxcomp/RenderChangePicture.cpp b/nxcomp/RenderChangePicture.cpp
new file mode 100644
index 000000000..5dbe39d52
--- /dev/null
+++ b/nxcomp/RenderChangePicture.cpp
@@ -0,0 +1,226 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderChangePicture.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_SIZE
+
+MESSAGE_BEGIN_DECODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(size, 16,
+ clientCache -> renderLengthCache, 5);
+
+ size = MESSAGE_OFFSET + (size << 2);
+
+ buffer = writeBuffer -> addMessage(size);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_SIZE
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 4, bigEndian),
+ clientCache -> renderSrcPictureCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> renderSrcPictureCache);
+
+ PutULONG(value, buffer + 4, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_ENCODE_DATA
+{
+ #ifdef DEBUG
+
+ if (size == MESSAGE_OFFSET + 4)
+ {
+ *logofs << name() << ": Mask is " << GetULONG(buffer + 8, bigEndian)
+ << " value is " << GetULONG(buffer + 12, bigEndian)
+ << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << name() << ": WARNING! Unexpected size. Mask is "
+ << GetULONG(buffer + 8, bigEndian) << ".\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ encodeLongData(encodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_DATA
+
+MESSAGE_BEGIN_DECODE_DATA
+{
+ decodeLongData(decodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_DATA
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.change_picture.type = *(buffer + 1);
+
+ renderExtension -> data.change_picture.src_id = GetULONG(buffer + 4, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.change_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.change_picture.type;
+
+ PutULONG(renderExtension -> data.change_picture.src_id, buffer + 4, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.change_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ md5_append(md5_state, buffer + 1, 3);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.change_picture.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ cachedRenderExtension -> data.change_picture.src_id =
+ renderExtension -> data.change_picture.src_id;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.change_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.change_picture.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.change_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/RenderChangePicture.h b/nxcomp/RenderChangePicture.h
new file mode 100644
index 000000000..e6a89a610
--- /dev/null
+++ b/nxcomp/RenderChangePicture.h
@@ -0,0 +1,80 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderChangePicture_H
+#define RenderChangePicture_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderChangePicture"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderChangePictureStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 8
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 1
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 1
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return MESSAGE_OFFSET;
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderChangePicture_H */
diff --git a/nxcomp/RenderComposite.cpp b/nxcomp/RenderComposite.cpp
new file mode 100644
index 000000000..e3c121b48
--- /dev/null
+++ b/nxcomp/RenderComposite.cpp
@@ -0,0 +1,388 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderComposite.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue(*(buffer + 4), 8,
+ clientCache -> renderOpCache);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian),
+ clientCache -> renderSrcPictureCache);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 12, bigEndian),
+ clientCache -> renderMaskPictureCache);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 16, bigEndian),
+ clientCache -> renderDstPictureCache);
+
+ //
+ // Src X and Y.
+ //
+
+ encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 20, bigEndian),
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 22, bigEndian),
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+ //
+ // Mask X and Y.
+ //
+
+ encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 24, bigEndian),
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 26, bigEndian),
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+
+ //
+ // Dst X and Y.
+ //
+
+ encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 28, bigEndian),
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 30, bigEndian),
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+
+ //
+ // Width and height.
+ //
+
+ encodeBuffer.encodeCachedValue(GetUINT(buffer + 32, bigEndian), 16,
+ clientCache -> renderWidthCache, 11);
+
+ encodeBuffer.encodeCachedValue(GetUINT(buffer + 34, bigEndian), 16,
+ clientCache -> renderHeightCache, 11);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeCachedValue(*(buffer + 4), 8,
+ clientCache -> renderOpCache);
+
+ decodeBuffer.decodeXidValue(value, clientCache -> renderSrcPictureCache);
+
+ PutULONG(value, buffer + 8, bigEndian);
+
+ decodeBuffer.decodeXidValue(value, clientCache -> renderMaskPictureCache);
+
+ PutULONG(value, buffer + 12, bigEndian);
+
+ decodeBuffer.decodeXidValue(value, clientCache -> renderDstPictureCache);
+
+ PutULONG(value, buffer + 16, bigEndian);
+
+ //
+ // Src X and Y.
+ //
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ PutUINT(clientCache -> renderLastX, buffer + 20, bigEndian);
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+
+ PutUINT(clientCache -> renderLastY, buffer + 22, bigEndian);
+
+ //
+ // Mask X and Y.
+ //
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ PutUINT(clientCache -> renderLastX, buffer + 24, bigEndian);
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+
+ PutUINT(clientCache -> renderLastY, buffer + 26, bigEndian);
+
+ //
+ // Dst X and Y.
+ //
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ PutUINT(clientCache -> renderLastX, buffer + 28, bigEndian);
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+
+ PutUINT(clientCache -> renderLastY, buffer + 30, bigEndian);
+
+ //
+ // Width and height.
+ //
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> renderWidthCache, 11);
+
+ PutUINT(value, buffer + 32, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> renderHeightCache, 11);
+
+ PutUINT(value, buffer + 34, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.composite.type = *(buffer + 1);
+ renderExtension -> data.composite.op = *(buffer + 4);
+
+ renderExtension -> data.composite.src_id = GetULONG(buffer + 8, bigEndian);
+ renderExtension -> data.composite.msk_id = GetULONG(buffer + 12, bigEndian);
+ renderExtension -> data.composite.dst_id = GetULONG(buffer + 16, bigEndian);
+
+ renderExtension -> data.composite.src_x = GetUINT(buffer + 20, bigEndian);
+ renderExtension -> data.composite.src_y = GetUINT(buffer + 22, bigEndian);
+
+ renderExtension -> data.composite.msk_x = GetUINT(buffer + 24, bigEndian);
+ renderExtension -> data.composite.msk_y = GetUINT(buffer + 26, bigEndian);
+
+ renderExtension -> data.composite.dst_x = GetUINT(buffer + 28, bigEndian);
+ renderExtension -> data.composite.dst_y = GetUINT(buffer + 30, bigEndian);
+
+ renderExtension -> data.composite.width = GetUINT(buffer + 32, bigEndian);
+ renderExtension -> data.composite.height = GetUINT(buffer + 34, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.composite.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.composite.type;
+ *(buffer + 4) = renderExtension -> data.composite.op;
+
+ PutULONG(renderExtension -> data.composite.src_id, buffer + 8, bigEndian);
+ PutULONG(renderExtension -> data.composite.msk_id, buffer + 12, bigEndian);
+ PutULONG(renderExtension -> data.composite.dst_id, buffer + 16, bigEndian);
+
+ PutUINT(renderExtension -> data.composite.src_x, buffer + 20, bigEndian);
+ PutUINT(renderExtension -> data.composite.src_y, buffer + 22, bigEndian);
+
+ PutUINT(renderExtension -> data.composite.msk_x, buffer + 24, bigEndian);
+ PutUINT(renderExtension -> data.composite.msk_y, buffer + 26, bigEndian);
+
+ PutUINT(renderExtension -> data.composite.dst_x, buffer + 28, bigEndian);
+ PutUINT(renderExtension -> data.composite.dst_y, buffer + 30, bigEndian);
+
+ PutUINT(renderExtension -> data.composite.width, buffer + 32, bigEndian);
+ PutUINT(renderExtension -> data.composite.height, buffer + 34, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.composite.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ //
+ // Include the minor opcode and size in the
+ // identity, plus the operator, the x and y
+ // of the source and mask and the width and
+ // height of the destination.
+ //
+
+ md5_append(md5_state, buffer + 1, 4);
+ md5_append(md5_state, buffer + 20, 8);
+ md5_append(md5_state, buffer + 32, 4);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Source " << renderExtension -> data.composite.src_id
+ << " mask " << renderExtension -> data.composite.msk_id
+ << " destination " << renderExtension -> data.composite.msk_id
+ << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.composite.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ cachedRenderExtension -> data.composite.src_id =
+ renderExtension -> data.composite.src_id;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.composite.msk_id,
+ clientCache -> renderMaskPictureCache);
+
+ cachedRenderExtension -> data.composite.msk_id =
+ renderExtension -> data.composite.msk_id;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.composite.dst_id,
+ clientCache -> renderDstPictureCache);
+
+ cachedRenderExtension -> data.composite.dst_id =
+ renderExtension -> data.composite.dst_id;
+
+ //
+ // Dst X and Y.
+ //
+
+ unsigned int value;
+ unsigned int previous;
+
+ value = renderExtension -> data.composite.dst_x;
+ previous = cachedRenderExtension -> data.composite.dst_x;
+
+ encodeBuffer.encodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderXCache, 11);
+
+ cachedRenderExtension -> data.composite.dst_x = value;
+
+ value = renderExtension -> data.composite.dst_y;
+ previous = cachedRenderExtension -> data.composite.dst_y;
+
+ encodeBuffer.encodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderYCache, 11);
+
+ cachedRenderExtension -> data.composite.dst_y = value;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.composite.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.composite.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.composite.msk_id,
+ clientCache -> renderMaskPictureCache);
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.composite.dst_id,
+ clientCache -> renderDstPictureCache);
+
+ //
+ // Dst X and Y.
+ //
+
+ unsigned int value;
+ unsigned int previous;
+
+ previous = renderExtension -> data.composite.dst_x;
+
+ decodeBuffer.decodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderXCache, 11);
+
+ renderExtension -> data.composite.dst_x = value;
+
+ previous = renderExtension -> data.composite.dst_y;
+
+ decodeBuffer.decodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderYCache, 11);
+
+ renderExtension -> data.composite.dst_y = value;
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.composite.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/RenderComposite.h b/nxcomp/RenderComposite.h
new file mode 100644
index 000000000..91fa30aaa
--- /dev/null
+++ b/nxcomp/RenderComposite.h
@@ -0,0 +1,80 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderComposite_H
+#define RenderComposite_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderComposite"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderCompositeStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 36
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 0
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 0
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return MESSAGE_OFFSET;
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderComposite_H */
diff --git a/nxcomp/RenderCompositeCompat.cpp b/nxcomp/RenderCompositeCompat.cpp
new file mode 100644
index 000000000..5a1eff213
--- /dev/null
+++ b/nxcomp/RenderCompositeCompat.cpp
@@ -0,0 +1,320 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderCompositeCompat.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_SIZE
+{
+ //
+ // Strictly speaking this request doesn't have
+ // a data part. We just encode the field from
+ // offset 24 to 36 as they were data using an
+ // int cache.
+ //
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_SIZE
+
+MESSAGE_BEGIN_DECODE_SIZE
+{
+ size = MESSAGE_OFFSET + 12;
+
+ buffer = writeBuffer -> addMessage(size);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_SIZE
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue(*(buffer + 4), 8,
+ clientCache -> renderOpCache);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian),
+ clientCache -> renderSrcPictureCache);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 12, bigEndian),
+ clientCache -> renderSrcPictureCache);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 16, bigEndian),
+ clientCache -> renderSrcPictureCache);
+
+ encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 20, bigEndian),
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 22, bigEndian),
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeCachedValue(*(buffer + 4), 8,
+ clientCache -> renderOpCache);
+
+ decodeBuffer.decodeXidValue(value, clientCache -> renderSrcPictureCache);
+
+ PutULONG(value, buffer + 8, bigEndian);
+
+ decodeBuffer.decodeXidValue(value, clientCache -> renderSrcPictureCache);
+
+ PutULONG(value, buffer + 12, bigEndian);
+
+ decodeBuffer.decodeXidValue(value, clientCache -> renderSrcPictureCache);
+
+ PutULONG(value, buffer + 16, bigEndian);
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ PutUINT(clientCache -> renderLastX, buffer + 20, bigEndian);
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+
+ PutUINT(clientCache -> renderLastY, buffer + 22, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_ENCODE_DATA
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ for (unsigned int i = MESSAGE_OFFSET, c = 0; i < size; i += 4)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding long value "
+ << GetULONG(buffer + i, bigEndian) << " with i = "
+ << i << " c = " << c << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + i, bigEndian), 32,
+ *clientCache -> renderCompositeDataCache[c]);
+
+ if (++c == 3) c = 0;
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_DATA
+
+MESSAGE_BEGIN_DECODE_DATA
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ for (unsigned int i = MESSAGE_OFFSET, c = 0; i < size; i += 4)
+ {
+ decodeBuffer.decodeCachedValue(value, 32,
+ *clientCache -> renderCompositeDataCache[c]);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded long value " << value
+ << " with i = " << i << " c = " << c << ".\n"
+ << logofs_flush;
+ #endif
+
+ PutULONG(value, buffer + i, bigEndian);
+
+ if (++c == 3) c = 0;
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_DATA
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.composite.type = *(buffer + 1);
+ renderExtension -> data.composite.op = *(buffer + 4);
+
+ renderExtension -> data.composite.src_id = GetULONG(buffer + 8, bigEndian);
+ renderExtension -> data.composite.msk_id = GetULONG(buffer + 12, bigEndian);
+ renderExtension -> data.composite.dst_id = GetULONG(buffer + 16, bigEndian);
+
+ renderExtension -> data.composite.src_x = GetUINT(buffer + 20, bigEndian);
+ renderExtension -> data.composite.src_y = GetUINT(buffer + 22, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.composite.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.composite.type;
+ *(buffer + 4) = renderExtension -> data.composite.op;
+
+ PutULONG(renderExtension -> data.composite.src_id, buffer + 8, bigEndian);
+ PutULONG(renderExtension -> data.composite.msk_id, buffer + 12, bigEndian);
+ PutULONG(renderExtension -> data.composite.dst_id, buffer + 16, bigEndian);
+
+ PutUINT(renderExtension -> data.composite.src_x, buffer + 20, bigEndian);
+ PutUINT(renderExtension -> data.composite.src_y, buffer + 22, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.composite.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ //
+ // Include minor opcode, size and
+ // operator in the identity, plus
+ // the x and y of the source.
+ //
+
+ md5_append(md5_state, buffer + 1, 4);
+ md5_append(md5_state, buffer + 20, 4);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Source " << renderExtension ->
+ data.composite.src_id << " mask " << renderExtension ->
+ data.composite.msk_id << " destination " << renderExtension ->
+ data.composite.msk_id << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.composite.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ cachedRenderExtension -> data.composite.src_id =
+ renderExtension -> data.composite.src_id;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.composite.msk_id,
+ clientCache -> renderSrcPictureCache);
+
+ cachedRenderExtension -> data.composite.msk_id =
+ renderExtension -> data.composite.msk_id;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.composite.dst_id,
+ clientCache -> renderSrcPictureCache);
+
+ cachedRenderExtension -> data.composite.dst_id =
+ renderExtension -> data.composite.dst_id;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.composite.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.composite.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.composite.msk_id,
+ clientCache -> renderSrcPictureCache);
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.composite.dst_id,
+ clientCache -> renderSrcPictureCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.composite.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/RenderCompositeCompat.h b/nxcomp/RenderCompositeCompat.h
new file mode 100644
index 000000000..a26db35ba
--- /dev/null
+++ b/nxcomp/RenderCompositeCompat.h
@@ -0,0 +1,80 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderCompositeCompat_H
+#define RenderCompositeCompat_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderCompositeCompat"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderCompositeCompatStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 24
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 1
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 1
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return MESSAGE_OFFSET;
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderCompositeCompat_H */
diff --git a/nxcomp/RenderCompositeGlyphs.cpp b/nxcomp/RenderCompositeGlyphs.cpp
new file mode 100644
index 000000000..1135633ff
--- /dev/null
+++ b/nxcomp/RenderCompositeGlyphs.cpp
@@ -0,0 +1,679 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderCompositeGlyphs.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding value "
+ << ((size - MESSAGE_OFFSET) >> 2) << ".\n"
+ << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_SIZE
+
+MESSAGE_BEGIN_DECODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(size, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << size
+ << ".\n" << logofs_flush;
+ #endif
+
+ size = MESSAGE_OFFSET + (size << 2);
+
+ buffer = writeBuffer -> addMessage(size);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_SIZE
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue(*(buffer + 4), 8,
+ clientCache -> renderOpCache);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian),
+ clientCache -> renderSrcPictureCache);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 12, bigEndian),
+ clientCache -> renderDstPictureCache);
+
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + 16, bigEndian), 32,
+ clientCache -> renderFormatCache);
+
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + 20, bigEndian), 29,
+ clientCache -> renderGlyphSetCache);
+
+ unsigned int src_x = GetUINT(buffer + 24, bigEndian);
+ unsigned int src_y = GetUINT(buffer + 26, bigEndian);
+
+ if (control -> isProtoStep8() == 1)
+ {
+ encodeBuffer.encodeDiffCachedValue(src_x,
+ clientCache -> renderGlyphX, 16,
+ clientCache -> renderGlyphXCache, 11);
+
+ encodeBuffer.encodeDiffCachedValue(src_y,
+ clientCache -> renderGlyphY, 16,
+ clientCache -> renderGlyphYCache, 11);
+ }
+ else
+ {
+ encodeBuffer.encodeDiffCachedValue(src_x,
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ encodeBuffer.encodeDiffCachedValue(src_y,
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded source X "
+ << GetUINT(buffer + 24, bigEndian) << " source Y "
+ << GetUINT(buffer + 26, bigEndian) << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Bytes from 28 to 36 contain in the order:
+ //
+ // 1 byte for the length of the first string.
+ // 3 bytes of padding.
+ // 2 bytes for the X offset.
+ // 2 bytes for the Y offset.
+ //
+ // Encode these bytes differentially to match
+ // all the strings that have equal glyphs.
+ //
+ // Only manage the first string of glyphs. The
+ // others strings should match, if they contain
+ // the same glyphs, since the offset are rela-
+ // tive to the first offset coordinates.
+ //
+
+ if (control -> isProtoStep8() == 1 &&
+ size >= MESSAGE_OFFSET_IF_PROTO_STEP_8)
+ {
+ unsigned int numGlyphs = *(buffer + 28);
+
+ encodeBuffer.encodeCachedValue(numGlyphs, 8,
+ clientCache -> renderNumGlyphsCache);
+
+ unsigned int offset_x = GetUINT(buffer + 32, bigEndian);
+ unsigned int offset_y = GetUINT(buffer + 34, bigEndian);
+
+ if (offset_x == src_x && offset_y == src_y)
+ {
+ encodeBuffer.encodeBoolValue(0);
+
+ #ifdef TEST
+ *logofs << name() << ": Matched offset X "
+ << GetUINT(buffer + 32, bigEndian) << " offset Y "
+ << GetUINT(buffer + 34, bigEndian) << ".\n"
+ << logofs_flush;
+ #endif
+ }
+ else
+ {
+ encodeBuffer.encodeBoolValue(1);
+
+ encodeBuffer.encodeDiffCachedValue(offset_x,
+ clientCache -> renderGlyphX, 16,
+ clientCache -> renderGlyphXCache, 11);
+
+ encodeBuffer.encodeDiffCachedValue(offset_y,
+ clientCache -> renderGlyphY, 16,
+ clientCache -> renderGlyphYCache, 11);
+
+ #ifdef TEST
+ *logofs << name() << ": Missed offset X "
+ << GetUINT(buffer + 32, bigEndian) << " offset Y "
+ << GetUINT(buffer + 34, bigEndian) << ".\n"
+ << logofs_flush;
+ #endif
+ }
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeCachedValue(*(buffer + 4), 8,
+ clientCache -> renderOpCache);
+
+ decodeBuffer.decodeXidValue(value,
+ clientCache -> renderSrcPictureCache);
+
+ PutULONG(value, buffer + 8, bigEndian);
+
+ decodeBuffer.decodeXidValue(value,
+ clientCache -> renderDstPictureCache);
+
+ PutULONG(value, buffer + 12, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 32,
+ clientCache -> renderFormatCache);
+
+ PutULONG(value, buffer + 16, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache -> renderGlyphSetCache);
+
+ PutULONG(value, buffer + 20, bigEndian);
+
+ unsigned int src_x;
+ unsigned int src_y;
+
+ if (control -> isProtoStep8() == 1)
+ {
+ decodeBuffer.decodeDiffCachedValue(src_x,
+ clientCache -> renderGlyphX, 16,
+ clientCache -> renderGlyphXCache, 11);
+
+ decodeBuffer.decodeDiffCachedValue(src_y,
+ clientCache -> renderGlyphY, 16,
+ clientCache -> renderGlyphYCache, 11);
+ }
+ else
+ {
+ decodeBuffer.decodeDiffCachedValue(src_x,
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ decodeBuffer.decodeDiffCachedValue(src_y,
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+ }
+
+ PutUINT(src_x, buffer + 24, bigEndian);
+ PutUINT(src_y, buffer + 26, bigEndian);
+
+ if (control -> isProtoStep8() == 1 &&
+ size >= MESSAGE_OFFSET_IF_PROTO_STEP_8)
+ {
+ decodeBuffer.decodeCachedValue(value, 8,
+ clientCache -> renderNumGlyphsCache);
+
+ *(buffer + 28) = value;
+
+ decodeBuffer.decodeBoolValue(value);
+
+ if (value == 0)
+ {
+ PutUINT(src_x, buffer + 32, bigEndian);
+ PutUINT(src_y, buffer + 34, bigEndian);
+ }
+ else
+ {
+ decodeBuffer.decodeDiffCachedValue(src_x,
+ clientCache -> renderGlyphX, 16,
+ clientCache -> renderGlyphXCache, 11);
+
+ PutUINT(src_x, buffer + 32, bigEndian);
+
+ decodeBuffer.decodeDiffCachedValue(src_y,
+ clientCache -> renderGlyphY, 16,
+ clientCache -> renderGlyphYCache, 11);
+
+ PutUINT(src_y, buffer + 34, bigEndian);
+ }
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_ENCODE_DATA
+{
+ if (control -> isProtoStep8() == 1 &&
+ size >= MESSAGE_OFFSET_IF_PROTO_STEP_8)
+ {
+ encodeCharData(encodeBuffer, buffer, MESSAGE_OFFSET_IF_PROTO_STEP_8,
+ size, bigEndian, channelCache);
+ }
+ else if (size > MESSAGE_OFFSET)
+ {
+ encodeCharData(encodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET
+ << " bytes of text data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_DATA
+
+MESSAGE_BEGIN_DECODE_DATA
+{
+ if (control -> isProtoStep8() == 1 &&
+ size >= MESSAGE_OFFSET_IF_PROTO_STEP_8)
+ {
+ decodeCharData(decodeBuffer, buffer, MESSAGE_OFFSET_IF_PROTO_STEP_8,
+ size, bigEndian, channelCache);
+ }
+ else if (size > MESSAGE_OFFSET)
+ {
+ decodeCharData(decodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_DATA
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.composite_glyphs.type = *(buffer + 1);
+ renderExtension -> data.composite_glyphs.op = *(buffer + 4);
+
+ renderExtension -> data.composite_glyphs.src_id = GetULONG(buffer + 8, bigEndian);
+ renderExtension -> data.composite_glyphs.dst_id = GetULONG(buffer + 12, bigEndian);
+
+ renderExtension -> data.composite_glyphs.format = GetULONG(buffer + 16, bigEndian);
+ renderExtension -> data.composite_glyphs.set_id = GetULONG(buffer + 20, bigEndian);
+
+ renderExtension -> data.composite_glyphs.src_x = GetUINT(buffer + 24, bigEndian);
+ renderExtension -> data.composite_glyphs.src_y = GetUINT(buffer + 26, bigEndian);
+
+ if (control -> isProtoStep8() == 1 &&
+ size >= MESSAGE_OFFSET_IF_PROTO_STEP_8)
+ {
+ renderExtension -> data.composite_glyphs.num_elm = *(buffer + 28);
+
+ renderExtension -> data.composite_glyphs.offset_x = GetUINT(buffer + 32, bigEndian);
+ renderExtension -> data.composite_glyphs.offset_y = GetUINT(buffer + 34, bigEndian);
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.composite_glyphs.type
+ << " size is " << renderExtension -> size_ << " identity size "
+ << renderExtension -> i_size_ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.composite_glyphs.type;
+ *(buffer + 4) = renderExtension -> data.composite_glyphs.op;
+
+ PutULONG(renderExtension -> data.composite_glyphs.src_id, buffer + 8, bigEndian);
+ PutULONG(renderExtension -> data.composite_glyphs.dst_id, buffer + 12, bigEndian);
+
+ PutULONG(renderExtension -> data.composite_glyphs.format, buffer + 16, bigEndian);
+ PutULONG(renderExtension -> data.composite_glyphs.set_id, buffer + 20, bigEndian);
+
+ PutUINT(renderExtension -> data.composite_glyphs.src_x, buffer + 24, bigEndian);
+ PutUINT(renderExtension -> data.composite_glyphs.src_y, buffer + 26, bigEndian);
+
+ if (control -> isProtoStep8() == 1 &&
+ size >= MESSAGE_OFFSET_IF_PROTO_STEP_8)
+ {
+ *(buffer + 28) = renderExtension -> data.composite_glyphs.num_elm;
+
+ PutUINT(renderExtension -> data.composite_glyphs.offset_x, buffer + 32, bigEndian);
+ PutUINT(renderExtension -> data.composite_glyphs.offset_y, buffer + 34, bigEndian);
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.composite_glyphs.type
+ << " size is " << renderExtension -> size_ << " identity size "
+ << renderExtension -> i_size_ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ //
+ // Include minor opcode, size and
+ // the composite operator in the
+ // identity.
+ //
+
+ md5_append(md5_state, buffer + 1, 4);
+
+ //
+ // Include the format.
+ //
+
+ md5_append(md5_state, buffer + 16, 4);
+
+ //
+ // Also include the length of the
+ // first string.
+ //
+
+ if (control -> isProtoStep8() == 1 &&
+ size >= MESSAGE_OFFSET_IF_PROTO_STEP_8)
+ {
+ md5_append(md5_state, buffer + 28, 1);
+ }
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.composite_glyphs.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ cachedRenderExtension -> data.composite_glyphs.src_id =
+ renderExtension -> data.composite_glyphs.src_id;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.composite_glyphs.dst_id,
+ clientCache -> renderDstPictureCache);
+
+ cachedRenderExtension -> data.composite_glyphs.dst_id =
+ renderExtension -> data.composite_glyphs.dst_id;
+
+ encodeBuffer.encodeCachedValue(renderExtension -> data.composite_glyphs.set_id, 29,
+ clientCache -> renderGlyphSetCache);
+
+ cachedRenderExtension -> data.composite_glyphs.set_id =
+ renderExtension -> data.composite_glyphs.set_id;
+
+ //
+ // Src X and Y.
+ //
+ // The source X and Y coordinates are
+ // encoded as differerences in respect
+ // to the cached message.
+ //
+
+ unsigned int value;
+ unsigned int previous;
+
+ if (control -> isProtoStep8() == 1)
+ {
+ value = renderExtension -> data.composite_glyphs.src_x;
+ previous = cachedRenderExtension -> data.composite_glyphs.src_x;
+
+ encodeBuffer.encodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderGlyphXCache, 11);
+
+ cachedRenderExtension -> data.composite_glyphs.src_x = value;
+
+ value = renderExtension -> data.composite_glyphs.src_y;
+ previous = cachedRenderExtension -> data.composite_glyphs.src_y;
+
+ encodeBuffer.encodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderGlyphYCache, 11);
+
+ cachedRenderExtension -> data.composite_glyphs.src_y = value;
+ }
+ else
+ {
+ value = renderExtension -> data.composite_glyphs.src_x;
+ previous = cachedRenderExtension -> data.composite_glyphs.src_x;
+
+ encodeBuffer.encodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderXCache, 11);
+
+ cachedRenderExtension -> data.composite_glyphs.src_x = value;
+
+ value = renderExtension -> data.composite_glyphs.src_y;
+ previous = cachedRenderExtension -> data.composite_glyphs.src_y;
+
+ encodeBuffer.encodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderYCache, 11);
+
+ cachedRenderExtension -> data.composite_glyphs.src_y = value;
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded source X "
+ << renderExtension -> data.composite_glyphs.src_x << " source Y "
+ << renderExtension -> data.composite_glyphs.src_y << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (control -> isProtoStep8() == 1 &&
+ renderExtension -> size_ >= MESSAGE_OFFSET_IF_PROTO_STEP_8)
+ {
+ //
+ // Offset X and Y.
+ //
+
+ if (renderExtension -> data.composite_glyphs.offset_x ==
+ renderExtension -> data.composite_glyphs.src_x &&
+ renderExtension -> data.composite_glyphs.offset_y ==
+ renderExtension -> data.composite_glyphs.src_y)
+ {
+ encodeBuffer.encodeBoolValue(0);
+
+ cachedRenderExtension -> data.composite_glyphs.offset_x =
+ renderExtension -> data.composite_glyphs.offset_x;
+
+ cachedRenderExtension -> data.composite_glyphs.offset_y =
+ renderExtension -> data.composite_glyphs.offset_y;
+
+ #ifdef TEST
+ *logofs << name() << ": Matched offset X "
+ << renderExtension -> data.composite_glyphs.offset_x << " offset Y "
+ << renderExtension -> data.composite_glyphs.offset_y << ".\n"
+ << logofs_flush;
+ #endif
+ }
+ else
+ {
+ encodeBuffer.encodeBoolValue(1);
+
+ value = renderExtension -> data.composite_glyphs.offset_x;
+ previous = cachedRenderExtension -> data.composite_glyphs.offset_x;
+
+ encodeBuffer.encodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderGlyphXCache, 11);
+
+ cachedRenderExtension -> data.composite_glyphs.offset_x = value;
+
+ value = renderExtension -> data.composite_glyphs.offset_y;
+ previous = cachedRenderExtension -> data.composite_glyphs.offset_y;
+
+ encodeBuffer.encodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderGlyphYCache, 11);
+
+ cachedRenderExtension -> data.composite_glyphs.offset_y = value;
+
+ #ifdef TEST
+ *logofs << name() << ": Missed offset X "
+ << renderExtension -> data.composite_glyphs.offset_x << " offset Y "
+ << renderExtension -> data.composite_glyphs.offset_y << ".\n"
+ << logofs_flush;
+ #endif
+ }
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.composite_glyphs.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.composite_glyphs.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.composite_glyphs.dst_id,
+ clientCache -> renderDstPictureCache);
+
+ decodeBuffer.decodeCachedValue(renderExtension -> data.composite_glyphs.set_id, 29,
+ clientCache -> renderGlyphSetCache);
+
+ //
+ // Src X and Y.
+ //
+
+ unsigned int value;
+ unsigned int previous;
+
+ if (control -> isProtoStep8() == 1)
+ {
+ previous = renderExtension -> data.composite_glyphs.src_x;
+
+ decodeBuffer.decodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderGlyphXCache, 11);
+
+ renderExtension -> data.composite_glyphs.src_x = value;
+
+ previous = renderExtension -> data.composite_glyphs.src_y;
+
+ decodeBuffer.decodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderGlyphYCache, 11);
+
+ renderExtension -> data.composite_glyphs.src_y = value;
+ }
+ else
+ {
+ previous = renderExtension -> data.composite_glyphs.src_x;
+
+ decodeBuffer.decodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderXCache, 11);
+
+ renderExtension -> data.composite_glyphs.src_x = value;
+
+ previous = renderExtension -> data.composite_glyphs.src_y;
+
+ decodeBuffer.decodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderYCache, 11);
+
+ renderExtension -> data.composite_glyphs.src_y = value;
+ }
+
+ if (control -> isProtoStep8() == 1 &&
+ renderExtension -> size_ >= MESSAGE_OFFSET_IF_PROTO_STEP_8)
+ {
+ //
+ // Offset X and Y.
+ //
+
+ decodeBuffer.decodeBoolValue(value);
+
+ if (value == 0)
+ {
+ renderExtension -> data.composite_glyphs.offset_x =
+ renderExtension -> data.composite_glyphs.src_x;
+
+ renderExtension -> data.composite_glyphs.offset_y =
+ renderExtension -> data.composite_glyphs.src_y;
+ }
+ else
+ {
+ previous = renderExtension -> data.composite_glyphs.offset_x;
+
+ decodeBuffer.decodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderGlyphXCache, 11);
+
+ renderExtension -> data.composite_glyphs.offset_x = value;
+
+ previous = renderExtension -> data.composite_glyphs.offset_y;
+
+ decodeBuffer.decodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderGlyphYCache, 11);
+
+ renderExtension -> data.composite_glyphs.offset_y = value;
+ }
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.composite_glyphs.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/RenderCompositeGlyphs.h b/nxcomp/RenderCompositeGlyphs.h
new file mode 100644
index 000000000..527fd3d12
--- /dev/null
+++ b/nxcomp/RenderCompositeGlyphs.h
@@ -0,0 +1,93 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderCompositeGlyphs_H
+#define RenderCompositeGlyphs_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderCompositeGlyphs"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderCompositeGlyphsStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 28
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 1
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 1
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Encode the first 8 bytes of the
+// data differentially in newer
+// protocol versions.
+//
+
+#undef MESSAGE_OFFSET_IF_PROTO_STEP_8
+#define MESSAGE_OFFSET_IF_PROTO_STEP_8 36
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ unsigned int offset = (control -> isProtoStep8() == 1 ?
+ MESSAGE_OFFSET_IF_PROTO_STEP_8 :
+ MESSAGE_OFFSET);
+
+ return (size >= offset ? offset : size);
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderCompositeGlyphs_H */
diff --git a/nxcomp/RenderCompositeGlyphsCompat.cpp b/nxcomp/RenderCompositeGlyphsCompat.cpp
new file mode 100644
index 000000000..3fe10fafb
--- /dev/null
+++ b/nxcomp/RenderCompositeGlyphsCompat.cpp
@@ -0,0 +1,602 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderCompositeGlyphsCompat.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ //
+ // The offset points 8 bytes after
+ // the beginning of the data part.
+ //
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding value "
+ << ((size - (MESSAGE_OFFSET - 8)) >> 2)
+ << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue((size - (MESSAGE_OFFSET - 8)) >> 2, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_SIZE
+
+MESSAGE_BEGIN_DECODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(size, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << size
+ << ".\n" << logofs_flush;
+ #endif
+
+ size = (MESSAGE_OFFSET - 8) + (size << 2);
+
+ buffer = writeBuffer -> addMessage(size);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_SIZE
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue(*(buffer + 4), 8,
+ clientCache -> renderOpCache);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian),
+ clientCache -> renderSrcPictureCache);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 12, bigEndian),
+ clientCache -> renderSrcPictureCache);
+
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + 16, bigEndian), 32,
+ clientCache -> renderFormatCache);
+
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + 20, bigEndian), 29,
+ clientCache -> renderGlyphSetCache);
+
+ encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 24, bigEndian),
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 26, bigEndian),
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+
+ //
+ // Try to save as many bits as possible by
+ // encoding the information about the first
+ // set of glyphs.
+ //
+
+ if (size >= MESSAGE_OFFSET)
+ {
+ unsigned int numGlyphs = *(buffer + 28);
+
+ encodeBuffer.encodeCachedValue(numGlyphs, 8,
+ clientCache -> renderNumGlyphsCache);
+
+ encodeBuffer.encodeCachedValue(GetUINT(buffer + 32, bigEndian), 16,
+ clientCache -> renderWidthCache, 11);
+
+ encodeBuffer.encodeCachedValue(GetUINT(buffer + 34, bigEndian), 16,
+ clientCache -> renderHeightCache, 11);
+
+ //
+ // Only manage the first set of glyphs,
+ // that is in most cases the only one.
+ //
+
+ switch (*(buffer + 1))
+ {
+ case X_RenderCompositeGlyphs8:
+ {
+ if (numGlyphs & 0x03)
+ {
+ memset((unsigned char *) buffer + MESSAGE_OFFSET + numGlyphs, '\0',
+ RoundUp4(numGlyphs) - numGlyphs);
+ }
+
+ break;
+ }
+ case X_RenderCompositeGlyphs16:
+ {
+ if (numGlyphs & 0x01)
+ {
+ memset((unsigned char *) buffer + MESSAGE_OFFSET + (numGlyphs * 2), '\0',
+ RoundUp4(numGlyphs * 2) - numGlyphs * 2);
+ }
+
+ break;
+ }
+ }
+
+ #ifdef TEST
+ if (*(buffer + (size - 1)) != '\0')
+ {
+ *logofs << name() << ": WARNING! Final byte is non-zero with size "
+ << size << " and " << (unsigned int) *(buffer + 28)
+ << " glyphs.\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << name() << ": Final byte is zero with size "
+ << size << " and " << (unsigned int) *(buffer + 28)
+ << " glyphs.\n" << logofs_flush;
+ }
+ #endif
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeCachedValue(*(buffer + 4), 8,
+ clientCache -> renderOpCache);
+
+ decodeBuffer.decodeXidValue(value,
+ clientCache -> renderSrcPictureCache);
+
+ PutULONG(value, buffer + 8, bigEndian);
+
+ decodeBuffer.decodeXidValue(value,
+ clientCache -> renderSrcPictureCache);
+
+ PutULONG(value, buffer + 12, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 32,
+ clientCache -> renderFormatCache);
+
+ PutULONG(value, buffer + 16, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache -> renderGlyphSetCache);
+
+ PutULONG(value, buffer + 20, bigEndian);
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ PutUINT(clientCache -> renderLastX, buffer + 24, bigEndian);
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+
+ PutUINT(clientCache -> renderLastY, buffer + 26, bigEndian);
+
+ if (size >= MESSAGE_OFFSET)
+ {
+ decodeBuffer.decodeCachedValue(value, 8,
+ clientCache -> renderNumGlyphsCache);
+
+ *(buffer + 28) = value;
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> renderWidthCache, 11);
+
+ PutUINT(value, buffer + 32, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> renderHeightCache, 11);
+
+ PutUINT(value, buffer + 34, bigEndian);
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_ENCODE_DATA
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ switch (*(buffer + 1))
+ {
+ case X_RenderCompositeGlyphs8:
+ {
+ clientCache -> renderTextCompressor.reset();
+
+ const unsigned char *next = buffer + MESSAGE_OFFSET;
+
+ for (unsigned int i = MESSAGE_OFFSET; i < size; i++)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding char with i = " << i
+ << ".\n" << logofs_flush;
+ #endif
+
+ clientCache -> renderTextCompressor.
+ encodeChar(*next++, encodeBuffer);
+ }
+
+ break;
+ }
+ case X_RenderCompositeGlyphs16:
+ {
+ for (unsigned int i = MESSAGE_OFFSET; i < size; i += 2)
+ {
+ value = GetUINT(buffer + i, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding short with i = " << i
+ << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(value, 16,
+ *clientCache -> renderCompositeGlyphsDataCache[clientCache ->
+ renderLastCompositeGlyphsData]);
+
+ clientCache -> renderLastCompositeGlyphsData = value % 16;
+ }
+
+ break;
+ }
+ default:
+ {
+ for (unsigned int i = MESSAGE_OFFSET; i < size; i += 4)
+ {
+ value = GetULONG(buffer + i, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding long with i = " << i
+ << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(value, 32,
+ *clientCache -> renderCompositeGlyphsDataCache[clientCache ->
+ renderLastCompositeGlyphsData]);
+
+ clientCache -> renderLastCompositeGlyphsData = value % 16;
+ }
+
+ break;
+ }
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_DATA
+
+MESSAGE_BEGIN_DECODE_DATA
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ switch (*(buffer + 1))
+ {
+ case X_RenderCompositeGlyphs8:
+ {
+ clientCache -> renderTextCompressor.reset();
+
+ unsigned char *next = buffer + MESSAGE_OFFSET;
+
+ for (unsigned int i = MESSAGE_OFFSET; i < size; i++)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding char with i = " << i
+ << ".\n" << logofs_flush;
+ #endif
+
+ *next++ = clientCache -> renderTextCompressor.
+ decodeChar(decodeBuffer);
+ }
+
+ break;
+ }
+ case X_RenderCompositeGlyphs16:
+ {
+ for (unsigned int i = MESSAGE_OFFSET; i < size; i += 2)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding short with i = " << i
+ << ".\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache -> renderCompositeGlyphsDataCache[clientCache ->
+ renderLastCompositeGlyphsData]);
+
+ PutUINT(value, buffer + i, bigEndian);
+
+ clientCache -> renderLastCompositeGlyphsData = value % 16;
+ }
+
+ break;
+ }
+ default:
+ {
+ for (unsigned int i = MESSAGE_OFFSET; i < size; i += 4)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding long with i = " << i
+ << ".\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 32,
+ *clientCache -> renderCompositeGlyphsDataCache[clientCache ->
+ renderLastCompositeGlyphsData]);
+
+ PutULONG(value, buffer + i, bigEndian);
+
+ clientCache -> renderLastCompositeGlyphsData = value % 16;
+ }
+
+ break;
+ }
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_DATA
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.composite_glyphs_compat.type = *(buffer + 1);
+ renderExtension -> data.composite_glyphs_compat.op = *(buffer + 4);
+
+ renderExtension -> data.composite_glyphs_compat.src_id = GetULONG(buffer + 8, bigEndian);
+ renderExtension -> data.composite_glyphs_compat.dst_id = GetULONG(buffer + 12, bigEndian);
+
+ renderExtension -> data.composite_glyphs_compat.format = GetULONG(buffer + 16, bigEndian);
+ renderExtension -> data.composite_glyphs_compat.set_id = GetULONG(buffer + 20, bigEndian);
+
+ renderExtension -> data.composite_glyphs_compat.src_x = GetUINT(buffer + 24, bigEndian);
+ renderExtension -> data.composite_glyphs_compat.src_y = GetUINT(buffer + 26, bigEndian);
+
+ if (size >= MESSAGE_OFFSET)
+ {
+ renderExtension -> data.composite_glyphs_compat.num_elm = *(buffer + 28);
+
+ renderExtension -> data.composite_glyphs_compat.delta_x = GetUINT(buffer + 32, bigEndian);
+ renderExtension -> data.composite_glyphs_compat.delta_y = GetUINT(buffer + 34, bigEndian);
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.composite_glyphs_compat.type
+ << " size is " << renderExtension -> size_ << " identity size "
+ << renderExtension -> i_size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.composite_glyphs_compat.type;
+ *(buffer + 4) = renderExtension -> data.composite_glyphs_compat.op;
+
+ PutULONG(renderExtension -> data.composite_glyphs_compat.src_id, buffer + 8, bigEndian);
+ PutULONG(renderExtension -> data.composite_glyphs_compat.dst_id, buffer + 12, bigEndian);
+
+ PutULONG(renderExtension -> data.composite_glyphs_compat.format, buffer + 16, bigEndian);
+ PutULONG(renderExtension -> data.composite_glyphs_compat.set_id, buffer + 20, bigEndian);
+
+ PutUINT(renderExtension -> data.composite_glyphs_compat.src_x, buffer + 24, bigEndian);
+ PutUINT(renderExtension -> data.composite_glyphs_compat.src_y, buffer + 26, bigEndian);
+
+ if (size >= MESSAGE_OFFSET)
+ {
+ *(buffer + 28) = renderExtension -> data.composite_glyphs_compat.num_elm;
+
+ PutUINT(renderExtension -> data.composite_glyphs_compat.delta_x, buffer + 32, bigEndian);
+ PutUINT(renderExtension -> data.composite_glyphs_compat.delta_y, buffer + 34, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Len is " << (unsigned int) *(buffer + 28)
+ << " delta X is " << GetUINT(buffer + 32, bigEndian)
+ << " delta Y is " << GetUINT(buffer + 34, bigEndian)
+ << ".\n" << logofs_flush;
+
+ *logofs << name() << ": Pad 1 is " << (unsigned int) *(buffer + 29)
+ << " pad 2 and 3 are " << GetUINT(buffer + 30, bigEndian)
+ << ".\n" << logofs_flush;
+ #endif
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.composite_glyphs_compat.type
+ << " size is " << renderExtension -> size_ << " identity size "
+ << renderExtension -> i_size_ << ".\n" << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ //
+ // Include minor opcode, size and
+ // the composite operator in the
+ // identity.
+ //
+
+ md5_append(md5_state, buffer + 1, 4);
+
+ //
+ // Include the format and the source
+ // x and y fields.
+ //
+
+ md5_append(md5_state, buffer + 16, 4);
+ md5_append(md5_state, buffer + 24, 4);
+
+ //
+ // Include the number of glyphs.
+ //
+
+ if (size >= MESSAGE_OFFSET)
+ {
+ md5_append(md5_state, buffer + 28, 1);
+ }
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.composite_glyphs_compat.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ cachedRenderExtension -> data.composite_glyphs_compat.src_id =
+ renderExtension -> data.composite_glyphs_compat.src_id;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.composite_glyphs_compat.dst_id,
+ clientCache -> renderSrcPictureCache);
+
+ cachedRenderExtension -> data.composite_glyphs_compat.dst_id =
+ renderExtension -> data.composite_glyphs_compat.dst_id;
+
+ encodeBuffer.encodeCachedValue(renderExtension -> data.composite_glyphs_compat.set_id, 29,
+ clientCache -> renderGlyphSetCache);
+
+ cachedRenderExtension -> data.composite_glyphs_compat.set_id =
+ renderExtension -> data.composite_glyphs_compat.set_id;
+
+ if (renderExtension -> size_ >= MESSAGE_OFFSET)
+ {
+ encodeBuffer.encodeCachedValue(renderExtension -> data.composite_glyphs_compat.delta_x, 16,
+ clientCache -> renderWidthCache, 11);
+
+ cachedRenderExtension -> data.composite_glyphs_compat.delta_x =
+ renderExtension -> data.composite_glyphs_compat.delta_x;
+
+ encodeBuffer.encodeCachedValue(renderExtension -> data.composite_glyphs_compat.delta_y, 16,
+ clientCache -> renderHeightCache, 11);
+
+ cachedRenderExtension -> data.composite_glyphs_compat.delta_y =
+ renderExtension -> data.composite_glyphs_compat.delta_y;
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.composite_glyphs_compat.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.composite_glyphs_compat.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.composite_glyphs_compat.dst_id,
+ clientCache -> renderSrcPictureCache);
+
+ decodeBuffer.decodeCachedValue(renderExtension -> data.composite_glyphs_compat.set_id, 29,
+ clientCache -> renderGlyphSetCache);
+
+ if (renderExtension -> size_ >= MESSAGE_OFFSET)
+ {
+ unsigned int value;
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> renderWidthCache, 11);
+
+ renderExtension -> data.composite_glyphs_compat.delta_x = value;
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> renderHeightCache, 11);
+
+ renderExtension -> data.composite_glyphs_compat.delta_y = value;
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.composite_glyphs_compat.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/RenderCompositeGlyphsCompat.h b/nxcomp/RenderCompositeGlyphsCompat.h
new file mode 100644
index 000000000..7a00608c2
--- /dev/null
+++ b/nxcomp/RenderCompositeGlyphsCompat.h
@@ -0,0 +1,80 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderCompositeGlyphsCompat_H
+#define RenderCompositeGlyphsCompat_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderCompositeGlyphsCompat"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderCompositeGlyphsCompatStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 36
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 1
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 1
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return (size >= MESSAGE_OFFSET ? MESSAGE_OFFSET : size);
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderCompositeGlyphsCompat_H */
diff --git a/nxcomp/RenderCreateGlyphSet.cpp b/nxcomp/RenderCreateGlyphSet.cpp
new file mode 100644
index 000000000..b9da8d7bf
--- /dev/null
+++ b/nxcomp/RenderCreateGlyphSet.cpp
@@ -0,0 +1,173 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderCreateGlyphSet.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeNewXidValue(GetULONG(buffer + 4, bigEndian),
+ clientCache -> lastId, clientCache -> lastIdCache,
+ clientCache -> renderGlyphSetCache,
+ clientCache -> renderFreeGlyphSetCache);
+
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + 8, bigEndian), 32,
+ clientCache -> renderFormatCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeNewXidValue(value,
+ clientCache -> lastId, clientCache -> lastIdCache,
+ clientCache -> renderGlyphSetCache,
+ clientCache -> renderFreeGlyphSetCache);
+
+ PutULONG(value, buffer + 4, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 32,
+ clientCache -> renderFormatCache);
+
+ PutULONG(value, buffer + 8, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.create_set.type = *(buffer + 1);
+
+ renderExtension -> data.create_set.set_id = GetULONG(buffer + 4, bigEndian);
+ renderExtension -> data.create_set.format = GetULONG(buffer + 8, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.create_set.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.create_set.type;
+
+ PutULONG(renderExtension -> data.create_set.set_id, buffer + 4, bigEndian);
+ PutULONG(renderExtension -> data.create_set.format, buffer + 8, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.create_set.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ md5_append(md5_state, buffer + 1, 3);
+ md5_append(md5_state, buffer + 8, 4);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeNewXidValue(renderExtension -> data.create_set.set_id,
+ clientCache -> lastId, clientCache -> lastIdCache,
+ clientCache -> renderGlyphSetCache,
+ clientCache -> renderFreeGlyphSetCache);
+
+ cachedRenderExtension -> data.create_set.set_id =
+ renderExtension -> data.create_set.set_id;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.create_set.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeNewXidValue(renderExtension -> data.create_set.set_id,
+ clientCache -> lastId, clientCache -> lastIdCache,
+ clientCache -> renderGlyphSetCache,
+ clientCache -> renderFreeGlyphSetCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.create_set.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/RenderCreateGlyphSet.h b/nxcomp/RenderCreateGlyphSet.h
new file mode 100644
index 000000000..0f14ce0c5
--- /dev/null
+++ b/nxcomp/RenderCreateGlyphSet.h
@@ -0,0 +1,80 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderCreateGlyphSet_H
+#define RenderCreateGlyphSet_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderCreateGlyphSet"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderCreateGlyphSetStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 12
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 0
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 0
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return MESSAGE_OFFSET;
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderCreateGlyphSet_H */
diff --git a/nxcomp/RenderCreateGlyphSetCompat.cpp b/nxcomp/RenderCreateGlyphSetCompat.cpp
new file mode 100644
index 000000000..49e9f741d
--- /dev/null
+++ b/nxcomp/RenderCreateGlyphSetCompat.cpp
@@ -0,0 +1,231 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderCreateGlyphSetCompat.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_SIZE
+{
+ //
+ // Strictly speaking this request doesn't have
+ // a data part. We encode the fields past the
+ // offset as they were data. An improvement
+ // would be to encode the format field using
+ // the cache.
+ //
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_SIZE
+
+MESSAGE_BEGIN_DECODE_SIZE
+{
+ size = MESSAGE_OFFSET + 4;
+
+ buffer = writeBuffer -> addMessage(size);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_SIZE
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeDiffCachedValue(GetULONG(buffer + 4, bigEndian),
+ clientCache -> renderLastId, 29,
+ clientCache -> renderIdCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastId, 29,
+ clientCache -> renderIdCache);
+
+ PutULONG(value, buffer + 4, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_ENCODE_DATA
+{
+ #ifdef DEBUG
+
+ *logofs << name() << ": Glyphset is " << GetULONG(buffer + 4, bigEndian)
+ << ".\n" << logofs_flush;
+
+ if (size > MESSAGE_OFFSET)
+ {
+ *logofs << name() << ": Format is " << GetULONG(buffer + 8, bigEndian)
+ << ".\n" << logofs_flush;
+ }
+
+ if (size > MESSAGE_OFFSET + 4)
+ {
+ *logofs << name() << ": WARNING! Unexpected size " << size
+ << ".\n" << logofs_flush;
+ }
+
+ #endif
+
+ encodeLongData(encodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_DATA
+
+MESSAGE_BEGIN_DECODE_DATA
+{
+ decodeLongData(decodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_DATA
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.create_set.type = *(buffer + 1);
+
+ renderExtension -> data.create_set.set_id = GetULONG(buffer + 4, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.create_set.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.create_set.type;
+
+ PutULONG(renderExtension -> data.create_set.set_id, buffer + 4, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.create_set.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ md5_append(md5_state, buffer + 1, 3);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeDiffCachedValue(renderExtension -> data.create_set.set_id,
+ clientCache -> renderLastId, 29,
+ clientCache -> renderIdCache);
+
+ cachedRenderExtension -> data.create_set.set_id =
+ renderExtension -> data.create_set.set_id;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.create_set.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeDiffCachedValue(renderExtension -> data.create_set.set_id,
+ clientCache -> renderLastId, 29,
+ clientCache -> renderIdCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.create_set.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/RenderCreateGlyphSetCompat.h b/nxcomp/RenderCreateGlyphSetCompat.h
new file mode 100644
index 000000000..174313e10
--- /dev/null
+++ b/nxcomp/RenderCreateGlyphSetCompat.h
@@ -0,0 +1,80 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderCreateGlyphSetCompat_H
+#define RenderCreateGlyphSetCompat_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderCreateGlyphSetCompat"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderCreateGlyphSetCompatStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 8
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 1
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 1
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return MESSAGE_OFFSET;
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderCreateGlyphSetCompat_H */
diff --git a/nxcomp/RenderCreatePicture.cpp b/nxcomp/RenderCreatePicture.cpp
new file mode 100644
index 000000000..cb3d56534
--- /dev/null
+++ b/nxcomp/RenderCreatePicture.cpp
@@ -0,0 +1,266 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderCreatePicture.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_SIZE
+
+MESSAGE_BEGIN_DECODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(size, 16,
+ clientCache -> renderLengthCache, 5);
+
+ size = MESSAGE_OFFSET + (size << 2);
+
+ buffer = writeBuffer -> addMessage(size);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_SIZE
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeNewXidValue(GetULONG(buffer + 4, bigEndian),
+ clientCache -> lastId, clientCache -> lastIdCache,
+ clientCache -> renderSrcPictureCache,
+ clientCache -> renderFreePictureCache);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian),
+ clientCache -> drawableCache);
+
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + 12, bigEndian), 32,
+ clientCache -> renderFormatCache);
+
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + 16, bigEndian), 32,
+ clientCache -> renderValueMaskCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeNewXidValue(value,
+ clientCache -> lastId, clientCache -> lastIdCache,
+ clientCache -> renderSrcPictureCache,
+ clientCache -> renderFreePictureCache);
+
+ PutULONG(value, buffer + 4, bigEndian);
+
+ decodeBuffer.decodeXidValue(value,
+ clientCache -> drawableCache);
+
+ PutULONG(value, buffer + 8, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 32,
+ clientCache -> renderFormatCache);
+
+ PutULONG(value, buffer + 12, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 32,
+ clientCache -> renderValueMaskCache);
+
+ PutULONG(value, buffer + 16, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_ENCODE_DATA
+{
+ encodeLongData(encodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_DATA
+
+MESSAGE_BEGIN_DECODE_DATA
+{
+ decodeLongData(decodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_DATA
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.create_picture.type = *(buffer + 1);
+
+ renderExtension -> data.create_picture.src_id = GetULONG(buffer + 4, bigEndian);
+ renderExtension -> data.create_picture.dst_id = GetULONG(buffer + 8, bigEndian);
+
+ renderExtension -> data.create_picture.format = GetULONG(buffer + 12, bigEndian);
+ renderExtension -> data.create_picture.mask = GetULONG(buffer + 16, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.create_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.create_picture.type;
+
+ PutULONG(renderExtension -> data.create_picture.src_id, buffer + 4, bigEndian);
+ PutULONG(renderExtension -> data.create_picture.dst_id, buffer + 8, bigEndian);
+
+ PutULONG(renderExtension -> data.create_picture.format, buffer + 12, bigEndian);
+ PutULONG(renderExtension -> data.create_picture.mask, buffer + 16, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.create_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ md5_append(md5_state, buffer + 1, 3);
+ md5_append(md5_state, buffer + 12, 8);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding new id value "
+ << renderExtension -> data.create_picture.src_id
+ << ".\n";
+ #endif
+
+ encodeBuffer.encodeNewXidValue(renderExtension -> data.create_picture.src_id,
+ clientCache -> lastId, clientCache -> lastIdCache,
+ clientCache -> renderSrcPictureCache,
+ clientCache -> renderFreePictureCache);
+
+ cachedRenderExtension -> data.create_picture.src_id =
+ renderExtension -> data.create_picture.src_id;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.create_picture.dst_id,
+ clientCache -> drawableCache);
+
+ cachedRenderExtension -> data.create_picture.dst_id =
+ renderExtension -> data.create_picture.dst_id;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.create_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeNewXidValue(renderExtension -> data.create_picture.src_id,
+ clientCache -> lastId, clientCache -> lastIdCache,
+ clientCache -> renderSrcPictureCache,
+ clientCache -> renderFreePictureCache);
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.create_picture.dst_id,
+ clientCache -> drawableCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.create_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/RenderCreatePicture.h b/nxcomp/RenderCreatePicture.h
new file mode 100644
index 000000000..35de9b86b
--- /dev/null
+++ b/nxcomp/RenderCreatePicture.h
@@ -0,0 +1,80 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderCreatePicture_H
+#define RenderCreatePicture_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderCreatePicture"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderCreatePictureStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 20
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 1
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 1
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return MESSAGE_OFFSET;
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderCreatePicture_H */
diff --git a/nxcomp/RenderCreatePictureCompat.cpp b/nxcomp/RenderCreatePictureCompat.cpp
new file mode 100644
index 000000000..fa4dcb400
--- /dev/null
+++ b/nxcomp/RenderCreatePictureCompat.cpp
@@ -0,0 +1,262 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderCreatePictureCompat.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_SIZE
+
+MESSAGE_BEGIN_DECODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(size, 16,
+ clientCache -> renderLengthCache, 5);
+
+ size = MESSAGE_OFFSET + (size << 2);
+
+ buffer = writeBuffer -> addMessage(size);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_SIZE
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeDiffCachedValue(GetULONG(buffer + 4, bigEndian),
+ clientCache -> renderLastId, 29,
+ clientCache -> renderIdCache);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian),
+ clientCache -> drawableCache);
+
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + 12, bigEndian), 32,
+ clientCache -> renderFormatCache);
+
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + 16, bigEndian), 32,
+ clientCache -> renderValueMaskCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastId, 29,
+ clientCache -> renderIdCache);
+
+ PutULONG(value, buffer + 4, bigEndian);
+
+ decodeBuffer.decodeXidValue(value,
+ clientCache -> drawableCache);
+
+ PutULONG(value, buffer + 8, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 32,
+ clientCache -> renderFormatCache);
+
+ PutULONG(value, buffer + 12, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 32,
+ clientCache -> renderValueMaskCache);
+
+ PutULONG(value, buffer + 16, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_ENCODE_DATA
+{
+ encodeLongData(encodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_DATA
+
+MESSAGE_BEGIN_DECODE_DATA
+{
+ decodeLongData(decodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_DATA
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.create_picture.type = *(buffer + 1);
+
+ renderExtension -> data.create_picture.src_id = GetULONG(buffer + 4, bigEndian);
+ renderExtension -> data.create_picture.dst_id = GetULONG(buffer + 8, bigEndian);
+
+ renderExtension -> data.create_picture.format = GetULONG(buffer + 12, bigEndian);
+ renderExtension -> data.create_picture.mask = GetULONG(buffer + 16, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.create_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.create_picture.type;
+
+ PutULONG(renderExtension -> data.create_picture.src_id, buffer + 4, bigEndian);
+ PutULONG(renderExtension -> data.create_picture.dst_id, buffer + 8, bigEndian);
+
+ PutULONG(renderExtension -> data.create_picture.format, buffer + 12, bigEndian);
+ PutULONG(renderExtension -> data.create_picture.mask, buffer + 16, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.create_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ md5_append(md5_state, buffer + 1, 3);
+ md5_append(md5_state, buffer + 12, 8);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding new id value "
+ << renderExtension -> data.create_picture.src_id -
+ clientCache -> renderLastId << ".\n";
+ #endif
+
+ encodeBuffer.encodeDiffCachedValue(renderExtension -> data.create_picture.src_id,
+ clientCache -> renderLastId, 29,
+ clientCache -> renderIdCache);
+
+ cachedRenderExtension -> data.create_picture.src_id =
+ renderExtension -> data.create_picture.src_id;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.create_picture.dst_id,
+ clientCache -> drawableCache);
+
+ cachedRenderExtension -> data.create_picture.dst_id =
+ renderExtension -> data.create_picture.dst_id;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.create_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeDiffCachedValue(renderExtension -> data.create_picture.src_id,
+ clientCache -> renderLastId, 29,
+ clientCache -> renderIdCache);
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.create_picture.dst_id,
+ clientCache -> drawableCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.create_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/RenderCreatePictureCompat.h b/nxcomp/RenderCreatePictureCompat.h
new file mode 100644
index 000000000..15c8c85b3
--- /dev/null
+++ b/nxcomp/RenderCreatePictureCompat.h
@@ -0,0 +1,80 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderCreatePictureCompat_H
+#define RenderCreatePictureCompat_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderCreatePictureCompat"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderCreatePictureCompatStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 20
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 1
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 1
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return MESSAGE_OFFSET;
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderCreatePictureCompat_H */
diff --git a/nxcomp/RenderExtension.cpp b/nxcomp/RenderExtension.cpp
new file mode 100644
index 000000000..79c26e64e
--- /dev/null
+++ b/nxcomp/RenderExtension.cpp
@@ -0,0 +1,567 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "NXrender.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "WriteBuffer.h"
+
+#include "RenderExtension.h"
+
+#include "RenderGenericRequest.h"
+#include "RenderCreatePicture.h"
+#include "RenderChangePicture.h"
+#include "RenderFreePicture.h"
+#include "RenderPictureClip.h"
+#include "RenderPictureTransform.h"
+#include "RenderPictureFilter.h"
+#include "RenderCreateGlyphSet.h"
+#include "RenderFreeGlyphSet.h"
+#include "RenderAddGlyphs.h"
+#include "RenderComposite.h"
+#include "RenderCompositeGlyphs.h"
+#include "RenderFillRectangles.h"
+#include "RenderTrapezoids.h"
+#include "RenderTriangles.h"
+
+#include "RenderCreatePictureCompat.h"
+#include "RenderFreePictureCompat.h"
+#include "RenderPictureClipCompat.h"
+#include "RenderCreateGlyphSetCompat.h"
+#include "RenderCompositeCompat.h"
+#include "RenderCompositeGlyphsCompat.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Constructor and destructor.
+//
+
+RenderExtensionStore::RenderExtensionStore(StaticCompressor *compressor)
+
+ : MessageStore(compressor)
+{
+ enableCache = RENDEREXTENSION_ENABLE_CACHE;
+ enableData = RENDEREXTENSION_ENABLE_DATA;
+ enableSplit = RENDEREXTENSION_ENABLE_SPLIT;
+ enableCompress = RENDEREXTENSION_ENABLE_COMPRESS;
+
+ generic_ = new RenderGenericRequestStore();
+
+ for (int i = 0; i < RENDEREXTENSION_MINOR_OPCODE_LIMIT; i++)
+ {
+ minors_[i] = generic_;
+ }
+
+ minors_[X_RenderChangePicture] = new RenderChangePictureStore();
+ minors_[X_RenderFillRectangles] = new RenderFillRectanglesStore();
+ minors_[X_RenderAddGlyphs] = new RenderAddGlyphsStore();
+
+ if (control -> isProtoStep7() == 1)
+ {
+ minors_[X_RenderCreatePicture] = new RenderCreatePictureStore();
+ minors_[X_RenderFreePicture] = new RenderFreePictureStore();
+ minors_[X_RenderSetPictureClipRectangles] = new RenderPictureClipStore();
+ minors_[X_RenderCreateGlyphSet] = new RenderCreateGlyphSetStore();
+ minors_[X_RenderComposite] = new RenderCompositeStore();
+ minors_[X_RenderCompositeGlyphs8] = new RenderCompositeGlyphsStore();
+ minors_[X_RenderCompositeGlyphs16] = new RenderCompositeGlyphsStore();
+ minors_[X_RenderCompositeGlyphs32] = new RenderCompositeGlyphsStore();
+
+ minors_[X_RenderSetPictureTransform] = new RenderPictureTransformStore();
+ minors_[X_RenderSetPictureFilter] = new RenderPictureFilterStore();
+ minors_[X_RenderFreeGlyphSet] = new RenderFreeGlyphSetStore();
+ minors_[X_RenderTrapezoids] = new RenderTrapezoidsStore();
+ minors_[X_RenderTriangles] = new RenderTrianglesStore();
+ }
+ else
+ {
+ minors_[X_RenderCreatePicture] = new RenderCreatePictureCompatStore();
+ minors_[X_RenderFreePicture] = new RenderFreePictureCompatStore();
+ minors_[X_RenderSetPictureClipRectangles] = new RenderPictureClipCompatStore();
+ minors_[X_RenderCreateGlyphSet] = new RenderCreateGlyphSetCompatStore();
+ minors_[X_RenderComposite] = new RenderCompositeCompatStore();
+ minors_[X_RenderCompositeGlyphs8] = new RenderCompositeGlyphsCompatStore();
+ minors_[X_RenderCompositeGlyphs16] = new RenderCompositeGlyphsCompatStore();
+ minors_[X_RenderCompositeGlyphs32] = new RenderCompositeGlyphsCompatStore();
+ }
+
+ dataLimit = RENDEREXTENSION_DATA_LIMIT;
+ dataOffset = RENDEREXTENSION_DATA_OFFSET;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ cacheSlots = RENDEREXTENSION_CACHE_SLOTS_IF_PROTO_STEP_7;
+ }
+ else
+ {
+ cacheSlots = RENDEREXTENSION_CACHE_SLOTS;
+ }
+
+ cacheThreshold = RENDEREXTENSION_CACHE_THRESHOLD;
+ cacheLowerThreshold = RENDEREXTENSION_CACHE_LOWER_THRESHOLD;
+
+ opcode_ = X_NXInternalRenderExtension;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+RenderExtensionStore::~RenderExtensionStore()
+{
+ for (int i = 0; i < RENDEREXTENSION_MINOR_OPCODE_LIMIT; i++)
+ {
+ if (minors_[i] != generic_)
+ {
+ delete minors_[i];
+ }
+ }
+
+ delete generic_;
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+int RenderExtensionStore::validateMessage(const unsigned char *buffer, int size)
+{
+ #ifdef TEST
+ *logofs << name() << ": Encoding message OPCODE#"
+ << (unsigned) *buffer << " MINOR#" << (unsigned)
+ *(buffer + 1) << " with size " << size
+ << ".\n" << logofs_flush;
+ #endif
+
+ return (size >= control -> MinimumMessageSize &&
+ size <= control -> MaximumMessageSize);
+}
+
+//
+// Here are the methods to handle the messages' content.
+//
+
+int RenderExtensionStore::identitySize(const unsigned char *buffer, unsigned int size)
+{
+ return minors_[*(buffer + 1)] -> identitySize(buffer, size);
+}
+
+int RenderExtensionStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ encodeBuffer.encodeOpcodeValue(*(buffer + 1),
+ ((ClientCache *) channelCache) -> renderOpcodeCache);
+
+ minors_[*(buffer + 1)] -> encodeMessage(encodeBuffer, buffer, size,
+ bigEndian, channelCache);
+
+ return 1;
+}
+
+int RenderExtensionStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const
+{
+ unsigned char type;
+
+ decodeBuffer.decodeOpcodeValue(type,
+ ((ClientCache *) channelCache) -> renderOpcodeCache);
+
+ minors_[type] -> decodeMessage(decodeBuffer, buffer, size, type,
+ bigEndian, writeBuffer, channelCache);
+
+ return 1;
+}
+
+int RenderExtensionStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ return minors_[*(buffer + 1)] -> parseIdentity(message, buffer, size, bigEndian);
+}
+
+int RenderExtensionStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ return minors_[((RenderExtensionMessage *) message) -> data.any.type] ->
+ unparseIdentity(message, buffer, size, bigEndian);
+}
+
+void RenderExtensionStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ minors_[*(buffer + 1)] -> identityChecksum(message, buffer, size, md5_state_, bigEndian);
+}
+
+void RenderExtensionStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ minors_[((RenderExtensionMessage *) message) -> data.any.type] ->
+ updateIdentity(encodeBuffer, message, cachedMessage, channelCache);
+}
+
+void RenderExtensionStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ minors_[((RenderExtensionMessage *) message) -> data.any.type] ->
+ updateIdentity(decodeBuffer, message, channelCache);
+}
+
+void RenderExtensionStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ #ifdef WARNING
+ *logofs << name() << ": WARNING! Dump of identity not implemented.\n"
+ << logofs_flush;
+ #endif
+
+ #endif
+}
+
+//
+// TODO: The following encoding and decoding functions
+// could be generalized further, for example by passing
+// the pointer to the data cache, the number of caches
+// made available by the caller and the first cache to
+// iterate through.
+//
+
+void RenderMinorExtensionStore::encodeLongData(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ unsigned int offset, unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ if (control -> isProtoStep7() == 1)
+ {
+ encodeBuffer.encodeLongData(buffer + offset, size - offset);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - offset
+ << " bytes of long data.\n" << logofs_flush;
+ #endif
+
+ return;
+ }
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ for (unsigned int i = offset, c = (offset - 4) % 16; i < size; i += 4)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding int with i = " << i << " c = "
+ << c << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + i, bigEndian), 32,
+ *clientCache -> renderDataCache[c]);
+
+ if (++c == 16) c = 0;
+ }
+}
+
+void RenderMinorExtensionStore::encodeIntData(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ unsigned int offset, unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ if (control -> isProtoStep7() == 1)
+ {
+ encodeBuffer.encodeIntData(buffer + offset, size - offset);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - offset
+ << " bytes of int data.\n" << logofs_flush;
+ #endif
+
+ return;
+ }
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ for (unsigned int i = offset, c = (offset - 4) % 16; i < size; i += 2)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding int with i = " << i << " c = "
+ << c << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(GetUINT(buffer + i, bigEndian), 16,
+ *clientCache -> renderDataCache[c]);
+
+ if (++c == 16) c = 0;
+ }
+}
+
+void RenderMinorExtensionStore::encodeCharData(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ unsigned int offset, unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ if (control -> isProtoStep7() == 1)
+ {
+ encodeBuffer.encodeTextData(buffer + offset, size - offset);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - offset
+ << " bytes of text data.\n" << logofs_flush;
+ #endif
+
+ return;
+ }
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ clientCache -> renderTextCompressor.reset();
+
+ const unsigned char *next = buffer + offset;
+
+ for (unsigned int i = offset; i < size; i++)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding char with i = " << i
+ << ".\n" << logofs_flush;
+ #endif
+
+ clientCache -> renderTextCompressor.
+ encodeChar(*next++, encodeBuffer);
+ }
+}
+
+void RenderMinorExtensionStore::decodeLongData(DecodeBuffer &decodeBuffer, unsigned char *buffer,
+ unsigned int offset, unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ if (control -> isProtoStep7() == 1)
+ {
+ decodeBuffer.decodeLongData(buffer + offset, size - offset);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - offset
+ << " bytes of long data.\n" << logofs_flush;
+ #endif
+
+ return;
+ }
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ for (unsigned int i = offset, c = (offset - 4) % 16; i < size; i += 4)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding int with i = " << i << " c = "
+ << c << ".\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 32,
+ *clientCache -> renderDataCache[c]);
+
+ PutULONG(value, buffer + i, bigEndian);
+
+ if (++c == 16) c = 0;
+ }
+}
+
+void RenderMinorExtensionStore::decodeIntData(DecodeBuffer &decodeBuffer, unsigned char *buffer,
+ unsigned int offset, unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ if (control -> isProtoStep7() == 1)
+ {
+ decodeBuffer.decodeIntData(buffer + offset, size - offset);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - offset
+ << " bytes of int data.\n" << logofs_flush;
+ #endif
+
+ return;
+ }
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ for (unsigned int i = offset, c = (offset - 4) % 16; i < size; i += 2)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding int with i = " << i << " c = "
+ << c << ".\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache -> renderDataCache[c]);
+
+ PutUINT(value, buffer + i, bigEndian);
+
+ if (++c == 16) c = 0;
+ }
+}
+
+void RenderMinorExtensionStore::decodeCharData(DecodeBuffer &decodeBuffer, unsigned char *buffer,
+ unsigned int offset, unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ if (control -> isProtoStep7() == 1)
+ {
+ decodeBuffer.decodeTextData(buffer + offset, size - offset);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - offset
+ << " bytes of text data.\n" << logofs_flush;
+ #endif
+
+ return;
+ }
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ clientCache -> renderTextCompressor.reset();
+
+ unsigned char *next = buffer + offset;
+
+ for (unsigned int i = offset; i < size; i++)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding char with i = " << i
+ << ".\n" << logofs_flush;
+ #endif
+
+ *next++ = clientCache -> renderTextCompressor.
+ decodeChar(decodeBuffer);
+ }
+}
+
+void RenderMinorExtensionStore::parseIntData(const Message *message, const unsigned char *buffer,
+ unsigned int offset, unsigned int size,
+ int bigEndian) const
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ unsigned int last = ((unsigned) message -> i_size_ > size ? size : message -> i_size_);
+
+ for (unsigned int i = offset, c = (offset - 4) % 16; i < last; i += 2)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Parsing int with i = " << i << " c = "
+ << c << ".\n" << logofs_flush;
+ #endif
+
+ renderExtension -> data.any.short_data[c] = GetUINT(buffer + i, bigEndian);
+
+ if (++c == 16) c = 0;
+ }
+}
+
+void RenderMinorExtensionStore::unparseIntData(const Message *message, unsigned char *buffer,
+ unsigned int offset, unsigned int size,
+ int bigEndian) const
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ unsigned int last = ((unsigned) message -> i_size_ > size ? size : message -> i_size_);
+
+ for (unsigned int i = offset, c = (offset - 4) % 16; i < last; i += 2)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsing int with i = " << i << " c = "
+ << c << ".\n" << logofs_flush;
+ #endif
+
+ PutUINT(renderExtension -> data.any.short_data[c], buffer + i, bigEndian);
+
+ if (++c == 16) c = 0;
+ }
+}
+
+void RenderMinorExtensionStore::updateIntData(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage, unsigned int offset,
+ unsigned int size, ChannelCache *channelCache) const
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int last = ((unsigned) message -> i_size_ > size ? size : message -> i_size_);
+
+ for (unsigned int i = offset, c = (offset - 4) % 16; i < last; i += 2)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding int update with i = " << i
+ << " c = " << c << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(renderExtension -> data.any.short_data[c], 16,
+ *clientCache -> renderDataCache[c]);
+
+ cachedRenderExtension -> data.any.short_data[c] =
+ renderExtension -> data.any.short_data[c];
+
+ if (++c == 16) c = 0;
+ }
+}
+
+void RenderMinorExtensionStore::updateIntData(DecodeBuffer &decodeBuffer, const Message *message,
+ unsigned int offset, unsigned int size,
+ ChannelCache *channelCache) const
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int last = ((unsigned) message -> i_size_ > size ? size : message -> i_size_);
+
+ unsigned int value;
+
+ for (unsigned int i = offset, c = (offset - 4) % 16; i < last; i += 2)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding int update with i = " << i
+ << " c = " << c << ".\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache -> renderDataCache[c]);
+
+ renderExtension -> data.any.short_data[c] = value;
+
+ if (++c == 16) c = 0;
+ }
+}
diff --git a/nxcomp/RenderExtension.h b/nxcomp/RenderExtension.h
new file mode 100644
index 000000000..275ef1c1d
--- /dev/null
+++ b/nxcomp/RenderExtension.h
@@ -0,0 +1,504 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderExtension_H
+#define RenderExtension_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Compression of data part is not enabled as
+// most messages of this type are smaller than
+// the current data size compression threshold.
+//
+
+#define RENDEREXTENSION_ENABLE_CACHE 1
+#define RENDEREXTENSION_ENABLE_DATA 0
+#define RENDEREXTENSION_ENABLE_SPLIT 0
+#define RENDEREXTENSION_ENABLE_COMPRESS 0
+
+#define RENDEREXTENSION_DATA_LIMIT 6144
+#define RENDEREXTENSION_DATA_OFFSET 36
+
+#define RENDEREXTENSION_CACHE_SLOTS 6000
+#define RENDEREXTENSION_CACHE_THRESHOLD 20
+#define RENDEREXTENSION_CACHE_LOWER_THRESHOLD 10
+
+#define RENDEREXTENSION_CACHE_SLOTS_IF_PROTO_STEP_7 8000
+
+//
+// Used to build the table of minor opcodes.
+//
+
+#define RENDEREXTENSION_MINOR_OPCODE_LIMIT 256
+
+//
+// The message class.
+//
+
+class RenderMinorExtensionStore;
+
+class RenderExtensionMessage : public Message
+{
+ friend class RenderExtensionStore;
+ friend class RenderMinorExtensionStore;
+
+ friend class RenderGenericRequestStore;
+ friend class RenderCreatePictureStore;
+ friend class RenderChangePictureStore;
+ friend class RenderFreePictureStore;
+ friend class RenderPictureClipStore;
+ friend class RenderPictureTransformStore;
+ friend class RenderPictureFilterStore;
+ friend class RenderCreateGlyphSetStore;
+ friend class RenderFreeGlyphSetStore;
+ friend class RenderAddGlyphsStore;
+ friend class RenderCompositeStore;
+ friend class RenderCompositeGlyphsStore;
+ friend class RenderFillRectanglesStore;
+ friend class RenderTrapezoidsStore;
+ friend class RenderTrianglesStore;
+
+ friend class RenderCreatePictureCompatStore;
+ friend class RenderFreePictureCompatStore;
+ friend class RenderPictureClipCompatStore;
+ friend class RenderCreateGlyphSetCompatStore;
+ friend class RenderCompositeCompatStore;
+ friend class RenderCompositeGlyphsCompatStore;
+
+ public:
+
+ RenderExtensionMessage()
+ {
+ }
+
+ ~RenderExtensionMessage()
+ {
+ }
+
+ //
+ // We consider for this message a data offset of 36,
+ // that is size of the biggest among all requests of
+ // this extension. The most common requests have a
+ // specific differential encoding, others are simply
+ // encoded through an array of int or char caches.
+ //
+
+ private:
+
+ union
+ {
+ struct
+ {
+ unsigned char type;
+
+ unsigned char char_data[32];
+ unsigned short short_data[16];
+ unsigned short long_data[8];
+ }
+ any;
+
+ struct
+ {
+ unsigned char type;
+
+ unsigned int src_id;
+ unsigned int dst_id;
+
+ unsigned int format;
+ unsigned int mask;
+ }
+ create_picture;
+
+ struct
+ {
+ unsigned char type;
+
+ unsigned int src_id;
+ }
+ change_picture;
+
+ struct
+ {
+ unsigned char type;
+
+ unsigned int src_id;
+ }
+ free_picture;
+
+ struct
+ {
+ unsigned char type;
+
+ unsigned int src_id;
+
+ unsigned short src_x;
+ unsigned short src_y;
+ }
+ picture_clip;
+
+ struct
+ {
+ unsigned char type;
+
+ unsigned int src_id;
+ }
+ picture_transform;
+
+ struct
+ {
+ unsigned char type;
+
+ unsigned int src_id;
+ unsigned int num_elm;
+ }
+ picture_filter;
+
+ struct
+ {
+ unsigned char type;
+
+ unsigned int set_id;
+ unsigned int format;
+ }
+ create_set;
+
+ struct
+ {
+ unsigned char type;
+
+ unsigned int set_id;
+ }
+ free_set;
+
+ struct
+ {
+ unsigned char type;
+
+ unsigned int set_id;
+ unsigned int num_elm;
+ }
+ add_glyphs;
+
+ struct
+ {
+ unsigned char type;
+
+ unsigned char op;
+
+ unsigned int src_id;
+ unsigned int msk_id;
+ unsigned int dst_id;
+
+ unsigned short src_x;
+ unsigned short src_y;
+
+ unsigned short msk_x;
+ unsigned short msk_y;
+
+ unsigned short dst_x;
+ unsigned short dst_y;
+
+ unsigned short width;
+ unsigned short height;
+ }
+ composite;
+
+ struct
+ {
+ unsigned char type;
+
+ unsigned char op;
+
+ unsigned char num_elm;
+
+ unsigned int src_id;
+ unsigned int dst_id;
+
+ unsigned int format;
+ unsigned int set_id;
+
+ unsigned short src_x;
+ unsigned short src_y;
+
+ unsigned short offset_x;
+ unsigned short offset_y;
+ }
+ composite_glyphs;
+
+ struct
+ {
+ unsigned char type;
+
+ unsigned char op;
+
+ unsigned int dst_id;
+ }
+ fill_rectangles;
+
+ struct
+ {
+ unsigned char type;
+
+ unsigned char op;
+
+ unsigned int src_id;
+ unsigned int dst_id;
+
+ unsigned int format;
+
+ unsigned short src_x;
+ unsigned short src_y;
+ }
+ trapezoids;
+
+ struct
+ {
+ unsigned char type;
+
+ unsigned char op;
+
+ unsigned int src_id;
+ unsigned int dst_id;
+
+ unsigned int format;
+
+ unsigned short src_x;
+ unsigned short src_y;
+ }
+ triangles;
+
+ struct
+ {
+ unsigned char type;
+
+ unsigned char op;
+
+ unsigned char num_elm;
+
+ unsigned int src_id;
+ unsigned int dst_id;
+
+ unsigned int format;
+ unsigned int set_id;
+
+ unsigned short src_x;
+ unsigned short src_y;
+
+ unsigned short delta_x;
+ unsigned short delta_y;
+ }
+ composite_glyphs_compat;
+
+ }
+ data;
+};
+
+class RenderExtensionStore : public MessageStore
+{
+ public:
+
+ RenderExtensionStore(StaticCompressor *compressor);
+
+ virtual ~RenderExtensionStore();
+
+ virtual const char *name() const
+ {
+ return "RenderExtension";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return opcode_;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(RenderExtensionMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new RenderExtensionMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new RenderExtensionMessage((const RenderExtensionMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (RenderExtensionMessage *) message;
+ }
+
+ //
+ // Determine if the message must be stored
+ // in the cache.
+ //
+
+ virtual int validateMessage(const unsigned char *buffer, int size);
+
+ //
+ // Since protocol step 5 these methods are
+ // specialized in their minor opcode stores.
+ //
+
+ virtual int identitySize(const unsigned char *buffer, unsigned int size);
+
+ virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const;
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+
+ private:
+
+ unsigned char opcode_;
+
+ //
+ // Keep pointers to specialized classes.
+ //
+
+ RenderMinorExtensionStore *minors_[RENDEREXTENSION_MINOR_OPCODE_LIMIT];
+
+ RenderMinorExtensionStore *generic_;
+};
+
+class RenderMinorExtensionStore : public MinorMessageStore
+{
+ public:
+
+ virtual const char *name() const = 0;
+
+ virtual int identitySize(const unsigned char *buffer, unsigned int size) = 0;
+
+ virtual int encodeMessage(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const = 0;
+
+ virtual int decodeMessage(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, unsigned char type, int bigEndian,
+ WriteBuffer *writeBuffer, ChannelCache *channelCache) const = 0;
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const = 0;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const = 0;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const = 0;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const = 0;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, md5_state_t *md5_state,
+ int bigEndian) const = 0;
+
+ //
+ // Internal encode and decode utilities.
+ //
+
+ protected:
+
+ void encodeLongData(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ unsigned int offset, unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ void encodeIntData(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ unsigned int offset, unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ void encodeCharData(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ unsigned int offset, unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ void decodeLongData(DecodeBuffer &decodeBuffer, unsigned char *buffer,
+ unsigned int offset, unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ void decodeIntData(DecodeBuffer &decodeBuffer, unsigned char *buffer,
+ unsigned int offset, unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ void decodeCharData(DecodeBuffer &decodeBuffer, unsigned char *buffer,
+ unsigned int offset, unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ /*
+ * The following methods are only used in the
+ * encoding of the generic render request. To
+ * be removed in future.
+ */
+
+ void parseIntData(const Message *message, const unsigned char *buffer,
+ unsigned int offset, unsigned int size,
+ int bigEndian) const;
+
+ void unparseIntData(const Message *message, unsigned char *buffer,
+ unsigned int offset, unsigned int size,
+ int bigEndian) const;
+
+ void updateIntData(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage, unsigned int offset,
+ unsigned int size, ChannelCache *channelCache) const;
+
+ void updateIntData(DecodeBuffer &decodeBuffer, const Message *message,
+ unsigned int offset, unsigned int size,
+ ChannelCache *channelCache) const;
+};
+
+#endif /* RenderExtension_H */
diff --git a/nxcomp/RenderFillRectangles.cpp b/nxcomp/RenderFillRectangles.cpp
new file mode 100644
index 000000000..6f08d97a4
--- /dev/null
+++ b/nxcomp/RenderFillRectangles.cpp
@@ -0,0 +1,225 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderFillRectangles.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_SIZE
+{
+ //
+ // The color structure (4 components, 2 bytes
+ // each) is included in the data part, so that
+ // it gets into the checksum. The rectangles
+ // are in the format x, y, width, height with
+ // 2 bytes per each field, so each request is
+ // at least 12 + 8 + 8 = 28 bytes long.
+ //
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_SIZE
+
+MESSAGE_BEGIN_DECODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(size, 16,
+ clientCache -> renderLengthCache, 5);
+
+ size = MESSAGE_OFFSET + (size << 2);
+
+ buffer = writeBuffer -> addMessage(size);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_SIZE
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue(*(buffer + 4), 8,
+ clientCache -> renderOpCache);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian),
+ clientCache -> renderSrcPictureCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeCachedValue(*(buffer + 4), 8,
+ clientCache -> renderOpCache);
+
+ decodeBuffer.decodeXidValue(value, clientCache -> renderSrcPictureCache);
+
+ PutULONG(value, buffer + 8, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_ENCODE_DATA
+{
+ encodeIntData(encodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_DATA
+
+MESSAGE_BEGIN_DECODE_DATA
+{
+ decodeIntData(decodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_DATA
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.fill_rectangles.type = *(buffer + 1);
+ renderExtension -> data.fill_rectangles.op = *(buffer + 4);
+
+ renderExtension -> data.fill_rectangles.dst_id = GetULONG(buffer + 8, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.fill_rectangles.type << " size is "
+ << renderExtension -> size_ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.fill_rectangles.type;
+ *(buffer + 4) = renderExtension -> data.fill_rectangles.op;
+
+ PutULONG(renderExtension -> data.fill_rectangles.dst_id, buffer + 8, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.fill_rectangles.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ md5_append(md5_state, buffer + 1, 4);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.fill_rectangles.dst_id,
+ clientCache -> renderSrcPictureCache);
+
+ cachedRenderExtension -> data.fill_rectangles.dst_id =
+ renderExtension -> data.fill_rectangles.dst_id;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.fill_rectangles.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.fill_rectangles.dst_id,
+ clientCache -> renderSrcPictureCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.fill_rectangles.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/RenderFillRectangles.h b/nxcomp/RenderFillRectangles.h
new file mode 100644
index 000000000..189855907
--- /dev/null
+++ b/nxcomp/RenderFillRectangles.h
@@ -0,0 +1,80 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderFillRectangles_H
+#define RenderFillRectangles_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderFillRectangles"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderFillRectanglesStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 12
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 1
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 1
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return MESSAGE_OFFSET;
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderFillRectangles_H */
diff --git a/nxcomp/RenderFreeGlyphSet.cpp b/nxcomp/RenderFreeGlyphSet.cpp
new file mode 100644
index 000000000..50010e381
--- /dev/null
+++ b/nxcomp/RenderFreeGlyphSet.cpp
@@ -0,0 +1,154 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderFreeGlyphSet.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeFreeXidValue(GetULONG(buffer + 4, bigEndian),
+ clientCache -> renderFreeGlyphSetCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ unsigned int value;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeFreeXidValue(value,
+ clientCache -> renderFreeGlyphSetCache);
+
+ PutULONG(value, buffer + 4, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.free_set.type = *(buffer + 1);
+
+ renderExtension -> data.free_set.set_id = GetULONG(buffer + 4, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.free_set.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.free_set.type;
+
+ PutULONG(renderExtension -> data.free_set.set_id, buffer + 4, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.free_set.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ md5_append(md5_state, buffer + 1, 3);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeFreeXidValue(renderExtension -> data.free_set.set_id,
+ clientCache -> renderFreeGlyphSetCache);
+
+ cachedRenderExtension -> data.free_set.set_id =
+ renderExtension -> data.free_set.set_id;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.free_set.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeFreeXidValue(renderExtension -> data.free_set.set_id,
+ clientCache -> renderFreeGlyphSetCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.free_set.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/RenderFreeGlyphSet.h b/nxcomp/RenderFreeGlyphSet.h
new file mode 100644
index 000000000..7233031ed
--- /dev/null
+++ b/nxcomp/RenderFreeGlyphSet.h
@@ -0,0 +1,80 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderFreeGlyphSet_H
+#define RenderFreeGlyphSet_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderFreeGlyphSet"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderFreeGlyphSetStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 8
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 0
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 0
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return MESSAGE_OFFSET;
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderFreeGlyphSet_H */
diff --git a/nxcomp/RenderFreePicture.cpp b/nxcomp/RenderFreePicture.cpp
new file mode 100644
index 000000000..b1074f33f
--- /dev/null
+++ b/nxcomp/RenderFreePicture.cpp
@@ -0,0 +1,154 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderFreePicture.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeFreeXidValue(GetULONG(buffer + 4, bigEndian),
+ clientCache -> renderFreePictureCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ unsigned int value;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeFreeXidValue(value,
+ clientCache -> renderFreePictureCache);
+
+ PutULONG(value, buffer + 4, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.free_picture.type = *(buffer + 1);
+
+ renderExtension -> data.free_picture.src_id = GetULONG(buffer + 4, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.free_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.free_picture.type;
+
+ PutULONG(renderExtension -> data.free_picture.src_id, buffer + 4, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.free_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ md5_append(md5_state, buffer + 1, 3);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeFreeXidValue(renderExtension -> data.free_picture.src_id,
+ clientCache -> renderFreePictureCache);
+
+ cachedRenderExtension -> data.free_picture.src_id =
+ renderExtension -> data.free_picture.src_id;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.free_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeFreeXidValue(renderExtension -> data.free_picture.src_id,
+ clientCache -> renderFreePictureCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.free_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/RenderFreePicture.h b/nxcomp/RenderFreePicture.h
new file mode 100644
index 000000000..2329cb4e5
--- /dev/null
+++ b/nxcomp/RenderFreePicture.h
@@ -0,0 +1,80 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderFreePicture_H
+#define RenderFreePicture_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderFreePicture"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderFreePictureStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 8
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 0
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 0
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return MESSAGE_OFFSET;
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderFreePicture_H */
diff --git a/nxcomp/RenderFreePictureCompat.cpp b/nxcomp/RenderFreePictureCompat.cpp
new file mode 100644
index 000000000..fb4c7ac54
--- /dev/null
+++ b/nxcomp/RenderFreePictureCompat.cpp
@@ -0,0 +1,158 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderFreePictureCompat.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeDiffCachedValue(GetULONG(buffer + 4, bigEndian),
+ clientCache -> renderLastId, 29,
+ clientCache -> renderIdCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ unsigned int value;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastId, 29,
+ clientCache -> renderIdCache);
+
+ PutULONG(value, buffer + 4, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.free_picture.type = *(buffer + 1);
+
+ renderExtension -> data.free_picture.src_id = GetULONG(buffer + 4, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.free_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.free_picture.type;
+
+ PutULONG(renderExtension -> data.free_picture.src_id, buffer + 4, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.free_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ md5_append(md5_state, buffer + 1, 3);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeDiffCachedValue(renderExtension -> data.free_picture.src_id,
+ clientCache -> renderLastId, 29,
+ clientCache -> renderIdCache);
+
+ cachedRenderExtension -> data.free_picture.src_id =
+ renderExtension -> data.free_picture.src_id;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.free_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeDiffCachedValue(renderExtension -> data.free_picture.src_id,
+ clientCache -> renderLastId, 29,
+ clientCache -> renderIdCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.free_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/RenderFreePictureCompat.h b/nxcomp/RenderFreePictureCompat.h
new file mode 100644
index 000000000..32d613ae0
--- /dev/null
+++ b/nxcomp/RenderFreePictureCompat.h
@@ -0,0 +1,80 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderFreePictureCompat_H
+#define RenderFreePictureCompat_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderFreePictureCompat"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderFreePictureCompatStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 8
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 0
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 0
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return MESSAGE_OFFSET;
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderFreePictureCompat_H */
diff --git a/nxcomp/RenderGenericRequest.cpp b/nxcomp/RenderGenericRequest.cpp
new file mode 100644
index 000000000..4f979c18f
--- /dev/null
+++ b/nxcomp/RenderGenericRequest.cpp
@@ -0,0 +1,258 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "NXrender.h"
+
+#include "RenderExtension.h"
+#include "RenderGenericRequest.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "WriteBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Here are the methods to handle the messages' content.
+//
+
+int RenderGenericRequestStore::encodeMessage(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding full message.\n"
+ << logofs_flush;
+
+ unsigned char type = *(buffer + 1);
+
+ #endif
+
+ encodeBuffer.encodeCachedValue(size >> 2, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding full unhandled message. "
+ << "Type is " << (unsigned int) type << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+
+ encodeIntData(encodeBuffer, buffer, 4, size,
+ bigEndian, clientCache);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoded full message.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int RenderGenericRequestStore::decodeMessage(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, unsigned char type, int bigEndian,
+ WriteBuffer *writeBuffer, ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding full message.\n"
+ << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(size, 16,
+ clientCache -> renderLengthCache, 5);
+
+ size <<= 2;
+
+ buffer = writeBuffer -> addMessage(size);
+
+ *(buffer + 1) = type;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding full unhandled message. "
+ << "Type is " << (unsigned int) type << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+
+ decodeIntData(decodeBuffer, buffer, 4, size,
+ bigEndian, clientCache);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded full message.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void RenderGenericRequestStore::encodeData(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+}
+
+void RenderGenericRequestStore::decodeData(DecodeBuffer &decodeBuffer, unsigned char *buffer,
+ unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+}
+
+int RenderGenericRequestStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ #ifdef DEBUG
+ *logofs << name() << ": Parsing identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ unsigned char type = *(buffer + 1);
+
+ renderExtension -> data.any.type = type;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsing unhandled identity. "
+ << "Type is " << (unsigned int) renderExtension -> data.any.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ parseIntData(message, buffer, 4, size, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int RenderGenericRequestStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsing identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ unsigned char type = renderExtension -> data.any.type;
+
+ *(buffer + 1) = type;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsing unhandled identity. "
+ << "Type is " << (unsigned int) renderExtension -> data.any.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ unparseIntData(message, buffer, 4, size, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void RenderGenericRequestStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, md5_state_t *md5_state,
+ int bigEndian) const
+{
+ //
+ // Include the minor opcode in the checksum.
+ // Because the data offset can be beyond the
+ // real end of the message, we need to include
+ // the size or we will match any message whose
+ // size is less or equal to the data offset.
+ //
+
+ md5_append(md5_state, buffer + 1, 3);
+}
+
+void RenderGenericRequestStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ //
+ // Encode the variant part.
+ //
+
+ #ifdef DEBUG
+ *logofs << name() << ": Updating identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding unhandled update. "
+ << "Type is " << (unsigned int) renderExtension -> data.any.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ updateIntData(encodeBuffer, message, cachedMessage, 4,
+ renderExtension -> size_, channelCache);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Updated identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+}
+
+void RenderGenericRequestStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ #ifdef DEBUG
+ *logofs << name() << ": Updating identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding unhandled update. "
+ << "Type is " << (unsigned int) renderExtension -> data.any.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ updateIntData(decodeBuffer, message, 4,
+ renderExtension -> size_, channelCache);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Updated identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+}
diff --git a/nxcomp/RenderGenericRequest.h b/nxcomp/RenderGenericRequest.h
new file mode 100644
index 000000000..8fa3acb76
--- /dev/null
+++ b/nxcomp/RenderGenericRequest.h
@@ -0,0 +1,81 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderGenericRequest_H
+#define RenderGenericRequest_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+class RenderGenericRequestStore : public RenderMinorExtensionStore
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return "RenderGenericRequest";
+ }
+
+ virtual int identitySize(const unsigned char *buffer, unsigned int size)
+ {
+ return RENDEREXTENSION_DATA_OFFSET;
+ }
+
+ virtual int encodeMessage(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual int decodeMessage(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, unsigned char type, int bigEndian,
+ WriteBuffer *writeBuffer, ChannelCache *channelCache) const;
+
+ virtual void encodeData(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual void decodeData(DecodeBuffer &decodeBuffer, unsigned char *buffer,
+ unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, md5_state_t *md5_state,
+ int bigEndian) const;
+};
+
+#endif /* RenderGenericRequest_H */
diff --git a/nxcomp/RenderMinorExtensionHeaders.h b/nxcomp/RenderMinorExtensionHeaders.h
new file mode 100644
index 000000000..dda0042d8
--- /dev/null
+++ b/nxcomp/RenderMinorExtensionHeaders.h
@@ -0,0 +1,34 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderMinorExtensionHeaders_H
+#define RenderMinorExtensionHeaders_H
+
+#include "NXrender.h"
+
+#include "Message.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "WriteBuffer.h"
+
+#include "RenderExtension.h"
+
+#endif /* RenderMinorExtensionHeaders_H */
diff --git a/nxcomp/RenderMinorExtensionMethods.h b/nxcomp/RenderMinorExtensionMethods.h
new file mode 100644
index 000000000..397f6966e
--- /dev/null
+++ b/nxcomp/RenderMinorExtensionMethods.h
@@ -0,0 +1,73 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+//
+// This file is included multiple times,
+// one for each message inheriting the
+// parent class.
+//
+
+public:
+
+#if MESSAGE_HAS_SIZE
+
+virtual void encodeSize(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+virtual void decodeSize(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, unsigned char type, int bigEndian,
+ WriteBuffer *writeBuffer, ChannelCache *channelCache) const;
+
+#endif
+
+#if MESSAGE_HAS_DATA
+
+virtual void encodeData(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+virtual void decodeData(DecodeBuffer &decodeBuffer, unsigned char *buffer,
+ unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+#endif
+
+virtual int encodeMessage(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+virtual int decodeMessage(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, unsigned char type, int bigEndian,
+ WriteBuffer *writeBuffer, ChannelCache *channelCache) const;
+
+virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, md5_state_t *md5_state,
+ int bigEndian) const;
diff --git a/nxcomp/RenderMinorExtensionTags.h b/nxcomp/RenderMinorExtensionTags.h
new file mode 100644
index 000000000..1d61b103c
--- /dev/null
+++ b/nxcomp/RenderMinorExtensionTags.h
@@ -0,0 +1,186 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderMinorExtensionTags_H
+#define RenderMinorExtensionTags_H
+
+//
+// Set in the message header file.
+//
+
+#if MESSAGE_HAS_SIZE
+
+#define MESSAGE_ENCODE_SIZE encodeSize(encodeBuffer, buffer, size, bigEndian, channelCache)
+#define MESSAGE_DECODE_SIZE decodeSize(decodeBuffer, buffer, size, type, bigEndian, writeBuffer, channelCache)
+
+#else
+
+#define MESSAGE_ENCODE_SIZE
+#define MESSAGE_DECODE_SIZE size = MESSAGE_OFFSET; buffer = writeBuffer -> addMessage(size);
+
+#endif
+
+#if MESSAGE_HAS_DATA
+
+#define MESSAGE_ENCODE_DATA encodeData(encodeBuffer, buffer, size, bigEndian, channelCache)
+#define MESSAGE_DECODE_DATA decodeData(decodeBuffer, buffer, size, bigEndian, channelCache)
+
+#else
+
+#define MESSAGE_ENCODE_DATA
+#define MESSAGE_DECODE_DATA
+
+#endif
+
+//
+// Prologue an epilogue of the message
+// handling functions.
+//
+
+#define MESSAGE_BEGIN_ENCODE_SIZE \
+\
+void MESSAGE_STORE::encodeSize(EncodeBuffer &encodeBuffer, const unsigned char *buffer, \
+ const unsigned int size, int bigEndian, \
+ ChannelCache *channelCache) const \
+{
+
+#define MESSAGE_END_ENCODE_SIZE \
+\
+}
+
+#define MESSAGE_BEGIN_DECODE_SIZE \
+\
+void MESSAGE_STORE::decodeSize(DecodeBuffer &decodeBuffer, unsigned char *&buffer, \
+ unsigned int &size, unsigned char type, int bigEndian, \
+ WriteBuffer *writeBuffer, ChannelCache *channelCache) const \
+{
+
+#define MESSAGE_END_DECODE_SIZE \
+\
+}
+
+#define MESSAGE_BEGIN_ENCODE_MESSAGE \
+\
+int MESSAGE_STORE::encodeMessage(EncodeBuffer &encodeBuffer, const unsigned char *buffer, \
+ const unsigned int size, int bigEndian, \
+ ChannelCache *channelCache) const \
+{ \
+ MESSAGE_ENCODE_SIZE;
+
+
+#define MESSAGE_END_ENCODE_MESSAGE \
+\
+ MESSAGE_ENCODE_DATA; \
+\
+ return 1; \
+}
+
+#define MESSAGE_BEGIN_DECODE_MESSAGE \
+\
+int MESSAGE_STORE::decodeMessage(DecodeBuffer &decodeBuffer, unsigned char *&buffer, \
+ unsigned int &size, unsigned char type, int bigEndian, \
+ WriteBuffer *writeBuffer, ChannelCache *channelCache) const \
+{ \
+ MESSAGE_DECODE_SIZE;
+
+
+#define MESSAGE_END_DECODE_MESSAGE \
+\
+ MESSAGE_DECODE_DATA; \
+\
+ return 1; \
+}
+
+#define MESSAGE_BEGIN_ENCODE_DATA \
+\
+void MESSAGE_STORE::encodeData(EncodeBuffer &encodeBuffer, const unsigned char *buffer, \
+ unsigned int size, int bigEndian, \
+ ChannelCache *channelCache) const \
+{
+
+#define MESSAGE_END_ENCODE_DATA \
+\
+}
+
+#define MESSAGE_BEGIN_DECODE_DATA \
+\
+void MESSAGE_STORE::decodeData(DecodeBuffer &decodeBuffer, unsigned char *buffer, \
+ unsigned int size, int bigEndian, \
+ ChannelCache *channelCache) const \
+{
+
+#define MESSAGE_END_DECODE_DATA \
+\
+}
+
+#define MESSAGE_BEGIN_PARSE_IDENTITY \
+\
+int MESSAGE_STORE::parseIdentity(Message *message, const unsigned char *buffer, \
+ unsigned int size, int bigEndian) const \
+{
+
+#define MESSAGE_END_PARSE_IDENTITY \
+\
+ return 1; \
+\
+}
+
+#define MESSAGE_BEGIN_UNPARSE_IDENTITY \
+\
+int MESSAGE_STORE::unparseIdentity(const Message *message, unsigned char *buffer, \
+ unsigned int size, int bigEndian) const \
+{
+
+#define MESSAGE_END_UNPARSE_IDENTITY \
+\
+ return 1; \
+\
+}
+
+#define MESSAGE_BEGIN_IDENTITY_CHECKSUM \
+\
+void MESSAGE_STORE::identityChecksum(const Message *message, const unsigned char *buffer, \
+ unsigned int size, md5_state_t *md5_state, \
+ int bigEndian) const \
+{
+
+#define MESSAGE_END_IDENTITY_CHECKSUM \
+\
+}
+
+#define MESSAGE_BEGIN_ENCODE_UPDATE \
+\
+void MESSAGE_STORE::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, \
+ const Message *cachedMessage, \
+ ChannelCache *channelCache) const \
+{
+
+#define MESSAGE_END_ENCODE_UPDATE \
+\
+}
+
+#define MESSAGE_BEGIN_DECODE_UPDATE \
+\
+void MESSAGE_STORE::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, \
+ ChannelCache *channelCache) const \
+{
+
+#define MESSAGE_END_DECODE_UPDATE \
+\
+}
+
+#endif /* RenderMinorExtensionTags_H */
diff --git a/nxcomp/RenderPictureClip.cpp b/nxcomp/RenderPictureClip.cpp
new file mode 100644
index 000000000..7428e7219
--- /dev/null
+++ b/nxcomp/RenderPictureClip.cpp
@@ -0,0 +1,291 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderPictureClip.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_SIZE
+{
+ //
+ // The data is constituted by a number of
+ // rectangles. Each rectangle is in the
+ // format x, y, width, height with 2 bytes
+ // per each field, so each request is at
+ // least 12 + 8 = 20 bytes long.
+ //
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_SIZE
+
+MESSAGE_BEGIN_DECODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(size, 16,
+ clientCache -> renderLengthCache, 5);
+
+ size = MESSAGE_OFFSET + (size << 2);
+
+ buffer = writeBuffer -> addMessage(size);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_SIZE
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 4, bigEndian),
+ clientCache -> renderSrcPictureCache);
+
+ encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 8, bigEndian),
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 10, bigEndian),
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeXidValue(value,
+ clientCache -> renderSrcPictureCache);
+
+ PutULONG(value, buffer + 4, bigEndian);
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ PutUINT(clientCache -> renderLastX, buffer + 8, bigEndian);
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+
+ PutUINT(clientCache -> renderLastY, buffer + 10, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_ENCODE_DATA
+{
+ encodeIntData(encodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_DATA
+
+MESSAGE_BEGIN_DECODE_DATA
+{
+ decodeIntData(decodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_DATA
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.picture_clip.type = *(buffer + 1);
+
+ renderExtension -> data.picture_clip.src_id = GetULONG(buffer + 4, bigEndian);
+
+ renderExtension -> data.picture_clip.src_x = GetUINT(buffer + 8, bigEndian);
+ renderExtension -> data.picture_clip.src_y = GetUINT(buffer + 10, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.picture_clip.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.picture_clip.type;
+
+ PutULONG(renderExtension -> data.picture_clip.src_id, buffer + 4, bigEndian);
+
+ PutUINT(renderExtension -> data.picture_clip.src_x, buffer + 8, bigEndian);
+ PutUINT(renderExtension -> data.picture_clip.src_y, buffer + 10, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.picture_clip.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ //
+ // Encode the picture id and the
+ // source x and y differentially.
+ //
+
+ md5_append(md5_state, buffer + 1, 3);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.picture_clip.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ cachedRenderExtension -> data.picture_clip.src_id =
+ renderExtension -> data.picture_clip.src_id;
+
+ //
+ // The source x and y coordinates are
+ // encoded as differerences in respect
+ // to the previous cached value.
+ //
+
+ unsigned int value;
+ unsigned int previous;
+
+ value = renderExtension -> data.picture_clip.src_x;
+ previous = cachedRenderExtension -> data.picture_clip.src_x;
+
+ encodeBuffer.encodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderXCache, 11);
+
+ cachedRenderExtension -> data.picture_clip.src_x = value;
+
+ value = renderExtension -> data.picture_clip.src_y;
+ previous = cachedRenderExtension -> data.picture_clip.src_y;
+
+ encodeBuffer.encodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderYCache, 11);
+
+ cachedRenderExtension -> data.picture_clip.src_y = value;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.picture_clip.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.picture_clip.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ unsigned int value;
+ unsigned int previous;
+
+ previous = renderExtension -> data.picture_clip.src_x;
+
+ decodeBuffer.decodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderXCache, 11);
+
+ renderExtension -> data.picture_clip.src_x = value;
+
+ previous = renderExtension -> data.picture_clip.src_y;
+
+ decodeBuffer.decodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderYCache, 11);
+
+ renderExtension -> data.picture_clip.src_y = value;
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.picture_clip.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/RenderPictureClip.h b/nxcomp/RenderPictureClip.h
new file mode 100644
index 000000000..35b320c31
--- /dev/null
+++ b/nxcomp/RenderPictureClip.h
@@ -0,0 +1,80 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderPictureClip_H
+#define RenderPictureClip_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderPictureClip"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderPictureClipStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 12
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 1
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 1
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return MESSAGE_OFFSET;
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderPictureClip_H */
diff --git a/nxcomp/RenderPictureClipCompat.cpp b/nxcomp/RenderPictureClipCompat.cpp
new file mode 100644
index 000000000..67d873008
--- /dev/null
+++ b/nxcomp/RenderPictureClipCompat.cpp
@@ -0,0 +1,237 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderPictureClipCompat.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_SIZE
+
+MESSAGE_BEGIN_DECODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(size, 16,
+ clientCache -> renderLengthCache, 5);
+
+ size = MESSAGE_OFFSET + (size << 2);
+
+ buffer = writeBuffer -> addMessage(size);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_SIZE
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 4, bigEndian),
+ clientCache -> renderSrcPictureCache);
+
+ encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 8, bigEndian),
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 10, bigEndian),
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeXidValue(value,
+ clientCache -> renderSrcPictureCache);
+
+ PutULONG(value, buffer + 4, bigEndian);
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ PutUINT(clientCache -> renderLastX, buffer + 8, bigEndian);
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+
+ PutUINT(clientCache -> renderLastY, buffer + 10, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_ENCODE_DATA
+{
+ encodeIntData(encodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_DATA
+
+MESSAGE_BEGIN_DECODE_DATA
+{
+ decodeIntData(decodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_DATA
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.picture_clip.type = *(buffer + 1);
+
+ renderExtension -> data.picture_clip.src_id = GetULONG(buffer + 4, bigEndian);
+
+ renderExtension -> data.picture_clip.src_x = GetUINT(buffer + 8, bigEndian);
+ renderExtension -> data.picture_clip.src_y = GetUINT(buffer + 10, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.picture_clip.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.picture_clip.type;
+
+ PutULONG(renderExtension -> data.picture_clip.src_id, buffer + 4, bigEndian);
+
+ PutUINT(renderExtension -> data.picture_clip.src_x, buffer + 8, bigEndian);
+ PutUINT(renderExtension -> data.picture_clip.src_y, buffer + 10, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.picture_clip.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ md5_append(md5_state, buffer + 1, 3);
+ md5_append(md5_state, buffer + 8, 4);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.picture_clip.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ cachedRenderExtension -> data.picture_clip.src_id =
+ renderExtension -> data.picture_clip.src_id;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.picture_clip.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.picture_clip.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.picture_clip.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/RenderPictureClipCompat.h b/nxcomp/RenderPictureClipCompat.h
new file mode 100644
index 000000000..05fc5cda8
--- /dev/null
+++ b/nxcomp/RenderPictureClipCompat.h
@@ -0,0 +1,80 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderPictureClipCompat_H
+#define RenderPictureClipCompat_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderPictureClipCompat"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderPictureClipCompatStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 12
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 1
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 1
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return MESSAGE_OFFSET;
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderPictureClipCompat_H */
diff --git a/nxcomp/RenderPictureFilter.cpp b/nxcomp/RenderPictureFilter.cpp
new file mode 100644
index 000000000..b48fdca15
--- /dev/null
+++ b/nxcomp/RenderPictureFilter.cpp
@@ -0,0 +1,266 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderPictureFilter.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding value "
+ << ((size - MESSAGE_OFFSET) >> 2) << ".\n"
+ << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_SIZE
+
+MESSAGE_BEGIN_DECODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(size, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << size
+ << ".\n" << logofs_flush;
+ #endif
+
+ size = MESSAGE_OFFSET + (size << 2);
+
+ buffer = writeBuffer -> addMessage(size);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_SIZE
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 4, bigEndian),
+ clientCache -> renderSrcPictureCache);
+
+ encodeBuffer.encodeCachedValue(GetUINT(buffer + 8, bigEndian), 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeXidValue(value,
+ clientCache -> renderSrcPictureCache);
+
+ PutULONG(value, buffer + 4, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> renderLengthCache, 5);
+
+ PutUINT(value, buffer + 8, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_ENCODE_DATA
+{
+ encodeCharData(encodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_DATA
+
+MESSAGE_BEGIN_DECODE_DATA
+{
+ decodeCharData(decodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_DATA
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.picture_filter.type = *(buffer + 1);
+
+ renderExtension -> data.picture_filter.src_id = GetULONG(buffer + 4, bigEndian);
+ renderExtension -> data.picture_filter.num_elm = GetUINT(buffer + 8, bigEndian);
+
+ //
+ // Clean the padding bytes. This
+ // should be the purpose of the
+ // filter.
+ //
+
+ #ifdef TEST
+ *logofs << name() << ": Cleaning "
+ << RoundUp4(renderExtension -> data.picture_filter.num_elm) -
+ renderExtension -> data.picture_filter.num_elm << " bytes "
+ << "at offset " << MESSAGE_OFFSET + renderExtension ->
+ data.picture_filter.num_elm << " with " << renderExtension ->
+ data.picture_filter.num_elm << " elements and size "
+ << renderExtension -> size_ << ".\n" << logofs_flush;
+ #endif
+
+ if (size >= MESSAGE_OFFSET + renderExtension ->
+ data.picture_filter.num_elm)
+ {
+ unsigned char *next = (unsigned char *) buffer +
+ MESSAGE_OFFSET + renderExtension ->
+ data.picture_filter.num_elm;
+
+ while (next < buffer + size)
+ {
+ *next++ = '\0';
+ }
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.picture_filter.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.picture_filter.type;
+
+ PutULONG(renderExtension -> data.picture_filter.src_id, buffer + 4, bigEndian);
+ PutUINT(renderExtension -> data.picture_filter.num_elm, buffer + 8, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.picture_filter.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ //
+ // Include the length of the filter name
+ // in the checksum.
+ //
+
+ md5_append(md5_state, buffer + 1, 3);
+ md5_append(md5_state, buffer + 8, 2);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.picture_filter.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ cachedRenderExtension -> data.picture_filter.src_id =
+ renderExtension -> data.picture_filter.src_id;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.picture_filter.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.picture_filter.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.picture_filter.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/RenderPictureFilter.h b/nxcomp/RenderPictureFilter.h
new file mode 100644
index 000000000..cf6ad5494
--- /dev/null
+++ b/nxcomp/RenderPictureFilter.h
@@ -0,0 +1,80 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderPictureFilter_H
+#define RenderPictureFilter_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderPictureFilter"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderPictureFilterStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 12
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 1
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 1
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return MESSAGE_OFFSET;
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderPictureFilter_H */
diff --git a/nxcomp/RenderPictureTransform.cpp b/nxcomp/RenderPictureTransform.cpp
new file mode 100644
index 000000000..048b73e6c
--- /dev/null
+++ b/nxcomp/RenderPictureTransform.cpp
@@ -0,0 +1,202 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderPictureTransform.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_SIZE
+{
+ //
+ // Size is always 44. The identity size
+ // is set to 8, so we encode the 36 bytes
+ // of the transformation matrix as our
+ // data.
+ //
+}
+MESSAGE_END_ENCODE_SIZE
+
+MESSAGE_BEGIN_DECODE_SIZE
+{
+ size = MESSAGE_OFFSET + 36;
+
+ buffer = writeBuffer -> addMessage(size);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_SIZE
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 4, bigEndian),
+ clientCache -> renderSrcPictureCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeXidValue(value,
+ clientCache -> renderSrcPictureCache);
+
+ PutULONG(value, buffer + 4, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_ENCODE_DATA
+{
+ encodeLongData(encodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_DATA
+
+MESSAGE_BEGIN_DECODE_DATA
+{
+ decodeLongData(decodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_DATA
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.picture_transform.type = *(buffer + 1);
+
+ renderExtension -> data.picture_transform.src_id = GetULONG(buffer + 4, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.picture_transform.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.picture_transform.type;
+
+ PutULONG(renderExtension -> data.picture_transform.src_id, buffer + 4, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.picture_transform.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ md5_append(md5_state, buffer + 1, 3);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.picture_transform.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ cachedRenderExtension -> data.picture_transform.src_id =
+ renderExtension -> data.picture_transform.src_id;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.picture_transform.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.picture_transform.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.picture_transform.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/RenderPictureTransform.h b/nxcomp/RenderPictureTransform.h
new file mode 100644
index 000000000..061b6a3d8
--- /dev/null
+++ b/nxcomp/RenderPictureTransform.h
@@ -0,0 +1,80 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderPictureTransform_H
+#define RenderPictureTransform_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderPictureTransform"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderPictureTransformStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 8
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 1
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 1
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return MESSAGE_OFFSET;
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderPictureTransform_H */
diff --git a/nxcomp/RenderTrapezoids.cpp b/nxcomp/RenderTrapezoids.cpp
new file mode 100644
index 000000000..32fcd01c0
--- /dev/null
+++ b/nxcomp/RenderTrapezoids.cpp
@@ -0,0 +1,360 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderTrapezoids.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_SIZE
+{
+ //
+ // The trapezoid data is made up of a structure
+ // containing a top and bottom coordinate in 4
+ // bytes format, plus two lines, each represent-
+ // ed as four points in 4 bytes format. Thus
+ // each trapezoid is 4 * 2 + (4 * 4) * 2 = 40
+ // bytes. Bytes are all padded to an long int,
+ // so we don't need to clean the message.
+ //
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding value "
+ << ((size - MESSAGE_OFFSET) >> 2) << ".\n"
+ << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_SIZE
+
+MESSAGE_BEGIN_DECODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(size, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << size
+ << ".\n" << logofs_flush;
+ #endif
+
+ size = MESSAGE_OFFSET + (size << 2);
+
+ buffer = writeBuffer -> addMessage(size);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_SIZE
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue(*(buffer + 4), 8,
+ clientCache -> renderOpCache);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian),
+ clientCache -> renderSrcPictureCache);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 12, bigEndian),
+ clientCache -> renderDstPictureCache);
+
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + 16, bigEndian), 32,
+ clientCache -> renderFormatCache);
+
+ encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 20, bigEndian),
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 22, bigEndian),
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeCachedValue(*(buffer + 4), 8,
+ clientCache -> renderOpCache);
+
+ decodeBuffer.decodeXidValue(value,
+ clientCache -> renderSrcPictureCache);
+
+ PutULONG(value, buffer + 8, bigEndian);
+
+ decodeBuffer.decodeXidValue(value,
+ clientCache -> renderDstPictureCache);
+
+ PutULONG(value, buffer + 12, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 32,
+ clientCache -> renderFormatCache);
+
+ PutULONG(value, buffer + 16, bigEndian);
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ PutUINT(clientCache -> renderLastX, buffer + 20, bigEndian);
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+
+ PutUINT(clientCache -> renderLastY, buffer + 22, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_ENCODE_DATA
+{
+ if (size > MESSAGE_OFFSET)
+ {
+ encodeLongData(encodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET
+ << " bytes of text data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_DATA
+
+MESSAGE_BEGIN_DECODE_DATA
+{
+ if (size > MESSAGE_OFFSET)
+ {
+ decodeLongData(decodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_DATA
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.trapezoids.type = *(buffer + 1);
+ renderExtension -> data.trapezoids.op = *(buffer + 4);
+
+ renderExtension -> data.trapezoids.src_id = GetULONG(buffer + 8, bigEndian);
+ renderExtension -> data.trapezoids.dst_id = GetULONG(buffer + 12, bigEndian);
+
+ renderExtension -> data.trapezoids.format = GetULONG(buffer + 16, bigEndian);
+
+ renderExtension -> data.trapezoids.src_x = GetUINT(buffer + 20, bigEndian);
+ renderExtension -> data.trapezoids.src_y = GetUINT(buffer + 22, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.trapezoids.type
+ << " size is " << renderExtension -> size_ << " identity size "
+ << renderExtension -> i_size_ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.trapezoids.type;
+ *(buffer + 4) = renderExtension -> data.trapezoids.op;
+
+ PutULONG(renderExtension -> data.trapezoids.src_id, buffer + 8, bigEndian);
+ PutULONG(renderExtension -> data.trapezoids.dst_id, buffer + 12, bigEndian);
+
+ PutULONG(renderExtension -> data.trapezoids.format, buffer + 16, bigEndian);
+
+ PutUINT(renderExtension -> data.trapezoids.src_x, buffer + 20, bigEndian);
+ PutUINT(renderExtension -> data.trapezoids.src_y, buffer + 22, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.trapezoids.type
+ << " size is " << renderExtension -> size_ << " identity size "
+ << renderExtension -> i_size_ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ //
+ // Include minor opcode, size and the
+ // operator in the identity.
+ //
+
+ md5_append(md5_state, buffer + 1, 4);
+
+ //
+ // Also include the format but not the
+ // x and y source.
+ //
+
+ md5_append(md5_state, buffer + 16, 4);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.trapezoids.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ cachedRenderExtension -> data.trapezoids.src_id =
+ renderExtension -> data.trapezoids.src_id;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.trapezoids.dst_id,
+ clientCache -> renderDstPictureCache);
+
+ cachedRenderExtension -> data.trapezoids.dst_id =
+ renderExtension -> data.trapezoids.dst_id;
+
+ //
+ // The source x and y coordinates are
+ // encoded as differerences in respect
+ // to the previous cached value.
+ //
+
+ unsigned int value;
+ unsigned int previous;
+
+ value = renderExtension -> data.trapezoids.src_x;
+ previous = cachedRenderExtension -> data.trapezoids.src_x;
+
+ encodeBuffer.encodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderXCache, 11);
+
+ cachedRenderExtension -> data.trapezoids.src_x = value;
+
+ value = renderExtension -> data.trapezoids.src_y;
+ previous = cachedRenderExtension -> data.trapezoids.src_y;
+
+ encodeBuffer.encodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderYCache, 11);
+
+ cachedRenderExtension -> data.trapezoids.src_y = value;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.trapezoids.type
+ << " size is " << renderExtension -> size_ << " source x "
+ << renderExtension -> data.trapezoids.src_x << " y "
+ << renderExtension -> data.trapezoids.src_y << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.trapezoids.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.trapezoids.dst_id,
+ clientCache -> renderDstPictureCache);
+
+ unsigned int value;
+ unsigned int previous;
+
+ previous = renderExtension -> data.trapezoids.src_x;
+
+ decodeBuffer.decodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderXCache, 11);
+
+ renderExtension -> data.trapezoids.src_x = value;
+
+ previous = renderExtension -> data.trapezoids.src_y;
+
+ decodeBuffer.decodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderYCache, 11);
+
+ renderExtension -> data.trapezoids.src_y = value;
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.trapezoids.type
+ << " size is " << renderExtension -> size_ << " source x "
+ << renderExtension -> data.trapezoids.src_x << " y "
+ << renderExtension -> data.trapezoids.src_y << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/RenderTrapezoids.h b/nxcomp/RenderTrapezoids.h
new file mode 100644
index 000000000..3f3202016
--- /dev/null
+++ b/nxcomp/RenderTrapezoids.h
@@ -0,0 +1,80 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderTrapezoids_H
+#define RenderTrapezoids_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderTrapezoids"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderTrapezoidsStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 24
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 1
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 1
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return (size >= MESSAGE_OFFSET ? MESSAGE_OFFSET : size);
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderTrapezoids_H */
diff --git a/nxcomp/RenderTriangles.cpp b/nxcomp/RenderTriangles.cpp
new file mode 100644
index 000000000..e98bf3506
--- /dev/null
+++ b/nxcomp/RenderTriangles.cpp
@@ -0,0 +1,350 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderTriangles.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding value "
+ << ((size - MESSAGE_OFFSET) >> 2) << ".\n"
+ << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_SIZE
+
+MESSAGE_BEGIN_DECODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(size, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << size
+ << ".\n" << logofs_flush;
+ #endif
+
+ size = MESSAGE_OFFSET + (size << 2);
+
+ buffer = writeBuffer -> addMessage(size);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_SIZE
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue(*(buffer + 4), 8,
+ clientCache -> renderOpCache);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian),
+ clientCache -> renderSrcPictureCache);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 12, bigEndian),
+ clientCache -> renderDstPictureCache);
+
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + 16, bigEndian), 32,
+ clientCache -> renderFormatCache);
+
+ encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 20, bigEndian),
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 22, bigEndian),
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeCachedValue(*(buffer + 4), 8,
+ clientCache -> renderOpCache);
+
+ decodeBuffer.decodeXidValue(value,
+ clientCache -> renderSrcPictureCache);
+
+ PutULONG(value, buffer + 8, bigEndian);
+
+ decodeBuffer.decodeXidValue(value,
+ clientCache -> renderDstPictureCache);
+
+ PutULONG(value, buffer + 12, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 32,
+ clientCache -> renderFormatCache);
+
+ PutULONG(value, buffer + 16, bigEndian);
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ PutUINT(clientCache -> renderLastX, buffer + 20, bigEndian);
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+
+ PutUINT(clientCache -> renderLastY, buffer + 22, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_ENCODE_DATA
+{
+ if (size > MESSAGE_OFFSET)
+ {
+ encodeLongData(encodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET
+ << " bytes of text data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_DATA
+
+MESSAGE_BEGIN_DECODE_DATA
+{
+ if (size > MESSAGE_OFFSET)
+ {
+ decodeLongData(decodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_DATA
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.triangles.type = *(buffer + 1);
+ renderExtension -> data.triangles.op = *(buffer + 4);
+
+ renderExtension -> data.triangles.src_id = GetULONG(buffer + 8, bigEndian);
+ renderExtension -> data.triangles.dst_id = GetULONG(buffer + 12, bigEndian);
+
+ renderExtension -> data.triangles.format = GetULONG(buffer + 16, bigEndian);
+
+ renderExtension -> data.triangles.src_x = GetUINT(buffer + 20, bigEndian);
+ renderExtension -> data.triangles.src_y = GetUINT(buffer + 22, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.triangles.type
+ << " size is " << renderExtension -> size_ << " identity size "
+ << renderExtension -> i_size_ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.triangles.type;
+ *(buffer + 4) = renderExtension -> data.triangles.op;
+
+ PutULONG(renderExtension -> data.triangles.src_id, buffer + 8, bigEndian);
+ PutULONG(renderExtension -> data.triangles.dst_id, buffer + 12, bigEndian);
+
+ PutULONG(renderExtension -> data.triangles.format, buffer + 16, bigEndian);
+
+ PutUINT(renderExtension -> data.triangles.src_x, buffer + 20, bigEndian);
+ PutUINT(renderExtension -> data.triangles.src_y, buffer + 22, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.triangles.type
+ << " size is " << renderExtension -> size_ << " identity size "
+ << renderExtension -> i_size_ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ //
+ // Include minor opcode, size and the
+ // operator in the identity.
+ //
+
+ md5_append(md5_state, buffer + 1, 4);
+
+ //
+ // Also include the format but not the
+ // x and y source.
+ //
+
+ md5_append(md5_state, buffer + 16, 4);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.triangles.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ cachedRenderExtension -> data.triangles.src_id =
+ renderExtension -> data.triangles.src_id;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.triangles.dst_id,
+ clientCache -> renderDstPictureCache);
+
+ cachedRenderExtension -> data.triangles.dst_id =
+ renderExtension -> data.triangles.dst_id;
+
+ //
+ // The source x and y coordinates are
+ // encoded as differerences in respect
+ // to the previous cached value.
+ //
+
+ unsigned int value;
+ unsigned int previous;
+
+ value = renderExtension -> data.triangles.src_x;
+ previous = cachedRenderExtension -> data.triangles.src_x;
+
+ encodeBuffer.encodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderXCache, 11);
+
+ cachedRenderExtension -> data.triangles.src_x = value;
+
+ value = renderExtension -> data.triangles.src_y;
+ previous = cachedRenderExtension -> data.triangles.src_y;
+
+ encodeBuffer.encodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderYCache, 11);
+
+ cachedRenderExtension -> data.triangles.src_y = value;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.triangles.type
+ << " size is " << renderExtension -> size_ << " source x "
+ << renderExtension -> data.triangles.src_x << " y "
+ << renderExtension -> data.triangles.src_y << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.triangles.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.triangles.dst_id,
+ clientCache -> renderDstPictureCache);
+
+ unsigned int value;
+ unsigned int previous;
+
+ previous = renderExtension -> data.triangles.src_x;
+
+ decodeBuffer.decodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderXCache, 11);
+
+ renderExtension -> data.triangles.src_x = value;
+
+ previous = renderExtension -> data.triangles.src_y;
+
+ decodeBuffer.decodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderYCache, 11);
+
+ renderExtension -> data.triangles.src_y = value;
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.triangles.type
+ << " size is " << renderExtension -> size_ << " source x "
+ << renderExtension -> data.triangles.src_x << " y "
+ << renderExtension -> data.triangles.src_y << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/RenderTriangles.h b/nxcomp/RenderTriangles.h
new file mode 100644
index 000000000..6c4d105a1
--- /dev/null
+++ b/nxcomp/RenderTriangles.h
@@ -0,0 +1,80 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderTriangles_H
+#define RenderTriangles_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderTriangles"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderTrianglesStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 24
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 1
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 1
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return (size >= MESSAGE_OFFSET ? MESSAGE_OFFSET : size);
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderTriangles_H */
diff --git a/nxcomp/Rgb.cpp b/nxcomp/Rgb.cpp
new file mode 100644
index 000000000..ad2e2fd92
--- /dev/null
+++ b/nxcomp/Rgb.cpp
@@ -0,0 +1,94 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "Misc.h"
+#include "Rgb.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+int UnpackRgb(T_geometry *geometry, unsigned char method, unsigned char *src_data,
+ int src_size, int dst_bpp, int dst_width, int dst_height,
+ unsigned char *dst_data, int dst_size)
+{
+ if (*src_data == 0)
+ {
+ if (dst_size != src_size - 1)
+ {
+ #ifdef TEST
+ *logofs << "UnpackRgb: PANIC! Invalid destination size "
+ << dst_size << " with source " << src_size
+ << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "UnpackRgb: Expanding " << src_size - 1
+ << " bytes of plain RGB data.\n" << logofs_flush;
+ #endif
+
+ memcpy(dst_data, src_data + 1, src_size - 1);
+
+ return 1;
+ }
+
+ unsigned int check_size = dst_size;
+
+ int result = ZDecompress(&unpackStream, dst_data, &check_size,
+ src_data + 1, src_size - 1);
+
+ if (result != Z_OK)
+ {
+ #ifdef PANIC
+ *logofs << "UnpackRgb: PANIC! Failure decompressing RGB data. "
+ << "Error is '" << zError(result) << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decompressing RGB data. "
+ << "Error is '" << zError(result) << "'.\n";
+
+ return -1;
+ }
+ else if (check_size != (unsigned int) dst_size)
+ {
+ #ifdef PANIC
+ *logofs << "UnpackRgb: PANIC! Size mismatch in RGB data. "
+ << "Resulting size is " << check_size << " with "
+ << "expected size " << dst_size << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Size mismatch in RGB data. "
+ << "Resulting size is " << check_size << " with "
+ << "expected size " << dst_size << ".\n";
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "UnpackRgb: Decompressed " << src_size - 1
+ << " bytes to " << dst_size << " bytes of RGB data.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
diff --git a/nxcomp/Rgb.h b/nxcomp/Rgb.h
new file mode 100644
index 000000000..98ead38e3
--- /dev/null
+++ b/nxcomp/Rgb.h
@@ -0,0 +1,28 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef Rgb_H
+#define Rgb_H
+
+#include "Unpack.h"
+
+int UnpackRgb(T_geometry *geometry, unsigned char method,
+ unsigned char *src_data, int src_size, int dst_bpp,
+ int dst_width, int dst_height, unsigned char *dst_data,
+ int dst_size);
+
+#endif /* Rgb_H */
diff --git a/nxcomp/Rle.cpp b/nxcomp/Rle.cpp
new file mode 100644
index 000000000..b7b1460bd
--- /dev/null
+++ b/nxcomp/Rle.cpp
@@ -0,0 +1,94 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "Misc.h"
+#include "Rle.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+int UnpackRle(T_geometry *geometry, unsigned char method, unsigned char *src_data,
+ int src_size, int dst_bpp, int dst_width, int dst_height,
+ unsigned char *dst_data, int dst_size)
+{
+ if (*src_data == 0)
+ {
+ if (dst_size != src_size - 1)
+ {
+ #ifdef TEST
+ *logofs << "UnpackRle: PANIC! Invalid destination size "
+ << dst_size << " with source " << src_size
+ << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "UnpackRle: Expanding " << src_size - 1
+ << " bytes of plain RLE data.\n" << logofs_flush;
+ #endif
+
+ memcpy(dst_data, src_data + 1, src_size - 1);
+
+ return 1;
+ }
+
+ unsigned int check_size = dst_size;
+
+ int result = ZDecompress(&unpackStream, dst_data, &check_size,
+ src_data + 1, src_size - 1);
+
+ if (result != Z_OK)
+ {
+ #ifdef PANIC
+ *logofs << "UnpackRle: PANIC! Failure decompressing RLE data. "
+ << "Error is '" << zError(result) << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decompressing RLE data. "
+ << "Error is '" << zError(result) << "'.\n";
+
+ return -1;
+ }
+ else if (check_size != (unsigned int) dst_size)
+ {
+ #ifdef PANIC
+ *logofs << "UnpackRle: PANIC! Size mismatch in RLE data. "
+ << "Resulting size is " << check_size << " with "
+ << "expected size " << dst_size << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Size mismatch in RLE data. "
+ << "Resulting size is " << check_size << " with "
+ << "expected size " << dst_size << ".\n";
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "UnpackRle: Decompressed " << src_size - 1
+ << " bytes to " << dst_size << " bytes of RLE data.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
diff --git a/nxcomp/Rle.h b/nxcomp/Rle.h
new file mode 100644
index 000000000..0e39fae5a
--- /dev/null
+++ b/nxcomp/Rle.h
@@ -0,0 +1,28 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef Rle_H
+#define Rle_H
+
+#include "Unpack.h"
+
+int UnpackRle(T_geometry *geometry, unsigned char method,
+ unsigned char *src_data, int src_size, int dst_bpp,
+ int dst_width, int dst_height, unsigned char *dst_data,
+ int dst_size);
+
+#endif /* Rle_H */
diff --git a/nxcomp/SendEvent.cpp b/nxcomp/SendEvent.cpp
new file mode 100644
index 000000000..f65b1dbb2
--- /dev/null
+++ b/nxcomp/SendEvent.cpp
@@ -0,0 +1,292 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "SendEvent.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "IntCache.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int SendEventStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ SendEventMessage *sendEvent = (SendEventMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ sendEvent -> propagate = *(buffer + 1);
+
+ sendEvent -> window = GetULONG(buffer + 4, bigEndian);
+ sendEvent -> mask = GetULONG(buffer + 8, bigEndian);
+
+ sendEvent -> code = *(buffer + 12);
+ sendEvent -> byte_data = *(buffer + 13);
+
+ sendEvent -> sequence = GetUINT(buffer + 14, bigEndian);
+
+ sendEvent -> int_data = GetULONG(buffer + 16, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int SendEventStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ SendEventMessage *sendEvent = (SendEventMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ *(buffer + 1) = sendEvent -> propagate;
+
+ PutULONG(sendEvent -> window, buffer + 4, bigEndian);
+ PutULONG(sendEvent -> mask, buffer + 8, bigEndian);
+
+ *(buffer + 12) = sendEvent -> code;
+ *(buffer + 13) = sendEvent -> byte_data;
+
+ PutUINT(sendEvent -> sequence, buffer + 14, bigEndian);
+
+ PutULONG(sendEvent -> int_data, buffer + 16, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void SendEventStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ SendEventMessage *sendEvent = (SendEventMessage *) message;
+
+ *logofs << name() << ": Identity propagate " << (unsigned int) sendEvent -> propagate
+ << ", window " << sendEvent -> window << ", mask " << sendEvent -> mask
+ << ", code " << (unsigned int) sendEvent -> code << ", byte_data "
+ << (unsigned int) sendEvent -> byte_data << ", sequence "
+ << sendEvent -> sequence << ", int_data " << sendEvent -> int_data
+ << ", size " << sendEvent -> size_ << ".\n" << logofs_flush;
+
+ #endif
+}
+
+void SendEventStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+}
+
+void SendEventStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ SendEventMessage *sendEvent = (SendEventMessage *) message;
+ SendEventMessage *cachedSendEvent = (SendEventMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << (unsigned int) sendEvent -> propagate
+ << " as propagate field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeBoolValue(sendEvent -> propagate);
+
+ cachedSendEvent -> propagate = sendEvent -> propagate;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << sendEvent -> window
+ << " as window field.\n" << logofs_flush;
+ #endif
+
+ if (sendEvent -> window == 0 || sendEvent -> window == 1)
+ {
+ encodeBuffer.encodeBoolValue(1);
+
+ encodeBuffer.encodeBoolValue(sendEvent -> window);
+ }
+ else
+ {
+ encodeBuffer.encodeBoolValue(0);
+
+ encodeBuffer.encodeXidValue(sendEvent -> window, clientCache -> windowCache);
+ }
+
+ cachedSendEvent -> window = sendEvent -> window;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << sendEvent -> mask
+ << " as mask field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(sendEvent -> mask, 32,
+ clientCache -> sendEventMaskCache);
+
+ cachedSendEvent -> mask = sendEvent -> mask;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << sendEvent -> code
+ << " as code field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(sendEvent -> code, 8,
+ clientCache -> sendEventCodeCache);
+
+ cachedSendEvent -> code = sendEvent -> code;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << sendEvent -> byte_data
+ << " as byte_data field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(sendEvent -> byte_data, 8,
+ clientCache -> sendEventByteDataCache);
+
+ cachedSendEvent -> byte_data = sendEvent -> byte_data;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << sendEvent -> sequence
+ << " as sequence field.\n" << logofs_flush;
+ #endif
+
+ unsigned int diffSeq = sendEvent -> sequence -
+ clientCache -> sendEventLastSequence;
+
+ clientCache -> sendEventLastSequence = sendEvent -> sequence;
+
+ encodeBuffer.encodeValue(diffSeq, 16, 4);
+
+ cachedSendEvent -> sequence = sendEvent -> sequence;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << sendEvent -> int_data
+ << " as int_data field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(sendEvent -> int_data, 32,
+ clientCache -> sendEventIntDataCache);
+
+ cachedSendEvent -> int_data = sendEvent -> int_data;
+}
+
+void SendEventStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ SendEventMessage *sendEvent = (SendEventMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeBoolValue(value);
+
+ sendEvent -> propagate = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << (unsigned int) sendEvent -> propagate
+ << " as propagate field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeBoolValue(value);
+
+ if (value)
+ {
+ decodeBuffer.decodeBoolValue(value);
+ }
+ else
+ {
+ decodeBuffer.decodeXidValue(value, clientCache -> windowCache);
+ }
+
+ sendEvent -> window = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << sendEvent -> window
+ << " as window field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(sendEvent -> mask, 32,
+ clientCache -> sendEventMaskCache);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << sendEvent -> mask
+ << " as mask field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(sendEvent -> code, 8,
+ clientCache -> sendEventCodeCache);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << sendEvent -> code
+ << " as code field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(sendEvent -> byte_data, 8,
+ clientCache -> sendEventByteDataCache);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << sendEvent -> byte_data
+ << " as byte_data field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeValue(value, 16, 4);
+
+ clientCache -> sendEventLastSequence += value;
+ clientCache -> sendEventLastSequence &= 0xffff;
+
+ sendEvent -> sequence = clientCache -> sendEventLastSequence;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << sendEvent -> sequence
+ << " as sequence field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(sendEvent -> int_data, 32,
+ clientCache -> sendEventIntDataCache);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << sendEvent -> int_data
+ << " as int_data field.\n" << logofs_flush;
+ #endif
+}
diff --git a/nxcomp/SendEvent.h b/nxcomp/SendEvent.h
new file mode 100644
index 000000000..9426180a4
--- /dev/null
+++ b/nxcomp/SendEvent.h
@@ -0,0 +1,187 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef SendEvent_H
+#define SendEvent_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define SENDEVENT_ENABLE_CACHE 1
+#define SENDEVENT_ENABLE_DATA 0
+#define SENDEVENT_ENABLE_SPLIT 0
+#define SENDEVENT_ENABLE_COMPRESS 0
+
+#define SENDEVENT_DATA_LIMIT 24
+#define SENDEVENT_DATA_OFFSET 20
+
+#define SENDEVENT_CACHE_SLOTS 2000
+#define SENDEVENT_CACHE_THRESHOLD 2
+#define SENDEVENT_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class SendEventMessage : public Message
+{
+ friend class SendEventStore;
+
+ public:
+
+ SendEventMessage()
+ {
+ }
+
+ ~SendEventMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char propagate;
+ unsigned int window;
+ unsigned int mask;
+
+ //
+ // These are part of the event data.
+ //
+
+ unsigned char code;
+ unsigned char byte_data;
+ unsigned short sequence;
+ unsigned int int_data;
+};
+
+class SendEventStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ SendEventStore() : MessageStore()
+ {
+ enableCache = SENDEVENT_ENABLE_CACHE;
+ enableData = SENDEVENT_ENABLE_DATA;
+ enableSplit = SENDEVENT_ENABLE_SPLIT;
+ enableCompress = SENDEVENT_ENABLE_COMPRESS;
+
+ dataLimit = SENDEVENT_DATA_LIMIT;
+ dataOffset = SENDEVENT_DATA_OFFSET;
+
+ cacheSlots = SENDEVENT_CACHE_SLOTS;
+ cacheThreshold = SENDEVENT_CACHE_THRESHOLD;
+ cacheLowerThreshold = SENDEVENT_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~SendEventStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "SendEvent";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_SendEvent;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(SendEventMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new SendEventMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new SendEventMessage((const SendEventMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (SendEventMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* SendEvent_H */
diff --git a/nxcomp/SequenceQueue.cpp b/nxcomp/SequenceQueue.cpp
new file mode 100644
index 000000000..ce044b1ff
--- /dev/null
+++ b/nxcomp/SequenceQueue.cpp
@@ -0,0 +1,162 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "SequenceQueue.h"
+
+static const unsigned int INITIAL_SIZE_ = 16;
+static const unsigned int GROWTH_INCREMENT = 16;
+
+SequenceQueue::SequenceQueue()
+
+ : queue_(new RequestSequence[INITIAL_SIZE_]), size_(INITIAL_SIZE_),
+ length_(0), start_(0), end_(0)
+{
+}
+
+SequenceQueue::~SequenceQueue()
+{
+ delete [] queue_;
+}
+
+void SequenceQueue::push(unsigned short int sequence, unsigned char opcode,
+ unsigned int data1, unsigned int data2,
+ unsigned int data3)
+{
+ if (length_ == 0)
+ {
+ start_ = end_ = 0;
+
+ queue_[0].opcode = opcode;
+ queue_[0].sequence = sequence;
+
+ queue_[0].data1 = data1;
+ queue_[0].data2 = data2;
+ queue_[0].data3 = data3;
+
+ length_ = 1;
+
+ return;
+ }
+
+ if (length_ == size_)
+ {
+ size_ += GROWTH_INCREMENT;
+
+ RequestSequence *newQueue = new RequestSequence[size_];
+
+ for (int i = start_; (unsigned int) i < length_; i++)
+ {
+ newQueue[i - start_] = queue_[i];
+ }
+
+ for (int i1 = 0; (unsigned int) i1 < start_; i1++)
+ {
+ newQueue[i1 + length_ - start_] = queue_[i1];
+ }
+
+ delete [] queue_;
+
+ queue_ = newQueue;
+
+ start_ = 0;
+
+ end_ = length_ - 1;
+ }
+
+ end_++;
+
+ if (end_ == size_)
+ {
+ end_ = 0;
+ }
+
+ queue_[end_].opcode = opcode;
+ queue_[end_].sequence = sequence;
+
+ queue_[end_].data1 = data1;
+ queue_[end_].data2 = data2;
+ queue_[end_].data3 = data3;
+
+ length_++;
+}
+
+int SequenceQueue::peek(unsigned short int &sequence,
+ unsigned char &opcode)
+{
+ if (length_ == 0)
+ {
+ return 0;
+ }
+ else
+ {
+ opcode = queue_[start_].opcode;
+ sequence = queue_[start_].sequence;
+
+ return 1;
+ }
+}
+
+int SequenceQueue::peek(unsigned short int &sequence, unsigned char &opcode,
+ unsigned int &data1, unsigned int &data2,
+ unsigned int &data3)
+{
+ if (length_ == 0)
+ {
+ return 0;
+ }
+ else
+ {
+ opcode = queue_[start_].opcode;
+ sequence = queue_[start_].sequence;
+
+ data1 = queue_[start_].data1;
+ data2 = queue_[start_].data2;
+ data3 = queue_[start_].data3;
+
+ return 1;
+ }
+}
+
+int SequenceQueue::pop(unsigned short int &sequence, unsigned char &opcode,
+ unsigned int &data1, unsigned int &data2,
+ unsigned int &data3)
+{
+ if (length_ == 0)
+ {
+ return 0;
+ }
+ else
+ {
+ opcode = queue_[start_].opcode;
+ sequence = queue_[start_].sequence;
+
+ data1 = queue_[start_].data1;
+ data2 = queue_[start_].data2;
+ data3 = queue_[start_].data3;
+
+ start_++;
+
+ if (start_ == size_)
+ {
+ start_ = 0;
+ }
+
+ length_--;
+
+ return 1;
+ }
+}
diff --git a/nxcomp/SequenceQueue.h b/nxcomp/SequenceQueue.h
new file mode 100644
index 000000000..b243c4c9f
--- /dev/null
+++ b/nxcomp/SequenceQueue.h
@@ -0,0 +1,83 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef SequenceQueue_H
+#define SequenceQueue_H
+
+//
+// List of outstanding request messages which
+// are waiting for a reply. This class is used
+// in X client and server channels to correlate
+// the replies sequence numbers to the original
+// request type.
+//
+
+class SequenceQueue
+{
+ public:
+
+ SequenceQueue();
+
+ virtual ~SequenceQueue();
+
+ void push(unsigned short int sequence, unsigned char opcode,
+ unsigned int data1 = 0, unsigned int data2 = 0,
+ unsigned int data3 = 0);
+
+ int peek(unsigned short int &sequence, unsigned char &opcode);
+
+ int peek(unsigned short int &sequence, unsigned char &opcode,
+ unsigned int &data1, unsigned int &data2,
+ unsigned int &data3);
+
+ int pop(unsigned short int &sequence, unsigned char &opcode,
+ unsigned int &data1, unsigned int &data2,
+ unsigned int &data3);
+
+ int pop(unsigned short int &sequence, unsigned char &opcode)
+ {
+ unsigned int data1, data2, data3;
+
+ return pop(sequence, opcode, data1, data2, data3);
+ }
+
+ int length()
+ {
+ return length_;
+ }
+
+ private:
+
+ struct RequestSequence
+ {
+ unsigned short int sequence;
+ unsigned char opcode;
+ unsigned int data1;
+ unsigned int data2;
+ unsigned int data3;
+ };
+
+ RequestSequence *queue_;
+
+ unsigned int size_;
+ unsigned int length_;
+
+ unsigned int start_;
+ unsigned int end_;
+};
+
+#endif /* SequenceQueue_H */
diff --git a/nxcomp/ServerCache.cpp b/nxcomp/ServerCache.cpp
new file mode 100644
index 000000000..f0cc6f825
--- /dev/null
+++ b/nxcomp/ServerCache.cpp
@@ -0,0 +1,186 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "ServerCache.h"
+
+//
+// Some global caches used to store information
+// common to all X connections.
+//
+
+BlockCache ServerCache::lastInitReply;
+BlockCache ServerCache::lastKeymap;
+unsigned char ServerCache::getKeyboardMappingLastKeysymsPerKeycode = 0;
+BlockCache ServerCache::getKeyboardMappingLastMap;
+BlockCache ServerCache::getModifierMappingLastMap;
+BlockCache ServerCache::xResources;
+BlockCacheSet ServerCache::queryFontFontCache(16);
+
+ServerCache::ServerCache() :
+
+ replySequenceCache(6), eventSequenceCache(6),
+ lastTimestamp(0), visualCache(8), colormapCache(8),
+
+ errorMinorCache(8),
+
+ colormapNotifyWindowCache(8), colormapNotifyColormapCache(8),
+
+ createNotifyWindowCache(8), createNotifyLastWindow(0),
+
+ exposeWindowCache(12),
+
+ focusInWindowCache(8),
+
+ keyPressLastKey(0),
+
+ mapNotifyEventCache(8), mapNotifyWindowCache(8),
+
+ motionNotifyTimestampCache(8), motionNotifyLastRootX(0),
+ motionNotifyLastRootY(0), motionNotifyRootXCache(8),
+ motionNotifyRootYCache(8), motionNotifyEventXCache(8),
+ motionNotifyEventYCache(8), motionNotifyStateCache(8),
+
+ noExposeDrawableCache(8), noExposeMinorCache(8),
+
+ propertyNotifyWindowCache(8), propertyNotifyAtomCache(8),
+
+ reparentNotifyWindowCache(8),
+
+ selectionClearWindowCache(8), selectionClearAtomCache(8),
+
+ visibilityNotifyWindowCache(8),
+
+ getGeometryRootCache(8),
+
+ getInputFocusWindowCache(8),
+
+ getKeyboardMappingKeysymCache(8),
+
+ getPropertyTypeCache(8),
+ getPropertyTextCompressor(textCache, SERVER_TEXT_CACHE_SIZE),
+
+ getSelectionOwnerCache(8),
+
+ getWindowAttributesClassCache(8), getWindowAttributesPlanesCache(8),
+ getWindowAttributesPixelCache(8), getWindowAttributesAllEventsCache(8),
+ getWindowAttributesYourEventsCache(8),
+ getWindowAttributesDontPropagateCache(8),
+
+ queryPointerRootCache(8), queryPointerChildCache(8),
+
+ translateCoordsChildCache(8), translateCoordsXCache(8),
+ translateCoordsYCache(8),
+
+ queryTreeWindowCache(8),
+
+ getAtomNameTextCompressor(textCache, SERVER_TEXT_CACHE_SIZE)
+
+{
+ unsigned int i;
+
+ for (i = 0; i < 3; i++)
+ {
+ configureNotifyWindowCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 5; i++)
+ {
+ configureNotifyGeomCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 5; i++)
+ {
+ exposeGeomCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+ motionNotifyWindowCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 5; i++)
+ {
+ getGeometryGeomCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 23; i++)
+ {
+ keyPressCache[i] = 0;
+ }
+
+ for (i = 0; i < 6; i++)
+ {
+ queryFontCharInfoCache[i] = new IntCache(8);
+ queryFontLastCharInfo[i] = 0;
+ }
+
+ for (i = 0; i < 12; i++)
+ {
+ genericReplyIntCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 14; i++)
+ {
+ genericEventIntCache[i] = new IntCache(8);
+ }
+}
+
+
+ServerCache::~ServerCache()
+{
+ unsigned int i;
+
+ for (i = 0; i < 3; i++)
+ {
+ delete configureNotifyWindowCache[i];
+ }
+
+ for (i = 0; i < 5; i++)
+ {
+ delete configureNotifyGeomCache[i];
+ }
+
+ for (i = 0; i < 5; i++)
+ {
+ delete exposeGeomCache[i];
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+ delete motionNotifyWindowCache[i];
+ }
+
+ for (i = 0; i < 5; i++)
+ {
+ delete getGeometryGeomCache[i];
+ }
+
+ for (i = 0; i < 6; i++)
+ {
+ delete queryFontCharInfoCache[i];
+ }
+
+ for (i = 0; i < 12; i++)
+ {
+ delete genericReplyIntCache[i];
+ }
+
+ for (i = 0; i < 14; i++)
+ {
+ delete genericEventIntCache[i];
+ }
+}
diff --git a/nxcomp/ServerCache.h b/nxcomp/ServerCache.h
new file mode 100644
index 000000000..ec213b89f
--- /dev/null
+++ b/nxcomp/ServerCache.h
@@ -0,0 +1,305 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef ServerCache_H
+#define ServerCache_H
+
+#include "Misc.h"
+
+#include "IntCache.h"
+#include "CharCache.h"
+#include "OpcodeCache.h"
+#include "TextCompressor.h"
+#include "BlockCache.h"
+#include "BlockCacheSet.h"
+
+#include "ChannelCache.h"
+
+class ServerCache : public ChannelCache
+{
+ public:
+
+ ServerCache();
+
+ ~ServerCache();
+
+ //
+ // Opcode prediction caches.
+ //
+
+ OpcodeCache opcodeCache;
+
+ //
+ // General-purpose caches.
+ //
+
+ CharCache textCache[SERVER_TEXT_CACHE_SIZE];
+ IntCache replySequenceCache;
+ IntCache eventSequenceCache;
+ unsigned int lastTimestamp;
+ CharCache depthCache;
+ IntCache visualCache;
+ IntCache colormapCache;
+ CharCache resourceCache;
+
+ //
+ // X connection startup.
+ //
+
+ static BlockCache lastInitReply;
+
+ //
+ // X errors.
+ //
+
+ CharCache errorCodeCache;
+ IntCache errorMinorCache;
+ CharCache errorMajorCache;
+
+ //
+ // ButtonPress and ButtonRelease events.
+ //
+
+ CharCache buttonCache;
+
+ //
+ // ColormapNotify event.
+ //
+
+ IntCache colormapNotifyWindowCache;
+ IntCache colormapNotifyColormapCache;
+
+ //
+ // ConfigureNotify event.
+ //
+
+ IntCache *configureNotifyWindowCache[3];
+ IntCache *configureNotifyGeomCache[5];
+
+ //
+ // CreateNotify event.
+ //
+
+ IntCache createNotifyWindowCache;
+ unsigned int createNotifyLastWindow;
+
+ //
+ // Expose event.
+ //
+
+ IntCache exposeWindowCache;
+ IntCache *exposeGeomCache[5];
+
+ //
+ // FocusIn event (also used for FocusOut).
+ //
+
+ IntCache focusInWindowCache;
+
+ //
+ // KeymapNotify event.
+ //
+
+ static BlockCache lastKeymap;
+
+ //
+ // KeyPress event.
+ //
+
+ unsigned char keyPressLastKey;
+ unsigned char keyPressCache[23];
+
+ //
+ // MapNotify event (also used for UnmapNotify).
+ //
+
+ IntCache mapNotifyEventCache;
+ IntCache mapNotifyWindowCache;
+
+ //
+ // MotionNotify event (also used for KeyPress,
+ // KeyRelease, ButtonPress, ButtonRelease,
+ // EnterNotify, and LeaveNotify events and
+ // QueryPointer reply).
+ //
+
+ IntCache motionNotifyTimestampCache;
+ unsigned int motionNotifyLastRootX;
+ unsigned int motionNotifyLastRootY;
+ IntCache motionNotifyRootXCache;
+ IntCache motionNotifyRootYCache;
+ IntCache motionNotifyEventXCache;
+ IntCache motionNotifyEventYCache;
+ IntCache motionNotifyStateCache;
+ IntCache *motionNotifyWindowCache[3];
+
+ //
+ // NoExpose event.
+ //
+
+ IntCache noExposeDrawableCache;
+ IntCache noExposeMinorCache;
+ CharCache noExposeMajorCache;
+
+ //
+ // PropertyNotify event.
+ //
+
+ IntCache propertyNotifyWindowCache;
+ IntCache propertyNotifyAtomCache;
+
+ //
+ // ReparentNotify event.
+ //
+
+ IntCache reparentNotifyWindowCache;
+
+ //
+ // SelectionClear event.
+ //
+
+ IntCache selectionClearWindowCache;
+ IntCache selectionClearAtomCache;
+
+ //
+ // VisibilityNotify event.
+ //
+
+ IntCache visibilityNotifyWindowCache;
+
+ //
+ // GetGeometry reply.
+ //
+
+ IntCache getGeometryRootCache;
+ IntCache *getGeometryGeomCache[5];
+
+ //
+ // GetInputFocus reply.
+ //
+
+ IntCache getInputFocusWindowCache;
+
+ //
+ // GetKeyboardMapping reply.
+ //
+
+ static unsigned char getKeyboardMappingLastKeysymsPerKeycode;
+ static BlockCache getKeyboardMappingLastMap;
+ IntCache getKeyboardMappingKeysymCache;
+ CharCache getKeyboardMappingLastByteCache;
+
+ //
+ // GetModifierMapping reply.
+ //
+
+ static BlockCache getModifierMappingLastMap;
+
+ //
+ // GetProperty reply.
+ //
+
+ CharCache getPropertyFormatCache;
+ IntCache getPropertyTypeCache;
+ TextCompressor getPropertyTextCompressor;
+ static BlockCache xResources;
+
+ //
+ // GetSelection reply.
+ //
+
+ IntCache getSelectionOwnerCache;
+
+ //
+ // GetWindowAttributes reply.
+ //
+
+ IntCache getWindowAttributesClassCache;
+ CharCache getWindowAttributesBitGravityCache;
+ CharCache getWindowAttributesWinGravityCache;
+ IntCache getWindowAttributesPlanesCache;
+ IntCache getWindowAttributesPixelCache;
+ IntCache getWindowAttributesAllEventsCache;
+ IntCache getWindowAttributesYourEventsCache;
+ IntCache getWindowAttributesDontPropagateCache;
+
+ //
+ // QueryColors reply.
+ //
+
+ BlockCache queryColorsLastReply;
+
+ //
+ // QueryFont reply.
+ //
+
+ static BlockCacheSet queryFontFontCache;
+ IntCache *queryFontCharInfoCache[6];
+ unsigned int queryFontLastCharInfo[6];
+
+ //
+ // QueryPointer reply.
+ //
+
+ IntCache queryPointerRootCache;
+ IntCache queryPointerChildCache;
+
+ //
+ // TranslateCoords reply.
+ //
+
+ IntCache translateCoordsChildCache;
+ IntCache translateCoordsXCache;
+ IntCache translateCoordsYCache;
+
+ //
+ // QueryTree reply.
+ //
+
+ IntCache queryTreeWindowCache;
+
+ //
+ // GetAtomName reply in protocol
+ // versions >= 3.
+ //
+
+ TextCompressor getAtomNameTextCompressor;
+
+ //
+ // Generic reply. Use short data
+ // in protocol versions >= 3.
+ //
+
+ CharCache genericReplyCharCache;
+ IntCache *genericReplyIntCache[12];
+
+ //
+ // Generic event. Only in protocol
+ // versions >= 3.
+ //
+
+ CharCache genericEventCharCache;
+ IntCache *genericEventIntCache[14];
+
+ //
+ // Used in the abort split events.
+ //
+
+ OpcodeCache abortOpcodeCache;
+};
+
+#endif /* ServerCache_H */
diff --git a/nxcomp/ServerChannel.cpp b/nxcomp/ServerChannel.cpp
new file mode 100644
index 000000000..4e6dea324
--- /dev/null
+++ b/nxcomp/ServerChannel.cpp
@@ -0,0 +1,8258 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+#include <X11/X.h>
+#include <X11/Xatom.h>
+
+#include "NXproto.h"
+#include "NXalert.h"
+#include "NXpack.h"
+#include "NXmitshm.h"
+
+#include "ServerChannel.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "StaticCompressor.h"
+
+#include "Statistics.h"
+#include "Proxy.h"
+
+#include "Auth.h"
+#include "Unpack.h"
+
+//
+// Available unpack methods.
+//
+
+#include "Alpha.h"
+#include "Colormap.h"
+#include "Bitmap.h"
+#include "Jpeg.h"
+#include "Pgn.h"
+#include "Rgb.h"
+#include "Rle.h"
+
+extern Proxy *proxy;
+
+//
+// Set the verbosity level. You also
+// need to define OPCODES in Misc.cpp
+// if you want literals instead of
+// opcodes' numbers.
+//
+
+#define PANIC
+#define WARNING
+#undef OPCODES
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Log the important tracepoints related
+// to writing packets to the peer proxy.
+//
+
+#undef FLUSH
+
+//
+// Log the operations related to splits.
+//
+
+#undef SPLIT
+
+//
+// Define this to log when a channel
+// is created or destroyed.
+//
+
+#undef REFERENCES
+
+//
+// Define this to exit and suspend the
+// session after a given number of X
+// messages decoded by the proxy.
+//
+
+#undef SUSPEND
+
+//
+// Define these to hide the server extensions.
+//
+
+#define HIDE_MIT_SHM_EXTENSION
+#define HIDE_BIG_REQUESTS_EXTENSION
+#define HIDE_XFree86_Bigfont_EXTENSION
+#undef HIDE_SHAPE_EXTENSION
+#undef HIDE_XKEYBOARD_EXTENSION
+
+//
+// Known reasons of connection failures.
+//
+
+#define INVALID_COOKIE_DATA "Invalid MIT-MAGIC-COOKIE-1 key"
+#define INVALID_COOKIE_SIZE ((int) sizeof(INVALID_COOKIE_DATA) - 1)
+
+#define NO_AUTH_PROTO_DATA "No protocol specified"
+#define NO_AUTH_PROTO_SIZE ((int) sizeof(NO_AUTH_PROTO_DATA) - 1)
+
+//
+// Here are the static members.
+//
+
+#ifdef REFERENCES
+
+int ServerChannel::references_ = 0;
+
+#endif
+
+ServerChannel::ServerChannel(Transport *transport, StaticCompressor *compressor)
+
+ : Channel(transport, compressor), readBuffer_(transport_, this)
+{
+ //
+ // Sequence number of the next message
+ // being encoded or decoded.
+ //
+
+ clientSequence_ = 0;
+ serverSequence_ = 0;
+
+ //
+ // Save the last motion event and flush
+ // it only when the timeout expires.
+ //
+
+ lastMotion_[0] = '\0';
+
+ //
+ // Clear the queue of sequence numbers
+ // of split commits. Used to mask the
+ // errors.
+ //
+
+ initCommitQueue();
+
+ //
+ // Do we enable or not sending of expose
+ // events to the X client.
+ //
+
+ enableExpose_ = 1;
+ enableGraphicsExpose_ = 1;
+ enableNoExpose_ = 1;
+
+ //
+ // Track data of image currently being
+ // decompressed.
+ //
+
+ imageState_ = NULL;
+
+ //
+ // Track MIT-SHM resources.
+ //
+
+ shmemState_ = NULL;
+
+ //
+ // Store the unpack state for each agent
+ // resource.
+ //
+
+ for (int i = 0; i < CONNECTIONS_LIMIT; i++)
+ {
+ unpackState_[i] = NULL;
+ }
+
+ //
+ // Data about the split parameters requested
+ // by the encoding side.
+ //
+
+ splitState_.resource = nothing;
+ splitState_.current = 0;
+ splitState_.save = 1;
+ splitState_.load = 1;
+ splitState_.commit = 0;
+
+ handleSplitEnable();
+
+ //
+ // It will be eventually set by
+ // the server proxy.
+ //
+
+ fontPort_ = -1;
+
+ #ifdef REFERENCES
+ *logofs << "ServerChannel: Created new object at "
+ << this << " for FD#" << fd_ << " out of "
+ << ++references_ << " allocated channels.\n"
+ << logofs_flush;
+ #endif
+}
+
+ServerChannel::~ServerChannel()
+{
+ #ifdef TEST
+ *logofs << "ServerChannel: Freeing image state information.\n"
+ << logofs_flush;
+ #endif
+
+ handleImageStateRemove();
+
+ #ifdef TEST
+ *logofs << "ServerChannel: Freeing shared memory information.\n"
+ << logofs_flush;
+ #endif
+
+ handleShmemStateRemove();
+
+ #ifdef TEST
+ *logofs << "ServerChannel: Freeing unpack state information.\n"
+ << logofs_flush;
+ #endif
+
+ for (int i = 0; i < CONNECTIONS_LIMIT; i++)
+ {
+ handleUnpackStateRemove(i);
+ }
+
+ #ifdef TEST
+ *logofs << "ServerChannel: Freeing channel caches.\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef REFERENCES
+ *logofs << "ServerChannel: Deleted object at "
+ << this << " for FD#" << fd_ << " out of "
+ << --references_ << " allocated channels.\n"
+ << logofs_flush;
+ #endif
+}
+
+//
+// Beginning of handleRead().
+//
+
+int ServerChannel::handleRead(EncodeBuffer &encodeBuffer, const unsigned char *message,
+ unsigned int length)
+{
+ #ifdef DEBUG
+ *logofs << "handleRead: Called for FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Pointer to located message and
+ // its size in bytes.
+ //
+
+ const unsigned char *inputMessage;
+ unsigned int inputLength;
+
+ //
+ // Set when message is found in
+ // cache.
+ //
+
+ int hit;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleRead: Trying to read from FD#"
+ << fd_ << " at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ int result = readBuffer_.readMessage();
+
+ #if defined(DEBUG) || defined(INFO)
+ *logofs << "handleRead: Read result on FD#" << fd_
+ << " is " << result << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (result < 0)
+ {
+ //
+ // Let the proxy close the channel.
+ //
+
+ return -1;
+ }
+ else if (result == 0)
+ {
+ #if defined(TEST) || defined(INFO)
+
+ //
+ // This can happen because we have the descriptor
+ // selected in the read set but we already read
+ // the data asynchronously, while decoding data
+ // read from the proxy.
+ //
+
+ *logofs << "handleRead: WARNING! No data read from FD#"
+ << fd_ << " while encoding messages.\n"
+ << logofs_flush;
+
+ #endif
+
+ return 0;
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+ *logofs << "handleRead: Encoding messages for FD#" << fd_
+ << " with " << readBuffer_.getLength() << " bytes "
+ << "in the buffer.\n" << logofs_flush;
+ #endif
+
+ //
+ // Extract any complete message which
+ // is available in the buffer.
+ //
+
+ if (proxy -> handleAsyncSwitch(fd_) < 0)
+ {
+ return -1;
+ }
+
+ while ((inputMessage = readBuffer_.getMessage(inputLength)) != NULL)
+ {
+ hit = 0;
+
+ if (firstReply_)
+ {
+ //
+ // Handle the X server's authorization reply.
+ //
+
+ if (handleAuthorization(inputMessage, inputLength) < 0)
+ {
+ return -1;
+ }
+
+ imageByteOrder_ = inputMessage[30];
+ bitmapBitOrder_ = inputMessage[31];
+ scanlineUnit_ = inputMessage[32];
+ scanlinePad_ = inputMessage[33];
+
+ encodeBuffer.encodeValue((unsigned int) inputMessage[0], 8);
+ encodeBuffer.encodeValue((unsigned int) inputMessage[1], 8);
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 2, bigEndian_), 16);
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 4, bigEndian_), 16);
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 6, bigEndian_), 16);
+
+ if (ServerCache::lastInitReply.compare(inputLength - 8, inputMessage + 8))
+ {
+ encodeBuffer.encodeBoolValue(1);
+ }
+ else
+ {
+ encodeBuffer.encodeBoolValue(0);
+
+ for (unsigned int i = 8; i < inputLength; i++)
+ {
+ encodeBuffer.encodeValue((unsigned int) inputMessage[i], 8);
+ }
+ }
+
+ firstReply_ = 0;
+
+ #if defined(TEST) || defined(OPCODES)
+
+ int bits = encodeBuffer.diffBits();
+
+ *logofs << "handleRead: Handled first reply. " << inputLength
+ << " bytes in, " << bits << " bits (" << ((float) bits) / 8
+ << " bytes) out.\n" << logofs_flush;
+
+ #endif
+
+ priority_++;
+
+ //
+ // Due to the way the loop was implemented
+ // we can't encode multiple messages if we
+ // are encoding the first request.
+ //
+
+ if (control -> isProtoStep7() == 0)
+ {
+ if (proxy -> handleAsyncInit() < 0)
+ {
+ return -1;
+ }
+ }
+ }
+ else
+ {
+ //
+ // NX client needs this line to consider
+ // the initialization phase successfully
+ // completed.
+ //
+
+ if (firstClient_ == -1)
+ {
+ cerr << "Info" << ": Established X server connection.\n" ;
+
+ firstClient_ = fd_;
+ }
+
+ //
+ // Check if this is a reply.
+ //
+
+ if (*inputMessage == X_Reply)
+ {
+ int bits = 0;
+
+ unsigned char inputOpcode = *inputMessage;
+
+ unsigned short int requestSequenceNum;
+ unsigned char requestOpcode;
+ unsigned int requestData[3];
+
+ unsigned int sequenceNum = GetUINT(inputMessage + 2, bigEndian_);
+
+ #ifdef SUSPEND
+
+ if (sequenceNum >= 1000)
+ {
+ cerr << "Warning" << ": Exiting to test the resilience of the agent.\n";
+
+ sleep(2);
+
+ HandleAbort();
+ }
+
+ #endif
+
+ //
+ // We managed all the events and errors caused
+ // by the previous requests. We can now reset
+ // the queue of split commits.
+ //
+
+ clearCommitQueue();
+
+ //
+ // Encode opcode and difference between
+ // current sequence and the last one.
+ //
+
+ encodeBuffer.encodeOpcodeValue(inputOpcode, serverCache_ -> opcodeCache);
+
+ unsigned int sequenceDiff = sequenceNum - serverSequence_;
+
+ serverSequence_ = sequenceNum;
+
+ #ifdef DEBUG
+ *logofs << "handleRead: Last server sequence number for FD#"
+ << fd_ << " is " << serverSequence_ << " with "
+ << "difference " << sequenceDiff << ".\n"
+ << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(sequenceDiff, 16,
+ serverCache_ -> replySequenceCache, 7);
+
+ //
+ // Now handle the data part.
+ //
+
+ if (sequenceQueue_.peek(requestSequenceNum, requestOpcode) &&
+ requestSequenceNum == sequenceNum)
+ {
+ //
+ // We've found the request that generated this reply.
+ // It is possible to compress the reply based on the
+ // specific request type.
+ //
+
+ sequenceQueue_.pop(requestSequenceNum, requestOpcode,
+ requestData[0], requestData[1], requestData[2]);
+
+ //
+ // If differential compression is disabled
+ // then use the most simple encoding.
+ //
+
+ if (control -> LocalDeltaCompression == 0)
+ {
+ int result = handleFastReadReply(encodeBuffer, requestOpcode,
+ inputMessage, inputLength);
+ if (result < 0)
+ {
+ return -1;
+ }
+ else if (result > 0)
+ {
+ continue;
+ }
+ }
+
+ switch (requestOpcode)
+ {
+ case X_AllocColor:
+ {
+ const unsigned char *nextSrc = inputMessage + 8;
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ unsigned int colorValue = GetUINT(nextSrc, bigEndian_);
+ nextSrc += 2;
+ if (colorValue == requestData[i])
+ encodeBuffer.encodeBoolValue(1);
+ else
+ {
+ encodeBuffer.encodeBoolValue(0);
+ encodeBuffer.encodeValue(colorValue - colorValue, 16, 6);
+ }
+ }
+ unsigned int pixel = GetULONG(inputMessage + 16, bigEndian_);
+ encodeBuffer.encodeValue(pixel, 32, 9);
+
+ priority_++;
+ }
+ break;
+ case X_GetAtomName:
+ {
+ unsigned int nameLength = GetUINT(inputMessage + 8, bigEndian_);
+ encodeBuffer.encodeValue(nameLength, 16, 6);
+ const unsigned char *nextSrc = inputMessage + 32;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ encodeBuffer.encodeTextData(nextSrc, nameLength);
+ }
+ else
+ {
+ serverCache_ -> getAtomNameTextCompressor.reset();
+ for (unsigned int i = 0; i < nameLength; i++)
+ {
+ serverCache_ -> getAtomNameTextCompressor.
+ encodeChar(*nextSrc++, encodeBuffer);
+ }
+ }
+
+ priority_++;
+ }
+ break;
+ case X_GetGeometry:
+ {
+ //
+ // TODO: This obtains a satisfactory 10:1, but
+ // could be cached to leverage the big amount
+ // of such requests issued by QT clients.
+ //
+
+ encodeBuffer.encodeCachedValue(inputMessage[1], 8,
+ serverCache_ -> depthCache);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_),
+ 29, serverCache_ -> getGeometryRootCache, 9);
+ const unsigned char *nextSrc = inputMessage + 12;
+ for (unsigned int i = 0; i < 5; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16,
+ *serverCache_ -> getGeometryGeomCache[i], 8);
+ nextSrc += 2;
+ }
+
+ priority_++;
+ }
+ break;
+ case X_GetInputFocus:
+ {
+ //
+ // Is it a real X_GetInputFocus or a
+ // masqueraded reply?
+ //
+
+ if (requestData[0] == X_GetInputFocus)
+ {
+ encodeBuffer.encodeValue((unsigned int) inputMessage[1], 2);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_),
+ 29, serverCache_ -> getInputFocusWindowCache);
+
+ priority_++;
+ }
+ else
+ {
+ //
+ // TODO: We are not setting priority in case
+ // of replies other than real X_GetInputFocus
+ // or X_NXGetUnpackParameters. We should check
+ // once again that this is OK.
+ //
+
+ #ifdef TEST
+ *logofs << "handleRead: Received tainted X_GetInputFocus reply "
+ << "for request OPCODE#" << requestData[0] << " with "
+ << "sequence " << sequenceNum << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Don't encode any data in case of sync
+ // messages or any other reply for which
+ // opcode is enough.
+ //
+
+ if (requestData[0] == opcodeStore_ -> getUnpackParameters)
+ {
+ for (int i = 0; i < PACK_METHOD_LIMIT; i++)
+ {
+ encodeBuffer.encodeBoolValue(control -> LocalUnpackMethods[i]);
+ }
+
+ priority_++;
+ }
+ else if (requestData[0] == opcodeStore_ -> getShmemParameters)
+ {
+ if (handleShmemReply(encodeBuffer, requestOpcode, requestData[1],
+ inputMessage, inputLength) < 0)
+ {
+ return -1;
+ }
+
+ priority_++;
+ }
+ else if (requestData[0] == opcodeStore_ -> getFontParameters)
+ {
+ if (handleFontReply(encodeBuffer, requestOpcode,
+ inputMessage, inputLength) < 0)
+ {
+ return -1;
+ }
+ }
+
+ //
+ // Account this data to the original opcode.
+ //
+
+ requestOpcode = requestData[0];
+ }
+ }
+ break;
+ case X_GetKeyboardMapping:
+ {
+ unsigned int keysymsPerKeycode = (unsigned int) inputMessage[1];
+ if (ServerCache::getKeyboardMappingLastMap.compare(inputLength - 32,
+ inputMessage + 32) && (keysymsPerKeycode ==
+ ServerCache::getKeyboardMappingLastKeysymsPerKeycode))
+ {
+ encodeBuffer.encodeBoolValue(1);
+
+ priority_++;
+
+ break;
+ }
+ ServerCache::getKeyboardMappingLastKeysymsPerKeycode = keysymsPerKeycode;
+ encodeBuffer.encodeBoolValue(0);
+ unsigned int numKeycodes =
+ (((inputLength - 32) / keysymsPerKeycode) >> 2);
+ encodeBuffer.encodeValue(numKeycodes, 8);
+ encodeBuffer.encodeValue(keysymsPerKeycode, 8, 4);
+ const unsigned char *nextSrc = inputMessage + 32;
+ unsigned char previous = 0;
+ for (unsigned int count = numKeycodes * keysymsPerKeycode;
+ count; --count)
+ {
+ unsigned int keysym = GetULONG(nextSrc, bigEndian_);
+ nextSrc += 4;
+ if (keysym == NoSymbol)
+ encodeBuffer.encodeBoolValue(1);
+ else
+ {
+ encodeBuffer.encodeBoolValue(0);
+ unsigned int first3Bytes = (keysym >> 8);
+ encodeBuffer.encodeCachedValue(first3Bytes, 24,
+ serverCache_ -> getKeyboardMappingKeysymCache, 9);
+ unsigned char lastByte = (unsigned char) (keysym & 0xff);
+ encodeBuffer.encodeCachedValue(lastByte - previous, 8,
+ serverCache_ -> getKeyboardMappingLastByteCache, 5);
+ previous = lastByte;
+ }
+ }
+
+ priority_++;
+ }
+ break;
+ case X_GetModifierMapping:
+ {
+ encodeBuffer.encodeValue((unsigned int) inputMessage[1], 8);
+ const unsigned char *nextDest = inputMessage + 32;
+ if (ServerCache::getModifierMappingLastMap.compare(inputLength - 32,
+ nextDest))
+ {
+ encodeBuffer.encodeBoolValue(1);
+
+ priority_++;
+
+ break;
+ }
+ encodeBuffer.encodeBoolValue(0);
+ for (unsigned int count = inputLength - 32; count; count--)
+ {
+ unsigned char next = *nextDest++;
+ if (next == 0)
+ encodeBuffer.encodeBoolValue(1);
+ else
+ {
+ encodeBuffer.encodeBoolValue(0);
+ encodeBuffer.encodeValue(next, 8);
+ }
+ }
+
+ priority_++;
+ }
+ break;
+ case X_GetProperty:
+ {
+ MessageStore *messageStore = serverStore_ ->
+ getReplyStore(X_GetProperty);
+
+ hit = handleEncode(encodeBuffer, serverCache_, messageStore,
+ requestOpcode, inputMessage, inputLength);
+
+ priority_++;
+ }
+ break;
+ case X_GetSelectionOwner:
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_),
+ 29, serverCache_ -> getSelectionOwnerCache, 9);
+ priority_++;
+ }
+ break;
+ case X_GetWindowAttributes:
+ {
+ encodeBuffer.encodeValue((unsigned int) inputMessage[1], 2);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_),
+ 29, serverCache_ -> visualCache);
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 12, bigEndian_),
+ 16, serverCache_ -> getWindowAttributesClassCache, 3);
+ encodeBuffer.encodeCachedValue(inputMessage[14], 8,
+ serverCache_ -> getWindowAttributesBitGravityCache);
+ encodeBuffer.encodeCachedValue(inputMessage[15], 8,
+ serverCache_ -> getWindowAttributesWinGravityCache);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 16, bigEndian_),
+ 32, serverCache_ -> getWindowAttributesPlanesCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 20, bigEndian_),
+ 32, serverCache_ -> getWindowAttributesPixelCache, 9);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[24]);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[25]);
+ encodeBuffer.encodeValue((unsigned int) inputMessage[26], 2);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[27]);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 28, bigEndian_),
+ 29, serverCache_ -> colormapCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 32, bigEndian_),
+ 32, serverCache_ -> getWindowAttributesAllEventsCache);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 36, bigEndian_),
+ 32, serverCache_ -> getWindowAttributesYourEventsCache);
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 40, bigEndian_),
+ 16, serverCache_ -> getWindowAttributesDontPropagateCache);
+
+ priority_++;
+ }
+ break;
+ case X_GrabKeyboard:
+ case X_GrabPointer:
+ {
+ encodeBuffer.encodeValue((unsigned int) inputMessage[1], 3);
+
+ priority_++;
+ }
+ break;
+ case X_InternAtom:
+ {
+ encodeBuffer.encodeValue(GetULONG(inputMessage + 8, bigEndian_), 29, 9);
+
+ priority_++;
+ }
+ break;
+ case X_ListExtensions:
+ {
+ encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 32, 8);
+ unsigned int numExtensions = (unsigned int) inputMessage[1];
+ encodeBuffer.encodeValue(numExtensions, 8);
+ const unsigned char *nextSrc = inputMessage + 32;
+
+ for (; numExtensions; numExtensions--)
+ {
+ unsigned int length = (unsigned int) (*nextSrc++);
+
+ encodeBuffer.encodeValue(length, 8);
+
+ #ifdef HIDE_MIT_SHM_EXTENSION
+
+ if (!strncmp((char *) nextSrc, "MIT-SHM", 7))
+ {
+ #ifdef TEST
+ *logofs << "handleRead: Hiding MIT-SHM extension in reply.\n"
+ << logofs_flush;
+ #endif
+
+ memcpy((unsigned char *) nextSrc, "NO-MIT-", 7);
+ }
+
+ #endif
+
+ #ifdef HIDE_BIG_REQUESTS_EXTENSION
+
+ if (!strncmp((char *) nextSrc, "BIG-REQUESTS", 12))
+ {
+ #ifdef TEST
+ *logofs << "handleRead: Hiding BIG-REQUESTS extension in reply.\n"
+ << logofs_flush;
+ #endif
+
+ memcpy((unsigned char *) nextSrc, "NO-BIG-REQUE", 12);
+ }
+
+ #endif
+
+ #ifdef HIDE_XKEYBOARD_EXTENSION
+
+ if (!strncmp((char *) nextSrc, "XKEYBOARD", 9))
+ {
+ #ifdef TEST
+ *logofs << "handleRead: Hiding XKEYBOARD extension in reply.\n"
+ << logofs_flush;
+ #endif
+
+ memcpy((unsigned char *) nextSrc, "NO-XKEYBO", 9);
+ }
+
+ #endif
+
+ #ifdef HIDE_XFree86_Bigfont_EXTENSION
+
+ if (!strncmp((char *) nextSrc, "XFree86-Bigfont", 15))
+ {
+ #ifdef TEST
+ *logofs << "handleRead: Hiding XFree86-Bigfont extension in reply.\n"
+ << logofs_flush;
+ #endif
+
+ memcpy((unsigned char *) nextSrc, "NO-XFree86-Bigf", 15);
+ }
+
+ #endif
+
+ #ifdef HIDE_SHAPE_EXTENSION
+
+ if (!strncmp((char *) nextSrc, "SHAPE", 5))
+ {
+ #ifdef TEST
+ *logofs << "handleRead: Hiding SHAPE extension in reply.\n"
+ << logofs_flush;
+ #endif
+
+ memcpy((unsigned char *) nextSrc, "NO-SH", 5);
+ }
+
+ #endif
+
+ //
+ // Check if user disabled RENDER extension.
+ //
+
+ if (control -> HideRender == 1 &&
+ !strncmp((char *) nextSrc, "RENDER", 6))
+ {
+ #ifdef TEST
+ *logofs << "handleRead: Hiding RENDER extension in reply.\n"
+ << logofs_flush;
+ #endif
+
+ memcpy((unsigned char *) nextSrc, "NO-REN", 6);
+ }
+
+ for (; length; length--)
+ {
+ encodeBuffer.encodeValue((unsigned int) (*nextSrc++), 8);
+ }
+ }
+
+ priority_++;
+ }
+ break;
+ case X_ListFonts:
+ {
+ MessageStore *messageStore = serverStore_ ->
+ getReplyStore(X_ListFonts);
+
+ if (handleEncodeCached(encodeBuffer, serverCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ priority_++;
+
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 32, 8);
+ unsigned int numFonts = GetUINT(inputMessage + 8, bigEndian_);
+ encodeBuffer.encodeValue(numFonts, 16, 6);
+
+ // Differential encoding.
+ encodeBuffer.encodeBoolValue(1);
+
+ const unsigned char* nextSrc = inputMessage + 32;
+ for (; numFonts; numFonts--)
+ {
+ unsigned int length = (unsigned int) (*nextSrc++);
+ encodeBuffer.encodeValue(length, 8);
+
+ if (control -> isProtoStep7() == 1)
+ {
+ encodeBuffer.encodeTextData(nextSrc, length);
+
+ nextSrc += length;
+ }
+ else
+ {
+ serverCache_ -> getPropertyTextCompressor.reset();
+ for (; length; length--)
+ {
+ serverCache_ -> getPropertyTextCompressor.encodeChar(
+ *nextSrc++, encodeBuffer);
+ }
+ }
+ }
+
+ priority_++;
+ }
+ break;
+ case X_LookupColor:
+ case X_AllocNamedColor:
+ {
+ const unsigned char *nextSrc = inputMessage + 8;
+ if (requestOpcode == X_AllocNamedColor)
+ {
+ encodeBuffer.encodeValue(GetULONG(nextSrc, bigEndian_), 32, 9);
+ nextSrc += 4;
+ }
+ unsigned int count = 3;
+ do
+ {
+ unsigned int exactColor = GetUINT(nextSrc, bigEndian_);
+ encodeBuffer.encodeValue(exactColor, 16, 9);
+ unsigned int visualColor = GetUINT(nextSrc + 6, bigEndian_) -
+ exactColor;
+ encodeBuffer.encodeValue(visualColor, 16, 5);
+ nextSrc += 2;
+ }
+ while (--count);
+
+ priority_++;
+ }
+ break;
+ case X_QueryBestSize:
+ {
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 8, bigEndian_), 16, 8);
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 10, bigEndian_), 16, 8);
+
+ priority_++;
+ }
+ break;
+ case X_QueryColors:
+ {
+ // Differential encoding.
+ encodeBuffer.encodeBoolValue(1);
+
+ unsigned int numColors = ((inputLength - 32) >> 3);
+ const unsigned char *nextSrc = inputMessage + 40;
+ unsigned char *nextDest = (unsigned char *) inputMessage + 38;
+ for (unsigned int c = 1; c < numColors; c++)
+ {
+ for (unsigned int i = 0; i < 6; i++)
+ *nextDest++ = *nextSrc++;
+ nextSrc += 2;
+ }
+ unsigned int colorsLength = numColors * 6;
+ if (serverCache_ -> queryColorsLastReply.compare(colorsLength,
+ inputMessage + 32))
+ encodeBuffer.encodeBoolValue(1);
+ else
+ {
+ const unsigned char *nextSrc = inputMessage + 32;
+ encodeBuffer.encodeBoolValue(0);
+ encodeBuffer.encodeValue(numColors, 16, 5);
+ for (numColors *= 3; numColors; numColors--)
+ {
+ encodeBuffer.encodeValue(GetUINT(nextSrc, bigEndian_), 16);
+ nextSrc += 2;
+ }
+ }
+
+ priority_++;
+ }
+ break;
+ case X_QueryExtension:
+ {
+ if (requestData[0] == X_QueryExtension)
+ {
+ //
+ // Value in requestData[0] will be nonzero
+ // if the request is for an extension that
+ // we should hide to the X client.
+ //
+
+ if (requestData[1])
+ {
+ encodeBuffer.encodeBoolValue(0);
+ encodeBuffer.encodeValue(0, 8);
+ }
+ else
+ {
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[8]);
+ encodeBuffer.encodeValue((unsigned int) inputMessage[9], 8);
+ }
+
+ encodeBuffer.encodeValue((unsigned int) inputMessage[10], 8);
+ encodeBuffer.encodeValue((unsigned int) inputMessage[11], 8);
+
+ if (requestData[2] == X_NXInternalShapeExtension)
+ {
+ opcodeStore_ -> shapeExtension = inputMessage[9];
+
+ #ifdef TEST
+ *logofs << "handleRead: Shape extension opcode for FD#" << fd_
+ << " is " << (unsigned int) opcodeStore_ -> shapeExtension
+ << ".\n" << logofs_flush;
+ #endif
+ }
+ else if (requestData[2] == X_NXInternalRenderExtension)
+ {
+ opcodeStore_ -> renderExtension = inputMessage[9];
+
+ #ifdef TEST
+ *logofs << "handleRead: Render extension opcode for FD#" << fd_
+ << " is " << (unsigned int) opcodeStore_ -> renderExtension
+ << ".\n" << logofs_flush;
+ #endif
+ }
+
+ priority_++;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "handleRead: Received tainted X_QueryExtension reply "
+ << "for request OPCODE#" << requestData[0] << " with "
+ << "sequence " << sequenceNum << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (requestData[0] == opcodeStore_ -> getShmemParameters)
+ {
+ if (handleShmemReply(encodeBuffer, requestOpcode, requestData[1],
+ inputMessage, inputLength) < 0)
+ {
+ return -1;
+ }
+
+ priority_++;
+ }
+
+ //
+ // Account this data to the original opcode.
+ //
+
+ requestOpcode = requestData[0];
+ }
+ }
+ break;
+ case X_QueryFont:
+ {
+ MessageStore *messageStore = serverStore_ ->
+ getReplyStore(X_QueryFont);
+
+ if (handleEncodeCached(encodeBuffer, serverCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ priority_++;
+
+ hit = 1;
+
+ break;
+ }
+
+ // Differential encoding.
+ encodeBuffer.encodeBoolValue(1);
+
+ unsigned int numProperties = GetUINT(inputMessage + 46, bigEndian_);
+ unsigned int numCharInfos = GetULONG(inputMessage + 56, bigEndian_);
+ encodeBuffer.encodeValue(numProperties, 16, 8);
+ encodeBuffer.encodeValue(numCharInfos, 32, 10);
+ handleEncodeCharInfo(inputMessage + 8, encodeBuffer);
+ handleEncodeCharInfo(inputMessage + 24, encodeBuffer);
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 40, bigEndian_), 16, 9);
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 42, bigEndian_), 16, 9);
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 44, bigEndian_), 16, 9);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[48]);
+ encodeBuffer.encodeValue((unsigned int) inputMessage[49], 8);
+ encodeBuffer.encodeValue((unsigned int) inputMessage[50], 8);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[51]);
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 52, bigEndian_), 16, 9);
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 54, bigEndian_), 16, 9);
+ const unsigned char *nextSrc = inputMessage + 60;
+ unsigned int index;
+
+ int end = 0;
+
+ if (ServerCache::queryFontFontCache.lookup(numProperties * 8 +
+ numCharInfos * 12, nextSrc, index))
+ {
+ encodeBuffer.encodeBoolValue(1);
+ encodeBuffer.encodeValue(index, 4);
+
+ end = 1;
+ }
+
+ if (end == 0)
+ {
+ encodeBuffer.encodeBoolValue(0);
+ for (; numProperties; numProperties--)
+ {
+ encodeBuffer.encodeValue(GetULONG(nextSrc, bigEndian_), 32, 9);
+ encodeBuffer.encodeValue(GetULONG(nextSrc + 4, bigEndian_), 32, 9);
+ nextSrc += 8;
+ }
+ for (; numCharInfos; numCharInfos--)
+ {
+ handleEncodeCharInfo(nextSrc, encodeBuffer);
+
+ nextSrc += 12;
+ }
+ }
+
+ priority_++;
+ }
+ break;
+ case X_QueryPointer:
+ {
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_),
+ 29, serverCache_ -> queryPointerRootCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_),
+ 29, serverCache_ -> queryPointerChildCache, 9);
+ unsigned int rootX = GetUINT(inputMessage + 16, bigEndian_);
+ unsigned int rootY = GetUINT(inputMessage + 18, bigEndian_);
+ unsigned int eventX = GetUINT(inputMessage + 20, bigEndian_);
+ unsigned int eventY = GetUINT(inputMessage + 22, bigEndian_);
+ eventX -= rootX;
+ eventY -= rootY;
+ encodeBuffer.encodeCachedValue(
+ rootX - serverCache_ -> motionNotifyLastRootX, 16,
+ serverCache_ -> motionNotifyRootXCache, 8);
+ serverCache_ -> motionNotifyLastRootX = rootX;
+ encodeBuffer.encodeCachedValue(
+ rootY - serverCache_ -> motionNotifyLastRootY, 16,
+ serverCache_ -> motionNotifyRootYCache, 8);
+ serverCache_ -> motionNotifyLastRootY = rootY;
+ encodeBuffer.encodeCachedValue(eventX, 16,
+ serverCache_ -> motionNotifyEventXCache, 8);
+ encodeBuffer.encodeCachedValue(eventY, 16,
+ serverCache_ -> motionNotifyEventYCache, 8);
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 24, bigEndian_),
+ 16, serverCache_ -> motionNotifyStateCache);
+ priority_++;
+ }
+ break;
+ case X_QueryTree:
+ {
+ //
+ // This was very inefficient. In practice
+ // it just copied data on the output. Now
+ // it obtains an average 7:1 compression
+ // and could optionally be cached.
+ //
+
+ unsigned int children = GetUINT(inputMessage + 16, bigEndian_);
+
+ encodeBuffer.encodeValue(children, 16, 8);
+
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), 29,
+ serverCache_ -> queryTreeWindowCache);
+
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), 29,
+ serverCache_ -> queryTreeWindowCache);
+
+ const unsigned char *next = inputMessage + 32;
+
+ for (unsigned int i = 0; i < children; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(next + (i * 4), bigEndian_), 29,
+ serverCache_ -> queryTreeWindowCache);
+ }
+
+ priority_++;
+ }
+ break;
+ case X_TranslateCoords:
+ {
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_),
+ 29, serverCache_ -> translateCoordsChildCache, 9);
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 12, bigEndian_),
+ 16, serverCache_ -> translateCoordsXCache, 8);
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 14, bigEndian_),
+ 16, serverCache_ -> translateCoordsYCache, 8);
+ priority_++;
+ }
+ break;
+ case X_GetImage:
+ {
+ MessageStore *messageStore = serverStore_ ->
+ getReplyStore(X_GetImage);
+
+ if (handleEncodeCached(encodeBuffer, serverCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ priority_++;
+
+ hit = 1;
+
+ break;
+ }
+
+ // Depth.
+ encodeBuffer.encodeCachedValue(inputMessage[1], 8,
+ serverCache_ -> depthCache);
+ // Reply length.
+ encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 32, 9);
+
+ // Visual.
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), 29,
+ serverCache_ -> visualCache);
+
+ if (control -> isProtoStep8() == 0)
+ {
+ unsigned int compressedDataSize = 0;
+ unsigned char *compressedData = NULL;
+
+ int compressed = handleCompress(encodeBuffer, requestOpcode, messageStore -> dataOffset,
+ inputMessage, inputLength, compressedData,
+ compressedDataSize);
+ if (compressed < 0)
+ {
+ return -1;
+ }
+ else if (compressed > 0)
+ {
+ //
+ // Update size according to result of image compression.
+ //
+
+ handleUpdate(messageStore, inputLength - messageStore ->
+ dataOffset, compressedDataSize);
+ }
+ }
+ else
+ {
+ handleCopy(encodeBuffer, requestOpcode, messageStore ->
+ dataOffset, inputMessage, inputLength);
+ }
+
+ priority_++;
+ }
+ break;
+ case X_GetPointerMapping:
+ {
+ encodeBuffer.encodeValue(inputMessage[1], 8, 4);
+ encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 32, 4);
+ for (unsigned int i = 32; i < inputLength; i++)
+ encodeBuffer.encodeValue((unsigned int) inputMessage[i], 8, 4);
+
+ priority_++;
+ }
+ break;
+ case X_GetKeyboardControl:
+ {
+ encodeBuffer.encodeValue(inputMessage[1], 8, 2);
+ encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 32, 8);
+ for (unsigned int i = 8; i < inputLength; i++)
+ encodeBuffer.encodeValue((unsigned int) inputMessage[i], 8, 4);
+
+ priority_++;
+ }
+ break;
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "ServerChannel: PANIC! No matching request with "
+ << "OPCODE#" << (unsigned int) requestOpcode
+ << " for reply with sequence number "
+ << requestSequenceNum << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": No matching request with OPCODE#"
+ << (unsigned int) requestOpcode << " for reply with "
+ << "sequence number " << requestSequenceNum << ".\n";
+
+ return -1;
+ }
+ }
+
+ bits = encodeBuffer.diffBits();
+
+ #if defined(TEST) || defined(OPCODES)
+
+ const char *cacheString = (hit ? "cached " : "");
+
+ *logofs << "handleRead: Handled " << cacheString << "reply to OPCODE#"
+ << (unsigned int) requestOpcode << " (" << DumpOpcode(requestOpcode)
+ << ") for FD#" << fd_ << " sequence " << serverSequence_
+ << ". " << inputLength << " bytes in, " << bits << " bits ("
+ << ((float) bits) / 8 << " bytes) out.\n" << logofs_flush;
+
+ #endif
+
+ } // End of if (sequenceQueue_.peek(requestSequenceNum, requestOpcode) && ...
+ else
+ {
+ //
+ // We didn't push the request opcode.
+ // Check if fast encoding is required.
+ //
+
+ requestOpcode = X_Reply;
+
+ if (control -> LocalDeltaCompression == 0)
+ {
+ int result = handleFastReadReply(encodeBuffer, requestOpcode,
+ inputMessage, inputLength);
+ if (result < 0)
+ {
+ return -1;
+ }
+ else if (result > 0)
+ {
+ continue;
+ }
+ }
+
+ //
+ // Group all replies whose opcode was not
+ // pushed in sequence number queue under
+ // the category 'generic reply'.
+ //
+
+ #ifdef DEBUG
+ *logofs << "handleRead: Identified generic reply.\n"
+ << logofs_flush;
+ #endif
+
+ MessageStore *messageStore = serverStore_ ->
+ getReplyStore(X_NXInternalGenericReply);
+
+ hit = handleEncode(encodeBuffer, serverCache_, messageStore,
+ requestOpcode, inputMessage, inputLength);
+
+ priority_++;
+
+ bits = encodeBuffer.diffBits();
+
+ #if defined(TEST) || defined(OPCODES)
+
+ const char *cacheString = (hit ? "cached " : "");
+
+ *logofs << "handleRead: Handled " << cacheString << "generic reply "
+ << "OPCODE#" << X_NXInternalGenericReply << " for FD#" << fd_
+ << " sequence " << serverSequence_ << ". " << inputLength
+ << " bytes in, " << bits << " bits (" << ((float) bits) / 8
+ << " bytes) out.\n" << logofs_flush;
+
+ #endif
+ }
+
+ if (hit)
+ {
+ statistics -> addCachedReply(requestOpcode);
+ }
+
+ statistics -> addReplyBits(requestOpcode, inputLength << 3, bits);
+
+ } // End of if (inputMessage[0] == 1) ...
+ else
+ {
+ //
+ // Event or error.
+ //
+
+ unsigned char inputOpcode = *inputMessage;
+
+ unsigned int inputSequence = GetUINT(inputMessage + 2, bigEndian_);
+
+ //
+ // Check if this is an event which we can discard.
+ //
+
+ if ((inputOpcode == Expose && enableExpose_ == 0) ||
+ (inputOpcode == GraphicsExpose && enableGraphicsExpose_ == 0) ||
+ (inputOpcode == NoExpose && enableNoExpose_ == 0))
+ {
+ continue;
+ }
+ else if (shmemState_ != NULL && shmemState_ -> enabled == 1 &&
+ inputOpcode == shmemState_ -> event &&
+ checkShmemEvent(inputOpcode, inputSequence,
+ inputMessage) > 0)
+ {
+ continue;
+ }
+ else if (inputOpcode == MotionNotify)
+ {
+ //
+ // Save the motion event and send when another
+ // event or error is received or the motion ti-
+ // meout is elapsed. If a previous motion event
+ // was already saved, we replace it with the
+ // new one and don't reset the timeout, so we
+ // still have a motion event every given ms.
+ //
+
+ memcpy(lastMotion_, inputMessage, 32);
+
+ #ifdef TEST
+ *logofs << "handleRead: Saved suppressed motion event for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ continue;
+ }
+ else if (inputOpcode == X_Error)
+ {
+ //
+ // Check if this is an error that matches a
+ // sequence number for which we are expecting
+ // a reply.
+ //
+
+ unsigned short int errorSequenceNum;
+ unsigned char errorOpcode;
+
+ if (sequenceQueue_.peek(errorSequenceNum, errorOpcode) &&
+ ((unsigned int) errorSequenceNum == inputSequence))
+ {
+ sequenceQueue_.pop(errorSequenceNum, errorOpcode);
+ }
+
+ //
+ // Check if error is due to an image commit
+ // generated at the end of a split.
+ //
+
+ if (checkCommitError(*(inputMessage + 1), inputSequence, inputMessage) > 0)
+ {
+ #ifdef TEST
+ *logofs << "handleRead: Skipping error on image commit for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ continue;
+ }
+
+ //
+ // Check if it's an error generated by a request
+ // concerning shared memory support.
+ //
+
+ else if (shmemState_ != NULL && (shmemState_ -> sequence ==
+ inputSequence || (shmemState_ -> enabled == 1 &&
+ (shmemState_ -> opcode == *(inputMessage + 10) ||
+ shmemState_ -> error == *(inputMessage + 1)))) &&
+ checkShmemError(*(inputMessage + 1), inputSequence,
+ inputMessage) > 0)
+ {
+ #ifdef TEST
+ *logofs << "handleRead: Skipping error on shmem operation for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ continue;
+ }
+ }
+
+ //
+ // Check if user pressed the CTRL+ALT+SHIFT+ESC key
+ // sequence because was unable to kill the session
+ // through the normal procedure.
+ //
+
+ if (inputOpcode == KeyPress)
+ {
+ if (checkKeyboardEvent(inputOpcode, inputSequence, inputMessage) == 1)
+ {
+ #ifdef TEST
+ *logofs << "handleRead: Removing the key sequence from the "
+ << "event stream for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ continue;
+ }
+ }
+
+ //
+ // We are going to handle an event or error
+ // that's not a mouse motion. Prepend any
+ // saved motion to it.
+ //
+
+ if (lastMotion_[0] != '\0')
+ {
+ if (handleMotion(encodeBuffer) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "handleRead: PANIC! Can't encode motion event for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't encode motion event for FD#"
+ << fd_ << ".\n";
+
+ return -1;
+ }
+ }
+
+ //
+ // Encode opcode and difference between
+ // current sequence and the last one.
+ //
+
+ encodeBuffer.encodeOpcodeValue(inputOpcode, serverCache_ -> opcodeCache);
+
+ unsigned int sequenceDiff = inputSequence - serverSequence_;
+
+ serverSequence_ = inputSequence;
+
+ #ifdef DEBUG
+ *logofs << "handleRead: Last server sequence number for FD#"
+ << fd_ << " is " << serverSequence_ << " with "
+ << "difference " << sequenceDiff << ".\n"
+ << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(sequenceDiff, 16,
+ serverCache_ -> eventSequenceCache, 7);
+
+ //
+ // If differential compression is disabled
+ // then use the most simple encoding.
+ //
+
+ if (control -> LocalDeltaCompression == 0)
+ {
+ int result = handleFastReadEvent(encodeBuffer, inputOpcode,
+ inputMessage, inputLength);
+ if (result < 0)
+ {
+ return -1;
+ }
+ else if (result > 0)
+ {
+ continue;
+ }
+ }
+
+ switch (inputOpcode)
+ {
+ case X_Error:
+ {
+ //
+ // Set the priority flag in the case of
+ // a X protocol error. This may restart
+ // the client if it was waiting for the
+ // reply.
+ //
+
+ priority_++;
+
+ unsigned char errorCode = *(inputMessage + 1);
+
+ encodeBuffer.encodeCachedValue(errorCode, 8,
+ serverCache_ -> errorCodeCache);
+
+ if (errorCode != 11 && errorCode != 8 &&
+ errorCode != 15 && errorCode != 1)
+ {
+ encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 32, 16);
+ }
+
+ if (errorCode >= 18)
+ {
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 8, bigEndian_), 16,
+ serverCache_ -> errorMinorCache);
+ }
+
+ encodeBuffer.encodeCachedValue(inputMessage[10], 8,
+ serverCache_ -> errorMajorCache);
+
+ if (errorCode >= 18)
+ {
+ const unsigned char *nextSrc = inputMessage + 11;
+ for (unsigned int i = 11; i < 32; i++)
+ encodeBuffer.encodeValue(*nextSrc++, 8);
+ }
+ }
+ break;
+ case ButtonPress:
+ case ButtonRelease:
+ case KeyPress:
+ case KeyRelease:
+ case MotionNotify:
+ case EnterNotify:
+ case LeaveNotify:
+ {
+ //
+ // Set the priority in the case this is
+ // an event that the remote side may
+ // care to receive as soon as possible.
+ //
+
+ switch (inputOpcode)
+ {
+ case ButtonPress:
+ case ButtonRelease:
+ case KeyPress:
+ case KeyRelease:
+ {
+ priority_++;
+ }
+ }
+
+ unsigned char detail = inputMessage[1];
+ if (*inputMessage == MotionNotify)
+ encodeBuffer.encodeBoolValue((unsigned int) detail);
+ else if ((*inputMessage == EnterNotify) || (*inputMessage == LeaveNotify))
+ encodeBuffer.encodeValue((unsigned int) detail, 3);
+ else if (*inputMessage == KeyRelease)
+ {
+ if (detail == serverCache_ -> keyPressLastKey)
+ encodeBuffer.encodeBoolValue(1);
+ else
+ {
+ encodeBuffer.encodeBoolValue(0);
+ encodeBuffer.encodeValue((unsigned int) detail, 8);
+ }
+ }
+ else if ((*inputMessage == ButtonPress) || (*inputMessage == ButtonRelease))
+ encodeBuffer.encodeCachedValue(detail, 8,
+ serverCache_ -> buttonCache);
+ else
+ encodeBuffer.encodeValue((unsigned int) detail, 8);
+ unsigned int timestamp = GetULONG(inputMessage + 4, bigEndian_);
+ unsigned int timestampDiff =
+ timestamp - serverCache_ -> lastTimestamp;
+ serverCache_ -> lastTimestamp = timestamp;
+ encodeBuffer.encodeCachedValue(timestampDiff, 32,
+ serverCache_ -> motionNotifyTimestampCache, 9);
+ int skipRest = 0;
+ if (*inputMessage == KeyRelease)
+ {
+ skipRest = 1;
+ for (unsigned int i = 8; i < 31; i++)
+ {
+ if (inputMessage[i] != serverCache_ -> keyPressCache[i - 8])
+ {
+ skipRest = 0;
+ break;
+ }
+ }
+ encodeBuffer.encodeBoolValue(skipRest);
+ }
+
+ if (!skipRest)
+ {
+ const unsigned char *nextSrc = inputMessage + 8;
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 29,
+ *serverCache_ -> motionNotifyWindowCache[i], 6);
+ nextSrc += 4;
+ }
+ unsigned int rootX = GetUINT(inputMessage + 20, bigEndian_);
+ unsigned int rootY = GetUINT(inputMessage + 22, bigEndian_);
+ unsigned int eventX = GetUINT(inputMessage + 24, bigEndian_);
+ unsigned int eventY = GetUINT(inputMessage + 26, bigEndian_);
+ eventX -= rootX;
+ eventY -= rootY;
+ encodeBuffer.encodeCachedValue(rootX -
+ serverCache_ -> motionNotifyLastRootX, 16,
+ serverCache_ -> motionNotifyRootXCache, 6);
+ serverCache_ -> motionNotifyLastRootX = rootX;
+ encodeBuffer.encodeCachedValue(rootY -
+ serverCache_ -> motionNotifyLastRootY, 16,
+ serverCache_ -> motionNotifyRootYCache, 6);
+ serverCache_ -> motionNotifyLastRootY = rootY;
+ encodeBuffer.encodeCachedValue(eventX, 16,
+ serverCache_ -> motionNotifyEventXCache, 6);
+ encodeBuffer.encodeCachedValue(eventY, 16,
+ serverCache_ -> motionNotifyEventYCache, 6);
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 28, bigEndian_),
+ 16, serverCache_ -> motionNotifyStateCache);
+ if ((*inputMessage == EnterNotify) || (*inputMessage == LeaveNotify))
+ encodeBuffer.encodeValue((unsigned int) inputMessage[30], 2);
+ else
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[30]);
+ if ((*inputMessage == EnterNotify) || (*inputMessage == LeaveNotify))
+ encodeBuffer.encodeValue((unsigned int) inputMessage[31], 2);
+ else if (*inputMessage == KeyPress)
+ {
+ serverCache_ -> keyPressLastKey = detail;
+ for (unsigned int i = 8; i < 31; i++)
+ {
+ serverCache_ -> keyPressCache[i - 8] = inputMessage[i];
+ }
+ }
+ }
+ }
+ break;
+ case ColormapNotify:
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_),
+ 29, serverCache_ -> colormapNotifyWindowCache, 8);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_),
+ 29, serverCache_ -> colormapNotifyColormapCache, 8);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[12]);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[13]);
+ }
+ break;
+ case ConfigureNotify:
+ {
+ const unsigned char *nextSrc = inputMessage + 4;
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 29,
+ *serverCache_ -> configureNotifyWindowCache[i], 9);
+ nextSrc += 4;
+ }
+ for (unsigned int j = 0; j < 5; j++)
+ {
+ encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16,
+ *serverCache_ -> configureNotifyGeomCache[j], 8);
+ nextSrc += 2;
+ }
+ encodeBuffer.encodeBoolValue(*nextSrc);
+ }
+ break;
+ case CreateNotify:
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_),
+ 29, serverCache_ -> createNotifyWindowCache, 9);
+ unsigned int window = GetULONG(inputMessage + 8, bigEndian_);
+ encodeBuffer.encodeValue(window -
+ serverCache_ -> createNotifyLastWindow, 29, 5);
+ serverCache_ -> createNotifyLastWindow = window;
+ const unsigned char* nextSrc = inputMessage + 12;
+ for (unsigned int i = 0; i < 5; i++)
+ {
+ encodeBuffer.encodeValue(GetUINT(nextSrc, bigEndian_), 16, 9);
+ nextSrc += 2;
+ }
+ encodeBuffer.encodeBoolValue(*nextSrc);
+ }
+ break;
+ case Expose:
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29,
+ serverCache_ -> exposeWindowCache, 9);
+ const unsigned char *nextSrc = inputMessage + 8;
+ for (unsigned int i = 0; i < 5; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16,
+ *serverCache_ -> exposeGeomCache[i], 6);
+ nextSrc += 2;
+ }
+ }
+ break;
+ case FocusIn:
+ case FocusOut:
+ {
+ encodeBuffer.encodeValue((unsigned int) inputMessage[1], 3);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_),
+ 29, serverCache_ -> focusInWindowCache, 9);
+ encodeBuffer.encodeValue((unsigned int) inputMessage[8], 2);
+ }
+ break;
+ case KeymapNotify:
+ {
+ if (ServerCache::lastKeymap.compare(31, inputMessage + 1))
+ encodeBuffer.encodeBoolValue(1);
+ else
+ {
+ encodeBuffer.encodeBoolValue(0);
+ const unsigned char *nextSrc = inputMessage + 1;
+ for (unsigned int i = 1; i < 32; i++)
+ encodeBuffer.encodeValue((unsigned int) *nextSrc++, 8);
+ }
+ }
+ break;
+ case MapNotify:
+ case UnmapNotify:
+ case DestroyNotify:
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_),
+ 29, serverCache_ -> mapNotifyEventCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_),
+ 29, serverCache_ -> mapNotifyWindowCache, 9);
+ if ((*inputMessage == MapNotify) || (*inputMessage == UnmapNotify))
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[12]);
+ }
+ break;
+ case NoExpose:
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_),
+ 29, serverCache_ -> noExposeDrawableCache, 9);
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 8, bigEndian_), 16,
+ serverCache_ -> noExposeMinorCache);
+ encodeBuffer.encodeCachedValue(inputMessage[10], 8,
+ serverCache_ -> noExposeMajorCache);
+ }
+ break;
+ case PropertyNotify:
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_),
+ 29, serverCache_ -> propertyNotifyWindowCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_),
+ 29, serverCache_ -> propertyNotifyAtomCache, 9);
+ unsigned int timestamp = GetULONG(inputMessage + 12, bigEndian_);
+ unsigned int timestampDiff =
+ timestamp - serverCache_ -> lastTimestamp;
+ serverCache_ -> lastTimestamp = timestamp;
+ encodeBuffer.encodeValue(timestampDiff, 32, 9);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[16]);
+ }
+ break;
+ case ReparentNotify:
+ {
+ const unsigned char* nextSrc = inputMessage + 4;
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_),
+ 29, serverCache_ -> reparentNotifyWindowCache, 9);
+ nextSrc += 4;
+ }
+ encodeBuffer.encodeValue(GetUINT(nextSrc, bigEndian_), 16, 6);
+ encodeBuffer.encodeValue(GetUINT(nextSrc + 2, bigEndian_), 16, 6);
+ encodeBuffer.encodeBoolValue((unsigned int)inputMessage[20]);
+ }
+ break;
+ case SelectionClear:
+ {
+ unsigned int timestamp = GetULONG(inputMessage + 4, bigEndian_);
+ unsigned int timestampDiff = timestamp - serverCache_ -> lastTimestamp;
+ serverCache_ -> lastTimestamp = timestamp;
+ encodeBuffer.encodeValue(timestampDiff, 32, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_),
+ 29, serverCache_ -> selectionClearWindowCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_),
+ 29, serverCache_ -> selectionClearAtomCache, 9);
+ }
+ break;
+ case SelectionRequest:
+ {
+ unsigned int timestamp = GetULONG(inputMessage + 4, bigEndian_);
+ unsigned int timestampDiff = timestamp - serverCache_ -> lastTimestamp;
+ serverCache_ -> lastTimestamp = timestamp;
+ encodeBuffer.encodeValue(timestampDiff, 32, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_),
+ 29, serverCache_ -> selectionClearWindowCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_),
+ 29, serverCache_ -> selectionClearWindowCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 16, bigEndian_),
+ 29, serverCache_ -> selectionClearAtomCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 20, bigEndian_),
+ 29, serverCache_ -> selectionClearAtomCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 24, bigEndian_),
+ 29, serverCache_ -> selectionClearAtomCache, 9);
+ }
+ break;
+ case VisibilityNotify:
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_),
+ 29, serverCache_ -> visibilityNotifyWindowCache, 9);
+ encodeBuffer.encodeValue((unsigned int) inputMessage[8], 2);
+ }
+ break;
+ default:
+ {
+ #ifdef TEST
+ *logofs << "handleRead: Using generic event compression "
+ << "for OPCODE#" << (unsigned int) inputOpcode
+ << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(*(inputMessage + 1), 8,
+ serverCache_ -> genericEventCharCache);
+
+ for (unsigned int i = 0; i < 14; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + i * 2 + 4, bigEndian_),
+ 16, *serverCache_ -> genericEventIntCache[i]);
+ }
+ }
+
+ } // switch (inputOpcode)...
+
+ int bits = encodeBuffer.diffBits();
+
+ #if defined(TEST) || defined(OPCODES)
+
+ if (*inputMessage == X_Error)
+ {
+ unsigned char code = *(inputMessage + 1);
+
+ *logofs << "handleRead: Handled error ERR_CODE#"
+ << (unsigned int) code << " for FD#" << fd_;
+
+ *logofs << " RES_ID#" << GetULONG(inputMessage + 4, bigEndian_);
+
+ *logofs << " MIN_OP#" << GetUINT(inputMessage + 8, bigEndian_);
+
+ *logofs << " MAJ_OP#" << (unsigned int) *(inputMessage + 10);
+
+ *logofs << " sequence " << inputSequence << ". " << inputLength
+ << " bytes in, " << bits << " bits (" << ((float) bits) / 8
+ << " bytes) out.\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: Handled event OPCODE#"
+ << (unsigned int) *inputMessage << " for FD#" << fd_
+ << " sequence " << inputSequence << ". " << inputLength
+ << " bytes in, " << bits << " bits (" << ((float) bits) / 8
+ << " bytes) out.\n" << logofs_flush;
+ }
+
+ #endif
+
+ statistics -> addEventBits(*inputMessage, inputLength << 3, bits);
+
+ } // End of if (inputMessage[0] == X_Reply) ... else ...
+
+ } // End of if (firstReply_) ... else ...
+
+ } // End of while ((inputMessage = readBuffer_.getMessage(inputLength)) != 0) ...
+
+ //
+ // Check if we need to flush because of
+ // prioritized data.
+ //
+
+ if (priority_ > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleRead: WARNING! Requesting flush "
+ << "because of " << priority_ << " prioritized "
+ << "messages for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (proxy -> handleAsyncPriority() < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Reset the priority flag.
+ //
+
+ priority_ = 0;
+ }
+
+ //
+ // Flush if we produced enough data.
+ //
+
+ if (proxy -> canAsyncFlush() == 1)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleRead: WARNING! Requesting flush "
+ << "because of token length exceeded.\n"
+ << logofs_flush;
+ #endif
+
+ if (proxy -> handleAsyncFlush() < 0)
+ {
+ return -1;
+ }
+ }
+
+ #if defined(TEST) || defined(INFO)
+
+ if (transport_ -> pending() != 0 ||
+ readBuffer_.checkMessage() != 0)
+ {
+ *logofs << "handleRead: PANIC! Buffer for X descriptor FD#"
+ << fd_ << " has " << transport_ -> pending()
+ << " bytes to read.\n" << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ //
+ // Reset the read buffer.
+ //
+
+ readBuffer_.fullReset();
+
+ return 1;
+}
+
+//
+// End of handleRead().
+//
+
+//
+// Beginning of handleWrite().
+//
+
+int ServerChannel::handleWrite(const unsigned char *message, unsigned int length)
+{
+ #ifdef TEST
+ *logofs << "handleWrite: Called for FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Create the buffer from which to
+ // decode messages.
+ //
+
+ DecodeBuffer decodeBuffer(message, length);
+
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+ *logofs << "handleWrite: Decoding messages for FD#" << fd_
+ << " with " << length << " bytes in the buffer.\n"
+ << logofs_flush;
+ #endif
+
+ if (firstRequest_)
+ {
+ //
+ // Need to add the length of the first request
+ // because it was not present in the previous
+ // versions. Length of the first request was
+ // assumed to be the same as the encode buffer
+ // but this may be not the case if a different
+ // encoding is used.
+ //
+
+ if (control -> isProtoStep7() == 1)
+ {
+ decodeBuffer.decodeValue(length, 8);
+ }
+
+ unsigned int nextByte;
+ unsigned char *outputMessage = writeBuffer_.addMessage(length);
+ unsigned char *nextDest = outputMessage;
+
+ for (unsigned int i = 0; i < length; i++)
+ {
+ decodeBuffer.decodeValue(nextByte, 8);
+
+ *nextDest++ = (unsigned char) nextByte;
+ }
+
+ if (*outputMessage == 0x42)
+ {
+ setBigEndian(1);
+ }
+ else
+ {
+ setBigEndian(0);
+ }
+
+ #ifdef TEST
+ *logofs << "handleWrite: First request detected.\n" << logofs_flush;
+ #endif
+
+ //
+ // Handle the fake authorization cookie.
+ //
+
+ if (handleAuthorization(outputMessage) < 0)
+ {
+ return -1;
+ }
+
+ firstRequest_ = 0;
+
+ } // End of if (firstRequest_)
+
+ //
+ // This was previously in a 'else' block.
+ // Due to the way the first request was
+ // handled, we could not decode multiple
+ // messages in the first frame.
+ //
+
+ { // Start of the decoding block.
+
+ unsigned char outputOpcode;
+
+ unsigned char *outputMessage;
+ unsigned int outputLength;
+
+ //
+ // Set when message is found in cache.
+ //
+
+ int hit;
+
+ while (decodeBuffer.decodeOpcodeValue(outputOpcode, clientCache_ -> opcodeCache, 1))
+ {
+ hit = 0;
+
+ //
+ // Splits are sent by client proxy outside the
+ // normal read loop. As we 'insert' splits in
+ // the real client-server X protocol, we must
+ // avoid to increment the sequence number or
+ // our clients would get confused.
+ //
+
+ if (outputOpcode != opcodeStore_ -> splitData)
+ {
+ clientSequence_++;
+ clientSequence_ &= 0xffff;
+
+ #ifdef DEBUG
+ *logofs << "handleWrite: Last client sequence number for FD#"
+ << fd_ << " is " << clientSequence_ << ".\n"
+ << logofs_flush;
+ #endif
+ }
+ else
+ {
+ //
+ // It's a split, not a normal
+ // burst of proxy data.
+ //
+
+ handleSplit(decodeBuffer);
+
+ continue;
+ }
+
+ #ifdef SUSPEND
+
+ if (clientSequence_ == 1000)
+ {
+ cerr << "Warning" << ": Exiting to test the resilience of the agent.\n";
+
+ sleep(2);
+
+ HandleAbort();
+ }
+
+ #endif
+
+ //
+ // Is differential encoding disabled?
+ //
+
+ if (control -> RemoteDeltaCompression == 0)
+ {
+ int result = handleFastWriteRequest(decodeBuffer, outputOpcode,
+ outputMessage, outputLength);
+ if (result < 0)
+ {
+ return -1;
+ }
+ else if (result > 0)
+ {
+ continue;
+ }
+ }
+
+ //
+ // General-purpose temp variables for
+ // decoding ints and chars.
+ //
+
+ unsigned int value;
+ unsigned char cValue;
+
+ #ifdef DEBUG
+ *logofs << "handleWrite: Going to handle request OPCODE#"
+ << (unsigned int) outputOpcode << " (" << DumpOpcode(outputOpcode)
+ << ") for FD#" << fd_ << " sequence " << clientSequence_
+ << ".\n" << logofs_flush;
+ #endif
+
+ switch (outputOpcode)
+ {
+ case X_AllocColor:
+ {
+ outputLength = 16;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache_ -> colormapCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ unsigned char *nextDest = outputMessage + 8;
+ unsigned int colorData[3];
+
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *(clientCache_ -> allocColorRGBCache[i]), 4);
+ PutUINT(value, nextDest, bigEndian_);
+ colorData[i] = value;
+ nextDest += 2;
+ }
+
+ sequenceQueue_.push(clientSequence_, outputOpcode,
+ colorData[0], colorData[1], colorData[2]);
+ }
+ break;
+ case X_ReparentWindow:
+ {
+ outputLength = 16;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeValue(value, 16, 11);
+ PutUINT(value, outputMessage + 12, bigEndian_);
+ decodeBuffer.decodeValue(value, 16, 11);
+ PutUINT(value, outputMessage + 14, bigEndian_);
+ }
+ break;
+ case X_ChangeProperty:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_ChangeProperty);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ unsigned char format;
+ decodeBuffer.decodeCachedValue(format, 8,
+ clientCache_ -> changePropertyFormatCache);
+ unsigned int dataLength;
+ decodeBuffer.decodeValue(dataLength, 32, 6);
+ outputLength = 24 + RoundUp4(dataLength * (format >> 3));
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeValue(value, 2);
+ outputMessage[1] = (unsigned char) value;
+ decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache_ -> changePropertyPropertyCache, 9);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache_ -> changePropertyTypeCache, 9);
+ PutULONG(value, outputMessage + 12, bigEndian_);
+ outputMessage[16] = format;
+ PutULONG(dataLength, outputMessage + 20, bigEndian_);
+ unsigned char *nextDest = outputMessage + 24;
+
+ if (format == 8)
+ {
+ if (control -> isProtoStep7() == 1)
+ {
+ decodeBuffer.decodeTextData(nextDest, dataLength);
+ }
+ else
+ {
+ clientCache_ -> changePropertyTextCompressor.reset();
+ for (unsigned int i = 0; i < dataLength; i++)
+ {
+ *nextDest++ = clientCache_ -> changePropertyTextCompressor.
+ decodeChar(decodeBuffer);
+ }
+ }
+ }
+ else if (format == 32)
+ {
+ for (unsigned int i = 0; i < dataLength; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 32,
+ clientCache_ -> changePropertyData32Cache);
+
+ PutULONG(value, nextDest, bigEndian_);
+
+ nextDest += 4;
+ }
+ }
+ else
+ {
+ for (unsigned int i = 0; i < dataLength; i++)
+ {
+ decodeBuffer.decodeValue(value, 16);
+
+ PutUINT(value, nextDest, bigEndian_);
+
+ nextDest += 2;
+ }
+ }
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_SendEvent:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_SendEvent);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ outputLength = 44;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeBoolValue(value);
+ *(outputMessage + 1) = value;
+ decodeBuffer.decodeBoolValue(value);
+ if (value)
+ {
+ decodeBuffer.decodeBoolValue(value);
+ }
+ else
+ {
+ decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache);
+ }
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 32,
+ clientCache_ -> sendEventMaskCache, 9);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(*(outputMessage + 12), 8,
+ clientCache_ -> sendEventCodeCache);
+ decodeBuffer.decodeCachedValue(*(outputMessage + 13), 8,
+ clientCache_ -> sendEventByteDataCache);
+ decodeBuffer.decodeValue(value, 16, 4);
+ clientCache_ -> sendEventLastSequence += value;
+ clientCache_ -> sendEventLastSequence &= 0xffff;
+ PutUINT(clientCache_ -> sendEventLastSequence, outputMessage + 14, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 32,
+ clientCache_ -> sendEventIntDataCache);
+ PutULONG(value, outputMessage + 16, bigEndian_);
+
+ for (unsigned int i = 20; i < 44; i++)
+ {
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache_ -> sendEventEventCache);
+ *(outputMessage + i) = cValue;
+ }
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_ChangeWindowAttributes:
+ {
+ unsigned int numAttrs;
+ decodeBuffer.decodeValue(numAttrs, 4);
+ outputLength = 12 + (numAttrs << 2);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ unsigned int bitmask;
+ decodeBuffer.decodeCachedValue(bitmask, 15,
+ clientCache_ -> createWindowBitmaskCache);
+ PutULONG(bitmask, outputMessage + 8, bigEndian_);
+ unsigned char *nextDest = outputMessage + 12;
+ unsigned int mask = 0x1;
+ for (unsigned int i = 0; i < 15; i++)
+ {
+ if (bitmask & mask)
+ {
+ decodeBuffer.decodeCachedValue(value, 32,
+ *clientCache_ -> createWindowAttrCache[i]);
+ PutULONG(value, nextDest, bigEndian_);
+ nextDest += 4;
+ }
+ mask <<= 1;
+ }
+ }
+ break;
+ case X_ClearArea:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_ClearArea);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ outputLength = 16;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[1] = (unsigned char) value;
+ decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ unsigned char *nextDest = outputMessage + 8;
+ for (unsigned int i = 0; i < 4; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> clearAreaGeomCache[i], 8);
+ PutUINT(value, nextDest, bigEndian_);
+ nextDest += 2;
+ }
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_CloseFont:
+ {
+ outputLength = 8;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeValue(value, 29, 5);
+ clientCache_ -> lastFont += value;
+ clientCache_ -> lastFont &= 0x1fffffff;
+ PutULONG(clientCache_ -> lastFont, outputMessage + 4, bigEndian_);
+ }
+ break;
+ case X_ConfigureWindow:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_ConfigureWindow);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ outputLength = 12;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ writeBuffer_.registerPointer(&outputMessage);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ unsigned int bitmask;
+ decodeBuffer.decodeCachedValue(bitmask, 7,
+ clientCache_ -> configureWindowBitmaskCache);
+ PutUINT(bitmask, outputMessage + 8, bigEndian_);
+ unsigned int mask = 0x1;
+ for (unsigned int i = 0; i < 7; i++)
+ {
+ if (bitmask & mask)
+ {
+ unsigned char* nextDest = writeBuffer_.addMessage(4);
+ outputLength += 4;
+ decodeBuffer.decodeCachedValue(value, CONFIGUREWINDOW_FIELD_WIDTH[i],
+ *clientCache_ -> configureWindowAttrCache[i], 8);
+ PutULONG(value, nextDest, bigEndian_);
+ nextDest += 4;
+ }
+ mask <<= 1;
+ }
+ writeBuffer_.unregisterPointer();
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_ConvertSelection:
+ {
+ outputLength = 24;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache_ -> convertSelectionRequestorCache, 9);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ unsigned char* nextDest = outputMessage + 8;
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 29,
+ *(clientCache_ -> convertSelectionAtomCache[i]), 9);
+ PutULONG(value, nextDest, bigEndian_);
+ nextDest += 4;
+ }
+ decodeBuffer.decodeValue(value, 32, 4);
+ clientCache_ -> convertSelectionLastTimestamp += value;
+ PutULONG(clientCache_ -> convertSelectionLastTimestamp,
+ nextDest, bigEndian_);
+ }
+ break;
+ case X_CopyArea:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_CopyArea);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ outputLength = 28;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 12, bigEndian_);
+ unsigned char *nextDest = outputMessage + 16;
+ for (unsigned int i = 0; i < 6; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> copyAreaGeomCache[i], 8);
+ PutUINT(value, nextDest, bigEndian_);
+ nextDest += 2;
+ }
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_CopyGC:
+ {
+ outputLength = 16;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 23,
+ clientCache_ -> createGCBitmaskCache);
+ PutULONG(value, outputMessage + 12, bigEndian_);
+ }
+ break;
+ case X_CopyPlane:
+ {
+ outputLength = 32;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 12, bigEndian_);
+ unsigned char *nextDest = outputMessage + 16;
+ for (unsigned int i = 0; i < 6; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> copyPlaneGeomCache[i], 8);
+ PutUINT(value, nextDest, bigEndian_);
+ nextDest += 2;
+ }
+ decodeBuffer.decodeCachedValue(value, 32,
+ clientCache_ -> copyPlaneBitPlaneCache, 10);
+ PutULONG(value, outputMessage + 28, bigEndian_);
+ }
+ break;
+ case X_CreateGC:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_CreateGC);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ outputLength = 16;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ writeBuffer_.registerPointer(&outputMessage);
+
+ if (control -> isProtoStep7() == 1)
+ {
+ decodeBuffer.decodeNewXidValue(value, clientCache_ -> lastId,
+ clientCache_ -> lastIdCache, clientCache_ -> gcCache,
+ clientCache_ -> freeGCCache);
+ }
+ else
+ {
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ }
+
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ unsigned int offset = 8;
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + offset, bigEndian_);
+ offset += 4;
+ unsigned int bitmask;
+ decodeBuffer.decodeCachedValue(bitmask, 23,
+ clientCache_ -> createGCBitmaskCache);
+ PutULONG(bitmask, outputMessage + offset, bigEndian_);
+ unsigned int mask = 0x1;
+ for (unsigned int i = 0; i < 23; i++)
+ {
+ if (bitmask & mask)
+ {
+ unsigned char* nextDest = writeBuffer_.addMessage(4);
+ outputLength += 4;
+ unsigned int fieldWidth = CREATEGC_FIELD_WIDTH[i];
+ if (fieldWidth <= 4)
+ decodeBuffer.decodeValue(value, fieldWidth);
+ else
+ decodeBuffer.decodeCachedValue(value, fieldWidth,
+ *clientCache_ -> createGCAttrCache[i]);
+ PutULONG(value, nextDest, bigEndian_);
+ }
+ mask <<= 1;
+ }
+ writeBuffer_.unregisterPointer();
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_ChangeGC:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_ChangeGC);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ outputLength = 12;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ writeBuffer_.registerPointer(&outputMessage);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ unsigned int offset = 8;
+ unsigned int bitmask;
+ decodeBuffer.decodeCachedValue(bitmask, 23,
+ clientCache_ -> createGCBitmaskCache);
+ PutULONG(bitmask, outputMessage + offset, bigEndian_);
+ unsigned int mask = 0x1;
+ for (unsigned int i = 0; i < 23; i++)
+ {
+ if (bitmask & mask)
+ {
+ unsigned char* nextDest = writeBuffer_.addMessage(4);
+ outputLength += 4;
+ unsigned int fieldWidth = CREATEGC_FIELD_WIDTH[i];
+ if (fieldWidth <= 4)
+ decodeBuffer.decodeValue(value, fieldWidth);
+ else
+ decodeBuffer.decodeCachedValue(value, fieldWidth,
+ *clientCache_ -> createGCAttrCache[i]);
+ PutULONG(value, nextDest, bigEndian_);
+ }
+ mask <<= 1;
+ }
+ writeBuffer_.unregisterPointer();
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_CreatePixmap:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_CreatePixmap);
+
+ hit = handleDecode(decodeBuffer, clientCache_, messageStore,
+ outputOpcode, outputMessage, outputLength);
+ }
+ break;
+ case X_CreateWindow:
+ {
+ outputLength = 32;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ writeBuffer_.registerPointer(&outputMessage);
+ decodeBuffer.decodeCachedValue(cValue, 8, clientCache_ -> depthCache);
+ outputMessage[1] = cValue;
+ decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+
+ if (control -> isProtoStep7() == 1)
+ {
+ decodeBuffer.decodeNewXidValue(value, clientCache_ -> lastId,
+ clientCache_ -> lastIdCache, clientCache_ -> windowCache,
+ clientCache_ -> freeWindowCache);
+ }
+ else
+ {
+ decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache);
+ }
+
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ unsigned char *nextDest = outputMessage + 12;
+ unsigned int i;
+ for (i = 0; i < 6; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> createWindowGeomCache[i], 8);
+ PutUINT(value, nextDest, bigEndian_);
+ nextDest += 2;
+ }
+ decodeBuffer.decodeCachedValue(value, 29, clientCache_ -> visualCache);
+ PutULONG(value, outputMessage + 24, bigEndian_);
+ unsigned int bitmask;
+ decodeBuffer.decodeCachedValue(bitmask, 15,
+ clientCache_ -> createWindowBitmaskCache);
+ PutULONG(bitmask, outputMessage + 28, bigEndian_);
+ unsigned int mask = 0x1;
+ for (i = 0; i < 15; i++)
+ {
+ if (bitmask & mask)
+ {
+ nextDest = writeBuffer_.addMessage(4);
+ outputLength += 4;
+ decodeBuffer.decodeCachedValue(value, 32,
+ *clientCache_ -> createWindowAttrCache[i]);
+ PutULONG(value, nextDest, bigEndian_);
+ }
+ mask <<= 1;
+ }
+ writeBuffer_.unregisterPointer();
+ }
+ break;
+ case X_DeleteProperty:
+ {
+ outputLength = 12;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeValue(value, 29, 9);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ }
+ break;
+ case X_FillPoly:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_FillPoly);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ unsigned int numPoints;
+
+ if (control -> isProtoStep10() == 1)
+ {
+ decodeBuffer.decodeCachedValue(numPoints, 16,
+ clientCache_ -> fillPolyNumPointsCache, 4);
+ }
+ else
+ {
+ decodeBuffer.decodeCachedValue(numPoints, 14,
+ clientCache_ -> fillPolyNumPointsCache, 4);
+ }
+
+ outputLength = 16 + (numPoints << 2);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeValue(value, 2);
+ outputMessage[12] = (unsigned char) value;
+ unsigned int relativeCoordMode;
+ decodeBuffer.decodeBoolValue(relativeCoordMode);
+ outputMessage[13] = (unsigned char) relativeCoordMode;
+ unsigned char *nextDest = outputMessage + 16;
+ unsigned int pointIndex = 0;
+ for (unsigned int i = 0; i < numPoints; i++)
+ {
+ if (relativeCoordMode)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> fillPolyXRelCache[pointIndex], 8);
+ PutUINT(value, nextDest, bigEndian_);
+ nextDest += 2;
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> fillPolyYRelCache[pointIndex], 8);
+ PutUINT(value, nextDest, bigEndian_);
+ nextDest += 2;
+ }
+ else
+ {
+ unsigned int x, y;
+ decodeBuffer.decodeBoolValue(value);
+ if (value)
+ {
+ decodeBuffer.decodeValue(value, 3);
+ x = clientCache_ -> fillPolyRecentX[value];
+ y = clientCache_ -> fillPolyRecentY[value];
+ }
+ else
+ {
+ decodeBuffer.decodeCachedValue(x, 16,
+ *clientCache_ -> fillPolyXAbsCache[pointIndex], 8);
+ decodeBuffer.decodeCachedValue(y, 16,
+ *clientCache_ -> fillPolyYAbsCache[pointIndex], 8);
+ clientCache_ -> fillPolyRecentX[clientCache_ -> fillPolyIndex] = x;
+ clientCache_ -> fillPolyRecentY[clientCache_ -> fillPolyIndex] = y;
+ clientCache_ -> fillPolyIndex++;
+ if (clientCache_ -> fillPolyIndex == 8)
+ clientCache_ -> fillPolyIndex = 0;
+ }
+ PutUINT(x, nextDest, bigEndian_);
+ nextDest += 2;
+ PutUINT(y, nextDest, bigEndian_);
+ nextDest += 2;
+ }
+
+ if (++pointIndex == 10) pointIndex = 0;
+ }
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_FreeColors:
+ {
+ unsigned int numPixels;
+ decodeBuffer.decodeValue(numPixels, 16, 4);
+ outputLength = 12 + (numPixels << 2);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache_ -> colormapCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeValue(value, 32, 4);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ unsigned char* nextDest = outputMessage + 12;
+ while (numPixels)
+ {
+ decodeBuffer.decodeValue(value, 32, 8);
+ PutULONG(value, nextDest, bigEndian_);
+ nextDest += 4;
+ numPixels--;
+ }
+ }
+ break;
+ case X_FreeCursor:
+ {
+ outputLength = 8;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeCachedValue(value, 29, clientCache_ -> cursorCache, 9);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ }
+ break;
+ case X_FreeGC:
+ {
+ outputLength = 8;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+
+ if (control -> isProtoStep7() == 1)
+ {
+ decodeBuffer.decodeFreeXidValue(value, clientCache_ -> freeGCCache);
+ }
+ else
+ {
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ }
+
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ }
+ break;
+ case X_FreePixmap:
+ {
+ outputLength = 8;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+
+ if (control -> isProtoStep7() == 1)
+ {
+ decodeBuffer.decodeFreeXidValue(value, clientCache_ -> freeDrawableCache);
+
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ }
+ else
+ {
+ decodeBuffer.decodeBoolValue(value);
+ if (!value)
+ {
+ decodeBuffer.decodeValue(value, 29, 4);
+ clientCache_ -> createPixmapLastId += value;
+ clientCache_ -> createPixmapLastId &= 0x1fffffff;
+ }
+ PutULONG(clientCache_ -> createPixmapLastId, outputMessage + 4, bigEndian_);
+ }
+ }
+ break;
+ case X_GetAtomName:
+ {
+ outputLength = 8;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeValue(value, 29, 9);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ break;
+ case X_GetGeometry:
+ {
+ outputLength = 8;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ break;
+ case X_GetInputFocus:
+ {
+ outputLength = 4;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode, outputOpcode);
+ }
+ break;
+ case X_GetModifierMapping:
+ {
+ outputLength = 4;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ break;
+ case X_GetKeyboardMapping:
+ {
+ outputLength = 8;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeValue(value, 8);
+ outputMessage[4] = value;
+ decodeBuffer.decodeValue(value, 8);
+ outputMessage[5] = value;
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ break;
+ case X_GetProperty:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_GetProperty);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ //
+ // Save a reference to identify the reply.
+ //
+
+ unsigned int property = GetULONG(outputMessage + 8, bigEndian_);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode, property);
+
+ break;
+ }
+
+ outputLength = 24;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[1] = (unsigned char) value;
+ decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ unsigned int property;
+ decodeBuffer.decodeValue(property, 29, 9);
+ PutULONG(property, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeValue(value, 29, 9);
+ PutULONG(value, outputMessage + 12, bigEndian_);
+ decodeBuffer.decodeValue(value, 32, 2);
+ PutULONG(value, outputMessage + 16, bigEndian_);
+ decodeBuffer.decodeValue(value, 32, 8);
+ PutULONG(value, outputMessage + 20, bigEndian_);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode, property);
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_GetSelectionOwner:
+ {
+ outputLength = 8;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache_ -> getSelectionOwnerSelectionCache, 9);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ break;
+ case X_GrabButton:
+ case X_GrabPointer:
+ {
+ outputLength = 24;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[1] = (unsigned char) value;
+ decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> grabButtonEventMaskCache);
+ PutUINT(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[10] = (unsigned char) value;
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[11] = (unsigned char) value;
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache_ -> grabButtonConfineCache, 9);
+ PutULONG(value, outputMessage + 12, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache_ -> cursorCache, 9);
+ PutULONG(value, outputMessage + 16, bigEndian_);
+ if (outputOpcode == X_GrabButton)
+ {
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache_ -> grabButtonButtonCache);
+ outputMessage[20] = cValue;
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> grabButtonModifierCache);
+ PutUINT(value, outputMessage + 22, bigEndian_);
+ }
+ else
+ {
+ decodeBuffer.decodeValue(value, 32, 4);
+ clientCache_ -> grabKeyboardLastTimestamp += value;
+ PutULONG(clientCache_ -> grabKeyboardLastTimestamp,
+ outputMessage + 20, bigEndian_);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ }
+ break;
+ case X_GrabKeyboard:
+ {
+ outputLength = 16;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[1] = (unsigned char) value;
+ decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeValue(value, 32, 4);
+ clientCache_ -> grabKeyboardLastTimestamp += value;
+ PutULONG(clientCache_ -> grabKeyboardLastTimestamp, outputMessage + 8,
+ bigEndian_);
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[12] = (unsigned char) value;
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[13] = (unsigned char) value;
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ break;
+ case X_GrabServer:
+ case X_UngrabServer:
+ case X_NoOperation:
+ {
+ #ifdef DEBUG
+ *logofs << "handleWrite: Managing (probably tainted) X_NoOperation request for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ outputLength = 4;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ }
+ break;
+ case X_PolyText8:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolyText8);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ outputLength = 16;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> polyTextCacheX);
+ clientCache_ -> polyTextLastX += value;
+ clientCache_ -> polyTextLastX &= 0xffff;
+ PutUINT(clientCache_ -> polyTextLastX, outputMessage + 12, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> polyTextCacheY);
+ clientCache_ -> polyTextLastY += value;
+ clientCache_ -> polyTextLastY &= 0xffff;
+ PutUINT(clientCache_ -> polyTextLastY, outputMessage + 14, bigEndian_);
+ unsigned int addedLength = 0;
+ writeBuffer_.registerPointer(&outputMessage);
+ for (;;)
+ {
+ decodeBuffer.decodeBoolValue(value);
+ if (!value)
+ break;
+ unsigned int textLength;
+ decodeBuffer.decodeValue(textLength, 8);
+ if (textLength == 255)
+ {
+ addedLength += 5;
+ unsigned char *nextSegment = writeBuffer_.addMessage(5);
+ *nextSegment = (unsigned char) textLength;
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache_ -> polyTextFontCache);
+ PutULONG(value, nextSegment + 1, 1);
+ }
+ else
+ {
+ addedLength += (textLength + 2);
+ unsigned char *nextSegment =
+ writeBuffer_.addMessage(textLength + 2);
+ *nextSegment = (unsigned char) textLength;
+ unsigned char *nextDest = nextSegment + 1;
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache_ -> polyTextDeltaCache);
+ *nextDest++ = cValue;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ decodeBuffer.decodeTextData(nextDest, textLength);
+
+ nextDest += textLength;
+ }
+ else
+ {
+ clientCache_ -> polyTextTextCompressor.reset();
+ while (textLength)
+ {
+ *nextDest++ = clientCache_ -> polyTextTextCompressor.decodeChar(decodeBuffer);
+ textLength--;
+ }
+ }
+ }
+ }
+ outputLength += addedLength;
+ unsigned int mod4 = (addedLength & 0x3);
+ if (mod4)
+ {
+ unsigned int extra = 4 - mod4;
+ unsigned char *nextDest = writeBuffer_.addMessage(extra);
+ for (unsigned int i = 0; i < extra; i++)
+ *nextDest++ = 0;
+ outputLength += extra;
+ }
+ writeBuffer_.unregisterPointer();
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_PolyText16:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolyText16);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ outputLength = 16;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> polyTextCacheX);
+ clientCache_ -> polyTextLastX += value;
+ clientCache_ -> polyTextLastX &= 0xffff;
+ PutUINT(clientCache_ -> polyTextLastX, outputMessage + 12, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> polyTextCacheY);
+ clientCache_ -> polyTextLastY += value;
+ clientCache_ -> polyTextLastY &= 0xffff;
+ PutUINT(clientCache_ -> polyTextLastY, outputMessage + 14, bigEndian_);
+ unsigned int addedLength = 0;
+ writeBuffer_.registerPointer(&outputMessage);
+ for (;;)
+ {
+ decodeBuffer.decodeBoolValue(value);
+ if (!value)
+ break;
+ unsigned int textLength;
+ decodeBuffer.decodeValue(textLength, 8);
+ if (textLength == 255)
+ {
+ addedLength += 5;
+ unsigned char *nextSegment = writeBuffer_.addMessage(5);
+ *nextSegment = (unsigned char) textLength;
+ decodeBuffer.decodeCachedValue(value, 29, clientCache_ -> polyTextFontCache);
+ PutULONG(value, nextSegment + 1, 1);
+ }
+ else
+ {
+ addedLength += (textLength * 2 + 2);
+ unsigned char *nextSegment =
+ writeBuffer_.addMessage(textLength * 2 + 2);
+ *nextSegment = (unsigned char) textLength;
+ unsigned char *nextDest = nextSegment + 1;
+ decodeBuffer.decodeCachedValue(cValue, 8, clientCache_ -> polyTextDeltaCache);
+ *nextDest++ = cValue;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ decodeBuffer.decodeTextData(nextDest, textLength * 2);
+
+ nextDest += textLength * 2;
+ }
+ else
+ {
+ clientCache_ -> polyTextTextCompressor.reset();
+ textLength <<= 1;
+ while (textLength)
+ {
+ *nextDest++ =
+ clientCache_ -> polyTextTextCompressor.decodeChar(decodeBuffer);
+ textLength--;
+ }
+ }
+ }
+ }
+ outputLength += addedLength;
+
+ unsigned int mod4 = (addedLength & 0x3);
+ if (mod4)
+ {
+ unsigned int extra = 4 - mod4;
+ unsigned char *nextDest = writeBuffer_.addMessage(extra);
+ for (unsigned int i = 0; i < extra; i++)
+ *nextDest++ = 0;
+ outputLength += extra;
+ }
+ writeBuffer_.unregisterPointer();
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_ImageText8:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_ImageText8);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ unsigned int textLength;
+ decodeBuffer.decodeCachedValue(textLength, 8,
+ clientCache_ -> imageTextLengthCache, 4);
+ outputLength = 16 + RoundUp4(textLength);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ outputMessage[1] = (unsigned char) textLength;
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> imageTextCacheX);
+ clientCache_ -> imageTextLastX += value;
+ clientCache_ -> imageTextLastX &= 0xffff;
+ PutUINT(clientCache_ -> imageTextLastX, outputMessage + 12, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> imageTextCacheY);
+ clientCache_ -> imageTextLastY += value;
+ clientCache_ -> imageTextLastY &= 0xffff;
+ PutUINT(clientCache_ -> imageTextLastY, outputMessage + 14, bigEndian_);
+ unsigned char *nextDest = outputMessage + 16;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ decodeBuffer.decodeTextData(nextDest, textLength);
+ }
+ else
+ {
+ clientCache_ -> imageTextTextCompressor.reset();
+ for (unsigned int j = 0; j < textLength; j++)
+ *nextDest++ = clientCache_ -> imageTextTextCompressor.decodeChar(decodeBuffer);
+ }
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_ImageText16:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_ImageText16);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ unsigned int textLength;
+ decodeBuffer.decodeCachedValue(textLength, 8,
+ clientCache_ -> imageTextLengthCache, 4);
+ outputLength = 16 + RoundUp4(textLength * 2);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ outputMessage[1] = (unsigned char) textLength;
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> imageTextCacheX);
+ clientCache_ -> imageTextLastX += value;
+ clientCache_ -> imageTextLastX &= 0xffff;
+ PutUINT(clientCache_ -> imageTextLastX, outputMessage + 12, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> imageTextCacheY);
+ clientCache_ -> imageTextLastY += value;
+ clientCache_ -> imageTextLastY &= 0xffff;
+ PutUINT(clientCache_ -> imageTextLastY, outputMessage + 14, bigEndian_);
+ unsigned char *nextDest = outputMessage + 16;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ decodeBuffer.decodeTextData(nextDest, textLength * 2);
+ }
+ else
+ {
+ clientCache_ -> imageTextTextCompressor.reset();
+ for (unsigned int j = 0; j < textLength * 2; j++)
+ *nextDest++ = clientCache_ -> imageTextTextCompressor.decodeChar(decodeBuffer);
+ }
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_InternAtom:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_InternAtom);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+
+ break;
+ }
+
+ unsigned int nameLength;
+ decodeBuffer.decodeValue(nameLength, 16, 6);
+ outputLength = RoundUp4(nameLength) + 8;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ PutUINT(nameLength, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[1] = (unsigned char) value;
+ unsigned char *nextDest = outputMessage + 8;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ decodeBuffer.decodeTextData(nextDest, nameLength);
+ }
+ else
+ {
+ clientCache_ -> internAtomTextCompressor.reset();
+ for (unsigned int i = 0; i < nameLength; i++)
+ {
+ *nextDest++ = clientCache_ -> internAtomTextCompressor.decodeChar(decodeBuffer);
+ }
+ }
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_ListExtensions:
+ {
+ outputLength = 4;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ break;
+ case X_ListFonts:
+ {
+ unsigned int textLength;
+ decodeBuffer.decodeValue(textLength, 16, 6);
+ outputLength = 8 + RoundUp4(textLength);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ PutUINT(textLength, outputMessage + 6, bigEndian_);
+ decodeBuffer.decodeValue(value, 16, 6);
+ PutUINT(value, outputMessage + 4, bigEndian_);
+ unsigned char* nextDest = outputMessage + 8;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ decodeBuffer.decodeTextData(nextDest, textLength);
+ }
+ else
+ {
+ clientCache_ -> polyTextTextCompressor.reset();
+ for (unsigned int i = 0; i < textLength; i++)
+ {
+ *nextDest++ = clientCache_ -> polyTextTextCompressor.decodeChar(decodeBuffer);
+ }
+ }
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ break;
+ case X_LookupColor:
+ case X_AllocNamedColor:
+ {
+ unsigned int textLength;
+ decodeBuffer.decodeValue(textLength, 16, 6);
+ outputLength = 12 + RoundUp4(textLength);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache_ -> colormapCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ PutUINT(textLength, outputMessage + 8, bigEndian_);
+ unsigned char *nextDest = outputMessage + 12;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ decodeBuffer.decodeTextData(nextDest, textLength);
+ }
+ else
+ {
+ clientCache_ -> polyTextTextCompressor.reset();
+ for (unsigned int i = 0; i < textLength; i++)
+ {
+ *nextDest++ = clientCache_ -> polyTextTextCompressor.decodeChar(decodeBuffer);
+ }
+ }
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ break;
+ case X_MapWindow:
+ case X_UnmapWindow:
+ case X_MapSubwindows:
+ case X_GetWindowAttributes:
+ case X_DestroyWindow:
+ case X_DestroySubwindows:
+ case X_QueryPointer:
+ case X_QueryTree:
+ {
+ outputLength = 8;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+
+ if (outputOpcode == X_DestroyWindow && control -> isProtoStep7() == 1)
+ {
+ decodeBuffer.decodeFreeXidValue(value, clientCache_ -> freeWindowCache);
+ }
+ else
+ {
+ decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache);
+ }
+
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ if (outputOpcode == X_QueryPointer ||
+ outputOpcode == X_GetWindowAttributes ||
+ outputOpcode == X_QueryTree)
+ {
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ }
+ break;
+ case X_OpenFont:
+ {
+ unsigned int nameLength;
+ decodeBuffer.decodeValue(nameLength, 16, 7);
+ outputLength = RoundUp4(12 + nameLength);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ PutUINT(nameLength, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeValue(value, 29, 5);
+ clientCache_ -> lastFont += value;
+ clientCache_ -> lastFont &= 0x1fffffff;
+ PutULONG(clientCache_ -> lastFont, outputMessage + 4, bigEndian_);
+ unsigned char *nextDest = outputMessage + 12;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ decodeBuffer.decodeTextData(nextDest, nameLength);
+ }
+ else
+ {
+ clientCache_ -> openFontTextCompressor.reset();
+ for (; nameLength; nameLength--)
+ {
+ *nextDest++ = clientCache_ -> openFontTextCompressor.
+ decodeChar(decodeBuffer);
+ }
+ }
+ }
+ break;
+ case X_PolyFillRectangle:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolyFillRectangle);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ outputLength = 12;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ writeBuffer_.registerPointer(&outputMessage);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+
+ unsigned int index = 0;
+ unsigned int lastX = 0, lastY = 0, lastWidth = 0, lastHeight = 0;
+ unsigned int numRectangles = 0;
+
+ for (;;)
+ {
+ outputLength += 8;
+ writeBuffer_.addMessage(8);
+ unsigned char *nextDest = outputMessage + 12 +
+ (numRectangles << 3);
+ numRectangles++;
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyFillRectangleCacheX[index], 8);
+ value += lastX;
+ PutUINT(value, nextDest, bigEndian_);
+ lastX = value;
+ nextDest += 2;
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyFillRectangleCacheY[index], 8);
+ value += lastY;
+ PutUINT(value, nextDest, bigEndian_);
+ lastY = value;
+ nextDest += 2;
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyFillRectangleCacheWidth[index], 8);
+ value += lastWidth;
+ PutUINT(value, nextDest, bigEndian_);
+ lastWidth = value;
+ nextDest += 2;
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyFillRectangleCacheHeight[index], 8);
+ value += lastHeight;
+ PutUINT(value, nextDest, bigEndian_);
+ lastHeight = value;
+ nextDest += 2;
+
+ if (++index == 4) index = 0;
+
+ decodeBuffer.decodeBoolValue(value);
+
+ if (!value) break;
+ }
+ writeBuffer_.unregisterPointer();
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_PolyFillArc:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolyFillArc);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ outputLength = 12;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ writeBuffer_.registerPointer(&outputMessage);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+
+ unsigned int index = 0;
+ unsigned int lastX = 0, lastY = 0,
+ lastWidth = 0, lastHeight = 0,
+ lastAngle1 = 0, lastAngle2 = 0;
+
+ unsigned int numArcs = 0;
+
+ for (;;)
+ {
+ outputLength += 12;
+ writeBuffer_.addMessage(12);
+
+ unsigned char *nextDest = outputMessage + 12 +
+ (numArcs * 12);
+ numArcs++;
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyFillArcCacheX[index], 8);
+ value += lastX;
+ PutUINT(value, nextDest, bigEndian_);
+ lastX = value;
+ nextDest += 2;
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyFillArcCacheY[index], 8);
+ value += lastY;
+ PutUINT(value, nextDest, bigEndian_);
+ lastY = value;
+ nextDest += 2;
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyFillArcCacheWidth[index], 8);
+ value += lastWidth;
+ PutUINT(value, nextDest, bigEndian_);
+ lastWidth = value;
+ nextDest += 2;
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyFillArcCacheHeight[index], 8);
+ value += lastHeight;
+ PutUINT(value, nextDest, bigEndian_);
+ lastHeight = value;
+ nextDest += 2;
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyFillArcCacheAngle1[index], 8);
+ value += lastAngle1;
+ PutUINT(value, nextDest, bigEndian_);
+ lastAngle1 = value;
+ nextDest += 2;
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyFillArcCacheAngle2[index], 8);
+ value += lastAngle2;
+ PutUINT(value, nextDest, bigEndian_);
+ lastAngle2 = value;
+ nextDest += 2;
+
+ if (++index == 2) index = 0;
+
+ decodeBuffer.decodeBoolValue(value);
+
+ if (!value) break;
+ }
+ writeBuffer_.unregisterPointer();
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_PolyArc:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolyArc);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ outputLength = 12;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ writeBuffer_.registerPointer(&outputMessage);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+
+ unsigned int index = 0;
+ unsigned int lastX = 0, lastY = 0,
+ lastWidth = 0, lastHeight = 0,
+ lastAngle1 = 0, lastAngle2 = 0;
+
+ unsigned int numArcs = 0;
+
+ for (;;)
+ {
+ outputLength += 12;
+ writeBuffer_.addMessage(12);
+
+ unsigned char *nextDest = outputMessage + 12 +
+ (numArcs * 12);
+ numArcs++;
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyArcCacheX[index], 8);
+ value += lastX;
+ PutUINT(value, nextDest, bigEndian_);
+ lastX = value;
+ nextDest += 2;
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyArcCacheY[index], 8);
+ value += lastY;
+ PutUINT(value, nextDest, bigEndian_);
+ lastY = value;
+ nextDest += 2;
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyArcCacheWidth[index], 8);
+ value += lastWidth;
+ PutUINT(value, nextDest, bigEndian_);
+ lastWidth = value;
+ nextDest += 2;
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyArcCacheHeight[index], 8);
+ value += lastHeight;
+ PutUINT(value, nextDest, bigEndian_);
+ lastHeight = value;
+ nextDest += 2;
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyArcCacheAngle1[index], 8);
+ value += lastAngle1;
+ PutUINT(value, nextDest, bigEndian_);
+ lastAngle1 = value;
+ nextDest += 2;
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyArcCacheAngle2[index], 8);
+ value += lastAngle2;
+ PutUINT(value, nextDest, bigEndian_);
+ lastAngle2 = value;
+ nextDest += 2;
+
+ if (++index == 2) index = 0;
+
+ decodeBuffer.decodeBoolValue(value);
+
+ if (!value) break;
+ }
+ writeBuffer_.unregisterPointer();
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_PolyPoint:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolyPoint);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ unsigned int numPoints;
+ decodeBuffer.decodeValue(numPoints, 16, 4);
+ outputLength = (numPoints << 2) + 12;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ unsigned int relativeCoordMode;
+ decodeBuffer.decodeBoolValue(relativeCoordMode);
+ outputMessage[1] = (unsigned char) relativeCoordMode;
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ unsigned char *nextDest = outputMessage + 12;
+
+ unsigned int index = 0;
+ unsigned int lastX = 0, lastY = 0;
+
+ for (unsigned int i = 0; i < numPoints; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyPointCacheX[index], 8);
+ lastX += value;
+ PutUINT(lastX, nextDest, bigEndian_);
+ nextDest += 2;
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyPointCacheY[index], 8);
+ lastY += value;
+ PutUINT(lastY, nextDest, bigEndian_);
+ nextDest += 2;
+
+ if (++index == 2) index = 0;
+ }
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_PolyLine:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolyLine);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ unsigned int numPoints;
+ decodeBuffer.decodeValue(numPoints, 16, 4);
+ outputLength = (numPoints << 2) + 12;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ unsigned int relativeCoordMode;
+ decodeBuffer.decodeBoolValue(relativeCoordMode);
+ outputMessage[1] = (unsigned char) relativeCoordMode;
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ unsigned char *nextDest = outputMessage + 12;
+
+ unsigned int index = 0;
+ unsigned int lastX = 0, lastY = 0;
+
+ for (unsigned int i = 0; i < numPoints; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyLineCacheX[index], 8);
+ lastX += value;
+ PutUINT(lastX, nextDest, bigEndian_);
+ nextDest += 2;
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyLineCacheY[index], 8);
+ lastY += value;
+ PutUINT(lastY, nextDest, bigEndian_);
+ nextDest += 2;
+
+ if (++index == 2) index = 0;
+ }
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_PolyRectangle:
+ {
+ unsigned int numRectangles;
+ decodeBuffer.decodeValue(numRectangles, 16, 3);
+ outputLength = (numRectangles << 3) + 12;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ unsigned char *nextDest = outputMessage + 12;
+ for (unsigned int i = 0; i < numRectangles; i++)
+ for (unsigned int k = 0; k < 4; k++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyRectangleGeomCache[k], 8);
+ PutUINT(value, nextDest, bigEndian_);
+ nextDest += 2;
+ }
+ }
+ break;
+ case X_PolySegment:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolySegment);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ unsigned int numSegments;
+ decodeBuffer.decodeValue(numSegments, 16, 4);
+ outputLength = (numSegments << 3) + 12;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ unsigned char *nextDest = outputMessage + 12;
+
+ for (numSegments *= 2; numSegments; numSegments--)
+ {
+ unsigned int index;
+ decodeBuffer.decodeBoolValue(index);
+ unsigned int x;
+ decodeBuffer.decodeCachedValue(x, 16,
+ clientCache_ -> polySegmentCacheX, 6);
+ x += clientCache_ -> polySegmentLastX[index];
+ PutUINT(x, nextDest, bigEndian_);
+ nextDest += 2;
+
+ unsigned int y;
+ decodeBuffer.decodeCachedValue(y, 16,
+ clientCache_ -> polySegmentCacheY, 6);
+ y += clientCache_ -> polySegmentLastY[index];
+ PutUINT(y, nextDest, bigEndian_);
+ nextDest += 2;
+
+ clientCache_ -> polySegmentLastX[clientCache_ -> polySegmentCacheIndex] = x;
+ clientCache_ -> polySegmentLastY[clientCache_ -> polySegmentCacheIndex] = y;
+
+ if (clientCache_ -> polySegmentCacheIndex == 1)
+ clientCache_ -> polySegmentCacheIndex = 0;
+ else
+ clientCache_ -> polySegmentCacheIndex = 1;
+ }
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_PutImage:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PutImage);
+
+ hit = handleDecode(decodeBuffer, clientCache_, messageStore,
+ outputOpcode, outputMessage, outputLength);
+
+ if (outputOpcode == X_PutImage)
+ {
+ handleImage(outputOpcode, outputMessage, outputLength);
+ }
+ }
+ break;
+ case X_QueryBestSize:
+ {
+ outputLength = 12;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeValue(value, 2);
+ outputMessage[1] = (unsigned char)value;
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeValue(value, 16, 8);
+ PutUINT(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeValue(value, 16, 8);
+ PutUINT(value, outputMessage + 10, bigEndian_);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ break;
+ case X_QueryColors:
+ {
+ // Differential or plain data compression?
+ decodeBuffer.decodeBoolValue(value);
+
+ if (value)
+ {
+ unsigned int numColors;
+ decodeBuffer.decodeValue(numColors, 16, 5);
+ outputLength = (numColors << 2) + 8;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache_ -> colormapCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ unsigned char *nextDest = outputMessage + 8;
+ unsigned int predictedPixel = clientCache_ -> queryColorsLastPixel;
+ for (unsigned int i = 0; i < numColors; i++)
+ {
+ unsigned int pixel;
+ decodeBuffer.decodeBoolValue(value);
+ if (value)
+ pixel = predictedPixel;
+ else
+ decodeBuffer.decodeValue(pixel, 32, 9);
+ PutULONG(pixel, nextDest, bigEndian_);
+ if (i == 0)
+ clientCache_ -> queryColorsLastPixel = pixel;
+ predictedPixel = pixel + 1;
+ nextDest += 4;
+ }
+ }
+ else
+ {
+ // Request length.
+ unsigned int requestLength;
+ decodeBuffer.decodeValue(requestLength, 16, 10);
+ outputLength = (requestLength << 2);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+
+ const unsigned char *compressedData = NULL;
+ unsigned int compressedDataSize = 0;
+
+ int decompressed = handleDecompress(decodeBuffer, outputOpcode, 4,
+ outputMessage, outputLength, compressedData,
+ compressedDataSize);
+ if (decompressed < 0)
+ {
+ return -1;
+ }
+ }
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ break;
+ case X_QueryExtension:
+ {
+ unsigned int nameLength;
+ decodeBuffer.decodeValue(nameLength, 16, 6);
+ outputLength = 8 + RoundUp4(nameLength);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ PutUINT(nameLength, outputMessage + 4, bigEndian_);
+ unsigned char *nextDest = outputMessage + 8;
+ for (unsigned int i = 0; i < nameLength; i++)
+ {
+ decodeBuffer.decodeValue(value, 8);
+ *nextDest++ = (unsigned char) value;
+ }
+
+ unsigned int hide = 0;
+
+ #ifdef HIDE_MIT_SHM_EXTENSION
+
+ if (!strncmp((char *) outputMessage + 8, "MIT-SHM", 7))
+ {
+ #ifdef TEST
+ *logofs << "handleWrite: Going to hide MIT-SHM extension in reply.\n"
+ << logofs_flush;
+ #endif
+
+ hide = 1;
+ }
+
+ #endif
+
+ #ifdef HIDE_BIG_REQUESTS_EXTENSION
+
+ if (!strncmp((char *) outputMessage + 8, "BIG-REQUESTS", 12))
+ {
+ #ifdef TEST
+ *logofs << "handleWrite: Going to hide BIG-REQUESTS extension in reply.\n"
+ << logofs_flush;
+ #endif
+
+ hide = 1;
+ }
+
+ #endif
+
+ #ifdef HIDE_XKEYBOARD_EXTENSION
+
+ else if (!strncmp((char *) outputMessage + 8, "XKEYBOARD", 9))
+ {
+ #ifdef TEST
+ *logofs << "handleWrite: Going to hide XKEYBOARD extension in reply.\n"
+ << logofs_flush;
+ #endif
+
+ hide = 1;
+ }
+
+ #endif
+
+ #ifdef HIDE_XFree86_Bigfont_EXTENSION
+
+ else if (!strncmp((char *) outputMessage + 8, "XFree86-Bigfont", 15))
+ {
+ #ifdef TEST
+ *logofs << "handleWrite: Going to hide XFree86-Bigfont extension in reply.\n"
+ << logofs_flush;
+ #endif
+
+ hide = 1;
+ }
+
+ #endif
+
+ //
+ // This is if you want to experiment disabling SHAPE extensions.
+ //
+
+ #ifdef HIDE_SHAPE_EXTENSION
+
+ if (!strncmp((char *) outputMessage + 8, "SHAPE", 5))
+ {
+ #ifdef DEBUG
+ *logofs << "handleWrite: Going to hide SHAPE extension in reply.\n"
+ << logofs_flush;
+ #endif
+
+ hide = 1;
+ }
+
+ #endif
+
+ //
+ // Check if user disabled RENDER extension.
+ //
+
+ if (control -> HideRender == 1 &&
+ strncmp((char *) outputMessage + 8, "RENDER", 6) == 0)
+ {
+ #ifdef TEST
+ *logofs << "handleWrite: Going to hide RENDER extension in reply.\n"
+ << logofs_flush;
+ #endif
+
+ hide = 1;
+ }
+
+ unsigned int extension = 0;
+
+ if (strncmp((char *) outputMessage + 8, "SHAPE", 5) == 0)
+ {
+ extension = X_NXInternalShapeExtension;
+ }
+ else if (strncmp((char *) outputMessage + 8, "RENDER", 6) == 0)
+ {
+ extension = X_NXInternalRenderExtension;
+ }
+
+ sequenceQueue_.push(clientSequence_, outputOpcode,
+ outputOpcode, hide, extension);
+ }
+ break;
+ case X_QueryFont:
+ {
+ outputLength = 8;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeValue(value, 29, 5);
+ clientCache_ -> lastFont += value;
+ clientCache_ -> lastFont &= 0x1fffffff;
+ PutULONG(clientCache_ -> lastFont, outputMessage + 4, bigEndian_);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ break;
+ case X_SetClipRectangles:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_SetClipRectangles);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ unsigned int numRectangles;
+
+ if (control -> isProtoStep9() == 1)
+ {
+ decodeBuffer.decodeValue(numRectangles, 15, 4);
+ }
+ else
+ {
+ decodeBuffer.decodeValue(numRectangles, 13, 4);
+ }
+
+ outputLength = (numRectangles << 3) + 12;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeValue(value, 2);
+ outputMessage[1] = (unsigned char) value;
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> setClipRectanglesXCache, 8);
+ PutUINT(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> setClipRectanglesYCache, 8);
+ PutUINT(value, outputMessage + 10, bigEndian_);
+ unsigned char *nextDest = outputMessage + 12;
+ for (unsigned int i = 0; i < numRectangles; i++)
+ {
+ for (unsigned int k = 0; k < 4; k++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> setClipRectanglesGeomCache[k], 8);
+ PutUINT(value, nextDest, bigEndian_);
+ nextDest += 2;
+ }
+ }
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_SetDashes:
+ {
+ unsigned int numDashes;
+ decodeBuffer.decodeCachedValue(numDashes, 16,
+ clientCache_ -> setDashesLengthCache, 5);
+ outputLength = 12 + RoundUp4(numDashes);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ PutUINT(numDashes, outputMessage + 10, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> setDashesOffsetCache, 5);
+ PutUINT(value, outputMessage + 8, bigEndian_);
+ unsigned char *nextDest = outputMessage + 12;
+ for (unsigned int i = 0; i < numDashes; i++)
+ {
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache_ -> setDashesDashCache_[i & 1], 5);
+ *nextDest++ = cValue;
+ }
+ }
+ break;
+ case X_SetSelectionOwner:
+ {
+ outputLength = 16;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache_ -> setSelectionOwnerCache, 9);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache_ -> getSelectionOwnerSelectionCache, 9);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 32,
+ clientCache_ -> setSelectionOwnerTimestampCache, 9);
+ PutULONG(value, outputMessage + 12, bigEndian_);
+ }
+ break;
+ case X_TranslateCoords:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_TranslateCoords);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+
+ break;
+ }
+
+ outputLength = 16;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache_ -> translateCoordsSrcCache, 9);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache_ -> translateCoordsDstCache, 9);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> translateCoordsXCache, 8);
+ PutUINT(value, outputMessage + 12, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> translateCoordsYCache, 8);
+ PutUINT(value, outputMessage + 14, bigEndian_);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_GetImage:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_GetImage);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+
+ break;
+ }
+
+ outputLength = 20;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ // Format.
+ unsigned int format;
+ decodeBuffer.decodeValue(format, 2);
+ outputMessage[1] = (unsigned char) format;
+ // Drawable.
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ // X.
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> putImageXCache, 8);
+ clientCache_ -> putImageLastX += value;
+ clientCache_ -> putImageLastX &= 0xffff;
+ PutUINT(clientCache_ -> putImageLastX, outputMessage + 8, bigEndian_);
+ // Y.
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> putImageYCache, 8);
+ clientCache_ -> putImageLastY += value;
+ clientCache_ -> putImageLastY &= 0xffff;
+ PutUINT(clientCache_ -> putImageLastY, outputMessage + 10, bigEndian_);
+ // Width.
+ unsigned int width;
+ decodeBuffer.decodeCachedValue(width, 16,
+ clientCache_ -> putImageWidthCache, 8);
+ PutUINT(width, outputMessage + 12, bigEndian_);
+ // Height.
+ unsigned int height;
+ decodeBuffer.decodeCachedValue(height, 16,
+ clientCache_ -> putImageHeightCache, 8);
+ PutUINT(height, outputMessage + 14, bigEndian_);
+ // Plane mask.
+ decodeBuffer.decodeCachedValue(value, 32,
+ clientCache_ -> getImagePlaneMaskCache, 5);
+ PutULONG(value, outputMessage + 16, bigEndian_);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_GetPointerMapping:
+ {
+ outputLength = 4;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ break;
+ case X_GetKeyboardControl:
+ {
+ outputLength = 4;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ break;
+ default:
+ {
+ if (outputOpcode == opcodeStore_ -> renderExtension)
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_NXInternalRenderExtension);
+
+ hit = handleDecode(decodeBuffer, clientCache_, messageStore,
+ outputOpcode, outputMessage, outputLength);
+ }
+ else if (outputOpcode == opcodeStore_ -> shapeExtension)
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_NXInternalShapeExtension);
+
+ hit = handleDecode(decodeBuffer, clientCache_, messageStore,
+ outputOpcode, outputMessage, outputLength);
+ }
+ else if (outputOpcode == opcodeStore_ -> putPackedImage)
+ {
+ #ifdef DEBUG
+ *logofs << "handleWrite: Decoding packed image request for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_NXPutPackedImage);
+
+ hit = handleDecode(decodeBuffer, clientCache_, messageStore,
+ outputOpcode, outputMessage, outputLength);
+
+ if (outputOpcode == opcodeStore_ -> putPackedImage)
+ {
+ handleImage(outputOpcode, outputMessage, outputLength);
+ }
+ }
+ else if (outputOpcode == opcodeStore_ -> setUnpackColormap)
+ {
+ #ifdef DEBUG
+ *logofs << "handleWrite: Decoding set unpack colormap request "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_NXSetUnpackColormap);
+
+ hit = handleDecode(decodeBuffer, clientCache_, messageStore,
+ outputOpcode, outputMessage, outputLength);
+ //
+ // Message could have been split.
+ //
+
+ if (outputOpcode == opcodeStore_ -> setUnpackColormap)
+ {
+ handleColormap(outputOpcode, outputMessage, outputLength);
+ }
+ }
+ else if (outputOpcode == opcodeStore_ -> setUnpackAlpha)
+ {
+ #ifdef DEBUG
+ *logofs << "handleWrite: Decoding unpack alpha request for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_NXSetUnpackAlpha);
+
+ hit = handleDecode(decodeBuffer, clientCache_, messageStore,
+ outputOpcode, outputMessage, outputLength);
+ //
+ // Message could have been split.
+ //
+
+ if (outputOpcode == opcodeStore_ -> setUnpackAlpha)
+ {
+ handleAlpha(outputOpcode, outputMessage, outputLength);
+ }
+ }
+ else if (outputOpcode == opcodeStore_ -> setUnpackGeometry)
+ {
+ #ifdef DEBUG
+ *logofs << "handleWrite: Decoding set unpack geometry request "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_NXSetUnpackGeometry);
+
+ hit = handleDecode(decodeBuffer, clientCache_, messageStore,
+ outputOpcode, outputMessage, outputLength);
+
+ handleGeometry(outputOpcode, outputMessage, outputLength);
+ }
+ else if (outputOpcode == opcodeStore_ -> startSplit)
+ {
+ handleStartSplitRequest(decodeBuffer, outputOpcode,
+ outputMessage, outputLength);
+ }
+ else if (outputOpcode == opcodeStore_ -> endSplit)
+ {
+ handleEndSplitRequest(decodeBuffer, outputOpcode,
+ outputMessage, outputLength);
+ }
+ else if (outputOpcode == opcodeStore_ -> commitSplit)
+ {
+ int result = handleCommitSplitRequest(decodeBuffer, outputOpcode,
+ outputMessage, outputLength);
+
+ //
+ // Check if message has been successfully
+ // extracted from the split store. In this
+ // case post-process it in the usual way.
+ //
+
+ if (result > 0)
+ {
+ if (outputOpcode == opcodeStore_ -> putPackedImage ||
+ outputOpcode == X_PutImage)
+ {
+ handleImage(outputOpcode, outputMessage, outputLength);
+ }
+ else if (outputOpcode == opcodeStore_ -> setUnpackColormap)
+ {
+ handleColormap(outputOpcode, outputMessage, outputLength);
+ }
+ else if (outputOpcode == opcodeStore_ -> setUnpackAlpha)
+ {
+ handleAlpha(outputOpcode, outputMessage, outputLength);
+ }
+ }
+ else if (result < 0)
+ {
+ return -1;
+ }
+ }
+ else if (outputOpcode == opcodeStore_ -> abortSplit)
+ {
+ handleAbortSplitRequest(decodeBuffer, outputOpcode,
+ outputMessage, outputLength);
+ }
+ else if (outputOpcode == opcodeStore_ -> finishSplit)
+ {
+ #ifdef DEBUG
+ *logofs << "handleWrite: Decoding finish split request "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache_ -> resourceCache);
+
+ handleNullRequest(outputOpcode, outputMessage, outputLength);
+ }
+ else if (outputOpcode == opcodeStore_ -> freeSplit)
+ {
+ #ifdef DEBUG
+ *logofs << "handleWrite: Decoding free split request "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache_ -> resourceCache);
+
+ handleNullRequest(outputOpcode, outputMessage, outputLength);
+ }
+ else if (outputOpcode == opcodeStore_ -> freeUnpack)
+ {
+ #ifdef DEBUG
+ *logofs << "handleWrite: Decoding free unpack request "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache_ -> resourceCache);
+
+ #ifdef DEBUG
+ *logofs << "handleWrite: Freeing unpack state for resource "
+ << (unsigned int) cValue << ".\n" << logofs_flush;
+ #endif
+
+ handleUnpackStateRemove(cValue);
+
+ handleNullRequest(outputOpcode, outputMessage, outputLength);
+ }
+ else if (outputOpcode == opcodeStore_ -> setExposeParameters)
+ {
+ //
+ // Send expose events according to agent's wish.
+ //
+
+ decodeBuffer.decodeBoolValue(enableExpose_);
+ decodeBuffer.decodeBoolValue(enableGraphicsExpose_);
+ decodeBuffer.decodeBoolValue(enableNoExpose_);
+
+ handleNullRequest(outputOpcode, outputMessage, outputLength);
+ }
+ else if (outputOpcode == opcodeStore_ -> getUnpackParameters)
+ {
+ //
+ // Client proxy needs the list of supported
+ // unpack methods. We would need an encode
+ // buffer, but this is in proxy, not here in
+ // channel.
+ //
+
+ #ifdef TEST
+ *logofs << "handleWrite: Sending X_GetInputFocus request for FD#"
+ << fd_ << " due to OPCODE#" << (unsigned int) outputOpcode
+ << ".\n" << logofs_flush;
+ #endif
+
+ outputOpcode = X_GetInputFocus;
+
+ outputLength = 4;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode,
+ opcodeStore_ -> getUnpackParameters);
+ }
+ else if (outputOpcode == opcodeStore_ -> getControlParameters ||
+ outputOpcode == opcodeStore_ -> getCleanupParameters ||
+ outputOpcode == opcodeStore_ -> getImageParameters)
+ {
+ handleNullRequest(outputOpcode, outputMessage, outputLength);
+ }
+ else if (outputOpcode == opcodeStore_ -> getShmemParameters)
+ {
+ if (handleShmemRequest(decodeBuffer, outputOpcode,
+ outputMessage, outputLength) < 0)
+ {
+ return -1;
+ }
+ }
+ else if (outputOpcode == opcodeStore_ -> setCacheParameters)
+ {
+ if (handleCacheRequest(decodeBuffer, outputOpcode,
+ outputMessage, outputLength) < 0)
+ {
+ return -1;
+ }
+ }
+ else if (outputOpcode == opcodeStore_ -> getFontParameters)
+ {
+ if (handleFontRequest(decodeBuffer, outputOpcode,
+ outputMessage, outputLength) < 0)
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_NXInternalGenericRequest);
+
+ hit = handleDecode(decodeBuffer, clientCache_, messageStore,
+ outputOpcode, outputMessage, outputLength);
+ }
+ }
+ } // End of switch on opcode.
+
+ //
+ // A packed image request can generate more than just
+ // a single X_PutImage. Write buffer is handled inside
+ // handleUnpack(). Cannot simply assume that the final
+ // opcode and size must be put at the buffer offset as
+ // as buffer could have been grown or could have been
+ // replaced by a scratch buffer. The same is true in
+ // the case of a shared memory image.
+ //
+
+ if (outputOpcode != 0)
+ {
+ //
+ // Commit opcode and size to the buffer.
+ //
+
+ *outputMessage = (unsigned char) outputOpcode;
+
+ PutUINT(outputLength >> 2, outputMessage + 2, bigEndian_);
+
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << "handleWrite: Handled request OPCODE#"
+ << (unsigned int) outputOpcode << " ("
+ << DumpOpcode(outputOpcode) << ") for FD#"
+ << fd_ << " sequence " << clientSequence_
+ << ". " << outputLength << " bytes out.\n"
+ << logofs_flush;
+ #endif
+ }
+ #if defined(TEST) || defined(OPCODES)
+ else
+ {
+ //
+ // In case of shared memory images the log doesn't
+ // reflect the actual opcode of the request that is
+ // going to be written. It would be possible to find
+ // the opcode of the original request received from
+ // the remote proxy in member imageState_ -> opcode,
+ // but we have probably already deleted the struct.
+ //
+
+ *logofs << "handleWrite: Handled image request for FD#"
+ << fd_ << " new sequence " << clientSequence_
+ << ". " << outputLength << " bytes out.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ //
+ // Check if we produced enough data. We need to
+ // decode all the proxy messages or the decode
+ // buffer will be left in an inconsistent state,
+ // so we just update the finish flag in case of
+ // failure.
+ //
+
+ handleFlush(flush_if_needed);
+
+ } // End of while (decodeBuffer.decodeOpcodeValue(outputOpcode, 8, ...
+
+ } // End of the decoding block.
+
+ //
+ // Write any remaining data to the X connection.
+ //
+
+ if (handleFlush(flush_if_any) < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Reset offset at which we read the
+ // last event looking for the shared
+ // memory completion.
+ //
+
+ if (shmemState_ != NULL)
+ {
+ shmemState_ -> checked = 0;
+ }
+
+ return 1;
+}
+
+//
+// End of handleWrite().
+//
+
+//
+// Other members.
+//
+
+int ServerChannel::handleSplit(DecodeBuffer &decodeBuffer, MessageStore *store,
+ T_store_action action, int position, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+{
+ if (control -> isProtoStep7() == 1)
+ {
+ splitState_.current = splitState_.resource;
+ }
+
+ handleSplitStoreAlloc(&splitResources_, splitState_.current);
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Message OPCODE#"
+ << (unsigned int) store -> opcode() << " of size " << size
+ << " [split] with resource " << splitState_.current
+ << " position " << position << " and action ["
+ << DumpAction(action) << "] at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Get the MD5 of the message being
+ // split.
+ //
+
+ T_checksum checksum = NULL;
+
+ if (action != IS_HIT)
+ {
+ handleSplitChecksum(decodeBuffer, checksum);
+ }
+
+ //
+ // The method must abort the connection
+ // if it can't allocate the split.
+ //
+
+ Split *splitMessage = clientStore_ -> getSplitStore(splitState_.current) ->
+ add(store, splitState_.current, position,
+ action, checksum, buffer, size);
+
+ //
+ // If we are connected to an old proxy
+ // version or the encoding side didn't
+ // provide a checksum, then don't send
+ // the split report.
+ //
+
+ if (control -> isProtoStep7() == 0 ||
+ checksum == NULL)
+ {
+ if (action == IS_HIT)
+ {
+ splitMessage -> setState(split_loaded);
+ }
+ else
+ {
+ splitMessage -> setState(split_missed);
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+
+ *logofs << "handleSplit: SPLIT! There are " << clientStore_ ->
+ getSplitTotalSize() << " messages and " << clientStore_ ->
+ getSplitTotalStorageSize() << " bytes to send in "
+ << "the split stores.\n" << logofs_flush;
+
+ clientStore_ -> dumpSplitStore(splitState_.current);
+
+ #endif
+
+ delete [] checksum;
+
+ return 1;
+ }
+
+ delete [] checksum;
+
+ //
+ // Tell the split store if it must use
+ // the disk cache to retrieve and save
+ // the message.
+ //
+
+ splitMessage -> setPolicy(splitState_.load, splitState_.save);
+
+ //
+ // Try to locate the message on disk.
+ //
+
+ if (clientStore_ -> getSplitStore(splitState_.current) ->
+ load(splitMessage) == 1)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Loaded the message "
+ << "from the image cache.\n" << logofs_flush;
+ #endif
+
+ splitMessage -> setState(split_loaded);
+ }
+ else
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: WARNING! SPLIT! Can't find the message "
+ << "in the image cache.\n" << logofs_flush;
+ #endif
+
+ splitMessage -> setState(split_missed);
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+
+ T_timestamp startTs = getTimestamp();
+
+ *logofs << "handleSplit: SPLIT! Encoding abort "
+ << "split events for FD#" << fd_ << " at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ if (proxy -> handleAsyncSplit(fd_, splitMessage) < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Send the encoded data immediately. We
+ // want the abort split message to reach
+ // the remote proxy as soon as possible.
+ //
+
+ if (proxy -> handleAsyncFlush() < 0)
+ {
+ return -1;
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+
+ *logofs << "handleSplit: SPLIT! Spent "
+ << diffTimestamp(startTs, getTimestamp()) << " Ms "
+ << "handling abort split events for FD#" << fd_
+ << ".\n" << logofs_flush;
+
+ *logofs << "handleSplit: SPLIT! There are " << clientStore_ ->
+ getSplitTotalSize() << " messages and " << clientStore_ ->
+ getSplitTotalStorageSize() << " bytes to send in "
+ << "the split stores.\n" << logofs_flush;
+
+ clientStore_ -> dumpSplitStore(splitState_.current);
+
+ #endif
+
+ return 1;
+}
+
+int ServerChannel::handleSplit(DecodeBuffer &decodeBuffer)
+{
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Going to handle splits "
+ << "for FD#" << fd_ << " at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ #endif
+
+ unsigned char resource;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ decodeBuffer.decodeCachedValue(resource, 8,
+ clientCache_ -> resourceCache);
+
+ splitState_.current = resource;
+ }
+
+ handleSplitStoreAlloc(&splitResources_, splitState_.current);
+
+ SplitStore *splitStore = clientStore_ -> getSplitStore(splitState_.current);
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Handling splits for "
+ << "resource [" << splitState_.current << "] with "
+ << splitStore -> getSize() << " elements "
+ << "in the split store.\n" << logofs_flush;
+ #endif
+
+ int result = splitStore -> receive(decodeBuffer);
+
+ if (result < 0)
+ {
+ #ifdef PANIC
+ *logofs << "handleSplit: PANIC! Receive of split for FD#" << fd_
+ << " failed.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Receive of split for FD#" << fd_
+ << " failed.\n";
+
+ return -1;
+ }
+ else if (result == 0)
+ {
+ //
+ // The split is still incomplete. It's time
+ // to check if we need to start the house-
+ // keeping process to take care of the image
+ // cache.
+ //
+
+ if (proxy -> handleAsyncKeeperCallback() < 0)
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ //
+ // Note that we don't need the resource id at the
+ // X server side and, thus, we don't provide it
+ // at the time we add split to the split store.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Remote agent should "
+ << "now commit a new split for resource ["
+ << splitState_.current << "].\n"
+ << logofs_flush;
+
+ clientStore_ -> dumpCommitStore();
+
+ #endif
+
+ if (splitStore -> getSize() == 0)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Removing split store "
+ << "for resource [" << splitState_.current
+ << "] at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ handleSplitStoreRemove(&splitResources_, splitState_.current);
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! There are [" << clientStore_ ->
+ getSplitTotalSize() << "] messages and " << clientStore_ ->
+ getSplitTotalStorageSize() << " bytes to send in "
+ << "the split stores.\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ //
+ // If the next split is discarded, it can be
+ // that, since the beginning of the split, we
+ // have saved the message on the disk, due to
+ // a more recent split operation. This is also
+ // the case when we had to discard the message
+ // because it was locked but, since then, we
+ // completed the transferral of the split.
+ //
+
+ Split *splitMessage = splitStore -> getFirstSplit();
+
+ if (splitMessage -> getAction() == is_discarded &&
+ splitMessage -> getState() == split_missed &&
+ splitStore -> load(splitMessage) == 1)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: WARNING! SPLIT! Asynchronously "
+ << "loaded the message from the image cache.\n"
+ << logofs_flush;
+ #endif
+
+ splitMessage -> setState(split_loaded);
+
+ #if defined(TEST) || defined(SPLIT)
+
+ T_timestamp startTs = getTimestamp();
+
+ *logofs << "handleSplit: WARNING! SPLIT! Asynchronously "
+ << "encoding abort split events for FD#" << fd_
+ << " at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (proxy -> handleAsyncSplit(fd_, splitMessage) < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Send the encoded data immediately. We
+ // want the abort split message to reach
+ // the remote proxy as soon as possible.
+ //
+
+ if (proxy -> handleAsyncFlush() < 0)
+ {
+ return -1;
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+
+ *logofs << "handleSplit: WARNING! SPLIT! Spent "
+ << diffTimestamp(startTs, getTimestamp()) << " Ms "
+ << "handling asynchronous abort split events for "
+ << "FD#" << fd_ << ".\n" << logofs_flush;
+
+ *logofs << "handleSplit: SPLIT! There are " << clientStore_ ->
+ getSplitTotalSize() << " messages and " << clientStore_ ->
+ getSplitTotalStorageSize() << " bytes to send in "
+ << "the split stores.\n" << logofs_flush;
+
+ clientStore_ -> dumpSplitStore(splitState_.current);
+
+ #endif
+ }
+ }
+ }
+
+ return 1;
+}
+
+int ServerChannel::handleSplitEvent(EncodeBuffer &encodeBuffer, Split *splitMessage)
+{
+ int resource = splitMessage -> getResource();
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitEvent: SPLIT! Going to send a "
+ << "split report for resource " << resource
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // This function is called only after the message
+ // has been searched in the disk cache. We need to
+ // inform the other side if the data transfer can
+ // start or it must be aborted to let the local
+ // side use the copy that was found on the disk.
+ //
+
+ #if defined(TEST) || defined(INFO)
+
+ if (splitMessage -> getState() != split_loaded &&
+ splitMessage -> getState() != split_missed)
+ {
+ *logofs << "handleSplitEvent: PANIC! Can't find the split to be aborted.\n"
+ << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ //
+ // We need to send a boolean telling if the split
+ // was found or not, followed by the checksum of
+ // message we are referencing.
+ //
+
+ T_checksum checksum = splitMessage -> getChecksum();
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitEvent: SPLIT! Sending split report for "
+ << "checksum [" << DumpChecksum(checksum) << "].\n"
+ << logofs_flush;
+ #endif
+
+ if (proxy -> handleAsyncSwitch(fd_) < 0)
+ {
+ return -1;
+ }
+
+ encodeBuffer.encodeOpcodeValue(opcodeStore_ -> splitEvent,
+ serverCache_ -> opcodeCache);
+
+ //
+ // The encoding in older protocol versions
+ // is different but we will never try to
+ // send a split report if the remote does
+ // not support our version.
+ //
+
+ encodeBuffer.encodeCachedValue(resource, 8,
+ serverCache_ -> resourceCache);
+
+ if (splitMessage -> getState() == split_loaded)
+ {
+ encodeBuffer.encodeBoolValue(1);
+
+ encodeBuffer.encodeOpcodeValue(splitMessage -> getStore() -> opcode(),
+ serverCache_ -> abortOpcodeCache);
+
+ encodeBuffer.encodeValue(splitMessage -> compressedSize(), 32, 14);
+ }
+ else
+ {
+ encodeBuffer.encodeBoolValue(0);
+ }
+
+ for (unsigned int i = 0; i < MD5_LENGTH; i++)
+ {
+ encodeBuffer.encodeValue((unsigned int) checksum[i], 8);
+ }
+
+ //
+ // Update statistics for this special opcode.
+ //
+
+ int bits = encodeBuffer.diffBits();
+
+ #if defined(TEST) || defined(OPCODES) || defined(INFO) || defined(SPLIT)
+ *logofs << "handleSplitEvent: SPLIT! Handled event OPCODE#"
+ << (unsigned int) opcodeStore_ -> splitEvent << " ("
+ << DumpOpcode(opcodeStore_ -> splitEvent) << ")" << " for FD#"
+ << fd_ << " sequence none. 0 bytes in, " << bits << " bits ("
+ << ((float) bits) / 8 << " bytes) out.\n" << logofs_flush;
+ #endif
+
+ statistics -> addEventBits(opcodeStore_ -> splitEvent, 0, bits);
+
+ return 1;
+}
+
+int ServerChannel::handleAbortSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+{
+ unsigned char resource;
+
+ decodeBuffer.decodeCachedValue(resource, 8,
+ clientCache_ -> resourceCache);
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleAbortSplitRequest: SPLIT! Handling abort split "
+ << "request for FD#" << fd_ << " and resource "
+ << (unsigned) resource << ".\n"
+ << logofs_flush;
+ #endif
+
+ int splits = 0;
+
+ SplitStore *splitStore = clientStore_ -> getSplitStore(resource);
+
+ if (splitStore != NULL)
+ {
+ //
+ // Discard from the memory cache the messages
+ // that are still incomplete and then get rid
+ // of the splits in the store.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+
+ clientStore_ -> dumpSplitStore(resource);
+
+ #endif
+
+ Split *splitMessage;
+
+ for (;;)
+ {
+ splitMessage = splitStore -> getFirstSplit();
+
+ if (splitMessage == NULL)
+ {
+ //
+ // Check if we had created the store
+ // but no message was added yet.
+ //
+
+ #ifdef WARNING
+
+ if (splits == 0)
+ {
+ *logofs << "handleAbortSplitRequest: WARNING! SPLIT! The "
+ << "split store for resource [" << (unsigned int)
+ resource << "] is unexpectedly empty.\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ break;
+ }
+
+ //
+ // Splits already aborted can't be in the
+ // split store.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+
+ if (splitMessage -> getState() == split_aborted)
+ {
+ *logofs << "handleAbortSplitRequest: PANIC! SPLIT! Found an "
+ << "aborted split in store [" << (unsigned int) resource
+ << "].\n" << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ if (splitMessage -> getAction() == IS_HIT)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleAbortSplitRequest: SPLIT! Removing the "
+ << "split from the memory cache.\n"
+ << logofs_flush;
+ #endif
+
+ splitMessage -> getStore() -> remove(splitMessage -> getPosition(),
+ discard_checksum, use_data);
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleAbortSplitRequest: SPLIT! Removing the "
+ << "split from the split store.\n"
+ << logofs_flush;
+ #endif
+
+ splitMessage = splitStore -> pop();
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleAbortSplitRequest: SPLIT! Freeing up the "
+ << "aborted split.\n" << logofs_flush;
+ #endif
+
+ delete splitMessage;
+
+ splits++;
+ }
+ }
+ #ifdef WARNING
+ else
+ {
+ *logofs << "handleAbortSplitRequest: WARNING! SPLIT! The "
+ << "split store for resource [" << (unsigned int)
+ resource << "] is already empty.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ handleNullRequest(opcode, buffer, size);
+
+ return (splits > 0);
+}
+
+int ServerChannel::handleCommitSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+{
+ //
+ // Get request type and position of the image
+ // to commit.
+ //
+
+ unsigned char request;
+
+ decodeBuffer.decodeOpcodeValue(request, clientCache_ -> opcodeCache);
+
+ unsigned int diffCommit;
+
+ decodeBuffer.decodeValue(diffCommit, 32, 5);
+
+ splitState_.commit += diffCommit;
+
+ unsigned char resource = 0;
+ unsigned int commit = 1;
+
+ //
+ // Send the resource id and the commit flag.
+ // The resource id is ignored at the moment.
+ // The message will be handled based on the
+ // resource id that was sent together with
+ // the original message.
+ //
+
+ decodeBuffer.decodeCachedValue(resource, 8,
+ clientCache_ -> resourceCache);
+
+ decodeBuffer.decodeBoolValue(commit);
+
+ Split *split = handleSplitCommitRemove(request, resource, splitState_.commit);
+
+ if (split == NULL)
+ {
+ return -1;
+ }
+
+ clientStore_ -> getCommitStore() -> update(split);
+
+ if (commit == 1)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleCommitSplitRequest: SPLIT! Handling split commit "
+ << "for FD#" << fd_ << " with commit " << commit
+ << " request " << (unsigned) request << " resource "
+ << (unsigned) resource << " and position "
+ << splitState_.commit << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Allocate as many bytes in the write
+ // buffer as the final length of the
+ // message in uncompressed form.
+ //
+
+ size = split -> plainSize();
+
+ buffer = writeBuffer_.addMessage(size);
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleCommitSplitRequest: SPLIT! Prepared an "
+ << "outgoing buffer of " << size << " bytes.\n"
+ << logofs_flush;
+ #endif
+
+ if (clientStore_ -> getCommitStore() -> expand(split, buffer, size) < 0)
+ {
+ writeBuffer_.removeMessage(size);
+
+ commit = 0;
+ }
+ }
+
+ //
+ // Free the split.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleCommitSplitRequest: SPLIT! Freeing up the "
+ << "committed split.\n" << logofs_flush;
+ #endif
+
+ delete split;
+
+ //
+ // Discard the operation and send a null
+ // message.
+ //
+
+ if (commit == 0)
+ {
+ handleNullRequest(opcode, buffer, size);
+ }
+ else
+ {
+ //
+ // Save the sequence number to be able
+ // to mask any error generated by the
+ // request.
+ //
+
+ updateCommitQueue(clientSequence_);
+
+ //
+ // Now in the write buffer there is
+ // a copy of this request.
+ //
+
+ opcode = request;
+ }
+
+ return commit;
+}
+
+int ServerChannel::handleGeometry(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size)
+{
+ //
+ // Replace the old geometry and taint
+ // the message into a X_NoOperation.
+ //
+
+ int resource = *(buffer + 1);
+
+ #ifdef TEST
+ *logofs << "handleGeometry: Setting new unpack geometry "
+ << "for resource " << resource << ".\n"
+ << logofs_flush;
+ #endif
+
+ handleUnpackStateInit(resource);
+
+ handleUnpackAllocGeometry(resource);
+
+ unpackState_[resource] -> geometry -> depth1_bpp = *(buffer + 4);
+ unpackState_[resource] -> geometry -> depth4_bpp = *(buffer + 5);
+ unpackState_[resource] -> geometry -> depth8_bpp = *(buffer + 6);
+ unpackState_[resource] -> geometry -> depth16_bpp = *(buffer + 7);
+ unpackState_[resource] -> geometry -> depth24_bpp = *(buffer + 8);
+ unpackState_[resource] -> geometry -> depth32_bpp = *(buffer + 9);
+
+ unpackState_[resource] -> geometry -> red_mask = GetULONG(buffer + 12, bigEndian_);
+ unpackState_[resource] -> geometry -> green_mask = GetULONG(buffer + 16, bigEndian_);
+ unpackState_[resource] -> geometry -> blue_mask = GetULONG(buffer + 20, bigEndian_);
+
+ handleCleanAndNullRequest(opcode, buffer, size);
+
+ return 1;
+}
+
+int ServerChannel::handleColormap(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size)
+{
+ //
+ // Replace the old colormap and taint
+ // the message into a X_NoOperation.
+ //
+
+ int resource = *(buffer + 1);
+
+ #ifdef TEST
+ *logofs << "handleColormap: Setting new unpack colormap "
+ << "for resource " << resource << ".\n"
+ << logofs_flush;
+ #endif
+
+ handleUnpackStateInit(resource);
+
+ handleUnpackAllocColormap(resource);
+
+ //
+ // New protocol versions send the alpha
+ // data in compressed form.
+ //
+
+ if (control -> isProtoStep7() == 1)
+ {
+ unsigned int packed = GetULONG(buffer + 8, bigEndian_);
+ unsigned int unpacked = GetULONG(buffer + 12, bigEndian_);
+
+ validateSize("colormap", packed, unpacked, 16, size);
+
+ if (unpackState_[resource] -> colormap -> entries != unpacked >> 2 &&
+ unpackState_[resource] -> colormap -> data != NULL)
+ {
+ #ifdef TEST
+ *logofs << "handleColormap: Freeing previously allocated "
+ << "unpack colormap data.\n" << logofs_flush;
+ #endif
+
+ delete [] unpackState_[resource] -> colormap -> data;
+
+ unpackState_[resource] -> colormap -> data = NULL;
+ unpackState_[resource] -> colormap -> entries = 0;
+ }
+
+ #ifdef TEST
+ *logofs << "handleColormap: Setting " << unpacked
+ << " bytes of unpack colormap data for resource "
+ << resource << ".\n" << logofs_flush;
+ #endif
+
+ if (unpackState_[resource] -> colormap -> data == NULL)
+ {
+ unpackState_[resource] -> colormap -> data =
+ (unsigned int *) new unsigned char[unpacked];
+
+ if (unpackState_[resource] -> colormap -> data == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "handleColormap: PANIC! Can't allocate "
+ << unpacked << " entries for unpack colormap data "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ goto handleColormapEnd;
+ }
+
+ #ifdef DEBUG
+ *logofs << "handleColormap: Size of new colormap data is "
+ << unpacked << ".\n" << logofs_flush;
+ #endif
+ }
+
+ unsigned int method = *(buffer + 4);
+
+ if (method == PACK_COLORMAP)
+ {
+ if (UnpackColormap(method, buffer + 16, packed,
+ (unsigned char *) unpackState_[resource] ->
+ colormap -> data, unpacked) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "handleColormap: PANIC! Can't unpack " << packed
+ << " bytes to " << unpacked << " entries for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ delete [] unpackState_[resource] -> colormap -> data;
+
+ unpackState_[resource] -> colormap -> data = NULL;
+ unpackState_[resource] -> colormap -> entries = 0;
+
+ goto handleColormapEnd;
+ }
+ }
+ else
+ {
+ memcpy((unsigned char *) unpackState_[resource] ->
+ colormap -> data, buffer + 16, unpacked);
+ }
+
+ unpackState_[resource] -> colormap -> entries = unpacked >> 2;
+
+ #if defined(DEBUG) && defined(DUMP)
+
+ *logofs << "handleColormap: Dumping colormap entries:\n"
+ << logofs_flush;
+
+ const unsigned char *p = unpackState_[resource] -> colormap -> data;
+
+ for (unsigned int i = 0; i < unpackState_[resource] ->
+ colormap -> entries; i++)
+ {
+ *logofs << "handleColormap: [" << i << "] ["
+ << (void *) ((int) p[i]) << "].\n"
+ << logofs_flush;
+ }
+
+ #endif
+ }
+ else
+ {
+ unsigned int entries = GetULONG(buffer + 4, bigEndian_);
+
+ if (size == entries * 4 + 8)
+ {
+ if (unpackState_[resource] -> colormap -> entries != entries &&
+ unpackState_[resource] -> colormap -> data != NULL)
+ {
+ #ifdef TEST
+ *logofs << "handleColormap: Freeing previously "
+ << "allocated unpack colormap.\n"
+ << logofs_flush;
+ #endif
+
+ delete [] unpackState_[resource] -> colormap -> data;
+
+ unpackState_[resource] -> colormap -> data = NULL;
+ unpackState_[resource] -> colormap -> entries = 0;
+ }
+
+ if (entries > 0)
+ {
+ if (unpackState_[resource] -> colormap -> data == NULL)
+ {
+ unpackState_[resource] ->
+ colormap -> data = new unsigned int[entries];
+ }
+
+ if (unpackState_[resource] -> colormap -> data != NULL)
+ {
+ unpackState_[resource] -> colormap -> entries = entries;
+
+ #ifdef DEBUG
+ *logofs << "handleColormap: Size of new colormap "
+ << "data is " << (entries << 2) << ".\n"
+ << logofs_flush;
+ #endif
+
+ memcpy((unsigned char *) unpackState_[resource] ->
+ colormap -> data, buffer + 8, entries << 2);
+
+ #if defined(DEBUG) && defined(DUMP)
+
+ *logofs << "handleColormap: Dumping colormap entries:\n"
+ << logofs_flush;
+
+ const unsigned int *p = (unsigned int *) buffer + 8;
+
+ for (unsigned int i = 0; i < entries; i++)
+ {
+ *logofs << "handleColormap: [" << i << "] ["
+ << (void *) p[i] << "].\n"
+ << logofs_flush;
+ }
+
+ #endif
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "handleColormap: PANIC! Can't allocate "
+ << entries << " entries for unpack colormap "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+ }
+ }
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "handleColormap: PANIC! Bad size " << size
+ << " for set unpack colormap message for FD#"
+ << fd_ << " with " << entries << " entries.\n"
+ << logofs_flush;
+ #endif
+ }
+ }
+
+handleColormapEnd:
+
+ handleCleanAndNullRequest(opcode, buffer, size);
+
+ return 1;
+}
+
+int ServerChannel::handleAlpha(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size)
+{
+ int resource = *(buffer + 1);
+
+ #ifdef TEST
+ *logofs << "handleAlpha: Setting new unpack alpha "
+ << "for resource " << resource << ".\n"
+ << logofs_flush;
+ #endif
+
+ handleUnpackStateInit(resource);
+
+ handleUnpackAllocAlpha(resource);
+
+ //
+ // New protocol versions send the alpha
+ // data in compressed form.
+ //
+
+ if (control -> isProtoStep7() == 1)
+ {
+ unsigned int packed = GetULONG(buffer + 8, bigEndian_);
+ unsigned int unpacked = GetULONG(buffer + 12, bigEndian_);
+
+ validateSize("alpha", packed, unpacked, 16, size);
+
+ if (unpackState_[resource] -> alpha -> entries != unpacked &&
+ unpackState_[resource] -> alpha -> data != NULL)
+ {
+ #ifdef TEST
+ *logofs << "handleAlpha: Freeing previously allocated "
+ << "unpack alpha data.\n" << logofs_flush;
+ #endif
+
+ delete [] unpackState_[resource] -> alpha -> data;
+
+ unpackState_[resource] -> alpha -> data = NULL;
+ unpackState_[resource] -> alpha -> entries = 0;
+ }
+
+ #ifdef TEST
+ *logofs << "handleAlpha: Setting " << unpacked
+ << " bytes of unpack alpha data for resource "
+ << resource << ".\n" << logofs_flush;
+ #endif
+
+ if (unpackState_[resource] -> alpha -> data == NULL)
+ {
+ unpackState_[resource] -> alpha -> data = new unsigned char[unpacked];
+
+ if (unpackState_[resource] -> alpha -> data == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "handleAlpha: PANIC! Can't allocate "
+ << unpacked << " entries for unpack alpha data "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ goto handleAlphaEnd;
+ }
+
+ #ifdef DEBUG
+ *logofs << "handleAlpha: Size of new alpha data is "
+ << unpacked << ".\n" << logofs_flush;
+ #endif
+ }
+
+ unsigned int method = *(buffer + 4);
+
+ if (method == PACK_ALPHA)
+ {
+ if (UnpackAlpha(method, buffer + 16, packed,
+ unpackState_[resource] -> alpha ->
+ data, unpacked) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "handleAlpha: PANIC! Can't unpack " << packed
+ << " bytes to " << unpacked << " entries for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ delete [] unpackState_[resource] -> alpha -> data;
+
+ unpackState_[resource] -> alpha -> data = NULL;
+ unpackState_[resource] -> alpha -> entries = 0;
+
+ goto handleAlphaEnd;
+ }
+ }
+ else
+ {
+ memcpy((unsigned char *) unpackState_[resource] ->
+ alpha -> data, buffer + 16, unpacked);
+ }
+
+ unpackState_[resource] -> alpha -> entries = unpacked;
+
+ #if defined(DEBUG) && defined(DUMP)
+
+ *logofs << "handleAlpha: Dumping alpha entries:\n"
+ << logofs_flush;
+
+ const unsigned char *p = unpackState_[resource] -> alpha -> data;
+
+ for (unsigned int i = 0; i < unpackState_[resource] ->
+ alpha -> entries; i++)
+ {
+ *logofs << "handleAlpha: [" << i << "] ["
+ << (void *) ((int) p[i]) << "].\n"
+ << logofs_flush;
+ }
+
+ #endif
+ }
+ else
+ {
+ unsigned int entries = GetULONG(buffer + 4, bigEndian_);
+
+ if (size == RoundUp4(entries) + 8)
+ {
+ if (unpackState_[resource] -> alpha -> entries != entries &&
+ unpackState_[resource] -> alpha -> data != NULL)
+ {
+ #ifdef TEST
+ *logofs << "handleAlpha: Freeing previously allocated "
+ << "unpack alpha data.\n" << logofs_flush;
+ #endif
+
+ delete [] unpackState_[resource] -> alpha -> data;
+
+ unpackState_[resource] -> alpha -> data = NULL;
+ unpackState_[resource] -> alpha -> entries = 0;
+ }
+
+ if (entries > 0)
+ {
+ if (unpackState_[resource] -> alpha -> data == NULL)
+ {
+ unpackState_[resource] -> alpha -> data = new unsigned char[entries];
+ }
+
+ if (unpackState_[resource] -> alpha -> data != NULL)
+ {
+ unpackState_[resource] -> alpha -> entries = entries;
+
+ #ifdef DEBUG
+ *logofs << "handleAlpha: Size of new alpha data is "
+ << entries << ".\n" << logofs_flush;
+ #endif
+
+ memcpy((unsigned char *) unpackState_[resource] ->
+ alpha -> data, buffer + 8, entries);
+
+ #if defined(DEBUG) && defined(DUMP)
+
+ *logofs << "handleAlpha: Dumping alpha entries:\n"
+ << logofs_flush;
+
+ const unsigned char *p = buffer + 8;
+
+ for (unsigned int i = 0; i < entries; i++)
+ {
+ *logofs << "handleAlpha: [" << i << "] ["
+ << (void *) ((int) p[i]) << "].\n"
+ << logofs_flush;
+ }
+
+ #endif
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "handleAlpha: PANIC! Can't allocate "
+ << entries << " entries for unpack alpha data "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+ }
+ }
+ }
+ #ifdef PANIC
+ else
+ {
+ *logofs << "handleAlpha: PANIC! Bad size " << size
+ << " for set unpack alpha message for FD#"
+ << fd_ << " with " << entries << " entries.\n"
+ << logofs_flush;
+ }
+ #endif
+ }
+
+handleAlphaEnd:
+
+ handleCleanAndNullRequest(opcode, buffer, size);
+
+ return 1;
+}
+
+int ServerChannel::handleImage(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size)
+{
+ int result = 1;
+
+ //
+ // Save the original opcode together with
+ // the image state so we can later find if
+ // this is a plain or a packed image when
+ // moving data to the shared memory area.
+ //
+
+ handleImageStateAlloc(opcode);
+
+ if (opcode == opcodeStore_ -> putPackedImage)
+ {
+ //
+ // Unpack the image and put a X_PutImage in a
+ // new buffer. Save the expected output size,
+ // so, in the case of a decoding error we can
+ // still update the statistics.
+ //
+
+ int length = GetULONG(buffer + 20, bigEndian_);
+
+ #ifdef TEST
+ *logofs << "handleImage: Sending image for FD#" << fd_
+ << " due to OPCODE#" << (unsigned int) opcode << " with "
+ << GetULONG(buffer + 16, bigEndian_) << " bytes packed "
+ << "and " << GetULONG(buffer + 20, bigEndian_)
+ << " bytes unpacked.\n" << logofs_flush;
+ #endif
+
+ statistics -> addPackedBytesIn(size);
+
+ result = handleUnpack(opcode, buffer, size);
+
+ if (result < 0)
+ {
+ //
+ // Recover from the error. Send a X_NoOperation
+ // to keep the sequence counter in sync with
+ // the remote peer.
+ //
+
+ size = 4;
+ buffer = writeBuffer_.addMessage(size);
+
+ *buffer = X_NoOperation;
+
+ PutUINT(size >> 2, buffer + 2, bigEndian_);
+
+ #ifdef PANIC
+ *logofs << "handleImage: PANIC! Sending X_NoOperation for FD#"
+ << fd_ << " to recover from failed unpack.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Set the output length to reflect the amount of
+ // data that would have been produced by unpacking
+ // the image. This is advisable to keep the count-
+ // ers in sync with those at remote proxy. Setting
+ // the size here doesn't have any effect on the
+ // size of data sent to the X server as the actual
+ // size will be taken from the content of the write
+ // buffer.
+ //
+
+ size = length;
+ }
+
+ statistics -> addPackedBytesOut(size);
+
+ //
+ // Refrain the write loop from putting
+ // opcode and size in the output buffer.
+ //
+
+ opcode = 0;
+ }
+
+ //
+ // Now image is unpacked as a X_PutImage
+ // in write buffer. Check if we can send
+ // the image using the MIT-SHM extension.
+ //
+
+ if (result > 0)
+ {
+ result = handleShmem(opcode, buffer, size);
+
+ //
+ // We already put opcode and size in
+ // the resulting buffer.
+ //
+
+ if (result > 0)
+ {
+ opcode = 0;
+ }
+ }
+
+ return 1;
+}
+
+int ServerChannel::handleMotion(EncodeBuffer &encodeBuffer)
+{
+ #if defined(TEST) || defined(INFO)
+
+ if (lastMotion_[0] == '\0')
+ {
+ *logofs << "handleMotion: PANIC! No motion events to send "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleMotion: Sending motion events for FD#"
+ << fd_ << " at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Replicate code from read loop. When have
+ // time and wish, try to split everything
+ // in functions.
+ //
+
+ if (proxy -> handleAsyncSwitch(fd_) < 0)
+ {
+ return -1;
+ }
+
+ const unsigned char *buffer = lastMotion_;
+ unsigned char opcode = *lastMotion_;
+ unsigned int size = 32;
+
+ if (GetUINT(buffer + 2, bigEndian_) < serverSequence_)
+ {
+ PutUINT(serverSequence_, (unsigned char *) buffer + 2, bigEndian_);
+ }
+
+ encodeBuffer.encodeOpcodeValue(opcode, serverCache_ -> opcodeCache);
+
+ unsigned int sequenceNum = GetUINT(buffer + 2, bigEndian_);
+
+ unsigned int sequenceDiff = sequenceNum - serverSequence_;
+
+ serverSequence_ = sequenceNum;
+
+ #ifdef DEBUG
+ *logofs << "handleMotion: Last server sequence number for FD#"
+ << fd_ << " is " << serverSequence_ << " with "
+ << "difference " << sequenceDiff << ".\n"
+ << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(sequenceDiff, 16,
+ serverCache_ -> eventSequenceCache, 7);
+
+ //
+ // If we fast encoded the message
+ // then skip the rest.
+ //
+
+ if (control -> LocalDeltaCompression == 0)
+ {
+ int result = handleFastReadEvent(encodeBuffer, opcode,
+ buffer, size);
+
+ #ifdef DEBUG
+ *logofs << "handleMotion: Sent saved motion event for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ lastMotion_[0] = '\0';
+
+ #ifdef DEBUG
+ *logofs << "handleMotion: Reset last motion event for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ if (result < 0)
+ {
+ return -1;
+ }
+ else if (result > 0)
+ {
+ return 1;
+ }
+ }
+
+ //
+ // This should be just the part specific
+ // for motion events but is currently a
+ // copy-paste of code from the read loop.
+ //
+
+ unsigned char detail = buffer[1];
+ if (*buffer == MotionNotify)
+ encodeBuffer.encodeBoolValue((unsigned int) detail);
+ else if ((*buffer == EnterNotify) || (*buffer == LeaveNotify))
+ encodeBuffer.encodeValue((unsigned int) detail, 3);
+ else if (*buffer == KeyRelease)
+ {
+ if (detail == serverCache_ -> keyPressLastKey)
+ encodeBuffer.encodeBoolValue(1);
+ else
+ {
+ encodeBuffer.encodeBoolValue(0);
+ encodeBuffer.encodeValue((unsigned int) detail, 8);
+ }
+ }
+ else if ((*buffer == ButtonPress) || (*buffer == ButtonRelease))
+ encodeBuffer.encodeCachedValue(detail, 8,
+ serverCache_ -> buttonCache);
+ else
+ encodeBuffer.encodeValue((unsigned int) detail, 8);
+ unsigned int timestamp = GetULONG(buffer + 4, bigEndian_);
+ unsigned int timestampDiff = timestamp - serverCache_ -> lastTimestamp;
+ serverCache_ -> lastTimestamp = timestamp;
+ encodeBuffer.encodeCachedValue(timestampDiff, 32,
+ serverCache_ -> motionNotifyTimestampCache, 9);
+ int skipRest = 0;
+ if (*buffer == KeyRelease)
+ {
+ skipRest = 1;
+ for (unsigned int i = 8; i < 31; i++)
+ {
+ if (buffer[i] != serverCache_ -> keyPressCache[i - 8])
+ {
+ skipRest = 0;
+ break;
+ }
+ }
+ encodeBuffer.encodeBoolValue(skipRest);
+ }
+ if (!skipRest)
+ {
+ const unsigned char *nextSrc = buffer + 8;
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 29,
+ *serverCache_ -> motionNotifyWindowCache[i], 6);
+ nextSrc += 4;
+ }
+ unsigned int rootX = GetUINT(buffer + 20, bigEndian_);
+ unsigned int rootY = GetUINT(buffer + 22, bigEndian_);
+ unsigned int eventX = GetUINT(buffer + 24, bigEndian_);
+ unsigned int eventY = GetUINT(buffer + 26, bigEndian_);
+ eventX -= rootX;
+ eventY -= rootY;
+ encodeBuffer.encodeCachedValue(rootX -
+ serverCache_ -> motionNotifyLastRootX, 16,
+ serverCache_ -> motionNotifyRootXCache, 6);
+ serverCache_ -> motionNotifyLastRootX = rootX;
+ encodeBuffer.encodeCachedValue(rootY -
+ serverCache_ -> motionNotifyLastRootY, 16,
+ serverCache_ -> motionNotifyRootYCache, 6);
+ serverCache_ -> motionNotifyLastRootY = rootY;
+ encodeBuffer.encodeCachedValue(eventX, 16,
+ serverCache_ -> motionNotifyEventXCache, 6);
+ encodeBuffer.encodeCachedValue(eventY, 16,
+ serverCache_ -> motionNotifyEventYCache, 6);
+ encodeBuffer.encodeCachedValue(GetUINT(buffer + 28, bigEndian_),
+ 16, serverCache_ -> motionNotifyStateCache);
+ if ((*buffer == EnterNotify) || (*buffer == LeaveNotify))
+ encodeBuffer.encodeValue((unsigned int) buffer[30], 2);
+ else
+ encodeBuffer.encodeBoolValue((unsigned int) buffer[30]);
+ if ((*buffer == EnterNotify) || (*buffer == LeaveNotify))
+ encodeBuffer.encodeValue((unsigned int) buffer[31], 2);
+ else if (*buffer == KeyPress)
+ {
+ serverCache_ -> keyPressLastKey = detail;
+ for (unsigned int i = 8; i < 31; i++)
+ {
+ serverCache_ -> keyPressCache[i - 8] = buffer[i];
+ }
+ }
+ }
+
+ //
+ // Print info about achieved compression
+ // and update the statistics.
+ //
+
+ int bits = encodeBuffer.diffBits();
+
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << "handleMotion: Handled event OPCODE#" << (unsigned int) buffer[0]
+ << " for FD#" << fd_ << " sequence " << sequenceNum << ". "
+ << size << " bytes in, " << bits << " bits (" << ((float) bits) / 8
+ << " bytes) out.\n" << logofs_flush;
+ #endif
+
+ statistics -> addEventBits(*buffer, size << 3, bits);
+
+ #ifdef DEBUG
+ *logofs << "handleMotion: Sent saved motion event for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ lastMotion_[0] = '\0';
+
+ #ifdef DEBUG
+ *logofs << "handleMotion: Reset last motion event for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int ServerChannel::handleConfiguration()
+{
+ #ifdef TEST
+ *logofs << "ServerChannel: Setting new buffer parameters "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ readBuffer_.setSize(control -> ServerInitialReadSize,
+ control -> ServerMaximumBufferSize);
+
+ writeBuffer_.setSize(control -> TransportXBufferSize,
+ control -> TransportXBufferThreshold,
+ control -> TransportMaximumBufferSize);
+
+ transport_ -> setSize(control -> TransportXBufferSize,
+ control -> TransportXBufferThreshold,
+ control -> TransportMaximumBufferSize);
+ return 1;
+}
+
+int ServerChannel::handleFinish()
+{
+ #ifdef TEST
+ *logofs << "ServerChannel: Finishing connection for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ congestion_ = 0;
+ priority_ = 0;
+
+ finish_ = 1;
+
+ //
+ // Reset the motion event.
+ //
+
+ lastMotion_[0] = '\0';
+
+ transport_ -> fullReset();
+
+ return 1;
+}
+
+int ServerChannel::handleAsyncEvents()
+{
+ //
+ // Encode more events while decoding the
+ // proxy messages.
+ //
+
+ if (transport_ -> readable() > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleAsyncEvents: WARNING! Encoding events "
+ << "for FD#" << fd_ << " at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ #endif
+
+ #if defined(TEST) || defined(INFO)
+
+ T_timestamp startTs = getTimestamp();
+
+ #endif
+
+ if (proxy -> handleAsyncRead(fd_) < 0)
+ {
+ return -1;
+ }
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleAsyncEvents: Spent " << diffTimestamp(startTs,
+ getTimestamp()) << " Ms handling events for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+ }
+
+ return 0;
+}
+
+int ServerChannel::handleUnpack(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size)
+{
+ int resource = *(buffer + 1);
+
+ #ifdef DEBUG
+ *logofs << "handleUnpack: Unpacking image for resource " << resource
+ << " with method " << (unsigned int) *(buffer + 12)
+ << ".\n" << logofs_flush;
+ #endif
+
+ handleUnpackStateInit(resource);
+
+ T_geometry *geometryState = unpackState_[resource] -> geometry;
+ T_colormap *colormapState = unpackState_[resource] -> colormap;
+ T_alpha *alphaState = unpackState_[resource] -> alpha;
+
+ if (geometryState == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "handleUnpack: PANIC! Missing geometry unpacking "
+ << "image for resource " << resource << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Missing geometry unpacking "
+ << "image for resource " << resource << ".\n";
+
+ return -1;
+ }
+
+ //
+ // Get the image data from the buffer.
+ //
+
+ imageState_ -> drawable = GetULONG(buffer + 4, bigEndian_);
+ imageState_ -> gcontext = GetULONG(buffer + 8, bigEndian_);
+
+ imageState_ -> method = *(buffer + 12);
+
+ imageState_ -> format = *(buffer + 13);
+ imageState_ -> srcDepth = *(buffer + 14);
+ imageState_ -> dstDepth = *(buffer + 15);
+
+ imageState_ -> srcLength = GetULONG(buffer + 16, bigEndian_);
+ imageState_ -> dstLength = GetULONG(buffer + 20, bigEndian_);
+
+ imageState_ -> srcX = GetUINT(buffer + 24, bigEndian_);
+ imageState_ -> srcY = GetUINT(buffer + 26, bigEndian_);
+ imageState_ -> srcWidth = GetUINT(buffer + 28, bigEndian_);
+ imageState_ -> srcHeight = GetUINT(buffer + 30, bigEndian_);
+
+ imageState_ -> dstX = GetUINT(buffer + 32, bigEndian_);
+ imageState_ -> dstY = GetUINT(buffer + 34, bigEndian_);
+ imageState_ -> dstWidth = GetUINT(buffer + 36, bigEndian_);
+ imageState_ -> dstHeight = GetUINT(buffer + 38, bigEndian_);
+
+ #ifdef TEST
+ *logofs << "handleUnpack: Source X is " << imageState_ -> srcX
+ << " Y is " << imageState_ -> srcY << " width is "
+ << imageState_ -> srcWidth << " height is "
+ << imageState_ -> srcHeight << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef TEST
+ *logofs << "handleUnpack: Destination X is " << imageState_ -> dstX
+ << " Y is " << imageState_ -> dstY << " width is "
+ << imageState_ -> dstWidth << " height is "
+ << imageState_ -> dstHeight << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (imageState_ -> srcX != 0 || imageState_ -> srcY != 0)
+ {
+ #ifdef PANIC
+ *logofs << "handleUnpack: PANIC! Unsupported source coordinates "
+ << "in unpack request.\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ else if (imageState_ -> method == PACK_COLORMAP_256_COLORS &&
+ (colormapState == NULL || colormapState -> data == NULL))
+ {
+ #ifdef PANIC
+ *logofs << "handleUnpack: PANIC! Cannot find any unpack colormap.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ //
+ // Field srcLength carries size of image data in
+ // packed format. Field dstLength is size of the
+ // image in the original X bitmap format.
+ //
+
+ unsigned int srcDataOffset = 40;
+
+ unsigned int srcSize = imageState_ -> srcLength;
+
+ unsigned int removeSize = size;
+
+ unsigned char *srcData = buffer + srcDataOffset;
+
+ //
+ // Get source and destination bits per pixel.
+ //
+
+ int srcBitsPerPixel = MethodBitsPerPixel(imageState_ -> method);
+
+ if (srcBitsPerPixel <= 0)
+ {
+ #ifdef PANIC
+ *logofs << "handleUnpack: PANIC! Can't identify source "
+ << "bits per pixel for method " << (unsigned int)
+ imageState_ -> method << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't identify source bits "
+ << "per pixel for method " << (unsigned int)
+ imageState_ -> method << ".\n";
+
+ writeBuffer_.removeMessage(removeSize);
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "handleUnpack: Source bits per pixel are "
+ << srcBitsPerPixel << " source data size is "
+ << srcSize << ".\n" << logofs_flush;
+ #endif
+
+ int dstBitsPerPixel = UnpackBitsPerPixel(geometryState, imageState_ -> dstDepth);
+
+ if (dstBitsPerPixel <= 0)
+ {
+ #ifdef PANIC
+ *logofs << "handleUnpack: PANIC! Can't identify "
+ << "destination bits per pixel for depth "
+ << (unsigned int) imageState_ -> dstDepth
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't identify "
+ << "destination bits per pixel for depth "
+ << (unsigned int) imageState_ -> dstDepth
+ << ".\n";
+
+ writeBuffer_.removeMessage(removeSize);
+
+ return -1;
+ }
+
+ //
+ // Destination is a PutImage request.
+ //
+
+ unsigned int dstDataOffset = 24;
+
+ //
+ // Output buffer size must match the number of input
+ // pixels multiplied by the number of bytes per pixel
+ // of current geometry.
+ //
+
+ size = (RoundUp4(imageState_ -> dstWidth * dstBitsPerPixel / 8) *
+ imageState_ -> dstHeight) + dstDataOffset;
+
+ #ifdef TEST
+ *logofs << "handleUnpack: Destination bits per pixel are "
+ << dstBitsPerPixel << " destination data size is "
+ << size - dstDataOffset << ".\n" << logofs_flush;
+ #endif
+
+ unsigned int dstSize = size - dstDataOffset;
+
+ imageState_ -> dstLines = imageState_ -> dstHeight;
+
+ unsigned char *dstData;
+
+ //
+ // Size of the final output buffer had to be stored
+ // in the offset field of XImage/NXPackedImage.
+ //
+
+ #ifdef WARNING
+
+ if (dstSize != imageState_ -> dstLength)
+ {
+ *logofs << "handleUnpack: WARNING! Destination size mismatch "
+ << "with reported " << imageState_ -> dstLength
+ << " and actual " << dstSize << ".\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ //
+ // The decoding algorithm has put the packed image
+ // in the plain write buffer. Let's use the scratch
+ // buffer to uncompress the image.
+ //
+
+ buffer = writeBuffer_.addScratchMessage(size);
+
+ dstData = buffer + dstDataOffset;
+
+ //
+ // Unpack image into the buffer.
+ //
+
+ *buffer = (unsigned char) X_PutImage;
+
+ *(buffer + 1) = imageState_ -> format;
+
+ PutUINT(size >> 2, buffer + 2, bigEndian_);
+
+ PutULONG(imageState_ -> drawable, buffer + 4, bigEndian_);
+ PutULONG(imageState_ -> gcontext, buffer + 8, bigEndian_);
+
+ PutUINT(imageState_ -> dstWidth, buffer + 12, bigEndian_);
+ PutUINT(imageState_ -> dstLines, buffer + 14, bigEndian_);
+
+ PutUINT(imageState_ -> dstX, buffer + 16, bigEndian_);
+ PutUINT(imageState_ -> dstY, buffer + 18, bigEndian_);
+
+ *(buffer + 20) = 0;
+ *(buffer + 21) = imageState_ -> dstDepth;
+
+ #ifdef TEST
+ *logofs << "handleUnpack: Write buffer size is "
+ << writeBuffer_.getLength() << " scratch size is "
+ << writeBuffer_.getScratchLength() << ".\n"
+ << logofs_flush;
+ #endif
+
+ int result = 0;
+
+ switch (imageState_ -> method)
+ {
+ case PACK_JPEG_8_COLORS:
+ case PACK_JPEG_64_COLORS:
+ case PACK_JPEG_256_COLORS:
+ case PACK_JPEG_512_COLORS:
+ case PACK_JPEG_4K_COLORS:
+ case PACK_JPEG_32K_COLORS:
+ case PACK_JPEG_64K_COLORS:
+ case PACK_JPEG_256K_COLORS:
+ case PACK_JPEG_2M_COLORS:
+ case PACK_JPEG_16M_COLORS:
+ {
+ result = UnpackJpeg(geometryState, imageState_ -> method, srcData,
+ srcSize, dstBitsPerPixel, imageState_ -> dstWidth,
+ imageState_ -> dstHeight, dstData, dstSize);
+ break;
+ }
+ case PACK_PNG_8_COLORS:
+ case PACK_PNG_64_COLORS:
+ case PACK_PNG_256_COLORS:
+ case PACK_PNG_512_COLORS:
+ case PACK_PNG_4K_COLORS:
+ case PACK_PNG_32K_COLORS:
+ case PACK_PNG_64K_COLORS:
+ case PACK_PNG_256K_COLORS:
+ case PACK_PNG_2M_COLORS:
+ case PACK_PNG_16M_COLORS:
+ {
+ result = UnpackPng(geometryState, imageState_ -> method, srcData,
+ srcSize, dstBitsPerPixel, imageState_ -> dstWidth,
+ imageState_ -> dstHeight, dstData, dstSize);
+ break;
+ }
+ case PACK_RGB_16M_COLORS:
+ {
+ result = UnpackRgb(geometryState, imageState_ -> method, srcData,
+ srcSize, dstBitsPerPixel, imageState_ -> dstWidth,
+ imageState_ -> dstHeight, dstData, dstSize);
+ break;
+ }
+ case PACK_RLE_16M_COLORS:
+ {
+ result = UnpackRle(geometryState, imageState_ -> method, srcData,
+ srcSize, dstBitsPerPixel, imageState_ -> dstWidth,
+ imageState_ -> dstHeight, dstData, dstSize);
+ break;
+ }
+ case PACK_BITMAP_16M_COLORS:
+ {
+ result = UnpackBitmap(geometryState, imageState_ -> method, srcData,
+ srcSize, dstBitsPerPixel, imageState_ -> dstWidth,
+ imageState_ -> dstHeight, dstData, dstSize);
+ break;
+ }
+ case PACK_COLORMAP_256_COLORS:
+ {
+ result = Unpack8(geometryState, colormapState, srcBitsPerPixel,
+ imageState_ -> srcWidth, imageState_ -> srcHeight, srcData,
+ srcSize, dstBitsPerPixel, imageState_ -> dstWidth,
+ imageState_ -> dstHeight, dstData, dstSize);
+
+ break;
+ }
+ default:
+ {
+ const T_colormask *colorMask = MethodColorMask(imageState_ -> method);
+
+ switch (imageState_ -> method)
+ {
+ case PACK_MASKED_8_COLORS:
+ case PACK_MASKED_64_COLORS:
+ case PACK_MASKED_256_COLORS:
+ {
+ result = Unpack8(geometryState, colorMask, imageState_ -> srcDepth,
+ imageState_ -> srcWidth, imageState_ -> srcHeight,
+ srcData, srcSize, imageState_ -> dstDepth,
+ imageState_ -> dstWidth, imageState_ -> dstHeight,
+ dstData, dstSize);
+ break;
+ }
+ case PACK_MASKED_512_COLORS:
+ case PACK_MASKED_4K_COLORS:
+ case PACK_MASKED_32K_COLORS:
+ case PACK_MASKED_64K_COLORS:
+ {
+ result = Unpack16(geometryState, colorMask, imageState_ -> srcDepth,
+ imageState_ -> srcWidth, imageState_ -> srcHeight,
+ srcData, srcSize, imageState_ -> dstDepth,
+ imageState_ -> dstWidth, imageState_ -> dstHeight,
+ dstData, dstSize);
+ break;
+ }
+ case PACK_MASKED_256K_COLORS:
+ case PACK_MASKED_2M_COLORS:
+ case PACK_MASKED_16M_COLORS:
+ {
+ result = Unpack24(geometryState, colorMask, imageState_ -> srcDepth,
+ imageState_ -> srcWidth, imageState_ -> srcHeight,
+ srcData, srcSize, imageState_ -> dstDepth,
+ imageState_ -> dstWidth, imageState_ -> dstHeight,
+ dstData, dstSize);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+ }
+
+ writeBuffer_.removeMessage(removeSize);
+
+ if (result <= 0)
+ {
+ #ifdef PANIC
+ *logofs << "handleUnpack: PANIC! Failed to unpack image "
+ << "with method '" << (unsigned int) imageState_ -> method
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Failed to unpack image "
+ << "with method '" << (unsigned int) imageState_ -> method
+ << "'.\n";
+
+ //
+ // TODO: We should mark the image somehow,
+ // and force the remote to remove it from
+ // the cache.
+ //
+
+ writeBuffer_.removeScratchMessage();
+
+ return -1;
+ }
+
+ //
+ // Alpha channel is used only on some 32 bits pixmaps
+ // and only if render extension is in use. Presently
+ // we don't have an efficient way to know in advance
+ // if mask must be applied or not to the image. If an
+ // alpha channel is set, the function will check if
+ // the size of the alpha data matches the size of the
+ // image. In the worst case we'll create an useless
+ // alpha plane for a pixmap that doesn't need it.
+ //
+
+ if (alphaState != NULL && alphaState -> data != NULL &&
+ imageState_ -> dstDepth == 32)
+ {
+ UnpackAlpha(alphaState, dstData, dstSize, imageByteOrder_);
+ }
+
+ return 1;
+}
+
+int ServerChannel::handleAuthorization(unsigned char *buffer)
+{
+ //
+ // At the present moment we don't support more than
+ // a single display for each proxy, so authorization
+ // data is shared among all the channels.
+ //
+ // Use the following code to simulate authentication
+ // failures on a LSB machine:
+ //
+ // memcpy(buffer + 12 + (((buffer[6] + 256 *
+ // buffer[7]) + 3) & ~3), "1234567890123456", 16);
+ //
+
+ if (auth == NULL)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleAuthorization: Forwarding the real cookie "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else if (auth -> checkCookie(buffer) == 1)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleAuthorization: Matched the fake cookie "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+ }
+ else
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleAuthorization: WARNING! Failed to match "
+ << "the fake cookie for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+}
+
+int ServerChannel::handleAuthorization(const unsigned char *buffer, int size)
+{
+ //
+ // Check the X server's response and, in the case of
+ // an error, print the textual information reported
+ // by the server.
+ //
+
+ if (*buffer != 1)
+ {
+ const char *reason = NULL;
+
+ //
+ // At the moment we don't take into account the end-
+ // ianess of the reply. This should work in any case
+ // because we simply try to match a few well-known
+ // error strings.
+ //
+
+ if (size >= INVALID_COOKIE_SIZE + 8 &&
+ memcmp(buffer + 8, INVALID_COOKIE_DATA,
+ INVALID_COOKIE_SIZE) == 0)
+ {
+ reason = INVALID_COOKIE_DATA;
+ }
+ else if (size >= NO_AUTH_PROTO_SIZE + 8 &&
+ memcmp(buffer + 8, NO_AUTH_PROTO_DATA,
+ NO_AUTH_PROTO_SIZE) == 0)
+ {
+ reason = NO_AUTH_PROTO_DATA;
+ }
+ else
+ {
+ reason = "Unknown";
+ }
+
+ #ifdef WARNING
+ *logofs << "handleAuthorization: WARNING! X connection failed "
+ << "with error '" << reason << "' on FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": X connection failed "
+ << "with error '" << reason << "'.\n";
+ }
+ #if defined(TEST) || defined(INFO)
+ else
+ {
+ *logofs << "handleAuthorization: X connection successful "
+ << "on FD#" << fd_ << ".\n" << logofs_flush;
+ }
+ #endif
+
+ return 1;
+}
+
+//
+// Use a simple encoding. Need to handle the image
+// requests in the usual way and the X_ListExtensions
+// and X_QueryExtension to hide MIT-SHM and RENDER
+// in the reply.
+//
+
+int ServerChannel::handleFastWriteRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+{
+ //
+ // All the NX requests are handled in the
+ // main message loop. The X_PutImage can
+ // be handled here only if a split was
+ // not requested.
+ //
+
+ if ((opcode >= X_NXFirstOpcode && opcode <= X_NXLastOpcode) ||
+ (control -> isProtoStep7() == 1 && opcode == X_PutImage &&
+ splitState_.resource != nothing) || opcode == X_ListExtensions ||
+ opcode == X_QueryExtension)
+ {
+ return 0;
+ }
+
+ #ifdef DEBUG
+ *logofs << "handleFastWriteRequest: Decoding raw request OPCODE#"
+ << (unsigned int) opcode << " for FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ buffer = writeBuffer_.addMessage(4);
+
+ #ifndef __sun
+
+ unsigned int *next = (unsigned int *) decodeBuffer.decodeMemory(4);
+
+ *((unsigned int *) buffer) = *next;
+
+ #else /* #ifndef __sun */
+
+ memcpy(buffer, decodeBuffer.decodeMemory(4), 4);
+
+ #endif /* #ifndef __sun */
+
+ size = GetUINT(buffer + 2, bigEndian_) << 2;
+
+ if (size < 4)
+ {
+ #ifdef WARNING
+ *logofs << "handleFastWriteRequest: WARNING! Assuming size 4 "
+ << "for suspicious message of size " << size
+ << ".\n" << logofs_flush;
+ #endif
+
+ size = 4;
+ }
+
+ writeBuffer_.registerPointer(&buffer);
+
+ if (writeBuffer_.getAvailable() < size - 4 ||
+ (int) size >= control -> TransportFlushBufferSize)
+ {
+ #ifdef DEBUG
+ *logofs << "handleFastWriteRequest: Using scratch buffer for OPCODE#"
+ << (unsigned int) opcode << " with size " << size << " and "
+ << writeBuffer_.getLength() << " bytes in buffer.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // The procedure moving data to shared memory
+ // assumes that the full message is stored in
+ // the scratch buffer. We can safely let the
+ // scratch buffer inherit the decode buffer
+ // at the next offset.
+ //
+
+ writeBuffer_.removeMessage(4);
+
+ buffer = writeBuffer_.addScratchMessage(((unsigned char *)
+ decodeBuffer.decodeMemory(size - 4)) - 4, size);
+ }
+ else
+ {
+ writeBuffer_.addMessage(size - 4);
+
+ #ifndef __sun
+
+ if (size <= 32)
+ {
+ next = (unsigned int *) decodeBuffer.decodeMemory(size - 4);
+
+ for (unsigned int i = 4; i < size; i += sizeof(unsigned int))
+ {
+ *((unsigned int *) (buffer + i)) = *next++;
+ }
+ }
+ else
+ {
+ memcpy(buffer + 4, decodeBuffer.decodeMemory(size - 4), size - 4);
+ }
+
+ #else /* #ifndef __sun */
+
+ memcpy(buffer + 4, decodeBuffer.decodeMemory(size - 4), size - 4);
+
+ #endif /* #ifndef __sun */
+ }
+
+ //
+ // Opcode could have been tainted by the client
+ // proxy. Replace the original opcode with the
+ // one sent in the decode buffer.
+ //
+
+ *buffer = opcode;
+
+ writeBuffer_.unregisterPointer();
+
+ if (opcode == X_PutImage)
+ {
+ handleImage(opcode, buffer, size);
+ }
+
+ #if defined(TEST) || defined(OPCODES)
+
+ if (opcode != 0)
+ {
+ *logofs << "handleFastWriteRequest: Handled request "
+ << "OPCODE#" << (unsigned int) opcode << " ("
+ << DumpOpcode(opcode) << ") for FD#" << fd_
+ << " sequence " << clientSequence_ << ". "
+ << size << " bytes out.\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleFastWriteRequest: Handled image or "
+ << "other request for FD#" << fd_
+ << " sequence " << clientSequence_ << ". "
+ << size << " bytes out.\n" << logofs_flush;
+ }
+
+ #endif
+
+ handleFlush(flush_if_needed);
+
+ return 1;
+}
+
+//
+// Use the simplest encoding except for replies that
+// need to be managed some way.
+//
+
+int ServerChannel::handleFastReadReply(EncodeBuffer &encodeBuffer, const unsigned char &opcode,
+ const unsigned char *&buffer, const unsigned int &size)
+{
+ //
+ // If we pushed a X_GetInputFocus in the sequence
+ // queue this means that the original message was
+ // a NX request for which we have to provide a NX
+ // reply.
+ //
+
+ if ((opcode >= X_NXFirstOpcode &&
+ opcode <= X_NXLastOpcode) ||
+ opcode == X_QueryExtension ||
+ opcode == X_ListExtensions ||
+ opcode == X_GetInputFocus)
+ {
+ return 0;
+ }
+
+ #ifdef DEBUG
+ *logofs << "handleFastReadReply: Encoding raw reply OPCODE#"
+ << (unsigned int) opcode << " for FD#" << fd_
+ << " with size " << size << ".\n"
+ << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeMemory(buffer, size);
+
+ //
+ // Send back the reply as soon
+ // as possible.
+ //
+
+ priority_++;
+
+ int bits = encodeBuffer.diffBits();
+
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << "handleFastReadReply: Handled raw reply OPCODE#"
+ << (unsigned int) opcode << " for FD#" << fd_ << " sequence "
+ << serverSequence_ << ". " << size << " bytes in, "
+ << bits << " bits (" << ((float) bits) / 8
+ << " bytes) out.\n" << logofs_flush;
+ #endif
+
+ statistics -> addReplyBits(opcode, size << 3, bits);
+
+ return 1;
+}
+
+int ServerChannel::handleFastReadEvent(EncodeBuffer &encodeBuffer, const unsigned char &opcode,
+ const unsigned char *&buffer, const unsigned int &size)
+{
+ #ifdef DEBUG
+ *logofs << "handleFastReadEvent: Encoding raw "
+ << (opcode == X_Error ? "error" : "event") << " OPCODE#"
+ << (unsigned int) opcode << " for FD#" << fd_
+ << " with size " << size << ".\n"
+ << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeMemory(buffer, size);
+
+ switch (opcode)
+ {
+ case X_Error:
+ case ButtonPress:
+ case ButtonRelease:
+ case KeyPress:
+ case KeyRelease:
+ {
+ priority_++;
+ }
+ }
+
+ int bits = encodeBuffer.diffBits();
+
+ #if defined(TEST) || defined(OPCODES)
+
+ if (opcode == X_Error)
+ {
+ unsigned char code = *(buffer + 1);
+
+ *logofs << "handleFastReadEvent: Handled error ERR_CODE#"
+ << (unsigned int) code << " for FD#" << fd_;
+
+ *logofs << " RES_ID#" << GetULONG(buffer + 4, bigEndian_);
+
+ *logofs << " MIN_OP#" << GetUINT(buffer + 8, bigEndian_);
+
+ *logofs << " MAJ_OP#" << (unsigned int) *(buffer + 10);
+
+ *logofs << " sequence " << serverSequence_ << ". " << size
+ << " bytes in, " << bits << " bits (" << ((float) bits) / 8
+ << " bytes) out.\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleFastReadEvent: Handled event OPCODE#"
+ << (unsigned int) *buffer << " for FD#" << fd_
+ << " sequence " << serverSequence_ << ". " << size
+ << " bytes in, " << bits << " bits (" << ((float) bits) / 8
+ << " bytes) out.\n" << logofs_flush;
+ }
+
+ #endif
+
+ statistics -> addEventBits(opcode, size << 3, bits);
+
+ return 1;
+}
+
+void ServerChannel::initCommitQueue()
+{
+ #ifdef TEST
+ *logofs << "initCommitQueue: Resetting the queue of split commits "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ for (int i = 0; i < MAX_COMMIT_SEQUENCE_QUEUE; i++)
+ {
+ commitSequenceQueue_[i] = 0;
+ }
+}
+
+void ServerChannel::updateCommitQueue(unsigned short sequence)
+{
+ for (int i = 0; i < MAX_COMMIT_SEQUENCE_QUEUE - 1; i++)
+ {
+ commitSequenceQueue_[i + 1] = commitSequenceQueue_[i];
+ }
+
+ #ifdef TEST
+ *logofs << "updateCommitQueue: Saved " << sequence
+ << " as last sequence number of image to commit.\n"
+ << logofs_flush;
+ #endif
+
+ commitSequenceQueue_[0] = sequence;
+}
+
+int ServerChannel::checkCommitError(unsigned char error, unsigned short sequence,
+ const unsigned char *buffer)
+{
+ //
+ // Check if error is due to an image commit
+ // generated at the end of a split.
+ //
+ // TODO: It should zero the head of the list
+ // when an event comes with a sequence number
+ // greater than the value of the last element
+ // added.
+ //
+
+ for (int i = 0; i < MAX_COMMIT_SEQUENCE_QUEUE &&
+ commitSequenceQueue_[i] != 0; i++)
+ {
+ #ifdef TEST
+ *logofs << "checkCommitError: Checking committed image's "
+ << "sequence number " << commitSequenceQueue_[i]
+ << " with input sequence " << sequence << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (commitSequenceQueue_[i] == sequence)
+ {
+ #ifdef WARNING
+
+ *logofs << "checkCommitError: WARNING! Failed operation for "
+ << "FD#" << fd_ << " with ERR_CODE#"
+ << (unsigned int) *(buffer + 1);
+
+ *logofs << " RES_ID#" << GetULONG(buffer + 4, bigEndian_);
+
+ *logofs << " MIN_OP#" << GetUINT(buffer + 8, bigEndian_);
+
+ *logofs << " MAJ_OP#" << (unsigned int) *(buffer + 10);
+
+ *logofs << " sequence " << sequence << ".\n";
+
+ *logofs << logofs_flush;
+
+ #endif
+
+ cerr << "Warning" << ": Failed commit operation "
+ << "with ERR_CODE#" << (unsigned int) error;
+
+ cerr << " RES_ID#" << GetULONG(buffer + 4, bigEndian_);
+
+ cerr << " MIN_OP#" << GetUINT(buffer + 8, bigEndian_);
+
+ cerr << " MAJ_OP#" << (unsigned int) *(buffer + 10);
+
+ cerr << ".\n";
+
+ #ifdef WARNING
+ *logofs << "checkCommitError: WARNING! Suppressing error on "
+ << "OPCODE#" << (unsigned int) opcodeStore_ -> commitSplit
+ << " for FD#" << fd_ << " with sequence " << sequence
+ << " at position " << i << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+//
+// Check if the user pressed the CTRL+ALT+SHIFT+ESC
+// keystroke. At the present moment it uses different
+// keycodes based on the client OS. This should be
+// implemented in a way that is platform independent
+// (that's not an easy task, considered that we don't
+// have access to the higher level X libraries).
+//
+
+int ServerChannel::checkKeyboardEvent(unsigned char event, unsigned short sequence,
+ const unsigned char *buffer)
+{
+ #ifdef TEST
+ *logofs << "checkKeyboardEvent: Checking escape sequence with byte [1] "
+ << (void *) ((unsigned) *(buffer + 1)) << " and bytes [28-29] "
+ << (void *) ((unsigned) GetUINT(buffer + 28, bigEndian_))
+ << " for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef __APPLE__
+
+ int alert = (*(buffer + 1) == 0x3d &&
+ GetUINT(buffer + 28, bigEndian_) == 0x2005);
+
+ #else
+
+ int alert = (*(buffer + 1) == 0x09 &&
+ ((GetUINT(buffer + 28, bigEndian_) &
+ 0x0d) == 0x0d));
+
+ #endif
+
+ if (alert == 1)
+ {
+ #ifdef PANIC
+ *logofs << "checkKeyboardEvent: PANIC! Received sequence "
+ << "CTRL+ALT+SHIFT+ESC " << "for FD#"<< fd_
+ << ". Showing the abort dialog.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Received sequence CTRL+ALT+SHIFT+ESC. "
+ << "Showing the abort dialog.\n";
+
+ HandleAlert(CLOSE_UNRESPONSIVE_X_SERVER_ALERT, 1);
+ }
+
+ return alert;
+}
+
+//
+// Handle the MIT-SHM initialization
+// messages exchanged with the remote
+// proxy.
+//
+
+int ServerChannel::handleShmemReply(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned int stage, const unsigned char *buffer,
+ const unsigned int size)
+{
+ #ifdef TEST
+ *logofs << "handleShmemReply: Returning shmem reply for "
+ << "stage " << stage << ".\n" << logofs_flush;
+ #endif
+
+ if (opcode == X_QueryExtension)
+ {
+ encodeBuffer.encodeValue(stage, 2);
+
+ shmemState_ -> present = *(buffer + 8);
+ shmemState_ -> opcode = *(buffer + 9);
+ shmemState_ -> event = *(buffer + 10);
+ shmemState_ -> error = *(buffer + 11);
+
+ #ifdef TEST
+ *logofs << "handleShmemReply: Extension present is "
+ << shmemState_ -> present << " with base OPCODE#"
+ << (unsigned int) shmemState_ -> opcode << " base event "
+ << (unsigned int) shmemState_ -> event << " base error "
+ << (unsigned int) shmemState_ -> error << ".\n"
+ << logofs_flush;
+ #endif
+ }
+ else if (opcode == X_GetInputFocus)
+ {
+ encodeBuffer.encodeValue(stage, 2);
+
+ encodeBuffer.encodeBoolValue(0);
+
+ if (shmemState_ -> present == 1 &&
+ shmemState_ -> address != NULL &&
+ shmemState_ -> segment > 0 &&
+ shmemState_ -> id > 0)
+ {
+ cerr << "Info" << ": Using shared memory parameters 1/"
+ << (shmemState_ -> size / 1024) << "K.\n";
+
+ shmemState_ -> enabled = 1;
+
+ encodeBuffer.encodeBoolValue(1);
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "handleShmemReply: WARNING! Not using shared memory "
+ << "support in X server for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Info" << ": Using shared memory parameters 0/0K.\n";
+
+ handleShmemStateRemove();
+
+ encodeBuffer.encodeBoolValue(0);
+ }
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "handleShmemReply: PANIC! Conversation error "
+ << "handling shared memory support for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Conversation error handling "
+ << "shared memory support.\n";
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int ServerChannel::handleShmemRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+{
+ //
+ // We need to query and initialize MIT-SHM on
+ // the real X server. To do this we'll need 3
+ // requests. At the end we'll have to encode
+ // the final reply for the X client side.
+ //
+
+ handleShmemStateAlloc();
+
+ unsigned int stage;
+
+ decodeBuffer.decodeValue(stage, 2);
+
+ unsigned int expected = shmemState_ -> stage + 1;
+
+ if (stage != expected || stage > 2)
+ {
+ #ifdef PANIC
+ *logofs << "handleShmemRequest: PANIC! Unexpected stage "
+ << stage << " in handling shared memory "
+ << "support for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Unexpected stage "
+ << stage << " in handling shared memory "
+ << "support for FD#" << fd_ << ".\n";
+
+ return -1;
+ }
+
+ switch (stage)
+ {
+ case 0:
+ {
+ unsigned int enableClient;
+ unsigned int enableServer;
+
+ decodeBuffer.decodeBoolValue(enableClient);
+ decodeBuffer.decodeBoolValue(enableServer);
+
+ unsigned int clientSegment;
+ unsigned int serverSegment;
+
+ decodeBuffer.decodeValue(clientSegment, 29, 9);
+ decodeBuffer.decodeValue(serverSegment, 29, 9);
+
+ shmemState_ -> segment = serverSegment;
+
+ #ifdef TEST
+ *logofs << "handleShmemRequest: Size of the shared memory "
+ << "segment will be " << control -> ShmemServerSize
+ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef TEST
+ *logofs << "handleShmemRequest: Sending X_QueryExtension request "
+ << "for FD#" << fd_ << " due to OPCODE#" << (unsigned int)
+ opcodeStore_ -> getShmemParameters << " in stage "
+ << stage << ".\n" << logofs_flush;
+ #endif
+
+ opcode = X_QueryExtension;
+
+ size = 16;
+ buffer = writeBuffer_.addMessage(size);
+
+ PutUINT(7, buffer + 4, bigEndian_);
+
+ //
+ // Simply make the query fail if shared
+ // memory support is disabled by the
+ // user.
+ //
+
+ if (control -> ShmemServer == 1 &&
+ control -> ShmemServerSize > 0 &&
+ enableServer == 1)
+ {
+ memcpy(buffer + 8, "MIT-SHM", 7);
+ }
+ else
+ {
+ memcpy(buffer + 8, "NO-MIT-", 7);
+ }
+
+ sequenceQueue_.push(clientSequence_, opcode,
+ opcodeStore_ -> getShmemParameters, stage);
+
+ //
+ // Save the sequence number so we can
+ // later identify any matching X error
+ // received from server.
+ //
+
+ shmemState_ -> sequence = clientSequence_;
+
+ break;
+ }
+ case 1:
+ {
+ if (shmemState_ -> present == 1)
+ {
+ //
+ // Make the segment read-write for everybody on
+ // Cygwin (to avoid any lack of support or any
+ // performance issue) and on MacOS/X (where the
+ // 0600 mask doesn't seem to work).
+ //
+
+ #if defined(__CYGWIN32__) || defined(__APPLE__)
+
+ int permissions = 0777;
+
+ #else
+
+ int permissions = 0600;
+
+ #endif
+
+ shmemState_ -> size = control -> ShmemServerSize;
+
+ shmemState_ -> id = shmget(IPC_PRIVATE, shmemState_ -> size,
+ IPC_CREAT | permissions);
+
+ if (shmemState_ -> id >= 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleShmemRequest: Allocated shared memory "
+ << "segment of " << shmemState_ -> size
+ << " bytes with id " << shmemState_ -> id
+ << ".\n" << logofs_flush;
+ #endif
+
+
+ shmemState_ -> address = shmat(shmemState_ -> id, 0, 0);
+
+ if (shmemState_ -> address != NULL)
+ {
+ #ifdef TEST
+ *logofs << "handleShmemRequest: Sending X_ShmAttach request "
+ << "for FD#" << fd_ << " due to OPCODE#" << (unsigned int)
+ opcodeStore_ -> getShmemParameters << " in stage "
+ << stage << ".\n" << logofs_flush;
+ #endif
+
+ opcode = shmemState_ -> opcode;
+
+ size = 16;
+ buffer = writeBuffer_.addMessage(size);
+
+ *(buffer + 1) = X_ShmAttach;
+
+ PutULONG(shmemState_ -> segment, buffer + 4, bigEndian_);
+ PutULONG(shmemState_ -> id, buffer + 8, bigEndian_);
+
+ *(buffer + 12) = 1;
+
+ shmemState_ -> sequence = clientSequence_;
+
+ break;
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "handleShmemRequest: WARNING! Can't attach the shared "
+ << "memory segment. Error is " << EGET() << " '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Can't attach the shared memory "
+ << "segment. Error is " << EGET() << " '"
+ << ESTR() << "'.\n";
+ }
+ }
+ else
+ {
+ #ifndef __CYGWIN32__
+
+ #ifdef WARNING
+ *logofs << "handleShmemRequest: WARNING! Can't create the shared "
+ << "memory segment. Error is " << EGET() << " '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Can't create the shared memory "
+ << "segment. Error is " << EGET() << " '"
+ << ESTR() << "'.\n";
+
+ #else
+
+ #ifdef TEST
+ *logofs << "handleShmemRequest: WARNING! Can't create the shared "
+ << "memory segment. Error is " << EGET() << " '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ #endif
+ }
+ }
+
+ if (shmemState_ -> present != 0)
+ {
+ #ifdef TEST
+ *logofs << "handleShmemRequest: Resetting shared memory "
+ << "presence flag for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ shmemState_ -> present = 0;
+ }
+
+ handleNullRequest(opcode, buffer, size);
+
+ break;
+ }
+ default:
+ {
+ #ifdef TEST
+ *logofs << "handleShmemRequest: Sending X_GetInputFocus request "
+ << "for FD#" << fd_ << " due to OPCODE#" << (unsigned int)
+ opcodeStore_ -> getShmemParameters << " in stage "
+ << stage << ".\n" << logofs_flush;
+ #endif
+
+ opcode = X_GetInputFocus;
+
+ size = 4;
+ buffer = writeBuffer_.addMessage(size);
+
+ sequenceQueue_.push(clientSequence_, opcode,
+ opcodeStore_ -> getShmemParameters, stage);
+ break;
+ }
+ }
+
+ shmemState_ -> stage += 1;
+
+ return 1;
+}
+
+//
+// Handling of MIT-SHM extension has been plugged late in
+// the design, so we have to make some assumptions. Image
+// is a X_PutImage request contained either in the scratch
+// buffer or in the normal write buffer. We need to move
+// the image data to the shared memory segment and replace
+// the X_PutImage request with a X_ShmPutImage.
+//
+
+int ServerChannel::handleShmem(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size)
+{
+ if (shmemState_ == NULL || shmemState_ -> enabled != 1)
+ {
+ #ifdef TEST
+
+ if (shmemState_ != NULL)
+ {
+ *logofs << "handleShmem: PANIC! Shared memory "
+ << "state found but support is not enabled "
+ << "for FD#" << fd_ << " in stage "
+ << shmemState_ -> stage << ".\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ return 0;
+ }
+
+ //
+ // Ignore null requests and requests that will not result
+ // in a single X_PutImage. To conform with the other func-
+ // tions, we get the opcode passed as a parameter. It can
+ // be zero if we don't want the write loop to put opcode
+ // and length in the resulting buffer. Anyway we are only
+ // interested in the original opcode of the request, that
+ // is stored in the image state.
+ //
+
+ unsigned char *dstData = buffer + 24;
+ unsigned int dstDataSize = size - 24;
+
+ if (dstDataSize == 0 || dstDataSize >
+ (unsigned int) control -> MaximumRequestSize)
+ {
+ #ifdef TEST
+ *logofs << "handleShmem: Ignoring image with opcode "
+ << (unsigned int) imageState_ -> opcode
+ << " and size " << size << " for FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ #ifdef TEST
+ *logofs << "handleShmem: Handling image with opcode "
+ << (unsigned int) imageState_ -> opcode
+ << " and size " << size << " for FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Get image data from buffer.
+ //
+
+ if (imageState_ -> opcode == X_PutImage)
+ {
+ //
+ // We still need to get the image's data.
+ //
+
+ imageState_ -> format = *(buffer + 1);
+
+ imageState_ -> drawable = GetULONG(buffer + 4, bigEndian_);
+ imageState_ -> gcontext = GetULONG(buffer + 8, bigEndian_);
+
+ imageState_ -> dstWidth = GetUINT(buffer + 12, bigEndian_);
+ imageState_ -> dstHeight = GetUINT(buffer + 14, bigEndian_);
+
+ imageState_ -> srcX = 0;
+ imageState_ -> srcY = 0;
+
+ imageState_ -> srcWidth = imageState_ -> dstWidth;
+ imageState_ -> srcHeight = imageState_ -> dstHeight;
+
+ imageState_ -> dstX = GetUINT(buffer + 16, bigEndian_);
+ imageState_ -> dstY = GetUINT(buffer + 18, bigEndian_);
+
+ imageState_ -> leftPad = *(buffer + 20);
+ imageState_ -> dstDepth = *(buffer + 21);
+
+ imageState_ -> dstLines = imageState_ -> dstHeight;
+
+ imageState_ -> dstLength = size - 24;
+ }
+
+ //
+ // Skip the MIT-SHM operation if the image
+ // is 1 bits-per-plane.
+ //
+
+ if (imageState_ -> dstDepth == 1)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleShmem: Ignoring image with opcode "
+ << (unsigned int) imageState_ -> opcode << " depth "
+ << (unsigned int) imageState_ -> dstDepth << " and "
+ << "size " << size << " for FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ //
+ // If the image can't fit in the available
+ // space, check if the completion event is
+ // arrived.
+ //
+
+ #if defined(TEST) || defined(INFO)
+
+ if (isTimestamp(shmemState_ -> last) == 0 &&
+ shmemState_ -> offset != 0)
+ {
+ *logofs << "handleShmem: PANIC! No timestamp for sequence "
+ << shmemState_ -> sequence << " with offset "
+ << shmemState_ -> offset << ".\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ if (shmemState_ -> offset + imageState_ -> dstLength >
+ shmemState_ -> size)
+ {
+ if (imageState_ -> dstLength > shmemState_ -> size)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleShmem: WARNING! Can't fit the image "
+ << "in the available memory segment for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else if (handleShmemEvent() <= 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleShmem: WARNING! Missing completion "
+ << "after " << diffTimestamp(shmemState_ -> last,
+ getTimestamp()) << " Ms for shared memory "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ }
+
+ //
+ // Let image start at current offset
+ // in the shared segment.
+ //
+
+ #ifdef TEST
+ *logofs << "handleShmem: Copying " << dstDataSize
+ << " bytes to shared memory at offset "
+ << shmemState_ -> offset << " for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ memcpy((unsigned char *) shmemState_ -> address +
+ shmemState_ -> offset, dstData, dstDataSize);
+
+ //
+ // Get rid of the original X_PutImage
+ // request.
+ //
+
+ if (writeBuffer_.getScratchData() != NULL)
+ {
+ writeBuffer_.removeScratchMessage();
+ }
+ else
+ {
+ writeBuffer_.removeMessage(size);
+ }
+
+ //
+ // Add a X_ShmPutImage request to the
+ // write buffer.
+ //
+
+ buffer = writeBuffer_.addMessage(40);
+
+ *buffer = shmemState_ -> opcode;
+
+ *(buffer + 1) = X_ShmPutImage;
+
+ PutUINT(40 >> 2, buffer + 2, bigEndian_);
+
+ PutULONG(imageState_ -> drawable, buffer + 4, bigEndian_);
+ PutULONG(imageState_ -> gcontext, buffer + 8, bigEndian_);
+
+ PutUINT(imageState_ -> dstWidth, buffer + 12, bigEndian_);
+ PutUINT(imageState_ -> dstLines, buffer + 14, bigEndian_);
+
+ PutUINT(imageState_ -> srcX, buffer + 16, bigEndian_);
+ PutUINT(imageState_ -> srcY, buffer + 18, bigEndian_);
+
+ PutUINT(imageState_ -> dstWidth, buffer + 20, bigEndian_);
+ PutUINT(imageState_ -> dstLines, buffer + 22, bigEndian_);
+
+ PutUINT(imageState_ -> dstX, buffer + 24, bigEndian_);
+ PutUINT(imageState_ -> dstY, buffer + 26, bigEndian_);
+
+ *(buffer + 28) = imageState_ -> dstDepth;
+ *(buffer + 29) = imageState_ -> format;
+ *(buffer + 30) = 1;
+
+ PutULONG(shmemState_ -> segment, buffer + 32, bigEndian_);
+ PutULONG(shmemState_ -> offset, buffer + 36, bigEndian_);
+
+ shmemState_ -> offset += dstDataSize;
+
+ shmemState_ -> sequence = clientSequence_;
+ shmemState_ -> last = getTimestamp();
+
+ #ifdef TEST
+ *logofs << "handleShmem: Saved shared memory sequence "
+ << shmemState_ -> sequence << " for FD#" << fd_
+ << " with offset " << shmemState_ -> offset
+ << " at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Make the X server read immediately
+ // from the shared memory buffer and
+ // produce the completion event.
+ //
+
+ handleFlush(flush_if_any);
+
+ return 1;
+}
+
+//
+// Try to read more events from the socket in the
+// attempt to get the completion event required
+// to reset the MIT-SHM segment.
+//
+
+int ServerChannel::handleShmemEvent()
+{
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleShmemEvent: Waiting for shared memory "
+ << "sequence " << shmemState_ -> sequence
+ << " for X server FD#" << fd_ << ".\n"
+ << logofs_flush;
+
+ T_timestamp startTs = getTimestamp();
+
+ #endif
+
+ while (isTimestamp(shmemState_ -> last) != 0)
+ {
+ if (handleWait(control -> ShmemTimeout) <= 0)
+ {
+ break;
+ }
+ #if defined(TEST) || defined(INFO)
+ else
+ {
+ *logofs << "handleShmemEvent: WARNING! Encoded events "
+ << "for FD#" << fd_ << " at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ }
+ #endif
+ }
+
+ if (isTimestamp(shmemState_ -> last) == 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleShmemEvent: Spent "
+ << diffTimestamp(startTs, getTimestamp()) << " Ms "
+ << "waiting for shared memory sequence for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+ }
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleShmemEvent: WARNING! Can't reset shared "
+ << "memory sequence for FD#" << fd_ << " after "
+ << diffTimestamp(shmemState_ -> last, getTimestamp())
+ << " Ms.\n" << logofs_flush;
+ #endif
+
+ return 0;
+}
+
+int ServerChannel::checkShmemEvent(unsigned char event, unsigned short sequence,
+ const unsigned char *buffer)
+{
+ if (isTimestamp(shmemState_ -> last) == 1 &&
+ sequence == shmemState_ -> sequence)
+ {
+ #ifdef TEST
+ *logofs << "checkShmemEvent: Reset shared memory sequence "
+ << shmemState_ -> sequence << " for FD#" << fd_
+ << " after " << diffTimestamp(shmemState_ -> last,
+ getTimestamp()) << " Ms.\n" << logofs_flush;
+ #endif
+
+ shmemState_ -> sequence = 0;
+ shmemState_ -> offset = 0;
+ shmemState_ -> last = nullTimestamp();
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "checkShmemEvent: Skipping past shared memory "
+ << "image sequence " << sequence << " for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ }
+ #endif
+
+ return 1;
+}
+
+int ServerChannel::checkShmemError(unsigned char error, unsigned short sequence,
+ const unsigned char *buffer)
+{
+ #ifdef TEST
+
+ *logofs << "checkShmemError: WARNING! Failed operation for "
+ << "FD#" << fd_ << " in stage " << shmemState_ -> stage
+ << " with ERR_CODE#" << (unsigned int) *(buffer + 1);
+
+ *logofs << " RES_ID#" << GetULONG(buffer + 4, bigEndian_);
+
+ *logofs << " MIN_OP#" << GetUINT(buffer + 8, bigEndian_);
+
+ *logofs << " MAJ_OP#" << (unsigned int) *(buffer + 10);
+
+ *logofs << " sequence " << sequence << ".\n";
+
+ *logofs << logofs_flush;
+
+ #endif
+
+ //
+ // If enabled flag is <= 0 we are still
+ // in the inizialization phase. In this
+ // case force presence to false.
+ //
+
+ if (shmemState_ -> enabled != 1)
+ {
+ if (shmemState_ -> present != 0)
+ {
+ #ifdef TEST
+ *logofs << "checkShmemError: Resetting shared memory "
+ << "presence flag for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ shmemState_ -> present = 0;
+ }
+
+ return 0;
+ }
+
+ if (shmemState_ -> sequence == sequence)
+ {
+ //
+ // Reset the sequence and timestamp.
+ //
+
+ shmemState_ -> sequence = 0;
+ shmemState_ -> offset = 0;
+ shmemState_ -> last = nullTimestamp();
+ }
+
+ return 1;
+}
+
+int ServerChannel::handleFontRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+{
+ //
+ // Send a synchronization request and use
+ // the reply to return the requested font
+ // path.
+ //
+
+ #ifdef TEST
+ *logofs << "handleFontRequest: Sending X_GetInputFocus request "
+ << "for FD#" << fd_ << " due to OPCODE#" << (unsigned int)
+ opcodeStore_ -> getFontParameters << ".\n"
+ << logofs_flush;
+ #endif
+
+ opcode = X_GetInputFocus;
+
+ size = 4;
+ buffer = writeBuffer_.addMessage(size);
+
+ sequenceQueue_.push(clientSequence_, X_GetInputFocus,
+ opcodeStore_ -> getFontParameters);
+
+ return 1;
+}
+
+int ServerChannel::handleFontReply(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size)
+{
+ #ifdef TEST
+ *logofs << "handleFontReply: Encoding font operation "
+ << "reply with size " << size << ".\n"
+ << logofs_flush;
+ #endif
+
+ char data[256];
+
+ if (fontPort_ != -1)
+ {
+ sprintf(data + 1, "tcp/localhost:%d", fontPort_);
+ }
+ else
+ {
+ *(data + 1) = '\0';
+ }
+
+ *data = strlen(data + 1);
+
+ unsigned char *next = (unsigned char *) data;
+
+ unsigned int length = (unsigned int) (*next++);
+
+ encodeBuffer.encodeValue(length, 8);
+
+ encodeBuffer.encodeTextData(next, length);
+
+ return 1;
+}
+
+int ServerChannel::handleCacheRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+{
+ unsigned int mask;
+
+ decodeBuffer.decodeCachedValue(mask, 32, clientCache_ ->
+ setCacheParametersCache);
+
+ splitState_.save = (mask >> 8) & 0xff;
+ splitState_.load = mask & 0xff;
+
+ //
+ // Just to be sure. We should never
+ // receive this request if connected
+ // to an old proxy version.
+ //
+
+ handleSplitEnable();
+
+ #ifdef TEST
+ *logofs << "handleCacheRequest: Set cache parameters to "
+ << "save " << splitState_.save << " load "
+ << splitState_.load << ".\n" << logofs_flush;
+ #endif
+
+ handleNullRequest(opcode, buffer, size);
+
+ return 1;
+}
+
+int ServerChannel::handleStartSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+{
+ //
+ // Prepare for the split for the selected
+ // resource. Old proxy versions only use
+ // the split store at position 0.
+ //
+
+ if (control -> isProtoStep7() == 1)
+ {
+ unsigned char resource;
+
+ decodeBuffer.decodeCachedValue(resource, 8,
+ clientCache_ -> resourceCache);
+
+ splitState_.resource = resource;
+
+ splitState_.current = splitState_.resource;
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleStartSplitRequest: SPLIT! Registered id "
+ << splitState_.resource << " as resource "
+ << "waiting for a split.\n" << logofs_flush;
+ #endif
+ }
+ #if defined(TEST) || defined(SPLIT)
+ else
+ {
+ *logofs << "handleStartSplitRequest: SPLIT! Assuming fake id "
+ << splitState_.current << " as resource "
+ << "waiting for a split.\n" << logofs_flush;
+ }
+ #endif
+
+ handleNullRequest(opcode, buffer, size);
+
+ return 1;
+}
+
+int ServerChannel::handleEndSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+{
+ //
+ // Verify that the agent resource matches.
+ //
+
+ if (control -> isProtoStep7() == 1)
+ {
+ unsigned char resource;
+
+ decodeBuffer.decodeCachedValue(resource, 8,
+ clientCache_ -> resourceCache);
+
+ #ifdef TEST
+
+ if (splitState_.resource == nothing)
+ {
+ #ifdef PANIC
+ *logofs << "handleEndSplitRequest: PANIC! SPLIT! Received an end of "
+ << "split for resource id " << (unsigned int) *(buffer + 1)
+ << " without a previous start.\n"
+ << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+ else if (resource != splitState_.resource)
+ {
+ #ifdef PANIC
+ *logofs << "handleEndSplitRequest: PANIC! SPLIT! Invalid resource id "
+ << resource << " received while waiting for resource id "
+ << splitState_.resource << ".\n" << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+
+ #endif
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleEndSplitRequest: SPLIT! Reset id "
+ << splitState_.resource << " as resource "
+ << "selected for splits.\n" << logofs_flush;
+ #endif
+
+ splitState_.resource = nothing;
+
+ handleNullRequest(opcode, buffer, size);
+
+ return 1;
+}
+
+int ServerChannel::handleSplitChecksum(DecodeBuffer &decodeBuffer, T_checksum &checksum)
+{
+ unsigned int receive;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ decodeBuffer.decodeBoolValue(receive);
+ }
+ else
+ {
+ receive = (control -> ImageCacheEnableLoad == 1 ||
+ control -> ImageCacheEnableSave == 1);
+ }
+
+ if (receive == 1)
+ {
+ checksum = new md5_byte_t[MD5_LENGTH];
+
+ for (unsigned int i = 0; i < MD5_LENGTH; i++)
+ {
+ decodeBuffer.decodeValue(receive, 8);
+
+ if (checksum != NULL)
+ {
+ checksum[i] = (unsigned char) receive;
+ }
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitChecksum: SPLIT! Received checksum "
+ << "[" << DumpChecksum(checksum) << "].\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+ }
+
+ return 0;
+}
+
+void ServerChannel::handleShmemStateAlloc()
+{
+ if (shmemState_ == NULL)
+ {
+ shmemState_ = new T_shmem_state();
+
+ shmemState_ -> stage = -1;
+ shmemState_ -> present = -1;
+ shmemState_ -> enabled = -1;
+
+ shmemState_ -> segment = -1;
+ shmemState_ -> id = -1;
+ shmemState_ -> address = NULL;
+ shmemState_ -> size = 0;
+
+ shmemState_ -> opcode = 0xff;
+ shmemState_ -> event = 0xff;
+ shmemState_ -> error = 0xff;
+
+ shmemState_ -> sequence = 0;
+ shmemState_ -> offset = 0;
+ shmemState_ -> last = nullTimestamp();
+
+ shmemState_ -> checked = 0;
+ }
+}
+
+void ServerChannel::handleShmemStateRemove()
+{
+ if (shmemState_ != NULL)
+ {
+ if (shmemState_ -> address != NULL)
+ {
+ shmdt((char *) shmemState_ -> address);
+ }
+
+ if (shmemState_ -> id > 0)
+ {
+ shmctl(shmemState_ -> id, IPC_RMID, 0);
+ }
+
+ delete shmemState_;
+
+ shmemState_ = NULL;
+ }
+}
+
+void ServerChannel::handleUnpackStateInit(int resource)
+{
+ if (unpackState_[resource] == NULL)
+ {
+ unpackState_[resource] = new T_unpack_state();
+
+ if (unpackState_[resource] == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "handleUnpackStateInit: PANIC! Can't allocate "
+ << "memory for unpack state in context [A].\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory for "
+ << "unpack state in context [A].\n";
+
+ HandleAbort();
+ }
+
+ unpackState_[resource] -> geometry = NULL;
+ unpackState_[resource] -> colormap = NULL;
+ unpackState_[resource] -> alpha = NULL;
+ }
+}
+
+void ServerChannel::handleUnpackAllocGeometry(int resource)
+{
+ if (unpackState_[resource] -> geometry == NULL)
+ {
+ unpackState_[resource] -> geometry = new T_geometry();
+
+ if (unpackState_[resource] -> geometry == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "handleUnpackAllocGeometry: PANIC! Can't allocate "
+ << "memory for unpack state in context [B].\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory for "
+ << "unpack state in context [B].\n";
+
+ HandleAbort();
+ }
+
+ unpackState_[resource] -> geometry -> depth1_bpp = 4;
+ unpackState_[resource] -> geometry -> depth4_bpp = 4;
+ unpackState_[resource] -> geometry -> depth8_bpp = 8;
+ unpackState_[resource] -> geometry -> depth16_bpp = 16;
+ unpackState_[resource] -> geometry -> depth24_bpp = 32;
+ unpackState_[resource] -> geometry -> depth32_bpp = 32;
+
+ unpackState_[resource] -> geometry -> red_mask = 0xff0000;
+ unpackState_[resource] -> geometry -> green_mask = 0x00ff00;
+ unpackState_[resource] -> geometry -> blue_mask = 0x0000ff;
+
+ unpackState_[resource] -> geometry -> image_byte_order = imageByteOrder_;
+ unpackState_[resource] -> geometry -> bitmap_bit_order = bitmapBitOrder_;
+ unpackState_[resource] -> geometry -> scanline_unit = scanlineUnit_;
+ unpackState_[resource] -> geometry -> scanline_pad = scanlinePad_;
+ }
+}
+
+void ServerChannel::handleUnpackAllocColormap(int resource)
+{
+ if (unpackState_[resource] -> colormap == NULL)
+ {
+ unpackState_[resource] -> colormap = new T_colormap();
+
+ if (unpackState_[resource] -> colormap == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "handleUnpackAllocColormap: PANIC! Can't allocate "
+ << "memory for unpack state in context [C].\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory for "
+ << "unpack state in context [C].\n";
+
+ HandleAbort();
+ }
+
+ unpackState_[resource] -> colormap -> entries = 0;
+ unpackState_[resource] -> colormap -> data = NULL;
+ }
+}
+
+void ServerChannel::handleUnpackAllocAlpha(int resource)
+{
+ if (unpackState_[resource] -> alpha == NULL)
+ {
+ unpackState_[resource] -> alpha = new T_alpha();
+
+ if (unpackState_[resource] -> alpha == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "handleUnpackAllocAlpha: PANIC! Can't allocate "
+ << "memory for unpack state in context [D].\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory for "
+ << "unpack state in context [D].\n";
+
+ HandleAbort();
+ }
+
+ unpackState_[resource] -> alpha -> entries = 0;
+ unpackState_[resource] -> alpha -> data = NULL;
+ }
+}
+
+void ServerChannel::handleUnpackStateRemove(int resource)
+{
+ if (unpackState_[resource] != NULL)
+ {
+ delete unpackState_[resource] -> geometry;
+
+ if (unpackState_[resource] -> colormap != NULL)
+ {
+ delete [] unpackState_[resource] -> colormap -> data;
+ }
+
+ delete unpackState_[resource] -> colormap;
+
+ if (unpackState_[resource] -> alpha != NULL)
+ {
+ delete [] unpackState_[resource] -> alpha -> data;
+ }
+
+ delete unpackState_[resource] -> alpha;
+
+ delete unpackState_[resource];
+
+ unpackState_[resource] = NULL;
+ }
+}
+
+void ServerChannel::handleEncodeCharInfo(const unsigned char *nextSrc, EncodeBuffer &encodeBuffer)
+{
+ unsigned int value = GetUINT(nextSrc, bigEndian_) |
+ (GetUINT(nextSrc + 10, bigEndian_) << 16);
+
+ encodeBuffer.encodeCachedValue(value, 32,
+ *serverCache_ -> queryFontCharInfoCache[0], 6);
+
+ nextSrc += 2;
+
+ for (unsigned int i = 1; i < 5; i++)
+ {
+ unsigned int value = GetUINT(nextSrc, bigEndian_);
+
+ nextSrc += 2;
+
+ encodeBuffer.encodeCachedValue(value, 16,
+ *serverCache_ -> queryFontCharInfoCache[i], 6);
+ }
+}
+
+int ServerChannel::setBigEndian(int flag)
+{
+ bigEndian_ = flag;
+
+ readBuffer_.setBigEndian(flag);
+
+ return 1;
+}
+
+int ServerChannel::setReferences()
+{
+ #ifdef TEST
+ *logofs << "ServerChannel: Initializing the static "
+ << "members for the server channels.\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef REFERENCES
+
+ references_ = 0;
+
+ #endif
+
+ return 1;
+}
diff --git a/nxcomp/ServerChannel.h b/nxcomp/ServerChannel.h
new file mode 100644
index 000000000..6536db829
--- /dev/null
+++ b/nxcomp/ServerChannel.h
@@ -0,0 +1,536 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef ServerChannel_H
+#define ServerChannel_H
+
+#include "List.h"
+#include "Channel.h"
+
+#include "SequenceQueue.h"
+
+#include "ServerReadBuffer.h"
+
+#include "Unpack.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// How many sequence numbers of split commit
+// requests we are going to save in order to
+// mask errors.
+//
+
+#define MAX_COMMIT_SEQUENCE_QUEUE 16
+
+//
+// Define this to know when a channel
+// is created or destroyed.
+//
+
+#undef REFERENCES
+
+//
+// This class implements the X server
+// side compression of X protocol.
+//
+
+class ServerChannel : public Channel
+{
+ public:
+
+ ServerChannel(Transport *transport, StaticCompressor *compressor);
+
+ virtual ~ServerChannel();
+
+ virtual int handleRead(EncodeBuffer &encodeBuffer, const unsigned char *message,
+ unsigned int length);
+
+ virtual int handleWrite(const unsigned char *message, unsigned int length);
+
+ virtual int handleSplit(EncodeBuffer &encodeBuffer, MessageStore *store,
+ T_store_action action, int position, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size)
+ {
+ return 0;
+ }
+
+ virtual int handleSplit(DecodeBuffer &decodeBuffer, MessageStore *store,
+ T_store_action action, int position, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size);
+
+ virtual int handleSplit(EncodeBuffer &encodeBuffer)
+ {
+ return 0;
+ }
+
+ virtual int handleSplit(DecodeBuffer &decodeBuffer);
+
+ virtual int handleSplitEvent(EncodeBuffer &encodeBuffer, Split *split);
+
+ virtual int handleSplitEvent(DecodeBuffer &decodeBuffer)
+ {
+ return 0;
+ }
+
+ //
+ // Send the last motion notify event
+ // received from the X server to the
+ // remote proxy.
+ //
+
+ virtual int handleMotion(EncodeBuffer &encodeBuffer);
+
+ virtual int handleCompletion(EncodeBuffer &encodeBuffer)
+ {
+ return 0;
+ }
+
+ virtual int handleConfiguration();
+
+ virtual int handleFinish();
+
+ virtual int handleAsyncEvents();
+
+ virtual int needSplit() const
+ {
+ return 0;
+ }
+
+ virtual int needMotion() const
+ {
+ return (lastMotion_[0] != '\0');
+ }
+
+ virtual T_channel_type getType() const
+ {
+ return channel_x11;
+ }
+
+ int setBigEndian(int flag);
+
+ //
+ // Initialize the static members.
+ //
+
+ static int setReferences();
+
+ private:
+
+ int handleFastReadReply(EncodeBuffer &encodeBuffer, const unsigned char &opcode,
+ const unsigned char *&buffer, const unsigned int &size);
+
+ int handleFastReadEvent(EncodeBuffer &encodeBuffer, const unsigned char &opcode,
+ const unsigned char *&buffer, const unsigned int &size);
+
+ int handleFastWriteRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size);
+
+ //
+ // Handle the fake authorization cookie
+ // and the X server's reply.
+ //
+
+ int handleAuthorization(unsigned char *buffer);
+ int handleAuthorization(const unsigned char *buffer, int size);
+
+ //
+ // Set the unpack colormap and the alpha
+ // blending data to be used to unpack
+ // images.
+ //
+
+ int handleGeometry(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size);
+
+ int handleColormap(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size);
+
+ int handleAlpha(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size);
+
+ //
+ // Manage the decoded buffer to unpack
+ // the image and move the data to the
+ // shared memory segment.
+ //
+
+ int handleImage(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size);
+
+ //
+ // Uncompress a packed image in one
+ // or more graphic X requests.
+ //
+
+ int handleUnpack(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size);
+
+ //
+ // Move the image to the shared
+ // memory buffer.
+ //
+
+ int handleShmem(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size);
+
+ //
+ // Handle suppression of error on
+ // commit of image splits.
+ //
+
+ void initCommitQueue();
+
+ void updateCommitQueue(unsigned short sequence);
+
+ int checkCommitError(unsigned char error, unsigned short sequence,
+ const unsigned char *buffer);
+
+ void clearCommitQueue()
+ {
+ if (commitSequenceQueue_[0] != 0)
+ {
+ initCommitQueue();
+ }
+ }
+
+ //
+ // Check if the user pressed the
+ // CTRL+ALT+SHIFT+ESC keystroke.
+ //
+
+ int checkKeyboardEvent(unsigned char event, unsigned short sequence,
+ const unsigned char *buffer);
+
+ //
+ // Other utilities.
+ //
+
+ void handleEncodeCharInfo(const unsigned char *nextSrc, EncodeBuffer &encodeBuffer);
+
+ //
+ // Handle the MIT-SHM initialization
+ // messages exchanged with the remote
+ // proxy.
+ //
+
+ int handleShmemRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size);
+
+
+ int handleShmemReply(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned int stage, const unsigned char *buffer,
+ const unsigned int size);
+
+ //
+ // Try to read more events in the attempt to
+ // get the MIT-SHM image completion event
+ // from the X server.
+ //
+
+ int handleShmemEvent();
+
+ //
+ // Handle the MIT-SHM events as they are read
+ // from the socket.
+ //
+
+ int checkShmemEvent(unsigned char event, unsigned short sequence,
+ const unsigned char *buffer);
+
+ int checkShmemError(unsigned char error, unsigned short sequence,
+ const unsigned char *buffer);
+
+ //
+ // Query the port used to tunnel
+ // the font server connections.
+ //
+
+ int handleFontRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size);
+
+ int handleFontReply(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size);
+
+ //
+ // Set the cache policy for image
+ // requests.
+ //
+
+ int handleCacheRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size);
+
+ //
+ // Decode the start and end split
+ // requests.
+ //
+
+ int handleStartSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size);
+
+ int handleEndSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size);
+
+ //
+ // Remove the split store and the
+ // incomplete messages from the
+ // memory cache.
+ //
+
+ int handleAbortSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size);
+
+ //
+ // Send the split requests to the
+ // X server once they have been
+ // recomposed.
+ //
+
+ int handleCommitSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size);
+
+ int handleSplitChecksum(DecodeBuffer &decodeBuffer, T_checksum &checksum);
+
+ void handleSplitEnable()
+ {
+ if (control -> isProtoStep7() == 0)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitEnable: WARNING! Disabling load "
+ << "and save with an old proxy version.\n"
+ << logofs_flush;
+ #endif
+
+ splitState_.save = 0;
+ splitState_.load = 0;
+ }
+ }
+
+ //
+ // Allocate and free the shared memory
+ // support resources.
+ //
+
+ void handleShmemStateAlloc();
+ void handleShmemStateRemove();
+
+ //
+ // Temporary storage for the image info.
+ //
+
+ void handleImageStateAlloc(unsigned char opcode)
+ {
+ if (imageState_ == NULL)
+ {
+ imageState_ = new T_image_state();
+ }
+
+ imageState_ -> opcode = opcode;
+ }
+
+ void handleImageStateRemove()
+ {
+ if (imageState_ != NULL)
+ {
+ delete imageState_;
+
+ imageState_ = NULL;
+ }
+ }
+
+ //
+ // Store the information needed to unpack
+ // images per each known agent's client.
+ //
+
+ void handleUnpackStateInit(int resource);
+
+ void handleUnpackAllocGeometry(int resource);
+ void handleUnpackAllocColormap(int resource);
+ void handleUnpackAllocAlpha(int resource);
+
+ void handleUnpackStateRemove(int resource);
+
+ typedef struct
+ {
+ T_geometry *geometry;
+ T_colormap *colormap;
+ T_alpha *alpha;
+
+ } T_unpack_state;
+
+ T_unpack_state *unpackState_[256];
+
+ //
+ // Own read buffer. It is able to identify
+ // full messages read from X descriptor.
+ //
+
+ ServerReadBuffer readBuffer_;
+
+ //
+ // Sequence number of last request coming
+ // from X client or X server.
+ //
+
+ unsigned int clientSequence_;
+ unsigned int serverSequence_;
+
+ //
+ // Used to identify replies based on sequence
+ // number of original request.
+ //
+
+ SequenceQueue sequenceQueue_;
+
+ //
+ // Last motion notify read from the X server.
+ //
+
+ unsigned char lastMotion_[32];
+
+ //
+ // Sequence numbers of last auto-generated
+ // put image requests. Needed to intercept
+ // and suppress errors generated by such
+ // requests.
+ //
+
+ unsigned int commitSequenceQueue_[MAX_COMMIT_SEQUENCE_QUEUE];
+
+ //
+ // Let agent select which expose
+ // events is going to receive.
+ //
+
+ unsigned int enableExpose_;
+ unsigned int enableGraphicsExpose_;
+ unsigned int enableNoExpose_;
+
+ //
+ // Used in initialization and handling
+ // of MIT-SHM shared memory put images.
+ //
+
+ typedef struct
+ {
+ int stage;
+ int present;
+ int enabled;
+ int segment;
+ int id;
+ void *address;
+ unsigned int size;
+
+ unsigned char opcode;
+ unsigned char event;
+ unsigned char error;
+
+ unsigned int sequence;
+ unsigned int offset;
+ T_timestamp last;
+
+ unsigned int checked;
+
+ } T_shmem_state;
+
+ T_shmem_state *shmemState_;
+
+ //
+ // Used to pass current image data between
+ // the different decompression stages.
+ //
+
+ typedef struct
+ {
+ unsigned char opcode;
+
+ unsigned int drawable;
+ unsigned int gcontext;
+
+ unsigned char method;
+
+ unsigned char format;
+ unsigned char srcDepth;
+ unsigned char dstDepth;
+
+ unsigned int srcLength;
+ unsigned int dstLength;
+ unsigned int dstLines;
+
+ short int srcX;
+ short int srcY;
+ unsigned short srcWidth;
+ unsigned short srcHeight;
+
+ short int dstX;
+ short int dstY;
+ unsigned short dstWidth;
+ unsigned short dstHeight;
+
+ unsigned char leftPad;
+
+ } T_image_state;
+
+ T_image_state *imageState_;
+
+ //
+ // The flags is set according to the
+ // split load and save policy set by
+ // the encoding side.
+ //
+
+ typedef struct
+ {
+ int resource;
+ int current;
+ int load;
+ int save;
+ int commit;
+
+ } T_split_state;
+
+ T_split_state splitState_;
+
+ //
+ // List of agent resources.
+ //
+
+ List splitResources_;
+
+ //
+ // Keep track of object creation and
+ // deletion.
+ //
+
+ private:
+
+ #ifdef REFERENCES
+
+ static int references_;
+
+ #endif
+};
+
+#endif /* ServerChannel_H */
diff --git a/nxcomp/ServerProxy.cpp b/nxcomp/ServerProxy.cpp
new file mode 100644
index 000000000..0a72fc301
--- /dev/null
+++ b/nxcomp/ServerProxy.cpp
@@ -0,0 +1,605 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include <unistd.h>
+
+#include "NXalert.h"
+
+#include "Socket.h"
+
+#include "ServerProxy.h"
+
+#include "ServerChannel.h"
+#include "GenericChannel.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Log the operations related to sending
+// and receiving the control tokens.
+//
+
+#undef TOKEN
+
+ServerProxy::ServerProxy(int proxyFd) : Proxy(proxyFd)
+
+{
+ xServerAddrFamily_ = -1;
+ xServerAddrLength_ = 0;
+
+ xServerAddr_ = NULL;
+ xServerDisplay_ = NULL;
+
+ cupsServerPort_ = -1;
+ smbServerPort_ = -1;
+ mediaServerPort_ = -1;
+ httpServerPort_ = -1;
+
+ fontServerPort_ = NULL;
+
+ #ifdef DEBUG
+ *logofs << "ServerProxy: Created new object at " << this
+ << ".\n" << logofs_flush;
+ #endif
+}
+
+ServerProxy::~ServerProxy()
+{
+ delete xServerAddr_;
+
+ delete [] xServerDisplay_;
+
+ delete [] fontServerPort_;
+
+ #ifdef DEBUG
+ *logofs << "ServerProxy: Deleted object at " << this
+ << ".\n" << logofs_flush;
+ #endif
+}
+
+void ServerProxy::handleDisplayConfiguration(const char *xServerDisplay, int xServerAddrFamily,
+ sockaddr *xServerAddr, unsigned int xServerAddrLength)
+{
+ delete xServerAddr_;
+
+ xServerAddr_ = xServerAddr;
+
+ xServerAddrFamily_ = xServerAddrFamily;
+ xServerAddrLength_ = xServerAddrLength;
+
+ delete [] xServerDisplay_;
+
+ xServerDisplay_ = new char[strlen(xServerDisplay) + 1];
+
+ strcpy(xServerDisplay_, xServerDisplay);
+
+ #ifdef DEBUG
+ *logofs << "ServerProxy: Set display configuration to display '"
+ << xServerDisplay_ << "'.\n"
+ << logofs_flush;
+ #endif
+}
+
+void ServerProxy::handlePortConfiguration(int cupsServerPort, int smbServerPort, int mediaServerPort,
+ int httpServerPort, const char *fontServerPort)
+{
+ cupsServerPort_ = cupsServerPort;
+ smbServerPort_ = smbServerPort;
+ mediaServerPort_ = mediaServerPort;
+ httpServerPort_ = httpServerPort;
+
+ delete [] fontServerPort_;
+
+ fontServerPort_ = new char[strlen(fontServerPort) + 1];
+
+ strcpy(fontServerPort_, fontServerPort);
+
+ #ifdef DEBUG
+ *logofs << "ServerProxy: Set port configuration to CUPS "
+ << cupsServerPort_ << ", SMB " << smbServerPort_
+ << ", media " << mediaServerPort_ << ", HTTP "
+ << httpServerPort_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+
+int ServerProxy::handleNewConnection(T_channel_type type, int clientFd)
+{
+ switch (type)
+ {
+ case channel_font:
+ {
+ return handleNewGenericConnection(clientFd, channel_font, "font");
+ }
+ case channel_slave:
+ {
+ return handleNewSlaveConnection(clientFd);
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "ServerProxy: PANIC! Unsupported channel with type '"
+ << getTypeName(type) << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Unsupported channel with type '"
+ << getTypeName(type) << "'.\n";
+
+ return -1;
+ }
+ }
+}
+
+int ServerProxy::handleNewConnectionFromProxy(T_channel_type type, int channelId)
+{
+ switch (type)
+ {
+ case channel_x11:
+ {
+ return handleNewXConnectionFromProxy(channelId);
+ }
+ case channel_cups:
+ {
+ return handleNewGenericConnectionFromProxy(channelId, channel_cups, "localhost",
+ cupsServerPort_, "CUPS");
+ }
+ case channel_smb:
+ {
+ return handleNewGenericConnectionFromProxy(channelId, channel_smb, getComputerName(),
+ smbServerPort_, "SMB");
+ }
+ case channel_media:
+ {
+ return handleNewGenericConnectionFromProxy(channelId, channel_media, "localhost",
+ mediaServerPort_, "media");
+ }
+ case channel_http:
+ {
+ return handleNewGenericConnectionFromProxy(channelId, channel_http, getComputerName(),
+ httpServerPort_, "HTTP");
+ }
+ case channel_slave:
+ {
+ return handleNewSlaveConnectionFromProxy(channelId);
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "ServerProxy: PANIC! Unsupported channel with type '"
+ << getTypeName(type) << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Unsupported channel with type '"
+ << getTypeName(type) << "'.\n";
+
+ return -1;
+ }
+ }
+}
+
+int ServerProxy::handleNewAgentConnection(Agent *agent)
+{
+ #ifdef PANIC
+ *logofs << "ServerProxy: PANIC! Can't create an agent "
+ << "connection at this side.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't create an agent "
+ << "connection at this side.\n";
+
+ return -1;
+}
+
+int ServerProxy::handleNewXConnection(int clientFd)
+{
+ #ifdef PANIC
+ *logofs << "ServerProxy: PANIC! Can't create a new X channel "
+ << "with FD#" << clientFd << " at this side.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't create a new X channel "
+ << "with FD#" << clientFd << " at this side.\n";
+
+ return -1;
+}
+
+int ServerProxy::handleNewXConnectionFromProxy(int channelId)
+{
+ //
+ // Connect to the real X server.
+ //
+
+ int retryConnect = control -> OptionServerRetryConnect;
+
+ int xServerFd;
+
+ for (;;)
+ {
+ xServerFd = socket(xServerAddrFamily_, SOCK_STREAM, PF_UNSPEC);
+
+ if (xServerFd < 0)
+ {
+ #ifdef PANIC
+ *logofs << "ServerProxy: PANIC! Call to socket failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to socket failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "ServerProxy: Trying to connect to X server '"
+ << xServerDisplay_ << "'.\n" << logofs_flush;
+ #endif
+
+ int result = connect(xServerFd, xServerAddr_, xServerAddrLength_);
+
+ getNewTimestamp();
+
+ if (result < 0)
+ {
+ #ifdef WARNING
+ *logofs << "ServerProxy: WARNING! Connection to '"
+ << xServerDisplay_ << "' failed with error '"
+ << ESTR() << "'. Retrying.\n" << logofs_flush;
+ #endif
+
+ close(xServerFd);
+
+ if (--retryConnect == 0)
+ {
+ #ifdef PANIC
+ *logofs << "ServerProxy: PANIC! Connection to '"
+ << xServerDisplay_ << "' for channel ID#"
+ << channelId << " failed. Error is "
+ << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Connection to '"
+ << xServerDisplay_ << "' failed. Error is "
+ << EGET() << " '" << ESTR() << "'.\n";
+
+ close(xServerFd);
+
+ return -1;
+ }
+
+ if (activeChannels_.getSize() == 0)
+ {
+ sleep(2);
+ }
+ else
+ {
+ sleep(1);
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ assignChannelMap(channelId, xServerFd);
+
+ #ifdef TEST
+ *logofs << "ServerProxy: X server descriptor FD#" << xServerFd
+ << " mapped to channel ID#" << channelId << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Turn queuing off for path proxy-to-X-server.
+ //
+
+ if (control -> OptionServerNoDelay == 1)
+ {
+ SetNoDelay(xServerFd, control -> OptionServerNoDelay);
+ }
+
+ //
+ // If requested, set the size of the TCP send
+ // and receive buffers.
+ //
+
+ if (control -> OptionServerSendBuffer != -1)
+ {
+ SetSendBuffer(xServerFd, control -> OptionServerSendBuffer);
+ }
+
+ if (control -> OptionServerReceiveBuffer != -1)
+ {
+ SetReceiveBuffer(xServerFd, control -> OptionServerReceiveBuffer);
+ }
+
+ if (allocateTransport(xServerFd, channelId) < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Starting from protocol level 3 client and server
+ // caches are created in proxy and shared between all
+ // channels. If remote proxy has older protocol level
+ // pointers are NULL and channels must create their
+ // own instances.
+ //
+
+ channels_[channelId] = new ServerChannel(transports_[channelId], compressor_);
+
+ if (channels_[channelId] == NULL)
+ {
+ deallocateTransport(channelId);
+
+ return -1;
+ }
+
+ increaseChannels(channelId);
+
+ //
+ // Propagate channel stores and caches to the new
+ // channel.
+ //
+
+ channels_[channelId] -> setOpcodes(opcodeStore_);
+
+ channels_[channelId] -> setStores(clientStore_, serverStore_);
+
+ channels_[channelId] -> setCaches(clientCache_, serverCache_);
+
+ int port = atoi(fontServerPort_);
+
+ if (port > 0)
+ {
+ channels_[channelId] -> setPorts(port);
+ }
+
+ //
+ // Let channel configure itself according
+ // to control parameters.
+ //
+
+ channels_[channelId] -> handleConfiguration();
+
+ //
+ // Check if we have successfully loaded the
+ // selected cache and, if not, remove it
+ // from disk.
+ //
+
+ handleCheckLoad();
+
+ return 1;
+}
+
+//
+// Check if we still need to drop a channel. We need
+// to check this explicitly at the time we receive a
+// request to load or save the cache because we could
+// receive the control message before having entered
+// the function handling the channel events.
+//
+
+int ServerProxy::handleCheckDrop()
+{
+ T_list channelList = activeChannels_.copyList();
+
+ for (T_list::iterator j = channelList.begin();
+ j != channelList.end(); j++)
+ {
+ int channelId = *j;
+
+ if (channels_[channelId] != NULL &&
+ (channels_[channelId] -> getDrop() == 1 ||
+ channels_[channelId] -> getClosing() == 1))
+ {
+ #ifdef TEST
+ *logofs << "ServerProxy: Dropping the descriptor FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << ".\n" << logofs_flush;
+ #endif
+
+ handleDrop(channelId);
+ }
+ }
+
+ return 1;
+}
+
+int ServerProxy::handleCheckLoad()
+{
+ //
+ // Check if we just created the first X channel
+ // but the client side didn't tell us to load
+ // the cache selected at the session negotiation.
+ // This is very likely because the load operation
+ // failed at the remote side, for example because
+ // the cache was invalid or corrupted.
+ //
+
+ int channelCount = getChannels(channel_x11);
+
+ if (channelCount != 1)
+ {
+ return 0;
+ }
+
+ if (control -> PersistentCacheEnableLoad == 1 &&
+ control -> PersistentCachePath != NULL &&
+ control -> PersistentCacheName != NULL &&
+ isTimestamp(timeouts_.loadTs) == 0)
+ {
+ #ifdef WARNING
+ *logofs << "ServerProxy: WARNING! Cache file '" << control -> PersistentCachePath
+ << "/" << control -> PersistentCacheName << "' not loaded.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Remove the cache file.
+ //
+
+ #ifdef WARNING
+ *logofs << "ServerProxy: WARNING! Removing supposedly "
+ << "incompatible cache '" << control -> PersistentCachePath
+ << "/" << control -> PersistentCacheName
+ << "'.\n" << logofs_flush;
+ #endif
+
+ handleResetPersistentCache();
+ }
+
+ return 1;
+}
+
+int ServerProxy::handleLoadFromProxy()
+{
+ //
+ // Be sure we drop any confirmed channel.
+ //
+
+ handleCheckDrop();
+
+ //
+ // Check that either no X channel is
+ // remaining or we are inside a reset.
+ //
+
+ int channelCount = getChannels(channel_x11);
+
+ if (channelCount > 0)
+ {
+ #ifdef PANIC
+ *logofs << "ServerProxy: PANIC! Protocol violation "
+ << "in command load with " << channelCount
+ << " channels.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Protocol violation "
+ << "in command load from proxy.\n";
+
+ return -1;
+ }
+ else if (handleLoadStores() < 0)
+ {
+ #ifdef WARNING
+ *logofs << "ServerProxy: WARNING! Failed to load content "
+ << "of persistent cache.\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int ServerProxy::handleSaveFromProxy()
+{
+ //
+ // Be sure we drop any confirmed channel.
+ //
+
+ handleCheckDrop();
+
+ //
+ // Now verify that all channels are gone.
+ //
+
+ int channelCount = getChannels(channel_x11);
+
+ if (channelCount > 0)
+ {
+ #ifdef PANIC
+ *logofs << "ServerProxy: PANIC! Protocol violation "
+ << "in command save with " << channelCount
+ << " channels.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Protocol violation "
+ << "in command save from proxy.\n";
+
+ return -1;
+ }
+ else if (handleSaveStores() < 0)
+ {
+ #ifdef PANIC
+ *logofs << "ServerProxy: PANIC! Failed to save stores "
+ << "to persistent cache.\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int ServerProxy::handleSaveAllStores(ostream *cachefs, md5_state_t *md5StateStream,
+ md5_state_t *md5StateClient) const
+{
+ if (clientStore_ -> saveRequestStores(cachefs, md5StateStream, md5StateClient,
+ discard_checksum, use_data) < 0)
+ {
+ return -1;
+ }
+ else if (serverStore_ -> saveReplyStores(cachefs, md5StateStream, md5StateClient,
+ use_checksum, discard_data) < 0)
+ {
+ return -1;
+ }
+ else if (serverStore_ -> saveEventStores(cachefs, md5StateStream, md5StateClient,
+ use_checksum, discard_data) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+int ServerProxy::handleLoadAllStores(istream *cachefs, md5_state_t *md5StateStream) const
+{
+ if (clientStore_ -> loadRequestStores(cachefs, md5StateStream,
+ discard_checksum, use_data) < 0)
+ {
+ return -1;
+ }
+ else if (serverStore_ -> loadReplyStores(cachefs, md5StateStream,
+ use_checksum, discard_data) < 0)
+ {
+ return -1;
+ }
+ else if (serverStore_ -> loadEventStores(cachefs, md5StateStream,
+ use_checksum, discard_data) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+}
diff --git a/nxcomp/ServerProxy.h b/nxcomp/ServerProxy.h
new file mode 100644
index 000000000..8c4a88410
--- /dev/null
+++ b/nxcomp/ServerProxy.h
@@ -0,0 +1,147 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef ServerProxy_H
+#define ServerProxy_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "Proxy.h"
+
+#include "Misc.h"
+
+//
+// Set the verbosity level.
+//
+
+#undef TEST
+#undef DEBUG
+
+class ServerProxy : public Proxy
+{
+ public:
+
+ ServerProxy(int proxyFd);
+
+ virtual ~ServerProxy();
+
+ virtual void handleDisplayConfiguration(const char *xServerDisplay, int xServerAddrFamily,
+ sockaddr *xServerAddr, unsigned int xServerAddrLength);
+
+ virtual void handlePortConfiguration(int cupsServerPort, int smbServerPort, int mediaServerPort,
+ int httpServerPort, const char *fontServerPort);
+
+ protected:
+
+ //
+ // Create a new channel.
+ //
+
+ virtual int handleNewConnection(T_channel_type type, int clientFd);
+
+ virtual int handleNewConnectionFromProxy(T_channel_type type, int channelId);
+
+ virtual int handleNewAgentConnection(Agent *agent);
+
+ virtual int handleNewXConnection(int clientFd);
+
+ virtual int handleNewXConnectionFromProxy(int channelId);
+
+ //
+ // Implement persistence according
+ // to our proxy mode.
+ //
+
+ virtual int handleLoad(T_load_type type)
+ {
+ return 0;
+ }
+
+ virtual int handleSave()
+ {
+ return 0;
+ }
+
+ virtual int handleAsyncEvents()
+ {
+ return 0;
+ }
+
+ virtual int handleLoadFromProxy();
+ virtual int handleSaveFromProxy();
+
+ virtual int handleSaveAllStores(ostream *cachefs, md5_state_t *md5StateStream,
+ md5_state_t *md5StateClient) const;
+
+ virtual int handleLoadAllStores(istream *cachefs, md5_state_t *md5StateStream) const;
+
+ int handleCheckDrop();
+ int handleCheckLoad();
+
+ //
+ // Utility function used to realize
+ // a new connection.
+ //
+
+ protected:
+
+ virtual int checkLocalChannelMap(int channelId)
+ {
+ if (control -> isProtoStep7() == 1)
+ {
+ return ((channelId & control -> ChannelMask) == 0);
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ private:
+
+ int xServerAddrFamily_;
+ sockaddr *xServerAddr_;
+ unsigned int xServerAddrLength_;
+
+ //
+ // This is the name of the X display where
+ // we are going to forward connections.
+ //
+
+ char *xServerDisplay_;
+
+ //
+ // Ports where to forward extended services'
+ // TCP connections.
+ //
+
+ int cupsServerPort_;
+ int smbServerPort_;
+ int mediaServerPort_;
+ int httpServerPort_;
+
+ //
+ // It will have to be passed to the channel
+ // so that it can set the port where the
+ // font server connections are tunneled.
+ //
+
+ char *fontServerPort_;
+};
+
+#endif /* ServerProxy_H */
diff --git a/nxcomp/ServerReadBuffer.cpp b/nxcomp/ServerReadBuffer.cpp
new file mode 100644
index 000000000..53c1dec57
--- /dev/null
+++ b/nxcomp/ServerReadBuffer.cpp
@@ -0,0 +1,235 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "ServerReadBuffer.h"
+#include "ServerChannel.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+unsigned int ServerReadBuffer::suggestedLength(unsigned int pendingLength)
+{
+ //
+ // Always read all the data that
+ // is available.
+ //
+
+ int readable = transport_ -> readable();
+
+ unsigned int readLength = (readable == -1 ? 0 : (unsigned int) readable);
+
+ if (readLength < pendingLength)
+ {
+ readLength = pendingLength;
+ }
+
+ //
+ // Even if the readable data is not
+ // enough to make a complete message,
+ // resize the buffer to accomodate
+ // it all.
+ //
+
+ if (pendingLength < remaining_)
+ {
+ readLength = remaining_;
+ }
+
+ return readLength;
+}
+
+int ServerReadBuffer::locateMessage(const unsigned char *start,
+ const unsigned char *end,
+ unsigned int &controlLength,
+ unsigned int &dataLength,
+ unsigned int &trailerLength)
+{
+ unsigned int size = end - start;
+
+ #ifdef TEST
+ *logofs << "ServerReadBuffer: Locating message for FD#"
+ << transport_ -> fd() << " with " << size
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ if (firstMessage_)
+ {
+ if (size < 8)
+ {
+ remaining_ = 8 - size;
+
+ #ifdef TEST
+ *logofs << "ServerReadBuffer: No message was located "
+ << "with remaining " << remaining_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ dataLength = 8 + (GetUINT(start + 6, bigEndian_) << 2);
+ }
+ else
+ {
+ if (size < 32)
+ {
+ remaining_ = 32 - size;
+
+ #ifdef TEST
+ *logofs << "ServerReadBuffer: No message was located "
+ << "with remaining " << remaining_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ if (*start == 1)
+ {
+ dataLength = 32 + (GetULONG(start + 4, bigEndian_) << 2);
+ }
+ else
+ {
+ dataLength = 32;
+ }
+
+ if (dataLength < 32)
+ {
+ #ifdef TEST
+ *logofs << "ServerReadBuffer: WARNING! Assuming length 32 "
+ << "for suspicious message of length " << dataLength
+ << ".\n" << logofs_flush;
+ #endif
+
+ dataLength = 32;
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "ServerReadBuffer: Length of the next message is "
+ << dataLength << ".\n" << logofs_flush;
+ #endif
+
+ if (size < dataLength)
+ {
+ remaining_ = dataLength - size;
+
+ #ifdef TEST
+ *logofs << "ServerReadBuffer: No message was located "
+ << "with remaining " << remaining_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ firstMessage_ = 0;
+
+ controlLength = 0;
+ trailerLength = 0;
+
+ remaining_ = 0;
+
+ #ifdef TEST
+ *logofs << "ServerReadBuffer: Located message with "
+ << "remaining " << remaining_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+//
+// Check if the data already read contains a
+// message matching the opcode and sequence,
+// starting at the given offset.
+//
+
+unsigned char *ServerReadBuffer::peekMessage(unsigned int &offset, unsigned char opcode,
+ unsigned short sequence)
+{
+ #ifdef TEST
+ *logofs << "ServerReadBuffer: Peeking message "
+ << "for FD#" << transport_ -> fd() << " with size "
+ << length_ << " offset " << offset << " opcode "
+ << (unsigned int) opcode << " and sequence "
+ << sequence << ".\n" << logofs_flush;
+ #endif
+
+ if (firstMessage_)
+ {
+ return NULL;
+ }
+
+ unsigned char *next = buffer_ + start_ + offset;
+ unsigned char *end = buffer_ + start_ + length_;
+
+ int found = 0;
+
+ while (end - next >= 32)
+ {
+ #ifdef DEBUG
+ *logofs << "ServerReadBuffer: Checking opcode "
+ << (unsigned int) *next << " sequence "
+ << GetUINT(next + 2, bigEndian_)
+ << " at " << next - buffer_ + start_
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (*next == opcode && GetUINT(next + 2, bigEndian_) == sequence)
+ {
+ found = 1;
+
+ break;
+ }
+ else if (*next == 1)
+ {
+ next += (32 + (GetULONG(next + 4, bigEndian_) << 2));
+ }
+ else
+ {
+ next += 32;
+ }
+ }
+
+ offset = next - buffer_ + start_;
+
+ if (found == 1)
+ {
+ #ifdef TEST
+ *logofs << "ServerReadBuffer: Found message at "
+ << "offset " << next - buffer_ + start_
+ << ".\n" << logofs_flush;
+ #endif
+
+ return next;
+ }
+
+ #ifdef TEST
+ *logofs << "ServerReadBuffer: Quitting loop at "
+ << "offset " << next - buffer_ + start_
+ << ".\n" << logofs_flush;
+ #endif
+
+ return NULL;
+}
diff --git a/nxcomp/ServerReadBuffer.h b/nxcomp/ServerReadBuffer.h
new file mode 100644
index 000000000..438e2f0da
--- /dev/null
+++ b/nxcomp/ServerReadBuffer.h
@@ -0,0 +1,65 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef ServerReadBuffer_H
+#define ServerReadBuffer_H
+
+#include "ReadBuffer.h"
+#include "Control.h"
+
+class ServerChannel;
+
+class ServerReadBuffer : public ReadBuffer
+{
+ public:
+
+ ServerReadBuffer(Transport *transport, ServerChannel *channel)
+
+ : ReadBuffer(transport), firstMessage_(1), channel_(channel)
+ {
+ }
+
+ virtual ~ServerReadBuffer()
+ {
+ }
+
+ void setBigEndian(int flag)
+ {
+ bigEndian_ = flag;
+ }
+
+ unsigned char *peekMessage(unsigned int &offset, unsigned char opcode,
+ unsigned short sequence);
+
+ protected:
+
+ virtual unsigned int suggestedLength(unsigned int pendingLength);
+
+ virtual int locateMessage(const unsigned char *start,
+ const unsigned char *end,
+ unsigned int &controlLength,
+ unsigned int &dataLength,
+ unsigned int &trailerLength);
+
+ int bigEndian_;
+
+ int firstMessage_;
+
+ ServerChannel *channel_;
+};
+
+#endif /* ServerReadBuffer_H */
diff --git a/nxcomp/ServerStore.cpp b/nxcomp/ServerStore.cpp
new file mode 100644
index 000000000..b0e13b1ad
--- /dev/null
+++ b/nxcomp/ServerStore.cpp
@@ -0,0 +1,171 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "ServerStore.h"
+
+//
+// Cached reply classes.
+//
+
+#include "GetImageReply.h"
+#include "ListFontsReply.h"
+#include "QueryFontReply.h"
+#include "GetPropertyReply.h"
+#include "GenericReply.h"
+
+//
+// Set the verbosity level.
+//
+
+#define WARNING
+#define PANIC
+#undef TEST
+
+ServerStore::ServerStore(StaticCompressor *compressor)
+{
+ if (logofs == NULL)
+ {
+ logofs = &cout;
+ }
+
+ for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++)
+ {
+ replies_[i] = NULL;
+ events_[i] = NULL;
+ }
+
+ replies_[X_ListFonts] = new ListFontsReplyStore(compressor);
+ replies_[X_QueryFont] = new QueryFontReplyStore(compressor);
+ replies_[X_GetImage] = new GetImageReplyStore(compressor);
+ replies_[X_GetProperty] = new GetPropertyReplyStore(compressor);
+
+ replies_[X_NXInternalGenericReply] = new GenericReplyStore(compressor);
+}
+
+ServerStore::~ServerStore()
+{
+ if (logofs == NULL)
+ {
+ logofs = &cout;
+ }
+
+ for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++)
+ {
+ delete replies_[i];
+ delete events_[i];
+ }
+}
+
+int ServerStore::saveReplyStores(ostream *cachefs, md5_state_t *md5StateStream,
+ md5_state_t *md5StateClient, T_checksum_action checksumAction,
+ T_data_action dataAction) const
+{
+ for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++)
+ {
+ if (replies_[i] != NULL &&
+ replies_[i] -> saveStore(cachefs, md5StateStream, md5StateClient,
+ checksumAction, dataAction,
+ storeBigEndian()) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "ServerStore: PANIC! Error saving reply store "
+ << "for OPCODE#" << (unsigned int) i << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error saving reply store "
+ << "for opcode '" << (unsigned int) i << "'.\n";
+
+ return -1;
+ }
+ }
+
+ return 1;
+}
+
+int ServerStore::saveEventStores(ostream *cachefs, md5_state_t *md5StateStream,
+ md5_state_t *md5StateClient, T_checksum_action checksumAction,
+ T_data_action dataAction) const
+{
+ for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++)
+ {
+ if (events_[i] != NULL &&
+ events_[i] -> saveStore(cachefs, md5StateStream, md5StateClient,
+ checksumAction, dataAction,
+ storeBigEndian()) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "ServerStore: PANIC! Error saving event store "
+ << "for OPCODE#" << (unsigned int) i << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error saving event store "
+ << "for opcode '" << (unsigned int) i << "'.\n";
+
+ return -1;
+ }
+ }
+
+ return 1;
+}
+
+int ServerStore::loadReplyStores(istream *cachefs, md5_state_t *md5StateStream,
+ T_checksum_action checksumAction, T_data_action dataAction) const
+{
+ for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++)
+ {
+ if (replies_[i] != NULL &&
+ replies_[i] -> loadStore(cachefs, md5StateStream,
+ checksumAction, dataAction,
+ storeBigEndian()) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "ServerStore: PANIC! Error loading reply store "
+ << "for OPCODE#" << (unsigned int) i << ".\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ }
+
+ return 1;
+}
+
+int ServerStore::loadEventStores(istream *cachefs, md5_state_t *md5StateStream,
+ T_checksum_action checksumAction, T_data_action dataAction) const
+{
+ for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++)
+ {
+ if (events_[i] != NULL &&
+ events_[i] -> loadStore(cachefs, md5StateStream,
+ checksumAction, dataAction,
+ storeBigEndian()) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "ServerStore: PANIC! Error loading event store "
+ << "for OPCODE#" << (unsigned int) i << ".\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ }
+
+ return 1;
+}
diff --git a/nxcomp/ServerStore.h b/nxcomp/ServerStore.h
new file mode 100644
index 000000000..8df1f1e9c
--- /dev/null
+++ b/nxcomp/ServerStore.h
@@ -0,0 +1,75 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef ServerStore_H
+#define ServerStore_H
+
+#include "Message.h"
+
+#include "ChannelStore.h"
+
+class StaticCompressor;
+
+class ServerStore : public ChannelStore
+{
+ public:
+
+ ServerStore(StaticCompressor *compressor);
+
+ virtual ~ServerStore();
+
+ MessageStore *getReplyStore(unsigned char opcode) const
+ {
+ return replies_[opcode];
+ }
+
+ MessageStore *getEventStore(unsigned char opcode) const
+ {
+ return events_[opcode];
+ }
+
+ //
+ // Actually save the message store
+ // to disk according to proxy mode.
+ //
+
+ int saveReplyStores(ostream *cachefs, md5_state_t *md5StateStream,
+ md5_state_t *md5StateClient, T_checksum_action checksumAction,
+ T_data_action dataAction) const;
+
+ int saveEventStores(ostream *cachefs, md5_state_t *md5StateStream,
+ md5_state_t *md5StateClient, T_checksum_action checksumAction,
+ T_data_action dataAction) const;
+
+
+ int loadReplyStores(istream *cachefs, md5_state_t *md5StateStream,
+ T_checksum_action checksumAction, T_data_action dataAction) const;
+
+ int loadEventStores(istream *cachefs, md5_state_t *md5StateStream,
+ T_checksum_action checksumAction, T_data_action dataAction) const;
+
+ private:
+
+ //
+ // A server store contains replies and events.
+ //
+
+ MessageStore *replies_[CHANNEL_STORE_OPCODE_LIMIT];
+ MessageStore *events_[CHANNEL_STORE_OPCODE_LIMIT];
+};
+
+#endif /* ServerStore_H */
diff --git a/nxcomp/SetClipRectangles.cpp b/nxcomp/SetClipRectangles.cpp
new file mode 100644
index 000000000..8774744fb
--- /dev/null
+++ b/nxcomp/SetClipRectangles.cpp
@@ -0,0 +1,142 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "SetClipRectangles.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int SetClipRectanglesStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ SetClipRectanglesMessage *setClipRectangles = (SetClipRectanglesMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ setClipRectangles -> ordering = *(buffer + 1);
+
+ setClipRectangles -> gcontext = GetULONG(buffer + 4, bigEndian);
+
+ setClipRectangles -> x_origin = GetUINT(buffer + 8, bigEndian);
+ setClipRectangles -> y_origin = GetUINT(buffer + 10, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int SetClipRectanglesStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ SetClipRectanglesMessage *setClipRectangles = (SetClipRectanglesMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ *(buffer + 1) = setClipRectangles -> ordering;
+
+ PutULONG(setClipRectangles -> gcontext, buffer + 4, bigEndian);
+
+ PutUINT(setClipRectangles -> x_origin, buffer + 8, bigEndian);
+ PutUINT(setClipRectangles -> y_origin, buffer + 10, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void SetClipRectanglesStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ SetClipRectanglesMessage *setClipRectangles = (SetClipRectanglesMessage *) message;
+
+ *logofs << name() << ": Identity ordering " << (unsigned int) setClipRectangles -> ordering
+ << ", gcontext " << setClipRectangles -> gcontext << ", x_origin "
+ << setClipRectangles -> x_origin << ", y_origin "
+ << setClipRectangles -> y_origin << ", size "
+ << setClipRectangles -> size_ << ".\n" << logofs_flush;
+ #endif
+}
+
+void SetClipRectanglesStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ md5_append(md5_state_, buffer + 1, 1);
+ md5_append(md5_state_, buffer + 8, 4);
+}
+
+void SetClipRectanglesStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ SetClipRectanglesMessage *setClipRectangles = (SetClipRectanglesMessage *) message;
+ SetClipRectanglesMessage *cachedSetClipRectangles = (SetClipRectanglesMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << setClipRectangles -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(setClipRectangles -> gcontext, clientCache -> gcCache);
+
+ cachedSetClipRectangles -> gcontext = setClipRectangles -> gcontext;
+}
+
+void SetClipRectanglesStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ SetClipRectanglesMessage *setClipRectangles = (SetClipRectanglesMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ setClipRectangles -> gcontext = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << setClipRectangles -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+}
diff --git a/nxcomp/SetClipRectangles.h b/nxcomp/SetClipRectangles.h
new file mode 100644
index 000000000..06b4421f5
--- /dev/null
+++ b/nxcomp/SetClipRectangles.h
@@ -0,0 +1,179 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef SetClipRectangles_H
+#define SetClipRectangles_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define SETCLIPRECTANGLES_ENABLE_CACHE 1
+#define SETCLIPRECTANGLES_ENABLE_DATA 0
+#define SETCLIPRECTANGLES_ENABLE_SPLIT 0
+#define SETCLIPRECTANGLES_ENABLE_COMPRESS 0
+
+#define SETCLIPRECTANGLES_DATA_LIMIT 2048
+#define SETCLIPRECTANGLES_DATA_OFFSET 12
+
+#define SETCLIPRECTANGLES_CACHE_SLOTS 3000
+#define SETCLIPRECTANGLES_CACHE_THRESHOLD 3
+#define SETCLIPRECTANGLES_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class SetClipRectanglesMessage : public Message
+{
+ friend class SetClipRectanglesStore;
+
+ public:
+
+ SetClipRectanglesMessage()
+ {
+ }
+
+ ~SetClipRectanglesMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char ordering;
+ unsigned int gcontext;
+ unsigned short x_origin;
+ unsigned short y_origin;
+};
+
+class SetClipRectanglesStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ SetClipRectanglesStore() : MessageStore()
+ {
+ enableCache = SETCLIPRECTANGLES_ENABLE_CACHE;
+ enableData = SETCLIPRECTANGLES_ENABLE_DATA;
+ enableSplit = SETCLIPRECTANGLES_ENABLE_SPLIT;
+ enableCompress = SETCLIPRECTANGLES_ENABLE_COMPRESS;
+
+ dataLimit = SETCLIPRECTANGLES_DATA_LIMIT;
+ dataOffset = SETCLIPRECTANGLES_DATA_OFFSET;
+
+ cacheSlots = SETCLIPRECTANGLES_CACHE_SLOTS;
+ cacheThreshold = SETCLIPRECTANGLES_CACHE_THRESHOLD;
+ cacheLowerThreshold = SETCLIPRECTANGLES_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~SetClipRectanglesStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "SetClipRectangles";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_SetClipRectangles;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(SetClipRectanglesMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new SetClipRectanglesMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new SetClipRectanglesMessage((const SetClipRectanglesMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (SetClipRectanglesMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* SetClipRectangles_H */
diff --git a/nxcomp/SetUnpackAlpha.cpp b/nxcomp/SetUnpackAlpha.cpp
new file mode 100644
index 000000000..a0dd1fc2e
--- /dev/null
+++ b/nxcomp/SetUnpackAlpha.cpp
@@ -0,0 +1,257 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "SetUnpackAlpha.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "WriteBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Constructors and destructors.
+//
+
+SetUnpackAlphaStore::SetUnpackAlphaStore(StaticCompressor *compressor)
+
+ : MessageStore(compressor)
+{
+ enableCache = SETUNPACKALPHA_ENABLE_CACHE;
+ enableData = SETUNPACKALPHA_ENABLE_DATA;
+ enableSplit = SETUNPACKALPHA_ENABLE_SPLIT_IF_PROTO_STEP_7;
+ enableCompress = SETUNPACKALPHA_ENABLE_COMPRESS_IF_PROTO_STEP_7;
+
+ dataLimit = SETUNPACKALPHA_DATA_LIMIT;
+ dataOffset = SETUNPACKALPHA_DATA_OFFSET_IF_PROTO_STEP_7;
+
+ cacheSlots = SETUNPACKALPHA_CACHE_SLOTS;
+ cacheThreshold = SETUNPACKALPHA_CACHE_THRESHOLD;
+ cacheLowerThreshold = SETUNPACKALPHA_CACHE_LOWER_THRESHOLD;
+
+ if (control -> isProtoStep8() == 1)
+ {
+ enableSplit = SETUNPACKALPHA_ENABLE_SPLIT_IF_PROTO_STEP_8;
+ }
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+SetUnpackAlphaStore::~SetUnpackAlphaStore()
+{
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int SetUnpackAlphaStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding full message identity.\n" << logofs_flush;
+ #endif
+
+ //
+ // Encode the source length first because
+ // we need it to determine the size of
+ // the output buffer.
+ //
+
+ // SrcLength.
+ encodeBuffer.encodeValue(GetULONG(buffer + 8, bigEndian), 32, 9);
+
+ // Client.
+ encodeBuffer.encodeCachedValue(*(buffer + 1), 8,
+ clientCache -> resourceCache);
+ // Method.
+ encodeBuffer.encodeCachedValue(*(buffer + 4), 8,
+ clientCache -> methodCache);
+ // DstLength.
+ encodeBuffer.encodeValue(GetULONG(buffer + 12, bigEndian), 32, 9);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int SetUnpackAlphaStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding full message identity.\n" << logofs_flush;
+ #endif
+
+ unsigned int value;
+ unsigned char cValue;
+
+ // SrcLength.
+ decodeBuffer.decodeValue(value, 32, 9);
+
+ size = RoundUp4(value) + 16;
+
+ buffer = writeBuffer -> addMessage(size);
+
+ PutULONG(value, buffer + 8, bigEndian);
+
+ // Client.
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> resourceCache);
+
+ *(buffer + 1) = cValue;
+
+ // Method.
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> methodCache);
+
+ *(buffer + 4) = cValue;
+
+ // DstLength.
+ decodeBuffer.decodeValue(value, 32, 9);
+
+ PutULONG(value, buffer + 12, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int SetUnpackAlphaStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ SetUnpackAlphaMessage *setUnpackAlpha = (SetUnpackAlphaMessage *) message;
+
+ setUnpackAlpha -> client = *(buffer + 1);
+ setUnpackAlpha -> method = *(buffer + 4);
+
+ setUnpackAlpha -> src_length = GetULONG(buffer + 8, bigEndian);
+ setUnpackAlpha -> dst_length = GetULONG(buffer + 12, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int SetUnpackAlphaStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ SetUnpackAlphaMessage *setUnpackAlpha = (SetUnpackAlphaMessage *) message;
+
+ *(buffer + 1) = setUnpackAlpha -> client;
+ *(buffer + 4) = setUnpackAlpha -> method;
+
+ PutULONG(setUnpackAlpha -> src_length, buffer + 8, bigEndian);
+ PutULONG(setUnpackAlpha -> dst_length, buffer + 12, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void SetUnpackAlphaStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ SetUnpackAlphaMessage *setUnpackAlpha = (SetUnpackAlphaMessage *) message;
+
+ *logofs << name() << ": Identity client "
+ << (unsigned int) setUnpackAlpha -> client << " method "
+ << (unsigned int) setUnpackAlpha -> method << " source length "
+ << setUnpackAlpha -> src_length << " destination length "
+ << setUnpackAlpha -> dst_length << " size "
+ << setUnpackAlpha -> size_ << ".\n";
+
+ #endif
+}
+
+void SetUnpackAlphaStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ //
+ // Include the pack method and the source
+ // and destination length.
+ //
+
+ md5_append(md5_state_, buffer + 4, 1);
+ md5_append(md5_state_, buffer + 8, 8);
+}
+
+void SetUnpackAlphaStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ SetUnpackAlphaMessage *setUnpackAlpha = (SetUnpackAlphaMessage *) message;
+ SetUnpackAlphaMessage *cachedSetUnpackAlpha = (SetUnpackAlphaMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue(setUnpackAlpha -> client, 8,
+ clientCache -> resourceCache);
+
+ cachedSetUnpackAlpha -> client = setUnpackAlpha -> client;
+}
+
+void SetUnpackAlphaStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ SetUnpackAlphaMessage *setUnpackAlpha = (SetUnpackAlphaMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(setUnpackAlpha -> client, 8,
+ clientCache -> resourceCache);
+}
diff --git a/nxcomp/SetUnpackAlpha.h b/nxcomp/SetUnpackAlpha.h
new file mode 100644
index 000000000..2e32a6590
--- /dev/null
+++ b/nxcomp/SetUnpackAlpha.h
@@ -0,0 +1,158 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef SetUnpackAlpha_H
+#define SetUnpackAlpha_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define SETUNPACKALPHA_ENABLE_CACHE 1
+#define SETUNPACKALPHA_ENABLE_DATA 1
+#define SETUNPACKALPHA_ENABLE_SPLIT 0
+#define SETUNPACKALPHA_ENABLE_COMPRESS 1
+
+#define SETUNPACKALPHA_DATA_LIMIT 16384
+#define SETUNPACKALPHA_DATA_OFFSET 8
+
+#define SETUNPACKALPHA_CACHE_SLOTS 2000
+#define SETUNPACKALPHA_CACHE_THRESHOLD 10
+#define SETUNPACKALPHA_CACHE_LOWER_THRESHOLD 5
+
+#define SETUNPACKALPHA_DATA_OFFSET_IF_PROTO_STEP_7 16
+#define SETUNPACKALPHA_ENABLE_SPLIT_IF_PROTO_STEP_7 1
+#define SETUNPACKALPHA_ENABLE_COMPRESS_IF_PROTO_STEP_7 0
+
+#define SETUNPACKALPHA_ENABLE_SPLIT_IF_PROTO_STEP_8 0
+
+//
+// The message class.
+//
+
+class SetUnpackAlphaMessage : public Message
+{
+ friend class SetUnpackAlphaStore;
+
+ public:
+
+ SetUnpackAlphaMessage()
+ {
+ }
+
+ ~SetUnpackAlphaMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char client;
+ unsigned char method;
+
+ unsigned int src_length;
+ unsigned int dst_length;
+};
+
+class SetUnpackAlphaStore : public MessageStore
+{
+ public:
+
+ SetUnpackAlphaStore(StaticCompressor *compressor);
+
+ virtual ~SetUnpackAlphaStore();
+
+ virtual const char *name() const
+ {
+ return "SetUnpackAlpha";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_NXSetUnpackAlpha;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(SetUnpackAlphaMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ protected:
+
+ virtual Message *create() const
+ {
+ return new SetUnpackAlphaMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new SetUnpackAlphaMessage((const SetUnpackAlphaMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (SetUnpackAlphaMessage *) message;
+ }
+
+ virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const;
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* SetUnpackAlpha_H */
diff --git a/nxcomp/SetUnpackAlphaCompat.cpp b/nxcomp/SetUnpackAlphaCompat.cpp
new file mode 100644
index 000000000..a8fcabdeb
--- /dev/null
+++ b/nxcomp/SetUnpackAlphaCompat.cpp
@@ -0,0 +1,250 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "SetUnpackAlphaCompat.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "WriteBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Constructors and destructors.
+//
+
+SetUnpackAlphaCompatStore::SetUnpackAlphaCompatStore(StaticCompressor *compressor)
+
+ : MessageStore(compressor)
+{
+ enableCache = SETUNPACKALPHA_ENABLE_CACHE;
+ enableData = SETUNPACKALPHA_ENABLE_DATA;
+ enableSplit = SETUNPACKALPHA_ENABLE_SPLIT;
+ enableCompress = SETUNPACKALPHA_ENABLE_COMPRESS;
+
+ dataLimit = SETUNPACKALPHA_DATA_LIMIT;
+ dataOffset = SETUNPACKALPHA_DATA_OFFSET;
+
+ cacheSlots = SETUNPACKALPHA_CACHE_SLOTS;
+ cacheThreshold = SETUNPACKALPHA_CACHE_THRESHOLD;
+ cacheLowerThreshold = SETUNPACKALPHA_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+SetUnpackAlphaCompatStore::~SetUnpackAlphaCompatStore()
+{
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int SetUnpackAlphaCompatStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding full message identity.\n" << logofs_flush;
+ #endif
+
+ // Client.
+ encodeBuffer.encodeCachedValue(*(buffer + 1), 8,
+ clientCache -> resourceCache);
+ // Entries.
+ encodeBuffer.encodeValue(GetULONG(buffer + 4, bigEndian), 32, 9);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int SetUnpackAlphaCompatStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding full message identity.\n" << logofs_flush;
+ #endif
+
+ unsigned int value;
+ unsigned char cValue;
+
+ // Client.
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> resourceCache);
+ // Entries.
+ decodeBuffer.decodeValue(value, 32, 9);
+
+ size = RoundUp4(value) + 8;
+
+ buffer = writeBuffer -> addMessage(size);
+
+ *(buffer + 1) = cValue;
+
+ PutULONG(value, buffer + 4, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int SetUnpackAlphaCompatStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ SetUnpackAlphaCompatMessage *setUnpackAlpha = (SetUnpackAlphaCompatMessage *) message;
+
+ setUnpackAlpha -> client = *(buffer + 1);
+
+ setUnpackAlpha -> entries = GetULONG(buffer + 4, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int SetUnpackAlphaCompatStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ SetUnpackAlphaCompatMessage *setUnpackAlpha = (SetUnpackAlphaCompatMessage *) message;
+
+ *(buffer + 1) = setUnpackAlpha -> client;
+
+ PutULONG(setUnpackAlpha -> entries, buffer + 4, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void SetUnpackAlphaCompatStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ SetUnpackAlphaCompatMessage *setUnpackAlpha = (SetUnpackAlphaCompatMessage *) message;
+
+ *logofs << name() << ": Identity client "
+ << (unsigned int) setUnpackAlpha -> client << " entries "
+ << setUnpackAlpha -> entries << " size "
+ << setUnpackAlpha -> size_ << ".\n";
+
+ #endif
+}
+
+void SetUnpackAlphaCompatStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ md5_append(md5_state_, buffer + 4, 4);
+}
+
+void SetUnpackAlphaCompatStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ SetUnpackAlphaCompatMessage *setUnpackAlpha = (SetUnpackAlphaCompatMessage *) message;
+ SetUnpackAlphaCompatMessage *cachedSetUnpackAlpha = (SetUnpackAlphaCompatMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue(setUnpackAlpha -> client, 8,
+ clientCache -> resourceCache);
+
+ cachedSetUnpackAlpha -> client = setUnpackAlpha -> client;
+
+ if (cachedSetUnpackAlpha -> entries != setUnpackAlpha -> entries)
+ {
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << setUnpackAlpha -> entries
+ << " as entries field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeBoolValue(1);
+
+ encodeBuffer.encodeValue(setUnpackAlpha -> entries, 32, 9);
+
+ cachedSetUnpackAlpha -> entries = setUnpackAlpha -> entries;
+ }
+ else
+ {
+ encodeBuffer.encodeBoolValue(0);
+ }
+}
+
+void SetUnpackAlphaCompatStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ SetUnpackAlphaCompatMessage *setUnpackAlpha = (SetUnpackAlphaCompatMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeCachedValue(setUnpackAlpha -> client, 8,
+ clientCache -> resourceCache);
+
+ decodeBuffer.decodeBoolValue(value);
+
+ if (value)
+ {
+ decodeBuffer.decodeValue(value, 32, 9);
+
+ setUnpackAlpha -> entries = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << setUnpackAlpha -> entries
+ << " as entries field.\n" << logofs_flush;
+ #endif
+ }
+}
diff --git a/nxcomp/SetUnpackAlphaCompat.h b/nxcomp/SetUnpackAlphaCompat.h
new file mode 100644
index 000000000..80d1522c0
--- /dev/null
+++ b/nxcomp/SetUnpackAlphaCompat.h
@@ -0,0 +1,149 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef SetUnpackAlphaCompat_H
+#define SetUnpackAlphaCompat_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define SETUNPACKALPHA_ENABLE_CACHE 1
+#define SETUNPACKALPHA_ENABLE_DATA 1
+#define SETUNPACKALPHA_ENABLE_SPLIT 0
+#define SETUNPACKALPHA_ENABLE_COMPRESS 1
+
+#define SETUNPACKALPHA_DATA_LIMIT 16384
+#define SETUNPACKALPHA_DATA_OFFSET 8
+
+#define SETUNPACKALPHA_CACHE_SLOTS 2000
+#define SETUNPACKALPHA_CACHE_THRESHOLD 10
+#define SETUNPACKALPHA_CACHE_LOWER_THRESHOLD 5
+
+//
+// The message class.
+//
+
+class SetUnpackAlphaCompatMessage : public Message
+{
+ friend class SetUnpackAlphaCompatStore;
+
+ public:
+
+ SetUnpackAlphaCompatMessage()
+ {
+ }
+
+ ~SetUnpackAlphaCompatMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char client;
+ unsigned int entries;
+};
+
+class SetUnpackAlphaCompatStore : public MessageStore
+{
+ public:
+
+ SetUnpackAlphaCompatStore(StaticCompressor *compressor);
+
+ virtual ~SetUnpackAlphaCompatStore();
+
+ virtual const char *name() const
+ {
+ return "SetUnpackAlphaCompat";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_NXSetUnpackAlpha;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(SetUnpackAlphaCompatMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ protected:
+
+ virtual Message *create() const
+ {
+ return new SetUnpackAlphaCompatMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new SetUnpackAlphaCompatMessage((const SetUnpackAlphaCompatMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (SetUnpackAlphaCompatMessage *) message;
+ }
+
+ virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const;
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* SetUnpackAlphaCompat_H */
diff --git a/nxcomp/SetUnpackColormap.cpp b/nxcomp/SetUnpackColormap.cpp
new file mode 100644
index 000000000..d522d328e
--- /dev/null
+++ b/nxcomp/SetUnpackColormap.cpp
@@ -0,0 +1,257 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "SetUnpackColormap.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "WriteBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Constructors and destructors.
+//
+
+SetUnpackColormapStore::SetUnpackColormapStore(StaticCompressor *compressor)
+
+ : MessageStore(compressor)
+{
+ enableCache = SETUNPACKCOLORMAP_ENABLE_CACHE;
+ enableData = SETUNPACKCOLORMAP_ENABLE_DATA;
+ enableSplit = SETUNPACKCOLORMAP_ENABLE_SPLIT;
+ enableCompress = SETUNPACKCOLORMAP_ENABLE_COMPRESS_IF_PROTO_STEP_7;
+
+ dataLimit = SETUNPACKCOLORMAP_DATA_LIMIT;
+ dataOffset = SETUNPACKCOLORMAP_DATA_OFFSET_IF_PROTO_STEP_7;
+
+ cacheSlots = SETUNPACKCOLORMAP_CACHE_SLOTS;
+ cacheThreshold = SETUNPACKCOLORMAP_CACHE_THRESHOLD;
+ cacheLowerThreshold = SETUNPACKCOLORMAP_CACHE_LOWER_THRESHOLD;
+
+ if (control -> isProtoStep8() == 1)
+ {
+ enableSplit = SETUNPACKCOLORMAP_ENABLE_SPLIT_IF_PROTO_STEP_8;
+ }
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+SetUnpackColormapStore::~SetUnpackColormapStore()
+{
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int SetUnpackColormapStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding full message identity.\n" << logofs_flush;
+ #endif
+
+ //
+ // Encode the source length first because
+ // we need it to determine the size of
+ // the output buffer.
+ //
+
+ // SrcLength.
+ encodeBuffer.encodeValue(GetULONG(buffer + 8, bigEndian), 32, 9);
+
+ // Client.
+ encodeBuffer.encodeCachedValue(*(buffer + 1), 8,
+ clientCache -> resourceCache);
+ // Method.
+ encodeBuffer.encodeCachedValue(*(buffer + 4), 8,
+ clientCache -> methodCache);
+ // DstLength.
+ encodeBuffer.encodeValue(GetULONG(buffer + 12, bigEndian), 32, 9);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int SetUnpackColormapStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding full message identity.\n" << logofs_flush;
+ #endif
+
+ unsigned int value;
+ unsigned char cValue;
+
+ // SrcLength.
+ decodeBuffer.decodeValue(value, 32, 9);
+
+ size = RoundUp4(value) + 16;
+
+ buffer = writeBuffer -> addMessage(size);
+
+ PutULONG(value, buffer + 8, bigEndian);
+
+ // Client.
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> resourceCache);
+
+ *(buffer + 1) = cValue;
+
+ // Method.
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> methodCache);
+
+ *(buffer + 4) = cValue;
+
+ // DstLength.
+ decodeBuffer.decodeValue(value, 32, 9);
+
+ PutULONG(value, buffer + 12, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int SetUnpackColormapStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ SetUnpackColormapMessage *setUnpackColormap = (SetUnpackColormapMessage *) message;
+
+ setUnpackColormap -> client = *(buffer + 1);
+ setUnpackColormap -> method = *(buffer + 4);
+
+ setUnpackColormap -> src_length = GetULONG(buffer + 8, bigEndian);
+ setUnpackColormap -> dst_length = GetULONG(buffer + 12, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int SetUnpackColormapStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ SetUnpackColormapMessage *setUnpackColormap = (SetUnpackColormapMessage *) message;
+
+ *(buffer + 1) = setUnpackColormap -> client;
+ *(buffer + 4) = setUnpackColormap -> method;
+
+ PutULONG(setUnpackColormap -> src_length, buffer + 8, bigEndian);
+ PutULONG(setUnpackColormap -> dst_length, buffer + 12, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void SetUnpackColormapStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ SetUnpackColormapMessage *setUnpackColormap = (SetUnpackColormapMessage *) message;
+
+ *logofs << name() << ": Identity client "
+ << (unsigned int) setUnpackColormap -> client << " method "
+ << (unsigned int) setUnpackColormap -> method << " source length "
+ << setUnpackColormap -> src_length << " destination length "
+ << setUnpackColormap -> dst_length << " size "
+ << setUnpackColormap -> size_ << ".\n";
+
+ #endif
+}
+
+void SetUnpackColormapStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ //
+ // Include the pack method and the source
+ // and destination length.
+ //
+
+ md5_append(md5_state_, buffer + 4, 1);
+ md5_append(md5_state_, buffer + 8, 8);
+}
+
+void SetUnpackColormapStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ SetUnpackColormapMessage *setUnpackColormap = (SetUnpackColormapMessage *) message;
+ SetUnpackColormapMessage *cachedSetUnpackColormap = (SetUnpackColormapMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue(setUnpackColormap -> client, 8,
+ clientCache -> resourceCache);
+
+ cachedSetUnpackColormap -> client = setUnpackColormap -> client;
+}
+
+void SetUnpackColormapStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ SetUnpackColormapMessage *setUnpackColormap = (SetUnpackColormapMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(setUnpackColormap -> client, 8,
+ clientCache -> resourceCache);
+}
diff --git a/nxcomp/SetUnpackColormap.h b/nxcomp/SetUnpackColormap.h
new file mode 100644
index 000000000..53277b3f0
--- /dev/null
+++ b/nxcomp/SetUnpackColormap.h
@@ -0,0 +1,157 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef SetUnpackColormap_H
+#define SetUnpackColormap_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define SETUNPACKCOLORMAP_ENABLE_CACHE 1
+#define SETUNPACKCOLORMAP_ENABLE_DATA 1
+#define SETUNPACKCOLORMAP_ENABLE_SPLIT 1
+#define SETUNPACKCOLORMAP_ENABLE_COMPRESS 1
+
+#define SETUNPACKCOLORMAP_DATA_LIMIT 4096
+#define SETUNPACKCOLORMAP_DATA_OFFSET 8
+
+#define SETUNPACKCOLORMAP_CACHE_SLOTS 2000
+#define SETUNPACKCOLORMAP_CACHE_THRESHOLD 5
+#define SETUNPACKCOLORMAP_CACHE_LOWER_THRESHOLD 0
+
+#define SETUNPACKCOLORMAP_DATA_OFFSET_IF_PROTO_STEP_7 16
+#define SETUNPACKCOLORMAP_ENABLE_COMPRESS_IF_PROTO_STEP_7 0
+
+#define SETUNPACKCOLORMAP_ENABLE_SPLIT_IF_PROTO_STEP_8 0
+
+//
+// The message class.
+//
+
+class SetUnpackColormapMessage : public Message
+{
+ friend class SetUnpackColormapStore;
+
+ public:
+
+ SetUnpackColormapMessage()
+ {
+ }
+
+ ~SetUnpackColormapMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char client;
+ unsigned char method;
+
+ unsigned int src_length;
+ unsigned int dst_length;
+};
+
+class SetUnpackColormapStore : public MessageStore
+{
+ public:
+
+ SetUnpackColormapStore(StaticCompressor *compressor);
+
+ virtual ~SetUnpackColormapStore();
+
+ virtual const char *name() const
+ {
+ return "SetUnpackColormap";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_NXSetUnpackColormap;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(SetUnpackColormapMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ protected:
+
+ virtual Message *create() const
+ {
+ return new SetUnpackColormapMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new SetUnpackColormapMessage((const SetUnpackColormapMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (SetUnpackColormapMessage *) message;
+ }
+
+ virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const;
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* SetUnpackColormap_H */
diff --git a/nxcomp/SetUnpackColormapCompat.cpp b/nxcomp/SetUnpackColormapCompat.cpp
new file mode 100644
index 000000000..65b108a82
--- /dev/null
+++ b/nxcomp/SetUnpackColormapCompat.cpp
@@ -0,0 +1,262 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "SetUnpackColormapCompat.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "WriteBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Constructors and destructors.
+//
+
+SetUnpackColormapCompatStore::SetUnpackColormapCompatStore(StaticCompressor *compressor)
+
+ : MessageStore(compressor)
+{
+ enableCache = SETUNPACKCOLORMAP_ENABLE_CACHE;
+ enableData = SETUNPACKCOLORMAP_ENABLE_DATA;
+ enableSplit = SETUNPACKCOLORMAP_ENABLE_SPLIT;
+ enableCompress = SETUNPACKCOLORMAP_ENABLE_COMPRESS;
+
+ dataLimit = SETUNPACKCOLORMAP_DATA_LIMIT;
+ dataOffset = SETUNPACKCOLORMAP_DATA_OFFSET;
+
+ cacheSlots = SETUNPACKCOLORMAP_CACHE_SLOTS;
+ cacheThreshold = SETUNPACKCOLORMAP_CACHE_THRESHOLD;
+ cacheLowerThreshold = SETUNPACKCOLORMAP_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+SetUnpackColormapCompatStore::~SetUnpackColormapCompatStore()
+{
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int SetUnpackColormapCompatStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding full message identity.\n" << logofs_flush;
+ #endif
+
+ // Client.
+ encodeBuffer.encodeCachedValue(*(buffer + 1), 8,
+ clientCache -> resourceCache);
+ // Entries.
+ encodeBuffer.encodeValue(GetULONG(buffer + 4, bigEndian), 32, 9);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int SetUnpackColormapCompatStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding full message identity.\n" << logofs_flush;
+ #endif
+
+ unsigned int value;
+ unsigned char cValue;
+
+ // Client.
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> resourceCache);
+ // Entries.
+ decodeBuffer.decodeValue(value, 32, 9);
+
+ size = (value << 2) + 8;
+
+ buffer = writeBuffer -> addMessage(size);
+
+ *(buffer + 1) = cValue;
+
+ PutULONG(value, buffer + 4, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int SetUnpackColormapCompatStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ SetUnpackColormapCompatMessage *setUnpackColormap = (SetUnpackColormapCompatMessage *) message;
+
+ setUnpackColormap -> client = *(buffer + 1);
+
+ setUnpackColormap -> entries = GetULONG(buffer + 4, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int SetUnpackColormapCompatStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ SetUnpackColormapCompatMessage *setUnpackColormap = (SetUnpackColormapCompatMessage *) message;
+
+ *(buffer + 1) = setUnpackColormap -> client;
+
+ PutULONG(setUnpackColormap -> entries, buffer + 4, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void SetUnpackColormapCompatStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ SetUnpackColormapCompatMessage *setUnpackColormap = (SetUnpackColormapCompatMessage *) message;
+
+ *logofs << name() << ": Identity client "
+ << (unsigned int) setUnpackColormap -> client << " entries "
+ << setUnpackColormap -> entries << " size "
+ << setUnpackColormap -> size_ << ".\n";
+
+ #endif
+}
+
+void SetUnpackColormapCompatStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ md5_append(md5_state_, buffer + 4, 4);
+}
+
+void SetUnpackColormapCompatStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ SetUnpackColormapCompatMessage *setUnpackColormap = (SetUnpackColormapCompatMessage *) message;
+ SetUnpackColormapCompatMessage *cachedSetUnpackColormap = (SetUnpackColormapCompatMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value "
+ << (unsigned int) setUnpackColormap -> client
+ << " as client field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(setUnpackColormap -> client, 8,
+ clientCache -> resourceCache);
+
+ cachedSetUnpackColormap -> client = setUnpackColormap -> client;
+
+ if (cachedSetUnpackColormap -> entries != setUnpackColormap -> entries)
+ {
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << setUnpackColormap -> entries
+ << " as entries field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeBoolValue(1);
+
+ encodeBuffer.encodeValue(setUnpackColormap -> entries, 32, 9);
+
+ cachedSetUnpackColormap -> entries = setUnpackColormap -> entries;
+ }
+ else
+ {
+ encodeBuffer.encodeBoolValue(0);
+ }
+}
+
+void SetUnpackColormapCompatStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ SetUnpackColormapCompatMessage *setUnpackColormap = (SetUnpackColormapCompatMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeCachedValue(setUnpackColormap -> client, 8,
+ clientCache -> resourceCache);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value "
+ << (unsigned int) setUnpackColormap -> client
+ << " as client field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeBoolValue(value);
+
+ if (value)
+ {
+ decodeBuffer.decodeValue(value, 32, 9);
+
+ setUnpackColormap -> entries = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << setUnpackColormap -> entries
+ << " as entries field.\n" << logofs_flush;
+ #endif
+ }
+}
diff --git a/nxcomp/SetUnpackColormapCompat.h b/nxcomp/SetUnpackColormapCompat.h
new file mode 100644
index 000000000..d1ffad876
--- /dev/null
+++ b/nxcomp/SetUnpackColormapCompat.h
@@ -0,0 +1,149 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef SetUnpackColormapCompat_H
+#define SetUnpackColormapCompat_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define SETUNPACKCOLORMAP_ENABLE_CACHE 1
+#define SETUNPACKCOLORMAP_ENABLE_DATA 1
+#define SETUNPACKCOLORMAP_ENABLE_SPLIT 1
+#define SETUNPACKCOLORMAP_ENABLE_COMPRESS 1
+
+#define SETUNPACKCOLORMAP_DATA_LIMIT 4096
+#define SETUNPACKCOLORMAP_DATA_OFFSET 8
+
+#define SETUNPACKCOLORMAP_CACHE_SLOTS 2000
+#define SETUNPACKCOLORMAP_CACHE_THRESHOLD 5
+#define SETUNPACKCOLORMAP_CACHE_LOWER_THRESHOLD 0
+
+//
+// The message class.
+//
+
+class SetUnpackColormapCompatMessage : public Message
+{
+ friend class SetUnpackColormapCompatStore;
+
+ public:
+
+ SetUnpackColormapCompatMessage()
+ {
+ }
+
+ ~SetUnpackColormapCompatMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char client;
+ unsigned int entries;
+};
+
+class SetUnpackColormapCompatStore : public MessageStore
+{
+ public:
+
+ SetUnpackColormapCompatStore(StaticCompressor *compressor);
+
+ virtual ~SetUnpackColormapCompatStore();
+
+ virtual const char *name() const
+ {
+ return "SetUnpackColormapCompat";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_NXSetUnpackColormap;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(SetUnpackColormapCompatMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ protected:
+
+ virtual Message *create() const
+ {
+ return new SetUnpackColormapCompatMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new SetUnpackColormapCompatMessage((const SetUnpackColormapCompatMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (SetUnpackColormapCompatMessage *) message;
+ }
+
+ virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const;
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* SetUnpackColormapCompat_H */
diff --git a/nxcomp/SetUnpackGeometry.cpp b/nxcomp/SetUnpackGeometry.cpp
new file mode 100644
index 000000000..67b79a410
--- /dev/null
+++ b/nxcomp/SetUnpackGeometry.cpp
@@ -0,0 +1,293 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "SetUnpackGeometry.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "WriteBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Constructors and destructors.
+//
+
+SetUnpackGeometryStore::SetUnpackGeometryStore(StaticCompressor *compressor)
+
+ : MessageStore(compressor)
+{
+ enableCache = SETUNPACKGEOMETRY_ENABLE_CACHE;
+ enableData = SETUNPACKGEOMETRY_ENABLE_DATA;
+ enableSplit = SETUNPACKGEOMETRY_ENABLE_SPLIT;
+ enableCompress = SETUNPACKGEOMETRY_ENABLE_COMPRESS;
+
+ dataLimit = SETUNPACKGEOMETRY_DATA_LIMIT;
+ dataOffset = SETUNPACKGEOMETRY_DATA_OFFSET;
+
+ cacheSlots = SETUNPACKGEOMETRY_CACHE_SLOTS;
+ cacheThreshold = SETUNPACKGEOMETRY_CACHE_THRESHOLD;
+ cacheLowerThreshold = SETUNPACKGEOMETRY_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+SetUnpackGeometryStore::~SetUnpackGeometryStore()
+{
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int SetUnpackGeometryStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding full message identity.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(*(buffer + 1), 8,
+ clientCache -> resourceCache);
+
+ const unsigned char *nextChar = buffer + 4;
+
+ for (int i = 0; i < 6; i++)
+ {
+ encodeBuffer.encodeCachedValue(*nextChar++, 8,
+ clientCache -> depthCache);
+ }
+
+ encodeBuffer.encodeValue(GetULONG(buffer + 12, bigEndian), 32);
+ encodeBuffer.encodeValue(GetULONG(buffer + 16, bigEndian), 32);
+ encodeBuffer.encodeValue(GetULONG(buffer + 20, bigEndian), 32);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int SetUnpackGeometryStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding full message identity.\n" << logofs_flush;
+ #endif
+
+ size = 24;
+ buffer = writeBuffer -> addMessage(size);
+
+ unsigned char cValue;
+
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> resourceCache);
+ *(buffer + 1) = cValue;
+
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> depthCache);
+ *(buffer + 4) = cValue;
+
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> depthCache);
+ *(buffer + 5) = cValue;
+
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> depthCache);
+ *(buffer + 6) = cValue;
+
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> depthCache);
+ *(buffer + 7) = cValue;
+
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> depthCache);
+ *(buffer + 8) = cValue;
+
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> depthCache);
+ *(buffer + 9) = cValue;
+
+ unsigned int value;
+
+ decodeBuffer.decodeValue(value, 32);
+ PutULONG(value, buffer + 12, bigEndian);
+
+ decodeBuffer.decodeValue(value, 32);
+ PutULONG(value, buffer + 16, bigEndian);
+
+ decodeBuffer.decodeValue(value, 32);
+ PutULONG(value, buffer + 20, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int SetUnpackGeometryStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ SetUnpackGeometryMessage *setUnpackGeometry = (SetUnpackGeometryMessage *) message;
+
+ setUnpackGeometry -> client = *(buffer + 1);
+
+ setUnpackGeometry -> depth_1_bpp = *(buffer + 4);
+ setUnpackGeometry -> depth_4_bpp = *(buffer + 5);
+ setUnpackGeometry -> depth_8_bpp = *(buffer + 6);
+ setUnpackGeometry -> depth_16_bpp = *(buffer + 7);
+ setUnpackGeometry -> depth_24_bpp = *(buffer + 8);
+ setUnpackGeometry -> depth_32_bpp = *(buffer + 9);
+
+ setUnpackGeometry -> red_mask = GetULONG(buffer + 12, bigEndian);
+ setUnpackGeometry -> green_mask = GetULONG(buffer + 16, bigEndian);
+ setUnpackGeometry -> blue_mask = GetULONG(buffer + 20, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int SetUnpackGeometryStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ SetUnpackGeometryMessage *setUnpackGeometry = (SetUnpackGeometryMessage *) message;
+
+ *(buffer + 1) = setUnpackGeometry -> client;
+
+ *(buffer + 4) = setUnpackGeometry -> depth_1_bpp;
+ *(buffer + 5) = setUnpackGeometry -> depth_4_bpp;
+ *(buffer + 6) = setUnpackGeometry -> depth_8_bpp;
+ *(buffer + 7) = setUnpackGeometry -> depth_16_bpp;
+ *(buffer + 8) = setUnpackGeometry -> depth_24_bpp;
+ *(buffer + 9) = setUnpackGeometry -> depth_32_bpp;
+
+ PutULONG(setUnpackGeometry -> red_mask, buffer + 12, bigEndian);
+ PutULONG(setUnpackGeometry -> green_mask, buffer + 16, bigEndian);
+ PutULONG(setUnpackGeometry -> blue_mask, buffer + 20, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void SetUnpackGeometryStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ SetUnpackGeometryMessage *setUnpackGeometry = (SetUnpackGeometryMessage *) message;
+
+ *logofs << name() << ": Identity client "
+ << (unsigned) setUnpackGeometry -> client << " depth_1_bpp "
+ << (unsigned) setUnpackGeometry -> depth_1_bpp << " depth_4_bpp "
+ << (unsigned int) setUnpackGeometry -> depth_4_bpp << " depth_8_bpp "
+ << (unsigned int) setUnpackGeometry -> depth_8_bpp << " depth_16_bpp "
+ << (unsigned int) setUnpackGeometry -> depth_16_bpp << " depth_24_bpp "
+ << (unsigned int) setUnpackGeometry -> depth_24_bpp << " depth_32_bpp "
+ << (unsigned int) setUnpackGeometry -> depth_32_bpp
+
+ << " red_mask " << setUnpackGeometry -> red_mask
+ << " green_mask " << setUnpackGeometry -> green_mask
+ << " blue_mask " << setUnpackGeometry -> blue_mask
+
+ << " size " << setUnpackGeometry -> size_ << ".\n";
+
+ #endif
+}
+
+void SetUnpackGeometryStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ md5_append(md5_state_, buffer + 4, 6);
+ md5_append(md5_state_, buffer + 12, 12);
+}
+
+void SetUnpackGeometryStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ SetUnpackGeometryMessage *setUnpackGeometry = (SetUnpackGeometryMessage *) message;
+ SetUnpackGeometryMessage *cachedSetUnpackGeometry = (SetUnpackGeometryMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value "
+ << (unsigned int) setUnpackGeometry -> client
+ << " as client field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(setUnpackGeometry -> client, 8,
+ clientCache -> resourceCache);
+
+ cachedSetUnpackGeometry -> client = setUnpackGeometry -> client;
+}
+
+void SetUnpackGeometryStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ SetUnpackGeometryMessage *setUnpackGeometry = (SetUnpackGeometryMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(setUnpackGeometry -> client, 8,
+ clientCache -> resourceCache);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value "
+ << (unsigned int) setUnpackGeometry -> client
+ << " as client field.\n" << logofs_flush;
+ #endif
+}
diff --git a/nxcomp/SetUnpackGeometry.h b/nxcomp/SetUnpackGeometry.h
new file mode 100644
index 000000000..3a8224dfb
--- /dev/null
+++ b/nxcomp/SetUnpackGeometry.h
@@ -0,0 +1,159 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef SetUnpackGeometry_H
+#define SetUnpackGeometry_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define SETUNPACKGEOMETRY_ENABLE_CACHE 1
+#define SETUNPACKGEOMETRY_ENABLE_DATA 0
+#define SETUNPACKGEOMETRY_ENABLE_SPLIT 0
+#define SETUNPACKGEOMETRY_ENABLE_COMPRESS 0
+
+#define SETUNPACKGEOMETRY_DATA_LIMIT 24
+#define SETUNPACKGEOMETRY_DATA_OFFSET 24
+
+#define SETUNPACKGEOMETRY_CACHE_SLOTS 20
+#define SETUNPACKGEOMETRY_CACHE_THRESHOLD 1
+#define SETUNPACKGEOMETRY_CACHE_LOWER_THRESHOLD 0
+
+//
+// The message class.
+//
+
+class SetUnpackGeometryMessage : public Message
+{
+ friend class SetUnpackGeometryStore;
+
+ public:
+
+ SetUnpackGeometryMessage()
+ {
+ }
+
+ ~SetUnpackGeometryMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char client;
+
+ unsigned char depth_1_bpp;
+ unsigned char depth_4_bpp;
+ unsigned char depth_8_bpp;
+ unsigned char depth_16_bpp;
+ unsigned char depth_24_bpp;
+ unsigned char depth_32_bpp;
+
+ unsigned int red_mask;
+ unsigned int green_mask;
+ unsigned int blue_mask;
+};
+
+class SetUnpackGeometryStore : public MessageStore
+{
+ public:
+
+ SetUnpackGeometryStore(StaticCompressor *compressor);
+
+ virtual ~SetUnpackGeometryStore();
+
+ virtual const char *name() const
+ {
+ return "SetUnpackGeometry";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_NXSetUnpackGeometry;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(SetUnpackGeometryMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ protected:
+
+ virtual Message *create() const
+ {
+ return new SetUnpackGeometryMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new SetUnpackGeometryMessage((const SetUnpackGeometryMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (SetUnpackGeometryMessage *) message;
+ }
+
+ virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const;
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* SetUnpackGeometry_H */
diff --git a/nxcomp/ShapeExtension.cpp b/nxcomp/ShapeExtension.cpp
new file mode 100644
index 000000000..daa19fbb4
--- /dev/null
+++ b/nxcomp/ShapeExtension.cpp
@@ -0,0 +1,296 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "ShapeExtension.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "WriteBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Constructors and destructors.
+//
+
+ShapeExtensionStore::ShapeExtensionStore(StaticCompressor *compressor)
+
+ : MessageStore(compressor)
+{
+ enableCache = SHAPEEXTENSION_ENABLE_CACHE;
+ enableData = SHAPEEXTENSION_ENABLE_DATA;
+ enableSplit = SHAPEEXTENSION_ENABLE_SPLIT;
+ enableCompress = SHAPEEXTENSION_ENABLE_COMPRESS;
+
+ if (control -> isProtoStep7() == 1)
+ {
+ enableCompress = SHAPEEXTENSION_ENABLE_COMPRESS_IF_PROTO_STEP_7;
+ }
+
+ dataLimit = SHAPEEXTENSION_DATA_LIMIT;
+ dataOffset = SHAPEEXTENSION_DATA_OFFSET;
+
+ cacheSlots = SHAPEEXTENSION_CACHE_SLOTS;
+ cacheThreshold = SHAPEEXTENSION_CACHE_THRESHOLD;
+ cacheLowerThreshold = SHAPEEXTENSION_CACHE_LOWER_THRESHOLD;
+
+ opcode_ = X_NXInternalShapeExtension;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+ShapeExtensionStore::~ShapeExtensionStore()
+{
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int ShapeExtensionStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ //
+ // Handle this extension in a way similar to shape.
+ //
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding full message identity.\n" << logofs_flush;
+ #endif
+
+ //
+ // We handle all possible requests of this extension
+ // using the same opcode. We give to message a data
+ // offset of 4 (or 16 if proto is >= 3) and handle
+ // the first 16 bytes through an array of caches.
+ //
+
+ encodeBuffer.encodeValue(size >> 2, 16, 10);
+
+ encodeBuffer.encodeCachedValue(*(buffer + 1), 8,
+ clientCache -> shapeOpcodeCache);
+
+ for (unsigned int i = 0; i < 8 && (i * 2 + 4) < size; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetUINT(buffer + (i * 2) + 4, bigEndian), 16,
+ *clientCache -> shapeDataCache[i]);
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int ShapeExtensionStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding full message identity.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeValue(size, 16, 10);
+
+ size <<= 2;
+
+ buffer = writeBuffer -> addMessage(size);
+
+ decodeBuffer.decodeCachedValue(*(buffer + 1), 8,
+ clientCache -> shapeOpcodeCache);
+
+ unsigned int value;
+
+ for (unsigned int i = 0; i < 8 && (i * 2 + 4) < size; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache -> shapeDataCache[i]);
+
+ PutUINT(value, buffer + 4 + (i * 2), bigEndian);
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int ShapeExtensionStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ShapeExtensionMessage *shapeExtension = (ShapeExtensionMessage *) message;
+
+ shapeExtension -> opcode = *(buffer + 1);
+
+ for (unsigned int i = 0; i < 8; i++)
+ {
+ if ((i * 2 + 4) < size)
+ {
+ shapeExtension -> data[i] = GetUINT(buffer + i * 2 + 4, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed data[" << i << "].\n"
+ << logofs_flush;
+ #endif
+ }
+ else
+ {
+ shapeExtension -> data[i] = 0;
+ }
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int ShapeExtensionStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ShapeExtensionMessage *shapeExtension = (ShapeExtensionMessage *) message;
+
+ *(buffer + 1) = shapeExtension -> opcode;
+
+ for (unsigned int i = 0; i < 8 && (i * 2 + 4) < size; i++)
+ {
+ PutUINT(shapeExtension -> data[i], buffer + i * 2 + 4, bigEndian);
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void ShapeExtensionStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ ShapeExtensionMessage *shapeExtension = (ShapeExtensionMessage *) message;
+
+ *logofs << name() << ": Identity opcode " << (unsigned) shapeExtension -> opcode;
+
+ for (int i = 0; i < 8; i++)
+ {
+ *logofs << ", data[" << i << "] " << shapeExtension -> data[i];
+ }
+
+ *logofs << ", size " << shapeExtension -> size_ << ".\n" << logofs_flush;
+
+ #endif
+}
+
+void ShapeExtensionStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ //
+ // Include minor opcode in the checksum. As data
+ // offset can be beyond the real end of message,
+ // we need to include size or we will match any
+ // message of size less or equal to data offset.
+ //
+
+ md5_append(md5_state_, buffer + 1, 3);
+}
+
+void ShapeExtensionStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ //
+ // Encode the variant part.
+ //
+
+ ShapeExtensionMessage *shapeExtension = (ShapeExtensionMessage *) message;
+ ShapeExtensionMessage *cachedShapeExtension = (ShapeExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ for (int i = 0; i < 8 && (i * 2 + 4) < shapeExtension -> size_; i++)
+ {
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << shapeExtension -> data[i]
+ << " as data[" << i << "] field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue((unsigned int) shapeExtension -> data[i], 16,
+ *clientCache -> shapeDataCache[i]);
+
+ cachedShapeExtension -> data[i] = shapeExtension -> data[i];
+ }
+}
+
+void ShapeExtensionStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ ShapeExtensionMessage *shapeExtension = (ShapeExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ for (int i = 0; i < 8 && (i * 2 + 4) < shapeExtension -> size_; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache -> shapeDataCache[i]);
+
+ shapeExtension -> data[i] = (unsigned short) value;
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded value " << shapeExtension -> data[i]
+ << " as data[" << i << "] field.\n" << logofs_flush;
+ #endif
+ }
+}
diff --git a/nxcomp/ShapeExtension.h b/nxcomp/ShapeExtension.h
new file mode 100644
index 000000000..32c3b55ef
--- /dev/null
+++ b/nxcomp/ShapeExtension.h
@@ -0,0 +1,157 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef ShapeExtension_H
+#define ShapeExtension_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define SHAPEEXTENSION_ENABLE_CACHE 1
+#define SHAPEEXTENSION_ENABLE_DATA 1
+#define SHAPEEXTENSION_ENABLE_SPLIT 0
+#define SHAPEEXTENSION_ENABLE_COMPRESS 1
+
+#define SHAPEEXTENSION_DATA_LIMIT 3200
+#define SHAPEEXTENSION_DATA_OFFSET 20
+
+#define SHAPEEXTENSION_CACHE_SLOTS 3000
+#define SHAPEEXTENSION_CACHE_THRESHOLD 10
+#define SHAPEEXTENSION_CACHE_LOWER_THRESHOLD 5
+
+#define SHAPEEXTENSION_ENABLE_COMPRESS_IF_PROTO_STEP_7 0
+
+//
+// The message class.
+//
+
+class ShapeExtensionMessage : public Message
+{
+ friend class ShapeExtensionStore;
+
+ public:
+
+ ShapeExtensionMessage()
+ {
+ }
+
+ ~ShapeExtensionMessage()
+ {
+ }
+
+ //
+ // Note for encoding in protocol level 1: we consider
+ // for this message a data offset of 4. Bytes from 5
+ // to 20, if present, are taken as part of identity
+ // and encoded through an array of int caches.
+ //
+
+ private:
+
+ unsigned char opcode;
+ unsigned short data[8];
+};
+
+class ShapeExtensionStore : public MessageStore
+{
+ public:
+
+ ShapeExtensionStore(StaticCompressor *compressor);
+
+ virtual ~ShapeExtensionStore();
+
+ virtual const char *name() const
+ {
+ return "ShapeExtension";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return opcode_;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(ShapeExtensionMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new ShapeExtensionMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new ShapeExtensionMessage((const ShapeExtensionMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (ShapeExtensionMessage *) message;
+ }
+
+ virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const;
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+
+ private:
+
+ unsigned char opcode_;
+};
+
+#endif /* ShapeExtension_H */
diff --git a/nxcomp/Socket.cpp b/nxcomp/Socket.cpp
new file mode 100644
index 000000000..ea00a9b4e
--- /dev/null
+++ b/nxcomp/Socket.cpp
@@ -0,0 +1,745 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include <sys/types.h>
+#include <sys/utsname.h>
+
+#if defined(__CYGWIN32__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__sun)
+#include <netinet/in_systm.h>
+#endif
+
+#ifdef __sun
+#include <unistd.h>
+#include <sys/termios.h>
+#endif
+
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#include <netdb.h>
+#include <fcntl.h>
+
+//
+// System specific defines.
+//
+
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__sun)
+#define SOL_IP IPPROTO_IP
+#endif
+
+#ifdef __sun
+#define INADDR_NONE ((unsigned int) -1)
+#endif
+
+//
+// The TIOCOUTQ ioctl is not implemented on Cygwin.
+// Note also that TIOCOUTQ and IPTOS_LOWDELAY are
+// disabled when running on MacOS/X.
+//
+
+#ifdef __CYGWIN32__
+#define TIOCOUTQ ((unsigned int) -1)
+#endif
+
+//
+// NX includes.
+//
+
+#include "Misc.h"
+#include "Socket.h"
+
+//
+// Set verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Set this only once by querying OS details.
+//
+
+static int _kernelStep = -1;
+
+int GetKernelStep()
+{
+ if (_kernelStep < 0)
+ {
+ //
+ // At the moment only NX clients run on Win32
+ // and MacOS/X so we are not really interested
+ // in the relevant OS dependent functions.
+ //
+
+ #if defined(__CYGWIN32__) || defined(__APPLE__)
+
+ _kernelStep = 0;
+
+ #else
+
+ struct utsname buffer;
+
+ if (uname(&buffer) < 0)
+ {
+ #ifdef WARNING
+ *logofs << "Socket: WARNING! Failed to get system info. Error is "
+ << EGET() << " '" << ESTR() << "'.\n" << logofs_flush;
+
+ *logofs << "Socket: WARNING! Assuming lowest system support.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Failed to get system info. Error is "
+ << EGET() << " '" << ESTR() << "'.\n";
+
+ cerr << "Warning" << ": Assuming lowest system support.\n";
+
+ _kernelStep = 0;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Socket: System is '" << buffer.sysname
+ << "' nodename '" << buffer.nodename << "' release '"
+ << buffer.release << "'.\n" << logofs_flush;
+
+ *logofs << "Socket: Version is '" << buffer.version << "' machine '"
+ << buffer.machine << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // Should test support on other operating systems.
+ //
+
+ if (strcmp(buffer.sysname, "Linux") == 0)
+ {
+ if (strncmp(buffer.release, "2.0.", 4) == 0 ||
+ strncmp(buffer.release, "2.2.", 4) == 0)
+ {
+ #ifdef TEST
+ *logofs << "Socket: Assuming level 2 system support.\n"
+ << logofs_flush;
+ #endif
+
+ _kernelStep = 2;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Socket: Assuming level 3 system support.\n"
+ << logofs_flush;
+ #endif
+
+ _kernelStep = 3;
+ }
+ }
+ else if (strcmp(buffer.sysname, "SunOS") == 0)
+ {
+ #ifdef TEST
+ *logofs << "Socket: Assuming level 1 system support.\n"
+ << logofs_flush;
+ #endif
+
+ _kernelStep = 1;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Socket: Assuming level 0 system support.\n"
+ << logofs_flush;
+ #endif
+
+ _kernelStep = 0;
+ }
+ }
+
+ #endif /* #if defined(__CYGWIN32__) || defined(__APPLE__) */
+ }
+
+ return _kernelStep;
+}
+
+int SetReuseAddress(int fd)
+{
+ int flag = 1;
+
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (char *) &flag, sizeof(flag)) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Socket: PANIC! Failed to set SO_REUSEADDR flag on FD#"
+ << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to set SO_REUSEADDR flag on FD#"
+ << fd << ". Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+
+ return -1;
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Socket: Set SO_REUSEADDR flag on FD#"
+ << fd << ".\n" << logofs_flush;
+ }
+ #endif
+
+ return 1;
+}
+
+int SetNonBlocking(int fd, int value)
+{
+ int flags = fcntl(fd, F_GETFL);
+
+ if (flags >= 0)
+ {
+ if (value == 0)
+ {
+ flags &= ~O_NONBLOCK;
+ }
+ else
+ {
+ flags |= O_NONBLOCK;
+ }
+ }
+
+ if (flags < 0 || fcntl(fd, F_SETFL, flags) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Socket: PANIC! Failed to set O_NONBLOCK flag on FD#"
+ << fd << " to " << value << ". Error is " << EGET()
+ << " '" << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to set O_NONBLOCK flag on FD#"
+ << fd << " to " << value << ". Error is " << EGET()
+ << " '" << ESTR() << "'.\n";
+
+ return -1;
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Socket: Set O_NONBLOCK flag on FD#"
+ << fd << " to " << value << ".\n"
+ << logofs_flush;
+ }
+ #endif
+
+ return 1;
+}
+
+int SetLingerTimeout(int fd, int timeout)
+{
+ struct linger linger_value;
+
+ if (timeout > 0)
+ {
+ linger_value.l_onoff = 1;
+ linger_value.l_linger = timeout;
+ }
+ else
+ {
+ linger_value.l_onoff = 0;
+ linger_value.l_linger = 0;
+ }
+
+ if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &linger_value, sizeof(linger_value)) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Socket: PANIC! Failed to set SO_LINGER values to "
+ << linger_value.l_onoff << " and " << linger_value.l_linger
+ << " on FD#" << fd << ". Error is " << EGET() << " '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to set SO_LINGER values to "
+ << linger_value.l_onoff << " and " << linger_value.l_linger
+ << " on FD#" << fd << ". Error is " << EGET() << " '"
+ << ESTR() << "'.\n";
+
+ return -1;
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Socket: Set SO_LINGER values to "
+ << linger_value.l_onoff << " and " << linger_value.l_linger
+ << " on FD#" << fd << ".\n" << logofs_flush;
+ }
+ #endif
+
+ return 1;
+}
+
+int SetSendBuffer(int fd, int size)
+{
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Socket: PANIC! Failed to set SO_SNDBUF size to "
+ << size << " on FD#" << fd << ". Error is "
+ << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to set SO_SNDBUF size to "
+ << size << " on FD#" << fd << ". Error is "
+ << EGET() << " '" << ESTR() << "'.\n";
+
+ return -1;
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Socket: Set SO_SNDBUF on FD#" << fd
+ << " to " << size << " bytes.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ return 1;
+}
+
+int SetReceiveBuffer(int fd, int size)
+{
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Socket: PANIC! Failed to set SO_RCVBUF size to "
+ << size << " on FD#" << fd << ". Error is "
+ << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to set SO_RCVBUF size to "
+ << size << " on FD#" << fd << ". Error is "
+ << EGET() << " '" << ESTR() << "'.\n";
+
+ return -1;
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Socket: Set SO_RCVBUF on FD#" << fd
+ << " to " << size << " bytes.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ return 1;
+}
+
+int SetNoDelay(int fd, int value)
+{
+ int result = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value));
+
+ if (result == 0)
+ {
+ result = 1;
+ }
+ else if (result < 0)
+ {
+ //
+ // Is it become a different error on
+ // Mac OSX 10.4?
+ //
+
+ #if defined(__APPLE__)
+
+ result = 0;
+
+ #endif
+
+ #if defined(__sun)
+
+ if (EGET() == ENOPROTOOPT)
+ {
+ result = 0;
+ }
+
+ #endif
+
+ #if !defined(__APPLE__) && !defined(__sun)
+
+ if (EGET() == EOPNOTSUPP)
+ {
+ result = 0;
+ }
+
+ #endif
+ }
+
+ if (result < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Socket: PANIC! Failed to set TCP_NODELAY flag on "
+ << "FD#" << fd << " to " << value << ". Error is "
+ << EGET() << " '" << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to set TCP_NODELAY flag on "
+ << "FD#" << fd << " to " << value << ". Error is "
+ << EGET() << " '" << ESTR() << "'.\n";
+ }
+ #ifdef TEST
+ else if (result == 0)
+ {
+ #ifdef TEST
+ *logofs << "Socket: Option TCP_NODELAY not supported "
+ << "on FD#" << fd << ".\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ *logofs << "Socket: Set TCP_NODELAY flag on FD#"
+ << fd << " to " << value << ".\n"
+ << logofs_flush;
+ }
+ #endif
+
+ return result;
+}
+
+int SetKeepAlive(int fd)
+{
+ int flag = 1;
+
+ if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &flag, sizeof(flag)) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Socket: PANIC! Failed to set SO_KEEPALIVE flag on "
+ << "FD#" << fd << ". Error is " << EGET() << " '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to set SO_KEEPALIVE flag on "
+ << "FD#" << fd << ". Error is " << EGET() << " '"
+ << ESTR() << "'.\n";
+
+ return -1;
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Socket: Set SO_KEEPALIVE flag on FD#"
+ << fd << ".\n" << logofs_flush;
+ }
+ #endif
+
+ return 1;
+}
+
+int SetLowDelay(int fd)
+{
+ if (_kernelStep < 0)
+ {
+ GetKernelStep();
+ }
+
+ switch (_kernelStep)
+ {
+ case 3:
+ case 2:
+ case 1:
+ {
+ int flag = IPTOS_LOWDELAY;
+
+ if (setsockopt(fd, SOL_IP, IP_TOS, &flag, sizeof(flag)) < 0)
+ {
+ if (EGET() == EOPNOTSUPP)
+ {
+ #ifdef TEST
+ *logofs << "Socket: Option IPTOS_LOWDELAY not supported "
+ << "on FD#" << fd << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "Socket: WARNING! Failed to set IPTOS_LOWDELAY flag on "
+ << "FD#" << fd << ". Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Failed to set IPTOS_LOWDELAY flag on "
+ << "FD#" << fd << ". Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+
+ return -1;
+ }
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Socket: Set IPTOS_LOWDELAY flag on FD#"
+ << fd << ".\n" << logofs_flush;
+ }
+ #endif
+
+ return 1;
+ }
+ default:
+ {
+ #ifdef TEST
+ *logofs << "Socket: Option IPTOS_LOWDELAY not "
+ << "supported on FD#" << fd << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ }
+}
+
+int SetCloseOnExec(int fd)
+{
+ if (fcntl(fd, F_SETFD, 1) != 0)
+ {
+ #ifdef TEST
+ *logofs << "NXClient: PANIC! Cannot set close-on-exec "
+ << "on FD#" << fd << ". Error is " << EGET()
+ << " '" << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot set close-on-exec on FD#"
+ << fd << ". Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int GetBytesReadable(int fd)
+{
+ long readable = 0;
+
+ //
+ // It may fail, for example at session
+ // shutdown.
+ //
+
+ if (ioctl(fd, FIONREAD, &readable) < 0)
+ {
+ #ifdef TEST
+ *logofs << "Socket: PANIC! Failed to get bytes readable "
+ << "from FD#" << fd << ". Error is " << EGET()
+ << " '" << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "Socket: Returning " << (int) readable
+ << " bytes readable on FD#" << fd << ".\n"
+ << logofs_flush;
+ #endif
+
+ return (int) readable;
+}
+
+int GetBytesWritable(int fd)
+{
+ if (_kernelStep < 0)
+ {
+ GetKernelStep();
+ }
+
+ long writable;
+
+ switch (_kernelStep)
+ {
+ case 3:
+ {
+ //
+ // TODO: Should query the real size
+ // of the TCP write buffer.
+ //
+
+ writable = 16384 - GetBytesQueued(fd);
+
+ if (writable < 0)
+ {
+ writable = 0;
+ }
+
+ break;
+ }
+ case 2:
+ {
+ if (ioctl(fd, TIOCOUTQ, (void *) &writable) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Socket: PANIC! Failed to get bytes writable "
+ << "on FD#" << fd << ". Error is " << EGET()
+ << " '" << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to get bytes writable "
+ << "on FD#" << fd << ". Error is " << EGET()
+ << " '" << ESTR() << "'.\n";
+
+ return -1;
+ }
+
+ break;
+ }
+ default:
+ {
+ #ifdef TEST
+ *logofs << "Socket: Option TIOCOUTQ not supported "
+ << "on FD#" << fd << ",\n" << logofs_flush;
+ #endif
+
+ //
+ // TODO: Should query the real size
+ // of the TCP write buffer.
+ //
+
+ writable = 16384;
+
+ break;
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "Socket: Returning " << writable
+ << " bytes writable on FD#" << fd
+ << ".\n" << logofs_flush;
+ #endif
+
+ return (int) writable;
+}
+
+int GetBytesQueued(int fd)
+{
+ //
+ // The TIOCOUTQ ioctl is not implemented on Cygwin
+ // and returns the space available on Linux Kernels
+ // 2.0 and 2.2 (like current MIPS for PS/2).
+ //
+
+ if (_kernelStep < 0)
+ {
+ GetKernelStep();
+ }
+
+ long queued;
+
+ switch (_kernelStep)
+ {
+ case 3:
+ {
+ if (ioctl(fd, TIOCOUTQ, (void *) &queued) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Socket: PANIC! Failed to get bytes queued "
+ << "on FD#" << fd << ". Error is " << EGET()
+ << " '" << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to get bytes queued "
+ << "on FD#" << fd << ". Error is " << EGET()
+ << " '" << ESTR() << "'.\n";
+
+ return -1;
+ }
+
+ break;
+ }
+ case 2:
+ {
+ //
+ // TODO: Should query the real size
+ // of the TCP write buffer.
+ //
+
+ queued = 16384 - GetBytesWritable(fd);
+
+ if (queued < 0)
+ {
+ queued = 0;
+ }
+
+ break;
+ }
+ default:
+ {
+ #ifdef TEST
+ *logofs << "Socket: Option TIOCOUTQ not supported "
+ << "on FD#" << fd << ",\n" << logofs_flush;
+ #endif
+
+ queued = 0;
+
+ break;
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "Socket: Returning " << queued
+ << " bytes queued on FD#" << fd
+ << ".\n" << logofs_flush;
+ #endif
+
+ return (int) queued;
+}
+
+int GetHostAddress(const char *name)
+{
+ hostent *host = gethostbyname(name);
+
+ if (host == NULL)
+ {
+ //
+ // On some Unices gethostbyname() doesn't
+ // accept IP addresses, so try inet_addr.
+ //
+
+ IN_ADDR_T address = inet_addr(name);
+
+ if (address == INADDR_NONE)
+ {
+ #ifdef PANIC
+ *logofs << "Socket: PANIC! Failed to resolve address of '"
+ << name << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to resolve address of '"
+ << name << "'.\n";
+
+ return 0;
+ }
+
+ return (int) address;
+ }
+ else
+ {
+ return (*((int *) host -> h_addr_list[0]));
+ }
+}
diff --git a/nxcomp/Socket.h b/nxcomp/Socket.h
new file mode 100644
index 000000000..27c330850
--- /dev/null
+++ b/nxcomp/Socket.h
@@ -0,0 +1,88 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef Socket_H
+#define Socket_H
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#ifdef __sun
+#include <stropts.h>
+#include <sys/filio.h>
+#endif
+
+//
+// Set socket options.
+//
+
+int SetReuseAddress(int fd);
+int SetNonBlocking(int fd, int value);
+int SetLingerTimeout(int fd, int timeout);
+int SetSendBuffer(int fd, int size);
+int SetReceiveBuffer(int fd, int size);
+int SetNoDelay(int fd, int value);
+int SetKeepAlive(int fd);
+int SetLowDelay(int fd);
+int SetCloseOnExec(int fd);
+
+//
+// Get kernel support level.
+//
+
+int GetKernelStep();
+
+//
+// Get socket info.
+//
+
+int GetBytesReadable(int fd);
+int GetBytesWritable(int fd);
+int GetBytesQueued(int fd);
+
+//
+// Inline version, providing direct access
+// to the interface.
+//
+
+#include "Misc.h"
+
+inline int GetBytesReadable(int fd, int *readable)
+{
+ long t;
+
+ int result = ioctl(fd, FIONREAD, &t);
+
+ #ifdef DEBUG
+ *logofs << "Socket: Bytes readable from FD#"
+ << fd << " are " << t << " with result "
+ << result << ".\n" << logofs_flush;
+ #endif
+
+ *readable = (int) t;
+
+ return result;
+}
+
+//
+// Query Internet address.
+//
+
+int GetHostAddress(const char *name);
+
+#endif /* Socket_H */
diff --git a/nxcomp/Split.cpp b/nxcomp/Split.cpp
new file mode 100644
index 000000000..50627e793
--- /dev/null
+++ b/nxcomp/Split.cpp
@@ -0,0 +1,1846 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include <unistd.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <utime.h>
+
+#include "Misc.h"
+
+#include "Split.h"
+
+#include "Control.h"
+#include "Statistics.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "StaticCompressor.h"
+
+#include "Unpack.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Define this to trace elements
+// allocated and deallocated.
+//
+
+#undef REFERENCES
+
+//
+// Counters used for store control.
+//
+
+int SplitStore::totalSplitSize_;
+int SplitStore::totalSplitStorageSize_;
+
+//
+// This is used for reference count.
+//
+
+#ifdef REFERENCES
+
+int Split::references_ = 0;
+
+#endif
+
+Split::Split()
+{
+ resource_ = nothing;
+ position_ = nothing;
+
+ store_ = NULL;
+
+ d_size_ = 0;
+ i_size_ = 0;
+ c_size_ = 0;
+ r_size_ = 0;
+
+ next_ = 0;
+ load_ = 0;
+ save_ = 0;
+
+ checksum_ = NULL;
+ state_ = split_undefined;
+ mode_ = split_none;
+ action_ = is_discarded;
+
+ #ifdef REFERENCES
+
+ references_++;
+
+ *logofs << "Split: Created new Split at "
+ << this << " out of " << references_
+ << " allocated references.\n" << logofs_flush;
+ #endif
+}
+
+Split::~Split()
+{
+ delete [] checksum_;
+
+ #ifdef REFERENCES
+
+ references_--;
+
+ *logofs << "Split: Deleted Split at "
+ << this << " out of " << references_
+ << " allocated references.\n" << logofs_flush;
+ #endif
+}
+
+SplitStore::SplitStore(StaticCompressor *compressor, CommitStore *commits, int resource)
+
+ : compressor_(compressor), commits_(commits), resource_(resource)
+{
+ splits_ = new T_splits();
+
+ current_ = splits_ -> end();
+
+ splitStorageSize_ = 0;
+
+ #ifdef TEST
+ *logofs << "SplitStore: Created new store [";
+
+ if (resource_ != nothing)
+ {
+ *logofs << resource_;
+ }
+ else
+ {
+ *logofs << "commit";
+ }
+
+ *logofs << "].\n" << logofs_flush;
+
+ *logofs << "SplitStore: Total messages in stores are "
+ << totalSplitSize_ << " with total storage size "
+ << totalSplitStorageSize_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+
+SplitStore::~SplitStore()
+{
+ totalSplitSize_ -= splits_ -> size();
+
+ totalSplitStorageSize_ -= splitStorageSize_;
+
+ for (T_splits::iterator i = splits_ -> begin();
+ i != splits_ -> end(); i++)
+ {
+ delete *i;
+ }
+
+ delete splits_;
+
+ #ifdef TEST
+ *logofs << "SplitStore: Deleted store [";
+
+ if (resource_ != nothing)
+ {
+ *logofs << resource_;
+ }
+ else
+ {
+ *logofs << "commit";
+ }
+
+ *logofs << "] with storage size " << splitStorageSize_
+ << ".\n" << logofs_flush;
+
+ *logofs << "SplitStore: Total messages in stores are "
+ << totalSplitSize_ << " with total storage size "
+ << totalSplitStorageSize_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+
+//
+// This is called at the encoding side.
+//
+
+Split *SplitStore::add(MessageStore *store, int resource, T_split_mode mode,
+ int position, T_store_action action, T_checksum checksum,
+ const unsigned char *buffer, const int size)
+{
+ #ifdef TEST
+ *logofs << "SplitStore: Adding message [" << (unsigned int) store ->
+ opcode() << "] resource " << resource << " mode " << mode
+ << " position " << position << " action [" << DumpAction(action)
+ << "] and checksum [" << DumpChecksum(checksum) << "]"
+ << ".\n" << logofs_flush;
+ #endif
+
+ Split *split = new Split();
+
+ if (split == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Can't allocate "
+ << "memory for the split.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory "
+ << "for the split.\n";
+
+ HandleAbort();
+ }
+
+ split -> store_ = store;
+ split -> resource_ = resource;
+ split -> mode_ = mode;
+ split -> position_ = position;
+ split -> action_ = action;
+
+ split -> store_ -> validateSize(size);
+
+ //
+ // The checksum is not provided if the
+ // message is cached.
+ //
+
+ if (checksum != NULL)
+ {
+ split -> checksum_ = new md5_byte_t[MD5_LENGTH];
+
+ memcpy(split -> checksum_, checksum, MD5_LENGTH);
+ }
+
+ //
+ // We don't need the identity data at the
+ // encoding side. This qualifies the split
+ // as a split generated at the encoding
+ // side.
+ //
+
+ split -> i_size_ = store -> identitySize(buffer, size);
+
+ split -> d_size_ = size - split -> i_size_;
+
+ if (action == IS_ADDED || action == is_discarded)
+ {
+ //
+ // If the message was added to message
+ // store or discarded we need to save
+ // the real data so we can transfer it
+ // at later time.
+ //
+
+ split -> data_.resize(split -> d_size_);
+
+ memcpy(split -> data_.begin(), buffer + split -> i_size_, split -> d_size_);
+
+ //
+ // If the message was added, lock it so
+ // it will not be used by the encoding
+ // side until it is recomposed.
+ //
+
+ if (action == IS_ADDED)
+ {
+ split -> store_ -> lock(split -> position_);
+
+ #ifdef TEST
+
+ commits_ -> validate(split);
+
+ #endif
+ }
+ }
+ #ifdef WARNING
+ else
+ {
+ *logofs << "SplitStore: WARNING! Not copying data for the cached message.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ push(split);
+
+ return split;
+}
+
+//
+// This is called at decoding side. If checksum
+// is provided, the message can be searched on
+// disk, then, if message is found, an event is
+// sent to abort the data transfer.
+//
+
+Split *SplitStore::add(MessageStore *store, int resource, int position,
+ T_store_action action, T_checksum checksum,
+ unsigned char *buffer, const int size)
+{
+ #ifdef TEST
+ *logofs << "SplitStore: Adding message ["
+ << (unsigned int) store -> opcode() << "] resource "
+ << resource << " position " << position << " action ["
+ << DumpAction(action) << "] and checksum ["
+ << DumpChecksum(checksum) << "].\n" << logofs_flush;
+ #endif
+
+ Split *split = new Split();
+
+ if (split == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Can't allocate "
+ << "memory for the split.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory "
+ << "for the split.\n";
+
+ HandleAbort();
+ }
+
+ split -> store_ = store;
+ split -> resource_ = resource;
+ split -> position_ = position;
+ split -> action_ = action;
+
+ split -> store_ -> validateSize(size);
+
+ //
+ // Check if the checksum was provided
+ // by the remote.
+ //
+
+ if (checksum != NULL)
+ {
+ split -> checksum_ = new md5_byte_t[MD5_LENGTH];
+
+ memcpy(split -> checksum_, checksum, MD5_LENGTH);
+ }
+
+ split -> i_size_ = store -> identitySize(buffer, size);
+
+ //
+ // Copy the identity so we can expand the
+ // message when it is committed.
+ //
+
+ split -> identity_.resize(split -> i_size_);
+
+ memcpy(split -> identity_.begin(), buffer, split -> i_size_);
+
+ split -> d_size_ = size - split -> i_size_;
+
+ if (action == IS_ADDED || action == is_discarded)
+ {
+ //
+ // The unpack procedure will check if the
+ // first 2 bytes of the buffer contain the
+ // pattern and will not try to expand the
+ // image.
+ //
+
+ split -> data_.resize(2);
+
+ unsigned char *data = split -> data_.begin();
+
+ data[0] = SPLIT_PATTERN;
+ data[1] = SPLIT_PATTERN;
+
+ //
+ // If the message was added to the store,
+ // we don't have the data part, yet, so
+ // we need to lock the message until it
+ // is recomposed.
+ //
+
+ if (action == IS_ADDED)
+ {
+ split -> store_ -> lock(split -> position_);
+
+ #ifdef TEST
+
+ commits_ -> validate(split);
+
+ #endif
+ }
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "SplitStore: WARNING! Copying data for the cached message.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // We may optionally take the data from the
+ // message store in compressed form, but,
+ // as the data has been decompressed in the
+ // buffer, we save a further decompression.
+ //
+
+ split -> data_.resize(split -> d_size_);
+
+ memcpy(split -> data_.begin(), buffer + split -> i_size_, split -> d_size_);
+ }
+
+ push(split);
+
+ return split;
+}
+
+void SplitStore::push(Split *split)
+{
+ splits_ -> push_back(split);
+
+ splitStorageSize_ += getNodeSize(split);
+
+ totalSplitSize_++;
+
+ totalSplitStorageSize_ += getNodeSize(split);
+
+ statistics -> addSplit();
+
+ #ifdef TEST
+ *logofs << "SplitStore: There are " << splits_ -> size()
+ << " messages in store [" << resource_ << "] with "
+ << "storage size " << splitStorageSize_ << ".\n"
+ << logofs_flush;
+
+ *logofs << "SplitStore: Total messages in stores are "
+ << totalSplitSize_ << " with total storage size "
+ << totalSplitStorageSize_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ split -> state_ = split_added;
+}
+
+void SplitStore::dump()
+{
+ #ifdef DUMP
+
+ int n;
+
+ Split *split;
+
+ *logofs << "SplitStore: DUMP! Dumping content of ";
+
+ if (commits_ == NULL)
+ {
+ *logofs << "[commits]";
+ }
+ else
+ {
+ *logofs << "[splits] for store [" << resource_ << "]";
+ }
+
+ *logofs << " with [" << getSize() << "] elements "
+ << "in the store.\n" << logofs_flush;
+
+ n = 0;
+
+ for (T_splits::iterator i = splits_ -> begin(); i != splits_ -> end(); i++, n++)
+ {
+ split = *i;
+
+ *logofs << "SplitStore: DUMP! Split [" << n << "] has action ["
+ << DumpAction(split -> action_) << "] state ["
+ << DumpState(split -> state_) << "] ";
+
+ if (split -> resource_ >= 0)
+ {
+ *logofs << "resource " << split -> resource_;
+ }
+
+ *logofs << " request " << (unsigned) split -> store_ -> opcode()
+ << " position " << split -> position_ << " size is "
+ << split -> data_.size() << " (" << split -> d_size_
+ << "/" << split -> c_size_ << "/" << split -> r_size_
+ << ") with " << split -> data_.size() - split -> next_
+ << "] bytes to go.\n" << logofs_flush;
+ }
+
+ #endif
+}
+
+int SplitStore::send(EncodeBuffer &encodeBuffer, int packetSize)
+{
+ if (splits_ -> size() == 0)
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Function send called with no splits available.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Function send called with no splits available.\n";
+
+ HandleAbort();
+ }
+
+ //
+ // A start operation must always be executed on
+ // the split, even in the case the split will be
+ // later aborted.
+ //
+
+ if (current_ == splits_ -> end())
+ {
+ start(encodeBuffer);
+ }
+
+ //
+ // If we have matched the checksum received from
+ // the remote side then we must abort the current
+ // split, else we can send another block of data
+ // to the remote peer.
+ //
+
+ Split *split = *current_;
+
+ unsigned int abort = 0;
+
+ if (split -> state_ == split_loaded)
+ {
+ abort = 1;
+ }
+
+ encodeBuffer.encodeBoolValue(abort);
+
+ if (abort == 1)
+ {
+ #ifdef TEST
+ *logofs << "SplitStore: Aborting split for checksum ["
+ << DumpChecksum(split -> checksum_) << "] position "
+ << split -> position_ << " with " << (split ->
+ data_.size() - split -> next_) << " bytes to go "
+ << "out of " << split -> data_.size()
+ << ".\n" << logofs_flush;
+ #endif
+
+ statistics -> addSplitAborted();
+
+ statistics -> addSplitAbortedBytesOut(split -> data_.size() - split -> next_);
+
+ split -> next_ = split -> data_.size();
+
+ split -> state_ = split_aborted;
+ }
+ else
+ {
+ int count = (packetSize <= 0 || split -> next_ +
+ packetSize > (int) split -> data_.size() ?
+ split -> data_.size() - split -> next_ : packetSize);
+
+ #ifdef TEST
+ *logofs << "SplitStore: Sending split for checksum ["
+ << DumpChecksum(split -> checksum_) << "] count "
+ << count << " position " << split -> position_
+ << ". Data size is " << split -> data_.size() << " ("
+ << split -> d_size_ << "/" << split -> c_size_ << "), "
+ << split -> data_.size() - (split -> next_ + count)
+ << " to go.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeValue(count, 32, 10);
+
+ encodeBuffer.encodeMemory(split -> data_.begin() + split -> next_, count);
+
+ split -> next_ += count;
+ }
+
+ //
+ // Was data completely transferred? We are the
+ // sending side. We must update the message in
+ // store, even if split was aborted.
+ //
+
+ if (split -> next_ != ((int) split -> data_.size()))
+ {
+ return 0;
+ }
+
+ //
+ // Move the split at the head of the
+ // list to the commits.
+ //
+
+ remove(split);
+
+ //
+ // Reset current position to the
+ // end of repository.
+ //
+
+ current_ = splits_ -> end();
+
+ #ifdef TEST
+ *logofs << "SplitStore: Removed split at head of the list. "
+ << "Resource is " << split -> resource_ << " request "
+ << (unsigned) split -> store_ -> opcode() << " position "
+ << split -> position_ << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int SplitStore::start(EncodeBuffer &encodeBuffer)
+{
+ //
+ // Get the element at the top of the
+ // list.
+ //
+
+ current_ = splits_ -> begin();
+
+ Split *split = *current_;
+
+ #ifdef TEST
+ *logofs << "SplitStore: Starting split for checksum ["
+ << DumpChecksum(split -> checksum_) << "] position "
+ << split -> position_ << " with " << (split ->
+ data_.size() - split -> next_) << " bytes to go "
+ << "out of " << split -> data_.size()
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // See if compression of the data part is
+ // enabled.
+ //
+
+ if (split -> store_ -> enableCompress)
+ {
+ //
+ // If the split is going to be aborted don't
+ // compress the data and go straight to the
+ // send. The new data size will be assumed
+ // from the disk cache.
+ //
+
+ if (split -> state_ != split_loaded)
+ {
+ unsigned int compressedSize = 0;
+ unsigned char *compressedData = NULL;
+
+ if (control -> LocalDataCompression &&
+ (compressor_ -> compressBuffer(split -> data_.begin(), split -> d_size_,
+ compressedData, compressedSize)))
+ {
+ //
+ // Replace the data with the one in
+ // compressed form.
+ //
+
+ #ifdef TEST
+ *logofs << "SplitStore: Split data of size " << split -> d_size_
+ << " has been compressed to " << compressedSize
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ split -> data_.clear();
+
+ split -> data_.resize(compressedSize);
+
+ memcpy(split -> data_.begin(), compressedData, compressedSize);
+
+ split -> c_size_ = compressedSize;
+
+ //
+ // Inform our peer that the data is
+ // compressed and send the new size.
+ //
+
+ encodeBuffer.encodeBoolValue(1);
+
+ encodeBuffer.encodeValue(compressedSize, 32, 14);
+
+ #ifdef TEST
+ *logofs << "SplitStore: Signaled " << split -> c_size_
+ << " bytes of compressed data for this message.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+ }
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "SplitStore: Not trying to compress the "
+ << "loaded message.\n" << logofs_flush;
+ }
+ #endif
+
+ //
+ // Tell to the remote that data will
+ // follow uncompressed.
+ //
+
+ encodeBuffer.encodeBoolValue(0);
+ }
+
+ return 1;
+}
+
+int SplitStore::start(DecodeBuffer &decodeBuffer)
+{
+ #ifdef TEST
+ *logofs << "SplitStore: Going to receive a new split from the remote side.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Get the element at the head
+ // of the list.
+ //
+
+ current_ = splits_ -> begin();
+
+ Split *split = *current_;
+
+ unsigned int compressedSize = 0;
+
+ //
+ // Save the data size known by the remote.
+ // This information will be needed if the
+ // remote will not have a chance to abort
+ // the split.
+ //
+
+ split -> r_size_ = split -> d_size_;
+
+ //
+ // Find out if data was compressed by the
+ // remote.
+ //
+
+ if (split -> store_ -> enableCompress)
+ {
+ decodeBuffer.decodeBoolValue(compressedSize);
+
+ if (compressedSize == 1)
+ {
+ //
+ // Get the compressed size.
+ //
+
+ if (control -> isProtoStep7() == 1)
+ {
+ decodeBuffer.decodeValue(compressedSize, 32, 14);
+ }
+ else
+ {
+ //
+ // As we can't refuse to handle the decoding
+ // of the split message when connected to an
+ // old proxy version, we need to decode this
+ // in a way that is compatible.
+ //
+
+ unsigned int diffSize;
+
+ decodeBuffer.decodeValue(diffSize, 32, 14);
+
+ split -> store_ -> lastResize += diffSize;
+
+ compressedSize = split -> store_ -> lastResize;
+ }
+
+ split -> store_ -> validateSize(split -> d_size_, compressedSize);
+
+ split -> r_size_ = compressedSize;
+ }
+ }
+
+ //
+ // Update the size if the split
+ // was not already loaded.
+ //
+
+ if (split -> state_ != split_loaded)
+ {
+ split -> data_.clear();
+
+ if (compressedSize > 0)
+ {
+ split -> c_size_ = compressedSize;
+
+ #ifdef TEST
+ *logofs << "SplitStore: Split data of size "
+ << split -> d_size_ << " was compressed to "
+ << split -> c_size_ << " bytes.\n"
+ << logofs_flush;
+ #endif
+
+ split -> data_.resize(split -> c_size_);
+ }
+ else
+ {
+ split -> data_.resize(split -> d_size_);
+ }
+
+ unsigned char *data = split -> data_.begin();
+
+ data[0] = SPLIT_PATTERN;
+ data[1] = SPLIT_PATTERN;
+ }
+ #ifdef TEST
+ else
+ {
+ //
+ // The message had been already
+ // loaded from disk.
+ //
+
+ if (compressedSize > 0)
+ {
+ if ((int) compressedSize != split -> c_size_)
+ {
+ *logofs << "SplitStore: WARNING! Compressed data size is "
+ << "different than the loaded compressed size.\n"
+ << logofs_flush;
+ }
+
+ *logofs << "SplitStore: Ignoring the new size with "
+ << "loaded compressed size " << split -> c_size_
+ << ".\n" << logofs_flush;
+ }
+ }
+ #endif
+
+ return 1;
+}
+
+int SplitStore::receive(DecodeBuffer &decodeBuffer)
+{
+ if (splits_ -> size() == 0)
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Function receive called with no splits available.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Function receive called with no splits available.\n";
+
+ HandleAbort();
+ }
+
+ if (current_ == splits_ -> end())
+ {
+ start(decodeBuffer);
+ }
+
+ //
+ // Check first if split was aborted, else add
+ // any new data to message being recomposed.
+ //
+
+ Split *split = *current_;
+
+ unsigned int abort = 0;
+
+ decodeBuffer.decodeBoolValue(abort);
+
+ if (abort == 1)
+ {
+ #ifdef TEST
+ *logofs << "SplitStore: Aborting split for checksum ["
+ << DumpChecksum(split -> checksum_) << "] position "
+ << split -> position_ << " with " << (split ->
+ data_.size() - split -> next_) << " bytes to go "
+ << "out of " << split -> data_.size()
+ << ".\n" << logofs_flush;
+ #endif
+
+ statistics -> addSplitAborted();
+
+ statistics -> addSplitAbortedBytesOut(split -> r_size_ - split -> next_);
+
+ split -> next_ = split -> r_size_;
+
+ split -> state_ = split_aborted;
+ }
+ else
+ {
+ //
+ // Get the size of the packet.
+ //
+
+ unsigned int count;
+
+ decodeBuffer.decodeValue(count, 32, 10);
+
+ //
+ // If the split was not already loaded from
+ // disk, decode the packet and update our
+ // copy of the data. The encoding side may
+ // have not received the abort event, yet,
+ // and may be unaware that the message is
+ // stored in compressed form at our side.
+ //
+
+ #ifdef TEST
+ *logofs << "SplitStore: Receiving split for checksum ["
+ << DumpChecksum(split -> checksum_) << "] count "
+ << count << " position " << split -> position_
+ << ". Data size is " << split -> data_.size() << " ("
+ << split -> d_size_ << "/" << split -> c_size_ << "/"
+ << split -> r_size_ << "), " << split -> r_size_ -
+ (split -> next_ + count) << " to go.\n"
+ << logofs_flush;
+ #endif
+
+ if (split -> next_ + count > (unsigned) split -> r_size_)
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Invalid data count "
+ << count << "provided in the split.\n"
+ << logofs_flush;
+
+ *logofs << "SplitStore: PANIC! While receiving split for "
+ << "checksum [" << DumpChecksum(split -> checksum_)
+ << "] with count " << count << " action ["
+ << DumpAction(split -> action_) << "] state ["
+ << DumpState(split -> state_) << "]. Data size is "
+ << split -> data_.size() << " (" << split -> d_size_
+ << "/" << split -> c_size_ << "), " << split ->
+ data_.size() - (split -> next_ + count)
+ << " to go.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid data count "
+ << count << "provided in the split.\n";
+
+ HandleAbort();
+ }
+
+ if (split -> state_ != split_loaded)
+ {
+ #ifdef TEST
+
+ if (split -> next_ + count > split -> data_.size())
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Inconsistent split data size "
+ << split -> data_.size() << " with expected size "
+ << split -> r_size_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ HandleAbort();
+ }
+
+ #endif
+
+ memcpy(split -> data_.begin() + split -> next_,
+ decodeBuffer.decodeMemory(count), count);
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "SplitStore: WARNING! Data discarded with split "
+ << "loaded from disk.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeMemory(count);
+ }
+
+ split -> next_ += count;
+ }
+
+ //
+ // Is unsplit complete?
+ //
+
+ if (split -> next_ != split -> r_size_)
+ {
+ return 0;
+ }
+
+ //
+ // If the persistent cache is enabled,
+ // we have a valid checksum and the
+ // split was not originally retrieved
+ // from disk, save the message on disk.
+ //
+
+ if (split -> state_ != split_loaded &&
+ split -> state_ != split_aborted)
+ {
+ save(split);
+ }
+
+ //
+ // Move the split at the head of the
+ // list to the commits.
+ //
+
+ remove(split);
+
+ //
+ // Reset the current position to the
+ // end of the repository.
+ //
+
+ current_ = splits_ -> end();
+
+ #ifdef TEST
+ *logofs << "SplitStore: Removed split at head of the list. "
+ << "Resource is " << split -> resource_ << " request "
+ << (unsigned) split -> store_ -> opcode() << " position "
+ << split -> position_ << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+Split *SplitStore::pop()
+{
+ if (splits_ -> size() == 0)
+ {
+ #ifdef TEST
+ *logofs << "SplitStore: The split store is empty.\n"
+ << logofs_flush;
+ #endif
+
+ return NULL;
+ }
+
+ //
+ // Move the pointer at the end of the list.
+ // The next send operation will eventually
+ // start a new split.
+ //
+
+ current_ = splits_ -> end();
+
+ Split *split = *(splits_ -> begin());
+
+ splits_ -> pop_front();
+
+ #ifdef TEST
+ *logofs << "SplitStore: Removed split at the head of the "
+ << "list with resource " << split -> resource_
+ << " request " << (unsigned) split -> store_ ->
+ opcode() << " position " << split -> position_
+ << ".\n" << logofs_flush;
+ #endif
+
+ splitStorageSize_ -= getNodeSize(split);
+
+ totalSplitSize_--;
+
+ totalSplitStorageSize_ -= getNodeSize(split);
+
+ #ifdef TEST
+ *logofs << "SplitStore: There are " << splits_ -> size()
+ << " messages in store [" << resource_ << "] with "
+ << "storage size " << splitStorageSize_ << ".\n"
+ << logofs_flush;
+
+ *logofs << "SplitStore: Total messages in stores are "
+ << totalSplitSize_ << " with total storage size "
+ << totalSplitStorageSize_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return split;
+}
+
+void SplitStore::remove(Split *split)
+{
+ #ifdef TEST
+ *logofs << "SplitStore: Going to remove the split from the list.\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef TEST
+
+ if (split != getFirstSplit())
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Trying to remove a split "
+ << "not at the head of the list.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Trying to remove a split "
+ << "not at the head of the list.\n";
+
+ HandleAbort();
+ }
+
+ #endif
+
+ //
+ // Move the split to the commit store.
+ //
+
+ splits_ -> pop_front();
+
+ commits_ -> splits_ -> push_back(split);
+
+ splitStorageSize_ -= getNodeSize(split);
+
+ totalSplitSize_--;
+
+ totalSplitStorageSize_ -= getNodeSize(split);
+
+ #ifdef TEST
+ *logofs << "SplitStore: There are " << splits_ -> size()
+ << " messages in store [" << resource_ << "] with "
+ << "storage size " << splitStorageSize_ << ".\n"
+ << logofs_flush;
+
+ *logofs << "SplitStore: Total messages in stores are "
+ << totalSplitSize_ << " with total storage size "
+ << totalSplitStorageSize_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef TEST
+
+ if (splits_ -> size() == 0)
+ {
+ if (splitStorageSize_ != 0)
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Internal error calculating "
+ << "split data size. It is " << splitStorageSize_
+ << " while should be 0.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Internal error calculating "
+ << "split data size. It is " << splitStorageSize_
+ << " while should be 0.\n";
+
+ HandleAbort();
+ }
+ }
+
+ #endif
+}
+
+const char *SplitStore::name(const T_checksum checksum)
+{
+ if (checksum == NULL)
+ {
+ return NULL;
+ }
+
+ char *pathName = control -> ImageCachePath;
+
+ if (pathName == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Cannot determine directory of "
+ << "NX image files.\n" << logofs_flush;
+ #endif
+
+ return NULL;
+ }
+
+ int pathSize = strlen(pathName);
+
+ //
+ // File name is "[path][/I-c/I-][checksum][\0]",
+ // where c is the first hex digit of checksum.
+ //
+
+ int nameSize = pathSize + 7 + MD5_LENGTH * 2 + 1;
+
+ char *fileName = new char[nameSize];
+
+ if (fileName == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Cannot allocate space for "
+ << "NX image file name.\n" << logofs_flush;
+ #endif
+
+ return NULL;
+ }
+
+ strcpy(fileName, pathName);
+
+ sprintf(fileName + pathSize, "/I-%1X/I-",
+ *((unsigned char *) checksum) >> 4);
+
+ for (unsigned int i = 0; i < MD5_LENGTH; i++)
+ {
+ sprintf(fileName + pathSize + 7 + (i * 2), "%02X",
+ ((unsigned char *) checksum)[i]);
+ }
+
+ return fileName;
+}
+
+int SplitStore::save(Split *split)
+{
+ //
+ // Check if saving the message on the
+ // persistent cache is enabled.
+ //
+
+ if (split -> save_ == 0)
+ {
+ return 0;
+ }
+
+ T_checksum checksum = split -> checksum_;
+
+ const char *fileName = name(checksum);
+
+ if (fileName == NULL)
+ {
+ return 0;
+ }
+
+ unsigned int splitSize;
+
+ ostream *fileStream = NULL;
+
+ unsigned char *fileHeader = NULL;
+
+ //
+ // Get the other data from the split.
+ //
+
+ unsigned char opcode = split -> store_ -> opcode();
+
+ unsigned char *data = split -> data_.begin();
+
+ int dataSize = split -> d_size_;
+ int compressedSize = split -> c_size_;
+
+ #ifdef DEBUG
+ *logofs << "SplitStore: Going to save split OPCODE#"
+ << (unsigned int) opcode << " to file '" << fileName
+ << "' with size " << dataSize << " and compressed size "
+ << compressedSize << ".\n" << logofs_flush;
+ #endif
+
+ DisableSignals();
+
+ //
+ // Change the mask to make the file only
+ // readable by the user, then restore the
+ // old mask.
+ //
+
+ mode_t fileMode;
+
+ //
+ // Check if the file already exists. We try to
+ // load the message when the split is started
+ // and save it only if it is not found. Still
+ // the remote side may send the same image mul-
+ // tiple time and we may not have the time to
+ // notify the abort.
+ //
+
+ struct stat fileStat;
+
+ if (stat(fileName, &fileStat) == 0)
+ {
+ #ifdef TEST
+ *logofs << "SplitStore: Image file '" << fileName
+ << "' already present on disk.\n"
+ << logofs_flush;
+ #endif
+
+ goto SplitStoreSaveError;
+ }
+
+ fileMode = umask(0077);
+
+ fileStream = new ofstream(fileName, ios::out | ios::binary);
+
+ umask(fileMode);
+
+ if (CheckData(fileStream) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Cannot open file '" << fileName
+ << "' for output.\n" << logofs_flush;
+ #endif
+
+ goto SplitStoreSaveError;
+ }
+
+ fileHeader = new unsigned char[SPLIT_HEADER_SIZE];
+
+ if (fileHeader == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Cannot allocate space for "
+ << "NX image header.\n" << logofs_flush;
+ #endif
+
+ goto SplitStoreSaveError;
+ }
+
+ //
+ // Leave 3 bytes for future use. Please note
+ // that, on some CPUs, we can't use PutULONG()
+ // to write integers that are not aligned to
+ // the word boundary.
+ //
+
+ *fileHeader = opcode;
+
+ *(fileHeader + 1) = 0;
+ *(fileHeader + 2) = 0;
+ *(fileHeader + 3) = 0;
+
+ PutULONG(dataSize, fileHeader + 4, false);
+ PutULONG(compressedSize, fileHeader + 8, false);
+
+ splitSize = (compressedSize > 0 ? compressedSize : dataSize);
+
+ if (PutData(fileStream, fileHeader, SPLIT_HEADER_SIZE) < 0 ||
+ PutData(fileStream, data, splitSize) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Cannot write to NX "
+ << "image file '" << fileName << "'.\n"
+ << logofs_flush;
+ #endif
+
+ goto SplitStoreSaveError;
+ }
+
+ //
+ // Check if all the data was written on the
+ // disk and, if not, remove the faulty copy.
+ //
+
+ FlushData(fileStream);
+
+ if (CheckData(fileStream) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Failed to write NX "
+ << "image file '" << fileName << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Failed to write NX "
+ << "image file '" << fileName << "'.\n";
+
+ goto SplitStoreSaveError;
+ }
+
+ #ifdef TEST
+ *logofs << "SplitStore: Saved split to file '" << fileName
+ << "' with data size " << dataSize << " and "
+ << "compressed data size " << compressedSize
+ << ".\n" << logofs_flush;
+ #endif
+
+ delete fileStream;
+
+ delete [] fileName;
+ delete [] fileHeader;
+
+ EnableSignals();
+
+ //
+ // Update the timestamp as the operation
+ // may have taken some time.
+ //
+
+ getNewTimestamp();
+
+ return 1;
+
+SplitStoreSaveError:
+
+ delete fileStream;
+
+ if (fileName != NULL)
+ {
+ unlink(fileName);
+ }
+
+ delete [] fileName;
+ delete [] fileHeader;
+
+ EnableSignals();
+
+ return -1;
+}
+
+int SplitStore::find(Split *split)
+{
+ const char *fileName = name(split -> checksum_);
+
+ if (fileName == NULL)
+ {
+ return 0;
+ }
+
+ #ifdef DEBUG
+ *logofs << "SplitStore: Going to find split OPCODE#"
+ << (unsigned) split -> store_ -> opcode()
+ << " in file '" << fileName << "'.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Check if the file exists and, at the
+ // same time, update the modification
+ // time to prevent its deletion.
+ //
+
+ if (utime(fileName, NULL) == 0)
+ {
+ #ifdef TEST
+ *logofs << "SplitStore: Found split OPCODE#"
+ << (unsigned) split -> store_ -> opcode()
+ << " in file '" << fileName << "'.\n"
+ << logofs_flush;
+ #endif
+
+ delete [] fileName;
+
+ return 1;
+ }
+
+ #ifdef TEST
+ *logofs << "SplitStore: WARNING! Can't find split "
+ << "OPCODE#" << (unsigned) split -> store_ ->
+ opcode() << " in file '" << fileName
+ << "'.\n" << logofs_flush;
+ #endif
+
+ delete [] fileName;
+
+ return 0;
+}
+
+int SplitStore::load(Split *split)
+{
+ //
+ // Check if loading the image is enabled.
+ //
+
+ if (split -> load_ == 0)
+ {
+ return 0;
+ }
+
+ const char *fileName = name(split -> checksum_);
+
+ if (fileName == NULL)
+ {
+ return 0;
+ }
+
+ unsigned char fileOpcode;
+
+ int fileSize;
+ int fileCSize;
+
+ istream *fileStream = NULL;
+
+ unsigned char *fileHeader = NULL;
+
+ DisableSignals();
+
+ #ifdef DEBUG
+ *logofs << "SplitStore: Going to load split OPCODE#"
+ << (unsigned int) split -> store_ -> opcode()
+ << " from file '" << fileName << "'.\n"
+ << logofs_flush;
+ #endif
+
+ fileStream = new ifstream(fileName, ios::in | ios::binary);
+
+ if (CheckData(fileStream) < 0)
+ {
+ #ifdef TEST
+ *logofs << "SplitStore: WARNING! Can't open image file '"
+ << fileName << "' on disk.\n" << logofs_flush;
+ #endif
+
+ goto SplitStoreLoadError;
+ }
+
+ fileHeader = new unsigned char[SPLIT_HEADER_SIZE];
+
+ if (fileHeader == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Cannot allocate space for "
+ << "NX image header.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot allocate space for "
+ << "NX image header.\n";
+
+ goto SplitStoreLoadError;
+ }
+
+ if (GetData(fileStream, fileHeader, SPLIT_HEADER_SIZE) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Cannot read header from "
+ << "NX image file '" << fileName << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Cannot read header from "
+ << "NX image file '" << fileName << "'.\n";
+
+ goto SplitStoreLoadError;
+ }
+
+ fileOpcode = *fileHeader;
+
+ fileSize = GetULONG(fileHeader + 4, false);
+ fileCSize = GetULONG(fileHeader + 8, false);
+
+ //
+ // Don't complain if we find that data was saved
+ // in compressed form even if we were not aware
+ // of the compressed data size. The remote side
+ // compresses the data only at the time it starts
+ // the transferral of the split. We replace our
+ // copy of the data with whatever we find on the
+ // disk.
+ //
+
+ if (fileOpcode != split -> store_ -> opcode() ||
+ fileSize != split -> d_size_ ||
+ fileSize > control -> MaximumRequestSize ||
+ fileCSize > control -> MaximumRequestSize)
+
+ {
+ #ifdef TEST
+ *logofs << "SplitStore: PANIC! Corrupted image file '" << fileName
+ << "'. Expected " << (unsigned int) split -> store_ -> opcode()
+ << "/" << split -> d_size_ << "/" << split -> c_size_ << " found "
+ << (unsigned int) fileOpcode << "/" << fileSize << "/"
+ << fileCSize << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Corrupted image file '" << fileName
+ << "'. Expected " << (unsigned int) split -> store_ -> opcode()
+ << "/" << split -> d_size_ << "/" << split -> c_size_ << " found "
+ << (unsigned int) fileOpcode << "/" << fileSize << "/"
+ << fileCSize << ".\n";
+
+ goto SplitStoreLoadError;
+ }
+
+ //
+ // Update the data size with the size
+ // we got from the disk record.
+ //
+
+ split -> d_size_ = fileSize;
+ split -> c_size_ = fileCSize;
+
+ unsigned int splitSize;
+
+ if (fileCSize > 0)
+ {
+ splitSize = fileCSize;
+ }
+ else
+ {
+ splitSize = fileSize;
+ }
+
+ //
+ // Allocate a new buffer if we didn't
+ // do that already or if the size is
+ // different.
+ //
+
+ if (split -> data_.size() != splitSize)
+ {
+ split -> data_.clear();
+
+ split -> data_.resize(splitSize);
+ }
+
+ if (GetData(fileStream, split -> data_.begin(), splitSize) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Cannot read data from "
+ << "NX image file '" << fileName << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Cannot read data from "
+ << "NX image file '" << fileName << "'.\n";
+
+ goto SplitStoreLoadError;
+ }
+
+ delete fileStream;
+
+ delete [] fileHeader;
+ delete [] fileName;
+
+ EnableSignals();
+
+ //
+ // Update the timestamp as the operation
+ // may have taken some time.
+ //
+
+ getNewTimestamp();
+
+ return 1;
+
+SplitStoreLoadError:
+
+ delete fileStream;
+
+ unlink(fileName);
+
+ delete [] fileName;
+ delete [] fileHeader;
+
+ EnableSignals();
+
+ return -1;
+}
+
+Split *CommitStore::pop()
+{
+ if (splits_ -> size() == 0)
+ {
+ #ifdef TEST
+ *logofs << "CommitStore: The commit store is empty.\n"
+ << logofs_flush;
+ #endif
+
+ return NULL;
+ }
+
+ Split *split = *(splits_ -> begin());
+
+ splits_ -> pop_front();
+
+ #ifdef TEST
+ *logofs << "CommitStore: Removed commit split at the head "
+ << "of the list with resource " << split -> resource_
+ << " request " << (unsigned) split -> store_ ->
+ opcode() << " position " << split -> position_
+ << ".\n" << logofs_flush;
+ #endif
+
+ return split;
+}
+
+int CommitStore::expand(Split *split, unsigned char *buffer, const int size)
+{
+ #ifdef TEST
+ *logofs << "CommitStore: Expanding split data with "
+ << size << " bytes to write.\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef TEST
+
+ if (size < split -> i_size_ + split -> d_size_)
+ {
+ #ifdef PANIC
+ *logofs << "CommitStore: PANIC! Wrong size of the provided "
+ << "buffer. It should be " << split -> i_size_ +
+ split -> d_size_ << " instead of " << size
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Wrong size of the provided "
+ << "buffer. It should be " << split -> i_size_ +
+ split -> d_size_ << " instead of " << size
+ << ".\n";
+
+ HandleAbort();
+ }
+
+ #endif
+
+ #ifdef DEBUG
+ *logofs << "CommitStore: Copying " << split -> i_size_
+ << " bytes of identity.\n" << logofs_flush;
+ #endif
+
+ memcpy(buffer, split -> identity_.begin(), split -> i_size_);
+
+ //
+ // Copy data, if any, to the buffer.
+ //
+
+ if (size > split -> i_size_)
+ {
+ //
+ // Check if message has been stored
+ // in compressed format.
+ //
+
+ if (split -> c_size_ == 0)
+ {
+ #ifdef DEBUG
+ *logofs << "CommitStore: Copying " << split -> d_size_
+ << " bytes of plain data.\n" << logofs_flush;
+ #endif
+
+ memcpy(buffer + split -> i_size_, split -> data_.begin(), split -> d_size_);
+ }
+ else
+ {
+ #ifdef DEBUG
+ *logofs << "CommitStore: Decompressing " << split -> c_size_
+ << " bytes and copying " << split -> d_size_
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+
+ if (compressor_ ->
+ decompressBuffer(buffer + split -> i_size_,
+ split -> d_size_, split -> data_.begin(),
+ split -> c_size_) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "CommitStore: PANIC! Split data decompression failed.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Split data decompression failed.\n";
+
+ return -1;
+ }
+ }
+ }
+
+ return 1;
+}
+
+int CommitStore::update(Split *split)
+{
+ if (split -> action_ != IS_ADDED)
+ {
+ return 0;
+ }
+
+ //
+ // We don't need the identity data at
+ // the encoding side.
+ //
+
+ if (split -> identity_.size() == 0)
+ {
+ #ifdef TEST
+ *logofs << "SplitStore: Going to update the size "
+ << "for object at position " << split -> position_
+ << " with data size " << split -> d_size_
+ << " and compressed data size " << split ->
+ c_size_ << ".\n" << logofs_flush;
+ #endif
+
+ split -> store_ -> updateData(split -> position_, split -> d_size_,
+ split -> c_size_);
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "SplitStore: Going to update data and size "
+ << "for object at position " << split -> position_
+ << " with data size " << split -> d_size_
+ << " and compressed data size " << split ->
+ c_size_ << ".\n" << logofs_flush;
+ #endif
+
+ split -> store_ -> updateData(split -> position_, split -> data_.begin(),
+ split -> d_size_, split -> c_size_);
+ }
+
+ //
+ // Unlock message so that we can remove
+ // or save it on disk at shutdown.
+ //
+
+ if (split -> action_ == IS_ADDED)
+ {
+ split -> store_ -> unlock(split -> position_);
+
+ #ifdef TEST
+
+ validate(split);
+
+ #endif
+ }
+
+ return 1;
+}
+
+int CommitStore::validate(Split *split)
+{
+ MessageStore *store = split -> store_;
+
+ int p, n, s;
+
+ s = store -> cacheSlots;
+
+ for (p = 0, n = 0; p < s; p++)
+ {
+ if (store -> getLocks(p) == 1)
+ {
+ n++;
+ }
+ else if (store -> getLocks(p) != 0)
+ {
+ #ifdef PANIC
+ *logofs << "CommitStore: PANIC! Repository for OPCODE#"
+ << (unsigned int) store -> opcode() << " has "
+ << store -> getLocks(p) << " locks for message "
+ << "at position " << p << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Repository for OPCODE#"
+ << (unsigned int) store -> opcode() << " has "
+ << store -> getLocks(p) << " locks for message "
+ << "at position " << p << ".\n";
+
+ HandleAbort();
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "CommitStore: Repository for OPCODE#"
+ << (unsigned int) store -> opcode()
+ << " has " << n << " locked messages.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
diff --git a/nxcomp/Split.h b/nxcomp/Split.h
new file mode 100644
index 000000000..c9a3c9ad3
--- /dev/null
+++ b/nxcomp/Split.h
@@ -0,0 +1,535 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef Split_H
+#define Split_H
+
+#include "Types.h"
+#include "Timestamp.h"
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Define this to know how many splits
+// are allocated and deallocated.
+//
+
+#undef REFERENCES
+
+//
+// Size of header of messages saved on
+// disk.
+//
+
+#define SPLIT_HEADER_SIZE 12
+
+//
+// This class is used to divide big messages
+// in smaller chunks and send them at idle
+// time.
+//
+
+class EncodeBuffer;
+class DecodeBuffer;
+
+class SplitStore;
+class CommitStore;
+
+//
+// Preferred message streaming policy.
+//
+
+typedef enum
+{
+ split_none = -1,
+ split_async = 1,
+ split_sync
+
+} T_split_mode;
+
+//
+// Current state of the split. Used to
+// implement the state machine.
+//
+
+typedef enum
+{
+ split_undefined = -1,
+ split_added,
+ split_missed,
+ split_loaded,
+ split_aborted,
+ split_notified
+
+} T_split_state;
+
+class Split
+{
+ friend class SplitStore;
+ friend class CommitStore;
+
+ public:
+
+ Split();
+
+ ~Split();
+
+ //
+ // Note that, differently from the message
+ // store, the split store doesn't account
+ // for the data offset when dealing with
+ // the data. This means that both the size_
+ // and c_size members represent the actual
+ // size of the data part.
+ //
+
+ void compressedSize(int size)
+ {
+ c_size_ = size;
+
+ store_ -> validateSize(d_size_, c_size_);
+ }
+
+ int compressedSize()
+ {
+ return c_size_;
+ }
+
+ int plainSize()
+ {
+ return i_size_ + d_size_;
+ }
+
+ T_checksum getChecksum()
+ {
+ return checksum_;
+ }
+
+ MessageStore *getStore()
+ {
+ return store_;
+ }
+
+ T_split_state getState()
+ {
+ return state_;
+ }
+
+ T_store_action getAction()
+ {
+ return action_;
+ }
+
+ //
+ // We may need to find the resource
+ // associated to the split message
+ // because old protocol version use
+ // a single store for all splits.
+ //
+
+ int getResource()
+ {
+ return resource_;
+ }
+
+ int getRequest()
+ {
+ return store_ -> opcode();
+ }
+
+ int getPosition()
+ {
+ return position_;
+ }
+
+ T_split_mode getMode()
+ {
+ return mode_;
+ }
+
+ void setPolicy(int load, int save)
+ {
+ load_ = load;
+ save_ = save;
+ }
+
+ void setState(T_split_state state)
+ {
+ state_ = state;
+ }
+
+ private:
+
+ //
+ // The agent's resource which is splitting
+ // the message.
+ //
+
+ int resource_;
+
+ //
+ // Where to find the message in the message
+ // store or the X sequence number of the
+ // original request, in recent versions.
+ //
+
+ int position_;
+
+ //
+ // Which store is involved.
+ //
+
+ MessageStore *store_;
+
+ //
+ // Identity size of the message.
+ //
+
+ int i_size_;
+
+ //
+ // This is the uncompressed data size of the
+ // original message.
+ //
+
+ int d_size_;
+
+ //
+ // This is the size of the compressed data,
+ // if the data is stored in this form.
+ //
+
+ int c_size_;
+
+ //
+ // Size of the data buffer, as known by the
+ // encoding side. This field is only used at
+ // the decoding side. The remote size can be
+ // different from the actual data size, if
+ // the encoding side did not confirm that it
+ // received the abort split event.
+ //
+
+ int r_size_;
+
+ //
+ // Position in the data buffer that will be
+ // the target of the next send or receive
+ // operation while streaming the message.
+ //
+
+ int next_;
+
+ //
+ // Load or save the split to disk.
+ //
+
+ int load_;
+ int save_;
+
+ //
+ // Checksum of the original message.
+ //
+
+ T_checksum checksum_;
+
+ //
+ // Was this split confirmed or aborted?
+ //
+
+ T_split_state state_;
+
+ //
+ // What's the policy for sending this split?
+ //
+
+ T_split_mode mode_;
+
+ //
+ // Operation that had been performed on the
+ // store at the time the split was added.
+ //
+
+ T_store_action action_;
+
+ //
+ // Container for the identity and data part
+ // of the X message.
+ //
+
+ T_data identity_;
+ T_data data_;
+
+ #ifdef REFERENCES
+
+ static int references_;
+
+ #endif
+};
+
+class SplitStore
+{
+ public:
+
+ SplitStore(StaticCompressor *compressor, CommitStore *commits, int resource);
+
+ ~SplitStore();
+
+ Split *getFirstSplit() const
+ {
+ if (splits_ -> size() > 0)
+ {
+ return (*(splits_ -> begin()));
+ }
+
+ return NULL;
+ }
+
+ Split *getLastSplit() const
+ {
+ if (splits_ -> size() > 0)
+ {
+ return (*(--(splits_ -> end())));
+ }
+
+ return NULL;
+ }
+
+ int getNodeSize(const Split *split) const
+ {
+ //
+ // Take in account 64 bytes of overhead
+ // for each node.
+ //
+
+ return (sizeof(class Split) + 64 +
+ split -> i_size_ + split -> d_size_);
+ }
+
+ int getStorageSize()
+ {
+ return splitStorageSize_;
+ }
+
+ static int getTotalSize()
+ {
+ return totalSplitSize_;
+ }
+
+ static int getTotalStorageSize()
+ {
+ return totalSplitStorageSize_;
+ }
+
+ int getResource()
+ {
+ return resource_;
+ }
+
+ int getSize()
+ {
+ return splits_ -> size();
+ }
+
+ T_splits *getSplits()
+ {
+ return splits_;
+ }
+
+ //
+ // Used, respectively, at the encoding
+ // and decoding side.
+ //
+
+ Split *add(MessageStore *store, int resource, T_split_mode mode,
+ int position, T_store_action action, T_checksum checksum,
+ const unsigned char *buffer, const int size);
+
+ Split *add(MessageStore *store, int resource, int position,
+ T_store_action action, T_checksum checksum,
+ unsigned char *buffer, const int size);
+
+ //
+ // Handle the streaming of the message data.
+ //
+
+ int send(EncodeBuffer &encodeBuffer, int packetSize);
+
+ int receive(DecodeBuffer &decodeBuffer);
+
+ //
+ // Remove the top element of the split store
+ // and update the storage size.
+ //
+
+ void remove(Split *split);
+
+ //
+ // Load the message from disk and replace the
+ // message in the store with the new copy.
+ //
+
+ int load(Split *split);
+
+ //
+ // Save the data to disk after the message has
+ // been recomposed at the local side.
+ //
+
+ int save(Split *split);
+
+ //
+ // Find the message on disk and update the last
+ // modification time. This is currently unused.
+ //
+
+ int find(Split *split);
+
+ //
+ // Remove the element on top of the queue and
+ // discard any split data that still needs to
+ // be transferred.
+ //
+
+ Split *pop();
+
+ //
+ // Dump the content of the store.
+ //
+
+ void dump();
+
+ protected:
+
+ //
+ // Repository where to add the splits.
+ //
+
+ T_splits *splits_;
+
+ //
+ // Compress and decompress the data payload.
+ //
+
+ StaticCompressor *compressor_;
+
+ private:
+
+ int start(EncodeBuffer &encodeBuffer);
+
+ int start(DecodeBuffer &decodeBuffer);
+
+ void push(Split *split);
+
+ //
+ // Determine the name of the file object based
+ // on the checksum.
+ //
+
+ const char *name(const T_checksum checksum);
+
+ //
+ // The number of elements and data bytes
+ // in the repository.
+ //
+
+ int splitStorageSize_;
+
+ static int totalSplitSize_;
+ static int totalSplitStorageSize_;
+
+ //
+ // Current element being transferred.
+ //
+
+ T_splits::iterator current_;
+
+ //
+ // Repository where to move the splits
+ // after they are completely recomposed.
+ //
+
+ CommitStore *commits_;
+
+ //
+ // Index in the client store or none,
+ // if this is a commit store.
+ //
+
+ int resource_;
+
+ #ifdef REFERENCES
+
+ static int references_;
+
+ #endif
+};
+
+class CommitStore : public SplitStore
+{
+ //
+ // This is just a split store.
+ //
+
+ public:
+
+ CommitStore(StaticCompressor *compressor)
+
+ : SplitStore(compressor, NULL, nothing)
+ {
+ }
+
+ //
+ // Move identity and data of the split to the
+ // provided buffer, uncompressing the message,
+ // if needed.
+ //
+
+ int expand(Split *split, unsigned char *buffer, const int size);
+
+ //
+ // We recomposed the data part. If the message
+ // was originally added to the message store,
+ // replace the data and/or update the size.
+ //
+
+ int update(Split *split);
+
+ //
+ // Remove the split from the commit queue.
+ //
+
+ Split *pop();
+
+ //
+ // This is just used for debug. It checks
+ // if any message in the message store has
+ // an invalid number of locks.
+ //
+
+ int validate(Split *split);
+};
+
+#endif /* Split_H */
diff --git a/nxcomp/StaticCompressor.cpp b/nxcomp/StaticCompressor.cpp
new file mode 100644
index 000000000..b30e61a80
--- /dev/null
+++ b/nxcomp/StaticCompressor.cpp
@@ -0,0 +1,420 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "Z.h"
+#include "Misc.h"
+#include "Control.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "StaticCompressor.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+StaticCompressor::StaticCompressor(int compressionLevel,
+ int compressionThreshold)
+{
+ buffer_ = NULL;
+ bufferSize_ = 0;
+
+ compressionStream_.zalloc = (alloc_func) 0;
+ compressionStream_.zfree = (free_func) 0;
+ compressionStream_.opaque = (voidpf) 0;
+
+ decompressionStream_.zalloc = (alloc_func) 0;
+ decompressionStream_.zfree = (free_func) 0;
+ decompressionStream_.opaque = (void *) 0;
+
+ decompressionStream_.next_in = (Bytef *) 0;
+ decompressionStream_.avail_in = 0;
+
+ #ifdef TEST
+ *logofs << "StaticCompressor: Compression level is "
+ << compressionLevel << ".\n" << logofs_flush;
+ #endif
+
+ int result = deflateInit2(&compressionStream_, compressionLevel, Z_DEFLATED,
+ 15, 9, Z_DEFAULT_STRATEGY);
+
+ if (result != Z_OK)
+ {
+ #ifdef PANIC
+ *logofs << "StaticCompressor: PANIC! Cannot initialize the "
+ << "compression stream. Error is '" << zError(result)
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot initialize the compression "
+ << "stream. Error is '" << zError(result) << "'.\n";
+
+ HandleAbort();
+ }
+
+ result = inflateInit2(&decompressionStream_, 15);
+
+ if (result != Z_OK)
+ {
+ #ifdef PANIC
+ *logofs << "StaticCompressor: PANIC! Cannot initialize the "
+ << "decompression stream. Error is '" << zError(result)
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot initialize the decompression "
+ << "stream. Error is '" << zError(result) << "'.\n";
+
+ HandleAbort();
+ }
+
+ #ifdef TEST
+ *logofs << "StaticCompressor: Compression threshold is "
+ << compressionThreshold << ".\n" << logofs_flush;
+ #endif
+
+ threshold_ = compressionThreshold;
+}
+
+StaticCompressor::~StaticCompressor()
+{
+ int result = deflateEnd(&compressionStream_);
+
+ if (result != Z_OK)
+ {
+ #ifdef PANIC
+ *logofs << "StaticCompressor: PANIC! Cannot deinitialize the "
+ << "compression stream. Error is '" << zError(result)
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot deinitialize the compression "
+ << "stream. Error is '" << zError(result) << "'.\n";
+ }
+
+ result = inflateEnd(&decompressionStream_);
+
+ if (result != Z_OK)
+ {
+ #ifdef PANIC
+ *logofs << "StaticCompressor: PANIC! Cannot deinitialize the "
+ << "decompression stream. Error is '" << zError(result)
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot deinitialize the decompression "
+ << "stream. Error is '" << zError(result) << "'.\n";
+ }
+
+ delete [] buffer_;
+}
+
+//
+// This function compresses and encodes the compressed
+// buffer. It returns a pointer to the internal buffer
+// where data was compressed.
+//
+
+int StaticCompressor::compressBuffer(const unsigned char *plainBuffer,
+ const unsigned int plainSize,
+ unsigned char *&compressedBuffer,
+ unsigned int &compressedSize,
+ EncodeBuffer &encodeBuffer)
+{
+ if (control -> LocalDataCompression == 0 ||
+ compressBuffer(plainBuffer, plainSize,
+ compressedBuffer, compressedSize) <= 0)
+ {
+ encodeBuffer.encodeBoolValue(0);
+
+ encodeBuffer.encodeMemory(plainBuffer, plainSize);
+
+ return 0;
+ }
+ else
+ {
+ encodeBuffer.encodeBoolValue(1);
+
+ encodeBuffer.encodeValue(compressedSize, 32, 14);
+ encodeBuffer.encodeValue(plainSize, 32, 14);
+
+ encodeBuffer.encodeMemory(compressedBuffer, compressedSize);
+
+ return 1;
+ }
+}
+
+//
+// This function compresses data into a dynamically
+// allocated buffer and returns a pointer to it, so
+// application must copy data before the next call.
+//
+
+int StaticCompressor::compressBuffer(const unsigned char *plainBuffer,
+ const unsigned int plainSize,
+ unsigned char *&compressedBuffer,
+ unsigned int &compressedSize)
+{
+ #ifdef DEBUG
+ *logofs << "StaticCompressor: Called for buffer at "
+ << (void *) plainBuffer << ".\n"
+ << logofs_flush;
+ #endif
+
+ compressedSize = plainSize;
+
+ if (plainSize < (unsigned int) threshold_)
+ {
+ #ifdef TEST
+ *logofs << "StaticCompressor: Leaving buffer unchanged. "
+ << "Plain size is " << plainSize << " with threshold "
+ << (unsigned int) threshold_ << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ //
+ // Determine the size of the temporary
+ // buffer.
+ //
+
+ unsigned int newSize = plainSize + (plainSize / 1000) + 12;
+
+ //
+ // Allocate a new buffer if it grows
+ // beyond 64K.
+ //
+
+ if (buffer_ == NULL || (bufferSize_ > 65536 &&
+ newSize < bufferSize_ / 2) || newSize > bufferSize_)
+ {
+ delete [] buffer_;
+
+ buffer_ = new unsigned char[newSize];
+
+ if (buffer_ == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "StaticCompressor: PANIC! Can't allocate compression "
+ << "buffer of " << newSize << " bytes. Error is " << EGET()
+ << " ' " << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Can't allocate compression buffer of "
+ << newSize << " bytes. Error is " << EGET()
+ << " '" << ESTR() << "'.\n";
+
+ bufferSize_ = 0;
+
+ return 0;
+ }
+
+ bufferSize_ = newSize;
+ }
+
+ unsigned int resultingSize = newSize;
+
+ int result = ZCompress(&compressionStream_, buffer_, &resultingSize,
+ plainBuffer, plainSize);
+
+ if (result == Z_OK)
+ {
+ if (resultingSize > newSize)
+ {
+ #ifdef PANIC
+ *logofs << "StaticCompressor: PANIC! Overflow in compression "
+ << "buffer size. " << "Expected size was " << newSize
+ << " while it is " << resultingSize << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Overflow in compress buffer size. "
+ << "Expected size was " << newSize << " while it is "
+ << resultingSize << ".\n";
+
+ return -1;
+ }
+ else if (resultingSize >= plainSize)
+ {
+ #ifdef TEST
+ *logofs << "StaticCompressor: Leaving buffer unchanged. "
+ << "Plain size is " << plainSize << " compressed "
+ << "size is " << resultingSize << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ compressedBuffer = buffer_;
+ compressedSize = resultingSize;
+
+ #ifdef TEST
+ *logofs << "StaticCompressor: Compressed buffer from "
+ << plainSize << " to " << resultingSize
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ return 1;
+ }
+
+ #ifdef PANIC
+ *logofs << "StaticCompressor: PANIC! Failed compression of buffer. "
+ << "Error is '" << zError(result) << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed compression of buffer. "
+ << "Error is '" << zError(result) << "'.\n";
+
+ return -1;
+}
+
+int StaticCompressor::decompressBuffer(unsigned char *plainBuffer,
+ unsigned int plainSize,
+ const unsigned char *&compressedBuffer,
+ unsigned int &compressedSize,
+ DecodeBuffer &decodeBuffer)
+{
+ #ifdef DEBUG
+ *logofs << "StaticCompressor: Called for buffer at "
+ << (void *) plainBuffer << ".\n"
+ << logofs_flush;
+ #endif
+
+ unsigned int value;
+
+ decodeBuffer.decodeBoolValue(value);
+
+ if (value == 0)
+ {
+ memcpy(plainBuffer,
+ decodeBuffer.decodeMemory(plainSize),
+ plainSize);
+
+ return 0;
+ }
+
+ unsigned int checkSize = plainSize;
+
+ decodeBuffer.decodeValue(value, 32, 14);
+ compressedSize = value;
+
+ decodeBuffer.decodeValue(value, 32, 14);
+ checkSize = value;
+
+ //
+ // If caller needs the original compressed
+ // data it must copy this to its own buffer
+ // before using any further decode function.
+ //
+
+ compressedBuffer = decodeBuffer.decodeMemory(compressedSize);
+
+ int result = ZDecompress(&decompressionStream_, plainBuffer, &checkSize,
+ compressedBuffer, compressedSize);
+
+ if (result != Z_OK)
+ {
+ #ifdef PANIC
+ *logofs << "StaticCompressor: PANIC! Failure decompressing buffer. "
+ << "Error is '" << zError(result) << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decompressing buffer. "
+ << "Error is '" << zError(result) << "'.\n";
+
+ return -1;
+ }
+ else if (plainSize != checkSize)
+ {
+ #ifdef PANIC
+ *logofs << "StaticCompressor: PANIC! Expected decompressed size was "
+ << plainSize << " while it is " << checkSize
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Expected decompressed size was "
+ << plainSize << " while it is " << checkSize
+ << ".\n";
+
+ return -1;
+ }
+
+ return 1;
+}
+
+//
+// This is used to uncompress on-the-fly
+// messages whose data has been stored
+// in compressed format.
+//
+
+int StaticCompressor::decompressBuffer(unsigned char *plainBuffer,
+ const unsigned int plainSize,
+ const unsigned char *compressedBuffer,
+ const unsigned int compressedSize)
+{
+ #ifdef TEST
+ *logofs << "StaticCompressor: Called for buffer at "
+ << (void *) plainBuffer << ".\n"
+ << logofs_flush;
+ #endif
+
+ unsigned int checkSize = plainSize;
+
+ int result = ZDecompress(&decompressionStream_, plainBuffer, &checkSize,
+ compressedBuffer, compressedSize);
+
+ if (result != Z_OK)
+ {
+ #ifdef PANIC
+ *logofs << "StaticCompressor: PANIC! Failure decompressing buffer. "
+ << "Error is '" << zError(result) << "'.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ if (plainSize != checkSize)
+ {
+ #ifdef PANIC
+ *logofs << "StaticCompressor: PANIC! Expected decompressed size was "
+ << plainSize << " while it is " << checkSize
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Expected decompressed size was "
+ << plainSize << " while it is " << checkSize
+ << ".\n";
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "StaticCompressor: Decompressed buffer from "
+ << compressedSize << " to " << plainSize
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
diff --git a/nxcomp/StaticCompressor.h b/nxcomp/StaticCompressor.h
new file mode 100644
index 000000000..3e5b25ca1
--- /dev/null
+++ b/nxcomp/StaticCompressor.h
@@ -0,0 +1,72 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef StaticCompressor_H
+#define StaticCompressor_H
+
+#include "Z.h"
+
+class EncodeBuffer;
+class DecodeBuffer;
+
+class StaticCompressor
+{
+ public:
+
+ StaticCompressor(int compressionLevel, int compressionThreshold);
+
+ ~StaticCompressor();
+
+ int compressBuffer(const unsigned char *plainBuffer, const unsigned int plainSize,
+ unsigned char *&compressedBuffer, unsigned int &compressedSize,
+ EncodeBuffer &encodeBuffer);
+
+ int compressBuffer(const unsigned char *plainBuffer, const unsigned int plainSize,
+ unsigned char *&compressedBuffer, unsigned int &compressedSize);
+
+ int decompressBuffer(unsigned char *plainBuffer, unsigned int plainSize,
+ const unsigned char *&compressedBuffer, unsigned int &compressedSize,
+ DecodeBuffer &decodeBuffer);
+
+ int decompressBuffer(unsigned char *plainBuffer, const unsigned int plainSize,
+ const unsigned char *compressedBuffer, const unsigned compressedSize);
+
+ static int isCompressionLevel(int compressionLevel)
+ {
+ return (compressionLevel == Z_DEFAULT_COMPRESSION ||
+ (compressionLevel >= Z_NO_COMPRESSION &&
+ compressionLevel <= Z_BEST_COMPRESSION));
+ }
+
+ int fullReset()
+ {
+ return (deflateReset(&compressionStream_) == Z_OK &&
+ inflateReset(&decompressionStream_) == Z_OK);
+ }
+
+ private:
+
+ z_stream compressionStream_;
+ z_stream decompressionStream_;
+
+ unsigned char *buffer_;
+ unsigned int bufferSize_;
+
+ int threshold_;
+};
+
+#endif
diff --git a/nxcomp/Statistics.cpp b/nxcomp/Statistics.cpp
new file mode 100644
index 000000000..294518fb7
--- /dev/null
+++ b/nxcomp/Statistics.cpp
@@ -0,0 +1,1995 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include <stdio.h>
+
+#include "Statistics.h"
+
+#include "Control.h"
+
+#include "Proxy.h"
+
+#include "ClientStore.h"
+#include "ServerStore.h"
+
+//
+// Length of temporary buffer
+// used to format output.
+//
+
+#define FORMAT_LENGTH 1024
+
+//
+// Log level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Note that when presenting statistics we invert the
+// correct semantics of X client and server entities.
+// This is questionable, but matches the user's pers-
+// pective of running remote X applications on its
+// local client.
+//
+
+Statistics::Statistics(Proxy *proxy) : proxy_(proxy)
+{
+ transportPartial_.idleTime_ = 0;
+ transportPartial_.readTime_ = 0;
+ transportPartial_.writeTime_ = 0;
+ transportPartial_.proxyBytesIn_ = 0;
+ transportPartial_.proxyBytesOut_ = 0;
+ transportPartial_.proxyFramesIn_ = 0;
+ transportPartial_.proxyFramesOut_ = 0;
+ transportPartial_.proxyWritesOut_ = 0;
+ transportPartial_.compressedBytesIn_ = 0;
+ transportPartial_.compressedBytesOut_ = 0;
+ transportPartial_.decompressedBytesIn_ = 0;
+ transportPartial_.decompressedBytesOut_ = 0;
+ transportPartial_.framingBitsOut_ = 0;
+
+ transportTotal_.idleTime_ = 0;
+ transportTotal_.readTime_ = 0;
+ transportTotal_.writeTime_ = 0;
+ transportTotal_.proxyBytesIn_ = 0;
+ transportTotal_.proxyBytesOut_ = 0;
+ transportTotal_.proxyFramesIn_ = 0;
+ transportTotal_.proxyFramesOut_ = 0;
+ transportTotal_.proxyWritesOut_ = 0;
+ transportTotal_.compressedBytesIn_ = 0;
+ transportTotal_.compressedBytesOut_ = 0;
+ transportTotal_.decompressedBytesIn_ = 0;
+ transportTotal_.decompressedBytesOut_ = 0;
+ transportTotal_.framingBitsOut_ = 0;
+
+ for (int i = 0; i < STATISTICS_OPCODE_MAX; i++)
+ {
+ protocolPartial_.requestCached_[i] = 0;
+ protocolPartial_.requestReplied_[i] = 0;
+ protocolPartial_.requestCount_[i] = 0;
+ protocolPartial_.requestBitsIn_[i] = 0;
+ protocolPartial_.requestBitsOut_[i] = 0;
+
+ protocolPartial_.renderRequestCached_[i] = 0;
+ protocolPartial_.renderRequestCount_[i] = 0;
+ protocolPartial_.renderRequestBitsIn_[i] = 0;
+ protocolPartial_.renderRequestBitsOut_[i] = 0;
+
+ protocolPartial_.replyCached_[i] = 0;
+ protocolPartial_.replyCount_[i] = 0;
+ protocolPartial_.replyBitsIn_[i] = 0;
+ protocolPartial_.replyBitsOut_[i] = 0;
+
+ protocolPartial_.eventCached_[i] = 0;
+ protocolPartial_.eventCount_[i] = 0;
+ protocolPartial_.eventBitsIn_[i] = 0;
+ protocolPartial_.eventBitsOut_[i] = 0;
+
+ protocolTotal_.requestCached_[i] = 0;
+ protocolTotal_.requestReplied_[i] = 0;
+ protocolTotal_.requestCount_[i] = 0;
+ protocolTotal_.requestBitsIn_[i] = 0;
+ protocolTotal_.requestBitsOut_[i] = 0;
+
+ protocolTotal_.renderRequestCached_[i] = 0;
+ protocolTotal_.renderRequestCount_[i] = 0;
+ protocolTotal_.renderRequestBitsIn_[i] = 0;
+ protocolTotal_.renderRequestBitsOut_[i] = 0;
+
+ protocolTotal_.replyCached_[i] = 0;
+ protocolTotal_.replyCount_[i] = 0;
+ protocolTotal_.replyBitsIn_[i] = 0;
+ protocolTotal_.replyBitsOut_[i] = 0;
+
+ protocolTotal_.eventCached_[i] = 0;
+ protocolTotal_.eventCount_[i] = 0;
+ protocolTotal_.eventBitsIn_[i] = 0;
+ protocolTotal_.eventBitsOut_[i] = 0;
+ }
+
+ protocolPartial_.cupsCount_ = 0;
+ protocolPartial_.cupsBitsIn_ = 0;
+ protocolPartial_.cupsBitsOut_ = 0;
+
+ protocolPartial_.smbCount_ = 0;
+ protocolPartial_.smbBitsIn_ = 0;
+ protocolPartial_.smbBitsOut_ = 0;
+
+ protocolPartial_.mediaCount_ = 0;
+ protocolPartial_.mediaBitsIn_ = 0;
+ protocolPartial_.mediaBitsOut_ = 0;
+
+ protocolPartial_.httpCount_ = 0;
+ protocolPartial_.httpBitsIn_ = 0;
+ protocolPartial_.httpBitsOut_ = 0;
+
+ protocolPartial_.fontCount_ = 0;
+ protocolPartial_.fontBitsIn_ = 0;
+ protocolPartial_.fontBitsOut_ = 0;
+
+ protocolPartial_.slaveCount_ = 0;
+ protocolPartial_.slaveBitsIn_ = 0;
+ protocolPartial_.slaveBitsOut_ = 0;
+
+ protocolTotal_.cupsCount_ = 0;
+ protocolTotal_.cupsBitsIn_ = 0;
+ protocolTotal_.cupsBitsOut_ = 0;
+
+ protocolTotal_.smbCount_ = 0;
+ protocolTotal_.smbBitsIn_ = 0;
+ protocolTotal_.smbBitsOut_ = 0;
+
+ protocolTotal_.mediaCount_ = 0;
+ protocolTotal_.mediaBitsIn_ = 0;
+ protocolTotal_.mediaBitsOut_ = 0;
+
+ protocolTotal_.httpCount_ = 0;
+ protocolTotal_.httpBitsIn_ = 0;
+ protocolTotal_.httpBitsOut_ = 0;
+
+ protocolTotal_.fontCount_ = 0;
+ protocolTotal_.fontBitsIn_ = 0;
+ protocolTotal_.fontBitsOut_ = 0;
+
+ protocolTotal_.slaveCount_ = 0;
+ protocolTotal_.slaveBitsIn_ = 0;
+ protocolTotal_.slaveBitsOut_ = 0;
+
+ packedPartial_.packedBytesIn_ = 0;
+ packedPartial_.packedBytesOut_ = 0;
+
+ packedTotal_.packedBytesIn_ = 0;
+ packedTotal_.packedBytesOut_ = 0;
+
+ splitPartial_.splitCount_ = 0;
+ splitPartial_.splitAborted_ = 0;
+ splitPartial_.splitAbortedBytesOut_ = 0;
+
+ splitTotal_.splitCount_ = 0;
+ splitTotal_.splitAborted_ = 0;
+ splitTotal_.splitAbortedBytesOut_ = 0;
+
+ overallPartial_.overallBytesIn_ = 0;
+ overallPartial_.overallBytesOut_ = 0;
+
+ overallTotal_.overallBytesIn_ = 0;
+ overallTotal_.overallBytesOut_ = 0;
+
+ proxyData_.protocolCount_ = 0;
+ proxyData_.controlCount_ = 0;
+ proxyData_.splitCount_ = 0;
+ proxyData_.dataCount_ = 0;
+
+ proxyData_.streamRatio_ = 1;
+
+ startShortFrameTs_ = getTimestamp();
+ startLongFrameTs_ = getTimestamp();
+ startFrameTs_ = getTimestamp();
+
+ bytesInShortFrame_ = 0;
+ bytesInLongFrame_ = 0;
+
+ bitrateInShortFrame_ = 0;
+ bitrateInLongFrame_ = 0;
+
+ topBitrate_ = 0;
+
+ congestionInFrame_ = 0;
+}
+
+Statistics::~Statistics()
+{
+}
+
+int Statistics::resetPartialStats()
+{
+ transportPartial_.idleTime_ = 0;
+ transportPartial_.readTime_ = 0;
+ transportPartial_.writeTime_ = 0;
+ transportPartial_.proxyBytesIn_ = 0;
+ transportPartial_.proxyBytesOut_ = 0;
+ transportPartial_.proxyFramesIn_ = 0;
+ transportPartial_.proxyFramesOut_ = 0;
+ transportPartial_.proxyWritesOut_ = 0;
+ transportPartial_.compressedBytesIn_ = 0;
+ transportPartial_.compressedBytesOut_ = 0;
+ transportPartial_.decompressedBytesIn_ = 0;
+ transportPartial_.decompressedBytesOut_ = 0;
+ transportPartial_.framingBitsOut_ = 0;
+
+ for (int i = 0; i < STATISTICS_OPCODE_MAX; i++)
+ {
+ protocolPartial_.requestCached_[i] = 0;
+ protocolPartial_.requestReplied_[i] = 0;
+ protocolPartial_.requestCount_[i] = 0;
+ protocolPartial_.requestBitsIn_[i] = 0;
+ protocolPartial_.requestBitsOut_[i] = 0;
+
+ protocolPartial_.renderRequestCached_[i] = 0;
+ protocolPartial_.renderRequestCount_[i] = 0;
+ protocolPartial_.renderRequestBitsIn_[i] = 0;
+ protocolPartial_.renderRequestBitsOut_[i] = 0;
+
+ protocolPartial_.replyCached_[i] = 0;
+ protocolPartial_.replyCount_[i] = 0;
+ protocolPartial_.replyBitsIn_[i] = 0;
+ protocolPartial_.replyBitsOut_[i] = 0;
+
+ protocolPartial_.eventCached_[i] = 0;
+ protocolPartial_.eventCount_[i] = 0;
+ protocolPartial_.eventBitsIn_[i] = 0;
+ protocolPartial_.eventBitsOut_[i] = 0;
+ }
+
+ protocolPartial_.cupsCount_ = 0;
+ protocolPartial_.cupsBitsIn_ = 0;
+ protocolPartial_.cupsBitsOut_ = 0;
+
+ protocolPartial_.smbCount_ = 0;
+ protocolPartial_.smbBitsIn_ = 0;
+ protocolPartial_.smbBitsOut_ = 0;
+
+ protocolPartial_.mediaCount_ = 0;
+ protocolPartial_.mediaBitsIn_ = 0;
+ protocolPartial_.mediaBitsOut_ = 0;
+
+ protocolPartial_.httpCount_ = 0;
+ protocolPartial_.httpBitsIn_ = 0;
+ protocolPartial_.httpBitsOut_ = 0;
+
+ protocolPartial_.fontCount_ = 0;
+ protocolPartial_.fontBitsIn_ = 0;
+ protocolPartial_.fontBitsOut_ = 0;
+
+ protocolPartial_.slaveCount_ = 0;
+ protocolPartial_.slaveBitsIn_ = 0;
+ protocolPartial_.slaveBitsOut_ = 0;
+
+ packedPartial_.packedBytesIn_ = 0;
+ packedPartial_.packedBytesOut_ = 0;
+
+ splitPartial_.splitCount_ = 0;
+ splitPartial_.splitAborted_ = 0;
+ splitPartial_.splitAbortedBytesOut_ = 0;
+
+ overallPartial_.overallBytesIn_ = 0;
+ overallPartial_.overallBytesOut_ = 0;
+
+ return 1;
+}
+
+void Statistics::addCompressedBytes(unsigned int bytesIn, unsigned int bytesOut)
+{
+ transportPartial_.compressedBytesIn_ += bytesIn;
+ transportTotal_.compressedBytesIn_ += bytesIn;
+
+ transportPartial_.compressedBytesOut_ += bytesOut;
+ transportTotal_.compressedBytesOut_ += bytesOut;
+
+ double ratio = bytesIn / bytesOut;
+
+ if (ratio < 1)
+ {
+ ratio = 1;
+ }
+
+ #if defined(TEST) || defined(TOKEN)
+ *logofs << "Statistics: TOKEN! Old ratio was "
+ << proxyData_.streamRatio_ << " current is "
+ << (double) ratio << " new ratio is " << (double)
+ ((proxyData_.streamRatio_ * 2) + ratio) / 3 << ".\n"
+ << logofs_flush;
+ #endif
+
+ proxyData_.streamRatio_ = ((proxyData_.streamRatio_ * 2) + ratio) / 3;
+
+ #if defined(TEST) || defined(TOKEN)
+ *logofs << "Statistics: TOKEN! Updated compressed bytes "
+ << "with " << bytesIn << " in " << bytesOut << " out "
+ << "and ratio " << (double) proxyData_.streamRatio_
+ << ".\n" << logofs_flush;
+ #endif
+}
+
+//
+// Recalculate the current bitrate. The bytes written
+// are accounted at the time the transport actually
+// writes the data to the network, not at the time it
+// receives the data from the upper layers. The reason
+// is that data can be compressed by the stream com-
+// pressor, so we can become aware of the new bitrate
+// only afer having flushed the ZLIB stream. This also
+// means that, to have a reliable estimate, we need to
+// flush the link often.
+//
+
+void Statistics::updateBitrate(int bytes)
+{
+ T_timestamp thisFrameTs = getNewTimestamp();
+
+ int diffFramesInMs = diffTimestamp(startFrameTs_, thisFrameTs);
+
+ #ifdef DEBUG
+ *logofs << "Statistics: Difference since previous timestamp is "
+ << diffFramesInMs << " Ms.\n" << logofs_flush;
+ #endif
+
+ if (diffFramesInMs > 0)
+ {
+ #ifdef DEBUG
+ *logofs << "Statistics: Removing " << diffFramesInMs
+ << " Ms in short and long time frame.\n"
+ << logofs_flush;
+ #endif
+
+ int shortBytesToRemove = (int) (((double) bytesInShortFrame_ * (double) diffFramesInMs) /
+ (double) control -> ShortBitrateTimeFrame);
+
+ int longBytesToRemove = (int) (((double) bytesInLongFrame_ * (double) diffFramesInMs) /
+ (double) control -> LongBitrateTimeFrame);
+
+ #ifdef DEBUG
+ *logofs << "Statistics: Removing " << shortBytesToRemove
+ << " bytes from " << bytesInShortFrame_
+ << " in the short frame.\n" << logofs_flush;
+ #endif
+
+ bytesInShortFrame_ -= shortBytesToRemove;
+
+ if (bytesInShortFrame_ < 0)
+ {
+ #ifdef TEST
+ *logofs << "Statistics: Bytes in short frame are "
+ << bytesInShortFrame_ << ". Set to 0.\n"
+ << logofs_flush;
+ #endif
+
+ bytesInShortFrame_ = 0;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Statistics: Removing " << longBytesToRemove
+ << " bytes from " << bytesInLongFrame_
+ << " in the long frame.\n" << logofs_flush;
+ #endif
+
+ bytesInLongFrame_ -= longBytesToRemove;
+
+ if (bytesInLongFrame_ < 0)
+ {
+ #ifdef TEST
+ *logofs << "Statistics: Bytes in long frame are "
+ << bytesInLongFrame_ << ". Set to 0.\n"
+ << logofs_flush;
+ #endif
+
+ bytesInLongFrame_ = 0;
+ }
+
+ int diffStartInMs;
+
+ diffStartInMs = diffTimestamp(thisFrameTs, startShortFrameTs_);
+
+ if (diffStartInMs > control -> ShortBitrateTimeFrame)
+ {
+ addMsTimestamp(startShortFrameTs_, diffStartInMs);
+ }
+
+ diffStartInMs = diffTimestamp(thisFrameTs, startLongFrameTs_);
+
+ if (diffStartInMs > control -> LongBitrateTimeFrame)
+ {
+ addMsTimestamp(startLongFrameTs_, diffStartInMs);
+ }
+
+ startFrameTs_ = thisFrameTs;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Statistics: Adding " << bytes << " bytes to "
+ << bytesInShortFrame_ << " in the short frame.\n"
+ << logofs_flush;
+ #endif
+
+ bytesInShortFrame_ = bytesInShortFrame_ + bytes;
+
+ #ifdef DEBUG
+ *logofs << "Statistics: Adding " << bytes << " bytes to "
+ << bytesInLongFrame_ << " in the long frame.\n"
+ << logofs_flush;
+ #endif
+
+ bytesInLongFrame_ = bytesInLongFrame_ + bytes;
+
+ bitrateInShortFrame_ = (int) ((double) bytesInShortFrame_ /
+ ((double) control -> ShortBitrateTimeFrame / 1000));
+
+ bitrateInLongFrame_ = (int) ((double) bytesInLongFrame_ /
+ ((double) control -> LongBitrateTimeFrame / 1000));
+
+ if (bitrateInShortFrame_ > topBitrate_)
+ {
+ topBitrate_ = bitrateInShortFrame_;
+ }
+
+ #ifdef TEST
+ *logofs << "Statistics: Current bitrate is short " << bitrateInShortFrame_
+ << " long " << bitrateInLongFrame_ << " top " << topBitrate_
+ << ".\n" << logofs_flush;
+ #endif
+}
+
+void Statistics::updateCongestion(int remaining, int limit)
+{
+ #ifdef TEST
+ *logofs << "Statistics: Updating the congestion "
+ << "counters at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ #endif
+
+ double current = remaining;
+
+ if (current < 0)
+ {
+ current = 0;
+ }
+
+ current = 9 * (limit - current) / limit;
+
+ #ifdef TEST
+ *logofs << "Statistics: Current congestion is "
+ << current << " with " << limit << " tokens "
+ << "and " << remaining << " remaining.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // If the current congestion counter is greater
+ // than the previous, take the current value,
+ // otherwise ramp down the value by calculating
+ // the average of the last 8 updates.
+ //
+
+ #ifdef TEST
+ *logofs << "Statistics: Old congestion was "
+ << congestionInFrame_;
+ #endif
+
+ if (current >= congestionInFrame_)
+ {
+ congestionInFrame_ = current;
+ }
+ else
+ {
+ congestionInFrame_ = ((congestionInFrame_ * 7) + current) / 8;
+ }
+
+ #ifdef TEST
+ *logofs << " new congestion is "
+ << ((congestionInFrame_ * 7) + current) / 8
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Call the function with 0 bytes flushed
+ // so the agent can update its congestion
+ // counter.
+ //
+
+ FlushCallback(0);
+}
+
+int Statistics::getClientCacheStats(int type, char *&buffer)
+{
+ if (type != PARTIAL_STATS && type != TOTAL_STATS)
+ {
+ #ifdef PANIC
+ *logofs << "Statistics: PANIC! Cannot produce statistics "
+ << "with qualifier '" << type << "'.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ //
+ // Print message cache data according
+ // to local and remote view.
+ //
+
+ MessageStore *currentStore = NULL;
+ MessageStore *anyStore = NULL;
+
+ char format[FORMAT_LENGTH];
+
+ strcat(buffer, "\nNX Cache Statistics\n");
+ strcat(buffer, "-------------------\n\n");
+
+ for (int m = proxy_client; m <= proxy_server; m++)
+ {
+ if (m == proxy_client)
+ {
+ strcat(buffer, "Request\tCached\tSize at Server\t\tSize at Client\t\tCache limit\n");
+ strcat(buffer, "-------\t------\t--------------\t\t--------------\t\t-----------\n");
+ }
+ else
+ {
+ strcat(buffer, "\nReply\tCached\tSize at Server\t\tSize at Client\t\tCache limit\n");
+ strcat(buffer, "-----\t------\t--------------\t\t--------------\t\t-----------\n");
+ }
+
+ for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++)
+ {
+ if (m == proxy_client)
+ {
+ currentStore = proxy_ -> getClientStore() -> getRequestStore(i);
+ }
+ else
+ {
+ currentStore = proxy_ -> getServerStore() -> getReplyStore(i);
+ }
+
+ if (currentStore != NULL &&
+ (currentStore -> getLocalStorageSize() ||
+ currentStore -> getRemoteStorageSize()))
+ {
+ anyStore = currentStore;
+
+ sprintf(format, "#%d\t%d\t", i, currentStore -> getSize());
+
+ strcat(buffer, format);
+
+ sprintf(format, "%d (%.0f KB)\t\t", currentStore -> getLocalStorageSize(),
+ ((double) currentStore -> getLocalStorageSize()) / 1024);
+
+ strcat(buffer, format);
+
+ sprintf(format, "%d (%.0f KB)\t\t", currentStore -> getRemoteStorageSize(),
+ ((double) currentStore -> getRemoteStorageSize()) / 1024);
+
+ strcat(buffer, format);
+
+ sprintf(format, "%d/%.0f KB\n", currentStore -> cacheSlots,
+ ((double) control -> getUpperStorageSize() / 100 *
+ currentStore -> cacheThreshold) / 1024);
+
+ strcat(buffer, format);
+ }
+ }
+
+ if (anyStore == NULL)
+ {
+ strcat(buffer, "N/A\n");
+ }
+ }
+
+ if (anyStore != NULL)
+ {
+ sprintf(format, "\ncache: %d bytes (%d KB) available at server.\n",
+ control -> ClientTotalStorageSize,
+ control -> ClientTotalStorageSize / 1024);
+
+ strcat(buffer, format);
+
+ sprintf(format, " %d bytes (%d KB) available at client.\n\n",
+ control -> ServerTotalStorageSize,
+ control -> ServerTotalStorageSize / 1024);
+
+ strcat(buffer, format);
+
+ sprintf(format, " %d bytes (%d KB) allocated at server.\n",
+ anyStore -> getLocalTotalStorageSize(),
+ anyStore -> getLocalTotalStorageSize() / 1024);
+
+ strcat(buffer, format);
+
+ sprintf(format, " %d bytes (%d KB) allocated at client.\n\n\n",
+ anyStore -> getRemoteTotalStorageSize(),
+ anyStore -> getRemoteTotalStorageSize() / 1024);
+
+ strcat(buffer, format);
+ }
+ else
+ {
+ strcat(buffer, "\ncache: N/A\n\n");
+ }
+
+ return 1;
+}
+
+int Statistics::getClientProtocolStats(int type, char *&buffer)
+{
+ if (type != PARTIAL_STATS && type != TOTAL_STATS)
+ {
+ #ifdef PANIC
+ *logofs << "Statistics: PANIC! Cannot produce statistics "
+ << "with qualifier '" << type << "'.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ struct T_transportData *transportData;
+ struct T_protocolData *protocolData;
+ struct T_overallData *overallData;
+
+ if (type == PARTIAL_STATS)
+ {
+ transportData = &transportPartial_;
+ protocolData = &protocolPartial_;
+ overallData = &overallPartial_;
+ }
+ else
+ {
+ transportData = &transportTotal_;
+ protocolData = &protocolTotal_;
+ overallData = &overallTotal_;
+ }
+
+ char format[FORMAT_LENGTH];
+
+ double countRequestIn = 0;
+ double countCachedRequestIn = 0;
+ double countRepliedRequestIn = 0;
+
+ double countRequestBitsIn = 0;
+ double countRequestBitsOut = 0;
+
+ double countAnyIn = 0;
+ double countBitsIn = 0;
+ double countBitsOut = 0;
+
+ //
+ // Print request data.
+ //
+
+ strcat(buffer, "NX Server Side Protocol Statistics\n");
+ strcat(buffer, "----------------------------------\n\n");
+
+ //
+ // Print render data.
+ //
+
+ strcat(buffer, "Render Total\tCached\tBits In\t\tBits Out\tBits/Request\t\tRatio\n");
+ strcat(buffer, "------- -----\t------\t-------\t\t--------\t------------\t\t-----\n");
+
+ for (int i = 0; i < STATISTICS_OPCODE_MAX; i++)
+ {
+ if (protocolData -> renderRequestCount_[i])
+ {
+ sprintf(format, "#%d ", i);
+
+ while (strlen(format) < 8)
+ {
+ strcat(format, " ");
+ }
+
+ strcat(buffer, format);
+
+ if (protocolData -> renderRequestCached_[i] > 0)
+ {
+ sprintf(format, "%.0f\t%.0f", protocolData -> renderRequestCount_[i],
+ protocolData -> renderRequestCached_[i]);
+ }
+ else
+ {
+ sprintf(format, "%.0f\t", protocolData -> renderRequestCount_[i]);
+ }
+
+ strcat(buffer, format);
+
+ sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1 \t",
+ protocolData -> renderRequestBitsIn_[i], protocolData -> renderRequestBitsIn_[i] / 8192,
+ protocolData -> renderRequestBitsOut_[i], protocolData -> renderRequestBitsOut_[i] / 8192,
+ protocolData -> renderRequestBitsIn_[i] / protocolData -> renderRequestCount_[i],
+ protocolData -> renderRequestBitsOut_[i] / protocolData -> renderRequestCount_[i]);
+
+ strcat(buffer, format);
+
+ if (protocolData -> renderRequestBitsOut_[i] > 0)
+ {
+ sprintf(format, "%5.3f:1\n", protocolData -> renderRequestBitsIn_[i] /
+ protocolData -> renderRequestBitsOut_[i]);
+
+ strcat(buffer, format);
+ }
+ else
+ {
+ strcat(buffer, "1:1\n");
+ }
+ }
+
+ countRequestIn += protocolData -> renderRequestCount_[i];
+ countCachedRequestIn += protocolData -> renderRequestCached_[i];
+
+ countRequestBitsIn += protocolData -> renderRequestBitsIn_[i];
+ countRequestBitsOut += protocolData -> renderRequestBitsOut_[i];
+
+ countAnyIn += protocolData -> renderRequestCount_[i];
+ countBitsIn += protocolData -> renderRequestBitsIn_[i];
+ countBitsOut += protocolData -> renderRequestBitsOut_[i];
+ }
+
+ if (countRequestIn > 0)
+ {
+ if (countCachedRequestIn > 0)
+ {
+ sprintf(format, "\ntotal: %.0f\t%.0f", countRequestIn, countCachedRequestIn);
+ }
+ else
+ {
+ sprintf(format, "\ntotal: %.0f\t", countRequestIn);
+ }
+
+ strcat(buffer, format);
+
+ sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1 \t",
+ countRequestBitsIn, countRequestBitsIn / 8192, countRequestBitsOut,
+ countRequestBitsOut / 8192, countRequestBitsIn / countRequestIn,
+ countRequestBitsOut / countRequestIn);
+
+ strcat(buffer, format);
+
+ if (countRequestBitsOut > 0)
+ {
+ sprintf(format, "%5.3f:1\n", countRequestBitsIn / countRequestBitsOut);
+ }
+ else
+ {
+ sprintf(format, "1:1\n");
+ }
+
+ strcat(buffer, format);
+ }
+ else
+ {
+ strcat(buffer, "N/A\n\n");
+ }
+
+ countRequestIn = 0;
+ countCachedRequestIn = 0;
+
+ countRequestBitsIn = 0;
+ countRequestBitsOut = 0;
+
+ countAnyIn = 0;
+ countBitsIn = 0;
+ countBitsOut = 0;
+
+ //
+ // Print other requests' data.
+ //
+
+ strcat(buffer, "\nRequest Total\tCached\tBits In\t\tBits Out\tBits/Request\t\tRatio\n");
+ strcat(buffer, "------- -----\t------\t-------\t\t--------\t------------\t\t-----\n");
+
+ for (int i = 0; i < STATISTICS_OPCODE_MAX; i++)
+ {
+ if (protocolData -> requestCount_[i])
+ {
+ sprintf(format, "#%d ", i);
+
+ while (strlen(format) < 5)
+ {
+ strcat(format, " ");
+ }
+
+ //
+ // Mark NX agent-related requests, those
+ // having a reply and finally those that
+ // have been probably tainted by client
+ // side.
+ //
+
+ if (i >= X_NXFirstOpcode && i <= X_NXLastOpcode)
+ {
+ strcat(format, "A");
+ }
+
+ if (i != X_NXInternalGenericReply && protocolData -> requestReplied_[i] > 0)
+ {
+ strcat(format, "R");
+ }
+
+ if (i == X_NoOperation && control -> TaintReplies)
+ {
+ strcat(format, "T");
+ }
+
+ while (strlen(format) < 8)
+ {
+ strcat(format, " ");
+ }
+
+ strcat(buffer, format);
+
+ if (protocolData -> requestCached_[i] > 0)
+ {
+ sprintf(format, "%.0f\t%.0f", protocolData -> requestCount_[i],
+ protocolData -> requestCached_[i]);
+ }
+ else
+ {
+ sprintf(format, "%.0f\t", protocolData -> requestCount_[i]);
+ }
+
+ strcat(buffer, format);
+
+ sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1 \t",
+ protocolData -> requestBitsIn_[i], protocolData -> requestBitsIn_[i] / 8192,
+ protocolData -> requestBitsOut_[i], protocolData -> requestBitsOut_[i] / 8192,
+ protocolData -> requestBitsIn_[i] / protocolData -> requestCount_[i],
+ protocolData -> requestBitsOut_[i] / protocolData -> requestCount_[i]);
+
+ strcat(buffer, format);
+
+ if (protocolData -> requestBitsOut_[i] > 0)
+ {
+ sprintf(format, "%5.3f:1\n", protocolData -> requestBitsIn_[i] /
+ protocolData -> requestBitsOut_[i]);
+
+ strcat(buffer, format);
+ }
+ else
+ {
+ strcat(buffer, "1:1\n");
+ }
+ }
+
+ countRequestIn += protocolData -> requestCount_[i];
+ countCachedRequestIn += protocolData -> requestCached_[i];
+ countRepliedRequestIn += protocolData -> requestReplied_[i];
+
+ countRequestBitsIn += protocolData -> requestBitsIn_[i];
+ countRequestBitsOut += protocolData -> requestBitsOut_[i];
+
+ countAnyIn += protocolData -> requestCount_[i];
+ countBitsIn += protocolData -> requestBitsIn_[i];
+ countBitsOut += protocolData -> requestBitsOut_[i];
+ }
+
+ if (countRequestIn > 0)
+ {
+ if (countCachedRequestIn > 0)
+ {
+ sprintf(format, "\ntotal: %.0f\t%.0f", countRequestIn, countCachedRequestIn);
+ }
+ else
+ {
+ sprintf(format, "\ntotal: %.0f\t", countRequestIn);
+ }
+
+ strcat(buffer, format);
+
+ sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1 \t",
+ countRequestBitsIn, countRequestBitsIn / 8192, countRequestBitsOut,
+ countRequestBitsOut / 8192, countRequestBitsIn / countRequestIn,
+ countRequestBitsOut / countRequestIn);
+
+ strcat(buffer, format);
+
+ if (countRequestBitsOut > 0)
+ {
+ sprintf(format, "%5.3f:1\n", countRequestBitsIn / countRequestBitsOut);
+ }
+ else
+ {
+ sprintf(format, "1:1\n");
+ }
+
+ strcat(buffer, format);
+ }
+ else
+ {
+ strcat(buffer, "N/A\n\n");
+ }
+
+ //
+ // Print transport data.
+ //
+
+ getTimeStats(type, buffer);
+
+ countAnyIn += protocolData -> cupsCount_;
+ countBitsIn += protocolData -> cupsBitsIn_;
+ countBitsOut += protocolData -> cupsBitsOut_;
+
+ countAnyIn += protocolData -> smbCount_;
+ countBitsIn += protocolData -> smbBitsIn_;
+ countBitsOut += protocolData -> smbBitsOut_;
+
+ countAnyIn += protocolData -> mediaCount_;
+ countBitsIn += protocolData -> mediaBitsIn_;
+ countBitsOut += protocolData -> mediaBitsOut_;
+
+ countAnyIn += protocolData -> httpCount_;
+ countBitsIn += protocolData -> httpBitsIn_;
+ countBitsOut += protocolData -> httpBitsOut_;
+
+ countAnyIn += protocolData -> fontCount_;
+ countBitsIn += protocolData -> fontBitsIn_;
+ countBitsOut += protocolData -> fontBitsOut_;
+
+ countAnyIn += protocolData -> slaveCount_;
+ countBitsIn += protocolData -> slaveBitsIn_;
+ countBitsOut += protocolData -> slaveBitsOut_;
+
+ //
+ // Save the overall amount of bytes
+ // coming from X clients.
+ //
+
+ overallData -> overallBytesIn_ = countBitsIn / 8;
+
+ //
+ // Print performance data.
+ //
+
+ if (transportData -> readTime_ > 0)
+ {
+ sprintf(format, " %.0f messages (%.0f KB) encoded per second.\n\n",
+ countAnyIn / (transportData -> readTime_ / 1000),
+ (countBitsIn + transportData -> framingBitsOut_) / 8192 /
+ (transportData -> readTime_ / 1000));
+ }
+ else
+ {
+ sprintf(format, " %.0f messages (%.0f KB) encoded per second.\n\n",
+ countAnyIn, (countBitsIn + transportData ->
+ framingBitsOut_) / 8192);
+ }
+
+ strcat(buffer, format);
+
+ strcat(buffer, "link: ");
+
+ //
+ // ZLIB compression stats.
+ //
+
+ getStreamStats(type, buffer);
+
+ //
+ // Save the overall amount of bytes
+ // sent on NX proxy link.
+ //
+
+ if (transportData -> compressedBytesOut_ > 0)
+ {
+ overallData -> overallBytesOut_ = transportData -> compressedBytesOut_;
+ }
+ else
+ {
+ overallData -> overallBytesOut_ = countBitsOut / 8;
+ }
+
+ //
+ // Print info on multiplexing overhead.
+ //
+
+ getFramingStats(type, buffer);
+
+ //
+ // Print stats about additional channels.
+ //
+
+ getServicesStats(type, buffer);
+
+ //
+ // Compression summary.
+ //
+
+ double ratio = 1;
+
+ if (transportData -> compressedBytesOut_ / 1024 > 0)
+ {
+ ratio = ((countBitsIn + transportData -> framingBitsOut_) / 8192) /
+ (transportData -> compressedBytesOut_ / 1024);
+
+ }
+ else if (countBitsOut > 0)
+ {
+ ratio = (countBitsIn + transportData -> framingBitsOut_) /
+ countBitsOut;
+ }
+
+ sprintf(format, " Protocol compression ratio is %5.3f:1.\n\n",
+ ratio);
+
+ strcat(buffer, format);
+
+ getBitrateStats(type, buffer);
+
+ getSplitStats(type, buffer);
+
+ sprintf(format, " %.0f total handled replies (%.0f unmatched).\n\n\n",
+ countRepliedRequestIn, protocolData -> requestReplied_[X_NXInternalGenericReply]);
+
+ strcat(buffer, format);
+
+ return 1;
+}
+
+int Statistics::getClientOverallStats(int type, char *&buffer)
+{
+ if (type != PARTIAL_STATS && type != TOTAL_STATS)
+ {
+ #ifdef PANIC
+ *logofs << "Statistics: PANIC! Cannot produce statistics "
+ << "with qualifier '" << type << "'.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ struct T_overallData *overallData;
+ struct T_packedData *packedData;
+
+ if (type == PARTIAL_STATS)
+ {
+ overallData = &overallPartial_;
+ packedData = &packedPartial_;
+ }
+ else
+ {
+ overallData = &overallTotal_;
+ packedData = &packedTotal_;
+ }
+
+ char format[FORMAT_LENGTH];
+
+ //
+ // Print header including link type,
+ // followed by info on packed images.
+ //
+
+ strcat(buffer, "NX Compression Summary\n");
+ strcat(buffer, "----------------------\n\n");
+
+ char label[FORMAT_LENGTH];
+
+ switch (control -> LinkMode)
+ {
+ case LINK_TYPE_NONE:
+ {
+ strcpy(label, "NONE");
+
+ break;
+ }
+ case LINK_TYPE_MODEM:
+ {
+ strcpy(label, "MODEM");
+
+ break;
+ }
+ case LINK_TYPE_ISDN:
+ {
+ strcpy(label, "ISDN");
+
+ break;
+ }
+ case LINK_TYPE_ADSL:
+ {
+ strcpy(label, "ADSL");
+
+ break;
+ }
+ case LINK_TYPE_WAN:
+ {
+ strcpy(label, "WAN");
+
+ break;
+ }
+ case LINK_TYPE_LAN:
+ {
+ strcpy(label, "LAN");
+
+ break;
+ }
+ default:
+ {
+ strcpy(label, "Unknown");
+
+ break;
+ }
+ }
+
+ sprintf(format, "link: %s", label);
+
+ if (control -> LocalDeltaCompression == 1)
+ {
+ strcat(format, " with protocol compression enabled.");
+ }
+ else
+ {
+ strcat(format, " with protocol compression disabled.");
+ }
+
+ strcat(format, "\n\n");
+
+ strcat(buffer, format);
+
+ if (packedData -> packedBytesIn_ > 0)
+ {
+ sprintf(format, "images: %.0f bytes (%.0f KB) packed to %.0f (%.0f KB).\n\n",
+ packedData -> packedBytesOut_, packedData -> packedBytesOut_ / 1024,
+ packedData -> packedBytesIn_, packedData -> packedBytesIn_ / 1024);
+
+ strcat(buffer, format);
+
+ sprintf(format, " Images compression ratio is %5.3f:1.\n\n",
+ packedData -> packedBytesOut_ / packedData -> packedBytesIn_);
+
+ strcat(buffer, format);
+ }
+
+ double overallIn = overallData -> overallBytesIn_ - packedData -> packedBytesIn_ +
+ packedData -> packedBytesOut_;
+
+ double overallOut = overallData -> overallBytesOut_;
+
+ sprintf(format, "overall: %.0f bytes (%.0f KB) in, %.0f bytes (%.0f KB) out.\n\n",
+ overallIn, overallIn / 1024, overallOut, overallOut / 1024);
+
+ strcat(buffer, format);
+
+ if (overallData -> overallBytesOut_ > 0)
+ {
+ sprintf(format, " Overall NX server compression ratio is %5.3f:1.\n\n\n",
+ overallIn / overallOut);
+ }
+ else
+ {
+ sprintf(format, " Overall NX server compression ratio is 1:1.\n\n\n");
+ }
+
+ strcat(buffer, format);
+
+ return 1;
+}
+
+int Statistics::getServerCacheStats(int type, char *&buffer)
+{
+ if (type != PARTIAL_STATS && type != TOTAL_STATS)
+ {
+ #ifdef PANIC
+ *logofs << "Statistics: PANIC! Cannot produce statistics "
+ << "with qualifier '" << type << "'.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ //
+ // Print message cache data according
+ // to local and remote view.
+ //
+
+ MessageStore *currentStore = NULL;
+ MessageStore *anyStore = NULL;
+
+ char format[FORMAT_LENGTH];
+
+ strcat(buffer, "\nNX Cache Statistics\n");
+ strcat(buffer, "-------------------\n\n");
+
+ for (int m = proxy_client; m <= proxy_server; m++)
+ {
+ if (m == proxy_client)
+ {
+ strcat(buffer, "Request\tCached\tSize at Server\t\tSize at Client\t\tCache limit\n");
+ strcat(buffer, "-------\t------\t--------------\t\t--------------\t\t-----------\n");
+ }
+ else
+ {
+ strcat(buffer, "\nReply\tCached\tSize at Server\t\tSize at Client\t\tCache limit\n");
+ strcat(buffer, "-----\t------\t--------------\t\t--------------\t\t-----------\n");
+ }
+
+ for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++)
+ {
+ if (m == proxy_client)
+ {
+ currentStore = proxy_ -> getClientStore() -> getRequestStore(i);
+ }
+ else
+ {
+ currentStore = proxy_ -> getServerStore() -> getReplyStore(i);
+ }
+
+ if (currentStore != NULL &&
+ (currentStore -> getLocalStorageSize() ||
+ currentStore -> getRemoteStorageSize()))
+ {
+ anyStore = currentStore;
+
+ sprintf(format, "#%d\t%d\t", i, currentStore -> getSize());
+
+ strcat(buffer, format);
+
+ sprintf(format, "%d (%.0f KB)\t\t", currentStore -> getRemoteStorageSize(),
+ ((double) currentStore -> getRemoteStorageSize()) / 1024);
+
+ strcat(buffer, format);
+
+ sprintf(format, "%d (%.0f KB)\t\t", currentStore -> getLocalStorageSize(),
+ ((double) currentStore -> getLocalStorageSize()) / 1024);
+
+ strcat(buffer, format);
+
+ sprintf(format, "%d/%.0f KB\n", currentStore -> cacheSlots,
+ ((double) control -> getUpperStorageSize() / 100 *
+ currentStore -> cacheThreshold) / 1024);
+
+ strcat(buffer, format);
+ }
+ }
+
+ if (anyStore == NULL)
+ {
+ strcat(buffer, "N/A\n");
+ }
+ }
+
+ if (anyStore != NULL)
+ {
+ sprintf(format, "\ncache: %d bytes (%d KB) available at server.\n",
+ control -> ClientTotalStorageSize,
+ control -> ClientTotalStorageSize / 1024);
+
+ strcat(buffer, format);
+
+ sprintf(format, " %d bytes (%d KB) available at client.\n\n",
+ control -> ServerTotalStorageSize,
+ control -> ServerTotalStorageSize / 1024);
+
+ strcat(buffer, format);
+
+ sprintf(format, " %d bytes (%d KB) allocated at server.\n",
+ anyStore -> getRemoteTotalStorageSize(),
+ anyStore -> getRemoteTotalStorageSize() / 1024);
+
+ strcat(buffer, format);
+
+ sprintf(format, " %d bytes (%d KB) allocated at client.\n\n\n",
+ anyStore -> getLocalTotalStorageSize(),
+ anyStore -> getLocalTotalStorageSize() / 1024);
+
+ strcat(buffer, format);
+ }
+ else
+ {
+ strcat(buffer, "\ncache: N/A\n\n");
+ }
+
+ return 1;
+}
+
+int Statistics::getServerProtocolStats(int type, char *&buffer)
+{
+ if (type != PARTIAL_STATS && type != TOTAL_STATS)
+ {
+ #ifdef PANIC
+ *logofs << "Statistics: PANIC! Cannot produce statistics "
+ << "with qualifier '" << type << "'.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ struct T_transportData *transportData;
+ struct T_protocolData *protocolData;
+ struct T_overallData *overallData;
+
+ if (type == PARTIAL_STATS)
+ {
+ transportData = &transportPartial_;
+ protocolData = &protocolPartial_;
+ overallData = &overallPartial_;
+ }
+ else
+ {
+ transportData = &transportTotal_;
+ protocolData = &protocolTotal_;
+ overallData = &overallTotal_;
+ }
+
+ char format[FORMAT_LENGTH];
+
+ double countReplyBitsIn = 0;
+ double countReplyBitsOut = 0;
+
+ double countReplyIn = 0;
+ double countCachedReplyIn = 0;
+
+ double countEventBitsIn = 0;
+ double countEventBitsOut = 0;
+
+ double countEventIn = 0;
+ double countCachedEventIn = 0;
+
+ double countAnyIn = 0;
+ double countBitsIn = 0;
+ double countBitsOut = 0;
+
+ //
+ // Print reply data.
+ //
+
+ strcat(buffer, "NX Client Side Protocol Statistics\n");
+ strcat(buffer, "----------------------------------\n\n");
+
+ strcat(buffer, "Reply Total\tCached\tBits In\t\tBits Out\tBits/Reply\t\tRatio\n");
+ strcat(buffer, "------- -----\t------\t-------\t\t--------\t----------\t\t-----\n");
+
+ for (int i = 0; i < STATISTICS_OPCODE_MAX; i++)
+ {
+ if (protocolData -> replyCount_[i])
+ {
+ sprintf(format, "#%d ", i);
+
+ while (strlen(format) < 5)
+ {
+ strcat(format, " ");
+ }
+
+ //
+ // Mark replies originated
+ // by NX agent requests.
+ //
+
+ if (i >= X_NXFirstOpcode && i <= X_NXLastOpcode)
+ {
+ strcat(format, "A");
+ }
+
+ //
+ // Mark replies that we didn't
+ // match against a request.
+ //
+
+ if (i == 1)
+ {
+ strcat(format, "U");
+ }
+
+ while (strlen(format) < 8)
+ {
+ strcat(format, " ");
+ }
+
+ strcat(buffer, format);
+
+ if (protocolData -> replyCached_[i] > 0)
+ {
+ sprintf(format, "%.0f\t%.0f", protocolData -> replyCount_[i],
+ protocolData -> replyCached_[i]);
+ }
+ else
+ {
+ sprintf(format, "%.0f\t", protocolData -> replyCount_[i]);
+ }
+
+ strcat(buffer, format);
+
+ sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1 \t",
+ protocolData -> replyBitsIn_[i], protocolData -> replyBitsIn_[i] / 8192,
+ protocolData -> replyBitsOut_[i], protocolData -> replyBitsOut_[i] / 8192,
+ protocolData -> replyBitsIn_[i] / protocolData -> replyCount_[i],
+ protocolData -> replyBitsOut_[i] / protocolData -> replyCount_[i]);
+
+ strcat(buffer, format);
+
+ if (protocolData -> replyBitsOut_[i] > 0)
+ {
+ sprintf(format, "%5.3f:1\n", protocolData -> replyBitsIn_[i] /
+ protocolData -> replyBitsOut_[i]);
+ }
+ else
+ {
+ sprintf(format, "1:1\n");
+ }
+
+ strcat(buffer, format);
+ }
+
+ countReplyIn += protocolData -> replyCount_[i];
+ countCachedReplyIn += protocolData -> replyCached_[i];
+
+ countReplyBitsIn += protocolData -> replyBitsIn_[i];
+ countReplyBitsOut += protocolData -> replyBitsOut_[i];
+
+ countAnyIn += protocolData -> replyCount_[i];
+ countBitsIn += protocolData -> replyBitsIn_[i];
+ countBitsOut += protocolData -> replyBitsOut_[i];
+ }
+
+ if (countReplyIn > 0)
+ {
+ if (countCachedReplyIn > 0)
+ {
+ sprintf(format, "\ntotal: %.0f\t%.0f", countReplyIn, countCachedReplyIn);
+ }
+ else
+ {
+ sprintf(format, "\ntotal: %.0f\t", countReplyIn);
+ }
+
+ strcat(buffer, format);
+
+ sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1 \t",
+ countReplyBitsIn, countReplyBitsIn / 8192, countReplyBitsOut,
+ countReplyBitsOut / 8192, countReplyBitsIn / countReplyIn,
+ countReplyBitsOut / countReplyIn);
+
+ strcat(buffer, format);
+
+ if (countReplyBitsOut > 0)
+ {
+ sprintf(format, "%5.3f:1\n", countReplyBitsIn / countReplyBitsOut);
+ }
+ else
+ {
+ sprintf(format, "1:1\n");
+ }
+
+ strcat(buffer, format);
+ }
+ else
+ {
+ strcat(buffer, "N/A\n");
+ }
+
+ strcat(buffer, "\n");
+
+ //
+ // Print event and error data.
+ //
+
+ strcat(buffer, "Event Total\tCached\tBits In\t\tBits Out\tBits/Event\t\tRatio\n");
+ strcat(buffer, "------- -----\t------\t-------\t\t--------\t----------\t\t-----\n");
+
+ for (int i = 0; i < STATISTICS_OPCODE_MAX; i++)
+ {
+ if (protocolData -> eventCount_[i])
+ {
+ sprintf(format, "#%d ", i);
+
+ while (strlen(format) < 8)
+ {
+ strcat(format, " ");
+ }
+
+ strcat(buffer, format);
+
+ if (protocolData -> eventCached_[i] > 0)
+ {
+ sprintf(format, "%.0f\t%.0f", protocolData -> eventCount_[i],
+ protocolData -> eventCached_[i]);
+ }
+ else
+ {
+ sprintf(format, "%.0f\t", protocolData -> eventCount_[i]);
+ }
+
+ strcat(buffer, format);
+
+ sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1 \t",
+ protocolData -> eventBitsIn_[i], protocolData -> eventBitsIn_[i] / 8192,
+ protocolData -> eventBitsOut_[i], protocolData -> eventBitsOut_[i] / 8192,
+ protocolData -> eventBitsIn_[i] / protocolData -> eventCount_[i],
+ protocolData -> eventBitsOut_[i] / protocolData -> eventCount_[i]);
+
+ strcat(buffer, format);
+
+ if (protocolData -> eventBitsOut_[i] > 0)
+ {
+ sprintf(format, "%5.3f:1\n", protocolData -> eventBitsIn_[i] /
+ protocolData -> eventBitsOut_[i]);
+ }
+ else
+ {
+ sprintf(format, "1:1\n");
+ }
+
+ strcat(buffer, format);
+ }
+
+ countEventIn += protocolData -> eventCount_[i];
+ countCachedEventIn += protocolData -> eventCached_[i];
+
+ countEventBitsIn += protocolData -> eventBitsIn_[i];
+ countEventBitsOut += protocolData -> eventBitsOut_[i];
+
+ countAnyIn += protocolData -> eventCount_[i];
+ countBitsIn += protocolData -> eventBitsIn_[i];
+ countBitsOut += protocolData -> eventBitsOut_[i];
+ }
+
+ if (countEventIn > 0)
+ {
+ if (countCachedEventIn > 0)
+ {
+ sprintf(format, "\ntotal: %.0f\t%.0f", countEventIn, countCachedEventIn);
+ }
+ else
+ {
+ sprintf(format, "\ntotal: %.0f\t", countEventIn);
+ }
+
+ strcat(buffer, format);
+
+ sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1 \t",
+ countEventBitsIn, countEventBitsIn / 8192, countEventBitsOut,
+ countEventBitsOut / 8192, countEventBitsIn / countEventIn,
+ countEventBitsOut / countEventIn);
+
+ strcat(buffer, format);
+
+ if (countEventBitsOut > 0)
+ {
+ sprintf(format, "%5.3f:1\n", countEventBitsIn / countEventBitsOut);
+ }
+ else
+ {
+ sprintf(format, "1:1\n");
+ }
+
+ strcat(buffer, format);
+ }
+ else
+ {
+ strcat(buffer, "N/A\n\n");
+ }
+
+ //
+ // Print transport data.
+ //
+
+ getTimeStats(type, buffer);
+
+ countAnyIn += protocolData -> cupsCount_;
+ countBitsIn += protocolData -> cupsBitsIn_;
+ countBitsOut += protocolData -> cupsBitsOut_;
+
+ countAnyIn += protocolData -> smbCount_;
+ countBitsIn += protocolData -> smbBitsIn_;
+ countBitsOut += protocolData -> smbBitsOut_;
+
+ countAnyIn += protocolData -> mediaCount_;
+ countBitsIn += protocolData -> mediaBitsIn_;
+ countBitsOut += protocolData -> mediaBitsOut_;
+
+ countAnyIn += protocolData -> httpCount_;
+ countBitsIn += protocolData -> httpBitsIn_;
+ countBitsOut += protocolData -> httpBitsOut_;
+
+ countAnyIn += protocolData -> fontCount_;
+ countBitsIn += protocolData -> fontBitsIn_;
+ countBitsOut += protocolData -> fontBitsOut_;
+
+ countAnyIn += protocolData -> slaveCount_;
+ countBitsIn += protocolData -> slaveBitsIn_;
+ countBitsOut += protocolData -> slaveBitsOut_;
+
+ //
+ // Save the overall amount of bytes
+ // coming from X clients.
+ //
+
+ overallData -> overallBytesIn_ = countBitsIn / 8;
+
+ //
+ // Print performance data.
+ //
+
+ if (transportData -> readTime_ > 0)
+ {
+ sprintf(format, " %.0f messages (%.0f KB) encoded per second.\n\n",
+ countAnyIn / (transportData -> readTime_ / 1000),
+ (countBitsIn + transportData -> framingBitsOut_) / 8192 /
+ (transportData -> readTime_ / 1000));
+ }
+ else
+ {
+ sprintf(format, " %.0f messages (%.0f KB) encoded per second.\n\n",
+ countAnyIn, (countBitsIn + transportData ->
+ framingBitsOut_) / 8192);
+ }
+
+ strcat(buffer, format);
+
+ strcat(buffer, "link: ");
+
+ //
+ // ZLIB compression stats.
+ //
+
+ getStreamStats(type, buffer);
+
+ //
+ // Save the overall amount of bytes
+ // sent on NX proxy link.
+ //
+
+ if (transportData -> compressedBytesOut_ > 0)
+ {
+ overallData -> overallBytesOut_ = transportData -> compressedBytesOut_;
+ }
+ else
+ {
+ overallData -> overallBytesOut_ = countBitsOut / 8;
+ }
+
+ //
+ // Print info on multiplexing overhead.
+ //
+
+ getFramingStats(type, buffer);
+
+ //
+ // Print stats about additional channels.
+ //
+
+ getServicesStats(type, buffer);
+
+ //
+ // Compression summary.
+ //
+
+ double ratio = 1;
+
+ if (transportData -> compressedBytesOut_ / 1024 > 0)
+ {
+ ratio = ((countBitsIn + transportData -> framingBitsOut_) / 8192) /
+ (transportData -> compressedBytesOut_ / 1024);
+
+ }
+ else if (countBitsOut > 0)
+ {
+ ratio = (countBitsIn + transportData -> framingBitsOut_) /
+ countBitsOut;
+ }
+
+ sprintf(format, " Protocol compression ratio is %5.3f:1.\n\n",
+ ratio);
+
+ strcat(buffer, format);
+
+ getBitrateStats(type, buffer);
+
+ //
+ // These are not included in output.
+ //
+ // getSplitStats(type, buffer);
+ //
+
+ strcat(buffer, "\n");
+
+ //
+ // These statistics are not included in output.
+ // You can check it anyway to get the effective
+ // amount of bytes produced by unpack procedure.
+ //
+ // getClientOverallStats(type, buffer);
+ //
+
+ return 1;
+}
+
+int Statistics::getServerOverallStats(int type, char *&buffer)
+{
+ return 1;
+}
+
+int Statistics::getTimeStats(int type, char *&buffer)
+{
+ struct T_transportData *transportData;
+
+ if (type == PARTIAL_STATS)
+ {
+ transportData = &transportPartial_;
+ }
+ else
+ {
+ transportData = &transportTotal_;
+ }
+
+ char format[FORMAT_LENGTH];
+
+ sprintf(format, "\ntime: %.0f Ms idle, %.0f Ms (%.0f Ms in read, %.0f Ms in write) running.\n\n",
+ transportData -> idleTime_, transportData -> readTime_,
+ transportData -> readTime_ - transportData -> writeTime_,
+ transportData -> writeTime_);
+
+ strcat(buffer, format);
+
+ return 1;
+}
+
+int Statistics::getStreamStats(int type, char *&buffer)
+{
+ struct T_transportData *transportData;
+
+ if (type == PARTIAL_STATS)
+ {
+ transportData = &transportPartial_;
+ }
+ else
+ {
+ transportData = &transportTotal_;
+ }
+
+ char format[FORMAT_LENGTH];
+
+ if (transportData -> compressedBytesOut_ > 0)
+ {
+ sprintf(format, "%.0f bytes (%.0f KB) compressed to %.0f (%.0f KB).\n",
+ transportData -> compressedBytesIn_, transportData -> compressedBytesIn_ / 1024,
+ transportData -> compressedBytesOut_, transportData -> compressedBytesOut_ / 1024);
+
+ strcat(buffer, format);
+
+ sprintf(format, " %5.3f:1 stream compression ratio.\n\n",
+ transportData -> compressedBytesIn_ / transportData -> compressedBytesOut_);
+
+ strcat(buffer, format);
+ }
+
+ if (transportData -> decompressedBytesOut_ > 0)
+ {
+ if (transportData -> compressedBytesOut_ > 0)
+ {
+ strcat(buffer, " ");
+ }
+
+ sprintf(format, "%.0f bytes (%.0f KB) decompressed to %.0f (%.0f KB).\n",
+ transportData -> decompressedBytesIn_, transportData -> decompressedBytesIn_ / 1024,
+ transportData -> decompressedBytesOut_, transportData -> decompressedBytesOut_ / 1024);
+
+ strcat(buffer, format);
+
+ sprintf(format, " %5.3f:1 stream compression ratio.\n\n",
+ transportData -> decompressedBytesOut_ / transportData -> decompressedBytesIn_);
+
+ strcat(buffer, format);
+ }
+
+ if (transportData -> compressedBytesOut_ > 0 ||
+ transportData -> decompressedBytesOut_ > 0)
+ {
+ strcat(buffer, " ");
+ }
+
+ return 1;
+}
+
+int Statistics::getServicesStats(int type, char *&buffer)
+{
+ struct T_protocolData *protocolData;
+
+ if (type == PARTIAL_STATS)
+ {
+ protocolData = &protocolPartial_;
+ }
+ else
+ {
+ protocolData = &protocolTotal_;
+ }
+
+ char format[FORMAT_LENGTH];
+
+ if (protocolData -> cupsBitsOut_ > 0)
+ {
+ sprintf(format, " %.0f CUPS messages, %.0f bytes (%.0f KB) in, %.0f bytes (%.0f KB) out.\n\n",
+ protocolData -> cupsCount_ , protocolData -> cupsBitsIn_ / 8,
+ protocolData -> cupsBitsIn_ / 8192, protocolData -> cupsBitsOut_ / 8,
+ protocolData -> cupsBitsOut_ / 8192);
+
+ strcat(buffer, format);
+ }
+
+ if (protocolData -> smbBitsOut_ > 0)
+ {
+ sprintf(format, " %.0f SMB messages, %.0f bytes (%.0f KB) in, %.0f bytes (%.0f KB) out.\n\n",
+ protocolData -> smbCount_ , protocolData -> smbBitsIn_ / 8,
+ protocolData -> smbBitsIn_ / 8192, protocolData -> smbBitsOut_ / 8,
+ protocolData -> smbBitsOut_ / 8192);
+
+ strcat(buffer, format);
+ }
+
+ if (protocolData -> mediaBitsOut_ > 0)
+ {
+ sprintf(format, " %.0f multimedia messages, %.0f bytes (%.0f KB) in, %.0f bytes (%.0f KB) out.\n\n",
+ protocolData -> mediaCount_ , protocolData -> mediaBitsIn_ / 8,
+ protocolData -> mediaBitsIn_ / 8192, protocolData -> mediaBitsOut_ / 8,
+ protocolData -> mediaBitsOut_ / 8192);
+
+ strcat(buffer, format);
+ }
+
+ if (protocolData -> httpBitsOut_ > 0)
+ {
+ sprintf(format, " %.0f HTTP messages, %.0f bytes (%.0f KB) in, %.0f bytes (%.0f KB) out.\n\n",
+ protocolData -> httpCount_ , protocolData -> httpBitsIn_ / 8,
+ protocolData -> httpBitsIn_ / 8192, protocolData -> httpBitsOut_ / 8,
+ protocolData -> httpBitsOut_ / 8192);
+
+ strcat(buffer, format);
+ }
+
+ if (protocolData -> fontBitsOut_ > 0)
+ {
+ sprintf(format, " %.0f font server messages, %.0f bytes (%.0f KB) in, %.0f bytes (%.0f KB) out.\n\n",
+ protocolData -> fontCount_ , protocolData -> fontBitsIn_ / 8,
+ protocolData -> fontBitsIn_ / 8192, protocolData -> fontBitsOut_ / 8,
+ protocolData -> fontBitsOut_ / 8192);
+
+ strcat(buffer, format);
+ }
+
+ if (protocolData -> slaveBitsOut_ > 0)
+ {
+ sprintf(format, " %.0f slave messages, %.0f bytes (%.0f KB) in, %.0f bytes (%.0f KB) out.\n\n",
+ protocolData -> slaveCount_ , protocolData -> slaveBitsIn_ / 8,
+ protocolData -> slaveBitsIn_ / 8192, protocolData -> slaveBitsOut_ / 8,
+ protocolData -> slaveBitsOut_ / 8192);
+
+ strcat(buffer, format);
+ }
+
+ return 1;
+}
+
+int Statistics::getFramingStats(int type, char *&buffer)
+{
+ struct T_transportData *transportData;
+
+ if (type == PARTIAL_STATS)
+ {
+ transportData = &transportPartial_;
+ }
+ else
+ {
+ transportData = &transportTotal_;
+ }
+
+ char format[FORMAT_LENGTH];
+
+ //
+ // Print info on multiplexing overhead.
+ //
+
+ sprintf(format, "%.0f frames in, %.0f frames out, %.0f writes out.\n\n",
+ transportData -> proxyFramesIn_, transportData -> proxyFramesOut_,
+ transportData -> proxyWritesOut_);
+
+ strcat(buffer, format);
+
+ sprintf(format, " %.0f bytes (%.0f KB) used for framing and multiplexing.\n\n",
+ transportData -> framingBitsOut_ / 8, transportData -> framingBitsOut_ / 8192);
+
+ strcat(buffer, format);
+
+ return 1;
+}
+
+int Statistics::getBitrateStats(int type, char *&buffer)
+{
+ struct T_transportData *transportData;
+ struct T_overallData *overallData;
+
+ if (type == PARTIAL_STATS)
+ {
+ transportData = &transportPartial_;
+ overallData = &overallPartial_;
+ }
+ else
+ {
+ transportData = &transportTotal_;
+ overallData = &overallTotal_;
+ }
+
+ double total = 0;
+
+ if (transportData -> idleTime_ + transportData -> readTime_ > 0)
+ {
+ total = overallData -> overallBytesOut_ /
+ ((transportData -> idleTime_ + transportData -> readTime_) / 1000);
+ }
+
+ char format[FORMAT_LENGTH];
+
+ sprintf(format, " %.0f B/s average, %d B/s %ds, %d B/s %ds, %d B/s maximum.\n\n",
+ total, getBitrateInShortFrame(), control -> ShortBitrateTimeFrame / 1000,
+ getBitrateInLongFrame(), control -> LongBitrateTimeFrame / 1000,
+ getTopBitrate());
+
+ strcat(buffer, format);
+
+ resetTopBitrate();
+
+ return 1;
+}
+
+int Statistics::getSplitStats(int type, char *&buffer)
+{
+ //
+ // Don't print these statistics if persistent
+ // cache of images is disabled.
+ //
+
+ if (control -> ImageCacheEnableLoad == 0 &&
+ control -> ImageCacheEnableSave == 0)
+ {
+ return 0;
+ }
+
+ struct T_splitData *splitData;
+
+ if (type == PARTIAL_STATS)
+ {
+ splitData = &splitPartial_;
+ }
+ else
+ {
+ splitData = &splitTotal_;
+ }
+
+ char format[FORMAT_LENGTH];
+
+ //
+ // Print info on split messages restored from disk.
+ //
+
+ sprintf(format, " %.0f images streamed, %.0f restored, %.0f bytes (%.0f KB) cached.\n\n",
+ splitData -> splitCount_, splitData -> splitAborted_, splitData -> splitAbortedBytesOut_,
+ splitData -> splitAbortedBytesOut_ / 1024);
+
+ strcat(buffer, format);
+
+ return 1;
+}
diff --git a/nxcomp/Statistics.h b/nxcomp/Statistics.h
new file mode 100644
index 000000000..44ff8834f
--- /dev/null
+++ b/nxcomp/Statistics.h
@@ -0,0 +1,737 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef Statistics_H
+#define Statistics_H
+
+#include "NXproto.h"
+
+#include "Misc.h"
+#include "Timestamp.h"
+
+class Proxy;
+
+//
+// Opcode 255 is for generic requests, 1 is for
+// generic replies (those which haven't a speci-
+// fic differential encoding), opcode 0 is for
+// generic messages from the auxiliary channels.
+//
+
+#define STATISTICS_OPCODE_MAX 256
+
+//
+// Maximum length of the buffer allocated for
+// the statistics output.
+//
+
+#define STATISTICS_LENGTH 16384
+
+//
+// Query type.
+//
+
+#define TOTAL_STATS 1
+#define PARTIAL_STATS 2
+#define NO_STATS 3
+
+//
+// Log level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Log the operations related to updating
+// the control tokens.
+//
+
+#undef TOKEN
+
+class Statistics
+{
+ public:
+
+ //
+ // Use the proxy class to get access
+ // to the message stores.
+ //
+
+ Statistics(Proxy *proxy);
+
+ ~Statistics();
+
+ void addIdleTime(unsigned int numMs)
+ {
+ transportPartial_.idleTime_ += numMs;
+ transportTotal_.idleTime_ += numMs;
+ }
+
+ void subIdleTime(unsigned int numMs)
+ {
+ transportPartial_.idleTime_ -= numMs;
+ transportTotal_.idleTime_ -= numMs;
+ }
+
+ void addReadTime(unsigned int numMs)
+ {
+ transportPartial_.readTime_ += numMs;
+ transportTotal_.readTime_ += numMs;
+ }
+
+ void subReadTime(unsigned int numMs)
+ {
+ transportPartial_.readTime_ -= numMs;
+ transportTotal_.readTime_ -= numMs;
+ }
+
+ void addWriteTime(unsigned int numMs)
+ {
+ transportPartial_.writeTime_ += numMs;
+ transportTotal_.writeTime_ += numMs;
+ }
+
+ void subWriteTime(unsigned int numMs)
+ {
+ transportPartial_.writeTime_ -= numMs;
+ transportTotal_.writeTime_ -= numMs;
+ }
+
+ void addBytesIn(unsigned int numBytes)
+ {
+ transportPartial_.proxyBytesIn_ += numBytes;
+ transportTotal_.proxyBytesIn_ += numBytes;
+ }
+
+ double getBytesIn()
+ {
+ return transportTotal_.proxyBytesIn_;
+ }
+
+ void addBytesOut(unsigned int numBytes)
+ {
+ transportPartial_.proxyBytesOut_ += numBytes;
+ transportTotal_.proxyBytesOut_ += numBytes;
+ }
+
+ double getBytesOut()
+ {
+ return transportTotal_.proxyBytesOut_;
+ }
+
+ void addFrameIn()
+ {
+ transportPartial_.proxyFramesIn_++;
+ transportTotal_.proxyFramesIn_++;
+
+ #ifdef TEST
+ *logofs << "Statistics: Updated total proxy frames in to "
+ << transportTotal_.proxyFramesIn_ << " at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+ }
+
+ void addFrameOut()
+ {
+ transportPartial_.proxyFramesOut_++;
+ transportTotal_.proxyFramesOut_++;
+
+ #ifdef TEST
+ *logofs << "Statistics: Updated total proxy frames out to "
+ << transportTotal_.proxyFramesOut_ << " at "
+ << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+ }
+
+ void addWriteOut()
+ {
+ transportPartial_.proxyWritesOut_++;
+ transportTotal_.proxyWritesOut_++;
+
+ #ifdef TEST
+ *logofs << "Statistics: Updated total proxy writes out to "
+ << transportTotal_.proxyWritesOut_ << " at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+ }
+
+ void addCompressedBytes(unsigned int bytesIn, unsigned int bytesOut);
+
+ void addDecompressedBytes(unsigned int bytesIn, unsigned int bytesOut)
+ {
+ transportPartial_.decompressedBytesIn_ += bytesIn;
+ transportTotal_.decompressedBytesIn_ += bytesIn;
+
+ transportPartial_.decompressedBytesOut_ += bytesOut;
+ transportTotal_.decompressedBytesOut_ += bytesOut;
+ }
+
+ void addFramingBits(unsigned int bitsOut)
+ {
+ transportPartial_.framingBitsOut_ += bitsOut;
+ transportTotal_.framingBitsOut_ += bitsOut;
+
+ proxyData_.protocolCount_ += bitsOut;
+ }
+
+ void addCachedRequest(unsigned int opcode)
+ {
+ protocolPartial_.requestCached_[opcode]++;
+ protocolTotal_.requestCached_[opcode]++;
+ }
+
+ void addRenderCachedRequest(unsigned int opcode)
+ {
+ protocolPartial_.renderRequestCached_[opcode]++;
+ protocolTotal_.renderRequestCached_[opcode]++;
+ }
+
+ void addRepliedRequest(unsigned int opcode)
+ {
+ protocolPartial_.requestReplied_[opcode]++;
+ protocolTotal_.requestReplied_[opcode]++;
+ }
+
+ void addCachedReply(unsigned int opcode)
+ {
+ protocolPartial_.replyCached_[opcode]++;
+ protocolTotal_.replyCached_[opcode]++;
+ }
+
+ void addRequestBits(unsigned int opcode, unsigned int bitsIn, unsigned int bitsOut)
+ {
+ #ifdef DEBUG
+ *logofs << "Statistcs: Added " << bitsIn << " bits in and "
+ << bitsOut << " bits out to opcode " << opcode
+ << ".\n" << logofs_flush;
+ #endif
+
+ protocolPartial_.requestCount_[opcode]++;
+ protocolTotal_.requestCount_[opcode]++;
+
+ protocolPartial_.requestBitsIn_[opcode] += bitsIn;
+ protocolTotal_.requestBitsIn_[opcode] += bitsIn;
+
+ protocolPartial_.requestBitsOut_[opcode] += bitsOut;
+ protocolTotal_.requestBitsOut_[opcode] += bitsOut;
+
+ //
+ // Don't account the split bits
+ // to the control token.
+ //
+
+ if (opcode != X_NXSplitData && opcode != X_NXSplitEvent)
+ {
+ proxyData_.protocolCount_ += bitsOut;
+ }
+ }
+
+ void addRenderRequestBits(unsigned int opcode, unsigned int bitsIn, unsigned int bitsOut)
+ {
+ #ifdef DEBUG
+ *logofs << "Statistcs: Added " << bitsIn << " bits in and "
+ << bitsOut << " bits out to render opcode " << opcode
+ << ".\n" << logofs_flush;
+ #endif
+
+ protocolPartial_.renderRequestCount_[opcode]++;
+ protocolTotal_.renderRequestCount_[opcode]++;
+
+ protocolPartial_.renderRequestBitsIn_[opcode] += bitsIn;
+ protocolTotal_.renderRequestBitsIn_[opcode] += bitsIn;
+
+ protocolPartial_.renderRequestBitsOut_[opcode] += bitsOut;
+ protocolTotal_.renderRequestBitsOut_[opcode] += bitsOut;
+ }
+
+ void addReplyBits(unsigned int opcode, unsigned int bitsIn, unsigned int bitsOut)
+ {
+ protocolPartial_.replyCount_[opcode]++;
+ protocolTotal_.replyCount_[opcode]++;
+
+ protocolPartial_.replyBitsIn_[opcode] += bitsIn;
+ protocolTotal_.replyBitsIn_[opcode] += bitsIn;
+
+ protocolPartial_.replyBitsOut_[opcode] += bitsOut;
+ protocolTotal_.replyBitsOut_[opcode] += bitsOut;
+
+ proxyData_.protocolCount_ += bitsOut;
+ }
+
+ void addEventBits(unsigned int opcode, unsigned int bitsIn, unsigned int bitsOut)
+ {
+ protocolPartial_.eventCount_[opcode]++;
+ protocolTotal_.eventCount_[opcode]++;
+
+ protocolPartial_.eventBitsIn_[opcode] += bitsIn;
+ protocolTotal_.eventBitsIn_[opcode] += bitsIn;
+
+ protocolPartial_.eventBitsOut_[opcode] += bitsOut;
+ protocolTotal_.eventBitsOut_[opcode] += bitsOut;
+
+ proxyData_.protocolCount_ += bitsOut;
+ }
+
+ void addCupsBits(unsigned int bitsIn, unsigned int bitsOut)
+ {
+ protocolPartial_.cupsCount_++;
+ protocolTotal_.cupsCount_++;
+
+ protocolPartial_.cupsBitsIn_ += bitsIn;
+ protocolTotal_.cupsBitsIn_ += bitsIn;
+
+ protocolPartial_.cupsBitsOut_ += bitsOut;
+ protocolTotal_.cupsBitsOut_ += bitsOut;
+ }
+
+ void addSmbBits(unsigned int bitsIn, unsigned int bitsOut)
+ {
+ protocolPartial_.smbCount_++;
+ protocolTotal_.smbCount_++;
+
+ protocolPartial_.smbBitsIn_ += bitsIn;
+ protocolTotal_.smbBitsIn_ += bitsIn;
+
+ protocolPartial_.smbBitsOut_ += bitsOut;
+ protocolTotal_.smbBitsOut_ += bitsOut;
+ }
+
+ void addMediaBits(unsigned int bitsIn, unsigned int bitsOut)
+ {
+ protocolPartial_.mediaCount_++;
+ protocolTotal_.mediaCount_++;
+
+ protocolPartial_.mediaBitsIn_ += bitsIn;
+ protocolTotal_.mediaBitsIn_ += bitsIn;
+
+ protocolPartial_.mediaBitsOut_ += bitsOut;
+ protocolTotal_.mediaBitsOut_ += bitsOut;
+ }
+
+ void addHttpBits(unsigned int bitsIn, unsigned int bitsOut)
+ {
+ protocolPartial_.httpCount_++;
+ protocolTotal_.httpCount_++;
+
+ protocolPartial_.httpBitsIn_ += bitsIn;
+ protocolTotal_.httpBitsIn_ += bitsIn;
+
+ protocolPartial_.httpBitsOut_ += bitsOut;
+ protocolTotal_.httpBitsOut_ += bitsOut;
+ }
+
+ void addFontBits(unsigned int bitsIn, unsigned int bitsOut)
+ {
+ protocolPartial_.fontCount_++;
+ protocolTotal_.fontCount_++;
+
+ protocolPartial_.fontBitsIn_ += bitsIn;
+ protocolTotal_.fontBitsIn_ += bitsIn;
+
+ protocolPartial_.fontBitsOut_ += bitsOut;
+ protocolTotal_.fontBitsOut_ += bitsOut;
+ }
+
+ void addSlaveBits(unsigned int bitsIn, unsigned int bitsOut)
+ {
+ protocolPartial_.slaveCount_++;
+ protocolTotal_.slaveCount_++;
+
+ protocolPartial_.slaveBitsIn_ += bitsIn;
+ protocolTotal_.slaveBitsIn_ += bitsIn;
+
+ protocolPartial_.slaveBitsOut_ += bitsOut;
+ protocolTotal_.slaveBitsOut_ += bitsOut;
+ }
+
+ void addPackedBytesIn(unsigned int numBytes)
+ {
+ packedPartial_.packedBytesIn_ += numBytes;
+ packedTotal_.packedBytesIn_ += numBytes;
+ }
+
+ void addPackedBytesOut(unsigned int numBytes)
+ {
+ packedPartial_.packedBytesOut_ += numBytes;
+ packedTotal_.packedBytesOut_ += numBytes;
+ }
+
+ void addSplit()
+ {
+ splitPartial_.splitCount_++;
+ splitTotal_.splitCount_++;
+ }
+
+ void addSplitAborted()
+ {
+ splitPartial_.splitAborted_++;
+ splitTotal_.splitAborted_++;
+ }
+
+ void addSplitAbortedBytesOut(unsigned int numBytes)
+ {
+ splitPartial_.splitAbortedBytesOut_ += numBytes;
+ splitTotal_.splitAbortedBytesOut_ += numBytes;
+ }
+
+ //
+ // Add the recorded protocol data to the proxy
+ // token counters. We want to send bpth the token
+ // request message and the data payload using a
+ // single system write, so we must guess how many
+ // output bytes we will generate.
+ //
+
+ void updateControlToken(int &count)
+ {
+ //
+ // Total is the total number of protocol bytes
+ // generated so far. We have saved the number
+ // of bytes generated the last time the function
+ // was called so we can calculate the difference.
+ //
+ // The number of protocol bits is updated as soon
+ // as new bits are accumulated, to avoid summing
+ // up all the opcodes in this routine. We add a
+ // byte to the control bytes difference to account
+ // for the framing bits that are very likely to
+ // be added to the payload.
+ //
+
+ double total = proxyData_.protocolCount_ / 8;
+
+ double diff = total - proxyData_.controlCount_ + 1;
+
+ #if defined(TEST) || defined(TOKEN)
+ *logofs << "Statistics: TOKEN! Protocol bytes are "
+ << total << " control bytes are "
+ << proxyData_.controlCount_ << " difference is "
+ << diff << ".\n" << logofs_flush;
+ #endif
+
+ count += (int) (diff / proxyData_.streamRatio_);
+
+ #if defined(TEST) || defined(TOKEN)
+ *logofs << "Statistics: TOKEN! Adding "
+ << (int) (diff / proxyData_.streamRatio_)
+ << " bytes to the control token with ratio "
+ << proxyData_.streamRatio_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ proxyData_.controlCount_ = total;
+
+ #if defined(TEST) || defined(TOKEN)
+ *logofs << "Statistics: TOKEN! New control token has "
+ << count << " bytes with total control bytes "
+ << proxyData_.controlCount_ << ".\n"
+ << logofs_flush;
+ #endif
+ }
+
+ void updateSplitToken(int &count)
+ {
+ double total = (protocolTotal_.requestBitsOut_[X_NXSplitData] +
+ protocolTotal_.eventBitsOut_[X_NXSplitEvent]) / 8;
+
+ double diff = total - proxyData_.splitCount_;
+
+ #if defined(TEST) || defined(TOKEN)
+ *logofs << "Statistics: TOKEN! Protocol bytes are "
+ << total << " split bytes are "
+ << proxyData_.splitCount_ << " difference is "
+ << diff << ".\n" << logofs_flush;
+ #endif
+
+ count += (int) (diff / proxyData_.streamRatio_);
+
+ #if defined(TEST) || defined(TOKEN)
+ *logofs << "Statistics: TOKEN! Adding "
+ << (int) (diff / proxyData_.streamRatio_)
+ << " bytes to the split token with ratio "
+ << proxyData_.streamRatio_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ proxyData_.splitCount_ = total;
+
+ #if defined(TEST) || defined(TOKEN)
+ *logofs << "Statistics: TOKEN! New split token has "
+ << count << " bytes with total split bytes "
+ << proxyData_.splitCount_ << ".\n"
+ << logofs_flush;
+ #endif
+ }
+
+ void updateDataToken(int &count)
+ {
+ double total = (protocolTotal_.cupsBitsOut_ +
+ protocolTotal_.smbBitsOut_ +
+ protocolTotal_.mediaBitsOut_ +
+ protocolTotal_.httpBitsOut_ +
+ protocolTotal_.fontBitsOut_ +
+ protocolTotal_.slaveBitsOut_) / 8;
+
+ double diff = total - proxyData_.dataCount_;
+
+ #if defined(TEST) || defined(TOKEN)
+ *logofs << "Statistics: TOKEN! Protocol bytes are "
+ << total << " data bytes are "
+ << proxyData_.dataCount_ << " difference is "
+ << diff << ".\n" << logofs_flush;
+ #endif
+
+ count += (int) (diff / proxyData_.streamRatio_);
+
+ #if defined(TEST) || defined(TOKEN)
+ *logofs << "Statistics: TOKEN! Adding "
+ << (int) (diff / proxyData_.streamRatio_)
+ << " bytes to the data token with ratio "
+ << proxyData_.streamRatio_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ proxyData_.dataCount_ = total;
+
+ #if defined(TEST) || defined(TOKEN)
+ *logofs << "Statistics: TOKEN! New data token has "
+ << count << " bytes with total data bytes "
+ << proxyData_.dataCount_ << ".\n"
+ << logofs_flush;
+ #endif
+ }
+
+ //
+ // Update the current bitrate.
+ //
+
+ void updateBitrate(int bytes);
+
+ //
+ // Return the current bitrate.
+ //
+
+ int getBitrateInShortFrame()
+ {
+ return bitrateInShortFrame_;
+ }
+
+ int getBitrateInLongFrame()
+ {
+ return bitrateInLongFrame_;
+ }
+
+ int getTopBitrate()
+ {
+ return topBitrate_;
+ }
+
+ void resetTopBitrate()
+ {
+ topBitrate_ = 0;
+ }
+
+ double getStreamRatio()
+ {
+ return proxyData_.streamRatio_;
+ }
+
+ //
+ // Manage the congestion level.
+ //
+
+ void updateCongestion(int remaining, int limit);
+
+ double getCongestionInFrame()
+ {
+ return congestionInFrame_;
+ }
+
+ //
+ // Produce a dump of the statistics on
+ // the provided buffer.
+ //
+
+ int getClientCacheStats(int type, char *&buffer);
+ int getClientProtocolStats(int type, char *&buffer);
+ int getClientOverallStats(int type, char *&buffer);
+
+ int getServerCacheStats(int type, char *&buffer);
+ int getServerProtocolStats(int type, char *&buffer);
+ int getServerOverallStats(int type, char *&buffer);
+
+ int resetPartialStats();
+
+ private:
+
+ int getTimeStats(int type, char *&buffer);
+ int getServicesStats(int type, char *&buffer);
+ int getFramingStats(int type, char *&buffer);
+ int getBitrateStats(int type, char *&buffer);
+ int getStreamStats(int type, char *&buffer);
+ int getSplitStats(int type, char *&buffer);
+
+ struct T_protocolData
+ {
+ double requestCached_[STATISTICS_OPCODE_MAX];
+ double requestReplied_[STATISTICS_OPCODE_MAX];
+ double requestCount_[STATISTICS_OPCODE_MAX];
+ double requestBitsIn_[STATISTICS_OPCODE_MAX];
+ double requestBitsOut_[STATISTICS_OPCODE_MAX];
+
+ double renderRequestCached_[STATISTICS_OPCODE_MAX];
+ double renderRequestCount_[STATISTICS_OPCODE_MAX];
+ double renderRequestBitsIn_[STATISTICS_OPCODE_MAX];
+ double renderRequestBitsOut_[STATISTICS_OPCODE_MAX];
+
+ double replyCached_[STATISTICS_OPCODE_MAX];
+ double replyCount_[STATISTICS_OPCODE_MAX];
+ double replyBitsIn_[STATISTICS_OPCODE_MAX];
+ double replyBitsOut_[STATISTICS_OPCODE_MAX];
+
+ double eventCached_[STATISTICS_OPCODE_MAX];
+ double eventCount_[STATISTICS_OPCODE_MAX];
+ double eventBitsIn_[STATISTICS_OPCODE_MAX];
+ double eventBitsOut_[STATISTICS_OPCODE_MAX];
+
+ double cupsCount_;
+ double cupsBitsIn_;
+ double cupsBitsOut_;
+
+ double smbCount_;
+ double smbBitsIn_;
+ double smbBitsOut_;
+
+ double mediaCount_;
+ double mediaBitsIn_;
+ double mediaBitsOut_;
+
+ double httpCount_;
+ double httpBitsIn_;
+ double httpBitsOut_;
+
+ double fontCount_;
+ double fontBitsIn_;
+ double fontBitsOut_;
+
+ double slaveCount_;
+ double slaveBitsIn_;
+ double slaveBitsOut_;
+ };
+
+ struct T_transportData
+ {
+ double idleTime_;
+ double readTime_;
+ double writeTime_;
+
+ double proxyBytesIn_;
+ double proxyBytesOut_;
+
+ double proxyFramesIn_;
+ double proxyFramesOut_;
+ double proxyWritesOut_;
+
+ double compressedBytesIn_;
+ double compressedBytesOut_;
+
+ double decompressedBytesIn_;
+ double decompressedBytesOut_;
+
+ double framingBitsOut_;
+ };
+
+ struct T_packedData
+ {
+ double packedBytesIn_;
+ double packedBytesOut_;
+ };
+
+ struct T_splitData
+ {
+ double splitCount_;
+ double splitAborted_;
+ double splitAbortedBytesOut_;
+ };
+
+ struct T_overallData
+ {
+ double overallBytesIn_;
+ double overallBytesOut_;
+ };
+
+ struct T_proxyData
+ {
+ double protocolCount_;
+ double controlCount_;
+ double splitCount_;
+ double dataCount_;
+
+ double streamRatio_;
+ };
+
+ T_protocolData protocolPartial_;
+ T_protocolData protocolTotal_;
+
+ T_transportData transportPartial_;
+ T_transportData transportTotal_;
+
+ T_packedData packedPartial_;
+ T_packedData packedTotal_;
+
+ T_splitData splitPartial_;
+ T_splitData splitTotal_;
+
+ T_overallData overallPartial_;
+ T_overallData overallTotal_;
+
+ T_proxyData proxyData_;
+
+ //
+ // Used to calculate the bandwidth usage
+ // of the proxy link.
+ //
+
+ T_timestamp startShortFrameTs_;
+ T_timestamp startLongFrameTs_;
+ T_timestamp startFrameTs_;
+
+ int bytesInShortFrame_;
+ int bytesInLongFrame_;
+
+ int bitrateInShortFrame_;
+ int bitrateInLongFrame_;
+
+ int topBitrate_;
+
+ double congestionInFrame_;
+
+ //
+ // Need the proxy pointer to print the
+ // statistics related to the client and
+ // server stores and to add the protocol
+ // data to the proxy token accumulators.
+ //
+
+ Proxy *proxy_;
+};
+
+#endif /* Statistics_H */
diff --git a/nxcomp/TextCompressor.cpp b/nxcomp/TextCompressor.cpp
new file mode 100644
index 000000000..16131222c
--- /dev/null
+++ b/nxcomp/TextCompressor.cpp
@@ -0,0 +1,77 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "TextCompressor.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// The compression obtained by this class is
+// very poor. In newer versions the text is
+// simply appended to the encode buffer and
+// compressed by leveraging the final stream
+// compression.
+//
+
+void
+TextCompressor::encodeChar(unsigned char ch, EncodeBuffer& encodeBuffer)
+{
+ // encode each successive character of text using
+ // a predictive model where most of the last 3 characters
+ // (low order 7 bits of the previous character, plus the
+ // low order 5 bits of the character before that, plus
+ // the low order 3 bits of the character before that)
+ // are used to find the right cache...
+
+ CharCache& cache = cache_[key_ % cacheSize_];
+ if ((key_ >= 128) && (cache.getSize() == 0))
+ {
+ // 3rd-order model doesn't have any statistics yet,
+ // so use the 1st-order one instead
+ CharCache& cache2 = cache_[(key_ & 0x7f) % cacheSize_];
+ encodeBuffer.encodeCachedValue((unsigned int) ch, 8, cache2);
+ cache.insert(ch);
+ }
+ else
+ {
+ encodeBuffer.encodeCachedValue((unsigned int) ch, 8, cache);
+ }
+
+ key_ = (((key_ & 0x1f) << 7) | ((key_ & 0x380) << 5) | (ch & 0x7f));
+}
+
+
+unsigned char
+TextCompressor::decodeChar(DecodeBuffer& decodeBuffer)
+{
+ unsigned char nextChar;
+ CharCache& cache = cache_[key_ % cacheSize_];
+ if ((key_ >= 128) && (cache.getSize() == 0))
+ {
+ CharCache& cache2 = cache_[(key_ & 0x7f) % cacheSize_];
+ decodeBuffer.decodeCachedValue(nextChar, 8, cache2);
+ cache.insert(nextChar);
+ }
+ else
+ {
+ decodeBuffer.decodeCachedValue(nextChar, 8, cache);
+ }
+
+ key_ = (((key_ & 0x1f) << 7) | ((key_ & 0x380) << 5) | (nextChar & 0x7f));
+ return nextChar;
+}
diff --git a/nxcomp/TextCompressor.h b/nxcomp/TextCompressor.h
new file mode 100644
index 000000000..b373b98b8
--- /dev/null
+++ b/nxcomp/TextCompressor.h
@@ -0,0 +1,49 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef TextCompressor_H
+#define TextCompressor_H
+
+#include "CharCache.h"
+
+class EncodeBuffer;
+class DecodeBuffer;
+
+class TextCompressor
+{
+ public:
+ TextCompressor(CharCache* cache, unsigned int cacheSize):
+ cache_(cache),
+ cacheSize_(cacheSize),
+ key_(0)
+ {
+ }
+
+ void encodeChar(unsigned char ch, EncodeBuffer &);
+ unsigned char decodeChar(DecodeBuffer &);
+ void reset(unsigned int newKey = 0)
+ {
+ key_ = newKey;
+ }
+
+ private:
+ CharCache* cache_;
+ unsigned int cacheSize_;
+ unsigned int key_;
+};
+
+#endif /* TextCompressor_H */
diff --git a/nxcomp/Timestamp.cpp b/nxcomp/Timestamp.cpp
new file mode 100644
index 000000000..295eb65cf
--- /dev/null
+++ b/nxcomp/Timestamp.cpp
@@ -0,0 +1,65 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "Timestamp.h"
+
+//
+// Log level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Last timestamp taken from the system.
+//
+
+T_timestamp timestamp;
+
+//
+// The following functions all use the ctime
+// static buffer from the C library.
+//
+
+char *strTimestamp(const T_timestamp &ts)
+{
+ char *ctime_now = ctime((time_t *) &ts.tv_sec);
+
+ ctime_now[24] = '\0';
+
+ return ctime_now;
+}
+
+//
+// This is especially dirty.
+//
+
+char *strMsTimestamp(const T_timestamp &ts)
+{
+ char *ctime_now = ctime((time_t *) &ts.tv_sec);
+
+ char ctime_new[25];
+
+ sprintf(ctime_new, "%.8s:%3.3f", ctime_now + 11,
+ (float) ts.tv_usec / 1000);
+
+ strncpy(ctime_now, ctime_new, 24);
+
+ return ctime_now;
+}
diff --git a/nxcomp/Timestamp.h b/nxcomp/Timestamp.h
new file mode 100644
index 000000000..69953988a
--- /dev/null
+++ b/nxcomp/Timestamp.h
@@ -0,0 +1,299 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef Timestamp_H
+#define Timestamp_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <time.h>
+#include <sys/time.h>
+
+#include "Misc.h"
+
+//
+// Log level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// If not defined, always query the system time.
+//
+
+#undef CACHE_TIMESTAMP
+
+//
+// Log a warning if the time difference since
+// the last update exceeds the given number
+// of milliseconds.
+//
+
+#define DRIFT_TIMESTAMP 1
+
+//
+// Type used for timeout manipulation.
+//
+
+typedef struct timeval T_timestamp;
+
+//
+// Last timestamp taken from the system. If the
+// timestamp is cached, we need to explicitly
+// get a new timestamp after any operation that
+// may have required a relevant amount of time.
+//
+
+extern T_timestamp timestamp;
+
+//
+// Get a timestamp instance with values set
+// at the given amount of milliseconds.
+//
+
+inline T_timestamp getTimestamp(long ms)
+{
+ struct timeval ts;
+
+ ts.tv_sec = ms / 1000;
+ ts.tv_usec = (ms % 1000) * 1000;
+
+ return ts;
+}
+
+//
+// Return the difference in milliseconds
+// between the two timestamps.
+//
+
+inline long diffTimestamp(const T_timestamp &ts1, const T_timestamp &ts2)
+{
+ //
+ // Add 500 microseconds to round up
+ // to the nearest millisecond.
+ //
+
+ return ((ts2.tv_sec * 1000 + (ts2.tv_usec + 500) / 1000) -
+ (ts1.tv_sec * 1000 + (ts1.tv_usec + 500) / 1000));
+}
+
+//
+// The same in microseconds. It doesn't
+// round the value.
+//
+
+inline long diffUsTimestamp(const T_timestamp &ts1, const T_timestamp &ts2)
+{
+ return ((ts2.tv_sec * 1000000 + ts2.tv_usec) -
+ (ts1.tv_sec * 1000000 + ts1.tv_usec));
+}
+
+//
+// Return the last timestamp taken from the
+// system. It doesn't update the timestamp.
+//
+
+inline T_timestamp getTimestamp()
+{
+ #ifdef CACHE_TIMESTAMP
+
+ #ifdef TEST
+
+ T_timestamp ts;
+
+ gettimeofday(&ts, NULL);
+
+ long diffTs = diffTimestamp(timestamp, ts);
+
+ if (diffTs > DRIFT_TIMESTAMP)
+ {
+ *logofs << "Timestamp: WARNING! Time difference since the "
+ << "current timestamp is " << diffTs << " Ms.\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ return timestamp;
+
+ #else
+
+ gettimeofday(&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/TranslateCoords.cpp b/nxcomp/TranslateCoords.cpp
new file mode 100644
index 000000000..e67e1dac5
--- /dev/null
+++ b/nxcomp/TranslateCoords.cpp
@@ -0,0 +1,103 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "TranslateCoords.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int TranslateCoordsStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ TranslateCoordsMessage *translateCoords = (TranslateCoordsMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ translateCoords -> src_window = GetULONG(buffer + 4, bigEndian);
+ translateCoords -> dst_window = GetULONG(buffer + 8, bigEndian);
+
+ translateCoords -> src_x = GetUINT(buffer + 12, bigEndian);
+ translateCoords -> src_y = GetUINT(buffer + 14, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int TranslateCoordsStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ TranslateCoordsMessage *translateCoords = (TranslateCoordsMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ PutULONG(translateCoords -> src_window, buffer + 4, bigEndian);
+ PutULONG(translateCoords -> dst_window, buffer + 8, bigEndian);
+
+ PutUINT(translateCoords -> src_x, buffer + 12, bigEndian);
+ PutUINT(translateCoords -> src_y, buffer + 14, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void TranslateCoordsStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ TranslateCoordsMessage *translateCoords = (TranslateCoordsMessage *) message;
+
+ *logofs << name() << ": Identity src_window " << translateCoords -> src_window << ", dst_window "
+ << translateCoords -> dst_window << ", src_x " << translateCoords -> src_x << ", src_y "
+ << translateCoords -> src_y << ", size " << translateCoords -> size_ << ".\n" << logofs_flush;
+
+ #endif
+}
+
+void TranslateCoordsStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ md5_append(md5_state_, buffer + 4, 4);
+ md5_append(md5_state_, buffer + 8, 4);
+ md5_append(md5_state_, buffer + 12, 2);
+ md5_append(md5_state_, buffer + 14, 2);
+}
diff --git a/nxcomp/TranslateCoords.h b/nxcomp/TranslateCoords.h
new file mode 100644
index 000000000..3f21b243c
--- /dev/null
+++ b/nxcomp/TranslateCoords.h
@@ -0,0 +1,177 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef TranslateCoords_H
+#define TranslateCoords_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define TRANSLATECOORDS_ENABLE_CACHE 1
+#define TRANSLATECOORDS_ENABLE_DATA 0
+#define TRANSLATECOORDS_ENABLE_SPLIT 0
+#define TRANSLATECOORDS_ENABLE_COMPRESS 0
+
+#define TRANSLATECOORDS_DATA_LIMIT 0
+#define TRANSLATECOORDS_DATA_OFFSET 16
+
+#define TRANSLATECOORDS_CACHE_SLOTS 3000
+#define TRANSLATECOORDS_CACHE_THRESHOLD 3
+#define TRANSLATECOORDS_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class TranslateCoordsMessage : public Message
+{
+ friend class TranslateCoordsStore;
+
+ public:
+
+ TranslateCoordsMessage()
+ {
+ }
+
+ ~TranslateCoordsMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned int src_window;
+ unsigned int dst_window;
+ unsigned int src_x;
+ unsigned int src_y;
+
+ unsigned char r_same_screen;
+ unsigned int r_child_window;
+ unsigned int r_dst_x;
+ unsigned int r_dst_y;
+};
+
+class TranslateCoordsStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ TranslateCoordsStore() : MessageStore()
+ {
+ enableCache = TRANSLATECOORDS_ENABLE_CACHE;
+ enableData = TRANSLATECOORDS_ENABLE_DATA;
+ enableSplit = TRANSLATECOORDS_ENABLE_SPLIT;
+ enableCompress = TRANSLATECOORDS_ENABLE_COMPRESS;
+
+ dataLimit = TRANSLATECOORDS_DATA_LIMIT;
+ dataOffset = TRANSLATECOORDS_DATA_OFFSET;
+
+ cacheSlots = TRANSLATECOORDS_CACHE_SLOTS;
+ cacheThreshold = TRANSLATECOORDS_CACHE_THRESHOLD;
+ cacheLowerThreshold = TRANSLATECOORDS_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~TranslateCoordsStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "TranslateCoords";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_TranslateCoords;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(TranslateCoordsMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new TranslateCoordsMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new TranslateCoordsMessage((const TranslateCoordsMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (TranslateCoordsMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* TranslateCoords_H */
diff --git a/nxcomp/Transport.cpp b/nxcomp/Transport.cpp
new file mode 100644
index 000000000..4b4967826
--- /dev/null
+++ b/nxcomp/Transport.cpp
@@ -0,0 +1,3056 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/socket.h>
+
+#include "Transport.h"
+
+#include "Statistics.h"
+
+//
+// Set the verbosity level. You also
+// need to define DUMP in Misc.cpp
+// if DUMP is defined here.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef INSPECT
+#undef DUMP
+
+//
+// Used to lock and unlock the transport
+// buffers before they are accessed by
+// different threads.
+//
+
+#undef THREADS
+
+//
+// Define this to get logging all the
+// operations performed by the parent
+// thread, the one that enqueues and
+// dequeues data.
+//
+
+#define PARENT
+
+//
+// Define this to know when a channel
+// is created or destroyed.
+//
+
+#undef REFERENCES
+
+//
+// Reference count for allocated buffers.
+//
+
+#ifdef REFERENCES
+
+int Transport::references_;
+int ProxyTransport::references_;
+int InternalTransport::references_;
+
+#endif
+
+//
+// This is the base class providing methods for read
+// and write buffering.
+//
+
+Transport::Transport(int fd) : fd_(fd)
+{
+ #ifdef TEST
+ *logofs << "Transport: Going to create base transport "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ type_ = transport_base;
+
+ //
+ // Set up the write buffer.
+ //
+
+ w_buffer_.length_ = 0;
+ w_buffer_.start_ = 0;
+
+ initialSize_ = TRANSPORT_BUFFER_DEFAULT_SIZE;
+ thresholdSize_ = TRANSPORT_BUFFER_DEFAULT_SIZE << 1;
+ maximumSize_ = TRANSPORT_BUFFER_DEFAULT_SIZE << 4;
+
+ w_buffer_.data_.resize(initialSize_);
+
+ //
+ // Set non-blocking IO on socket.
+ //
+
+ SetNonBlocking(fd_, 1);
+
+ blocked_ = 0;
+ finish_ = 0;
+
+ #ifdef REFERENCES
+ *logofs << "Transport: Created new object at "
+ << this << " out of " << ++references_
+ << " allocated references.\n" << logofs_flush;
+ #endif
+}
+
+Transport::~Transport()
+{
+ #ifdef TEST
+ *logofs << "Transport: Going to destroy base class "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ ::close(fd_);
+
+ #ifdef REFERENCES
+ *logofs << "Transport: Deleted object at "
+ << this << " out of " << --references_
+ << " allocated references.\n" << logofs_flush;
+ #endif
+}
+
+//
+// Read data from its file descriptor.
+//
+
+int Transport::read(unsigned char *data, unsigned int size)
+{
+ #ifdef DEBUG
+ *logofs << "Transport: Going to read " << size << " bytes from "
+ << "FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Read the available data from the socket.
+ //
+
+ int result = ::read(fd_, data, size);
+
+ //
+ // Update the current timestamp as the read
+ // can have scheduled some other process.
+ //
+
+ getNewTimestamp();
+
+ if (result < 0)
+ {
+ if (EGET() == EAGAIN)
+ {
+ #ifdef TEST
+ *logofs << "Transport: WARNING! Read of " << size << " bytes from "
+ << "FD#" << fd_ << " would block.\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else if (EGET() == EINTR)
+ {
+ #ifdef TEST
+ *logofs << "Transport: Read of " << size << " bytes from "
+ << "FD#" << fd_ << " was interrupted.\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Transport: Error reading from "
+ << "FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ finish();
+
+ return -1;
+ }
+ }
+ else if (result == 0)
+ {
+ #ifdef TEST
+ *logofs << "Transport: No data read from "
+ << "FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ finish();
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "Transport: Read " << result << " bytes out of "
+ << size << " from FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef DUMP
+
+ *logofs << "Transport: Dumping content of read data.\n"
+ << logofs_flush;
+
+ DumpData(data, result);
+
+ #endif
+
+ return result;
+}
+
+//
+// Write as many bytes as possible to socket.
+// Append the remaining data bytes to the end
+// of the buffer and update length to reflect
+// changes.
+//
+
+int Transport::write(T_write type, const unsigned char *data, const unsigned int size)
+{
+ //
+ // If an immediate write was requested then
+ // flush the enqueued data first.
+ //
+ // Alternatively may try to write only if
+ // the socket is not blocked.
+ //
+ // if (w_buffer_.length_ > 0 && blocked_ == 0 &&
+ // type == write_immediate)
+ // {
+ // ...
+ // }
+ //
+
+ if (w_buffer_.length_ > 0 && type == write_immediate)
+
+ {
+ #ifdef TEST
+ *logofs << "Transport: Writing " << w_buffer_.length_
+ << " bytes of previous data to FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ int result = Transport::flush();
+
+ if (result < 0)
+ {
+ return -1;
+ }
+ }
+
+ //
+ // If nothing is remained, write immediately
+ // to the socket.
+ //
+
+ unsigned int written = 0;
+
+ if (w_buffer_.length_ == 0 && blocked_ == 0 &&
+ type == write_immediate)
+ {
+ //
+ // Limit the amount of data sent.
+ //
+
+ unsigned int toWrite = size;
+
+ #ifdef DUMP
+
+ *logofs << "Transport: Going to write " << toWrite
+ << " bytes to FD#" << fd_ << " with checksum ";
+
+ DumpChecksum(data, size);
+
+ *logofs << ".\n" << logofs_flush;
+
+ #endif
+
+ T_timestamp writeTs;
+
+ int diffTs;
+
+ while (written < toWrite)
+ {
+ //
+ // Trace system time spent writing data.
+ //
+
+ writeTs = getTimestamp();
+
+ int result = ::write(fd_, data + written, toWrite - written);
+
+ diffTs = diffTimestamp(writeTs, getNewTimestamp());
+
+ statistics -> addWriteTime(diffTs);
+
+ if (result <= 0)
+ {
+ if (EGET() == EAGAIN)
+ {
+ #ifdef TEST
+ *logofs << "Transport: Write of " << toWrite - written
+ << " bytes on FD#" << fd_ << " would block.\n"
+ << logofs_flush;
+ #endif
+
+ blocked_ = 1;
+
+ break;
+ }
+ else if (EGET() == EINTR)
+ {
+ #ifdef TEST
+ *logofs << "Transport: Write of " << toWrite - written
+ << " bytes on FD#" << fd_ << " was interrupted.\n"
+ << logofs_flush;
+ #endif
+
+ continue;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Transport: Write to " << "FD#"
+ << fd_ << " failed.\n" << logofs_flush;
+ #endif
+
+ finish();
+
+ return -1;
+ }
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Transport: Immediately written " << result
+ << " bytes on " << "FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ written += result;
+ }
+ }
+
+ #ifdef DUMP
+
+ if (written > 0)
+ {
+ *logofs << "Transport: Dumping content of immediately written data.\n"
+ << logofs_flush;
+
+ DumpData(data, written);
+ }
+
+ #endif
+ }
+
+ if (written == size)
+ {
+ //
+ // We will not affect the write buffer.
+ //
+
+ return written;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Transport: Going to append " << size - written
+ << " bytes to write buffer for " << "FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (resize(w_buffer_, size - written) < 0)
+ {
+ return -1;
+ }
+
+ memmove(w_buffer_.data_.begin() + w_buffer_.start_ + w_buffer_.length_,
+ data + written, size - written);
+
+ w_buffer_.length_ += size - written;
+
+ #ifdef TEST
+ *logofs << "Transport: Write buffer for FD#" << fd_
+ << " has data for " << w_buffer_.length_ << " bytes.\n"
+ << logofs_flush;
+
+ *logofs << "Transport: Start is " << w_buffer_.start_
+ << " length is " << w_buffer_.length_ << " size is "
+ << w_buffer_.data_.size() << " capacity is "
+ << w_buffer_.data_.capacity() << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Note that this function always returns the whole
+ // size of buffer that was provided, either if not
+ // all the data could be actually written.
+ //
+
+ return size;
+}
+
+//
+// Write pending data to its file descriptor.
+//
+
+int Transport::flush()
+{
+ if (w_buffer_.length_ == 0)
+ {
+ #ifdef TEST
+ *logofs << "Transport: No data to flush on "
+ << "FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef WARNING
+ if (blocked_ != 0)
+ {
+ *logofs << "Transport: Blocked flag is " << blocked_
+ << " with no data to flush on FD#" << fd_
+ << ".\n" << logofs_flush;
+ }
+ #endif
+
+ return 0;
+ }
+
+ //
+ // It's time to move data from the
+ // write buffer to the real link.
+ //
+
+ int written = 0;
+
+ int toWrite = w_buffer_.length_;
+
+ //
+ // We will do our best to write any available
+ // data to the socket, so let's say we start
+ // from a clean state.
+ //
+
+ blocked_ = 0;
+
+ #ifdef TEST
+ *logofs << "Transport: Going to flush " << toWrite
+ << " bytes on FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ T_timestamp writeTs;
+
+ int diffTs;
+
+ while (written < toWrite)
+ {
+ writeTs = getTimestamp();
+
+ int result = ::write(fd_, w_buffer_.data_.begin() + w_buffer_.start_ +
+ written, toWrite - written);
+
+ diffTs = diffTimestamp(writeTs, getNewTimestamp());
+
+ statistics -> addWriteTime(diffTs);
+
+ if (result <= 0)
+ {
+ if (EGET() == EAGAIN)
+ {
+ #ifdef TEST
+ *logofs << "Transport: Write of " << toWrite - written
+ << " bytes on FD#" << fd_ << " would block.\n"
+ << logofs_flush;
+ #endif
+
+ blocked_ = 1;
+
+ break;
+ }
+ else if (EGET() == EINTR)
+ {
+ #ifdef TEST
+ *logofs << "Transport: Write of " << toWrite - written
+ << " bytes on FD#" << fd_ << " was interrupted.\n"
+ << logofs_flush;
+ #endif
+
+ continue;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Transport: Write to " << "FD#"
+ << fd_ << " failed.\n" << logofs_flush;
+ #endif
+
+ finish();
+
+ return -1;
+ }
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Transport: Flushed " << result << " bytes on "
+ << "FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ written += result;
+ }
+ }
+
+ if (written > 0)
+ {
+ #ifdef DUMP
+
+ *logofs << "Transport: Dumping content of flushed data.\n"
+ << logofs_flush;
+
+ DumpData(w_buffer_.data_.begin() + w_buffer_.start_, written);
+
+ #endif
+
+ //
+ // Update the buffer status.
+ //
+
+ w_buffer_.length_ -= written;
+
+ if (w_buffer_.length_ == 0)
+ {
+ w_buffer_.start_ = 0;
+ }
+ else
+ {
+ w_buffer_.start_ += written;
+ }
+ }
+
+ //
+ // It can be that we wrote less bytes than
+ // available because of the write limit.
+ //
+
+ if (w_buffer_.length_ > 0)
+ {
+ #ifdef TEST
+ *logofs << "Transport: There are still " << w_buffer_.length_
+ << " bytes in write buffer for " << "FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ blocked_ = 1;
+ }
+
+ #ifdef TEST
+ *logofs << "Transport: Write buffer for FD#" << fd_
+ << " has data for " << w_buffer_.length_ << " bytes.\n"
+ << logofs_flush;
+
+ *logofs << "Transport: Start is " << w_buffer_.start_
+ << " length is " << w_buffer_.length_ << " size is "
+ << w_buffer_.data_.size() << " capacity is "
+ << w_buffer_.data_.capacity() << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // No new data was produced for the link except
+ // any outstanding data from previous writes.
+ //
+
+ return 0;
+}
+
+int Transport::drain(int limit, int timeout)
+{
+ if (w_buffer_.length_ <= limit)
+ {
+ return 1;
+ }
+
+ //
+ // Write the data accumulated in the write
+ // buffer until it is below the limit or
+ // the timeout is elapsed.
+ //
+
+ int toWrite = w_buffer_.length_;
+
+ int written = 0;
+
+ #ifdef TEST
+ *logofs << "Transport: Draining " << toWrite - limit
+ << " bytes on FD#" << fd_ << " with limit set to "
+ << limit << ".\n" << logofs_flush;
+ #endif
+
+ T_timestamp startTs = getNewTimestamp();
+
+ T_timestamp selectTs;
+ T_timestamp writeTs;
+ T_timestamp idleTs;
+
+ T_timestamp nowTs = startTs;
+
+ int diffTs;
+
+ fd_set writeSet;
+ fd_set readSet;
+
+ FD_ZERO(&writeSet);
+ FD_ZERO(&readSet);
+
+ int result;
+ int ready;
+
+ while (w_buffer_.length_ - written > limit)
+ {
+ nowTs = getNewTimestamp();
+
+ //
+ // Wait for descriptor to become
+ // readable or writable.
+ //
+
+ FD_SET(fd_, &writeSet);
+ FD_SET(fd_, &readSet);
+
+ setTimestamp(selectTs, timeout / 2);
+
+ idleTs = nowTs;
+
+ result = select(fd_ + 1, &readSet, &writeSet, NULL, &selectTs);
+
+ nowTs = getNewTimestamp();
+
+ diffTs = diffTimestamp(idleTs, nowTs);
+
+ statistics -> addIdleTime(diffTs);
+
+ statistics -> subReadTime(diffTs);
+
+ if (result < 0)
+ {
+ if (EGET() == EINTR)
+ {
+ #ifdef TEST
+ *logofs << "Transport: Select on FD#" << fd_
+ << " was interrupted.\n" << logofs_flush;
+ #endif
+
+ continue;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Transport: Select on FD#" << fd_
+ << " failed.\n" << logofs_flush;
+ #endif
+
+ finish();
+
+ return -1;
+ }
+ }
+ else if (result > 0)
+ {
+ ready = result;
+
+ if (FD_ISSET(fd_, &writeSet))
+ {
+ writeTs = getNewTimestamp();
+
+ result = ::write(fd_, w_buffer_.data_.begin() + w_buffer_.start_ +
+ written, toWrite - written);
+
+ nowTs = getNewTimestamp();
+
+ diffTs = diffTimestamp(writeTs, nowTs);
+
+ statistics -> addWriteTime(diffTs);
+
+ if (result > 0)
+ {
+ #ifdef TEST
+ *logofs << "Transport: Forced flush of " << result
+ << " bytes on " << "FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ written += result;
+ }
+ else if (result < 0 && EGET() == EINTR)
+ {
+ #ifdef TEST
+ *logofs << "Transport: Write to FD#" << fd_
+ << " was interrupted.\n" << logofs_flush;
+ #endif
+
+ continue;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Transport: Write to FD#" << fd_
+ << " failed.\n" << logofs_flush;
+ #endif
+
+ finish();
+
+ return -1;
+ }
+
+ ready--;
+ }
+
+ if (ready > 0)
+ {
+ if (FD_ISSET(fd_, &readSet))
+ {
+ #ifdef TEST
+ *logofs << "Transport: Not draining further "
+ << "due to data readable on FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ break;
+ }
+ }
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Transport: Timeout encountered "
+ << "waiting for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ }
+ #endif
+
+ nowTs = getNewTimestamp();
+
+ diffTs = diffTimestamp(startTs, nowTs);
+
+ if (diffTs >= timeout)
+ {
+ #ifdef TEST
+ *logofs << "Transport: Not draining further "
+ << "due to the timeout on FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ break;
+ }
+ }
+
+ if (written > 0)
+ {
+ #ifdef DUMP
+
+ *logofs << "Transport: Dumping content of flushed data.\n"
+ << logofs_flush;
+
+ DumpData(w_buffer_.data_.begin() + w_buffer_.start_, written);
+
+ #endif
+
+ //
+ // Update the buffer status.
+ //
+
+ w_buffer_.length_ -= written;
+
+ if (w_buffer_.length_ == 0)
+ {
+ w_buffer_.start_ = 0;
+
+ blocked_ = 0;
+ }
+ else
+ {
+ w_buffer_.start_ += written;
+
+ #ifdef TEST
+ *logofs << "Transport: There are still " << w_buffer_.length_
+ << " bytes in write buffer for " << "FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ blocked_ = 1;
+ }
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Transport: WARNING! No data written to FD#" << fd_
+ << " with " << toWrite << " bytes to drain and limit "
+ << "set to " << limit << ".\n" << logofs_flush;
+ }
+ #endif
+
+ #ifdef TEST
+ *logofs << "Transport: Write buffer for FD#" << fd_
+ << " has data for " << w_buffer_.length_ << " bytes.\n"
+ << logofs_flush;
+
+ *logofs << "Transport: Start is " << w_buffer_.start_
+ << " length is " << w_buffer_.length_ << " size is "
+ << w_buffer_.data_.size() << " capacity is "
+ << w_buffer_.data_.capacity() << ".\n"
+ << logofs_flush;
+ #endif
+
+ return (w_buffer_.length_ <= limit);
+}
+
+int Transport::wait(int timeout) const
+{
+ T_timestamp startTs = getNewTimestamp();
+
+ T_timestamp idleTs;
+ T_timestamp selectTs;
+
+ T_timestamp nowTs = startTs;
+
+ long available = 0;
+ int result = 0;
+
+ int diffTs;
+
+ fd_set readSet;
+
+ FD_ZERO(&readSet);
+ FD_SET(fd_, &readSet);
+
+ for (;;)
+ {
+ available = readable();
+
+ diffTs = diffTimestamp(startTs, nowTs);
+
+ if (available != 0 || timeout == 0 ||
+ (diffTs + (timeout / 10)) >= timeout)
+ {
+ #ifdef TEST
+ *logofs << "Transport: There are " << available
+ << " bytes on FD#" << fd_ << " after "
+ << diffTs << " Ms.\n" << logofs_flush;
+ #endif
+
+ return available;
+ }
+ else if (available == 0 && result > 0)
+ {
+ #ifdef TEST
+ *logofs << "Transport: Read on " << "FD#"
+ << fd_ << " failed.\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ //
+ // TODO: Should subtract the time
+ // already spent in select.
+ //
+
+ selectTs.tv_sec = 0;
+ selectTs.tv_usec = timeout * 1000;
+
+ idleTs = nowTs;
+
+ //
+ // Wait for descriptor to become readable.
+ //
+
+ result = select(fd_ + 1, &readSet, NULL, NULL, &selectTs);
+
+ nowTs = getNewTimestamp();
+
+ diffTs = diffTimestamp(idleTs, nowTs);
+
+ statistics -> addIdleTime(diffTs);
+
+ statistics -> subReadTime(diffTs);
+
+ if (result < 0)
+ {
+ if (EGET() == EINTR)
+ {
+ #ifdef TEST
+ *logofs << "Transport: Select on FD#" << fd_
+ << " was interrupted.\n" << logofs_flush;
+ #endif
+
+ continue;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Transport: Select on " << "FD#"
+ << fd_ << " failed.\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ }
+ #ifdef TEST
+ else if (result == 0)
+ {
+ *logofs << "Transport: No data available on FD#" << fd_
+ << " after " << diffTimestamp(startTs, nowTs)
+ << " Ms.\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "Transport: Data became available on FD#" << fd_
+ << " after " << diffTimestamp(startTs, nowTs)
+ << " Ms.\n" << logofs_flush;
+ }
+ #endif
+ }
+}
+
+void Transport::setSize(unsigned int initialSize, unsigned int thresholdSize,
+ unsigned int maximumSize)
+{
+ initialSize_ = initialSize;
+ thresholdSize_ = thresholdSize;
+ maximumSize_ = maximumSize;
+
+ #ifdef TEST
+ *logofs << "Transport: Set buffer sizes for FD#" << fd_
+ << " to " << initialSize_ << "/" << thresholdSize_
+ << "/" << maximumSize_ << ".\n" << logofs_flush;
+ #endif
+}
+
+void Transport::fullReset()
+{
+ blocked_ = 0;
+ finish_ = 0;
+
+ fullReset(w_buffer_);
+}
+
+int Transport::resize(T_buffer &buffer, const int &size)
+{
+ if ((int) buffer.data_.size() >= (buffer.length_ + size) &&
+ (buffer.start_ + buffer.length_ + size) >
+ (int) buffer.data_.size())
+ {
+ if (buffer.length_ > 0)
+ {
+ //
+ // There is enough space in buffer but we need
+ // to move existing data at the beginning.
+ //
+
+ #ifdef TEST
+ *logofs << "Transport: Moving " << buffer.length_
+ << " bytes of data for " << "FD#" << fd_
+ << " to make room in the buffer.\n"
+ << logofs_flush;
+ #endif
+
+ memmove(buffer.data_.begin(), buffer.data_.begin() +
+ buffer.start_, buffer.length_);
+ }
+
+ buffer.start_ = 0;
+
+ #ifdef DEBUG
+ *logofs << "Transport: Made room for "
+ << buffer.data_.size() - buffer.start_
+ << " bytes in buffer for " << "FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+ }
+ else if ((buffer.length_ + size) > (int) buffer.data_.size())
+ {
+ //
+ // Not enough space, so increase
+ // the size of the buffer.
+ //
+
+ if (buffer.start_ != 0 && buffer.length_ > 0)
+ {
+ #ifdef TEST
+ *logofs << "Transport: Moving " << buffer.length_
+ << " bytes of data for " << "FD#" << fd_
+ << " to resize the buffer.\n"
+ << logofs_flush;
+ #endif
+
+ memmove(buffer.data_.begin(), buffer.data_.begin() +
+ buffer.start_, buffer.length_);
+ }
+
+ buffer.start_ = 0;
+
+ unsigned int newSize = thresholdSize_;
+
+ while (newSize < (unsigned int) buffer.length_ + size)
+ {
+ newSize <<= 1;
+
+ if (newSize >= maximumSize_)
+ {
+ newSize = buffer.length_ + size + initialSize_;
+ }
+ }
+
+ #ifdef DEBUG
+ *logofs << "Transport: Buffer for " << "FD#" << fd_
+ << " will be enlarged from " << buffer.data_.size()
+ << " to at least " << buffer.length_ + size
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ buffer.data_.resize(newSize);
+
+ #ifdef TEST
+ if (newSize >= maximumSize_)
+ {
+ *logofs << "Transport: WARNING! Buffer for FD#" << fd_
+ << " grown to reach size of " << newSize
+ << " bytes.\n" << logofs_flush;
+ }
+ #endif
+
+ #ifdef TEST
+ *logofs << "Transport: Data buffer for " << "FD#"
+ << fd_ << " has now size " << buffer.data_.size()
+ << " and capacity " << buffer.data_.capacity()
+ << ".\n" << logofs_flush;
+ #endif
+ }
+
+ return (buffer.length_ + size);
+}
+
+void Transport::fullReset(T_buffer &buffer)
+{
+ //
+ // Force deallocation and allocation
+ // of the initial size.
+ //
+
+ #ifdef TEST
+ *logofs << "Transport: Resetting buffer for " << "FD#"
+ << fd_ << " with size " << buffer.data_.size()
+ << " and capacity " << buffer.data_.capacity()
+ << ".\n" << logofs_flush;
+ #endif
+
+ buffer.start_ = 0;
+ buffer.length_ = 0;
+
+ if (buffer.data_.size() > (unsigned int) initialSize_ &&
+ buffer.data_.capacity() > (unsigned int) initialSize_)
+ {
+ buffer.data_.clear();
+
+ buffer.data_.resize(initialSize_);
+
+ #ifdef TEST
+ *logofs << "Transport: Data buffer for " << "FD#"
+ << fd_ << " shrunk to size " << buffer.data_.size()
+ << " and capacity " << buffer.data_.capacity()
+ << ".\n" << logofs_flush;
+ #endif
+ }
+}
+
+ProxyTransport::ProxyTransport(int fd) : Transport(fd)
+{
+ #ifdef TEST
+ *logofs << "ProxyTransport: Going to create proxy transport "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ type_ = transport_proxy;
+
+ //
+ // Set up the read buffer.
+ //
+
+ r_buffer_.length_ = 0;
+ r_buffer_.start_ = 0;
+
+ r_buffer_.data_.resize(initialSize_);
+
+ //
+ // For now we own the buffer.
+ //
+
+ owner_ = 1;
+
+ //
+ // Set up ZLIB compression.
+ //
+
+ int result;
+
+ r_stream_.zalloc = NULL;
+ r_stream_.zfree = NULL;
+ r_stream_.opaque = NULL;
+
+ r_stream_.next_in = NULL;
+ r_stream_.avail_in = 0;
+
+ if ((result = inflateInit2(&r_stream_, 15)) != Z_OK)
+ {
+ #ifdef PANIC
+ *logofs << "ProxyTransport: PANIC! Failed initialization of ZLIB read stream. "
+ << "Error is '" << zError(result) << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed initialization of ZLIB read stream. "
+ << "Error is '" << zError(result) << "'.\n";
+
+ HandleCleanup();
+ }
+
+ if (control -> LocalStreamCompression)
+ {
+ w_stream_.zalloc = NULL;
+ w_stream_.zfree = NULL;
+ w_stream_.opaque = NULL;
+
+ if ((result = deflateInit2(&w_stream_, control -> LocalStreamCompressionLevel, Z_DEFLATED,
+ 15, 9, Z_DEFAULT_STRATEGY)) != Z_OK)
+ {
+ #ifdef PANIC
+ *logofs << "ProxyTransport: PANIC! Failed initialization of ZLIB write stream. "
+ << "Error is '" << zError(result) << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed initialization of ZLIB write stream. "
+ << "Error is '" << zError(result) << "'.\n";
+
+ HandleCleanup();
+ }
+ }
+
+ //
+ // No ZLIB stream to flush yet.
+ //
+
+ flush_ = 0;
+
+ #ifdef REFERENCES
+ *logofs << "ProxyTransport: Created new object at "
+ << this << " out of " << ++references_
+ << " allocated references.\n" << logofs_flush;
+ #endif
+}
+
+ProxyTransport::~ProxyTransport()
+{
+ #ifdef TEST
+ *logofs << "ProxyTransport: Going to destroy derived class "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Deallocate the ZLIB stream state.
+ //
+
+ inflateEnd(&r_stream_);
+
+ if (control -> LocalStreamCompression)
+ {
+ deflateEnd(&w_stream_);
+ }
+
+ #ifdef REFERENCES
+ *logofs << "ProxyTransport: Deleted object at "
+ << this << " out of " << --references_
+ << " allocated references.\n" << logofs_flush;
+ #endif
+}
+
+//
+// Read data from its file descriptor.
+//
+
+int ProxyTransport::read(unsigned char *data, unsigned int size)
+{
+ //
+ // If the remote peer is not compressing
+ // the stream then just return any byte
+ // read from the socket.
+ //
+
+ if (control -> RemoteStreamCompression == 0)
+ {
+ int result = Transport::read(data, size);
+
+ if (result <= 0)
+ {
+ return result;
+ }
+
+ statistics -> addBytesIn(result);
+
+ return result;
+ }
+
+ //
+ // Return any pending data first.
+ //
+
+ if (r_buffer_.length_ > 0)
+ {
+ //
+ // If the size of the buffer doesn't
+ // match the amount of data pending,
+ // force the caller to retry.
+ //
+
+ if ((int) size < r_buffer_.length_)
+ {
+ #ifdef TEST
+ *logofs << "ProxyTransport: WARNING! Forcing a retry with "
+ << r_buffer_.length_ << " bytes pending and "
+ << size << " in the buffer.\n"
+ << logofs_flush;
+ #endif
+
+ ESET(EAGAIN);
+
+ return -1;
+ }
+
+ int copied = (r_buffer_.length_ > ((int) size) ?
+ ((int) size) : r_buffer_.length_);
+
+ memcpy(data, r_buffer_.data_.begin() + r_buffer_.start_, copied);
+
+ //
+ // Update the buffer status.
+ //
+
+ #ifdef DEBUG
+ *logofs << "ProxyTransport: Going to immediately return " << copied
+ << " bytes from proxy FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ r_buffer_.length_ -= copied;
+
+ if (r_buffer_.length_ == 0)
+ {
+ r_buffer_.start_ = 0;
+ }
+ else
+ {
+ r_buffer_.start_ += copied;
+
+ #ifdef TEST
+ *logofs << "ProxyTransport: There are still " << r_buffer_.length_
+ << " bytes in read buffer for proxy " << "FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+ }
+
+ return copied;
+ }
+
+ //
+ // Read data in the user buffer.
+ //
+
+ int result = Transport::read(data, size);
+
+ if (result <= 0)
+ {
+ return result;
+ }
+
+ statistics -> addBytesIn(result);
+
+ //
+ // Decompress the data into the read
+ // buffer.
+ //
+
+ #ifdef DEBUG
+ *logofs << "ProxyTransport: Going to decompress data for "
+ << "proxy FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ int saveTotalIn = r_stream_.total_in;
+ int saveTotalOut = r_stream_.total_out;
+
+ int oldTotalIn = saveTotalIn;
+ int oldTotalOut = saveTotalOut;
+
+ int diffTotalIn;
+ int diffTotalOut;
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: oldTotalIn = " << oldTotalIn
+ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: oldTotalOut = " << oldTotalOut
+ << ".\n" << logofs_flush;
+ #endif
+
+ r_stream_.next_in = (Bytef *) data;
+ r_stream_.avail_in = result;
+
+ //
+ // Let ZLIB use all the space already
+ // available in the buffer.
+ //
+
+ unsigned int newAvailOut = r_buffer_.data_.size() - r_buffer_.start_ -
+ r_buffer_.length_;
+
+ #ifdef TEST
+ *logofs << "ProxyTransport: Initial decompress buffer is "
+ << newAvailOut << " bytes.\n" << logofs_flush;
+ #endif
+
+ for (;;)
+ {
+ #ifdef INSPECT
+ *logofs << "\nProxyTransport: Running the decompress loop.\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: r_buffer_.length_ = " << r_buffer_.length_
+ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: r_buffer_.data_.size() = " << r_buffer_.data_.size()
+ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: newAvailOut = " << newAvailOut
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (resize(r_buffer_, newAvailOut) < 0)
+ {
+ return -1;
+ }
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: r_buffer_.data_.size() = "
+ << r_buffer_.data_.size() << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: r_stream_.next_in = "
+ << (void *) r_stream_.next_in << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: r_stream_.avail_in = "
+ << r_stream_.avail_in << ".\n"
+ << logofs_flush;
+ #endif
+
+ r_stream_.next_out = r_buffer_.data_.begin() + r_buffer_.start_ +
+ r_buffer_.length_;
+
+ r_stream_.avail_out = newAvailOut;
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: r_stream_.next_out = "
+ << (void *) r_stream_.next_out << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: r_stream_.avail_out = "
+ << r_stream_.avail_out << ".\n"
+ << logofs_flush;
+ #endif
+
+ int result = inflate(&r_stream_, Z_SYNC_FLUSH);
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: Called inflate() result is "
+ << result << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: r_stream_.avail_in = "
+ << r_stream_.avail_in << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: r_stream_.avail_out = "
+ << r_stream_.avail_out << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: r_stream_.total_in = "
+ << r_stream_.total_in << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: r_stream_.total_out = "
+ << r_stream_.total_out << ".\n"
+ << logofs_flush;
+ #endif
+
+ diffTotalIn = r_stream_.total_in - oldTotalIn;
+ diffTotalOut = r_stream_.total_out - oldTotalOut;
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: diffTotalIn = "
+ << diffTotalIn << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: diffTotalOut = "
+ << diffTotalOut << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: r_buffer_.length_ = "
+ << r_buffer_.length_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ r_buffer_.length_ += diffTotalOut;
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: r_buffer_.length_ = "
+ << r_buffer_.length_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ oldTotalIn = r_stream_.total_in;
+ oldTotalOut = r_stream_.total_out;
+
+ if (result == Z_OK)
+ {
+ if (r_stream_.avail_in == 0 && r_stream_.avail_out > 0)
+ {
+ break;
+ }
+ }
+ else if (result == Z_BUF_ERROR && r_stream_.avail_out > 0 &&
+ r_stream_.avail_in == 0)
+ {
+ #ifdef TEST
+ *logofs << "ProxyTransport: WARNING! Raised Z_BUF_ERROR decompressing data.\n"
+ << logofs_flush;
+ #endif
+
+ break;
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "ProxyTransport: PANIC! Decompression of data failed. "
+ << "Error is '" << zError(result) << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Decompression of data failed. Error is '"
+ << zError(result) << "'.\n";
+
+ finish();
+
+ return -1;
+ }
+
+ //
+ // Add more bytes to the buffer.
+ //
+
+ if (newAvailOut < thresholdSize_)
+ {
+ newAvailOut = thresholdSize_;
+ }
+
+ #ifdef TEST
+ *logofs << "ProxyTransport: Need to add " << newAvailOut
+ << " bytes to the decompress buffer in read.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ diffTotalIn = r_stream_.total_in - saveTotalIn;
+ diffTotalOut = r_stream_.total_out - saveTotalOut;
+
+ #ifdef DEBUG
+ *logofs << "ProxyTransport: Decompressed data from "
+ << diffTotalIn << " to " << diffTotalOut
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ statistics -> addDecompressedBytes(diffTotalIn, diffTotalOut);
+
+ //
+ // Check if the size of the buffer
+ // matches the produced data.
+ //
+
+ if ((int) size < r_buffer_.length_)
+ {
+ #ifdef TEST
+ *logofs << "ProxyTransport: WARNING! Forcing a retry with "
+ << r_buffer_.length_ << " bytes pending and "
+ << size << " in the buffer.\n"
+ << logofs_flush;
+ #endif
+
+ ESET(EAGAIN);
+
+ return -1;
+ }
+
+ //
+ // Copy the decompressed data to the
+ // provided buffer.
+ //
+
+ int copied = (r_buffer_.length_ > ((int) size) ?
+ ((int) size) : r_buffer_.length_);
+
+ #ifdef DEBUG
+ *logofs << "ProxyTransport: Going to return " << copied
+ << " bytes from proxy FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ memcpy(data, r_buffer_.data_.begin() + r_buffer_.start_, copied);
+
+ //
+ // Update the buffer status.
+ //
+
+ r_buffer_.length_ -= copied;
+
+ if (r_buffer_.length_ == 0)
+ {
+ r_buffer_.start_ = 0;
+ }
+ else
+ {
+ r_buffer_.start_ += copied;
+
+ #ifdef TEST
+ *logofs << "ProxyTransport: There are still " << r_buffer_.length_
+ << " bytes in read buffer for proxy FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+ }
+
+ return copied;
+}
+
+//
+// If required compress data, else write it to socket.
+//
+
+int ProxyTransport::write(T_write type, const unsigned char *data, const unsigned int size)
+{
+ #ifdef TEST
+ if (size == 0)
+ {
+ *logofs << "ProxyTransport: WARNING! Write called for FD#"
+ << fd_ << " without any data to write.\n"
+ << logofs_flush;
+
+ return 0;
+ }
+ #endif
+
+ //
+ // If there is no compression revert to
+ // plain socket management.
+ //
+
+ if (control -> LocalStreamCompression == 0)
+ {
+ int result = Transport::write(type, data, size);
+
+ if (result <= 0)
+ {
+ return result;
+ }
+
+ statistics -> addBytesOut(result);
+
+ statistics -> updateBitrate(result);
+
+ FlushCallback(result);
+
+ return result;
+ }
+
+ #ifdef DEBUG
+ *logofs << "ProxyTransport: Going to compress " << size
+ << " bytes to write buffer for proxy FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Compress data into the write buffer.
+ //
+
+ int saveTotalIn = w_stream_.total_in;
+ int saveTotalOut = w_stream_.total_out;
+
+ int oldTotalIn = saveTotalIn;
+ int oldTotalOut = saveTotalOut;
+
+ int diffTotalIn;
+ int diffTotalOut;
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: oldTotalIn = " << oldTotalIn
+ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: oldTotalOut = " << oldTotalOut
+ << ".\n" << logofs_flush;
+ #endif
+
+ w_stream_.next_in = (Bytef *) data;
+ w_stream_.avail_in = size;
+
+ //
+ // Let ZLIB use all the space already
+ // available in the buffer.
+ //
+
+ unsigned int newAvailOut = w_buffer_.data_.size() - w_buffer_.start_ -
+ w_buffer_.length_;
+
+ #ifdef TEST
+ *logofs << "ProxyTransport: Initial compress buffer is "
+ << newAvailOut << " bytes.\n" << logofs_flush;
+ #endif
+
+ for (;;)
+ {
+ #ifdef INSPECT
+ *logofs << "\nProxyTransport: Running the compress loop.\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_buffer_.length_ = "
+ << w_buffer_.length_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_buffer_.data_.size() = "
+ << w_buffer_.data_.size() << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: newAvailOut = "
+ << newAvailOut << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (resize(w_buffer_, newAvailOut) < 0)
+ {
+ return -1;
+ }
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_buffer_.data_.size() = "
+ << w_buffer_.data_.size() << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.next_in = "
+ << (void *) w_stream_.next_in << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.avail_in = "
+ << w_stream_.avail_in << ".\n"
+ << logofs_flush;
+ #endif
+
+ w_stream_.next_out = w_buffer_.data_.begin() + w_buffer_.start_ +
+ w_buffer_.length_;
+
+ w_stream_.avail_out = newAvailOut;
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.next_out = "
+ << (void *) w_stream_.next_out << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.avail_out = "
+ << w_stream_.avail_out << ".\n"
+ << logofs_flush;
+ #endif
+
+ int result = deflate(&w_stream_, (type == write_delayed ?
+ Z_NO_FLUSH : Z_SYNC_FLUSH));
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: Called deflate() result is "
+ << result << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.avail_in = "
+ << w_stream_.avail_in << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.avail_out = "
+ << w_stream_.avail_out << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.total_in = "
+ << w_stream_.total_in << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.total_out = "
+ << w_stream_.total_out << ".\n"
+ << logofs_flush;
+ #endif
+
+ diffTotalOut = w_stream_.total_out - oldTotalOut;
+ diffTotalIn = w_stream_.total_in - oldTotalIn;
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: diffTotalIn = "
+ << diffTotalIn << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: diffTotalOut = "
+ << diffTotalOut << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_buffer_.length_ = "
+ << w_buffer_.length_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ w_buffer_.length_ += diffTotalOut;
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_buffer_.length_ = "
+ << w_buffer_.length_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ oldTotalOut = w_stream_.total_out;
+ oldTotalIn = w_stream_.total_in;
+
+ if (result == Z_OK)
+ {
+ if (w_stream_.avail_in == 0 && w_stream_.avail_out > 0)
+ {
+ break;
+ }
+ }
+ else if (result == Z_BUF_ERROR && w_stream_.avail_out > 0 &&
+ w_stream_.avail_in == 0)
+ {
+ #ifdef TEST
+ *logofs << "ProxyTransport: WARNING! Raised Z_BUF_ERROR compressing data.\n"
+ << logofs_flush;
+ #endif
+
+ break;
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "ProxyTransport: PANIC! Compression of data failed. "
+ << "Error is '" << zError(result) << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Compression of data failed. Error is '"
+ << zError(result) << "'.\n";
+
+ finish();
+
+ return -1;
+ }
+
+ //
+ // Add more bytes to the buffer.
+ //
+
+ if (newAvailOut < thresholdSize_)
+ {
+ newAvailOut = thresholdSize_;
+ }
+
+ #ifdef TEST
+ *logofs << "ProxyTransport: Need to add " << newAvailOut
+ << " bytes to the compress buffer in write.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ diffTotalIn = w_stream_.total_in - saveTotalIn;
+ diffTotalOut = w_stream_.total_out - saveTotalOut;
+
+ #ifdef TEST
+
+ *logofs << "ProxyTransport: Compressed data from "
+ << diffTotalIn << " to " << diffTotalOut
+ << " bytes.\n" << logofs_flush;
+
+ if (diffTotalIn != (int) size)
+ {
+ #ifdef PANIC
+ *logofs << "ProxyTransport: PANIC! Bytes provided to ZLIB stream "
+ << "should be " << size << " but they look to be "
+ << diffTotalIn << ".\n" << logofs_flush;
+ #endif
+ }
+
+ #endif
+
+ //
+ // Find out what we have to do with the
+ // produced data.
+ //
+
+ if (type == write_immediate)
+ {
+ //
+ // If user requested an immediate write we
+ // flushed the ZLIB buffer. We can now reset
+ // the counter and write data to socket.
+ //
+
+ flush_ = 0;
+
+ #ifdef TEST
+ *logofs << "ProxyTransport: Write buffer for proxy FD#" << fd_
+ << " has data for " << w_buffer_.length_ << " bytes.\n"
+ << logofs_flush;
+
+ *logofs << "ProxyTransport: Start is " << w_buffer_.start_
+ << " length is " << w_buffer_.length_ << " flush is "
+ << flush_ << " size is " << w_buffer_.data_.size()
+ << " capacity is " << w_buffer_.data_.capacity()
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Alternatively may try to write only if
+ // the socket is not blocked.
+ //
+ // if (w_buffer_.length_ > 0 && blocked_ == 0)
+ // {
+ // ...
+ // }
+ //
+
+ if (w_buffer_.length_ > 0)
+
+ {
+ #ifdef TEST
+ *logofs << "ProxyTransport: Writing " << w_buffer_.length_
+ << " bytes of produced data to FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ int result = Transport::flush();
+
+ if (result < 0)
+ {
+ return -1;
+ }
+ }
+ }
+ else
+ {
+ //
+ // We haven't flushed the ZLIB compression
+ // buffer, so user will have to call proxy
+ // transport's flush explicitly.
+ //
+
+ flush_ += diffTotalIn;
+ }
+
+ //
+ // We either wrote the data or added it to the
+ // write buffer. It's convenient to update the
+ // counters at this stage to get the current
+ // bitrate earlier.
+ //
+
+ statistics -> addCompressedBytes(diffTotalIn, diffTotalOut);
+
+ statistics -> addBytesOut(diffTotalOut);
+
+ statistics -> updateBitrate(diffTotalOut);
+
+ FlushCallback(diffTotalOut);
+
+ #ifdef TEST
+ *logofs << "ProxyTransport: Write buffer for proxy FD#" << fd_
+ << " has data for " << w_buffer_.length_ << " bytes.\n"
+ << logofs_flush;
+
+ *logofs << "ProxyTransport: Start is " << w_buffer_.start_
+ << " length is " << w_buffer_.length_ << " flush is "
+ << flush_ << " size is " << w_buffer_.data_.size()
+ << " capacity is " << w_buffer_.data_.capacity()
+ << ".\n" << logofs_flush;
+ #endif
+
+ return size;
+}
+
+//
+// Write data to its file descriptor.
+//
+
+int ProxyTransport::flush()
+{
+ //
+ // If there is no compression or we already compressed
+ // outgoing data and just need to write it to socket
+ // because of previous incomplete writes then revert
+ // to plain socket management.
+ //
+
+ if (flush_ == 0 || control -> LocalStreamCompression == 0)
+ {
+ int result = Transport::flush();
+
+ if (result < 0)
+ {
+ return -1;
+ }
+
+ return result;
+ }
+
+ #ifdef DEBUG
+ *logofs << "ProxyTransport: Going to flush compression on "
+ << "proxy FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef TEST
+ *logofs << "ProxyTransport: Flush counter for proxy FD#" << fd_
+ << " is " << flush_ << " bytes.\n" << logofs_flush;
+ #endif
+
+ //
+ // Flush ZLIB stream into the write buffer.
+ //
+
+ int saveTotalIn = w_stream_.total_in;
+ int saveTotalOut = w_stream_.total_out;
+
+ int oldTotalIn = saveTotalIn;
+ int oldTotalOut = saveTotalOut;
+
+ int diffTotalOut;
+ int diffTotalIn;
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: oldTotalIn = " << oldTotalIn
+ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: oldTotalOut = " << oldTotalOut
+ << ".\n" << logofs_flush;
+ #endif
+
+ w_stream_.next_in = w_buffer_.data_.begin() + w_buffer_.start_ + w_buffer_.length_;
+ w_stream_.avail_in = 0;
+
+ //
+ // Let ZLIB use all the space already
+ // available in the buffer.
+ //
+
+ unsigned int newAvailOut = w_buffer_.data_.size() - w_buffer_.start_ -
+ w_buffer_.length_;
+
+ #ifdef DEBUG
+ *logofs << "ProxyTransport: Initial flush buffer is "
+ << newAvailOut << " bytes.\n" << logofs_flush;
+ #endif
+
+ for (;;)
+ {
+ #ifdef INSPECT
+ *logofs << "\nProxyTransport: Running the flush loop.\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_buffer_.length_ = "
+ << w_buffer_.length_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_buffer_.data_.size() = "
+ << w_buffer_.data_.size() << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: newAvailOut = "
+ << newAvailOut << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (resize(w_buffer_, newAvailOut) < 0)
+ {
+ return -1;
+ }
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_buffer_.data_.size() = "
+ << w_buffer_.data_.size() << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.next_in = "
+ << (void *) w_stream_.next_in << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.avail_in = "
+ << w_stream_.avail_in << ".\n"
+ << logofs_flush;
+ #endif
+
+ w_stream_.next_out = w_buffer_.data_.begin() + w_buffer_.start_ +
+ w_buffer_.length_;
+
+ w_stream_.avail_out = newAvailOut;
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.next_out = "
+ << (void *) w_stream_.next_out << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.avail_out = "
+ << w_stream_.avail_out << ".\n"
+ << logofs_flush;
+ #endif
+
+ int result = deflate(&w_stream_, Z_SYNC_FLUSH);
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: Called deflate() result is "
+ << result << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.avail_in = "
+ << w_stream_.avail_in << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.avail_out = "
+ << w_stream_.avail_out << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.total_in = "
+ << w_stream_.total_in << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.total_out = "
+ << w_stream_.total_out << ".\n"
+ << logofs_flush;
+ #endif
+
+ diffTotalOut = w_stream_.total_out - oldTotalOut;
+ diffTotalIn = w_stream_.total_in - oldTotalIn;
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: diffTotalIn = "
+ << diffTotalIn << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: diffTotalOut = "
+ << diffTotalOut << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_buffer_.length_ = "
+ << w_buffer_.length_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ w_buffer_.length_ += diffTotalOut;
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_buffer_.length_ = "
+ << w_buffer_.length_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ oldTotalOut = w_stream_.total_out;
+ oldTotalIn = w_stream_.total_in;
+
+ if (result == Z_OK)
+ {
+ if (w_stream_.avail_in == 0 && w_stream_.avail_out > 0)
+ {
+ break;
+ }
+ }
+ else if (result == Z_BUF_ERROR && w_stream_.avail_out > 0 &&
+ w_stream_.avail_in == 0)
+ {
+ #ifdef TEST
+ *logofs << "ProxyTransport: WARNING! Raised Z_BUF_ERROR flushing data.\n"
+ << logofs_flush;
+ #endif
+
+ break;
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "ProxyTransport: PANIC! Flush of compressed data failed. "
+ << "Error is '" << zError(result) << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Flush of compressed data failed. Error is '"
+ << zError(result) << "'.\n";
+
+ finish();
+
+ return -1;
+ }
+
+ //
+ // Add more bytes to the buffer.
+ //
+
+ if (newAvailOut < thresholdSize_)
+ {
+ newAvailOut = thresholdSize_;
+ }
+
+ #ifdef TEST
+ *logofs << "ProxyTransport: Need to add " << newAvailOut
+ << " bytes to the compress buffer in flush.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ diffTotalIn = w_stream_.total_in - saveTotalIn;
+ diffTotalOut = w_stream_.total_out - saveTotalOut;
+
+ #ifdef TEST
+ *logofs << "ProxyTransport: Compressed flush data from "
+ << diffTotalIn << " to " << diffTotalOut
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ //
+ // Time to move data from the write
+ // buffer to the real link.
+ //
+
+ #ifdef DEBUG
+ *logofs << "ProxyTransport: Reset flush counter for proxy FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ flush_ = 0;
+
+ #ifdef TEST
+ *logofs << "ProxyTransport: Write buffer for proxy FD#" << fd_
+ << " has data for " << w_buffer_.length_ << " bytes.\n"
+ << logofs_flush;
+
+ *logofs << "ProxyTransport: Start is " << w_buffer_.start_
+ << " length is " << w_buffer_.length_ << " flush is "
+ << flush_ << " size is " << w_buffer_.data_.size()
+ << " capacity is " << w_buffer_.data_.capacity()
+ << ".\n" << logofs_flush;
+ #endif
+
+ int result = Transport::flush();
+
+ if (result < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Update all the counters.
+ //
+
+ statistics -> addCompressedBytes(diffTotalIn, diffTotalOut);
+
+ statistics -> addBytesOut(diffTotalOut);
+
+ statistics -> updateBitrate(diffTotalOut);
+
+ FlushCallback(diffTotalOut);
+
+ return result;
+}
+
+unsigned int ProxyTransport::getPending(unsigned char *&data)
+{
+ //
+ // Return a pointer to the data in the
+ // read buffer. It is up to the caller
+ // to ensure that the data is consumed
+ // before the read buffer is reused.
+ //
+
+ if (r_buffer_.length_ > 0)
+ {
+ unsigned int size = r_buffer_.length_;
+
+ data = r_buffer_.data_.begin() + r_buffer_.start_;
+
+ #ifdef DEBUG
+ *logofs << "ProxyTransport: Returning " << size
+ << " pending bytes from proxy FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ r_buffer_.length_ = 0;
+ r_buffer_.start_ = 0;
+
+ //
+ // Prevent the deletion of the buffer.
+ //
+
+ owner_ = 0;
+
+ return size;
+ }
+
+ #ifdef TEST
+ *logofs << "ProxyTransport: WARNING! No pending data "
+ << "for proxy FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ data = NULL;
+
+ return 0;
+}
+
+void ProxyTransport::fullReset()
+{
+ blocked_ = 0;
+ finish_ = 0;
+ flush_ = 0;
+
+ if (control -> RemoteStreamCompression)
+ {
+ inflateReset(&r_stream_);
+ }
+
+ if (control -> LocalStreamCompression)
+ {
+ deflateReset(&w_stream_);
+ }
+
+ if (owner_ == 1)
+ {
+ Transport::fullReset(r_buffer_);
+ }
+
+ Transport::fullReset(w_buffer_);
+}
+
+AgentTransport::AgentTransport(int fd) : Transport(fd)
+{
+ #ifdef TEST
+ *logofs << "AgentTransport: Going to create agent transport "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ type_ = transport_agent;
+
+ //
+ // Set up the read buffer.
+ //
+
+ r_buffer_.length_ = 0;
+ r_buffer_.start_ = 0;
+
+ r_buffer_.data_.resize(initialSize_);
+
+ //
+ // For now we own the buffer.
+ //
+
+ owner_ = 1;
+
+ //
+ // Set up the mutexes.
+ //
+
+ #ifdef THREADS
+
+ pthread_mutexattr_t m_attributes;
+
+ pthread_mutexattr_init(&m_attributes);
+
+ //
+ // Interfaces in pthread to handle mutex
+ // type do not work in current version.
+ //
+
+ m_attributes.__mutexkind = PTHREAD_MUTEX_ERRORCHECK_NP;
+
+ if (pthread_mutex_init(&m_read_, &m_attributes) != 0)
+ {
+ #ifdef TEST
+ *logofs << "AgentTransport: Child: Creation of read mutex failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ if (pthread_mutex_init(&m_write_, &m_attributes) != 0)
+ {
+ #ifdef TEST
+ *logofs << "AgentTransport: Child: Creation of write mutex failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ #endif
+
+ #ifdef REFERENCES
+ *logofs << "AgentTransport: Child: Created new object at "
+ << this << " out of " << ++references_
+ << " allocated references.\n" << logofs_flush;
+ #endif
+}
+
+AgentTransport::~AgentTransport()
+{
+ #ifdef TEST
+ *logofs << "AgentTransport: Going to destroy derived class "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Unlock and free all mutexes.
+ //
+
+ #ifdef THREADS
+
+ pthread_mutex_unlock(&m_read_);
+ pthread_mutex_unlock(&m_write_);
+
+ pthread_mutex_destroy(&m_read_);
+ pthread_mutex_destroy(&m_write_);
+
+ #endif
+
+ #ifdef REFERENCES
+ *logofs << "AgentTransport: Child: Deleted object at "
+ << this << " out of " << --references_
+ << " allocated references.\n" << logofs_flush;
+ #endif
+}
+
+//
+// Read data enqueued by the other thread.
+//
+
+int AgentTransport::read(unsigned char *data, unsigned int size)
+{
+ #ifdef THREADS
+
+ lockRead();
+
+ #endif
+
+ #ifdef DEBUG
+ *logofs << "AgentTransport: Child: Going to read " << size
+ << " bytes from " << "FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ int copied = -1;
+
+ if (r_buffer_.length_ > 0)
+ {
+ if ((int) size < r_buffer_.length_)
+ {
+ #ifdef TEST
+ *logofs << "AgentTransport: WARNING! Forcing a retry with "
+ << r_buffer_.length_ << " bytes pending and "
+ << size << " in the buffer.\n"
+ << logofs_flush;
+ #endif
+
+ ESET(EAGAIN);
+ }
+ else
+ {
+ copied = (r_buffer_.length_ > ((int) size) ?
+ ((int) size) : r_buffer_.length_);
+
+ memcpy(data, r_buffer_.data_.begin() + r_buffer_.start_, copied);
+
+ //
+ // Update the buffer status.
+ //
+
+ #ifdef TEST
+ *logofs << "AgentTransport: Child: Going to immediately return "
+ << copied << " bytes from FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef DUMP
+
+ *logofs << "AgentTransport: Child: Dumping content of read data.\n"
+ << logofs_flush;
+
+ DumpData(data, copied);
+
+ #endif
+
+ r_buffer_.length_ -= copied;
+
+ if (r_buffer_.length_ == 0)
+ {
+ r_buffer_.start_ = 0;
+ }
+ else
+ {
+ r_buffer_.start_ += copied;
+
+ #ifdef TEST
+ *logofs << "AgentTransport: Child: There are still "
+ << r_buffer_.length_ << " bytes in read buffer for "
+ << "FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+ }
+ }
+ }
+ else
+ {
+ #ifdef DEBUG
+ *logofs << "AgentTransport: Child: No data can be got "
+ << "from read buffer for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ ESET(EAGAIN);
+ }
+
+ #ifdef THREADS
+
+ unlockRead();
+
+ #endif
+
+ return copied;
+}
+
+//
+// Write data to buffer so that the other
+// thread can get it.
+//
+
+int AgentTransport::write(T_write type, const unsigned char *data, const unsigned int size)
+{
+ #ifdef THREADS
+
+ lockWrite();
+
+ #endif
+
+ //
+ // Just append data to socket's write buffer.
+ // Note that we don't care if buffer exceeds
+ // the size limits set for this type of
+ // transport.
+ //
+
+ #ifdef TEST
+ *logofs << "AgentTransport: Child: Going to append " << size
+ << " bytes to write buffer for " << "FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ int copied = -1;
+
+ if (resize(w_buffer_, size) < 0)
+ {
+ finish();
+
+ ESET(EPIPE);
+ }
+ else
+ {
+ memmove(w_buffer_.data_.begin() + w_buffer_.start_ + w_buffer_.length_, data, size);
+
+ w_buffer_.length_ += size;
+
+ #ifdef DUMP
+
+ *logofs << "AgentTransport: Child: Dumping content of written data.\n"
+ << logofs_flush;
+
+ DumpData(data, size);
+
+ #endif
+
+ #ifdef TEST
+ *logofs << "AgentTransport: Child: Write buffer for FD#" << fd_
+ << " has data for " << w_buffer_.length_ << " bytes.\n"
+ << logofs_flush;
+
+ *logofs << "AgentTransport: Child: Start is " << w_buffer_.start_
+ << " length is " << w_buffer_.length_ << " size is "
+ << w_buffer_.data_.size() << " capacity is "
+ << w_buffer_.data_.capacity() << ".\n"
+ << logofs_flush;
+ #endif
+
+ copied = size;
+ }
+
+ //
+ // Let child access again the read buffer.
+ //
+
+ #ifdef THREADS
+
+ unlockWrite();
+
+ #endif
+
+ return copied;
+}
+
+int AgentTransport::flush()
+{
+ //
+ // In case of memory-to-memory transport
+ // this function should never be called.
+ //
+
+ #ifdef PANIC
+ *logofs << "AgentTransport: Child: PANIC! Called flush() for "
+ << "memory to memory transport on " << "FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Called flush() for "
+ << "memory to memory transport on " << "FD#"
+ << fd_ << ".\n";
+
+ HandleAbort();
+}
+
+int AgentTransport::drain(int limit, int timeout)
+{
+ //
+ // We can't drain the channel in the case
+ // of the memory-to-memory transport. Data
+ // is enqueued for the agent to read but
+ // the agent could require multiple loops
+ // to read it all.
+ //
+
+ //
+ // In case of memory-to-memory transport
+ // this function should never be called.
+ //
+
+ #ifdef PANIC
+ *logofs << "AgentTransport: Child: PANIC! Called drain() for "
+ << "memory to memory transport on " << "FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Called drain() for "
+ << "memory to memory transport on " << "FD#"
+ << fd_ << ".\n";
+
+ HandleAbort();
+}
+
+unsigned int AgentTransport::getPending(unsigned char *&data)
+{
+ #ifdef THREADS
+
+ lockRead();
+
+ #endif
+
+ if (r_buffer_.length_ > 0)
+ {
+ unsigned int size = r_buffer_.length_;
+
+ data = r_buffer_.data_.begin() + r_buffer_.start_;
+
+ #ifdef DEBUG
+ *logofs << "AgentTransport: Child: Returning " << size
+ << " pending bytes from FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ r_buffer_.length_ = 0;
+ r_buffer_.start_ = 0;
+
+ #ifdef THREADS
+
+ unlockRead();
+
+ #endif
+
+ //
+ // Prevent the deletion of the buffer.
+ //
+
+ owner_ = 0;
+
+ return size;
+ }
+
+ #ifdef TEST
+ *logofs << "AgentTransport: WARNING! No pending data "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef THREADS
+
+ unlockRead();
+
+ #endif
+
+ data = NULL;
+
+ return 0;
+}
+
+void AgentTransport::fullReset()
+{
+ #ifdef THREADS
+
+ lockRead();
+ lockWrite();
+
+ #endif
+
+ #ifdef TEST
+ *logofs << "AgentTransport: Child: Resetting transport "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ blocked_ = 0;
+ finish_ = 0;
+
+ if (owner_ == 1)
+ {
+ Transport::fullReset(r_buffer_);
+ }
+
+ Transport::fullReset(w_buffer_);
+}
+
+int AgentTransport::enqueue(const char *data, const int size)
+{
+ #ifdef THREADS
+
+ lockRead();
+
+ #endif
+
+ if (finish_ == 1)
+ {
+ #if defined(PARENT) && defined(TEST)
+ *logofs << "AgentTransport: Parent: Returning EPIPE in "
+ << "write for finishing FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ ESET(EPIPE);
+
+ return -1;
+ }
+
+ //
+ // Always allow the agent to write
+ // all its data.
+ //
+
+ int toPut = size;
+
+ #if defined(PARENT) && defined(TEST)
+ *logofs << "AgentTransport: Parent: Going to put " << toPut
+ << " bytes into read buffer for FD#" << fd_
+ << ". Buffer length is " << r_buffer_.length_
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (resize(r_buffer_, toPut) < 0)
+ {
+ finish();
+
+ #ifdef THREADS
+
+ unlockRead();
+
+ #endif
+
+ return -1;
+ }
+
+ memcpy(r_buffer_.data_.begin() + r_buffer_.start_ + r_buffer_.length_, data, toPut);
+
+ r_buffer_.length_ += toPut;
+
+ #if defined(DUMP) && defined(PARENT)
+
+ *logofs << "AgentTransport: Parent: Dumping content of enqueued data.\n"
+ << logofs_flush;
+
+ DumpData((const unsigned char *) data, toPut);
+
+ #endif
+
+ #if defined(PARENT) && defined(TEST)
+ *logofs << "AgentTransport: Parent: Read buffer for FD#" << fd_
+ << " has now data for " << r_buffer_.length_
+ << " bytes.\n" << logofs_flush;
+
+ *logofs << "AgentTransport: Parent: Start is " << r_buffer_.start_
+ << " length is " << r_buffer_.length_ << " size is "
+ << r_buffer_.data_.size() << " capacity is "
+ << r_buffer_.data_.capacity() << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef THREADS
+
+ unlockRead();
+
+ #endif
+
+ return toPut;
+}
+
+int AgentTransport::dequeue(char *data, int size)
+{
+ #ifdef THREADS
+
+ lockWrite();
+
+ #endif
+
+ if (w_buffer_.length_ == 0)
+ {
+ if (finish_ == 1)
+ {
+ #if defined(PARENT) && defined(TEST)
+ *logofs << "AgentTransport: Parent: Returning 0 in read "
+ << "for finishing FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ #if defined(PARENT) && defined(TEST)
+ *logofs << "AgentTransport: Parent: No data can be read "
+ << "from write buffer for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ ESET(EAGAIN);
+
+ #ifdef THREADS
+
+ unlockWrite();
+
+ #endif
+
+ return -1;
+ }
+
+ //
+ // Return as many bytes as possible.
+ //
+
+ int toGet = ((int) size > w_buffer_.length_ ? w_buffer_.length_ : size);
+
+ #if defined(PARENT) && defined(TEST)
+ *logofs << "AgentTransport: Parent: Going to get " << toGet
+ << " bytes from write buffer for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ memcpy(data, w_buffer_.data_.begin() + w_buffer_.start_, toGet);
+
+ w_buffer_.start_ += toGet;
+ w_buffer_.length_ -= toGet;
+
+ #if defined(DUMP) && defined(PARENT)
+
+ *logofs << "AgentTransport: Parent: Dumping content of dequeued data.\n"
+ << logofs_flush;
+
+ DumpData((const unsigned char *) data, toGet);
+
+ #endif
+
+ #if defined(PARENT) && defined(TEST)
+ *logofs << "AgentTransport: Parent: Write buffer for FD#" << fd_
+ << " has now data for " << length() << " bytes.\n"
+ << logofs_flush;
+
+ *logofs << "AgentTransport: Parent: Start is " << w_buffer_.start_
+ << " length is " << w_buffer_.length_ << " size is "
+ << w_buffer_.data_.size() << " capacity is "
+ << w_buffer_.data_.capacity() << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef THREADS
+
+ unlockWrite();
+
+ #endif
+
+ return toGet;
+}
+
+int AgentTransport::dequeuable()
+{
+ if (finish_ == 1)
+ {
+ #if defined(PARENT) && defined(TEST)
+ *logofs << "AgentTransport: Parent: Returning EPIPE in "
+ << "readable for finishing FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ ESET(EPIPE);
+
+ return -1;
+ }
+
+ #if defined(PARENT) && defined(TEST)
+ *logofs << "AgentTransport: Parent: Returning "
+ << w_buffer_.length_ << " as data readable "
+ << "from read buffer for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return w_buffer_.length_;
+}
+
+#ifdef THREADS
+
+int AgentTransport::lockRead()
+{
+ for (;;)
+ {
+ int result = pthread_mutex_lock(&m_read_);
+
+ if (result == 0)
+ {
+ #ifdef DEBUG
+ *logofs << "AgentTransport: Read mutex locked by thread id "
+ << pthread_self() << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else if (EGET() == EINTR)
+ {
+ continue;
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "AgentTransport: WARNING! Locking of read mutex by thread id "
+ << pthread_self() << " returned " << result << ". Error is '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ return result;
+ }
+ }
+}
+
+int AgentTransport::lockWrite()
+{
+ for (;;)
+ {
+ int result = pthread_mutex_lock(&m_write_);
+
+ if (result == 0)
+ {
+ #ifdef DEBUG
+ *logofs << "AgentTransport: Write mutex locked by thread id "
+ << pthread_self() << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else if (EGET() == EINTR)
+ {
+ continue;
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "AgentTransport: WARNING! Locking of write mutex by thread id "
+ << pthread_self() << " returned " << result << ". Error is '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ return result;
+ }
+ }
+}
+
+int AgentTransport::unlockRead()
+{
+ for (;;)
+ {
+ int result = pthread_mutex_unlock(&m_read_);
+
+ if (result == 0)
+ {
+ #ifdef DEBUG
+ *logofs << "AgentTransport: Read mutex unlocked by thread id "
+ << pthread_self() << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else if (EGET() == EINTR)
+ {
+ continue;
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "AgentTransport: WARNING! Unlocking of read mutex by thread id "
+ << pthread_self() << " returned " << result << ". Error is '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ return result;
+ }
+ }
+}
+
+int AgentTransport::unlockWrite()
+{
+ for (;;)
+ {
+ int result = pthread_mutex_unlock(&m_write_);
+
+ if (result == 0)
+ {
+ #ifdef DEBUG
+ *logofs << "AgentTransport: Write mutex unlocked by thread id "
+ << pthread_self() << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else if (EGET() == EINTR)
+ {
+ continue;
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "AgentTransport: WARNING! Unlocking of write mutex by thread id "
+ << pthread_self() << " returned " << result << ". Error is '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ return result;
+ }
+ }
+}
+
+#endif
diff --git a/nxcomp/Transport.h b/nxcomp/Transport.h
new file mode 100644
index 000000000..2f313b29f
--- /dev/null
+++ b/nxcomp/Transport.h
@@ -0,0 +1,569 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef Transport_H
+#define Transport_H
+
+#include <zlib.h>
+#include <errno.h>
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "Misc.h"
+#include "Control.h"
+
+#include "Types.h"
+#include "Timestamp.h"
+#include "Socket.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Define this to lock and unlock the
+// memory-to-memory transport buffers
+// before they are accessed. The code
+// is outdated and doesn't work with
+// the current pthread library.
+//
+
+#undef THREADS
+
+//
+// Define this to know when a socket
+// is created or destroyed.
+//
+
+#undef REFERENCES
+
+//
+// Size of buffer if not set by user.
+//
+
+#define TRANSPORT_BUFFER_DEFAULT_SIZE 16384
+
+//
+// Type of transport.
+//
+
+typedef enum
+{
+ transport_base,
+ transport_proxy,
+ transport_agent,
+ transport_last_tag
+
+} T_transport_type;
+
+//
+// This class handles the buffered I/O on
+// the network sockets.
+//
+
+//
+// TODO: This class is useful but adds a lot of
+// overhead. There are many improvements we can
+// make here:
+//
+// - There should be a generic Buffer class, ac-
+// comodating a list of memory buffers. This
+// would enable the use of the readv() and
+// writev() functions to perform the I/O on
+// the socket.
+//
+// - The buffering should be moved to the Write-
+// Buffer and ReadBuffer classes. By performing
+// the buffering here and there, we are dupli-
+// cating a lot of code and are adding a lot
+// of useless memory copies.
+//
+// - Stream compression should be removed. The
+// proxy should compress the frames based on
+// the type and should include the length of
+// the decompressed data in the header of the
+// packet. Besides avoiding the compression
+// of packets that cannot be reduced in size,
+// we would also save the additional memory
+// allocations due to the fact that we don't
+// know the size of the decode buffer at the
+// time we read the packet from the network.
+//
+// - The other utilities implemented here, like
+// the functions forcing a write on the socket
+// or waiting for more data to become available
+// should be moved to the Proxy or the Channel
+// classes.
+//
+
+class Transport
+{
+ public:
+
+ //
+ // Member functions.
+ //
+
+ Transport(int fd);
+
+ virtual ~Transport();
+
+ int fd() const
+ {
+ return fd_;
+ }
+
+ T_transport_type getType()
+ {
+ return type_;
+ }
+
+ //
+ // Virtual members redefined by proxy
+ // and 'memory-to-memory' I/O layers.
+ //
+
+ virtual int read(unsigned char *data, unsigned int size);
+
+ virtual int write(T_write type, const unsigned char *data, const unsigned int size);
+
+ virtual int flush();
+
+ virtual int drain(int limit, int timeout);
+
+ virtual void finish()
+ {
+ fullReset();
+
+ finish_ = 1;
+ }
+
+ virtual int length() const
+ {
+ return w_buffer_.length_;
+ }
+
+ virtual int pending() const
+ {
+ return 0;
+ }
+
+ virtual int readable() const
+ {
+ return GetBytesReadable(fd_);
+ }
+
+ virtual int writable() const
+ {
+ return GetBytesWritable(fd_);
+ }
+
+ virtual int queued() const
+ {
+ return GetBytesQueued(fd_);
+ }
+
+ virtual int flushable() const
+ {
+ return 0;
+ }
+
+ virtual int wait(int timeout) const;
+
+ void setSize(unsigned int initialSize,
+ unsigned int thresholdSize,
+ unsigned int maximumSize);
+
+ //
+ // Return a pointer to the data
+ // in the read buffer.
+ //
+
+ virtual unsigned int getPending(unsigned char *&data)
+ {
+ data = NULL;
+
+ return 0;
+ }
+
+ virtual void pendingReset()
+ {
+ }
+
+ virtual void partialReset()
+ {
+ partialReset(w_buffer_);
+ }
+
+ virtual void fullReset();
+
+ int blocked() const
+ {
+ return blocked_;
+ }
+
+ protected:
+
+ //
+ // Make room in the buffer to accomodate
+ // at least size bytes.
+ //
+
+ int resize(T_buffer &buffer, const int &size);
+
+ void partialReset(T_buffer &buffer)
+ {
+ if (buffer.length_ == 0 &&
+ (buffer.data_.size() > initialSize_ ||
+ buffer.data_.capacity() > initialSize_))
+ {
+ fullReset(buffer);
+ }
+ }
+
+ void fullReset(T_buffer &buffer);
+
+ //
+ // Data members.
+ //
+
+ int fd_;
+
+ int blocked_;
+ int finish_;
+
+ T_buffer w_buffer_;
+
+ unsigned int initialSize_;
+ unsigned int thresholdSize_;
+ unsigned int maximumSize_;
+
+ T_transport_type type_;
+
+ private:
+
+ #ifdef REFERENCES
+
+ static int references_;
+
+ #endif
+};
+
+//
+// This class handles buffered I/O and
+// compression of the proxy stream.
+//
+
+class ProxyTransport : public Transport
+{
+ public:
+
+ ProxyTransport(int fd);
+
+ virtual ~ProxyTransport();
+
+ virtual int read(unsigned char *data, unsigned int size);
+
+ virtual int write(T_write type, const unsigned char *data, const unsigned int size);
+
+ virtual int flush();
+
+ //
+ // Same as in the base class.
+ //
+ // virtual int drain(int limit, int timeout);
+ //
+ // virtual void finish();
+ //
+
+ //
+ // Same as in the base class.
+ //
+ // virtual int length() const
+ //
+
+ virtual int pending() const
+ {
+ return r_buffer_.length_;
+ }
+
+ //
+ // Same as in the base class.
+ //
+ // virtual int readable() const;
+ //
+ // virtual int writable() const;
+ //
+ // virtual int queued() const;
+ //
+
+ virtual int flushable() const
+ {
+ return flush_;
+ }
+
+ //
+ // Same as in the base class, but
+ // should not be called.
+ //
+ // int drained() const;
+ //
+ // Same as in the base class.
+ //
+ // virtual int wait(int timeout) const;
+ //
+ // Same as in the base class.
+ //
+ // void setSize(unsigned int initialSize,
+ // unsigned int thresholdSize,
+ // unsigned int maximumSize);
+ //
+
+ virtual unsigned int getPending(unsigned char *&data);
+
+ virtual void pendingReset()
+ {
+ owner_ = 1;
+ }
+
+ virtual void partialReset()
+ {
+ if (owner_ == 1)
+ {
+ Transport::partialReset(r_buffer_);
+ }
+
+ Transport::partialReset(w_buffer_);
+ }
+
+ virtual void fullReset();
+
+ //
+ // Same as in the base class.
+ //
+ // int blocked() const;
+ //
+
+ protected:
+
+ int flush_;
+ int owner_;
+
+ T_buffer r_buffer_;
+
+ z_stream r_stream_;
+ z_stream w_stream_;
+
+ private:
+
+ #ifdef REFERENCES
+
+ static int references_;
+
+ #endif
+};
+
+//
+// Handle memory-to-memory data transfers between
+// an agent and the proxy.
+//
+
+class AgentTransport : public Transport
+{
+ public:
+
+ AgentTransport(int fd);
+
+ virtual ~AgentTransport();
+
+ virtual int read(unsigned char *data, unsigned int size);
+
+ virtual int write(T_write type, const unsigned char *data, const unsigned int size);
+
+ //
+ // These two should never be called.
+ //
+
+ virtual int flush();
+
+ virtual int drain(int limit, int timeout);
+
+ //
+ // Same as in the base class.
+ //
+ // virtual void finish();
+ //
+
+ //
+ // Same as in the base class.
+ //
+ // virtual int length() const
+ //
+
+ virtual int pending() const
+ {
+ return r_buffer_.length_;
+ }
+
+ //
+ // These are intended to operate only
+ // on the internal buffers.
+ //
+
+ virtual int readable() const
+ {
+ return r_buffer_.length_;
+ }
+
+ virtual int writable() const
+ {
+ return control -> TransportMaximumBufferSize;
+ }
+
+ virtual int queued() const
+ {
+ return 0;
+ }
+
+ //
+ // Same as in the base class.
+ //
+ // virtual int flushable() const;
+ //
+ // Same as in the base class, but
+ // should not be called.
+ //
+ // int drained() const;
+ //
+
+ //
+ // Return immediately or will
+ // block until the timeout.
+ //
+
+ virtual int wait(int timeout) const
+ {
+ return 0;
+ }
+
+ //
+ // Same as in the base class.
+ //
+ // void setSize(unsigned int initialSize,
+ // unsigned int thresholdSize,
+ // unsigned int maximumSize);
+ //
+
+ virtual unsigned int getPending(unsigned char *&data);
+
+ virtual void pendingReset()
+ {
+ owner_ = 1;
+ }
+
+ virtual void partialReset()
+ {
+ if (owner_ == 1)
+ {
+ Transport::partialReset(r_buffer_);
+ }
+
+ Transport::partialReset(w_buffer_);
+ }
+
+ virtual void fullReset();
+
+ //
+ // Same as in the base class.
+ //
+ // int blocked() const;
+ //
+
+ //
+ // The following are specific of the
+ // memory-to-memory transport.
+ //
+
+ int enqueue(const char *data, const int size);
+
+ int dequeue(char *data, int size);
+
+ int queuable()
+ {
+ //
+ // Always allow the agent to enqueue
+ // more data.
+ //
+
+ return control -> TransportMaximumBufferSize;
+ }
+
+ int dequeuable();
+
+ protected:
+
+ //
+ // Lock the buffer to handle reads and
+ // writes safely.
+ //
+
+ #ifdef THREADS
+
+ int lockRead();
+ int lockWrite();
+
+ int unlockRead();
+ int unlockWrite();
+
+ #endif
+
+ //
+ // Data members.
+ //
+
+ int owner_;
+
+ T_buffer r_buffer_;
+
+ //
+ // Mutexes for safe read and write.
+ //
+
+ #ifdef THREADS
+
+ pthread_mutex_t m_read_;
+ pthread_mutex_t m_write_;
+
+ #endif
+
+ private:
+
+ #ifdef REFERENCES
+
+ static int references_;
+
+ #endif
+};
+
+#endif /* Transport_H */
diff --git a/nxcomp/Types.h b/nxcomp/Types.h
new file mode 100644
index 000000000..05f62bd00
--- /dev/null
+++ b/nxcomp/Types.h
@@ -0,0 +1,255 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef Types_H
+#define Types_H
+
+using namespace std;
+
+#include <vector>
+#include <list>
+#include <map>
+#include <set>
+
+#include "MD5.h"
+
+//
+// This is MD5 length.
+//
+
+#define MD5_LENGTH 16
+
+//
+// Types of repositories. Replace the original
+// clear() methods from STL in order to actually
+// free the unused memory.
+//
+
+class Message;
+
+class T_data : public vector < unsigned char >
+{
+ public:
+
+ unsigned char *begin()
+ {
+ return &*(vector < unsigned char >::begin());
+ }
+
+ const unsigned char *begin() const
+ {
+ return &*(vector < unsigned char >::begin());
+ }
+
+ void clear()
+ {
+ #if defined(__STL_USE_STD_ALLOCATORS) || defined(__GLIBCPP_INTERNAL_VECTOR_H)
+
+ #if defined(__GLIBCPP_INTERNAL_VECTOR_H)
+
+ _Destroy(_M_start, _M_finish);
+
+ #else /* #if defined(__GLIBCPP_INTERNAL_VECTOR_H) */
+
+ destroy(_M_start, _M_finish);
+
+ #endif /* #if defined(__GLIBCPP_INTERNAL_VECTOR_H) */
+
+ _M_deallocate(_M_start, _M_end_of_storage - _M_start);
+
+ _M_start = _M_finish = _M_end_of_storage = 0;
+
+ #else /* #if defined(__STL_USE_STD_ALLOCATORS) || defined(__GLIBCPP_INTERNAL_VECTOR_H) */
+
+ #if defined(_GLIBCXX_VECTOR)
+
+ _Destroy(this->_M_impl._M_start, this->_M_impl._M_finish);
+
+ _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage - this->_M_impl._M_start);
+
+ this->_M_impl._M_start = this->_M_impl._M_finish = this->_M_impl._M_end_of_storage = 0;
+
+ #else /* #if defined(_GLIBCXX_VECTOR) */
+
+ destroy(start, finish);
+
+ deallocate();
+
+ start = finish = end_of_storage = 0;
+
+ #endif /* #if defined(_GLIBCXX_VECTOR) */
+
+ #endif /* #if defined(__STL_USE_STD_ALLOCATORS) || defined(__GLIBCPP_INTERNAL_VECTOR_H) */
+ }
+};
+
+class T_messages : public vector < Message * >
+{
+ public:
+
+ void clear()
+ {
+ #if defined(__STL_USE_STD_ALLOCATORS) || defined(__GLIBCPP_INTERNAL_VECTOR_H)
+
+ #if defined(__GLIBCPP_INTERNAL_VECTOR_H)
+
+ _Destroy(_M_start, _M_finish);
+
+ #else /* #if defined(__GLIBCPP_INTERNAL_VECTOR_H) */
+
+ destroy(_M_start, _M_finish);
+
+ #endif /* #if defined(__GLIBCPP_INTERNAL_VECTOR_H) */
+
+ _M_deallocate(_M_start, _M_end_of_storage - _M_start);
+
+ _M_start = _M_finish = _M_end_of_storage = 0;
+
+ #else /* #if defined(__STL_USE_STD_ALLOCATORS) || defined(__GLIBCPP_INTERNAL_VECTOR_H) */
+
+ #if defined(_GLIBCXX_VECTOR)
+
+ _Destroy(this->_M_impl._M_start, this->_M_impl._M_finish);
+
+ _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage - this->_M_impl._M_start);
+
+ this->_M_impl._M_start = this->_M_impl._M_finish = this->_M_impl._M_end_of_storage = 0;
+
+ #else /* #if defined(_GLIBCXX_VECTOR) */
+
+ destroy(start, finish);
+
+ deallocate();
+
+ start = finish = end_of_storage = 0;
+
+ #endif /* #if defined(_GLIBCXX_VECTOR) */
+
+ #endif /* #if defined(__STL_USE_STD_ALLOCATORS) || defined(__GLIBCPP_INTERNAL_VECTOR_H) */
+ }
+};
+
+typedef md5_byte_t * T_checksum;
+
+struct T_less
+{
+ bool operator()(T_checksum a, T_checksum b) const
+ {
+ return (memcmp(a, b, MD5_LENGTH) < 0);
+ }
+};
+
+typedef map < T_checksum, int, T_less > T_checksums;
+
+class Split;
+
+typedef list < Split * > T_splits;
+
+class File;
+
+struct T_older
+{
+ bool operator()(File *a, File *b) const;
+};
+
+typedef set < File *, T_older > T_files;
+
+typedef list < int > T_list;
+
+//
+// Used to accomodate data to be read and
+// written to a socket.
+//
+
+typedef struct
+{
+ T_data data_;
+ int length_;
+ int start_;
+}
+T_buffer;
+
+//
+// The message store operation that was
+// executed for the message. The channels
+// use these values to determine how to
+// handle the message after it has been
+// received at the decoding side.
+//
+
+enum T_store_action
+{
+ is_hit,
+ is_added,
+ is_discarded,
+ is_removed,
+ is_added_compat = 0,
+ is_hit_compat = 1
+};
+
+#define IS_HIT (control -> isProtoStep8() == 1 ? is_hit : is_hit_compat)
+#define IS_ADDED (control -> isProtoStep8() == 1 ? is_added : is_added_compat)
+
+enum T_checksum_action
+{
+ use_checksum,
+ discard_checksum
+};
+
+enum T_data_action
+{
+ use_data,
+ discard_data
+};
+
+//
+// Message is going to be weighted for
+// deletion at insert or cleanup time?
+//
+
+enum T_rating
+{
+ rating_for_insert,
+ rating_for_clean
+};
+
+//
+// How to handle the writes to the X
+// and proxy connections.
+//
+
+enum T_write
+{
+ write_immediate,
+ write_delayed
+};
+
+enum T_flush
+{
+ flush_if_needed,
+ flush_if_any
+};
+
+//
+// This is the value to indicate an
+// invalid position in the message
+// store.
+//
+
+static const int nothing = -1;
+
+#endif /* Types_H */
diff --git a/nxcomp/Unpack.cpp b/nxcomp/Unpack.cpp
new file mode 100644
index 000000000..5fc494465
--- /dev/null
+++ b/nxcomp/Unpack.cpp
@@ -0,0 +1,1502 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "Misc.h"
+#include "Unpack.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Used for the ZLIB decompression
+// of RGB, alpha and colormap data.
+//
+
+z_stream unpackStream;
+
+static int unpackInitialized;
+
+int Unpack8To8(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+int Unpack8To8(T_colormap *colormap, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+int Unpack8To16(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+int Unpack8To16(T_colormap *colormap, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+int Unpack8To24(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+int Unpack8To24(T_colormap *colormap, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+int Unpack8To32(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+int Unpack8To32(T_colormap *colormap, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+int Unpack15To16(const unsigned char *data, unsigned char *out,
+ unsigned char *end);
+
+int Unpack15To24(const unsigned char *data, unsigned char *out,
+ unsigned char *end);
+
+int Unpack15To32(const unsigned char *data, unsigned char *out,
+ unsigned char *end);
+
+int Unpack16To16(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+int Unpack16To16(const unsigned char *data, unsigned char *out,
+ unsigned char *end, int imageByteOrder);
+
+int Unpack16To24(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+int Unpack16To24(const unsigned char *data, unsigned char *out,
+ unsigned char *end, int imageByteOrder);
+
+int Unpack16To32(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+int Unpack16To32(const unsigned char *data, unsigned char *out,
+ unsigned char *end, int imageByteOrder);
+
+int Unpack24To24(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+int Unpack24To24(const unsigned char *data, unsigned char *out,
+ unsigned char *end);
+
+int Unpack24To32(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+int Unpack24To32(const unsigned char *data, unsigned char *out, unsigned char *end);
+
+int Unpack32To32(const T_colormask *colormask, const unsigned int *data,
+ unsigned int *out, unsigned int *end);
+
+
+void UnpackInit()
+{
+ if (unpackInitialized == 0)
+ {
+ unpackStream.zalloc = (alloc_func) 0;
+ unpackStream.zfree = (free_func) 0;
+ unpackStream.opaque = (voidpf) 0;
+
+ unpackStream.next_in = (Bytef *) 0;
+ unpackStream.avail_in = 0;
+
+ int result = inflateInit2(&unpackStream, 15);
+
+ if (result != Z_OK)
+ {
+ #ifdef PANIC
+ *logofs << "UnpackInit: PANIC! Cannot initialize the Z stream "
+ << "for decompression. Error is '" << zError(result)
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot initialize the Z stream for "
+ << "decompression. Error is '" << zError(result)
+ << "'.\n";
+ }
+ else
+ {
+ unpackInitialized = 1;
+ }
+ }
+}
+
+void UnpackDestroy()
+{
+ if (unpackInitialized == 1)
+ {
+ inflateEnd(&unpackStream);
+
+ unpackInitialized = 0;
+ }
+}
+
+//
+// Get bits per pixel set by client
+// according to display geometry.
+//
+
+int UnpackBitsPerPixel(T_geometry *geometry, unsigned int depth)
+{
+ switch (depth)
+ {
+ case 1:
+ {
+ return geometry -> depth1_bpp;
+ }
+ case 4:
+ {
+ return geometry -> depth4_bpp;
+ }
+ case 8:
+ {
+ return geometry -> depth8_bpp;
+ }
+ case 15:
+ case 16:
+ {
+ return geometry -> depth16_bpp;
+ }
+ case 24:
+ {
+ return geometry -> depth24_bpp;
+ }
+ case 32:
+ {
+ return geometry -> depth32_bpp;
+ }
+ default:
+ {
+ return 0;
+ }
+ }
+}
+
+int Unpack8To8(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack8To8: Unpacking " << end - out
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+
+ memcpy(out, data, end - out);
+
+ return 1;
+}
+
+int Unpack8To16(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack8To16: Unpacking " << end - out
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+
+ unsigned short *out16 = (unsigned short *) out;
+ unsigned short *end16 = (unsigned short *) end;
+
+ while (out16 < end16)
+ {
+ if (*data == 0)
+ {
+ *out16 = 0x0;
+ }
+ else if (*data == 0xff)
+ {
+ *out16 = 0xffff;
+ }
+ else
+ {
+ //
+ // Pixel layout:
+ //
+ // 8bits 00RRGGBB -> 16bits RR000GG0 000BB000.
+ //
+
+ *out16 = (((((*data & 0x30) << 2) | colormask -> correction_mask) << 8) & 0xf800) |
+ (((((*data & 0xc) << 4) | colormask -> correction_mask) << 3) & 0x7e0) |
+ (((((*data & 0x3) << 6) | colormask -> correction_mask) >> 3) & 0x1f);
+ }
+
+ out16++;
+ data++;
+ }
+
+ return 1;
+}
+
+int Unpack8To24(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack8To24: Unpacking " << end - out
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+
+ while (out < (end - 2))
+ {
+ if (*data == 0x00)
+ {
+ out[0] = out[1] = out[2] = 0x00;
+ }
+ else if (*data == 0xff)
+ {
+ out[0] = out[1] = out[2] = 0xff;
+ }
+ else
+ {
+ //
+ // Pixel layout:
+ //
+ // 8bits 00RRGGBB -> 24bits RR000000 GG00000 BB000000.
+ //
+
+ out[0] = (((*data & 0x30) << 2) | colormask -> correction_mask);
+ out[1] = (((*data & 0x0c) << 4) | colormask -> correction_mask);
+ out[2] = (((*data & 0x03) << 6) | colormask -> correction_mask);
+ }
+
+ out += 3;
+ data += 1;
+ }
+
+ return 1;
+}
+
+int Unpack8To32(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack8To32: Unpacking " << end - out
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+
+ unsigned int *out32 = (unsigned int *) out;
+ unsigned int *end32 = (unsigned int *) end;
+
+ while (out32 < end32)
+ {
+ if (*data == 0)
+ {
+ *out32 = 0x0;
+ }
+ else if (*data == 0xff)
+ {
+ *out32 = 0xffffff;
+ }
+ else
+ {
+ *out32 = ((((*data & 0x30) << 2) | colormask -> correction_mask) << 16) |
+ ((((*data & 0xc) << 4) | colormask -> correction_mask) << 8) |
+ (((*data & 0x3) << 6) | colormask -> correction_mask);
+ }
+
+ out32++;
+ data++;
+ }
+
+ return 1;
+}
+
+int Unpack8(T_geometry *geometry, const T_colormask *colormask, int src_depth, int src_width,
+ int src_height, unsigned char *src_data, int src_size, int dst_depth,
+ int dst_width, int dst_height, unsigned char *dst_data, int dst_size)
+{
+ int dst_bpp = UnpackBitsPerPixel(geometry, dst_depth);
+
+ int (*unpack)(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+ switch (dst_bpp)
+ {
+ case 8:
+ {
+ unpack = Unpack8To8;
+
+ break;
+ }
+ case 16:
+ {
+ unpack = Unpack8To16;
+
+ break;
+ }
+ case 24:
+ {
+ unpack = Unpack8To24;
+
+ break;
+ }
+ case 32:
+ {
+ unpack = Unpack8To32;
+
+ break;
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "Unpack8: PANIC! Bad destination bits per pixel "
+ << dst_bpp << ". Only 16/24/32 are supported.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ }
+
+ if (dst_bpp == 24)
+ {
+ unsigned char *dst_end = dst_data;
+
+ #ifdef TEST
+ *logofs << "Unpack8: Handling 24 bits with dst_size "
+ << dst_size << ".\n" << logofs_flush;
+ #endif
+
+ for (int y = 0; y < dst_height; y++)
+ {
+ dst_data = dst_end;
+
+ dst_end += RoundUp4(dst_width * 3);
+
+ (*unpack)(colormask, src_data, dst_data, dst_end);
+
+ src_data += src_width;
+ }
+ }
+ else
+ {
+ unsigned char *dst_end = dst_data + dst_size;
+
+ (*unpack)(colormask, src_data, dst_data, dst_end);
+ }
+
+ return 1;
+}
+
+int Unpack16To16(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack16To16: Unpacking " << end - out
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+
+ if (colormask -> correction_mask)
+ {
+ unsigned short *data16 = (unsigned short *) data;
+
+ unsigned short *out16 = (unsigned short *) out;
+ unsigned short *end16 = (unsigned short *) end;
+
+ while (out16 < end16)
+ {
+ if (*data16 == 0x0000)
+ {
+ *out16 = 0x0000;
+ }
+ else if (*data16 == 0xffff)
+ {
+ *out16 = 0xffff;
+ }
+ else
+ {
+ //
+ // Pixel layout:
+ //
+ // 16bit RRRRRGGG GG0BBBBB -> RRRRRGGG GGGBBBBB.
+ //
+
+ *out16 = (((((*data16 & 0xf100) >> 8) | colormask -> correction_mask) << 8) & 0xf800) |
+ (((((*data16 & 0x7c0) >> 3) | colormask -> correction_mask) << 3) & 0x7e0) |
+ (((((*data16 & 0x1f) << 3) | colormask -> correction_mask) >> 3) & 0x1f);
+ }
+
+ out16++;
+ data16++;
+ }
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Unpack16To16: Using bitwise copy due to null correction mask.\n"
+ << logofs_flush;
+ #endif
+
+ memcpy((unsigned char *) out, (unsigned char *) data, end - out);
+ }
+
+ return 1;
+}
+
+int Unpack16To24(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack16To24: Unpacking " << end - out
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+
+ unsigned short *data16 = (unsigned short *) data;
+
+
+ while (out < end - 2)
+ {
+ if (*data16 == 0x0)
+ {
+ out[0] = 0x00;
+ out[1] = 0x00;
+ out[2] = 0x00;
+ }
+ else if (*data16 == 0xffff)
+ {
+ out[0] = 0xff;
+ out[1] = 0xff;
+ out[2] = 0xff;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Unpack16To24: Pixel [" << *data16 << "]\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Pixel layout:
+ //
+ // 16bit 0RRRRRGG GGGBBBBB -> 24 bit RRRRR000 GGGGG000 BBBBB000
+ //
+
+ out[0] = (((*data16 & 0x7c00) >> 7) | colormask -> correction_mask);
+ out[1] = (((*data16 & 0x03e0) >> 2) | colormask -> correction_mask);
+ out[2] = (((*data16 & 0x001f) << 3) | colormask -> correction_mask);
+ }
+
+ out += 3;
+ data16 += 1;
+ }
+
+ return 1;
+}
+
+int Unpack16To32(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack16To32: Unpacking " << end - out
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+
+ unsigned short *data16 = (unsigned short *) data;
+
+ unsigned int *out32 = (unsigned int *) out;
+ unsigned int *end32 = (unsigned int *) end;
+
+ while (out32 < end32)
+ {
+ if (*data16 == 0x0)
+ {
+ *out32 = 0x0;
+ }
+ else if (*data16 == 0xffff)
+ {
+ *out32 = 0xffffff;
+ }
+ else
+ {
+ *out32 = ((((*data16 & 0x7c00) >> 7) | colormask -> correction_mask) << 16) |
+ ((((*data16 & 0x3e0) >> 2) | colormask -> correction_mask) << 8) |
+ (((*data16 & 0x1f) << 3) | colormask -> correction_mask);
+ }
+
+ out32++;
+ data16++;
+ }
+
+ return 1;
+}
+
+int Unpack16(T_geometry *geometry, const T_colormask *colormask, int src_depth, int src_width,
+ int src_height, unsigned char *src_data, int src_size, int dst_depth,
+ int dst_width, int dst_height, unsigned char *dst_data, int dst_size)
+{
+ int dst_bpp = UnpackBitsPerPixel(geometry, dst_depth);
+
+ int (*unpack)(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+ switch (dst_bpp)
+ {
+ case 16:
+ {
+ unpack = Unpack16To16;
+
+ break;
+ }
+ case 24:
+ {
+ unpack = Unpack16To24;
+
+ break;
+ }
+ case 32:
+ {
+ unpack = Unpack16To32;
+
+ break;
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "Unpack16: PANIC! Bad destination bits per pixel "
+ << dst_bpp << ". Only 24/32 are supported.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ }
+
+ if (dst_bpp == 24)
+ {
+ unsigned char *dst_end = dst_data;
+
+ for (int y = 0; y < dst_height; y++)
+ {
+ dst_data = dst_end;
+
+ dst_end += RoundUp4(dst_width * 3);
+
+ (*unpack)(colormask, src_data, dst_data, dst_end);
+
+ src_data += (src_width * 2);
+ }
+
+ }
+ else
+ {
+ unsigned char *dst_end = dst_data + dst_size;
+
+ (*unpack)(colormask, src_data, dst_data, dst_end);
+ }
+
+ return 1;
+}
+
+int Unpack24To24(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack24To24: Unpacking " << end - out
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+
+ if (colormask -> correction_mask)
+ {
+ while (out < end)
+ {
+ if (data[0] == 0x00 &&
+ data[1] == 0x00 &&
+ data[2] == 0x00)
+ {
+ out[0] = out[1] = out[2] = 0x00;
+ }
+ else if (data[0] == 0xff &&
+ data[1] == 0xff &&
+ data[2] == 0xff)
+ {
+ out[0] = out[1] = out[2] = 0xff;
+ }
+ else
+ {
+ out[0] = (data[0] | colormask -> correction_mask);
+ out[1] = (data[1] | colormask -> correction_mask);
+ out[2] = (data[2] | colormask -> correction_mask);
+ }
+
+ out += 3;
+ data += 3;
+ }
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Unpack24To24: Using bitwise copy due to null correction mask.\n"
+ << logofs_flush;
+ #endif
+
+ memcpy((unsigned char *) out, (unsigned char *) data, end - out);
+ }
+
+ return 1;
+}
+
+int Unpack24To32(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack24To32: Unpacking " << end - out
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+
+ unsigned int *out32 = (unsigned int *) out;
+ unsigned int *end32 = (unsigned int *) end;
+
+ while (out32 < end32)
+ {
+ if (colormask -> color_mask == 0xff)
+ {
+ *out32 = (data[0] << 16) | (data[1] << 8) | data[2];
+ }
+ else
+ {
+ if (data[0] == 0x0 && data[1] == 0x0 && data[2] == 0x0)
+ {
+ *out32 = 0x0;
+ }
+ else if (data[0] == 0xff && data[1] == 0xff && data[2] == 0xff)
+ {
+ *out32 = 0xffffff;
+ }
+ else
+ {
+ *out32 = (((unsigned int) data[0] | colormask -> correction_mask) << 16) |
+ (((unsigned int) data[1] | colormask -> correction_mask) << 8) |
+ ((unsigned int) data[2] | colormask -> correction_mask);
+ }
+ }
+
+ out32 += 1;
+ data += 3;
+ }
+
+ return 1;
+}
+
+int Unpack24(T_geometry *geometry, const T_colormask *colormask, int src_depth, int src_width,
+ int src_height, unsigned char *src_data, int src_size, int dst_depth,
+ int dst_width, int dst_height, unsigned char *dst_data, int dst_size)
+{
+ int dst_bpp = UnpackBitsPerPixel(geometry, dst_depth);
+
+ int (*unpack)(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+ switch (dst_bpp)
+ {
+ case 24:
+ {
+ unpack = Unpack24To24;
+
+ break;
+ }
+ case 32:
+ {
+ unpack = Unpack24To32;
+
+ break;
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "Unpack24: PANIC! Bad destination bits per pixel "
+ << dst_bpp << ". Only 32 is supported.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ }
+
+ if (dst_bpp == 24)
+ {
+ unsigned char *dst_end;
+ unsigned long scanline_size = RoundUp4(dst_width * dst_bpp / 8);
+
+ dst_end = dst_data;
+
+ #ifdef TEST
+ *logofs << "Unpack24: Handling 24 bits with dst_height "
+ << dst_height << " scanline_size " << scanline_size
+ << " dst_size " << dst_size << ".\n" << logofs_flush;
+ #endif
+
+ for (int y = 0; y < dst_height; y++)
+ {
+ dst_data = dst_end;
+
+ dst_end += scanline_size;
+
+ (*unpack)(colormask, src_data, dst_data, dst_end);
+
+ src_data += scanline_size;
+ }
+ }
+ else
+ {
+ unsigned char *dst_end = dst_data + dst_size;
+
+ (*unpack)(colormask, src_data, dst_data, dst_end);
+ }
+
+ return 1;
+}
+
+int Unpack8To8(T_colormap *colormap, const unsigned char *data,
+ unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack8To8: Unpacking " << end - out
+ << " bytes of colormapped data.\n"
+ << logofs_flush;
+ #endif
+
+ while (out < end)
+ {
+ *(out++) = (unsigned char) colormap -> data[*(data++)];
+ }
+
+ return 1;
+}
+
+int Unpack8To16(T_colormap *colormap, const unsigned char *data,
+ unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack8To16: Unpacking " << end - out
+ << " bytes of colormapped data.\n"
+ << logofs_flush;
+ #endif
+
+ unsigned short *out16 = (unsigned short *) out;
+ unsigned short *end16 = (unsigned short *) end;
+
+ while (out16 < end16)
+ {
+ *(out16++) = (unsigned short) colormap -> data[*(data++)];
+ }
+
+ return 1;
+}
+
+int Unpack8To24(T_colormap *colormap, const unsigned char *data,
+ unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack8To24: Unpacking " << end - out
+ << " bytes of colormapped data.\n"
+ << logofs_flush;
+ #endif
+
+ unsigned int value;
+
+ while (out < end)
+ {
+ value = colormap -> data[*(data++)];
+
+ *(out++) = value;
+ *(out++) = value >> 8;
+ *(out++) = value >> 16;
+ }
+
+ return 1;
+}
+
+int Unpack8To32(T_colormap *colormap, const unsigned char *data,
+ unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack8To32: Unpacking " << end - out
+ << " bytes of colormapped data.\n"
+ << logofs_flush;
+ #endif
+
+ unsigned int *out32 = (unsigned int *) out;
+ unsigned int *end32 = (unsigned int *) end;
+
+ while (out32 < end32)
+ {
+ *(out32++) = colormap -> data[*(data++)];
+ }
+
+ return 1;
+}
+
+int Unpack8(T_geometry *geometry, T_colormap *colormap, int src_depth, int src_width, int src_height,
+ unsigned char *src_data, int src_size, int dst_depth, int dst_width,
+ int dst_height, unsigned char *dst_data, int dst_size)
+{
+ if (src_depth != 8)
+ {
+ #ifdef PANIC
+ *logofs << "Unpack8: PANIC! Cannot unpack colormapped image of source depth "
+ << src_depth << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ int (*unpack)(T_colormap *colormap, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+ int dst_bpp = UnpackBitsPerPixel(geometry, dst_depth);
+
+ switch (dst_bpp)
+ {
+ case 8:
+ {
+ unpack = Unpack8To8;
+
+ break;
+ }
+ case 16:
+ {
+ unpack = Unpack8To16;
+
+ break;
+ }
+ case 24:
+ {
+ unpack = Unpack8To24;
+
+ break;
+ }
+ case 32:
+ {
+ unpack = Unpack8To32;
+
+ break;
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "Unpack8: PANIC! Bad destination bits per pixel "
+ << dst_bpp << ". Only 8/16/24/32 are supported.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ }
+
+ if (src_width == dst_width &&
+ src_height == dst_height)
+ {
+ unsigned char *dst_end = dst_data + dst_size;
+
+ (*unpack)(colormap, src_data, dst_data, dst_end);
+ }
+ else if (src_width >= dst_width &&
+ src_height >= dst_height)
+ {
+ unsigned char *dst_end = dst_data;
+
+ for (int y = 0; y < dst_height; y++)
+ {
+ dst_data = dst_end;
+
+ dst_end += RoundUp4(dst_width * dst_bpp / 8);
+
+ (*unpack)(colormap, src_data, dst_data, dst_end);
+
+ src_data += src_width;
+ }
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Unpack8: PANIC! Cannot unpack image. "
+ << "Destination area " << dst_width << "x"
+ << dst_height << " is not fully contained in "
+ << src_width << "x" << src_height << " source.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int Unpack15To16(const unsigned char *data, unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack15To16: Unpacking " << end - out
+ << " bytes of colormapped data.\n"
+ << logofs_flush;
+ #endif
+
+ unsigned short *data16 = (unsigned short *) data;
+ unsigned short *out16 = (unsigned short *) out;
+ unsigned short *end16 = (unsigned short *) end;
+
+ while (out16 < end16)
+ {
+ if (*data16 == 0x0)
+ {
+ *out16 = 0x0;
+ }
+ else if (*data16 == 0x7fff)
+ {
+ *out16 = 0xffff;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Unpack15To16: Pixel [" << *data16 << "]\n"
+ << logofs_flush;
+ #endif
+
+ *out16 = ((*data16 & 0x7ff0) << 1) |
+ (*data16 & 0x001f);
+ }
+
+ out16 += 1;
+ data16 += 1;
+ }
+
+ return 1;
+}
+
+int Unpack15To24(const unsigned char *data, unsigned char *out, unsigned char *end)
+
+{
+ #ifdef TEST
+ *logofs << "Unpack15To24: Unpacking " << end - out
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+
+ unsigned short *data16 = (unsigned short *) data;
+
+ while (out < end - 2)
+ {
+ if (*data16 == 0x0)
+ {
+ out[0] = 0x00;
+ out[1] = 0x00;
+ out[2] = 0x00;
+ }
+ else if (*data16 == 0x7fff)
+ {
+ out[0] = 0xff;
+ out[1] = 0xff;
+ out[2] = 0xff;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Unpack15To24: Pixel [" << *data16 << "]\n"
+ << logofs_flush;
+ #endif
+
+ out[0] = ((*data16 >> 7) & 0xf8) | ((*data16 >> 12) & 0x07);
+ out[1] = ((*data16 >> 2) & 0xf8) | ((*data16 >> 8) & 0x07);
+ out[2] = ((*data16 << 3) & 0xf8) | ((*data16 >> 2) & 0x07);
+ }
+
+ out += 3;
+ data16 += 1;
+ }
+
+ return 1;
+}
+
+int Unpack15To32(const unsigned char *data, unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack15To32: Unpacking " << end - out
+ << " bytes of data.\n"
+ << logofs_flush;
+ #endif
+
+ unsigned short *data16 = (unsigned short *) data;
+ unsigned int *out32 = (unsigned int *) out;
+ unsigned int *end32 = (unsigned int *) end;
+
+ while (out32 < end32)
+ {
+ if (*data16 == 0x0)
+ {
+ *out32 = 0x0;
+ }
+ else if (*data16 == 0xffff)
+ {
+ *out32 = 0xffffff;
+ }
+ else
+ {
+ *out32 = ((((*data16 >> 7) & 0xf8) | ((*data16 >> 12) & 0x07)) << 16) |
+ ((((*data16 >> 2) & 0xf8) | ((*data16 >> 8) & 0x07)) << 8) |
+ (((*data16 << 3) & 0xf8) | ((*data16 >> 2) & 0x07));
+ }
+
+ out32++;
+ data16++;
+ }
+
+ return 1;
+}
+
+int Unpack15(T_geometry *geometry, int src_depth, int src_width, int src_height,
+ unsigned char *src_data, int src_size, int dst_depth, int dst_width,
+ int dst_height, unsigned char *dst_data, int dst_size)
+{
+ if (src_depth != 16)
+ {
+ #ifdef PANIC
+ *logofs << "Unpack15: PANIC! Cannot unpack colormapped image of source depth "
+ << src_depth << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ int (*unpack)(const unsigned char *data, unsigned char *out, unsigned char *end);
+
+ int dst_bpp = UnpackBitsPerPixel(geometry, dst_depth);
+
+ switch (dst_bpp)
+ {
+ case 16:
+ {
+ unpack = Unpack15To16;
+
+ break;
+ }
+ case 24:
+ {
+ unpack = Unpack15To24;
+
+ break;
+ }
+ case 32:
+ {
+ unpack = Unpack15To32;
+
+ break;
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "Unpack15: PANIC! Bad destination bits per pixel "
+ << dst_bpp << ". Only 16/24/32 are supported.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ }
+
+ if (src_width == dst_width && src_height == dst_height)
+ {
+ unsigned char *dst_end = dst_data + dst_size;
+
+ (*unpack)(src_data, dst_data, dst_end);
+ }
+ else if (src_width >= dst_width && src_height >= dst_height)
+ {
+ unsigned char *dst_end = dst_data;
+
+ for (int y = 0; y < dst_height; y++)
+ {
+ dst_data = dst_end;
+ dst_end += RoundUp4(dst_width * dst_bpp / 8);
+
+ (*unpack)(src_data, dst_data, dst_end);
+
+ src_data += src_width * 2;
+ }
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Unpack15: PANIC! Cannot unpack image. "
+ << "Destination area " << dst_width << "x"
+ << dst_height << " is not fully contained in "
+ << src_width << "x" << src_height << " source.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int Unpack16To16(const unsigned char *data, unsigned char *out,
+ unsigned char *end, int imageByteOrder)
+{
+ #ifdef TEST
+ *logofs << "Unpack16To16: Unpacking " << end - out
+ << " bytes of colormapped data.\n"
+ << logofs_flush;
+ #endif
+
+ memcpy((unsigned char *) out, (unsigned char *) data, end - out);
+
+ return 1;
+}
+
+int Unpack16To24(const unsigned char *data, unsigned char *out,
+ unsigned char *end, int imageByteOrder)
+
+{
+ #ifdef TEST
+ *logofs << "Unpack16To24: Unpacking " << end - out
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+
+ unsigned short *data16 = (unsigned short *) data;
+
+ while (out < end - 2)
+ {
+ if (*data16 == 0x0)
+ {
+ out[0] = 0x00;
+ out[1] = 0x00;
+ out[2] = 0x00;
+ }
+ else if (*data16 == 0xffff)
+ {
+ out[0] = 0xff;
+ out[1] = 0xff;
+ out[2] = 0xff;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Unpack16To24: Pixel [" << *data16 << "]\n"
+ << logofs_flush;
+ #endif
+
+ out[0] = ((*data16 >> 8) & 0xf8) | ((*data16 >> 13) & 0x07);
+ out[1] = ((*data16 >> 3) & 0xfc) | ((*data16 >> 9) & 0x03);
+ out[2] = ((*data16 << 3) & 0xf8) | ((*data16 >> 2) & 0x07);
+ }
+
+ out += 3;
+ data16 += 1;
+ }
+
+ return 1;
+}
+
+
+int Unpack16To32(const unsigned char *data, unsigned char *out,
+ unsigned char *end, int imageByteOrder)
+{
+ #ifdef TEST
+ *logofs << "Unpack16To32: Unpacking " << end - out
+ << " bytes of data.\n"
+ << logofs_flush;
+ #endif
+
+ unsigned short *data16 = (unsigned short *) data;
+ unsigned int *out32 = (unsigned int *) out;
+ unsigned int *end32 = (unsigned int *) end;
+
+ unsigned short pixel16;
+ unsigned int pixel32;
+
+ while (out32 < end32)
+ {
+
+ pixel16 = GetUINT((unsigned char *)data16, 0);
+
+ if (pixel16 == 0x0)
+ {
+ PutULONG(0x0, (unsigned char *) out32, imageByteOrder);
+ }
+ else if (pixel16 == 0xffff)
+ {
+ PutULONG(0xffffff, (unsigned char *) out32, imageByteOrder);
+ }
+ else
+ {
+ pixel32 = ((((pixel16 >> 8) & 0xf8) | ((pixel16 >> 13) & 0x07)) << 16) |
+ ((((pixel16 >> 3) & 0xfc) | ((pixel16 >> 9) & 0x03)) << 8) |
+ (((pixel16 << 3) & 0xf8) | ((pixel16 >> 2) & 0x07));
+
+ PutULONG(pixel32, (unsigned char *) out32, imageByteOrder);
+ }
+
+ out32++;
+ data16++;
+ }
+ return 1;
+}
+
+int Unpack16(T_geometry *geometry, int src_depth, int src_width, int src_height,
+ unsigned char *src_data, int src_size, int dst_depth, int dst_width,
+ int dst_height, unsigned char *dst_data, int dst_size)
+{
+ int imageByteOrder = geometry -> image_byte_order;
+
+ if (src_depth != 16)
+ {
+ #ifdef PANIC
+ *logofs << "Unpack16: PANIC! Cannot unpack colormapped image of source depth "
+ << src_depth << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ int (*unpack)(const unsigned char *data, unsigned char *out,
+ unsigned char *end, int imageByteOrder);
+
+ int dst_bpp = UnpackBitsPerPixel(geometry, dst_depth);
+
+ switch (dst_bpp)
+ {
+ case 16:
+ {
+ unpack = Unpack16To16;
+
+ break;
+ }
+ case 24:
+ {
+ unpack = Unpack16To24;
+
+ break;
+ }
+ case 32:
+ {
+ unpack = Unpack16To32;
+
+ break;
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "Unpack16: PANIC! Bad destination bits per pixel "
+ << dst_bpp << ". Only 16/24/32 are supported.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ }
+
+ if (src_width == dst_width && src_height == dst_height)
+ {
+ unsigned char *dst_end = dst_data + dst_size;
+
+ (*unpack)(src_data, dst_data, dst_end, imageByteOrder);
+ }
+ else if (src_width >= dst_width && src_height >= dst_height)
+ {
+ unsigned char *dst_end = dst_data;
+
+ for (int y = 0; y < dst_height; y++)
+ {
+ dst_data = dst_end;
+ dst_end += RoundUp4(dst_width * dst_bpp / 8);
+
+ (*unpack)(src_data, dst_data, dst_end, imageByteOrder);
+
+ src_data += src_width * 2;
+ }
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Unpack16: PANIC! Cannot unpack image. "
+ << "Destination area " << dst_width << "x"
+ << dst_height << " is not fully contained in "
+ << src_width << "x" << src_height << " source.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int Unpack24To24(const unsigned char *data,
+ unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack124To24: Unpacking " << end - out
+ << " bytes of colormapped data.\n"
+ << logofs_flush;
+ #endif
+
+ while (out < end)
+ {
+ *(out++) = *(data++);
+ }
+
+ return 1;
+}
+
+int Unpack24To32(const unsigned char *data, unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack24To32: Unpacking " << end - out
+ << " bytes of colormapped data.\n"
+ << logofs_flush;
+ #endif
+
+ unsigned int *out32 = (unsigned int *) out;
+ unsigned int *end32 = (unsigned int *) end;
+
+ while (out32 < end32)
+ {
+ if (data[0] == 0x0 && data[1] == 0x0 && data[2] == 0x0)
+ {
+ *out32 = 0x0;
+ }
+ else if (data[0] == 0xff && data[1] == 0xff && data[2] == 0xff)
+ {
+ *out32 = 0xffffff;
+ }
+ else
+ {
+ *out32 = (data[2] << 16) | (data[1] << 8) | data[0];
+ }
+
+ out32 += 1;
+ data += 3;
+ }
+
+ return 1;
+}
+
+int Unpack24(T_geometry *geometry, int src_depth, int src_width, int src_height,
+ unsigned char *src_data, int src_size, int dst_depth, int dst_width,
+ int dst_height, unsigned char *dst_data, int dst_size)
+
+{
+ if (src_depth != 24)
+ {
+ #ifdef PANIC
+ *logofs << "Unpack24: PANIC! Cannot unpack colormapped image of source depth "
+ << src_depth << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ int (*unpack)(const unsigned char *data, unsigned char *out, unsigned char *end);
+
+ int dst_bpp = UnpackBitsPerPixel(geometry, dst_depth);
+
+ switch (dst_bpp)
+ {
+ case 24:
+ {
+ unpack = Unpack24To24;
+
+ break;
+ }
+ case 32:
+ {
+ unpack = Unpack24To32;
+
+ break;
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "Unpack24: PANIC! Bad destination bits per pixel "
+ << dst_bpp << ". Only 24/32 are supported.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ }
+
+ if (src_width == dst_width && src_height == dst_height)
+ {
+ unsigned char *dst_end = dst_data + dst_size;
+
+ (*unpack)(src_data, dst_data, dst_end);
+ }
+ else if (src_width >= dst_width && src_height >= dst_height)
+ {
+ unsigned char *dst_end = dst_data;
+
+ for (int y = 0; y < dst_height; y++)
+ {
+ dst_data = dst_end;
+ dst_end += RoundUp4(dst_width * dst_bpp / 8);
+
+ (*unpack)(src_data, dst_data, dst_end);
+
+ src_data += src_width * 3;
+ }
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Unpack24: PANIC! Cannot unpack image. "
+ << "Destination area " << dst_width << "x"
+ << dst_height << " is not fully contained in "
+ << src_width << "x" << src_height << " source.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int Unpack32To32(const T_colormask *colormask, const unsigned int *data,
+ unsigned int *out, unsigned int *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack32To32: Unpacking " << end - out
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+
+ if (colormask -> correction_mask)
+ {
+ while (out < end)
+ {
+ if (*data == 0x00000000)
+ {
+ *out = 0x00000000;
+ }
+ else if (*data == 0xFFFFFFFF)
+ {
+ *out = 0xFFFFFFFF;
+ }
+ else
+ {
+ *out = *data | ((colormask -> correction_mask << 16) |
+ (colormask -> correction_mask << 8) |
+ colormask -> correction_mask);
+ }
+
+ out += 1;
+ data += 1;
+ }
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Unpack32To32: Using bitwise copy due to null correction mask.\n"
+ << logofs_flush;
+ #endif
+
+ memcpy((unsigned char *) out, (unsigned char *) data, end - out);
+ }
+
+ return 1;
+}
diff --git a/nxcomp/Unpack.h b/nxcomp/Unpack.h
new file mode 100644
index 000000000..65a410fb6
--- /dev/null
+++ b/nxcomp/Unpack.h
@@ -0,0 +1,141 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef Unpack_H
+#define Unpack_H
+
+#include "NXpack.h"
+
+#include "Z.h"
+
+#define LSBFirst 0
+#define MSBFirst 1
+
+#define SPLIT_PATTERN 0x88
+
+typedef ColorMask T_colormask;
+
+//
+// Pixel geometry of channel's display.
+//
+
+typedef struct
+{
+ unsigned int depth1_bpp;
+ unsigned int depth4_bpp;
+ unsigned int depth8_bpp;
+ unsigned int depth16_bpp;
+ unsigned int depth24_bpp;
+ unsigned int depth32_bpp;
+
+ unsigned int red_mask;
+ unsigned int green_mask;
+ unsigned int blue_mask;
+
+ unsigned int image_byte_order;
+ unsigned int bitmap_bit_order;
+ unsigned int scanline_unit;
+ unsigned int scanline_pad;
+
+} T_geometry;
+
+//
+// Colormap is used to remap colors
+// from source to destination depth.
+//
+
+typedef struct
+{
+ unsigned int entries;
+ unsigned int *data;
+
+} T_colormap;
+
+//
+// Alpha channel data is added to 32
+// bits images at the time they are
+// unpacked.
+//
+
+typedef struct
+{
+ unsigned int entries;
+ unsigned char *data;
+
+} T_alpha;
+
+//
+// The ZLIB stream structure used for
+// the decompression.
+//
+
+extern z_stream unpackStream;
+
+//
+// Initialize the ZLIB stream used for
+// decompression.
+//
+
+void UnpackInit();
+
+//
+// Free the ZLIB stream.
+//
+
+void UnpackDestroy();
+
+//
+// Get the destination bits per pixel
+// based on the drawable depth.
+//
+
+int UnpackBitsPerPixel(T_geometry *geometry, unsigned int depth);
+
+//
+// Unpack the source data into the X
+// bitmap.
+//
+
+int Unpack8(T_geometry *geometry, const T_colormask *colormask, int src_depth, int src_width,
+ int src_height, unsigned char *src_data, int src_size, int dst_depth,
+ int dst_width, int dst_height, unsigned char *dst_data, int dst_size);
+
+int Unpack16(T_geometry *geometry, const T_colormask *colormask, int src_depth, int src_width,
+ int src_height, unsigned char *src_data, int src_size, int dst_depth,
+ int dst_width, int dst_height, unsigned char *dst_data, int dst_size);
+
+int Unpack24(T_geometry *geometry, const T_colormask *colormask, int src_depth, int src_width,
+ int src_height, unsigned char *src_data, int src_size, int dst_depth,
+ int dst_width, int dst_height, unsigned char *dst_data, int dst_size);
+
+int Unpack8(T_geometry *geometry, T_colormap *colormap, int src_depth, int src_width,
+ int src_height, unsigned char *src_data, int src_size, int dst_depth,
+ int dst_width, int dst_height, unsigned char *dst_data, int dst_size);
+
+int Unpack15(T_geometry *geometry, int src_depth, int src_width,
+ int src_height, unsigned char *src_data, int src_size, int dst_depth,
+ int dst_width, int dst_height, unsigned char *dst_data, int dst_size);
+
+int Unpack16(T_geometry *geometry, int src_depth, int src_width,
+ int src_height, unsigned char *src_data, int src_size, int dst_depth,
+ int dst_width, int dst_height, unsigned char *dst_data, int dst_size);
+
+int Unpack24(T_geometry *geometry, int src_depth, int src_width,
+ int src_height, unsigned char *src_data, int src_size, int dst_depth,
+ int dst_width, int dst_height, unsigned char *dst_data, int dst_size);
+
+#endif /* Unpack_H */
diff --git a/nxcomp/Utils.cpp b/nxcomp/Utils.cpp
new file mode 100644
index 000000000..a2820782a
--- /dev/null
+++ b/nxcomp/Utils.cpp
@@ -0,0 +1,35 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "NX.h"
+
+#include "Timestamp.h"
+
+//
+// Log level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+int NXTransSearch(char *data, int size, int step, int threshold)
+{
+ return -1;
+}
+
diff --git a/nxcomp/VERSION b/nxcomp/VERSION
new file mode 100644
index 000000000..1545d9665
--- /dev/null
+++ b/nxcomp/VERSION
@@ -0,0 +1 @@
+3.5.0
diff --git a/nxcomp/Vars.c b/nxcomp/Vars.c
new file mode 100644
index 000000000..0d93a6dc6
--- /dev/null
+++ b/nxcomp/Vars.c
@@ -0,0 +1,46 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+
+#include "NXvars.h"
+
+/*
+ * Allocate here instances of variables and
+ * pointers declared in NXvars.h.
+ */
+
+int _NXHandleDisplayError = 0;
+
+NXDisplayErrorPredicate _NXDisplayErrorFunction = NULL;
+
+int _NXUnsetLibraryPath = 0;
+
+NXLostSequenceHandler _NXLostSequenceFunction = NULL;
+
+NXDisplayBlockHandler _NXDisplayBlockFunction = NULL;
+NXDisplayWriteHandler _NXDisplayWriteFunction = NULL;
+NXDisplayFlushHandler _NXDisplayFlushFunction = NULL;
+NXDisplayStatisticsHandler _NXDisplayStatisticsFunction = NULL;
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/nxcomp/WriteBuffer.cpp b/nxcomp/WriteBuffer.cpp
new file mode 100644
index 000000000..ac38fe688
--- /dev/null
+++ b/nxcomp/WriteBuffer.cpp
@@ -0,0 +1,488 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "Misc.h"
+#include "Control.h"
+
+#include "WriteBuffer.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+WriteBuffer::WriteBuffer()
+{
+ size_ = WRITE_BUFFER_DEFAULT_SIZE;
+ buffer_ = new unsigned char[size_];
+ length_ = 0;
+ index_ = NULL;
+
+ scratchLength_ = 0;
+ scratchBuffer_ = NULL;
+ scratchOwner_ = 1;
+
+ initialSize_ = WRITE_BUFFER_DEFAULT_SIZE;
+ thresholdSize_ = WRITE_BUFFER_DEFAULT_SIZE << 1;
+ maximumSize_ = WRITE_BUFFER_DEFAULT_SIZE << 4;
+
+ #ifdef VALGRIND
+
+ memset(buffer_, '\0', size_);
+
+ #endif
+}
+
+WriteBuffer::~WriteBuffer()
+{
+ if (scratchOwner_ == 1 &&
+ scratchBuffer_ != NULL)
+ {
+ delete [] scratchBuffer_;
+ }
+
+ delete [] buffer_;
+}
+
+void WriteBuffer::setSize(unsigned int initialSize, unsigned int thresholdSize,
+ unsigned int maximumSize)
+{
+ initialSize_ = initialSize;
+ thresholdSize_ = thresholdSize;
+ maximumSize_ = maximumSize;
+
+ #ifdef TEST
+ *logofs << "WriteBuffer: Set buffer sizes to "
+ << initialSize_ << "/" << thresholdSize_
+ << "/" << maximumSize_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+
+void WriteBuffer::partialReset()
+{
+ if (scratchBuffer_ != NULL)
+ {
+ if (scratchOwner_)
+ {
+ #ifdef DEBUG
+ *logofs << "WriteBuffer: Going to delete "
+ << scratchLength_ << " bytes from the "
+ << "scratch buffer.\n" << logofs_flush;
+ #endif
+
+ delete [] scratchBuffer_;
+ }
+
+ scratchLength_ = 0;
+ scratchBuffer_ = NULL;
+ scratchOwner_ = 1;
+ }
+
+ length_ = 0;
+ index_ = NULL;
+
+ #ifdef DEBUG
+ *logofs << "WriteBuffer: Performed partial reset with "
+ << size_ << " bytes in buffer.\n"
+ << logofs_flush;
+ #endif
+}
+
+void WriteBuffer::fullReset()
+{
+ if (scratchBuffer_ != NULL)
+ {
+ if (scratchOwner_ == 1)
+ {
+ #ifdef DEBUG
+ *logofs << "WriteBuffer: Going to delete "
+ << scratchLength_ << " bytes from the "
+ << "scratch buffer.\n" << logofs_flush;
+ #endif
+
+ delete [] scratchBuffer_;
+ }
+
+ scratchLength_ = 0;
+ scratchBuffer_ = NULL;
+ scratchOwner_ = 1;
+ }
+
+ length_ = 0;
+ index_ = NULL;
+
+ if (size_ > initialSize_)
+ {
+ #ifdef TEST
+ *logofs << "WriteBuffer: Reallocating a new buffer of "
+ << initialSize_ << " bytes.\n" << logofs_flush;
+ #endif
+
+ delete [] buffer_;
+
+ size_ = initialSize_;
+
+ buffer_ = new unsigned char[size_];
+
+ if (buffer_ == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "WriteBuffer: PANIC! Can't allocate memory for "
+ << "X messages in context [A].\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory for "
+ << "X messages in context [A].\n";
+
+ HandleAbort();
+ }
+
+ #ifdef VALGRIND
+
+ memset(buffer_, '\0', size_);
+
+ #endif
+ }
+
+ #ifdef DEBUG
+ *logofs << "WriteBuffer: Performed full reset with "
+ << size_ << " bytes in buffer.\n"
+ << logofs_flush;
+ #endif
+}
+
+unsigned char *WriteBuffer::addMessage(unsigned int numBytes)
+{
+ #ifdef DEBUG
+ *logofs << "WriteBuffer: Adding " << numBytes << " bytes to "
+ << length_ << " bytes already in buffer.\n"
+ << logofs_flush;
+ #endif
+
+ if (numBytes > WRITE_BUFFER_OVERFLOW_SIZE)
+ {
+ #ifdef PANIC
+ *logofs << "WriteBuffer: PANIC! Can't add a message of "
+ << numBytes << " bytes.\n" << logofs_flush;
+
+ *logofs << "WriteBuffer: PANIC! Assuming error handling "
+ << "data in context [B].\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't add a message of "
+ << numBytes << " bytes to write buffer.\n";
+
+ cerr << "Error" << ": Assuming error handling "
+ << "data in context [B].\n";
+
+ HandleAbort();
+ }
+ else if (length_ + numBytes > size_)
+ {
+ unsigned int newSize = thresholdSize_;
+
+ while (newSize < length_ + numBytes)
+ {
+ newSize <<= 1;
+
+ if (newSize > maximumSize_)
+ {
+ newSize = length_ + numBytes + initialSize_;
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "WriteBuffer: Growing buffer from "
+ << size_ << " to " << newSize << " bytes.\n"
+ << logofs_flush;
+ #endif
+
+ unsigned int indexOffset = 0;
+
+ if (index_ && *index_)
+ {
+ indexOffset = *index_ - buffer_;
+ }
+
+ size_ = newSize;
+
+ unsigned char *newBuffer = new unsigned char[size_];
+
+ if (newBuffer == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "WriteBuffer: PANIC! Can't allocate memory for "
+ << "X messages in context [C].\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory for "
+ << "X messages in context [C].\n";
+
+ HandleAbort();
+ }
+
+ #ifdef TEST
+ if (newSize >= maximumSize_)
+ {
+ *logofs << "WriteBuffer: WARNING! Buffer grown to reach "
+ << "size of " << newSize << " bytes.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ #ifdef VALGRIND
+
+ memset(newBuffer, '\0', size_);
+
+ #endif
+
+ memcpy(newBuffer, buffer_, length_);
+
+ #ifdef DEBUG
+ *logofs << "WriteBuffer: Going to delete the "
+ << "old buffer with new size " << size_
+ << ".\n" << logofs_flush;
+ #endif
+
+ delete [] buffer_;
+
+ buffer_ = newBuffer;
+
+ if (index_ && *index_)
+ {
+ *index_ = buffer_ + indexOffset;
+ }
+ }
+
+ unsigned char *result = buffer_ + length_;
+
+ length_ += numBytes;
+
+ #ifdef DEBUG
+ *logofs << "WriteBuffer: Bytes in buffer are "
+ << length_ << " while size is " << size_
+ << ".\n" << logofs_flush;
+ #endif
+
+ return result;
+}
+
+unsigned char *WriteBuffer::removeMessage(unsigned int numBytes)
+{
+ #ifdef TEST
+ *logofs << "WriteBuffer: Removing " << numBytes
+ << " bytes from buffer.\n" << logofs_flush;
+ #endif
+
+ if (numBytes > length_)
+ {
+ #ifdef PANIC
+ *logofs << "WriteBuffer: PANIC! Can't remove "
+ << numBytes << " bytes with only " << length_
+ << " bytes in buffer.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Buffer underflow handling "
+ << "write buffer in context [D].\n";
+
+ HandleAbort();
+ }
+
+ length_ -= numBytes;
+
+ #ifdef TEST
+ *logofs << "WriteBuffer: Bytes in buffer are now "
+ << length_ << " while size is "
+ << size_ << ".\n" << logofs_flush;
+ #endif
+
+ return (buffer_ + length_);
+}
+
+unsigned char *WriteBuffer::addScratchMessage(unsigned int numBytes)
+{
+ if (numBytes > WRITE_BUFFER_OVERFLOW_SIZE)
+ {
+ #ifdef PANIC
+ *logofs << "WriteBuffer: PANIC! Can't add a message of "
+ << numBytes << " bytes.\n" << logofs_flush;
+
+ *logofs << "WriteBuffer: PANIC! Assuming error handling "
+ << "data in context [E].\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't add a message of "
+ << numBytes << " bytes to write buffer.\n";
+
+ cerr << "Error" << ": Assuming error handling "
+ << "data in context [E].\n";
+
+ HandleAbort();
+ }
+ else if (scratchBuffer_ != NULL)
+ {
+ #ifdef PANIC
+ *logofs << "WriteBuffer: PANIC! Can't add a message of "
+ << numBytes << " bytes with " << scratchLength_
+ << " bytes already in scratch buffer.\n"
+ << logofs_flush;
+
+ *logofs << "WriteBuffer: PANIC! Assuming error handling "
+ << "data in context [F].\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't add a message of "
+ << numBytes << " bytes with " << scratchLength_
+ << " bytes already in scratch buffer.\n";
+
+ cerr << "Error" << ": Assuming error handling "
+ << "data in context [F].\n";
+
+ HandleAbort();
+ }
+
+ #ifdef DEBUG
+ *logofs << "WriteBuffer: Adding " << numBytes << " bytes "
+ << "to scratch buffer.\n" << logofs_flush;
+ #endif
+
+ unsigned char *newBuffer = new unsigned char[numBytes];
+
+ if (newBuffer == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "WriteBuffer: PANIC! Can't allocate memory for "
+ << "X messages in context [G].\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory for "
+ << "X messages in context [G].\n";
+
+ HandleAbort();
+ }
+
+ #ifdef VALGRIND
+
+ memset(newBuffer, '\0', numBytes);
+
+ #endif
+
+ scratchBuffer_ = newBuffer;
+ scratchOwner_ = 1;
+ scratchLength_ = numBytes;
+
+ return newBuffer;
+}
+
+unsigned char *WriteBuffer::addScratchMessage(unsigned char *newBuffer, unsigned int numBytes)
+{
+ if (numBytes > WRITE_BUFFER_OVERFLOW_SIZE)
+ {
+ #ifdef PANIC
+ *logofs << "WriteBuffer: PANIC! Can't add a message of "
+ << numBytes << " bytes.\n" << logofs_flush;
+
+ *logofs << "WriteBuffer: PANIC! Assuming error handling "
+ << "data in context [H].\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't add a message of "
+ << numBytes << " bytes to write buffer.\n";
+
+ cerr << "Error" << ": Assuming error handling "
+ << "data in context [H].\n";
+
+ HandleAbort();
+ }
+ else if (scratchBuffer_ != NULL)
+ {
+ #ifdef PANIC
+ *logofs << "WriteBuffer: PANIC! Can't add a foreign "
+ << "message of " << numBytes << " bytes with "
+ << scratchLength_ << " bytes already in "
+ << "scratch buffer.\n" << logofs_flush;
+
+ *logofs << "WriteBuffer: PANIC! Assuming error handling "
+ << "data in context [I].\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't add a foreign message of "
+ << numBytes << " bytes with " << scratchLength_
+ << " bytes already in scratch buffer.\n";
+
+ cerr << "Error" << ": Assuming error handling "
+ << "data in context [I].\n";
+
+ HandleAbort();
+ }
+
+ #ifdef DEBUG
+ *logofs << "WriteBuffer: Adding " << numBytes << " bytes "
+ << "from a foreign message to scratch buffer.\n"
+ << logofs_flush;
+ #endif
+
+ scratchBuffer_ = newBuffer;
+ scratchLength_ = numBytes;
+ scratchOwner_ = 0;
+
+ return newBuffer;
+}
+
+void WriteBuffer::removeScratchMessage()
+{
+ #ifdef TEST
+
+ if (scratchLength_ == 0 || scratchBuffer_ == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "WriteBuffer: PANIC! Can't remove non existent scratch message.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't remove non existent scratch message.\n";
+
+ HandleAbort();
+ }
+
+ *logofs << "WriteBuffer: Removing " << scratchLength_
+ << " bytes from scratch buffer.\n"
+ << logofs_flush;
+
+ #endif
+
+ if (scratchOwner_ == 1)
+ {
+ #ifdef DEBUG
+ *logofs << "WriteBuffer: Going to delete "
+ << scratchLength_ << " bytes from the "
+ << "scratch buffer.\n" << logofs_flush;
+ #endif
+
+ delete [] scratchBuffer_;
+ }
+
+ scratchLength_ = 0;
+ scratchBuffer_ = NULL;
+ scratchOwner_ = 1;
+}
diff --git a/nxcomp/WriteBuffer.h b/nxcomp/WriteBuffer.h
new file mode 100644
index 000000000..4673cecee
--- /dev/null
+++ b/nxcomp/WriteBuffer.h
@@ -0,0 +1,126 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef WriteBuffer_H
+#define WriteBuffer_H
+
+#include "Misc.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#define WRITE_BUFFER_DEFAULT_SIZE 16384
+
+//
+// Adjust for the biggest reply that we could receive.
+// This is likely to be a reply to a X_ListFonts where
+// user has a large amount of installed fonts.
+//
+
+#define WRITE_BUFFER_OVERFLOW_SIZE 4194304
+
+class WriteBuffer
+{
+ public:
+
+ WriteBuffer();
+
+ ~WriteBuffer();
+
+ void setSize(unsigned int initialSize, unsigned int thresholdSize,
+ unsigned int maximumSize);
+
+ unsigned char *addMessage(unsigned int numBytes);
+
+ unsigned char *removeMessage(unsigned int numBytes);
+
+ unsigned char *addScratchMessage(unsigned int numBytes);
+
+ //
+ // This form allows user to provide its own
+ // buffer as write buffer's scratch area.
+ //
+
+ unsigned char *addScratchMessage(unsigned char *newBuffer, unsigned int numBytes);
+
+ void removeScratchMessage();
+
+ void partialReset();
+
+ void fullReset();
+
+ unsigned char *getData() const
+ {
+ return buffer_;
+ }
+
+ unsigned int getLength() const
+ {
+ return length_;
+ }
+
+ unsigned int getAvailable() const
+ {
+ return (size_ - length_);
+ }
+
+ unsigned char *getScratchData() const
+ {
+ return scratchBuffer_;
+ }
+
+ unsigned int getScratchLength() const
+ {
+ return scratchLength_;
+ }
+
+ unsigned int getTotalLength() const
+ {
+ return (length_ + scratchLength_);
+ }
+
+ void registerPointer(unsigned char **pointer)
+ {
+ index_ = pointer;
+ }
+
+ void unregisterPointer()
+ {
+ index_ = 0;
+ }
+
+ private:
+
+ unsigned int size_;
+ unsigned int length_;
+
+ unsigned char *buffer_;
+ unsigned char **index_;
+
+ unsigned int scratchLength_;
+ unsigned char *scratchBuffer_;
+
+ int scratchOwner_;
+
+ unsigned int initialSize_;
+ unsigned int thresholdSize_;
+ unsigned int maximumSize_;
+};
+
+#endif /* WriteBuffer_H */
diff --git a/nxcomp/XidCache.cpp b/nxcomp/XidCache.cpp
new file mode 100644
index 000000000..a9a723c76
--- /dev/null
+++ b/nxcomp/XidCache.cpp
@@ -0,0 +1,39 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "Control.h"
+
+#include "XidCache.h"
+
+XidCache::XidCache()
+{
+ for (int i = 0; i < 256; i++)
+ {
+ base_[i] = new IntCache(8);
+ }
+
+ slot_ = 0;
+ last_ = 0;
+}
+
+XidCache::~XidCache()
+{
+ for (int i = 0; i < 256; i++)
+ {
+ delete base_[i];
+ }
+}
diff --git a/nxcomp/XidCache.h b/nxcomp/XidCache.h
new file mode 100644
index 000000000..78a94d8d0
--- /dev/null
+++ b/nxcomp/XidCache.h
@@ -0,0 +1,41 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef XidCache_H
+#define XidCache_H
+
+#include "IntCache.h"
+
+class XidCache
+{
+ friend class EncodeBuffer;
+ friend class DecodeBuffer;
+
+ public:
+
+ XidCache();
+ ~XidCache();
+
+ private:
+
+ IntCache *base_[256];
+
+ unsigned int slot_;
+ unsigned int last_;
+};
+
+#endif /* XidCache_H */
diff --git a/nxcomp/Z.cpp b/nxcomp/Z.cpp
new file mode 100644
index 000000000..e6c93cd10
--- /dev/null
+++ b/nxcomp/Z.cpp
@@ -0,0 +1,134 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "Z.h"
+#include "Misc.h"
+
+int ZCompress(z_stream *stream, unsigned char *dest, unsigned int *destLen,
+ const unsigned char *source, unsigned int sourceLen)
+{
+ //
+ // Deal with the possible overflow.
+ //
+
+ if (stream -> total_out & 0x80000000)
+ {
+ #ifdef TEST
+ *logofs << "ZCompress: Reset stream counters with "
+ << "total in " << stream -> total_in
+ << " and total out " << stream -> total_out
+ << ".\n" << logofs_flush;
+ #endif
+
+ stream -> total_in = 0;
+ stream -> total_out = 0;
+ }
+
+ unsigned int saveOut = stream -> total_out;
+
+ stream -> next_in = (Bytef *) source;
+ stream -> avail_in = sourceLen;
+
+ //
+ // Check if the source is bigger than
+ // 64K on 16-bit machine.
+ //
+
+ #ifdef MAXSEG_64K
+
+ if ((uLong) stream -> avail_in != sourceLen) return Z_BUF_ERROR;
+
+ #endif
+
+ stream -> next_out = dest;
+ stream -> avail_out = *destLen;
+
+ #ifdef MAXSEG_64K
+
+ if ((uLong) stream -> avail_out != *destLen) return Z_BUF_ERROR;
+
+ #endif
+
+ int result = deflate(stream, Z_FINISH);
+
+ if (result != Z_STREAM_END)
+ {
+ deflateReset(stream);
+
+ return (result == Z_OK ? Z_BUF_ERROR : result);
+ }
+
+ *destLen = stream -> total_out - saveOut;
+
+ result = deflateReset(stream);
+
+ return result;
+}
+
+int ZDecompress(z_stream *stream, unsigned char *dest, unsigned int *destLen,
+ const unsigned char *source, unsigned int sourceLen)
+{
+ stream -> next_in = (Bytef *) source;
+ stream -> avail_in = sourceLen;
+
+ //
+ // Deal with the possible overflow.
+ //
+
+ if (stream -> total_out & 0x80000000)
+ {
+ #ifdef TEST
+ *logofs << "ZDecompress: Reset stream counters with "
+ << "total in " << stream -> total_in
+ << " and total out " << stream -> total_out
+ << ".\n" << logofs_flush;
+ #endif
+
+ stream -> total_in = 0;
+ stream -> total_out = 0;
+ }
+
+ unsigned int saveOut = stream -> total_out;
+
+ if (stream -> avail_in != sourceLen)
+ {
+ return Z_BUF_ERROR;
+ }
+
+ stream -> next_out = dest;
+ stream -> avail_out = *destLen;
+
+ if (stream -> avail_out != *destLen)
+ {
+ return Z_BUF_ERROR;
+ }
+
+ int result = inflate(stream, Z_FINISH);
+
+ if (result != Z_STREAM_END)
+ {
+ inflateReset(stream);
+
+ return (result == Z_OK ? Z_BUF_ERROR : result);
+ }
+
+ *destLen = stream -> total_out - saveOut;
+
+ result = inflateReset(stream);
+
+ return result;
+}
diff --git a/nxcomp/Z.h b/nxcomp/Z.h
new file mode 100644
index 000000000..d7f7fa185
--- /dev/null
+++ b/nxcomp/Z.h
@@ -0,0 +1,29 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifndef Z_H
+#define Z_H
+
+#include <zlib.h>
+
+int ZCompress(z_stream *stream, unsigned char *dest, unsigned int *destLen,
+ const unsigned char *source, unsigned int sourceLen);
+
+int ZDecompress(z_stream *stream, unsigned char *dest, unsigned int *destLen,
+ const unsigned char *source, unsigned int sourceLen);
+
+#endif /* Z_H */
diff --git a/nxcomp/configure b/nxcomp/configure
new file mode 100755
index 000000000..633baf840
--- /dev/null
+++ b/nxcomp/configure
@@ -0,0 +1,5923 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.59.
+#
+# Copyright (C) 2003 Free Software Foundation, Inc.
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+ set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+ LC_TELEPHONE LC_TIME
+do
+ if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+ eval $as_var=C; export $as_var
+ else
+ $as_unset $as_var
+ fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)$' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+ /^X\/\(\/\/\)$/{ s//\1/; q; }
+ /^X\/\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" || {
+ # Find who we are. Look in the path if we contain no path at all
+ # relative or not.
+ case $0 in
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+ ;;
+ esac
+ # We did not find ourselves, most probably we were run as `sh COMMAND'
+ # in which case we are not to be found in the path.
+ if test "x$as_myself" = x; then
+ as_myself=$0
+ fi
+ if test ! -f "$as_myself"; then
+ { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2
+ { (exit 1); exit 1; }; }
+ fi
+ case $CONFIG_SHELL in
+ '')
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for as_base in sh bash ksh sh5; do
+ case $as_dir in
+ /*)
+ if ("$as_dir/$as_base" -c '
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then
+ $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+ $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+ CONFIG_SHELL=$as_dir/$as_base
+ export CONFIG_SHELL
+ exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+ fi;;
+ esac
+ done
+done
+;;
+ esac
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line before each line; the second 'sed' does the real
+ # work. The second script uses 'N' to pair each line-number line
+ # with the numbered line, and appends trailing '-' during
+ # substitution so that $LINENO is not a special case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-)
+ sed '=' <$as_myself |
+ sed '
+ N
+ s,$,-,
+ : loop
+ s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+ t loop
+ s,-$,,
+ s,^['$as_cr_digits']*\n,,
+ ' >$as_me.lineno &&
+ chmod +x $as_me.lineno ||
+ { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensible to this).
+ . ./$as_me.lineno
+ # Exit status is that of the last command.
+ exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+ *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T=' ' ;;
+ *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+ *) ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+ # We could just check for DJGPP; but this test a) works b) is more generic
+ # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+ if test -f conf$$.exe; then
+ # Don't use ln at all; we don't have any links
+ as_ln_s='cp -p'
+ else
+ as_ln_s='ln -s'
+ fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p=:
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" $as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+exec 6>&1
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_config_libobj_dir=.
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Maximum number of lines to put in a shell here document.
+# This variable seems obsolete. It should probably be removed, and
+# only ac_max_sed_lines should be used.
+: ${ac_max_here_lines=38}
+
+# Identity of this package.
+PACKAGE_NAME=
+PACKAGE_TARNAME=
+PACKAGE_VERSION=
+PACKAGE_STRING=
+PACKAGE_BUGREPORT=
+
+ac_unique_file="NX.h"
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS armcxx armcc CXX CXXFLAGS LDFLAGS CPPFLAGS ac_ct_CXX EXEEXT OBJEXT CC CFLAGS ac_ct_CC INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CXXCPP X_CFLAGS X_PRE_LIBS X_LIBS X_EXTRA_LIBS LIBVERSION VERSION MAKEDEPEND ALL LIBOBJS LTLIBOBJS'
+ac_subst_files=''
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+ac_prev=
+for ac_option
+do
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval "$ac_prev=\$ac_option"
+ ac_prev=
+ continue
+ fi
+
+ ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'`
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case $ac_option in
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir=$ac_optarg ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build_alias ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build_alias=$ac_optarg ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file=$ac_optarg ;;
+
+ --config-cache | -C)
+ cache_file=config.cache ;;
+
+ -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+ | --da=*)
+ datadir=$ac_optarg ;;
+
+ -disable-* | --disable-*)
+ ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+ { (exit 1); exit 1; }; }
+ ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+ eval "enable_$ac_feature=no" ;;
+
+ -enable-* | --enable-*)
+ ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+ { (exit 1); exit 1; }; }
+ ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+ case $ac_option in
+ *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "enable_$ac_feature='$ac_optarg'" ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix=$ac_optarg ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he | -h)
+ ac_init_help=long ;;
+ -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+ ac_init_help=recursive ;;
+ -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+ ac_init_help=short ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host_alias ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host_alias=$ac_optarg ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir=$ac_optarg ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir=$ac_optarg ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir=$ac_optarg ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir=$ac_optarg ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst \
+ | --locals | --local | --loca | --loc | --lo)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+ localstatedir=$ac_optarg ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir=$ac_optarg ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c | -n)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir=$ac_optarg ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix=$ac_optarg ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix=$ac_optarg ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix=$ac_optarg ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name=$ac_optarg ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir=$ac_optarg ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir=$ac_optarg ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site=$ac_optarg ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir=$ac_optarg ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir=$ac_optarg ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target_alias ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target_alias=$ac_optarg ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers | -V)
+ ac_init_version=: ;;
+
+ -with-* | --with-*)
+ ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid package name: $ac_package" >&2
+ { (exit 1); exit 1; }; }
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ case $ac_option in
+ *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "with_$ac_package='$ac_optarg'" ;;
+
+ -without-* | --without-*)
+ ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid package name: $ac_package" >&2
+ { (exit 1); exit 1; }; }
+ ac_package=`echo $ac_package | sed 's/-/_/g'`
+ eval "with_$ac_package=no" ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes=$ac_optarg ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries=$ac_optarg ;;
+
+ -*) { echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+ { (exit 1); exit 1; }; }
+ ;;
+
+ *=*)
+ ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+ { (exit 1); exit 1; }; }
+ ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`
+ eval "$ac_envvar='$ac_optarg'"
+ export $ac_envvar ;;
+
+ *)
+ # FIXME: should be removed in autoconf 3.0.
+ echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+ expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+ : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+ { echo "$as_me: error: missing argument to $ac_option" >&2
+ { (exit 1); exit 1; }; }
+fi
+
+# Be sure to have absolute paths.
+for ac_var in exec_prefix prefix
+do
+ eval ac_val=$`echo $ac_var`
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* | NONE | '' ) ;;
+ *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+# Be sure to have absolute paths.
+for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \
+ localstatedir libdir includedir oldincludedir infodir mandir
+do
+ eval ac_val=$`echo $ac_var`
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* ) ;;
+ *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+ if test "x$build_alias" = x; then
+ cross_compiling=maybe
+ echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+ If a cross compiler is detected then cross compile mode will be used." >&2
+ elif test "x$build_alias" != "x$host_alias"; then
+ cross_compiling=yes
+ fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then its parent.
+ ac_confdir=`(dirname "$0") 2>/dev/null ||
+$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$0" : 'X\(//\)[^/]' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$0" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ srcdir=$ac_confdir
+ if test ! -r $srcdir/$ac_unique_file; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+ if test "$ac_srcdir_defaulted" = yes; then
+ { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2
+ { (exit 1); exit 1; }; }
+ else
+ { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+ { (exit 1); exit 1; }; }
+ fi
+fi
+(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null ||
+ { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2
+ { (exit 1); exit 1; }; }
+srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'`
+ac_env_build_alias_set=${build_alias+set}
+ac_env_build_alias_value=$build_alias
+ac_cv_env_build_alias_set=${build_alias+set}
+ac_cv_env_build_alias_value=$build_alias
+ac_env_host_alias_set=${host_alias+set}
+ac_env_host_alias_value=$host_alias
+ac_cv_env_host_alias_set=${host_alias+set}
+ac_cv_env_host_alias_value=$host_alias
+ac_env_target_alias_set=${target_alias+set}
+ac_env_target_alias_value=$target_alias
+ac_cv_env_target_alias_set=${target_alias+set}
+ac_cv_env_target_alias_value=$target_alias
+ac_env_CXX_set=${CXX+set}
+ac_env_CXX_value=$CXX
+ac_cv_env_CXX_set=${CXX+set}
+ac_cv_env_CXX_value=$CXX
+ac_env_CXXFLAGS_set=${CXXFLAGS+set}
+ac_env_CXXFLAGS_value=$CXXFLAGS
+ac_cv_env_CXXFLAGS_set=${CXXFLAGS+set}
+ac_cv_env_CXXFLAGS_value=$CXXFLAGS
+ac_env_LDFLAGS_set=${LDFLAGS+set}
+ac_env_LDFLAGS_value=$LDFLAGS
+ac_cv_env_LDFLAGS_set=${LDFLAGS+set}
+ac_cv_env_LDFLAGS_value=$LDFLAGS
+ac_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_env_CPPFLAGS_value=$CPPFLAGS
+ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_cv_env_CPPFLAGS_value=$CPPFLAGS
+ac_env_CC_set=${CC+set}
+ac_env_CC_value=$CC
+ac_cv_env_CC_set=${CC+set}
+ac_cv_env_CC_value=$CC
+ac_env_CFLAGS_set=${CFLAGS+set}
+ac_env_CFLAGS_value=$CFLAGS
+ac_cv_env_CFLAGS_set=${CFLAGS+set}
+ac_cv_env_CFLAGS_value=$CFLAGS
+ac_env_CXXCPP_set=${CXXCPP+set}
+ac_env_CXXCPP_value=$CXXCPP
+ac_cv_env_CXXCPP_set=${CXXCPP+set}
+ac_cv_env_CXXCPP_value=$CXXCPP
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat <<_ACEOF
+\`configure' configures this package to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE. See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+ -h, --help display this help and exit
+ --help=short display options specific to this package
+ --help=recursive display the short help of all the included packages
+ -V, --version display version information and exit
+ -q, --quiet, --silent do not print \`checking...' messages
+ --cache-file=FILE cache test results in FILE [disabled]
+ -C, --config-cache alias for \`--cache-file=config.cache'
+ -n, --no-create do not create output files
+ --srcdir=DIR find the sources in DIR [configure dir or \`..']
+
+_ACEOF
+
+ cat <<_ACEOF
+Installation directories:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+ --bindir=DIR user executables [EPREFIX/bin]
+ --sbindir=DIR system admin executables [EPREFIX/sbin]
+ --libexecdir=DIR program executables [EPREFIX/libexec]
+ --datadir=DIR read-only architecture-independent data [PREFIX/share]
+ --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --libdir=DIR object code libraries [EPREFIX/lib]
+ --includedir=DIR C header files [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc [/usr/include]
+ --infodir=DIR info documentation [PREFIX/info]
+ --mandir=DIR man documentation [PREFIX/man]
+_ACEOF
+
+ cat <<\_ACEOF
+
+X features:
+ --x-includes=DIR X include files are in DIR
+ --x-libraries=DIR X library files are in DIR
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+
+ cat <<\_ACEOF
+
+Optional Packages:
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --with-x use the X Window System
+
+Some influential environment variables:
+ CXX C++ compiler command
+ CXXFLAGS C++ compiler flags
+ LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
+ nonstandard directory <lib dir>
+ CPPFLAGS C/C++ preprocessor flags, e.g. -I<include dir> if you have
+ headers in a nonstandard directory <include dir>
+ CC C compiler command
+ CFLAGS C compiler flags
+ CXXCPP C++ preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+_ACEOF
+fi
+
+if test "$ac_init_help" = "recursive"; then
+ # If there are subdirs, report their specific --help.
+ ac_popdir=`pwd`
+ for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+ test -d $ac_dir || continue
+ ac_builddir=.
+
+if test "$ac_dir" != .; then
+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+ ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+ .) # No --srcdir option. We are building in place.
+ ac_srcdir=.
+ if test -z "$ac_top_builddir"; then
+ ac_top_srcdir=.
+ else
+ ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+ fi ;;
+ [\\/]* | ?:[\\/]* ) # Absolute path.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir ;;
+ *) # Relative path.
+ ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+ case "$ac_dir" in
+ .) ac_abs_builddir=`pwd`;;
+ [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+ *) ac_abs_builddir=`pwd`/"$ac_dir";;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+ case ${ac_top_builddir}. in
+ .) ac_abs_top_builddir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+ *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+ case $ac_srcdir in
+ .) ac_abs_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+ *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+ case $ac_top_srcdir in
+ .) ac_abs_top_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+ *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+ esac;;
+esac
+
+ cd $ac_dir
+ # Check for guested configure; otherwise get Cygnus style configure.
+ if test -f $ac_srcdir/configure.gnu; then
+ echo
+ $SHELL $ac_srcdir/configure.gnu --help=recursive
+ elif test -f $ac_srcdir/configure; then
+ echo
+ $SHELL $ac_srcdir/configure --help=recursive
+ elif test -f $ac_srcdir/configure.ac ||
+ test -f $ac_srcdir/configure.in; then
+ echo
+ $ac_configure --help
+ else
+ echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+ fi
+ cd $ac_popdir
+ done
+fi
+
+test -n "$ac_init_help" && exit 0
+if $ac_init_version; then
+ cat <<\_ACEOF
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+ exit 0
+fi
+exec 5>config.log
+cat >&5 <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by $as_me, which was
+generated by GNU Autoconf 2.59. Invocation command line was
+
+ $ $0 $@
+
+_ACEOF
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
+
+/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+hostinfo = `(hostinfo) 2>/dev/null || echo unknown`
+/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
+/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ echo "PATH: $as_dir"
+done
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_sep=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+ for ac_arg
+ do
+ case $ac_arg in
+ -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ continue ;;
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+ ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ case $ac_pass in
+ 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
+ 2)
+ ac_configure_args1="$ac_configure_args1 '$ac_arg'"
+ if test $ac_must_keep_next = true; then
+ ac_must_keep_next=false # Got value, back to normal.
+ else
+ case $ac_arg in
+ *=* | --config-cache | -C | -disable-* | --disable-* \
+ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+ | -with-* | --with-* | -without-* | --without-* | --x)
+ case "$ac_configure_args0 " in
+ "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+ esac
+ ;;
+ -* ) ac_must_keep_next=true ;;
+ esac
+ fi
+ ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'"
+ # Get rid of the leading space.
+ ac_sep=" "
+ ;;
+ esac
+ done
+done
+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log. We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Be sure not to use single quotes in there, as some shells,
+# such as our DU 5.0 friend, will then `close' the trap.
+trap 'exit_status=$?
+ # Save into config.log some information that might help in debugging.
+ {
+ echo
+
+ cat <<\_ASBOX
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+_ASBOX
+ echo
+ # The following way of writing the cache mishandles newlines in values,
+{
+ (set) 2>&1 |
+ case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in
+ *ac_space=\ *)
+ sed -n \
+ "s/'"'"'/'"'"'\\\\'"'"''"'"'/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p"
+ ;;
+ *)
+ sed -n \
+ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+ ;;
+ esac;
+}
+ echo
+
+ cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+ echo
+ for ac_var in $ac_subst_vars
+ do
+ eval ac_val=$`echo $ac_var`
+ echo "$ac_var='"'"'$ac_val'"'"'"
+ done | sort
+ echo
+
+ if test -n "$ac_subst_files"; then
+ cat <<\_ASBOX
+## ------------- ##
+## Output files. ##
+## ------------- ##
+_ASBOX
+ echo
+ for ac_var in $ac_subst_files
+ do
+ eval ac_val=$`echo $ac_var`
+ echo "$ac_var='"'"'$ac_val'"'"'"
+ done | sort
+ echo
+ fi
+
+ if test -s confdefs.h; then
+ cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+ echo
+ sed "/^$/d" confdefs.h | sort
+ echo
+ fi
+ test "$ac_signal" != 0 &&
+ echo "$as_me: caught signal $ac_signal"
+ echo "$as_me: exit $exit_status"
+ } >&5
+ rm -f core *.core &&
+ rm -rf conftest* confdefs* conf$$* $ac_clean_files &&
+ exit $exit_status
+ ' 0
+for ac_signal in 1 2 13 15; do
+ trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo >confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+ if test "x$prefix" != xNONE; then
+ CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+ else
+ CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+ fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+ if test -r "$ac_site_file"; then
+ { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+echo "$as_me: loading site script $ac_site_file" >&6;}
+ sed 's/^/| /' "$ac_site_file" >&5
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ # Some versions of bash will fail to source /dev/null (special
+ # files actually), so we avoid doing that.
+ if test -f "$cache_file"; then
+ { echo "$as_me:$LINENO: loading cache $cache_file" >&5
+echo "$as_me: loading cache $cache_file" >&6;}
+ case $cache_file in
+ [\\/]* | ?:[\\/]* ) . $cache_file;;
+ *) . ./$cache_file;;
+ esac
+ fi
+else
+ { echo "$as_me:$LINENO: creating cache $cache_file" >&5
+echo "$as_me: creating cache $cache_file" >&6;}
+ >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in `(set) 2>&1 |
+ sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do
+ eval ac_old_set=\$ac_cv_env_${ac_var}_set
+ eval ac_new_set=\$ac_env_${ac_var}_set
+ eval ac_old_val="\$ac_cv_env_${ac_var}_value"
+ eval ac_new_val="\$ac_env_${ac_var}_value"
+ case $ac_old_set,$ac_new_set in
+ set,)
+ { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,set)
+ { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,);;
+ *)
+ if test "x$ac_old_val" != "x$ac_new_val"; then
+ { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+ { echo "$as_me:$LINENO: former value: $ac_old_val" >&5
+echo "$as_me: former value: $ac_old_val" >&2;}
+ { echo "$as_me:$LINENO: current value: $ac_new_val" >&5
+echo "$as_me: current value: $ac_new_val" >&2;}
+ ac_cache_corrupted=:
+ fi;;
+ esac
+ # Pass precious variables to config.status.
+ if test "$ac_new_set" = set; then
+ case $ac_new_val in
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+ ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *) ac_arg=$ac_var=$ac_new_val ;;
+ esac
+ case " $ac_configure_args " in
+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
+ *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ esac
+ fi
+done
+if $ac_cache_corrupted; then
+ { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+ { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+CXXFLAGS="-O3 -fno-rtti -fno-exceptions"
+CFLAGS="-O3"
+
+
+LIBSTATIC=""
+LIBSHARED=""
+
+
+if test -d "../nx-X11/include" ; then
+ CXXFLAGS="$CXXFLAGS -I../nx-X11/exports/include"
+ CFLAGS="$CFLAGS -I../nx-X11/exports/include"
+ LIBS="$LIBS -L../nx-X11/exports/lib"
+fi
+
+
+if test "${with_ipaq}" = yes; then
+ echo -e "enabling IPAQ configuration"
+ CXX="arm-linux-c++"
+ CC="arm-linux-gcc"
+ unset ac_cv_prog_armcxx
+ unset ac_cv_prog_armcc
+ unset ac_cv_prog_CXXCPP
+ # Extract the first word of ""$CXX"", so it can be a program name with args.
+set dummy "$CXX"; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_armcxx+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$armcxx"; then
+ ac_cv_prog_armcxx="$armcxx" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_armcxx="yes"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+ test -z "$ac_cv_prog_armcxx" && ac_cv_prog_armcxx="no"
+fi
+fi
+armcxx=$ac_cv_prog_armcxx
+if test -n "$armcxx"; then
+ echo "$as_me:$LINENO: result: $armcxx" >&5
+echo "${ECHO_T}$armcxx" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ # Extract the first word of ""$CC"", so it can be a program name with args.
+set dummy "$CC"; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_armcc+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$armcc"; then
+ ac_cv_prog_armcc="$armcc" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_armcc="yes"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+ test -z "$ac_cv_prog_armcc" && ac_cv_prog_armcc="no"
+fi
+fi
+armcc=$ac_cv_prog_armcc
+if test -n "$armcc"; then
+ echo "$as_me:$LINENO: result: $armcc" >&5
+echo "${ECHO_T}$armcc" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ if test $armcxx = "yes" && test $armcc = "yes" ; then
+ ac_cv_prog_CXX="$CXX"
+ ac_cv_prog_CC="$CC"
+ else
+ { { echo "$as_me:$LINENO: error: Installation or configuration problem. Cannot find compiler for arm-linux." >&5
+echo "$as_me: error: Installation or configuration problem. Cannot find compiler for arm-linux." >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+else
+ unset ac_cv_prog_CXX
+ unset ac_cv_prog_CC
+ unset ac_cv_prog_CXXCPP
+fi
+
+
+ac_ext=cc
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+ for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CXX+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CXX"; then
+ ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+CXX=$ac_cv_prog_CXX
+if test -n "$CXX"; then
+ echo "$as_me:$LINENO: result: $CXX" >&5
+echo "${ECHO_T}$CXX" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ test -n "$CXX" && break
+ done
+fi
+if test -z "$CXX"; then
+ ac_ct_CXX=$CXX
+ for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CXX"; then
+ ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CXX="$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+ac_ct_CXX=$ac_cv_prog_ac_ct_CXX
+if test -n "$ac_ct_CXX"; then
+ echo "$as_me:$LINENO: result: $ac_ct_CXX" >&5
+echo "${ECHO_T}$ac_ct_CXX" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ test -n "$ac_ct_CXX" && break
+done
+test -n "$ac_ct_CXX" || ac_ct_CXX="g++"
+
+ CXX=$ac_ct_CXX
+fi
+
+
+# Provide some information about the compiler.
+echo "$as_me:$LINENO:" \
+ "checking for C++ compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
+ (eval $ac_compiler --version </dev/null >&5) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
+ (eval $ac_compiler -v </dev/null >&5) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
+ (eval $ac_compiler -V </dev/null >&5) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+echo "$as_me:$LINENO: checking for C++ compiler default output file name" >&5
+echo $ECHO_N "checking for C++ compiler default output file name... $ECHO_C" >&6
+ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5
+ (eval $ac_link_default) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ # Find the output, starting from the most likely. This scheme is
+# not robust to junk in `.', hence go to wildcards (a.*) only as a last
+# resort.
+
+# Be careful to initialize this variable, since it used to be cached.
+# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile.
+ac_cv_exeext=
+# b.out is created by i960 compilers.
+for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out
+do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj )
+ ;;
+ conftest.$ac_ext )
+ # This is the source file.
+ ;;
+ [ab].out )
+ # We found the default executable, but exeext='' is most
+ # certainly right.
+ break;;
+ *.* )
+ ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ # FIXME: I believe we export ac_cv_exeext for Libtool,
+ # but it would be cool to find out if it's true. Does anybody
+ # maintain Libtool? --akim.
+ export ac_cv_exeext
+ break;;
+ * )
+ break;;
+ esac
+done
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: C++ compiler cannot create executables
+See \`config.log' for more details." >&5
+echo "$as_me: error: C++ compiler cannot create executables
+See \`config.log' for more details." >&2;}
+ { (exit 77); exit 77; }; }
+fi
+
+ac_exeext=$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_file" >&5
+echo "${ECHO_T}$ac_file" >&6
+
+# Check the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether the C++ compiler works" >&5
+echo $ECHO_N "checking whether the C++ compiler works... $ECHO_C" >&6
+# FIXME: These cross compiler hacks should be removed for Autoconf 3.0
+# If not cross compiling, check that we can run a simple program.
+if test "$cross_compiling" != yes; then
+ if { ac_try='./$ac_file'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ cross_compiling=no
+ else
+ if test "$cross_compiling" = maybe; then
+ cross_compiling=yes
+ else
+ { { echo "$as_me:$LINENO: error: cannot run C++ compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot run C++ compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ fi
+fi
+echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+
+rm -f a.out a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+# Check the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
+echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6
+echo "$as_me:$LINENO: result: $cross_compiling" >&5
+echo "${ECHO_T}$cross_compiling" >&6
+
+echo "$as_me:$LINENO: checking for suffix of executables" >&5
+echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;;
+ *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ export ac_cv_exeext
+ break;;
+ * ) break;;
+ esac
+done
+else
+ { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
+echo "${ECHO_T}$ac_cv_exeext" >&6
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+echo "$as_me:$LINENO: checking for suffix of object files" >&5
+echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6
+if test "${ac_cv_objext+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;;
+ *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+ break;;
+ esac
+done
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
+echo "${ECHO_T}$ac_cv_objext" >&6
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+echo "$as_me:$LINENO: checking whether we are using the GNU C++ compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C++ compiler... $ECHO_C" >&6
+if test "${ac_cv_cxx_compiler_gnu+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_compiler_gnu=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_compiler_gnu=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_cxx_compiler_gnu=$ac_compiler_gnu
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_cxx_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_cxx_compiler_gnu" >&6
+GXX=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CXXFLAGS=${CXXFLAGS+set}
+ac_save_CXXFLAGS=$CXXFLAGS
+CXXFLAGS="-g"
+echo "$as_me:$LINENO: checking whether $CXX accepts -g" >&5
+echo $ECHO_N "checking whether $CXX accepts -g... $ECHO_C" >&6
+if test "${ac_cv_prog_cxx_g+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_prog_cxx_g=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_prog_cxx_g=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_cxx_g" >&5
+echo "${ECHO_T}$ac_cv_prog_cxx_g" >&6
+if test "$ac_test_CXXFLAGS" = set; then
+ CXXFLAGS=$ac_save_CXXFLAGS
+elif test $ac_cv_prog_cxx_g = yes; then
+ if test "$GXX" = yes; then
+ CXXFLAGS="-g -O2"
+ else
+ CXXFLAGS="-g"
+ fi
+else
+ if test "$GXX" = yes; then
+ CXXFLAGS="-O2"
+ else
+ CXXFLAGS=
+ fi
+fi
+for ac_declaration in \
+ '' \
+ 'extern "C" void std::exit (int) throw (); using std::exit;' \
+ 'extern "C" void std::exit (int); using std::exit;' \
+ 'extern "C" void exit (int) throw ();' \
+ 'extern "C" void exit (int);' \
+ 'void exit (int);'
+do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_declaration
+#include <stdlib.h>
+int
+main ()
+{
+exit (42);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+continue
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_declaration
+int
+main ()
+{
+exit (42);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+rm -f conftest*
+if test -n "$ac_declaration"; then
+ echo '#ifdef __cplusplus' >>confdefs.h
+ echo $ac_declaration >>confdefs.h
+ echo '#endif' >>confdefs.h
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="gcc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ CC=$ac_ct_CC
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}cc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="cc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ CC=$ac_ct_CC
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+fi
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# != 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+ fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in cl
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ test -n "$CC" && break
+ done
+fi
+if test -z "$CC"; then
+ ac_ct_CC=$CC
+ for ac_prog in cl
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ test -n "$ac_ct_CC" && break
+done
+
+ CC=$ac_ct_CC
+fi
+
+fi
+
+
+test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&5
+echo "$as_me: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+
+# Provide some information about the compiler.
+echo "$as_me:$LINENO:" \
+ "checking for C compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
+ (eval $ac_compiler --version </dev/null >&5) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
+ (eval $ac_compiler -v </dev/null >&5) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
+ (eval $ac_compiler -V </dev/null >&5) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+
+echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6
+if test "${ac_cv_c_compiler_gnu+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_compiler_gnu=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_compiler_gnu=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6
+GCC=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+CFLAGS="-g"
+echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
+echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_g+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_prog_cc_g=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_prog_cc_g=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_g" >&6
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5
+echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_stdc+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_prog_cc_stdc=no
+ac_save_CC=$CC
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std1 is added to get
+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
+ array size at least. It's necessary to write '\x00'==0 to get something
+ that's true only with -std1. */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+ ;
+ return 0;
+}
+_ACEOF
+# Don't try gcc -ansi; that turns off useful extensions and
+# breaks some systems' header files.
+# AIX -qlanglvl=ansi
+# Ultrix and OSF/1 -std1
+# HP-UX 10.20 and later -Ae
+# HP-UX older versions -Aa -D_HPUX_SOURCE
+# SVR4 -Xc -D__EXTENSIONS__
+for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_prog_cc_stdc=$ac_arg
+break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext
+done
+rm -f conftest.$ac_ext conftest.$ac_objext
+CC=$ac_save_CC
+
+fi
+
+case "x$ac_cv_prog_cc_stdc" in
+ x|xno)
+ echo "$as_me:$LINENO: result: none needed" >&5
+echo "${ECHO_T}none needed" >&6 ;;
+ *)
+ echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6
+ CC="$CC $ac_cv_prog_cc_stdc" ;;
+esac
+
+# Some people use a C++ compiler to compile C. Since we use `exit',
+# in C++ we need to declare it. In case someone uses the same compiler
+# for both compiling C and C++ we need to have the C++ compiler decide
+# the declaration of exit, since it's the most demanding environment.
+cat >conftest.$ac_ext <<_ACEOF
+#ifndef __cplusplus
+ choke me
+#endif
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ for ac_declaration in \
+ '' \
+ 'extern "C" void std::exit (int) throw (); using std::exit;' \
+ 'extern "C" void std::exit (int); using std::exit;' \
+ 'extern "C" void exit (int) throw ();' \
+ 'extern "C" void exit (int);' \
+ 'void exit (int);'
+do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_declaration
+#include <stdlib.h>
+int
+main ()
+{
+exit (42);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+continue
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_declaration
+int
+main ()
+{
+exit (42);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+rm -f conftest*
+if test -n "$ac_declaration"; then
+ echo '#ifdef __cplusplus' >>confdefs.h
+ echo $ac_declaration >>confdefs.h
+ echo '#endif' >>confdefs.h
+fi
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ac_ext=cc
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+
+
+echo "$as_me:$LINENO: checking whether compiler needs -Wno-deprecated" >&5
+echo $ECHO_N "checking whether compiler needs -Wno-deprecated... $ECHO_C" >&6
+gcc_version=`${CC} --version | grep 'gcc (GCC) [3-4].' | head -n 1`
+case "${gcc_version}" in
+ gcc*)
+ echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+ CXXFLAGS="$CXXFLAGS -Wno-deprecated"
+ ;;
+
+ *)
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+ ;;
+esac
+
+echo "$as_me:$LINENO: checking whether compiler accepts -Wmissing-declarations" >&5
+echo $ECHO_N "checking whether compiler accepts -Wmissing-declarations... $ECHO_C" >&6
+gcc_version=`${CC} --version | grep 'gcc (GCC) [3-4].' | head -n 1`
+case "${gcc_version}" in
+ gcc*)
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+ ;;
+
+ *)
+ echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+ CXXFLAGS="$CXXFLAGS -Wmissing-declarations"
+ ;;
+esac
+
+
+ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+ if test -f $ac_dir/install-sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f $ac_dir/install.sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ elif test -f $ac_dir/shtool; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/shtool install -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5
+echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+ac_config_guess="$SHELL $ac_aux_dir/config.guess"
+ac_config_sub="$SHELL $ac_aux_dir/config.sub"
+ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure.
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5
+echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6
+if test -z "$INSTALL"; then
+if test "${ac_cv_path_install+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in
+ ./ | .// | /cC/* | \
+ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+ ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \
+ /usr/ucb/* ) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
+ if test $ac_prog = install &&
+ grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ elif test $ac_prog = install &&
+ grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # program-specific install script used by HP pwplus--don't use.
+ :
+ else
+ ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+ break 3
+ fi
+ fi
+ done
+ done
+ ;;
+esac
+done
+
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL=$ac_cv_path_install
+ else
+ # As a last resort, use the slow shell script. We don't cache a
+ # path for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the path is relative.
+ INSTALL=$ac_install_sh
+ fi
+fi
+echo "$as_me:$LINENO: result: $INSTALL" >&5
+echo "${ECHO_T}$INSTALL" >&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+
+
+
+ac_ext=cc
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+echo "$as_me:$LINENO: checking how to run the C++ preprocessor" >&5
+echo $ECHO_N "checking how to run the C++ preprocessor... $ECHO_C" >&6
+if test -z "$CXXCPP"; then
+ if test "${ac_cv_prog_CXXCPP+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ # Double quotes because CXXCPP needs to be expanded
+ for CXXCPP in "$CXX -E" "/lib/cpp"
+ do
+ ac_preproc_ok=false
+for ac_cxx_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_cxx_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether non-existent headers
+ # can be detected and how.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_cxx_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ # Broken: success on invalid input.
+continue
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+ break
+fi
+
+ done
+ ac_cv_prog_CXXCPP=$CXXCPP
+
+fi
+ CXXCPP=$ac_cv_prog_CXXCPP
+else
+ ac_cv_prog_CXXCPP=$CXXCPP
+fi
+echo "$as_me:$LINENO: result: $CXXCPP" >&5
+echo "${ECHO_T}$CXXCPP" >&6
+ac_preproc_ok=false
+for ac_cxx_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_cxx_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether non-existent headers
+ # can be detected and how.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_cxx_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ # Broken: success on invalid input.
+continue
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+ :
+else
+ { { echo "$as_me:$LINENO: error: C++ preprocessor \"$CXXCPP\" fails sanity check
+See \`config.log' for more details." >&5
+echo "$as_me: error: C++ preprocessor \"$CXXCPP\" fails sanity check
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+ac_ext=cc
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+
+echo "$as_me:$LINENO: checking for X" >&5
+echo $ECHO_N "checking for X... $ECHO_C" >&6
+
+
+# Check whether --with-x or --without-x was given.
+if test "${with_x+set}" = set; then
+ withval="$with_x"
+
+fi;
+# $have_x is `yes', `no', `disabled', or empty when we do not yet know.
+if test "x$with_x" = xno; then
+ # The user explicitly disabled X.
+ have_x=disabled
+else
+ if test "x$x_includes" != xNONE && test "x$x_libraries" != xNONE; then
+ # Both variables are already set.
+ have_x=yes
+ else
+ if test "${ac_cv_have_x+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ # One or both of the vars are not set, and there is no cached value.
+ac_x_includes=no ac_x_libraries=no
+rm -fr conftest.dir
+if mkdir conftest.dir; then
+ cd conftest.dir
+ # Make sure to not put "make" in the Imakefile rules, since we grep it out.
+ cat >Imakefile <<'_ACEOF'
+acfindx:
+ @echo 'ac_im_incroot="${INCROOT}"; ac_im_usrlibdir="${USRLIBDIR}"; ac_im_libdir="${LIBDIR}"'
+_ACEOF
+ if (xmkmf) >/dev/null 2>/dev/null && test -f Makefile; then
+ # GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+ eval `${MAKE-make} acfindx 2>/dev/null | grep -v make`
+ # Open Windows xmkmf reportedly sets LIBDIR instead of USRLIBDIR.
+ for ac_extension in a so sl; do
+ if test ! -f $ac_im_usrlibdir/libX11.$ac_extension &&
+ test -f $ac_im_libdir/libX11.$ac_extension; then
+ ac_im_usrlibdir=$ac_im_libdir; break
+ fi
+ done
+ # Screen out bogus values from the imake configuration. They are
+ # bogus both because they are the default anyway, and because
+ # using them would break gcc on systems where it needs fixed includes.
+ case $ac_im_incroot in
+ /usr/include) ;;
+ *) test -f "$ac_im_incroot/X11/Xos.h" && ac_x_includes=$ac_im_incroot;;
+ esac
+ case $ac_im_usrlibdir in
+ /usr/lib | /lib) ;;
+ *) test -d "$ac_im_usrlibdir" && ac_x_libraries=$ac_im_usrlibdir ;;
+ esac
+ fi
+ cd ..
+ rm -fr conftest.dir
+fi
+
+# Standard set of common directories for X headers.
+# Check X11 before X11Rn because it is often a symlink to the current release.
+ac_x_header_dirs='
+/usr/X11/include
+/usr/X11R6/include
+/usr/X11R5/include
+/usr/X11R4/include
+
+/usr/include/X11
+/usr/include/X11R6
+/usr/include/X11R5
+/usr/include/X11R4
+
+/usr/local/X11/include
+/usr/local/X11R6/include
+/usr/local/X11R5/include
+/usr/local/X11R4/include
+
+/usr/local/include/X11
+/usr/local/include/X11R6
+/usr/local/include/X11R5
+/usr/local/include/X11R4
+
+/usr/X386/include
+/usr/x386/include
+/usr/XFree86/include/X11
+
+/usr/include
+/usr/local/include
+/usr/unsupported/include
+/usr/athena/include
+/usr/local/x11r5/include
+/usr/lpp/Xamples/include
+
+/usr/openwin/include
+/usr/openwin/share/include'
+
+if test "$ac_x_includes" = no; then
+ # Guess where to find include files, by looking for Intrinsic.h.
+ # First, try using that file with no special directory specified.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <X11/Intrinsic.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_cxx_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ # We can compile using X headers with no special include directory.
+ac_x_includes=
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ for ac_dir in $ac_x_header_dirs; do
+ if test -r "$ac_dir/X11/Intrinsic.h"; then
+ ac_x_includes=$ac_dir
+ break
+ fi
+done
+fi
+rm -f conftest.err conftest.$ac_ext
+fi # $ac_x_includes = no
+
+if test "$ac_x_libraries" = no; then
+ # Check for the libraries.
+ # See if we find them without any special options.
+ # Don't add to $LIBS permanently.
+ ac_save_LIBS=$LIBS
+ LIBS="-lXt $LIBS"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <X11/Intrinsic.h>
+int
+main ()
+{
+XtMalloc (0)
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ LIBS=$ac_save_LIBS
+# We can link X programs with no special library path.
+ac_x_libraries=
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+LIBS=$ac_save_LIBS
+for ac_dir in `echo "$ac_x_includes $ac_x_header_dirs" | sed s/include/lib/g`
+do
+ # Don't even attempt the hair of trying to link an X program!
+ for ac_extension in a so sl; do
+ if test -r $ac_dir/libXt.$ac_extension; then
+ ac_x_libraries=$ac_dir
+ break 2
+ fi
+ done
+done
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi # $ac_x_libraries = no
+
+if test "$ac_x_includes" = no || test "$ac_x_libraries" = no; then
+ # Didn't find X anywhere. Cache the known absence of X.
+ ac_cv_have_x="have_x=no"
+else
+ # Record where we found X for the cache.
+ ac_cv_have_x="have_x=yes \
+ ac_x_includes=$ac_x_includes ac_x_libraries=$ac_x_libraries"
+fi
+fi
+
+ fi
+ eval "$ac_cv_have_x"
+fi # $with_x != no
+
+if test "$have_x" != yes; then
+ echo "$as_me:$LINENO: result: $have_x" >&5
+echo "${ECHO_T}$have_x" >&6
+ no_x=yes
+else
+ # If each of the values was on the command line, it overrides each guess.
+ test "x$x_includes" = xNONE && x_includes=$ac_x_includes
+ test "x$x_libraries" = xNONE && x_libraries=$ac_x_libraries
+ # Update the cache value to reflect the command line values.
+ ac_cv_have_x="have_x=yes \
+ ac_x_includes=$x_includes ac_x_libraries=$x_libraries"
+ echo "$as_me:$LINENO: result: libraries $x_libraries, headers $x_includes" >&5
+echo "${ECHO_T}libraries $x_libraries, headers $x_includes" >&6
+fi
+
+if test "$no_x" = yes; then
+ # Not all programs may use this symbol, but it does not hurt to define it.
+
+cat >>confdefs.h <<\_ACEOF
+#define X_DISPLAY_MISSING 1
+_ACEOF
+
+ X_CFLAGS= X_PRE_LIBS= X_LIBS= X_EXTRA_LIBS=
+else
+ if test -n "$x_includes"; then
+ X_CFLAGS="$X_CFLAGS -I$x_includes"
+ fi
+
+ # It would also be nice to do this for all -L options, not just this one.
+ if test -n "$x_libraries"; then
+ X_LIBS="$X_LIBS -L$x_libraries"
+ # For Solaris; some versions of Sun CC require a space after -R and
+ # others require no space. Words are not sufficient . . . .
+ case `(uname -sr) 2>/dev/null` in
+ "SunOS 5"*)
+ echo "$as_me:$LINENO: checking whether -R must be followed by a space" >&5
+echo $ECHO_N "checking whether -R must be followed by a space... $ECHO_C" >&6
+ ac_xsave_LIBS=$LIBS; LIBS="$LIBS -R$x_libraries"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_R_nospace=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_R_nospace=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ if test $ac_R_nospace = yes; then
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+ X_LIBS="$X_LIBS -R$x_libraries"
+ else
+ LIBS="$ac_xsave_LIBS -R $x_libraries"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_R_space=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_R_space=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ if test $ac_R_space = yes; then
+ echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+ X_LIBS="$X_LIBS -R $x_libraries"
+ else
+ echo "$as_me:$LINENO: result: neither works" >&5
+echo "${ECHO_T}neither works" >&6
+ fi
+ fi
+ LIBS=$ac_xsave_LIBS
+ esac
+ fi
+
+ # Check for system-dependent libraries X programs must link with.
+ # Do this before checking for the system-independent R6 libraries
+ # (-lICE), since we may need -lsocket or whatever for X linking.
+
+ if test "$ISC" = yes; then
+ X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl_s -linet"
+ else
+ # Martyn Johnson says this is needed for Ultrix, if the X
+ # libraries were built with DECnet support. And Karl Berry says
+ # the Alpha needs dnet_stub (dnet does not exist).
+ ac_xsave_LIBS="$LIBS"; LIBS="$LIBS $X_LIBS -lX11"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char XOpenDisplay ();
+int
+main ()
+{
+XOpenDisplay ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+echo "$as_me:$LINENO: checking for dnet_ntoa in -ldnet" >&5
+echo $ECHO_N "checking for dnet_ntoa in -ldnet... $ECHO_C" >&6
+if test "${ac_cv_lib_dnet_dnet_ntoa+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldnet $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char dnet_ntoa ();
+int
+main ()
+{
+dnet_ntoa ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_dnet_dnet_ntoa=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_dnet_dnet_ntoa=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_dnet_dnet_ntoa" >&5
+echo "${ECHO_T}$ac_cv_lib_dnet_dnet_ntoa" >&6
+if test $ac_cv_lib_dnet_dnet_ntoa = yes; then
+ X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet"
+fi
+
+ if test $ac_cv_lib_dnet_dnet_ntoa = no; then
+ echo "$as_me:$LINENO: checking for dnet_ntoa in -ldnet_stub" >&5
+echo $ECHO_N "checking for dnet_ntoa in -ldnet_stub... $ECHO_C" >&6
+if test "${ac_cv_lib_dnet_stub_dnet_ntoa+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldnet_stub $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char dnet_ntoa ();
+int
+main ()
+{
+dnet_ntoa ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_dnet_stub_dnet_ntoa=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_dnet_stub_dnet_ntoa=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_dnet_stub_dnet_ntoa" >&5
+echo "${ECHO_T}$ac_cv_lib_dnet_stub_dnet_ntoa" >&6
+if test $ac_cv_lib_dnet_stub_dnet_ntoa = yes; then
+ X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet_stub"
+fi
+
+ fi
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LIBS="$ac_xsave_LIBS"
+
+ # msh@cis.ufl.edu says -lnsl (and -lsocket) are needed for his 386/AT,
+ # to get the SysV transport functions.
+ # Chad R. Larson says the Pyramis MIS-ES running DC/OSx (SVR4)
+ # needs -lnsl.
+ # The nsl library prevents programs from opening the X display
+ # on Irix 5.2, according to T.E. Dickey.
+ # The functions gethostbyname, getservbyname, and inet_addr are
+ # in -lbsd on LynxOS 3.0.1/i386, according to Lars Hecking.
+ echo "$as_me:$LINENO: checking for gethostbyname" >&5
+echo $ECHO_N "checking for gethostbyname... $ECHO_C" >&6
+if test "${ac_cv_func_gethostbyname+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define gethostbyname to an innocuous variant, in case <limits.h> declares gethostbyname.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define gethostbyname innocuous_gethostbyname
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char gethostbyname (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef gethostbyname
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char gethostbyname ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_gethostbyname) || defined (__stub___gethostbyname)
+choke me
+#else
+char (*f) () = gethostbyname;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != gethostbyname;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_gethostbyname=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_gethostbyname=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_gethostbyname" >&5
+echo "${ECHO_T}$ac_cv_func_gethostbyname" >&6
+
+ if test $ac_cv_func_gethostbyname = no; then
+ echo "$as_me:$LINENO: checking for gethostbyname in -lnsl" >&5
+echo $ECHO_N "checking for gethostbyname in -lnsl... $ECHO_C" >&6
+if test "${ac_cv_lib_nsl_gethostbyname+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lnsl $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char gethostbyname ();
+int
+main ()
+{
+gethostbyname ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_nsl_gethostbyname=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_nsl_gethostbyname=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_nsl_gethostbyname" >&5
+echo "${ECHO_T}$ac_cv_lib_nsl_gethostbyname" >&6
+if test $ac_cv_lib_nsl_gethostbyname = yes; then
+ X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl"
+fi
+
+ if test $ac_cv_lib_nsl_gethostbyname = no; then
+ echo "$as_me:$LINENO: checking for gethostbyname in -lbsd" >&5
+echo $ECHO_N "checking for gethostbyname in -lbsd... $ECHO_C" >&6
+if test "${ac_cv_lib_bsd_gethostbyname+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lbsd $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char gethostbyname ();
+int
+main ()
+{
+gethostbyname ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_bsd_gethostbyname=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_bsd_gethostbyname=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_bsd_gethostbyname" >&5
+echo "${ECHO_T}$ac_cv_lib_bsd_gethostbyname" >&6
+if test $ac_cv_lib_bsd_gethostbyname = yes; then
+ X_EXTRA_LIBS="$X_EXTRA_LIBS -lbsd"
+fi
+
+ fi
+ fi
+
+ # lieder@skyler.mavd.honeywell.com says without -lsocket,
+ # socket/setsockopt and other routines are undefined under SCO ODT
+ # 2.0. But -lsocket is broken on IRIX 5.2 (and is not necessary
+ # on later versions), says Simon Leinen: it contains gethostby*
+ # variants that don't use the name server (or something). -lsocket
+ # must be given before -lnsl if both are needed. We assume that
+ # if connect needs -lnsl, so does gethostbyname.
+ echo "$as_me:$LINENO: checking for connect" >&5
+echo $ECHO_N "checking for connect... $ECHO_C" >&6
+if test "${ac_cv_func_connect+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define connect to an innocuous variant, in case <limits.h> declares connect.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define connect innocuous_connect
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char connect (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef connect
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char connect ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_connect) || defined (__stub___connect)
+choke me
+#else
+char (*f) () = connect;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != connect;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_connect=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_connect=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_connect" >&5
+echo "${ECHO_T}$ac_cv_func_connect" >&6
+
+ if test $ac_cv_func_connect = no; then
+ echo "$as_me:$LINENO: checking for connect in -lsocket" >&5
+echo $ECHO_N "checking for connect in -lsocket... $ECHO_C" >&6
+if test "${ac_cv_lib_socket_connect+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsocket $X_EXTRA_LIBS $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char connect ();
+int
+main ()
+{
+connect ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_socket_connect=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_socket_connect=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_socket_connect" >&5
+echo "${ECHO_T}$ac_cv_lib_socket_connect" >&6
+if test $ac_cv_lib_socket_connect = yes; then
+ X_EXTRA_LIBS="-lsocket $X_EXTRA_LIBS"
+fi
+
+ fi
+
+ # Guillermo Gomez says -lposix is necessary on A/UX.
+ echo "$as_me:$LINENO: checking for remove" >&5
+echo $ECHO_N "checking for remove... $ECHO_C" >&6
+if test "${ac_cv_func_remove+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define remove to an innocuous variant, in case <limits.h> declares remove.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define remove innocuous_remove
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char remove (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef remove
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char remove ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_remove) || defined (__stub___remove)
+choke me
+#else
+char (*f) () = remove;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != remove;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_remove=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_remove=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_remove" >&5
+echo "${ECHO_T}$ac_cv_func_remove" >&6
+
+ if test $ac_cv_func_remove = no; then
+ echo "$as_me:$LINENO: checking for remove in -lposix" >&5
+echo $ECHO_N "checking for remove in -lposix... $ECHO_C" >&6
+if test "${ac_cv_lib_posix_remove+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lposix $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char remove ();
+int
+main ()
+{
+remove ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_posix_remove=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_posix_remove=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_posix_remove" >&5
+echo "${ECHO_T}$ac_cv_lib_posix_remove" >&6
+if test $ac_cv_lib_posix_remove = yes; then
+ X_EXTRA_LIBS="$X_EXTRA_LIBS -lposix"
+fi
+
+ fi
+
+ # BSDI BSD/OS 2.1 needs -lipc for XOpenDisplay.
+ echo "$as_me:$LINENO: checking for shmat" >&5
+echo $ECHO_N "checking for shmat... $ECHO_C" >&6
+if test "${ac_cv_func_shmat+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define shmat to an innocuous variant, in case <limits.h> declares shmat.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define shmat innocuous_shmat
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char shmat (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef shmat
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char shmat ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_shmat) || defined (__stub___shmat)
+choke me
+#else
+char (*f) () = shmat;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != shmat;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_shmat=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_shmat=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_shmat" >&5
+echo "${ECHO_T}$ac_cv_func_shmat" >&6
+
+ if test $ac_cv_func_shmat = no; then
+ echo "$as_me:$LINENO: checking for shmat in -lipc" >&5
+echo $ECHO_N "checking for shmat in -lipc... $ECHO_C" >&6
+if test "${ac_cv_lib_ipc_shmat+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lipc $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char shmat ();
+int
+main ()
+{
+shmat ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_ipc_shmat=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_ipc_shmat=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_ipc_shmat" >&5
+echo "${ECHO_T}$ac_cv_lib_ipc_shmat" >&6
+if test $ac_cv_lib_ipc_shmat = yes; then
+ X_EXTRA_LIBS="$X_EXTRA_LIBS -lipc"
+fi
+
+ fi
+ fi
+
+ # Check for libraries that X11R6 Xt/Xaw programs need.
+ ac_save_LDFLAGS=$LDFLAGS
+ test -n "$x_libraries" && LDFLAGS="$LDFLAGS -L$x_libraries"
+ # SM needs ICE to (dynamically) link under SunOS 4.x (so we have to
+ # check for ICE first), but we must link in the order -lSM -lICE or
+ # we get undefined symbols. So assume we have SM if we have ICE.
+ # These have to be linked with before -lX11, unlike the other
+ # libraries we check for below, so use a different variable.
+ # John Interrante, Karl Berry
+ echo "$as_me:$LINENO: checking for IceConnectionNumber in -lICE" >&5
+echo $ECHO_N "checking for IceConnectionNumber in -lICE... $ECHO_C" >&6
+if test "${ac_cv_lib_ICE_IceConnectionNumber+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lICE $X_EXTRA_LIBS $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char IceConnectionNumber ();
+int
+main ()
+{
+IceConnectionNumber ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_ICE_IceConnectionNumber=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_ICE_IceConnectionNumber=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_ICE_IceConnectionNumber" >&5
+echo "${ECHO_T}$ac_cv_lib_ICE_IceConnectionNumber" >&6
+if test $ac_cv_lib_ICE_IceConnectionNumber = yes; then
+ X_PRE_LIBS="$X_PRE_LIBS -lSM -lICE"
+fi
+
+ LDFLAGS=$ac_save_LDFLAGS
+
+fi
+
+
+
+ac_help="$ac_help
+ --with-symbols add the -g flag to produce the debug symbols
+ --with-use-malloc add the __USE_MALLOC flag to avoid the STL allocators
+ --with-info define INFO at compile time to get basic log output
+ --with-valgrind clean up allocated buffers to avoid valgrind warnings
+ --with-version use this version for produced libraries
+
+ --with-static-png enable static linking of PNG library
+ --with-static-jpeg enable static linking of JPEG library
+ --with-static-z enable static linking of Z library"
+
+
+
+echo "$as_me:$LINENO: checking for Cygwin32 environment" >&5
+echo $ECHO_N "checking for Cygwin32 environment... $ECHO_C" >&6
+if test "${nxconf_cv_cygwin32+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+return __CYGWIN32__;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ nxconf_cv_cygwin32=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+nxconf_cv_cygwin32=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f conftest*
+fi
+echo "$as_me:$LINENO: result: $nxconf_cv_cygwin32" >&5
+echo "${ECHO_T}$nxconf_cv_cygwin32" >&6
+CYGWIN32=
+test "$nxconf_cv_cygwin32" = yes && CYGWIN32=yes
+
+
+
+echo "$as_me:$LINENO: checking for Amd64 environment" >&5
+echo $ECHO_N "checking for Amd64 environment... $ECHO_C" >&6
+if test "${nxconf_cv_amd64+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+return (__amd64__ || __x86_64__);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ nxconf_cv_amd64=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+nxconf_cv_amd64=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f conftest*
+fi
+echo "$as_me:$LINENO: result: $nxconf_cv_amd64" >&5
+echo "${ECHO_T}$nxconf_cv_amd64" >&6
+AMD64=
+test "$nxconf_cv_amd64" = yes && AMD64=yes
+
+
+
+echo "$as_me:$LINENO: checking for Darwin environment" >&5
+echo $ECHO_N "checking for Darwin environment... $ECHO_C" >&6
+if test "${nxconf_cv_darwin+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+return __APPLE__;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ nxconf_cv_darwin=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+nxconf_cv_darwin=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f conftest*
+fi
+echo "$as_me:$LINENO: result: $nxconf_cv_darwin" >&5
+echo "${ECHO_T}$nxconf_cv_darwin" >&6
+DARWIN=
+test "$nxconf_cv_darwin" = yes && DARWIN=yes
+
+
+
+echo "$as_me:$LINENO: checking for Solaris environment" >&5
+echo $ECHO_N "checking for Solaris environment... $ECHO_C" >&6
+if test "${nxconf_cv_sun+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+return __sun;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ nxconf_cv_sun=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+nxconf_cv_sun=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f conftest*
+fi
+echo "$as_me:$LINENO: result: $nxconf_cv_sun" >&5
+echo "${ECHO_T}$nxconf_cv_sun" >&6
+SUN=
+test "$nxconf_cv_sun" = yes && SUN=yes
+
+
+
+echo "$as_me:$LINENO: checking for FreeBSD environment" >&5
+echo $ECHO_N "checking for FreeBSD environment... $ECHO_C" >&6
+if test "${nxconf_cv_freebsd+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+return __FreeBSD__;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ nxconf_cv_freebsd=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+nxconf_cv_freebsd=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f conftest*
+fi
+echo "$as_me:$LINENO: result: $nxconf_cv_freebsd" >&5
+echo "${ECHO_T}$nxconf_cv_freebsd" >&6
+FreeBSD=
+test "$nxconf_cv_freebsd" = yes && FreeBSD=yes
+
+
+if test "$CYGWIN32" != yes -a "$DARWIN" != yes; then
+ CXXFLAGS="$CXXFLAGS -fPIC"
+ CFLAGS="$CFLAGS -fPIC"
+fi
+
+
+if test "$SUN" = yes; then
+ LIBS="$LIBS -L/usr/sfw/lib -lsocket "
+ CXXFLAGS="$CXXFLAGS -I/usr/sfw/include"
+ CFLAGS="$CFLAGS -I/usr/sfw/include"
+fi
+
+
+if test "$FreeBSD" = yes; then
+ LIBS="$LIBS -L/usr/local/lib"
+ CXXFLAGS="$CXXFLAGS -I/usr/local/include"
+ CFLAGS="$CFLAGS -I/usr/local/include"
+fi
+
+
+if test "$DARWIN" = yes; then
+ LDFLAGS="$LDFLAGS -bundle"
+elif test "$SUN" = yes; then
+ LDFLAGS="$LDFLAGS -G -h \$(LIBLOAD)"
+else
+ LDFLAGS="$LDFLAGS -Wl,-soname,\$(LIBLOAD)"
+fi
+
+
+
+echo "$as_me:$LINENO: checking for in_addr_t" >&5
+echo $ECHO_N "checking for in_addr_t... $ECHO_C" >&6
+if test "${nxconf_cv_inaddrt+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <netinet/in.h>
+int
+main ()
+{
+in_addr_t t; t = 1; return t;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ nxconf_cv_inaddrt=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+nxconf_cv_inaddrt=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f conftest*
+fi
+echo "$as_me:$LINENO: result: $nxconf_cv_inaddrt" >&5
+echo "${ECHO_T}$nxconf_cv_inaddrt" >&6
+INADDRT=
+test "$nxconf_cv_inaddrt" = yes && INADDRT=yes
+
+
+if test "$INADDRT" != yes ; then
+ echo -e "using unsigned int for type in_addr_t"
+ CXXFLAGS="$CXXFLAGS -DIN_ADDR_T=unsigned"
+ CFLAGS="$CFLAGS -DIN_ADDR_T=unsigned"
+else
+ CXXFLAGS="$CXXFLAGS -DIN_ADDR_T=in_addr_t"
+ CFLAGS="$CFLAGS -DIN_ADDR_T=in_addr_t"
+fi
+
+
+
+
+if test "${with_version}" = yes; then
+ VERSION=${ac_option}
+else
+ VERSION=`cat VERSION`
+fi
+echo -e "compiling version ${VERSION}"
+
+LIBVERSION=`echo ${VERSION} | cut -d '.' -f 1`
+
+CXXFLAGS="$CXXFLAGS -DVERSION=\\\"${VERSION}\\\""
+CFLAGS="$CFLAGS -DVERSION=\\\"${VERSION}\\\""
+
+
+if test "${with_static_png}" = yes; then
+ echo -e "enabling static linking of PNG library"
+ if test "$SUN" = yes && test -f "/usr/sfw/lib/libpng.a"; then
+ LIBSTATIC="$LIBSTATIC /usr/sfw/lib/libpng.a"
+ else
+ if test -f "/usr/lib/libpng.a" ; then
+ LIBSTATIC="$LIBSTATIC /usr/lib/libpng.a"
+ else
+ if test -f "/usr/local/lib/libpng.a" ; then
+ echo -e "assuming libpng.a in /usr/local/lib"
+ LIBSTATIC="$LIBSTATIC /usr/local/lib/libpng.a"
+ else
+ echo -e "Warning: assuming libpng.a in the local path"
+ LIBSTATIC="$LIBSTATIC libpng.a"
+ fi
+ fi
+ fi
+else
+ echo -e "enabling dynamic linking of PNG library"
+ LIBSHARED="$LIBSHARED -lpng"
+fi
+
+
+if test "${with_static_jpeg}" = yes; then
+ echo -e "enabling static linking of JPEG library"
+ if test "$SUN" = yes && test -f "/usr/sfw/lib/libjpeg.a"; then
+ LIBSTATIC="$LIBSTATIC /usr/sfw/lib/libjpeg.a"
+ else
+ if test -f "/usr/lib/libjpeg.a" ; then
+ LIBSTATIC="$LIBSTATIC /usr/lib/libjpeg.a"
+ else
+ if test -f "/usr/local/lib/libjpeg.a" ; then
+ echo -e "assuming libjpeg.a in /usr/local/lib"
+ LIBSTATIC="$LIBSTATIC /usr/local/lib/libjpeg.a"
+ else
+ echo -e "Warning: assuming libjpeg.a in the local path"
+ LIBSTATIC="$LIBSTATIC libjpeg.a"
+ fi
+ fi
+ fi
+else
+ echo -e "enabling dynamic linking of JPEG library"
+ LIBSHARED="$LIBSHARED -ljpeg"
+fi
+
+
+if test "${with_static_z}" = yes; then
+ echo -e "enabling static linking of Z library"
+ if test "$SUN" = yes && test -f "/usr/sfw/lib/libz.a"; then
+ LIBSTATIC="$LIBSTATIC /usr/sfw/lib/libz.a"
+ else
+ if test -f "/usr/lib/libz.a" ; then
+ LIBSTATIC="$LIBSTATIC /usr/lib/libz.a"
+ else
+ if test -f "/usr/local/lib/libz.a" ; then
+ echo -e "assuming libz.a in /usr/local/lib"
+ LIBSTATIC="$LIBSTATIC /usr/local/lib/libz.a"
+ else
+ echo -e "Warning: assuming libz.a in the local path"
+ LIBSTATIC="$LIBSTATIC libz.a"
+ fi
+ fi
+ fi
+else
+ echo -e "enabling dynamic linking of Z library"
+ LIBSHARED="$LIBSHARED -lz"
+fi
+
+
+if test "$DARWIN" = yes ; then
+ LIBS="$LIBS $LIBSTATIC $LIBSHARED"
+elif test "$SUN" = yes ; then
+ LIBS="$LIBS $LIBSTATIC $LIBSHARED"
+else
+ LIBS="$LIBS $LIBSTATIC -shared $LIBSHARED"
+fi
+
+
+if test "${with_symbols}" = yes; then
+ echo -e "enabling production of debug symbols"
+ CXXFLAGS="-g $CXXFLAGS"
+ CFLAGS="-g $CFLAGS"
+else
+ echo -e "disabling production of debug symbols"
+fi
+
+
+if test "${with_use_malloc}" = yes; then
+ echo -e "disabling use of the STL allocators"
+ CXXFLAGS="$CXXFLAGS -D__USE_MALLOC"
+else
+ echo -e "enabling use of the STL allocators"
+fi
+
+
+if test "${with_info}" = yes; then
+ echo -e "enabling info output in the log file"
+ CXXFLAGS="$CXXFLAGS -DINFO"
+ CFLAGS="$CFLAGS -DINFO"
+else
+ echo -e "disabling info output in the log file"
+fi
+
+
+if test "${with_valgrind}" = yes; then
+ echo -e "enabling valgrind memory checker workarounds"
+ CXXFLAGS="$CXXFLAGS -DVALGRIND"
+ CFLAGS="$CFLAGS -DVALGRIND"
+else
+ echo -e "disabling valgrind memory checker workarounds"
+fi
+
+
+
+
+if test -x "../nx-X11/config/makedepend/makedepend" ; then
+ MAKEDEPEND=../nx-X11/config/makedepend/makedepend
+else
+ if test -x "/usr/X11R6/bin/makedepend" ; then
+ MAKEDEPEND=/usr/X11R6/bin/makedepend
+ else
+ if test -x "/usr/openwin/bin/makedepend" ; then
+ MAKEDEPEND=/usr/openwin/bin/makedepend
+ else
+ MAKEDEPEND=/usr/bin/makedepend
+ fi
+ fi
+fi
+
+
+
+
+if test "$CYGWIN32" = yes; then
+ ALL="\$(LIBCYGARCHIVE) \$(LIBCYGSHARED) \$(LIBARCHIVE)"
+ LIBS="-lstdc++ -lpng -ljpeg -lz"
+else
+ ALL="\$(LIBFULL) \$(LIBLOAD) \$(LIBSHARED) \$(LIBARCHIVE)"
+fi
+
+ ac_config_files="$ac_config_files Makefile"
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems. If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+{
+ (set) 2>&1 |
+ case `(ac_space=' '; set | grep ac_space) 2>&1` in
+ *ac_space=\ *)
+ # `set' does not quote correctly, so add quotes (double-quote
+ # substitution turns \\\\ into \\, and sed turns \\ into \).
+ sed -n \
+ "s/'/'\\\\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+ ;;
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n \
+ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+ ;;
+ esac;
+} |
+ sed '
+ t clear
+ : clear
+ s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+ t end
+ /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+ : end' >>confcache
+if diff $cache_file confcache >/dev/null 2>&1; then :; else
+ if test -w $cache_file; then
+ test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file"
+ cat confcache >$cache_file
+ else
+ echo "not updating unwritable cache $cache_file"
+ fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=/{
+s/:*\$(srcdir):*/:/;
+s/:*\${srcdir}:*/:/;
+s/:*@srcdir@:*/:/;
+s/^\([^=]*=[ ]*\):*/\1/;
+s/:*$//;
+s/^[^=]*=[ ]*$//;
+}'
+fi
+
+# Transform confdefs.h into DEFS.
+# Protect against shell expansion while executing Makefile rules.
+# Protect against Makefile macro expansion.
+#
+# If the first sed substitution is executed (which looks for macros that
+# take arguments), then we branch to the quote section. Otherwise,
+# look for a macro that doesn't take arguments.
+cat >confdef2opt.sed <<\_ACEOF
+t clear
+: clear
+s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\),-D\1=\2,g
+t quote
+s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\),-D\1=\2,g
+t quote
+d
+: quote
+s,[ `~#$^&*(){}\\|;'"<>?],\\&,g
+s,\[,\\&,g
+s,\],\\&,g
+s,\$,$$,g
+p
+_ACEOF
+# We use echo to avoid assuming a particular line-breaking character.
+# The extra dot is to prevent the shell from consuming trailing
+# line-breaks from the sub-command output. A line-break within
+# single-quotes doesn't work because, if this script is created in a
+# platform that uses two characters for line-breaks (e.g., DOS), tr
+# would break.
+ac_LF_and_DOT=`echo; echo .`
+DEFS=`sed -n -f confdef2opt.sed confdefs.h | tr "$ac_LF_and_DOT" ' .'`
+rm -f confdef2opt.sed
+
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+ # 1. Remove the extension, and $U if already installed.
+ ac_i=`echo "$ac_i" |
+ sed 's/\$U\././;s/\.o$//;s/\.obj$//'`
+ # 2. Add them.
+ ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext"
+ ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: ${CONFIG_STATUS=./config.status}
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+ set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+ LC_TELEPHONE LC_TIME
+do
+ if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+ eval $as_var=C; export $as_var
+ else
+ $as_unset $as_var
+ fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)$' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+ /^X\/\(\/\/\)$/{ s//\1/; q; }
+ /^X\/\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" || {
+ # Find who we are. Look in the path if we contain no path at all
+ # relative or not.
+ case $0 in
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+ ;;
+ esac
+ # We did not find ourselves, most probably we were run as `sh COMMAND'
+ # in which case we are not to be found in the path.
+ if test "x$as_myself" = x; then
+ as_myself=$0
+ fi
+ if test ! -f "$as_myself"; then
+ { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5
+echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ case $CONFIG_SHELL in
+ '')
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for as_base in sh bash ksh sh5; do
+ case $as_dir in
+ /*)
+ if ("$as_dir/$as_base" -c '
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then
+ $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+ $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+ CONFIG_SHELL=$as_dir/$as_base
+ export CONFIG_SHELL
+ exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+ fi;;
+ esac
+ done
+done
+;;
+ esac
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line before each line; the second 'sed' does the real
+ # work. The second script uses 'N' to pair each line-number line
+ # with the numbered line, and appends trailing '-' during
+ # substitution so that $LINENO is not a special case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-)
+ sed '=' <$as_myself |
+ sed '
+ N
+ s,$,-,
+ : loop
+ s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+ t loop
+ s,-$,,
+ s,^['$as_cr_digits']*\n,,
+ ' >$as_me.lineno &&
+ chmod +x $as_me.lineno ||
+ { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5
+echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;}
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensible to this).
+ . ./$as_me.lineno
+ # Exit status is that of the last command.
+ exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+ *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T=' ' ;;
+ *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+ *) ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+ # We could just check for DJGPP; but this test a) works b) is more generic
+ # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+ if test -f conf$$.exe; then
+ # Don't use ln at all; we don't have any links
+ as_ln_s='cp -p'
+ else
+ as_ln_s='ln -s'
+ fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p=:
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" $as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+exec 6>&1
+
+# Open the log real soon, to keep \$[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling. Logging --version etc. is OK.
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+} >&5
+cat >&5 <<_CSEOF
+
+This file was extended by $as_me, which was
+generated by GNU Autoconf 2.59. Invocation command line was
+
+ CONFIG_FILES = $CONFIG_FILES
+ CONFIG_HEADERS = $CONFIG_HEADERS
+ CONFIG_LINKS = $CONFIG_LINKS
+ CONFIG_COMMANDS = $CONFIG_COMMANDS
+ $ $0 $@
+
+_CSEOF
+echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5
+echo >&5
+_ACEOF
+
+# Files that config.status was made for.
+if test -n "$ac_config_files"; then
+ echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_headers"; then
+ echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_links"; then
+ echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_commands"; then
+ echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTIONS] [FILE]...
+
+ -h, --help print this help, then exit
+ -V, --version print version number, then exit
+ -q, --quiet do not print progress messages
+ -d, --debug don't remove temporary files
+ --recheck update $as_me by reconfiguring in the same conditions
+ --file=FILE[:TEMPLATE]
+ instantiate the configuration file FILE
+
+Configuration files:
+$config_files
+
+Report bugs to <bug-autoconf@gnu.org>."
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+ac_cs_version="\\
+config.status
+configured by $0, generated by GNU Autoconf 2.59,
+ with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+srcdir=$srcdir
+INSTALL="$INSTALL"
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+# If no file are specified by the user, then we need to provide default
+# value. By we need to know if files were specified by the user.
+ac_need_defaults=:
+while test $# != 0
+do
+ case $1 in
+ --*=*)
+ ac_option=`expr "x$1" : 'x\([^=]*\)='`
+ ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'`
+ ac_shift=:
+ ;;
+ -*)
+ ac_option=$1
+ ac_optarg=$2
+ ac_shift=shift
+ ;;
+ *) # This is not an option, so the user has probably given explicit
+ # arguments.
+ ac_option=$1
+ ac_need_defaults=false;;
+ esac
+
+ case $ac_option in
+ # Handling of the options.
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ ac_cs_recheck=: ;;
+ --version | --vers* | -V )
+ echo "$ac_cs_version"; exit 0 ;;
+ --he | --h)
+ # Conflict between --help and --header
+ { { echo "$as_me:$LINENO: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2;}
+ { (exit 1); exit 1; }; };;
+ --help | --hel | -h )
+ echo "$ac_cs_usage"; exit 0 ;;
+ --debug | --d* | -d )
+ debug=: ;;
+ --file | --fil | --fi | --f )
+ $ac_shift
+ CONFIG_FILES="$CONFIG_FILES $ac_optarg"
+ ac_need_defaults=false;;
+ --header | --heade | --head | --hea )
+ $ac_shift
+ CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg"
+ ac_need_defaults=false;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil | --si | --s)
+ ac_cs_silent=: ;;
+
+ # This is an error.
+ -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2;}
+ { (exit 1); exit 1; }; } ;;
+
+ *) ac_config_targets="$ac_config_targets $1" ;;
+
+ esac
+ shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+ exec 6>/dev/null
+ ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+if \$ac_cs_recheck; then
+ echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6
+ exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+fi
+
+_ACEOF
+
+
+
+
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_config_target in $ac_config_targets
+do
+ case "$ac_config_target" in
+ # Handling of arguments.
+ "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+ *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used. Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+ test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+fi
+
+# Have a temporary directory for convenience. Make it in the build tree
+# simply because there is no reason to put it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Create a temporary directory, and hook for its removal unless debugging.
+$debug ||
+{
+ trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0
+ trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+
+# Create a (secure) tmp directory for tmp files.
+
+{
+ tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` &&
+ test -n "$tmp" && test -d "$tmp"
+} ||
+{
+ tmp=./confstat$$-$RANDOM
+ (umask 077 && mkdir $tmp)
+} ||
+{
+ echo "$me: cannot create a temporary directory in ." >&2
+ { (exit 1); exit 1; }
+}
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+
+#
+# CONFIG_FILES section.
+#
+
+# No need to generate the scripts if there are no CONFIG_FILES.
+# This happens for instance when ./config.status config.h
+if test -n "\$CONFIG_FILES"; then
+ # Protect against being on the right side of a sed subst in config.status.
+ sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g;
+ s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF
+s,@SHELL@,$SHELL,;t t
+s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t
+s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t
+s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t
+s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t
+s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t
+s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t
+s,@exec_prefix@,$exec_prefix,;t t
+s,@prefix@,$prefix,;t t
+s,@program_transform_name@,$program_transform_name,;t t
+s,@bindir@,$bindir,;t t
+s,@sbindir@,$sbindir,;t t
+s,@libexecdir@,$libexecdir,;t t
+s,@datadir@,$datadir,;t t
+s,@sysconfdir@,$sysconfdir,;t t
+s,@sharedstatedir@,$sharedstatedir,;t t
+s,@localstatedir@,$localstatedir,;t t
+s,@libdir@,$libdir,;t t
+s,@includedir@,$includedir,;t t
+s,@oldincludedir@,$oldincludedir,;t t
+s,@infodir@,$infodir,;t t
+s,@mandir@,$mandir,;t t
+s,@build_alias@,$build_alias,;t t
+s,@host_alias@,$host_alias,;t t
+s,@target_alias@,$target_alias,;t t
+s,@DEFS@,$DEFS,;t t
+s,@ECHO_C@,$ECHO_C,;t t
+s,@ECHO_N@,$ECHO_N,;t t
+s,@ECHO_T@,$ECHO_T,;t t
+s,@LIBS@,$LIBS,;t t
+s,@armcxx@,$armcxx,;t t
+s,@armcc@,$armcc,;t t
+s,@CXX@,$CXX,;t t
+s,@CXXFLAGS@,$CXXFLAGS,;t t
+s,@LDFLAGS@,$LDFLAGS,;t t
+s,@CPPFLAGS@,$CPPFLAGS,;t t
+s,@ac_ct_CXX@,$ac_ct_CXX,;t t
+s,@EXEEXT@,$EXEEXT,;t t
+s,@OBJEXT@,$OBJEXT,;t t
+s,@CC@,$CC,;t t
+s,@CFLAGS@,$CFLAGS,;t t
+s,@ac_ct_CC@,$ac_ct_CC,;t t
+s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t
+s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t
+s,@INSTALL_DATA@,$INSTALL_DATA,;t t
+s,@CXXCPP@,$CXXCPP,;t t
+s,@X_CFLAGS@,$X_CFLAGS,;t t
+s,@X_PRE_LIBS@,$X_PRE_LIBS,;t t
+s,@X_LIBS@,$X_LIBS,;t t
+s,@X_EXTRA_LIBS@,$X_EXTRA_LIBS,;t t
+s,@LIBVERSION@,$LIBVERSION,;t t
+s,@VERSION@,$VERSION,;t t
+s,@MAKEDEPEND@,$MAKEDEPEND,;t t
+s,@ALL@,$ALL,;t t
+s,@LIBOBJS@,$LIBOBJS,;t t
+s,@LTLIBOBJS@,$LTLIBOBJS,;t t
+CEOF
+
+_ACEOF
+
+ cat >>$CONFIG_STATUS <<\_ACEOF
+ # Split the substitutions into bite-sized pieces for seds with
+ # small command number limits, like on Digital OSF/1 and HP-UX.
+ ac_max_sed_lines=48
+ ac_sed_frag=1 # Number of current file.
+ ac_beg=1 # First line for current file.
+ ac_end=$ac_max_sed_lines # Line after last line for current file.
+ ac_more_lines=:
+ ac_sed_cmds=
+ while $ac_more_lines; do
+ if test $ac_beg -gt 1; then
+ sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+ else
+ sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+ fi
+ if test ! -s $tmp/subs.frag; then
+ ac_more_lines=false
+ else
+ # The purpose of the label and of the branching condition is to
+ # speed up the sed processing (if there are no `@' at all, there
+ # is no need to browse any of the substitutions).
+ # These are the two extra sed commands mentioned above.
+ (echo ':t
+ /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed"
+ else
+ ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed"
+ fi
+ ac_sed_frag=`expr $ac_sed_frag + 1`
+ ac_beg=$ac_end
+ ac_end=`expr $ac_end + $ac_max_sed_lines`
+ fi
+ done
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds=cat
+ fi
+fi # test -n "$CONFIG_FILES"
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case $ac_file in
+ - | *:- | *:-:* ) # input from stdin
+ cat >$tmp/stdin
+ ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+ *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+ * ) ac_file_in=$ac_file.in ;;
+ esac
+
+ # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories.
+ ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ { if $as_mkdir_p; then
+ mkdir -p "$ac_dir"
+ else
+ as_dir="$ac_dir"
+ as_dirs=
+ while test ! -d "$as_dir"; do
+ as_dirs="$as_dir $as_dirs"
+ as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ done
+ test ! -n "$as_dirs" || mkdir $as_dirs
+ fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+ { (exit 1); exit 1; }; }; }
+
+ ac_builddir=.
+
+if test "$ac_dir" != .; then
+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+ ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+ .) # No --srcdir option. We are building in place.
+ ac_srcdir=.
+ if test -z "$ac_top_builddir"; then
+ ac_top_srcdir=.
+ else
+ ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+ fi ;;
+ [\\/]* | ?:[\\/]* ) # Absolute path.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir ;;
+ *) # Relative path.
+ ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+ case "$ac_dir" in
+ .) ac_abs_builddir=`pwd`;;
+ [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+ *) ac_abs_builddir=`pwd`/"$ac_dir";;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+ case ${ac_top_builddir}. in
+ .) ac_abs_top_builddir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+ *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+ case $ac_srcdir in
+ .) ac_abs_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+ *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+ case $ac_top_srcdir in
+ .) ac_abs_top_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+ *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+ esac;;
+esac
+
+
+ case $INSTALL in
+ [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+ *) ac_INSTALL=$ac_top_builddir$INSTALL ;;
+ esac
+
+ if test x"$ac_file" != x-; then
+ { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+ rm -f "$ac_file"
+ fi
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ if test x"$ac_file" = x-; then
+ configure_input=
+ else
+ configure_input="$ac_file. "
+ fi
+ configure_input=$configure_input"Generated from `echo $ac_file_in |
+ sed 's,.*/,,'` by configure."
+
+ # First look for the input files in the build tree, otherwise in the
+ # src tree.
+ ac_file_inputs=`IFS=:
+ for f in $ac_file_in; do
+ case $f in
+ -) echo $tmp/stdin ;;
+ [\\/$]*)
+ # Absolute (can't be DOS-style, as IFS=:)
+ test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+ { (exit 1); exit 1; }; }
+ echo "$f";;
+ *) # Relative
+ if test -f "$f"; then
+ # Build tree
+ echo "$f"
+ elif test -f "$srcdir/$f"; then
+ # Source tree
+ echo "$srcdir/$f"
+ else
+ # /dev/null tree
+ { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+ { (exit 1); exit 1; }; }
+ fi;;
+ esac
+ done` || { (exit 1); exit 1; }
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+ sed "$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s,@configure_input@,$configure_input,;t t
+s,@srcdir@,$ac_srcdir,;t t
+s,@abs_srcdir@,$ac_abs_srcdir,;t t
+s,@top_srcdir@,$ac_top_srcdir,;t t
+s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t
+s,@builddir@,$ac_builddir,;t t
+s,@abs_builddir@,$ac_abs_builddir,;t t
+s,@top_builddir@,$ac_top_builddir,;t t
+s,@abs_top_builddir@,$ac_abs_top_builddir,;t t
+s,@INSTALL@,$ac_INSTALL,;t t
+" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out
+ rm -f $tmp/stdin
+ if test x"$ac_file" != x-; then
+ mv $tmp/out $ac_file
+ else
+ cat $tmp/out
+ rm -f $tmp/out
+ fi
+
+done
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded. So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status. When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+ ac_cs_success=:
+ ac_config_status_args=
+ test "$silent" = yes &&
+ ac_config_status_args="$ac_config_status_args --quiet"
+ exec 5>/dev/null
+ $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+ exec 5>>config.log
+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+ # would make configure fail if this is the last instruction.
+ $ac_cs_success || { (exit 1); exit 1; }
+fi
+
diff --git a/nxcomp/configure.in b/nxcomp/configure.in
new file mode 100644
index 000000000..e9ab81da8
--- /dev/null
+++ b/nxcomp/configure.in
@@ -0,0 +1,393 @@
+dnl Process this file with autoconf to produce a configure script.
+
+dnl Prolog
+
+AC_INIT(NX.h)
+AC_PREREQ(2.13)
+
+dnl Set our default compilation flags.
+
+CXXFLAGS="-O3 -fno-rtti -fno-exceptions"
+CFLAGS="-O3"
+
+dnl Reset default linking directives.
+
+LIBSTATIC=""
+LIBSHARED=""
+
+dnl Prefer headers and libraries from nx-X11, if present.
+
+if test -d "../nx-X11/include" ; then
+ CXXFLAGS="$CXXFLAGS -I../nx-X11/exports/include"
+ CFLAGS="$CFLAGS -I../nx-X11/exports/include"
+ LIBS="$LIBS -L../nx-X11/exports/lib"
+fi
+
+dnl Check whether --with-ipaq was given.
+
+if test "${with_ipaq}" = yes; then
+ echo -e "enabling IPAQ configuration"
+ CXX="arm-linux-c++"
+ CC="arm-linux-gcc"
+ unset ac_cv_prog_armcxx
+ unset ac_cv_prog_armcc
+ unset ac_cv_prog_CXXCPP
+ AC_CHECK_PROG([armcxx],["$CXX"],[yes],[no],[$PATH])
+ AC_CHECK_PROG([armcc],["$CC"],[yes],[no],[$PATH])
+ if test $armcxx = "yes" && test $armcc = "yes" ; then
+ ac_cv_prog_CXX="$CXX"
+ ac_cv_prog_CC="$CC"
+ else
+ AC_MSG_ERROR(Installation or configuration problem. Cannot find compiler for arm-linux.)
+ fi
+else
+ unset ac_cv_prog_CXX
+ unset ac_cv_prog_CC
+ unset ac_cv_prog_CXXCPP
+fi
+
+dnl Check for programs.
+
+AC_PROG_CXX
+AC_PROG_CC
+AC_LANG_CPLUSPLUS
+
+dnl Check whether option -Wno-deprecated
+dnl is needed by GCC compiler.
+
+AC_MSG_CHECKING([whether compiler needs -Wno-deprecated])
+gcc_version=`${CC} --version | grep 'gcc (GCC) [[3-4]].' | head -n 1`
+case "${gcc_version}" in
+ gcc*)
+ AC_MSG_RESULT([yes])
+ CXXFLAGS="$CXXFLAGS -Wno-deprecated"
+ ;;
+
+ *)
+ AC_MSG_RESULT([no])
+ ;;
+esac
+
+AC_MSG_CHECKING([whether compiler accepts -Wmissing-declarations])
+gcc_version=`${CC} --version | grep 'gcc (GCC) [[3-4]].' | head -n 1`
+case "${gcc_version}" in
+ gcc*)
+ AC_MSG_RESULT([no])
+ ;;
+
+ *)
+ AC_MSG_RESULT([yes])
+ CXXFLAGS="$CXXFLAGS -Wmissing-declarations"
+ ;;
+esac
+
+dnl Check for BSD compatible install.
+
+AC_PROG_INSTALL
+
+dnl Check for extra header files.
+
+AC_PATH_XTRA
+
+dnl Custom addition.
+
+ac_help="$ac_help
+ --with-symbols add the -g flag to produce the debug symbols
+ --with-use-malloc add the __USE_MALLOC flag to avoid the STL allocators
+ --with-info define INFO at compile time to get basic log output
+ --with-valgrind clean up allocated buffers to avoid valgrind warnings
+ --with-version use this version for produced libraries
+
+ --with-static-png enable static linking of PNG library
+ --with-static-jpeg enable static linking of JPEG library
+ --with-static-z enable static linking of Z library"
+
+dnl Check to see if we're running under Cygwin32.
+
+AC_DEFUN(nxconf_CYGWIN32,
+[AC_CACHE_CHECK(for Cygwin32 environment, nxconf_cv_cygwin32,
+[AC_TRY_COMPILE(,[return __CYGWIN32__;],
+nxconf_cv_cygwin32=yes, nxconf_cv_cygwin32=no)
+rm -f conftest*])
+CYGWIN32=
+test "$nxconf_cv_cygwin32" = yes && CYGWIN32=yes])
+nxconf_CYGWIN32
+
+dnl Check whether we're building on a AMD64.
+
+AC_DEFUN(nxconf_AMD64,
+[AC_CACHE_CHECK(for Amd64 environment, nxconf_cv_amd64,
+[AC_TRY_COMPILE(,[return (__amd64__ || __x86_64__);],
+nxconf_cv_amd64=yes, nxconf_cv_amd64=no)
+rm -f conftest*])
+AMD64=
+test "$nxconf_cv_amd64" = yes && AMD64=yes])
+nxconf_AMD64
+
+dnl Check for Darwin environment.
+
+AC_DEFUN(nxconf_DARWIN,
+[AC_CACHE_CHECK(for Darwin environment, nxconf_cv_darwin,
+[AC_TRY_COMPILE(,[return __APPLE__;],
+nxconf_cv_darwin=yes, nxconf_cv_darwin=no)
+rm -f conftest*])
+DARWIN=
+test "$nxconf_cv_darwin" = yes && DARWIN=yes])
+nxconf_DARWIN
+
+dnl Check to see if we're running under Solaris.
+
+AC_DEFUN(nxconf_SUN,
+[AC_CACHE_CHECK(for Solaris environment, nxconf_cv_sun,
+[AC_TRY_COMPILE(,[return __sun;],
+nxconf_cv_sun=yes, nxconf_cv_sun=no)
+rm -f conftest*])
+SUN=
+test "$nxconf_cv_sun" = yes && SUN=yes])
+nxconf_SUN
+
+dnl Check to see if we're running under FreeBSD.
+
+AC_DEFUN(nxconf_FreeBSD,
+[AC_CACHE_CHECK(for FreeBSD environment, nxconf_cv_freebsd,
+[AC_TRY_COMPILE(,[return __FreeBSD__;],
+nxconf_cv_freebsd=yes, nxconf_cv_freebsd=no)
+rm -f conftest*])
+FreeBSD=
+test "$nxconf_cv_freebsd" = yes && FreeBSD=yes])
+nxconf_FreeBSD
+
+dnl Build PIC libraries.
+
+if test "$CYGWIN32" != yes -a "$DARWIN" != yes; then
+ CXXFLAGS="$CXXFLAGS -fPIC"
+ CFLAGS="$CFLAGS -fPIC"
+fi
+
+dnl Solaris requires the socket and gcc_s libs explicitly linked.
+dnl Note also that headers from default /usr/openwin/include/X11
+dnl cause a warning due to pragma in Xmd.h.
+
+if test "$SUN" = yes; then
+ LIBS="$LIBS -L/usr/sfw/lib -lsocket "
+ CXXFLAGS="$CXXFLAGS -I/usr/sfw/include"
+ CFLAGS="$CFLAGS -I/usr/sfw/include"
+fi
+
+dnl On FreeBSD search libraries and includes under /usr/local.
+
+if test "$FreeBSD" = yes; then
+ LIBS="$LIBS -L/usr/local/lib"
+ CXXFLAGS="$CXXFLAGS -I/usr/local/include"
+ CFLAGS="$CFLAGS -I/usr/local/include"
+fi
+
+dnl Under Darwin we don't have support for -soname option and
+dnl we need the -bundle flag. Under Solaris, instead, we need
+dnl the options -G -h.
+
+if test "$DARWIN" = yes; then
+ LDFLAGS="$LDFLAGS -bundle"
+elif test "$SUN" = yes; then
+ LDFLAGS="$LDFLAGS -G -h \$(LIBLOAD)"
+else
+ LDFLAGS="$LDFLAGS -Wl,-soname,\$(LIBLOAD)"
+fi
+
+dnl Check to see if in_addr_t is defined.
+dnl Could use a specific configure test.
+
+AC_DEFUN(nxconf_INADDRT,
+[AC_CACHE_CHECK(for in_addr_t, nxconf_cv_inaddrt,
+[AC_TRY_COMPILE([#include <netinet/in.h>],[in_addr_t t; t = 1; return t;],
+nxconf_cv_inaddrt=yes, nxconf_cv_inaddrt=no)
+rm -f conftest*])
+INADDRT=
+test "$nxconf_cv_inaddrt" = yes && INADDRT=yes])
+nxconf_INADDRT
+
+dnl If in_addr_t is not defined use unsigned int.
+
+if test "$INADDRT" != yes ; then
+ echo -e "using unsigned int for type in_addr_t"
+ CXXFLAGS="$CXXFLAGS -DIN_ADDR_T=unsigned"
+ CFLAGS="$CFLAGS -DIN_ADDR_T=unsigned"
+else
+ CXXFLAGS="$CXXFLAGS -DIN_ADDR_T=in_addr_t"
+ CFLAGS="$CFLAGS -DIN_ADDR_T=in_addr_t"
+fi
+
+dnl Check whether --with-version was given.
+
+AC_SUBST(LIBVERSION)
+AC_SUBST(VERSION)
+if test "${with_version}" = yes; then
+ VERSION=${ac_option}
+else
+ VERSION=`cat VERSION`
+fi
+echo -e "compiling version ${VERSION}"
+
+LIBVERSION=`echo ${VERSION} | cut -d '.' -f 1`
+
+CXXFLAGS="$CXXFLAGS -DVERSION=\\\"${VERSION}\\\""
+CFLAGS="$CFLAGS -DVERSION=\\\"${VERSION}\\\""
+
+dnl Check whether --with-static-png was given and
+dnl add -lpng or libpng.a to linking.
+
+if test "${with_static_png}" = yes; then
+ echo -e "enabling static linking of PNG library"
+ if test "$SUN" = yes && test -f "/usr/sfw/lib/libpng.a"; then
+ LIBSTATIC="$LIBSTATIC /usr/sfw/lib/libpng.a"
+ else
+ if test -f "/usr/lib/libpng.a" ; then
+ LIBSTATIC="$LIBSTATIC /usr/lib/libpng.a"
+ else
+ if test -f "/usr/local/lib/libpng.a" ; then
+ echo -e "assuming libpng.a in /usr/local/lib"
+ LIBSTATIC="$LIBSTATIC /usr/local/lib/libpng.a"
+ else
+ echo -e "Warning: assuming libpng.a in the local path"
+ LIBSTATIC="$LIBSTATIC libpng.a"
+ fi
+ fi
+ fi
+else
+ echo -e "enabling dynamic linking of PNG library"
+ LIBSHARED="$LIBSHARED -lpng"
+fi
+
+dnl Check whether --with-static-jpeg was given and
+dnl add -ljpeg or libjpeg.a to linking.
+
+if test "${with_static_jpeg}" = yes; then
+ echo -e "enabling static linking of JPEG library"
+ if test "$SUN" = yes && test -f "/usr/sfw/lib/libjpeg.a"; then
+ LIBSTATIC="$LIBSTATIC /usr/sfw/lib/libjpeg.a"
+ else
+ if test -f "/usr/lib/libjpeg.a" ; then
+ LIBSTATIC="$LIBSTATIC /usr/lib/libjpeg.a"
+ else
+ if test -f "/usr/local/lib/libjpeg.a" ; then
+ echo -e "assuming libjpeg.a in /usr/local/lib"
+ LIBSTATIC="$LIBSTATIC /usr/local/lib/libjpeg.a"
+ else
+ echo -e "Warning: assuming libjpeg.a in the local path"
+ LIBSTATIC="$LIBSTATIC libjpeg.a"
+ fi
+ fi
+ fi
+else
+ echo -e "enabling dynamic linking of JPEG library"
+ LIBSHARED="$LIBSHARED -ljpeg"
+fi
+
+dnl Check whether --with-static-z was given and
+dnl add -lz or libz.a to linking.
+
+if test "${with_static_z}" = yes; then
+ echo -e "enabling static linking of Z library"
+ if test "$SUN" = yes && test -f "/usr/sfw/lib/libz.a"; then
+ LIBSTATIC="$LIBSTATIC /usr/sfw/lib/libz.a"
+ else
+ if test -f "/usr/lib/libz.a" ; then
+ LIBSTATIC="$LIBSTATIC /usr/lib/libz.a"
+ else
+ if test -f "/usr/local/lib/libz.a" ; then
+ echo -e "assuming libz.a in /usr/local/lib"
+ LIBSTATIC="$LIBSTATIC /usr/local/lib/libz.a"
+ else
+ echo -e "Warning: assuming libz.a in the local path"
+ LIBSTATIC="$LIBSTATIC libz.a"
+ fi
+ fi
+ fi
+else
+ echo -e "enabling dynamic linking of Z library"
+ LIBSHARED="$LIBSHARED -lz"
+fi
+
+dnl Finally compose the LIB variable.
+
+if test "$DARWIN" = yes ; then
+ LIBS="$LIBS $LIBSTATIC $LIBSHARED"
+elif test "$SUN" = yes ; then
+ LIBS="$LIBS $LIBSTATIC $LIBSHARED"
+else
+ LIBS="$LIBS $LIBSTATIC -shared $LIBSHARED"
+fi
+
+dnl Check whether --with-symbols or --without-symbols was
+dnl given and set the required optimization level.
+
+if test "${with_symbols}" = yes; then
+ echo -e "enabling production of debug symbols"
+ CXXFLAGS="-g $CXXFLAGS"
+ CFLAGS="-g $CFLAGS"
+else
+ echo -e "disabling production of debug symbols"
+fi
+
+dnl Check whether --with-use-malloc or --without-use-malloc
+dnl was given.
+
+if test "${with_use_malloc}" = yes; then
+ echo -e "disabling use of the STL allocators"
+ CXXFLAGS="$CXXFLAGS -D__USE_MALLOC"
+else
+ echo -e "enabling use of the STL allocators"
+fi
+
+dnl Check whether --with-info or --without-info was given.
+
+if test "${with_info}" = yes; then
+ echo -e "enabling info output in the log file"
+ CXXFLAGS="$CXXFLAGS -DINFO"
+ CFLAGS="$CFLAGS -DINFO"
+else
+ echo -e "disabling info output in the log file"
+fi
+
+dnl Check whether --with-valgrind or --without-valgrind was given.
+
+if test "${with_valgrind}" = yes; then
+ echo -e "enabling valgrind memory checker workarounds"
+ CXXFLAGS="$CXXFLAGS -DVALGRIND"
+ CFLAGS="$CFLAGS -DVALGRIND"
+else
+ echo -e "disabling valgrind memory checker workarounds"
+fi
+
+dnl Find makedepend somewhere.
+
+AC_SUBST(MAKEDEPEND)
+
+if test -x "../nx-X11/config/makedepend/makedepend" ; then
+ MAKEDEPEND=../nx-X11/config/makedepend/makedepend
+else
+ if test -x "/usr/X11R6/bin/makedepend" ; then
+ MAKEDEPEND=/usr/X11R6/bin/makedepend
+ else
+ if test -x "/usr/openwin/bin/makedepend" ; then
+ MAKEDEPEND=/usr/openwin/bin/makedepend
+ else
+ MAKEDEPEND=/usr/bin/makedepend
+ fi
+ fi
+fi
+
+dnl Determine what to build based on the platform.
+dnl Override the LIBS settings on Cygwin32 so that
+dnl we always link with the exact set of libraries.
+
+AC_SUBST(ALL)
+
+if test "$CYGWIN32" = yes; then
+ ALL="\$(LIBCYGARCHIVE) \$(LIBCYGSHARED) \$(LIBARCHIVE)"
+ LIBS="-lstdc++ -lpng -ljpeg -lz"
+else
+ ALL="\$(LIBFULL) \$(LIBLOAD) \$(LIBSHARED) \$(LIBARCHIVE)"
+fi
+
+AC_OUTPUT(Makefile)
diff --git a/nxcomp/install-sh b/nxcomp/install-sh
new file mode 100755
index 000000000..58719246f
--- /dev/null
+++ b/nxcomp/install-sh
@@ -0,0 +1,238 @@
+#! /bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+#
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
diff --git a/nxcomp/mkinstalldirs b/nxcomp/mkinstalldirs
new file mode 100755
index 000000000..936cf3407
--- /dev/null
+++ b/nxcomp/mkinstalldirs
@@ -0,0 +1,34 @@
+#! /bin/sh
+# mkinstalldirs --- make directory hierarchy
+# Author: Noah Friedman <friedman@prep.ai.mit.edu>
+# Created: 1993-05-16
+# Last modified: 1995-03-05
+# Public domain
+
+errstatus=0
+
+for file in ${1+"$@"} ; do
+ set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
+ shift
+
+ pathcomp=
+ for d in ${1+"$@"} ; do
+ pathcomp="$pathcomp$d"
+ case "$pathcomp" in
+ -* ) pathcomp=./$pathcomp ;;
+ esac
+
+ if test ! -d "$pathcomp"; then
+ echo "mkdir $pathcomp" 1>&2
+ mkdir "$pathcomp" > /dev/null 2>&1 || lasterr=$?
+ fi
+
+ if test ! -d "$pathcomp"; then
+ errstatus=$lasterr
+ fi
+
+ pathcomp="$pathcomp/"
+ done
+done
+
+exit $errstatus