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,41 +1,28 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::EZweb parses a bounce email which created by au EZweb.
3
- # Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::EZweb parses a bounce email which created by au EZweb. Methods in the module are
3
+ # called from only Sisimai::Message.
4
4
  module EZweb
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/EZweb.pm
7
6
  require 'sisimai/lhost'
8
7
 
9
8
  Indicators = Sisimai::Lhost.INDICATORS
10
- ReBackbone = %r<^(?:[-]{50}|Content-Type:[ ]*message/rfc822)>.freeze
11
- MarkingsOf = {
12
- message: %r{\A(?:
13
- The[ ]user[(]s[)][ ]
14
- |Your[ ]message[ ]
15
- |Each[ ]of[ ]the[ ]following
16
- |[<][^ ]+[@][^ ]+[>]\z
17
- )
18
- }x,
19
- }.freeze
9
+ Boundaries = ['--------------------------------------------------', 'Content-Type: message/rfc822'].freeze
10
+ MarkingsOf = { message: ['The user(s) ', 'Your message ', 'Each of the following', '<'] }.freeze
20
11
  ReFailures = {
21
- # notaccept: [ %r/The following recipients did not receive this message:/ ],
22
- 'mailboxfull' => [
23
- %r/The user[(]s[)] account is temporarily over quota/,
24
- ],
12
+ # notaccept: ['The following recipients did not receive this message:'],
13
+ 'mailboxfull' => ['The user(s) account is temporarily over quota'],
25
14
  'suspend' => [
26
15
  # http://www.naruhodo-au.kddi.com/qa3429203.html
27
16
  # The recipient may be unpaid user...?
28
- %r/The user[(]s[)] account is disabled[.]/,
29
- %r/The user[(]s[)] account is temporarily limited[.]/,
17
+ 'The user(s) account is disabled.',
18
+ 'The user(s) account is temporarily limited.',
30
19
  ],
31
20
  'expired' => [
32
21
  # Your message was not delivered within 0 days and 1 hours.
33
22
  # Remote host is not responding.
34
- %r/Your message was not delivered within /,
35
- ],
36
- 'onhold' => [
37
- %r/Each of the following recipients was rejected by a remote mail server/,
23
+ 'Your message was not delivered within ',
38
24
  ],
25
+ 'onhold' => ['Each of the following recipients was rejected by a remote mail server'],
39
26
  }.freeze
40
27
 
41
28
  # Parse bounce messages from au EZweb
@@ -43,7 +30,7 @@ module Sisimai::Lhost
43
30
  # @param [String] mbody Message body of a bounce email
44
31
  # @return [Hash] Bounce data list and message/rfc822 part
45
32
  # @return [Nil] it failed to parse or the arguments are missing
46
- def make(mhead, mbody)
33
+ def inquire(mhead, mbody)
47
34
  match = 0
48
35
  match += 1 if mhead['from'].include?('Postmaster@ezweb.ne.jp')
49
36
  match += 1 if mhead['from'].include?('Postmaster@au.com')
@@ -55,31 +42,21 @@ module Sisimai::Lhost
55
42
  end
56
43
  return nil if match < 2
57
44
 
58
- require 'sisimai/rfc1894'
59
45
  fieldtable = Sisimai::RFC1894.FIELDTABLE
60
46
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
61
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
62
- bodyslices = emailsteak[0].split("\n")
47
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
48
+ bodyslices = emailparts[0].split("\n")
63
49
  readcursor = 0 # (Integer) Points the current cursor position
64
50
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
65
- rxboundary = %r/\A__SISIMAI_PSEUDO_BOUNDARY__\z/
51
+ rxmessages = []; ReFailures.each_value { |a| rxmessages << a }
66
52
  v = nil
67
53
 
68
- if mhead['content-type']
69
- # Get the boundary string and set regular expression for matching with
70
- # the boundary string.
71
- b0 = Sisimai::MIME.boundary(mhead['content-type'], 1)
72
- rxboundary = Regexp.new('\A' << Regexp.escape(b0) << '\z') unless b0.empty?
73
- end
74
- rxmessages = []
75
- ReFailures.each_value { |a| rxmessages << a }
76
-
77
54
  while e = bodyslices.shift do
