sisimai 4.25.16 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (177) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -3
  3. data/ANALYTICAL-PRECISION +2 -2
  4. data/Benchmarks.mk +3 -3
  5. data/CONTRIBUTING +1 -1
  6. data/ChangeLog.md +412 -393
  7. data/Developers.mk +5 -6
  8. data/Gemfile +1 -1
  9. data/Makefile +15 -15
  10. data/README-JA.md +140 -78
  11. data/README.md +290 -143
  12. data/Rakefile +9 -3
  13. data/Repository.mk +2 -3
  14. data/lib/sisimai/address.rb +118 -74
  15. data/lib/sisimai/arf.rb +84 -82
  16. data/lib/sisimai/datetime.rb +5 -52
  17. data/lib/sisimai/{data → fact}/json.rb +7 -9
  18. data/lib/sisimai/fact/yaml.rb +31 -0
  19. data/lib/sisimai/fact.rb +468 -0
  20. data/lib/sisimai/lhost/activehunter.rb +12 -14
  21. data/lib/sisimai/lhost/amavis.rb +11 -14
  22. data/lib/sisimai/lhost/amazonses.rb +37 -41
  23. data/lib/sisimai/lhost/amazonworkmail.rb +15 -18
  24. data/lib/sisimai/lhost/aol.rb +12 -14
  25. data/lib/sisimai/lhost/apachejames.rb +19 -21
  26. data/lib/sisimai/lhost/barracuda.rb +10 -12
  27. data/lib/sisimai/lhost/bigfoot.rb +21 -21
  28. data/lib/sisimai/lhost/biglobe.rb +15 -16
  29. data/lib/sisimai/lhost/courier.rb +20 -20
  30. data/lib/sisimai/lhost/domino.rb +23 -19
  31. data/lib/sisimai/lhost/einsundeins.rb +23 -18
  32. data/lib/sisimai/lhost/exchange2003.rb +30 -29
  33. data/lib/sisimai/lhost/exchange2007.rb +70 -58
  34. data/lib/sisimai/lhost/exim.rb +175 -161
  35. data/lib/sisimai/lhost/ezweb.rb +31 -56
  36. data/lib/sisimai/lhost/facebook.rb +21 -33
  37. data/lib/sisimai/lhost/fml.rb +43 -48
  38. data/lib/sisimai/lhost/gmail.rb +29 -29
  39. data/lib/sisimai/lhost/gmx.rb +18 -17
  40. data/lib/sisimai/lhost/googlegroups.rb +9 -10
  41. data/lib/sisimai/lhost/gsuite.rb +21 -27
  42. data/lib/sisimai/lhost/imailserver.rb +25 -39
  43. data/lib/sisimai/lhost/interscanmss.rb +28 -31
  44. data/lib/sisimai/lhost/kddi.rb +22 -28
  45. data/lib/sisimai/lhost/mailfoundry.rb +11 -12
  46. data/lib/sisimai/lhost/mailmarshalsmtp.rb +25 -29
  47. data/lib/sisimai/lhost/mailru.rb +33 -27
  48. data/lib/sisimai/lhost/mcafee.rb +21 -31
  49. data/lib/sisimai/lhost/messagelabs.rb +17 -20
  50. data/lib/sisimai/lhost/messagingserver.rb +40 -37
  51. data/lib/sisimai/lhost/mfilter.rb +15 -16
  52. data/lib/sisimai/lhost/mxlogic.rb +24 -23
  53. data/lib/sisimai/lhost/notes.rb +17 -17
  54. data/lib/sisimai/lhost/office365.rb +63 -27
  55. data/lib/sisimai/lhost/opensmtpd.rb +12 -13
  56. data/lib/sisimai/lhost/outlook.rb +12 -15
  57. data/lib/sisimai/lhost/postfix.rb +179 -129
  58. data/lib/sisimai/lhost/powermta.rb +12 -14
  59. data/lib/sisimai/lhost/qmail.rb +44 -47
  60. data/lib/sisimai/lhost/receivingses.rb +15 -20
  61. data/lib/sisimai/lhost/sendgrid.rb +34 -32
  62. data/lib/sisimai/lhost/sendmail.rb +66 -53
  63. data/lib/sisimai/lhost/surfcontrol.rb +19 -19
  64. data/lib/sisimai/lhost/v5sendmail.rb +45 -39
  65. data/lib/sisimai/lhost/verizon.rb +35 -39
  66. data/lib/sisimai/lhost/x1.rb +18 -17
  67. data/lib/sisimai/lhost/x2.rb +17 -14
  68. data/lib/sisimai/lhost/x3.rb +19 -19
  69. data/lib/sisimai/lhost/x4.rb +72 -57
  70. data/lib/sisimai/lhost/x5.rb +17 -19
  71. data/lib/sisimai/lhost/x6.rb +41 -17
  72. data/lib/sisimai/lhost/yahoo.rb +17 -16
  73. data/lib/sisimai/lhost/yandex.rb +16 -20
  74. data/lib/sisimai/lhost/zoho.rb +16 -15
  75. data/lib/sisimai/lhost.rb +8 -10
  76. data/lib/sisimai/mail/maildir.rb +1 -3
  77. data/lib/sisimai/mail/mbox.rb +3 -4
  78. data/lib/sisimai/mail/memory.rb +0 -1
  79. data/lib/sisimai/mail/stdin.rb +1 -3
  80. data/lib/sisimai/mail.rb +3 -7
  81. data/lib/sisimai/mda.rb +28 -42
  82. data/lib/sisimai/message.rb +435 -326
  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 +12 -12
  134. data/lib/sisimai/rhost/spectrum.rb +10 -12
  135. data/lib/sisimai/rhost/{tencentqq.rb → tencent.rb} +7 -8
  136. data/lib/sisimai/rhost.rb +23 -31
  137. data/lib/sisimai/smtp/command.rb +59 -0
  138. data/lib/sisimai/smtp/error.rb +4 -7
  139. data/lib/sisimai/smtp/reply.rb +161 -74
  140. data/lib/sisimai/smtp/status.rb +504 -393
  141. data/lib/sisimai/smtp/transcript.rb +124 -0
  142. data/lib/sisimai/smtp.rb +0 -1
  143. data/lib/sisimai/string.rb +74 -5
  144. data/lib/sisimai/time.rb +1 -2
  145. data/lib/sisimai/version.rb +1 -1
  146. data/lib/sisimai.rb +35 -21
  147. data/set-of-emails/maildir/bsd/lhost-domino-02.eml +6 -3
  148. data/set-of-emails/maildir/bsd/lhost-googlegroups-15.eml +174 -0
  149. data/set-of-emails/maildir/bsd/lhost-gsuite-15.eml +229 -0
  150. data/set-of-emails/maildir/bsd/lhost-postfix-75.eml +51 -0
  151. data/set-of-emails/maildir/bsd/lhost-postfix-76.eml +101 -0
  152. data/set-of-emails/maildir/bsd/lhost-postfix-77.eml +74 -0
  153. data/set-of-emails/maildir/bsd/lhost-postfix-78.eml +91 -0
  154. data/set-of-emails/maildir/bsd/lhost-receivingses-08.eml +88 -0
  155. data/set-of-emails/maildir/bsd/rfc3464-43.eml +88 -0
  156. data/set-of-emails/maildir/bsd/rhost-google-03.eml +101 -0
  157. data/set-of-emails/maildir/bsd/rhost-google-04.eml +102 -0
  158. data/set-of-emails/maildir/bsd/rhost-google-05.eml +82 -0
  159. data/set-of-emails/maildir/bsd/rhost-google-06.eml +102 -0
  160. data/set-of-emails/maildir/bsd/rhost-google-07.eml +69 -0
  161. data/set-of-emails/maildir/bsd/rhost-google-08.eml +99 -0
  162. data/sisimai-java.gemspec +1 -1
  163. data/sisimai.gemspec +1 -1
  164. metadata +41 -20
  165. data/.rspec +0 -2
  166. data/lib/sisimai/data/yaml.rb +0 -33
  167. data/lib/sisimai/data.rb +0 -411
  168. data/lib/sisimai/mime.rb +0 -456
  169. /data/set-of-emails/maildir/bsd/{rfc3464-41.eml → rfc3834-05.eml} +0 -0
  170. /data/set-of-emails/maildir/bsd/{rhost-googleapps-01.eml → rhost-google-01.eml} +0 -0
  171. /data/set-of-emails/maildir/bsd/{rhost-googleapps-02.eml → rhost-google-02.eml} +0 -0
  172. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-01.eml → rhost-microsoft-01.eml} +0 -0
  173. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-02.eml → rhost-microsoft-02.eml} +0 -0
  174. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-03.eml → rhost-microsoft-03.eml} +0 -0
  175. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-01.eml → rhost-tencent-01.eml} +0 -0
  176. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-02.eml → rhost-tencent-02.eml} +0 -0
  177. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-03.eml → rhost-tencent-03.eml} +0 -0
