sisimai 5.6.0-java → 5.7.0-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rake-test.yml +0 -4
  3. data/ChangeLog.md +32 -0
  4. data/README-JA.md +19 -16
  5. data/README.md +23 -21
  6. data/lib/sisimai/address.rb +8 -31
  7. data/lib/sisimai/arf.rb +3 -5
  8. data/lib/sisimai/fact.rb +12 -37
  9. data/lib/sisimai/lhost/activehunter.rb +0 -2
  10. data/lib/sisimai/lhost/amazonses.rb +6 -8
  11. data/lib/sisimai/lhost/apachejames.rb +0 -1
  12. data/lib/sisimai/lhost/biglobe.rb +0 -16
  13. data/lib/sisimai/lhost/courier.rb +5 -4
  14. data/lib/sisimai/lhost/deutschetelekom.rb +120 -0
  15. data/lib/sisimai/lhost/domino.rb +0 -3
  16. data/lib/sisimai/lhost/dragonfly.rb +0 -27
  17. data/lib/sisimai/lhost/einsundeins.rb +1 -10
  18. data/lib/sisimai/lhost/exchange2003.rb +4 -4
  19. data/lib/sisimai/lhost/exchange2007.rb +3 -4
  20. data/lib/sisimai/lhost/exim.rb +30 -78
  21. data/lib/sisimai/lhost/ezweb.rb +12 -49
  22. data/lib/sisimai/lhost/fml.rb +4 -29
  23. data/lib/sisimai/lhost/gmail.rb +0 -23
  24. data/lib/sisimai/lhost/gmx.rb +7 -24
  25. data/lib/sisimai/lhost/googlegroups.rb +3 -3
  26. data/lib/sisimai/lhost/googleworkspace.rb +0 -4
  27. data/lib/sisimai/lhost/imailserver.rb +3 -9
  28. data/lib/sisimai/lhost/kddi.rb +6 -20
  29. data/lib/sisimai/lhost/mailfoundry.rb +0 -2
  30. data/lib/sisimai/lhost/mailmarshal.rb +1 -3
  31. data/lib/sisimai/lhost/messagingserver.rb +4 -15
  32. data/lib/sisimai/lhost/mfilter.rb +0 -1
  33. data/lib/sisimai/lhost/mimecast.rb +0 -1
  34. data/lib/sisimai/lhost/notes.rb +1 -2
  35. data/lib/sisimai/lhost/opensmtpd.rb +0 -40
  36. data/lib/sisimai/lhost/postfix.rb +10 -11
  37. data/lib/sisimai/lhost/qmail.rb +14 -81
  38. data/lib/sisimai/lhost/sendmail.rb +4 -4
  39. data/lib/sisimai/lhost/trendmicro.rb +3 -3
  40. data/lib/sisimai/lhost/v5sendmail.rb +0 -1
  41. data/lib/sisimai/lhost/verizon.rb +1 -2
  42. data/lib/sisimai/lhost/x1.rb +1 -2
  43. data/lib/sisimai/lhost/x2.rb +0 -2
  44. data/lib/sisimai/lhost/x3.rb +4 -9
  45. data/lib/sisimai/lhost/x6.rb +0 -1
  46. data/lib/sisimai/lhost/zoho.rb +0 -12
  47. data/lib/sisimai/lhost.rb +38 -19
  48. data/lib/sisimai/message.rb +1 -1
  49. data/lib/sisimai/order.rb +4 -1
  50. data/lib/sisimai/reason/authfailure.rb +2 -2
  51. data/lib/sisimai/reason/contenterror.rb +2 -0
  52. data/lib/sisimai/reason/emailtoolarge.rb +1 -1
  53. data/lib/sisimai/reason/expired.rb +13 -2
  54. data/lib/sisimai/reason/hostunknown.rb +9 -0
  55. data/lib/sisimai/reason/mailboxfull.rb +3 -2
  56. data/lib/sisimai/reason/networkerror.rb +13 -1
  57. data/lib/sisimai/reason/norelaying.rb +4 -3
  58. data/lib/sisimai/reason/notaccept.rb +10 -3
  59. data/lib/sisimai/reason/notcompliantrfc.rb +1 -0
  60. data/lib/sisimai/reason/policyviolation.rb +6 -1
  61. data/lib/sisimai/reason/rejected.rb +6 -0
  62. data/lib/sisimai/reason/securityerror.rb +1 -0
  63. data/lib/sisimai/reason/suspend.rb +4 -0
  64. data/lib/sisimai/reason/syntaxerror.rb +1 -8
  65. data/lib/sisimai/reason/systemerror.rb +18 -0
  66. data/lib/sisimai/reason/userunknown.rb +2 -0
  67. data/lib/sisimai/reason.rb +7 -7
  68. data/lib/sisimai/rfc1123.rb +1 -1
  69. data/lib/sisimai/rfc1894.rb +7 -6
  70. data/lib/sisimai/rfc2045.rb +2 -2
  71. data/lib/sisimai/rfc3464/thirdparty.rb +1 -1
  72. data/lib/sisimai/rfc3464.rb +10 -14
  73. data/lib/sisimai/rfc3834.rb +3 -4
  74. data/lib/sisimai/rfc791.rb +3 -38
  75. data/lib/sisimai/rhost/microsoft.rb +4 -0
  76. data/lib/sisimai/rhost.rb +1 -1
  77. data/lib/sisimai/smtp/status.rb +23 -19
  78. data/lib/sisimai/string.rb +0 -12
  79. data/lib/sisimai/version.rb +1 -1
  80. data/set-of-emails/maildir/bsd/lhost-deutschetelekom-01.eml +66 -0
  81. data/set-of-emails/maildir/bsd/lhost-deutschetelekom-02.eml +68 -0
  82. data/set-of-emails/maildir/bsd/lhost-deutschetelekom-03.eml +50 -0
  83. data/set-of-emails/should-not-crash/p5-664-iomart-mail-filter.eml +258 -0
  84. data/set-of-emails/to-be-debugged-because/sisimai-cannot-parse-yet/.gitkeep +0 -0
  85. metadata +8 -2
