sisimai 4.25.16-java → 5.0.2-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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,13 +1,12 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::GMX parses a bounce email which created by GMX.
3
- # Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::GMX parses a bounce email which created by GMX. Methods in the module are called
3
+ # from only Sisimai::Message.
4
4
  module GMX
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/GMX.pm
7
6
  require 'sisimai/lhost'
8
7
 
9
8
  Indicators = Sisimai::Lhost.INDICATORS
10
- ReBackbone = %r|^---[ ]The[ ]header[ ]of[ ]the[ ]original[ ]message[ ]is[ ]following[.][ ]---|.freeze
9
+ Boundaries = ['--- The header of the original message is following. ---'].freeze
11
10
  StartingOf = { message: ['This message was created automatically by mail delivery software'] }.freeze
12
11
  MessagesOf = { 'expired' => ['delivery retry timeout exceeded'] }.freeze
13
12
 
@@ -16,23 +15,24 @@ 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
  # Envelope-To: <kijitora@mail.example.com>
21
20
  # X-GMX-Antispam: 0 (Mail was not recognized as spam); Detail=V3;
22
21
  # X-GMX-Antivirus: 0 (no virus found)
23
22
  # X-UI-Out-Filterresults: unknown:0;
24
23
  return nil unless mhead['x-gmx-antispam']
25
24
 
25
+ require 'sisimai/smtp/command'
26
26
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
27
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
28
- bodyslices = emailsteak[0].split("\n")
27
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
28
+ bodyslices = emailparts[0].split("\n")
29
29
  readcursor = 0 # (Integer) Points the current cursor position
30
30
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
31
31
  v = nil
32
32
 
33
33
  while e = bodyslices.shift do
34
- # Read error messages and delivery status lines from the head of the email
35
- # to the previous line of the beginning of the original message.
34
+ # Read error messages and delivery status lines from the head of the email to the previous
35
+ # line of the beginning of the original message.
36
36
  if readcursor == 0
37
37
  # Beginning of the bounce message or delivery status part
38
38
  readcursor |= Indicators[:deliverystatus] if e.start_with?(StartingOf[:message][0])
@@ -53,7 +53,7 @@ module Sisimai::Lhost
53
53
  # 5.1.1 <shironeko@example.jp>... User Unknown
54
54
  v = dscontents[-1]
55
55
 
56
- if cv = e.match(/\A["]([^ ]+[@][^ ]+)["]:\z/) || e.match(/\A[<]([^ ]+[@][^ ]+)[>]\z/)
56
+ if e.include?('@') && ( e.start_with?('"') || e.start_with?('<') )
57
57
  # "shironeko@example.jp":
58
58
  # ---- OR ----
59
59
  # <kijitora@6jo.example.co.jp>
@@ -65,19 +65,20 @@ module Sisimai::Lhost
65
65
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
66
66
  v = dscontents[-1]
67
67
  end
68
- v['recipient'] = cv[1]
68
+ v['recipient'] = Sisimai::Address.s3s4(e)
69
69
  recipients += 1
70
70
 
71
- elsif cv = e.match(/\ASMTP error .+ ([A-Z]{4}) command:\z/)
71
+ elsif e.start_with?('SMTP error ')
72
72
  # SMTP error from remote server after RCPT command:
73
- v['command'] = cv[1]
73
+ v['command'] = Sisimai::SMTP::Command.find(e)
74
74
 
75
- elsif cv = e.match(/\Ahost:[ \t]*(.+)\z/)
75
+ elsif e.start_with?('host:')
76
76
  # host: mx.example.jp
77
- v['rhost'] = cv[1]
77
+ v['rhost'] = e[6, e.size]
78
78
  else
79
79
  # Get error message
80
- if e =~ /\b[45][.]\d[.]\d\b/ || e =~ /[<][^ ]+[@][^ ]+[>]/ || e =~ /\b[45]\d{2}\b/
80
+ if Sisimai::SMTP::Status.find(e) || Sisimai::String.aligned(e, ['<', '@', '>'])
81
+ # 5.1.1 <shironeko@example.jp>... User Unknown
81
82
  v['diagnosis'] ||= e
82
83
  else
83
84
  next if e.empty?
@@ -105,7 +106,7 @@ module Sisimai::Lhost
105
106
  end
106
107
  end
107
108
 
108
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
109
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
109
110
  end
110
111
  def description; return 'GMX: https://www.gmx.net'; end
111
112
  end