78
- # Read error messages and delivery status lines from the head of the email
79
- # to the previous line of the beginning of the original message.
55
+ # Read error messages and delivery status lines from the head of the email to the previous
56
+ # line of the beginning of the original message.
80
57
  if readcursor == 0
81
58
  # Beginning of the bounce message or delivery status part
82
- readcursor |= Indicators[:deliverystatus] if e =~ MarkingsOf[:message]
59
+ readcursor |= Indicators[:deliverystatus] if MarkingsOf[:message].any? { |a| e.include?(a) }
83
60
  end
84
61
  next if (readcursor & Indicators[:deliverystatus]) == 0
85
62
  next if e.empty?
@@ -97,18 +74,17 @@ module Sisimai::Lhost
97
74
  # <<< 550 <******@ezweb.ne.jp>: User unknown
98
75
  v = dscontents[-1]
99
76
 
100
- if cv = e.match(/\A[<]([^ ]+[@][^ ]+)[>]\z/) ||
101
- e.match(/\A[<]([^ ]+[@][^ ]+)[>]:?(.*)\z/) ||
102
- e.match(/\A[ \t]+Recipient: [<]([^ ]+[@][^ ]+)[>]/)
77
+ if Sisimai::String.aligned(e, ['<', '@', '>']) && (e.include?('Recipient: <') || e.start_with?('<'))
78
+ # Recipient: <******@ezweb.ne.jp> OR <***@ezweb.ne.jp>: 550 user unknown ...
79
+ p1 = e.index('<') || -1
80
+ p2 = e.index('>') || -1
103
81
 
104
82
  if v['recipient']
105
83
  # There are multiple recipient addresses in the message body.
106
84
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
107
85
  v = dscontents[-1]
108
86
  end
109
-
110
- r = Sisimai::Address.s3s4(cv[1])
111
- v['recipient'] = r
87
+ v['recipient'] = Sisimai::Address.s3s4(e[p1, p2 - p1])
112
88
  recipients += 1
113
89
 
114
90
  elsif f = Sisimai::RFC1894.match(e)
@@ -120,12 +96,12 @@ module Sisimai::Lhost
120
96
  else
121
97
  # The line does not begin with a DSN field defined in RFC3464
122
98
  next if Sisimai::String.is_8bit(e)
123
- if cv = e.match(/\A[ \t]+[>]{3}[ \t]+([A-Z]{4})/)
99
+ if e.include?('>>> ')
124
100
  # >>> RCPT TO:<******@ezweb.ne.jp>
125
- v['command'] = cv[1]
101
+ v['command'] = Sisimai::SMTP::Command.find(e) || ''
126
102
  else
127
103
  # Check error message
128
- if rxmessages.any? { |messages| messages.any? { |message| e =~ message } }
104
+ if rxmessages.any? { |messages| messages.any? { |message| e.include?(message) } }
129
105
  # Check with regular expressions of each error
130
106
  v['diagnosis'] ||= ''
131
107
  v['diagnosis'] << ' ' << e
@@ -149,7 +125,7 @@ module Sisimai::Lhost
149
125
  end
150
126
  e.delete('alterrors')
151
127
  end
152
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
128
+ e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''
153
129
 
154
130
  if mhead['x-spasign'].to_s == 'NG'
155
131
  # Content-Type: text/plain; ..., X-SPASIGN: NG (spamghetti, au by EZweb)
@@ -157,17 +133,16 @@ module Sisimai::Lhost
157
133
  e['reason'] = 'filtered'
158
134
  else
159
135
  if e['command'] == 'RCPT'
160
- # set "userunknown" when the remote server rejected after RCPT
161
- # command.
136
+ # set "userunknown" when the remote server rejected after RCPT command.
162
137
  e['reason'] = 'userunknown'
163
138
  else
164
139
  # SMTP command is not RCPT
165
140
  catch :SESSION do
166
141
  ReFailures.each_key do |r|
167
- # Verify each regular expression of session errors
142
+ # Try to match with each session error message
168
143
  ReFailures[r].each do |rr|
169
- # Check each regular expression
170
- next unless e['diagnosis'] =~ rr
144
+ # Check each error message pattern
145
+ next unless e['diagnosis'].include?(rr)
171
146
  e['reason'] = r
