sisimai 4.22.3 → 4.22.4

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sisimai might be problematic. Click here for more details.

Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/Benchmarks.mk +54 -0
  4. data/ChangeLog.md +23 -2
  5. data/Developers.mk +42 -35
  6. data/Makefile +10 -0
  7. data/README-JA.md +13 -13
  8. data/README.md +14 -14
  9. data/lib/sisimai.rb +12 -18
  10. data/lib/sisimai/address.rb +64 -82
  11. data/lib/sisimai/arf.rb +27 -42
  12. data/lib/sisimai/bite/email.rb +2 -4
  13. data/lib/sisimai/bite/email/activehunter.rb +12 -17
  14. data/lib/sisimai/bite/email/amazonses.rb +30 -48
  15. data/lib/sisimai/bite/email/amazonworkmail.rb +20 -27
  16. data/lib/sisimai/bite/email/aol.rb +27 -35
  17. data/lib/sisimai/bite/email/apachejames.rb +17 -28
  18. data/lib/sisimai/bite/email/bigfoot.rb +20 -33
  19. data/lib/sisimai/bite/email/biglobe.rb +15 -24
  20. data/lib/sisimai/bite/email/courier.rb +37 -61
  21. data/lib/sisimai/bite/email/domino.rb +19 -28
  22. data/lib/sisimai/bite/email/einsundeins.rb +20 -34
  23. data/lib/sisimai/bite/email/exchange2003.rb +25 -43
  24. data/lib/sisimai/bite/email/exchange2007.rb +15 -23
  25. data/lib/sisimai/bite/email/exim.rb +101 -120
  26. data/lib/sisimai/bite/email/ezweb.rb +28 -44
  27. data/lib/sisimai/bite/email/facebook.rb +26 -37
  28. data/lib/sisimai/bite/email/fml.rb +11 -20
  29. data/lib/sisimai/bite/email/gmx.rb +17 -27
  30. data/lib/sisimai/bite/email/google.rb +19 -29
  31. data/lib/sisimai/bite/email/gsuite.rb +39 -48
  32. data/lib/sisimai/bite/email/imailserver.rb +25 -39
  33. data/lib/sisimai/bite/email/interscanmss.rb +19 -26
  34. data/lib/sisimai/bite/email/kddi.rb +20 -33
  35. data/lib/sisimai/bite/email/mailfoundry.rb +14 -24
  36. data/lib/sisimai/bite/email/mailmarshalsmtp.rb +15 -24
  37. data/lib/sisimai/bite/email/mailru.rb +40 -59
  38. data/lib/sisimai/bite/email/mcafee.rb +21 -35
  39. data/lib/sisimai/bite/email/messagelabs.rb +23 -38
  40. data/lib/sisimai/bite/email/messagingserver.rb +15 -27
  41. data/lib/sisimai/bite/email/mfilter.rb +19 -28
  42. data/lib/sisimai/bite/email/mxlogic.rb +31 -49
  43. data/lib/sisimai/bite/email/notes.rb +16 -24
  44. data/lib/sisimai/bite/email/office365.rb +29 -38
  45. data/lib/sisimai/bite/email/opensmtpd.rb +50 -67
  46. data/lib/sisimai/bite/email/outlook.rb +24 -36
  47. data/lib/sisimai/bite/email/postfix.rb +33 -42
  48. data/lib/sisimai/bite/email/qmail.rb +44 -59
  49. data/lib/sisimai/bite/email/receivingses.rb +28 -36
  50. data/lib/sisimai/bite/email/sendgrid.rb +28 -37
  51. data/lib/sisimai/bite/email/sendmail.rb +35 -51
  52. data/lib/sisimai/bite/email/surfcontrol.rb +17 -25
  53. data/lib/sisimai/bite/email/userdefined.rb +17 -28
  54. data/lib/sisimai/bite/email/v5sendmail.rb +32 -41
  55. data/lib/sisimai/bite/email/verizon.rb +31 -56
  56. data/lib/sisimai/bite/email/x1.rb +11 -18
  57. data/lib/sisimai/bite/email/x2.rb +11 -23
  58. data/lib/sisimai/bite/email/x3.rb +10 -19
  59. data/lib/sisimai/bite/email/x4.rb +46 -65
  60. data/lib/sisimai/bite/email/x5.rb +26 -37
  61. data/lib/sisimai/bite/email/yahoo.rb +11 -19
  62. data/lib/sisimai/bite/email/yandex.rb +19 -30
  63. data/lib/sisimai/bite/email/zoho.rb +21 -30
  64. data/lib/sisimai/bite/json.rb +1 -2
  65. data/lib/sisimai/bite/json/amazonses.rb +20 -25
  66. data/lib/sisimai/bite/json/sendgrid.rb +1 -1
  67. data/lib/sisimai/data.rb +36 -55
  68. data/lib/sisimai/data/json.rb +3 -3
  69. data/lib/sisimai/data/yaml.rb +1 -1
  70. data/lib/sisimai/datetime.rb +5 -21
  71. data/lib/sisimai/mail.rb +4 -6
  72. data/lib/sisimai/mail/maildir.rb +1 -1
  73. data/lib/sisimai/mda.rb +41 -44
  74. data/lib/sisimai/message.rb +2 -3
  75. data/lib/sisimai/message/email.rb +42 -52
  76. data/lib/sisimai/message/json.rb +7 -7
  77. data/lib/sisimai/mime.rb +25 -23
  78. data/lib/sisimai/order/email.rb +2 -2
  79. data/lib/sisimai/order/json.rb +2 -7
  80. data/lib/sisimai/reason.rb +41 -46
  81. data/lib/sisimai/reason/blocked.rb +60 -71
  82. data/lib/sisimai/reason/contenterror.rb +4 -8
  83. data/lib/sisimai/reason/delivered.rb +1 -3
  84. data/lib/sisimai/reason/exceedlimit.rb +10 -20
  85. data/lib/sisimai/reason/expired.rb +5 -9
  86. data/lib/sisimai/reason/feedback.rb +1 -3
  87. data/lib/sisimai/reason/filtered.rb +19 -38
  88. data/lib/sisimai/reason/hasmoved.rb +5 -8
  89. data/lib/sisimai/reason/hostunknown.rb +11 -18
  90. data/lib/sisimai/reason/mailboxfull.rb +14 -24
  91. data/lib/sisimai/reason/mailererror.rb +3 -5
  92. data/lib/sisimai/reason/mesgtoobig.rb +15 -25
  93. data/lib/sisimai/reason/networkerror.rb +8 -10
  94. data/lib/sisimai/reason/norelaying.rb +9 -14
  95. data/lib/sisimai/reason/notaccept.rb +9 -21
  96. data/lib/sisimai/reason/onhold.rb +3 -8
  97. data/lib/sisimai/reason/policyviolation.rb +8 -10
  98. data/lib/sisimai/reason/rejected.rb +36 -49
  99. data/lib/sisimai/reason/securityerror.rb +11 -13
  100. data/lib/sisimai/reason/spamdetected.rb +23 -37
  101. data/lib/sisimai/reason/suspend.rb +9 -10
  102. data/lib/sisimai/reason/syntaxerror.rb +3 -4
  103. data/lib/sisimai/reason/systemerror.rb +7 -9
  104. data/lib/sisimai/reason/systemfull.rb +2 -4
  105. data/lib/sisimai/reason/toomanyconn.rb +17 -30
  106. data/lib/sisimai/reason/undefined.rb +1 -3
  107. data/lib/sisimai/reason/userunknown.rb +28 -38
  108. data/lib/sisimai/reason/vacation.rb +4 -6
  109. data/lib/sisimai/reason/virusdetected.rb +4 -6
  110. data/lib/sisimai/rfc2606.rb +1 -2
  111. data/lib/sisimai/rfc3464.rb +87 -101
  112. data/lib/sisimai/rfc3834.rb +29 -39
  113. data/lib/sisimai/rfc5322.rb +17 -24
  114. data/lib/sisimai/rhost.rb +10 -7
  115. data/lib/sisimai/rhost/exchangeonline.rb +124 -255
  116. data/lib/sisimai/rhost/franceptt.rb +2 -2
  117. data/lib/sisimai/rhost/godaddy.rb +12 -25
  118. data/lib/sisimai/rhost/googleapps.rb +82 -183
  119. data/lib/sisimai/smtp.rb +4 -4
  120. data/lib/sisimai/smtp/error.rb +8 -8
  121. data/lib/sisimai/smtp/reply.rb +1 -1
  122. data/lib/sisimai/smtp/status.rb +1 -0
  123. data/lib/sisimai/string.rb +5 -7
  124. data/lib/sisimai/version.rb +1 -1
  125. data/set-of-emails/README.md +1 -1
  126. data/set-of-emails/maildir/bsd/README.md +50 -50
  127. data/sisimai-java.gemspec +1 -1
  128. data/sisimai.gemspec +1 -1
  129. metadata +5 -5
  130. data/lib/sisimai/skeleton.rb +0 -43