@@ -1,13 +1,12 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::GoogleGroups parses a bounce email which created by Google
3
- # Groups. Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::GoogleGroups parses a bounce email which created by Google Groups. Methods in the
3
+ # module are called from only Sisimai::Message.
4
4
  module GoogleGroups
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/GoogleGroups.pm
7
6
  require 'sisimai/lhost'
8
7
 
9
8
  Indicators = Sisimai::Lhost.INDICATORS
10
- ReBackbone = %r/^-----[ ]Original[ ]message[ ]-----$/.freeze
9
+ Boundaries = ['----- Original message -----'].freeze
11
10
 
12
11
  # Parse bounce messages from Google Groups
13
12
  # @param [Hash] mhead Message headers of a bounce email
@@ -15,7 +14,7 @@ module Sisimai::Lhost
15
14
  # @return [Hash] Bounce data list and message/rfc822 part
16
15
  # @return [Nil] it failed to parse or the arguments are missing
17
16
  # @since v4.25.6
18
- def make(mhead, mbody)
17
+ def inquire(mhead, mbody)
19
18
  return nil unless mhead['from'].end_with?('<mailer-daemon@googlemail.com>')
20
19
  return nil unless mhead['subject'].start_with?('Delivery Status Notification')
21
20
  return nil unless mhead['x-failed-recipients']
@@ -39,7 +38,7 @@ module Sisimai::Lhost
39
38
  #
40
39
  # Google Groups
41
40
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
42
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
41
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
43
42
  recordwide = { 'rhost' => '', 'reason' => '', 'diagnosis' => '' }
44
43
  recipients = 0
45
44
  v = dscontents[-1]
@@ -48,14 +47,15 @@ module Sisimai::Lhost
48
47
  # * The owner of the group may have removed this group.
49
48
  # * You may need to join the group before receiving permission to post.
50
49
  # * This group may not be open to posting.
51
- entiremesg = emailsteak[0].split(/\n\n/, 5).slice(0, 4).join(' ').tr("\n", ' ');
50
+ entiremesg = emailparts[0].split(/\n\n/, 5).slice(0, 4).join(' ').tr("\n", ' ');
51
+ receivedby = mhead['received'] || []
52
52
  recordwide['diagnosis'] = Sisimai::String.sweep(entiremesg)
53
- recordwide['reason'] = emailsteak[0].scan(/^[ ]?[*][ ]?/).size == 4 ? 'rejected' : 'onhold'
54
- recordwide['rhost'] = Sisimai::RFC5322.received(mhead['received'][0]).shift
53
+ recordwide['reason'] = emailparts[0].scan(/^[ ]?[*][ ]?/).size == 4 ? 'rejected' : 'onhold'
54
+ recordwide['rhost'] = Sisimai::RFC5322.received(receivedby[0])[1]
55
55
 
56
56
  mhead['x-failed-recipients'].split(',').each do |e|
57
57
  # X-Failed-Recipients: neko@example.jp, nyaan@example.org, ...
58
- next unless Sisimai::RFC5322.is_emailaddress(e)
58
+ next unless Sisimai::Address.is_emailaddress(e)
59
59
 
60
60
  if v['recipient']
61
61
  # There are multiple recipient addresses in the message body.
@@ -67,7 +67,7 @@ module Sisimai::Lhost
67
67
  recordwide.each_key { |r| v[r] = recordwide[r] }
68
68
  end
69
69
  return nil unless recipients > 0
70
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
70
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
71
71
  end
72
72
  def description; return 'Google Groups: https://groups.google.com'; end
73
73
  end
@@ -1,17 +1,15 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::GSuite parses a bounce email which created by G Suite.
3
- # Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::GSuite parses a bounce email which created by G Suite. Methods in the module are
3
+ # called from only Sisimai::Message.
4
4
  module GSuite
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/GSuite.pm
7
6
  require 'sisimai/lhost'
8
7
 
9
8
  Indicators = Sisimai::Lhost.INDICATORS
