Cutelee  6.1.0
stringfilters.cpp
1 /*
2  This file is part of the Cutelee template system.
3 
4  Copyright (c) 2009,2010 Stephen Kelly <steveire@gmail.com>
5 
6  This library is free software; you can redistribute it and/or
7  modify it under the terms of the GNU Lesser General Public
8  License as published by the Free Software Foundation; either version
9  2.1 of the Licence, or (at your option) any later version.
10 
11  This library 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 GNU
14  Lesser General Public License for more details.
15 
16  You should have received a copy of the GNU Lesser General Public
17  License along with this library. If not, see <http://www.gnu.org/licenses/>.
18 
19 */
20 
21 #include "stringfilters.h"
22 
23 #include "util.h"
24 
25 #include <QtCore/QRegularExpression>
26 #include <QtCore/QVariant>
27 
29  const QVariant &argument,
30  bool autoescape) const
31 {
32  Q_UNUSED(argument)
33  Q_UNUSED(autoescape)
34  auto safeString = getSafeString(input);
35  safeString.get()
36  .replace(QLatin1Char('\\'), QStringLiteral("\\\\"))
37  .get()
38  .replace(QLatin1Char('\"'), QStringLiteral("\\\""))
39  .get()
40  .replace(QLatin1Char('\''), QStringLiteral("\\\'"));
41  return safeString;
42 }
43 
45  const QVariant &argument,
46  bool autoescape) const
47 {
48  Q_UNUSED(argument)
49  Q_UNUSED(autoescape)
50  auto safeString = getSafeString(input);
51  if (safeString.get().isEmpty())
52  return QString();
53 
54  return QVariant(safeString.get().at(0).toUpper()
55  + static_cast<QString>(
56  safeString.get().right(safeString.get().size() - 1)));
57 }
58 
59 EscapeJsFilter::EscapeJsFilter() {}
60 
61 static QList<std::pair<QString, QString>> getJsEscapes()
62 {
64  jsEscapes << std::pair<QString, QString>(QChar::fromLatin1('\\'),
65  QStringLiteral("\\u005C"))
66  << std::pair<QString, QString>(QChar::fromLatin1('\''),
67  QStringLiteral("\\u0027"))
68  << std::pair<QString, QString>(QChar::fromLatin1('\"'),
69  QStringLiteral("\\u0022"))
70  << std::pair<QString, QString>(QChar::fromLatin1('>'),
71  QStringLiteral("\\u003E"))
72  << std::pair<QString, QString>(QChar::fromLatin1('<'),
73  QStringLiteral("\\u003C"))
74  << std::pair<QString, QString>(QChar::fromLatin1('&'),
75  QStringLiteral("\\u0026"))
76  << std::pair<QString, QString>(QChar::fromLatin1('='),
77  QStringLiteral("\\u003D"))
78  << std::pair<QString, QString>(QChar::fromLatin1('-'),
79  QStringLiteral("\\u002D"))
80  << std::pair<QString, QString>(QChar::fromLatin1(';'),
81  QStringLiteral("\\u003B"))
82  << std::pair<QString, QString>(QChar(0x2028), QStringLiteral("\\u2028"))
83  << std::pair<QString, QString>(QChar(0x2029),
84  QStringLiteral("\\u2029"));
85 
86  for (auto i = 0; i < 32; ++i) {
87  jsEscapes << std::pair<QString, QString>(
88  QChar(i),
89  QStringLiteral("\\u00")
90  + QStringLiteral("%1").arg(i, 2, 16, QLatin1Char('0')).toUpper());
91  }
92  return jsEscapes;
93 }
94 
96  const QVariant &argument,
97  bool autoescape) const
98 {
99  Q_UNUSED(argument)
100  Q_UNUSED(autoescape)
101  QString retString = getSafeString(input);
102 
103  static const auto jsEscapes = getJsEscapes();
104 
105  for (auto &escape : jsEscapes) {
106  retString = retString.replace(escape.first, escape.second);
107  }
108  return retString;
109 }
110 
112  const QVariant &argument,
113  bool autoescape) const
114 {
115  Q_UNUSED(argument)
116  Q_UNUSED(autoescape)
117  auto safeString = getSafeString(input);
118 
119  const QRegularExpression fixAmpersandsRegexp(
120  QStringLiteral("&(?!(\\w+|#\\d+);)"));
121 
122  safeString.get().replace(fixAmpersandsRegexp, QStringLiteral("&amp;"));
123 
124  return safeString;
125 }
126 
127 QVariant CutFilter::doFilter(const QVariant &input, const QVariant &argument,
128  bool autoescape) const
129 {
130  Q_UNUSED(autoescape)
131  auto retString = getSafeString(input);
132  auto argString = getSafeString(argument);
133 
134  auto inputSafe = retString.isSafe();
135 
136  retString.get().remove(argString);
137 
138  if (inputSafe && argString.get() != QChar::fromLatin1(';'))
139  return SafeString(retString, true);
140  else
141  return retString;
142 }
143 
144 QVariant SafeFilter::doFilter(const QVariant &input, const QVariant &argument,
145  bool autoescape) const
146 {
147  Q_UNUSED(argument)
148  Q_UNUSED(autoescape)
149  return markSafe(getSafeString(input));
150 }
151 
153  const QVariant &argument,
154  bool autoescape) const
155 {
156  Q_UNUSED(argument)
157  auto safeString = getSafeString(input);
158  auto lines = safeString.get().split(QLatin1Char('\n'));
159  auto width = QString::number(lines.size()).size();
160 
161  const auto shouldEscape = (autoescape && !safeString.isSafe());
162  for (auto i = 0; i < lines.size(); ++i) {
163  lines[i]
164  = QStringLiteral("%1. %2")
165  .arg(i + 1, width)
166  .arg(shouldEscape ? QString(escape(lines.at(i))) : lines.at(i));
167  }
168 
169  return SafeString(lines.join(QChar::fromLatin1('\n')), true);
170 }
171 
172 QVariant LowerFilter::doFilter(const QVariant &input, const QVariant &argument,
173  bool autoescape) const
174 {
175  Q_UNUSED(argument)
176  Q_UNUSED(autoescape)
177  return getSafeString(input).get().toLower();
178 }
179 
181  const QVariant &argument,
182  bool autoescape) const
183 {
184  Q_UNUSED(autoescape)
185  SafeString a;
186  if (isSafeString(input))
187  a = getSafeString(input);
188  else if (input.userType() == qMetaTypeId<QVariantList>()) {
189  a = toString(input.value<QVariantList>());
190  }
191 
192  return SafeString(getSafeString(argument).get().arg(a),
193  getSafeString(input).isSafe());
194 }
195 
196 QVariant TitleFilter::doFilter(const QVariant &input, const QVariant &argument,
197  bool autoescape) const
198 {
199  Q_UNUSED(argument)
200  Q_UNUSED(autoescape)
201 
202  QString str = getSafeString(input);
203 
204  auto it = str.begin();
205  const auto end = str.end();
206 
207  auto toUpper = true;
208  for (; it != end; ++it) {
209  if (toUpper)
210  *it = it->toUpper();
211  else
212  *it = it->toLower();
213  toUpper = it->isSpace();
214  }
215 
216  return str;
217 }
218 
220  const QVariant &argument,
221  bool autoescape) const
222 {
223  Q_UNUSED(autoescape)
224  auto s = getSafeString(argument);
225 
226  bool ok;
227  auto numWords = s.get().toInt(&ok);
228 
229  if (!ok) {
230  return input.toString();
231  }
232 
233  QString inputString = getSafeString(input);
234 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
235  auto words = inputString.split(QLatin1Char(' '), QString::SkipEmptyParts);
236 #else
237  auto words = inputString.split(QLatin1Char(' '), Qt::SkipEmptyParts);
238 #endif
239 
240  if (words.size() > numWords) {
241  words = words.mid(0, numWords);
242  if (!words.at(words.size() - 1).endsWith(QStringLiteral("..."))) {
243  words << QStringLiteral("...");
244  }
245  }
246  return words.join(QChar::fromLatin1(' '));
247 }
248 
249 QVariant UpperFilter::doFilter(const QVariant &input, const QVariant &argument,
250  bool autoescape) const
251 {
252  Q_UNUSED(argument)
253  Q_UNUSED(autoescape)
254  return getSafeString(input).get().toUpper();
255 }
256 
258  const QVariant &argument,
259  bool autoescape) const
260 {
261  Q_UNUSED(argument)
262  Q_UNUSED(autoescape)
263  return QString::number(
264  getSafeString(input).get().split(QLatin1Char(' ')).size());
265 }
266 
267 QVariant LJustFilter::doFilter(const QVariant &input, const QVariant &argument,
268  bool autoescape) const
269 {
270  Q_UNUSED(autoescape)
271  return getSafeString(input).get().leftJustified(
272  getSafeString(argument).get().toInt());
273 }
274 
275 QVariant RJustFilter::doFilter(const QVariant &input, const QVariant &argument,
276  bool autoescape) const
277 {
278  Q_UNUSED(autoescape)
279  return getSafeString(input).get().rightJustified(
280  getSafeString(argument).get().toInt());
281 }
282 
283 QVariant CenterFilter::doFilter(const QVariant &input, const QVariant &argument,
284  bool autoescape) const
285 {
286  Q_UNUSED(autoescape)
287  QString value = getSafeString(input);
288  const auto valueWidth = value.size();
289  const auto width = getSafeString(argument).get().toInt();
290  const auto totalPadding = width - valueWidth;
291  const auto rightPadding = totalPadding >> 1;
292 
293  return value.leftJustified(valueWidth + rightPadding).rightJustified(width);
294 }
295 
296 QVariant EscapeFilter::doFilter(const QVariant &input, const QVariant &argument,
297  bool autoescape) const
298 {
299  Q_UNUSED(argument)
300  Q_UNUSED(autoescape)
301  return markForEscaping(getSafeString(input));
302 }
303 
305  const QVariant &argument,
306  bool autoescape) const
307 {
308  Q_UNUSED(argument)
309  Q_UNUSED(autoescape)
310  return markSafe(escape(getSafeString(input)));
311 }
312 
314  const QVariant &argument,
315  bool autoescape) const
316 {
317  Q_UNUSED(autoescape)
318  const auto tags = getSafeString(argument).get().split(QLatin1Char(' '));
319  const auto tagRe
320  = QStringLiteral("(%1)").arg(tags.join(QChar::fromLatin1('|')));
321  const QRegularExpression startTag(
322  QStringLiteral("<%1(/?>|(\\s+[^>]*>))").arg(tagRe));
323  const QRegularExpression endTag(QStringLiteral("</%1>").arg(tagRe));
324 
325  auto value = getSafeString(input);
326  const auto safeInput = value.isSafe();
327  value.get().remove(startTag);
328  value.get().remove(endTag);
329  if (safeInput)
330  return markSafe(value);
331  return value;
332 }
333 
335  const QVariant &argument,
336  bool autoescape) const
337 {
338  Q_UNUSED(argument)
339  Q_UNUSED(autoescape)
340  static QRegularExpression tagRe(QStringLiteral("<[^>]*>"),
342 
343  QString value = getSafeString(input);
344  value.remove(tagRe);
345  return value;
346 }
347 
349  const QVariant &argument,
350  bool autoescape) const
351 {
352  Q_UNUSED(autoescape)
353  QString _input = getSafeString(input);
354  auto width = argument.value<int>();
355 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
356  auto partList = _input.split(QLatin1Char(' '), QString::SkipEmptyParts);
357 #else
358  auto partList = _input.split(QLatin1Char(' '), Qt::SkipEmptyParts);
359 #endif
360  if (partList.isEmpty())
361  return QVariant();
362  auto output = partList.takeFirst();
363  auto pos = output.size() - output.lastIndexOf(QLatin1Char('\n')) - 1;
364  Q_FOREACH (const QString &part, partList) {
365  QStringList lines;
366  if (part.contains(QLatin1Char('\n'))) {
367  lines = part.split(QLatin1Char('\n'));
368  } else {
369  lines.append(part);
370  }
371  pos += lines.first().size() + 1;
372  if (pos > width) {
373  output.append(QLatin1Char('\n'));
374  pos += lines.last().size();
375  } else {
376  output.append(QLatin1Char(' '));
377  if (lines.size() > 1)
378  pos += lines.last().size();
379  }
380  output.append(part);
381  }
382  return output;
383 }
384 
386  const QVariant &argument,
387  bool autoescape) const
388 {
389  Q_UNUSED(autoescape)
390  double inputDouble;
391  switch (input.userType()) {
392  case QMetaType::Int:
393  case QMetaType::UInt:
394  case QMetaType::LongLong:
396  case QMetaType::Double:
397  inputDouble = input.toDouble();
398  break;
399  default:
400  inputDouble = getSafeString(input).get().toDouble();
401  }
402 
403  int precision;
404  if (argument.isValid())
405  precision = getSafeString(argument).get().toInt();
406  else
407  precision = 1;
408 
409  return QString::number(inputDouble, 'f', precision);
410 }
411 
413  const QVariant &argument,
414  bool autoescape) const
415 {
416  Q_UNUSED(argument)
417  Q_UNUSED(autoescape)
418  QVariantList list;
419  if (input.userType() == qMetaTypeId<QVariantList>()) {
420  const auto inputList = input.value<QVariantList>();
421  for (const auto &item : inputList) {
422  list << markSafe(getSafeString(item));
423  }
424  }
425  return list;
426 }
427 
429  const QVariant &argument,
430  bool autoescape) const
431 {
432  Q_UNUSED(argument)
433  auto inputString = getSafeString(input);
434  static const QRegularExpression re(QStringLiteral("\n{2,}"));
435  QStringList output;
436 
437  Q_FOREACH (const QString &bit, inputString.get().split(re)) {
438  auto _bit = SafeString(bit, inputString.isSafe());
439  if (autoescape)
440  _bit = conditionalEscape(_bit);
441  _bit.get().replace(QLatin1Char('\n'), QStringLiteral("<br />"));
442  output.append(QStringLiteral("<p>%1</p>").arg(_bit));
443  }
444  return markSafe(output.join(QStringLiteral("\n\n")));
445 }
446 
448  const QVariant &argument,
449  bool autoescape) const
450 {
451  Q_UNUSED(argument)
452  auto inputString = getSafeString(input);
453  if (autoescape && isSafeString(input)) {
454  inputString = conditionalEscape(inputString);
455  }
456  return markSafe(
457  inputString.get().replace(QLatin1Char('\n'), QStringLiteral("<br />")));
458 }
459 
460 static QString nofailStringToAscii(const QString &input)
461 {
462  QString output;
463  output.reserve(input.size());
464 
465  auto it = input.constBegin();
466  const auto end = input.constEnd();
467  static const QChar asciiEndPoint(128);
468  for (; it != end; ++it)
469  if (*it < asciiEndPoint)
470  output.append(*it);
471 
472  return output;
473 }
474 
476  const QVariant &argument,
477  bool autoescape) const
478 {
479  Q_UNUSED(argument)
480  Q_UNUSED(autoescape)
481  QString inputString = getSafeString(input);
482  inputString = inputString.normalized(QString::NormalizationForm_KD);
483  inputString = nofailStringToAscii(inputString);
484  inputString = inputString.trimmed()
485  .toLower()
486  .remove(QRegularExpression(QStringLiteral("[^\\w\\s-]")))
487  .replace(QRegularExpression(QStringLiteral("[-\\s]+")), QStringLiteral("-"));
488  return SafeString(inputString, true);
489 }
490 
492  const QVariant &argument,
493  bool autoescape) const
494 {
495  QVariant ret;
496 
497  Q_UNUSED(autoescape)
498  const auto arg = getSafeString(argument);
499  bool numberConvert = true;
500 
501  qreal size = 0.0f;
502  if (input.canConvert<qreal>()) {
503  size = input.toReal(&numberConvert);
504  if (!numberConvert) {
505  qWarning("%s", "Failed to convert input file size into floating point value.");
506  }
507  } else {
508  size = getSafeString(input).get().toDouble(&numberConvert);
509  if (!numberConvert) {
510  qWarning("%s", "Failed to convert input file size into floating point value.");
511  }
512  }
513 
514  int unitSystem = 10;
515  int precision = 2;
516  qreal multiplier = 1.0f;
517 
518  if (!arg.get().isEmpty()) {
519 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
520  const auto argList = arg.get().split(QLatin1Char(','), QString::SkipEmptyParts);
521 #else
522  const auto argList = arg.get().split(QLatin1Char(','), Qt::SkipEmptyParts);
523 #endif
524  const auto numArgs = argList.size();
525  if (numArgs > 0) {
526  unitSystem = argList.at(0).toInt(&numberConvert);
527  if (!numberConvert) {
528  qWarning("%s", "Failed to convert filse size format unit system into integer. Falling back to default 10.");
529  unitSystem = 10;
530  }
531  }
532 
533  if (numArgs > 1) {
534  precision = argList.at(1).toInt(&numberConvert);
535  if (!numberConvert) {
536  qWarning("%s", "Failed to convert file size format decimal precision into integer. Falling back to default 2.");
537  precision = 2;
538  }
539  }
540 
541  if (numArgs > 2) {
542  multiplier = argList.at(2).toDouble(&numberConvert);
543  if (!numberConvert) {
544  qWarning("%s", "Failed to convert file size format multiplier into double value. Falling back to default 1.0");
545  multiplier = 1.0f;
546  } else {
547  if (multiplier == 0.0f) {
548  qWarning("%s", "It makes no sense to multiply the file size by zero. Using default value 1.0.");
549  multiplier = 1.0f;
550  }
551  }
552  }
553  }
554 
555  const double sizeMult = size * multiplier;
556 
557  if (unitSystem == 10) {
558  if ((sizeMult > -1000) && (sizeMult < 1000)) {
559  precision = 0;
560  }
561  } else if (unitSystem == 2) {
562  if ((sizeMult > - 1024) && (sizeMult < 1024)) {
563  precision = 0;
564  }
565  }
566 
567  const std::pair<qreal,QString> sizePair = calcFileSize(size, unitSystem, multiplier);
568 
569  const QString retString = QString::number(sizePair.first, 'f', precision) + QLatin1Char(' ') + sizePair.second;
570 
571  ret.setValue(retString);
572 
573  return ret;
574 }
575 
576 QVariant TruncateCharsFilter::doFilter(const QVariant &input, const QVariant &argument, bool autoescape) const
577 {
578  Q_UNUSED(autoescape)
579  QString retString = getSafeString(input);
580  int count = getSafeString(argument).get().toInt();
581 
582  if(retString.length() < count) return retString;
583  retString.truncate(count);
584  retString.append(QStringLiteral("..."));
585  return retString;
586 }
bool canConvert(int targetTypeId) const const
QString::const_iterator constBegin() const const
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QString & append(QChar ch)
QVariant doFilter(const QVariant &input, const QVariant &argument=QVariant(), bool autoescape={}) const override
QString toUpper() const const
void truncate(int position)
QString::iterator end()
Cutelee::SafeString markSafe(const Cutelee::SafeString &input)
Definition: util.cpp:90
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
SafeString escape(const QString &input) const
Definition: filter.cpp:10
int size() const const
T value() const const
QString join(const QString &separator) const const
QString & remove(int position, int n)
double toDouble(bool *ok) const const
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
int size() const const
bool isSafeString(const QVariant &input)
Definition: util.cpp:117
QString normalized(QString::NormalizationForm mode, QChar::UnicodeVersion version) const const
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
SafeString conditionalEscape(const SafeString &input) const
Definition: filter.cpp:22
Cutelee::SafeString markForEscaping(const Cutelee::SafeString &input)
Definition: util.cpp:98
QString::const_iterator constEnd() const const
QString number(int n, int base)
void append(const T &value)
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QString rightJustified(int width, QChar fill, bool truncate) const const
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QChar fromLatin1(char c)
int toInt(bool *ok, int base) const const
QString trimmed() const const
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
T & first()
Utility functions used throughout Cutelee.
A QString wrapper class for containing whether a string is safe or needs to be escaped.
Definition: safestring.h:91
int userType() const const
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QString toLower() const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
SkipEmptyParts
void setValue(const T &value)
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QString & replace(int position, int n, QChar after)
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QChar toUpper() const const
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
const QChar at(int position) const const
T & last()
QList< T > mid(int pos, int length) const const
int length() const const
void reserve(int size)
QString leftJustified(int width, QChar fill, bool truncate) const const
NormalizationForm_KD
bool isValid() const const
double toDouble(bool *ok) const const
std::pair< qreal, QString > calcFileSize(qreal size, int unitSystem=10, qreal multiplier=1.0)
Definition: util.cpp:215
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
qreal toReal(bool *ok) const const
QString::iterator begin()
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
bool isSafe() const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QString toString() const const
QVariant doFilter(const QVariant &input, const QVariant &argument=QVariant(), bool autoescape={}) const override
const NestedString & get() const
Definition: safestring.h:340
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
Cutelee::SafeString getSafeString(const QVariant &input)
Definition: util.cpp:108
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override