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,20 +1,13 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::KDDI parses a bounce email which created by au by KDDI.
3
- # Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::KDDI parses a bounce email which created by au by KDDI. Methods in the module are
3
+ # called from only Sisimai::Message.
4
4
  module KDDI
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/KDDI.pm
7
6
  require 'sisimai/lhost'
8
7
 
9
8
  Indicators = Sisimai::Lhost.INDICATORS
10
- ReBackbone = %r|^Content-Type:[ ]message/rfc822|.freeze
11
- MarkingsOf = {
12
- message: %r/\AYour[ ]mail[ ](?:
13
- sent[ ]on:?[ ][A-Z][a-z]{2}[,]
14
- |attempted[ ]to[ ]be[ ]delivered[ ]on:?[ ][A-Z][a-z]{2}[,]
15
- )
16
- /x,
17
- }.freeze
9
+ Boundaries = ['Content-Type: message/rfc822'].freeze
10
+ MarkingsOf = { message: ['Your mail sent on:', 'Your mail attempted to be delivered on:'] }.freeze
18
11
  MessagesOf = {
19
12
  'mailboxfull' => ['As their mailbox is full'],
20
13
  'norelaying' => ['Due to the following SMTP relay error'],
@@ -26,35 +19,35 @@ module Sisimai::Lhost
26
19
  # @param [String] mbody Message body of a bounce email
27
20
  # @return [Hash] Bounce data list and message/rfc822 part
28
21
  # @return [Nil] it failed to parse or the arguments are missing
29
- def make(mhead, mbody)
22
+ def inquire(mhead, mbody)
30
23
  # :'message-id' => %r/[@].+[.]ezweb[.]ne[.]jp[>]\z/,
31
24
  match = 0
32
- match += 1 if mhead['from'] =~ /no-reply[@].+[.]dion[.]ne[.]jp/
25
+ match += 1 if Sisimai::String.aligned(mhead['from'], ['no-reply@.', '.dion.ne.jp'])
33
26
  match += 1 if mhead['reply-to'].to_s == 'no-reply@app.auone-net.jp'
34
27
  match += 1 if mhead['received'].any? { |a| a.include?('ezweb.ne.jp (') }
35
28
  match += 1 if mhead['received'].any? { |a| a.include?('.au.com (') }
36
29
  return nil unless match > 0
37
30
 
38
31
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
39
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
40
- bodyslices = emailsteak[0].split("\n")
32
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
33
+ bodyslices = emailparts[0].split("\n")
41
34
  readcursor = 0 # (Integer) Points the current cursor position
42
35
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
43
36
  v = nil
44
37
 
45
38
  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.
39
+ # Read error messages and delivery status lines from the head of the email to the previous
40
+ # line of the beginning of the original message.
48
41
  if readcursor == 0
49
42
  # Beginning of the bounce message or delivery status part
50
- readcursor |= Indicators[:deliverystatus] if e =~ MarkingsOf[:message]
43
+ readcursor |= Indicators[:deliverystatus] if MarkingsOf[:message].any? { |a| e.start_with?(a) }
51
44
  next
52
45
  end
53
46
  next if (readcursor & Indicators[:deliverystatus]) == 0
54
47
  next if e.empty?
55
48
 
56
49
  v = dscontents[-1]
57
- if cv = e.match(/\A[ \t]+Could not be delivered to: [<]([^ ]+[@][^ ]+)[>]/)
50
+ if e.include?(' Could not be delivered to: <')
58
51
  # Your mail sent on: Thu, 29 Apr 2010 11:04:47 +0900
59
52
  # Could not be delivered to: <******@**.***.**>
60
53
  # As their mailbox is full.
@@ -63,24 +56,26 @@ module Sisimai::Lhost
63
56
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
64
57
  v = dscontents[-1]
65
58
  end
66
- r = Sisimai::Address.s3s4(cv[1])
67
- next unless Sisimai::RFC5322.is_emailaddress(r)
59
+ r = Sisimai::Address.s3s4(e[e.index('<') + 1, e.size])
60
+ next unless Sisimai::Address.is_emailaddress(r)
68
61
  v['recipient'] = r
69
62
  recipients += 1
70
63
 
71
- elsif cv = e.match(/Your mail sent on: (.+)\z/)
64
+ elsif e.include?('Your mail sent on: ')
72
65
  # Your mail sent on: Thu, 29 Apr 2010 11:04:47 +0900
73
- v['date'] = cv[1]
66
+ v['date'] = e[19, e.size]
74
67
  else
75
68
  # As their mailbox is full.
76
69
  v['diagnosis'] ||= ''
77
- v['diagnosis'] << e + ' ' if e.start_with?(' ', "\t")
70
+ v['diagnosis'] << e + ' ' if e.start_with?(' ')
78
71
  end
79
72
  end
80
73
  return nil unless recipients > 0
81
74
 
75
+ require 'sisimai/smtp/command'
82
76
  dscontents.each do |e|
83
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
77
+ e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''
78
+ e['command'] = Sisimai::SMTP::Command.find(e['diagnosis'])
84
79
 
85
80
  if mhead['x-spasign'].to_s == 'NG'
86
81
  # Content-Type: text/plain; ..., X-SPASIGN: NG (spamghetti, au by KDDI)
@@ -88,8 +83,7 @@ module Sisimai::Lhost
88
83
  e['reason'] = 'filtered'
89
84
  else
90
85
  if e['command'] == 'RCPT'
91
- # set "userunknown" when the remote server rejected after RCPT
92
- # command.
86
+ # set "userunknown" when the remote server rejected after RCPT command.
93
87
  e['reason'] = 'userunknown'
94
88
  else
95
89
  # SMTP command is not RCPT
@@ -104,7 +98,7 @@ module Sisimai::Lhost
104
98
 
105
99
  end
106
100
 
107
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
101
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
108
102
  end
109
103
  def description; return 'au by KDDI: https://www.au.kddi.com'; end
110
104
  end
@@ -1,13 +1,12 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::MailFoundry parses a bounce email which created by
3
- # MailFoundry. Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::MailFoundry parses a bounce email which created by MailFoundry. Methods in the
3
+ # module are called from only Sisimai::Message.
4
4
  module MailFoundry
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/MailFoundry.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
  message: ['Unable to deliver message to:'],
13
12
  error: ['Delivery failed for the following reason:'],
@@ -18,20 +17,20 @@ module Sisimai::Lhost
18
17
  # @param [String] mbody Message body of a bounce email
19
18
  # @return [Hash] Bounce data list and message/rfc822 part
20
19
  # @return [Nil] it failed to parse or the arguments are missing
21
- def make(mhead, mbody)
20
+ def inquire(mhead, mbody)
22
21
  return nil unless mhead['subject'] == 'Message delivery has failed'
23
22
  return nil unless mhead['received'].any? { |a| a.include?('(MAILFOUNDRY) id ') }
24
23
 
25
24
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
26
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
27
- bodyslices = emailsteak[0].split("\n")
25
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
26
+ bodyslices = emailparts[0].split("\n")
28
27
  readcursor = 0 # (Integer) Points the current cursor position
29
28
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
30
29
  v = nil
31
30
 
32
31
  while e = bodyslices.shift do
33
- # Read error messages and delivery status lines from the head of the email
34
- # to the previous line of the beginning of the original message.
32
+ # Read error messages and delivery status lines from the head of the email to the previous
33
+ # line of the beginning of the original message.
35
34
  if readcursor == 0
36
35
  # Beginning of the bounce message or delivery status part
37
36
  readcursor |= Indicators[:deliverystatus] if e.start_with?(StartingOf[:message][0])
@@ -46,14 +45,14 @@ module Sisimai::Lhost
46
45
  # This has been a permanent failure. No further delivery attempts will be made.
47
46
  v = dscontents[-1]
48
47
 
49
- if cv = e.match(/\AUnable to deliver message to: [<]([^ ]+[@][^ ]+)[>]\z/)
48
+ if e.start_with?('Unable to deliver message to: <') && e.index('@') > 1
50
49
  # Unable to deliver message to: <kijitora@example.org>
51
50
  if v['recipient']
52
51
  # There are multiple recipient addresses in the message body.
53
52
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
54
53
  v = dscontents[-1]
55
54
  end
56
- v['recipient'] = cv[1]
55
+ v['recipient'] = e[e.index('<'), e.size]
57
56
  recipients += 1
58
57
  else
59
58
  # Error message
@@ -74,7 +73,7 @@ module Sisimai::Lhost
74
73
  return nil unless recipients > 0
75
74
 
76
75
  dscontents.each { |e| e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) }