@@ -6,26 +6,19 @@ module Sisimai::Bite::Email
6
6
  # Imported from p5-Sisimail/lib/Sisimai/Bite/Email/ApacheJames.pm
7
7
  require 'sisimai/bite/email'
8
8
 
9
- Re0 = {
10
- :'subject' => %r/\A\[BOUNCE\]\z/,
11
- :'received' => %r/JAMES SMTP Server/,
12
- :'message-id' => %r/\d+[.]JavaMail[.].+[@]/,
13
- }.freeze
14
- Re1 = {
9
+ Indicators = Sisimai::Bite::Email.INDICATORS
10
+ StartingOf = {
15
11
  # apache-james-2.3.2/src/java/org/apache/james/transport/mailets/
16
12
  # AbstractNotify.java|124: out.println("Error message below:");
17
13
  # AbstractNotify.java|128: out.println("Message details:");
18
- :begin => %r/\AContent-Disposition:[ ]inline/,
19
- :error => %r/\AError message below:\z/,
20
- :rfc822 => %r|\AContent-Type: message/rfc822|,
21
- :endof => %r/\A__END_OF_EMAIL_MESSAGE__\z/,
14
+ message: ['Content-Disposition: inline'],
15
+ rfc822: ['Content-Type: message/rfc822'],
16
+ error: ['Error message below:'],
22
17
  }.freeze
23
- Indicators = Sisimai::Bite::Email.INDICATORS
24
18
 
25
19
  def description; return 'Java Apache Mail Enterprise Server'; end
26
20
  def smtpagent; return Sisimai::Bite.smtpagent(self); end
27
21
  def headerlist; return []; end
28
- def pattern; return Re0; end
29
22
 
30
23
  # Parse bounce messages from Apache James
31
24
  # @param [Hash] mhead Message headers of a bounce email
@@ -43,9 +36,9 @@ module Sisimai::Bite::Email
43
36
  return nil unless mbody
44
37
 
45
38
  match = 0
46
- match += 1 if mhead['subject'] =~ Re0[:subject]
47
- match += 1 if mhead['message-id'] && mhead['message-id'] =~ Re0[:'message-id']
48
- match += 1 if mhead['received'].find { |a| a =~ Re0[:received] }
39
+ match += 1 if mhead['subject'] == '[BOUNCE]'
40
+ match += 1 if mhead['message-id'].to_s.include?('.JavaMail.')
41
+ match += 1 if mhead['received'].find { |a| a.include?('JAMES SMTP Server') }
49
42
  return if match.zero?
50
43
 
51
44
  dscontents = [Sisimai::Bite.DELIVERYSTATUS]
@@ -59,10 +52,10 @@ module Sisimai::Bite::Email
59
52
  gotmessage = -1 # (Integer) Flag for error message
60
53
  v = nil
61
54
 
62
- hasdivided.each do |e|
55
+ while e = hasdivided.shift do
63
56
  if readcursor.zero?
64
57
  # Beginning of the bounce message or delivery status part
65
- if e =~ Re1[:begin]
58
+ if e.start_with?(StartingOf[:message][0])
66
59
  readcursor |= Indicators[:deliverystatus]
67
60
  next
68
61
  end
@@ -70,7 +63,7 @@ module Sisimai::Bite::Email
70
63
 
71
64
  if (readcursor & Indicators[:'message-rfc822']).zero?
72
65
  # Beginning of the original message part
73
- if e =~ Re1[:rfc822]
66
+ if e.start_with?(StartingOf[:rfc822][0])
74
67
  readcursor |= Indicators[:'message-rfc822']
75
68
  next
76
69
  end
@@ -84,7 +77,6 @@ module Sisimai::Bite::Email
84
77
  next
85
78
  end
86
79
  rfc822list << e
87
-
88
80
  else
89
81
  # Before "message/rfc822"
90
82
  next if (readcursor & Indicators[:deliverystatus]).zero?
@@ -118,12 +110,11 @@ module Sisimai::Bite::Email
118
110
  elsif cv = e.match(/\A[ ][ ]Subject:[ ](.+)\z/)
119
111
  # Subject: Nyaaan
120
112
  subjecttxt = cv[1]
121
-
122
113
  else
123
114
  next if gotmessage == 1
124
115
  if v['diagnosis']
125
116
  # Get an error message text
126
- if e =~ /\AMessage[ ]details:\z/
117
+ if e.start_with?('Message details:')
127
118
  # Message details:
128
119
  # Subject: nyaan
129
120
  # ...
@@ -132,27 +123,25 @@ module Sisimai::Bite::Email
132
123
  # Append error message text like the followng:
133
124
  # Error message below:
134
125
  # 550 - Requested action not taken: no such user here
135
- v['diagnosis'] ||= ''
136
- v['diagnosis'] += ' ' + e
126
+ v['diagnosis'] << ' ' << e
137
127
  end
138
-
139
128
  else
140
129
  # Error message below:
141
130
  # 550 - Requested action not taken: no such user here
142
- v['diagnosis'] = e if e =~ Re1[:error]
131
+ v['diagnosis'] = e if e == StartingOf[:error][0]
143
132
  end
144
133
  end
145
134
  end
146
135
  end
147
136
  return nil if recipients.zero?
148
- require 'sisimai/string'
149
137
 
150
- unless rfc822list.find { |a| a =~ /^Subject:/ }
138
+ unless rfc822list.find { |a| a.start_with?('Subject:') }
151
139
  # Set the value of subjecttxt as a Subject if there is no original
152
140
  # message in the bounce mail.
153
- rfc822list << sprintf('Subject: %s', subjecttxt)
141
+ rfc822list << ('Subject: ' << subjecttxt)
154
142
  end
155
143
 
144
+ require 'sisimai/string'
156
145
  dscontents.map do |e|
157
146
  e['agent'] = self.smtpagent
158
147
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'] || diagnostic)
@@ -6,22 +6,13 @@ module Sisimai::Bite::Email
6
6
  # Imported from p5-Sisimail/lib/Sisimai/Bite/Email/Bigfoot.pm
7
7
  require 'sisimai/bite/email'
8
8
 
9
- Re0 = {
10
- :from => %r/[@]bigfoot[.]com[>]/,
11
- :subject => %r/\AReturned mail: /,
12
- :received => %r/\w+[.]bigfoot[.]com\b/,
13
- }.freeze
14
- Re1 = {
15
- :begin => %r/\A[ \t]+[-]+[ \t]*Transcript of session follows/,
16
- :rfc822 => %r|\AContent-Type: message/partial|,
17
- :endof => %r/\A__END_OF_EMAIL_MESSAGE__\z/,
18
- }.freeze
19
9
  Indicators = Sisimai::Bite::Email.INDICATORS
10
+ StartingOf = { rfc822: ['Content-Type: message/partial'] }.freeze
11
+ MarkingsOf = { message: %r/\A[ \t]+[-]+[ \t]*Transcript of session follows/ }.freeze
20
12
 
21
13
  def description; return 'Bigfoot: http://www.bigfoot.com'; end
22
14
  def smtpagent; return Sisimai::Bite.smtpagent(self); end
23
15
  def headerlist; return []; end
24
- def pattern; return Re0; end
25
16
 
26
17
  # Parse bounce messages from Bigfoot
27
18
  # @param [Hash] mhead Message headers of a bounce email
@@ -37,10 +28,11 @@ module Sisimai::Bite::Email
37
28
  def scan(mhead, mbody)
38
29
  return nil unless mhead
39
30
  return nil unless mbody
40
-
31
+
32
+ # :subject => %r/\AReturned mail: /,
41
33
  match = 0
42
- match += 1 if mhead['from'] =~ Re0[:from]
43
- match += 1 if mhead['received'].find { |a| a =~ Re0[:received] }
34
+ match += 1 if mhead['from'].include?('@bigfoot.com>')
35
+ match += 1 if mhead['received'].find { |a| a.include?('.bigfoot.com') }
44
36
  return nil if match.zero?
45
37
 
46
38
  require 'sisimai/address'
@@ -60,14 +52,14 @@ module Sisimai::Bite::Email
60
52
  }
