sisimai 5.0.3 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/codecovio.yml +3 -1
  3. data/.github/workflows/rake-test.yml +7 -3
  4. data/ChangeLog.md +136 -0
  5. data/Makefile +4 -2
  6. data/README-JA.md +32 -22
  7. data/README.md +31 -21
  8. data/lib/sisimai/arf.rb +124 -213
  9. data/lib/sisimai/fact/json.rb +2 -2
  10. data/lib/sisimai/fact/yaml.rb +2 -2
  11. data/lib/sisimai/fact.rb +208 -173
  12. data/lib/sisimai/lda.rb +98 -0
  13. data/lib/sisimai/lhost/activehunter.rb +5 -4
  14. data/lib/sisimai/lhost/amazonses.rb +189 -305
  15. data/lib/sisimai/lhost/apachejames.rb +52 -55
  16. data/lib/sisimai/lhost/biglobe.rb +5 -6
  17. data/lib/sisimai/lhost/courier.rb +14 -12
  18. data/lib/sisimai/lhost/domino.rb +29 -29
  19. data/lib/sisimai/lhost/dragonfly.rb +113 -0
  20. data/lib/sisimai/lhost/einsundeins.rb +7 -8
  21. data/lib/sisimai/lhost/exchange2003.rb +10 -11
  22. data/lib/sisimai/lhost/exchange2007.rb +115 -104
  23. data/lib/sisimai/lhost/exim.rb +236 -246
  24. data/lib/sisimai/lhost/ezweb.rb +47 -55
  25. data/lib/sisimai/lhost/fml.rb +6 -7
  26. data/lib/sisimai/lhost/gmail.rb +36 -32
  27. data/lib/sisimai/lhost/gmx.rb +8 -20
  28. data/lib/sisimai/lhost/googlegroups.rb +13 -12
  29. data/lib/sisimai/lhost/googleworkspace.rb +94 -0
  30. data/lib/sisimai/lhost/imailserver.rb +11 -19
  31. data/lib/sisimai/lhost/interscanmss.rb +6 -5
  32. data/lib/sisimai/lhost/kddi.rb +7 -8
  33. data/lib/sisimai/lhost/mailfoundry.rb +6 -9
  34. data/lib/sisimai/lhost/mailmarshalsmtp.rb +6 -6
  35. data/lib/sisimai/lhost/messagingserver.rb +19 -17
  36. data/lib/sisimai/lhost/mfilter.rb +8 -7
  37. data/lib/sisimai/lhost/notes.rb +6 -8
  38. data/lib/sisimai/lhost/opensmtpd.rb +11 -9
  39. data/lib/sisimai/lhost/postfix.rb +29 -31
  40. data/lib/sisimai/lhost/qmail.rb +136 -112
  41. data/lib/sisimai/lhost/sendmail.rb +23 -22
  42. data/lib/sisimai/lhost/v5sendmail.rb +93 -64
  43. data/lib/sisimai/lhost/verizon.rb +6 -6
  44. data/lib/sisimai/lhost/x1.rb +4 -4
  45. data/lib/sisimai/lhost/x2.rb +4 -5
  46. data/lib/sisimai/lhost/x3.rb +5 -5
  47. data/lib/sisimai/lhost/x6.rb +4 -4
  48. data/lib/sisimai/lhost/zoho.rb +6 -6
  49. data/lib/sisimai/lhost.rb +21 -24
  50. data/lib/sisimai/mail/maildir.rb +1 -1
  51. data/lib/sisimai/mail/stdin.rb +1 -1
  52. data/lib/sisimai/message.rb +100 -153
  53. data/lib/sisimai/order.rb +22 -77
  54. data/lib/sisimai/reason/authfailure.rb +1 -4
  55. data/lib/sisimai/reason/badreputation.rb +3 -3
  56. data/lib/sisimai/reason/blocked.rb +7 -10
  57. data/lib/sisimai/reason/contenterror.rb +7 -1
  58. data/lib/sisimai/reason/exceedlimit.rb +1 -4
  59. data/lib/sisimai/reason/failedstarttls.rb +42 -0
  60. data/lib/sisimai/reason/filtered.rb +5 -4
  61. data/lib/sisimai/reason/hasmoved.rb +1 -2
  62. data/lib/sisimai/reason/hostunknown.rb +3 -3
  63. data/lib/sisimai/reason/mailboxfull.rb +2 -4
  64. data/lib/sisimai/reason/mailererror.rb +1 -2
  65. data/lib/sisimai/reason/mesgtoobig.rb +2 -4
  66. data/lib/sisimai/reason/norelaying.rb +3 -3
  67. data/lib/sisimai/reason/notaccept.rb +2 -3
  68. data/lib/sisimai/reason/notcompliantrfc.rb +10 -4
  69. data/lib/sisimai/reason/rejected.rb +2 -1
  70. data/lib/sisimai/reason/requireptr.rb +2 -2
  71. data/lib/sisimai/reason/securityerror.rb +1 -3
  72. data/lib/sisimai/reason/spamdetected.rb +6 -8
  73. data/lib/sisimai/reason/speeding.rb +1 -2
  74. data/lib/sisimai/reason/suppressed.rb +36 -0
  75. data/lib/sisimai/reason/suspend.rb +1 -3
  76. data/lib/sisimai/reason/systemerror.rb +5 -0
  77. data/lib/sisimai/reason/toomanyconn.rb +1 -2
  78. data/lib/sisimai/reason/userunknown.rb +1 -1
  79. data/lib/sisimai/reason/virusdetected.rb +5 -6
  80. data/lib/sisimai/reason.rb +82 -78
  81. data/lib/sisimai/rfc1123.rb +152 -0
  82. data/lib/sisimai/rfc1894.rb +102 -62
  83. data/lib/sisimai/rfc2045.rb +2 -1
  84. data/lib/sisimai/rfc3464/thirdparty.rb +102 -0
  85. data/lib/sisimai/rfc3464.rb +224 -345
  86. data/lib/sisimai/rfc3834.rb +3 -3
  87. data/lib/sisimai/rfc5322.rb +7 -17
  88. data/lib/sisimai/rfc791.rb +69 -0
  89. data/lib/sisimai/rhost/aol.rb +36 -0
  90. data/lib/sisimai/rhost/apple.rb +95 -0
  91. data/lib/sisimai/rhost/cox.rb +84 -34
  92. data/lib/sisimai/rhost/facebook.rb +100 -0
  93. data/lib/sisimai/rhost/franceptt.rb +87 -83
  94. data/lib/sisimai/rhost/godaddy.rb +208 -45
  95. data/lib/sisimai/rhost/google.rb +22 -22
  96. data/lib/sisimai/rhost/gsuite.rb +42 -0
  97. data/lib/sisimai/rhost/iua.rb +5 -5
  98. data/lib/sisimai/rhost/kddi.rb +9 -7
  99. data/lib/sisimai/rhost/messagelabs.rb +37 -0
  100. data/lib/sisimai/rhost/microsoft.rb +60 -54
  101. data/lib/sisimai/rhost/mimecast.rb +44 -31
  102. data/lib/sisimai/rhost/nttdocomo.rb +5 -4
  103. data/lib/sisimai/rhost/outlook.rb +36 -0
  104. data/lib/sisimai/rhost/spectrum.rb +102 -41
  105. data/lib/sisimai/rhost/tencent.rb +48 -26
  106. data/lib/sisimai/rhost/yahooinc.rb +111 -0
  107. data/lib/sisimai/rhost.rb +65 -42
  108. data/lib/sisimai/smtp/command.rb +31 -21
  109. data/lib/sisimai/smtp/failure.rb +103 -0
  110. data/lib/sisimai/smtp/reply.rb +29 -24
  111. data/lib/sisimai/smtp/status.rb +36 -19
  112. data/lib/sisimai/smtp/transcript.rb +18 -18
  113. data/lib/sisimai/string.rb +0 -46
  114. data/lib/sisimai/version.rb +1 -1
  115. data/lib/sisimai.rb +0 -6
  116. data/set-of-emails/maildir/bsd/lhost-dragonfly-01.eml +36 -0
  117. data/set-of-emails/maildir/bsd/lhost-dragonfly-02.eml +32 -0
  118. data/set-of-emails/maildir/bsd/lhost-dragonfly-03.eml +32 -0
  119. data/set-of-emails/maildir/bsd/lhost-dragonfly-04.eml +31 -0
  120. data/set-of-emails/maildir/bsd/lhost-dragonfly-05.eml +32 -0
  121. data/set-of-emails/maildir/bsd/lhost-dragonfly-06.eml +32 -0
  122. data/set-of-emails/maildir/bsd/lhost-dragonfly-07.eml +32 -0
  123. data/set-of-emails/maildir/bsd/lhost-dragonfly-08.eml +32 -0
  124. data/set-of-emails/maildir/bsd/lhost-dragonfly-09.eml +32 -0
  125. data/set-of-emails/maildir/bsd/lhost-dragonfly-10.eml +32 -0
  126. data/set-of-emails/maildir/bsd/lhost-dragonfly-11.eml +32 -0
  127. data/set-of-emails/maildir/bsd/lhost-dragonfly-12.eml +32 -0
  128. data/set-of-emails/maildir/bsd/lhost-dragonfly-13.eml +32 -0
  129. data/set-of-emails/maildir/bsd/lhost-dragonfly-14.eml +32 -0
  130. data/set-of-emails/maildir/bsd/lhost-dragonfly-15.eml +32 -0
  131. data/set-of-emails/maildir/bsd/lhost-dragonfly-16.eml +32 -0
  132. data/set-of-emails/maildir/bsd/lhost-dragonfly-17.eml +32 -0
  133. data/set-of-emails/maildir/bsd/lhost-dragonfly-18.eml +32 -0
  134. data/set-of-emails/maildir/bsd/lhost-dragonfly-19.eml +32 -0
  135. data/set-of-emails/maildir/bsd/lhost-dragonfly-20.eml +32 -0
  136. data/set-of-emails/maildir/bsd/lhost-dragonfly-21.eml +33 -0
  137. data/set-of-emails/maildir/bsd/lhost-dragonfly-22.eml +32 -0
  138. data/set-of-emails/maildir/bsd/lhost-dragonfly-23.eml +32 -0
  139. data/set-of-emails/maildir/bsd/lhost-dragonfly-24.eml +32 -0
  140. data/set-of-emails/maildir/bsd/lhost-dragonfly-25.eml +33 -0
  141. data/set-of-emails/maildir/bsd/lhost-dragonfly-26.eml +33 -0
  142. data/set-of-emails/maildir/bsd/lhost-dragonfly-27.eml +47 -0
  143. data/set-of-emails/maildir/bsd/lhost-dragonfly-28.eml +47 -0
  144. data/set-of-emails/maildir/bsd/lhost-dragonfly-29.eml +32 -0
  145. data/set-of-emails/maildir/bsd/lhost-dragonfly-30.eml +32 -0
  146. data/set-of-emails/maildir/bsd/lhost-opensmtpd-10.eml +58 -0
  147. data/set-of-emails/maildir/bsd/lhost-opensmtpd-11.eml +58 -0
  148. data/set-of-emails/maildir/bsd/lhost-opensmtpd-12.eml +62 -0
  149. data/set-of-emails/maildir/bsd/lhost-opensmtpd-13.eml +62 -0
  150. data/set-of-emails/maildir/bsd/lhost-opensmtpd-14.eml +58 -0
  151. data/set-of-emails/maildir/bsd/lhost-opensmtpd-15.eml +63 -0
  152. data/set-of-emails/maildir/bsd/lhost-opensmtpd-16.eml +62 -0
  153. data/set-of-emails/maildir/bsd/lhost-opensmtpd-17.eml +63 -0
  154. data/set-of-emails/maildir/bsd/lhost-postfix-30.eml +81 -81
  155. data/set-of-emails/maildir/bsd/lhost-qmail-11.eml +29 -0
  156. data/set-of-emails/maildir/bsd/lhost-qmail-12.eml +29 -0
  157. data/set-of-emails/maildir/bsd/lhost-qmail-13.eml +29 -0
  158. data/set-of-emails/maildir/bsd/lhost-qmail-14.eml +34 -0
  159. data/set-of-emails/maildir/bsd/lhost-qmail-15.eml +30 -0
  160. data/set-of-emails/maildir/bsd/lhost-qmail-16.eml +31 -0
  161. data/set-of-emails/maildir/bsd/lhost-qmail-17.eml +38 -0
  162. data/set-of-emails/maildir/bsd/lhost-qmail-18.eml +31 -0
  163. data/set-of-emails/maildir/bsd/lhost-qmail-19.eml +31 -0
  164. data/set-of-emails/maildir/bsd/lhost-qmail-20.eml +46 -0
  165. data/set-of-emails/maildir/bsd/lhost-qmail-21.eml +41 -0
  166. data/set-of-emails/maildir/bsd/lhost-qmail-22.eml +42 -0
  167. data/set-of-emails/maildir/bsd/lhost-qmail-23.eml +43 -0
  168. data/set-of-emails/maildir/bsd/lhost-qmail-24.eml +43 -0
  169. data/set-of-emails/maildir/bsd/lhost-qmail-25.eml +54 -0
  170. data/set-of-emails/maildir/bsd/{lhost-aol-03.eml → rhost-aol-03.eml} +1264 -1264
  171. data/set-of-emails/maildir/bsd/{lhost-aol-04.eml → rhost-aol-04.eml} +1260 -1260
  172. data/set-of-emails/maildir/bsd/{lhost-aol-05.eml → rhost-aol-05.eml} +105 -105
  173. data/set-of-emails/maildir/bsd/{lhost-aol-06.eml → rhost-aol-06.eml} +105 -105
  174. data/set-of-emails/maildir/bsd/rhost-apple-01.eml +79 -0
  175. data/set-of-emails/maildir/bsd/rhost-apple-02.eml +81 -0
  176. data/set-of-emails/maildir/bsd/rhost-apple-03.eml +75 -0
  177. data/set-of-emails/maildir/bsd/rhost-apple-04.eml +74 -0
  178. data/set-of-emails/maildir/bsd/rhost-gsuite-01.eml +189 -0
  179. data/set-of-emails/maildir/bsd/rhost-gsuite-02.eml +180 -0
  180. data/set-of-emails/maildir/bsd/rhost-gsuite-03.eml +251 -0
  181. data/set-of-emails/maildir/bsd/rhost-gsuite-04.eml +211 -0
  182. data/set-of-emails/maildir/bsd/rhost-gsuite-05.eml +226 -0
  183. data/set-of-emails/maildir/bsd/rhost-gsuite-06.eml +257 -0
  184. data/set-of-emails/maildir/bsd/rhost-gsuite-07.eml +289 -0
  185. data/set-of-emails/maildir/bsd/rhost-gsuite-08.eml +231 -0
  186. data/set-of-emails/maildir/bsd/rhost-gsuite-09.eml +231 -0
  187. data/set-of-emails/maildir/bsd/rhost-gsuite-10.eml +254 -0
  188. data/set-of-emails/maildir/bsd/rhost-gsuite-11.eml +228 -0
  189. data/set-of-emails/maildir/bsd/rhost-gsuite-12.eml +271 -0
  190. data/set-of-emails/maildir/bsd/rhost-gsuite-13.eml +261 -0
  191. data/set-of-emails/maildir/bsd/rhost-gsuite-14.eml +273 -0
  192. data/set-of-emails/maildir/bsd/rhost-gsuite-15.eml +229 -0
  193. data/set-of-emails/maildir/bsd/{lhost-messagelabs-01.eml → rhost-messagelabs-01.eml} +93 -93
  194. data/set-of-emails/maildir/bsd/rhost-outlook-01.eml +72 -0
  195. data/set-of-emails/maildir/bsd/rhost-outlook-02.eml +72 -0
  196. data/set-of-emails/maildir/bsd/rhost-outlook-03.eml +72 -0
  197. data/set-of-emails/maildir/bsd/rhost-outlook-04.eml +79 -0
  198. data/set-of-emails/maildir/bsd/rhost-outlook-06.eml +75 -0
  199. data/set-of-emails/maildir/bsd/rhost-outlook-07.eml +70 -0
  200. data/set-of-emails/maildir/bsd/rhost-outlook-08.eml +70 -0
  201. data/set-of-emails/maildir/bsd/rhost-outlook-09.eml +56 -0
  202. data/set-of-emails/maildir/bsd/rhost-yahooinc-01.eml +80 -0
  203. data/set-of-emails/maildir/bsd/rhost-yahooinc-02.eml +83 -0
  204. data/set-of-emails/maildir/bsd/rhost-yahooinc-03.eml +125 -0
  205. data/set-of-emails/maildir/tmp/arf-22.eml +49 -0
  206. data/set-of-emails/maildir/tmp/arf-23.eml +49 -0
  207. data/set-of-emails/maildir/tmp/arf-24.eml +50 -0
  208. data/set-of-emails/maildir/tmp/lhost-exim-07.eml +28 -0
  209. metadata +136 -56
  210. data/lib/sisimai/lhost/amavis.rb +0 -163
  211. data/lib/sisimai/lhost/amazonworkmail.rb +0 -127
  212. data/lib/sisimai/lhost/aol.rb +0 -125
  213. data/lib/sisimai/lhost/barracuda.rb +0 -92
  214. data/lib/sisimai/lhost/bigfoot.rb +0 -125
  215. data/lib/sisimai/lhost/facebook.rb +0 -188
  216. data/lib/sisimai/lhost/gsuite.rb +0 -194
  217. data/lib/sisimai/lhost/mailru.rb +0 -214
  218. data/lib/sisimai/lhost/mcafee.rb +0 -109
  219. data/lib/sisimai/lhost/messagelabs.rb +0 -119
  220. data/lib/sisimai/lhost/mxlogic.rb +0 -198
  221. data/lib/sisimai/lhost/office365.rb +0 -252
  222. data/lib/sisimai/lhost/outlook.rb +0 -129
  223. data/lib/sisimai/lhost/powermta.rb +0 -118
  224. data/lib/sisimai/lhost/receivingses.rb +0 -126
  225. data/lib/sisimai/lhost/sendgrid.rb +0 -150
  226. data/lib/sisimai/lhost/surfcontrol.rb +0 -105
  227. data/lib/sisimai/lhost/x4.rb +0 -269
  228. data/lib/sisimai/lhost/x5.rb +0 -112
  229. data/lib/sisimai/lhost/yahoo.rb +0 -102
  230. data/lib/sisimai/lhost/yandex.rb +0 -118
  231. data/lib/sisimai/mda.rb +0 -121
  232. data/lib/sisimai/smtp/error.rb +0 -119
  233. /data/set-of-emails/maildir/bsd/{lhost-googlegroups-15.eml → lhost-googleworkspace-01.eml} +0 -0
  234. /data/set-of-emails/maildir/bsd/{lhost-x4-08.eml → lhost-x2-06.eml} +0 -0
  235. /data/set-of-emails/maildir/bsd/{lhost-gsuite-01.eml → rfc3464-51.eml} +0 -0
  236. /data/set-of-emails/maildir/bsd/{lhost-gsuite-03.eml → rfc3464-52.eml} +0 -0
  237. /data/set-of-emails/maildir/bsd/{lhost-gsuite-04.eml → rfc3464-53.eml} +0 -0
  238. /data/set-of-emails/maildir/bsd/{lhost-gsuite-05.eml → rfc3464-54.eml} +0 -0
  239. /data/set-of-emails/maildir/bsd/{lhost-gsuite-06.eml → rfc3464-55.eml} +0 -0
  240. /data/set-of-emails/maildir/bsd/{lhost-gsuite-07.eml → rfc3464-56.eml} +0 -0
  241. /data/set-of-emails/maildir/bsd/{lhost-gsuite-08.eml → rfc3464-57.eml} +0 -0
  242. /data/set-of-emails/maildir/bsd/{lhost-gsuite-09.eml → rfc3464-58.eml} +0 -0
  243. /data/set-of-emails/maildir/bsd/{lhost-gsuite-10.eml → rfc3464-59.eml} +0 -0
  244. /data/set-of-emails/maildir/bsd/{lhost-gsuite-11.eml → rfc3464-60.eml} +0 -0
  245. /data/set-of-emails/maildir/bsd/{lhost-gsuite-12.eml → rfc3464-61.eml} +0 -0
  246. /data/set-of-emails/maildir/bsd/{lhost-gsuite-13.eml → rfc3464-62.eml} +0 -0
  247. /data/set-of-emails/maildir/bsd/{lhost-gsuite-14.eml → rfc3464-63.eml} +0 -0
  248. /data/set-of-emails/maildir/bsd/{lhost-gsuite-15.eml → rfc3464-64.eml} +0 -0
  249. /data/set-of-emails/maildir/bsd/{lhost-gsuite-02.eml → rfc3464-65.eml} +0 -0
  250. /data/set-of-emails/maildir/bsd/{lhost-aol-01.eml → rhost-aol-01.eml} +0 -0
  251. /data/set-of-emails/maildir/bsd/{lhost-aol-02.eml → rhost-aol-02.eml} +0 -0
  252. /data/set-of-emails/maildir/bsd/{lhost-facebook-03.eml → rhost-facebook-03.eml} +0 -0
  253. /data/set-of-emails/maildir/bsd/{lhost-facebook-04.eml → rhost-facebook-04.eml} +0 -0
  254. /data/set-of-emails/maildir/bsd/{lhost-messagelabs-02.eml → rhost-messagelabs-02.eml} +0 -0
  255. /data/set-of-emails/maildir/bsd/{lhost-messagelabs-03.eml → rhost-messagelabs-03.eml} +0 -0
  256. /data/set-of-emails/maildir/{bsd → tmp}/rfc3464-37.eml +0 -0
  257. /data/set-of-emails/maildir/{bsd → tmp}/rfc3464-38.eml +0 -0
  258. /data/set-of-emails/maildir/{bsd → tmp}/rfc3464-39.eml +0 -0
