enhanced.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. package response
  2. import (
  3. "fmt"
  4. )
  5. const (
  6. // ClassSuccess specifies that the DSN is reporting a positive delivery
  7. // action. Detail sub-codes may provide notification of
  8. // transformations required for delivery.
  9. ClassSuccess = 2
  10. // ClassTransientFailure - a persistent transient failure is one in which the message as
  11. // sent is valid, but persistence of some temporary condition has
  12. // caused abandonment or delay of attempts to send the message.
  13. // If this code accompanies a delivery failure report, sending in
  14. // the future may be successful.
  15. ClassTransientFailure = 4
  16. // ClassPermanentFailure - a permanent failure is one which is not likely to be resolved
  17. // by resending the message in the current form. Some change to
  18. // the message or the destination must be made for successful
  19. // delivery.
  20. ClassPermanentFailure = 5
  21. )
  22. // class is a type for ClassSuccess, ClassTransientFailure and ClassPermanentFailure constants
  23. type class int
  24. // String implements stringer for the class type
  25. func (c class) String() string {
  26. return fmt.Sprintf("%c00", c)
  27. }
  28. // codeMap for mapping Enhanced Status Code to Basic Code
  29. // Mapping according to https://www.iana.org/assignments/smtp-enhanced-status-codes/smtp-enhanced-status-codes.xml
  30. // This might not be entirely useful
  31. var codeMap = struct {
  32. m map[EnhancedStatusCode]int
  33. }{m: map[EnhancedStatusCode]int{
  34. EnhancedStatusCode{ClassSuccess, OtherAddressStatus}: 250,
  35. EnhancedStatusCode{ClassSuccess, DestinationMailboxAddressValid}: 250,
  36. EnhancedStatusCode{ClassSuccess, OtherOrUndefinedMailSystemStatus}: 250,
  37. EnhancedStatusCode{ClassSuccess, OtherOrUndefinedProtocolStatus}: 250,
  38. EnhancedStatusCode{ClassSuccess, ConversionWithLossPerformed}: 250,
  39. EnhancedStatusCode{ClassSuccess, ".6.8"}: 252,
  40. EnhancedStatusCode{ClassSuccess, ".7.0"}: 220,
  41. EnhancedStatusCode{ClassTransientFailure, BadDestinationMailboxAddress}: 451,
  42. EnhancedStatusCode{ClassTransientFailure, BadSendersSystemAddress}: 451,
  43. EnhancedStatusCode{ClassTransientFailure, MailingListExpansionProblem}: 450,
  44. EnhancedStatusCode{ClassTransientFailure, OtherOrUndefinedMailSystemStatus}: 421,
  45. EnhancedStatusCode{ClassTransientFailure, MailSystemFull}: 452,
  46. EnhancedStatusCode{ClassTransientFailure, SystemNotAcceptingNetworkMessages}: 453,
  47. EnhancedStatusCode{ClassTransientFailure, NoAnswerFromHost}: 451,
  48. EnhancedStatusCode{ClassTransientFailure, BadConnection}: 421,
  49. EnhancedStatusCode{ClassTransientFailure, RoutingServerFailure}: 451,
  50. EnhancedStatusCode{ClassTransientFailure, NetworkCongestion}: 451,
  51. EnhancedStatusCode{ClassTransientFailure, OtherOrUndefinedProtocolStatus}: 451,
  52. EnhancedStatusCode{ClassTransientFailure, InvalidCommand}: 430,
  53. EnhancedStatusCode{ClassTransientFailure, TooManyRecipients}: 452,
  54. EnhancedStatusCode{ClassTransientFailure, InvalidCommandArguments}: 451,
  55. EnhancedStatusCode{ClassTransientFailure, ".7.0"}: 450,
  56. EnhancedStatusCode{ClassTransientFailure, ".7.1"}: 451,
  57. EnhancedStatusCode{ClassTransientFailure, ".7.12"}: 422,
  58. EnhancedStatusCode{ClassTransientFailure, ".7.15"}: 450,
  59. EnhancedStatusCode{ClassTransientFailure, ".7.24"}: 451,
  60. EnhancedStatusCode{ClassPermanentFailure, BadDestinationMailboxAddress}: 550,
  61. EnhancedStatusCode{ClassPermanentFailure, BadDestinationMailboxAddressSyntax}: 501,
  62. EnhancedStatusCode{ClassPermanentFailure, BadSendersSystemAddress}: 501,
  63. EnhancedStatusCode{ClassPermanentFailure, ".1.10"}: 556,
  64. EnhancedStatusCode{ClassPermanentFailure, MailboxFull}: 552,
  65. EnhancedStatusCode{ClassPermanentFailure, MessageLengthExceedsAdministrativeLimit}: 552,
  66. EnhancedStatusCode{ClassPermanentFailure, OtherOrUndefinedMailSystemStatus}: 550,
  67. EnhancedStatusCode{ClassPermanentFailure, MessageTooBigForSystem}: 552,
  68. EnhancedStatusCode{ClassPermanentFailure, RoutingServerFailure}: 550,
  69. EnhancedStatusCode{ClassPermanentFailure, OtherOrUndefinedProtocolStatus}: 501,
  70. EnhancedStatusCode{ClassPermanentFailure, InvalidCommand}: 500,
  71. EnhancedStatusCode{ClassPermanentFailure, SyntaxError}: 500,
  72. EnhancedStatusCode{ClassPermanentFailure, InvalidCommandArguments}: 501,
  73. EnhancedStatusCode{ClassPermanentFailure, ".5.6"}: 500,
  74. EnhancedStatusCode{ClassPermanentFailure, ConversionRequiredButNotSupported}: 554,
  75. EnhancedStatusCode{ClassPermanentFailure, ".6.6"}: 554,
  76. EnhancedStatusCode{ClassPermanentFailure, ".6.7"}: 553,
  77. EnhancedStatusCode{ClassPermanentFailure, ".6.8"}: 550,
  78. EnhancedStatusCode{ClassPermanentFailure, ".6.9"}: 550,
  79. EnhancedStatusCode{ClassPermanentFailure, ".7.0"}: 550,
  80. EnhancedStatusCode{ClassPermanentFailure, ".7.1"}: 551,
  81. EnhancedStatusCode{ClassPermanentFailure, ".7.2"}: 550,
  82. EnhancedStatusCode{ClassPermanentFailure, ".7.4"}: 504,
  83. EnhancedStatusCode{ClassPermanentFailure, ".7.8"}: 554,
  84. EnhancedStatusCode{ClassPermanentFailure, ".7.9"}: 534,
  85. EnhancedStatusCode{ClassPermanentFailure, ".7.10"}: 523,
  86. EnhancedStatusCode{ClassPermanentFailure, ".7.11"}: 524,
  87. EnhancedStatusCode{ClassPermanentFailure, ".7.13"}: 525,
  88. EnhancedStatusCode{ClassPermanentFailure, ".7.14"}: 535,
  89. EnhancedStatusCode{ClassPermanentFailure, ".7.15"}: 550,
  90. EnhancedStatusCode{ClassPermanentFailure, ".7.16"}: 552,
  91. EnhancedStatusCode{ClassPermanentFailure, ".7.17"}: 500,
  92. EnhancedStatusCode{ClassPermanentFailure, ".7.18"}: 500,
  93. EnhancedStatusCode{ClassPermanentFailure, ".7.19"}: 500,
  94. EnhancedStatusCode{ClassPermanentFailure, ".7.20"}: 550,
  95. EnhancedStatusCode{ClassPermanentFailure, ".7.21"}: 550,
  96. EnhancedStatusCode{ClassPermanentFailure, ".7.22"}: 550,
  97. EnhancedStatusCode{ClassPermanentFailure, ".7.23"}: 550,
  98. EnhancedStatusCode{ClassPermanentFailure, ".7.24"}: 550,
  99. EnhancedStatusCode{ClassPermanentFailure, ".7.25"}: 550,
  100. EnhancedStatusCode{ClassPermanentFailure, ".7.26"}: 550,
  101. EnhancedStatusCode{ClassPermanentFailure, ".7.27"}: 550,
  102. }}
  103. var (
  104. // Canned is to be read-only, except in the init() function
  105. Canned Responses
  106. )
  107. // Responses has some already pre-constructed responses
  108. type Responses struct {
  109. // The 500's
  110. FailLineTooLong string
  111. FailNestedMailCmd string
  112. FailNoSenderDataCmd string
  113. FailNoRecipientsDataCmd string
  114. FailUnrecognizedCmd string
  115. FailMaxUnrecognizedCmd string
  116. FailReadLimitExceededDataCmd string
  117. FailMessageSizeExceeded string
  118. FailReadErrorDataCmd string
  119. FailPathTooLong string
  120. FailInvalidAddress string
  121. FailLocalPartTooLong string
  122. FailDomainTooLong string
  123. FailBackendNotRunning string
  124. FailBackendTransaction string
  125. FailBackendTimeout string
  126. // The 400's
  127. ErrorTooManyRecipients string
  128. ErrorRelayDenied string
  129. ErrorShutdown string
  130. // The 200's
  131. SuccessMailCmd string
  132. SuccessRcptCmd string
  133. SuccessResetCmd string
  134. SuccessVerifyCmd string
  135. SuccessNoopCmd string
  136. SuccessQuitCmd string
  137. SuccessDataCmd string
  138. SuccessStartTLSCmd string
  139. SuccessMessageQueued string
  140. }
  141. // Called automatically during package load to build up the Responses struct
  142. func init() {
  143. // There's even a Wikipedia page for canned responses: https://en.wikipedia.org/wiki/Canned_response
  144. Canned = Responses{}
  145. Canned.FailLineTooLong = (&Response{
  146. EnhancedCode: InvalidCommand,
  147. BasicCode: 554,
  148. Class: ClassPermanentFailure,
  149. Comment: "Line too long.",
  150. }).String()
  151. Canned.FailNestedMailCmd = (&Response{
  152. EnhancedCode: InvalidCommand,
  153. BasicCode: 503,
  154. Class: ClassPermanentFailure,
  155. Comment: "Error: nested MAIL command",
  156. }).String()
  157. Canned.SuccessMailCmd = (&Response{
  158. EnhancedCode: OtherAddressStatus,
  159. Class: ClassSuccess,
  160. }).String()
  161. Canned.SuccessRcptCmd = (&Response{
  162. EnhancedCode: DestinationMailboxAddressValid,
  163. Class: ClassSuccess,
  164. }).String()
  165. Canned.SuccessResetCmd = Canned.SuccessMailCmd
  166. Canned.SuccessNoopCmd = (&Response{
  167. EnhancedCode: OtherStatus,
  168. Class: ClassSuccess,
  169. }).String()
  170. Canned.SuccessVerifyCmd = (&Response{
  171. EnhancedCode: OtherOrUndefinedProtocolStatus,
  172. BasicCode: 252,
  173. Class: ClassSuccess,
  174. Comment: "Cannot verify user",
  175. }).String()
  176. Canned.ErrorTooManyRecipients = (&Response{
  177. EnhancedCode: TooManyRecipients,
  178. BasicCode: 452,
  179. Class: ClassTransientFailure,
  180. Comment: "Too many recipients",
  181. }).String()
  182. Canned.ErrorRelayDenied = (&Response{
  183. EnhancedCode: BadDestinationMailboxAddress,
  184. BasicCode: 454,
  185. Class: ClassTransientFailure,
  186. Comment: "Error: Relay access denied: ",
  187. }).String()
  188. Canned.SuccessQuitCmd = (&Response{
  189. EnhancedCode: OtherStatus,
  190. BasicCode: 221,
  191. Class: ClassSuccess,
  192. Comment: "Bye",
  193. }).String()
  194. Canned.FailNoSenderDataCmd = (&Response{
  195. EnhancedCode: InvalidCommand,
  196. BasicCode: 503,
  197. Class: ClassPermanentFailure,
  198. Comment: "Error: No sender",
  199. }).String()
  200. Canned.FailNoRecipientsDataCmd = (&Response{
  201. EnhancedCode: InvalidCommand,
  202. BasicCode: 503,
  203. Class: ClassPermanentFailure,
  204. Comment: "Error: No recipients",
  205. }).String()
  206. Canned.SuccessDataCmd = "354 Enter message, ending with '.' on a line by itself"
  207. Canned.SuccessStartTLSCmd = (&Response{
  208. EnhancedCode: OtherStatus,
  209. BasicCode: 220,
  210. Class: ClassSuccess,
  211. Comment: "Ready to start TLS",
  212. }).String()
  213. Canned.FailUnrecognizedCmd = (&Response{
  214. EnhancedCode: InvalidCommand,
  215. BasicCode: 554,
  216. Class: ClassPermanentFailure,
  217. Comment: "Unrecognized command",
  218. }).String()
  219. Canned.FailMaxUnrecognizedCmd = (&Response{
  220. EnhancedCode: InvalidCommand,
  221. BasicCode: 554,
  222. Class: ClassPermanentFailure,
  223. Comment: "Too many unrecognized commands",
  224. }).String()
  225. Canned.ErrorShutdown = (&Response{
  226. EnhancedCode: OtherOrUndefinedMailSystemStatus,
  227. BasicCode: 421,
  228. Class: ClassTransientFailure,
  229. Comment: "Server is shutting down. Please try again later. Sayonara!",
  230. }).String()
  231. Canned.FailReadLimitExceededDataCmd = (&Response{
  232. EnhancedCode: SyntaxError,
  233. BasicCode: 550,
  234. Class: ClassPermanentFailure,
  235. Comment: "Error: ",
  236. }).String()
  237. Canned.FailMessageSizeExceeded = (&Response{
  238. EnhancedCode: SyntaxError,
  239. BasicCode: 550,
  240. Class: ClassPermanentFailure,
  241. Comment: "Error: ",
  242. }).String()
  243. Canned.FailReadErrorDataCmd = (&Response{
  244. EnhancedCode: OtherOrUndefinedMailSystemStatus,
  245. BasicCode: 451,
  246. Class: ClassTransientFailure,
  247. Comment: "Error: ",
  248. }).String()
  249. Canned.FailPathTooLong = (&Response{
  250. EnhancedCode: InvalidCommandArguments,
  251. BasicCode: 550,
  252. Class: ClassPermanentFailure,
  253. Comment: "Path too long",
  254. }).String()
  255. Canned.FailInvalidAddress = (&Response{
  256. EnhancedCode: InvalidCommandArguments,
  257. BasicCode: 501,
  258. Class: ClassPermanentFailure,
  259. Comment: "Invalid address",
  260. }).String()
  261. Canned.FailLocalPartTooLong = (&Response{
  262. EnhancedCode: InvalidCommandArguments,
  263. BasicCode: 550,
  264. Class: ClassPermanentFailure,
  265. Comment: "Local part too long, cannot exceed 64 characters",
  266. }).String()
  267. Canned.FailDomainTooLong = (&Response{
  268. EnhancedCode: InvalidCommandArguments,
  269. BasicCode: 550,
  270. Class: ClassPermanentFailure,
  271. Comment: "Domain cannot exceed 255 characters",
  272. }).String()
  273. Canned.FailBackendNotRunning = (&Response{
  274. EnhancedCode: OtherOrUndefinedProtocolStatus,
  275. BasicCode: 554,
  276. Class: ClassPermanentFailure,
  277. Comment: "Transaction failed - backend not running ",
  278. }).String()
  279. Canned.FailBackendTransaction = (&Response{
  280. EnhancedCode: OtherOrUndefinedProtocolStatus,
  281. BasicCode: 554,
  282. Class: ClassPermanentFailure,
  283. Comment: "Error: ",
  284. }).String()
  285. Canned.SuccessMessageQueued = (&Response{
  286. EnhancedCode: OtherStatus,
  287. BasicCode: 250,
  288. Class: ClassSuccess,
  289. Comment: "OK : queued as ",
  290. }).String()
  291. Canned.FailBackendTimeout = (&Response{
  292. EnhancedCode: OtherOrUndefinedProtocolStatus,
  293. BasicCode: 554,
  294. Class: ClassPermanentFailure,
  295. Comment: "Error: transaction timeout",
  296. }).String()
  297. }
  298. // DefaultMap contains defined default codes (RfC 3463)
  299. const (
  300. OtherStatus = ".0.0"
  301. OtherAddressStatus = ".1.0"
  302. BadDestinationMailboxAddress = ".1.1"
  303. BadDestinationSystemAddress = ".1.2"
  304. BadDestinationMailboxAddressSyntax = ".1.3"
  305. DestinationMailboxAddressAmbiguous = ".1.4"
  306. DestinationMailboxAddressValid = ".1.5"
  307. MailboxHasMoved = ".1.6"
  308. BadSendersMailboxAddressSyntax = ".1.7"
  309. BadSendersSystemAddress = ".1.8"
  310. OtherOrUndefinedMailboxStatus = ".2.0"
  311. MailboxDisabled = ".2.1"
  312. MailboxFull = ".2.2"
  313. MessageLengthExceedsAdministrativeLimit = ".2.3"
  314. MailingListExpansionProblem = ".2.4"
  315. OtherOrUndefinedMailSystemStatus = ".3.0"
  316. MailSystemFull = ".3.1"
  317. SystemNotAcceptingNetworkMessages = ".3.2"
  318. SystemNotCapableOfSelectedFeatures = ".3.3"
  319. MessageTooBigForSystem = ".3.4"
  320. OtherOrUndefinedNetworkOrRoutingStatus = ".4.0"
  321. NoAnswerFromHost = ".4.1"
  322. BadConnection = ".4.2"
  323. RoutingServerFailure = ".4.3"
  324. UnableToRoute = ".4.4"
  325. NetworkCongestion = ".4.5"
  326. RoutingLoopDetected = ".4.6"
  327. DeliveryTimeExpired = ".4.7"
  328. OtherOrUndefinedProtocolStatus = ".5.0"
  329. InvalidCommand = ".5.1"
  330. SyntaxError = ".5.2"
  331. TooManyRecipients = ".5.3"
  332. InvalidCommandArguments = ".5.4"
  333. WrongProtocolVersion = ".5.5"
  334. OtherOrUndefinedMediaError = ".6.0"
  335. MediaNotSupported = ".6.1"
  336. ConversionRequiredAndProhibited = ".6.2"
  337. ConversionRequiredButNotSupported = ".6.3"
  338. ConversionWithLossPerformed = ".6.4"
  339. ConversionFailed = ".6.5"
  340. )
  341. var defaultTexts = struct {
  342. m map[EnhancedStatusCode]string
  343. }{m: map[EnhancedStatusCode]string{
  344. EnhancedStatusCode{ClassSuccess, ".0.0"}: "OK",
  345. EnhancedStatusCode{ClassSuccess, ".1.0"}: "OK",
  346. EnhancedStatusCode{ClassSuccess, ".1.5"}: "OK",
  347. EnhancedStatusCode{ClassSuccess, ".5.0"}: "OK",
  348. EnhancedStatusCode{ClassTransientFailure, ".5.3"}: "Too many recipients",
  349. EnhancedStatusCode{ClassTransientFailure, ".5.4"}: "Relay access denied",
  350. EnhancedStatusCode{ClassPermanentFailure, ".5.1"}: "Invalid command",
  351. }}
  352. // Response type for Stringer interface
  353. type Response struct {
  354. EnhancedCode subjectDetail
  355. BasicCode int
  356. Class class
  357. // Comment is optional
  358. Comment string
  359. }
  360. // it looks like this ".5.4"
  361. type subjectDetail string
  362. // EnhancedStatus are the ones that look like 2.1.0
  363. type EnhancedStatusCode struct {
  364. Class class
  365. SubjectDetailCode subjectDetail
  366. }
  367. // String returns a string representation of EnhancedStatus
  368. func (e EnhancedStatusCode) String() string {
  369. return fmt.Sprintf("%d%s", e.Class, e.SubjectDetailCode)
  370. }
  371. // String returns a custom Response as a string
  372. func (r *Response) String() string {
  373. basicCode := r.BasicCode
  374. comment := r.Comment
  375. if len(comment) == 0 && r.BasicCode == 0 {
  376. var ok bool
  377. if comment, ok = defaultTexts.m[EnhancedStatusCode{r.Class, r.EnhancedCode}]; !ok {
  378. switch r.Class {
  379. case 2:
  380. comment = "OK"
  381. case 4:
  382. comment = "Temporary failure."
  383. case 5:
  384. comment = "Permanent failure."
  385. }
  386. }
  387. }
  388. e := EnhancedStatusCode{r.Class, r.EnhancedCode}
  389. if r.BasicCode == 0 {
  390. basicCode = getBasicStatusCode(e)
  391. }
  392. return fmt.Sprintf("%d %s %s", basicCode, e.String(), comment)
  393. }
  394. // getBasicStatusCode gets the basic status code from codeMap, or fallback code if not mapped
  395. func getBasicStatusCode(e EnhancedStatusCode) int {
  396. if val, ok := codeMap.m[e]; ok {
  397. return val
  398. }
  399. // Fallback if code is not defined
  400. return int(e.Class) * 100
  401. }