//go:build linux || freebsd package contactform import ( "net/mail" "strings" "unicode/utf8" ) // validServices is the allow-list for contact-type forms. var validServices = map[string]bool{ "": true, "architecture": true, "ai": true, "infrastructure": true, "software": true, "unix": true, "other": true, } // MinNameRunes is the minimum allowed length of a name. const MinNameRunes = 2 // MaxNameRunes is the maximum allowed length of a name. const MaxNameRunes = 100 // MinMessageRunes is the minimum allowed length of a message body. const MinMessageRunes = 10 // MaxMessageRunes is the maximum allowed length of a message body. const MaxMessageRunes = 5000 // Validate dispatches to the correct type-specific validator. // Supported types: contact, feedback, newsletter, generic. Default is "contact". func Validate(r *Request, formType string) []FieldError { switch formType { case "newsletter": return validateNewsletter(r) case "feedback", "generic": return validateGeneric(r) default: return validateContact(r) } } func validateContact(r *Request) []FieldError { r.Name = strings.TrimSpace(r.Name) r.Email = strings.TrimSpace(r.Email) r.Service = strings.TrimSpace(r.Service) r.Message = strings.TrimSpace(r.Message) var errs []FieldError if n := utf8.RuneCountInString(r.Name); n < MinNameRunes { errs = append(errs, FieldError{Field: "name", Message: "name must be at least 2 characters"}) } else if n > MaxNameRunes { errs = append(errs, FieldError{Field: "name", Message: "name must be at most 100 characters"}) } if _, err := mail.ParseAddress(r.Email); err != nil { errs = append(errs, FieldError{Field: "email", Message: "email is invalid"}) } if !validServices[r.Service] { errs = append(errs, FieldError{Field: "service", Message: "service is not a recognised value"}) } if n := utf8.RuneCountInString(r.Message); n < MinMessageRunes { errs = append(errs, FieldError{Field: "message", Message: "message must be at least 10 characters"}) } else if n > MaxMessageRunes { errs = append(errs, FieldError{Field: "message", Message: "message must be at most 5000 characters"}) } return errs } func validateNewsletter(r *Request) []FieldError { r.Email = strings.TrimSpace(r.Email) if _, err := mail.ParseAddress(r.Email); err != nil { return []FieldError{{Field: "email", Message: "email is invalid"}} } return nil } func validateGeneric(r *Request) []FieldError { r.Name = strings.TrimSpace(r.Name) r.Email = strings.TrimSpace(r.Email) r.Message = strings.TrimSpace(r.Message) var errs []FieldError if n := utf8.RuneCountInString(r.Name); n < MinNameRunes { errs = append(errs, FieldError{Field: "name", Message: "name must be at least 2 characters"}) } else if n > MaxNameRunes { errs = append(errs, FieldError{Field: "name", Message: "name must be at most 100 characters"}) } if _, err := mail.ParseAddress(r.Email); err != nil { errs = append(errs, FieldError{Field: "email", Message: "email is invalid"}) } if n := utf8.RuneCountInString(r.Message); n < MinMessageRunes { errs = append(errs, FieldError{Field: "message", Message: "message must be at least 10 characters"}) } else if n > MaxMessageRunes { errs = append(errs, FieldError{Field: "message", Message: "message must be at most 5000 characters"}) } return errs }