sisimai 4.25.16-java → 5.0.2-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (180) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/rake-test.yml +55 -0
  3. data/.travis.yml +3 -3
  4. data/ANALYTICAL-PRECISION +2 -2
  5. data/Benchmarks.mk +3 -3
  6. data/CONTRIBUTING +1 -1
  7. data/ChangeLog.md +451 -393
  8. data/Developers.mk +5 -6
  9. data/Gemfile +1 -1
  10. data/Makefile +15 -15
  11. data/README-JA.md +323 -149
  12. data/README.md +319 -149
  13. data/Rakefile +9 -3
  14. data/Repository.mk +2 -3
  15. data/lib/sisimai/address.rb +118 -74
  16. data/lib/sisimai/arf.rb +84 -82
  17. data/lib/sisimai/datetime.rb +5 -52
  18. data/lib/sisimai/{data → fact}/json.rb +7 -9
  19. data/lib/sisimai/fact/yaml.rb +31 -0
  20. data/lib/sisimai/fact.rb +506 -0
  21. data/lib/sisimai/lhost/activehunter.rb +12 -14
  22. data/lib/sisimai/lhost/amavis.rb +11 -14
  23. data/lib/sisimai/lhost/amazonses.rb +37 -42
  24. data/lib/sisimai/lhost/amazonworkmail.rb +15 -19
  25. data/lib/sisimai/lhost/aol.rb +12 -15
  26. data/lib/sisimai/lhost/apachejames.rb +19 -21
  27. data/lib/sisimai/lhost/barracuda.rb +10 -12
  28. data/lib/sisimai/lhost/bigfoot.rb +21 -22
  29. data/lib/sisimai/lhost/biglobe.rb +15 -16
  30. data/lib/sisimai/lhost/courier.rb +20 -20
  31. data/lib/sisimai/lhost/domino.rb +23 -20
  32. data/lib/sisimai/lhost/einsundeins.rb +23 -18
  33. data/lib/sisimai/lhost/exchange2003.rb +30 -29
  34. data/lib/sisimai/lhost/exchange2007.rb +70 -58
  35. data/lib/sisimai/lhost/exim.rb +179 -174
  36. data/lib/sisimai/lhost/ezweb.rb +31 -56
  37. data/lib/sisimai/lhost/facebook.rb +21 -34
  38. data/lib/sisimai/lhost/fml.rb +43 -48
  39. data/lib/sisimai/lhost/gmail.rb +29 -29
  40. data/lib/sisimai/lhost/gmx.rb +18 -17
  41. data/lib/sisimai/lhost/googlegroups.rb +11 -11
  42. data/lib/sisimai/lhost/gsuite.rb +21 -28
  43. data/lib/sisimai/lhost/imailserver.rb +25 -39
  44. data/lib/sisimai/lhost/interscanmss.rb +28 -31
  45. data/lib/sisimai/lhost/kddi.rb +22 -28
  46. data/lib/sisimai/lhost/mailfoundry.rb +11 -12
  47. data/lib/sisimai/lhost/mailmarshalsmtp.rb +25 -29
  48. data/lib/sisimai/lhost/mailru.rb +37 -40
  49. data/lib/sisimai/lhost/mcafee.rb +21 -31
  50. data/lib/sisimai/lhost/messagelabs.rb +17 -21
  51. data/lib/sisimai/lhost/messagingserver.rb +40 -37
  52. data/lib/sisimai/lhost/mfilter.rb +16 -17
  53. data/lib/sisimai/lhost/mxlogic.rb +24 -33
  54. data/lib/sisimai/lhost/notes.rb +17 -17
  55. data/lib/sisimai/lhost/office365.rb +64 -28
  56. data/lib/sisimai/lhost/opensmtpd.rb +12 -13
  57. data/lib/sisimai/lhost/outlook.rb +12 -16
  58. data/lib/sisimai/lhost/postfix.rb +179 -130
  59. data/lib/sisimai/lhost/powermta.rb +12 -14
  60. data/lib/sisimai/lhost/qmail.rb +44 -47
  61. data/lib/sisimai/lhost/receivingses.rb +15 -21
  62. data/lib/sisimai/lhost/sendgrid.rb +34 -34
  63. data/lib/sisimai/lhost/sendmail.rb +65 -53
  64. data/lib/sisimai/lhost/surfcontrol.rb +19 -19
  65. data/lib/sisimai/lhost/v5sendmail.rb +45 -39
  66. data/lib/sisimai/lhost/verizon.rb +35 -39
  67. data/lib/sisimai/lhost/x1.rb +18 -17
  68. data/lib/sisimai/lhost/x2.rb +17 -14
  69. data/lib/sisimai/lhost/x3.rb +19 -19
  70. data/lib/sisimai/lhost/x4.rb +72 -57
  71. data/lib/sisimai/lhost/x5.rb +17 -19
  72. data/lib/sisimai/lhost/x6.rb +41 -17
  73. data/lib/sisimai/lhost/yahoo.rb +17 -16
  74. data/lib/sisimai/lhost/yandex.rb +16 -21
  75. data/lib/sisimai/lhost/zoho.rb +16 -15
  76. data/lib/sisimai/lhost.rb +8 -10
  77. data/lib/sisimai/mail/maildir.rb +1 -3
  78. data/lib/sisimai/mail/mbox.rb +3 -4
  79. data/lib/sisimai/mail/memory.rb +0 -1
  80. data/lib/sisimai/mail/stdin.rb +1 -3
  81. data/lib/sisimai/mail.rb +3 -7
  82. data/lib/sisimai/mda.rb +28 -42
  83. data/lib/sisimai/message.rb +444 -326
  84. data/lib/sisimai/order.rb +5 -5
  85. data/lib/sisimai/reason/authfailure.rb +65 -0
  86. data/lib/sisimai/reason/badreputation.rb +53 -0
  87. data/lib/sisimai/reason/blocked.rb +96 -160
  88. data/lib/sisimai/reason/contenterror.rb +8 -9
  89. data/lib/sisimai/reason/delivered.rb +4 -6
  90. data/lib/sisimai/reason/exceedlimit.rb +10 -12
  91. data/lib/sisimai/reason/expired.rb +7 -8
  92. data/lib/sisimai/reason/feedback.rb +2 -3
  93. data/lib/sisimai/reason/filtered.rb +17 -19
  94. data/lib/sisimai/reason/hasmoved.rb +9 -10
  95. data/lib/sisimai/reason/hostunknown.rb +15 -15
  96. data/lib/sisimai/reason/mailboxfull.rb +11 -12
  97. data/lib/sisimai/reason/mailererror.rb +18 -20
  98. data/lib/sisimai/reason/mesgtoobig.rb +9 -11
  99. data/lib/sisimai/reason/networkerror.rb +5 -8
  100. data/lib/sisimai/reason/norelaying.rb +8 -11
  101. data/lib/sisimai/reason/notaccept.rb +13 -14
  102. data/lib/sisimai/reason/notcompliantrfc.rb +43 -0
  103. data/lib/sisimai/reason/onhold.rb +6 -9
  104. data/lib/sisimai/reason/policyviolation.rb +14 -12
  105. data/lib/sisimai/reason/rejected.rb +26 -24
  106. data/lib/sisimai/reason/requireptr.rb +69 -0
  107. data/lib/sisimai/reason/securityerror.rb +34 -36
  108. data/lib/sisimai/reason/spamdetected.rb +115 -147
  109. data/lib/sisimai/reason/speeding.rb +49 -0
  110. data/lib/sisimai/reason/suspend.rb +12 -11
  111. data/lib/sisimai/reason/syntaxerror.rb +11 -10
  112. data/lib/sisimai/reason/systemerror.rb +7 -9
  113. data/lib/sisimai/reason/systemfull.rb +7 -8
  114. data/lib/sisimai/reason/toomanyconn.rb +9 -11
  115. data/lib/sisimai/reason/undefined.rb +2 -3
  116. data/lib/sisimai/reason/userunknown.rb +129 -146
  117. data/lib/sisimai/reason/vacation.rb +3 -4
  118. data/lib/sisimai/reason/virusdetected.rb +10 -11
  119. data/lib/sisimai/reason.rb +59 -64
  120. data/lib/sisimai/rfc1894.rb +55 -28
  121. data/lib/sisimai/rfc2045.rb +373 -0
  122. data/lib/sisimai/rfc3464.rb +250 -308
  123. data/lib/sisimai/rfc3834.rb +42 -45
  124. data/lib/sisimai/rfc5322.rb +177 -146
  125. data/lib/sisimai/rfc5965.rb +31 -0
  126. data/lib/sisimai/rhost/cox.rb +5 -6
  127. data/lib/sisimai/rhost/franceptt.rb +6 -8
  128. data/lib/sisimai/rhost/godaddy.rb +12 -12
  129. data/lib/sisimai/rhost/google.rb +530 -0
  130. data/lib/sisimai/rhost/iua.rb +9 -10
  131. data/lib/sisimai/rhost/kddi.rb +6 -8
  132. data/lib/sisimai/rhost/{exchangeonline.rb → microsoft.rb} +115 -114
  133. data/lib/sisimai/rhost/mimecast.rb +51 -42
  134. data/lib/sisimai/rhost/nttdocomo.rb +12 -12
  135. data/lib/sisimai/rhost/spectrum.rb +10 -12
  136. data/lib/sisimai/rhost/{tencentqq.rb → tencent.rb} +7 -8
  137. data/lib/sisimai/rhost.rb +23 -31
  138. data/lib/sisimai/smtp/command.rb +59 -0
  139. data/lib/sisimai/smtp/error.rb +4 -7
  140. data/lib/sisimai/smtp/reply.rb +161 -74
  141. data/lib/sisimai/smtp/status.rb +507 -393
  142. data/lib/sisimai/smtp/transcript.rb +124 -0
  143. data/lib/sisimai/smtp.rb +0 -1
  144. data/lib/sisimai/string.rb +74 -5
  145. data/lib/sisimai/time.rb +1 -2
  146. data/lib/sisimai/version.rb +1 -1
  147. data/lib/sisimai.rb +46 -31
  148. data/set-of-emails/maildir/bsd/lhost-domino-02.eml +6 -3
  149. data/set-of-emails/maildir/bsd/lhost-googlegroups-15.eml +174 -0
  150. data/set-of-emails/maildir/bsd/lhost-gsuite-15.eml +229 -0
  151. data/set-of-emails/maildir/bsd/lhost-postfix-75.eml +51 -0
  152. data/set-of-emails/maildir/bsd/lhost-postfix-76.eml +101 -0
  153. data/set-of-emails/maildir/bsd/lhost-postfix-77.eml +74 -0
  154. data/set-of-emails/maildir/bsd/lhost-postfix-78.eml +91 -0
  155. data/set-of-emails/maildir/bsd/lhost-receivingses-08.eml +88 -0
  156. data/set-of-emails/maildir/bsd/lhost-sendmail-60.eml +85 -0
  157. data/set-of-emails/maildir/bsd/rfc3464-43.eml +88 -0
  158. data/set-of-emails/maildir/bsd/rhost-google-03.eml +101 -0
  159. data/set-of-emails/maildir/bsd/rhost-google-04.eml +102 -0
  160. data/set-of-emails/maildir/bsd/rhost-google-05.eml +82 -0
  161. data/set-of-emails/maildir/bsd/rhost-google-06.eml +102 -0
  162. data/set-of-emails/maildir/bsd/rhost-google-07.eml +69 -0
  163. data/set-of-emails/maildir/bsd/rhost-google-08.eml +99 -0
  164. data/sisimai-java.gemspec +1 -1
  165. data/sisimai.gemspec +1 -1
  166. metadata +48 -26
  167. data/.rspec +0 -2
  168. data/lib/sisimai/data/yaml.rb +0 -33
  169. data/lib/sisimai/data.rb +0 -411
  170. data/lib/sisimai/mime.rb +0 -456
  171. data/lib/sisimai/rhost/googleapps.rb +0 -261
  172. /data/set-of-emails/maildir/bsd/{rfc3464-41.eml → rfc3834-05.eml} +0 -0
  173. /data/set-of-emails/maildir/bsd/{rhost-googleapps-01.eml → rhost-google-01.eml} +0 -0
  174. /data/set-of-emails/maildir/bsd/{rhost-googleapps-02.eml → rhost-google-02.eml} +0 -0
  175. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-01.eml → rhost-microsoft-01.eml} +0 -0
  176. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-02.eml → rhost-microsoft-02.eml} +0 -0
  177. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-03.eml → rhost-microsoft-03.eml} +0 -0
  178. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-01.eml → rhost-tencent-01.eml} +0 -0
  179. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-02.eml → rhost-tencent-02.eml} +0 -0
  180. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-03.eml → rhost-tencent-03.eml} +0 -0
