THATMobile/lib/core/common/validators.dart
2026-02-26 10:39:42 +07:00

224 lines
6.4 KiB
Dart

RegExp _email = RegExp(
r"^((([a-z]|\d|[!#\$%&'*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$");
RegExp _phoneNumber = RegExp(r'^(84|0[3|5|7|8|9])+([0-9]{8})\b$');
RegExp _ipv4Maybe = RegExp(r'^(\d?\d?\d)\.(\d?\d?\d)\.(\d?\d?\d)\.(\d?\d?\d)$');
RegExp _ipv6 = RegExp(r'^::|^::1|^([a-fA-F0-9]{1,4}::?){1,7}([a-fA-F0-9]{1,4})$');
RegExp _creditCard = RegExp(
r'^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$');
/// check if the string [str] is an email
bool isEmail(String str) {
return _email.hasMatch(str.toLowerCase());
}
bool isPhoneNumber(String str) {
return _phoneNumber.hasMatch(str.toLowerCase());
}
T? shift<T>(List<T> l) {
if (l.isNotEmpty) {
// ignore: always_specify_types
final first = l.first;
l.removeAt(0);
return first;
}
return null;
}
/// check if the string [str] is a URL
///
/// * [protocols] sets the list of allowed protocols
/// * [requireTld] sets if TLD is required
/// * [requireProtocol] is a `bool` that sets if protocol is required for validation
/// * [allowUnderscore] sets if underscores are allowed
/// * [hostWhitelist] sets the list of allowed hosts
/// * [hostBlacklist] sets the list of disallowed hosts
bool isURL(String? str,
{List<String?> protocols = const ['http', 'https', 'ftp'],
bool requireTld = true,
bool requireProtocol = false,
bool allowUnderscore = false,
List<String> hostWhitelist = const [],
List<String> hostBlacklist = const []}) {
if (str == null || str.isEmpty || str.length > 2083 || str.startsWith('mailto:')) {
return false;
}
int port;
String? protocol, auth, user;
String host, hostname, portStr, path, query, hash;
// check protocol
var split = str.split('://');
if (split.length > 1) {
protocol = shift(split);
if (!protocols.contains(protocol)) {
return false;
}
} else if (requireProtocol == true) {
return false;
}
str = split.join('://');
// check hash
split = str.split('#');
str = shift(split);
hash = split.join('#');
if (hash.isNotEmpty && RegExp(r'\s').hasMatch(hash)) {
return false;
}
// check query params
split = str!.split('?');
str = shift(split);
query = split.join('?');
if (query.isNotEmpty && RegExp(r'\s').hasMatch(query)) {
return false;
}
// check path
split = str!.split('/');
str = shift(split);
path = split.join('/');
if (path.isNotEmpty && RegExp(r'\s').hasMatch(path)) {
return false;
}
// check auth type urls
split = str!.split('@');
if (split.length > 1) {
auth = shift(split);
if (auth?.contains(':') ?? false) {
user = shift(auth!.split(':'))!;
if (!RegExp(r'^\S+$').hasMatch(user)) {
return false;
}
if (!RegExp(r'^\S*$').hasMatch(user)) {
return false;
}
}
}
// check hostname
hostname = split.join('@');
split = hostname.split(':');
host = shift(split)!;
if (split.isNotEmpty) {
portStr = split.join(':');
try {
port = int.parse(portStr, radix: 10);
} catch (e) {
return false;
}
if (!RegExp(r'^[0-9]+$').hasMatch(portStr) || port <= 0 || port > 65535) {
return false;
}
}
if (!isIP(host, null) &&
!isFQDN(host, requireTld: requireTld, allowUnderscores: allowUnderscore) &&
host != 'localhost') {
return false;
}
if (hostWhitelist.isNotEmpty && !hostWhitelist.contains(host)) {
return false;
}
if (hostBlacklist.isNotEmpty && hostBlacklist.contains(host)) {
return false;
}
return true;
}
/// check if the string [str] is IP [version] 4 or 6
///
/// * [version] is a String or an `int`.
bool isIP(String? str, int? version) {
if (version == null) {
return isIP(str, 4) || isIP(str, 6);
} else if (version == 4) {
if (!_ipv4Maybe.hasMatch(str!)) {
return false;
}
var parts = str.split('.');
parts.sort((a, b) => int.parse(a) - int.parse(b));
return int.parse(parts[3]) <= 255;
}
return version == 6 && _ipv6.hasMatch(str!);
}
/// check if the string [str] is a fully qualified domain name (e.g. domain.com).
///
/// * [requireTld] sets if TLD is required
/// * [allowUnderscore] sets if underscores are allowed
bool isFQDN(String str, {bool requireTld = true, bool allowUnderscores = false}) {
var parts = str.split('.');
if (requireTld) {
var tld = parts.removeLast();
if (parts.isEmpty || !RegExp(r'^[a-z]{2,}$').hasMatch(tld)) {
return false;
}
}
for (var part in parts) {
if (allowUnderscores) {
if (part.contains('__')) {
return false;
}
}
if (!RegExp(r'^[a-z\\u00a1-\\uffff0-9-]+$').hasMatch(part)) {
return false;
}
if (part[0] == '-' || part[part.length - 1] == '-' || part.contains('---')) {
return false;
}
}
return true;
}
/// check if the string is a credit card
bool isCreditCard(String str) {
var sanitized = str.replaceAll(RegExp(r'[^0-9]+'), '');
if (!_creditCard.hasMatch(sanitized)) {
return false;
}
// Luhn algorithm
var sum = 0;
String digit;
var shouldDouble = false;
for (var i = sanitized.length - 1; i >= 0; i--) {
digit = sanitized.substring(i, (i + 1));
var tmpNum = int.parse(digit);
if (shouldDouble == true) {
tmpNum *= 2;
if (tmpNum >= 10) {
sum += ((tmpNum % 10) + 1);
} else {
sum += tmpNum;
}
} else {
sum += tmpNum;
}
shouldDouble = !shouldDouble;
}
return (sum % 10 == 0);
}
/// check if the string is a date
bool isDate(String str) {
try {
DateTime.parse(str);
return true;
} catch (e) {
return false;
}
}