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,67 +1,65 @@
1
1
  module Sisimai
2
2
  # Sisimai::RFC3834 - RFC3834 auto reply message detector
3
3
  module RFC3834
4
- # Imported from p5-Sisimail/lib/Sisimai/RFC3834.pm
5
4
  class << self
6
5
  # http://tools.ietf.org/html/rfc3834
7
- MarkingsOf = { :boundary => %r/\A__SISIMAI_PSEUDO_BOUNDARY__\z/ }
8
- AutoReply1 = {
9
- # http://www.iana.org/assignments/auto-submitted-keywords/auto-submitted-keywords.xhtml
10
- 'auto-submitted' => %r/\Aauto-(?:generated|replied|notified)/,
11
- 'x-apple-action' => %r/\Avacation\z/,
12
- 'precedence' => %r/\Aauto_reply\z/,
13
- 'subject' => %r/\A(?>
14
- auto:
15
- |auto[ ]response:
16
- |automatic[ ]reply:
17
- |out[ ]of[ ](?:the[ ])*office:
18
- )
19
- /x,
6
+ MarkingsOf = { :boundary => '__SISIMAI_PSEUDO_BOUNDARY__' }
7
+ LowerLabel = %w[from to subject auto-submitted precedence x-apple-action].freeze
8
+ DoNotParse = {
9
+ 'from' => ['root@', 'postmaster@', 'mailer-daemon@'],
10
+ 'to' => ['root@'],
11
+ 'subject' => [
12
+ 'security information for', # sudo(1)
13
+ 'mail failure -', # Exim
14
+ ],
20
15
  }.freeze
21
- Excludings = {
22
- 'subject' => %r/(?:
23
- security[ ]information[ ]for # sudo
24
- |mail[ ]failure[ ][-] # Exim
25
- )
26
- /x,
27
- 'from' => %r/(?:root|postmaster|mailer-daemon)[@]/,
28
- 'to' => %r/root[@]/,
16
+ AutoReply0 = {
17
+ # http://www.iana.org/assignments/auto-submitted-keywords/auto-submitted-keywords.xhtml
18
+ 'auto-submitted' => ['auto-generated', 'auto-replied', 'auto-notified'],
19
+ 'precedence' => ['auto_reply'],
20
+ 'subject' => ['auto:', 'auto response:', 'automatic reply:', 'out of office:', 'out of the office:'],
21
+ 'x-apple-action' => ['vacation'],
29
22
  }.freeze
30
23
  SubjectSet = %r{\A(?>
31
- (?:.+?)?Re:
32
- |Auto(?:[ ]Response):
33
- |Automatic[ ]reply:
34
- |Out[ ]of[ ]Office:
24
+ (?:.+?)?re:
25
+ |auto(?:[ ]response):
26
+ |automatic[ ]reply:
27
+ |out[ ]of[ ]office:
35
28
  )
36
29
  [ ]*(.+)\z
37
- }xi.freeze
30
+ }x.freeze
38
31
 
39
32
  # Detect auto reply message as RFC3834
40
33
  # @param [Hash] mhead Message headers of a bounce email
41
34
  # @param [String] mbody Message body of a bounce email
42
35
  # @return [Hash] Bounce data list and message/rfc822 part
43
36
  # @return [Nil] it failed to parse or the arguments are missing
44
- def make(mhead, mbody)
37
+ def inquire(mhead, mbody)
45
38
  leave = 0
46
39
  match = 0
40
+ lower = {}
41
+
42
+ LowerLabel.each do |e|
43
+ # Set lower-cased value of each header related to auto-response
44
+ next unless mhead.has_key?(e)
45
+ lower[e] = mhead[e].downcase
46
+ end
47
47
 
48
48
  # DETECT_EXCLUSION_MESSAGE
49
- Excludings.each_key do |e|
49
+ DoNotParse.each_key do |e|
50
50
  # Exclude message from root@
51
- next unless mhead[e]
52
- next unless mhead[e]
53
- next unless mhead[e].downcase =~ Excludings[e]
51
+ next unless lower[e]
52
+ next unless DoNotParse[e].any? { |a| lower[e].include?(a) }
54
53
  leave = 1
55
54
  break
56
55
  end
57
56
  return nil if leave > 0
58
57
 
59
- # DETECT_AUTO_REPLY_MESSAGE
60
- AutoReply1.each_key do |e|
58
+ # DETECT_AUTO_REPLY_MESSAGE0
59
+ AutoReply0.each_key do |e|
61
60
  # RFC3834 Auto-Submitted and other headers
62
- next unless mhead[e]
63
- next unless mhead[e]
64
- next unless mhead[e].downcase =~ AutoReply1[e]
61
+ next unless lower[e]
62
+ next unless AutoReply0[e].any? { |a| lower[e].include?(a) }
65
63
  match += 1
66
64
  break
67
65
  end
@@ -95,16 +93,15 @@ module Sisimai
95
93
  return nil unless recipients > 0
96
94
 
97
95
  if mhead['content-type']
98
- # Get the boundary string and set regular expression for matching with
99
- # the boundary string.
100
- b0 = Sisimai::MIME.boundary(mhead['content-type'], 0) || ''
101
- MarkingsOf[:boundary] = %r/\A\Q#{b0}\E\z/ unless b0.empty?
96
+ # Get the boundary string and set regular expression for matching with the boundary string.
97
+ q = Sisimai::RFC2045.boundary(mhead['content-type'], 0) || ''
98
+ MarkingsOf[:boundary] = q unless q.empty?
102
99
  end
103
100
 
104
101
  # BODY_PARSER: Get vacation message
105
102
  while e = bodyslices.shift do
106
103
  # Read the first 5 lines except a blank line
107
- countuntil += 1 if e =~ MarkingsOf[:boundary]
104
+ countuntil += 1 if e.include?(MarkingsOf[:boundary])
108
105
 
109
106
  if e.empty?
110
107
  # Check a blank line
@@ -113,8 +110,8 @@ module Sisimai
113
110
  next
114
111
  end
115
112
  next unless e.include?(' ')
116
- next if e.start_with?('Content-Type')
117
- next if e.start_with?('Content-Transfer')
113
+ next if e.start_with?('Content-Type')
114
+ next if e.start_with?('Content-Transfer')
118
115
 
119
116
  v['diagnosis'] ||= ''
120
117
  v['diagnosis'] << e + ' '
@@ -127,7 +124,7 @@ module Sisimai
127
124
  v['date'] = mhead['date']
128
125
  v['status'] = ''
129
126
 
130
- if cv = mhead['subject'].match(SubjectSet)
127
+ if cv = lower['subject'].match(SubjectSet)
131
128
  # Get the Subject header from the original message
132
129
  rfc822part = 'Subject: ' << cv[1] + "\n"
133
130
  end
@@ -1,8 +1,8 @@
1
1
  module Sisimai
2
2
  # Sisimai::RFC5322 provide methods for checking email address.
3
3
  module RFC5322
4
- # Imported from p5-Sisimail/lib/Sisimai/RFC5322.pm
5
4
  class << self
5
+ require 'sisimai/string'
6
6
  HeaderTable = {
7
7
  :messageid => %w[message-id],
8
8
  :subject => %w[subject],
@@ -12,46 +12,26 @@ module Sisimai
12
12
  :recipient => %w[to delivered-to forward-path envelope-to x-envelope-to resent-to apparently-to],
13
13
  }.freeze
14
14
 
15
- build_regular_expressions = lambda do
16
- # See http://www.ietf.org/rfc/rfc5322.txt
17
- # or http://www.ex-parrot.com/pdw/Mail-RFC822-Address.html ...
18
- # addr-spec = local-part "@" domain
19
- # local-part = dot-atom / quoted-string / obs-local-part
20
- # domain = dot-atom / domain-literal / obs-domain
21
- # domain-literal = [CFWS] "[" *([FWS] dcontent) [FWS] "]" [CFWS]
22
- # dcontent = dtext / quoted-pair
23
- # dtext = NO-WS-CTL / ; Non white space controls
24
- # %d33-90 / ; The rest of the US-ASCII
25
- # %d94-126 ; characters not including "[",
26
- # ; "]", or "\"
27
- re = { rfc5322: nil, ignored: nil, domain: nil }
28
- atom = %r([a-zA-Z0-9_!#\$\%&'*+/=?\^`{}~|\-]+)o
29
- quoted_string = %r/"(?:\\[^\r\n]|[^\\"])*"/o
30
- domain_literal = %r/\[(?:\\[\x01-\x09\x0B-\x0c\x0e-\x7f]|[\x21-\x5a\x5e-\x7e])*\]/o
31
- dot_atom = %r/#{atom}(?:[.]#{atom})*/o
32
- local_part = %r/(?:#{dot_atom}|#{quoted_string})/o
33
- domain = %r/(?:#{dot_atom}|#{domain_literal})/o
34
-
35
- re[:rfc5322] = %r/\A#{local_part}[@]#{domain}\z/o
36
- re[:ignored] = %r/\A#{local_part}[.]*[@]#{domain}\z/o
37
- re[:domain] = %r/\A#{domain}\z/o
38
-
39
- return re
40
- end
41
-
42
15
  build_flatten_rfc822header_list = lambda do
43
- # Convert HEADER: structured hash table to flatten hash table for being
44
- # called from Sisimai::Lhost::*
16
+ # Convert HEADER: structured hash table to flatten hash table for being called from Sisimai::Lhost::*
45
17
  fv = {}
46
18
  HeaderTable.each_value do |e|
47
19
  e.each { |ee| fv[ee] = true }
48
20
  end
49
21
  return fv
50
22
  end
51
-
52
- Re = build_regular_expressions.call
53
23
  HeaderIndex = build_flatten_rfc822header_list.call
54
24
 
25
+ def FIELDINDEX
26
+ return %w[
27
+ Resent-Date From Sender Reply-To To Message-ID Subject Return-Path Received Date X-Mailer
28
+ Content-Type Content-Transfer-Encoding Content-Description Content-Disposition
29
+ ]
30
+ # The following fields are not referred in Sisimai
31
+ # Resent-From Resent-Sender Resent-Cc Cc Bcc Resent-Bcc In-Reply-To References
32
+ # Comments Keywords
33
+ end
34
+
55
35
  # Grouped RFC822 headers
56
36
  # @param [Symbol] group RFC822 Header group name
57
37
  # @return [Array,Hash] RFC822 Header list
@@ -67,66 +47,37 @@ module Sisimai
67
47
  return { 'to' => true, 'from' => true, 'subject' => true, 'message-id' => true }
68
48
  end
69
49
 
70
- # Check that the argument is an email address or not
71
- # @param [String] email Email address string
72
- # @return [True,False] true: is an email address
73
- # false: is not an email address
74
- def is_emailaddress(email)
75
- return false unless email.is_a?(::String)
76
- return false if email =~ %r/(?:[\x00-\x1f]|\x1f)/
77
- return false if email.size > 254
78
- return true if email =~ Re[:ignored]
79
- return false
80
- end
81
-
82
- # Check that the argument is mailer-daemon or not
83
- # @param [String] email Email address
84
- # @return [True,False] true: mailer-daemon
85
- # false: Not mailer-daemon
86
- def is_mailerdaemon(email)
87
- return false unless email.is_a?(::String)
88
- regex = %r/(?:
89
- (?:mailer-daemon|postmaster)[@]
90
- |[<(](?:mailer-daemon|postmaster)[)>]
91
- |\A(?:mailer-daemon|postmaster)\z
92
- |[ ]?mailer-daemon[ ]
93
- )
94
- /x.freeze
95
- return true if email.downcase =~ regex
96
- return false
97
- end
98
-
99
50
  # Convert Received headers to a structured data
100
- # @param [String] argvs Received header
51
+ # @param [String] argv1 Received header
101
52
  # @return [Array] Received header as a structured data
102
- def received(argvs)
103
- return [] unless argvs.is_a?(::String)
53
+ def received(argv1)
54
+ return [] unless argv1.is_a?(::String)
104
55
 
105
56
  hosts = []
106
57
  value = { 'from' => '', 'by' => '' }
107
58
 
108
59
  # Received: (qmail 10000 invoked by uid 999); 24 Apr 2013 00:00:00 +0900
109
- return [] if argvs =~ /qmail[ ]+.+invoked[ ]+/
60
+ return [] if argv1.include?('(qmail ') && argv1.include?(' invoked ')
61
+
62
+ p1 = argv1.index('from ') || -1
63
+ p2 = argv1.index('by ') || -1
64
+ p3 = argv1.index(' ', p2 + 3) || -1
110
65
 
111
- if cr = argvs.match(/\Afrom[ ]+(.+)[ ]+by[ ]+([^ ]+)/)
112
- # Received: from localhost (localhost)
113
- # by nijo.example.jp (V8/cf) id s1QB5ma0018057;
66
+ if p1 == 0 && p2 > 1 && p2 < p3
67
+ # Received: from localhost (localhost) by nijo.example.jp (V8/cf) id s1QB5ma0018057;
114
68
  # Wed, 26 Feb 2014 06:05:48 -0500
115
- value['from'] = cr[1]
116
- value['by'] = cr[2]
117
-
118
- elsif cr = argvs.match(/\bby[ ]+([^ ]+)(.+)/)
119
- # Received: by 10.70.22.98 with SMTP id c2mr1838265pdf.3; Fri, 18 Jul 2014
120
- # 00:31:02 -0700 (PDT)
121
- value['from'] = cr[1] + cr[2]
122
- value['by'] = cr[1]
69
+ value['from'] = Sisimai::String.sweep(argv1[p1 + 5, p2 - p1 - 5])
70
+ value['by'] = Sisimai::String.sweep(argv1[p2 + 3, p3 - p2 - 3])
71
+
72
+ elsif p1 != 0 && p2 > -1
73
+ # Received: by 10.70.22.98 with SMTP id c2mr1838265pdf.3; Fri, 18 Jul 2014 00:31:02 -0700 (PDT)
74
+ value['from'] = Sisimai::String.sweep(argv1[p2 + 3, argv1.size])
75
+ value['by'] = Sisimai::String.sweep(argv1[p2 + 3, p3 - p2 - 3])
123
76
  end
124
77
 
125
78
  if value['from'].include?(' ')
126
- # Received: from [10.22.22.222] (smtp-gateway.kyoto.ocn.ne.jp [192.0.2.222])
127
- # (authenticated bits=0)
128
- # by nijo.example.jp (V8/cf) with ESMTP id s1QB5ka0018055;
129
- # Wed, 26 Feb 2014 06:05:47 -0500
79
+ # Received: from [10.22.22.222] (smtp.kyoto.ocn.ne.jp [192.0.2.222]) (authenticated bits=0)
80
+ # by nijo.example.jp (V8/cf) with ESMTP id s1QB5ka0018055; Wed, 26 Feb 2014 06:05:47 -0500
130
81
  received = value['from'].split(' ')
131
82
  namelist = []
132
83
  addrlist = []
@@ -135,13 +86,13 @@ module Sisimai
135
86
 
136
87
  while e = received.shift do
137
88
  # Received: from [10.22.22.222] (smtp-gateway.kyoto.ocn.ne.jp [192.0.2.222])
138
- if e =~ /\A[\[(]\d+[.]\d+[.]\d+[.]\d+[)\]]\z/
89
+ cv = Sisimai::String.ipv4(e) || []
90
+ if cv.size > 0
139
91
  # [192.0.2.1] or (192.0.2.1)
140
- e = e.delete('[]()')
141
- addrlist << e
92
+ addrlist.append(*cv)
142
93
  else
143
94
  # hostname
144
- e = e.delete('()')
95
+ e = e.delete('()').strip
145
96
  namelist << e
146
97
  end
147
98
  end
@@ -176,31 +127,55 @@ module Sisimai
176
127
  return hosts
177
128
  end
178
129
 
179
- # Split given entire message body into error message lines and the original
180
- # message part only include email headers
181
- # @param [String] mbody Entire message body
182
- # @param [Regexp] regex Regular expression of the message/rfc822 or the
183
- # beginning of the original message part
130
+ # Split given entire message body into error message lines and the original message part only
131
+ # include email headers
132
+ # @param [String] email Entire message body
133
+ # @param [Array] cutby List of strings which is a boundary of the original message part
134
+ # @param [Bool] keeps Flag for keeping strings after "\n\n"
184
135
  # @return [Array] [Error message lines, The original message]
185
- # @since v4.25.5
186
- def fillet(mbody = '', regex)
187
- return nil if mbody.empty?
188
- return nil unless regex
136
+ # @since v5.0.0
137
+ def part(email = '', cutby = [], keeps = false)
138
+ return nil if email.empty?
139
+ return nil if cutby.empty?
140
+
141
+ boundaryor = '' # A boundary string divides the error message part and the original message part
142
+ positionor = nil # A Position of the boundary string
143
+ formerpart = '' # The error message part
144
+ latterpart = '' # The original message part
145
+
146
+ cutby.each do |e|
147
+ # Find a boundary string(2nd argument) from the 1st argument
148
+ positionor = email.index(e); next unless positionor
149
+ boundaryor = e
150
+ break
151
+ end
189
152
 
190
- v = mbody.split(regex, 2)
191
- v[1] ||= ''
153
+ if positionor
154
+ # There is the boundary string in the message body
155
+ formerpart = email[0, positionor]
156
+ latterpart = email[positionor + boundaryor.size + 1, email.size - positionor]
157
+ else
158
+ # Substitute the entire message to the former part when the boundary string is not included
159
+ # the "email"
160
+ formerpart = email
161
+ latterpart = ''
162
+ end
192
163
 
193
- unless v[1].empty?
164
+ if latterpart.size > 0
194
165
  # Remove blank lines, the message body of the original message, and append "\n" at the end
195
166
  # of the original message headers
196
167
  # 1. Remove leading blank lines
197
168
  # 2. Remove text after the first blank line: \n\n
198
169
  # 3. Append "\n" at the end of test block when the last character is not "\n"
199
- v[1].sub!(/\A[\r\n\s]+/, '')
200
- v[1] = v[1][0, v[1].index("\n\n")] if v[1].include?("\n\n")
201
- v[1] << "\n" unless v[1].end_with?("\n")
170
+ latterpart.sub!(/\A[\r\n\s]+/, '')
171
+ if keeps == false
172
+ # Remove text after the first blank line: \n\n when "keeps" is false
173
+ latterpart = latterpart[0, latterpart.index("\n\n")] if latterpart.include?("\n\n")
174
+ end
175
+ latterpart << "\n" unless latterpart.end_with?("\n")
202
176
  end
203
- return v
177
+
178
+ return [formerpart, latterpart]
204
179
  end
205
180
 
206
181
  end
@@ -0,0 +1,31 @@
1
+ module Sisimai
2
+ # Sisimai::RFC5965 - A class for An Extensible Format for Email Feedback Reports
3
+ module RFC5965
4
+ class << self
5
+ # https://datatracker.ietf.org/doc/html/rfc5965
6
+ def FIELDINDEX
7
+ return [
8
+ # Required Fields
9
+ # The following report header fields MUST appear exactly once:
10
+ 'Feedback-Type', 'User-Agent', 'Version',
11
+
12
+ # Optional Fields Appearing Once
13
+ # The following header fields are optional and MUST NOT appear more than once:
14
+ # - "Reporting-MTA" is defined in Sisimai::RFC1894->FIELDINDEX()
15
+ 'Original-Envelope-Id', 'Original-Mail-From', 'Arrival-Date', 'Source-IP', 'Incidents',
16
+
17
+ # Optional Fields Appearing Multiple Times
18
+ # The following set of header fields are optional and may appear any number of times as
19
+ # appropriate:
20
+ 'Authentication-Results', 'Original-Rcpt-To', 'Reported-Domain', 'Reported-URI',
21
+
22
+ # The historic field "Received-Date" SHOULD also be accepted and interpreted identically
23
+ # to "Arrival-Date". However, if both are present, the report is malformed and SHOULD be
24
+ # treated as described in Section 4.
25
+ 'Received-Date',
26
+ ]
27
+ end
28
+ end
29
+ end
30
+ end
31
+
@@ -1,11 +1,10 @@
1
1
  module Sisimai
2
2
  module Rhost
3
- # Sisimai::Rhost detects the bounce reason from the content of Sisimai::Data
4
- # object as an argument of get() method when the value of "destination" of
5
- # the object is "charter.net". This class is called only Sisimai::Data class.
3
+ # Sisimai::Rhost detects the bounce reason from the content of Sisimai::Fact object as an argument
4
+ # of get() method when the value of "destination" of the object is "charter.net". This class is
5
+ # called only Sisimai::Fact class.
6
6
  module Cox
7
7
  class << self
8
- # Imported from p5-Sisimail/lib/Sisimai/Rhost/Cox.pm
9
8
  ErrorCodes = {
10
9
  # https://www.cox.com/residential/support/email-error-codes.html
11
10
  'CXBL' => 'blocked', # The sending IP address has been blocked by Cox due to exhibiting spam-like behavior.
@@ -79,11 +78,11 @@ module Sisimai
79
78
  }.freeze
80
79
 
81
80
  # Detect bounce reason from https://cox.com/
82
- # @param [Sisimai::Data] argvs Parsed email object
81
+ # @param [Sisimai::Fact] argvs Parsed email object
83
82
  # @return [String, Nil] The bounce reason at Cox
84
83
  # @since v4.25.8
85
84
  def get(argvs)
86
- statusmesg = argvs.diagnosticcode
85
+ statusmesg = argvs['diagnosticcode']
87
86
  codenumber = 0
88
87
 
89
88
  if cv = statusmesg.match(/AUP#([0-9A-Z]+)/)
@@ -1,12 +1,10 @@
1
1
  module Sisimai
2
2
  module Rhost
3
- # Sisimai::Rhost detects the bounce reason from the content of Sisimai::Data
4
- # object as an argument of get() method when the value of "rhost" of the object
5
- # is "*.laposte.net" or "*.orange.fr".
6
- # This class is called only Sisimai::Data class.
3
+ # Sisimai::Rhost detects the bounce reason from the content of Sisimai::Fact object as an argument
4
+ # of get() method when the value of "rhost" of the object is "*.laposte.net" or "*.orange.fr".
5
+ # This class is called only Sisimai::Fact class.
7
6
  module FrancePTT
8
7
  class << self
9
- # Imported from p5-Sisimail/lib/Sisimai/Rhost/FrancePTT.pm
10
8
  ErrorCodes = {
11
9
  # 550 5.7.1 Service unavailable; client [X.X.X.X] blocked using Spamhaus
12
10
  # Les emails envoyes vers la messagerie Laposte.net ont ete bloques par nos services.
@@ -133,12 +131,12 @@ module Sisimai
133
131
  }.freeze
134
132
 
135
133
  # Detect bounce reason from Oranage or La Poste
136
- # @param [Sisimai::Data] argvs Parsed email object
134
+ # @param [Sisimai::Fact] argvs Parsed email object
137
135
  # @return [String] The bounce reason for Orange or La Poste
138
136
  def get(argvs)
139
- return argvs.reason unless argvs.reason.empty?
137
+ return argvs['reason'] unless argvs['reason'].empty?
140
138
 
141
- statusmesg = argvs.diagnosticcode
139
+ statusmesg = argvs['diagnosticcode']
142
140
  reasontext = ''
143
141
 
144
142
  if cv = statusmesg.match(/\b(LPN|LPNAAA|OFR|OUK)(_[0-9]{3}|[0-9]{3}[-_][0-9]{3})\b/i)
@@ -1,14 +1,13 @@
1
1
  module Sisimai
2
2
  module Rhost
3
- # Sisimai::Rhost detects the bounce reason from the content of Sisimai::Data
4
- # object as an argument of get() method when the value of "rhost" of the object
5
- # is "*.secureserver.net". This class is called only Sisimai::Data class.
3
+ # Sisimai::Rhost detects the bounce reason from the content of Sisimai::Fact object as an argument
4
+ # of get() method when the value of "rhost" of the object is "*.secureserver.net". This class is
5
+ # called only Sisimai::Fact class.
6
6
  module GoDaddy
7
7
  class << self
8
- # Imported from p5-Sisimail/lib/Sisimai/Rhost/GoDaddy.pm
9
8
  # https://www.godaddy.com/help/what-does-my-email-bounceback-mean-3568
10
9
  ErrorCodes = {
11
- 'IB103' => 'blocked', # 554 Connection refused. This IP has a poor reputation on Cloudmark Sender Intelligence (CSI). IB103
10
+ 'IB103' => 'badreputation', # 554 Connection refused. This IP has a poor reputation on Cloudmark Sender Intelligence (CSI). IB103
12
11
  'IB104' => 'blocked', # 554 Connection refused. This IP is listed on the Spamhaus Block List (SBL). IB104
13
12
  'IB105' => 'blocked', # 554 Connection refused. This IP is listed on the Exploits Block List (XBL). IB105
14
13
  'IB106' => 'blocked', # 554 Connection refused. This IP is listed on the Policy Block List (PBL). IB106
@@ -39,19 +38,19 @@ module Sisimai
39
38
  }.freeze
40
39
 
41
40
  # Detect bounce reason from GoDaddy
42
- # @param [Sisimai::Data] argvs Parsed email object
41
+ # @param [Sisimai::Fact] argvs Parsed email object
43
42
  # @return [String] The bounce reason for GoDaddy
44
43
  # @see https://www.godaddy.com/help/what-does-my-email-bounceback-mean-3568
45
44
  def get(argvs)
46
- return argvs.reason unless argvs.reason.empty?
45
+ return argvs['reason'] unless argvs['reason'].empty?
47
46
 
48
- statusmesg = argvs.diagnosticcode
47
+ statusmesg = argvs['diagnosticcode']
48
+ positionib = statusmesg.index(' IB') || -1
49
49
  reasontext = ''
50
50
 
51
- if cv = statusmesg.match(/\s(IB\d{3})\b/)
52
- # 192.0.2.22 has sent to too many recipients this hour. IB607 ...
53
- reasontext = ErrorCodes[cv[1]]
54
- else
51
+ # 192.0.2.22 has sent to too many recipients this hour. IB607 ...
52
+ reasontext = ErrorCodes[statusmesg[positionib + 1, 5]] || '' if positionib > 1
53
+ if reasontext.empty?
55
54
  # 553 http://www.spamhaus.org/query/bl?ip=192.0.0.222
56
55
  MessagesOf.each_key do |e|
57
56
  MessagesOf[e].each do |f|
@@ -62,6 +61,7 @@ module Sisimai
62
61
  break unless reasontext.empty?
63
62
  end
64
63
  end
64
+
65
65
  return reasontext
66
66
  end
67
67