sisimai 4.25.15 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (177) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -3
  3. data/ANALYTICAL-PRECISION +2 -2
  4. data/Benchmarks.mk +3 -3
  5. data/CONTRIBUTING +1 -1
  6. data/ChangeLog.md +419 -388
  7. data/Developers.mk +5 -6
  8. data/Gemfile +1 -1
  9. data/Makefile +15 -15
  10. data/README-JA.md +140 -78
  11. data/README.md +290 -143
  12. data/Rakefile +9 -3
  13. data/Repository.mk +2 -3
  14. data/lib/sisimai/address.rb +118 -74
  15. data/lib/sisimai/arf.rb +84 -82
  16. data/lib/sisimai/datetime.rb +5 -52
  17. data/lib/sisimai/{data → fact}/json.rb +7 -9
  18. data/lib/sisimai/fact/yaml.rb +31 -0
  19. data/lib/sisimai/fact.rb +468 -0
  20. data/lib/sisimai/lhost/activehunter.rb +12 -14
  21. data/lib/sisimai/lhost/amavis.rb +11 -14
  22. data/lib/sisimai/lhost/amazonses.rb +37 -41
  23. data/lib/sisimai/lhost/amazonworkmail.rb +15 -18
  24. data/lib/sisimai/lhost/aol.rb +12 -14
  25. data/lib/sisimai/lhost/apachejames.rb +19 -21
  26. data/lib/sisimai/lhost/barracuda.rb +10 -12
  27. data/lib/sisimai/lhost/bigfoot.rb +21 -21
  28. data/lib/sisimai/lhost/biglobe.rb +15 -16
  29. data/lib/sisimai/lhost/courier.rb +20 -20
  30. data/lib/sisimai/lhost/domino.rb +23 -19
  31. data/lib/sisimai/lhost/einsundeins.rb +23 -18
  32. data/lib/sisimai/lhost/exchange2003.rb +30 -29
  33. data/lib/sisimai/lhost/exchange2007.rb +70 -58
  34. data/lib/sisimai/lhost/exim.rb +175 -161
  35. data/lib/sisimai/lhost/ezweb.rb +31 -56
  36. data/lib/sisimai/lhost/facebook.rb +21 -33
  37. data/lib/sisimai/lhost/fml.rb +43 -48
  38. data/lib/sisimai/lhost/gmail.rb +29 -29
  39. data/lib/sisimai/lhost/gmx.rb +18 -17
  40. data/lib/sisimai/lhost/googlegroups.rb +9 -10
  41. data/lib/sisimai/lhost/gsuite.rb +21 -27
  42. data/lib/sisimai/lhost/imailserver.rb +25 -39
  43. data/lib/sisimai/lhost/interscanmss.rb +28 -31
  44. data/lib/sisimai/lhost/kddi.rb +22 -28
  45. data/lib/sisimai/lhost/mailfoundry.rb +11 -12
  46. data/lib/sisimai/lhost/mailmarshalsmtp.rb +25 -29
  47. data/lib/sisimai/lhost/mailru.rb +33 -27
  48. data/lib/sisimai/lhost/mcafee.rb +21 -31
  49. data/lib/sisimai/lhost/messagelabs.rb +17 -20
  50. data/lib/sisimai/lhost/messagingserver.rb +40 -37
  51. data/lib/sisimai/lhost/mfilter.rb +15 -16
  52. data/lib/sisimai/lhost/mxlogic.rb +24 -23
  53. data/lib/sisimai/lhost/notes.rb +17 -17
  54. data/lib/sisimai/lhost/office365.rb +63 -27
  55. data/lib/sisimai/lhost/opensmtpd.rb +12 -13
  56. data/lib/sisimai/lhost/outlook.rb +12 -15
  57. data/lib/sisimai/lhost/postfix.rb +179 -129
  58. data/lib/sisimai/lhost/powermta.rb +12 -14
  59. data/lib/sisimai/lhost/qmail.rb +44 -47
  60. data/lib/sisimai/lhost/receivingses.rb +15 -20
  61. data/lib/sisimai/lhost/sendgrid.rb +34 -32
  62. data/lib/sisimai/lhost/sendmail.rb +66 -53
  63. data/lib/sisimai/lhost/surfcontrol.rb +19 -19
  64. data/lib/sisimai/lhost/v5sendmail.rb +45 -39
  65. data/lib/sisimai/lhost/verizon.rb +35 -39
  66. data/lib/sisimai/lhost/x1.rb +18 -17
  67. data/lib/sisimai/lhost/x2.rb +17 -14
  68. data/lib/sisimai/lhost/x3.rb +19 -19
  69. data/lib/sisimai/lhost/x4.rb +72 -57
  70. data/lib/sisimai/lhost/x5.rb +17 -19
  71. data/lib/sisimai/lhost/x6.rb +41 -17
  72. data/lib/sisimai/lhost/yahoo.rb +17 -16
  73. data/lib/sisimai/lhost/yandex.rb +16 -20
  74. data/lib/sisimai/lhost/zoho.rb +16 -15
  75. data/lib/sisimai/lhost.rb +8 -10
  76. data/lib/sisimai/mail/maildir.rb +1 -3
  77. data/lib/sisimai/mail/mbox.rb +3 -4
  78. data/lib/sisimai/mail/memory.rb +0 -1
  79. data/lib/sisimai/mail/stdin.rb +1 -3
  80. data/lib/sisimai/mail.rb +3 -7
  81. data/lib/sisimai/mda.rb +28 -42
  82. data/lib/sisimai/message.rb +435 -313
  83. data/lib/sisimai/order.rb +5 -5
  84. data/lib/sisimai/reason/authfailure.rb +64 -0
  85. data/lib/sisimai/reason/badreputation.rb +53 -0
  86. data/lib/sisimai/reason/blocked.rb +94 -160
  87. data/lib/sisimai/reason/contenterror.rb +8 -9
  88. data/lib/sisimai/reason/delivered.rb +4 -6
  89. data/lib/sisimai/reason/exceedlimit.rb +10 -12
  90. data/lib/sisimai/reason/expired.rb +6 -8
  91. data/lib/sisimai/reason/feedback.rb +2 -3
  92. data/lib/sisimai/reason/filtered.rb +17 -19
  93. data/lib/sisimai/reason/hasmoved.rb +9 -10
  94. data/lib/sisimai/reason/hostunknown.rb +15 -15
  95. data/lib/sisimai/reason/mailboxfull.rb +10 -12
  96. data/lib/sisimai/reason/mailererror.rb +18 -20
  97. data/lib/sisimai/reason/mesgtoobig.rb +9 -11
  98. data/lib/sisimai/reason/networkerror.rb +5 -8
  99. data/lib/sisimai/reason/norelaying.rb +8 -11
  100. data/lib/sisimai/reason/notaccept.rb +13 -14
  101. data/lib/sisimai/reason/notcompliantrfc.rb +43 -0
  102. data/lib/sisimai/reason/onhold.rb +6 -9
  103. data/lib/sisimai/reason/policyviolation.rb +14 -12
  104. data/lib/sisimai/reason/rejected.rb +26 -24
  105. data/lib/sisimai/reason/requireptr.rb +69 -0
  106. data/lib/sisimai/reason/securityerror.rb +33 -36
  107. data/lib/sisimai/reason/spamdetected.rb +114 -147
  108. data/lib/sisimai/reason/speeding.rb +49 -0
  109. data/lib/sisimai/reason/suspend.rb +11 -11
  110. data/lib/sisimai/reason/syntaxerror.rb +11 -10
  111. data/lib/sisimai/reason/systemerror.rb +7 -9
  112. data/lib/sisimai/reason/systemfull.rb +7 -8
  113. data/lib/sisimai/reason/toomanyconn.rb +9 -11
  114. data/lib/sisimai/reason/undefined.rb +2 -3
  115. data/lib/sisimai/reason/userunknown.rb +129 -146
  116. data/lib/sisimai/reason/vacation.rb +3 -4
  117. data/lib/sisimai/reason/virusdetected.rb +10 -11
  118. data/lib/sisimai/reason.rb +59 -64
  119. data/lib/sisimai/rfc1894.rb +55 -28
  120. data/lib/sisimai/rfc2045.rb +373 -0
  121. data/lib/sisimai/rfc3464.rb +250 -308
  122. data/lib/sisimai/rfc3834.rb +42 -47
  123. data/lib/sisimai/rfc5322.rb +75 -100
  124. data/lib/sisimai/rfc5965.rb +31 -0
  125. data/lib/sisimai/rhost/cox.rb +5 -6
  126. data/lib/sisimai/rhost/franceptt.rb +6 -8
  127. data/lib/sisimai/rhost/godaddy.rb +12 -12
  128. data/lib/sisimai/rhost/{googleapps.rb → google.rb} +80 -72
  129. data/lib/sisimai/rhost/iua.rb +9 -10
  130. data/lib/sisimai/rhost/kddi.rb +6 -8
  131. data/lib/sisimai/rhost/{exchangeonline.rb → microsoft.rb} +115 -114
  132. data/lib/sisimai/rhost/mimecast.rb +42 -40
  133. data/lib/sisimai/rhost/nttdocomo.rb +12 -12
  134. data/lib/sisimai/rhost/spectrum.rb +10 -12
  135. data/lib/sisimai/rhost/{tencentqq.rb → tencent.rb} +7 -8
  136. data/lib/sisimai/rhost.rb +23 -31
  137. data/lib/sisimai/smtp/command.rb +59 -0
  138. data/lib/sisimai/smtp/error.rb +4 -7
  139. data/lib/sisimai/smtp/reply.rb +161 -74
  140. data/lib/sisimai/smtp/status.rb +504 -393
  141. data/lib/sisimai/smtp/transcript.rb +124 -0
  142. data/lib/sisimai/smtp.rb +0 -1
  143. data/lib/sisimai/string.rb +74 -5
  144. data/lib/sisimai/time.rb +1 -2
  145. data/lib/sisimai/version.rb +1 -1
  146. data/lib/sisimai.rb +35 -21
  147. data/set-of-emails/maildir/bsd/lhost-domino-02.eml +6 -3
  148. data/set-of-emails/maildir/bsd/lhost-googlegroups-15.eml +174 -0
  149. data/set-of-emails/maildir/bsd/lhost-gsuite-15.eml +229 -0
  150. data/set-of-emails/maildir/bsd/lhost-postfix-75.eml +51 -0
  151. data/set-of-emails/maildir/bsd/lhost-postfix-76.eml +101 -0
  152. data/set-of-emails/maildir/bsd/lhost-postfix-77.eml +74 -0
  153. data/set-of-emails/maildir/bsd/lhost-postfix-78.eml +91 -0
  154. data/set-of-emails/maildir/bsd/lhost-receivingses-08.eml +88 -0
  155. data/set-of-emails/maildir/bsd/rfc3464-43.eml +88 -0
  156. data/set-of-emails/maildir/bsd/rhost-google-03.eml +101 -0
  157. data/set-of-emails/maildir/bsd/rhost-google-04.eml +102 -0
  158. data/set-of-emails/maildir/bsd/rhost-google-05.eml +82 -0
  159. data/set-of-emails/maildir/bsd/rhost-google-06.eml +102 -0
  160. data/set-of-emails/maildir/bsd/rhost-google-07.eml +69 -0
  161. data/set-of-emails/maildir/bsd/rhost-google-08.eml +99 -0
  162. data/sisimai-java.gemspec +1 -1
  163. data/sisimai.gemspec +1 -1
  164. metadata +41 -20
  165. data/.rspec +0 -2
  166. data/lib/sisimai/data/yaml.rb +0 -33
  167. data/lib/sisimai/data.rb +0 -411
  168. data/lib/sisimai/mime.rb +0 -456
  169. /data/set-of-emails/maildir/bsd/{rfc3464-41.eml → rfc3834-05.eml} +0 -0
  170. /data/set-of-emails/maildir/bsd/{rhost-googleapps-01.eml → rhost-google-01.eml} +0 -0
  171. /data/set-of-emails/maildir/bsd/{rhost-googleapps-02.eml → rhost-google-02.eml} +0 -0
  172. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-01.eml → rhost-microsoft-01.eml} +0 -0
  173. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-02.eml → rhost-microsoft-02.eml} +0 -0
  174. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-03.eml → rhost-microsoft-03.eml} +0 -0
  175. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-01.eml → rhost-tencent-01.eml} +0 -0
  176. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-02.eml → rhost-tencent-02.eml} +0 -0
  177. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-03.eml → rhost-tencent-03.eml} +0 -0