@@ -1,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,32 @@ 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
72
  localhost0 = '' # (String) Local MTA
72
73
  v = nil
73
74
 
74
75
  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.
76
+ # Read error messages and delivery status lines from the head of the email to the previous
77
+ # line of the beginning of the original message.
77
78
  if readcursor == 0
78
79
  # Beginning of the bounce message or delivery status part
79
80
  readcursor |= Indicators[:deliverystatus] if e.start_with?(StartingOf[:message][0])
@@ -102,14 +103,14 @@ module Sisimai::Lhost
102
103
  # host neko.example.jp [192.0.2.222]: 550 5.1.1 <kijitora@example.jp>... User Unknown
103
104
  v = dscontents[-1]
104
105
 
105
- if cv = e.match(/\A[ \t]+([^ \t]+[@][^ \t]+[.][a-zA-Z]+)\z/)
106
+ if e.start_with?(' ') && e.include?(' ') == false && e.index('@') > 1
106
107
  # kijitora@example.jp
107
108
  if v['recipient']
108
109
  # There are multiple recipient addresses in the message body.
109
110
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
110
111
  v = dscontents[-1]
111
112
  end
112
- v['recipient'] = cv[1]
113
+ v['recipient'] = e[2, e.size]
113
114
  recipients += 1
114
115
 
