cutelyst 4.8.0
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
validatoremail.cpp
1/*
2 * SPDX-FileCopyrightText: (C) 2017-2023 Matthias Fehring <mf@huessenbergnetz.de>
3 * SPDX-License-Identifier: BSD-3-Clause
4 */
5
6#include "validatoremail_p.h"
7
8#include <algorithm>
9#include <functional>
10
11#include <QDnsLookup>
12#include <QEventLoop>
13#include <QTimer>
14#include <QUrl>
15
16using namespace Cutelyst;
17using namespace Qt::Literals::StringLiterals;
18
19const QRegularExpression ValidatorEmailPrivate::ipv4Regex{
20 u"\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25["
21 "0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"_s};
22const QRegularExpression ValidatorEmailPrivate::ipv6PartRegex{u"^[0-9A-Fa-f]{0,4}$"_s};
23const QString ValidatorEmailPrivate::stringSpecials{u"()<>[]:;@\\,.\""_s};
24
26 Category threshold,
27 Options options,
28 const Cutelyst::ValidatorMessages &messages,
29 const QString &defValKey)
30 : ValidatorRule(*new ValidatorEmailPrivate(field, threshold, options, messages, defValKey))
31{
32}
33
35
37{
39
40 const QString v = value(params);
41
42 Q_D(const ValidatorEmail);
43
44 if (!v.isEmpty()) {
45
46 // QString email;
47 // const int atPos = v.lastIndexOf(QLatin1Char('@'));
48 // if (atPos > 0) {
49 // const QStringRef local = v.leftRef(atPos);
50 // const QString domain = v.mid(atPos + 1);
51 // bool asciiDomain = true;
52 // for (const QChar &ch : domain) {
53 // const ushort &uc = ch.unicode();
54 // if (uc > 127) {
55 // asciiDomain = false;
56 // break;
57 // }
58 // }
59
60 // if (asciiDomain) {
61 // email = v;
62 // } else {
63 // email = local + QLatin1Char('@') +
64 // QString::fromLatin1(QUrl::toAce(domain));
65 // }
66 // } else {
67 // email = v;
68 // }
69
70 ValidatorEmailDiagnoseStruct diag;
71
72 if (ValidatorEmailPrivate::checkEmail(v, d->options, d->threshold, &diag)) {
73 if (!diag.literal.isEmpty()) {
74 result.value.setValue<QString>(diag.localpart + QLatin1Char('@') + diag.literal);
75 } else {
76 result.value.setValue<QString>(diag.localpart + QLatin1Char('@') + diag.domain);
77 }
78 } else {
79 result.errorMessage =
81 }
82
83 result.extra = QVariant::fromValue<QList<Diagnose>>(diag.returnStatus);
84
85 } else {
86 defaultValue(c, &result);
87 }
88
89 return result;
90}
91
93{
94 QString error;
95
96 error = ValidatorEmail::diagnoseString(c, errorData.value<Diagnose>(), label(c));
97
98 return error;
99}
100
101bool ValidatorEmailPrivate::checkEmail(const QString &address,
102 ValidatorEmail::Options options,
103 ValidatorEmail::Category threshold,
104 ValidatorEmailDiagnoseStruct *diagnoseStruct)
105{
107
108 EmailPart context = ComponentLocalpart;
109 QList<EmailPart> contextStack{context};
110 EmailPart contextPrior = ComponentLocalpart;
111
112 QChar token;
113 QChar tokenPrior;
114
115 QString parseLocalPart;
116 QString parseDomain;
117 QString parseLiteral;
118 QMap<int, QString> atomListLocalPart;
119 QMap<int, QString> atomListDomain;
120 int elementCount = 0;
121 int elementLen = 0;
122 bool hypenFlag = false;
123 bool endOrDie = false;
124 int crlf_count = 0;
125
126 const bool checkDns = options.testFlag(ValidatorEmail::CheckDNS);
127 const bool allowUtf8Local = options.testFlag(ValidatorEmail::UTF8Local);
128 const bool allowIdn = options.testFlag(ValidatorEmail::AllowIDN);
129
130 QString email;
131 const qsizetype atPos = address.lastIndexOf(QLatin1Char('@'));
132 if (allowIdn) {
133 if (atPos > 0) {
134 const QString local = address.left(atPos);
135 const QString domain = address.mid(atPos + 1);
136 bool asciiDomain = true;
137 for (const QChar &ch : domain) {
138 const ushort &uc = ch.unicode();
139 if (uc > ValidatorEmailPrivate::asciiEnd) {
140 asciiDomain = false;
141 break;
142 }
143 }
144
145 if (asciiDomain) {
146 email = address;
147 } else {
148 email = local + QLatin1Char('@') + QString::fromLatin1(QUrl::toAce(domain));
149 }
150 } else {
151 email = address;
152 }
153 } else {
154 email = address;
155 }
156
157 const qsizetype rawLength = email.length();
158
159 for (int i = 0; i < rawLength; i++) {
160 token = email[i];
161
162 switch (context) {
163 //-------------------------------------------------------------
164 // local-part
165 //-------------------------------------------------------------
166 case ComponentLocalpart:
167 {
168 // https://tools.ietf.org/html/rfc5322#section-3.4.1
169 // local-part = dot-atom / quoted-string / obs-local-part
170 //
171 // dot-atom = [CFWS] dot-atom-text [CFWS]
172 //
173 // dot-atom-text = 1*atext *("." 1*atext)
174 //
175 // quoted-string = [CFWS]
176 // DQUOTE *([FWS] qcontent) [FWS] DQUOTE
177 // [CFWS]
178 //
179 // obs-local-part = word *("." word)
180 //
181 // word = atom / quoted-string
182 //
183 // atom = [CFWS] 1*atext [CFWS]
184
185 if (token == QLatin1Char('(')) { // comment
186 if (elementLen == 0) {
187 // Comments are OK at the beginning of an element
188 returnStatus.push_back((elementCount == 0) ? ValidatorEmail::CFWSComment
190 } else {
192 endOrDie = true; // We can't start a comment in the middle of an element, so
193 // this better be the end
194 }
195
196 contextStack.push_back(context);
197 context = ContextComment;
198 } else if (token == QLatin1Char('.')) { // Next dot-atom element
199 if (elementLen == 0) {
200 // Another dot, already?
201 returnStatus.push_back((elementCount == 0)
204 } else {
205 // The entire local part can be a quoted string for RFC 5321
206 // If it's just one atom that is quoten then it's an RFC 5322 obsolete form
207 if (endOrDie) {
209 }
210 }
211
212 endOrDie = false; // CFWS & quoted strings are OK again now we're at the beginning
213 // of an element (although they are obsolete forms)
214 elementLen = 0;
215 elementCount++;
216 parseLocalPart += token;
217 atomListLocalPart[elementCount] = QString();
218 } else if (token == QLatin1Char('"')) {
219 if (elementLen == 0) {
220 // The entire local-part can be a quoted string for RFC 5321
221 // If it's just one atom that is quoted then it's an RFC 5322 obsolete form
222 returnStatus.push_back((elementCount == 0)
225
226 parseLocalPart += token;
227 atomListLocalPart[elementCount] += token;
228 elementLen++;
229 endOrDie = true; // quoted string must be the entire element
230 contextStack.push_back(context);
231 context = ContextQuotedString;
232 } else {
233 returnStatus.push_back(ValidatorEmail::ErrorExpectingAText); // Fatal error
234 }
235 } else if ((token == QChar(QChar::CarriageReturn)) || (token == QChar(QChar::Space)) ||
236 (token == QChar(QChar::Tabulation))) { // Folding White Space
237 if ((token == QChar(QChar::CarriageReturn)) &&
238 ((++i == rawLength) || (email[i] != QChar(QChar::LineFeed)))) {
240 break;
241 }
242
243 if (elementLen == 0) {
244 returnStatus.push_back((elementCount == 0) ? ValidatorEmail::CFWSFWS
246 } else {
247 endOrDie = true; // We can't start FWS in the middle of an element, so this
248 // better be the end
249 }
250
251 contextStack.push_back(context);
252 context = ContextFWS;
253 tokenPrior = token;
254 } else if (token == QLatin1Char('@')) {
255 // At this point we should have a valid local part
256 if (contextStack.size() != 1) {
258 qCCritical(C_VALIDATOR) << "ValidatorEmail: Unexpected item on context stack";
259 break;
260 }
261
262 if (parseLocalPart.isEmpty()) {
263 returnStatus.push_back(ValidatorEmail::ErrorNoLocalPart); // Fatal error
264 } else if (elementLen == 0) {
265 returnStatus.push_back(ValidatorEmail::ErrorDotEnd); // Fatal Error
266 } else if (parseLocalPart.size() > ValidatorEmailPrivate::maxLocalPartLength) {
267 // https://tools.ietf.org/html/rfc5321#section-4.5.3.1.1
268 // The maximum total length of a user name or other local-part is 64
269 // octets.
271 } else if ((contextPrior == ContextComment) || (contextPrior == ContextFWS)) {
272 // https://tools.ietf.org/html/rfc5322#section-3.4.1
273 // Comments and folding white space
274 // SHOULD NOT be used around the "@" in the addr-spec.
275 //
276 // https://tools.ietf.org/html/rfc2119
277 // 4. SHOULD NOT This phrase, or the phrase "NOT RECOMMENDED" mean that
278 // there may exist valid reasons in particular circumstances when the
279 // particular behavior is acceptable or even useful, but the full
280 // implications should be understood and the case carefully weighed
281 // before implementing any behavior described with this label.
283 }
284
285 context = ComponentDomain;
286 contextStack.clear();
287 contextStack.push_back(context);
288 elementCount = 0;
289 elementLen = 0;
290 endOrDie = false;
291
292 } else { // atext
293 // https://tools.ietf.org/html/rfc5322#section-3.2.3
294 // atext = ALPHA / DIGIT / ; Printable US-ASCII
295 // "!" / "#" / ; characters not including
296 // "$" / "%" / ; specials. Used for atoms.
297 // "&" / "'" /
298 // "*" / "+" /
299 // "-" / "/" /
300 // "=" / "?" /
301 // "^" / "_" /
302 // "`" / "{" /
303 // "|" / "}" /
304 //
305 if (endOrDie) {
306 switch (contextPrior) {
307 case ContextComment:
308 case ContextFWS:
310 break;
311 case ContextQuotedString:
313 break;
314 default:
316 qCCritical(C_VALIDATOR)
317 << "ValidatorEmail: More atext found where none is allowed, "
318 "but unrecognizes prior context";
319 break;
320 }
321 } else {
322 contextPrior = context;
323 const char16_t uni = token.unicode();
324
325 if (!allowUtf8Local) {
326 if ((uni < ValidatorEmailPrivate::asciiExclamationMark) ||
327 (uni > ValidatorEmailPrivate::asciiTilde) ||
328 ValidatorEmailPrivate::stringSpecials.contains(token)) {
329 returnStatus.push_back(
331 }
332 } else {
333 if (!token.isLetterOrNumber()) {
334 if ((uni < ValidatorEmailPrivate::asciiExclamationMark) ||
335 (uni > ValidatorEmailPrivate::asciiTilde) ||
336 ValidatorEmailPrivate::stringSpecials.contains(token)) {
337 returnStatus.push_back(
339 }
340 }
341 }
342
343 parseLocalPart += token;
344 atomListLocalPart[elementCount] += token;
345 elementLen++;
346 }
347 }
348 } break;
349 //-----------------------------------------
350 // Domain
351 //-----------------------------------------
352 case ComponentDomain:
353 {
354 // https://tools.ietf.org/html/rfc5322#section-3.4.1
355 // domain = dot-atom / domain-literal / obs-domain
356 //
357 // dot-atom = [CFWS] dot-atom-text [CFWS]
358 //
359 // dot-atom-text = 1*atext *("." 1*atext)
360 //
361 // domain-literal = [CFWS] "[" *([FWS] dtext) [FWS] "]" [CFWS]
362 //
363 // dtext = %d33-90 / ; Printable US-ASCII
364 // %d94-126 / ; characters not including
365 // obs-dtext ; "[", "]", or "\"
366 //
367 // obs-domain = atom *("." atom)
368 //
369 // atom = [CFWS] 1*atext [CFWS]
370 // https://tools.ietf.org/html/rfc5321#section-4.1.2
371 // Mailbox = Local-part "@" ( Domain / address-literal )
372 //
373 // Domain = sub-domain *("." sub-domain)
374 //
375 // address-literal = "[" ( IPv4-address-literal /
376 // IPv6-address-literal /
377 // General-address-literal ) "]"
378 // ; See Section 4.1.3
379 // https://tools.ietf.org/html/rfc5322#section-3.4.1
380 // Note: A liberal syntax for the domain portion of addr-spec is
381 // given here. However, the domain portion contains addressing
382 // information specified by and used in other protocols (e.g.,
383 // [RFC1034], [RFC1035], [RFC1123], [RFC5321]). It is therefore
384 // incumbent upon implementations to conform to the syntax of
385 // addresses for the context in which they are used.
386 // is_email() author's note: it's not clear how to interpret this in
387 // the context of a general email address validator. The conclusion I
388 // have reached is this: "addressing information" must comply with
389 // RFC 5321 (and in turn RFC 1035), anything that is "semantically
390 // invisible" must comply only with RFC 5322.
391
392 if (token == QLatin1Char('(')) { // comment
393 if (elementLen == 0) {
394 // Comments at the start of the domain are deprecated in the text
395 // Comments at the start of a subdomain are obs-domain
396 // (https://tools.ietf.org/html/rfc5322#section-3.4.1)
397 returnStatus.push_back((elementCount == 0)
400 } else {
402 endOrDie = true; // We can't start a comment in the middle of an element, so
403 // this better be the end
404 }
405
406 contextStack.push_back(context);
407 context = ContextComment;
408 } else if (token == QLatin1Char('.')) { // next dot-atom element
409 if (elementLen == 0) {
410 // another dot, already?
411 returnStatus.push_back((elementCount == 0)
414 } else if (hypenFlag) {
415 // Previous subdomain ended in a hyphen
416 returnStatus.push_back(ValidatorEmail::ErrorDomainHyphenEnd); // fatal error
417 } else {
418 // Nowhere in RFC 5321 does it say explicitly that the
419 // domain part of a Mailbox must be a valid domain according
420 // to the DNS standards set out in RFC 1035, but this *is*
421 // implied in several places. For instance, wherever the idea
422 // of host routing is discussed the RFC says that the domain
423 // must be looked up in the DNS. This would be nonsense unless
424 // the domain was designed to be a valid DNS domain. Hence we
425 // must conclude that the RFC 1035 restriction on label length
426 // also applies to RFC 5321 domains.
427 //
428 // https://tools.ietf.org/html/rfc1035#section-2.3.4
429 // labels 63 octets or less
430 if (elementLen > ValidatorEmailPrivate::maxDnsLabelLength) {
432 }
433 }
434
435 endOrDie = false; // CFWS is OK again now we're at the beginning of an element
436 // (although it may be obsolete CFWS)
437 elementLen = 0;
438 elementCount++;
439 atomListDomain[elementCount] = QString();
440 parseDomain += token;
441
442 } else if (token == QLatin1Char('[')) { // Domain literal
443 if (parseDomain.isEmpty()) {
444 endOrDie = true; // domain literal must be the only component
445 elementLen++;
446 contextStack.push_back(context);
447 context = ComponentLiteral;
448 parseDomain += token;
449 atomListDomain[elementCount] += token;
450 parseLiteral = QString();
451 } else {
452 returnStatus.push_back(ValidatorEmail::ErrorExpectingAText); // Fatal error
453 }
454 } else if ((token == QChar(QChar::CarriageReturn)) || (token == QChar(QChar::Space)) ||
455 (token == QChar(QChar::Tabulation))) { // Folding White Space
456 if ((token == QChar(QChar::CarriageReturn)) &&
457 ((++i == rawLength) || email[i] != QChar(QChar::LineFeed))) {
458 returnStatus.push_back(ValidatorEmail::ErrorCRnoLF); // Fatal error
459 break;
460 }
461
462 if (elementLen == 0) {
463 returnStatus.push_back((elementCount == 0)
466 } else {
468 endOrDie = true; // We can't start FWS in the middle of an element, so this
469 // better be the end
470 }
471
472 contextStack.push_back(context);
473 context = ContextFWS;
474 tokenPrior = token;
475
476 } else { // atext
477 // RFC 5322 allows any atext...
478 // https://tools.ietf.org/html/rfc5322#section-3.2.3
479 // atext = ALPHA / DIGIT / ; Printable US-ASCII
480 // "!" / "#" / ; characters not including
481 // "$" / "%" / ; specials. Used for atoms.
482 // "&" / "'" /
483 // "*" / "+" /
484 // "-" / "/" /
485 // "=" / "?" /
486 // "^" / "_" /
487 // "`" / "{" /
488 // "|" / "}" /
489 // "~"
490 // But RFC 5321 only allows letter-digit-hyphen to comply with DNS rules (RFCs 1034
491 // & 1123) https://tools.ietf.org/html/rfc5321#section-4.1.2
492 // sub-domain = Let-dig [Ldh-str]
493 //
494 // Let-dig = ALPHA / DIGIT
495 //
496 // Ldh-str = *( ALPHA / DIGIT / "-" ) Let-dig
497 //
498
499 if (endOrDie) {
500 // We have encountered atext where it is no longer valid
501 switch (contextPrior) {
502 case ContextComment:
503 case ContextFWS:
505 break;
506 case ComponentLiteral:
508 break;
509 default:
511 qCCritical(C_VALIDATOR)
512 << "ValidatorEmail: More atext found where none is allowed, but"
513 << "unrecognised prior context.";
514 break;
515 }
516 }
517
518 const char16_t uni = token.unicode();
519 hypenFlag = false; // Assume this token isn't a hyphen unless we discover it is
520
521 if ((uni < ValidatorEmailPrivate::asciiExclamationMark) ||
522 (uni > ValidatorEmailPrivate::asciiTilde) ||
523 ValidatorEmailPrivate::stringSpecials.contains(token)) {
524 returnStatus.push_back(ValidatorEmail::ErrorExpectingAText); // Fatal error
525 } else if (token == QLatin1Char('-')) {
526 if (elementLen == 0) {
527 // Hyphens can't be at the beggining of a subdomain
528 returnStatus.push_back(
530 }
531 hypenFlag = true;
532 } else if (!(((uni >= ValidatorRulePrivate::ascii_0) &&
533 (uni <= ValidatorRulePrivate::ascii_9)) ||
534 ((uni >= ValidatorRulePrivate::ascii_A) &&
535 (uni <= ValidatorRulePrivate::ascii_Z)) ||
536 ((uni >= ValidatorRulePrivate::ascii_a) &&
537 (uni <= ValidatorRulePrivate::ascii_z)))) {
538 // NOt an RFC 5321 subdomain, but still ok by RFC 5322
540 }
541
542 parseDomain += token;
543 atomListDomain[elementCount] += token;
544 elementLen++;
545 }
546 } break;
547 //-------------------------------------------------------------
548 // Domain literal
549 //-------------------------------------------------------------
550 case ComponentLiteral:
551 {
552 // https://tools.ietf.org/html/rfc5322#section-3.4.1
553 // domain-literal = [CFWS] "[" *([FWS] dtext) [FWS] "]" [CFWS]
554 //
555 // dtext = %d33-90 / ; Printable US-ASCII
556 // %d94-126 / ; characters not including
557 // obs-dtext ; "[", "]", or "\"
558 //
559 // obs-dtext = obs-NO-WS-CTL / quoted-pair
560 if (token == QLatin1Char(']')) { // End of domain literal
561 if (static_cast<int>(
562 *std::max_element(returnStatus.constBegin(), returnStatus.constEnd())) <
563 static_cast<int>(ValidatorEmail::Deprecated)) {
564 // Could be a valid RFC 5321 address literal, so let's check
565
566 // https://tools.ietf.org/html/rfc5321#section-4.1.2
567 // address-literal = "[" ( IPv4-address-literal /
568 // IPv6-address-literal /
569 // General-address-literal ) "]"
570 // ; See Section 4.1.3
571 //
572 // https://tools.ietf.org/html/rfc5321#section-4.1.3
573 // IPv4-address-literal = Snum 3("." Snum)
574 //
575 // IPv6-address-literal = "IPv6:" IPv6-addr
576 //
577 // General-address-literal = Standardized-tag ":" 1*dcontent
578 //
579 // Standardized-tag = Ldh-str
580 // ; Standardized-tag MUST be specified in a
581 // ; Standards-Track RFC and registered with IANA
582 //
583 // dcontent = %d33-90 / ; Printable US-ASCII
584 // %d94-126 ; excl. "[", "\", "]"
585 //
586 // Snum = 1*3DIGIT
587 // ; representing a decimal integer
588 // ; value in the range 0 through 255
589 //
590 // IPv6-addr = IPv6-full / IPv6-comp / IPv6v4-full / IPv6v4-comp
591 //
592 // IPv6-hex = 1*4HEXDIG
593 //
594 // IPv6-full = IPv6-hex 7(":" IPv6-hex)
595 //
596 // IPv6-comp = [IPv6-hex *5(":" IPv6-hex)] "::"
597 // [IPv6-hex *5(":" IPv6-hex)]
598 // ; The "::" represents at least 2 16-bit groups of
599 // ; zeros. No more than 6 groups in addition to the
600 // ; "::" may be present.
601 //
602 // IPv6v4-full = IPv6-hex 5(":" IPv6-hex) ":" IPv4-address-literal
603 //
604 // IPv6v4-comp = [IPv6-hex *3(":" IPv6-hex)] "::"
605 // [IPv6-hex *3(":" IPv6-hex) ":"]
606 // IPv4-address-literal
607 // ; The "::" represents at least 2 16-bit groups of
608 // ; zeros. No more than 4 groups in addition to the
609 // ; "::" and IPv4-address-literal may be present.
610 //
611 // is_email() author's note: We can't use ip2long() to validate
612 // IPv4 addresses because it accepts abbreviated addresses
613 // (xxx.xxx.xxx), expanding the last group to complete the address.
614 // filter_var() validates IPv6 address inconsistently (up to PHP 5.3.3
615 // at least) -- see https://bugs.php.net/bug.php?id=53236 for example
616
617 int maxGroups = 8; // NOLINT(cppcoreguidelines-avoid-magic-numbers)
618 qsizetype index = -1;
619 QString addressLiteral = parseLiteral;
620
621 const QRegularExpressionMatch ipv4Match =
622 ValidatorEmailPrivate::ipv4Regex.match(addressLiteral);
623 if (ipv4Match.hasMatch()) {
624 index = addressLiteral.lastIndexOf(ipv4Match.captured());
625 if (index != 0) {
626 addressLiteral =
627 addressLiteral.mid(0, index) +
628 QLatin1String(
629 "0:0"); // Convert IPv4 part to IPv6 format for further testing
630 }
631 }
632
633 if (index == 0) {
634 // Nothing there except a valid IPv4 address, so...
636 } else if (QString::compare(
637 addressLiteral.left(5),
638 QLatin1String(
639 "IPv6:")) != // NOLINT(cppcoreguidelines-avoid-magic-numbers)
640 0) {
642 } else {
643 const QString ipv6 = addressLiteral.mid(5);
644 const QStringList matchesIP = ipv6.split(QLatin1Char(':'));
645 qsizetype groupCount = matchesIP.size();
646 index = ipv6.indexOf(QLatin1String("::"));
647
648 if (index < 0) {
649 // We need exactly the right number of groups
650 if (groupCount != maxGroups) {
652 }
653 } else {
654 if (index != ipv6.lastIndexOf(QLatin1String("::"))) {
656 } else {
657 if ((index == 0) || (index == (ipv6.length() - 2))) {
658 maxGroups++;
659 }
660
661 if (groupCount > maxGroups) {
663 } else if (groupCount == maxGroups) {
664 returnStatus.push_back(
665 ValidatorEmail::RFC5321IPv6Deprecated); // Eliding a single
666 // "::"
667 }
668 }
669 }
670
671 if ((ipv6.size() == 1 && ipv6[0] == QLatin1Char(':')) ||
672 (ipv6[0] == QLatin1Char(':') && ipv6[1] != QLatin1Char(':'))) {
673 returnStatus.push_back(
674 ValidatorEmail::RFC5322IPv6ColonStart); // Address starts with a
675 // single colon
676 } else if (ipv6.right(2).at(1) == QLatin1Char(':') &&
677 ipv6.right(2).at(0) != QLatin1Char(':')) {
678 returnStatus.push_back(
679 ValidatorEmail::RFC5322IPv6ColonEnd); // Address ends with a single
680 // colon
681 } else {
682 int unmatchedChars = 0;
683 for (const QString &ip : matchesIP) {
684 if (!ip.contains(ValidatorEmailPrivate::ipv6PartRegex)) {
685 unmatchedChars++;
686 }
687 }
688 if (unmatchedChars != 0) {
690 } else {
692 }
693 }
694 }
695
696 } else {
698 }
699
700 parseDomain += token;
701 atomListDomain[elementCount] += token;
702 elementLen++;
703 contextPrior = context;
704 context = contextStack.takeLast();
705 } else if (token == QLatin1Char('\\')) {
707 contextStack.push_back(context);
708 context = ContextQuotedPair;
709 } else if ((token == QChar(QChar::CarriageReturn)) || (token == QChar(QChar::Space)) ||
710 (token == QChar(QChar::Tabulation))) { // Folding White Space
711 if ((token == QChar(QChar::CarriageReturn)) &&
712 ((++i == rawLength) || (email[i] != QChar(QChar::LineFeed)))) {
713 returnStatus.push_back(ValidatorEmail::ErrorCRnoLF); // Fatal error
714 break;
715 }
716
718 contextStack.push_back(context);
719 context = ContextFWS;
720 tokenPrior = token;
721
722 } else { // dtext
723 // https://tools.ietf.org/html/rfc5322#section-3.4.1
724 // dtext = %d33-90 / ; Printable US-ASCII
725 // %d94-126 / ; characters not including
726 // obs-dtext ; "[", "]", or "\"
727 //
728 // obs-dtext = obs-NO-WS-CTL / quoted-pair
729 //
730 // obs-NO-WS-CTL = %d1-8 / ; US-ASCII control
731 // %d11 / ; characters that do not
732 // %d12 / ; include the carriage
733 // %d14-31 / ; return, line feed, and
734 // %d127 ; white space characters
735 const char16_t uni = token.unicode();
736
737 // CR, LF, SP & HTAB have already been parsed above
738 if ((uni > ValidatorEmailPrivate::asciiEnd) || (uni == 0) ||
739 (uni == QLatin1Char('[').unicode())) {
740 returnStatus.push_back(ValidatorEmail::ErrorExpectingDText); // Fatal error
741 break;
742 } else if ((uni < ValidatorEmailPrivate::asciiExclamationMark) ||
743 (uni == ValidatorEmailPrivate::asciiEnd)) {
745 }
746
747 parseLiteral += token;
748 parseDomain += token;
749 atomListDomain[elementCount] += token;
750 elementLen++;
751 }
752 } break;
753 //-------------------------------------------------------------
754 // Quoted string
755 //-------------------------------------------------------------
756 case ContextQuotedString:
757 {
758 // https://tools.ietf.org/html/rfc5322#section-3.2.4
759 // quoted-string = [CFWS]
760 // DQUOTE *([FWS] qcontent) [FWS] DQUOTE
761 // [CFWS]
762 //
763 // qcontent = qtext / quoted-pair
764 if (token == QLatin1Char('\\')) { // Quoted pair
765 contextStack.push_back(context);
766 context = ContextQuotedPair;
767 } else if ((token == QChar(QChar::CarriageReturn)) ||
768 (token == QChar(QChar::Tabulation))) { // Folding White Space
769 // Inside a quoted string, spaces are allowed as regular characters.
770 // It's only FWS if we include HTAB or CRLF
771 if ((token == QChar(QChar::CarriageReturn)) &&
772 ((++i == rawLength) || (email[i] != QChar(QChar::LineFeed)))) {
774 break;
775 }
776
777 // https://tools.ietf.org/html/rfc5322#section-3.2.2
778 // Runs of FWS, comment, or CFWS that occur between lexical tokens in a
779 // structured header field are semantically interpreted as a single
780 // space character.
781
782 // https://tools.ietf.org/html/rfc5322#section-3.2.4
783 // the CRLF in any FWS/CFWS that appears within the quoted-string [is]
784 // semantically "invisible" and therefore not part of the quoted-string
785
786 parseLocalPart += QChar(QChar::Space);
787 atomListLocalPart[elementCount] += QChar(QChar::Space);
788 elementLen++;
789
791 contextStack.push_back(context);
792 context = ContextFWS;
793 tokenPrior = token;
794 } else if (token == QLatin1Char('"')) { // end of quoted string
795 parseLocalPart += token;
796 atomListLocalPart[elementCount] += token;
797 elementLen++;
798 contextPrior = context;
799 context = contextStack.takeLast();
800 } else { // qtext
801 // https://tools.ietf.org/html/rfc5322#section-3.2.4
802 // qtext = %d33 / ; Printable US-ASCII
803 // %d35-91 / ; characters not including
804 // %d93-126 / ; "\" or the quote character
805 // obs-qtext
806 //
807 // obs-qtext = obs-NO-WS-CTL
808 //
809 // obs-NO-WS-CTL = %d1-8 / ; US-ASCII control
810 // %d11 / ; characters that do not
811 // %d12 / ; include the carriage
812 // %d14-31 / ; return, line feed, and
813 // %d127 ; white space characters
814 const char16_t uni = token.unicode();
815
816 if (!allowUtf8Local) {
817 if ((uni > ValidatorEmailPrivate::asciiEnd) || (uni == 0) ||
818 (uni == ValidatorEmailPrivate::asciiLF)) {
819 returnStatus.push_back(ValidatorEmail::ErrorExpectingQText); // Fatal error
820 } else if ((uni < ValidatorRulePrivate::asciiSpace) ||
821 (uni == ValidatorEmailPrivate::asciiEnd)) {
823 }
824 } else {
825 if (!token.isLetterOrNumber()) {
826 if ((uni > ValidatorEmailPrivate::asciiEnd) || (uni == 0) ||
827 (uni == ValidatorEmailPrivate::asciiLF)) {
828 returnStatus.push_back(
830 } else if ((uni < ValidatorRulePrivate::asciiSpace) ||
831 (uni == ValidatorEmailPrivate::asciiEnd)) {
833 }
834 }
835 }
836
837 parseLocalPart += token;
838 atomListLocalPart[elementCount] += token;
839 elementLen++;
840 }
841
842 // https://tools.ietf.org/html/rfc5322#section-3.4.1
843 // If the
844 // string can be represented as a dot-atom (that is, it contains no
845 // characters other than atext characters or "." surrounded by atext
846 // characters), then the dot-atom form SHOULD be used and the quoted-
847 // string form SHOULD NOT be used.
848 // To do
849 } break;
850 //-------------------------------------------------------------
851 // Quoted pair
852 //-------------------------------------------------------------
853 case ContextQuotedPair:
854 {
855 // https://tools.ietf.org/html/rfc5322#section-3.2.1
856 // quoted-pair = ("\" (VCHAR / WSP)) / obs-qp
857 //
858 // VCHAR = %d33-126 ; visible (printing) characters
859 // WSP = SP / HTAB ; white space
860 //
861 // obs-qp = "\" (%d0 / obs-NO-WS-CTL / LF / CR)
862 //
863 // obs-NO-WS-CTL = %d1-8 / ; US-ASCII control
864 // %d11 / ; characters that do not
865 // %d12 / ; include the carriage
866 // %d14-31 / ; return, line feed, and
867 // %d127 ; white space characters
868 //
869 // i.e. obs-qp = "\" (%d0-8, %d10-31 / %d127)
870
871 const char16_t uni = token.unicode();
872
873 if (uni > ValidatorEmailPrivate::asciiEnd) {
874 returnStatus.push_back(ValidatorEmail::ErrorExpectingQpair); // Fatal error
875 } else if (((uni < ValidatorEmailPrivate::asciiUS) &&
876 (uni != ValidatorRulePrivate::asciiTab)) ||
877 (uni == ValidatorEmailPrivate::asciiEnd)) {
879 }
880
881 // At this point we know where this qpair occurred so
882 // we could check to see if the character actually
883 // needed to be quoted at all.
884 // https://tools.ietf.org/html/rfc5321#section-4.1.2
885 // the sending system SHOULD transmit the
886 // form that uses the minimum quoting possible.
887
888 contextPrior = context;
889 context = contextStack.takeLast();
890
891 switch (context) {
892 case ContextComment:
893 break;
894 case ContextQuotedString:
895 parseLocalPart += QLatin1Char('\\');
896 parseLocalPart += token;
897 atomListLocalPart[elementCount] += QLatin1Char('\\');
898 atomListLocalPart[elementCount] += token;
899 elementLen += 2; // The maximum sizes specified by RFC 5321 are octet counts, so we
900 // must include the backslash
901 break;
902 case ComponentLiteral:
903 parseDomain += QLatin1Char('\\');
904 parseDomain += token;
905 atomListDomain[elementCount] += QLatin1Char('\\');
906 atomListDomain[elementCount] += token;
907 elementLen += 2; // The maximum sizes specified by RFC 5321 are octet counts, so we
908 // must include the backslash
909 break;
910 default:
912 qCCritical(C_VALIDATOR)
913 << "ValidatorEmail: Quoted pair logic invoked in an invalid context.";
914 break;
915 }
916 } break;
917 //-------------------------------------------------------------
918 // Comment
919 //-------------------------------------------------------------
920 case ContextComment:
921 {
922 // https://tools.ietf.org/html/rfc5322#section-3.2.2
923 // comment = "(" *([FWS] ccontent) [FWS] ")"
924 //
925 // ccontent = ctext / quoted-pair / comment
926 if (token == QLatin1Char('(')) { // netsted comment
927 // nested comments are OK
928 contextStack.push_back(context);
929 context = ContextComment;
930 } else if (token == QLatin1Char(')')) {
931 contextPrior = context;
932 context = contextStack.takeLast();
933
934 // https://tools.ietf.org/html/rfc5322#section-3.2.2
935 // Runs of FWS, comment, or CFWS that occur between lexical tokens in a
936 // structured header field are semantically interpreted as a single
937 // space character.
938 //
939 // is_email() author's note: This *cannot* mean that we must add a
940 // space to the address wherever CFWS appears. This would result in
941 // any addr-spec that had CFWS outside a quoted string being invalid
942 // for RFC 5321.
943 // if (($context === ISEMAIL_COMPONENT_LOCALPART) ||
944 //($context === ISEMAIL_COMPONENT_DOMAIN)) {
945 // $parsedata[$context] .=
946 // ISEMAIL_STRING_SP;
947 // $atomlist[$context][$element_count]
948 // .= ISEMAIL_STRING_SP; $element_len++;
949 // }
950 } else if (token == QLatin1Char('\\')) { // Quoted pair
951 contextStack.push_back(context);
952 context = ContextQuotedPair;
953 } else if ((token == QChar(QChar::CarriageReturn)) || (token == QChar(QChar::Space)) ||
954 (token == QChar(QChar::Tabulation))) { // Folding White Space
955 if ((token == QChar(QChar::CarriageReturn)) &&
956 ((++i == rawLength) || (email[i] != QChar(QChar::LineFeed)))) {
958 break;
959 }
960
962 contextStack.push_back(context);
963 context = ContextFWS;
964 tokenPrior = token;
965 } else { // ctext
966 // https://tools.ietf.org/html/rfc5322#section-3.2.3
967 // ctext = %d33-39 / ; Printable US-ASCII
968 // %d42-91 / ; characters not including
969 // %d93-126 / ; "(", ")", or "\"
970 // obs-ctext
971 //
972 // obs-ctext = obs-NO-WS-CTL
973 //
974 // obs-NO-WS-CTL = %d1-8 / ; US-ASCII control
975 // %d11 / ; characters that do not
976 // %d12 / ; include the carriage
977 // %d14-31 / ; return, line feed, and
978 // %d127 ; white space characters
979
980 const ushort uni = token.unicode();
981
982 if ((uni > ValidatorEmailPrivate::asciiEnd) || (uni == 0) ||
983 (uni == ValidatorEmailPrivate::asciiLF)) {
984 returnStatus.push_back(ValidatorEmail::ErrorExpectingCText); // Fatal error
985 break;
986 } else if ((uni < ValidatorRulePrivate::asciiSpace) ||
987 (uni == ValidatorEmailPrivate::asciiEnd)) {
989 }
990 }
991 } break;
992 //-------------------------------------------------------------
993 // Folding White Space
994 //-------------------------------------------------------------
995 case ContextFWS:
996 {
997 // https://tools.ietf.org/html/rfc5322#section-3.2.2
998 // FWS = ([*WSP CRLF] 1*WSP) / obs-FWS
999 // ; Folding white space
1000 // But note the erratum:
1001 // https://www.rfc-editor.org/errata_search.php?rfc=5322&eid=1908:
1002 // In the obsolete syntax, any amount of folding white space MAY be
1003 // inserted where the obs-FWS rule is allowed. This creates the
1004 // possibility of having two consecutive "folds" in a line, and
1005 // therefore the possibility that a line which makes up a folded header
1006 // field could be composed entirely of white space.
1007 //
1008 // obs-FWS = 1*([CRLF] WSP)
1009 if (tokenPrior == QChar(QChar::CarriageReturn)) {
1010 if (token == QChar(QChar::CarriageReturn)) {
1011 returnStatus.push_back(ValidatorEmail::ErrorFWSCRLFx2); // Fatal error
1012 break;
1013 }
1014
1015 if (crlf_count > 0) {
1016 if (++crlf_count > 1) {
1017 returnStatus.push_back(
1018 ValidatorEmail::DeprecatedFWS); // Multiple folds = obsolete FWS
1019 }
1020 } else {
1021 crlf_count = 1;
1022 }
1023 }
1024
1025 if (token == QChar(QChar::CarriageReturn)) {
1026 if ((++i == rawLength) || (email[i] != QChar(QChar::LineFeed))) {
1028 break;
1029 }
1030 } else if ((token != QChar(QChar::Space)) && (token != QChar(QChar::Tabulation))) {
1031 if (tokenPrior == QChar(QChar::CarriageReturn)) {
1032 returnStatus.push_back(ValidatorEmail::ErrorFWSCRLFEnd); // Fatal error
1033 break;
1034 }
1035
1036 if (crlf_count > 0) {
1037 crlf_count = 0;
1038 }
1039
1040 contextPrior = context;
1041 context = contextStack.takeLast(); // End of FWS
1042
1043 // https://tools.ietf.org/html/rfc5322#section-3.2.2
1044 // Runs of FWS, comment, or CFWS that occur between lexical tokens in a
1045 // structured header field are semantically interpreted as a single
1046 // space character.
1047 //
1048 // is_email() author's note: This *cannot* mean that we must add a
1049 // space to the address wherever CFWS appears. This would result in
1050 // any addr-spec that had CFWS outside a quoted string being invalid
1051 // for RFC 5321.
1052 // if (($context === ISEMAIL_COMPONENT_LOCALPART) ||
1053 //($context === ISEMAIL_COMPONENT_DOMAIN)) {
1054 // $parsedata[$context] .=
1055 // ISEMAIL_STRING_SP;
1056 // $atomlist[$context][$element_count]
1057 // .= ISEMAIL_STRING_SP; $element_len++;
1058 // }
1059
1060 i--; // Look at this token again in the parent context
1061 }
1062
1063 tokenPrior = token;
1064 } break;
1065 default:
1067 qCCritical(C_VALIDATOR) << "ValidatorEmail: Unknown context";
1068 break;
1069 }
1070
1071 if (static_cast<int>(
1072 *std::max_element(returnStatus.constBegin(), returnStatus.constEnd())) >
1073 static_cast<int>(ValidatorEmail::RFC5322)) {
1074 break;
1075 }
1076 }
1077
1078 // Some simple final tests
1079 if (static_cast<int>(*std::max_element(returnStatus.constBegin(), returnStatus.constEnd())) <
1080 static_cast<int>(ValidatorEmail::RFC5322)) {
1081 if (context == ContextQuotedString) {
1083 } else if (context == ContextQuotedPair) {
1085 } else if (context == ContextComment) {
1087 } else if (context == ComponentLiteral) {
1089 } else if (token == QChar(QChar::CarriageReturn)) {
1091 } else if (parseDomain.isEmpty()) {
1093 } else if (elementLen == 0) {
1095 } else if (hypenFlag) {
1097 } else if (parseDomain.size() > ValidatorEmailPrivate::maxDomainLength) {
1098 // https://tools.ietf.org/html/rfc5321#section-4.5.3.1.2
1099 // The maximum total length of a domain name or number is 255 octets.
1101 } else if ((parseLocalPart.size() + 1 + parseDomain.size()) >
1102 ValidatorEmailPrivate::maxMailboxLength) {
1103 // https://tools.ietf.org/html/rfc5321#section-4.1.2
1104 // Forward-path = Path
1105 //
1106 // Path = "<" [ A-d-l ":" ] Mailbox ">"
1107 //
1108 // https://tools.ietf.org/html/rfc5321#section-4.5.3.1.3
1109 // The maximum total length of a reverse-path or forward-path is 256
1110 // octets (including the punctuation and element separators).
1111 //
1112 // Thus, even without (obsolete) routing information, the Mailbox can
1113 // only be 254 characters long. This is confirmed by this verified
1114 // erratum to RFC 3696:
1115 //
1116 // https://www.rfc-editor.org/errata_search.php?rfc=3696&eid=1690
1117 // However, there is a restriction in RFC 2821 on the length of an
1118 // address in MAIL and RCPT commands of 254 characters. Since addresses
1119 // that do not fit in those fields are not normally useful, the upper
1120 // limit on address lengths should normally be considered to be 254.
1122 } else if (elementLen > ValidatorEmailPrivate::maxDnsLabelLength) {
1124 }
1125 }
1126
1127 // Check DNS?
1128 bool dnsChecked = false;
1129
1130 if (checkDns &&
1131 (static_cast<int>(*std::max_element(returnStatus.constBegin(), returnStatus.constEnd())) <
1132 static_cast<int>(threshold))) {
1133 // https://tools.ietf.org/html/rfc5321#section-2.3.5
1134 // Names that can
1135 // be resolved to MX RRs or address (i.e., A or AAAA) RRs (as discussed
1136 // in Section 5) are permitted, as are CNAME RRs whose targets can be
1137 // resolved, in turn, to MX or address RRs.
1138 //
1139 // https://tools.ietf.org/html/rfc5321#section-5.1
1140 // The lookup first attempts to locate an MX record associated with the
1141 // name. If a CNAME record is found, the resulting name is processed as
1142 // if it were the initial name. ... If an empty list of MXs is returned,
1143 // the address is treated as if it was associated with an implicit MX
1144 // RR, with a preference of 0, pointing to that host.
1145
1146 if (elementCount == 0) {
1147 parseDomain += QLatin1Char('.');
1148 }
1149
1150 QDnsLookup mxLookup(QDnsLookup::MX, parseDomain);
1151 QEventLoop mxLoop;
1153 QTimer::singleShot(ValidatorEmailPrivate::dnsLookupTimeout, &mxLookup, &QDnsLookup::abort);
1154 mxLookup.lookup();
1155 mxLoop.exec();
1156
1157 if ((mxLookup.error() == QDnsLookup::NoError) && !mxLookup.mailExchangeRecords().empty()) {
1158 dnsChecked = true;
1159 } else {
1161 QDnsLookup aLookup(QDnsLookup::A, parseDomain);
1162 QEventLoop aLoop;
1165 ValidatorEmailPrivate::dnsLookupTimeout, &aLookup, &QDnsLookup::abort);
1166 aLookup.lookup();
1167 aLoop.exec();
1168
1169 if ((aLookup.error() == QDnsLookup::NoError) && !aLookup.hostAddressRecords().empty()) {
1170 dnsChecked = true;
1171 } else {
1173 }
1174 }
1175 }
1176
1177 // Check for TLD addresses
1178 // -----------------------
1179 // TLD addresses are specifically allowed in RFC 5321 but they are
1180 // unusual to say the least. We will allocate a separate
1181 // status to these addresses on the basis that they are more likely
1182 // to be typos than genuine addresses (unless we've already
1183 // established that the domain does have an MX record)
1184 //
1185 // https://tools.ietf.org/html/rfc5321#section-2.3.5
1186 // In the case
1187 // of a top-level domain used by itself in an email address, a single
1188 // string is used without any dots. This makes the requirement,
1189 // described in more detail below, that only fully-qualified domain
1190 // names appear in SMTP transactions on the public Internet,
1191 // particularly important where top-level domains are involved.
1192 //
1193 // TLD format
1194 // ----------
1195 // The format of TLDs has changed a number of times. The standards
1196 // used by IANA have been largely ignored by ICANN, leading to
1197 // confusion over the standards being followed. These are not defined
1198 // anywhere, except as a general component of a DNS host name (a label).
1199 // However, this could potentially lead to 123.123.123.123 being a
1200 // valid DNS name (rather than an IP address) and thereby creating
1201 // an ambiguity. The most authoritative statement on TLD formats that
1202 // the author can find is in a (rejected!) erratum to RFC 1123
1203 // submitted by John Klensin, the author of RFC 5321:
1204 //
1205 // https://www.rfc-editor.org/errata_search.php?rfc=1123&eid=1353
1206 // However, a valid host name can never have the dotted-decimal
1207 // form #.#.#.#, since this change does not permit the highest-level
1208 // component label to start with a digit even if it is not all-numeric.
1209 if (!dnsChecked &&
1210 (static_cast<int>(*std::max_element(returnStatus.constBegin(), returnStatus.constEnd())) <
1211 static_cast<int>(ValidatorEmail::DNSWarn))) {
1212 if (elementCount == 0) {
1214 }
1215
1216 if (QStringLiteral("0123456789").contains(atomListDomain[elementCount][0])) {
1218 }
1219 }
1220
1221 if (returnStatus.size() != 1) {
1222 QList<ValidatorEmail::Diagnose> _rs;
1223 for (const ValidatorEmail::Diagnose dia : std::as_const(returnStatus)) {
1224 if (!_rs.contains(dia) && (dia != ValidatorEmail::ValidAddress)) {
1225 _rs.append(dia); // clazy:exclude=reserve-candidates
1226 }
1227 }
1228 returnStatus = _rs;
1229
1230 std::sort(returnStatus.begin(), returnStatus.end(), std::greater<>());
1231 }
1232
1233 const ValidatorEmail::Diagnose finalStatus = returnStatus.at(0);
1234
1235 if (diagnoseStruct) {
1236 diagnoseStruct->finalStatus = finalStatus;
1237 diagnoseStruct->returnStatus = returnStatus;
1238 diagnoseStruct->localpart = parseLocalPart;
1239 diagnoseStruct->domain = parseDomain;
1240 diagnoseStruct->literal = parseLiteral;
1241 }
1242
1243 return static_cast<int>(finalStatus) < static_cast<int>(threshold);
1244}
1245
1247{
1248 if (label.isEmpty()) {
1249 switch (diagnose) {
1250 case ValidAddress:
1251 //% "Address is valid. Please note that this does not mean that both the "
1252 //% "address and the domain actually exist. This address could be issued "
1253 //% "by the domain owner without breaking the rules of any RFCs."
1254 return c->qtTrId("cutelyst-valemail-diag-valid");
1255 case DnsWarnNoMxRecord:
1256 //% "Could not find an MX record for this address’ domain but an A record exists."
1257 return c->qtTrId("cutelyst-valemail-diag-nomx");
1258 case DnsWarnNoRecord:
1259 //% "Could neither find an MX record nor an A record for this address’ domain."
1260 return c->qtTrId("cutelyst-valemail-diag-noarec");
1261 case RFC5321TLD:
1262 //% "Address is valid but at a Top Level Domain."
1263 return c->qtTrId("cutelyst-valemail-diag-rfc5321tld");
1264 case RFC5321TLDNumeric:
1265 //% "Address is valid but the Top Level Domain begins with a number."
1266 return c->qtTrId("cutelyst-valemail-diag-rfc5321tldnumeric");
1268 //% "Address is valid but contains a quoted string."
1269 return c->qtTrId("cutelyst-valemail-diag-rfc5321quotedstring");
1271 //% "Address is valid but uses an IP address instead of a domain name."
1272 return c->qtTrId("cutelyst-valemail-diag-rfc5321addressliteral");
1274 //% "Address is valid but uses an IP address that contains a :: only "
1275 //% "eliding one zero group. All implementations must accept and be "
1276 //% "able to handle any legitimate RFC 4291 format."
1277 return c->qtTrId("cutelyst-valemail-diag-rfc5321ipv6deprecated");
1278 case CFWSComment:
1279 //% "Address contains comments."
1280 return c->qtTrId("cutelyst-valemail-diag-cfwscomment");
1281 case CFWSFWS:
1282 //% "Address contains folding white spaces like line breaks."
1283 return c->qtTrId("cutelyst-valemail-diag-cfwsfws");
1285 //% "The local part is in a deprecated form."
1286 return c->qtTrId("cutelyst-valemail-diag-deprecatedlocalpart");
1287 case DeprecatedFWS:
1288 //% "Address contains an obsolete form of folding white spaces."
1289 return c->qtTrId("cutelyst-valemail-diag-deprecatedfws");
1290 case DeprecatedQText:
1291 //% "A quoted string contains a deprecated character."
1292 return c->qtTrId("cutelyst-valemail-diag-deprecatedqtext");
1293 case DeprecatedQP:
1294 //% "A quoted pair contains a deprecated character."
1295 return c->qtTrId("cutelyst-valemail-diag-deprecatedqp");
1296 case DeprecatedComment:
1297 //% "Address contains a comment in a position that is deprecated."
1298 return c->qtTrId("cutelyst-valemail-diag-deprecatedcomment");
1299 case DeprecatedCText:
1300 //% "A comment contains a deprecated character."
1301 return c->qtTrId("cutelyst-valemail-diag-deprecatedctext");
1303 //% "Address contains a comment or folding white space around the @ sign."
1304 return c->qtTrId("cutelyst-valemail-diag-cfwsnearat");
1305 case RFC5322Domain:
1306 //% "Address is RFC 5322 compliant but contains domain characters that "
1307 //% "are not allowed by DNS."
1308 return c->qtTrId("cutelyst-valemail-diag-rfc5322domain");
1309 case RFC5322TooLong:
1310 //% "The address exceeds the maximum allowed length of %1 characters."
1311 return c->qtTrId("cutelyst-valemail-diag-rfc5322toolong")
1312 .arg(c->locale().toString(ValidatorEmailPrivate::maxMailboxLength));
1314 //% "The local part of the address exceeds the maximum allowed length "
1315 //% "of %1 characters."
1316 return c->qtTrId("cutelyst-valemail-diag-rfc5322localtoolong")
1317 .arg(c->locale().toString(ValidatorEmailPrivate::maxLocalPartLength));
1319 //% "The domain part exceeds the maximum allowed length of %1 characters."
1320 return c->qtTrId("cutelyst-valemail-diag-rfc5322domaintoolong")
1321 .arg(c->locale().toString(ValidatorEmailPrivate::maxDomainLength));
1323 //% "One of the labels/sections in the domain part exceeds the maximum allowed "
1324 //% "length of %1 characters."
1325 return c->qtTrId("cutelyst-valemail-diag-rfc5322labeltoolong")
1326 .arg(c->locale().toString(ValidatorEmailPrivate::maxDnsLabelLength));
1328 //% "The domain literal is not a valid RFC 5321 address literal."
1329 return c->qtTrId("cutelyst-valemail-diag-rfc5322domainliteral");
1331 //% "The domain literal is not a valid RFC 5321 domain literal and it "
1332 //% "contains obsolete characters."
1333 return c->qtTrId("cutelyst-valemail-diag-rfc5322domlitobsdtext");
1335 //% "The IPv6 literal address contains the wrong number of groups."
1336 return c->qtTrId("cutelyst-valemail-diag-rfc5322ipv6groupcount");
1338 //% "The IPv6 literal address contains too many :: sequences."
1339 return c->qtTrId("cutelyst-valemail-diag-rfc5322ipv62x2xcolon");
1340 case RFC5322IPv6BadChar:
1341 //% "The IPv6 address contains an illegal group of characters."
1342 return c->qtTrId("cutelyst-valemail-diag-rfc5322ipv6badchar");
1344 //% "The IPv6 address has too many groups."
1345 return c->qtTrId("cutelyst-valemail-diag-rfc5322ipv6maxgroups");
1347 //% "The IPv6 address starts with a single colon."
1348 return c->qtTrId("cutelyst-valemail-diag-rfc5322ipv6colonstart");
1350 //% "The IPv6 address ends with a single colon."
1351 return c->qtTrId("cutelyst-valemail-diag-rfc5322ipv6colonend");
1353 //% "A domain literal contains a character that is not allowed."
1354 return c->qtTrId("cutelyst-valemail-diag-errexpectingdtext");
1355 case ErrorNoLocalPart:
1356 //% "Address has no local part."
1357 return c->qtTrId("cutelyst-valemail-diag-errnolocalpart");
1358 case ErrorNoDomain:
1359 //% "Address has no domain part."
1360 return c->qtTrId("cutelyst-valemail-diag-errnodomain");
1362 //% "The address must not contain consecutive dots."
1363 return c->qtTrId("cutelyst-valemail-diag-errconsecutivedots");
1365 //% "Address contains text after a comment or folding white space."
1366 return c->qtTrId("cutelyst-valemail-diag-erratextaftercfws");
1367 case ErrorATextAfterQS:
1368 //% "Address contains text after a quoted string."
1369 return c->qtTrId("cutelyst-valemail-diag-erratextafterqs");
1371 //% "Extra characters were found after the end of the domain literal."
1372 return c->qtTrId("cutelyst-valemail-diag-erratextafterdomlit");
1374 //% "The Address contains a character that is not allowed in a quoted pair."
1375 return c->qtTrId("cutelyst-valemail-diag-errexpectingqpair");
1377 //% "Address contains a character that is not allowed."
1378 return c->qtTrId("cutelyst-valemail-diag-errexpectingatext");
1380 //% "A quoted string contains a character that is not allowed."
1381 return c->qtTrId("cutelyst-valemail-diag-errexpectingqtext");
1383 //% "A comment contains a character that is not allowed."
1384 return c->qtTrId("cutelyst-valemail-diag-errexpectingctext");
1385 case ErrorBackslashEnd:
1386 //% "The address can not end with a backslash."
1387 return c->qtTrId("cutelyst-valemail-diag-errbackslashend");
1388 case ErrorDotStart:
1389 //% "Neither part of the address may begin with a dot."
1390 return c->qtTrId("cutelyst-valemail-diag-errdotstart");
1391 case ErrorDotEnd:
1392 //% "Neither part of the address may end with a dot."
1393 return c->qtTrId("cutelyst-valemail-diag-errdotend");
1395 //% "A domain or subdomain can not begin with a hyphen."
1396 return c->qtTrId("cutelyst-valemail-diag-errdomainhyphenstart");
1398 //% "A domain or subdomain can not end with a hyphen."
1399 return c->qtTrId("cutelyst-valemail-diag-errdomainhyphenend");
1401 //% "Unclosed quoted string. (Missing double quotation mark)"
1402 return c->qtTrId("cutelyst-valemail-diag-errunclosedquotedstr");
1404 //% "Unclosed comment. (Missing closing parentheses)"
1405 return c->qtTrId("cutelyst-valemail-diag-errunclosedcomment");
1407 //% "Domain literal is missing its closing bracket."
1408 return c->qtTrId("cutelyst-valemail-diag-erruncloseddomliteral");
1409 case ErrorFWSCRLFx2:
1410 //% "Folding white space contains consecutive line break sequences (CRLF)."
1411 return c->qtTrId("cutelyst-valemail-diag-errfwscrlfx2");
1412 case ErrorFWSCRLFEnd:
1413 //% "Folding white space ends with a line break sequence (CRLF)."
1414 return c->qtTrId("cutelyst-valemail-diag-errfwscrlfend");
1415 case ErrorCRnoLF:
1416 //% "Address contains a carriage return (CR) that is not followed by a "
1417 //% "line feed (LF)."
1418 return c->qtTrId("cutelyst-valemail-diag-errcrnolf");
1419 case ErrorFatal:
1420 //% "A fatal error occurred while parsing the address."
1421 return c->qtTrId("cutelyst-valemail-diag-errfatal");
1422 default:
1423 return {};
1424 }
1425
1426 } else {
1427
1428 switch (diagnose) {
1429 case ValidAddress:
1430 //% "The address in the “%1” field is valid. Please note that this does not mean "
1431 //% "that both the address and the domain actually exist. This address could be "
1432 //% "issued by the domain owner without breaking the rules of any RFCs."
1433 return c->qtTrId("cutelyst-valemail-diag-valid-label").arg(label);
1434 case DnsWarnNoMxRecord:
1435 //% "Could not find an MX record for the address’ domain in the “%1” "
1436 //% "field but an A record exists."
1437 return c->qtTrId("cutelyst-valemail-diag-nomx-label").arg(label);
1438 case DnsWarnNoRecord:
1439 //% "Could neither find an MX record nor an A record for the address’ "
1440 //% "domain in the “%1” field."
1441 return c->qtTrId("cutelyst-valemail-diag-noarec-label").arg(label);
1442 case RFC5321TLD:
1443 //% "The address in the “%1” field is valid but at a Top Level Domain."
1444 return c->qtTrId("cutelyst-valemail-diag-rfc5321tld-label").arg(label);
1445 case RFC5321TLDNumeric:
1446 //% "The address in the “%1” field is valid but the Top Level Domain "
1447 //% "begins with a number."
1448 return c->qtTrId("cutelyst-valemail-diag-rfc5321tldnumeric-label").arg(label);
1450 //% "The address in the “%1” field is valid but contains a quoted string."
1451 return c->qtTrId("cutelyst-valemail-diag-rfc5321quotedstring-label").arg(label);
1453 //% "The address in the “%1” field is valid but uses an IP address "
1454 //% "instead of a domain name."
1455 return c->qtTrId("cutelyst-valemail-diag-rfc5321addressliteral-label").arg(label);
1457 //% "The address in the “%1” field is valid but uses an IP address that "
1458 //% "contains a :: only eliding one zero group. All implementations "
1459 //% "must accept and be able to handle any legitimate RFC 4291 format."
1460 return c->qtTrId("cutelyst-valemail-diag-rfc5321ipv6deprecated-label").arg(label);
1461 case CFWSComment:
1462 //% "The address in the “%1” field contains comments."
1463 return c->qtTrId("cutelyst-valemail-diag-cfwscomment-label").arg(label);
1464 case CFWSFWS:
1465 //% "The address in the “%1” field contains folding white spaces like "
1466 //% "line breaks."
1467 return c->qtTrId("cutelyst-valemail-diag-cfwsfws-label").arg(label);
1469 //% "The local part of the address in the “%1” field is in a deprecated form."
1470 return c->qtTrId("cutelyst-valemail-diag-deprecatedlocalpart-label").arg(label);
1471 case DeprecatedFWS:
1472 //% "The address in the “%1” field contains an obsolete form of folding "
1473 //% "white spaces."
1474 return c->qtTrId("cutelyst-valemail-diag-deprecatedfws-label").arg(label);
1475 case DeprecatedQText:
1476 //% "A quoted string in the address in the “%1” field contains a "
1477 //% "deprecated character."
1478 return c->qtTrId("cutelyst-valemail-diag-deprecatedqtext-label").arg(label);
1479 case DeprecatedQP:
1480 //% "A quoted pair in the address in the “%1” field contains a "
1481 //% "deprecate character."
1482 return c->qtTrId("cutelyst-valemail-diag-deprecatedqp-label").arg(label);
1483 case DeprecatedComment:
1484 //% "The address in the “%1” field contains a comment in a position "
1485 //% "that is deprecated."
1486 return c->qtTrId("cutelyst-valemail-diag-deprecatedcomment-label").arg(label);
1487 case DeprecatedCText:
1488 //% "A comment in the address in the “%1” field contains a deprecated character."
1489 return c->qtTrId("cutelyst-valemail-diag-deprecatedctext-label").arg(label);
1491 //% "The address in the “%1” field contains a comment or folding white "
1492 //% "space around the @ sign."
1493 return c->qtTrId("cutelyst-valemail-diag-cfwsnearat-label").arg(label);
1494 case RFC5322Domain:
1495 //% "The address in the “%1” field is RFC 5322 compliant but contains "
1496 //% "domain characters that are not allowed by DNS."
1497 return c->qtTrId("cutelyst-valemail-diag-rfc5322domain-label").arg(label);
1498 case RFC5322TooLong:
1499 //% "The address in the “%1” field exceeds the maximum allowed length "
1500 //% "of %2 characters."
1501 return c->qtTrId("cutelyst-valemail-diag-rfc5322toolong-label")
1502 .arg(label, c->locale().toString(ValidatorEmailPrivate::maxMailboxLength));
1504 //% "The local part of the address in the “%1” field exceeds the maximum allowed "
1505 //% "length of %2 characters."
1506 return c->qtTrId("cutelyst-valemail-diag-rfc5322localtoolong-label")
1507 .arg(label, c->locale().toString(ValidatorEmailPrivate::maxLocalPartLength));
1509 //% "The domain part of the address in the “%1” field exceeds the maximum "
1510 //% "allowed length of %2 characters."
1511 return c->qtTrId("cutelyst-valemail-diag-rfc5322domaintoolong-label")
1512 .arg(label, c->locale().toString(ValidatorEmailPrivate::maxDomainLength));
1514 //% "The domain part of the address in the “%1” field contains an element/section "
1515 //% "that exceeds the maximum allowed lenght of %2 characters."
1516 return c->qtTrId("cutelyst-valemail-diag-rfc5322labeltoolong-label")
1517 .arg(label, c->locale().toString(ValidatorEmailPrivate::maxDnsLabelLength));
1519 //% "The domain literal of the address in the “%1” field is not a valid "
1520 //% "RFC 5321 address literal."
1521 return c->qtTrId("cutelyst-valemail-diag-rfc5322domainliteral-label").arg(label);
1523 //% "The domain literal of the address in the “%1” field is not a valid "
1524 //% "RFC 5321 domain literal and it contains obsolete characters."
1525 return c->qtTrId("cutelyst-valemail-diag-rfc5322domlitobsdtext-label").arg(label);
1527 //% "The IPv6 literal of the address in the “%1” field contains the "
1528 //% "wrong number of groups."
1529 return c->qtTrId("cutelyst-valemail-diag-rfc5322ipv6groupcount-label").arg(label);
1531 //% "The IPv6 literal of the address in the “%1” field contains too "
1532 //% "many :: sequences."
1533 return c->qtTrId("cutelyst-valemail-diag-rfc5322ipv62x2xcolon-label").arg(label);
1534 case RFC5322IPv6BadChar:
1535 //% "The IPv6 address of the email address in the “%1” field contains "
1536 //% "an illegal group of characters."
1537 return c->qtTrId("cutelyst-valemail-diag-rfc5322ipv6badchar-label").arg(label);
1539 //% "The IPv6 address of the email address in the “%1” field has too many groups."
1540 return c->qtTrId("cutelyst-valemail-diag-rfc5322ipv6maxgroups-label").arg(label);
1542 //% "The IPv6 address of the email address in the “%1” field starts "
1543 //% "with a single colon."
1544 return c->qtTrId("cutelyst-valemail-diag-rfc5322ipv6colonstart-label").arg(label);
1546 //% "The IPv6 address of the email address in the “%1” field ends with "
1547 //% "a single colon."
1548 return c->qtTrId("cutelyst-valemail-diag-rfc5322ipv6colonend-label").arg(label);
1550 //% "A domain literal of the address in the “%1” field contains a "
1551 //% "character that is not allowed."
1552 return c->qtTrId("cutelyst-valemail-diag-errexpectingdtext-label").arg(label);
1553 case ErrorNoLocalPart:
1554 //% "The address in the “%1” field has no local part."
1555 return c->qtTrId("cutelyst-valemail-diag-errnolocalpart-label").arg(label);
1556 case ErrorNoDomain:
1557 //% "The address in the “%1” field has no domain part."
1558 return c->qtTrId("cutelyst-valemail-diag-errnodomain-label").arg(label);
1560 //% "The address in the “%1” field must not contain consecutive dots."
1561 return c->qtTrId("cutelyst-valemail-diag-errconsecutivedots-label").arg(label);
1563 //% "The address in the “%1” field contains text after a comment or "
1564 //% "folding white space."
1565 return c->qtTrId("cutelyst-valemail-diag-erratextaftercfws-label").arg(label);
1566 case ErrorATextAfterQS:
1567 //% "The address in the “%1” field contains text after a quoted string."
1568 return c->qtTrId("cutelyst-valemail-diag-erratextafterqs-label").arg(label);
1570 //% "Extra characters were found after the end of the domain literal of "
1571 //% "the address in the “%1” field."
1572 return c->qtTrId("cutelyst-valemail-diag-erratextafterdomlit-label").arg(label);
1574 //% "The address in the “%1” field contains a character that is not "
1575 //% "allowed in a quoted pair."
1576 return c->qtTrId("cutelyst-valemail-diag-errexpectingqpair-label").arg(label);
1578 //% "The address in the “%1” field contains a character that is not allowed."
1579 return c->qtTrId("cutelyst-valemail-diag-errexpectingatext-label").arg(label);
1581 //% "A quoted string in the address in the “%1” field contains a "
1582 //% "character that is not allowed."
1583 return c->qtTrId("cutelyst-valemail-diag-errexpectingqtext-label").arg(label);
1585 //% "A comment in the address in the “%1” field contains a character "
1586 //% "that is not allowed."
1587 return c->qtTrId("cutelyst-valemail-diag-errexpectingctext-label").arg(label);
1588 case ErrorBackslashEnd:
1589 //% "The address in the “%1” field can't end with a backslash."
1590 return c->qtTrId("cutelyst-valemail-diag-errbackslashend-label").arg(label);
1591 case ErrorDotStart:
1592 //% "Neither part of the address in the “%1” field may begin with a dot."
1593 return c->qtTrId("cutelyst-valemail-diag-errdotstart-label").arg(label);
1594 case ErrorDotEnd:
1595 //% "Neither part of the address in the “%1” field may end with a dot."
1596 return c->qtTrId("cutelyst-valemail-diag-errdotend-label").arg(label);
1598 //% "A domain or subdomain of the address in the “%1” field can not "
1599 //% "begin with a hyphen."
1600 return c->qtTrId("cutelyst-valemail-diag-errdomainhyphenstart-label").arg(label);
1602 //% "A domain or subdomain of the address in the “%1” field can not end "
1603 //% "with a hyphen."
1604 return c->qtTrId("cutelyst-valemail-diag-errdomainhyphenend-label").arg(label);
1606 //% "Unclosed quoted string in the address in the “%1” field. (Missing "
1607 //% "double quotation mark)"
1608 return c->qtTrId("cutelyst-valemail-diag-errunclosedquotedstr-label").arg(label);
1610 //% "Unclosed comment in the address in the “%1” field. (Missing "
1611 //% "closing parentheses)"
1612 return c->qtTrId("cutelyst-valemail-diag-errunclosedcomment-label").arg(label);
1614 //% "Domain literal of the address in the “%1” field is missing its "
1615 //% "closing bracket."
1616 return c->qtTrId("cutelyst-valemail-diag-erruncloseddomliteral-label").arg(label);
1617 case ErrorFWSCRLFx2:
1618 //% "Folding white space in the address in the “%1” field contains "
1619 //% "consecutive line break sequences (CRLF)."
1620 return c->qtTrId("cutelyst-valemail-diag-errfwscrlfx2-label").arg(label);
1621 case ErrorFWSCRLFEnd:
1622 //% "Folding white space in the address in the “%1” field ends with a "
1623 //% "line break sequence (CRLF)."
1624 return c->qtTrId("cutelyst-valemail-diag-errfwscrlfend-label").arg(label);
1625 case ErrorCRnoLF:
1626 //% "The address in the “%1” field contains a carriage return (CR) that "
1627 //% "is not followed by a line feed (LF)."
1628 return c->qtTrId("cutelyst-valemail-diag-errcrnolf-label").arg(label);
1629 case ErrorFatal:
1630 //% "A fatal error occurred while parsing the address in the “%1” field."
1631 return c->qtTrId("cutelyst-valemail-diag-errfatal-label").arg(label);
1632 default:
1633 return {};
1634 }
1635 }
1636}
1637
1639{
1640 if (label.isEmpty()) {
1641 switch (category) {
1642 case Valid:
1643 //% "Address is valid."
1644 return c->qtTrId("cutelyst-valemail-cat-valid");
1645 case DNSWarn:
1646 //% "Address is valid but a DNS check was not successful."
1647 return c->qtTrId("cutelyst-valemail-cat-dnswarn");
1648 case RFC5321:
1649 //% "Address is valid for SMTP but has unusual elements."
1650 return c->qtTrId("cutelyst-valemail-cat-rfc5321");
1651 case CFWS:
1652 //% "Address is valid within the message but can not be used unmodified "
1653 //% "for the envelope."
1654 return c->qtTrId("cutelyst-valemail-cat-cfws");
1655 case Deprecated:
1656 //% "Address contains deprecated elements but may still be valid in "
1657 //% "restricted contexts."
1658 return c->qtTrId("cutelyst-valemail-cat-deprecated");
1659 case RFC5322:
1660 //% "The address is only valid according to the broad definition of RFC "
1661 //% "5322. It is otherwise invalid."
1662 return c->qtTrId("cutelyst-valemail-cat-rfc5322");
1663 default:
1664 //% "Address is invalid for any purpose."
1665 return c->qtTrId("cutelyst-valemail-cat-invalid");
1666 }
1667 } else {
1668 switch (category) {
1669 case Valid:
1670 //% "The address in the “%1” field is valid."
1671 return c->qtTrId("cutelyst-valemail-cat-valid-label").arg(label);
1672 case DNSWarn:
1673 //% "The address in the “%1” field is valid but a DNS check was not successful."
1674 return c->qtTrId("cutelyst-valemail-cat-dnswarn-label").arg(label);
1675 case RFC5321:
1676 //% "The address in the “%1” field is valid for SMTP but has unusual elements."
1677 return c->qtTrId("cutelyst-valemail-cat-rfc5321-label").arg(label);
1678 case CFWS:
1679 //% "The address in the “%1” field is valid within the message but can "
1680 //% "not be used unmodified for the envelope."
1681 return c->qtTrId("cutelyst-valemail-cat-cfws-label").arg(label);
1682 case Deprecated:
1683 //% "The address in the “%1” field contains deprecated elements but may "
1684 //% "still be valid in restricted contexts."
1685 return c->qtTrId("cutelyst-valemail-cat-deprecated-label").arg(label);
1686 case RFC5322:
1687 //% "The address in the “%1” field is only valid according to the broad "
1688 //% "definition of RFC 5322. It is otherwise invalid."
1689 return c->qtTrId("cutelyst-valemail-cat-rfc5322-label").arg(label);
1690 default:
1691 //% "The address in the “%1” field is invalid for any purpose."
1692 return c->qtTrId("cutelyst-valemail-cat-invalid-label").arg(label);
1693 }
1694 }
1695}
1696
1698{
1699 Category cat = Error;
1700
1701 const auto diag = static_cast<int>(diagnose);
1702
1703 if (diag < static_cast<int>(Valid)) {
1704 cat = Valid;
1705 } else if (diag < static_cast<int>(DNSWarn)) {
1706 cat = DNSWarn;
1707 } else if (diag < static_cast<int>(RFC5321)) {
1708 cat = RFC5321;
1709 } else if (diag < static_cast<int>(CFWS)) {
1710 cat = CFWS;
1711 } else if (diag < static_cast<int>(Deprecated)) {
1712 cat = Deprecated;
1713 } else if (diag < static_cast<int>(RFC5322)) {
1714 cat = RFC5322;
1715 }
1716
1717 return cat;
1718}
1719
1721{
1722 return categoryString(c, category(diagnose), label);
1723}
1724
1726 Category threshold,
1727 Options options,
1729{
1730 ValidatorEmailDiagnoseStruct diag;
1731 bool ret = ValidatorEmailPrivate::checkEmail(email, options, threshold, &diag);
1732
1733 if (diagnoses) {
1734 *diagnoses = diag.returnStatus;
1735 }
1736
1737 return ret;
1738}
1739
1740#include "moc_validatoremail.cpp"
The Cutelyst Context.
Definition context.h:42
QLocale locale() const noexcept
Definition context.cpp:461
QString qtTrId(const char *id, int n=-1) const
Definition context.h:656
static Category category(Diagnose diagnose)
Category
Validation category, used as threshold to define valid addresses.
Diagnose
Single diagnose values that show why an address is not valid.
QString genericValidationError(Context *c, const QVariant &errorData=QVariant()) const override
static QString categoryString(Context *c, Category category, const QString &label={})
static QString diagnoseString(Context *c, Diagnose diagnose, const QString &label={})
ValidatorEmail(const QString &field, Category threshold=RFC5321, Options options=NoOption, const ValidatorMessages &messages=ValidatorMessages(), const QString &defValKey=QString())
QString field() const noexcept
QString validationError(Context *c, const QVariant &errorData={}) const
QString label(Context *c) const
ValidatorRule(const QString &field, const ValidatorMessages &messages={}, const QString &defValKey={}, QByteArrayView validatorName=nullptr)
void defaultValue(Context *c, ValidatorReturnType *result) const
QString value(const ParamsMultiMap &params) const
QMultiMap< QString, QString > ParamsMultiMap
static bool validate(const QString &email, Category threshold=RFC5321, Options options=NoOption, QList< Diagnose > *diagnoses=nullptr)
Returns true if email is a valid address according to the Category given in the threshold.
The Cutelyst namespace holds all public Cutelyst API.
CarriageReturn
bool isLetterOrNumber(char32_t ucs4)
char16_t & unicode()
void abort()
void finished()
int exec(QEventLoop::ProcessEventsFlags flags)
void quit()
void append(QList< T > &&value)
QList< T >::const_reference at(qsizetype i) const const
QList< T >::iterator begin()
void clear()
QList< T >::const_iterator constBegin() const const
QList< T >::const_iterator constEnd() const const
bool contains(const AT &value) const const
QList< T >::iterator end()
void push_back(QList< T >::parameter_type value)
qsizetype size() const const
QList< T >::value_type takeLast()
QString toString(QDate date, QLocale::FormatType format) const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QRegularExpressionMatch match(QStringView subjectView, qsizetype offset, QRegularExpression::MatchType matchType, QRegularExpression::MatchOptions matchOptions) const const
QString captured(QAnyStringView name) const const
bool hasMatch() const const
QString arg(Args &&... args) const const
const QChar at(qsizetype position) const const
int compare(QLatin1StringView s1, const QString &s2, Qt::CaseSensitivity cs)
QString fromLatin1(QByteArrayView str)
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
qsizetype lastIndexOf(QChar ch, Qt::CaseSensitivity cs) const const
QString left(qsizetype n) &&
qsizetype length() const const
QString mid(qsizetype position, qsizetype n) &&
QString right(qsizetype n) &&
qsizetype size() const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QByteArray toAce(const QString &domain, QUrl::AceProcessingOptions options)
QVariant fromValue(T &&value)
void setValue(QVariant &&value)
T value() const &const
Stores custom error messages and the input field label.
Contains the result of a single input parameter validation.