@@ -1,12 +1,27 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::Qmail parses a bounce email which created by qmail. Methods in the module are called
3
- # from only Sisimai::Message.
2
+ # Sisimai::Lhost::Qmail decodes a bounce email which created by qmail https://cr.yp.to/qmail.html
3
+ # or qmail clones or notqmail https://notqmail.org/.
4
+ # Methods in the module are called from only Sisimai::Message.
4
5
  module Qmail
5
6
  class << self
6
7
  require 'sisimai/lhost'
8
+ require 'sisimai/string'
7
9
 
8
10
  Indicators = Sisimai::Lhost.INDICATORS
9
- Boundaries = ['--- Below this line is a copy of the message.'].freeze
11
+ Boundaries = [
12
+ # qmail-send.c:qmail_puts(&qqt,*sender.s ? "--- Below this line is a copy of the message.\n\n" :...
13
+ "--- Below this line is a copy of the message.", # qmail-1.03
14
+ "--- Below this line is a copy of the mail header.",
15
+ "--- Below the next line is a copy of the message.", # The followings are the qmail clone
16
+ "--- Mensaje original adjunto.",
17
+ "Content-Type: message/rfc822",
18
+ "Original message follows.",
19
+ ].freeze
20
+ RelayedVia = [["(qmail ", "invoked for bounce)"], ["(qmail ", "invoked from ", "network)"]].freeze
21
+ EmailTitle = [
22
+ "failure notice", # qmail-send.c:Subject: failure notice\n\
23
+ "Failure Notice", # Yahoo
24
+ ].freeze
10
25
  StartingOf = {
11
26
  # qmail-remote.c:248| if (code >= 500) {
12
27
  # qmail-remote.c:249| out("h"); outhost(); out(" does not like recipient.\n");
@@ -15,97 +30,119 @@ module Sisimai::Lhost
15
30
  #
16
31
  # Characters: K,Z,D in qmail-qmqpc.c, qmail-send.c, qmail-rspawn.c
17
32
  # K = success, Z = temporary error, D = permanent error
18
- error: ['Remote host said:'],
19
- message: ['Hi. This is the qmail'],
20
- rhost: ['Giving up on ', 'Connected to ', 'remote host '],
33
+ "error" => ["Remote host said:"],
34
+ "message" => [
35
+ "Hi. This is the qmail", # qmail-send.c:Hi. This is the qmail-send program at ");
36
+ "He/Her is not ", # The followings are the qmail clone
37
+ "unable to deliver your message to the following addresses",
38
+ "Su mensaje no pudo ser entregado",
39
+ "Sorry, we were unable to deliver your message to the following address",
40
+ "This is the machine generated message from mail service",
41
+ "This is the mail delivery agent at",
42
+ "Unable to deliver message to the following address",
43
+ "unable to deliver your message to the following addresses",
44
+ "Unfortunately, your mail was not delivered to the following address:",
45
+ "Your mail message to the following address",
46
+ "Your message to the following addresses",
47
+ "We're sorry.",
48
+ ],
49
+ "rhost" => ['Giving up on ', 'Connected to ', 'remote host '],
21
50
  }.freeze