@@ -1,14 +1,12 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::Activehunter parses a bounce email which created by
3
- # TransWARE Active!hunter.
2
+ # Sisimai::Lhost::Activehunter parses a bounce email which created by TransWARE Active!hunter.
4
3
  # Methods in the module are called from only Sisimai::Message.
5
4
  module Activehunter
6
5
  class << self
7
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/Activehunter.pm
8
6
  require 'sisimai/lhost'
9
7
 
10
8
  Indicators = Sisimai::Lhost.INDICATORS
11
- ReBackbone = %r|^Content-type:[ ]message/rfc822|.freeze
9
+ Boundaries = ['Content-Type: message/rfc822'].freeze
12
10
  StartingOf = { message: [' ----- The following addresses had permanent fatal errors -----'] }.freeze
13
11
 
14
12
  # Parse bounce messages from TransWARE Active!hunter
@@ -16,21 +14,21 @@ module Sisimai::Lhost
16
14
  # @param [String] mbody Message body of a bounce email
17
15
  # @return [Hash] Bounce data list and message/rfc822 part
18
16
  # @return [Nil] it failed to parse or the arguments are missing
19
- def make(mhead, mbody)
17
+ def inquire(mhead, mbody)
20
18
  # :from => %r/\A"MAILER-DAEMON"/,
