cutelyst  3.7.0
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
validatorfilesize.cpp
1 /*
2  * SPDX-FileCopyrightText: (C) 2018-2022 Matthias Fehring <mf@huessenbergnetz.de>
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 
6 #include "validatorfilesize_p.h"
7 #include <cmath>
8 #include <limits>
9 
10 using namespace Cutelyst;
11 
12 ValidatorFileSize::ValidatorFileSize(const QString &field, Option option, const QVariant &min, const QVariant &max, const ValidatorMessages &messages, const QString &defValKey) :
13  ValidatorRule(*new ValidatorFileSizePrivate(field, option, min, max, messages, defValKey))
14 {
15 
16 }
17 
19 {
20 
21 }
22 
23 bool ValidatorFileSize::validate(const QString &value, double min, double max, Cutelyst::ValidatorFileSize::Option option, const QLocale &locale, double *fileSize)
24 {
25  bool valid = true;
26 
27  QString digitPart;
28  QString symbolPart;
29  bool decimalPointFound = false;
30  const QString decimalPoint(locale.decimalPoint());
31  int multiplier = 0;
32  bool binary = false;
33  bool byteSignFound = false;
34  qint8 startsWith = 0; // 0 not set, -1 digit part, 1 symbol part
35 
36  for (const QChar &ch : value) {
37  if (valid) {
38  const ushort &uc = ch.unicode();
39  if (((uc > 47) && (uc < 58)) || (ch == decimalPoint)) {
40  if (startsWith == 0) {
41  startsWith = -1;
42  }
43  if (ch == decimalPoint) {
44  if (decimalPointFound) {
45  valid = false;
46  break;
47  } else {
48  decimalPointFound = true;
49  }
50  }
51  if ((symbolPart.isEmpty() && (startsWith < 0)) || (!symbolPart.isEmpty() && (startsWith > 0))) {
52  digitPart.append(ch);
53  } else {
54  valid = false;
55  break;
56  }
57  } else if ((uc != 9) && (uc != 32)) { // not a digit or decimal point and not a space or tab
58  if (startsWith == 0) {
59  startsWith = 1;
60  }
61  if ((digitPart.isEmpty() && (startsWith > 0)) || (!digitPart.isEmpty() && (startsWith < 0))) {
62  switch (uc) {
63  case 75: // K
64  case 107: // k
65  {
66  if (multiplier > 0) {
67  valid = false;
68  } else {
69  multiplier = 1;
70  symbolPart.append(ch);
71  }
72  }
73  break;
74  case 77: // M
75  case 109: // m
76  {
77  if (multiplier > 0) {
78  valid = false;
79  } else {
80  multiplier = 2;
81  symbolPart.append(ch);
82  }
83  }
84  break;
85  case 71: // G
86  case 103: // g
87  {
88  if (multiplier > 0) {
89  valid = false;
90  } else {
91  multiplier = 3;
92  symbolPart.append(ch);
93  }
94  }
95  break;
96  case 84: // T
97  case 116: // t
98  {
99  if (multiplier > 0) {
100  valid = false;
101  } else {
102  multiplier = 4;
103  symbolPart.append(ch);
104  }
105  }
106  break;
107  case 80: // P
108  case 112: // p
109  {
110  if (multiplier > 0) {
111  valid = false;
112  } else {
113  multiplier = 5;
114  symbolPart.append(ch);
115  }
116  }
117  break;
118  case 69: // E
119  case 101: // e
120  {
121  if (multiplier > 0) {
122  valid = false;
123  } else {
124  multiplier = 6;
125  symbolPart.append(ch);
126  }
127  }
128  break;
129  case 90: // Z
130  case 122: // z
131  {
132  if (multiplier > 0) {
133  valid = false;
134  } else {
135  multiplier = 7;
136  symbolPart.append(ch);
137  }
138  }
139  break;
140  case 89: // Y
141  case 121: // y
142  {
143  if (multiplier > 0) {
144  valid = false;
145  } else {
146  multiplier = 8;
147  symbolPart.append(ch);
148  }
149  }
150  break;
151  case 73: // I
152  case 105: // i
153  {
154  if ((multiplier == 0) || binary) {
155  valid = false;
156  } else {
157  binary = true;
158  symbolPart.append(ch);
159  }
160  }
161  break;
162  case 66: // B
163  case 98: // b
164  {
165  if (byteSignFound) {
166  valid = false;
167  } else {
168  byteSignFound = true;
169  symbolPart.append(ch);
170  }
171  }
172  break;
173  case 9: // horizontal tab
174  case 32: // space
175  break;
176  default:
177  valid = false;
178  break;
179  }
180  } else {
181  valid = false;
182  break;
183  }
184  }
185  } else {
186  break;
187  }
188  }
189 
190  if ((option == OnlyBinary) && !binary) {
191  valid = false;
192  } else if ((option == OnlyDecimal) && binary) {
193  valid = false;
194  } else if (option == ForceBinary) {
195  binary = true;
196  } else if (option == ForceDecimal) {
197  binary = false;
198  }
199 
200  if (valid) {
201  bool ok = false;
202  double size = locale.toDouble(digitPart, &ok);
203  if (!ok) {
204  valid = false;
205  } else {
206  if (multiplier > 0) {
207  const double _mult = binary ? std::exp2(multiplier * 10) : std::pow(10.0, multiplier * 3);
208  size *= _mult;
209  }
210  if ((min >= 1.0) && (size < min)) {
211  valid = false;
212  }
213  if ((max >= 1.0) && (size > max)) {
214  valid = false;
215  }
216  if (valid && fileSize) {
217  *fileSize = size;
218  }
219  }
220  }
221 
222  return valid;
223 }
224 
226 {
227  ValidatorReturnType result;
228 
229  Q_D(const ValidatorFileSize);
230 
231  const QString v = value(params);
232 
233  if (!v.isEmpty()) {
234 
235  double min = -1;
236  double max = -1;
237  bool ok = true;
238  if (d->min.isValid()) {
239  min = d->extractDouble(c, params, d->min, &ok);
240  if (!ok) {
241  result.errorMessage = validationDataError(c, 0);
242  }
243  }
244 
245  if (ok && d->max.isValid()) {
246  max = d->extractDouble(c, params, d->max, &ok);
247  if (!ok) {
248  result.errorMessage = validationDataError(c, 1);
249  }
250  }
251 
252  if (ok) {
253  double size = 0;
254  if (ValidatorFileSize::validate(v, min, max, d->option, c->locale(), &size)) {
255  if (size < static_cast<double>(std::numeric_limits<qulonglong>::max())) {
256  result.value.setValue<qulonglong>(static_cast<qulonglong>(size + 0.5));
257  } else {
258  result.value.setValue(size);
259  }
260  } else {
261  result.errorMessage = validationError(c);
262  }
263  }
264 
265  } else {
266  defaultValue(c, &result, "ValidatorFileSize");
267  }
268 
269  return result;
270 }
271 
272 QString ValidatorFileSize::genericValidationError(Context *c, const QVariant &errorData) const
273 {
274  QString error;
275  Q_D(const ValidatorFileSize);
276  Q_UNUSED(errorData)
277  const QString _label = label(c);
278  if (d->min.isValid() || d->max.isValid()) {
279  if (_label.isEmpty()) {
280  error = c->translate("Cutelyst::ValidatorFileSize", "Invalid file size or file size not within the allowed limits.");
281  } else {
282  error = c->translate("Cutelyst::ValidatorFileSize", "The value in the “%1” field is either not a valid file size or not within the allowed limits.").arg(_label);
283  }
284  } else {
285  if (_label.isEmpty()) {
286  error = c->translate("Cutelyst::ValidatorFileSize", "Invalid file size.");
287  } else {
288  error = c->translate("Cutelyst::ValidatorFileSize", "The “%1” field does not contain a valid file size.").arg(_label);
289  }
290  }
291 
292  return error;
293 }
294 
295 QString ValidatorFileSize::genericValidationDataError(Context *c, const QVariant &errorData) const
296 {
297  QString error;
298 
299  const QString _label = label(c);
300 
301  const int sizeType = errorData.toInt();
302 
303  if (sizeType == 0) { // minimum file size
304  if (_label.isEmpty()) {
305  error = c->translate("Cutelyst::ValidatorFileSize", "The minimum file size comparison value is not valid.");
306  } else {
307  error = c->translate("Cutelyst::ValidatorFileSize", "The minimum file size comparison value for the “%1” field is not valid.").arg(_label);
308  }
309  } else {
310  if (_label.isEmpty()) {
311  error = c->translate("Cutelyst::ValidatorFileSize", "The maximum file size comparison value is not valid.");
312  } else {
313  error = c->translate("Cutelyst::ValidatorFileSize", "The maximum file size comparison value for the “%1” field is not valid.").arg(_label);
314  }
315  }
316 
317  return error;
318 }
319 
320 void ValidatorFileSize::inputPattern(Context *c, const QString &stashKey)
321 {
322  Q_ASSERT(c);
323  c->setStash(stashKey, c->locale().textDirection() == Qt::LeftToRight ? QStringLiteral("^\\d+[,.٫]?\\d*\\s*[KkMmGgTt]?[Ii]?[Bb]?") : QStringLiteral("[KkMmGgTt]?[Ii]?[Bb]?\\s*\\d+[,.٫]?\\d*"));
324 }
The Cutelyst Context.
Definition: context.h:39
QLocale locale() const noexcept
Definition: context.cpp:453
QString translate(const char *context, const char *sourceText, const char *disambiguation=nullptr, int n=-1) const
Definition: context.cpp:477
void setStash(const QString &key, const QVariant &value)
Definition: context.cpp:218
Checks if the input field contains a valid file size string like 1.5 GB.
~ValidatorFileSize() override
Deconstructs the file size validator.
QString genericValidationDataError(Context *c, const QVariant &errorData) const override
Returns a generic error messages if validation data is missing or invalid.
Option
Options for ValidatorFileSize.
QString genericValidationError(Context *c, const QVariant &errorData=QVariant()) const override
Returns a generic error message if validation failed.
static void inputPattern(Context *c, const QString &stashKey=QStringLiteral("fileSizePattern"))
Puts an HTML input pattern for file sizes into the stash.
ValidatorFileSize(const QString &field, Option option=NoOption, const QVariant &min=QVariant(), const QVariant &max=QVariant(), const ValidatorMessages &messages=ValidatorMessages(), const QString &defValKey=QString())
Constructs a new file size validator.
Base class for all validator rules.
QString label(Context *c) const
Returns the human readable field label used for generic error messages.
void defaultValue(Context *c, ValidatorReturnType *result, const char *validatorName) const
I a defValKey has been set in the constructor, this will try to get the default value from the stash ...
QString value(const ParamsMultiMap &params) const
Returns the value of the field from the input params.
QString validationDataError(Context *c, const QVariant &errorData=QVariant()) const
Returns an error message if any validation data is missing or invalid.
QString validationError(Context *c, const QVariant &errorData=QVariant()) const
Returns a descriptive error message if validation failed.
static bool validate(const QString &value, double min=-1, double max=-1, Option option=NoOption, const QLocale &locale=QLocale(), double *fileSize=nullptr)
Returns true if value is a valid file size string.
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:8
QMultiMap< QString, QString > ParamsMultiMap
Stores custom error messages and the input field label.
Contains the result of a single input parameter validation.
Definition: validatorrule.h:49