@@ -72,7 +72,8 @@ module Sisimai::Lhost
72
72
  next unless o = Sisimai::RFC1894.field(e)
73
73
  v = dscontents[-1]
74
74
 
75
- if o[3] == 'addr'
75
+ case o[3]
76
+ when "addr"
76
77
  # Final-Recipient: rfc822; kijitora@example.jp
77
78
  # X-Actual-Recipient: rfc822; kijitora@example.co.jp
78
79
  if o[0] == 'final-recipient'
@@ -88,7 +89,7 @@ module Sisimai::Lhost
88
89
  # X-Actual-Recipient: rfc822; kijitora@example.co.jp
89
90
  v['alias'] = o[2]
90
91
  end
91
- elsif o[3] == 'code'
92
+ when "code"
92
93
  # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
93
94
  v['spec'] = o[1]
94
95
  v['diagnosis'] = o[2]
@@ -116,7 +117,7 @@ module Sisimai::Lhost
116
117
  # Continued line of the value of Diagnostic-Code field
117
118
  next if readslices[-2].start_with?('Diagnostic-Code:') == false
118
119
  next if e.start_with?(' ') == false
119
- v['diagnosis'] += " #{Sisimai::String.sweep(e)}"
120
+ v['diagnosis'] += " " + e
120
121
  readslices[-1] = "Diagnostic-Code: #{e}"
121
122
  end
122
123
  end
@@ -127,7 +128,7 @@ module Sisimai::Lhost
127
128
  # Set default values if each value is empty.
128
129
  permessage.each_key { |a| e[a] ||= permessage[a] || '' }
129
130
  e['command'] = thecommand if e["command"].empty?
130
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''
131
+ e['diagnosis'] = e['diagnosis'] || ''
131
132
 
132
133
  MessagesOf.each_key do |r|
133
134
  # Verify each regular expression of session errors