22
51
  CommandSet = {
23
- # Error text regular expressions which defined in qmail-remote.c
24
52
  # qmail-remote.c:225| if (smtpcode() != 220) quit("ZConnected to "," but greeting failed");
25
- 'conn' => [' but greeting failed.'],
53
+ "CONN" => [" but greeting failed."],
26
54
  # qmail-remote.c:231| if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected");
27
- 'ehlo' => [' but my name was rejected.'],
55
+ "EHLO" => [" but my name was rejected."],
28
56
  # qmail-remote.c:238| if (code >= 500) quit("DConnected to "," but sender was rejected");
29
57
  # reason = rejected
30
- 'mail' => [' but sender was rejected.'],
58
+ "MAIL" => [" but sender was rejected."],
31
59
  # qmail-remote.c:249| out("h"); outhost(); out(" does not like recipient.\n");
32
60
  # qmail-remote.c:253| out("s"); outhost(); out(" does not like recipient.\n");
33
61
  # reason = userunknown
34
- 'rcpt' => [' does not like recipient.'],
62
+ "RCPT" => [" does not like recipient."],
35
63
  # qmail-remote.c:265| if (code >= 500) quit("D"," failed on DATA command");
36
64
  # qmail-remote.c:266| if (code >= 400) quit("Z"," failed on DATA command");
37
65
  # qmail-remote.c:271| if (code >= 500) quit("D"," failed after I sent the message");
38
66
  # qmail-remote.c:272| if (code >= 400) quit("Z"," failed after I sent the message");
39
- 'data' => [' failed on DATA command', ' failed after I sent the message'],
67
+ "DATA" => [" failed on DATA command", " failed after I sent the message"],
40
68
  }.freeze
