sisimai 5.1.0-java → 5.2.0-java

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 (192) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rake-test.yml +1 -1
  3. data/ChangeLog.md +102 -0
  4. data/Makefile +4 -2
  5. data/README-JA.md +23 -16
  6. data/README.md +22 -15
  7. data/lib/sisimai/arf.rb +121 -210
  8. data/lib/sisimai/fact.rb +208 -158
  9. data/lib/sisimai/lda.rb +98 -0
  10. data/lib/sisimai/lhost/activehunter.rb +1 -1
  11. data/lib/sisimai/lhost/amazonses.rb +185 -301
  12. data/lib/sisimai/lhost/apachejames.rb +48 -51
  13. data/lib/sisimai/lhost/biglobe.rb +1 -2
  14. data/lib/sisimai/lhost/courier.rb +10 -8
  15. data/lib/sisimai/lhost/domino.rb +25 -25
  16. data/lib/sisimai/lhost/dragonfly.rb +3 -4
  17. data/lib/sisimai/lhost/einsundeins.rb +3 -4
  18. data/lib/sisimai/lhost/exchange2003.rb +6 -8
  19. data/lib/sisimai/lhost/exchange2007.rb +111 -101
  20. data/lib/sisimai/lhost/exim.rb +232 -242
  21. data/lib/sisimai/lhost/ezweb.rb +43 -51
  22. data/lib/sisimai/lhost/fml.rb +2 -3
  23. data/lib/sisimai/lhost/gmail.rb +32 -28
  24. data/lib/sisimai/lhost/gmx.rb +4 -16
  25. data/lib/sisimai/lhost/googlegroups.rb +9 -8
  26. data/lib/sisimai/lhost/googleworkspace.rb +94 -0
  27. data/lib/sisimai/lhost/imailserver.rb +7 -16
  28. data/lib/sisimai/lhost/interscanmss.rb +1 -1
  29. data/lib/sisimai/lhost/kddi.rb +3 -4
  30. data/lib/sisimai/lhost/mailfoundry.rb +2 -5
  31. data/lib/sisimai/lhost/mailmarshalsmtp.rb +1 -2
  32. data/lib/sisimai/lhost/messagingserver.rb +14 -13
  33. data/lib/sisimai/lhost/mfilter.rb +4 -3
  34. data/lib/sisimai/lhost/notes.rb +2 -4
  35. data/lib/sisimai/lhost/opensmtpd.rb +2 -2
  36. data/lib/sisimai/lhost/postfix.rb +25 -27
  37. data/lib/sisimai/lhost/qmail.rb +130 -106
  38. data/lib/sisimai/lhost/sendmail.rb +19 -18
  39. data/lib/sisimai/lhost/v5sendmail.rb +88 -60
  40. data/lib/sisimai/lhost/verizon.rb +2 -2
  41. data/lib/sisimai/lhost/x1.rb +1 -1
  42. data/lib/sisimai/lhost/x2.rb +1 -2
  43. data/lib/sisimai/lhost/x3.rb +2 -2
  44. data/lib/sisimai/lhost/x6.rb +1 -1
  45. data/lib/sisimai/lhost/zoho.rb +2 -2
  46. data/lib/sisimai/lhost.rb +18 -21
  47. data/lib/sisimai/message.rb +93 -146
  48. data/lib/sisimai/order.rb +21 -77
  49. data/lib/sisimai/reason/authfailure.rb +1 -4
  50. data/lib/sisimai/reason/badreputation.rb +2 -2
  51. data/lib/sisimai/reason/blocked.rb +7 -10
  52. data/lib/sisimai/reason/contenterror.rb +7 -1
  53. data/lib/sisimai/reason/exceedlimit.rb +1 -4
  54. data/lib/sisimai/reason/failedstarttls.rb +42 -0
  55. data/lib/sisimai/reason/filtered.rb +5 -4
  56. data/lib/sisimai/reason/hasmoved.rb +1 -2
  57. data/lib/sisimai/reason/hostunknown.rb +3 -3
  58. data/lib/sisimai/reason/mailboxfull.rb +2 -4
  59. data/lib/sisimai/reason/mailererror.rb +1 -2
  60. data/lib/sisimai/reason/mesgtoobig.rb +2 -4
  61. data/lib/sisimai/reason/norelaying.rb +2 -3
  62. data/lib/sisimai/reason/notaccept.rb +2 -3
  63. data/lib/sisimai/reason/notcompliantrfc.rb +10 -4
  64. data/lib/sisimai/reason/rejected.rb +1 -1
  65. data/lib/sisimai/reason/requireptr.rb +2 -2
  66. data/lib/sisimai/reason/securityerror.rb +1 -3
  67. data/lib/sisimai/reason/spamdetected.rb +6 -8
  68. data/lib/sisimai/reason/speeding.rb +1 -2
  69. data/lib/sisimai/reason/suppressed.rb +36 -0
  70. data/lib/sisimai/reason/suspend.rb +1 -3
  71. data/lib/sisimai/reason/systemerror.rb +5 -0
  72. data/lib/sisimai/reason/toomanyconn.rb +1 -2
  73. data/lib/sisimai/reason/userunknown.rb +1 -1
  74. data/lib/sisimai/reason/virusdetected.rb +5 -6
  75. data/lib/sisimai/reason.rb +77 -73
  76. data/lib/sisimai/rfc1123.rb +152 -0
  77. data/lib/sisimai/rfc1894.rb +102 -62
  78. data/lib/sisimai/rfc2045.rb +2 -1
  79. data/lib/sisimai/rfc3464/thirdparty.rb +102 -0
  80. data/lib/sisimai/rfc3464.rb +222 -343
  81. data/lib/sisimai/rfc3834.rb +1 -1
  82. data/lib/sisimai/rfc5322.rb +7 -17
  83. data/lib/sisimai/rfc791.rb +69 -0
  84. data/lib/sisimai/rhost/aol.rb +36 -0
  85. data/lib/sisimai/rhost/apple.rb +5 -2
  86. data/lib/sisimai/rhost/cox.rb +3 -2
  87. data/lib/sisimai/rhost/facebook.rb +100 -0
  88. data/lib/sisimai/rhost/franceptt.rb +3 -2
  89. data/lib/sisimai/rhost/godaddy.rb +3 -2
  90. data/lib/sisimai/rhost/google.rb +19 -17
  91. data/lib/sisimai/rhost/gsuite.rb +42 -0
  92. data/lib/sisimai/rhost/iua.rb +3 -3
  93. data/lib/sisimai/rhost/kddi.rb +3 -2
  94. data/lib/sisimai/rhost/messagelabs.rb +37 -0
  95. data/lib/sisimai/rhost/microsoft.rb +56 -49
  96. data/lib/sisimai/rhost/mimecast.rb +29 -27
  97. data/lib/sisimai/rhost/nttdocomo.rb +4 -3
  98. data/lib/sisimai/rhost/outlook.rb +36 -0
  99. data/lib/sisimai/rhost/spectrum.rb +3 -2
  100. data/lib/sisimai/rhost/tencent.rb +3 -2
  101. data/lib/sisimai/rhost/yahooinc.rb +4 -3
  102. data/lib/sisimai/rhost.rb +69 -39
  103. data/lib/sisimai/smtp/command.rb +31 -21
  104. data/lib/sisimai/smtp/failure.rb +103 -0
  105. data/lib/sisimai/smtp/reply.rb +29 -25
  106. data/lib/sisimai/smtp/status.rb +36 -19
  107. data/lib/sisimai/smtp/transcript.rb +15 -15
  108. data/lib/sisimai/string.rb +0 -46
  109. data/lib/sisimai/version.rb +1 -1
  110. data/set-of-emails/maildir/bsd/lhost-postfix-30.eml +81 -81
  111. data/set-of-emails/maildir/bsd/{lhost-aol-03.eml → rhost-aol-03.eml} +1264 -1264
  112. data/set-of-emails/maildir/bsd/{lhost-aol-04.eml → rhost-aol-04.eml} +1260 -1260
  113. data/set-of-emails/maildir/bsd/{lhost-aol-05.eml → rhost-aol-05.eml} +105 -105
  114. data/set-of-emails/maildir/bsd/{lhost-aol-06.eml → rhost-aol-06.eml} +105 -105
  115. data/set-of-emails/maildir/bsd/rhost-gsuite-01.eml +189 -0
  116. data/set-of-emails/maildir/bsd/rhost-gsuite-02.eml +180 -0
  117. data/set-of-emails/maildir/bsd/rhost-gsuite-03.eml +251 -0
  118. data/set-of-emails/maildir/bsd/rhost-gsuite-04.eml +211 -0
  119. data/set-of-emails/maildir/bsd/rhost-gsuite-05.eml +226 -0
  120. data/set-of-emails/maildir/bsd/rhost-gsuite-06.eml +257 -0
  121. data/set-of-emails/maildir/bsd/rhost-gsuite-07.eml +289 -0
  122. data/set-of-emails/maildir/bsd/rhost-gsuite-08.eml +231 -0
  123. data/set-of-emails/maildir/bsd/rhost-gsuite-09.eml +231 -0
  124. data/set-of-emails/maildir/bsd/rhost-gsuite-10.eml +254 -0
  125. data/set-of-emails/maildir/bsd/rhost-gsuite-11.eml +228 -0
  126. data/set-of-emails/maildir/bsd/rhost-gsuite-12.eml +271 -0
  127. data/set-of-emails/maildir/bsd/rhost-gsuite-13.eml +261 -0
  128. data/set-of-emails/maildir/bsd/rhost-gsuite-14.eml +273 -0
  129. data/set-of-emails/maildir/bsd/rhost-gsuite-15.eml +229 -0
  130. data/set-of-emails/maildir/bsd/{lhost-messagelabs-01.eml → rhost-messagelabs-01.eml} +93 -93
  131. data/set-of-emails/maildir/bsd/rhost-outlook-01.eml +72 -0
  132. data/set-of-emails/maildir/bsd/rhost-outlook-02.eml +72 -0
  133. data/set-of-emails/maildir/bsd/rhost-outlook-03.eml +72 -0
  134. data/set-of-emails/maildir/bsd/rhost-outlook-04.eml +79 -0
  135. data/set-of-emails/maildir/bsd/rhost-outlook-06.eml +75 -0
  136. data/set-of-emails/maildir/bsd/rhost-outlook-07.eml +70 -0
  137. data/set-of-emails/maildir/bsd/rhost-outlook-08.eml +70 -0
  138. data/set-of-emails/maildir/bsd/rhost-outlook-09.eml +56 -0
  139. data/set-of-emails/maildir/tmp/arf-22.eml +49 -0
  140. data/set-of-emails/maildir/tmp/arf-23.eml +49 -0
  141. data/set-of-emails/maildir/tmp/arf-24.eml +50 -0
  142. data/set-of-emails/maildir/tmp/lhost-exim-07.eml +28 -0
  143. metadata +73 -56
  144. data/lib/sisimai/lhost/amavis.rb +0 -163
  145. data/lib/sisimai/lhost/amazonworkmail.rb +0 -127
  146. data/lib/sisimai/lhost/aol.rb +0 -125
  147. data/lib/sisimai/lhost/barracuda.rb +0 -92
  148. data/lib/sisimai/lhost/bigfoot.rb +0 -125
  149. data/lib/sisimai/lhost/facebook.rb +0 -188
  150. data/lib/sisimai/lhost/gsuite.rb +0 -194
  151. data/lib/sisimai/lhost/mailru.rb +0 -214
  152. data/lib/sisimai/lhost/mcafee.rb +0 -109
  153. data/lib/sisimai/lhost/messagelabs.rb +0 -120
  154. data/lib/sisimai/lhost/mxlogic.rb +0 -198
  155. data/lib/sisimai/lhost/office365.rb +0 -252
  156. data/lib/sisimai/lhost/outlook.rb +0 -129
  157. data/lib/sisimai/lhost/powermta.rb +0 -118
  158. data/lib/sisimai/lhost/receivingses.rb +0 -126
  159. data/lib/sisimai/lhost/sendgrid.rb +0 -150
  160. data/lib/sisimai/lhost/surfcontrol.rb +0 -105
  161. data/lib/sisimai/lhost/x4.rb +0 -269
  162. data/lib/sisimai/lhost/x5.rb +0 -112
  163. data/lib/sisimai/lhost/yahoo.rb +0 -102
  164. data/lib/sisimai/lhost/yandex.rb +0 -118
  165. data/lib/sisimai/mda.rb +0 -121
  166. data/lib/sisimai/smtp/error.rb +0 -119
  167. /data/set-of-emails/maildir/bsd/{lhost-googlegroups-15.eml → lhost-googleworkspace-01.eml} +0 -0
  168. /data/set-of-emails/maildir/bsd/{lhost-x4-08.eml → lhost-x2-06.eml} +0 -0
  169. /data/set-of-emails/maildir/bsd/{lhost-gsuite-01.eml → rfc3464-51.eml} +0 -0
  170. /data/set-of-emails/maildir/bsd/{lhost-gsuite-03.eml → rfc3464-52.eml} +0 -0
  171. /data/set-of-emails/maildir/bsd/{lhost-gsuite-04.eml → rfc3464-53.eml} +0 -0
  172. /data/set-of-emails/maildir/bsd/{lhost-gsuite-05.eml → rfc3464-54.eml} +0 -0
  173. /data/set-of-emails/maildir/bsd/{lhost-gsuite-06.eml → rfc3464-55.eml} +0 -0
  174. /data/set-of-emails/maildir/bsd/{lhost-gsuite-07.eml → rfc3464-56.eml} +0 -0
  175. /data/set-of-emails/maildir/bsd/{lhost-gsuite-08.eml → rfc3464-57.eml} +0 -0
  176. /data/set-of-emails/maildir/bsd/{lhost-gsuite-09.eml → rfc3464-58.eml} +0 -0
  177. /data/set-of-emails/maildir/bsd/{lhost-gsuite-10.eml → rfc3464-59.eml} +0 -0
  178. /data/set-of-emails/maildir/bsd/{lhost-gsuite-11.eml → rfc3464-60.eml} +0 -0
  179. /data/set-of-emails/maildir/bsd/{lhost-gsuite-12.eml → rfc3464-61.eml} +0 -0
  180. /data/set-of-emails/maildir/bsd/{lhost-gsuite-13.eml → rfc3464-62.eml} +0 -0
  181. /data/set-of-emails/maildir/bsd/{lhost-gsuite-14.eml → rfc3464-63.eml} +0 -0
  182. /data/set-of-emails/maildir/bsd/{lhost-gsuite-15.eml → rfc3464-64.eml} +0 -0
  183. /data/set-of-emails/maildir/bsd/{lhost-gsuite-02.eml → rfc3464-65.eml} +0 -0
  184. /data/set-of-emails/maildir/bsd/{lhost-aol-01.eml → rhost-aol-01.eml} +0 -0
  185. /data/set-of-emails/maildir/bsd/{lhost-aol-02.eml → rhost-aol-02.eml} +0 -0
  186. /data/set-of-emails/maildir/bsd/{lhost-facebook-03.eml → rhost-facebook-03.eml} +0 -0
  187. /data/set-of-emails/maildir/bsd/{lhost-facebook-04.eml → rhost-facebook-04.eml} +0 -0
  188. /data/set-of-emails/maildir/bsd/{lhost-messagelabs-02.eml → rhost-messagelabs-02.eml} +0 -0
  189. /data/set-of-emails/maildir/bsd/{lhost-messagelabs-03.eml → rhost-messagelabs-03.eml} +0 -0
  190. /data/set-of-emails/maildir/{bsd → tmp}/rfc3464-37.eml +0 -0
  191. /data/set-of-emails/maildir/{bsd → tmp}/rfc3464-38.eml +0 -0
  192. /data/set-of-emails/maildir/{bsd → tmp}/rfc3464-39.eml +0 -0