21
19
  # :subject => %r/FAILURE NOTICE :/,
22
20
  return nil unless mhead['x-ahmailid']
23
21
 
24
22
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
25
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
26
- bodyslices = emailsteak[0].split("\n")
23
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
24
+ bodyslices = emailparts[0].split("\n")
27
25
  readcursor = 0 # (Integer) Points the current cursor position
28
26
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
29
27
  v = nil
30
28
 
31
29
  while e = bodyslices.shift do
32
- # Read error messages and delivery status lines from the head of the email
33
- # to the previous line of the beginning of the original message.
30
+ # Read error messages and delivery status lines from the head of the email to the previous
31
+ # line of the beginning of the original message.
34
32
  if readcursor == 0
35
33
  # Beginning of the bounce message or delivery status part
36
34
  readcursor |= Indicators[:deliverystatus] if e == StartingOf[:message][0]
@@ -47,29 +45,29 @@ module Sisimai::Lhost
47
45
  # 550 sorry, no mailbox here by that name (#5.1.1 - chkusr)
48
46
  v = dscontents[-1]
49
47
 
50
- if cv = e.match(/\A[>]{3}[ \t]+.+[<]([^ ]+?[@][^ ]+?)[>]\z/)
48
+ if e.start_with?('>>> ') && e.index('@') > 1
51
49
  # >>> kijitora@example.org <kijitora@example.org>
52
50
  if v['recipient']
53
51
  # There are multiple recipient addresses in the message body.
54
52
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
55
53
  v = dscontents[-1]
56
54
  end
57
- v['recipient'] = cv[1]
55
+ v['recipient'] = Sisimai::Address.s3s4(e[e.index('<'), e.size])
58
56
  v['diagnosis'] = ''