172
147
  throw :SESSION
173
148
  end
@@ -181,7 +156,7 @@ module Sisimai::Lhost
181
156
  e['reason'] = 'userunknown'
182
157
  end
183
158
 
184
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
159
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
185
160
  end
186
161
  def description; return 'au EZweb: http://www.au.kddi.com/mobile/'; end
187
162
  end
@@ -1,13 +1,12 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::Facebook parses a bounce email which created by Facebook.
3
- # Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::Facebook parses a bounce email which created by Facebook. Methods in the module
3
+ # are called from only Sisimai::Message.
4
4
  module Facebook
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/Facebook.pm
7
6
  require 'sisimai/lhost'
8
7
 
9
8
  Indicators = Sisimai::Lhost.INDICATORS
10
- ReBackbone = %r|^Content-Disposition:[ ]inline|.freeze
9
+ Boundaries = ['Content-Disposition: inline'].freeze
11
10
  StartingOf = { message: ['This message was created automatically by Facebook.'] }.freeze
12
11
  ReFailures = {
13
12
  # http://postmaster.facebook.com/response_codes
@@ -22,21 +21,20 @@ module Sisimai::Lhost
22
21
  'RCP-P2', # The attempted recipient's preferences prevent messages from being delivered.
23
22
  'RCP-P3', # The attempted recipient's privacy settings blocked the delivery.
24
23
  ],
25
- 'blocked' => [
26
- 'POL-P1', # Your mail server's IP Address is listed on the Spamhaus PBL.
27
- 'POL-P2', # Facebook will no longer accept mail from your mail server's IP Address.
28
- ],
29
24
  'mesgtoobig' => [
30
25
  'MSG-P1', # The message exceeds Facebook's maximum allowed size.
31
26
  'INT-P2', # The message exceeds Facebook's maximum allowed size.
32
27
  ],
33
28
  'contenterror' => [
34
29
  'MSG-P2', # The message contains an attachment type that Facebook does not accept.
35
- 'MSG-P3', # The message contains multiple instances of a header field that can only be present once.
30
+ 'MSG-P3', # The message contains multiple instances of a header field that can only be present once. Please see RFC 5322, section 3.6 for more information
36
31
  'POL-P6', # The message contains a url that has been blocked by Facebook.
37
32
  'POL-P7', # The message does not comply with Facebook's abuse policies and will not be accepted.
38
33
  ],
39
34
  'securityerror' => [
35
+ 'POL-P1', # Your mail server's IP Address is listed on the Spamhaus PBL.
36
+ 'POL-P2', # Facebook will no longer accept mail from your mail server's IP Address.
37
+ 'POL-P5', # The message contains a virus.
40
38
  'POL-P7', # The message does not comply with Facebook's Domain Authentication requirements.
41
39
  ],
42
40
  'notaccept' => [
@@ -55,20 +53,17 @@ module Sisimai::Lhost
55
53
  ],
56
54
  'systemerror' => [
57
55
  'CON-T1', # Facebook's mail server currently has too many connections open to allow another one.
58
- 'RCP-T1', # The attempted recipient address is not currently available due to an internal system issue. This is a temporary condition.
59
- ],
60
- 'virusdetected' => [
61
- 'POL-P5', # The message contains a virus.
62
56
  ],
63
57
  'toomanyconn' => [
64
- 'CON-T2', # Your mail server currently has too many connections open to Facebook's mail servers.
65
58
  'CON-T3', # Your mail server has opened too many new connections to Facebook's mail servers in a short period of time.
66
59
  ],
67
60
  'suspend' => [
68
61
  'RCP-T4', # The attempted recipient address is currently deactivated. The user may or may not reactivate it.
69
62
  ],
70
63
  'undefined' => [
64
+ 'RCP-T1', # The attempted recipient address is not currently available due to an internal system issue. This is a temporary condition.
71
65
  'MSG-T1', # The number of recipients on the message exceeds Facebook's allowed maximum.
66
+ 'CON-T2', # Your mail server currently has too many connections open to Facebook's mail servers.
72
67
  'CON-T4', # Your mail server has exceeded the maximum number of recipients for its current connection.
73
68
  ],
