enhanced.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  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. FailRcptCmd string
  127. // The 400's
  128. ErrorTooManyRecipients string
  129. ErrorRelayDenied string
  130. ErrorShutdown string
  131. // The 200's
  132. SuccessMailCmd string
  133. SuccessRcptCmd string
  134. SuccessResetCmd string
  135. SuccessVerifyCmd string
  136. SuccessNoopCmd string
  137. SuccessQuitCmd string
  138. SuccessDataCmd string
  139. SuccessStartTLSCmd string
  140. SuccessMessageQueued string
  141. }
  142. // Called automatically during package load to build up the Responses struct
  143. func init() {
  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. Canned.FailRcptCmd = (&Response{
  298. EnhancedCode: BadDestinationMailboxAddress,
  299. BasicCode: 550,
  300. Class: ClassPermanentFailure,
  301. Comment: "User unknown in local recipient table",
  302. }).String()
  303. }
  304. // DefaultMap contains defined default codes (RfC 3463)
  305. const (
  306. OtherStatus = ".0.0"
  307. OtherAddressStatus = ".1.0"
  308. BadDestinationMailboxAddress = ".1.1"
  309. BadDestinationSystemAddress = ".1.2"
  310. BadDestinationMailboxAddressSyntax = ".1.3"
  311. DestinationMailboxAddressAmbiguous = ".1.4"
  312. DestinationMailboxAddressValid = ".1.5"
  313. MailboxHasMoved = ".1.6"
  314. BadSendersMailboxAddressSyntax = ".1.7"
  315. BadSendersSystemAddress = ".1.8"
  316. OtherOrUndefinedMailboxStatus = ".2.0"
  317. MailboxDisabled = ".2.1"
  318. MailboxFull = ".2.2"
  319. MessageLengthExceedsAdministrativeLimit = ".2.3"
  320. MailingListExpansionProblem = ".2.4"
  321. OtherOrUndefinedMailSystemStatus = ".3.0"
  322. MailSystemFull = ".3.1"
  323. SystemNotAcceptingNetworkMessages = ".3.2"
  324. SystemNotCapableOfSelectedFeatures = ".3.3"
  325. MessageTooBigForSystem = ".3.4"
  326. OtherOrUndefinedNetworkOrRoutingStatus = ".4.0"
  327. NoAnswerFromHost = ".4.1"
  328. BadConnection = ".4.2"
  329. RoutingServerFailure = ".4.3"
  330. UnableToRoute = ".4.4"
  331. NetworkCongestion = ".4.5"
  332. RoutingLoopDetected = ".4.6"
  333. DeliveryTimeExpired = ".4.7"
  334. OtherOrUndefinedProtocolStatus = ".5.0"
  335. InvalidCommand = ".5.1"
  336. SyntaxError = ".5.2"
  337. TooManyRecipients = ".5.3"
  338. InvalidCommandArguments = ".5.4"
  339. WrongProtocolVersion = ".5.5"
  340. OtherOrUndefinedMediaError = ".6.0"
  341. MediaNotSupported = ".6.1"
  342. ConversionRequiredAndProhibited = ".6.2"
  343. ConversionRequiredButNotSupported = ".6.3"
  344. ConversionWithLossPerformed = ".6.4"
  345. ConversionFailed = ".6.5"
  346. )
  347. var defaultTexts = struct {
  348. m map[EnhancedStatusCode]string
  349. }{m: map[EnhancedStatusCode]string{
  350. EnhancedStatusCode{ClassSuccess, ".0.0"}: "OK",
  351. EnhancedStatusCode{ClassSuccess, ".1.0"}: "OK",
  352. EnhancedStatusCode{ClassSuccess, ".1.5"}: "OK",
  353. EnhancedStatusCode{ClassSuccess, ".5.0"}: "OK",
  354. EnhancedStatusCode{ClassTransientFailure, ".5.3"}: "Too many recipients",
  355. EnhancedStatusCode{ClassTransientFailure, ".5.4"}: "Relay access denied",
  356. EnhancedStatusCode{ClassPermanentFailure, ".5.1"}: "Invalid command",
  357. }}
  358. // Response type for Stringer interface
  359. type Response struct {
  360. EnhancedCode subjectDetail
  361. BasicCode int
  362. Class class
  363. // Comment is optional
  364. Comment string
  365. }
  366. // it looks like this ".5.4"
  367. type subjectDetail string
  368. // EnhancedStatus are the ones that look like 2.1.0
  369. type EnhancedStatusCode struct {
  370. Class class
  371. SubjectDetailCode subjectDetail
  372. }
  373. // String returns a string representation of EnhancedStatus
  374. func (e EnhancedStatusCode) String() string {
  375. return fmt.Sprintf("%d%s", e.Class, e.SubjectDetailCode)
  376. }
  377. // String returns a custom Response as a string
  378. func (r *Response) String() string {
  379. basicCode := r.BasicCode
  380. comment := r.Comment
  381. if len(comment) == 0 && r.BasicCode == 0 {
  382. var ok bool
  383. if comment, ok = defaultTexts.m[EnhancedStatusCode{r.Class, r.EnhancedCode}]; !ok {
  384. switch r.Class {
  385. case 2:
  386. comment = "OK"
  387. case 4:
  388. comment = "Temporary failure."
  389. case 5:
  390. comment = "Permanent failure."
  391. }
  392. }
  393. }
  394. e := EnhancedStatusCode{r.Class, r.EnhancedCode}
  395. if r.BasicCode == 0 {
  396. basicCode = getBasicStatusCode(e)
  397. }
  398. return fmt.Sprintf("%d %s %s", basicCode, e.String(), comment)
  399. }
  400. // getBasicStatusCode gets the basic status code from codeMap, or fallback code if not mapped
  401. func getBasicStatusCode(e EnhancedStatusCode) int {
  402. if val, ok := codeMap.m[e]; ok {
  403. return val
  404. }
  405. // Fallback if code is not defined
  406. return int(e.Class) * 100
  407. }