59
57
  recipients += 1
60
58
  else
61
59
  # ----- Transcript of session follows -----
62
60
  # 550 sorry, no mailbox here by that name (#5.1.1 - chkusr)
63
- next unless e =~ /\A[0-9A-Za-z]+/
61
+ next if e[0, 1].ord < 48
62
+ next if e[0, 1].ord > 122
64
63
  next unless v['diagnosis'].empty?
65
64
  v['diagnosis'] = e
66
65
  end
67
66
  end
68
67
  return nil unless recipients > 0
69
68
 
70
- require 'sisimai/string'
71
69
  dscontents.each { |e| e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) }
72
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
70
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
73
71
  end
74
72
  def description; return 'TransWARE Active!hunter'; end
75
73
  end
@@ -1,13 +1,12 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::Amavis parses a bounce email which created by
3
- # amavsid-new. Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::Amavis parses a bounce email which created by amavsid-new. Methods in the module
3
+ # are called from only Sisimai::Message.
4
4
  module Amavis
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/Amavis.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: ['The message '] }.freeze
12
11
  MessagesOf = {
13
12
  # amavisd-new-2.11.1/amavisd:1840|%smtp_reason_by_ccat = (
@@ -75,25 +74,24 @@ module Sisimai::Lhost
75
74
  # @return [Hash] Bounce data list and message/rfc822 part
76
75
  # @return [Nil] it failed to parse or the arguments are missing
77
76
  # @since v4.25.0
78
- def make(mhead, mbody)
77
+ def inquire(mhead, mbody)
79
78
  # From: "Content-filter at neko1.example.jp" <postmaster@neko1.example.jp>
80
79
  # Subject: Undeliverable mail, MTA-BLOCKED
81
80
  return nil unless mhead['from'].to_s.start_with?('"Content-filter at ')
82
81
 
83
- require 'sisimai/rfc1894'
84
82
  fieldtable = Sisimai::RFC1894.FIELDTABLE
85
83
  permessage = {} # (Hash) Store values of each Per-Message field
86
84
 
87
85
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
88
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
89
- bodyslices = emailsteak[0].split("\n")
86
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
87
+ bodyslices = emailparts[0].split("\n")
90
88
  readcursor = 0 # (Integer) Points the current cursor position
91
89
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
92
90
  v = nil
93
91
 
94
92
  while e = bodyslices.shift do
95
- # Read error messages and delivery status lines from the head of the email
96
- # to the previous line of the beginning of the original message.
93
+ # Read error messages and delivery status lines from the head of the email to the previous
94
+ # line of the beginning of the original message.
97
95
 
98
96
  if readcursor == 0
99
97
  # Beginning of the bounce message or message/delivery-status part
@@ -133,7 +131,7 @@ module Sisimai::Lhost
133
131
  next unless fieldtable[o[0]]
134
132
  v[fieldtable[o[0]]] = o[2]
135
133
 
136
- next unless f == 1
134
+ next unless f
137
135
  permessage[fieldtable[o[0]]] = o[2]
138
136
  end
139
137
  end
@@ -148,8 +146,7 @@ module Sisimai::Lhost
148
146
  MessagesOf.each_key do |p|
149
147
  # Try to detect an error reason
150
148
  MessagesOf[p].each do |r|
151
- # Try to find an error message including lower-cased string
152
- # defined in MessagesOf constant
149
+ # Try to find an error message including lower-cased string defined in MessagesOf constant
153
150
  next unless q.include?(r)
154
151
  e['reason'] = p
155
152
  throw :DETECT_REASON
@@ -157,7 +154,7 @@ module Sisimai::Lhost
157
154
  end
158
155
  end
159
156
  end
160
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
157
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
161
158
  end
162
159
  def description; return 'amavisd-new: https://www.amavis.org/'; end
163
160
  end
@@ -1,15 +1,13 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::AmazonSES parses a bounce email which created by
3
- # Amazon Simple Email Service.
2
+ # Sisimai::Lhost::AmazonSES parses a bounce email which created by Amazon Simple Email Service.
4
3
  # Methods in the module are called from only Sisimai::Message.
5
4
  module AmazonSES
6
5
  class << self
7
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/AmazonSES.pm
8
6
  require 'sisimai/lhost'
9
7
 
10
8
  # https://aws.amazon.com/ses/
11
9
  Indicators = Sisimai::Lhost.INDICATORS
12
- ReBackbone = %r|^content-type:[ ]message/rfc822|.freeze
10
+ Boundaries = ['Content-Type: message/rfc822'].freeze
13
11
  StartingOf = {
14
12
  message: ['The following message to <', 'An error occurred while trying to deliver the mail'],
15
13
  }.freeze
@@ -20,14 +18,14 @@ module Sisimai::Lhost
20
18
  # @param [String] mbody Message body of a bounce email
21
19
  # @return [Hash] Bounce data list and message/rfc822 part
22
20
  # @return [Nil] it failed to parse or the arguments are missing
23
- def make(mhead, mbody)
21
+ def inquire(mhead, mbody)
24
22
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
25
23
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
26
24
 
27
25
  if mbody.start_with?('{')
28
26
  # The message body is JSON string
29
27
  return nil unless mhead['x-amz-sns-message-id']
30
- return nil if mhead['x-amz-sns-message-id'].empty?
28
+ return nil if mhead['x-amz-sns-message-id'].empty?
31
29
 
32
30
  # https://docs.aws.amazon.com/en_us/ses/latest/DeveloperGuide/notification-contents.html
33
31
  bouncetype = {
@@ -102,7 +100,7 @@ module Sisimai::Lhost
102
100
  while e = r.shift do
103
101
  # 'bouncedRecipients' => [ { 'emailAddress' => 'bounce@si...' }, ... ]
104
102
  # 'complainedRecipients' => [ { 'emailAddress' => 'complaint@si...' }, ... ]
105
- next unless Sisimai::RFC5322.is_emailaddress(e['emailAddress'])
103
+ next unless Sisimai::Address.is_emailaddress(e['emailAddress'])
106
104
 
107
105
  v = dscontents[-1]
108
106
  if v['recipient']
@@ -123,22 +121,22 @@ module Sisimai::Lhost
123
121
  v['action'] = e['action']
124
122
  v['status'] = e['status']
125
123
 
126
- if cv = e['diagnosticCode'].match(/\A(.+?);[ ]*(.+)\z/)
124
+ if p0 = e['diagnosticCode'].index(';') || -1; p0 > 3
127
125
  # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
128
- v['spec'] = cv[1].upcase
129
- v['diagnosis'] = cv[2]
126
+ v['spec'] = e['diagnosticCode'][0, p0].upcase
127
+ v['diagnosis'] = e['diagnosticCode'][p0 + 2, e['diagnosticCode'].size]
130
128
  else
131
129
  v['diagnosis'] = e['diagnosticCode']
132
130
  end
133
131
 
134
132
  # 'reportingMTA' => 'dsn; a27-23.smtp-out.us-west-2.amazonses.com',
135
- if cv = o['reportingMTA'].match(/\Adsn;[ ](.+)\z/) then v['lhost'] = cv[1] end
133
+ p0 = o['reportingMTA'].index('dsn; ')
134
+ v['lhost'] = Sisimai::String.sweep(o['reportingMTA'][p0 + 5, o['reportingMTA'].size]) if p0
136
135
 
137
- if bouncetype[o['bounceType']] &&
138
- bouncetype[o['bounceType']][o['bounceSubType']]
136
+ if bouncetype[o['bounceType']] && bouncetype[o['bounceType']][o['bounceSubType']]
139
137
  # 'bounce' => {
140
- # 'bounceType' => 'Permanent',
141
- # 'bounceSubType' => 'General'
138
+ # 'bounceType' => 'Permanent',
139
+ # 'bounceSubType' => 'General'
142
140
  # },
143
141
  v['reason'] = bouncetype[o['bounceType']][o['bounceSubType']]
144
142
  end
@@ -159,15 +157,15 @@ module Sisimai::Lhost
159
157
 
160
158
  while e = r.shift do
161
159
  # 'delivery' => {
162
- # 'timestamp' => '2016-11-23T12:01:03.512Z',
163
- # 'processingTimeMillis' => 3982,
164
- # 'reportingMTA' => 'a27-29.smtp-out.us-west-2.amazonses.com',
165
- # 'recipients' => [
166
- # 'success@simulator.amazonses.com'
167
- # ],
168
- # 'smtpResponse' => '250 2.6.0 Message received'
169
- # },
170
- next unless Sisimai::RFC5322.is_emailaddress(e)
160
+ # 'timestamp' => '2016-11-23T12:01:03.512Z',
161
+ # 'processingTimeMillis' => 3982,
162
+ # 'reportingMTA' => 'a27-29.smtp-out.us-west-2.amazonses.com',
163
+ # 'recipients' => [
164
+ # 'success@simulator.amazonses.com'
165
+ # ],
166
+ # 'smtpResponse' => '250 2.6.0 Message received'
167
+ # },
168
+ next unless Sisimai::Address.is_emailaddress(e)
171
169
 
172
170
  v = dscontents[-1]
173
171
  if v['recipient']
@@ -180,7 +178,6 @@ module Sisimai::Lhost
180
178
  v['lhost'] = o['reportingMTA'] || ''
181
179
  v['diagnosis'] = o['smtpResponse'] || ''
182
180
  v['status'] = Sisimai::SMTP::Status.find(v['diagnosis']) || ''
183
- v['replycode'] = Sisimai::SMTP::Reply.find(v['diagnosis']) || ''
184
181
  v['reason'] = 'delivered'
185
182
  v['action'] = 'delivered'
186
183
 
@@ -188,8 +185,7 @@ module Sisimai::Lhost
188
185
  v['date'].sub!(/[.]\d+Z\z/, '')
189
186
  end
190
187
  else
191
- # The value of "notificationType" is not any of "Bounce", "Complaint",
192
- # or "Delivery".
188
+ # The value of "notificationType" is not any of "Bounce", "Complaint", or "Delivery".
193
189
  return nil
194
190
  end
195
191
  return nil unless recipients > 0
@@ -232,15 +228,15 @@ module Sisimai::Lhost
232
228
  fieldtable = Sisimai::RFC1894.FIELDTABLE
233
229
  permessage = {} # (Hash) Store values of each Per-Message field
234
230
 
235
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
236
- bodyslices = emailsteak[0].split("\n")
231
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
232
+ bodyslices = emailparts[0].split("\n")
237
233
  readslices = ['']
238
234
  readcursor = 0 # (Integer) Points the current cursor position
239
235
  v = nil
240
236
 
241
237
  while e = bodyslices.shift do
242
- # Read error messages and delivery status lines from the head of the email
243
- # to the previous line of the beginning of the original message.
238
+ # Read error messages and delivery status lines from the head of the email to the previous
239
+ # line of the beginning of the original message.
244
240
  readslices << e # Save the current line for the next loop
245
241
 
246
242
  if readcursor == 0
@@ -283,14 +279,14 @@ module Sisimai::Lhost
283
279
  next unless fieldtable[o[0]]
284
280
  v[fieldtable[o[0]]] = o[2]
285
281
 
286
- next unless f == 1
282
+ next unless f
287
283
  permessage[fieldtable[o[0]]] = o[2]
288
284
  end
289
285
  else
290
286
  # Continued line of the value of Diagnostic-Code field
291
287
  next unless readslices[-2].start_with?('Diagnostic-Code:')
292
- next unless cv = e.match(/\A[ \t]+(.+)\z/)
293
- v['diagnosis'] << ' ' << cv[1]
288
+ next unless e.start_with?(' ')
289
+ v['diagnosis'] << ' ' << e
294
290
  readslices[-1] = 'Diagnostic-Code: ' << e
295
291
  end
296
292
  end
@@ -313,16 +309,16 @@ module Sisimai::Lhost
313
309
  permessage.each_key { |a| e[a] ||= permessage[a] || '' }
314
310
 
315
311
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'].to_s.tr("\n", ' '))
316
- if e['status'].to_s.start_with?('5.0.0', '5.1.0', '4.0.0', '4.1.0')
312
+ if e['status'].to_s.end_with?('.0.0', '.1.0')
317
313
  # Get other D.S.N. value from the error message
314
+ # 5.1.0 - Unknown address error 550-'5.7.1 ...
318
315
  errormessage = e['diagnosis']
319
-
320
- if cv = e['diagnosis'].match(/["'](\d[.]\d[.]\d.+)['"]/)
321
- # 5.1.0 - Unknown address error 550-'5.7.1 ...
322
- errormessage = cv[1]
323
- end
316
+ p1 = e['diagnosis'].index("-'"); p1 = e['diagnosis'].index('-"') unless p1
317
+ p2 = e['diagnosis'].rindex("' "); p2 = e['diagnosis'].rindex('" ') unless p2
318
+ errormessage = e['diagnosis'][p1 + 2, p2 - p1 - 2] if p1 && p2
324
319
  e['status'] = Sisimai::SMTP::Status.find(errormessage) || e['status']
325
320
  end
321
+ e['replycode'] ||= Sisimai::SMTP::Reply.find(e['diagnosis'], e['status'])
326
322
 
327
323
  MessagesOf.each_key do |r|
328
324
  # Verify each regular expression of session errors
@@ -332,7 +328,7 @@ module Sisimai::Lhost
332
328
  end
333
329
  end
334
330
 
335
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
331
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
336
332
  end # END of a parser for email message
337
333
 
338
334
  end
@@ -1,14 +1,13 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::AmazonWorkMail parses a bounce email which created by
3
- # Amazon WorkMail. Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::AmazonWorkMail parses a bounce email which created by Amazon WorkMail. Methods
3
+ # in the module are called from only Sisimai::Message.
4
4
  module AmazonWorkMail
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/AmazonWorkMail.pm
7
6
  require 'sisimai/lhost'
8
7
 
9
8
  # https://aws.amazon.com/workmail/
10
9
  Indicators = Sisimai::Lhost.INDICATORS
11
- ReBackbone = %r|^content-type:[ ]message/rfc822|.freeze
10
+ Boundaries = ['Content-Type: message/rfc822'].freeze
12
11
  StartingOf = { message: ['Technical report:'] }.freeze
13
12
 
14
13
  # Parse bounce messages from Amazon WorkMail
@@ -16,7 +15,7 @@ module Sisimai::Lhost
16
15
  # @param [String] mbody Message body of a bounce email
17
16
  # @return [Hash] Bounce data list and message/rfc822 part
18
17
  # @return [Nil] it failed to parse or the arguments are missing
19
- def make(mhead, mbody)
18
+ def inquire(mhead, mbody)
20
19
  # X-Mailer: Amazon WorkMail
21
20
  # X-Original-Mailer: Amazon WorkMail
22
21
  # X-Ses-Outgoing: 2016.01.14-54.240.27.159
@@ -36,15 +35,15 @@ module Sisimai::Lhost
36
35
  permessage = {} # (Hash) Store values of each Per-Message field
37
36
 
38
37
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
39
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
40
- bodyslices = emailsteak[0].split("\n")
38
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
39
+ bodyslices = emailparts[0].split("\n")
41
40
  readcursor = 0 # (Integer) Points the current cursor position
42
41
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
43
42
  v = nil
44
43
 
45
44
  while e = bodyslices.shift do
46
- # Read error messages and delivery status lines from the head of the email
47
- # to the previous line of the beginning of the original message.
45
+ # Read error messages and delivery status lines from the head of the email to the previous
46
+ # line of the beginning of the original message.
48
47
 
49
48
  if readcursor == 0
50
49
  # Beginning of the bounce message or message/delivery-status part
@@ -84,7 +83,7 @@ module Sisimai::Lhost
84
83
  next unless fieldtable[o[0]]
85
84
  v[fieldtable[o[0]]] = o[2]
86
85
 
87
- next unless f == 1
86
+ next unless f
88
87
  permessage[fieldtable[o[0]]] = o[2]
89
88
  end
90
89
  end
@@ -103,7 +102,7 @@ module Sisimai::Lhost
103
102
  permessage.each_key { |a| e[a] ||= permessage[a] || '' }
104
103
 
105
104
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
106
- if e['status'].to_s.start_with?('5.0.0', '5.1.0', '4.0.0', '4.1.0')
105
+ if e['status'].to_s.end_with?('.0.0', '.1.0')
107
106
  # Get other D.S.N. value from the error message
108
107
  errormessage = e['diagnosis']
109
108
 
@@ -114,15 +113,13 @@ module Sisimai::Lhost
114
113
  e['status'] = Sisimai::SMTP::Status.find(errormessage) || e['status']
115
114
  end
116
115
 
117
- if cv = e['diagnosis'].match(/[<]([245]\d\d)[ ].+[>]/)
118
- # 554 4.4.7 Message expired: unable to deliver in 840 minutes.
119
- # <421 4.4.2 Connection timed out>
120
- e['replycode'] = cv[1]
121
- end
122
- e['reason'] ||= Sisimai::SMTP::Status.name(e['status']) || ''
116
+ # 554 4.4.7 Message expired: unable to deliver in 840 minutes.
117
+ # <421 4.4.2 Connection timed out>
118
+ e['replycode'] = Sisimai::SMTP::Reply.find(e['diagnosis']) || ''
119
+ e['reason'] ||= Sisimai::SMTP::Status.name(e['status']) || ''
123
120
  end
124
121
 
125
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
122
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
126
123
  end
127
124
  def description; return 'Amazon WorkMail: https://aws.amazon.com/workmail/'; end
128
125
  end
@@ -1,13 +1,12 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::Aol parses a bounce email which created by Aol Mail.
3
- # Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::Aol parses a bounce email which created by Aol Mail. Methods in the module are
3
+ # called from only Sisimai::Message.
4
4
  module Aol
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/Aol.pm
7
6
  require 'sisimai/lhost'
8
7
 
9
8
  Indicators = Sisimai::Lhost.INDICATORS
10
- ReBackbone = %r|^Content-Type:[ ]message/rfc822|.freeze
9
+ Boundaries = ['Content-Type: message/rfc822'].freeze
11
10
  StartingOf = { message: ['Content-Type: message/delivery-status'] }.freeze
12
11
  MessagesOf = {
13
12
  'hostunknown' => ['Host or domain name not found'],
@@ -19,7 +18,7 @@ module Sisimai::Lhost
19
18
  # @param [String] mbody Message body of a bounce email
20
19
  # @return [Hash] Bounce data list and message/rfc822 part
21
20
  # @return [Nil] it failed to parse or the arguments are missing
22
- def make(mhead, mbody)
21
+ def inquire(mhead, mbody)
23
22
  # X-AOL-IP: 192.0.2.135
24
23
  # X-AOL-VSS-INFO: 5600.1067/98281
25
24
  # X-AOL-VSS-CODE: clean
@@ -32,21 +31,20 @@ module Sisimai::Lhost
32
31
  # X-Outbound-Mail-Relay-Sender: rfc822; shironeko@aol.example.jp
33
32
  return nil unless mhead['x-aol-ip']
34
33
 
35
- require 'sisimai/rfc1894'
36
34
  fieldtable = Sisimai::RFC1894.FIELDTABLE
37
35
  permessage = {} # (Hash) Store values of each Per-Message field
38
36
 
39
37
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
40
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
41
- bodyslices = emailsteak[0].split("\n")
38
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
39
+ bodyslices = emailparts[0].split("\n")
42
40
  readslices = ['']
43
41
  readcursor = 0 # (Integer) Points the current cursor position
44
42
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
45
43
  v = nil
46
44
 
47
45
  while e = bodyslices.shift do
48
- # Read error messages and delivery status lines from the head of the email
49
- # to the previous line of the beginning of the original message.
46
+ # Read error messages and delivery status lines from the head of the email to the previous
47
+ # line of the beginning of the original message.
50
48
  readslices << e # Save the current line for the next loop
51
49
 
52
50
  if readcursor == 0
@@ -87,14 +85,14 @@ module Sisimai::Lhost
87
85
  next unless fieldtable[o[0]]
88
86
  v[fieldtable[o[0]]] = o[2]
89
87
 
90
- next unless f == 1
88
+ next unless f
91
89
  permessage[fieldtable[o[0]]] = o[2]
92
90
  end
93
91
  else
94
92
  # Continued line of the value of Diagnostic-Code field
95
93
  next unless readslices[-2].start_with?('Diagnostic-Code:')
96
- next unless cv = e.match(/\A[ \t]+(.+)\z/)
97
- v['diagnosis'] << ' ' << cv[1]
94
+ next unless e.start_with?(' ')
95
+ v['diagnosis'] << ' ' << Sisimai::String.sweep(e)
98
96
  readslices[-1] = 'Diagnostic-Code: ' << e
99
97
  end
100
98
  end
@@ -119,7 +117,7 @@ module Sisimai::Lhost
119
117
  end
120
118
  end
121
119
 
122
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
120
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
123
121
  end
124
122
  def description; return 'Aol Mail: https://www.aol.com'; end
125
123
  end
@@ -1,13 +1,12 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::ApacheJames parses a bounce email which created by ApacheJames.
3
- # Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::ApacheJames parses a bounce email which created by ApacheJames. Methods in the
3
+ # module are called from only Sisimai::Message.
4
4
  module ApacheJames
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/ApacheJames.pm
7
6
  require 'sisimai/lhost'
8
7
 
9
8
  Indicators = Sisimai::Lhost.INDICATORS
10
- ReBackbone = %r|^Content-Type:[ ]message/rfc822|.freeze
9
+ Boundaries = ['Content-Type: message/rfc822'].freeze
11
10
  StartingOf = {
12
11
  # apache-james-2.3.2/src/java/org/apache/james/transport/mailets/
13
12
  # AbstractNotify.java|124: out.println("Error message below:");
@@ -21,7 +20,7 @@ module Sisimai::Lhost
21
20
  # @param [String] mbody Message body of a bounce email
22
21
  # @return [Hash] Bounce data list and message/rfc822 part
23
22
  # @return [Nil] it failed to parse or the arguments are missing
24
- def make(mhead, mbody)
23
+ def inquire(mhead, mbody)
25
24
  match = 0
26
25
  match += 1 if mhead['subject'] == '[BOUNCE]'
27
26
  match += 1 if mhead['message-id'].to_s.include?('.JavaMail.')
@@ -29,18 +28,18 @@ module Sisimai::Lhost
29
28
  return nil unless match > 0
30
29
 
31
30
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
32
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
33
- bodyslices = emailsteak[0].split("\n")
31
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
32
+ bodyslices = emailparts[0].split("\n")
34
33
  readcursor = 0 # (Integer) Points the current cursor position
35
34
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
36
- diagnostic = '' # (String) Alternative diagnostic message
35
+ issuedcode = '' # (String) Alternative diagnostic message
37
36
  subjecttxt = nil # (String) Alternative Subject text
38
37
  gotmessage = nil # (Boolean) Flag for error message
39
38
  v = nil
40
39
 
41
40
  while e = bodyslices.shift do
42
- # Read error messages and delivery status lines from the head of the email
43
- # to the previous line of the beginning of the original message.
41
+ # Read error messages and delivery status lines from the head of the email to the previous
42
+ # line of the beginning of the original message.
44
43
 
45
44
  if readcursor == 0
46
45
  # Beginning of the bounce message or delivery status part
@@ -61,23 +60,23 @@ module Sisimai::Lhost
61
60
  # Number of lines: 64
62
61
  v = dscontents[-1]
63
62
 
64
- if cv = e.match(/\A[ ][ ]RCPT[ ]TO:[ ]([^ ]+[@][^ ]+)\z/)
63
+ if e.start_with?(' RCPT TO: ')
65
64
  # RCPT TO: kijitora@example.org
66
65
  if v['recipient']
67
66
  # There are multiple recipient addresses in the message body.
68
67
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
69
68
  v = dscontents[-1]
70
69
  end
71
- v['recipient'] = cv[1]
70
+ v['recipient'] = e[12, e.size]
72
71
  recipients += 1
73
72
 
74
- elsif cv = e.match(/\A[ ][ ]Sent[ ]date:[ ](.+)\z/)
73
+ elsif e.start_with?(' Sent date: ')
75
74
  # Sent date: Thu Apr 29 01:20:50 JST 2015
76
- v['date'] = cv[1]
75
+ v['date'] = e[13, e.size]
77
76
 
78
- elsif cv = e.match(/\A[ ][ ]Subject:[ ](.+)\z/)
77
+ elsif e.start_with?(' Subject: ')
79
78
  # Subject: Nyaaan
80
- subjecttxt = cv[1]
79
+ subjecttxt = e[11, e.size]
81
80
  else
82
81
  next if gotmessage
83
82
  if v['diagnosis']
@@ -106,12 +105,11 @@ module Sisimai::Lhost
106
105
  end
107
106
  return nil unless recipients > 0
108
107
 
109
- # Set the value of subjecttxt as a Subject if there is no original
110
- # message in the bounce mail.
111
- emailsteak[1] << ('Subject: ' << subjecttxt << "\n") unless emailsteak[1] =~ /^Subject: /
108
+ # Set the value of subjecttxt as a Subject if there is no original message in the bounce mail.
109
+ emailparts[1] << ('Subject: ' << subjecttxt << "\n") unless emailparts[1].index("\nSubject:")
112
110
 
113
- dscontents.each { |e| e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'] || diagnostic) }
114
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
111
+ dscontents.each { |e| e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'] || issuedcode) }
112
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
115
113
  end
116
114
  def description; return 'Java Apache Mail Enterprise Server'; end
117
115
  end