77
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
76
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
78
77
  end
79
78
  def description; return 'MailFoundry'; end
80
79
  end
@@ -1,14 +1,12 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::MailMarshalSMTP parses a bounce email which created
3
- # by Trustwave Secure Email Gateway: formerly MailMarshal SMTP. Methods in
4
- # the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::MailMarshalSMTP parses a bounce email which created by Trustwave Secure Email
3
+ # Gateway: formerly MailMarshal SMTP. Methods in the module are called from only Sisimai::Message.
5
4
  module MailMarshalSMTP
6
5
  class << self
7
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/MailMarshalSMTP.pm
8
6
  require 'sisimai/lhost'
9
7
 
10
8
  Indicators = Sisimai::Lhost.INDICATORS
11
- ReBackbone = %r/^[ \t]*[+]+[ \t]*/.freeze
9
+ Boundaries = ['+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++']
12
10
  StartingOf = {
13
11
  message: ['Your message:'],
14
12
  error: ['Could not be delivered because of'],
@@ -20,26 +18,19 @@ 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
  return nil unless mhead['subject'].start_with?('Undeliverable Mail: "')
25
23
 
26
24
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
27
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
28
- bodyslices = emailsteak[0].split("\n")
25
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
26
+ bodyslices = emailparts[0].split("\n")
29
27
  readcursor = 0 # (Integer) Points the current cursor position
30
28
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
31
29
  endoferror = false # (Boolean) Flag for the end of error message
32
30
  regularexp = nil
31
+ q = Sisimai::RFC2045.boundary(mhead['content-type'], 1); Boundaries << q if q
33
32
  v = nil
34
33
 
35
- boundary00 = Sisimai::MIME.boundary(mhead['content-type'], 1) || ''
36
- regularexp = if boundary00.size > 0
37
- # Convert to regular expression
38
- Regexp.new('\A' << Regexp.escape(boundary00) << '\z')
39
- else
40
- regularexp = %r/\A[ \t]*[+]+[ \t]*\z/
41
- end
42
-
43
34
  while e = bodyslices.shift do
44
35
  # Read error messages and delivery status lines from the head of the email
45
36
  # to the previous line of the beginning of the original message.
@@ -61,7 +52,7 @@ module Sisimai::Lhost
61
52
  # dummyuser@blabla.xxxxxxxxxxxx.com
62
53
  v = dscontents[-1]
63
54
 
64
- if cv = e.match(/\A[ ]{4}([^ ]+[@][^ ]+)\z/)
55
+ if e.start_with?(' ') && e.index('@') > 1
65
56
  # The following recipients were affected:
66
57
  # dummyuser@blabla.xxxxxxxxxxxx.com
67
58
  if v['recipient']
@@ -69,7 +60,7 @@ module Sisimai::Lhost
69
60
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
70
61
  v = dscontents[-1]
71
62
  end
72
- v['recipient'] = cv[1]
63
+ v['recipient'] = e[4, e.size]
73
64
  recipients += 1
74
65
  else
75
66
  # Get error message lines
@@ -93,24 +84,29 @@ module Sisimai::Lhost
93
84
  # Reporting-MTA: <relay.xxxxxxxxxxxx.com>
94
85
  # MessageName: <B549996730000.000000000001.0003.mml>
95
86
  # Last-Attempt-Date: <16:21:07 seg, 22 Dezembro 2014>
96
- if cv = e.match(/\AOriginal Sender:[ \t]+[<](.+)[>]\z/)
87
+ p1 = e.index('<')
88
+ p2 = e.index('>')
89
+ if e.start_with?('Original Sender: ')
97
90
  # Original Sender: <originalsender@example.com>
98
- # Use this line instead of "From" header of the original
99
- # message.
100
- emailsteak[1] << ('From: ' << cv[1] << "\n")
91
+ # Use this line instead of "From" header of the original message.
92
+ emailparts[1] << ('From: ' << e[p1 + 1, p2 - p1 - 1] << "\n")
101
93
 
102
- elsif cv = e.match(/\ASender-MTA:[ \t]+[<](.+)[>]\z/)
94
+ elsif e.start_with?('Sender-MTA: ')
103
95
  # Sender-MTA: <10.11.12.13>
104
- v['lhost'] = cv[1]
96
+ v['lhost'] = e[p1 + 1, p2 - p1 - 1]
105
97
 
106
- elsif cv = e.match(/\AReporting-MTA:[ \t]+[<](.+)[>]\z/)
98
+ elsif e.start_with?('Reporting-MTA: ')
107
99
  # Reporting-MTA: <relay.xxxxxxxxxxxx.com>
108
- v['rhost'] = cv[1]
100
+ v['rhost'] = e[p1 + 1, p2 - p1 - 1]
109
101
 
110
- elsif cv = e.match(/\A\s+(From|Subject):\s*(.+)\z/)
102
+ elsif e.include?(' From:') || e.include?(' Subject:')
111
103
  # From: originalsender@example.com
112
104
  # Subject: ...
113
- emailsteak[1] << sprintf("%s: %s\n", cv[1], cv[2])
105
+ p1 = e.index(' From:') || e.index(' Subject:')
106
+ p2 = e.index(':')
107
+ cf = e[p1 + 1, p2 - p1 - 1]
108
+ cv = Sisimai::String.sweep(e[p2 + 1, e.size])
109
+ emailparts[1] << sprintf("%s: %s\n", cf, cv)
114
110
  end
115
111
  end
116
112
  end
@@ -118,7 +114,7 @@ module Sisimai::Lhost
118
114
  return nil unless recipients > 0
119
115
 
120
116
  dscontents.each { |e| e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) }
