enhanced.go 17 KB

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