common/Util.cpp                  |   10 +
 common/Util.hpp                  |    3 
 loleaflet/html/loleaflet.html.m4 |    2 
 loleaflet/src/core/Socket.js     |    9 -
 net/WebSocketHandler.hpp         |  275 +++++++++++++++++++++++++--------------
 test/test.cpp                    |   39 ++---
 wsd/Admin.cpp                    |    7 
 wsd/ClientSession.cpp            |    8 -
 wsd/DocumentBroker.cpp           |    3 
 wsd/LOOLWSD.cpp                  |   15 +-
 wsd/LOOLWSD.hpp                  |    3 
 11 files changed, 248 insertions(+), 126 deletions(-)

New commits:
commit 994669aedbe131e3386110210b6863988c74fa5c
Author:     Gabriel Masei <[email protected]>
AuthorDate: Fri Mar 8 10:21:17 2019 +0200
Commit:     Michael Meeks <[email protected]>
CommitDate: Fri May 17 11:44:38 2019 +0100

    Added support for defragmentation of incoming websocket fragmented messages 
and handled some protocol error cases
    
    Change-Id: I4d11a6527b6b131c65101fd53b71015529645f74
    Reviewed-on: https://gerrit.libreoffice.org/68901
    Reviewed-by: Michael Meeks <[email protected]>
    Tested-by: Michael Meeks <[email protected]>

diff --git a/net/WebSocketHandler.hpp b/net/WebSocketHandler.hpp
index 3b65ee89a..b4c14ede6 100644
--- a/net/WebSocketHandler.hpp
+++ b/net/WebSocketHandler.hpp
@@ -37,6 +37,8 @@ private:
     std::atomic<bool> _shuttingDown;
     bool _isClient;
     bool _isMasking;
+    bool _inFragmentBlock;
+    bool _isManualDefrag;
 
 protected:
     struct WSFrameMask
@@ -50,16 +52,29 @@ protected:
 
 public:
     /// Perform upgrade ourselves, or select a client web socket.
-    WebSocketHandler(bool isClient = false, bool isMasking = true) :
+    /// Parameters:
+    /// isClient: the instance should behave like a client (true) or like a 
server (false)
+    ///           (from websocket perspective)
+    /// isMasking: a client should mask (true) or not (false) outgoing frames
+    /// isManualDefrag: the message handler should be called for every 
fragment of a message and
+    ///                 defragmentation should be handled inside message 
handler (true) or the message handler
+    ///                 should be called after all fragments of a message were 
received and the message
+    ///                 was defragmented (false).
+    WebSocketHandler(bool isClient = false, bool isMasking = true, bool 
isManualDefrag = false) :
         _lastPingSentTime(std::chrono::steady_clock::now()),
         _pingTimeUs(0),
         _shuttingDown(false),
         _isClient(isClient),
-        _isMasking(isClient && isMasking)
+        _isMasking(isClient && isMasking),
+        _inFragmentBlock(false),
+        _isManualDefrag(isManualDefrag)
     {
     }
 
     /// Upgrades itself to a websocket directly.
+    /// Parameters:
+    /// socket: the TCP socket which received the upgrade request
+    /// request: the HTTP upgrade request to WebSocket
     WebSocketHandler(const std::weak_ptr<StreamSocket>& socket,
                      const Poco::Net::HTTPRequest& request) :
         _socket(socket),
@@ -69,7 +84,9 @@ public:
         _pingTimeUs(0),
         _shuttingDown(false),
         _isClient(false),
-        _isMasking(false)
+        _isMasking(false),
+        _inFragmentBlock(false),
+        _isManualDefrag(false)
     {
         upgradeToWebSocket(request);
     }
@@ -99,8 +116,8 @@ public:
         RESERVED_TLS_FAILURE    = 1015
     };
 
-    /// Sends WS shutdown message to the peer.
-    void shutdown(const StatusCodes statusCode = StatusCodes::NORMAL_CLOSE, 
const std::string& statusMessage = "")
+    /// Sends WS Close frame to the peer.
+    void sendCloseFrame(const StatusCodes statusCode = 
StatusCodes::NORMAL_CLOSE, const std::string& statusMessage = "")
     {
         std::shared_ptr<StreamSocket> socket = _socket.lock();
         if (socket == nullptr)
@@ -126,7 +143,22 @@ public:
 #endif
     }
 