@@ -60,20 +60,21 @@ module Sisimai::Lhost
60
60
  # Remote system: dns;mx.example.jp (TCP|17.111.174.67|47323|192.0.2.225|25) (6jo.example.jp ESMTP SENDMAIL-VM)
61
61
  v = dscontents[-1]
62
62
 
63
- if e.start_with?(' Recipient address: ') && e.index('@') > 1
64
- # Recipient address: kijitora@example.jp
65
- if v['recipient']
63
+ if Sisimai::String.aligned(e, [' Recipient address: ', '@', '.']) ||
64
+ Sisimai::String.aligned(e, [' Original address: ', '@', '.'])
65
+ # Recipient address: @smtp.example.net:kijitora@server
66
+ # Original address: kijitora@example.jp
67
+ cv = Sisimai::Address.s3s4(e[e.rindex(' ') + 1, e.size])
68
+ next unless Sisimai::Address.is_emailaddress(cv)
69
+
70
+ if v["recipient"] != "" && cv != v['recipient']
66
71
  # There are multiple recipient addresses in the message body.
67
72
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
68
73
  v = dscontents[-1]
69
74
  end
70
- v['recipient'] = Sisimai::Address.s3s4(e[e.rindex(' ') + 1, e.size])
75
+ v['recipient'] = cv
71
76
  recipients += 1
