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,59 +6,46 @@ module Sisimai::Bite::Email
6
6
  # Imported from p5-Sisimail/lib/Sisimai/Bite/Email/OpenSMTPD.pm
7
7
  require 'sisimai/bite/email'
8
8
 
9
- Re0 = {
10
- :from => %r/\AMailer Daemon [<][^ ]+[@]/,
11
- :subject => %r/\ADelivery status notification/,
12
- :received => %r/[ ][(]OpenSMTPD[)][ ]with[ ]/,
13
- }.freeze
14
- # http://www.openbsd.org/cgi-bin/man.cgi?query=smtpd&sektion=8
15
- # opensmtpd-5.4.2p1/smtpd/
16
- # bounce.c/317:#define NOTICE_INTRO \
17
- # bounce.c/318: " Hi!\n\n" \
18
- # bounce.c/319: " This is the MAILER-DAEMON, please DO NOT REPLY to this e-mail.\n"
19
- # bounce.c/320:
20
- # bounce.c/321:const char *notice_error =
21
- # bounce.c/322: " An error has occurred while attempting to deliver a message for\n"
22
- # bounce.c/323: " the following list of recipients:\n\n";
23
- # bounce.c/324:
24
- # bounce.c/325:const char *notice_warning =
25
- # bounce.c/326: " A message is delayed for more than %s for the following\n"
26
- # bounce.c/327: " list of recipients:\n\n";
27
- # bounce.c/328:
28
- # bounce.c/329:const char *notice_warning2 =
29
- # bounce.c/330: " Please note that this is only a temporary failure report.\n"
30
- # bounce.c/331: " The message is kept in the queue for up to %s.\n"
31
- # bounce.c/332: " You DO NOT NEED to re-send the message to these recipients.\n\n";
32
- # bounce.c/333:
33
- # bounce.c/334:const char *notice_success =
34
- # bounce.c/335: " Your message was successfully delivered to these recipients.\n\n";
35
- # bounce.c/336:
36
- # bounce.c/337:const char *notice_relay =
37
- # bounce.c/338: " Your message was relayed to these recipients.\n\n";
38
- # bounce.c/339:
39
- Re1 = {
40
- :begin => %r/\A[ \t]*This is the MAILER-DAEMON, please DO NOT REPLY to this e[-]?mail[.]\z/,
41
- :rfc822 => %r/\A[ \t]*Below is a copy of the original message:\z/,
42
- :endof => %r/\A__END_OF_EMAIL_MESSAGE__\z/,
9
+ Indicators = Sisimai::Bite::Email.INDICATORS
10
+ StartingOf = {
11
+ # http://www.openbsd.org/cgi-bin/man.cgi?query=smtpd&sektion=8
12
+ # opensmtpd-5.4.2p1/smtpd/
13
+ # bounce.c/317:#define NOTICE_INTRO \
14
+ # bounce.c/318: " Hi!\n\n" \
15
+ # bounce.c/319: " This is the MAILER-DAEMON, please DO NOT REPLY to this e-mail.\n"
16
+ # bounce.c/320:
17
+ # bounce.c/321:const char *notice_error =
18
+ # bounce.c/322: " An error has occurred while attempting to deliver a message for\n"
19
+ # bounce.c/323: " the following list of recipients:\n\n";
20
+ # bounce.c/324:
21
+ # bounce.c/325:const char *notice_warning =
22
+ # bounce.c/326: " A message is delayed for more than %s for the following\n"
23
+ # bounce.c/327: " list of recipients:\n\n";
24
+ # bounce.c/328:
25
+ # bounce.c/329:const char *notice_warning2 =
26
+ # bounce.c/330: " Please note that this is only a temporary failure report.\n"
27
+ # bounce.c/331: " The message is kept in the queue for up to %s.\n"
28
+ # bounce.c/332: " You DO NOT NEED to re-send the message to these recipients.\n\n";
29
+ # bounce.c/333:
30
+ # bounce.c/334:const char *notice_success =
31
+ # bounce.c/335: " Your message was successfully delivered to these recipients.\n\n";
32
+ # bounce.c/336:
33
+ # bounce.c/337:const char *notice_relay =
34
+ # bounce.c/338: " Your message was relayed to these recipients.\n\n";
35
+ # bounce.c/339:
36
+ message: [' This is the MAILER-DAEMON, please DO NOT REPLY to this '],
37
+ rfc822: [' Below is a copy of the original message:'],
43
38
  }.freeze
44
- ReFailure = {
45
- expired: %r{
46
- # smtpd/queue.c:221| envelope_set_errormsg(&evp, "Envelope expired");
47
- Envelope[ ]expired
48
- }x,
49
- hostunknown: %r{(?:
50
- # smtpd/mta.c:976| relay->failstr = "Invalid domain name";
51
- Invalid[ ]domain[ ]name
52
- # smtpd/mta.c:980| relay->failstr = "Domain does not exist";
53
- |Domain[ ]does[ ]not[ ]exist
54
- )
55
- }x,
56
- notaccept: %r{
57
- # smtp/mta.c:1085| relay->failstr = "Destination seem to reject all mails";
58
- Destination[ ]seem[ ]to[ ]reject[ ]all[ ]mails
59
- }x,
39
+ ReFailures = {
40
+ # smtpd/queue.c:221| envelope_set_errormsg(&evp, "Envelope expired");
41
+ expired: %r/Envelope expired/,
42
+ # smtpd/mta.c:976| relay->failstr = "Invalid domain name";
43
+ # smtpd/mta.c:980| relay->failstr = "Domain does not exist";
44
+ hostunknown: %r/(?:Invalid domain name|Domain does not exist)/,
45
+ # smtp/mta.c:1085| relay->failstr = "Destination seem to reject all mails";
46
+ notaccept: %r/Destination seem to reject all mails/,
47
+ # smtpd/mta.c:972| relay->failstr = "Temporary failure in MX lookup";
60
48
  networkerror: %r{(?>
61
- # smtpd/mta.c:972| relay->failstr = "Temporary failure in MX lookup";
62
49
  Address[ ]family[ ]mismatch[ ]on[ ]destination[ ]MXs
63
50
  |All[ ]routes[ ]to[ ]destination[ ]blocked
64
51
  |bad[ ]DNS[ ]lookup[ ]error[ ]code
@@ -72,17 +59,13 @@ module Sisimai::Bite::Email
72
59
  |Temporary[ ]failure[ ]in[ ]MX[ ]lookup
73
60
  )
74
61
  }x,
75
- securityerror: %r{
76
- # smtpd/mta.c:1013| relay->failstr = "Could not retrieve credentials";
77
- Could[ ]not[ ]retrieve[ ]credentials
78
- }x,
62
+ # smtpd/mta.c:1013| relay->failstr = "Could not retrieve credentials";
63
+ securityerror: %r/Could not retrieve credentials/,
79
64
  }.freeze
80
- Indicators = Sisimai::Bite::Email.INDICATORS
81
65
 
82
66
  def description; return 'OpenSMTPD'; end
83
67
  def smtpagent; return Sisimai::Bite.smtpagent(self); end
84
68
  def headerlist; return []; end
85
- def pattern; return Re0; end
86
69
 
87
70
  # Parse bounce messages from OpenSMTPD
88
71
  # @param [Hash] mhead Message headers of a bounce email
@@ -98,9 +81,10 @@ module Sisimai::Bite::Email
98
81
  def scan(mhead, mbody)
99
82
  return nil unless mhead
100
83
  return nil unless mbody
101
- return nil unless mhead['subject'] =~ Re0[:subject]
102
- return nil unless mhead['from'] =~ Re0[:from]
103
- return nil unless mhead['received'].find { |a| a =~ Re0[:received] }
84
+
85
+ return nil unless mhead['subject'].start_with?('Delivery status notification')
86
+ return nil unless mhead['from'].start_with?('Mailer Daemon <')
87
+ return nil unless mhead['received'].find { |a| a.include?(' (OpenSMTPD) with ') }
104
88
 
105
89
  dscontents = [Sisimai::Bite.DELIVERYSTATUS]
106
90
  hasdivided = mbody.split("\n")
@@ -110,10 +94,10 @@ module Sisimai::Bite::Email
110
94
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
111
95
  v = nil
112
96
 
113
- hasdivided.each do |e|
97
+ while e = hasdivided.shift do
114
98
  if readcursor.zero?
115
99
  # Beginning of the bounce message or delivery status part
116
- if e =~ Re1[:begin]
100
+ if e.include?(StartingOf[:message][0])
117
101
  readcursor |= Indicators[:deliverystatus]
118
102
  next
119
103
  end
@@ -121,7 +105,7 @@ module Sisimai::Bite::Email
121
105
 
122
106
  if (readcursor & Indicators[:'message-rfc822']).zero?
123
107
  # Beginning of the original message part
124
- if e =~ Re1[:rfc822]
108
+ if e.include?(StartingOf[:rfc822][0])
125
109
  readcursor |= Indicators[:'message-rfc822']
126
110
  next
127
111
  end
@@ -135,7 +119,6 @@ module Sisimai::Bite::Email
135
119
  next
136
120
  end
137
121
  rfc822list << e
138
-
139
122
  else
140
123
  # Before "message/rfc822"
141
124
  next if (readcursor & Indicators[:deliverystatus]).zero?
@@ -167,15 +150,15 @@ module Sisimai::Bite::Email
167
150
  end
168
151
  end
169
152
  return nil if recipients.zero?
170
- require 'sisimai/string'
171
153
 
154
+ require 'sisimai/string'
172
155
  dscontents.map do |e|
173
156
  e['agent'] = self.smtpagent
174
157
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
175
158
 
176
- ReFailure.each_key do |r|
159
+ ReFailures.each_key do |r|
177
160
  # Verify each regular expression of session errors
178
- next unless e['diagnosis'] =~ ReFailure[r]
161
+ next unless e['diagnosis'] =~ ReFailures[r]
179
162
  e['reason'] = r.to_s
180
163
  break
181
164
  end
@@ -7,22 +7,15 @@ module Sisimai::Bite::Email
7
7
  # Imported from p5-Sisimail/lib/Sisimai/Bite/Email/US/Outlook.pm
8
8
  require 'sisimai/bite/email'
9
9
 
10
- Re0 = {
11
- :from => %r/postmaster[@]/,
12
- :subject => %r/Delivery Status Notification/,
13
- :received => %r/.+[.]hotmail[.]com\b/,
14
- }.freeze
15
- Re1 = {
16
- :begin => %r/\AThis is an automatically generated Delivery Status Notification/,
17
- :error => %r/\A[.]+ while talking to .+[:]\z/,
18
- :rfc822 => %r|\AContent-Type: message/rfc822\z|,
19
- :endof => %r/\A__END_OF_EMAIL_MESSAGE__\z/,
10
+ Indicators = Sisimai::Bite::Email.INDICATORS
11
+ StartingOf = {
12
+ message: ['This is an automatically generated Delivery Status Notification'],
13
+ rfc822: ['Content-Type: message/rfc822'],
20
14
  }.freeze
21
- ReFailure = {
15
+ ReFailures = {
22
16
  hostunknown: %r/The mail could not be delivered to the recipient because the domain is not reachable/,
23
17
  userunknown: %r/Requested action not taken: mailbox unavailable/,
24
18
  }.freeze
25
- Indicators = Sisimai::Bite::Email.INDICATORS
26
19
 
27
20
  def description; return 'Microsoft Outlook.com: https://www.outlook.com/'; end
28
21
  def smtpagent; return Sisimai::Bite.smtpagent(self); end
@@ -30,7 +23,6 @@ module Sisimai::Bite::Email
30
23
  # X-Message-Delivery: Vj0xLjE7RD0wO0dEPTA7U0NMPTk7bD0xO3VzPTE=
31
24
  # X-Message-Info: AuEzbeVr9u5fkDpn2vR5iCu5wb6HBeY4iruBjnutBzpStnUabbM...
32
25
  def headerlist; return ['X-Message-Delivery', 'X-Message-Info']; end
33
- def pattern; return Re0; end
34
26
 
35
27
  # Parse bounce messages from Microsoft Outlook.com
36
28
  # @param [Hash] mhead Message headers of a bounce email
@@ -47,11 +39,12 @@ module Sisimai::Bite::Email
47
39
  return nil unless mhead
48
40
  return nil unless mbody
49
41
 
42
+ # :from => %r/postmaster[@]/,
50
43
  match = 0
51
- match += 1 if mhead['subject'] =~ Re0[:subject]
44
+ match += 1 if mhead['subject'].include?('Delivery Status Notification')
52
45
  match += 1 if mhead['x-message-delivery']
53
46
  match += 1 if mhead['x-message-info']
54
- match += 1 if mhead['received'].find { |a| a =~ Re0[:received] }
47
+ match += 1 if mhead['received'].find { |a| a.include?('.hotmail.com') }
55
48
  return nil if match < 2
56
49
 
57
50
  dscontents = [Sisimai::Bite.DELIVERYSTATUS]
@@ -68,14 +61,14 @@ module Sisimai::Bite::Email
68
61
  }
69
62
  v = nil
70
63
 
71
- hasdivided.each do |e|
64
+ while e = hasdivided.shift do
72
65
  # Save the current line for the next loop
73
66
  havepassed << e
74
67
  p = havepassed[-2]
75
68
 
76
69
  if readcursor.zero?
77
70
  # Beginning of the bounce message or delivery status part
78
- if e =~ Re1[:begin]
71
+ if e.start_with?(StartingOf[:message][0])
79
72
  readcursor |= Indicators[:deliverystatus]
80
73
  next
81
74
  end
@@ -83,7 +76,7 @@ module Sisimai::Bite::Email
83
76
 
84
77
  if (readcursor & Indicators[:'message-rfc822']).zero?
85
78
  # Beginning of the original message part
86
- if e =~ Re1[:rfc822]
79
+ if e == StartingOf[:rfc822][0]
87
80
  readcursor |= Indicators[:'message-rfc822']
88
81
  next
89
82
  end
@@ -97,7 +90,6 @@ module Sisimai::Bite::Email
97
90
  next
98
91
  end
99
92
  rfc822list << e
100
-
101
93
  else
102
94
  # Before "message/rfc822"
103
95
  next if (readcursor & Indicators[:deliverystatus]).zero?
@@ -116,7 +108,7 @@ module Sisimai::Bite::Email
116
108
  # Diagnostic-Code: smtp;550 5.2.2 <kijitora@example.jp>... Mailbox Full
117
109
  v = dscontents[-1]
118
110
 
119
- if cv = e.match(/\A[Ff]inal-[Rr]ecipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/)
111
+ if cv = e.match(/\AFinal-Recipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/)
120
112
  # Final-Recipient: rfc822;kijitora@example.jp
121
113
  if v['recipient']
122
114
  # There are multiple recipient addresses in the message body.
@@ -126,39 +118,36 @@ module Sisimai::Bite::Email
126
118
  v['recipient'] = cv[1]
127
119
  recipients += 1
128
120
 
129
- elsif cv = e.match(/\A[Aa]ction:[ ]*(.+)\z/)
121
+ elsif cv = e.match(/\AAction:[ ]*(.+)\z/)
130
122
  # Action: failed
131
123
  v['action'] = cv[1].downcase
132
124
 
133
- elsif cv = e.match(/\A[Ss]tatus:[ ]*(\d[.]\d+[.]\d+)/)
125
+ elsif cv = e.match(/\AStatus:[ ]*(\d[.]\d+[.]\d+)/)
134
126
  # Status:5.2.0
135
127
  v['status'] = cv[1]
136
-
137
128
  else
138
- if cv = e.match(/\A[Dd]iagnostic-[Cc]ode:[ ]*(.+?);[ ]*(.+)\z/)
129
+ if cv = e.match(/\ADiagnostic-Code:[ ]*(.+?);[ ]*(.+)\z/)
139
130
  # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
140
131
  v['spec'] = cv[1].upcase
141
132
  v['diagnosis'] = cv[2]
142
133
 
143
- elsif p =~ /\A[Dd]iagnostic-[Cc]ode:[ ]*/ && cv = e.match(/\A[ \t]+(.+)\z/)
134
+ elsif p.start_with?('Diagnostic-Code:') && cv = e.match(/\A[ \t]+(.+)\z/)
144
135
  # Continued line of the value of Diagnostic-Code header
145
- v['diagnosis'] ||= ''
146
- v['diagnosis'] += ' ' + cv[1]
147
- havepassed[-1] = 'Diagnostic-Code: ' + e
136
+ v['diagnosis'] << ' ' << cv[1]
137
+ havepassed[-1] = 'Diagnostic-Code: ' << e
148
138
  end
149
-
150
139
  end
151
140
  else
152
141
  # Reporting-MTA: dns;BLU004-OMC3S13.hotmail.example.com
153
142
  # Received-From-MTA: dns;BLU436-SMTP66
154
143
  # Arrival-Date: Fri, 21 Nov 2014 14:17:34 -0800
155
- if cv = e.match(/\A[Rr]eporting-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
144
+ if cv = e.match(/\AReporting-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
156
145
  # Reporting-MTA: dns;BLU004-OMC3S13.hotmail.example.com
157
146
  next if connheader['lhost'].size > 0
158
147
  connheader['lhost'] = cv[1].downcase
159
148
  connvalues += 1
160
149
 
161
- elsif cv = e.match(/\A[Aa]rrival-[Dd]ate:[ ]*(.+)\z/)
150
+ elsif cv = e.match(/\AArrival-Date:[ ]*(.+)\z/)
162
151
  # Arrival-Date: Wed, 29 Apr 2009 16:03:18 +0900
163
152
  next if connheader['date'].size > 0
164
153
  connheader['date'] = cv[1]
@@ -168,8 +157,8 @@ module Sisimai::Bite::Email
168
157
  end
169
158
  end
170
159
  return nil if recipients.zero?
171
- require 'sisimai/string'
172
160
 
161
+ require 'sisimai/string'
173
162
  dscontents.map do |e|
174
163
  # Set default values if each value is empty.
175
164
  connheader.each_key { |a| e[a] ||= connheader[a] || '' }
@@ -181,17 +170,16 @@ module Sisimai::Bite::Email
181
170
  if e['action'] == 'delayed'
182
171
  # Set pseudo diagnostic code message for delaying
183
172
  e['diagnosis'] = 'Delivery to the following recipients has been delayed.'
184
-
185
173
  else
186
174
  # Set pseudo diagnostic code message
187
175
  e['diagnosis'] = 'Unable to deliver message to the following recipients, '
188
- e['diagnosis'] += 'due to being unable to connect successfully to the destination mail server.'
176
+ e['diagnosis'] << 'due to being unable to connect successfully to the destination mail server.'
189
177
  end
190
178
  end
191
179
 
192
- ReFailure.each_key do |r|
180
+ ReFailures.each_key do |r|
193
181
  # Verify each regular expression of session errors
194
- next unless e['diagnosis'] =~ ReFailure[r]
182
+ next unless e['diagnosis'] =~ ReFailures[r]
195
183
  e['reason'] = r.to_s
196
184
  break
197
185
  end
@@ -7,12 +7,10 @@ module Sisimai::Bite::Email
7
7
  require 'sisimai/bite/email'
8
8
 
9
9
  # Postfix manual - bounce(5) - http://www.postfix.org/bounce.5.html
10
- Re0 = {
11
- :from => %r/ [(]Mail Delivery System[)]\z/,
12
- :subject => %r/\AUndelivered Mail Returned to Sender\z/,
13
- }.freeze
14
- Re1 = {
15
- :begin => %r{\A(?>
10
+ Indicators = Sisimai::Bite::Email.INDICATORS
11
+ StartingOf = { rfc822: ['Content-Type: message/rfc822', 'Content-Type: text/rfc822-headers'] }.freeze
12
+ MarkingsOf = {
13
+ message: %r{\A(?>
16
14
  [ ]+The[ ](?:
17
15
  Postfix[ ](?:
18
16
  program\z # The Postfix program
@@ -30,15 +28,11 @@ module Sisimai::Bite::Email
30
28
  )
31
29
  )
32
30
  }x,
33
- :rfc822 => %r!\AContent-Type:[ \t]*(?:message/rfc822|text/rfc822-headers)\z!x,
34
- :endof => %r/\A__END_OF_EMAIL_MESSAGE__\z/,
35
31
  }.freeze
36
- Indicators = Sisimai::Bite::Email.INDICATORS
37
32
 
38
33
  def description; return 'Postfix'; end
39
34
  def smtpagent; return Sisimai::Bite.smtpagent(self); end
40
35
  def headerlist; return []; end
41
- def pattern; return Re0; end
42
36
 
43
37
  # Parse bounce messages from Postfix
44
38
  # @param [Hash] mhead Message headers of a bounce email
@@ -54,7 +48,9 @@ module Sisimai::Bite::Email
54
48
  def scan(mhead, mbody)
55
49
  return nil unless mhead
56
50
  return nil unless mbody
57
- return nil unless mhead['subject'] =~ Re0[:subject]
51
+
52
+ # :from => %r/ [(]Mail Delivery System[)]\z/,
53
+ return nil unless mhead['subject'] == 'Undelivered Mail Returned to Sender'
58
54
 
59
55
  dscontents = [Sisimai::Bite.DELIVERYSTATUS]
60
56
  hasdivided = mbody.split("\n")
@@ -72,14 +68,14 @@ module Sisimai::Bite::Email
72
68
  anotherset = {} # Another error information
73
69
  v = nil
74
70
 
75
- hasdivided.each do |e|
71
+ while e = hasdivided.shift do
76
72
  # Save the current line for the next loop
77
73
  havepassed << e
78
74
  p = havepassed[-2]
79
75
 
80
76
  if readcursor.zero?
81
77
  # Beginning of the bounce message or delivery status part
82
- if e =~ Re1[:begin]
78
+ if e =~ MarkingsOf[:message]
83
79
  readcursor |= Indicators[:deliverystatus]
84
80
  next
85
81
  end
@@ -87,7 +83,7 @@ module Sisimai::Bite::Email
87
83
 
88
84
  if (readcursor & Indicators[:'message-rfc822']).zero?
89
85
  # Beginning of the original message part
90
- if e =~ Re1[:rfc822]
86
+ if e.start_with?(StartingOf[:rfc822][0], StartingOf[:rfc822][1])
91
87
  readcursor |= Indicators[:'message-rfc822']
92
88
  next
93
89
  end
@@ -101,7 +97,6 @@ module Sisimai::Bite::Email
101
97
  next
102
98
  end
103
99
  rfc822list << e
104
-
105
100
  else
106
101
  # Before "message/rfc822"
107
102
  next if (readcursor & Indicators[:deliverystatus]).zero?
@@ -116,7 +111,7 @@ module Sisimai::Bite::Email
116
111
  # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
117
112
  # Last-Attempt-Date: Fri, 14 Feb 2014 12:30:08 -0500
118
113
  v = dscontents[-1]
119
- if cv = e.match(/\A[Ff]inal-[Rr]ecipient:[ ]*(?:RFC|rfc)822;[ ]*(.+)\z/)
114
+ if cv = e.match(/\AFinal-Recipient:[ ]*(?:RFC|rfc)822;[ ]*(.+)\z/)
120
115
  # Final-Recipient: RFC822; userunknown@example.jp
121
116
  if v['recipient']
122
117
  # There are multiple recipient addresses in the message body.
@@ -126,27 +121,27 @@ module Sisimai::Bite::Email
126
121
  v['recipient'] = cv[1]
127
122
  recipients += 1
128
123
 
129
- elsif cv = e.match(/\A[Xx]-[Aa]ctual-[Rr]ecipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/) ||
130
- e.match(/\A[Oo]riginal-[Rr]ecipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/)
124
+ elsif cv = e.match(/\AX-Actual-Recipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/) ||
125
+ e.match(/\AOriginal-Recipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/)
131
126
  # X-Actual-Recipient: RFC822; kijitora@example.co.jp
132
127
  # Original-Recipient: rfc822;kijitora@example.co.jp
133
128
  v['alias'] = cv[1]
134
129
 
135
- elsif cv = e.match(/\A[Aa]ction:[ ]*(.+)\z/)
130
+ elsif cv = e.match(/\AAction:[ ]*(.+)\z/)
136
131
  # Action: failed
137
132
  v['action'] = cv[1].downcase
138
133
 
139
- elsif cv = e.match(/\A[Ss]tatus:[ ]*(\d[.]\d+[.]\d+)/)
134
+ elsif cv = e.match(/\AStatus:[ ]*(\d[.]\d+[.]\d+)/)
140
135
  # Status: 5.1.1
141
136
  # Status:5.2.0
142
137
  # Status: 5.1.0 (permanent failure)
143
138
  v['status'] = cv[1]
144
139
 
145
- elsif cv = e.match(/\A[Rr]emote-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
140
+ elsif cv = e.match(/\ARemote-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
146
141
  # Remote-MTA: DNS; mx.example.jp
147
142
  v['rhost'] = cv[1].downcase
148
143
 
149
- elsif cv = e.match(/\A[Ll]ast-[Aa]ttempt-[Dd]ate:[ ]*(.+)\z/)
144
+ elsif cv = e.match(/\ALast-Attempt-Date:[ ]*(.+)\z/)
150
145
  # Last-Attempt-Date: Fri, 14 Feb 2014 12:30:08 -0500
151
146
  #
152
147
  # src/bounce/bounce_notify_util.c:
@@ -156,22 +151,19 @@ module Sisimai::Bite::Email
156
151
  # 684 mail_date(dsn->time));
157
152
  # 685 #endif
158
153
  v['date'] = cv[1]
159
-
160
154
  else
161
- if cv = e.match(/\A[Dd]iagnostic-[Cc]ode:[ ]*(.+?);[ ]*(.+)\z/)
155
+ if cv = e.match(/\ADiagnostic-Code:[ ]*(.+?);[ ]*(.+)\z/)
162
156
  # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
163
157
  v['spec'] = cv[1].upcase
164
158
  v['diagnosis'] = cv[2]
165
159
  v['spec'] = 'SMTP' if v['spec'] == 'X-POSTFIX'
166
160
 
167
- elsif p =~ /\A[Dd]iagnostic-[Cc]ode:[ ]*/ && cv = e.match(/\A[ \t]+(.+)\z/)
161
+ elsif p.start_with?('Diagnostic-Code:') && cv = e.match(/\A[ \t]+(.+)\z/)
168
162
  # Continued line of the value of Diagnostic-Code header
169
- v['diagnosis'] ||= ''
170
- v['diagnosis'] += ' ' + cv[1]
171
- havepassed[-1] = 'Diagnostic-Code: ' + e
163
+ v['diagnosis'] << ' ' << cv[1]
164
+ havepassed[-1] = 'Diagnostic-Code: ' << e
172
165
  end
173
166
  end
174
-
175
167
  else
176
168
  # If you do so, please include this problem report. You can
177
169
  # delete your own text from the attached returned message.
@@ -185,22 +177,22 @@ module Sisimai::Bite::Email
185
177
  # 5.1.1 <userunknown@example.co.jp>... User Unknown (in reply to RCPT TO
186
178
  commandset << cv[1]
187
179
  anotherset['diagnosis'] ||= ''
188
- anotherset['diagnosis'] += ' ' + e
180
+ anotherset['diagnosis'] << ' ' << e
189
181
 
190
182
  elsif cv = e.match(/([A-Z]{4})[ \t]*.*command[)]\z/)
191
183
  # to MAIL command)
192
184
  commandset << cv[1]
193
185
  anotherset['diagnosis'] ||= ''
194
- anotherset['diagnosis'] += ' ' + e
186
+ anotherset['diagnosis'] << ' ' << e
195
187
 
196
188
  else
197
- if cv = e.match(/\A[Rr]eporting-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
189
+ if cv = e.match(/\AReporting-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
198
190
  # Reporting-MTA: dns; mx.example.jp
199
191
  next if connheader['lhost'].size > 0
200
192
  connheader['lhost'] = cv[1].downcase
201
193
  connvalues += 1
202
194
 
203
- elsif cv = e.match(/\A[Aa]rrival-[Dd]ate:[ ]*(.+)\z/)
195
+ elsif cv = e.match(/\AArrival-Date:[ ]*(.+)\z/)
204
196
  # Arrival-Date: Wed, 29 Apr 2009 16:03:18 +0900
205
197
  next if connheader['date'].size > 0
206
198
  connheader['date'] = cv[1]
@@ -208,8 +200,7 @@ module Sisimai::Bite::Email
208
200
 
209
201
  elsif cv = e.match(/\A(X-Postfix-Sender):[ ]*rfc822;[ ]*(.+)\z/)
210
202
  # X-Postfix-Sender: rfc822; shironeko@example.org
211
- rfc822list << sprintf('%s: %s', cv[1], cv[2])
212
-
203
+ rfc822list << (cv[1] << ': ' << cv[2])
213
204
  else
214
205
  # Alternative error message and recipient
215
206
  if cv = e.match(/\A[<]([^ ]+[@][^ ]+)[>] [(]expanded from [<](.+)[>][)]:[ \t]*(.+)\z/)
@@ -222,16 +213,14 @@ module Sisimai::Bite::Email
222
213
  # <kijitora@exmaple.jp>: ...
223
214
  anotherset['recipient'] = cv[1]
224
215
  anotherset['diagnosis'] = cv[2]
225
-
226
216
  else
227
217
  # Get error message continued from the previous line
228
218
  next unless anotherset['diagnosis']
229
219
  if e =~ /\A[ \t]{4}(.+)\z/
230
220
  # host mx.example.jp said:...
231
- anotherset['diagnosis'] += ' ' + e
221
+ anotherset['diagnosis'] << ' ' << e
232
222
  end
233
223
  end
234
-
235
224
  end
236
225
  end
237
226
  end
@@ -240,7 +229,7 @@ module Sisimai::Bite::Email
240
229
 
241
230
  if recipients.zero?
242
231
  # Fallback: set recipient address from error message
243
- if anotherset['recipient'] && anotherset['recipient'].size > 0
232
+ if anotherset['recipient'].to_s.size > 0
244
233
  # Set recipient address
245
234
  dscontents[-1]['recipient'] = anotherset['recipient']
246
235
  recipients += 1
@@ -251,7 +240,6 @@ module Sisimai::Bite::Email
251
240
  require 'sisimai/string'
252
241
  require 'sisimai/smtp/status'
253
242
  require 'sisimai/smtp/reply'
254
-
255
243
  dscontents.map do |e|
256
244
  # Set default values if each value is empty.
257
245
  connheader.each_key { |a| e[a] ||= connheader[a] || '' }
@@ -270,7 +258,10 @@ module Sisimai::Bite::Email
270
258
  as = nil # status
271
259
  ar = nil # replycode
272
260
 
273
- if e['status'] == '' || e['status'] =~ /\A[45][.]0[.]0\z/
261
+ e['status'] ||= ''
262
+ e['replycode'] ||= ''
263
+
264
+ if e['status'] == '' || e['status'].start_with?('4.0.0', '5.0.0')
274
265
  # Check the value of D.S.N. in anotherset
275
266
  as = Sisimai::SMTP::Status.find(anotherset['diagnosis'])
276
267
  if as.size > 0 && as[-3, 3] != '0.0'
@@ -279,7 +270,7 @@ module Sisimai::Bite::Email
279
270
  end
280
271
  end
281
272
 
282
- if e['replycode'] == '' || e['replycode'] =~ /\A[45]00\z/
273
+ if e['replycode'] == '' || e['replycode'].start_with?('400', '500')
283
274
  # Check the value of SMTP reply code in anotherset
284
275
  ar = Sisimai::SMTP::Reply.find(anotherset['diagnosis'])
285
276
  if ar.size > 0 && ar[-2, 2].to_i != 0