-    bool handleOneIncomingMessage(const std::shared_ptr<StreamSocket>& socket)
+    void shutdown(const StatusCodes statusCode = StatusCodes::NORMAL_CLOSE, 
const std::string& statusMessage = "")
+    {
+        if (!_shuttingDown)
+            sendCloseFrame(statusCode, statusMessage);
+        std::shared_ptr<StreamSocket> socket = _socket.lock();
+        if (socket)
+        {
+            socket->closeConnection();
+            socket->getInBuffer().clear();
+        }
+        _wsPayload.clear();
+        _inFragmentBlock = false;
+        _shuttingDown = false;
+    }
+
+    bool handleTCPStream(const std::shared_ptr<StreamSocket>& socket)
     {
         assert(socket && "Expected a valid socket instance.");
 
@@ -177,7 +209,7 @@ public:
             headerLen += 8;
         }
 
-        unsigned char *data, *mask;
+        unsigned char *data, *mask = nullptr;
 
         if (hasMask)
         {
@@ -187,121 +219,164 @@ public:
 
         if (payloadLen + headerLen > len)
         { // partial read wait for more data.
-            LOG_TRC("#" << socket->getFD() << ": Still incomplete WebSocket 
message, have " << len
-                        << " bytes, message is " << payloadLen + headerLen << 
" bytes");
+            LOG_TRC("#" << socket->getFD() << ": Still incomplete WebSocket 
frame, have " << len << " bytes, frame is " << payloadLen + headerLen << " 
bytes");
             return false;
         }
 
-        LOG_TRC("#" << socket->getFD() << ": Incoming WebSocket data of " << 
len << " bytes: "
-                    << Util::stringifyHexLine(socket->getInBuffer(), 0, 
std::min((size_t)32, len)));
-
-        data = p + headerLen;
-
-        if (hasMask)
+        if (hasMask && _isClient)
         {
-            const size_t end = _wsPayload.size();
-            _wsPayload.resize(end + payloadLen);
-            char* wsData = &_wsPayload[end];
-            for (size_t i = 0; i < payloadLen; ++i)
-                *wsData++ = data[i] ^ mask[i % 4];
-        } else
-            _wsPayload.insert(_wsPayload.end(), data, data + payloadLen);
-#else
-        unsigned char * const p = reinterpret_cast<unsigned 
char*>(&socket->getInBuffer()[0]);
-        _wsPayload.insert(_wsPayload.end(), p, p + len);
-        const size_t headerLen = 0;
-        const size_t payloadLen = len;
-#endif
-
-        assert(_wsPayload.size() >= payloadLen);
+            LOG_ERR("#" << socket->getFD() << ": Servers should not send 
masked frames. Only clients.");
+            shutdown(StatusCodes::PROTOCOL_ERROR);
+            return true;
+        }
 
-        socket->getInBuffer().erase(socket->getInBuffer().begin(),
-                                    socket->getInBuffer().begin() + headerLen 
+ payloadLen);
+        LOG_TRC("#" << socket->getFD() << ": Incoming WebSocket data of " << 
len << " bytes: " << Util::stringifyHexLine(socket->getInBuffer(), 0, 
std::min((size_t)32, len)));
 
-#if !MOBILEAPP
+        data = p + headerLen;
 
-        // FIXME: fin, aggregating payloads into _wsPayload etc.
-        LOG_TRC("#" << socket->getFD() << ": Incoming WebSocket message code "
-                    << static_cast<unsigned>(code) << ", fin? " << fin << ", 
mask? " << hasMask
-                    << ", payload length: " << _wsPayload.size()
-                    << ", residual socket data: " << 
socket->getInBuffer().size() << " bytes.");
+        if (isControlFrame(code))
+        {
+            //Process control frames
+            std::vector<char> ctrlPayload;
 
-        bool doClose = false;
+            readPayload(data, payloadLen, mask, ctrlPayload);
+            socket->getInBuffer().erase(socket->getInBuffer().begin(), 
socket->getInBuffer().begin() + headerLen + payloadLen);
+            LOG_TRC("#" << socket->getFD() << ": Incoming WebSocket frame code 
" << static_cast<unsigned>(code) <<
+                ", fin? " << fin << ", mask? " << hasMask << ", payload 
length: " << payloadLen <<
+                ", residual socket data: " << socket->getInBuffer().size() << 
" bytes.");
 
-        switch (code)
-        {
-        case WSOpCode::Pong:
-        {
-            if (_isClient)
+            // All control frames MUST NOT be fragmented and MUST have a 
payload length of 125 bytes or less
+            if (!fin)
             {
-                LOG_ERR("#" << socket->getFD() << ": Servers should not send 
pongs, only clients");
-                doClose = true;
-                break;
+                LOG_ERR("#" << socket->getFD() << ": A control frame cannot be 
fragmented.");
+                shutdown(StatusCodes::PROTOCOL_ERROR);
+                return true;
             }
-            else
+            if (payloadLen > 125)
             {
-                _pingTimeUs = 
std::chrono::duration_cast<std::chrono::microseconds>
-                    (std::chrono::steady_clock::now() - 
_lastPingSentTime).count();
-                LOG_TRC("#" << socket->getFD() << ": Pong received: " << 
_pingTimeUs << " microseconds");
-                break;
+                LOG_ERR("#" << socket->getFD() << ": The payload length of a 
control frame must not exceed 125 bytes.");
+                shutdown(StatusCodes::PROTOCOL_ERROR);
+                return true;
             }
-        }
-        case WSOpCode::Ping:
-            if (_isClient)
+
+            switch (code)
             {
-                auto now = std::chrono::steady_clock::now();
-                _pingTimeUs = 
std::chrono::duration_cast<std::chrono::microseconds>
-                                        (now - _lastPingSentTime).count();
-                sendPong(now, &_wsPayload[0], payloadLen, socket);
+            case WSOpCode::Pong:
+                if (_isClient)
+                {
+                    LOG_ERR("#" << socket->getFD() << ": Servers should not 
send pongs, only clients");
+                    shutdown(StatusCodes::POLICY_VIOLATION);
+                    return true;
+                }
+                else
+                {
+                    _pingTimeUs = 
std::chrono::duration_cast<std::chrono::microseconds>
+                        (std::chrono::steady_clock::now() - 
_lastPingSentTime).count();
+                    LOG_TRC("#" << socket->getFD() << ": Pong received: " << 
_pingTimeUs << " microseconds");
+                }
+                break;
+            case WSOpCode::Ping:
+                if (_isClient)
+                {
+                    auto now = std::chrono::steady_clock::now();
+                    _pingTimeUs = 
std::chrono::duration_cast<std::chrono::microseconds>
+                                            (now - _lastPingSentTime).count();
+                    sendPong(now, &ctrlPayload[0], payloadLen, socket);
+                }
+                else
+                {
+                    LOG_ERR("#" << socket->getFD() << ": Clients should not 
send pings, only servers");
+                    shutdown(StatusCodes::POLICY_VIOLATION);
+                    return true;
+                }
+                break;
+            case WSOpCode::Close:
+                {
+                    std::string message;
+                    StatusCodes statusCode = StatusCodes::NORMAL_CLOSE;
+                    if (!_shuttingDown)
+                    {
+                        // Peer-initiated shutdown must be echoed.
+                        // Otherwise, this is the echo to _our_ shutdown 
message, which we should ignore.
+                        LOG_TRC("#" << socket->getFD() << ": Peer initiated 
socket shutdown. Code: " << static_cast<int>(statusCode));
+                        if (ctrlPayload.size())
+                        {
+                            statusCode = 
static_cast<StatusCodes>((((uint64_t)(unsigned char)ctrlPayload[0]) << 8) +
+                                                                
(((uint64_t)(unsigned char)ctrlPayload[1]) << 0));
+                            if (ctrlPayload.size() > 2)
+                                message.assign(&ctrlPayload[2], 
&ctrlPayload[2] + ctrlPayload.size() - 2);
+                        }
+                    }
+                    shutdown(statusCode, message);
+                    return true;
+                }
+            default:
+                LOG_ERR("#" << socket->getFD() << ": Received unknown control 
code");
+                shutdown(StatusCodes::PROTOCOL_ERROR);
                 break;
             }
-            else
+
+            return true;
+        }
+
+        // Check data frames for errors
+        if (_inFragmentBlock)
+        {
+            if (code != WSOpCode::Continuation)
             {
-                LOG_ERR("#" << socket->getFD() << ": Clients should not send 
pings, only servers");
-                doClose = true;
+                LOG_ERR("#" << socket->getFD() << ": A fragment that is not 
the first fragment of a message must have the opcode equal to 0.");
+                shutdown(StatusCodes::PROTOCOL_ERROR);
+                return true;
             }
-            break;
-        case WSOpCode::Close:
-            doClose = true;
-            break;
-        default:
-            handleMessage(fin, code, _wsPayload);
-            break;
+        }
+        else if (code == WSOpCode::Continuation)
+        {
+            LOG_ERR("#" << socket->getFD() << ": An unfragmented message or 
the first fragment of a fragmented message must have the opcode different than 
0.");
+            shutdown(StatusCodes::PROTOCOL_ERROR);
+            return true;
         }
 
+        //Process data frame
+        readPayload(data, payloadLen, mask, _wsPayload);
 #else
-        handleMessage(true, WSOpCode::Binary, _wsPayload);
-
+        unsigned char * const p = reinterpret_cast<unsigned 
char*>(&socket->getInBuffer()[0]);
+        _wsPayload.insert(_wsPayload.end(), p, p + len);
+        const size_t headerLen = 0;
+        const size_t payloadLen = len;
 #endif
 
+        socket->getInBuffer().erase(socket->getInBuffer().begin(), 
socket->getInBuffer().begin() + headerLen + payloadLen);
+
 #if !MOBILEAPP
-        if (doClose)
+
+        LOG_TRC("#" << socket->getFD() << ": Incoming WebSocket frame code " 
<< static_cast<unsigned>(code) <<
+                ", fin? " << fin << ", mask? " << hasMask << ", payload 
length: " << payloadLen <<
+                ", residual socket data: " << socket->getInBuffer().size() << 
" bytes.");
+
+        if (fin)
+        {
+            //If is final fragment then process the accumulated message.
+            handleMessage(fin, code, _wsPayload);
+            _inFragmentBlock = false;
+        }
+        else
         {
-            if (!_shuttingDown)
+            if (_isManualDefrag)
             {
-                // Peer-initiated shutdown must be echoed.
-                // Otherwise, this is the echo to _our_ shutdown message, 
which we should ignore.
-                const StatusCodes statusCode = 
static_cast<StatusCodes>((((uint64_t)(unsigned char)_wsPayload[0]) << 8) +
-                                                                        
(((uint64_t)(unsigned char)_wsPayload[1]) << 0));
-                LOG_TRC("#" << socket->getFD() << ": Client initiated socket 
shutdown. Code: " << static_cast<int>(statusCode));
-                if (_wsPayload.size() > 2)
-                {
-                    const std::string message(&_wsPayload[2], &_wsPayload[2] + 
_wsPayload.size() - 2);
-                    shutdown(statusCode, message);
-                }
-                else
-                {
-                    shutdown(statusCode);
-                }
+                //If the user wants to process defragmentation on its own then 
let him process it.
+                handleMessage(fin, code, _wsPayload);
+                _inFragmentBlock = true;
             }
             else
             {
-                LOG_TRC("#" << socket->getFD() << ": Client responded to our 
shutdown.");
+                _inFragmentBlock = true;
+                //If is not final fragment then wait for next fragment.
+                return false;
             }
-
-            // TCP Close.
-            socket->closeConnection();
         }
+#else
+        handleMessage(true, WSOpCode::Binary, _wsPayload);
+
 #endif
 
         _wsPayload.clear();
@@ -332,7 +407,7 @@ public:
 #endif
         else
         {
-            while (handleOneIncomingMessage(socket))
+            while (handleTCPStream(socket))
                 ; // might have multiple messages in the accumulated buffer.
         }
     }
@@ -516,6 +591,22 @@ private:
 
 protected:
 
+    bool isControlFrame(WSOpCode code){ return code >= WSOpCode::Close; }
+
+    void readPayload(unsigned char *data, size_t dataLen, unsigned char* mask, 
std::vector<char>& payload)
+    {
+        if (mask)
+        {
+            size_t end = payload.size();
+            payload.resize(end + dataLen);
+            char* wsData = &payload[end];
+            for (size_t i = 0; i < dataLen; ++i)
+                *wsData++ = data[i] ^ mask[i % 4];
+        }
+        else
+            payload.insert(payload.end(), data, data + dataLen);
+    }
+
     /// To be overriden to handle the websocket messages the way you need.
     virtual void handleMessage(bool /*fin*/, WSOpCode /*code*/, 
std::vector<char> &/*data*/)
     {
commit 204de0e1120360af0451dcac83159cc8490073e2
Author:     Michael Meeks <[email protected]>
AuthorDate: Thu May 16 21:12:20 2019 +0100
Commit:     Michael Meeks <[email protected]>
CommitDate: Thu May 16 21:12:20 2019 +0100

    debug: show WebSocketURI & a unique host id in help -> about.
    
    Rather useful for debugging clustering issues.
    
    Change-Id: I6d5f224bf8a3e4034c419137c8ad2b17fdf265ed

diff --git a/common/Util.cpp b/common/Util.cpp
index b0af5c6c6..6f2322c8f 100644
--- a/common/Util.cpp
+++ b/common/Util.cpp
@@ -44,6 +44,7 @@
 #include <thread>
 
 #include <Poco/Base64Encoder.h>
+#include <Poco/HexBinaryEncoder.h>
 #include <Poco/ConsoleChannel.h>
 #include <Poco/Exception.h>
 #include <Poco/Format.h>
@@ -100,6 +101,15 @@ namespace Util
             return v;
         }
 
+        /// Generate a string of random characters.
+        std::string getHexString(const size_t length)
+        {
+            std::stringstream ss;
+            Poco::HexBinaryEncoder hex(ss);
+            hex.write(getBytes(length).data(), length);
+            return ss.str().substr(0, length);
+        }
+
         /// Generates a random string in Base64.
         /// Note: May contain '/' characters.
         std::string getB64String(const size_t length)
diff --git a/common/Util.hpp b/common/Util.hpp
index a92f0c0cc..96e61d4e9 100644
--- a/common/Util.hpp
+++ b/common/Util.hpp
@@ -46,6 +46,9 @@ namespace Util
         /// Generate an array of random characters.
         std::vector<char> getBytes(const size_t length);
 
+        /// Generate a string of random characters.
+        std::string getHexString(const size_t length);
+
         /// Generates a random string suitable for
         /// file/directory names.
         std::string getFilename(const size_t length);
diff --git a/loleaflet/html/loleaflet.html.m4 b/loleaflet/html/loleaflet.html.m4
index 367ed3fe6..1a44c00b6 100644
--- a/loleaflet/html/loleaflet.html.m4
+++ b/loleaflet/html/loleaflet.html.m4
@@ -156,6 +156,8 @@ ifelse(MOBILEAPP,[true],
       <div id="loolwsd-version"></div>
       <h3>LOKit</h3>
       <div id="lokit-version"></div>
+      <h3>Id</h3>
+      <div id="loolwsd-id"></div>
       <p>Copyright _YEAR_, Collabora Productivity Limited.</p>
     </div>
 
diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index 275ea8720..c57d0b691 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -50,6 +50,10 @@ L.Socket = L.Class.extend({
                this._msgQueue = [];
        },
 
+       getWebSocketBaseURI: function(map) {
+               return map.options.server + map.options.serviceRoot + '/lool/' 
+ encodeURIComponent(map.options.doc + '?' + $.param(map.options.docParams)) + 
'/ws';
+       },
+
        connect: function(socket) {
                var map = this._map;
                if (map.options.permission) {
@@ -70,8 +74,7 @@ L.Socket = L.Class.extend({
                        }
 
                        try {
-                               var websocketURI = map.options.server + 
map.options.serviceRoot + '/lool/' + encodeURIComponent(map.options.doc + '?' + 
$.param(map.options.docParams)) + '/ws' + wopiSrc;
-                               this.socket = new WebSocket(websocketURI);
+                               this.socket = new 
WebSocket(this.getWebSocketBaseURI(map) + wopiSrc);
                        } catch (e) {
                                // On IE 11 there is a limitation on the number 
of WebSockets open to a single domain (6 by default and can go to 128).
                                // Detect this and hint the user.
@@ -284,6 +287,8 @@ L.Socket = L.Class.extend({
                                
$('#loolwsd-version').text(loolwsdVersionObj.Version);
                        }
 
+                       $('#loolwsd-id').html('<p>' + 
this.getWebSocketBaseURI(this._map) + '</p><p>' + loolwsdVersionObj.Id + 
'</p>');
+
                        // TODO: For now we expect perfect match in protocol 
versions
                        if (loolwsdVersionObj.Protocol !== 
this.ProtocolVersionNumber) {
                                this._map.fire('error', {msg: _('Unsupported 
server version.')});
diff --git a/wsd/Admin.cpp b/wsd/Admin.cpp
index 414427027..6dd699b09 100644
--- a/wsd/Admin.cpp
+++ b/wsd/Admin.cpp
@@ -122,12 +122,7 @@ void AdminSocketHandler::handleMessage(bool /* fin */, 
WSOpCode /* code */,
     else if (tokens[0] == "version")
     {
         // Send LOOL version information
-        std::string version, hash;
-        Util::getVersionInfo(version, hash);
-        std::string versionStr =
-            "{ \"Version\":  \"" + version + "\", " +
-            "\"Hash\":  \"" + hash  + "\" }";
-        sendTextFrame("loolserver " + versionStr);
+        sendTextFrame("loolserver " + LOOLWSD::getVersionJSON());
         // Send LOKit version information
         sendTextFrame("lokitversion " + LOOLWSD::LOKitVersion);
     }
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 3ba3580e7..f1ae9e897 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -112,13 +112,7 @@ bool ClientSession::_handleInput(const char *buffer, int 
length)
         }
 
         // Send LOOL version information
-        std::string version, hash;
-        Util::getVersionInfo(version, hash);
-        std::string versionStr =
-            "{ \"Version\":  \"" + version + "\", " +
-            "\"Hash\":     \"" + hash + "\", " +
-            "\"Protocol\": \"" + GetProtocolVersion() + "\" }";
-        sendTextFrame("loolserver " + versionStr);
+        sendTextFrame("loolserver " + LOOLWSD::getVersionJSON());
         // Send LOKit version information
         sendTextFrame("lokitversion " + LOOLWSD::LOKitVersion);
 
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index f355e6b7b..9bdd7d434 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -686,6 +686,7 @@ std::string LOOLWSD::ServerName;
 std::string LOOLWSD::FileServerRoot;
 std::string LOOLWSD::ServiceRoot;
 std::string LOOLWSD::LOKitVersion;
+std::string LOOLWSD::HostIdentifier;
 std::string LOOLWSD::ConfigFile = LOOLWSD_CONFIGDIR "/loolwsd.xml";
 std::string LOOLWSD::ConfigDir = LOOLWSD_CONFIGDIR "/conf.d";
 std::string LOOLWSD::LogLevel = "trace";
@@ -3085,6 +3086,17 @@ private:
     }
 };
 
+std::string LOOLWSD::getVersionJSON()
+{
+    std::string version, hash;
+    Util::getVersionInfo(version, hash);
+    return
+        "{ \"Version\":  \"" + version + "\", "
+        "\"Hash\":     \"" + hash + "\", "
+        "\"Protocol\": \"" + GetProtocolVersion() + "\", "
+        "\"Id\":  \"" + HostIdentifier + "\" }";
+}
+
 static LOOLWSDServer srv;
 
 int LOOLWSD::innerMain()
@@ -3100,11 +3112,12 @@ int LOOLWSD::innerMain()
     Environment::set("LD_BIND_NOW", "1");
 
 #if !MOBILEAPP
+    HostIdentifier = Util::rng::getHexString(8);
     if (DisplayVersion)
     {
         std::string version, hash;
         Util::getVersionInfo(version, hash);
-        LOG_INF("Loolwsd version details: " << version << " - " << hash);
+        LOG_INF("Loolwsd version details: " << version << " - " << hash << " - 
id " << HostIdentifier);
     }
 #endif
 #endif
diff --git a/wsd/LOOLWSD.hpp b/wsd/LOOLWSD.hpp
index ce5f8d172..7cb03920f 100644
--- a/wsd/LOOLWSD.hpp
+++ b/wsd/LOOLWSD.hpp
@@ -63,6 +63,7 @@ public:
     static std::string FileServerRoot;
     static std::string ServiceRoot; ///< There are installations that need 
prefixing every page with some path.
     static std::string LOKitVersion;
+    static std::string HostIdentifier; ///< A unique random hash that 
identifies this server
     static std::string LogLevel;
     static bool AnonymizeUserData;
     static std::uint64_t AnonymizationSalt;
@@ -152,6 +153,8 @@ public:
         return AnonymizeUserData ? Util::anonymize(username, 
AnonymizationSalt) : username;
     }
 
+    static std::string getVersionJSON();
+
     int innerMain();
 
 protected:
commit b108a8997a01099a4120c6c5c3f01c61b086cebf
Author:     Michael Meeks <[email protected]>
AuthorDate: Thu May 16 20:38:36 2019 +0100
Commit:     Michael Meeks <[email protected]>
CommitDate: Thu May 16 20:38:36 2019 +0100

    debug: dump content of paste messages.
    
    Problematic not to be able to see the content, helps hunt weirdness
    in eg. pasted RTF.
    
    Change-Id: I301bfe040a2424b6ca84ab94b8eee865439fb680

diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 744d4ec87..d33337cc1 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -1738,6 +1738,9 @@ bool DocumentBroker::forwardToChild(const std::string& 
viewId, const std::string
 
     LOG_TRC("Forwarding payload to child [" << viewId << "]: " << 
getAbbreviatedMessage(message));
 
+    if (Log::traceEnabled() && Util::startsWith(message, "paste "))
+        LOG_TRC("Logging paste payload (" << message.size() << " bytes) '" << 
message << "' end paste");
+
     std::string msg = "child-" + viewId + ' ' + message;
 
     const auto it = _sessions.find(viewId);
commit 1fb87c20a3ddb672c113524fb8e15dfdf47d344c
Author:     Michael Meeks <[email protected]>
AuthorDate: Mon May 13 15:39:14 2019 +0100
Commit:     Michael Meeks <[email protected]>
CommitDate: Thu May 16 14:49:41 2019 +0100

    test: use process groups as well to allow concurrent make checks.
    
    Change-Id: Ib1a55f53c51835a8f9fb1c17146f30e887103906

diff --git a/test/test.cpp b/test/test.cpp
index 9745dda00..4f90ee1b5 100644
--- a/test/test.cpp
+++ b/test/test.cpp
@@ -164,11 +164,14 @@ bool runClientTests(bool standalone, bool verbose)
 // Versions assuming a single user, on a single machine
 #ifndef UNIT_CLIENT_TESTS
 
-std::vector<int> getProcPids(const char* exec_filename, bool ignoreZombies = 
true)
+std::vector<int> getProcPids(const char* exec_filename)
 {
     std::vector<int> pids;
 
-    // Crash all lokit processes.
+    // Ensure we're in the same group.
+    int grp = getpgrp();
+
+    // Get all lokit processes.
     for (auto it = Poco::DirectoryIterator(std::string("/proc")); it != 
Poco::DirectoryIterator(); ++it)
     {
         try
@@ -192,24 +195,24 @@ std::vector<int> getProcPids(const char* exec_filename, 
bool ignoreZombies = tru
                 std::string statString;
                 Poco::StreamCopier::copyToString(stat, statString);
                 Poco::StringTokenizer tokens(statString, " ");
-                if (tokens.count() > 3 && tokens[1] == exec_filename)
+                if (tokens.count() > 6 && tokens[1] == exec_filename)
                 {
-                    if (ignoreZombies)
+                    // We could have several make checks running at once.
+                    int kidGrp = std::atoi(tokens[4].c_str());
+                    if (kidGrp != grp)
+                        continue;
+
+                    switch (tokens[2].c_str()[0])
                     {
-                        switch (tokens[2].c_str()[0])
-                        {
-                        // Dead & zombie markers for old and new kernels.
-                        case 'x':
-                        case 'X':
-                        case 'Z':
-                            break;
-                        default:
-                            pids.push_back(pid);
-                            break;
-                        }
-                    }
-                    else
+                    // Dead & zombie markers for old and new kernels.
+                    case 'x':
+                    case 'X':
+                    case 'Z':
+                        break;
+                    default:
                         pids.push_back(pid);
+                        break;
+                    }
                 }
             }
         }
@@ -231,7 +234,7 @@ std::vector<int> getKitPids()
 
 int getLoolKitProcessCount()
 {
-    return getProcPids("(loolkit)", true).size();
+    return getProcPids("(loolkit)").size();
 }
 
 std::vector<int> getForKitPids()
_______________________________________________
Libreoffice-commits mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to