72
77
 
73
- elsif e.start_with?(' Original address: ') && e.index('@') > 1
74
- # Original address: kijitora@example.jp
75
- v['recipient'] = Sisimai::Address.s3s4(e[e.rindex(' ') + 1, e.size])
76
-
77
78
  elsif e.start_with?(' Date: ')
78
79
  # Date: Fri, 21 Nov 2014 23:34:45 +0900
79
80
  v['date'] = e[e.index(':') + 2, e.size]
@@ -120,18 +121,18 @@ module Sisimai::Lhost
120
121
  # Status: 5.1.1 (Remote SMTP server has rejected address)
121
122
  p1 = e.index(':')
122
123
  p2 = e.index('(')
123
- v['status'] = e[p1 + 2, p2 - p1 - 3]
124
- v['diagnosis'] ||= e[p2 + 1, e[e.index(')') - p2 - 1]]
124
+ v['status'] = e[p1 + 2, p2 - p1 - 3]
125
+ v['diagnosis'] = e[p2 + 1, e[e.index(')') - p2 - 1]] if v["diagnosis"].empty?
125
126
 
126
127
  elsif e.start_with?('Arrival-Date: ')
127
128
  # Arrival-date: Thu, 29 Apr 2014 23:34:45 +0000 (GMT)
128
- v['date'] ||= e[e.index(':') + 2, e.size]
129
+ v['date'] = e[e.index(':') + 2, e.size] if v["date"].empty?
129
130
 