74
69
  }.freeze
@@ -78,17 +73,16 @@ module Sisimai::Lhost
78
73
  # @param [String] mbody Message body of a bounce email
79
74
  # @return [Hash] Bounce data list and message/rfc822 part
80
75
  # @return [Nil] it failed to parse or the arguments are missing
81
- def make(mhead, mbody)
76
+ def inquire(mhead, mbody)
82
77
  return nil unless mhead['from'] == 'Facebook <mailer-daemon@mx.facebook.com>'
83
78
  return nil unless mhead['subject'] == 'Sorry, your message could not be delivered'
84
79
 
85
- require 'sisimai/rfc1894'
86
80
  fieldtable = Sisimai::RFC1894.FIELDTABLE
87
81
  permessage = {} # (Hash) Store values of each Per-Message field
88
82
 
89
83
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
90
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
91
- bodyslices = emailsteak[0].split("\n")
84
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
85
+ bodyslices = emailparts[0].split("\n")
92
86
  readslices = ['']
93
87
  readcursor = 0 # (Integer) Points the current cursor position
94
88
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
@@ -96,8 +90,8 @@ module Sisimai::Lhost
96
90
  v = nil
97
91
 
98
92
  while e = bodyslices.shift do
99
- # Read error messages and delivery status lines from the head of the email
100
- # to the previous line of the beginning of the original message.
93
+ # Read error messages and delivery status lines from the head of the email to the previous
94
+ # line of the beginning of the original message.
101
95
  readslices << e # Save the current line for the next loop
102
96
 
103
97
  if readcursor == 0
@@ -138,14 +132,14 @@ module Sisimai::Lhost
138
132
  next unless fieldtable[o[0]]
139
133
  v[fieldtable[o[0]]] = o[2]
140
134
 
141
- next unless f == 1
135
+ next unless f
142
136
  permessage[fieldtable[o[0]]] = o[2]
143
137
  end
144
138
  else
145
139
  # Continued line of the value of Diagnostic-Code field
146
140
  next unless readslices[-2].start_with?('Diagnostic-Code:')
147
- next unless cv = e.match(/\A[ \t]+(.+)\z/)
148
- v['diagnosis'] << ' ' << cv[1]
141
+ next unless e.start_with?(' ')
142
+ v['diagnosis'] << ' ' << e[e.rindex(' ') + 1, e.size]
149
143
  readslices[-1] = 'Diagnostic-Code: ' << e
150
144
  end
151
145
  end
@@ -155,14 +149,8 @@ module Sisimai::Lhost
155
149
  e['lhost'] ||= permessage['lhost']
156
150
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
157
151
 
158
- if cv = e['diagnosis'].match(/\b([A-Z]{3})[-]([A-Z])(\d)\b/)
159
- # Diagnostic-Code: smtp; 550 5.1.1 RCP-P2
160
- lhs = cv[1]
161
- rhs = cv[2]
162
- num = cv[3]
163
-
164
- fbresponse = sprintf('%s-%s%d', lhs, rhs, num)
165
- end
152
+ p0 = e['diagnosis'].index('-') || -1
153
+ fbresponse = e['diagnosis'][p0 - 3, 6] if p0 > 0
166
154
 
167
155
  catch :SESSION do
168
156
  ReFailures.each_key do |r|
@@ -188,11 +176,11 @@ module Sisimai::Lhost
188
176
  # https://groups.google.com/forum/#!topic/cdmix/eXfi4ddgYLQ
189
177
  # This block has not been tested because we have no email sample
190
178
  # including "INT-T?" error code.
191
- next unless fbresponse =~ /\AINT-T\d+\z/
179
+ next unless fbresponse.start_with?('INT-T')
192
180
  e['reason'] = 'systemerror'
193
181
  end
194
182
 
195
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
183
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
196
184
  end
197
185
  def description; return 'Facebook: https://www.facebook.com'; end
198
186
  end
@@ -1,48 +1,41 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::FML parses a bounce email which created by fml.
3
- # Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::FML parses a bounce email which created by fml. Methods in the module are called
3
+ # from only Sisimai::Message.
4
4
  module FML
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/FML.pm
7
6
  require 'sisimai/lhost'
8
7
 
