qTox  Version: nightly | Commit: bc751c8e1cac455f9690654fcfe0f560d2d7dfdd
message.cpp
Go to the documentation of this file.
1 /*
2  Copyright © 2019 by The qTox Project Contributors
3 
4  This file is part of qTox, a Qt-based graphical interface for Tox.
5 
6  qTox is libre software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  qTox is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with qTox. If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include "message.h"
21 #include "friend.h"
22 #include "src/core/core.h"
23 
24 #include <cassert>
25 
26 namespace {
27  QStringList splitMessage(const QString& message, uint64_t maxLength)
28  {
29  QStringList splittedMsgs;
30  QByteArray ba_message{message.toUtf8()};
31  while (static_cast<uint64_t>(ba_message.size()) > maxLength) {
32  int splitPos = ba_message.lastIndexOf('\n', maxLength - 1);
33 
34  if (splitPos <= 0) {
35  splitPos = ba_message.lastIndexOf(' ', maxLength - 1);
36  }
37 
38  if (splitPos <= 0) {
39  constexpr uint8_t firstOfMultiByteMask = 0xC0;
40  constexpr uint8_t multiByteMask = 0x80;
41  splitPos = maxLength;
42  // don't split a utf8 character
43  if ((ba_message[splitPos] & multiByteMask) == multiByteMask) {
44  while ((ba_message[splitPos] & firstOfMultiByteMask) != firstOfMultiByteMask) {
45  --splitPos;
46  }
47  }
48  --splitPos;
49  }
50  splittedMsgs.append(QString{ba_message.left(splitPos + 1)});
51  ba_message = ba_message.mid(splitPos + 1);
52  }
53 
54  splittedMsgs.append(QString{ba_message});
55  return splittedMsgs;
56  }
57 }
58 void MessageProcessor::SharedParams::onUserNameSet(const QString& username)
59 {
60  QString sanename = username;
61  sanename.remove(QRegularExpression("[\\t\\n\\v\\f\\r\\x0000]"));
62  nameMention = QRegularExpression("\\b" + QRegularExpression::escape(username) + "\\b",
63  QRegularExpression::CaseInsensitiveOption);
64  sanitizedNameMention = QRegularExpression("\\b" + QRegularExpression::escape(sanename) + "\\b",
65  QRegularExpression::CaseInsensitiveOption);
66 }
67 
73 {
74  // no sanitization needed, we expect a ToxPk in its string form
75  pubKeyMention = QRegularExpression("\\b" + pk + "\\b",
76  QRegularExpression::CaseInsensitiveOption);
77 }
78 
81 {}
82 
86 std::vector<Message> MessageProcessor::processOutgoingMessage(bool isAction, QString const& content, ExtensionSet extensions)
87 {
88  std::vector<Message> ret;
89 
90  const auto maxSendingSize = extensions[ExtensionType::messages]
93 
94  const auto splitMsgs = splitMessage(content, maxSendingSize);
95 
96  ret.reserve(splitMsgs.size());
97 
98  QDateTime timestamp = QDateTime::currentDateTime();
99  std::transform(splitMsgs.begin(), splitMsgs.end(), std::back_inserter(ret),
100  [&](const QString& part) {
101  Message message;
102  message.isAction = isAction;
103  message.content = part;
104  message.timestamp = timestamp;
105  // In theory we could limit this only to the extensions
106  // required but since Core owns the splitting logic it
107  // isn't trivial to do that now
108  message.extensionSet = extensions;
109  return message;
110  });
111 
112  return ret;
113 }
114 
118 Message MessageProcessor::processIncomingCoreMessage(bool isAction, QString const& message)
119 {
120  QDateTime timestamp = QDateTime::currentDateTime();
121  auto ret = Message{};
122  ret.isAction = isAction;
123  ret.content = message;
124  ret.timestamp = timestamp;
125 
126  if (detectingMentions) {
127  auto nameMention = sharedParams.getNameMention();
128  auto sanitizedNameMention = sharedParams.getSanitizedNameMention();
129  auto pubKeyMention = sharedParams.getPublicKeyMention();
130 
131  for (auto const& mention : {nameMention, sanitizedNameMention, pubKeyMention}) {
132  auto matchIt = mention.globalMatch(ret.content);
133  if (!matchIt.hasNext()) {
134  continue;
135  }
136 
137  auto match = matchIt.next();
138 
139  auto pos = static_cast<size_t>(match.capturedStart());
140  auto length = static_cast<size_t>(match.capturedLength());
141 
142  // skip matches on empty usernames
143  if (length == 0) {
144  continue;
145  }
146 
147  ret.metadata.push_back({MessageMetadataType::selfMention, pos, pos + length});
148  break;
149  }
150  }
151 
152  return ret;
153 }
154 
156 {
157  // Note: detectingMentions not implemented here since mentions are only
158  // currently useful in group messages which do not support extensions. If we
159  // were to support mentions we would probably want to do something more
160  // intelligent anyways
161  assert(detectingMentions == false);
162  auto message = Message();
163  message.timestamp = QDateTime::currentDateTime();
164  message.content = content;
165  message.extensionSet |= ExtensionType::messages;
166 
167  return message;
168 }
friend.h
MessageProcessor::SharedParams::getNameMention
QRegularExpression getNameMention() const
Definition: message.h:79
MessageProcessor::SharedParams::getMaxCoreMessageSize
uint64_t getMaxCoreMessageSize() const
Definition: message.h:94
Message::isAction
bool isAction
Definition: message.h:54
MessageProcessor::SharedParams::nameMention
QRegularExpression nameMention
Definition: message.h:107
MessageProcessor::processIncomingExtMessage
Message processIncomingExtMessage(const QString &content)
Definition: message.cpp:155
ExtensionType::messages
@ messages
Definition: extension.h:28
message.h
MessageMetadataType::selfMention
@ selfMention
MessageProcessor::SharedParams::sanitizedNameMention
QRegularExpression sanitizedNameMention
Definition: message.h:108
HistMessageContentType::message
@ message
MessageProcessor::SharedParams
Definition: message.h:70
MessageProcessor::MessageProcessor
MessageProcessor(const SharedParams &sharedParams)
Definition: message.cpp:79
MessageProcessor::detectingMentions
bool detectingMentions
Definition: message.h:135
MessageProcessor::processOutgoingMessage
std::vector< Message > processOutgoingMessage(bool isAction, const QString &content, ExtensionSet extensions)
Converts an outgoing message into one (or many) sanitized Message(s)
Definition: message.cpp:86
MessageProcessor::sharedParams
const SharedParams & sharedParams
Definition: message.h:136
ExtensionSet
std::bitset< ExtensionType::max > ExtensionSet
Definition: extension.h:32
MessageProcessor::processIncomingCoreMessage
Message processIncomingCoreMessage(bool isAction, const QString &content)
Converts an incoming message into a sanitized Message.
Definition: message.cpp:118
MessageProcessor::SharedParams::getSanitizedNameMention
QRegularExpression getSanitizedNameMention() const
Definition: message.h:83
core.h
MessageProcessor::SharedParams::getPublicKeyMention
QRegularExpression getPublicKeyMention() const
Definition: message.h:87
MessageProcessor::SharedParams::setPublicKey
void setPublicKey(const QString &pk)
Set the public key on which a message should be highlighted.
Definition: message.cpp:72
MessageProcessor::SharedParams::getMaxExtendedMessageSize
uint64_t getMaxExtendedMessageSize() const
Definition: message.h:99
Message
Definition: message.h:52
MessageProcessor::SharedParams::onUserNameSet
void onUserNameSet(const QString &username)
Definition: message.cpp:58