41
69
 
42
70
  # qmail-send.c:922| ... (&dline[c],"I'm not going to try again; this message has been in the queue too long.\n")) nomem();
43
- HasExpired = 'this message has been in the queue too long.'
44
- OnHoldPair = [' does not like recipient.', 'this message has been in the queue too long.'].freeze
71
+ # qmail-remote-fallback.patch
72
+ HasExpired = "this message has been in the queue too long.".freeze
73
+ OnHoldPair = [" does not like recipient.", "this message has been in the queue too long."].freeze
45
74
  FailOnLDAP = {
46
75
  # qmail-ldap-1.03-20040101.patch:19817 - 19866
47
- 'suspend' => ['Mailaddress is administrative?le?y disabled'], # 5.2.1
48
- 'userunknown' => ['Sorry, no mailbox here by that name'], # 5.1.1
49
- 'exceedlimit' => ['The message exeeded the maximum size the user accepts'], # 5.2.3
50
- 'systemerror' => [
51
- 'Automatic homedir creator crashed', # 4.3.0
52
- 'Illegal value in LDAP attribute', # 5.3.5
53
- 'LDAP attribute is not given but mandatory', # 5.3.5
54
- 'Timeout while performing search on LDAP server', # 4.4.3
55
- 'Too many results returned but needs to be unique', # 5.3.5
56
- 'Permanent error while executing qmail-forward', # 5.4.4
57
- 'Temporary error in automatic homedir creation', # 4.3.0 or 5.3.0
58
- 'Temporary error while executing qmail-forward', # 4.4.4
59
- 'Temporary failure in LDAP lookup', # 4.4.3
60
- 'Unable to contact LDAP server', # 4.4.3
61
- 'Unable to login into LDAP server, bad credentials',# 4.4.3
76
+ "exceedlimit" => ["The message exeeded the maximum size the user accepts"], # 5.2.3
77
+ "userunknown" => ["Sorry, no mailbox here by that name"], # 5.1.1
78
+ "suspend" => [ # 5.2.1
79
+ "Mailaddress is administrativly disabled",
80
+ "Mailaddress is administrativley disabled",
81
+ "Mailaddress is administratively disabled",
82
+ "Mailaddress is administrativeley disabled",
83
+ ],
84
+ "systemerror" => [
85
+ "Automatic homedir creator crashed", # 4.3.0
86
+ "Illegal value in LDAP attribute", # 5.3.5
87
+ "LDAP attribute is not given but mandatory", # 5.3.5
88
+ "Timeout while performing search on LDAP server", # 4.4.3
89
+ "Too many results returned but needs to be unique", # 5.3.5
90
+ "Permanent error while executing qmail-forward", # 5.4.4
91
+ "Temporary error in automatic homedir creation", # 4.3.0 or 5.3.0
92
+ "Temporary error while executing qmail-forward", # 4.4.4
93
+ "Temporary failure in LDAP lookup", # 4.4.3
94
+ "Unable to contact LDAP server", # 4.4.3
95
+ "Unable to login into LDAP server, bad credentials",# 4.4.3
62
96
  ],
63
97
  }.freeze
64
98
  MessagesOf = {
65
- # qmail-local.c:589| strerr_die1x(100,"Sorry, no mailbox here by that name. (#5.1.1)");
66
- # qmail-remote.c:253| out("s"); outhost(); out(" does not like recipient.\n");
67
- 'userunknown' => ['no mailbox here by that name', 'does not like recipient.'],
99
+ # qmail-remote.c:68| Sorry, I couldn't find any host by that name. (#4.1.2)\n"); zerodie();
100
+ # qmail-remote.c:78| Sorry, I couldn't find any host named ");
101
+ "hostunknown" => ["Sorry, I couldn't find any host "],
68
102
  # error_str.c:192| X(EDQUOT,"disk quota exceeded")
69
- 'mailboxfull' => ['disk quota exceeded'],
103
+ "mailboxfull" => ["disk quota exceeded"],
70
104
  # qmail-qmtpd.c:233| ... result = "Dsorry, that message size exceeds my databytes limit (#5.3.4)";
71
105
  # qmail-smtpd.c:391| ... out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); return;
72
- 'mesgtoobig' => ['Message size exceeds fixed maximum message size:'],
73
- # qmail-remote.c:68| Sorry, I couldn't find any host by that name. (#4.1.2)\n"); zerodie();
74
- # qmail-remote.c:78| Sorry, I couldn't find any host named ");
75
- 'hostunknown' => ["Sorry, I couldn't find any host "],
76
- 'systemfull' => ['Requested action not taken: mailbox unavailable (not enough free space)'],
77
- 'systemerror' => [
78
- 'bad interpreter: No such file or directory',
79
- 'system error',
80
- 'Unable to',
106
+ "mesgtoobig" => ["Message size exceeds fixed maximum message size:"],
107
+ "networkerror"=> [
108
+ "Sorry, I wasn't able to establish an SMTP connection",
109
+ "Sorry. Although I'm listed as a best-preference MX or A for that host",
110
+ ],
111
+ "notaccept" => [
112
+ # notqmail 1.08 returns the following error message when the destination MX is NullMX
113
+ "Sorry, I couldn't find a mail exchanger or IP address",
81
114
  ],
82
- 'networkerror' => [
83
- "Sorry, I wasn't able to establish an SMTP connection",
84
- "Sorry, I couldn't find a mail exchanger or IP address",
85
- "Sorry. Although I'm listed as a best-preference MX or A for that host",
115
+ "systemerror" => [
116
+ "bad interpreter: No such file or directory",
117
+ "system error",
118
+ "Unable to",
86
119
  ],
120
+ "systemfull" => ["Requested action not taken: mailbox unavailable (not enough free space)"],
121
+ # qmail-local.c:589| strerr_die1x(100,"Sorry, no mailbox here by that name. (#5.1.1)");
122
+ # qmail-remote.c:253| out("s"); outhost(); out(" does not like recipient.\n");
123
+ "userunknown" => ["no mailbox here by that name"],
87
124
  }.freeze
88
125
 
89
- # Parse bounce messages from qmail
126
+ # @abstract Decodes the bounce message from qmail
90
127
  # @param [Hash] mhead Message headers of a bounce email
91
128
  # @param [String] mbody Message body of a bounce email
92
129
  # @return [Hash] Bounce data list and message/rfc822 part
93
- # @return [Nil] it failed to parse or the arguments are missing
130
+ # @return [Nil] it failed to decode or the arguments are missing
94
131
  def inquire(mhead, mbody)
95
132
  # Pre process email headers and the body part of the message which generated
96
133
  # by qmail, see https://cr.yp.to/qmail.html
97
134
  # e.g.) Received: (qmail 12345 invoked for bounce); 29 Apr 2009 12:34:56 -0000
98
135
  # Subject: failure notice
99
- tryto = [['(qmail ', 'invoked for bounce)'], ['(qmail ', 'invoked from ', 'network)']]
100
- match = 0
101
- match += 1 if mhead['subject'] == 'failure notice'
102
- mhead['received'].each do |e|
136
+ proceedsto = false
137
+ proceedsto = true if EmailTitle.any? { |a| mhead["subject"] == a }
138
+ mhead["received"].each do |e|
103
139
  # Received: (qmail 2222 invoked for bounce);29 Apr 2017 23:34:45 +0900
104
140
  # Received: (qmail 2202 invoked from network); 29 Apr 2018 00:00:00 +0900
105
- match += 1 if tryto.any? { |a| Sisimai::String.aligned(e, a) }
141
+ proceedsto = true if RelayedVia.any? { |a| Sisimai::String.aligned(e, a) }
106
142
  end
107
- return nil unless match > 0
143
+ return nil if proceedsto == false
108
144
 
145
+ require "sisimai/smtp/command"
109
146
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
110
147
  emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
111
148
  bodyslices = emailparts[0].split("\n")
@@ -118,7 +155,7 @@ module Sisimai::Lhost
118
155
  # line of the beginning of the original message.
119
156
  if readcursor == 0
120
157
  # Beginning of the bounce message or delivery status part
121
- readcursor |= Indicators[:deliverystatus] if e.start_with?(StartingOf[:message][0])
158
+ readcursor |= Indicators[:deliverystatus] if StartingOf["message"].any? { |a| e.include?(a) }
122
159
  next
123
160
  end
124
161
  next if (readcursor & Indicators[:deliverystatus]) == 0
@@ -130,31 +167,29 @@ module Sisimai::Lhost
130
167
  # Giving up on 192.0.2.153.
131
168
  v = dscontents[-1]
132
169
 
133
- if e.start_with?('<') && Sisimai::String.aligned(e, ['<', '@', '>', ':'])
170
+ if e.start_with?('<') && Sisimai::String.aligned(e, ['<', '@', '>:'])
134
171
  # <kijitora@example.jp>:
135
- if v['recipient']
172
+ if v["recipient"] != ""
136
173
  # There are multiple recipient addresses in the message body.
137
174
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
138
175
  v = dscontents[-1]
139
176
  end
140
- v['recipient'] = Sisimai::Address.s3s4(e[e.index('<'), e.size])
177
+ v["recipient"] = Sisimai::Address.s3s4(e[e.index("<"), e.size])
141
178
  recipients += 1
142
179
 
143
180
  elsif dscontents.size == recipients
144
181
  # Append error message
145
- next if e.empty?
146
- v['diagnosis'] ||= ''
147
- v['diagnosis'] << e + ' '
148
- v['alterrors'] = e if e.start_with?(StartingOf[:error][0])
182
+ v["diagnosis"] << e + " "
183
+ v["alterrors"] = e if e.start_with?(StartingOf["error"][0])
149
184
 
150
- next if v['rhost']
151
- StartingOf[:rhost].each do |r|
185
+ next if v["rhost"] != ""
186
+ StartingOf["rhost"].each do |r|
152
187
  # Find a remote host name
153
188
  p1 = e.index(r); next unless p1
154
189
  cm = r.size
155
- p2 = e.index(' ', p1 + cm + 1) || p2 = e.rindex('.')
190
+ p2 = e.index(" ", p1 + cm + 1) || p2 = e.rindex(".")
156
191
 
157
- v['rhost'] = e[p1 + cm, p2 - p1 - cm]
192
+ v["rhost"] = Sisimai::String.sweep(e[p1 + cm, p2 - p1 - cm])
158
193
  break
159
194
  end
160
195
  end
@@ -162,72 +197,61 @@ module Sisimai::Lhost
162
197
  return nil unless recipients > 0
163
198
 
164
199
  dscontents.each do |e|
165
- e['agent'] = 'qmail'
166
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''
167
-
168
- unless e['command']
169
- # Get the SMTP command name for the session
170
- CommandSet.each_key do |r|
171
- # Verify each regular expression of SMTP commands
172
- next unless CommandSet[r].any? { |a| e['diagnosis'].include?(a) }
173
- e['command'] = r.upcase
174
- break
175
- end
200
+ # Tidy up the error message in e['diagnosis'], Try to detect the bounce reason.
201
+ e["diagnosis"] = Sisimai::String.sweep(e["diagnosis"])
176
202
 
177
- if e['diagnosis'].include?('Sorry, no SMTP connection got far enough; most progress was ')
178
- # Get the last SMTP command:from the error message
179
- e['command'] ||= Sisimai::SMTP::Command.find(e['diagnosis']) || ''
180
- end
203
+ # Get the SMTP command name for the session
204
+ CommandSet.each_key do |r|
205
+ # Get the last SMTP command
206
+ next unless CommandSet[r].any? { |a| e["diagnosis"].include?(a) }
207
+ e["command"] = r
208
+ break
181
209
  end
182
210
 
183
- # Detect the reason of bounce
184
- if e['command'] == 'MAIL'
185
- # MAIL | Connected to 192.0.2.135 but sender was rejected.
186
- e['reason'] = 'rejected'
211
+ if e["diagnosis"].include?("Sorry, no SMTP connection got far enough")
212
+ # Sorry, no SMTP connection got far enough; most progress was RCPT TO response; ...
213
+ e["command"] = Sisimai::SMTP::Command.find(e["diagnosis"]) if e["command"].empty?
214
+ end
187
215
 
188
- elsif %w[HELO EHLO].index(e['command'])
216
+ # Detect the reason of bounce
217
+ if %w[HELO EHLO].index(e["command"])
189
218
  # HELO | Connected to 192.0.2.135 but my name was rejected.
190
- e['reason'] = 'blocked'
219
+ e["reason"] = "blocked"
191
220
  else
192
221
  # Try to match with each error message in the table
193
- if Sisimai::String.aligned(e['diagnosis'], OnHoldPair)
222
+ if Sisimai::String.aligned(e["diagnosis"], OnHoldPair)
194
223
  # To decide the reason require pattern match with Sisimai::Reason::* modules
195
- e['reason'] = 'onhold'
224
+ e["reason"] = "onhold"
196
225
  else
197
- MessagesOf.each_key do |r|
198
- # Verify each regular expression of session errors
199
- if e['alterrors']
200
- # Check the value of "alterrors"
201
- next unless MessagesOf[r].any? { |a| e['alterrors'].include?(a) }
202
- e['reason'] = r
226
+ # Check that the error message includes any of message patterns or not
227
+ [e["alterrors"], e["diagnosis"]].each do |f|
228
+ # Try to detect an error reason
229
+ break if e["reason"] != ""
230
+ next unless f
231
+ MessagesOf.each_key do |r|
232
+ # The key is a bounce reason name
233
+ next unless MessagesOf[r].any? { |a| f.include?(a) }
234
+ e["reason"] = r
235
+ break
203
236
  end
204
- break if e['reason']
205
-
206
- next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
207
- e['reason'] = r
208
- break
209
- end
237
+ break if e["reason"]
210
238
 
211
- unless e['reason']
212
239
  FailOnLDAP.each_key do |r|
213
- # Verify each regular expression of LDAP errors
214
- next unless FailOnLDAP[r].any? { |a| e['diagnosis'].include?(a) }
215
- e['reason'] = r
240
+ # The key is a bounce reason name
241
+ next unless FailOnLDAP[r].any? { |a| f.include?(a) }
242
+ e["reason"] = r
216
243
  break
217
244
  end
218
- end
219
-
220
- unless e['reason']
221
- e['reason'] = 'expired' if e['diagnosis'].include?(HasExpired)
245
+ break if e["reason"]
246
+ e["reason"] = "expired" if e["diagnosis"].include?(HasExpired)
222
247
  end
223
248
  end
224
249
  end
225
250
 
226
- e['command'] ||= Sisimai::SMTP::Command.find(e['diagnosis'])
227
- e['status'] = Sisimai::SMTP::Status.find(e['diagnosis']) || ''
251
+ e["command"] = Sisimai::SMTP::Command.find(e["diagnosis"]) if e["command"].empty?
228
252
  end
229
253
 
230
- return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
254
+ return { "ds" => dscontents, "rfc822" => emailparts[1] }
231
255
  end
232
256
  def description; return 'qmail'; end
233
257
  end
@@ -1,9 +1,10 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::Sendmail parses a bounce email which created by v8 Sendmail. Methods in the module
3
- # are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::Sendmail decodes a bounce email which created by Sendmail Open Source https://sendmail.org/.
3
+ # Methods in the module are called from only Sisimai::Message.
4
4
  module Sendmail
5
5
  class << self
6
6
  require 'sisimai/lhost'
7
+ require 'sisimai/rfc1123'
7
8
  require 'sisimai/smtp/reply'
8
9
  require 'sisimai/smtp/status'
9
10
  require 'sisimai/smtp/command'
@@ -23,11 +24,11 @@ module Sisimai::Lhost
23
24
  error: ['... while talking to '],
24
25
  }.freeze
25
26
 
26
- # Parse bounce messages from Sendmail
27
+ # @abstract Decodes the bounce message from Sendmail Open Source
27
28
  # @param [Hash] mhead Message headers of a bounce email
28
29
  # @param [String] mbody Message body of a bounce email
29
30
  # @return [Hash] Bounce data list and message/rfc822 part
30
- # @return [Nil] it failed to parse or the arguments are missing
31
+ # @return [Nil] it failed to decode or the arguments are missing
31
32
  def inquire(mhead, mbody)
32
33
  return nil if mhead['x-aol-ip']
33
34
  match = nil
@@ -55,24 +56,25 @@ module Sisimai::Lhost
55
56
  readslices << e # Save the current line for the next loop
56
57
 
57
58
  if readcursor == 0
58
- # Beginning of the bounce message or message/delivery-status part
59
+ # Beginning of the bounce message or the message/delivery-status part
59
60
  readcursor |= Indicators[:deliverystatus] if e.start_with?(StartingOf[:message][0])
60
61
  next
61
62
  end
62
63
  next if (readcursor & Indicators[:deliverystatus]) == 0
63
64
  next if e.empty?
64
65
 
65
- if f = Sisimai::RFC1894.match(e)
66
+ f = Sisimai::RFC1894.match(e)
67
+ if f > 0
66
68
  # "e" matched with any field defined in RFC3464
67
69
  o = Sisimai::RFC1894.field(e) || next
68
70
  v = dscontents[-1]
69
71
 
70
- if o[-1] == 'addr'
72
+ if o[3] == 'addr'
71
73
  # Final-Recipient: rfc822; kijitora@example.jp
72
74
  # X-Actual-Recipient: rfc822; kijitora@example.co.jp
73
75
  if o[0] == 'final-recipient'
74
76
  # Final-Recipient: rfc822; kijitora@example.jp
75
- if v['recipient']
77
+ if v["recipient"] != ""
76
78
  # There are multiple recipient addresses in the message body.
77
79
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
78
80
  v = dscontents[-1]
@@ -83,16 +85,17 @@ module Sisimai::Lhost
83
85
  # X-Actual-Recipient: rfc822; kijitora@example.co.jp
84
86
  v['alias'] = o[2]
85
87
  end
86
- elsif o[-1] == 'code'
88
+ elsif o[3] == 'code'
87
89
  # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
88
90
  v['spec'] = o[1]
89
91
  v['diagnosis'] = o[2]
90
92
  else
91
93
  # Other DSN fields defined in RFC3464
92
94
  next unless fieldtable[o[0]]
95
+ next if o[3] == "host" && Sisimai::RFC1123.is_internethost(o[2]) == false
93
96
  v[fieldtable[o[0]]] = o[2]
94
97
 
95
- next unless f
98
+ next unless f == 1
96
99
  permessage[fieldtable[o[0]]] = o[2]
97
100
  end
98
101
  else
@@ -109,15 +112,15 @@ module Sisimai::Lhost
109
112
  # Arrival-Date: Wed, 29 Apr 2009 16:03:18 +0900
110
113
  unless e.start_with?(' ')
111
114
  if e.start_with?('>>> ')
112
- # >>> DATA
113
- thecommand = Sisimai::SMTP::Command.find(e)
115
+ # >>> DATA (Client Command)
116
+ thecommand = Sisimai::SMTP::Command.find(e) if thecommand.empty?
114
117
 
115
118
  elsif e.start_with?('<<< ')
116
- # <<< Response
119
+ # <<< Response from the SMTP server
117
120
  cv = e[4, e.size - 4]
118
121
  esmtpreply << cv unless esmtpreply.index(cv)
119
122
  else
120
- # Detect SMTP session error or connection error
123
+ # Detect an SMTP session error or a connection error
121
124
  next if sessionerr
122
125
  if e.start_with?(StartingOf[:error][0])
123
126
  # ----- Transcript of session follows -----
@@ -134,8 +137,8 @@ module Sisimai::Lhost
134
137
  # ----- Transcript of session follows -----
135
138
  # Message could not be delivered for too long
136
139
  # Message will be deleted from queue
137
- cr = Sisimai::SMTP::Reply.find(e) || ''
138
- cs = Sisimai::SMTP::Status.find(e) || ''
140
+ cr = Sisimai::SMTP::Reply.find(e)
141
+ cs = Sisimai::SMTP::Status.find(e)
139
142
 
140
143
  if cr.size + cs.size > 7
141
144
  # 550 5.1.2 <kijitora@example.org>... Message
@@ -167,13 +170,12 @@ module Sisimai::Lhost
167
170
 
168
171
  dscontents.each do |e|
169
172
  # Set default values if each value is empty.
170
- e['diagnosis'] ||= ''
171
173
  permessage.each_key { |a| e[a] ||= permessage[a] || '' }
172
174
 
173
175
  if anotherset['diagnosis']
174
176
  # Copy alternative error message
175
177
  e['diagnosis'] = anotherset['diagnosis'] if e['diagnosis'].start_with?(' ')
176
- e['diagnosis'] = anotherset['diagnosis'] if e['diagnosis'].=~ /\A\d+\z/
178
+ e['diagnosis'] = anotherset['diagnosis'] if e['diagnosis'] =~ /\A\d+\z/
177
179
  e['diagnosis'] = anotherset['diagnosis'] if e['diagnosis'].empty?
178
180
  end
179
181
 
@@ -188,10 +190,9 @@ module Sisimai::Lhost
188
190
  end
189
191
 
190
192
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
191
- e['command'] ||= thecommand || Sisimai::SMTP::Command.find(e['diagnosis']) || ''
192
- if e['command'].empty?
193
- e['command'] = 'EHLO' unless esmtpreply.empty?
194
- end
193
+ e["command"] = thecommand if e["command"].empty?
194
+ e["command"] = Sisimai::SMTP::Command.find(e['diagnosis']) if e["command"].empty?
195
+ e["command"] = "EHLO" if e["command"].empty? && esmtpreply.size > 0
195
196
 
196
197
  while true
197
198
  # Check alternative status code and override it