@@ -0,0 +1,120 @@
1
+ module Sisimai::Lhost
2
+ # Sisimai::Lhost::DeutscheTelekom decodes a bounce email which created by Deutsche Telekom or
3
+ # T-Online. Methods in the module are called from only Sisimai::Message.
4
+ module DeutscheTelekom
5
+ class << self
6
+ require 'sisimai/lhost'
7
+
8
+ Indicators = Sisimai::Lhost.INDICATORS
9
+ BannerDTAG = Sisimai::Lhost.BannerDTAG
10
+ StartingOf = { message: [BannerDTAG[1]] }.freeze
11
+
12
+ # @abstract Decodes the bounce message from DeutscheTelekom
13
+ # @param [Hash] mhead Message headers of a bounce email
14
+ # @param [String] mbody Message body of a bounce email
15
+ # @return [Hash] Bounce data list and message/rfc822 part
16
+ # @return [Nil] it failed to decode or the arguments are missing
17
+ def inquire(mhead, mbody)
18
+ # - T-Online: https://www.t-online.de/, @t-online.de, @magenta.de
19
+ # - DeutscheTelekom: https://www.telekom.com/
20
+ # - Based on the bounce format of Smail 3, the original design model for Exim
21
+ # - Tailored for Deutsche Telekom's internal Smail 3 fork with custom banners
22
+ # - Module name follows the infrastructure owner for cross-language compatibility
23
+ # - Smail 3: http://www.weird.com/~woods/projects/smail.html
24
+ return nil unless BannerDTAG.any? { |a| mbody.include?(a) }
25
+
26
+ # smail-3.2.0.108/src/
27
+ # notify.c:1052|(void) fprintf(f, "Subject: mail failed, %s\nReference: <%s@%s>\n\n",
28
+ # notify.c:1053| subject_to, message_id, primary_name);
29
+ #
30
+ # T-Online specific headers
31
+ # Received: from mailin42.aul.t-online.de (mailin42.aul.t-online.de [192.51.100.1])
32
+ # by mailout11.t-online.de (Postfix) with SMTP id 05E5A1CAC0
33
+ # From: Mail Delivery System <Mailer-Daemon@t-online.de>
34
+ # X-TOI-MSGID: c9412855-531f-497b-b007-5ffc033877a0
35
+ dscontents = [Sisimai::Lhost.DELIVERYSTATUS]; v = nil
36
+ emailparts = Sisimai::RFC5322.part(mbody, [BannerDTAG[3], BannerDTAG[2]])
37
+ bodyslices = emailparts[0].split("\n")
38
+ messagelog = ''
39
+ readcursor = 0 # (Integer) Points the current cursor position
40
+ recipients = 0 # (Integer) The number of 'Final-Recipient' header
41
+
42
+ while e = bodyslices.shift do
43
+ # Read error messages and delivery status lines from the head of the email to the previous
44
+ # line of the beginning of the original message.
45
+ if readcursor == 0
46
+ # Beginning of the bounce message or delivery status part
47
+ if e.start_with?(StartingOf[:message][0])
48
+ # |------------------------- Failed addresses follow: ---------------------|
49
+ readcursor |= Indicators[:deliverystatus]
50
+ else
51
+ # |------------------------- Message log follows: -------------------------|
52
+ # The line above may appears only in Smail 3.
53
+ #
54
+ # smail-3.2.0.108/src/
55
+ # models.c:787| if (deliver == NULL && defer == NULL) {
56
+ # models.c:788| write_log(WRITE_LOG_MLOG, "no valid recipients were found for this message");
57
+ # models.c:789| return_to_sender = TRUE;
58
+ # models.c:879| }
59
+ messagelog += ' ' + e if e != "" && e.include?(BannerDTAG[0]) == false
60
+ end
61
+ next
62
+ end
63
+ next if (readcursor & Indicators[:deliverystatus]) == 0 || e.empty?
64
+
65
+ # |------------------------- Failed addresses follow: ---------------------|
66
+ # <example@t-online.de>
67
+ # 552 5.2.2 <example@t-online.de> Quota exceeded (mailbox for user is full)
68
+ #
69
+ # |------------------------- Message header follows: ----------------------|
70
+ # Received: from mail.fragdenstaat.de ([94.130.55.89]) by mailin41.mgt.mul.t-online.de.example.com
71
+ # with (TLSv1.3:TLS_AES_256_GCM_SHA384 encrypted)
72
+ # ...
73
+ v = dscontents[-1]
74
+
75
+
76
+ if e.start_with?(' <') && e.end_with?('>') && e.count(' ') == 1
77
+ # Deutsche Telekom: The recipient address is enclosed in angle brackets.
78
+ # |------------------------- Failed addresses follow: ---------------------|
79
+ if v["recipient"] != ""
80
+ # There are multiple recipient addresses in the message body.
81
+ dscontents << Sisimai::Lhost.DELIVERYSTATUS
82
+ v = dscontents[-1]
83
+ end
84
+ v['recipient'] = e[2, e.size]
85
+ recipients += 1
86
+
87
+ elsif Sisimai::String.aligned(e, [' ', '@', '.', ' ... '])
88
+ # Smail 3:
89
+ # - The recipient address is not enclosed in angle brackets.
90
+ # - Error message begins with " ... failed:"
91
+ # smail-3.2.0.108/src/
92
+ # notify.c:845| if (cur->error) {
93
+ # notify.c:846| (void) fprintf(f, " %s ... failed: %s\n",
94
+ # notify.c:847| cur->in_addr ? cur->in_addr : "(unknown)",
95
+ # notify.c:848| cur->error->message);
96
+ # notify.c:849| }
97
+ # |------------------------- Failed addresses follow: ---------------------|
98
+ # kijitora@neko.nyaan.example.com ... unknown host
99
+ if v["recipient"] != ""
100
+ # There are multiple recipient addresses in the message body.
101
+ dscontents << Sisimai::Lhost.DELIVERYSTATUS
102
+ v = dscontents[-1]
103
+ end
104
+ v['recipient'] = e[1, e.index(' ... ')]
105
+ v['diagnosis'] = messagelog << ' ' + e
106
+ recipients += 1
107
+
108
+ else
109
+ # 552 5.2.2 <example@t-online.de> Quota exceeded (mailbox for user is full)
110
+ v['diagnosis'] = e
111
+ end
112
+ end
113
+ return nil if recipients == 0
114
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
115
+ end
116
+ def description; return 'DeutscheTelekom'; end
117
+ end
118
+ end
119
+ end
120
+
@@ -9,8 +9,6 @@ module Sisimai::Lhost
9
9
  Boundaries = ['Content-Type: message/rfc822'].freeze
