123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485 |
- package response
- import (
- "fmt"
- )
- const (
- // ClassSuccess specifies that the DSN is reporting a positive delivery
- // action. Detail sub-codes may provide notification of
- // transformations required for delivery.
- ClassSuccess = 2
- // ClassTransientFailure - a persistent transient failure is one in which the message as
- // sent is valid, but persistence of some temporary condition has
- // caused abandonment or delay of attempts to send the message.
- // If this code accompanies a delivery failure report, sending in
- // the future may be successful.
- ClassTransientFailure = 4
- // ClassPermanentFailure - a permanent failure is one which is not likely to be resolved
- // by resending the message in the current form. Some change to
- // the message or the destination must be made for successful
- // delivery.
- ClassPermanentFailure = 5
- )
- // space char
- const SP = " "
- // class is a type for ClassSuccess, ClassTransientFailure and ClassPermanentFailure constants
- type class int
- // String implements stringer for the class type
- func (c class) String() string {
- return fmt.Sprintf("%c00", c)
- }
- // codeMap for mapping Enhanced Status Code to Basic Code
- // Mapping according to https://www.iana.org/assignments/smtp-enhanced-status-codes/smtp-enhanced-status-codes.xml
- // This might not be entirely useful
- var codeMap = struct {
- m map[EnhancedStatusCode]int
- }{m: map[EnhancedStatusCode]int{
- EnhancedStatusCode{ClassSuccess, OtherAddressStatus}: 250,
- EnhancedStatusCode{ClassSuccess, DestinationMailboxAddressValid}: 250,
- EnhancedStatusCode{ClassSuccess, OtherOrUndefinedMailSystemStatus}: 250,
- EnhancedStatusCode{ClassSuccess, OtherOrUndefinedProtocolStatus}: 250,
- EnhancedStatusCode{ClassSuccess, ConversionWithLossPerformed}: 250,
- EnhancedStatusCode{ClassSuccess, ".6.8"}: 252,
- EnhancedStatusCode{ClassSuccess, ".7.0"}: 220,
- EnhancedStatusCode{ClassTransientFailure, BadDestinationMailboxAddress}: 451,
- EnhancedStatusCode{ClassTransientFailure, BadSendersSystemAddress}: 451,
- EnhancedStatusCode{ClassTransientFailure, MailingListExpansionProblem}: 450,
- EnhancedStatusCode{ClassTransientFailure, OtherOrUndefinedMailSystemStatus}: 421,
- EnhancedStatusCode{ClassTransientFailure, MailSystemFull}: 452,
- EnhancedStatusCode{ClassTransientFailure, SystemNotAcceptingNetworkMessages}: 453,
- EnhancedStatusCode{ClassTransientFailure, NoAnswerFromHost}: 451,
- EnhancedStatusCode{ClassTransientFailure, BadConnection}: 421,
- EnhancedStatusCode{ClassTransientFailure, RoutingServerFailure}: 451,
- EnhancedStatusCode{ClassTransientFailure, NetworkCongestion}: 451,
- EnhancedStatusCode{ClassTransientFailure, OtherOrUndefinedProtocolStatus}: 451,
- EnhancedStatusCode{ClassTransientFailure, InvalidCommand}: 430,
- EnhancedStatusCode{ClassTransientFailure, TooManyRecipients}: 452,
- EnhancedStatusCode{ClassTransientFailure, InvalidCommandArguments}: 451,
- EnhancedStatusCode{ClassTransientFailure, ".7.0"}: 450,
- EnhancedStatusCode{ClassTransientFailure, ".7.1"}: 451,
- EnhancedStatusCode{ClassTransientFailure, ".7.12"}: 422,
- EnhancedStatusCode{ClassTransientFailure, ".7.15"}: 450,
- EnhancedStatusCode{ClassTransientFailure, ".7.24"}: 451,
- EnhancedStatusCode{ClassPermanentFailure, BadDestinationMailboxAddress}: 550,
- EnhancedStatusCode{ClassPermanentFailure, BadDestinationMailboxAddressSyntax}: 501,
- EnhancedStatusCode{ClassPermanentFailure, BadSendersSystemAddress}: 501,
- EnhancedStatusCode{ClassPermanentFailure, ".1.10"}: 556,
- EnhancedStatusCode{ClassPermanentFailure, MailboxFull}: 552,
- EnhancedStatusCode{ClassPermanentFailure, MessageLengthExceedsAdministrativeLimit}: 552,
- EnhancedStatusCode{ClassPermanentFailure, OtherOrUndefinedMailSystemStatus}: 550,
- EnhancedStatusCode{ClassPermanentFailure, MessageTooBigForSystem}: 552,
- EnhancedStatusCode{ClassPermanentFailure, RoutingServerFailure}: 550,
- EnhancedStatusCode{ClassPermanentFailure, OtherOrUndefinedProtocolStatus}: 501,
- EnhancedStatusCode{ClassPermanentFailure, InvalidCommand}: 500,
- EnhancedStatusCode{ClassPermanentFailure, SyntaxError}: 500,
- EnhancedStatusCode{ClassPermanentFailure, InvalidCommandArguments}: 501,
- EnhancedStatusCode{ClassPermanentFailure, ".5.6"}: 500,
- EnhancedStatusCode{ClassPermanentFailure, ConversionRequiredButNotSupported}: 554,
- EnhancedStatusCode{ClassPermanentFailure, ".6.6"}: 554,
- EnhancedStatusCode{ClassPermanentFailure, ".6.7"}: 553,
- EnhancedStatusCode{ClassPermanentFailure, ".6.8"}: 550,
- EnhancedStatusCode{ClassPermanentFailure, ".6.9"}: 550,
- EnhancedStatusCode{ClassPermanentFailure, ".7.0"}: 550,
- EnhancedStatusCode{ClassPermanentFailure, ".7.1"}: 551,
- EnhancedStatusCode{ClassPermanentFailure, ".7.2"}: 550,
- EnhancedStatusCode{ClassPermanentFailure, ".7.4"}: 504,
- EnhancedStatusCode{ClassPermanentFailure, ".7.8"}: 554,
- EnhancedStatusCode{ClassPermanentFailure, ".7.9"}: 534,
- EnhancedStatusCode{ClassPermanentFailure, ".7.10"}: 523,
- EnhancedStatusCode{ClassPermanentFailure, ".7.11"}: 524,
- EnhancedStatusCode{ClassPermanentFailure, ".7.13"}: 525,
- EnhancedStatusCode{ClassPermanentFailure, ".7.14"}: 535,
- EnhancedStatusCode{ClassPermanentFailure, ".7.15"}: 550,
- EnhancedStatusCode{ClassPermanentFailure, ".7.16"}: 552,
- EnhancedStatusCode{ClassPermanentFailure, ".7.17"}: 500,
- EnhancedStatusCode{ClassPermanentFailure, ".7.18"}: 500,
- EnhancedStatusCode{ClassPermanentFailure, ".7.19"}: 500,
- EnhancedStatusCode{ClassPermanentFailure, ".7.20"}: 550,
- EnhancedStatusCode{ClassPermanentFailure, ".7.21"}: 550,
- EnhancedStatusCode{ClassPermanentFailure, ".7.22"}: 550,
- EnhancedStatusCode{ClassPermanentFailure, ".7.23"}: 550,
- EnhancedStatusCode{ClassPermanentFailure, ".7.24"}: 550,
- EnhancedStatusCode{ClassPermanentFailure, ".7.25"}: 550,
- EnhancedStatusCode{ClassPermanentFailure, ".7.26"}: 550,
- EnhancedStatusCode{ClassPermanentFailure, ".7.27"}: 550,
- }}
- var (
- // Canned is to be read-only, except in the init() function
- Canned Responses
- )
- // Responses has some already pre-constructed responses
- type Responses struct {
- // The 500's
- FailLineTooLong *Response
- FailNestedMailCmd *Response
- FailNoSenderDataCmd *Response
- FailNoRecipientsDataCmd *Response
- FailUnrecognizedCmd *Response
- FailMaxUnrecognizedCmd *Response
- FailSyntaxError *Response
- FailReadLimitExceededDataCmd *Response
- FailMessageSizeExceeded *Response
- FailReadErrorDataCmd *Response
- FailPathTooLong *Response
- FailInvalidAddress *Response
- FailLocalPartTooLong *Response
- FailDomainTooLong *Response
- FailBackendNotRunning *Response
- FailBackendTransaction *Response
- FailBackendTimeout *Response
- FailRcptCmd *Response
- // The 400's
- ErrorTooManyRecipients *Response
- ErrorRelayDenied *Response
- ErrorShutdown *Response
- // The 200's
- SuccessMailCmd *Response
- SuccessRcptCmd *Response
- SuccessResetCmd *Response
- SuccessVerifyCmd *Response
- SuccessNoopCmd *Response
- SuccessQuitCmd *Response
- SuccessDataCmd *Response
- SuccessStartTLSCmd *Response
- SuccessMessageQueued *Response
- }
- // Called automatically during package load to build up the Responses struct
- func init() {
- Canned = Responses{}
- Canned.FailLineTooLong = &Response{
- EnhancedCode: InvalidCommand,
- BasicCode: 554,
- Class: ClassPermanentFailure,
- Comment: "Line too long.",
- }
- Canned.FailNestedMailCmd = &Response{
- EnhancedCode: InvalidCommand,
- BasicCode: 503,
- Class: ClassPermanentFailure,
- Comment: "Error: nested MAIL command",
- }
- Canned.SuccessMailCmd = &Response{
- EnhancedCode: OtherAddressStatus,
- Class: ClassSuccess,
- }
- Canned.SuccessRcptCmd = &Response{
- EnhancedCode: DestinationMailboxAddressValid,
- Class: ClassSuccess,
- }
- Canned.SuccessResetCmd = Canned.SuccessMailCmd
- Canned.SuccessNoopCmd = &Response{
- EnhancedCode: OtherStatus,
- Class: ClassSuccess,
- }
- Canned.SuccessVerifyCmd = &Response{
- EnhancedCode: OtherOrUndefinedProtocolStatus,
- BasicCode: 252,
- Class: ClassSuccess,
- Comment: "Cannot verify user",
- }
- Canned.ErrorTooManyRecipients = &Response{
- EnhancedCode: TooManyRecipients,
- BasicCode: 452,
- Class: ClassTransientFailure,
- Comment: "Too many recipients",
- }
- Canned.ErrorRelayDenied = &Response{
- EnhancedCode: BadDestinationMailboxAddress,
- BasicCode: 454,
- Class: ClassTransientFailure,
- Comment: "Error: Relay access denied:",
- }
- Canned.SuccessQuitCmd = &Response{
- EnhancedCode: OtherStatus,
- BasicCode: 221,
- Class: ClassSuccess,
- Comment: "Bye",
- }
- Canned.FailNoSenderDataCmd = &Response{
- EnhancedCode: InvalidCommand,
- BasicCode: 503,
- Class: ClassPermanentFailure,
- Comment: "Error: No sender",
- }
- Canned.FailNoRecipientsDataCmd = &Response{
- EnhancedCode: InvalidCommand,
- BasicCode: 503,
- Class: ClassPermanentFailure,
- Comment: "Error: No recipients",
- }
- Canned.SuccessDataCmd = &Response{
- BasicCode: 354,
- Comment: "354 Enter message, ending with '.' on a line by itself",
- }
- Canned.SuccessStartTLSCmd = &Response{
- EnhancedCode: OtherStatus,
- BasicCode: 220,
- Class: ClassSuccess,
- Comment: "Ready to start TLS",
- }
- Canned.FailUnrecognizedCmd = &Response{
- EnhancedCode: InvalidCommand,
- BasicCode: 554,
- Class: ClassPermanentFailure,
- Comment: "Unrecognized command",
- }
- Canned.FailMaxUnrecognizedCmd = &Response{
- EnhancedCode: InvalidCommand,
- BasicCode: 554,
- Class: ClassPermanentFailure,
- Comment: "Too many unrecognized commands",
- }
- Canned.ErrorShutdown = &Response{
- EnhancedCode: OtherOrUndefinedMailSystemStatus,
- BasicCode: 421,
- Class: ClassTransientFailure,
- Comment: "Server is shutting down. Please try again later. Sayonara!",
- }
- Canned.FailSyntaxError = &Response{
- EnhancedCode: SyntaxError,
- BasicCode: 550,
- Class: ClassPermanentFailure,
- Comment: "Syntax error",
- }
- Canned.FailReadLimitExceededDataCmd = &Response{
- EnhancedCode: MessageLengthExceedsAdministrativeLimit,
- BasicCode: 550,
- Class: ClassPermanentFailure,
- Comment: "Error:",
- }
- Canned.FailMessageSizeExceeded = &Response{
- EnhancedCode: OtherOrUndefinedNetworkOrRoutingStatus,
- BasicCode: 552,
- Class: ClassPermanentFailure,
- Comment: "Error:",
- }
- Canned.FailReadErrorDataCmd = &Response{
- EnhancedCode: OtherOrUndefinedMailSystemStatus,
- BasicCode: 451,
- Class: ClassTransientFailure,
- Comment: "Error:",
- }
- Canned.FailPathTooLong = &Response{
- EnhancedCode: InvalidCommandArguments,
- BasicCode: 550,
- Class: ClassPermanentFailure,
- Comment: "Path too long",
- }
- Canned.FailInvalidAddress = &Response{
- EnhancedCode: InvalidCommandArguments,
- BasicCode: 501,
- Class: ClassPermanentFailure,
- Comment: "Invalid address",
- }
- Canned.FailLocalPartTooLong = &Response{
- EnhancedCode: InvalidCommandArguments,
- BasicCode: 550,
- Class: ClassPermanentFailure,
- Comment: "Local part too long, cannot exceed 64 characters",
- }
- Canned.FailDomainTooLong = &Response{
- EnhancedCode: InvalidCommandArguments,
- BasicCode: 550,
- Class: ClassPermanentFailure,
- Comment: "Domain cannot exceed 255 characters",
- }
- Canned.FailBackendNotRunning = &Response{
- EnhancedCode: OtherOrUndefinedProtocolStatus,
- BasicCode: 554,
- Class: ClassPermanentFailure,
- Comment: "Transaction failed - backend not running",
- }
- Canned.FailBackendTransaction = &Response{
- EnhancedCode: OtherOrUndefinedProtocolStatus,
- BasicCode: 554,
- Class: ClassPermanentFailure,
- Comment: "Error:",
- }
- Canned.SuccessMessageQueued = &Response{
- EnhancedCode: OtherStatus,
- BasicCode: 250,
- Class: ClassSuccess,
- Comment: "OK: queued as",
- }
- Canned.FailBackendTimeout = &Response{
- EnhancedCode: OtherOrUndefinedProtocolStatus,
- BasicCode: 554,
- Class: ClassPermanentFailure,
- Comment: "Error: transaction timeout",
- }
- Canned.FailRcptCmd = &Response{
- EnhancedCode: BadDestinationMailboxAddress,
- BasicCode: 550,
- Class: ClassPermanentFailure,
- Comment: "User unknown in local recipient table",
- }
- }
- // DefaultMap contains defined default codes (RfC 3463)
- const (
- OtherStatus = ".0.0"
- OtherAddressStatus = ".1.0"
- BadDestinationMailboxAddress = ".1.1"
- BadDestinationSystemAddress = ".1.2"
- BadDestinationMailboxAddressSyntax = ".1.3"
- DestinationMailboxAddressAmbiguous = ".1.4"
- DestinationMailboxAddressValid = ".1.5"
- MailboxHasMoved = ".1.6"
- BadSendersMailboxAddressSyntax = ".1.7"
- BadSendersSystemAddress = ".1.8"
- OtherOrUndefinedMailboxStatus = ".2.0"
- MailboxDisabled = ".2.1"
- MailboxFull = ".2.2"
- MessageLengthExceedsAdministrativeLimit = ".2.3"
- MailingListExpansionProblem = ".2.4"
- OtherOrUndefinedMailSystemStatus = ".3.0"
- MailSystemFull = ".3.1"
- SystemNotAcceptingNetworkMessages = ".3.2"
- SystemNotCapableOfSelectedFeatures = ".3.3"
- MessageTooBigForSystem = ".3.4"
- OtherOrUndefinedNetworkOrRoutingStatus = ".4.0"
- NoAnswerFromHost = ".4.1"
- BadConnection = ".4.2"
- RoutingServerFailure = ".4.3"
- UnableToRoute = ".4.4"
- NetworkCongestion = ".4.5"
- RoutingLoopDetected = ".4.6"
- DeliveryTimeExpired = ".4.7"
- OtherOrUndefinedProtocolStatus = ".5.0"
- InvalidCommand = ".5.1"
- SyntaxError = ".5.2"
- TooManyRecipients = ".5.3"
- InvalidCommandArguments = ".5.4"
- WrongProtocolVersion = ".5.5"
- OtherOrUndefinedMediaError = ".6.0"
- MediaNotSupported = ".6.1"
- ConversionRequiredAndProhibited = ".6.2"
- ConversionRequiredButNotSupported = ".6.3"
- ConversionWithLossPerformed = ".6.4"
- ConversionFailed = ".6.5"
- )
- var defaultTexts = struct {
- m map[EnhancedStatusCode]string
- }{m: map[EnhancedStatusCode]string{
- EnhancedStatusCode{ClassSuccess, ".0.0"}: "OK",
- EnhancedStatusCode{ClassSuccess, ".1.0"}: "OK",
- EnhancedStatusCode{ClassSuccess, ".1.5"}: "OK",
- EnhancedStatusCode{ClassSuccess, ".5.0"}: "OK",
- EnhancedStatusCode{ClassTransientFailure, ".5.3"}: "Too many recipients",
- EnhancedStatusCode{ClassTransientFailure, ".5.4"}: "Relay access denied",
- EnhancedStatusCode{ClassPermanentFailure, ".5.1"}: "Invalid command",
- }}
- // Response type for Stringer interface
- type Response struct {
- EnhancedCode subjectDetail
- BasicCode int
- Class class
- // Comment is optional
- Comment string
- cached string
- }
- // it looks like this ".5.4"
- type subjectDetail string
- // EnhancedStatus are the ones that look like 2.1.0
- type EnhancedStatusCode struct {
- Class class
- SubjectDetailCode subjectDetail
- }
- // String returns a string representation of EnhancedStatus
- func (e EnhancedStatusCode) String() string {
- return fmt.Sprintf("%d%s", e.Class, e.SubjectDetailCode)
- }
- // String returns a custom Response as a string
- func (r *Response) String() string {
- if r.cached != "" {
- return r.cached
- }
- if r.EnhancedCode == "" {
- r.cached = r.Comment
- return r.Comment
- }
- basicCode := r.BasicCode
- comment := r.Comment
- if len(comment) == 0 && r.BasicCode == 0 {
- var ok bool
- if comment, ok = defaultTexts.m[EnhancedStatusCode{r.Class, r.EnhancedCode}]; !ok {
- switch r.Class {
- case 2:
- comment = "OK"
- case 4:
- comment = "Temporary failure."
- case 5:
- comment = "Permanent failure."
- }
- }
- }
- e := EnhancedStatusCode{r.Class, r.EnhancedCode}
- if r.BasicCode == 0 {
- basicCode = getBasicStatusCode(e)
- }
- r.cached = fmt.Sprintf("%d %s %s", basicCode, e.String(), comment)
- return r.cached
- }
- // getBasicStatusCode gets the basic status code from codeMap, or fallback code if not mapped
- func getBasicStatusCode(e EnhancedStatusCode) int {
- if val, ok := codeMap.m[e]; ok {
- return val
- }
- // Fallback if code is not defined
- return int(e.Class) * 100
- }
|