sisimai 4.25.17 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) 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 +406 -407
  7. data/Developers.mk +5 -6
  8. data/Gemfile +1 -1
  9. data/Makefile +12 -12
  10. data/README-JA.md +142 -94
  11. data/README.md +282 -150
  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 +20 -16
  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 -325
  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 -45
  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 +13 -18
  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 -21
  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/mac/reported-from-nick4tech-san-01.eml +0 -6
  170. /data/set-of-emails/maildir/bsd/{rfc3464-41.eml → rfc3834-05.eml} +0 -0
  171. /data/set-of-emails/maildir/bsd/{rhost-googleapps-01.eml → rhost-google-01.eml} +0 -0
  172. /data/set-of-emails/maildir/bsd/{rhost-googleapps-02.eml → rhost-google-02.eml} +0 -0
  173. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-01.eml → rhost-microsoft-01.eml} +0 -0
  174. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-02.eml → rhost-microsoft-02.eml} +0 -0
  175. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-03.eml → rhost-microsoft-03.eml} +0 -0
  176. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-01.eml → rhost-tencent-01.eml} +0 -0
  177. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-02.eml → rhost-tencent-02.eml} +0 -0
  178. /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