10
10
  StartingOf = {message: ['Your message']}.freeze
11
11
  MessagesOf = {
12
- "filtered" => ["Cannot route mail to user"],
13
- "systemerror" => ["Several matches found in Domino Directory"],
14
12
  "userunknown" => [
15
13
  "not listed in Domino Directory",
16
14
  "not listed in public Name & Address Book",
@@ -120,7 +118,6 @@ module Sisimai::Lhost
120
118
  return nil if recipients == 0
121
119
 
122
120
  dscontents.each do |e|
123
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
124
121
  e['recipient'] = Sisimai::Address.s3s4(e['recipient'])
125
122
  permessage.each_key { |a| e[a] ||= permessage[a] || '' }
126
123
 
@@ -14,23 +14,6 @@ module Sisimai::Lhost
14
14
  # https://github.com/corecode/dma/blob/ffad280aa40c242aa9a2cb9ca5b1b6e8efedd17e/mail.c#L84
15
15
  message: ['This is the DragonFly Mail Agent '],
16
16
  }.freeze
17
- MessagesOf = {
18
- 'expired' => [
19
- # https://github.com/corecode/dma/blob/master/dma.c#L370C1-L374C19
20
- # dma.c:370| if (gettimeofday(&now, NULL) == 0 &&
21
- # dma.c:371| (now.tv_sec - st.st_mtim.tv_sec > MAX_TIMEOUT)) {
22
- # dma.c:372| snprintf(errmsg, sizeof(errmsg),
23
- # dma.c:373| "Could not deliver for the last %d seconds. Giving up.",
24
- # dma.c:374| MAX_TIMEOUT);
25
- # dma.c:375| goto bounce;
26
- # dma.c:376| }
27
- 'Could not deliver for the last ',
28
- ],
29
- 'hostunknown' => [
30
- # net.c:663| snprintf(errmsg, sizeof(errmsg), "DNS lookup failure: host %s not found", host);
31
- 'DNS lookup failure: host ',
32
- ],
33
- }.freeze
34
17
 
35
18
  # @abstract Decodes the bounce message from DMA: DragonFly Mail Agent
36
19
  # @param [Hash] mhead Message headers of a bounce email
@@ -92,16 +75,6 @@ module Sisimai::Lhost
92
75
  end
93
76
  end
94
77
  return nil if recipients == 0
95
-
96
- dscontents.each do |e|
97
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
98
- MessagesOf.each_key do |r|
99
- # Verify each regular expression of session errors
100
- next if MessagesOf[r].none? { |a| e['diagnosis'].include?(a) }
101
- e['reason'] = r
102
- break
103
- end
104
- end
105
78
  return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
106
79
  end
107
80
  def description; return 'DragonFly'; end
@@ -11,7 +11,6 @@ module Sisimai::Lhost
11
11
  message: ['This message was created automatically by mail delivery software'],
12
12
  error: ['For the following reason:'],
13
13
  }.freeze
14
- MessagesOf = {'emailtoolarge' => ['Mail size limit exceeded']}.freeze
15
14
 
16
15
  # @abstract Decode the bounce message from 1&1
17
16
  # @param [Hash] mhead Message headers of a bounce email
@@ -90,7 +89,7 @@ module Sisimai::Lhost
90
89
  p1 = e['diagnosis'].index('host: ')
91
90
  p2 = e['diagnosis'].index(' reason:')
92
91
 
93
- e['rhost'] = Sisimai::String.sweep(e['diagnosis'][p1 + 6, p2 - p1 - 6])
92
+ e['rhost'] = e['diagnosis'][p1 + 6, p2 - p1 - 6]
94
93
  e['command'] = 'DATA' if e['diagnosis'].include?('for TEXT command')
95
94
  e['spec'] = 'SMTP' if e['diagnosis'].include?('SMTP error')
96
95
  e['status'] = Sisimai::SMTP::Status.find(e['diagnosis'])
@@ -98,14 +97,6 @@ module Sisimai::Lhost
98
97
  # For the following reason:
99
98
  e['diagnosis'][0, StartingOf[:error][0].size] = ''
100
99
  end
101
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
102
-
103
- MessagesOf.each_key do |r|
104
- # Verify each regular expression of session errors
105
- next if MessagesOf[r].none? { |a| e['diagnosis'].include?(a) }
106
- e['reason'] = r
107
- break
108
- end
109
100
  end
110
101
 
111
102
  return {"ds" => dscontents, "rfc822" => emailparts[1]}
@@ -158,19 +158,20 @@ module Sisimai::Lhost
158
158
  # Subject: ...
159
159
  # Sent: Thu, 29 Apr 2010 18:14:35 +0000
160
160
  #
161
- if e.start_with?(' To: ') || e.start_with?(' To: ')
161
+ case
162
+ when e.start_with?(' To: ', ' To: ')
162
163
  # To: shironeko@example.jp
163
164
  next if connheader['to'].empty? == false