61
53
  v = nil
62
54
 
63
- hasdivided.each do |e|
55
+ while e = hasdivided.shift do
64
56
  # Save the current line for the next loop
65
57
  havepassed << e
66
58
  p = havepassed[-2]
67
59
 
68
60
  if readcursor.zero?
69
61
  # Beginning of the bounce message or delivery status part
70
- if e =~ Re1[:begin]
62
+ if e =~ MarkingsOf[:message]
71
63
  readcursor |= Indicators[:deliverystatus]
72
64
  next
73
65
  end
@@ -75,7 +67,7 @@ module Sisimai::Bite::Email
75
67
 
76
68
  if (readcursor & Indicators[:'message-rfc822']).zero?
77
69
  # Beginning of the original message part
78
- if e =~ Re1[:rfc822]
70
+ if e.start_with?(StartingOf[:rfc822][0])
79
71
  readcursor |= Indicators[:'message-rfc822']
80
72
  next
81
73
  end
@@ -89,7 +81,6 @@ module Sisimai::Bite::Email
89
81
  next
90
82
  end
91
83
  rfc822list << e
92
-
93
84
  else
94
85
  # Before "message/rfc822"
95
86
  next if (readcursor & Indicators[:deliverystatus]).zero?
@@ -104,7 +95,7 @@ module Sisimai::Bite::Email
104
95
  # Last-Attempt-Date: Sun, 28 Dec 2014 18:17:16 -0800
