xref: /haiku/src/apps/haikudepot/util/ValidationUtils.cpp (revision 445d4fd926c569e7b9ae28017da86280aaecbae2)
1 /*
2  * Copyright 2019, Andrew Lindesay <apl@lindesay.co.nz>.
3  * All rights reserved. Distributed under the terms of the MIT License.
4  */
5 
6 #include "ValidationUtils.h"
7 
8 #include <ctype.h>
9 
10 #include <UnicodeChar.h>
11 
12 
13 #define MIN_LENGTH_NICKNAME			4
14 #define MAX_LENGTH_NICKNAME			32
15 
16 #define MIN_LENGTH_PASSWORD_CLEAR	8
17 #define MIN_UPPER_PASSWORD_CLEAR	1
18 #define MIN_DIGITS_PASSWORD_CLEAR	2
19 
20 /*! 1 if the character would be suitable for use in an email address mailbox
21     or domain part.
22 */
23 
24 static int
25 hd_is_email_domain_or_mailbox_part(int c)
26 {
27 	if (0 == isspace(c) && c != 0x40)
28 		return 1;
29 	return 0;
30 }
31 
32 
33 /*! Returns true if the entire string is lower case alpha numeric.
34  */
35 
36 static int
37 hd_is_lower_alnum(int c)
38 {
39 	if ((c >= 0x30 && c <= 0x39) || (c >= 0x60 && c <= 0x7a))
40 		return 1;
41 	return 0;
42 }
43 
44 
45 static bool
46 hd_str_all_matches_fn(const BString& string, int (*hd_match_c)(int c))
47 {
48 	const char* c = string.String();
49 	for (int32 i = 0; i < string.CountChars(); i++) {
50 		if (0 == hd_match_c(c[i]))
51 			return false;
52 	}
53 
54 	return true;
55 }
56 
57 
58 static int32
59 hd_str_count_upper_case(const BString& string)
60 {
61 	int32 upperCaseLetters = 0;
62 	const char* c = string.String();
63 	for (int32 i = 0; i < string.CountChars(); i++) {
64 		uint32 unicodeChar = BUnicodeChar::FromUTF8(&c);
65 		if (BUnicodeChar::IsUpper(unicodeChar))
66 			upperCaseLetters++;
67 	}
68 	return upperCaseLetters;
69 }
70 
71 
72 static int32
73 hd_str_count_digit(const BString& string)
74 {
75 	int32 digits = 0;
76 	const char* c = string.String();
77 	for (int32 i = 0; i < string.CountChars(); i++) {
78 		uint32 unicodeChar = BUnicodeChar::FromUTF8(&c);
79 		if (BUnicodeChar::IsDigit(unicodeChar))
80 			digits++;
81 	}
82 	return digits;
83 }
84 
85 
86 /*static*/ bool
87 ValidationUtils::IsValidNickname(const BString& value)
88 {
89 	return hd_str_all_matches_fn(value, &hd_is_lower_alnum)
90 		&& value.CountChars() >= MIN_LENGTH_NICKNAME
91 		&& value.CountChars() <= MAX_LENGTH_NICKNAME;
92 }
93 
94 
95 /*! Email addresses are quite difficult to validate 100% correctly so go fairly
96     light on the enforcement here; it should be a string with an '@' symbol,
97     something either side of the '@' and there should be no whitespace.
98 */
99 
100 /*static*/ bool
101 ValidationUtils::IsValidEmail(const BString& value)
102 {
103 	const char* c = value.String();
104 	size_t len = strlen(c);
105 	bool foundAt = false;
106 
107 	for (size_t i = 0; i < len; i++) {
108 		if (c[i] == 0x40 && !foundAt) {
109 			if (i == 0 || i == len - 1)
110 				return false;
111 			foundAt = true;
112 		}
113 		else {
114 			if (0 == hd_is_email_domain_or_mailbox_part(c[i]))
115 				return false;
116 		}
117 	}
118 
119 	return foundAt;
120 }
121 
122 
123 // TODO: needs to reflect the data from the server rather than be hard coded
124 /*static*/ bool
125 ValidationUtils::IsValidPasswordClear(const BString& value)
126 {
127 	return value.Length() >= MIN_LENGTH_PASSWORD_CLEAR
128 		&& hd_str_count_digit(value) >= MIN_DIGITS_PASSWORD_CLEAR
129 		&& hd_str_count_upper_case(value) >= MIN_UPPER_PASSWORD_CLEAR;
130 }
131 
132