cutelyst 4.0.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-2023 Matthias Fehring <mf@huessenbergnetz.de>
3 * SPDX-License-Identifier: BSD-3-Clause
4 */
5
6#include "validatorfilesize_p.h"
7
8#include <cmath>
9#include <limits>
10
11using namespace Cutelyst;
12
14 Option option,
15 const QVariant &min,
16 const QVariant &max,
17 const ValidatorMessages &messages,
18 const QString &defValKey)
19 : ValidatorRule(*new ValidatorFileSizePrivate(field, option, min, max, messages, defValKey))
20{
21}
22
24
25bool ValidatorFileSize::validate(const QString &value,
26 double min,
27 double max,
29 const QLocale &locale,
30 double *fileSize)
31{
32 bool valid = true;
33
34 const QString str = value.simplified();
35 QString digitPart;
36 QString symbolPart;
37 bool decimalPointFound = false;
38 const QString decimalPoint(locale.decimalPoint());
39 int multiplier = 0;
40 bool binary = false;
41 bool byteSignFound = false;
42 ValidatorFileSizePrivate::StartsWith startsWith{ValidatorFileSizePrivate::StartsWith::NotSet};
43
44 for (const QChar &ch : str) {
45 if (valid) {
46 const char16_t uc = ch.toUpper().unicode();
47 if (((uc >= ValidatorRulePrivate::ascii_0) && (uc <= ValidatorRulePrivate::ascii_9)) ||
48 (ch == decimalPoint)) {
49 if (startsWith == ValidatorFileSizePrivate::StartsWith::NotSet) {
50 startsWith = ValidatorFileSizePrivate::StartsWith::DigitPart;
51 }
52 if (ch == decimalPoint) {
53 if (decimalPointFound) {
54 qCDebug(C_VALIDATOR).nospace()
55 << "ValidatorFileSize: Validation failed for " << value << ": "
56 << "two decimal seperators in a row";
57 valid = false;
58 break;
59 } else {
60 decimalPointFound = true;
61 }
62 }
63 if ((symbolPart.isEmpty() &&
64 (startsWith == ValidatorFileSizePrivate::StartsWith::DigitPart)) ||
65 (!symbolPart.isEmpty() &&
66 (startsWith == ValidatorFileSizePrivate::StartsWith::SymbolPart))) {
67 digitPart.append(ch);
68 } else {
69 qCDebug(C_VALIDATOR).nospace()
70 << "ValidatorFileSize: Validation failed for " << value << ": "
71 << "symbol inside digit part";
72 valid = false;
73 break;
74 }
75 } else if ((uc != ValidatorRulePrivate::asciiTab) &&
76 (uc != ValidatorRulePrivate::asciiSpace)) { // not a digit or decimal point
77 // and not a space or tab
78 if (startsWith == ValidatorFileSizePrivate::StartsWith::NotSet) {
79 startsWith = ValidatorFileSizePrivate::StartsWith::SymbolPart;
80 }
81 if ((digitPart.isEmpty() &&
82 (startsWith == ValidatorFileSizePrivate::StartsWith::SymbolPart)) ||
83 (!digitPart.isEmpty() &&
84 (startsWith == ValidatorFileSizePrivate::StartsWith::DigitPart))) {
85 switch (uc) {
86 case ValidatorFileSizePrivate::ascii_K:
87 {
88 if (multiplier > 0) {
89 valid = false;
90 qCDebug(C_VALIDATOR).nospace()
91 << "ValdatorFileSize: Validation failed for " << value << ": "
92 << "unit symbol K already found";
93 } else {
94 multiplier = 1;
95 symbolPart.append(ch);
96 }
97 } break;
98 case ValidatorFileSizePrivate::ascii_M:
99 {
100 if (multiplier > 0) {
101 valid = false;
102 qCDebug(C_VALIDATOR).nospace()
103 << "ValdatorFileSize: Validation failed for " << value << ": "
104 << "unit symbol M already found";
105 } else {
106 multiplier = 2;
107 symbolPart.append(ch);
108 }
109 } break;
110 case ValidatorFileSizePrivate::ascii_G:
111 {
112 if (multiplier > 0) {
113 valid = false;
114 qCDebug(C_VALIDATOR).nospace()
115 << "ValdatorFileSize: Validation failed for " << value << ": "
116 << "unit symbol G already found";
117 } else {
118 multiplier = 3;
119 symbolPart.append(ch);
120 }
121 } break;
122 case ValidatorFileSizePrivate::ascii_T:
123 {
124 if (multiplier > 0) {
125 valid = false;
126 qCDebug(C_VALIDATOR).nospace()
127 << "ValdatorFileSize: Validation failed for " << value << ": "
128 << "unit symbol T already found";
129 } else {
130 multiplier = 4;
131 symbolPart.append(ch);
132 }
133 } break;
134 case ValidatorFileSizePrivate::ascii_P:
135 {
136 if (multiplier > 0) {
137 valid = false;
138 qCDebug(C_VALIDATOR).nospace()
139 << "ValdatorFileSize: Validation failed for " << value << ": "
140 << "unit symbol P already found";
141 } else {
142 multiplier = 5;
143 symbolPart.append(ch);
144 }
145 } break;
146 case ValidatorFileSizePrivate::ascii_E:
147 {
148 if (multiplier > 0) {
149 valid = false;
150 qCDebug(C_VALIDATOR).nospace()
151 << "ValdatorFileSize: Validation failed for " << value << ": "
152 << "unit symbol E already found";
153 } else {
154 multiplier = 6;
155 symbolPart.append(ch);
156 }
157 } break;
158 case ValidatorRulePrivate::ascii_Z:
159 {
160 if (multiplier > 0) {
161 valid = false;
162 qCDebug(C_VALIDATOR).nospace()
163 << "ValdatorFileSize: Validation failed for " << value << ": "
164 << "unit symbol Z already found";
165 } else {
166 multiplier = 7;
167 symbolPart.append(ch);
168 }
169 } break;
170 case ValidatorFileSizePrivate::ascii_Y:
171 {
172 if (multiplier > 0) {
173 valid = false;
174 qCDebug(C_VALIDATOR).nospace()
175 << "ValdatorFileSize: Validation failed for " << value << ": "
176 << "unit symbol Y already found";
177 } else {
178 multiplier = 8;
179 symbolPart.append(ch);
180 }
181 } break;
182 case ValidatorFileSizePrivate::ascii_I:
183 {
184 if ((multiplier == 0) || binary) {
185 valid = false;
186 qCDebug(C_VALIDATOR).nospace()
187 << "ValdatorFileSize: Validation failed for " << value << ": "
188 << "binary indicator I already found or no unit symbol given "
189 "before";
190 } else {
191 binary = true;
192 symbolPart.append(ch);
193 }
194 } break;
195 case ValidatorFileSizePrivate::ascii_B:
196 {
197 if (byteSignFound) {
198 valid = false;
199 qCDebug(C_VALIDATOR).nospace()
200 << "ValdatorFileSize: Validation failed for " << value << ": "
201 << "byte symbol B already found";
202 } else {
203 byteSignFound = true;
204 symbolPart.append(ch);
205 }
206 } break;
207 case ValidatorRulePrivate::asciiTab:
208 case ValidatorRulePrivate::asciiSpace:
209 break;
210 default:
211 valid = false;
212 qCDebug(C_VALIDATOR).nospace()
213 << "ValdatorFileSize: Validation failed for " << value << ": "
214 << "invalid character in symbol part";
215 break;
216 }
217 } else {
218 valid = false;
219 break;
220 }
221 }
222 } else {
223 break;
224 }
225 }
226
227 if ((option == OnlyBinary) && !binary) {
228 valid = false;
229 } else if ((option == OnlyDecimal) && binary) {
230 valid = false;
231 } else if (option == ForceBinary) {
232 binary = true;
233 } else if (option == ForceDecimal) {
234 binary = false;
235 }
236
237 if (valid) {
238 bool ok = false;
239 double size = locale.toDouble(digitPart, &ok);
240 if (!ok) {
241 valid = false;
242 } else {
243 if (multiplier > 0) {
244 const double _mult =
245 binary ? std::exp2(multiplier * 10) : std::pow(10.0, multiplier * 3);
246 size *= _mult;
247 }
248 if ((min >= 1.0) && (size < min)) {
249 valid = false;
250 }
251 if ((max >= 1.0) && (size > max)) {
252 valid = false;
253 }
254 if (valid && fileSize) {
255 *fileSize = size;
256 }
257 }
258 }
259
260 return valid;
261}
262
264{
265 ValidatorReturnType result;
266
267 Q_D(const ValidatorFileSize);
268
269 const QString v = value(params);
270
271 if (!v.isEmpty()) {
272
273 double min = -1;
274 double max = -1;
275 bool ok = true;
276 if (d->min.isValid()) {
277 min = d->extractDouble(c, params, d->min, &ok);
278 if (!ok) {
279 qCWarning(C_VALIDATOR).noquote()
280 << debugString(c) << "Invalid minimum size comparison data";
281 result.errorMessage = validationDataError(c, 0);
282 }
283 }
284
285 if (ok && d->max.isValid()) {
286 max = d->extractDouble(c, params, d->max, &ok);
287 if (!ok) {
288 qCWarning(C_VALIDATOR).noquote()
289 << debugString(c) << "Invalid maximum size comparison data";
290 result.errorMessage = validationDataError(c, 1);
291 }
292 }
293
294 if (ok) {
295 double size = 0;
296 if (ValidatorFileSize::validate(v, min, max, d->option, c->locale(), &size)) {
297 if (size < static_cast<double>(std::numeric_limits<qulonglong>::max())) {
298 result.value.setValue<qulonglong>(static_cast<qulonglong>(size + 0.5));
299 } else {
300 result.value.setValue(size);
301 }
302 } else {
303 result.errorMessage = validationError(c);
304 qCWarning(C_VALIDATOR).noquote()
305 << debugString(c) << v << "is not a valid data size string";
306 }
307 }
308
309 } else {
310 defaultValue(c, &result);
311 }
312
313 return result;
314}
315
316QString ValidatorFileSize::genericValidationError(Context *c, const QVariant &errorData) const
317{
318 QString error;
319 Q_D(const ValidatorFileSize);
320 Q_UNUSED(errorData)
321 const QString _label = label(c);
322 if (d->min.isValid() || d->max.isValid()) {
323 if (_label.isEmpty()) {
324 error = c->translate("Cutelyst::ValidatorFileSize",
325 "Invalid file size or file size not within the allowed limits.");
326 } else {
327 error = c->translate("Cutelyst::ValidatorFileSize",
328 "The value in the “%1” field is either not a valid file size or "
329 "not within the allowed limits.")
330 .arg(_label);
331 }
332 } else {
333 if (_label.isEmpty()) {
334 error = c->translate("Cutelyst::ValidatorFileSize", "Invalid file size.");
335 } else {
336 error = c->translate("Cutelyst::ValidatorFileSize",
337 "The “%1” field does not contain a valid file size.")
338 .arg(_label);
339 }
340 }
341
342 return error;
343}
344
345QString ValidatorFileSize::genericValidationDataError(Context *c, const QVariant &errorData) const
346{
347 QString error;
348
349 const QString _label = label(c);
350
351 const int sizeType = errorData.toInt();
352
353 if (sizeType == 0) { // minimum file size
354 if (_label.isEmpty()) {
355 error = c->translate("Cutelyst::ValidatorFileSize",
356 "The minimum file size comparison value is not valid.");
357 } else {
358 error = c->translate(
359 "Cutelyst::ValidatorFileSize",
360 "The minimum file size comparison value for the “%1” field is not valid.")
361 .arg(_label);
362 }
363 } else {
364 if (_label.isEmpty()) {
365 error = c->translate("Cutelyst::ValidatorFileSize",
366 "The maximum file size comparison value is not valid.");
367 } else {
368 error = c->translate(
369 "Cutelyst::ValidatorFileSize",
370 "The maximum file size comparison value for the “%1” field is not valid.")
371 .arg(_label);
372 }
373 }
374
375 return error;
376}
377
378void ValidatorFileSize::inputPattern(Context *c, const QString &stashKey)
379{
380 Q_ASSERT(c);
381 c->setStash(stashKey,
382 c->locale().textDirection() == Qt::LeftToRight
383 ? QStringLiteral("^\\d+[,.٫]?\\d*\\s*[KkMmGgTt]?[Ii]?[Bb]?")
384 : QStringLiteral("[KkMmGgTt]?[Ii]?[Bb]?\\s*\\d+[,.٫]?\\d*"));
385}
The Cutelyst Context.
Definition: context.h:38
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:211
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
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.
QString debugString(Context *c) const
Returns a string that can be used for debug output if validation fails.
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