121
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
117
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
122
118
  end
123
119
  def description; return 'Trustwave Secure Email Gateway'; end
124
120
  end
@@ -1,14 +1,13 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::MailRu parses a bounce email which created by @mail.ru.
3
- # Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::MailRu parses a bounce email which created by @mail.ru. Methods in the module are
3
+ # called from only Sisimai::Message.
4
4
  module MailRu
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/MailRu.pm
7
6
  # Based on Sisimai::Lhost::Exim
8
7
  require 'sisimai/lhost'
9
8
 
10
9
  Indicators = Sisimai::Lhost.INDICATORS
11
- ReBackbone = %r|^------ This is a copy of the message, including all the headers[.] ------|.freeze
10
+ Boundaries = ['------ This is a copy of the message, including all the headers. ------'].freeze
12
11
  StartingOf = { message: ['This message was created automatically by mail delivery software.'] }.freeze
13
12
  ReCommands = [
14
13
  %r/SMTP error from remote (?:mail server|mailer) after ([A-Za-z]{4})/,
@@ -50,30 +49,31 @@ module Sisimai::Lhost
50
49
  # @param [String] mbody Message body of a bounce email
51
50
  # @return [Hash] Bounce data list and message/rfc822 part
52
51
  # @return [Nil] it failed to parse or the arguments are missing
53
- def make(mhead, mbody)
54
- return nil unless mhead['from'] =~ /[<]?mailer-daemon[@].*mail[.]ru[>]?/i
55
- return nil unless mhead['message-id'].end_with?('.mail.ru>', 'smailru.net>')
56
- return nil unless mhead['subject'] =~ %r{(?:
57
- Mail[ ]delivery[ ]failed(:[ ]returning[ ]message[ ]to[ ]sender)?
58
- |Warning:[ ]message[ ].+[ ]delayed[ ]+
59
- |Delivery[ ]Status[ ]Notification
60
- |Mail[ ]failure
61
- |Message[ ]frozen
62
- |error[(]s[)][ ]in[ ]forwarding[ ]or[ ]filtering
63
- )
64
- }x
52
+ def inquire(mhead, mbody)
53
+ mfrom = mhead['from'].downcase
54
+ msgid = mhead['message-id'] ? mhead['message-id'].downcase : ''
55
+ match = 0
56
+ match += 1 if mfrom.include?('mailer-daemon@') && mfrom.include?('mail.ru')
57
+ match += 1 if msgid.end_with?('.mail.ru>', 'smailru.net>')
58
+ match += 1 if [
59
+ 'Delivery Status Notification',
60
+ 'Mail delivery failed',
61
+ 'Mail failure',
62
+ 'Message frozen',
63
+ 'Warning: message ',
64
+ 'error(s) in forwarding or filtering'].any? { |a| mhead['subject'].include?(a) }
65
+ return nil unless match > 2
65
66
 
66
67
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
67
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
68
- bodyslices = emailsteak[0].split("\n")
68
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
69
+ bodyslices = emailparts[0].split("\n")
69
70
  readcursor = 0 # (Integer) Points the current cursor position
70
71
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
71
- localhost0 = '' # (String) Local MTA
72
72
  v = nil
73
73
 
74
74
  while e = bodyslices.shift do
75
- # Read error messages and delivery status lines from the head of the email
76
- # to the previous line of the beginning of the original message.
75
+ # Read error messages and delivery status lines from the head of the email to the previous
76
+ # line of the beginning of the original message.
77
77
  if readcursor == 0
78
78
  # Beginning of the bounce message or delivery status part
79
79
  readcursor |= Indicators[:deliverystatus] if e.start_with?(StartingOf[:message][0])
@@ -102,14 +102,14 @@ module Sisimai::Lhost
102
102
  # host neko.example.jp [192.0.2.222]: 550 5.1.1 <kijitora@example.jp>... User Unknown
103
103
  v = dscontents[-1]
104
104
 
105
- if cv = e.match(/\A[ \t]+([^ \t]+[@][^ \t]+[.][a-zA-Z]+)\z/)
105
+ if e.start_with?(' ') && e.include?(' ') == false && e.index('@') > 1
106
106
  # kijitora@example.jp
107
107
  if v['recipient']
108
108
  # There are multiple recipient addresses in the message body.
109
109
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
110
110
  v = dscontents[-1]
111
111
  end
112
- v['recipient'] = cv[1]
112
+ v['recipient'] = e[2, e.size]
113
113
  recipients += 1
114
114
 
115
115
  elsif dscontents.size == recipients
@@ -120,7 +120,7 @@ module Sisimai::Lhost
120
120
  else
121
121
  # Error message when email address above does not include '@'
122
122
  # and domain part.
123
- next unless e.start_with?(' ', "\t")
123
+ next unless e.start_with?(' ')
124
124
  v['alterrors'] ||= ''
125
125
  v['alterrors'] << e + ' '
126
126
  end
@@ -144,16 +144,13 @@ module Sisimai::Lhost
144
144
  end
145
145
  return nil unless recipients > 0
146
146
 
147
- unless mhead['received'].empty?
148
- # Get the name of local MTA
149
- # Received: from marutamachi.example.org (c192128.example.net [192.0.2.128])
150
- if cv = mhead['received'][-1].match(/from[ \t]([^ ]+)/) then localhost0 = cv[1] end
151
- end
147
+ # Get the name of the local MTA
148
+ # Received: from marutamachi.example.org (c192128.example.net [192.0.2.128])
149
+ receivedby = mhead['received'] || []
150
+ recvdtoken = Sisimai::RFC5322.received(receivedby[-1])
152
151
 
153
152
  dscontents.each do |e|
154
- # Set default values if each value is empty.
155
- e['lhost'] ||= localhost0
156
-
153
+ # Check the error message, the rhost, the lhost, and the smtp command.
157
154
  unless e['alterrors'].to_s.empty?
158
155
  # Copy alternative error message
159
156
  e['diagnosis'] ||= e['alterrors']
@@ -164,18 +161,18 @@ module Sisimai::Lhost
164
161
  e.delete('alterrors')
165
162
  end
166
163
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''
167
- e['diagnosis'].sub!(/\b__.+\z/, '')
164
+ p1 = e['diagnosis'].rindex('__') || -1
165
+ e['diagnosis'] = e['diagnosis'][0, p1 - 1] if p1 > 2
168
166
 
169
167
  unless e['rhost']
170
168
  # Get the remote host name
171
169
  # host neko.example.jp [192.0.2.222]: 550 5.1.1 <kijitora@example.jp>... User Unknown
172
- if cv = e['diagnosis'].match(/host[ ]+([^ \t]+)[ ]\[.+\]:[ ]/) then e['rhost'] = cv[1] end
173
-
174
- unless e['rhost']
175
- # Get localhost and remote host name from Received header.
176
- e['rhost'] = Sisimai::RFC5322.received(mhead['received'][-1]).pop unless mhead['received'].empty?
177
- end
170
+ p1 = e['diagnosis'].index('host ') || -1
171
+ p2 = e['diagnosis'].index(' ', p1 + 5) || -1
172
+ e['rhost'] = e['diagnosis'][p1 + 5, p2 - p1 - 5] if p1 > -1
173
+ e['rhost'] ||= recvdtoken[1]
178
174
  end
175
+ e['lhost'] ||= recvdtoken[0]
179
176
 
180
177
  unless e['command']
181
178
  # Get the SMTP command name for the session
@@ -208,7 +205,7 @@ module Sisimai::Lhost
208
205
  e['command'] ||= ''
209
206
  end
210
207
 
211
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
208
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
212
209
  end
213
210
  def description; return '@mail.ru: https://mail.ru'; end
214
211
  end
@@ -1,49 +1,39 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::McAfee parses a bounce email which created by McAfee
3
- # Email Appliance. Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::McAfee parses a bounce email which created by McAfee Email Appliance. Methods in
3
+ # the module are called from only Sisimai::Message.
4
4
  module McAfee
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/McAfee.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: ['--- The following addresses had delivery problems ---'] }.freeze
12
- ReFailures = {
13
- 'userunknown' => %r{(?:
14
- [ ]User[ ][(].+[@].+[)][ ]unknown[.][ ]
15
- |550[ ]Unknown[ ]user[ ][^ ]+[@][^ ]+
16
- |550[ ][<].+?[@].+?[>][.]+[ ]User[ ]not[ ]exist
17
- |No[ ]such[ ]user
18
- )
19
- }x,
20
- }.freeze
11
+ MessagesOf = { 'userunknown' => [' User not exist', ' unknown.', '550 Unknown user ', 'No such user'] }.freeze
21
12
 
