212 lines
5.5 KiB
Go
212 lines
5.5 KiB
Go
package validation
|
||
|
||
import (
|
||
"errors"
|
||
"regexp"
|
||
"slices"
|
||
"strconv"
|
||
"strings"
|
||
"unicode/utf8"
|
||
)
|
||
|
||
func SanitizeEmail(input string) string {
|
||
input = strings.ToLower(strings.TrimSpace(input))
|
||
|
||
re := regexp.MustCompile(`[a-z0-9!#$%&'*+/=?^_` + "`" + `{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_` + "`" + `{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?`)
|
||
match := re.FindString(input)
|
||
|
||
return match
|
||
}
|
||
|
||
// Checks an email address against a first and last name and returns true if it matches one of the following patterns:
|
||
// firstlast@domain.tld, flast@domain.tld, firstl@domain.tld, first.last@domain.tld, f.last@domain.tld,
|
||
// first.l@domain.tld, first-last@domain.tld, f-last@domain.tld, first-l@domain.tld. Otherwise, false.
|
||
// Also handles hyphenated and space-separated last names (e.g., Meyer-Ogren or Van Der Berg)
|
||
func DoesNameMatchEmail(email string, firstName string, lastName string) bool {
|
||
// Validate inputs
|
||
if email == "" || firstName == "" || lastName == "" {
|
||
return true
|
||
}
|
||
|
||
email = strings.ToLower(email)
|
||
firstName = strings.ToLower(firstName)
|
||
lastName = strings.ToLower(lastName)
|
||
|
||
emailIdentifier := strings.Split(email, "@")[0]
|
||
firstInitial := string(firstName[0])
|
||
|
||
// Split hyphenated and space-separated last names
|
||
lastNameParts := []string{lastName}
|
||
|
||
// Also add concatenated version (removing hyphens/spaces)
|
||
lastNameNormalized := strings.ReplaceAll(lastName, "-", "")
|
||
lastNameNormalized = strings.ReplaceAll(lastNameNormalized, " ", "")
|
||
if lastNameNormalized != lastName {
|
||
lastNameParts = append(lastNameParts, lastNameNormalized)
|
||
}
|
||
|
||
// Add individual parts
|
||
if strings.Contains(lastName, "-") {
|
||
lastNameParts = append(lastNameParts, strings.Split(lastName, "-")...)
|
||
}
|
||
if strings.Contains(lastName, " ") {
|
||
lastNameParts = append(lastNameParts, strings.Split(lastName, " ")...)
|
||
}
|
||
|
||
separators := []string{"", ".", "-"}
|
||
|
||
// Check standalone first or last names
|
||
if emailIdentifier == firstName {
|
||
return true
|
||
}
|
||
|
||
// Check each part of the last name
|
||
for _, lastPart := range lastNameParts {
|
||
if lastPart == "" {
|
||
continue
|
||
}
|
||
|
||
if emailIdentifier == lastPart {
|
||
return true
|
||
}
|
||
|
||
lastPartInitial := string(lastPart[0])
|
||
|
||
// Check all separator combinations with each last name part
|
||
for _, separator := range separators {
|
||
switch emailIdentifier {
|
||
case strings.Join([]string{firstName, lastPart}, separator),
|
||
strings.Join([]string{firstInitial, lastPart}, separator),
|
||
strings.Join([]string{firstName, lastPartInitial}, separator):
|
||
return true
|
||
}
|
||
}
|
||
}
|
||
|
||
return false
|
||
}
|
||
|
||
func SanitizePhone(input string) string {
|
||
// Regular expression to match non-digit characters
|
||
re := regexp.MustCompile(`[^0-9]`)
|
||
|
||
// Replace non-digit characters with empty string
|
||
sanitized := re.ReplaceAllString(input, "")
|
||
|
||
return sanitized
|
||
}
|
||
|
||
func ValidatePhoneNumber(phoneNumber string) error {
|
||
isNumber, _ := regexp.MatchString(`^\d+$`, phoneNumber)
|
||
if !isNumber {
|
||
return errors.New("Phone number must only contain numbers.")
|
||
}
|
||
|
||
if len(phoneNumber) != 10 {
|
||
return errors.New("Invalid phone number length.")
|
||
}
|
||
|
||
areaCode, _ := strconv.Atoi(phoneNumber[:3])
|
||
|
||
// Lowest area code is 200, so less than (200) 000-0000
|
||
if areaCode < 200 {
|
||
return errors.New("Phone number has invalid area code.")
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// Removes invalid UTF-8 sequences and replaces special characters
|
||
func SanitizeString(s string) string {
|
||
// Replace non-breaking spaces (0xa0) with regular spaces
|
||
s = strings.ReplaceAll(s, "\u00a0", " ")
|
||
|
||
// Remove other common problematic characters
|
||
s = strings.ReplaceAll(s, "\r", "")
|
||
|
||
// Remove replacement character (<28>) that appears when invalid UTF-8 is encountered
|
||
s = strings.ReplaceAll(s, "\ufffd", "")
|
||
|
||
// Validate and fix UTF-8 encoding
|
||
if !utf8.ValidString(s) {
|
||
// Convert to valid UTF-8 by removing invalid bytes
|
||
v := make([]rune, 0, len(s))
|
||
for _, r := range s {
|
||
if r != utf8.RuneError {
|
||
v = append(v, r)
|
||
}
|
||
}
|
||
s = string(v)
|
||
}
|
||
|
||
// Trim whitespace
|
||
return strings.TrimSpace(s)
|
||
}
|
||
|
||
var stateCodes []string = []string{
|
||
"AL", "AK", "AZ", "AR", "AS", "CA", "CO", "CT", "DE", "DC", "FL", "GA",
|
||
"GU", "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MD", "MA",
|
||
"MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ", "NM", "NY", "NC",
|
||
"ND", "MP", "OH", "OK", "OR", "PA", "PR", "RI", "SC", "SD", "TN", "TX",
|
||
"TT", "UT", "VT", "VA", "VI", "WA", "WV", "WI", "WY",
|
||
}
|
||
|
||
// Checks if the input string is a proper two-letter state code
|
||
func ValidateStateCode(s string) error {
|
||
if slices.Contains(stateCodes, s) {
|
||
return nil
|
||
}
|
||
|
||
return errors.New("Invalid state code.")
|
||
}
|
||
|
||
func ValidateTaxId(id string) error {
|
||
isNumber, _ := regexp.MatchString(`^\d+$`, id)
|
||
if !isNumber {
|
||
return errors.New("Tax ID must only contain numbers.")
|
||
}
|
||
|
||
if len(id) != 9 {
|
||
return errors.New("Tax ID must be 9 digits long.")
|
||
}
|
||
|
||
idNum, _ := strconv.Atoi(id)
|
||
if idNum == 0 {
|
||
return errors.New("Tax ID cannot be 00-0000000.")
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func ValidateZipCode(zip string) error {
|
||
isNumber, _ := regexp.MatchString(`^\d+$`, zip)
|
||
if !isNumber {
|
||
return errors.New("ZIP code must only contain numbers.")
|
||
}
|
||
|
||
if (len(zip) != 5) && (len(zip) != 9) {
|
||
return errors.New("Invalid length for ZIP code.")
|
||
}
|
||
|
||
zipCode, _ := strconv.Atoi(zip[:5])
|
||
|
||
// Lowest 5 digit ZIP code is 00501
|
||
if zipCode < 501 {
|
||
return errors.New("Invalid ZIP code.")
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func ValidateUrl(url string) error {
|
||
err := errors.New("Invalid URL.")
|
||
|
||
valid, _ := regexp.MatchString(`[(http(s)?):\/\/(www\.)?a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)`, url)
|
||
|
||
if !valid {
|
||
return err
|
||
}
|
||
|
||
return nil
|
||
}
|