115
116
  elsif dscontents.size == recipients
@@ -120,7 +121,7 @@ module Sisimai::Lhost
120
121
  else
121
122
  # Error message when email address above does not include '@'
122
123
  # and domain part.
123
- next unless e.start_with?(' ', "\t")
124
+ next unless e.start_with?(' ')
124
125
  v['alterrors'] ||= ''
125
126
  v['alterrors'] << e + ' '
126
127
  end
@@ -147,7 +148,9 @@ module Sisimai::Lhost
147
148
  unless mhead['received'].empty?
148
149
  # Get the name of local MTA
149
150
  # 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
+ p1 = mhead['received'][-1].index('from ') || -1
152
+ p2 = mhead['received'][-1].index(' ', p1 + 5) || -1
153
+ localhost0 = mhead['received'][-1][p1 + 5, p2 - p1 - 5] if p1 > -1
151
154
  end
152
155
 
153
156
  dscontents.each do |e|
@@ -164,12 +167,15 @@ module Sisimai::Lhost
164
167
  e.delete('alterrors')
165
168
  end
166
169
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''
167
- e['diagnosis'].sub!(/\b__.+\z/, '')
170
+ p1 = e['diagnosis'].rindex('__') || -1
171
+ e['diagnosis'] = e['diagnosis'][0, p1 - 1] if p1 > 2
168
172
 
169
173
  unless e['rhost']
170
174
  # Get the remote host name
171
175
  # 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
176
+ p1 = e['diagnosis'].index('host ') || -1
177
+ p2 = e['diagnosis'].index(' ', p1 + 5) || -1
178
+ e['rhost'] = e['diagnosis'][p1 + 5, p2 - p1 - 5] if p1 > -1
173
179
 
174
180
  unless e['rhost']
175
181
  # Get localhost and remote host name from Received header.
@@ -208,7 +214,7 @@ module Sisimai::Lhost
208
214
  e['command'] ||= ''
209
215
  end
210
216
 
211
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
217
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
212
218
  end