130
131
  elsif e.start_with?('Reporting-MTA: ')
131
132
  # Reporting-MTA: dns;mr21p30im-asmtp004.me.com (tcp-daemon)
132
133
  localhost = e[e.index(';') + 1, e.size]
133
- v['lhost'] ||= localhost
134
- v['lhost'] = localhost unless v['lhost'].index('.') > 0
134
+ v['lhost'] = localhost if v["lhost"].empty?
135
+ v['lhost'] = localhost unless v['lhost'].index('.') > 0
135
136
  end
136
137
  end
137
138
  end
@@ -62,7 +62,7 @@ module Sisimai::Lhost
62
62
  if e.include?('@') && e.include?(' ') == false
63
63
  # 以下のメールアドレスへの送信に失敗しました。
64
64
  # kijitora@example.jp
65
- if v['recipient']
65
+ if v["recipient"] != ""
66
66
  # There are multiple recipient addresses in the message body.
67
67
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
68
68
  v = dscontents[-1]
@@ -75,6 +75,7 @@ module Sisimai::Lhost
75
75
  # DATA
76
76
  next if v['command']
77
77
  v['command'] = e if markingset['command']
78
+
78
79
  else
79
80
  # Get error message and SMTP command
80
81
  if e == StartingOf[:error][0]
@@ -87,7 +88,7 @@ module Sisimai::Lhost
87
88
  else
88
89
  # 550 5.1.1 unknown user <kijitora@example.jp>
89
90
  next if e.start_with?('-')
90
- next if v['diagnosis']
91
+ next if v['diagnosis'] != ""
91
92
  v['diagnosis'] = e
92
93
  end
93
94
  end
@@ -103,7 +104,7 @@ module Sisimai::Lhost
103
104
  rheads = mhead['received']
104
105
  rhosts = Sisimai::RFC5322.received(rheads[-1])
105
106
 
