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  }