github.com/go-email-validator/go-email-validator@v0.0.0-20230409163946-b8b9e6a0552e/pkg/presentation/converter/smtp.go (about) 1 package converter 2 3 import ( 4 "errors" 5 "github.com/go-email-validator/go-email-validator/pkg/ev" 6 "github.com/go-email-validator/go-email-validator/pkg/ev/evmail" 7 "github.com/go-email-validator/go-email-validator/pkg/ev/evsmtp" 8 "net/textproto" 9 "strings" 10 "sync" 11 ) 12 13 // SMTPPresentation is a presentation of smtp from ev.ValidationResult 14 type SMTPPresentation struct { 15 CanConnectSMTP bool `json:"can_connect_smtp"` 16 HasFullInbox bool `json:"has_full_inbox"` 17 IsCatchAll bool `json:"is_catch_all"` 18 IsDeliverable bool `json:"is_deliverable"` 19 IsDisabled bool `json:"is_disabled"` 20 IsGreyListed bool `json:"is_grey_listed"` 21 } 22 23 // Default results 24 var ( 25 WithoutErrsSMTPPresentation = SMTPPresentation{ 26 CanConnectSMTP: true, 27 HasFullInbox: false, 28 IsCatchAll: true, 29 IsDeliverable: true, 30 IsDisabled: false, 31 IsGreyListed: false, 32 } 33 FalseSMTPPresentation = SMTPPresentation{ 34 CanConnectSMTP: false, 35 HasFullInbox: false, 36 IsCatchAll: false, 37 IsDeliverable: false, 38 IsDisabled: false, 39 IsGreyListed: false, 40 } 41 ) 42 43 var smtpConverter *SMTPConverter 44 45 var muNewSMTPConverter sync.Mutex 46 47 // NewSMTPConverter creates SMTPConverter 48 func NewSMTPConverter() *SMTPConverter { 49 muNewSMTPConverter.Lock() 50 defer muNewSMTPConverter.Unlock() 51 52 if smtpConverter == nil { 53 smtpConverter = &SMTPConverter{} 54 } 55 56 return smtpConverter 57 } 58 59 // SMTPConverter converts ev.ValidationResult in SMTPConverter 60 type SMTPConverter struct{} 61 62 // Can ev.ValidationResult be converted in SMTPConverter 63 func (SMTPConverter) Can(_ evmail.Address, result ev.ValidationResult, _ Options) bool { 64 return result.ValidatorName() == ev.SMTPValidatorName 65 } 66 67 // Convert ev.ValidationResult in SMTPConverter 68 func (SMTPConverter) Convert(_ evmail.Address, result ev.ValidationResult, _ Options) interface{} { 69 var presentation = WithoutErrsSMTPPresentation 70 var errString string 71 var errCode int 72 var smtpError evsmtp.Error 73 var depError *ev.DepsError 74 75 errs := result.Errors() 76 errs = append(errs, result.Warnings()...) 77 78 for _, err := range errs { 79 if !errors.As(err, &smtpError) { 80 if errors.As(err, &depError) { 81 return FalseSMTPPresentation 82 } 83 continue 84 } 85 86 sourceErr := errors.Unwrap(smtpError) 87 errString = strings.ToLower(sourceErr.Error()) 88 89 errCode = 0 90 switch v := sourceErr.(type) { 91 case *textproto.Error: 92 errCode = v.Code 93 } 94 if strings.Contains(errString, "greylist") { 95 presentation.IsGreyListed = true 96 } 97 98 switch smtpError.Stage() { 99 case evsmtp.ConnectionStage: 100 presentation = FalseSMTPPresentation 101 case evsmtp.HelloStage, 102 evsmtp.AuthStage, 103 evsmtp.MailStage: 104 presentation.IsDeliverable = false 105 case evsmtp.RandomRCPTStage: 106 presentation.IsCatchAll = false 107 case evsmtp.RCPTsStage: 108 presentation.IsDeliverable = false 109 switch { 110 case strings.Contains(errString, "disabled") || 111 strings.Contains(errString, "discontinued"): 112 presentation.IsDisabled = true 113 case errCode == 452 && (strings.Contains(errString, "full") || 114 strings.Contains(errString, "insufficient") || 115 strings.Contains(errString, "over quota") || 116 strings.Contains(errString, "space") || 117 strings.Contains(errString, "too many messages")): 118 119 presentation.HasFullInbox = true 120 case strings.Contains(errString, "the user you are trying to contact is receiving mail at a rate that"): 121 presentation.IsDeliverable = true 122 } 123 } 124 } 125 126 return presentation 127 }