22
13
  # Parse bounce messages from McAfee Email Appliance
23
14
  # @param [Hash] mhead Message headers of a bounce email
24
15
  # @param [String] mbody Message body of a bounce email
25
16
  # @return [Hash] Bounce data list and message/rfc822 part
26
17
  # @return [Nil] it failed to parse or the arguments are missing
27
- def make(mhead, mbody)
18
+ def inquire(mhead, mbody)
28
19
  # X-NAI-Header: Modified by McAfee Email and Web Security Virtual Appliance
29
20
  return nil unless mhead['x-nai-header']
30
21
  return nil unless mhead['x-nai-header'].start_with?('Modified by McAfee ')
31
22
  return nil unless mhead['subject'] == 'Delivery Status'
32
23
 
33
- require 'sisimai/rfc1894'
34
24
  fieldtable = Sisimai::RFC1894.FIELDTABLE
35
25
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
36
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
37
- bodyslices = emailsteak[0].split("\n")
26
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
27
+ bodyslices = emailparts[0].split("\n")
38
28
  readslices = ['']
39
29
  readcursor = 0 # (Integer) Points the current cursor position
40
30
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
41
- diagnostic = '' # (String) Alternative diagnostic message
31
+ issuedcode = '' # (String) Alternative diagnostic message
42
32
  v = nil