9
8
  Indicators = Sisimai::Lhost.INDICATORS
10
- ReBackbone = %r|^Original[ ]mail[ ]as[ ]follows:|.freeze
9
+ Boundaries = ['Original mail as follows:'].freeze
11
10
  ErrorTitle = {
12
- 'rejected' => %r{(?>
13
- (?:Ignored[ ])*NOT[ ]MEMBER[ ]article[ ]from[ ]
14
- |reject[ ]mail[ ](?:.+:|from)[ ],
15
- |Spam[ ]mail[ ]from[ ]a[ ]spammer[ ]is[ ]rejected
16
- |You[ ].+[ ]are[ ]not[ ]member
17
- )
18
- }x,
19
- 'systemerror' => %r{(?:
20
- fml[ ]system[ ]error[ ]message
21
- |Loop[ ]Alert:[ ]
22
- |Loop[ ]Back[ ]Warning:[ ]
23
- |WARNING:[ ]UNIX[ ]FROM[ ]Loop
24
- )
25
- }x,
26
- 'securityerror' => %r/Security Alert/,
11
+ 'rejected' => [
12
+ ' are not member',
13
+ 'NOT MEMBER article from ',
14
+ 'reject mail ',
15
+ 'Spam mail from a spammer is rejected',
16
+ ],
17
+ 'systemerror' => [
18
+ 'fml system error message',
19
+ 'Loop Alert: ',
20
+ 'Loop Back Warning: ',
21
+ 'WARNING: UNIX FROM Loop',
22
+ ],
23
+ 'securityerror' => ['Security Alert'],
27
24
  }.freeze
28
25
  ErrorTable = {
29
- 'rejected' => %r{(?>
30
- (?:Ignored[ ])*NOT[ ]MEMBER[ ]article[ ]from[ ]
31
- |reject[ ](?:
32
- mail[ ]from[ ].+[@].+
33
- |since[ ].+[ ]header[ ]may[ ]cause[ ]mail[ ]loop
34
- |spammers:
35
- )
36
- |You[ ]are[ ]not[ ]a[ ]member[ ]of[ ]this[ ]mailing[ ]list
37
- )
38
- }x,
39
- 'systemerror' => %r{(?:
40
- Duplicated[ ]Message-ID
41
- |fml[ ].+[ ]has[ ]detected[ ]a[ ]loop[ ]condition[ ]so[ ]that
42
- |Loop[ ]Back[ ]Warning:
43
- )
44
- }x,
45
- 'securityerror' => %r/Security alert:/,
26
+ 'rejected' => [
27
+ ' header may cause mail loop',
28
+ 'NOT MEMBER article from ',
29
+ 'reject mail from ',
30
+ 'reject spammers:',
31
+ 'You are not a member of this mailing list',
32
+ ],
33
+ 'systemerror' => [
34
+ ' has detected a loop condition so that',
35
+ 'Duplicated Message-ID',
36
+ 'Loop Back Warning:',
37
+ ],
38
+ 'securityerror' => ['Security alert:'],
46
39
  }.freeze
47
40
 
48
41
  # Parse bounce messages from fml mailling list server/manager
@@ -51,34 +44,36 @@ module Sisimai::Lhost
51
44
  # @return [Hash] Bounce data list and message/rfc822 part
52
45
  # @return [Nil] it failed to parse or the arguments are missing
53
46
  # @since v4.22.3
54
- def make(mhead, mbody)
47
+ def inquire(mhead, mbody)
55
48
  return nil unless mhead['x-mlserver']
56
- return nil unless mhead['from'] =~ /.+[-]admin[@].+/
57
- return nil unless mhead['message-id'] =~ /\A[<]\d+[.]FML.+[@].+[>]\z/
49
+ return nil unless mhead['from'].include?('-admin@')
50
+ return nil unless mhead['message-id'].index('.FML') > 1
58
51
 
59
52
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
60
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
61
- bodyslices = emailsteak[0].split("\n")
53
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
54
+ bodyslices = emailparts[0].split("\n")
62
55
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
63
56
  v = nil
64
57
 
65
58
  while e = bodyslices.shift do