106
- e['lhost'] ||= Sisimai::RFC5322.received(rheads[0]).shift
107
+ e['lhost'] = Sisimai::RFC5322.received(rheads[0]).shift if e["lhost"].empty?
107
108
  [rhosts[0], rhosts[1]].each do |ee|
108
109
  # Avoid "... by m-FILTER"
109
110
  next unless ee.include?('.')
@@ -58,12 +58,12 @@ module Sisimai::Lhost
58
58
  v = dscontents[-1]
59
59
  if e.include?('@') && e.index(' ').nil?
60
60
  # kijitora@notes.example.jp
61
- if v['recipient']
61
+ if v["recipient"] != ""
62
62
  # There are multiple recipient addresses in the message body.
63
63
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
64
64
  v = dscontents[-1]
65
65
  end
66
- v['recipient'] ||= e
66
+ v["recipient"] = e if v["recipient"].empty?
67
67
  recipients += 1
68
68
  else
69
69
  next if e.empty?
@@ -83,11 +83,9 @@ module Sisimai::Lhost
83
83
  # No character set in Content-Type header
84
84
  encodedmsg = removedmsg
85
85
  end
86
- v['diagnosis'] ||= ''
87
86
  v['diagnosis'] << encodedmsg
88
87
  else
89
88
  # Error message does not include multi-byte character
90
- v['diagnosis'] ||= ''
91
89
  v['diagnosis'] << e
92
90
  end
93
91
  end
@@ -106,9 +106,9 @@ module Sisimai::Lhost
106
106
  # Below is a copy of the original message:
107
107
  v = dscontents[-1]
108
108
 
109
- if e.include?('@') && Sisimai::String.aligned(e, ['@', ' '])
109
+ if Sisimai::String.aligned(e, ['@', ' '])
110
110
  # kijitora@example.jp: 550 5.2.2 <kijitora@example>... Mailbox Full
111
- if v['recipient']
111
+ if v["recipient"] != ""
112
112
  # There are multiple recipient addresses in the message body.
113
113
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
114
114
  v = dscontents[-1]
@@ -4,6 +4,8 @@ module Sisimai::Lhost
4
4
  module Postfix
5
5
  class << self
6
6
  require 'sisimai/lhost'
7
+ require 'sisimai/rfc1123'
8
+ require 'sisimai/smtp/reply'
7
9
  require 'sisimai/smtp/command'
8
10
 
9
11
  # Postfix manual - bounce(5) - http://www.postfix.org/bounce.5.html
@@ -26,19 +28,17 @@ module Sisimai::Lhost
26
28
  # @return [Hash] Bounce data list and message/rfc822 part
27
29
  # @return [Nil] it failed to decode or the arguments are missing
28
30
  def inquire(mhead, mbody)
29
- match = nil
30
- sessx = nil
31
+ match = 0
31
32
 
32
33
  if mhead['subject'].include?('SMTP server: errors from ')
33
34
  # src/smtpd/smtpd_chat.c:|337: post_mail_fprintf(notice, "Subject: %s SMTP server: errors from %s",
34
35
  # src/smtpd/smtpd_chat.c:|338: var_mail_name, state->namaddr);
35
- match = true
36
- sessx = true
36
+ match = 2
37
37
  else
38
38
  # Subject: Undelivered Mail Returned to Sender
39
- match = true if mhead['subject'] == 'Undelivered Mail Returned to Sender'
39
+ match = 1 if mhead['subject'] == 'Undelivered Mail Returned to Sender'
40
40
  end
41
- return nil unless match
41
+ return nil if match == 0
42
42
  return nil if mhead['x-aol-ip']
43
43
 
44
44
  permessage = {} # (Hash) Store values of each Per-Message field
@@ -52,7 +52,7 @@ module Sisimai::Lhost
52
52
  anotherset = {} # Another error information
53
53
  v = nil
54
54
 
55
- if sessx
55
+ if match == 2
56
56
  # The message body starts with 'Transcript of session follows.'
57
57
  require 'sisimai/smtp/transcript'
58
58
  transcript = Sisimai::SMTP::Transcript.rise(emailparts[0], 'In:', 'Out:')
@@ -75,7 +75,7 @@ module Sisimai::Lhost
75
75
 
76
76
  elsif e['command'] == 'RCPT'
77
77
  # RCPT TO: <...>
78
- if v['recipient']
78
+ if v["recipient"] != ""
79
79
  # There are multiple recipient addresses in the transcript of session
80
80
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
81
81
  v = dscontents[-1]
@@ -86,9 +86,9 @@ module Sisimai::Lhost
86
86
 
87
87
  next if p['reply'].to_i < 400
88
88
  commandset << e['command']
89
- v['diagnosis'] ||= p['text'].join(' ')
90
- v['replycode'] ||= p['reply']
91
- v['status'] ||= p['status']
89
+ v['diagnosis'] = p['text'].join(' ') if v["diagnosis"].empty?
90
+ v['replycode'] = p['reply'] if v["replycode"].empty?
91
+ v['status'] = p['status'] if v["status"].empty?
92
92
  end
93
93
  else
94
94
  fieldtable = Sisimai::RFC1894.FIELDTABLE
@@ -107,17 +107,18 @@ module Sisimai::Lhost
107
107
  next if (readcursor & Indicators[:deliverystatus]) == 0
108
108
  next if e.empty?
