/* Maddy Mail Server - Composable all-in-one email server. Copyright © 2019-2020 Max Mazurov , Maddy Mail Server contributors This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package exterrors import ( "fmt" "github.com/emersion/go-smtp" ) type EnhancedCode smtp.EnhancedCode func (ec EnhancedCode) FormatLog() string { return fmt.Sprintf("%d.%d.%d", ec[0], ec[1], ec[2]) } // SMTPError type is a copy of emersion/go-smtp.SMTPError type // that extends it with Fields method for logging and reporting // in maddy. It should be used instead of the go-smtp library type for all // errors. type SMTPError struct { // SMTP status code. Most of these codes are overly generic and are barely // useful. Nonetheless, take a look at the 'Associated basic status code' // in the SMTP Enhanced Status Codes registry (below), then check RFC 5321 // (Section 4.3.2) and pick what you like. Stick to 451 and 554 if there are // no useful codes. Code int // Enhanced SMTP status code. If you are unsure, take a look at // https://www.iana.org/assignments/smtp-enhanced-status-codes/smtp-enhanced-status-codes.xhtml EnhancedCode EnhancedCode // Error message that should be returned to the SMTP client. // Usually, it should be a short and generic description of the error // that excludes any details. Especially, for checks, avoid // mentioning the exact policy mechanism used to avoid disclosing the // server configuration details. Don't say "DNS error during DMARC check", // say "DNS error during policy check". Same goes for network and file I/O // errors. ESPECIALLY, don't include any configuration variables or object // identifiers in it. Message string // If the error was generated by a message check // this field includes module name. CheckName string // If the error was generated by a delivery target // this field includes module name. TargetName string // If the error was generated by a message modifier // this field includes module name. ModifierName string // If the error was generated as a result of another // error - this field contains the original error object. // // Err.Error() will be copied into the 'reason' field returned // by the Fields method unless a different values is specified // using the Reason field below. Err error // Textual explanation of the actual error reason. Defaults to the // Err.Error() value if Err is not nil, empty string otherwise. Reason string Misc map[string]interface{} } func (se *SMTPError) Unwrap() error { return se.Err } func (se *SMTPError) Fields() map[string]interface{} { ctx := make(map[string]interface{}, len(se.Misc)+3) for k, v := range se.Misc { ctx[k] = v } ctx["smtp_code"] = se.Code ctx["smtp_enchcode"] = se.EnhancedCode ctx["smtp_msg"] = se.Message if se.CheckName != "" { ctx["check"] = se.CheckName } if se.TargetName != "" { ctx["target"] = se.TargetName } if se.Reason != "" { ctx["reason"] = se.Reason } else if se.Err != nil { ctx["reason"] = se.Err.Error() } return ctx } // Temporary reports whether func (se *SMTPError) Temporary() bool { return se.Code/100 == 4 } func (se *SMTPError) Error() string { if se.Reason != "" { return se.Reason } if se.Err != nil { return se.Err.Error() } return se.Message } // SMTPCode is a convenience function that returns one of its arguments // depending on the result of exterrors.IsTemporary for the specified error // object. func SMTPCode(err error, temporaryCode, permanentCode int) int { if IsTemporary(err) { return temporaryCode } return permanentCode } // SMTPEnchCode is a convenience function changes the first number of the SMTP enhanced // status code based on the value exterrors.IsTemporary returns for the specified // error object. func SMTPEnchCode(err error, code EnhancedCode) EnhancedCode { if IsTemporary(err) { code[0] = 4 } code[0] = 5 return code }