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,37 +6,33 @@ module Sisimai::Bite::Email
6
6
  # Imported from p5-Sisimail/lib/Sisimai/Bite/Email/qmail.pm
7
7
  require 'sisimai/bite/email'
8
8
 
9
- Re0 = {
10
- :subject => %r/\Afailure notice/i,
11
- :received => %r/\A[(]qmail[ ]+\d+[ ]+invoked[ ]+(?:for[ ]+bounce|from[ ]+network)[)]/,
12
- }.freeze
13
- # qmail-remote.c:248| if (code >= 500) {
14
- # qmail-remote.c:249| out("h"); outhost(); out(" does not like recipient.\n");
15
- # qmail-remote.c:265| if (code >= 500) quit("D"," failed on DATA command");
16
- # qmail-remote.c:271| if (code >= 500) quit("D"," failed after I sent the message");
17
- #
18
- # Characters: K,Z,D in qmail-qmqpc.c, qmail-send.c, qmail-rspawn.c
19
- # K = success, Z = temporary error, D = permanent error
20
- Re1 = {
21
- :begin => %r/\AHi[.] This is the qmail/,
22
- :rfc822 => %r/\A--- Below this line is a copy of the message[.]\z/,
23
- :error => %r/\ARemote host said:/,
24
- :sorry => %r/\A[Ss]orry[,.][ ]/,
25
- :endof => %r/\A__END_OF_EMAIL_MESSAGE__\z/,
9
+ Indicators = Sisimai::Bite::Email.INDICATORS
10
+ StartingOf = {
11
+ # qmail-remote.c:248| if (code >= 500) {
12
+ # qmail-remote.c:249| out("h"); outhost(); out(" does not like recipient.\n");
13
+ # qmail-remote.c:265| if (code >= 500) quit("D"," failed on DATA command");
14
+ # qmail-remote.c:271| if (code >= 500) quit("D"," failed after I sent the message");
15
+ #
16
+ # Characters: K,Z,D in qmail-qmqpc.c, qmail-send.c, qmail-rspawn.c
17
+ # K = success, Z = temporary error, D = permanent error
18
+ message: ['Hi. This is the qmail'],
19
+ rfc822: ['--- Below this line is a copy of the message.'],
20
+ error: ['Remote host said:'],
26
21
  }.freeze
22
+
27
23
  ReSMTP = {
28
24
  # Error text regular expressions which defined in qmail-remote.c
29
25
  # qmail-remote.c:225| if (smtpcode() != 220) quit("ZConnected to "," but greeting failed");
30
- conn: %r/(?:Error:)?Connected[ ]to[ ].+[ ]but[ ]greeting[ ]failed[.]/x,
26
+ conn: %r/(?:Error:)?Connected to .+ but greeting failed[.]/,
31
27
  # qmail-remote.c:231| if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected");
32
- ehlo: %r/(?:Error:)?Connected[ ]to[ ].+[ ]but[ ]my[ ]name[ ]was[ ]rejected[.]/x,
28
+ ehlo: %r/(?:Error:)?Connected to .+ but my name was rejected[.]/,
33
29
  # qmail-remote.c:238| if (code >= 500) quit("DConnected to "," but sender was rejected");
34
30
  # reason = rejected
35
- mail: %r/(?:Error:)?Connected[ ]to[ ].+[ ]but[ ]sender[ ]was[ ]rejected[.]/x,
31
+ mail: %r/(?:Error:)?Connected to .+ but sender was rejected[.]/,
36
32
  # qmail-remote.c:249| out("h"); outhost(); out(" does not like recipient.\n");
37
33
  # qmail-remote.c:253| out("s"); outhost(); out(" does not like recipient.\n");
38
34
  # reason = userunknown
39
- rcpt: %r/(?:Error:)?.+[ ]does[ ]not[ ]like[ ]recipient[.]/x,
35
+ rcpt: %r/(?:Error:)?.+ does not like recipient[.]/,
40
36
  # qmail-remote.c:265| if (code >= 500) quit("D"," failed on DATA command");
41
37
  # qmail-remote.c:266| if (code >= 400) quit("Z"," failed on DATA command");
42
38
  # qmail-remote.c:271| if (code >= 500) quit("D"," failed after I sent the message");
@@ -80,26 +76,24 @@ module Sisimai::Bite::Email
80
76
  )
81
77
  }x,
