enhanced.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. package response
  2. import (
  3. "fmt"
  4. "strconv"
  5. )
  6. const (
  7. // ClassSuccess specifies that the DSN is reporting a positive delivery
  8. // action. Detail sub-codes may provide notification of
  9. // transformations required for delivery.
  10. ClassSuccess = 2
  11. // ClassTransientFailure - a persistent transient failure is one in which the message as
  12. // sent is valid, but persistence of some temporary condition has
  13. // caused abandonment or delay of attempts to send the message.
  14. // If this code accompanies a delivery failure report, sending in
  15. // the future may be successful.
  16. ClassTransientFailure = 4
  17. // ClassPermanentFailure - a permanent failure is one which is not likely to be resolved
  18. // by resending the message in the current form. Some change to
  19. // the message or the destination must be made for successful
  20. // delivery.
  21. ClassPermanentFailure = 5
  22. )
  23. // codeMap for mapping Enhanced Status Code to Basic Code
  24. // Mapping according to https://www.iana.org/assignments/smtp-enhanced-status-codes/smtp-enhanced-status-codes.xml
  25. // This might not be entierly useful
  26. var codeMap = struct {
  27. m map[string]int
  28. }{m: map[string]int{
  29. "2.1.0": 250,
  30. "2.1.5": 250,
  31. "2.3.0": 250,
  32. "2.5.0": 250,
  33. "2.6.4": 250,
  34. "2.6.8": 252,
  35. "2.7.0": 220,
  36. "4.1.1": 451,
  37. "4.1.8": 451,
  38. "4.2.4": 450,
  39. "4.3.0": 421,
  40. "4.3.1": 452,
  41. "4.3.2": 453,
  42. "4.4.1": 451,
  43. "4.4.2": 421,
  44. "4.4.3": 451,
  45. "4.4.5": 451,
  46. "4.5.0": 451,
  47. "4.5.1": 430,
  48. "4.5.3": 452,
  49. "4.5.4": 451,
  50. "4.7.0": 450,
  51. "4.7.1": 451,
  52. "4.7.12": 422,
  53. "4.7.15": 450,
  54. "4.7.24": 451,
  55. "5.1.1": 550,
  56. "5.1.3": 501,
  57. "5.1.8": 501,
  58. "5.1.10": 556,
  59. "5.2.2": 552,
  60. "5.2.3": 552,
  61. "5.3.0": 550,
  62. "5.3.4": 552,
  63. "5.4.3": 550,
  64. "5.5.0": 501,
  65. "5.5.1": 500,
  66. "5.5.2": 500,
  67. "5.5.4": 501,
  68. "5.5.6": 500,
  69. "5.6.3": 554,
  70. "5.6.6": 554,
  71. "5.6.7": 553,
  72. "5.6.8": 550,
  73. "5.6.9": 550,
  74. "5.7.0": 550,
  75. "5.7.1": 551,
  76. "5.7.2": 550,
  77. "5.7.4": 504,
  78. "5.7.8": 554,
  79. "5.7.9": 534,
  80. "5.7.10": 523,
  81. "5.7.11": 524,
  82. "5.7.13": 525,
  83. "5.7.14": 535,
  84. "5.7.15": 550,
  85. "5.7.16": 552,
  86. "5.7.17": 500,
  87. "5.7.18": 500,
  88. "5.7.19": 500,
  89. "5.7.20": 550,
  90. "5.7.21": 550,
  91. "5.7.22": 550,
  92. "5.7.23": 550,
  93. "5.7.24": 550,
  94. "5.7.25": 550,
  95. "5.7.26": 550,
  96. "5.7.27": 550,
  97. }}
  98. // DefaultMap contains defined default codes (RfC 3463)
  99. const (
  100. OtherStatus = ".0.0"
  101. OtherAddressStatus = ".1.0"
  102. BadDestinationMailboxAddress = ".1.1"
  103. BadDestinationSystemAddress = ".1.2"
  104. BadDestinationMailboxAddressSyntax = ".1.3"
  105. DestinationMailboxAddressAmbiguous = ".1.4"
  106. DestinationMailboxAddressValid = ".1.5"
  107. MailboxHasMoved = ".1.6"
  108. BadSendersMailboxAddressSyntax = ".1.7"
  109. BadSendersSystemAddress = ".1.8"
  110. OtherOrUndefinedMailboxStatus = ".2.0"
  111. MailboxDisabled = ".2.1"
  112. MailboxFull = ".2.2"
  113. MessageLengthExceedsAdministrativeLimit = ".2.3"
  114. MailingListExpansionProblem = ".2.4"
  115. OtherOrUndefinedMailSystemStatus = ".3.0"
  116. MailSystemFull = ".3.1"
  117. SystemNotAcceptingNetworkMessages = ".3.2"
  118. SystemNotCapableOfSelectedFeatures = ".3.3"
  119. MessageTooBigForSystem = ".3.4"
  120. OtherOrUndefinedNetworkOrRoutingStatus = ".4.0"
  121. NoAnswerFromHost = ".4.1"
  122. BadConnection = ".4.2"
  123. RoutingServerFailure = ".4.3"
  124. UnableToRoute = ".4.4"
  125. NetworkCongestion = ".4.5"
  126. RoutingLoopDetected = ".4.6"
  127. DeliveryTimeExpired = ".4.7"
  128. OtherOrUndefinedProtocolStatus = ".5.0"
  129. InvalidCommand = ".5.1"
  130. SyntaxError = ".5.2"
  131. TooManyRecipients = ".5.3"
  132. InvalidCommandArguments = ".5.4"
  133. WrongProtocolVersion = ".5.5"
  134. OtherOrUndefinedMediaError = ".6.0"
  135. MediaNotSupported = ".6.1"
  136. ConversionRequiredAndProhibited = ".6.2"
  137. ConversionRequiredButNotSupported = ".6.3"
  138. ConversionWithLossPerformed = ".6.4"
  139. ConversionFailed = ".6.5"
  140. )
  141. // TODO: More defaults needed....
  142. var defaultTexts = struct {
  143. m map[string]string
  144. }{m: map[string]string{
  145. "2.0.0": "OK",
  146. "2.1.0": "OK",
  147. "2.1.5": "Recipient valid",
  148. "2.5.0": "OK",
  149. "4.5.3": "Too many recipients",
  150. "4.5.4": "Relay access denied",
  151. "5.5.1": "Invalid command",
  152. }}
  153. // Response type for Stringer interface
  154. type Response struct {
  155. EnhancedCode string
  156. BasicCode int
  157. Class int
  158. // Comment is optional
  159. Comment string
  160. }
  161. // Custom returns a custom Response Stringer
  162. func (r *Response) String() string {
  163. e := buildEnhancedResponseFromDefaultStatus(r.Class, r.EnhancedCode)
  164. basicCode := r.BasicCode
  165. comment := r.Comment
  166. if len(comment) == 0 && r.BasicCode == 0 {
  167. comment = defaultTexts.m[r.EnhancedCode]
  168. if len(comment) == 0 {
  169. switch r.Class {
  170. case 2:
  171. comment = "OK"
  172. case 4:
  173. comment = "Temporary failure."
  174. case 5:
  175. comment = "Permanent failure."
  176. }
  177. }
  178. }
  179. if r.BasicCode == 0 {
  180. basicCode = getBasicStatusCode(e)
  181. }
  182. return fmt.Sprintf("%d %s %s", basicCode, e, comment)
  183. }
  184. /*
  185. // CustomString builds an enhanced status code string using your custom string and basic code
  186. func CustomString(enhancedCode string, basicCode, class int, comment string) string {
  187. e := buildEnhancedResponseFromDefaultStatus(class, enhancedCode)
  188. return fmt.Sprintf("%d %s %s", basicCode, e, comment)
  189. }
  190. // String builds an enhanced status code string
  191. func String(enhancedCode string, class int) string {
  192. e := buildEnhancedResponseFromDefaultStatus(class, enhancedCode)
  193. basicCode := getBasicStatusCode(e)
  194. comment := defaultTexts.m[enhancedCode]
  195. if len(comment) == 0 {
  196. switch class {
  197. case 2:
  198. comment = "OK"
  199. case 4:
  200. comment = "Temporary failure."
  201. case 5:
  202. comment = "Permanent failure."
  203. }
  204. }
  205. return CustomString(enhancedCode, basicCode, class, comment)
  206. }
  207. */
  208. func getBasicStatusCode(enhancedStatusCode string) int {
  209. if val, ok := codeMap.m[enhancedStatusCode]; ok {
  210. return val
  211. }
  212. // Fallback if code is not defined
  213. fb, _ := strconv.Atoi(fmt.Sprintf("%c00", enhancedStatusCode[0]))
  214. return fb
  215. }
  216. func buildEnhancedResponseFromDefaultStatus(c int, status string) string {
  217. // Construct code
  218. return fmt.Sprintf("%d%s", c, status)
  219. }