164
165
  connheader['to'] = e[e.rindex(' ') + 1, e.size]
165
166
  connvalues += 1
166
167
 
167
- elsif e.start_with?(' Subject: ') || e.start_with?(' Subject: ')
168
+ when e.start_with?(' Subject: ', ' Subject: ')
168
169
  # Subject: ...
169
170
  next if connheader['subject'].empty? == false
170
171
  connheader['subject'] = e[e.rindex(' ') + 1, e.size]
171
172
  connvalues += 1
172
173
 
173
- elsif e.start_with?(' Sent: ') || e.start_with?(' Sent: ')
174
+ when e.start_with?(' Sent: ', ' Sent: ')
174
175
  # Sent: Thu, 29 Apr 2010 18:14:35 +0000
175
176
  # Sent: 4/29/99 9:19:59 AM
176
177
  next if connheader['date'].empty? == false
@@ -207,7 +208,6 @@ module Sisimai::Lhost
207
208
 
208
209
  # Copy alternative error message
209
210
  e['diagnosis'] = "#{e['alterrors']} #{e['diagnosis']}"
210
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
211
211
  e.delete('alterrors')
212
212
  end
213
213
  end
@@ -64,7 +64,9 @@ module Sisimai::Lhost
64
64
  def inquire(mhead, mbody)
65
65
  proceedsto = 0
66
66
  proceedsto += 1 if EmailTitle.any? { |a| mhead["subject"].include?(a) }
67
- proceedsto += 1 if MailSender.any? { |a| mhead["from"].include?(a) }
67
+ proceedsto += 1 if MailSender.any? { |a| mhead["from"].include?(a) }
68
+ proceedsto += 1 if StartingOf[:error].any? { |a| mbody.include?(a) }
69
+ proceedsto += 1 if StartingOf[:message].any? { |a| mbody.include?(a) }
68
70
  proceedsto += 1 if mhead["content-language"]
69
71
  return nil if proceedsto < 2
70
72
 
@@ -150,9 +152,6 @@ module Sisimai::Lhost
150
152
  return nil if recipients == 0
151
153
 
152
154
  dscontents.each do |e|
153
- # Tidy up the error message in $e->{'diagnosis'}, Try to detect the bounce reason.
154
- e["diagnosis"] = Sisimai::String.sweep(e["diagnosis"])
155
-
156
155
  p0 = -1; StartingOf[:error].each do |r|
157
156
  # Try to find the NDR subject string such as "RESOLVER.ADR.RecipientNotFound" from the
158
157
  # error message
@@ -58,32 +58,9 @@ module Sisimai::Lhost
58
58
  ],
59
59
  }.freeze
60
60
  MessagesOf = {
61
- # find exim/ -type f -exec grep 'message = US' {} /dev/null \;
62
- # route.c:1158| DEBUG(D_uid) debug_printf("getpwnam() returned NULL (user not found)\n");
63
61
  # find exim/ -type f -exec grep 'message = US' {} /dev/null \;
64
62
  # route.c:1158| DEBUG(D_uid) debug_printf("getpwnam() returned NULL (user not found)\n");
65
63
  "userunknown" => ["user not found"],
66
- # transports/smtp.c:3524| addr->message = US"all host address lookups failed permanently";
67
- # routers/dnslookup.c:331| addr->message = US"all relevant MX records point to non-existent hosts";
68
- # route.c:1826| uschar *message = US"Unrouteable address";
69
- "hostunknown" => [
70
- "all host address lookups failed permanently",
71
- "all relevant MX records point to non-existent hosts",
72
- "Unrouteable address",
73
- ],
74
- # transports/appendfile.c:2567| addr->user_message = US"mailbox is full";
75
- # transports/appendfile.c:3049| addr->message = string_sprintf("mailbox is full "
76
- # transports/appendfile.c:3050| "(quota exceeded while writing to file %s)", filename);
77
- "mailboxfull" => [
78
- "mailbox is full",
79
- "error: quota exceed",
80
- ],
81
- # routers/dnslookup.c:328| addr->message = US"an MX or SRV record indicated no SMTP service";
82
- # transports/smtp.c:3502| addr->message = US"no host found for existing SMTP connection";
83
- "notaccept" => [
84
- "an MX or SRV record indicated no SMTP service",
85
- "no host found for existing SMTP connection",
86
- ],
87
64
  # parser.c:666| *errorptr = string_sprintf("%s (expected word or \"<\")", *errorptr);
88
65
  # parser.c:701| if(bracket_count++ > 5) FAILED(US"angle-brackets nested too deep");
89
66
  # parser.c:738| FAILED(US"domain missing in source-routed address");
@@ -94,35 +71,14 @@ module Sisimai::Lhost
94
71
  "domain missing in source-routed address",
95
72
  "malformed address:",
96
73
  ],