66
- # Read error messages and delivery status lines from the head of the email
67
- # to the previous line of the beginning of the original message.
59
+ # Read error messages and delivery status lines from the head of the email to the previous
60
+ # line of the beginning of the original message.
68
61
  next if e.empty?
69
62
 
70
63
  # Duplicated Message-ID in <2ndml@example.com>.
71
64
  # Original mail as follows:
72
65
  v = dscontents[-1]
73
66
 
74
- if cv = e.match(/[<]([^ ]+?[@][^ ]+?)[>][.]\z/)
75
- # Duplicated Message-ID in <2ndml@example.com>.
67
+ p1 = e.index('<') || -1
68
+ p2 = e.rindex('>') || -1
69
+ if p1 > 0 && p2 > 0
70
+ # You are not a member of this mailing list <neko-nyaan@example.org>.
76
71
  if v['recipient']
77
72
  # There are multiple recipient addresses in the message body.
78
73
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
79
74
  v = dscontents[-1]
80
75
  end
81
- v['recipient'] = cv[1]
76
+ v['recipient'] = e[p1 + 1, p2 - p1 - 1]
82
77
  v['diagnosis'] = e
83
78
  recipients += 1
84
79
  else
@@ -94,7 +89,7 @@ module Sisimai::Lhost
94
89
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
95
90
  ErrorTable.each_key do |f|
96
91
  # Try to match with error messages defined in ErrorTable
97
- next unless e['diagnosis'] =~ ErrorTable[f]
92
+ next unless ErrorTable[f].any? { |a| e['diagnosis'].include?(a) }
98
93
  e['reason'] = f
99
94
  break
100
95
  end
@@ -110,7 +105,7 @@ module Sisimai::Lhost
110
105
  end
111
106
  end
112
107
 
113
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
108
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
114
109
  end
115
110
  def description; return 'fml mailing list server/manager'; end
116
111
  end
@@ -1,18 +1,16 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::Gmail parses a bounce email which created by Gmail.
3
- # Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::Gmail parses a bounce email which created by Gmail. Methods in the module are
3
+ # called from only Sisimai::Message.
4
4
  module Gmail
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/Gmail.pm
7
6
  require 'sisimai/lhost'
8
7
 
9
8
  Indicators = Sisimai::Lhost.INDICATORS
10
- ReBackbone = %r/^[ ]*-----[ ](?:Original[ ]message|Message[ ]header[ ]follows)[ ]-----/.freeze
9
+ Boundaries = ['----- Original message -----', '----- Message header follows -----'].freeze
11
10
  StartingOf = {
12
11
  message: ['Delivery to the following recipient'],
13
12
  error: ['The error that the other server returned was:'],
14
13
  }.freeze