82
78
  }.freeze
83
- # userunknown + expired
84
- ReOnHold = %r/\A[^ ]+ does not like recipient[.][ ]+.+this message has been in the queue too long[.]\z/
85
- # qmail-remote-fallback.patch
86
- ReCommand = %r/Sorry,[ ]no[ ]SMTP[ ]connection[ ]got[ ]far[ ]enough;[ ]most[ ]progress[ ]was[ ]([A-Z]{4})[ ]/x
87
- ReFailure = {
79
+
80
+ # qmail-send.c:922| ... (&dline[c],"I'm not going to try again; this message has been in the queue too long.\n")) nomem();
81
+ ReDelaying = %r/this message has been in the queue too long[.]\z/
82
+ ReCommands = %r/Sorry, no SMTP connection got far enough; most progress was ([A-Z]{4}) /
83
+ ReIsOnHold = %r/\A[^ ]+ does not like recipient[.][ ]+.+this message has been in the queue too long[.]\z/
84
+ ReFailures = {
88
85
  # qmail-local.c:589| strerr_die1x(100,"Sorry, no mailbox here by that name. (#5.1.1)");
89
86
  # qmail-remote.c:253| out("s"); outhost(); out(" does not like recipient.\n");
90
- userunknown: %r{(?:
91
- no[ ]mailbox[ ]here[ ]by[ ]that[ ]name
92
- |[ ]does[ ]not[ ]like[ ]recipient[.]
93
- )
94
- }x,
87
+ userunknown: %r/(?:no mailbox here by that name| does not like recipient[.])/,
95
88
  # error_str.c:192| X(EDQUOT,"disk quota exceeded")
96
- mailboxfull: %r/disk[ ]quota[ ]exceeded/x,
89
+ mailboxfull: %r/disk quota exceeded/,
97
90
  # qmail-qmtpd.c:233| ... result = "Dsorry, that message size exceeds my databytes limit (#5.3.4)";
98
91
  # qmail-smtpd.c:391| ... out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); return;
99
- mesgtoobig: %r/Message[ ]size[ ]exceeds[ ]fixed[ ]maximum[ ]message[ ]size:/x,
92
+ mesgtoobig: %r/Message size exceeds fixed maximum message size:/,
100
93
  # qmail-remote.c:68| Sorry, I couldn't find any host by that name. (#4.1.2)\n"); zerodie();
101
94
  # qmail-remote.c:78| Sorry, I couldn't find any host named ");
102
- hostunknown: %r/\ASorry[,][ ]I[ ]couldn[']t[ ]find[ ]any[ ]host[ ]/x,
95
+ hostunknown: %r/\ASorry, I couldn't find any host /,
96
+ systemfull: %r/Requested action not taken: mailbox unavailable [(]not enough free space[)]/,
103
97
  systemerror: %r{(?>
104
98
  bad[ ]interpreter:[ ]No[ ]such[ ]file[ ]or[ ]directory
105
99
  |system[ ]error
@@ -109,20 +103,14 @@ module Sisimai::Bite::Email
109
103
  networkerror: %r{Sorry(?:
110
104
  [,][ ]I[ ]wasn[']t[ ]able[ ]to[ ]establish[ ]an[ ]SMTP[ ]connection
111
105
  |[,][ ]I[ ]couldn[']t[ ]find[ ]a[ ]mail[ ]exchanger[ ]or[ ]IP[ ]address
112
- |[.][ ]Although[ ]I[']m[ ]listed[ ]as[ ]a[ ]best[-]preference[ ]MX[ ]
113
- or[ ]A[ ]for[ ]that[ ]host
106
+ |[.][ ]Although[ ]I[']m[ ]listed[ ]as[ ]a[ ]best[-]preference[ ]MX[ ]or[ ]A[ ]for[ ]that[ ]host
114
107
  )
115
108
  }x,
116
- systemfull: %r/Requested action not taken: mailbox unavailable [(]not enough free space[)]/,
117
109
  }.freeze
118
- # qmail-send.c:922| ... (&dline[c],"I'm not going to try again; this message has been in the queue too long.\n")) nomem();
119
- ReDelayed = %r/this[ ]message[ ]has[ ]been[ ]in[ ]the[ ]queue[ ]too[ ]long[.]\z/x
120
- Indicators = Sisimai::Bite::Email.INDICATORS
121
110
 
122
111
  def description; return 'qmail'; end
123
112
  def smtpagent; return 'Email::qmail'; end
124
113
  def headerlist; return []; end
125
- def pattern; return Re0; end
126
114
 
127
115
  # Parse bounce messages from qmail
128
116
  # @param [Hash] mhead Message headers of a bounce email
@@ -143,9 +131,10 @@ module Sisimai::Bite::Email
143
131
  # by qmail, see http://cr.yp.to/qmail.html
144
132
  # e.g.) Received: (qmail 12345 invoked for bounce); 29 Apr 2009 12:34:56 -0000
145
133
  # Subject: failure notice
134
+ tryto = /\A[(]qmail[ ]+\d+[ ]+invoked[ ]+(?:for[ ]+bounce|from[ ]+network)[)]/
146
135
  match = 0
147
- match += 1 if mhead['subject'] =~ Re0[:subject]
148
- match += 1 if mhead['received'].find { |a| a =~ Re0[:received] }
136
+ match += 1 if mhead['subject'] == 'failure notice'
137
+ match += 1 if mhead['received'].find { |a| a =~ tryto }
149
138
  return nil if match.zero?
150
139
 
151
140
  dscontents = [Sisimai::Bite.DELIVERYSTATUS]
@@ -156,10 +145,10 @@ module Sisimai::Bite::Email
156
145
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
157
146
  v = nil
158
147
 
159
- hasdivided.each do |e|
148
+ while e = hasdivided.shift do
160
149
  if readcursor.zero?
161
150
  # Beginning of the bounce message or delivery status part
162
- if e =~ Re1[:begin]
151
+ if e.start_with?(StartingOf[:message][0])
163
152
  readcursor |= Indicators[:deliverystatus]
164
153
  next
165
154
  end
@@ -167,7 +156,7 @@ module Sisimai::Bite::Email
167
156
 
168
157
  if (readcursor & Indicators[:'message-rfc822']).zero?
169
158
  # Beginning of the original message part
170
- if e =~ Re1[:rfc822]
159
+ if e == StartingOf[:rfc822][0]
171
160
  readcursor |= Indicators[:'message-rfc822']
172
161
  next
173
162
  end
@@ -181,7 +170,6 @@ module Sisimai::Bite::Email
181
170
  next
182
171
  end
183
172
  rfc822list << e
184
-
185
173
  else
186
174
  # Before "message/rfc822"
187
175
  next if (readcursor & Indicators[:deliverystatus]).zero?
@@ -207,8 +195,8 @@ module Sisimai::Bite::Email
207
195
  # Append error message
208
196
  next if e.empty?
209
197
  v['diagnosis'] ||= ''
210
- v['diagnosis'] += e + ' '
211
- v['alterrors'] = e if e =~ Re1[:error]
198
+ v['diagnosis'] << e + ' '
199
+ v['alterrors'] = e if e.start_with?(StartingOf[:error][0])
212
200
 
213
201
  next if v['rhost']
214
202
  if cv = e.match(ReHost)
@@ -221,7 +209,6 @@ module Sisimai::Bite::Email
221
209
 
222
210
  require 'sisimai/string'
223
211
  require 'sisimai/smtp/status'
224
-
225
212
  dscontents.map do |e|
226
213
  e['agent'] = self.smtpagent
227
214
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''
@@ -237,7 +224,7 @@ module Sisimai::Bite::Email
237
224
 
238
225
  unless e['command']
239
226
  # Verify each regular expression of patches
240
- if cv = e['diagnosis'].match(ReCommand)
227
+ if cv = e['diagnosis'].match(ReCommands)
241
228
  e['command'] = cv[1].upcase
242
229
  end
243
230
  e['command'] ||= ''
@@ -249,28 +236,26 @@ module Sisimai::Bite::Email
249
236
  # MAIL | Connected to 192.0.2.135 but sender was rejected.
250
237
  e['reason'] = 'rejected'
251
238
 
252
- elsif e['command'] =~ /\A(?:HELO|EHLO)\z/
239
+ elsif %w[HELO EHLO].index(e['command'])
253
240
  # HELO | Connected to 192.0.2.135 but my name was rejected.
254
241
  e['reason'] = 'blocked'
255
-
256
242
  else
257
243
  # Try to match with each error message in the table
258
- if e['diagnosis'] =~ ReOnHold
244
+ if e['diagnosis'] =~ ReIsOnHold
259
245
  # To decide the reason require pattern match with
260
246
  # Sisimai::Reason::* modules
261
247
  e['reason'] = 'onhold'
262
-
263
248
  else
264
- ReFailure.each_key do |r|
249
+ ReFailures.each_key do |r|
265
250
  # Verify each regular expression of session errors
266
251
  if e['alterrors']
267
252
  # Check the value of "alterrors"
268
- next unless e['alterrors'] =~ ReFailure[r]
253
+ next unless e['alterrors'] =~ ReFailures[r]
269
254
  e['reason'] = r.to_s
270
255
  end
271
256
  break if e['reason']
272
257
 
273
- next unless e['diagnosis'] =~ ReFailure[r]
258
+ next unless e['diagnosis'] =~ ReFailures[r]
274
259
  e['reason'] = r.to_s
275
260
  break
276
261
  end
@@ -285,7 +270,7 @@ module Sisimai::Bite::Email
285
270
  end
286
271
 
287
272
  unless e['reason']
288
- e['reason'] = 'expired' if e['diagnosis'] =~ ReDelayed
273
+ e['reason'] = 'expired' if e['diagnosis'] =~ ReDelaying
289
274
  end
290
275
  end
291
276
  end
@@ -8,23 +8,18 @@ module Sisimai::Bite::Email
8
8
  require 'sisimai/bite/email'
9
9
 
10
10
  # http://aws.amazon.com/ses/
11
- Re0 = {
12
- :subject => %r/\ADelivery Status Notification [(]Failure[)]\z/,
13
- :received => %r/.+[.]smtp-out[.].+[.]amazonses[.]com\b/,
14
- }.freeze
15
- Re1 = {
16
- :begin => %r/\AThis message could not be delivered[.]\z/,
17
- :rfc822 => %r|\Acontent-type: text/rfc822-headers\z|,
18
- :endof => %r/\A__END_OF_EMAIL_MESSAGE__\z/,
11
+ Indicators = Sisimai::Bite::Email.INDICATORS
12
+ StartingOf = {
13
+ message: ['This message could not be delivered.'],
14
+ rfc822: ['content-type: text/rfc822-headers'],
19
15
  }.freeze
20
- ReFailure = {
16
+ ReFailures = {
21
17
  # The followings are error messages in Rule sets/*/Actions/Template
22
18
  filtered: %r/Mailbox does not exist/,
23
19
  mesgtoobig: %r/Message too large/,
24
20
  mailboxfull: %r/Mailbox full/,
25
21
  contenterror: %r/Message content rejected/,
26
22
  }.freeze
27
- Indicators = Sisimai::Bite::Email.INDICATORS
28
23
 
29
24
  def description; return 'Amazon SES(Receiving): http://aws.amazon.com/ses/'; end
30
25
  def smtpagent; return Sisimai::Bite.smtpagent(self); end
@@ -32,7 +27,6 @@ module Sisimai::Bite::Email
32
27
  # X-SES-Outgoing: 2015.10.01-54.240.27.7
33
28
  # Feedback-ID: 1.us-west-2.HX6/J9OVlHTadQhEu1+wdF9DBj6n6Pa9sW5Y/0pSOi8=:AmazonSES
34
29
  def headerlist; return ['X-SES-Outgoing']; end
35
- def pattern; return Re0; end
36
30
 
37
31
  # Parse bounce messages from Amazon SES/Receiving
38
32
  # @param [Hash] mhead Message headers of a bounce email
@@ -48,6 +42,9 @@ module Sisimai::Bite::Email
48
42
  def scan(mhead, mbody)
49
43
  return nil unless mhead
50
44
  return nil unless mbody
45
+
46
+ # :subject => %r/\ADelivery Status Notification [(]Failure[)]\z/,
47
+ # :received => %r/.+[.]smtp-out[.].+[.]amazonses[.]com\b/,
51
48
  return nil unless mhead['x-ses-outgoing']
52
49
 
53
50
  dscontents = [Sisimai::Bite.DELIVERYSTATUS]
@@ -64,14 +61,14 @@ module Sisimai::Bite::Email
64
61
  }
65
62
  v = nil
66
63
 
67
- hasdivided.each do |e|
64
+ while e = hasdivided.shift do
68
65
  # Save the current line for the next loop
69
66
  havepassed << e
70
67
  p = havepassed[-2]
71
68
 
72
69
  if readcursor.zero?
73
70
  # Beginning of the bounce message or delivery status part
74
- if e =~ Re1[:begin]
71
+ if e == StartingOf[:message][0]
75
72
  readcursor |= Indicators[:deliverystatus]
76
73
  next
77
74
  end
@@ -79,7 +76,7 @@ module Sisimai::Bite::Email
79
76
 
80
77
  if (readcursor & Indicators[:'message-rfc822']).zero?
81
78
  # Beginning of the original message part
82
- if e =~ Re1[:rfc822]
79
+ if e == StartingOf[:rfc822][0]
83
80
  readcursor |= Indicators[:'message-rfc822']
84
81
  next
85
82
  end
@@ -93,7 +90,6 @@ module Sisimai::Bite::Email
93
90
  next
94
91
  end
95
92
  rfc822list << e
96
-
97
93
  else
98
94
  # Before "message/rfc822"
99
95
  next if (readcursor & Indicators[:deliverystatus]).zero?
@@ -107,7 +103,7 @@ module Sisimai::Bite::Email
107
103
  # Status: 5.1.1
108
104
  v = dscontents[-1]
109
105
 
110
- if cv = e.match(/\A[Ff]inal-[Rr]ecipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/)
106
+ if cv = e.match(/\AFinal-Recipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/)
111
107
  # Final-Recipient: RFC822; kijitora@example.jp
112
108
  if v['recipient']
113
109
  # There are multiple recipient addresses in the message body.
@@ -117,41 +113,38 @@ module Sisimai::Bite::Email
117
113
  v['recipient'] = cv[1]
118
114
  recipients += 1
119
115
 
120
- elsif cv = e.match(/\A[Xx]-[Aa]ctual-[Rr]ecipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/) ||
121
- e.match(/\A[Oo]riginal-[Rr]ecipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/)
116
+ elsif cv = e.match(/\AX-Actual-Recipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/) ||
117
+ e.match(/\AOriginal-Recipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/)
122
118
  # X-Actual-Recipient: RFC822; kijitora@example.co.jp
123
119
  # Original-Recipient: rfc822; kijitora@example.co.jp
124
120
  v['alias'] = cv[1]
125
121
 
126
- elsif cv = e.match(/\A[Aa]ction:[ ]*(.+)\z/)
122
+ elsif cv = e.match(/\AAction:[ ]*(.+)\z/)
127
123
  # Action: failed
128
124
  v['action'] = cv[1].downcase
129
125
 
130
- elsif cv = e.match(/\A[Ss]tatus:[ ]*(\d[.]\d+[.]\d+)/)
126
+ elsif cv = e.match(/\AStatus:[ ]*(\d[.]\d+[.]\d+)/)
131
127
  # Status: 5.1.1
132
128
  v['status'] = cv[1]
133
129
 
134
- elsif cv = e.match(/\A[Rr]emote-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
130
+ elsif cv = e.match(/\ARemote-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
135
131
  # Remote-MTA: DNS; mx.example.jp
136
132
  v['rhost'] = cv[1].downcase
137
133
 
138
- elsif cv = e.match(/\A[Ll]ast-[Aa]ttempt-[Dd]ate:[ ]*(.+)\z/)
134
+ elsif cv = e.match(/\ALast-Attempt-Date:[ ]*(.+)\z/)
139
135
  # Last-Attempt-Date: Fri, 14 Feb 2014 12:30:08 -0500
140
136
  v['date'] = cv[1]
141
-
142
137
  else
143
- if cv = e.match(/\A[Dd]iagnostic-[Cc]ode:[ ]*(.+?);[ ]*(.+)\z/)
138
+ if cv = e.match(/\ADiagnostic-Code:[ ]*(.+?);[ ]*(.+)\z/)
144
139
  # Diagnostic-Code: SMTP; 550 5.1.1 <kijitora@example.jp>... User Unknown
145
140
  v['spec'] = cv[1].upcase
146
141
  v['diagnosis'] = cv[2]
147
142
 
148
- elsif p =~ /\A[Dd]iagnostic-[Cc]ode:[ ]*/ && cv = e.match(/\A[ \t]+(.+)\z/)
143
+ elsif p.start_with?('Diagnostic-Code:') && cv = e.match(/\A[ \t]+(.+)\z/)
149
144
  # Continued line of the value of Diagnostic-Code header
150
- v['diagnosis'] ||= ''
151
- v['diagnosis'] += ' ' + cv[1]
152
- havepassed[-1] = 'Diagnostic-Code: ' + e
145
+ v['diagnosis'] << ' ' << cv[1]
146
+ havepassed[-1] = 'Diagnostic-Code: ' << e
153
147
  end
154
-
155
148
  end
156
149
  else
157
150
  # This message could not be delivered.
@@ -161,13 +154,13 @@ module Sisimai::Bite::Email
161
154
  # Content-Description: Delivery Status Notification
162
155
  #
163
156
  # Reporting-MTA: dns; inbound-smtp.us-west-2.amazonaws.com
164
- if cv = e.match(/\A[Rr]eporting-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
157
+ if cv = e.match(/\AReporting-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
165
158
  # Reporting-MTA: dns; mx.example.jp
166
159
  next if connheader['lhost'].size > 0
167
160
  connheader['lhost'] = cv[1].downcase
168
161
  connvalues += 1
169
162
 
170
- elsif cv = e.match(/\A[Aa]rrival-[Dd]ate:[ ]*(.+)\z/)
163
+ elsif cv = e.match(/\AArrival-Date:[ ]*(.+)\z/)
171
164
  # Arrival-Date: Wed, 29 Apr 2009 16:03:18 +0900
172
165
  next if connheader['date'].size > 0
173
166
  connheader['date'] = cv[1]
@@ -176,11 +169,10 @@ module Sisimai::Bite::Email
176
169
  end
177
170
  end
178
171
  end
179
-
180
172
  return nil if recipients.zero?
173
+
181
174
  require 'sisimai/string'
182
175
  require 'sisimai/smtp/status'
183
-
184
176
  dscontents.map do |e|
185
177
  # Set default values if each value is empty.
186
178
  connheader.each_key { |a| e[a] ||= connheader[a] || '' }
@@ -188,7 +180,7 @@ module Sisimai::Bite::Email
188
180
  e['diagnosis'] = e['diagnosis'].gsub(/\\n/, ' ')
189
181
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
190
182
 
191
- if e['status'] =~ /\A[45][.][01][.]0\z/
183
+ if e['status'].to_s.start_with?('5.0.0', '5.1.0', '4.0.0', '4.1.0')
192
184
  # Get other D.S.N. value from the error message
193
185
  errormessage = e['diagnosis']
194
186
 
@@ -200,9 +192,9 @@ module Sisimai::Bite::Email
200
192
  e['status'] = pseudostatus if pseudostatus.size > 0
201
193
  end
202
194
 
203
- ReFailure.each_key do |r|
195
+ ReFailures.each_key do |r|
204
196
  # Verify each regular expression of session errors
205
- next unless e['diagnosis'] =~ ReFailure[r]
197
+ next unless e['diagnosis'] =~ ReFailures[r]
206
198
  e['reason'] = r.to_s
207
199
  break
208
200
  end
@@ -6,18 +6,11 @@ module Sisimai::Bite::Email
6
6
  # Imported from p5-Sisimail/lib/Sisimai/Bite/Email/SendGrid.pm
7
7
  require 'sisimai/bite/email'
8
8
 
9
- Re0 = {
10
- :'from' => %r/\AMAILER-DAEMON\z/,
11
- :'return-path' => %r/\A[<]apps[@]sendgrid[.]net[>]\z/,
12
- :'subject' => %r/\AUndelivered Mail Returned to Sender\z/,
13
- }.freeze
14
- Re1 = {
15
- :begin => %r/\AThis is an automatically generated message from SendGrid[.]\z/,
16
- :error => %r/\AIf you require assistance with this, please contact SendGrid support[.]\z/,
17
- :rfc822 => %r|\AContent-Type: message/rfc822|,
18
- :endof => %r/\A__END_OF_EMAIL_MESSAGE__\z/,
19
- }.freeze
20
9
  Indicators = Sisimai::Bite::Email.INDICATORS
10
+ StartingOf = {
11
+ message: ['This is an automatically generated message from SendGrid.'],
12
+ rfc822: ['Content-Type: message/rfc822'],
13
+ }.freeze
21
14
 
22
15
  def description; return 'SendGrid: http://sendgrid.com/'; end
23
16
  def smtpagent; return Sisimai::Bite.smtpagent(self); end
@@ -25,7 +18,6 @@ module Sisimai::Bite::Email
25
18
  # Return-Path: <apps@sendgrid.net>
26
19
  # X-Mailer: MIME-tools 5.502 (Entity 5.502)
27
20
  def headerlist; return ['Return-Path', 'X-Mailer']; end
28
- def pattern; return Re0; end
29
21
 
30
22
  # Parse bounce messages from SendGrid
31
23
  # @param [Hash] mhead Message headers of a bounce email
@@ -41,9 +33,11 @@ module Sisimai::Bite::Email
41
33
  def scan(mhead, mbody)
42
34
  return nil unless mhead
43
35
  return nil unless mbody
36
+
37
+ # :'from' => %r/\AMAILER-DAEMON\z/,
44
38
  return nil unless mhead['return-path']
45
- return nil unless mhead['return-path'] =~ Re0[:'return-path']
46
- return nil unless mhead['subject'] =~ Re0[:'subject']
39
+ return nil unless mhead['return-path'] == '<apps@sendgrid.net>'
40
+ return nil unless mhead['subject'] == 'Undelivered Mail Returned to Sender'
47
41
 
48
42
  require 'sisimai/datetime'
49
43
  dscontents = [Sisimai::Bite.DELIVERYSTATUS]
@@ -60,14 +54,14 @@ module Sisimai::Bite::Email
60
54
  }
61
55
  v = nil
62
56
 
63
- hasdivided.each do |e|
57
+ while e = hasdivided.shift do
64
58
  # Save the current line for the next loop
65
59
  havepassed << e
66
60
  p = havepassed[-2]
67
61
 
68
62
  if readcursor.zero?
69
63
  # Beginning of the bounce message or delivery status part
70
- if e =~ Re1[:begin]
64
+ if e == StartingOf[:message][0]
71
65
  readcursor |= Indicators[:deliverystatus]
72
66
  next
73
67
  end
@@ -75,7 +69,7 @@ module Sisimai::Bite::Email
75
69
 
76
70
  if (readcursor & Indicators[:'message-rfc822']).zero?
77
71
  # Beginning of the original message part
78
- if e =~ Re1[:rfc822]
72
+ if e == StartingOf[:rfc822][0]
79
73
  readcursor |= Indicators[:'message-rfc822']
80
74
  next
81
75
  end
@@ -89,7 +83,6 @@ module Sisimai::Bite::Email
89
83
  next
90
84
  end
91
85
  rfc822list << e
92
-
93
86
  else
94
87
  # Before "message/rfc822"
95
88
  next if (readcursor & Indicators[:deliverystatus]).zero?
@@ -103,7 +96,7 @@ module Sisimai::Bite::Email
103
96
  # Diagnostic-Code: 550 5.1.1 <kijitora@example.jp>... User Unknown
104
97
  v = dscontents[-1]
105
98
 
106
- if cv = e.match(/\A[Ff]inal-[Rr]ecipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/)
99
+ if cv = e.match(/\AFinal-Recipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/)
107
100
  # Final-Recipient: RFC822; userunknown@example.jp
108
101
  if v['recipient']
109
102
  # There are multiple recipient addresses in the message body.
@@ -113,26 +106,25 @@ module Sisimai::Bite::Email
113
106
  v['recipient'] = cv[1]
114
107
  recipients += 1
115
108
 
116
- elsif cv = e.match(/\A[Aa]ction:[ ]*(.+)\z/)
109
+ elsif cv = e.match(/\AAction:[ ]*(.+)\z/)
117
110
  # Action: failed
118
111
  v['action'] = cv[1].downcase
119
112
 
120
- elsif cv = e.match(/\A[Ss]tatus:[ ]*(\d[.]\d+[.]\d+)/)
113
+ elsif cv = e.match(/\AStatus:[ ]*(\d[.]\d+[.]\d+)/)
121
114
  # Status: 5.1.1
122
115
  # Status:5.2.0
123
116
  # Status: 5.1.0 (permanent failure)
124
117
  v['status'] = cv[1]
125
118
 
126
119
  else
127
- if cv = e.match(/\A[Dd]iagnostic-[Cc]ode:[ ]*(.+)\z/)
120
+ if cv = e.match(/\ADiagnostic-Code:[ ]*(.+)\z/)
128
121
  # Diagnostic-Code: 550 5.1.1 <userunknown@example.jp>... User Unknown
129
122
  v['diagnosis'] = cv[1]
130
123
 
131
- elsif p =~ /\A[Dd]iagnostic-[Cc]ode:[ ]*/ && cv = e.match(/\A[ \t]+(.+)\z/)
124
+ elsif p.start_with?('Diagnostic-Code:') && cv = e.match(/\A[ \t]+(.+)\z/)
132
125
  # Continued line of the value of Diagnostic-Code header
133
- v['diagnosis'] ||= ''
134
- v['diagnosis'] += ' ' + cv[1]
135
- havepassed[-1] = 'Diagnostic-Code: ' + e
126
+ v['diagnosis'] << ' ' << cv[1]
127
+ havepassed[-1] = 'Diagnostic-Code: ' << e
136
128
  end
137
129
  end
138
130
  else
@@ -159,17 +151,17 @@ module Sisimai::Bite::Email
159
151
  # in RCPT TO, in MAIL FROM, end of DATA
160
152
  commandtxt = cv[1]
161
153
 
162
- elsif cv = e.match(/\A[Aa]rrival-[Dd]ate:[ ]*(.+)\z/)
154
+ elsif cv = e.match(/\AArrival-Date:[ ]*(.+)\z/)
163
155
  # Arrival-Date: Wed, 29 Apr 2009 16:03:18 +0900
164
156
  next if connheader['date'].size > 0
165
157
  arrivaldate = cv[1]
166
158
 
167
- if cv = e.match(/\A[Aa]rrival-[Dd]ate: (\d{4})[-](\d{2})[-](\d{2}) (\d{2})[-](\d{2})[-](\d{2})\z/)
159
+ if cv = e.match(/\AArrival-Date: (\d{4})[-](\d{2})[-](\d{2}) (\d{2})[-](\d{2})[-](\d{2})\z/)
168
160
  # Arrival-Date: 2011-08-12 01-05-05
169
- arrivaldate += 'Thu, ' + cv[3] + ' '
170
- arrivaldate += Sisimai::DateTime.monthname(0)[cv[2].to_i - 1]
171
- arrivaldate += ' ' + cv[1] + ' ' + [cv[4], cv[5], cv[6]].join(':')
172
- arrivaldate += ' ' + Sisimai::DateTime.abbr2tz('CDT')
161
+ arrivaldate << 'Thu, ' << cv[3] + ' '
162
+ arrivaldate << Sisimai::DateTime.monthname(0)[cv[2].to_i - 1]
163
+ arrivaldate << ' ' << cv[1] + ' ' << [cv[4], cv[5], cv[6]].join(':')
164
+ arrivaldate << ' ' << Sisimai::DateTime.abbr2tz('CDT')
173
165
  end
174
166
  connheader['date'] = arrivaldate
175
167
  connvalues += 1
@@ -177,21 +169,20 @@ module Sisimai::Bite::Email
177
169
  end
178
170
  end
179
171
  end
180
-
181
172
  return nil if recipients.zero?
173
+
182
174
  require 'sisimai/string'
183
175
  require 'sisimai/smtp/status'
184
-
185
176
  dscontents.map do |e|
186
177
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
187
178
 
188
179
  # Get the value of SMTP status code as a pseudo D.S.N.
189
180
  if cv = e['diagnosis'].match(/\b([45])\d\d[ \t]*/)
190
181
  # 4xx or 5xx
191
- e['status'] = sprintf('%d.0.0', cv[1])
182
+ e['status'] = cv[1] + '.0.0'
192
183
  end
193
184
 
194
- if e['status'] =~ /[45][.]0[.]0/
185
+ if e['status'] == '5.0.0' || e['status'] == '4.0.0'
195
186
  # Get the value of D.S.N. from the error message or the value of
196
187
  # Diagnostic-Code header.
197
188
  pseudostatus = Sisimai::SMTP::Status.find(e['diagnosis'])
@@ -201,7 +192,7 @@ module Sisimai::Bite::Email
201
192
  if e['action'] == 'expired'
202
193
  # Action: expired
203
194
  e['reason'] = 'expired'
204
- if !e['status'] || e['status'] =~ /[45][.]0[.]0/
195
+ if !e['status'] || e['status'].end_with?('.0.0')
205
196
  # Set pseudo Status code value if the value of Status is not
206
197
  # defined or 4.0.0 or 5.0.0.
207
198
  pseudostatus = Sisimai::SMTP::Status.code('expired')