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