109
109
 
110
- if f = Sisimai::RFC1894.match(e)
110
+ f = Sisimai::RFC1894.match(e)
111
+ if f > 0
111
112
  # "e" matched with any field defined in RFC3464
112
113
  next unless o = Sisimai::RFC1894.field(e)
113
114
  v = dscontents[-1]
114
115
 
115
- if o[-1] == 'addr'
116
+ if o[3] == 'addr'
116
117
  # Final-Recipient: rfc822; kijitora@example.jp
117
118
  # X-Actual-Recipient: rfc822; kijitora@example.co.jp
118
119
  if o[0] == 'final-recipient'
119
120
  # Final-Recipient: rfc822; kijitora@example.jp
120
- if v['recipient']
121
+ if v["recipient"] != ""
121
122
  # There are multiple recipient addresses in the message body.
122
123
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
123
124
  v = dscontents[-1]
@@ -128,17 +129,18 @@ module Sisimai::Lhost
128
129
  # X-Actual-Recipient: rfc822; kijitora@example.co.jp
129
130
  v['alias'] = o[2]
130
131
  end
131
- elsif o[-1] == 'code'
132
+ elsif o[3] == 'code'
132
133
  # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
133
134
  v['spec'] = o[1]
134
- v['spec'] = 'SMTP' if v['spec'] == 'X-POSTFIX'
135
+ v['spec'] = 'SMTP' if v['spec'].upcase == 'X-POSTFIX'
135
136
  v['diagnosis'] = o[2]
136
137
  else
137
138
  # Other DSN fields defined in RFC3464
138
139
  next unless fieldtable[o[0]]
140
+ next if o[3] == "host" && Sisimai::RFC1123.is_internethost(o[2]) == false
139
141
  v[fieldtable[o[0]]] = o[2]
140
142
 
141
- next unless f
143
+ next unless f == 1
142
144
  permessage[fieldtable[o[0]]] = o[2]
143
145
  end
144
146
  else
@@ -162,7 +164,7 @@ module Sisimai::Lhost
162
164
  # Alternative error message and recipient
163
165
  if e.include?(' (in reply to ') || e.include?('command)')
164
166
  # 5.1.1 <userunknown@example.co.jp>... User Unknown (in reply to RCPT TO
165
- q = Sisimai::SMTP::Command.find(e); commandset << q if q
167
+ cv = Sisimai::SMTP::Command.find(e) || ""; commandset << cv unless cv.empty?
166
168
  anotherset['diagnosis'] ||= ''
167
169
  anotherset['diagnosis'] << ' ' << e
168
170
 
@@ -205,7 +207,7 @@ module Sisimai::Lhost
205
207
 
206
208
  end
207
209
 
208
- unless recipients > 0
210
+ if recipients == 0
209
211
  # Fallback: get a recipient address from error messages
210
212
  if anotherset['recipient'].to_s.size > 0
211
213
  # Set a recipient address
@@ -241,13 +243,9 @@ module Sisimai::Lhost
241
243
  # More detailed error message is in "anotherset"
242
244
  as = '' # status
243
245
  ar = '' # replycode
244
-
245
- e['status'] ||= ''
246
- e['replycode'] ||= ''
247
-
248
246
  if e['status'].empty? || e['status'].start_with?('4.0.0', '5.0.0')
249
247
  # Check the value of D.S.N. in anotherset
250
- as = Sisimai::SMTP::Status.find(anotherset['diagnosis']) || ''
248
+ as = Sisimai::SMTP::Status.find(anotherset['diagnosis'])
251
249
  if as.size > 0 && as[-4, 4] != '.0.0'
252
250
  # The D.S.N. is neither an empty nor *.0.0
253
251
  e['status'] = as
@@ -256,7 +254,7 @@ module Sisimai::Lhost
256
254
 
257
255
  if e['replycode'].empty? || e['replycode'].end_with?('00')
258
256
  # Check the value of SMTP reply code in $anotherset
259
- ar = Sisimai::SMTP::Reply.find(anotherset['diagnosis']) || ''
257
+ ar = Sisimai::SMTP::Reply.find(anotherset['diagnosis'])
260
258
  if ar.size > 0 && ar.end_with?('00') == false
261
259
  # The SMTP reply code is neither an empty nor *00
262
260
  e['replycode'] = ar
@@ -278,8 +276,8 @@ module Sisimai::Lhost
278
276
 
279
277
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''
280
278
  e['command'] = commandset.shift || Sisimai::SMTP::Command.find(e['diagnosis'])
281
- e['command'] ||= 'HELO' if e['diagnosis'].include?('refused to talk to me:')
282
- e['spec'] ||= 'SMTP' if Sisimai::String.aligned(e['diagnosis'], ['host ', ' said:'])
279
+ e['command'] = 'HELO' if e["command"].empty? && e['diagnosis'].include?('refused to talk to me:')
280
+ e['spec'] = 'SMTP' if e["spec"].empty? && Sisimai::String.aligned(e['diagnosis'], ['host ', ' said:'])
283
281
  end
284
282
 
285
283
  return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
@@ -5,9 +5,23 @@ module Sisimai::Lhost
5
5
  module Qmail
6
6
  class << self
7
7
  require 'sisimai/lhost'