@@ -1,34 +1,23 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::Postfix parses a bounce email which created by
3
- # Postfix. Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::Postfix parses a bounce email which created by Postfix. Methods in the module are
3
+ # called from only Sisimai::Message.
4
4
  module Postfix
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/Postfix.pm
7
6
  require 'sisimai/lhost'
7
+ require 'sisimai/smtp/command'
8
8
 
9
9
  # Postfix manual - bounce(5) - http://www.postfix.org/bounce.5.html
10
10
  Indicators = Sisimai::Lhost.INDICATORS
11
- ReBackbone = %r<^Content-Type:[ ](?:message/rfc822|text/rfc822-headers)>.freeze
12
- MarkingsOf = {
13
- message: %r{\A(?>
14
- [ ]+The[ ](?:
15
- Postfix[ ](?:
16
- program\z # The Postfix program
17
- |on[ ].+[ ]program\z # The Postfix on <os name> program
18
- )
19
- |\w+[ ]Postfix[ ]program\z # The <name> Postfix program
20
- |mail[ \t]system\z # The mail system
21
- |\w+[ \t]program\z # The <custmized-name> program
22
- )
23
- |This[ ]is[ ]the[ ](?:
24
- Postfix[ ]program # This is the Postfix program
25
- |\w+[ ]Postfix[ ]program # This is the <name> Postfix program
26
- |\w+[ ]program # This is the <customized-name> Postfix program
27
- |mail[ ]system[ ]at[ ]host # This is the mail system at host <hostname>.
28
- )
29
- )
30
- }x,
31
- # :from => %r/ [(]Mail Delivery System[)]\z/,
11
+ Boundaries = ['Content-Type: message/rfc822', 'Content-Type: text/rfc822-headers'].freeze
12
+ StartingOf = {
13
+ # Postfix manual - bounce(5) - http://www.postfix.org/bounce.5.html
14
+ message: [
15
+ ['The ', 'Postfix '], # The Postfix program, The Postfix on <os> program
16
+ ['The ', 'mail system'], # The mail system
17
+ ['The ', 'program'], # The <name> pogram
18
+ ['This is the', 'Postfix'], # This is the Postfix program
19
+ ['This is the', 'mail system'], # This is the mail system at host <hostname>
20
+ ],
32
21
  }.freeze
33
22
 
34
23
  # Parse bounce messages from Postfix
@@ -36,109 +25,160 @@ module Sisimai::Lhost
36
25
  # @param [String] mbody Message body of a bounce email
37
26
  # @return [Hash] Bounce data list and message/rfc822 part
38
27
  # @return [Nil] it failed to parse or the arguments are missing
39
- def make(mhead, mbody)
40
- return nil unless mhead['subject'] == 'Undelivered Mail Returned to Sender'
28
+ def inquire(mhead, mbody)
29
+ match = nil
30
+ sessx = nil
31
+
32
+ if mhead['subject'].include?('SMTP server: errors from ')
33
+ # src/smtpd/smtpd_chat.c:|337: post_mail_fprintf(notice, "Subject: %s SMTP server: errors from %s",
34
+ # src/smtpd/smtpd_chat.c:|338: var_mail_name, state->namaddr);
35
+ match = true
36
+ sessx = true
37
+ else
38
+ # Subject: Undelivered Mail Returned to Sender
39
+ match = true if mhead['subject'] == 'Undelivered Mail Returned to Sender'
40
+ end
41
+ return nil unless match
41
42
  return nil if mhead['x-aol-ip']
42
43
 
43
- require 'sisimai/rfc1894'
44
- require 'sisimai/address'
45
- fieldtable = Sisimai::RFC1894.FIELDTABLE
46
44
  permessage = {} # (Hash) Store values of each Per-Message field
47
-
48
45
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
49
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
50
- bodyslices = emailsteak[0].split("\n")
46
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
47
+ bodyslices = emailparts[0].split("\n")
51
48
  readslices = ['']
52
- readcursor = 0 # (Integer) Points the current cursor position
53
49
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
54
50
  nomessages = false # (Boolean) Delivery report unavailable
55
51
  commandset = [] # (Array) ``in reply to * command'' list
56
52
  anotherset = {} # Another error information
57
53
  v = nil
58
54
 
59
- while e = bodyslices.shift do
60
- # Read error messages and delivery status lines from the head of the email
61
- # to the previous line of the beginning of the original message.
62
- readslices << e # Save the current line for the next loop
55
+ if sessx
56
+ # The message body starts with 'Transcript of session follows.'
57
+ require 'sisimai/smtp/transcript'
58
+ transcript = Sisimai::SMTP::Transcript.rise(emailparts[0], 'In:', 'Out:')
59
+
60
+ return nil unless transcript
61
+ return nil if transcript.size == 0
62
+
63
+ transcript.each do |e|
64
+ # Pick email addresses, error messages, and the last SMTP command.
65
+ v ||= dscontents[-1]
66
+ p = e['response']
67
+
68
+ if e['command'] == 'HELO' || e['command'] == 'EHLO'
69
+ # Use the argument of EHLO/HELO command as a value of "lhost"
70
+ v['lhost'] = e['argument']
71
+
72
+ elsif e['command'] == 'MAIL'
73
+ # Set the argument of "MAIL" command to pseudo To: header of the original message
74
+ emailparts[1] += sprintf("To: %s\n", e['argument']) if emailparts[1].size == 0
63
75
 
64
- if readcursor == 0
65
- # Beginning of the bounce message or message/delivery-status part
66
- readcursor |= Indicators[:deliverystatus] if e =~ MarkingsOf[:message]
67
- next
76
+ elsif e['command'] == 'RCPT'
77
+ # RCPT TO: <...>
78
+ if v['recipient']
79
+ # There are multiple recipient addresses in the transcript of session
80
+ dscontents << Sisimai::Lhost.DELIVERYSTATUS
81
+ v = dscontents[-1]
82
+ end
83
+ v['recipient'] = e['argument']
84
+ recipients += 1
85
+ end
86
+
87
+ next if p['reply'].to_i < 400
88
+ commandset << e['command']
89
+ v['diagnosis'] ||= p['text'].join(' ')
90
+ v['replycode'] ||= p['reply']
91
+ v['status'] ||= p['status']
68
92
  end
69
- next if (readcursor & Indicators[:deliverystatus]) == 0
70
- next if e.empty?
71
-
72
- if f = Sisimai::RFC1894.match(e)
73
- # "e" matched with any field defined in RFC3464
74
- next unless o = Sisimai::RFC1894.field(e)
75
- v = dscontents[-1]
76
-
77
- if o[-1] == 'addr'
78
- # Final-Recipient: rfc822; kijitora@example.jp
79
- # X-Actual-Recipient: rfc822; kijitora@example.co.jp
80
- if o[0] == 'final-recipient'
93
+ else
94
+ fieldtable = Sisimai::RFC1894.FIELDTABLE
95
+ readcursor = 0 # (Integer) Points the current cursor position
96
+
97
+ while e = bodyslices.shift do
98
+ # Read error messages and delivery status lines from the head of the email to the previous
99
+ # line of the beginning of the original message.
100
+ readslices << e # Save the current line for the next loop
101
+
102
+ if readcursor == 0
103
+ # Beginning of the bounce message or message/delivery-status part
104
+ readcursor |= Indicators[:deliverystatus] if StartingOf[:message].any? { |a| Sisimai::String.aligned(e, a) }
105
+ next
106
+ end
107
+ next if (readcursor & Indicators[:deliverystatus]) == 0
108
+ next if e.empty?
109
+
110
+ if f = Sisimai::RFC1894.match(e)
111
+ # "e" matched with any field defined in RFC3464
112
+ next unless o = Sisimai::RFC1894.field(e)
113
+ v = dscontents[-1]
114
+
115
+ if o[-1] == 'addr'
81
116
  # Final-Recipient: rfc822; kijitora@example.jp
82
- if v['recipient']
83
- # There are multiple recipient addresses in the message body.
84
- dscontents << Sisimai::Lhost.DELIVERYSTATUS
85
- v = dscontents[-1]
117
+ # X-Actual-Recipient: rfc822; kijitora@example.co.jp
118
+ if o[0] == 'final-recipient'
119
+ # Final-Recipient: rfc822; kijitora@example.jp
120
+ if v['recipient']
121
+ # There are multiple recipient addresses in the message body.
122
+ dscontents << Sisimai::Lhost.DELIVERYSTATUS
123
+ v = dscontents[-1]
124
+ end
125
+ v['recipient'] = o[2]
126
+ recipients += 1
127
+ else
128
+ # X-Actual-Recipient: rfc822; kijitora@example.co.jp
129
+ v['alias'] = o[2]
86
130
  end
87
- v['recipient'] = o[2]
88
- recipients += 1
131
+ elsif o[-1] == 'code'
132
+ # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
133
+ v['spec'] = o[1]
134
+ v['spec'] = 'SMTP' if v['spec'] == 'X-POSTFIX'
135
+ v['diagnosis'] = o[2]
89
136
  else
90
- # X-Actual-Recipient: rfc822; kijitora@example.co.jp
91
- v['alias'] = o[2]
137
+ # Other DSN fields defined in RFC3464
138
+ next unless fieldtable[o[0]]
139
+ v[fieldtable[o[0]]] = o[2]
140
+
141
+ next unless f
142
+ permessage[fieldtable[o[0]]] = o[2]
92
143
  end
93
- elsif o[-1] == 'code'
94
- # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
95
- v['spec'] = o[1]
96
- v['spec'] = 'SMTP' if v['spec'] == 'X-POSTFIX'
97
- v['diagnosis'] = o[2]
98
144
  else
99
- # Other DSN fields defined in RFC3464
100
- next unless fieldtable[o[0]]
101
- v[fieldtable[o[0]]] = o[2]
145
+ # If you do so, please include this problem report. You can
146
+ # delete your own text from the attached returned message.
147
+ #
148
+ # The mail system
149
+ #
150
+ # <userunknown@example.co.jp>: host mx.example.co.jp[192.0.2.153] said: 550
151
+ # 5.1.1 <userunknown@example.co.jp>... User Unknown (in reply to RCPT TO command)
152
+ if readslices[-2].start_with?('Diagnostic-Code:') && e.include?(' ')
153
+ # Continued line of the value of Diagnostic-Code header
154
+ v['diagnosis'] << ' ' << Sisimai::String.sweep(e)
155
+ readslices[-1] = 'Diagnostic-Code: ' << e
102
156
 
103
- next unless f == 1
104
- permessage[fieldtable[o[0]]] = o[2]
105
- end
106
- else
107
- # If you do so, please include this problem report. You can
108
- # delete your own text from the attached returned message.
109
- #
110
- # The mail system
111
- #
112
- # <userunknown@example.co.jp>: host mx.example.co.jp[192.0.2.153] said: 550
113
- # 5.1.1 <userunknown@example.co.jp>... User Unknown (in reply to RCPT TO command)
114
- if readslices[-2].start_with?('Diagnostic-Code:') && cv = e.match(/\A[ \t]+(.+)\z/)
115
- # Continued line of the value of Diagnostic-Code header
116
- v['diagnosis'] << ' ' << cv[1]
117
- readslices[-1] = 'Diagnostic-Code: ' << e
118
-
119
- elsif cv = e.match(/\A(X-Postfix-Sender):[ ]*rfc822;[ ]*(.+)\z/)
120
- # X-Postfix-Sender: rfc822; shironeko@example.org
121
- emailsteak[1] << cv[1] << ': ' << cv[2] << "\n"
157
+ elsif Sisimai::String.aligned(e, ['X-Postfix-Sender:', 'rfc822;', '@'])
158
+ # X-Postfix-Sender: rfc822; shironeko@example.org
159
+ emailparts[1] << 'X-Postfix-Sender: ' << Sisimai::Address.s3s4(e[e.index(';') + 1, e.size]) << "\n"
122
160
 
123
- else
124
- if cv = e.match(/[ \t][(]in reply to (?:end of )?([A-Z]{4}).*/) ||
125
- cv = e.match(/([A-Z]{4})[ \t]*.*command[)]\z/)
126
- # 5.1.1 <userunknown@example.co.jp>... User Unknown (in reply to RCPT TO
127
- commandset << cv[1]
128
- anotherset['diagnosis'] ||= ''
129
- anotherset['diagnosis'] << ' ' << e
130
161
  else
131
162
  # Alternative error message and recipient
132
- if cv = e.match(/\A[<]([^ ]+[@][^ ]+)[>] [(]expanded from [<](.+)[>][)]:[ \t]*(.+)\z/)
163
+ if e.include?(' (in reply to ') || e.include?('command)')
164
+ # 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
166
+ anotherset['diagnosis'] ||= ''
167
+ anotherset['diagnosis'] << ' ' << e
168
+
169
+ elsif Sisimai::String.aligned(e, ['<', '@', '>', '(expanded from<', '):'])
133
170
  # <r@example.ne.jp> (expanded from <kijitora@example.org>): user ...
134
- anotherset['recipient'] = cv[1]
135
- anotherset['alias'] = cv[2]
136
- anotherset['diagnosis'] = cv[3]
171
+ p1 = e.index('> ')
172
+ p2 = e.index('(expanded from ', p1)
173
+ p3 = e.index('>): ', p2 + 14)
174
+ anotherset['recipient'] = Sisimai::Address.s3s4(e[0, p1])
175
+ anotherset['alias'] = Sisimai::Address.s3s4(e[p2 + 15, p3 - p2 - 15])
176
+ anotherset['diagnosis'] = e[p3 + 3, e.size]
137
177
 
138
- elsif cv = e.match(/\A[<]([^ ]+[@][^ ]+)[>]:(.*)\z/)
178
+ elsif e.start_with?('<') && Sisimai::String.aligned(e, ['<', '@', '>:'])
139
179
  # <kijitora@exmaple.jp>: ...
140
- anotherset['recipient'] = cv[1]
141
- anotherset['diagnosis'] = cv[2]
180
+ anotherset['recipient'] = Sisimai::Address.s3s4(e[0, e.index('>')])
181
+ anotherset['diagnosis'] = e[e.index('>:') + 2, e.size]
142
182
 
143
183
  elsif e.include?('--- Delivery report unavailable ---')
144
184
  # postfix-3.1.4/src/bounce/bounce_notify_util.c
@@ -152,16 +192,17 @@ module Sisimai::Lhost
152
192
  # bounce_notify_util.c:602|} else {
153
193
  nomessages = true
154
194
  else
155
- # Get error message continued from the previous line
195
+ # Get an error message continued from the previous line
156
196
  next unless anotherset['diagnosis']
157
- if e =~ /\A[ \t]{4}(.+)\z/
197
+ if e.start_with?(' ')
158
198
  # host mx.example.jp said:...
159
- anotherset['diagnosis'] << ' ' << e
199
+ anotherset['diagnosis'] << ' ' << e[4, e.size]
160
200
  end
161
201
  end
162
202
  end
163
203
  end
164
- end
204
+ end # end of while()
205
+
165
206
  end
166
207
 
167
208
  unless recipients > 0
@@ -171,12 +212,13 @@ module Sisimai::Lhost
171
212
  dscontents[-1]['recipient'] = anotherset['recipient']
172
213
  recipients += 1
173
214
  else
174
- # Get a recipient address from message/rfc822 part if the delivery
175
- # report was unavailable: '--- Delivery report unavailable ---'
176
- if nomessages && cv = emailsteak[1].match(/^To:[ ]*(.+)$/)
177
- # Try to get a recipient address from To: field in the original
178
- # message at message/rfc822 part
179
- dscontents[-1]['recipient'] = Sisimai::Address.s3s4(cv[1])
215
+ # Get a recipient address from message/rfc822 part if the delivery report was unavailable:
216
+ # '--- Delivery report unavailable ---'
217
+ p1 = emailparts[1].index("\nTo: ") || -1
218
+ p2 = emailparts[1].index("\n", p1 + 6) || -1
219
+ if nomessages && p1 > 0
220
+ # Try to get a recipient address from To: field in the original message at message/rfc822 part
221
+ dscontents[-1]['recipient'] = Sisimai::Address.s3s4(emailparts[1][p1 + 5, p2 - p1 - 5])
180
222
  recipients += 1
181
223
  end
182
224
  end
@@ -185,55 +227,62 @@ module Sisimai::Lhost
185
227
 
186
228
  dscontents.each do |e|
187
229
  # Set default values if each value is empty.
188
- e['lhost'] ||= permessage['rhost']
189
230
  permessage.each_key { |a| e[a] ||= permessage[a] || '' }
190
231
 
191
232
  if anotherset['diagnosis']
192
233
  # Copy alternative error message
193
- e['diagnosis'] = anotherset['diagnosis'] unless e['diagnosis']
234
+ anotherset['diagnosis'] = Sisimai::String.sweep(anotherset['diagnosis'])
235
+ e['diagnosis'] = anotherset['diagnosis'] if e['diagnosis'].nil? || e['diagnosis'].empty?
194
236
 
195
237
  if e['diagnosis'] =~ /\A\d+\z/
238
+ # Override the value of diagnostic code message
196
239
  e['diagnosis'] = anotherset['diagnosis']
197
240
  else
198
241
  # More detailed error message is in "anotherset"
199
- as = nil # status
200
- ar = nil # replycode
242
+ as = '' # status
243
+ ar = '' # replycode
201
244
 
202
245
  e['status'] ||= ''
203
246
  e['replycode'] ||= ''
204
247
 
205
- if e['status'] == '' || e['status'].start_with?('4.0.0', '5.0.0')
248
+ if e['status'].empty? || e['status'].start_with?('4.0.0', '5.0.0')
206
249
  # Check the value of D.S.N. in anotherset
207
- as = Sisimai::SMTP::Status.find(anotherset['diagnosis'])
208
- if as && as[-3, 3] != '0.0'
250
+ as = Sisimai::SMTP::Status.find(anotherset['diagnosis']) || ''
251
+ if as.size > 0 && as[-4, 4] != '.0.0'
209
252
  # The D.S.N. is neither an empty nor *.0.0
210
253
  e['status'] = as
211
254
  end
212
255
  end
213
256
 
214
- if e['replycode'] == '' || e['replycode'].start_with?('400', '500')
215
- # Check the value of SMTP reply code in anotherset
216
- ar = Sisimai::SMTP::Reply.find(anotherset['diagnosis'])
217
- if ar && ar[-2, 2].to_i != 0
257
+ if e['replycode'].empty? || e['replycode'].end_with?('00')
258
+ # Check the value of SMTP reply code in $anotherset
259
+ ar = Sisimai::SMTP::Reply.find(anotherset['diagnosis']) || ''
260
+ if ar.size > 0 && ar.end_with?('00') == false
218
261
  # The SMTP reply code is neither an empty nor *00
219
262
  e['replycode'] = ar
220
263
  end
221
264
  end
222
265
 
223
- if (as || ar) && (anotherset['diagnosis'].size > e['diagnosis'].size)
224
- # Update the error message in e['diagnosis']
266
+ while true
267
+ # Replace e['diagnosis'] with the value of anotherset['diagnosis'] when all the
268
+ # following conditions have not matched.
269
+ break if (as + ar).size == 0
270
+ break if anotherset['diagnosis'].size < e['diagnosis'].size
271
+ break if anotherset['diagnosis'].include?(e['diagnosis']) == false
272
+
225
273
  e['diagnosis'] = anotherset['diagnosis']
274
+ break
226
275
  end
227
276
  end
228
277
  end
229
278
 
230
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
231
- e['command'] = commandset.shift || nil
232
- e['command'] ||= 'HELO' if e['diagnosis'] =~ /refused to talk to me:/
233
- e['spec'] ||= 'SMTP' if e['diagnosis'] =~ /host .+ said:/
279
+ e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''
280
+ 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:'])
234
283
  end
235
284
 
236
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
285
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
237
286
  end
238
287
  def description; return 'Postfix'; end
239
288
  end
@@ -1,13 +1,12 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::PowerMTA parses a bounce email which created by
3
- # amavsid-new. Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::PowerMTA parses a bounce email which created by PowerMTA. Methods in the module
3
+ # are called from only Sisimai::Message.
4
4
  module PowerMTA
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/PowerMTA.pm
7
6
  require 'sisimai/lhost'
8
7
 
9
8
  Indicators = Sisimai::Lhost.INDICATORS
10
- ReBackbone = %r|^Content-Type:[ ]text/rfc822-headers|.freeze
9
+ Boundaries = ['Content-Type: text/rfc822-headers'].freeze
11
10
  StartingOf = { message: ['Hello, this is the mail server on '] }.freeze
12
11
  Categories = {
13
12
  'bad-domain' => 'hostunknown',
@@ -27,23 +26,22 @@ module Sisimai::Lhost
27
26
  # @return [Hash] Bounce data list and message/rfc822 part
28
27
  # @return [Nil] it failed to parse or the arguments are missing
29
28
  # @since v4.25.6
30
- def make(mhead, mbody)
29
+ def inquire(mhead, mbody)
31
30
  return nil unless mhead['subject'].to_s.start_with?('Delivery report')
32
31
 
33
- require 'sisimai/rfc1894'
34
32
  fieldtable = Sisimai::RFC1894.FIELDTABLE
35
33
  permessage = {} # (Hash) Store values of each Per-Message field
36
34
 
37
35
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
38
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
39
- bodyslices = emailsteak[0].split("\n")
36
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
37
+ bodyslices = emailparts[0].split("\n")
40
38
  readcursor = 0 # (Integer) Points the current cursor position
41
39
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
42
40
  v = nil
43
41
 
44
42
  while e = bodyslices.shift do
45
- # Read error messages and delivery status lines from the head of the email
46
- # to the previous line of the beginning of the original message.
43
+ # Read error messages and delivery status lines from the head of the email to the previous
44
+ # line of the beginning of the original message.
47
45
  if readcursor == 0
48
46
  # Beginning of the bounce message or message/delivery-status part
49
47
  readcursor |= Indicators[:deliverystatus] if e.start_with?(StartingOf[:message][0])
@@ -82,7 +80,7 @@ module Sisimai::Lhost
82
80
  next unless fieldtable[o[0]]
83
81
  v[fieldtable[o[0]]] = o[2]
84
82
 
85
- next unless f == 1
83
+ next unless f
86
84
  permessage[fieldtable[o[0]]] = o[2]
87
85
  end
88
86
  else
@@ -96,9 +94,9 @@ module Sisimai::Lhost
96
94
  #
97
95
  # <kijitora@example.jp> delivery failed; will not continue trying
98
96
  #
99
- if cv = e.match(/\AX-PowerMTA-BounceCategory:[ ]*(.+)\z/)
97
+ if e.start_with?('X-PowerMTA-BounceCategory: ')
100
98
  # X-PowerMTA-BounceCategory: bad-mailbox
101
- v['category'] = cv[1]
99
+ v['category'] = e[e.index(':') + 2, e.size]
102
100
  end
103
101
  end
104
102
  end
@@ -111,7 +109,7 @@ module Sisimai::Lhost
111
109
  e['reason'] = Categories[e['category']] || ''
112
110
  end
113
111
 
114
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
112
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
115
113
  end
116
114
  def description; return 'PowerMTA: https://www.sparkpost.com/powermta/'; end
117
115
  end