15
- MarkingsOf = { start: %r/Technical details of (?:permanent|temporary) failure:/ }.freeze
16
14
  MessagesOf = {
17
15
  'expired' => [
18
16
  'DNS Error: Could not contact DNS servers',
@@ -103,7 +101,7 @@ module Sisimai::Lhost
103
101
  # @param [String] mbody Message body of a bounce email
104
102
  # @return [Hash] Bounce data list and message/rfc822 part
105
103
  # @return [Nil] it failed to parse or the arguments are missing
106
- def make(mhead, mbody)
104
+ def inquire(mhead, mbody)
107
105
  # From: Mail Delivery Subsystem <mailer-daemon@googlemail.com>
108
106
  # Received: from vw-in-f109.1e100.net [74.125.113.109] by ...
109
107
  #
@@ -154,16 +152,15 @@ module Sisimai::Lhost
154
152
  return nil unless mhead['subject'].start_with?('Delivery Status Notification')
155
153
 
156
154
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
157
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
158
- bodyslices = emailsteak[0].split("\n")
155
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
156
+ bodyslices = emailparts[0].split("\n")
159
157
  readcursor = 0 # (Integer) Points the current cursor position
160
158
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
161
- statecode0 = 0 # (Integer) The value of (state *) in the error message
162
159
  v = nil
163
160
 
164
161
  while e = bodyslices.shift do
165
- # Read error messages and delivery status lines from the head of the email
166
- # to the previous line of the beginning of the original message.
162
+ # Read error messages and delivery status lines from the head of the email to the previous
163
+ # line of the beginning of the original message.
167
164
  if readcursor == 0
168
165
  # Beginning of the bounce message or delivery status part
169
166
  readcursor |= Indicators[:deliverystatus] if e.start_with?(StartingOf[:message][0])
@@ -188,7 +185,7 @@ module Sisimai::Lhost
188
185
  #
189
186
  v = dscontents[-1]
190
187
 
191
- if cv = e.match(/\A[ \t]+([^ ]+[@][^ ]+)\z/)
188
+ if e.start_with?(' ') && e.include?('@')
192
189
  # kijitora@example.jp: 550 5.2.2 <kijitora@example>... Mailbox Full
193
190
  if v['recipient']
194
191
  # There are multiple recipient addresses in the message body.
@@ -196,8 +193,8 @@ module Sisimai::Lhost
196
193
  v = dscontents[-1]
197
194
  end
198
195
 
199
- r = Sisimai::Address.s3s4(cv[1])
200
- next unless Sisimai::RFC5322.is_emailaddress(r)
196
+ r = Sisimai::Address.s3s4(e[e.rindex(' ') + 1, e.size])
197
+ next unless Sisimai::Address.is_emailaddress(r)
201
198
  v['recipient'] = r
202
199
  recipients += 1
203
200
  else
@@ -212,22 +209,24 @@ module Sisimai::Lhost
212
209
 
213
210
  unless e['rhost']
214
211
  # Get the value of remote host
215
- if cv = e['diagnosis'].match(/[ \t]+by[ \t]+([^ ]+)[.][ \t]+\[(\d+[.]\d+[.]\d+[.]\d+)\][.]/)
216
- # Google tried to deliver your message, but it was rejected by
217
- # the server for the recipient domain example.jp by mx.example.jp. [192.0.2.153].
218
- hostname = cv[1]
219
- ipv4addr = cv[2]
220
- e['rhost'] = if hostname =~ /[-0-9a-zA-Z]+[.][a-zA-Z]+\z/
221
- # Maybe valid hostname
222
- hostname.downcase
223
- else
224
- # Use IP address instead
225
- ipv4addr
226
- end
212
+ if Sisimai::String.aligned(e['diagnosis'], [' by ', '. [', ']. '])
213
+ # Google tried to deliver your message, but it was rejected by the server for the recipient
214
+ # domain example.jp by mx.example.jp. [192.0.2.153].
215
+ p1 = e['diagnosis'].rindex(' by ') || -1
216
+ p2 = e['diagnosis'].rindex('. [' ) || -1
217
+ hostname = e['diagnosis'][p1 + 4, p2 - p1 - 4]
218
+ ipv4addr = e['diagnosis'][p2 + 3, e['diagnosis'].rindex(']. ') - p2 - 3]
219
+ lastchar = hostname[-1, 1].upcase.ord
220
+
221
+ e['rhost'] = hostname if lastchar > 64 && lastchar < 91
222
+ e['rhost'] ||= ipv4addr
227
223
  end
228
224
  end
229
225
 
230
- if cv = e['diagnosis'].match(/[(]state[ ](\d+)[)][.]/) then statecode0 = cv[1] end
226
+ p1 = e['diagnosis'].rindex(' ') || -1
227
+ p2 = e['diagnosis'].rindex(')') || -1
228
+ statecode0 = e['diagnosis'][p1 + 1, p2 - p1 - 1] || 0
229
+
231
230
  if StateTable[statecode0]
232
231
  # (state *)
233
232
  e['reason'] = StateTable[statecode0]['reason']
@@ -245,10 +244,11 @@ module Sisimai::Lhost
245
244
 
246
245
  # Set pseudo status code
247
246
  e['status'] = Sisimai::SMTP::Status.find(e['diagnosis']) || ''
248
- e['reason'] = Sisimai::SMTP::Status.name(e['status']).to_s if e['status'] =~ /\A[45][.][1-7][.][1-9]\z/
247
+ next if e['status'].size == 0 || e['status'].include?('.0')
248
+ e['reason'] = Sisimai::SMTP::Status.name(e['status']).to_s || ''
249
249
  end
250
250
 
251
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
251
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
252
252
  end
253
253
  def description; return 'Gmail: https://mail.google.com'; end
254
254
  end