8
+ require 'sisimai/string'
8
9
 
9
10
  Indicators = Sisimai::Lhost.INDICATORS
10
- 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
11
25
  StartingOf = {
12
26
  # qmail-remote.c:248| if (code >= 500) {
13
27
  # qmail-remote.c:249| out("h"); outhost(); out(" does not like recipient.\n");
@@ -16,78 +30,97 @@ module Sisimai::Lhost
16
30
  #
17
31
  # Characters: K,Z,D in qmail-qmqpc.c, qmail-send.c, qmail-rspawn.c
18
32
  # K = success, Z = temporary error, D = permanent error
19
- error: ['Remote host said:'],
20
- message: ['Hi. This is the qmail'],
21
- 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 '],
22
50
  }.freeze
23
51
  CommandSet = {
24
- # Error text regular expressions which defined in qmail-remote.c
25
52
  # qmail-remote.c:225| if (smtpcode() != 220) quit("ZConnected to "," but greeting failed");
26
- 'conn' => [' but greeting failed.'],
53
+ "CONN" => [" but greeting failed."],
27
54
  # qmail-remote.c:231| if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected");
28
- 'ehlo' => [' but my name was rejected.'],
55
+ "EHLO" => [" but my name was rejected."],
29
56
  # qmail-remote.c:238| if (code >= 500) quit("DConnected to "," but sender was rejected");
30
57
  # reason = rejected
31
- 'mail' => [' but sender was rejected.'],
58
+ "MAIL" => [" but sender was rejected."],
32
59
  # qmail-remote.c:249| out("h"); outhost(); out(" does not like recipient.\n");
33
60
  # qmail-remote.c:253| out("s"); outhost(); out(" does not like recipient.\n");
34
61
  # reason = userunknown
35
- 'rcpt' => [' does not like recipient.'],
62
+ "RCPT" => [" does not like recipient."],
36
63
  # qmail-remote.c:265| if (code >= 500) quit("D"," failed on DATA command");
37
64
  # qmail-remote.c:266| if (code >= 400) quit("Z"," failed on DATA command");
38
65
  # qmail-remote.c:271| if (code >= 500) quit("D"," failed after I sent the message");
39
66
  # qmail-remote.c:272| if (code >= 400) quit("Z"," failed after I sent the message");
40
- 'data' => [' failed on DATA command', ' failed after I sent the message'],
67
+ "DATA" => [" failed on DATA command", " failed after I sent the message"],
41
68
  }.freeze
42
69
 
43
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();
44
- HasExpired = 'this message has been in the queue too long.'
45
- 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
46
74
  FailOnLDAP = {
47
75
  # qmail-ldap-1.03-20040101.patch:19817 - 19866
48
- 'suspend' => ['Mailaddress is administrative?le?y disabled'], # 5.2.1
49
- 'userunknown' => ['Sorry, no mailbox here by that name'], # 5.1.1
50
- 'exceedlimit' => ['The message exeeded the maximum size the user accepts'], # 5.2.3
51
- 'systemerror' => [
52
- 'Automatic homedir creator crashed', # 4.3.0
53
- 'Illegal value in LDAP attribute', # 5.3.5
54
- 'LDAP attribute is not given but mandatory', # 5.3.5
55
- 'Timeout while performing search on LDAP server', # 4.4.3
56
- 'Too many results returned but needs to be unique', # 5.3.5
57
- 'Permanent error while executing qmail-forward', # 5.4.4
58
- 'Temporary error in automatic homedir creation', # 4.3.0 or 5.3.0
59
- 'Temporary error while executing qmail-forward', # 4.4.4
60
- 'Temporary failure in LDAP lookup', # 4.4.3
61
- 'Unable to contact LDAP server', # 4.4.3
62
- '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
63
96
  ],
64
97
  }.freeze