97
- # deliver.c:5614| addr->message = US"delivery to file forbidden";
98
- # deliver.c:5624| addr->message = US"delivery to pipe forbidden";
99
- # transports/pipe.c:1156| addr->user_message = US"local delivery failed";
100
- "systemerror" => [
101
- "delivery to file forbidden",
102
- "delivery to pipe forbidden",
103
- "local delivery failed",
104
- "LMTP error after ",
105
- ],
106
- # deliver.c:5425| new->message = US"Too many \"Received\" headers - suspected mail loop";
107
- "contenterror" => ['Too many "Received" headers'],
108
74
  }.freeze
109
75
  DelayedFor = [
110
- # retry.c:902| addr->message = (addr->message == NULL)? US"retry timeout exceeded" :
111
- # deliver.c:7475| "No action is required on your part. Delivery attempts will continue for\n"
112
- # smtp.c:3508| US"retry time not reached for any host after a long failure period" :
113
- # smtp.c:3508| US"all hosts have been failing for a long time and were last tried "
114
- # "after this message arrived";
115
- # deliver.c:7459| print_address_error(addr, f, US"Delay reason: ");
116
- # deliver.c:7586| "Message %s has been frozen%s.\nThe sender is <%s>.\n", message_id,
117
- # receive.c:4021| moan_tell_someone(freeze_tell, NULL, US"Message frozen on arrival",
118
- # receive.c:4022| "Message %s was frozen on arrival by %s.\nThe sender is <%s>.\n",
119
- "retry timeout exceeded",
76
+ # deliver.c:7475| "No action is required on your part. Delivery attempts will continue for\n"
77
+ # smtp.c:3508| US"retry time not reached for any host after a long failure period" :
78
+ # deliver.c:7459| print_address_error(addr, f, US"Delay reason: ");
120
79
  "No action is required on your part",
121
80
  "retry time not reached for any host after a long failure period",
122
- "all hosts have been failing for a long time and were last tried",
123
81
  "Delay reason: ",
124
- "has been frozen",
125
- "was frozen on arrival by ",
126
82
  ].freeze
127
83
 
128
84
  # @abstract Decodes the bounce message from Exim
@@ -133,6 +89,8 @@ module Sisimai::Lhost
133
89
  def inquire(mhead, mbody)
134
90
  # Message-Id: <E1P1YNN-0003AD-Ga@example.org>
135
91
  # X-Failed-Recipients: kijitora@example.ed.jp
92
+ return nil if Sisimai::Lhost.BannerDTAG.any? { |a| mbody.include?(a) }
93
+
136
94
  thirdparty = false
137
95
  proceedsto = 0
138
96
  messageidv = mhead["message-id"] || ""
@@ -255,7 +213,7 @@ module Sisimai::Lhost
255
213
  # parser.c:749| goto PARSE_FAILED;
256
214
  # parser.c:750| }
257
215
  cv = Sisimai::Address.s3s4(e[p1, p2 - p1 - 1])
258
- v["diagnosis"] = Sisimai::String.sweep(e[p2 + 1, e.size])
216
+ v["diagnosis"] = e[p2 + 1, e.size]
259
217
  else
260
218
  # There is an email address only in the line
261
219
  # kijitora@example.jp
@@ -288,7 +246,8 @@ module Sisimai::Lhost
288
246
  # "e" matched with any field defined in RFC3464
289
247
  next unless o = Sisimai::RFC1894.field(e)
290
248
 
291
- if o[3] == "addr"
249
+ case o[3]
250
+ when "addr"
292
251
  # Final-Recipient: rfc822; kijitora@example.jp
293
252
  # X-Actual-Recipient: rfc822; kijitora@example.co.jp
294
253
  next if o[0] != "final-recipient"
@@ -296,7 +255,7 @@ module Sisimai::Lhost
296
255
  v["spec"] = o[2].include?('@') ? "SMTP" : "X-UNIX"
297
256
  end
298
257
 
299
- elsif o[3] == "code"
258
+ when "code"
300
259
  # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
301
260
  v["spec"] = o[1].upcase
302
261
  v["diagnosis"] = o[2]
@@ -341,24 +300,22 @@ module Sisimai::Lhost
341
300
  q["recipient"] = q["alias"]
342
301
  end
343
302
  end
344
- else
303
+ elsif mhead["x-failed-recipients"]
345
304
  # Fallback for getting recipient addresses
346
- if mhead["x-failed-recipients"]
347
- # X-Failed-Recipients: kijitora@example.jp
348
- rcptinhead = mhead["x-failed-recipients"].split(",")
349
- rcptinhead.each do |e|
350
- # Remove space characters
351
- e.lstrip!
352
- e.rstrip!
353
- end
354
- recipients = rcptinhead.size
305
+ # X-Failed-Recipients: kijitora@example.jp
306
+ rcptinhead = mhead["x-failed-recipients"].split(",")
307
+ rcptinhead.each do |e|
308
+ # Remove space characters
309
+ e.lstrip!
310
+ e.rstrip!
311
+ end
312
+ recipients = rcptinhead.size
355
313
 