43
33
 
44
34
  while e = bodyslices.shift do
45
- # Read error messages and delivery status lines from the head of the email
46
- # to the previous line of the beginning of the original message.
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.
47
37
  readslices << e # Save the current line for the next loop
48
38
 
49
39
  if readcursor == 0
@@ -65,15 +55,15 @@ module Sisimai::Lhost
65
55
  #
66
56
  v = dscontents[-1]
67
57
 
68
- if cv = e.match(/\A[<]([^ ]+[@][^ ]+)[>][ \t]+[(](.+)[)]\z/)
58
+ if Sisimai::String.aligned(e, ['<', '@', '>', '(', ')'])
69
59
  # <kijitora@example.co.jp> (Unknown user kijitora@example.co.jp)
70
60
  if v['recipient']
71
61
  # There are multiple recipient addresses in the message body.
72
62
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
73
63
  v = dscontents[-1]
74
64
  end
75
- v['recipient'] = cv[1]
76
- diagnostic = cv[2]
65
+ v['recipient'] = Sisimai::Address.s3s4(e[e.index('<'), e.index('>')])
66
+ issuedcode = e[e.index('(') + 1, e.size]
77
67
  recipients += 1
78
68
 
79
69
  elsif f = Sisimai::RFC1894.match(e)
@@ -82,8 +72,8 @@ module Sisimai::Lhost
82
72
  unless o