65
98
  MessagesOf = {
66
- # qmail-local.c:589| strerr_die1x(100,"Sorry, no mailbox here by that name. (#5.1.1)");
67
- # qmail-remote.c:253| out("s"); outhost(); out(" does not like recipient.\n");
68
- '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 "],
69
102
  # error_str.c:192| X(EDQUOT,"disk quota exceeded")
70
- 'mailboxfull' => ['disk quota exceeded'],
103
+ "mailboxfull" => ["disk quota exceeded"],
71
104
  # qmail-qmtpd.c:233| ... result = "Dsorry, that message size exceeds my databytes limit (#5.3.4)";
72
105
  # qmail-smtpd.c:391| ... out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); return;
73
- 'mesgtoobig' => ['Message size exceeds fixed maximum message size:'],
74
- # qmail-remote.c:68| Sorry, I couldn't find any host by that name. (#4.1.2)\n"); zerodie();
75
- # qmail-remote.c:78| Sorry, I couldn't find any host named ");
76
- 'hostunknown' => ["Sorry, I couldn't find any host "],
77
- 'systemfull' => ['Requested action not taken: mailbox unavailable (not enough free space)'],
78
- 'systemerror' => [
79
- 'bad interpreter: No such file or directory',
80
- 'system error',
81
- '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",
82
110
  ],
83
- 'notaccept' => [
84
- # notqmail 1.08 returns the following error message when the destination MX is NullMX
85
- "Sorry, I couldn't find a mail exchanger or IP address",
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",
86
114
  ],
87
- 'networkerror' => [
88
- "Sorry, I wasn't able to establish an SMTP connection",
89
- "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",
90
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"],
91
124
  }.freeze
92
125
 
93
126
  # @abstract Decodes the bounce message from qmail
@@ -100,16 +133,16 @@ module Sisimai::Lhost
100
133
  # by qmail, see https://cr.yp.to/qmail.html
101
134
  # e.g.) Received: (qmail 12345 invoked for bounce); 29 Apr 2009 12:34:56 -0000
102
135
  # Subject: failure notice
103
- tryto = [['(qmail ', 'invoked for bounce)'], ['(qmail ', 'invoked from ', 'network)']]
104
- match = 0
105
- match += 1 if mhead['subject'] == 'failure notice'
106
- mhead['received'].each do |e|
136
+ proceedsto = false
137
+ proceedsto = true if EmailTitle.any? { |a| mhead["subject"] == a }
138
+ mhead["received"].each do |e|
107
139
  # Received: (qmail 2222 invoked for bounce);29 Apr 2017 23:34:45 +0900
108
140
  # Received: (qmail 2202 invoked from network); 29 Apr 2018 00:00:00 +0900
109
- match += 1 if tryto.any? { |a| Sisimai::String.aligned(e, a) }
141
+ proceedsto = true if RelayedVia.any? { |a| Sisimai::String.aligned(e, a) }
110
142
  end
111
- return nil unless match > 0
143
+ return nil if proceedsto == false
112
144
 
145
+ require "sisimai/smtp/command"
113
146
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
114
147
  emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
115
148
  bodyslices = emailparts[0].split("\n")
@@ -122,7 +155,7 @@ module Sisimai::Lhost
122
155
  # line of the beginning of the original message.
123
156
  if readcursor == 0
124
157
  # Beginning of the bounce message or delivery status part
125
- readcursor |= Indicators[:deliverystatus] if e.start_with?(StartingOf[:message][0])
158
+ readcursor |= Indicators[:deliverystatus] if StartingOf["message"].any? { |a| e.include?(a) }
126
159
  next
127
160
  end
128
161
  next if (readcursor & Indicators[:deliverystatus]) == 0
@@ -134,31 +167,29 @@ module Sisimai::Lhost
134
167
  # Giving up on 192.0.2.153.
135
168
  v = dscontents[-1]
136
169
 
137
- if e.start_with?('<') && Sisimai::String.aligned(e, ['<', '@', '>', ':'])
170
+ if e.start_with?('<') && Sisimai::String.aligned(e, ['<', '@', '>:'])
138
171
  # <kijitora@example.jp>:
139
- if v['recipient']
172
+ if v["recipient"] != ""
140
173
  # There are multiple recipient addresses in the message body.
141
174
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
142
175
  v = dscontents[-1]
143
176
  end
144
- v['recipient'] = Sisimai::Address.s3s4(e[e.index('<'), e.size])
177
+ v["recipient"] = Sisimai::Address.s3s4(e[e.index("<"), e.size])
145
178
  recipients += 1
146
179
 
147
180
  elsif dscontents.size == recipients
148
181
  # Append error message
149
- next if e.empty?
150
- v['diagnosis'] ||= ''
151
- v['diagnosis'] << e + ' '
152
- 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])
153
184
 
154
- next if v['rhost']
155
- StartingOf[:rhost].each do |r|
185
+ next if v["rhost"] != ""
186
+ StartingOf["rhost"].each do |r|
156
187
  # Find a remote host name
157
188
  p1 = e.index(r); next unless p1
158
189
  cm = r.size
159
- p2 = e.index(' ', p1 + cm + 1) || p2 = e.rindex('.')
190
+ p2 = e.index(" ", p1 + cm + 1) || p2 = e.rindex(".")
160
191
 
161
- v['rhost'] = e[p1 + cm, p2 - p1 - cm]
192
+ v["rhost"] = Sisimai::String.sweep(e[p1 + cm, p2 - p1 - cm])
162
193
  break
163
194
  end
164
195
  end
@@ -166,68 +197,61 @@ module Sisimai::Lhost
166
197
  return nil unless recipients > 0
167
198
 
168
199
  dscontents.each do |e|
169
- e['agent'] = 'qmail'
170
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''
171
-
172
- unless e['command']
173
- # Get the SMTP command name for the session
174
- CommandSet.each_key do |r|
175
- # Verify each regular expression of SMTP commands
176
- next unless CommandSet[r].any? { |a| e['diagnosis'].include?(a) }
177
- e['command'] = r.upcase
178
- break
179
- end
200
+ # Tidy up the error message in e['diagnosis'], Try to detect the bounce reason.
201
+ e["diagnosis"] = Sisimai::String.sweep(e["diagnosis"])
180
202
 
181
- if e['diagnosis'].include?('Sorry, no SMTP connection got far enough; most progress was ')
182
- # Get the last SMTP command:from the error message
183
- e['command'] ||= Sisimai::SMTP::Command.find(e['diagnosis']) || ''
184
- 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
209
+ end
210
+
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?
185
214
  end
186
215
 
187
216
  # Detect the reason of bounce
188
- if %w[HELO EHLO].index(e['command'])
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