356
- while e = rcptinhead.shift do
357
- # Insert each recipient address into dscontents
358
- dscontents[-1]["recipient"] = e
359
- next if dscontents.size == recipients
360
- dscontents << Sisimai::Lhost.DELIVERYSTATUS
361
- end
314
+ while e = rcptinhead.shift do
315
+ # Insert each recipient address into dscontents
316
+ dscontents[-1]["recipient"] = e
317
+ next if dscontents.size == recipients
318
+ dscontents << Sisimai::Lhost.DELIVERYSTATUS
362
319
  end
363
320
  end
364
321
  return nil if recipients == 0
@@ -406,7 +363,7 @@ module Sisimai::Lhost
406
363
  end
407
364
  e.delete("alterrors")
408
365
  end
409
- e["diagnosis"] = Sisimai::String.sweep(e["diagnosis"]) || ""; p1 = e["diagnosis"].index("__") || -1
366
+ p1 = e["diagnosis"].index("__") || -1
410
367
  e["diagnosis"] = e["diagnosis"][0, p1] if p1 > 1
411
368
 
412
369
  if e["rhost"].empty?
@@ -429,14 +386,11 @@ module Sisimai::Lhost
429
386
  end
430
387
 
431
388
  # Detect the reason of bounce
432
- if %w[HELO EHLO].index(e["command"])
433
- # HELO | Connected to 192.0.2.135 but my name was rejected.
434
- e["reason"] = "blocked"
435
-
436
- elsif e["command"] == "MAIL"
437
- # MAIL | Connected to 192.0.2.135 but sender was rejected.
438
- e["reason"] = "onhold"
439
-
389
+ case e["command"]
390
+ # - HELO | Connected to 192.0.2.135 but my name was rejected.
391
+ # - MAIL | Connected to 192.0.2.135 but sender was rejected.
392
+ when "HELO", "EHLO" then e["reason"] = "blocked"
393
+ when "MAIL" then e["reason"] = "onhold"
440
394
  else
441
395
  # Try to match the error message with each message pattern
442
396
  MessagesOf.each_key do |r|
@@ -447,9 +401,7 @@ module Sisimai::Lhost
447
401
  end
448
402
 
449
403
  if e["reason"].empty?
450
- # The reason "expired", or "mailererror"
451
404
  e["reason"] = "expired" if DelayedFor.any? { |a| e["diagnosis"].include?(a) }
452
- e["reason"] = "mailererror" if e["reason"].empty? && e["diagnosis"].include?("pipe to |")
453
405
  end
454
406
  end
455
407
  end
@@ -468,7 +420,7 @@ module Sisimai::Lhost
468
420
  re = e["reason"]
469
421
  cv = ""
470
422
 
471
- if Sisimai::SMTP::Failure.is_temporary(cr) || re == "expired" || re == "mailboxfull"
423
+ if Sisimai::SMTP::Failure.is_temporary(cr) || re == "expired"
472
424
  # Set the pseudo status code as a temporary error
473
425
  cv = Sisimai::SMTP::Status.code(re, true) if Sisimai::Reason.is_explicit(re)
474
426
  end
@@ -8,22 +8,12 @@ module Sisimai::Lhost
8
8
  Indicators = Sisimai::Lhost.INDICATORS
9
9
  Boundaries = ["--------------------------------------------------", "Content-Type: message/rfc822"].freeze
10
10
  StartingOf = {message: ['The user(s) ', 'Your message ', 'Each of the following', '<']}.freeze
11
- Messagesof = {
12
- # notaccept: ['The following recipients did not receive this message:'],
13
- 'expired' => [
14
- # Your message was not delivered within 0 days and 1 hours.
15
- # Remote host is not responding.
16
- 'Your message was not delivered within ',
17
- ],
18
- 'mailboxfull' => ['The user(s) account is temporarily over quota'],
19
- 'onhold' => ['Each of the following recipients was rejected by a remote mail server'],
20
- 'suspend' => [
21
- # http://www.naruhodo-au.kddi.com/qa3429203.html
22
- # The recipient may be unpaid user...?
23
- 'The user(s) account is disabled.',
24
- 'The user(s) account is temporarily limited.',
25
- ],
26
- }.freeze
11
+ UnpaidUser = [
12
+ # http://www.naruhodo-au.kddi.com/qa3429203.html
13
+ # The recipient may be unpaid user...?
14
+ 'The user(s) account is disabled.',
15
+ 'The user(s) account is temporarily limited.',
16
+ ].freeze
27
17
 
28
18
  # @abstract Decodes the bounce message from au EZweb
29
19
  # @param [Hash] mhead Message headers of a bounce email
@@ -48,7 +38,6 @@ module Sisimai::Lhost
48
38
  bodyslices = emailparts[0].split("\n")
49
39
  readcursor = 0 # (Integer) Points the current cursor position
