2015-04-13 04:09:02 -05:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
|
2015-03-26 09:49:07 -05:00
|
|
|
/*
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
*/
|
|
|
|
|
2020-04-18 03:39:50 -05:00
|
|
|
#pragma once
|
2015-03-26 09:49:07 -05:00
|
|
|
|
2017-01-04 06:36:13 -06:00
|
|
|
#include <cstdint>
|
2018-10-21 15:57:23 -05:00
|
|
|
#include <cstdlib>
|
loolwsd: include cleanup and organization
A source file (.cpp) must include its own header first.
This insures that the header is self-contained and
doesn't depend on arbitrary (and accidental) includes
before it to compile.
Furthermore, system headers should go next, followed by
C then C++ headers, then libraries (Poco, etc) and, finally,
project headers come last.
This makes sure that headers and included in the same dependency
order to avoid side-effects. For example, Poco should never rely on
anything from our project in the same way that a C header should
never rely on anything in C++, Poco, or project headers.
Also, includes ought to be sorted where possible, to improve
readability and avoid accidental duplicates (of which there
were a few).
Change-Id: I62cc1343e4a091d69195e37ed659dba20cfcb1ef
Reviewed-on: https://gerrit.libreoffice.org/25262
Reviewed-by: Ashod Nakashian <ashnakash@gmail.com>
Tested-by: Ashod Nakashian <ashnakash@gmail.com>
2016-05-21 09:23:07 -05:00
|
|
|
#include <cstring>
|
2018-01-22 08:11:43 -06:00
|
|
|
#include <iomanip>
|
2015-04-27 13:09:27 -05:00
|
|
|
#include <map>
|
2019-11-08 14:37:21 -06:00
|
|
|
#include <regex>
|
2016-10-18 08:24:41 -05:00
|
|
|
#include <sstream>
|
2015-04-27 13:09:27 -05:00
|
|
|
#include <string>
|
2019-11-08 07:50:33 -06:00
|
|
|
#include <vector>
|
2016-04-20 08:44:12 -05:00
|
|
|
|
2022-03-29 20:37:57 -05:00
|
|
|
#include <StringVector.hpp>
|
2018-07-12 23:18:30 -05:00
|
|
|
#include <Util.hpp>
|
|
|
|
|
2015-03-26 09:49:07 -05:00
|
|
|
#define LOK_USE_UNSTABLE_API
|
|
|
|
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
|
|
|
|
|
2021-11-18 06:08:14 -06:00
|
|
|
namespace COOLProtocol
|
2015-03-26 09:49:07 -05:00
|
|
|
{
|
2016-01-06 11:00:44 -06:00
|
|
|
// Protocol Version Number.
|
|
|
|
// See protocol.txt.
|
|
|
|
constexpr unsigned ProtocolMajorVersionNumber = 0;
|
|
|
|
constexpr unsigned ProtocolMinorVersionNumber = 1;
|
|
|
|
|
2016-10-29 20:15:00 -05:00
|
|
|
inline std::string GetProtocolVersion()
|
2016-01-06 11:00:44 -06:00
|
|
|
{
|
|
|
|
return std::to_string(ProtocolMajorVersionNumber) + '.'
|
2016-10-29 20:15:00 -05:00
|
|
|
+ std::to_string(ProtocolMinorVersionNumber);
|
2016-01-06 11:00:44 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Parse a string into a version tuple.
|
|
|
|
// Negative numbers for error.
|
2016-02-23 02:22:18 -06:00
|
|
|
std::tuple<int, int, std::string> ParseVersion(const std::string& version);
|
2016-01-06 11:00:44 -06:00
|
|
|
|
2020-06-02 17:19:23 -05:00
|
|
|
inline bool stringToInteger(const std::string& input, int& value)
|
|
|
|
{
|
|
|
|
bool res;
|
|
|
|
std::tie(value, res) = Util::i32FromString(input);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool stringToUInt32(const std::string& input, uint32_t& value)
|
|
|
|
{
|
|
|
|
bool res;
|
|
|
|
std::tie(value, res) = Util::i32FromString(input);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool stringToUInt64(const std::string& input, uint64_t& value)
|
|
|
|
{
|
|
|
|
bool res;
|
|
|
|
std::tie(value, res) = Util::u64FromString(input);
|
|
|
|
return res;
|
|
|
|
}
|
2017-01-04 06:36:13 -06:00
|
|
|
|
2016-10-08 11:40:21 -05:00
|
|
|
inline
|
|
|
|
bool parseNameValuePair(const std::string& token, std::string& name, std::string& value, const char delim = '=')
|
|
|
|
{
|
2018-02-07 03:17:19 -06:00
|
|
|
const size_t mid = token.find_first_of(delim);
|
2016-10-08 11:40:21 -05:00
|
|
|
if (mid != std::string::npos)
|
|
|
|
{
|
|
|
|
name = token.substr(0, mid);
|
|
|
|
value = token.substr(mid + 1);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-06-09 14:26:05 -05:00
|
|
|
bool getTokenInteger(const std::string& token, const std::string_view name, int& value);
|
|
|
|
bool getTokenUInt32(const std::string& token, const std::string_view name, uint32_t& value);
|
|
|
|
bool getTokenUInt64(const std::string& token, const std::string_view name, uint64_t& value);
|
|
|
|
bool getTokenString(const std::string& token, const std::string_view name, std::string& value);
|
|
|
|
bool getTokenKeyword(const std::string& token, const std::string_view name, const std::map<std::string, int>& map, int& value);
|
2015-03-26 09:49:07 -05:00
|
|
|
|
2023-06-09 14:26:05 -05:00
|
|
|
bool getTokenKeyword(const StringVector& tokens, const std::string_view name, const std::map<std::string, int>& map, int& value);
|
2017-01-30 19:43:27 -06:00
|
|
|
|
2023-06-09 14:26:05 -05:00
|
|
|
bool getTokenInteger(const StringVector& tokens, const std::string_view name, int& value);
|
2017-01-30 19:43:27 -06:00
|
|
|
|
2020-05-31 12:44:22 -05:00
|
|
|
/// Literal-string token names.
|
|
|
|
template <std::size_t N>
|
|
|
|
inline bool getTokenInteger(const std::string& token, const char (&name)[N], int& value)
|
|
|
|
{
|
|
|
|
// N includes null termination.
|
|
|
|
static_assert(N > 1, "Token name must be at least one character long.");
|
|
|
|
if (token.size() > N && token[N - 1] == '=' && token.compare(0, N - 1, name) == 0)
|
|
|
|
{
|
|
|
|
const char* str = token.data() + N;
|
|
|
|
char* endptr = nullptr;
|
|
|
|
value = std::strtol(str, &endptr, 10);
|
|
|
|
return (endptr > str);
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-12-08 01:52:16 -06:00
|
|
|
/// Extracts a name and value from token. Returns true if value is a non-negative integer.
|
|
|
|
template <std::size_t N>
|
|
|
|
inline bool getNonNegTokenInteger(const std::string& token, const char (&name)[N], int& value)
|
|
|
|
{
|
|
|
|
return getTokenInteger(token, name, value) && value >= 0;
|
|
|
|
}
|
|
|
|
|
2020-02-28 03:50:58 -06:00
|
|
|
inline bool getTokenString(const StringVector& tokens,
|
2023-06-09 14:26:05 -05:00
|
|
|
const std::string_view name,
|
2017-01-19 19:44:39 -06:00
|
|
|
std::string& value)
|
|
|
|
{
|
|
|
|
for (const auto& token : tokens)
|
|
|
|
{
|
2020-02-28 07:51:22 -06:00
|
|
|
if (getTokenString(tokens.getParam(token), name, value))
|
2017-01-19 19:44:39 -06:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2016-04-20 08:44:12 -05:00
|
|
|
|
2023-06-09 14:26:05 -05:00
|
|
|
bool getTokenStringFromMessage(const std::string& message, const std::string_view name, std::string& value);
|
|
|
|
bool getTokenKeywordFromMessage(const std::string& message, const std::string_view name, const std::map<std::string, int>& map, int& value);
|
2016-09-26 11:31:19 -05:00
|
|
|
|
2018-10-21 15:57:23 -05:00
|
|
|
inline
|
2019-10-08 04:23:29 -05:00
|
|
|
std::vector<int> tokenizeInts(const char* data, const size_t size, const char delimiter = ',')
|
2018-10-21 15:57:23 -05:00
|
|
|
{
|
|
|
|
std::vector<int> tokens;
|
|
|
|
if (size == 0 || data == nullptr)
|
|
|
|
return tokens;
|
|
|
|
|
|
|
|
const char* start = data;
|
|
|
|
const char* end = data;
|
|
|
|
for (size_t i = 0; i < size && data[i] != '\n'; ++i, ++end)
|
|
|
|
{
|
2019-10-08 04:23:29 -05:00
|
|
|
if (data[i] == delimiter)
|
2018-10-21 15:57:23 -05:00
|
|
|
{
|
2019-10-08 04:23:29 -05:00
|
|
|
if (start != end && *start != delimiter)
|
2018-10-21 15:57:23 -05:00
|
|
|
tokens.emplace_back(std::atoi(start));
|
|
|
|
|
|
|
|
start = end;
|
|
|
|
}
|
2019-10-08 04:23:29 -05:00
|
|
|
else if (*start == delimiter)
|
2018-10-21 15:57:23 -05:00
|
|
|
++start;
|
|
|
|
}
|
|
|
|
|
2019-10-08 04:23:29 -05:00
|
|
|
if (start != end && *start != delimiter && *start != '\n')
|
2018-10-21 15:57:23 -05:00
|
|
|
tokens.emplace_back(std::atoi(start));
|
|
|
|
|
|
|
|
return tokens;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline
|
2019-10-08 04:23:29 -05:00
|
|
|
std::vector<int> tokenizeInts(const std::string& s, const char delimiter = ',')
|
2018-10-21 15:57:23 -05:00
|
|
|
{
|
2019-10-08 04:23:29 -05:00
|
|
|
return tokenizeInts(s.data(), s.size(), delimiter);
|
2018-10-21 15:57:23 -05:00
|
|
|
}
|
|
|
|
|
2023-06-09 14:26:05 -05:00
|
|
|
inline bool getTokenIntegerFromMessage(const std::string& message, const std::string_view name, int& value)
|
2017-01-30 19:43:27 -06:00
|
|
|
{
|
2022-03-29 20:37:57 -05:00
|
|
|
return getTokenInteger(StringVector::tokenize(message), name, value);
|
2017-01-30 19:43:27 -06:00
|
|
|
}
|
|
|
|
|
2016-04-11 02:11:49 -05:00
|
|
|
/// Returns the first token of a message.
|
|
|
|
inline
|
2017-01-19 22:25:51 -06:00
|
|
|
std::string getFirstToken(const char *message, const int length, const char delim = ' ')
|
2016-04-11 02:11:49 -05:00
|
|
|
{
|
2018-07-12 23:18:30 -05:00
|
|
|
return Util::getDelimitedInitialSubstring(message, length, delim);
|
2016-04-11 02:11:49 -05:00
|
|
|
}
|
|
|
|
|
2016-05-08 23:35:45 -05:00
|
|
|
template <typename T>
|
2016-10-08 12:12:16 -05:00
|
|
|
std::string getFirstToken(const T& message, const char delim = ' ')
|
2016-03-27 13:30:03 -05:00
|
|
|
{
|
2016-10-08 12:12:16 -05:00
|
|
|
return getFirstToken(message.data(), message.size(), delim);
|
2016-03-27 13:30:03 -05:00
|
|
|
}
|
|
|
|
|
2016-10-21 11:59:03 -05:00
|
|
|
inline
|
2023-06-06 08:14:10 -05:00
|
|
|
bool matchPrefix(const std::string_view prefix, const std::string_view message)
|
2016-10-21 11:59:03 -05:00
|
|
|
{
|
|
|
|
return (message.size() >= prefix.size() &&
|
|
|
|
message.compare(0, prefix.size(), prefix) == 0);
|
|
|
|
}
|
|
|
|
|
2016-11-29 20:55:44 -06:00
|
|
|
inline
|
2023-06-06 08:14:10 -05:00
|
|
|
bool matchPrefix(const std::string_view prefix, const std::vector<char>& message)
|
2016-11-29 20:55:44 -06:00
|
|
|
{
|
|
|
|
return (message.size() >= prefix.size() &&
|
|
|
|
prefix.compare(0, prefix.size(), message.data(), prefix.size()) == 0);
|
|
|
|
}
|
|
|
|
|
2016-10-21 11:59:03 -05:00
|
|
|
inline
|
2023-06-06 08:14:10 -05:00
|
|
|
bool matchPrefix(const std::string_view prefix, const std::string_view message, const bool ignoreWhitespace)
|
2016-10-21 11:59:03 -05:00
|
|
|
{
|
|
|
|
if (ignoreWhitespace)
|
|
|
|
{
|
2018-02-07 03:17:19 -06:00
|
|
|
const size_t posPre = prefix.find_first_not_of(' ');
|
|
|
|
const size_t posMsg = message.find_first_not_of(' ');
|
2016-10-21 11:59:03 -05:00
|
|
|
|
|
|
|
return matchPrefix(posPre == std::string::npos ? prefix : prefix.substr(posPre),
|
|
|
|
posMsg == std::string::npos ? message : message.substr(posMsg));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return matchPrefix(prefix, message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-09 22:16:09 -05:00
|
|
|
/// Returns true if the token is a user-interaction token.
|
|
|
|
/// Currently this excludes commands sent automatically.
|
|
|
|
/// Notice that this doesn't guarantee editing activity,
|
|
|
|
/// rather just user interaction with the UI.
|
2016-04-06 03:36:33 -05:00
|
|
|
inline
|
2023-06-09 14:26:05 -05:00
|
|
|
bool tokenIndicatesUserInteraction(const std::string_view token)
|
2016-04-06 03:36:33 -05:00
|
|
|
{
|
2023-06-10 11:54:23 -05:00
|
|
|
// Exclude tokens that include these keywords, such as statusindicator.
|
2016-09-26 02:12:48 -05:00
|
|
|
|
|
|
|
// FIXME: This is wrong. That the token happens to contain (or not) a certain substring is
|
|
|
|
// no guarantee that it "indicates user interaction". It might be like that at the moment,
|
|
|
|
// but that is coincidental. We should check what the actual whole token is, at least, not
|
|
|
|
// look for a substring.
|
|
|
|
|
2016-04-09 22:16:09 -05:00
|
|
|
return (token.find("tile") == std::string::npos &&
|
|
|
|
token.find("status") == std::string::npos &&
|
2016-12-14 09:23:42 -06:00
|
|
|
token.find("state") == std::string::npos &&
|
|
|
|
token != "userinactive");
|
2016-04-06 03:36:33 -05:00
|
|
|
}
|
|
|
|
|
2022-08-04 19:02:06 -05:00
|
|
|
/// Returns true if the token is a likely document modifying command.
|
|
|
|
/// This is never 100% accurate, but it is needed to filter out tokens
|
|
|
|
/// that certainly do not modify the document, such as 'load' and 'save'
|
|
|
|
/// commands. Some commands are certainly modifying, e.g. 'key', others
|
|
|
|
/// can only potentially be modifying, e.g. 'mouse' while dragging.
|
|
|
|
/// Note: this is only used when we don't have the modified flag from
|
|
|
|
/// Core so we flag the document as user-modified more accurately.
|
2023-02-04 15:59:08 -06:00
|
|
|
inline bool tokenIndicatesDocumentModification(const StringVector& tokens)
|
2022-08-04 19:02:06 -05:00
|
|
|
{
|
|
|
|
// These keywords are chosen to cover the largest set of
|
|
|
|
// commands that may potentially modify the document.
|
|
|
|
// We need to assume modification rather than not.
|
2023-02-04 15:59:08 -06:00
|
|
|
if (tokens.equals(0, "key") || tokens.equals(0, "outlinestate") ||
|
|
|
|
tokens.equals(0, "paste") || tokens.equals(0, "insertfile") ||
|
|
|
|
tokens.equals(0, "textinput") || tokens.equals(0, "windowkey") ||
|
2023-03-20 04:05:59 -05:00
|
|
|
tokens.equals(0, "windowmouse") || tokens.equals(0, "windowgesture"))
|
2023-02-04 15:59:08 -06:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tokens.size() > 1 && tokens.equals(0, "uno"))
|
|
|
|
{
|
|
|
|
// By default, all uno commands are modifying, unless we are certain they don't.
|
|
|
|
return !tokens.equals(1, ".uno:SidebarHide") && !tokens.equals(1, ".uno:SidebarShow") &&
|
|
|
|
!tokens.equals(1, ".uno:Copy") && !tokens.equals(1, ".uno:Save");
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2022-08-04 19:02:06 -05:00
|
|
|
}
|
|
|
|
|
2016-03-27 13:30:03 -05:00
|
|
|
/// Returns the first line of a message.
|
|
|
|
inline
|
|
|
|
std::string getFirstLine(const char *message, const int length)
|
|
|
|
{
|
2018-07-12 23:18:30 -05:00
|
|
|
return Util::getDelimitedInitialSubstring(message, length, '\n');
|
2016-03-27 13:30:03 -05:00
|
|
|
}
|
|
|
|
|
2016-10-08 12:12:16 -05:00
|
|
|
/// Returns the first line of any data which payload char*.
|
2016-05-08 23:35:45 -05:00
|
|
|
template <typename T>
|
|
|
|
std::string getFirstLine(const T& message)
|
2016-03-27 13:30:03 -05:00
|
|
|
{
|
|
|
|
return getFirstLine(message.data(), message.size());
|
|
|
|
}
|
|
|
|
|
2020-04-26 15:55:16 -05:00
|
|
|
/// Returns an abbreviation of the message (the first line, indicating truncation). We assume
|
2021-11-15 10:07:24 -06:00
|
|
|
/// that it adhers to the COOL protocol, i.e. that there is always a first (or only) line that
|
2016-10-18 07:51:29 -05:00
|
|
|
/// is in printable UTF-8. I.e. no encoding of binary bytes is done. The format of the result is
|
|
|
|
/// not guaranteed to be stable. It is to be used for logging purposes only, not for decoding
|
|
|
|
/// protocol frames.
|
2016-10-08 11:40:21 -05:00
|
|
|
inline
|
|
|
|
std::string getAbbreviatedMessage(const char *message, const int length)
|
|
|
|
{
|
|
|
|
if (message == nullptr || length <= 0)
|
|
|
|
{
|
2016-12-18 12:09:13 -06:00
|
|
|
return std::string();
|
2016-10-08 11:40:21 -05:00
|
|
|
}
|
|
|
|
|
2018-02-07 03:17:19 -06:00
|
|
|
const std::string firstLine = getFirstLine(message, std::min(length, 500));
|
2016-10-08 11:40:21 -05:00
|
|
|
|
|
|
|
// If first line is less than the length (minus newline), add ellipsis.
|
|
|
|
if (firstLine.size() < static_cast<std::string::size_type>(length) - 1)
|
|
|
|
{
|
|
|
|
return firstLine + "...";
|
|
|
|
}
|
|
|
|
|
|
|
|
return firstLine;
|
|
|
|
}
|
2016-04-10 12:13:05 -05:00
|
|
|
|
2016-12-18 12:09:13 -06:00
|
|
|
inline std::string getAbbreviatedMessage(const std::string& message)
|
|
|
|
{
|
2019-02-12 03:15:20 -06:00
|
|
|
const size_t pos = Util::getDelimiterPosition(message.data(), std::min<size_t>(message.size(), 501), '\n');
|
2016-12-18 12:09:13 -06:00
|
|
|
|
|
|
|
// If first line is less than the length (minus newline), add ellipsis.
|
|
|
|
if (pos < static_cast<std::string::size_type>(message.size()) - 1)
|
|
|
|
{
|
|
|
|
return message.substr(0, pos) + "...";
|
|
|
|
}
|
|
|
|
|
|
|
|
return message;
|
|
|
|
}
|
|
|
|
|
2016-05-08 23:35:45 -05:00
|
|
|
template <typename T>
|
|
|
|
std::string getAbbreviatedMessage(const T& message)
|
2016-05-01 19:31:40 -05:00
|
|
|
{
|
|
|
|
return getAbbreviatedMessage(message.data(), message.size());
|
|
|
|
}
|
2015-03-26 09:49:07 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|