83
73
  # Fallback code for empty value or invalid formatted value
84
74
  # - Original-Recipient: <kijitora@example.co.jp>
85
- if cv = e.match(/\AOriginal-Recipient:[ ]*([^ ]+)\z/)
86
- v['alias'] = Sisimai::Address.s3s4(cv[1])
75
+ if e.start_with?('Original-Recipient: ')
76
+ v['alias'] = Sisimai::Address.s3s4(e[e.index(':') + 1, e.size])
87
77
  end
88
78
  next
89
79
  end
@@ -93,24 +83,24 @@ module Sisimai::Lhost
93
83
  else
94
84
  # Continued line of the value of Diagnostic-Code field
95
85
  next unless readslices[-2].start_with?('Diagnostic-Code:')
96
- next unless cv = e.match(/\A[ \t]+(.+)\z/)
97
- v['diagnosis'] << ' ' << cv[1]
86
+ next unless e.start_with?(' ')
87
+ v['diagnosis'] << ' ' << Sisimai::String.sweep(e)
98
88
  readslices[-1] = 'Diagnostic-Code: ' << e
99
89
  end
100
90
  end
101
91
  return nil unless recipients > 0
102
92
 
103
93
  dscontents.each do |e|
104
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'] || diagnostic)
105
- ReFailures.each_key do |r|
94
+ e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'] || issuedcode)
95
+ MessagesOf.each_key do |r|
106
96
  # Verify each regular expression of session errors
107
- next unless e['diagnosis'] =~ ReFailures[r]
97
+ next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
108
98
  e['reason'] = r
109
99
  break
110
100
  end
111
101
  end
112
102
 
113
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
103
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
114
104
  end
115
105
  def description; return 'McAfee Email Appliance'; end
116
106
  end