50
40
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
51
- substrings = []; Messagesof.each_value { |a| substrings << a }; substrings.flatten!
52
41
 
53
42
  while e = bodyslices.shift do
54
43
  # Read error messages and delivery status lines from the head of the email to the previous
@@ -94,53 +83,27 @@ module Sisimai::Lhost
94
83
 
95
84
  else
96
85
  # Other error messages
86
+ # >>> RCPT TO:<******@ezweb.ne.jp>
87
+ # <<< 550 ...
97
88
  next if Sisimai::String.is_8bit(e)
98
- if e.include?(" >>> ")
99
- # >>> RCPT TO:<******@ezweb.ne.jp>
100
- v["command"] = Sisimai::SMTP::Command.find(e)
101
- v["diagnosis"] += " #{e}"
102
-
103
- elsif e.include?(" <<< ")
104
- # <<< 550 ...
105
- v["diagnosis"] += " #{e}"
106
-
107
- else
108
- # Check error message
109
- isincluded = false
110
- if substrings.any? { |a| e.include?(a) }
111
- # Check with regular expressions of each error
112
- v["diagnosis"] += " #{e}"
113
- isincluded = true
114
- end
115
- v["diagnosis"] += " #{e}" if isincluded
116
- end
89
+ v["command"] = Sisimai::SMTP::Command.find(e) if e.include?(" >>> ")
90
+ v["diagnosis"] += " #{e}"
117
91
  end
118
92
  end
119
93
  return nil if recipients == 0
120
94
 
121
95
  dscontents.each do |e|
122
96
  # Check each value of DeliveryMatter{}, try to detect the bounce reason.
123
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
97
+ e['diagnosis'] = e['diagnosis'].split.join(" ")
124
98
  e["command"] = Sisimai::SMTP::Command.find(e["diagnosis"]) if e["command"].empty?
125
99
 
126
100
  if mhead['x-spasign'].to_s == 'NG'
127
101
  # Content-Type: text/plain; ..., X-SPASIGN: NG (spamghetti, au by EZweb)
128
102
  # Filtered recipient returns message that include 'X-SPASIGN' header
129
103
  e['reason'] = 'filtered'
130
- e['toxic'] = true
131
104
  else
132
105
  # There is no X-SPASIGN header or the value of the header is not "NG"
133
- catch :FINDREASON do
134
- Messagesof.each_key do |r|
135
- # Try to match with each session error message
136
- Messagesof[r].each do |f|
137
- # Check each error message pattern
138
- next if e['diagnosis'].include?(f) == false
139
- e['reason'] = r
140
- throw :FINDREASON
141
- end
142
- end
143
- end
106
+ e['reason'] = "suspend" if UnpaidUser.any? { |a| e['diagnosis'].include?(a) }
144
107
  end
145
108
  next if e['reason'] != ""
146
109
  next if e['recipient'].end_with?('@ezweb.ne.jp', '@au.com')
@@ -22,21 +22,6 @@ module Sisimai::Lhost
22
22
  ],
23
23
  'securityerror' => ['Security Alert'],
24
24
  }.freeze
25
- ErrorTable = {
26
- 'rejected' => [
27
- ' header may cause mail loop',
28
- 'NOT MEMBER article from ',
29
- 'reject mail from ',
30
- 'reject spammers:',
31
- 'You are not a member of this mailing list',
32
- ],
33
- 'notcompliantrfc' => ['Duplicated Message-ID'],
34
- 'securityerror' => ['Security alert:'],
35
- 'systemerror' => [
36
- ' has detected a loop condition so that',
37
- 'Loop Back Warning:',
38
- ],
39
- }.freeze
40
25
 
41
26
  # @abstract Decodes the bounce message from fml mailling list server/manager
42
27
  # @param [Hash] mhead Message headers of a bounce email
@@ -84,23 +69,13 @@ module Sisimai::Lhost
84
69
  return nil if recipients == 0
85
70
 
86
71
  dscontents.each do |e|
87
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
88
- ErrorTable.each_key do |f|
89
- # Try to match with error messages defined in ErrorTable
90
- next if ErrorTable[f].none? { |a| e['diagnosis'].include?(a) }
72
+ # Error messages in the message body did not matched
73
+ ErrorTitle.each_key do |f|
74
+ # Try to match with the Subject string
75
+ next if ErrorTitle[f].none? { |a| mhead["subject"].include?(a) }
91
76
  e['reason'] = f
92
77
  break
93
78
  end
94
-
95
- if e['reason'].nil?
96
- # Error messages in the message body did not matched
97
- ErrorTitle.each_key do |f|
98
- # Try to match with the Subject string
99
- next if ErrorTitle[f].none? { |a| mhead["subject"].include?(a) }
100
- e['reason'] = f
101
- break
102
- end
103
- end
104
79
  end
105
80
 
106
81
  return { 'ds' => dscontents, 'rfc822' => emailparts[1] }