10
- ReBackbone = %r<^Content-Type:[ ](?:message/rfc822|text/rfc822-headers)>.freeze
9
+ Boundaries = ['Content-Type: message/rfc822', 'Content-Type: text/rfc822-headers'].freeze
11
10
  MarkingsOf = {
12
- message: %r/\A[*][*][ ].+[ ][*][*]\z/,
13
- error: %r/\AThe[ ]response([ ]from[ ]the[ ]remote[ ]server)?[ ]was:\z/,
14
- html: %r{\AContent-Type:[ ]*text/html;[ ]*charset=['"]?(?:UTF|utf)[-]8['"]?\z},
11
+ message: ['** '],
12
+ error: ['The response was:', 'The response from the remote server was:'],
15
13
  }.freeze
16
14
  MessagesOf = {
17
15
  'userunknown' => ["because the address couldn't be found. Check for typos or unnecessary spaces and try again."],
@@ -24,18 +22,17 @@ module Sisimai::Lhost
24
22
  # @param [String] mbody Message body of a bounce email
25
23
  # @return [Hash] Bounce data list and message/rfc822 part
26
24
  # @return [Nil] it failed to parse or the arguments are missing
27
- def make(mhead, mbody)
25
+ def inquire(mhead, mbody)
28
26
  return nil unless mhead['from'].end_with?('<mailer-daemon@googlemail.com>')
29
27
  return nil unless mhead['subject'].start_with?('Delivery Status Notification')
30
28
  return nil unless mhead['x-gm-message-state']
31
29
 
32
- require 'sisimai/rfc1894'
33
30
  fieldtable = Sisimai::RFC1894.FIELDTABLE
34
31
  permessage = {} # (Hash) Store values of each Per-Message field
35
32
 
36
33
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
37
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
38
- bodyslices = emailsteak[0].split("\n")
34
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
35
+ bodyslices = emailparts[0].split("\n")
39
36
  readcursor = 0 # (Integer) Points the current cursor position
40
37
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
41
38
  endoferror = false # (Integer) Flag for a blank line after error messages
@@ -44,11 +41,11 @@ module Sisimai::Lhost
44
41
  v = nil
45
42
 
46
43
  while e = bodyslices.shift do
47
- # Read error messages and delivery status lines from the head of the email
48
- # to the previous line of the beginning of the original message.
44
+ # Read error messages and delivery status lines from the head of the email to the previous
45
+ # line of the beginning of the original message.
49
46
  if readcursor == 0
50
47
  # Beginning of the bounce message or message/delivery-status part
51
- readcursor |= Indicators[:deliverystatus] if e =~ MarkingsOf[:message]
48
+ readcursor |= Indicators[:deliverystatus] if MarkingsOf[:message].any? { |a| e.start_with?(a) }
52
49
  end
53
50
  next if (readcursor & Indicators[:deliverystatus]) == 0
54
51
 
@@ -87,12 +84,12 @@ module Sisimai::Lhost
87
84
  v['lhost'] = '' if v['lhost'].include?('@')
88
85
  end
89
86
 
90
- next unless f == 1
87
+ next unless f
91
88
  permessage[fieldtable[o[0]]] = o[2]
92
89
  end
93
90
  else
94
- # The line does not begin with a DSN field defined in RFC3464
95
- # Append error messages continued from the previous line
91
+ # The line does not begin with a DSN field defined in RFC3464 Append error messages continued
92
+ # from the previous line
96
93
  if endoferror == false && v && ! v['diagnosis'].to_s.empty?
97
94
  endoferror ||= true if e.empty?
98
95
 
@@ -100,10 +97,10 @@ module Sisimai::Lhost
100
97
  next unless e.start_with?(' ')
101
98
  v['diagnosis'] << e
102
99
 
103
- elsif e =~ MarkingsOf[:error]
100
+ elsif MarkingsOf[:error].any? { |a| e.start_with?(a) }
104
101
  # Detect SMTP session error or connection error
105
102
  # The response from the remote server was:
106
- anotherset['diagnosis'] << e
103
+ anotherset['diagnosis'] << ' ' << e
107
104
  else
108
105
  # ** Address not found **
109
106
  #
@@ -112,12 +109,9 @@ module Sisimai::Lhost
112
109
  #
113
110
  # The response from the remote server was:
114
111
  # 550 #5.1.0 Address rejected.
115
- next if e =~ MarkingsOf[:html]
116
-
112
+ next if e.start_with?('Content-Type:')
117
113
  if anotherset['diagnosis']
118
- # Continued error messages from the previous line like
119
- # "550 #5.1.0 Address rejected."
120
- next if e =~ /\AContent-Type:/
114
+ # Continued error messages from the previous line like "550 #5.1.0 Address rejected."
121
115
  next if emptylines > 5
122
116
  if e.empty?
123
117
  # Count and next()
@@ -131,7 +125,7 @@ module Sisimai::Lhost
131
125
  # Your message wasn't delivered to * because the address couldn't be found.
132
126
  # Check for typos or unnecessary spaces and try again.
133
127
  next if e.empty?
134
- next unless e =~ MarkingsOf[:message]
128
+ next unless MarkingsOf[:message].any? { |a| e.start_with?(a) }
135
129
  anotherset['diagnosis'] = e
136
130
  end
137
131
  end
@@ -141,14 +135,13 @@ module Sisimai::Lhost
141
135
 
142
136
  dscontents.each do |e|
143
137
  # Set default values if each value is empty.
144
- e['lhost'] ||= permessage['rhost']
145
138
  permessage.each_key { |a| e[a] ||= permessage[a] || '' }
146
139
 
147
140
  if anotherset['diagnosis']
148
141
  # Copy alternative error message
149
142
  e['diagnosis'] = anotherset['diagnosis'] unless e['diagnosis']
150
143
 
151
- if e['diagnosis'] =~ /\A\d+\z/
144
+ if e['diagnosis'].include?(' ') == false && e['diagnosis'].to_i > 0
152
145
  e['diagnosis'] = anotherset['diagnosis']
153
146
  else
154
147
  # More detailed error message is in "anotherset"
@@ -192,7 +185,7 @@ module Sisimai::Lhost
192
185
  end
193
186
  end
194
187
 
195
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
188
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
196
189
  end
197
190
  def description; return 'G Suite: https://gsuite.google.com'; end
198
191
  end
@@ -1,29 +1,19 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::IMailServer parses a bounce email which created by
3
- # Ipswitch IMail Server.
2
+ # Sisimai::Lhost::IMailServer parses a bounce email which created by Ipswitch IMail Server.
4
3
  # Methods in the module are called from only Sisimai::Message.
5
4
  module IMailServer
6
5
  class << self
7
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/IMailServer.pm
8
6
  require 'sisimai/lhost'
9
7
 
10
- ReBackbone = %r|^Original[ ]message[ ]follows[.]|.freeze
8
+ Boundaries = ['Original message follows.'].freeze
11
9
  StartingOf = { error: ['Body of message generated response:'] }.freeze
12
-
13
- ReSMTP = {
14
- 'conn' => %r/(?:SMTP connection failed,|Unexpected connection response from server:)/,
15
- 'ehlo' => %r|Unexpected response to EHLO/HELO:|,
16
- 'mail' => %r|Server response to MAIL FROM:|,
17
- 'rcpt' => %r|Additional RCPT TO generated following response:|,
18
- 'data' => %r|DATA command generated response:|,
19
- }.freeze
20
10
  ReFailures = {
21
- 'hostunknown' => %r/Unknown host/,
22
- 'userunknown' => %r/\A(?:Unknown user|Invalid final delivery userid)/,
23
- 'mailboxfull' => %r/\AUser mailbox exceeds allowed size/,
24
- 'virusdetected' => %r/\ARequested action not taken: virus detected/,
25
- 'undefined' => %r/\Aundeliverable to /,
26
- 'expired' => %r/\ADelivery failed \d+ attempts/,
11
+ 'hostunknown' => ['Unknown host'],
12
+ 'userunknown' => ['Unknown user', 'Invalid final delivery userid'],
13
+ 'mailboxfull' => ['User mailbox exceeds allowed size'],
14
+ 'virusdetected' => ['Requested action not taken: virus detected'],
15
+ 'undefined' => ['undeliverable to'],
16
+ 'expired' => ['Delivery failed '],
27
17
  }.freeze
28
18
 
29
19
  # Parse bounce messages from IMailServer
@@ -31,47 +21,48 @@ module Sisimai::Lhost
31
21
  # @param [String] mbody Message body of a bounce email
32
22
  # @return [Hash] Bounce data list and message/rfc822 part
33
23
  # @return [Nil] it failed to parse or the arguments are missing
34
- def make(mhead, mbody)
24
+ def inquire(mhead, mbody)
35
25
  # X-Mailer: <SMTP32 v8.22>
36
26
  match = 0
37
- match += 1 if mhead['subject'] =~ /\AUndeliverable Mail[ ]*\z/
27
+ match += 1 if mhead['subject'].start_with?('Undeliverable Mail ')
38
28
  match += 1 if mhead['x-mailer'].to_s.start_with?('<SMTP32 v')
39
29
  return nil unless match > 0
40
30
 
41
31
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
42
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
43
- bodyslices = emailsteak[0].split("\n")
32
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
33
+ bodyslices = emailparts[0].split("\n")
44
34
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
45
35
  v = nil
46
36
 
47
37
  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.
38
+ # Read error messages and delivery status lines from the head of the email to the previous
39
+ # line of the beginning of the original message.
50
40
 
51
41
  # Unknown user: kijitora@example.com
52
42
  #
53
43
  # Original message follows.
54
44
  v = dscontents[-1]
55
45
 
56
- if cv = e.match(/\A([^ ]+)[ ](.+)[:][ \t]*([^ ]+[@][^ ]+)/)
46
+ p0 = e.index(': ') || -1
47
+ if p0 > 8 && Sisimai::String.aligned(e, [': ', '@'])
57
48
  # Unknown user: kijitora@example.com
58
49
  if v['recipient']
59
50
  # There are multiple recipient addresses in the message body.
60
51
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
61
52
  v = dscontents[-1]
62
53
  end
63
- v['diagnosis'] = cv[1] + ' ' + cv[2]
64
- v['recipient'] = cv[3]
54
+ v['diagnosis'] = e
55
+ v['recipient'] = Sisimai::Address.s3s4(e[p0 + 2, e.size])
65
56
  recipients += 1
66
57
 
67
- elsif cv = e.match(/\Aundeliverable[ ]+to[ ]+(.+)\z/)
58
+ elsif e.start_with?('undeliverable ')
68
59
  # undeliverable to kijitora@example.com
69
60
  if v['recipient']
70
61
  # There are multiple recipient addresses in the message body.
71
62
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
72
63
  v = dscontents[-1]
73
64
  end
74
- v['recipient'] = Sisimai::Address.s3s4(cv[1])
65
+ v['recipient'] = Sisimai::Address.s3s4(e)
75
66
  recipients += 1
76
67
  else
77
68
  # Other error message text
@@ -81,6 +72,7 @@ module Sisimai::Lhost
81
72
  end
82
73
  return nil unless recipients > 0
83
74
 
75
+ require 'sisimai/smtp/command'
84
76
  dscontents.each do |e|
85
77
  unless e['alterrors'].to_s.empty?
86
78
  # Copy alternative error message
@@ -92,24 +84,18 @@ module Sisimai::Lhost
92
84
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
93
85
  e.delete('alterrors')
94
86
  end
95
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
96
-
97
- ReSMTP.each_key do |r|
98
- # Detect SMTP command from the message
99
- next unless e['diagnosis'] =~ ReSMTP[r]
100
- e['command'] = r.upcase
101
- break
102
- end
87
+ e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''
88
+ e['command'] = Sisimai::SMTP::Command.find(e['diagnosis'])
103
89
 
104
90
  ReFailures.each_key do |r|
105
91
  # Verify each regular expression of session errors
106
- next unless e['diagnosis'] =~ ReFailures[r]
92
+ next unless ReFailures[r].any? { |a| e['diagnosis'].include?(a) }
107
93
  e['reason'] = r
108
94
  break
109
95
  end
110
96
  end
111
97
 
112
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
98
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
113
99
  end
114
100
  def description; return 'IPSWITCH IMail Server'; end
115
101
  end
@@ -1,19 +1,17 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::InterScanMSS parses a bounce email which created by
3
- # Trend Micro InterScan Messaging Security Suite. Methods in the module are
4
- # called from only Sisimai::Message.
2
+ # Sisimai::Lhost::InterScanMSS parses a bounce email which created by Trend Micro InterScan Messaging
3
+ # Security Suite. Methods in the module are called from only Sisimai::Message.
5
4
  module InterScanMSS
6
5
  class << self
7
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/InterScanMSS.pm
8
6
  require 'sisimai/lhost'
9
- ReBackbone = %r|^Content-type:[ ]message/rfc822|.freeze
7
+ Boundaries = ['Content-Type: message/rfc822'].freeze
10
8
 
11
9
  # Parse bounce messages from InterScanMSS
12
10
  # @param [Hash] mhead Message headers of a bounce email
13
11
  # @param [String] mbody Message body of a bounce email
14
12
  # @return [Hash] Bounce data list and message/rfc822 part
15
13
  # @return [Nil] it failed to parse or the arguments are missing
16
- def make(mhead, mbody)
14
+ def inquire(mhead, mbody)
17
15
  # :received => %r/[ ][(]InterScanMSS[)][ ]with[ ]/,
18
16
  match = 0
19
17
  tryto = [
@@ -26,60 +24,59 @@ module Sisimai::Lhost
26
24
  match += 1 if tryto.any? { |a| mhead['subject'] == a }
27
25
  return nil unless match > 0
28
26
 
27
+ require 'sisimai/smtp/command'
29
28
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
30
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
31
- bodyslices = emailsteak[0].split("\n")
29
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
30
+ bodyslices = emailparts[0].split("\n")
32
31
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
33
32
  v = nil
34
33
 
35
34
  while e = bodyslices.shift do
36
- # Read error messages and delivery status lines from the head of the email
37
- # to the previous line of the beginning of the original message.
35
+ # Read error messages and delivery status lines from the head of the email to the previous
36
+ # line of the beginning of the original message.
38
37
  next if e.empty?
39
38
 
40
- v = dscontents[-1]
41
- if cv = e.match(/\A.+[<>]{3}[ \t]+.+[<]([^ ]+[@][^ ]+)[>]\z/) ||
42
- e.match(/\A.+[<>]{3}[ \t]+.+[<]([^ ]+[@][^ ]+)[>]/) ||
43
- e.match(/\A(?:Reason:[ ]+)?Unable[ ]to[ ]deliver[ ]message[ ]to[ ][<](.+)[>]/)
39
+ v = dscontents[-1]
40
+ p1 = e.index(' <<< ') || -1 # Sent <<< ...
41
+ p2 = e.index(' >>> ') || -1 # Received >>> ...
42
+ if e.include?('@') && e.include?(' <') && ( p1 > 1 || p2 > 1 || e.include?('Unable to deliver ') )
44
43
  # Sent <<< RCPT TO:<kijitora@example.co.jp>
45
44
  # Received >>> 550 5.1.1 <kijitora@example.co.jp>... user unknown
46
45
  # Unable to deliver message to <kijitora@neko.example.jp>
47
- if v['recipient'] && cv[1] != v['recipient']
46
+ cr = e[e.rindex('<') + 1, e.rindex('>') - e.rindex('<') - 1]
47
+
48
+ if v['recipient'] && cr != v['recipient']
48
49
  # There are multiple recipient addresses in the message body.
49
50
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
50
51
  v = dscontents[-1]
51
52
  end
52
- v['recipient'] = cv[1]
53
- v['diagnosis'] = e if e =~ /Unable[ ]to[ ]deliver[ ]/
53
+ v['recipient'] = cr
54
+ v['diagnosis'] = e if e.include?('Unable to deliver ')
54
55
  recipients = dscontents.size
55
56
  end
56
57
 
57
- if cv = e.match(/\ASent[ ]+[<]{3}[ ]+([A-Z]{4})[ ]/)
58
+ if e.start_with?('Sent <<< ')
58
59
  # Sent <<< RCPT TO:<kijitora@example.co.jp>
59
- v['command'] = cv[1]
60
+ v['command'] = Sisimai::SMTP::Command.find(e)
60
61
 
61
- elsif cv = e.match(/\AReceived[ ]+[>]{3}[ ]+(\d{3}[ ]+.+)\z/)
62
+ elsif e.start_with?('Received >>> ')
62
63
  # Received >>> 550 5.1.1 <kijitora@example.co.jp>... user unknown
63
- v['diagnosis'] = cv[1]
64
+ v['diagnosis'] = e[e.index(' >>> ') + 4, e.size]
64
65
  else
65
66
  # Error message in non-English
66
- if cv = e.match(/[ ][>]{3}[ ]([A-Z]{4})/)
67
- # >>> RCPT TO ...
68
- v['command'] = cv[1]
69
-
70
- elsif cv = e.match(/[ ][<]{3}[ ](.+)/)
71
- # <<< 550 5.1.1 User unknown
72
- v['diagnosis'] = cv[1]
73
- end
67
+ v['command'] = Sisimai::SMTP::Command.find(e) if e.include?(' >>> ')
68
+ p3 = e.index(' <<< ')
69
+ next unless p3
70
+ v['diagnosis'] = e[p3 + 4, e.size]
74
71
  end
75
72
  end
76
73
  return nil unless recipients > 0
77
74
 
78
75
  dscontents.each do |e|
79
76
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
80
- e['reason'] = 'userunknown' if e['diagnosis'] =~ /Unable[ ]to[ ]deliver/
77
+ e['reason'] = 'userunknown' if e['diagnosis'].include?('Unable to deliver')
81
78
  end
82
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
79
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
83
80
  end
84
81
  def description; return 'Trend Micro InterScan Messaging Security Suite'; end
85
82
  end