enhanced.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  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. FailSyntaxError *Response
  119. FailReadLimitExceededDataCmd *Response
  120. FailMessageSizeExceeded *Response
  121. FailReadErrorDataCmd *Response
  122. FailPathTooLong *Response
  123. FailInvalidAddress *Response
  124. FailLocalPartTooLong *Response
  125. FailDomainTooLong *Response
  126. FailBackendNotRunning *Response
  127. FailBackendTransaction *Response
  128. FailBackendTimeout *Response
  129. FailRcptCmd *Response
  130. // The 400's
  131. ErrorTooManyRecipients *Response
  132. ErrorRelayDenied *Response
  133. ErrorShutdown *Response
  134. // The 200's
  135. SuccessMailCmd *Response
  136. SuccessRcptCmd *Response
  137. SuccessResetCmd *Response
  138. SuccessVerifyCmd *Response
  139. SuccessNoopCmd *Response
  140. SuccessQuitCmd *Response
  141. SuccessDataCmd *Response
  142. SuccessStartTLSCmd *Response
  143. SuccessMessageQueued *Response
  144. }
  145. // Called automatically during package load to build up the Responses struct
  146. func init() {
  147. Canned = Responses{}
  148. Canned.FailLineTooLong = &Response{
  149. EnhancedCode: InvalidCommand,
  150. BasicCode: 554,
  151. Class: ClassPermanentFailure,
  152. Comment: "Line too long.",
  153. }
  154. Canned.FailNestedMailCmd = &Response{
  155. EnhancedCode: InvalidCommand,
  156. BasicCode: 503,
  157. Class: ClassPermanentFailure,
  158. Comment: "Error: nested MAIL command",
  159. }
  160. Canned.SuccessMailCmd = &Response{
  161. EnhancedCode: OtherAddressStatus,
  162. Class: ClassSuccess,
  163. }
  164. Canned.SuccessRcptCmd = &Response{
  165. EnhancedCode: DestinationMailboxAddressValid,
  166. Class: ClassSuccess,
  167. }
  168. Canned.SuccessResetCmd = Canned.SuccessMailCmd
  169. Canned.SuccessNoopCmd = &Response{
  170. EnhancedCode: OtherStatus,
  171. Class: ClassSuccess,
  172. }
  173. Canned.SuccessVerifyCmd = &Response{
  174. EnhancedCode: OtherOrUndefinedProtocolStatus,
  175. BasicCode: 252,
  176. Class: ClassSuccess,
  177. Comment: "Cannot verify user",
  178. }
  179. Canned.ErrorTooManyRecipients = &Response{
  180. EnhancedCode: TooManyRecipients,
  181. BasicCode: 452,
  182. Class: ClassTransientFailure,
  183. Comment: "Too many recipients",
  184. }
  185. Canned.ErrorRelayDenied = &Response{
  186. EnhancedCode: BadDestinationMailboxAddress,
  187. BasicCode: 454,
  188. Class: ClassTransientFailure,
  189. Comment: "Error: Relay access denied:",
  190. }
  191. Canned.SuccessQuitCmd = &Response{
  192. EnhancedCode: OtherStatus,
  193. BasicCode: 221,
  194. Class: ClassSuccess,
  195. Comment: "Bye",
  196. }
  197. Canned.FailNoSenderDataCmd = &Response{
  198. EnhancedCode: InvalidCommand,
  199. BasicCode: 503,
  200. Class: ClassPermanentFailure,
  201. Comment: "Error: No sender",
  202. }
  203. Canned.FailNoRecipientsDataCmd = &Response{
  204. EnhancedCode: InvalidCommand,
  205. BasicCode: 503,
  206. Class: ClassPermanentFailure,
  207. Comment: "Error: No recipients",
  208. }
  209. Canned.SuccessDataCmd = &Response{
  210. BasicCode: 354,
  211. Comment: "354 Enter message, ending with '.' on a line by itself",
  212. }
  213. Canned.SuccessStartTLSCmd = &Response{
  214. EnhancedCode: OtherStatus,
  215. BasicCode: 220,
  216. Class: ClassSuccess,
  217. Comment: "Ready to start TLS",
  218. }
  219. Canned.FailUnrecognizedCmd = &Response{
  220. EnhancedCode: InvalidCommand,
  221. BasicCode: 554,
  222. Class: ClassPermanentFailure,
  223. Comment: "Unrecognized command",
  224. }
  225. Canned.FailMaxUnrecognizedCmd = &Response{
  226. EnhancedCode: InvalidCommand,
  227. BasicCode: 554,
  228. Class: ClassPermanentFailure,
  229. Comment: "Too many unrecognized commands",
  230. }
  231. Canned.ErrorShutdown = &Response{
  232. EnhancedCode: OtherOrUndefinedMailSystemStatus,
  233. BasicCode: 421,
  234. Class: ClassTransientFailure,
  235. Comment: "Server is shutting down. Please try again later. Sayonara!",
  236. }
  237. Canned.FailSyntaxError = &Response{
  238. EnhancedCode: SyntaxError,
  239. BasicCode: 550,
  240. Class: ClassPermanentFailure,
  241. Comment: "Syntax error",
  242. }
  243. Canned.FailReadLimitExceededDataCmd = &Response{
  244. EnhancedCode: MessageLengthExceedsAdministrativeLimit,
  245. BasicCode: 550,
  246. Class: ClassPermanentFailure,
  247. Comment: "Error:",
  248. }
  249. Canned.FailMessageSizeExceeded = &Response{
  250. EnhancedCode: OtherOrUndefinedNetworkOrRoutingStatus,
  251. BasicCode: 552,
  252. Class: ClassPermanentFailure,
  253. Comment: "Error:",
  254. }
  255. Canned.FailReadErrorDataCmd = &Response{
  256. EnhancedCode: OtherOrUndefinedMailSystemStatus,
  257. BasicCode: 451,
  258. Class: ClassTransientFailure,
  259. Comment: "Error:",
  260. }
  261. Canned.FailPathTooLong = &Response{
  262. EnhancedCode: InvalidCommandArguments,
  263. BasicCode: 550,
  264. Class: ClassPermanentFailure,
  265. Comment: "Path too long",
  266. }
  267. Canned.FailInvalidAddress = &Response{
  268. EnhancedCode: InvalidCommandArguments,
  269. BasicCode: 501,
  270. Class: ClassPermanentFailure,
  271. Comment: "Invalid address",
  272. }
  273. Canned.FailLocalPartTooLong = &Response{
  274. EnhancedCode: InvalidCommandArguments,
  275. BasicCode: 550,
  276. Class: ClassPermanentFailure,
  277. Comment: "Local part too long, cannot exceed 64 characters",
  278. }
  279. Canned.FailDomainTooLong = &Response{
  280. EnhancedCode: InvalidCommandArguments,
  281. BasicCode: 550,
  282. Class: ClassPermanentFailure,
  283. Comment: "Domain cannot exceed 255 characters",
  284. }
  285. Canned.FailBackendNotRunning = &Response{
  286. EnhancedCode: OtherOrUndefinedProtocolStatus,
  287. BasicCode: 554,
  288. Class: ClassPermanentFailure,
  289. Comment: "Transaction failed - backend not running",
  290. }
  291. Canned.FailBackendTransaction = &Response{
  292. EnhancedCode: OtherOrUndefinedProtocolStatus,
  293. BasicCode: 554,
  294. Class: ClassPermanentFailure,
  295. Comment: "Error:",
  296. }
  297. Canned.SuccessMessageQueued = &Response{
  298. EnhancedCode: OtherStatus,
  299. BasicCode: 250,
  300. Class: ClassSuccess,
  301. Comment: "OK: queued as",
  302. }
  303. Canned.FailBackendTimeout = &Response{
  304. EnhancedCode: OtherOrUndefinedProtocolStatus,
  305. BasicCode: 554,
  306. Class: ClassPermanentFailure,
  307. Comment: "Error: transaction timeout",
  308. }
  309. Canned.FailRcptCmd = &Response{
  310. EnhancedCode: BadDestinationMailboxAddress,
  311. BasicCode: 550,
  312. Class: ClassPermanentFailure,
  313. Comment: "User unknown in local recipient table",
  314. }
  315. }
  316. // DefaultMap contains defined default codes (RfC 3463)
  317. const (
  318. OtherStatus = ".0.0"
  319. OtherAddressStatus = ".1.0"
  320. BadDestinationMailboxAddress = ".1.1"
  321. BadDestinationSystemAddress = ".1.2"
  322. BadDestinationMailboxAddressSyntax = ".1.3"
  323. DestinationMailboxAddressAmbiguous = ".1.4"
  324. DestinationMailboxAddressValid = ".1.5"
  325. MailboxHasMoved = ".1.6"
  326. BadSendersMailboxAddressSyntax = ".1.7"
  327. BadSendersSystemAddress = ".1.8"
  328. OtherOrUndefinedMailboxStatus = ".2.0"
  329. MailboxDisabled = ".2.1"
  330. MailboxFull = ".2.2"
  331. MessageLengthExceedsAdministrativeLimit = ".2.3"
  332. MailingListExpansionProblem = ".2.4"
  333. OtherOrUndefinedMailSystemStatus = ".3.0"
  334. MailSystemFull = ".3.1"
  335. SystemNotAcceptingNetworkMessages = ".3.2"
  336. SystemNotCapableOfSelectedFeatures = ".3.3"
  337. MessageTooBigForSystem = ".3.4"
  338. OtherOrUndefinedNetworkOrRoutingStatus = ".4.0"
  339. NoAnswerFromHost = ".4.1"
  340. BadConnection = ".4.2"
  341. RoutingServerFailure = ".4.3"
  342. UnableToRoute = ".4.4"
  343. NetworkCongestion = ".4.5"
  344. RoutingLoopDetected = ".4.6"
  345. DeliveryTimeExpired = ".4.7"
  346. OtherOrUndefinedProtocolStatus = ".5.0"
  347. InvalidCommand = ".5.1"
  348. SyntaxError = ".5.2"
  349. TooManyRecipients = ".5.3"
  350. InvalidCommandArguments = ".5.4"
  351. WrongProtocolVersion = ".5.5"
  352. OtherOrUndefinedMediaError = ".6.0"
  353. MediaNotSupported = ".6.1"
  354. ConversionRequiredAndProhibited = ".6.2"
  355. ConversionRequiredButNotSupported = ".6.3"
  356. ConversionWithLossPerformed = ".6.4"
  357. ConversionFailed = ".6.5"
  358. )
  359. var defaultTexts = struct {
  360. m map[EnhancedStatusCode]string
  361. }{m: map[EnhancedStatusCode]string{
  362. EnhancedStatusCode{ClassSuccess, ".0.0"}: "OK",
  363. EnhancedStatusCode{ClassSuccess, ".1.0"}: "OK",
  364. EnhancedStatusCode{ClassSuccess, ".1.5"}: "OK",
  365. EnhancedStatusCode{ClassSuccess, ".5.0"}: "OK",
  366. EnhancedStatusCode{ClassTransientFailure, ".5.3"}: "Too many recipients",
  367. EnhancedStatusCode{ClassTransientFailure, ".5.4"}: "Relay access denied",
  368. EnhancedStatusCode{ClassPermanentFailure, ".5.1"}: "Invalid command",
  369. }}
  370. // Response type for Stringer interface
  371. type Response struct {
  372. EnhancedCode subjectDetail
  373. BasicCode int
  374. Class class
  375. // Comment is optional
  376. Comment string
  377. cached string
  378. }
  379. // it looks like this ".5.4"
  380. type subjectDetail string
  381. // EnhancedStatus are the ones that look like 2.1.0
  382. type EnhancedStatusCode struct {
  383. Class class
  384. SubjectDetailCode subjectDetail
  385. }
  386. // String returns a string representation of EnhancedStatus
  387. func (e EnhancedStatusCode) String() string {
  388. return fmt.Sprintf("%d%s", e.Class, e.SubjectDetailCode)
  389. }
  390. // String returns a custom Response as a string
  391. func (r *Response) String() string {
  392. if r.cached != "" {
  393. return r.cached
  394. }
  395. if r.EnhancedCode == "" {
  396. r.cached = r.Comment
  397. return r.Comment
  398. }
  399. basicCode := r.BasicCode
  400. comment := r.Comment
  401. if len(comment) == 0 && r.BasicCode == 0 {
  402. var ok bool
  403. if comment, ok = defaultTexts.m[EnhancedStatusCode{r.Class, r.EnhancedCode}]; !ok {
  404. switch r.Class {
  405. case 2:
  406. comment = "OK"
  407. case 4:
  408. comment = "Temporary failure."
  409. case 5:
  410. comment = "Permanent failure."
  411. }
  412. }
  413. }
  414. e := EnhancedStatusCode{r.Class, r.EnhancedCode}
  415. if r.BasicCode == 0 {
  416. basicCode = getBasicStatusCode(e)
  417. }
  418. r.cached = fmt.Sprintf("%d %s %s", basicCode, e.String(), comment)
  419. return r.cached
  420. }
  421. // getBasicStatusCode gets the basic status code from codeMap, or fallback code if not mapped
  422. func getBasicStatusCode(e EnhancedStatusCode) int {
  423. if val, ok := codeMap.m[e]; ok {
  424. return val
  425. }
  426. // Fallback if code is not defined
  427. return int(e.Class) * 100
  428. }