213
219
  def description; return '@mail.ru: https://mail.ru'; end
214
220
  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
@@ -1,18 +1,16 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::MessageLabs parses a bounce email which created by
3
- # Symantec.cloud: formerly MessageLabs. Methods in the module are called
4
- # from only Sisimai::Message.
2
+ # Sisimai::Lhost::MessageLabs parses a bounce email which created by Symantec.cloud: formerly MessageLabs.
3
+ # Methods in the module are called from only Sisimai::Message.
5
4
  module MessageLabs
6
5
  class << self
7
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/MessageLabs.pm
8
6
  require 'sisimai/lhost'
9
7
 
10
8
  Indicators = Sisimai::Lhost.INDICATORS
11
- ReBackbone = %r|^Content-Type:[ ]text/rfc822-headers|.freeze
9
+ Boundaries = ['Content-Type: text/rfc822-headers'].freeze
12
10
  StartingOf = { message: ['Content-Type: message/delivery-status'] }.freeze
13
- ReFailures = {
14
- 'userunknown' => %r/(?:542 .+ Rejected|No such user)/,
15
- 'securityerror' => %r/Please turn on SMTP Authentication in your mail client/,
11
+ MessagesOf = {
12
+ 'userunknown' => ['542 ', ' Rejected', 'No such user'],
13
+ 'securityerror' => ['Please turn on SMTP Authentication in your mail client'],
16
14
  }.freeze
17
15
 
18
16
  # Parse bounce messages from Symantec.cloud(MessageLabs)
@@ -20,7 +18,7 @@ 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
  # X-Msg-Ref: server-11.tower-143.messagelabs.com!1419367175!36473369!1
25
23
  # X-Originating-IP: [10.245.230.38]
26
24
  # X-StarScan-Received:
@@ -30,13 +28,12 @@ module Sisimai::Lhost
30
28
  return nil unless mhead['from'].include?('MAILER-DAEMON@messagelabs.com')
31
29
  return nil unless mhead['subject'].start_with?('Mail Delivery Failure')
32
30
 
33
- require 'sisimai/rfc1894'
34
31
  fieldtable = Sisimai::RFC1894.FIELDTABLE
35
32
  permessage = {} # (Hash) Store values of each Per-Message field
36
33
 
37
34
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
38
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
39
- bodyslices = emailsteak[0].split("\n")
35
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
36
+ bodyslices = emailparts[0].split("\n")
40
37
  readslices = ['']
41
38
  readcursor = 0 # (Integer) Points the current cursor position
42
39
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
@@ -44,8 +41,8 @@ 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
  readslices << e # Save the current line for the next loop
50
47
 
51
48
  if readcursor == 0
@@ -86,14 +83,14 @@ module Sisimai::Lhost
86
83
  next unless fieldtable[o[0]]
87
84
  v[fieldtable[o[0]]] = o[2]
88
85
 
89
- next unless f == 1
86
+ next unless f
90
87
  permessage[fieldtable[o[0]]] = o[2]
91
88
  end
92
89
  else
93
90
  # Continued line of the value of Diagnostic-Code field
94
91
  next unless readslices[-2].start_with?('Diagnostic-Code:')
95
- next unless cv = e.match(/\A[ \t]+(.+)\z/)
96
- v['diagnosis'] << ' ' << cv[1]
92
+ next unless e.start_with?(' ')
93
+ v['diagnosis'] << ' ' << Sisimai::String.sweep(e)
97
94
  readslices[-1] = 'Diagnostic-Code: ' << e
98
95
  end
99
96
  end
@@ -106,15 +103,15 @@ module Sisimai::Lhost
106
103
  e['command'] = commandset.shift || ''
107
104
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
108
105
 
109
- ReFailures.each_key do |r|
106
+ MessagesOf.each_key do |r|
110
107
  # Verify each regular expression of session errors
111
- next unless e['diagnosis'] =~ ReFailures[r]
108
+ next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
112
109
  e['reason'] = r
113
110
  break
114
111
  end
115
112
  end
116
113
 
117
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
114
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
118
115
  end
119
116
  def description; return 'Symantec.cloud http://www.messagelabs.com'; end
120
117
  end