105
96
  v = dscontents[-1]
106
97
 
107
- if cv = e.match(/\A[Ff]inal-[Rr]ecipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/)
98
+ if cv = e.match(/\AFinal-Recipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/)
108
99
  # Final-Recipient: RFC822; <destinaion@example.net>
109
100
  if v['recipient']
110
101
  # There are multiple recipient addresses in the message body.
@@ -114,33 +105,30 @@ module Sisimai::Bite::Email
114
105
  v['recipient'] = Sisimai::Address.s3s4(cv[1])
115
106
  recipients += 1
116
107
 
117
- elsif cv = e.match(/\A[Aa]ction:[ ]*(.+)\z/)
108
+ elsif cv = e.match(/\AAction:[ ]*(.+)\z/)
118
109
  # Action: failed
119
110
  v['action'] = cv[1].downcase
120
111
 
121
- elsif cv = e.match(/\A[Ss]tatus:[ ]*(\d[.]\d+[.]\d+)/)
112
+ elsif cv = e.match(/\AStatus:[ ]*(\d[.]\d+[.]\d+)/)
122
113
  # Status: 5.7.1
123
114
  v['status'] = cv[1]
124
115
 
125
- elsif cv = e.match(/\A[Rr]emote-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
116
+ elsif cv = e.match(/\ARemote-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
126
117
  # Remote-MTA: DNS; p01c11m075.mx.example.net
127
118
  v['rhost'] = cv[1].downcase
128
-
129
119
  else
130
120
  # Get error message
131
- if cv = e.match(/\A[Dd]iagnostic-[Cc]ode:[ ]*(.+?);[ ]*(.+)\z/)
121
+ if cv = e.match(/\ADiagnostic-Code:[ ]*(.+?);[ ]*(.+)\z/)
132
122
  # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
133
123
  v['spec'] = cv[1].upcase
134
124
  v['diagnosis'] = cv[2]
135
125
 
136
- elsif p =~ /\A[Dd]iagnostic-[Cc]ode:[ ]*/ && cv = e.match(/\A[ \t]+(.+)\z/)
126
+ elsif p.start_with?('Diagnostic-Code:') && cv = e.match(/\A[ \t]+(.+)\z/)
137
127
  # Continued line of the value of Diagnostic-Code header
138
- v['diagnosis'] ||= ''
139
- v['diagnosis'] += ' ' + cv[1]
140
- havepassed[-1] = 'Diagnostic-Code: ' + e
128
+ v['diagnosis'] << ' ' << cv[1]
129
+ havepassed[-1] = 'Diagnostic-Code: ' << e
141
130
  end
142
131
  end
143
-
144
132
  else
145
133
  # ----- Transcript of session follows -----
146
134
  # >>> RCPT TO:<destinaion@example.net>
@@ -152,18 +140,17 @@ module Sisimai::Bite::Email
152
140
  # Reporting-MTA: dns; litemail57.bigfoot.com
153
141
  # Arrival-Date: Sun, 28 Dec 2014 18:17:16 -0800
154
142
  #
155
- if cv = e.match(/\A[Rr]eporting-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
143
+ if cv = e.match(/\AReporting-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
156
144
  # Reporting-MTA: dns; mx.example.jp
157
145
  next if connheader['lhost'].size > 0
158
146
  connheader['lhost'] = cv[1].downcase
159
147
  connvalues += 1
160
148
 
161
- elsif cv = e.match(/\A[Aa]rrival-[Dd]ate:[ ]*(.+)\z/)
149
+ elsif cv = e.match(/\AArrival-Date:[ ]*(.+)\z/)
162
150
  # Arrival-Date: Wed, 29 Apr 2009 16:03:18 +0900
163
151
  next if connheader['date'].size > 0
164
152
  connheader['date'] = cv[1]
165
153
  connvalues += 1
166
-
167
154
  else
168
155
  # ----- Transcript of session follows -----
169
156
  # >>> RCPT TO:<destinaion@example.net>
@@ -183,8 +170,8 @@ module Sisimai::Bite::Email
183
170
  end
184
171
  end
185
172
  return nil if recipients.zero?
186
- require 'sisimai/string'
187
173
 
174
+ require 'sisimai/string'
188
175
  dscontents.map do |e|
189
176
  # Set default values if each value is empty.
190
177
  connheader.each_key { |a| e[a] ||= connheader[a] || '' }
@@ -6,26 +6,20 @@ module Sisimai::Bite::Email
6
6
  # Imported from p5-Sisimail/lib/Sisimai/Bite/Email/Biglobe.pm
7
7
  require 'sisimai/bite/email'
8
8
 
9
- Re0 = {
10
- :subject => %r/\AReturned mail:/,
11
- :from => %r/postmaster[@](?:biglobe|inacatv|tmtv|ttv)[.]ne[.]jp/,
12
- }.freeze
13
- Re1 = {
14
- :begin => %r/\A ----- The following addresses had delivery problems -----\z/,
15
- :error => %r/\A ----- Non-delivered information -----\z/,
16
- :rfc822 => %r|\AContent-Type: message/rfc822\z|,
17
- :endof => %r/\A__END_OF_EMAIL_MESSAGE__\z/,
9
+ Indicators = Sisimai::Bite::Email.INDICATORS
10
+ StartingOf = {
11
+ message: [' ----- The following addresses had delivery problems -----'],
12
+ error: [' ----- Non-delivered information -----'],
13
+ rfc822: ['Content-Type: message/rfc822'],
18
14
  }.freeze
19
- ReFailure = {
15
+ ReFailures = {
20
16
  filtered: %r/Mail Delivery Failed[.]+ User unknown/,
21
17
  mailboxfull: %r/The number of messages in recipient's mailbox exceeded the local limit[.]/,
22
18
  }.freeze
23
- Indicators = Sisimai::Bite::Email.INDICATORS
24
19
 
25
20
  def description; return 'BIGLOBE: http://www.biglobe.ne.jp'; end
26
21
  def smtpagent; return Sisimai::Bite.smtpagent(self); end
27
22
  def headerlist; return []; end
28
- def pattern; return Re0; end
29
23
 
30
24
  # Parse bounce messages from Biglobe
31
25
  # @param [Hash] mhead Message headers of a bounce email
@@ -41,8 +35,8 @@ module Sisimai::Bite::Email
41
35
  def scan(mhead, mbody)
42
36
  return nil unless mhead
43
37
  return nil unless mbody
44
- return nil unless mhead['from'] =~ Re0[:from]
45
- return nil unless mhead['subject'] =~ Re0[:subject]
38
+ return nil unless mhead['from'] =~ /postmaster[@](?:biglobe|inacatv|tmtv|ttv)[.]ne[.]jp/
39
+ return nil unless mhead['subject'].start_with?('Returned mail:')
46
40
 
47
41
  require 'sisimai/address'
48
42
  dscontents = [Sisimai::Bite.DELIVERYSTATUS]
@@ -53,10 +47,10 @@ module Sisimai::Bite::Email
53
47
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
54
48
  v = nil
55
49
 
56
- hasdivided.each do |e|
50
+ while e = hasdivided.shift do
57
51
  if readcursor.zero?
58
52
  # Beginning of the bounce message or delivery status part
59
- if e =~ Re1[:begin]
53
+ if e == StartingOf[:message][0]
60
54
  readcursor |= Indicators[:deliverystatus]
61
55
  next
62
56
  end
@@ -64,7 +58,7 @@ module Sisimai::Bite::Email
64
58
 
65
59
  if (readcursor & Indicators[:'message-rfc822']).zero?
66
60
  # Beginning of the original message part
67
- if e =~ Re1[:rfc822]
61
+ if e == StartingOf[:rfc822][0]
68
62
  readcursor |= Indicators[:'message-rfc822']
69
63
  next
70
64
  end
@@ -78,7 +72,6 @@ module Sisimai::Bite::Email
78
72
  next
79
73
  end
80
74
  rfc822list << e
81
-
82
75
  else
83
76
  # Before "message/rfc822"
84
77
  next if (readcursor & Indicators[:deliverystatus]).zero?
@@ -114,25 +107,23 @@ module Sisimai::Bite::Email
114
107
  v['recipient'] = r
115
108
  recipients += 1
116
109
  end
117
-
118
110
  else
119
111
  next if e =~ /\A[^\w]/
120
112
  v['diagnosis'] ||= ''
121
- v['diagnosis'] += e + ' '
113
+ v['diagnosis'] << e + ' '
122
114
  end
123
115
  end
124
116
  end
125
-
126
117
  return nil if recipients.zero?
127
- require 'sisimai/string'
128
118
 
119
+ require 'sisimai/string'
129
120
  dscontents.map do |e|
130
121
  e['agent'] = self.smtpagent
131
122
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
132
123
 
133
- ReFailure.each_key do |r|
124
+ ReFailures.each_key do |r|
134
125
  # Verify each regular expression of session errors
135
- next unless e['diagnosis'] =~ ReFailure[r]
126
+ next unless e['diagnosis'] =~ ReFailures[r]
136
127
  e['reason'] = r.to_s
137
128
  break
138
129
  end
@@ -7,46 +7,28 @@ module Sisimai::Bite::Email
7
7
  require 'sisimai/bite/email'
8
8
 
9
9
  # http://www.courier-mta.org/courierdsn.html
10
- # courier/module.dsn/dsn*.txt
11
- Re0 = {
12
- :'from' => %r/Courier mail server at /,
13
- :'subject' => %r{(?:
14
- NOTICE:[ ]mail[ ]delivery[ ]status[.]
15
- |WARNING:[ ]delayed[ ]mail[.]
16
- )
17
- }x,
18
- :'message-id' => %r/\A[<]courier[.][0-9A-F]+[.]/,
19
- }.freeze
20
- Re1 = {
21
- :begin => %r{(?:
22
- DELAYS[ ]IN[ ]DELIVERING[ ]YOUR[ ]MESSAGE
23
- |UNDELIVERABLE[ ]MAIL
24
- )
25
- }x,
26
- :rfc822 => %r{\AContent-Type:[ ]*(?:
27
- message/rfc822
28
- |text/rfc822-headers
29
- )\z
30
- }x,
31
- :endof => %r/\A__END_OF_EMAIL_MESSAGE__\z/,
10
+ Indicators = Sisimai::Bite::Email.INDICATORS
11
+ StartingOf = {
12
+ # courier/module.dsn/dsn*.txt
13
+ message: ['DELAYS IN DELIVERING YOUR MESSAGE', 'UNDELIVERABLE MAIL'],
14
+ rfc822: ['Content-Type: message/rfc822', 'Content-Type: text/rfc822-headers'],
32
15
  }.freeze
33
- ReFailure = {
16
+
17
+ ReFailures = {
34
18
  # courier/module.esmtp/esmtpclient.c:526| hard_error(del, ctf, "No such domain.");
35
- hostunknown: %r/\ANo[ ]such[ ]domain[.]\z/x,
19
+ hostunknown: %r/\ANo such domain[.]\z/,
36
20
  # courier/module.esmtp/esmtpclient.c:531| hard_error(del, ctf,
37
21
  # courier/module.esmtp/esmtpclient.c:532| "This domain's DNS violates RFC 1035.");
38
- systemerror: %r/\AThis[ ]domain's[ ]DNS[ ]violates[ ]RFC[ ]1035[.]\z/x,
22
+ systemerror: %r/\AThis domain's DNS violates RFC 1035[.]\z/,
39
23
  }.freeze
40
- ReDelayed = {
24
+ ReDelaying = {
41
25
  # courier/module.esmtp/esmtpclient.c:535| soft_error(del, ctf, "DNS lookup failed.");
42
- networkerror: %r/\ADNS[ ]lookup[ ]failed[.]\z/x,
26
+ networkerror: %r/\ADNS lookup failed[.]\z/,
43
27
  }.freeze
44
- Indicators = Sisimai::Bite::Email.INDICATORS
45
28
 
46
29
  def description; return 'Courier MTA'; end
47
30
  def smtpagent; return Sisimai::Bite.smtpagent(self); end
48
31
  def headerlist; return []; end
49
- def pattern; return Re0; end
50
32
 
51
33
  # Parse bounce messages from Courier MTA
52
34
  # @param [Hash] mhead Message headers of a bounce email
@@ -64,11 +46,11 @@ module Sisimai::Bite::Email
64
46
  return nil unless mbody
65
47
 
66
48
  match = 0
67
- match += 1 if mhead['from'] =~ Re0[:from]
68
- match += 1 if mhead['subject'] =~ Re0[:subject]
49
+ match += 1 if mhead['from'].include?('Courier mail server at ')
50
+ match += 1 if mhead['subject'] =~ /(?:NOTICE: mail delivery status[.]|WARNING: delayed mail[.])/
69
51
  if mhead['message-id']
70
52
  # Message-ID: <courier.4D025E3A.00001792@5jo.example.org>
71
- match += 1 if mhead['message-id'] =~ Re0[:'message-id']
53
+ match += 1 if mhead['message-id'] =~ /\A[<]courier[.][0-9A-F]+[.]/
72
54
  end
73
55
  return nil if match.zero?
74
56
 
@@ -88,14 +70,14 @@ module Sisimai::Bite::Email
88
70
  }
89
71
  v = nil
90
72
 
91
- hasdivided.each do |e|
73
+ while e = hasdivided.shift do
92
74
  # Save the current line for the next loop
93
75
  havepassed << e
94
76
  p = havepassed[-2]
95
77
 
96
78
  if readcursor.zero?
97
79
  # Beginning of the bounce message or delivery status part
98
- if e =~ Re1[:begin]
80
+ if e.include?(StartingOf[:message][0]) || e.include?(StartingOf[:message][1])
99
81
  readcursor |= Indicators[:deliverystatus]
100
82
  next
101
83
  end
@@ -103,7 +85,7 @@ module Sisimai::Bite::Email
103
85
 
104
86
  if (readcursor & Indicators[:'message-rfc822']).zero?
105
87
  # Beginning of the original message part
106
- if e =~ Re1[:rfc822]
88
+ if e.start_with?(StartingOf[:rfc822][0], StartingOf[:rfc822][1])
107
89
  readcursor |= Indicators[:'message-rfc822']
108
90
  next
109
91
  end
@@ -117,7 +99,6 @@ module Sisimai::Bite::Email
117
99
  next
118
100
  end
119
101
  rfc822list << e
120
-
121
102
  else
122
103
  # Before "message/rfc822"
123
104
  next if (readcursor & Indicators[:deliverystatus]).zero?
@@ -131,7 +112,7 @@ module Sisimai::Bite::Email
131
112
  # Diagnostic-Code: smtp; 550 5.1.1 <kijitora@example.co.jp>... User Unknown
132
113
  v = dscontents[-1]
133
114
 
134
- if cv = e.match(/\A[Ff]inal-[Rr]ecipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/)
115
+ if cv = e.match(/\AFinal-Recipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/)
135
116
  # Final-Recipient: rfc822; kijitora@example.co.jp
136
117
  if v['recipient']
137
118
  # There are multiple recipient addresses in the message body.
@@ -141,46 +122,41 @@ module Sisimai::Bite::Email
141
122
  v['recipient'] = cv[1]
142
123
  recipients += 1
143
124
 
144
- elsif cv = e.match(/\A[Xx]-[Aa]ctual-[Rr]ecipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/)
125
+ elsif cv = e.match(/\AX-Actual-Recipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/)
145
126
  # X-Actual-Recipient: RFC822; kijitora@example.co.jp
146
127
  v['alias'] = cv[1]
147
128
 
148
- elsif cv = e.match(/\A[Aa]ction:[ ]*(.+)\z/)
129
+ elsif cv = e.match(/\AAction:[ ]*(.+)\z/)
149
130
  # Action: failed
150
131
  v['action'] = cv[1].downcase
151
132
 
152
- elsif cv = e.match(/\A[Ss]tatus:[ ]*(\d[.]\d+[.]\d+)/)
133
+ elsif cv = e.match(/\AStatus:[ ]*(\d[.]\d+[.]\d+)/)
153
134
  # Status: 5.1.1
154
135
  # Status:5.2.0
155
136
  # Status: 5.1.0 (permanent failure)
156
137
  v['status'] = cv[1]
157
138
 
158
- elsif cv = e.match(/\A[Rr]emote-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
139
+ elsif cv = e.match(/\ARemote-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
159
140
  # Remote-MTA: DNS; mx.example.jp
141
+ # Get the first element
160
142
  v['rhost'] = cv[1].downcase
161
- if v['rhost'] =~ / /
162
- # Get the first element
163
- v['rhost'] = v['rhost'].split(' ').shift
164
- end
143
+ v['rhost'] = v['rhost'].split(' ').shift if v['rhost'].include?(' ')
165
144
 
166
- elsif cv = e.match(/\A[Ll]ast-[Aa]ttempt-[Dd]ate:[ ]*(.+)\z/)
145
+ elsif cv = e.match(/\ALast-Attempt-Date:[ ]*(.+)\z/)
167
146
  # Last-Attempt-Date: Fri, 14 Feb 2014 12:30:08 -0500
168
147
  v['date'] = cv[1]
169
-
170
148
  else
171
- if cv = e.match(/\A[Dd]iagnostic-[Cc]ode:[ ]*(.+?);[ ]*(.+)\z/)
149
+ if cv = e.match(/\ADiagnostic-Code:[ ]*(.+?);[ ]*(.+)\z/)
172
150
  # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
173
151
  v['spec'] = cv[1].upcase
174
152
  v['diagnosis'] = cv[2]
175
153
 
176
- elsif p =~ /\A[Dd]iagnostic-[Cc]ode:[ ]*/ && cv = e.match(/\A[ \t]+(.+)\z/)
154
+ elsif p.start_with?('Diagnostic-Code:') && cv = e.match(/\A[ \t]+(.+)\z/)
177
155
  # Continued line of the value of Diagnostic-Code header
178
- v['diagnosis'] ||= ''
179
- v['diagnosis'] += ' ' + cv[1]
180
- havepassed[-1] = 'Diagnostic-Code: ' + e
156
+ v['diagnosis'] << ' ' << cv[1]
157
+ havepassed[-1] = 'Diagnostic-Code: ' << e
181
158
  end
182
159
  end
183
-
184
160
  else
185
161
  # This is a delivery status notification from marutamachi.example.org,
186
162
  # running the Courier mail server, version 0.65.2.
@@ -205,19 +181,19 @@ module Sisimai::Bite::Email
205
181
  next if commandtxt.size > 0
206
182
  commandtxt = cv[1]
207
183
 
208
- elsif cv = e.match(/\A[Rr]eporting-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
184
+ elsif cv = e.match(/\AReporting-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
209
185
  # Reporting-MTA: dns; mx.example.jp
210
186
  next if connheader['rhost'].size > 0
211
187
  connheader['rhost'] = cv[1].downcase
212
188
  connvalues += 1
213
189
 
214
- elsif cv = e.match(/\A[Rr]eceived-[Ff]rom-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
190
+ elsif cv = e.match(/\AReceived-From-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
215
191
  # Received-From-MTA: DNS; x1x2x3x4.dhcp.example.ne.jp
216
192
  next if connheader['lhost'].size > 0
217
193
  connheader['lhost'] = cv[1].downcase
218
194
  connvalues += 1
219
195
 
220
- elsif cv = e.match(/\A[Aa]rrival-[Dd]ate:[ ]*(.+)\z/)
196
+ elsif cv = e.match(/\AArrival-Date:[ ]*(.+)\z/)
221
197
  # Arrival-Date: Wed, 29 Apr 2009 16:03:18 +0900
222
198
  next if connheader['date'].size > 0
223
199
  connheader['date'] = cv[1]
@@ -227,24 +203,24 @@ module Sisimai::Bite::Email
227
203
  end
228
204
  end
229
205
  return nil if recipients.zero?
230
- require 'sisimai/string'
231
206
 
207
+ require 'sisimai/string'
232
208
  dscontents.map do |e|
233
209
  # Set default values if each value is empty.
234
210
  connheader.each_key { |a| e[a] ||= connheader[a] || '' }
235
211
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
236
212
 
237
- ReFailure.each_key do |r|
213
+ ReFailures.each_key do |r|
238
214
  # Verify each regular expression of session errors
239
- next unless e['diagnosis'] =~ ReFailure[r]
215
+ next unless e['diagnosis'] =~ ReFailures[r]
240
216
  e['reason'] = r.to_s
241
217
  break
242
218
  end
243
219
 
244
220
  unless e['reason']
245
- ReDelayed.each_key do |r|
221
+ ReDelaying.each_key do |r|
246
222
  # Verify each regular expression of session errors
247
- next unless e['diagnosis'] =~ ReDelayed[r]
223
+ next unless e['diagnosis'] =~ ReDelaying[r]
248
224
  e['reason'] = r.to_s
249
225
  break
250
226
  end