sisimai 4.25.15 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (177) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -3
  3. data/ANALYTICAL-PRECISION +2 -2
  4. data/Benchmarks.mk +3 -3
  5. data/CONTRIBUTING +1 -1
  6. data/ChangeLog.md +419 -388
  7. data/Developers.mk +5 -6
  8. data/Gemfile +1 -1
  9. data/Makefile +15 -15
  10. data/README-JA.md +140 -78
  11. data/README.md +290 -143
  12. data/Rakefile +9 -3
  13. data/Repository.mk +2 -3
  14. data/lib/sisimai/address.rb +118 -74
  15. data/lib/sisimai/arf.rb +84 -82
  16. data/lib/sisimai/datetime.rb +5 -52
  17. data/lib/sisimai/{data → fact}/json.rb +7 -9
  18. data/lib/sisimai/fact/yaml.rb +31 -0
  19. data/lib/sisimai/fact.rb +468 -0
  20. data/lib/sisimai/lhost/activehunter.rb +12 -14
  21. data/lib/sisimai/lhost/amavis.rb +11 -14
  22. data/lib/sisimai/lhost/amazonses.rb +37 -41
  23. data/lib/sisimai/lhost/amazonworkmail.rb +15 -18
  24. data/lib/sisimai/lhost/aol.rb +12 -14
  25. data/lib/sisimai/lhost/apachejames.rb +19 -21
  26. data/lib/sisimai/lhost/barracuda.rb +10 -12
  27. data/lib/sisimai/lhost/bigfoot.rb +21 -21
  28. data/lib/sisimai/lhost/biglobe.rb +15 -16
  29. data/lib/sisimai/lhost/courier.rb +20 -20
  30. data/lib/sisimai/lhost/domino.rb +23 -19
  31. data/lib/sisimai/lhost/einsundeins.rb +23 -18
  32. data/lib/sisimai/lhost/exchange2003.rb +30 -29
  33. data/lib/sisimai/lhost/exchange2007.rb +70 -58
  34. data/lib/sisimai/lhost/exim.rb +175 -161
  35. data/lib/sisimai/lhost/ezweb.rb +31 -56
  36. data/lib/sisimai/lhost/facebook.rb +21 -33
  37. data/lib/sisimai/lhost/fml.rb +43 -48
  38. data/lib/sisimai/lhost/gmail.rb +29 -29
  39. data/lib/sisimai/lhost/gmx.rb +18 -17
  40. data/lib/sisimai/lhost/googlegroups.rb +9 -10
  41. data/lib/sisimai/lhost/gsuite.rb +21 -27
  42. data/lib/sisimai/lhost/imailserver.rb +25 -39
  43. data/lib/sisimai/lhost/interscanmss.rb +28 -31
  44. data/lib/sisimai/lhost/kddi.rb +22 -28
  45. data/lib/sisimai/lhost/mailfoundry.rb +11 -12
  46. data/lib/sisimai/lhost/mailmarshalsmtp.rb +25 -29
  47. data/lib/sisimai/lhost/mailru.rb +33 -27
  48. data/lib/sisimai/lhost/mcafee.rb +21 -31
  49. data/lib/sisimai/lhost/messagelabs.rb +17 -20
  50. data/lib/sisimai/lhost/messagingserver.rb +40 -37
  51. data/lib/sisimai/lhost/mfilter.rb +15 -16
  52. data/lib/sisimai/lhost/mxlogic.rb +24 -23
  53. data/lib/sisimai/lhost/notes.rb +17 -17
  54. data/lib/sisimai/lhost/office365.rb +63 -27
  55. data/lib/sisimai/lhost/opensmtpd.rb +12 -13
  56. data/lib/sisimai/lhost/outlook.rb +12 -15
  57. data/lib/sisimai/lhost/postfix.rb +179 -129
  58. data/lib/sisimai/lhost/powermta.rb +12 -14
  59. data/lib/sisimai/lhost/qmail.rb +44 -47
  60. data/lib/sisimai/lhost/receivingses.rb +15 -20
  61. data/lib/sisimai/lhost/sendgrid.rb +34 -32
  62. data/lib/sisimai/lhost/sendmail.rb +66 -53
  63. data/lib/sisimai/lhost/surfcontrol.rb +19 -19
  64. data/lib/sisimai/lhost/v5sendmail.rb +45 -39
  65. data/lib/sisimai/lhost/verizon.rb +35 -39
  66. data/lib/sisimai/lhost/x1.rb +18 -17
  67. data/lib/sisimai/lhost/x2.rb +17 -14
  68. data/lib/sisimai/lhost/x3.rb +19 -19
  69. data/lib/sisimai/lhost/x4.rb +72 -57
  70. data/lib/sisimai/lhost/x5.rb +17 -19
  71. data/lib/sisimai/lhost/x6.rb +41 -17
  72. data/lib/sisimai/lhost/yahoo.rb +17 -16
  73. data/lib/sisimai/lhost/yandex.rb +16 -20
  74. data/lib/sisimai/lhost/zoho.rb +16 -15
  75. data/lib/sisimai/lhost.rb +8 -10
  76. data/lib/sisimai/mail/maildir.rb +1 -3
  77. data/lib/sisimai/mail/mbox.rb +3 -4
  78. data/lib/sisimai/mail/memory.rb +0 -1
  79. data/lib/sisimai/mail/stdin.rb +1 -3
  80. data/lib/sisimai/mail.rb +3 -7
  81. data/lib/sisimai/mda.rb +28 -42
  82. data/lib/sisimai/message.rb +435 -313
  83. data/lib/sisimai/order.rb +5 -5
  84. data/lib/sisimai/reason/authfailure.rb +64 -0
  85. data/lib/sisimai/reason/badreputation.rb +53 -0
  86. data/lib/sisimai/reason/blocked.rb +94 -160
  87. data/lib/sisimai/reason/contenterror.rb +8 -9
  88. data/lib/sisimai/reason/delivered.rb +4 -6
  89. data/lib/sisimai/reason/exceedlimit.rb +10 -12
  90. data/lib/sisimai/reason/expired.rb +6 -8
  91. data/lib/sisimai/reason/feedback.rb +2 -3
  92. data/lib/sisimai/reason/filtered.rb +17 -19
  93. data/lib/sisimai/reason/hasmoved.rb +9 -10
  94. data/lib/sisimai/reason/hostunknown.rb +15 -15
  95. data/lib/sisimai/reason/mailboxfull.rb +10 -12
  96. data/lib/sisimai/reason/mailererror.rb +18 -20
  97. data/lib/sisimai/reason/mesgtoobig.rb +9 -11
  98. data/lib/sisimai/reason/networkerror.rb +5 -8
  99. data/lib/sisimai/reason/norelaying.rb +8 -11
  100. data/lib/sisimai/reason/notaccept.rb +13 -14
  101. data/lib/sisimai/reason/notcompliantrfc.rb +43 -0
  102. data/lib/sisimai/reason/onhold.rb +6 -9
  103. data/lib/sisimai/reason/policyviolation.rb +14 -12
  104. data/lib/sisimai/reason/rejected.rb +26 -24
  105. data/lib/sisimai/reason/requireptr.rb +69 -0
  106. data/lib/sisimai/reason/securityerror.rb +33 -36
  107. data/lib/sisimai/reason/spamdetected.rb +114 -147
  108. data/lib/sisimai/reason/speeding.rb +49 -0
  109. data/lib/sisimai/reason/suspend.rb +11 -11
  110. data/lib/sisimai/reason/syntaxerror.rb +11 -10
  111. data/lib/sisimai/reason/systemerror.rb +7 -9
  112. data/lib/sisimai/reason/systemfull.rb +7 -8
  113. data/lib/sisimai/reason/toomanyconn.rb +9 -11
  114. data/lib/sisimai/reason/undefined.rb +2 -3
  115. data/lib/sisimai/reason/userunknown.rb +129 -146
  116. data/lib/sisimai/reason/vacation.rb +3 -4
  117. data/lib/sisimai/reason/virusdetected.rb +10 -11
  118. data/lib/sisimai/reason.rb +59 -64
  119. data/lib/sisimai/rfc1894.rb +55 -28
  120. data/lib/sisimai/rfc2045.rb +373 -0
  121. data/lib/sisimai/rfc3464.rb +250 -308
  122. data/lib/sisimai/rfc3834.rb +42 -47
  123. data/lib/sisimai/rfc5322.rb +75 -100
  124. data/lib/sisimai/rfc5965.rb +31 -0
  125. data/lib/sisimai/rhost/cox.rb +5 -6
  126. data/lib/sisimai/rhost/franceptt.rb +6 -8
  127. data/lib/sisimai/rhost/godaddy.rb +12 -12
  128. data/lib/sisimai/rhost/{googleapps.rb → google.rb} +80 -72
  129. data/lib/sisimai/rhost/iua.rb +9 -10
  130. data/lib/sisimai/rhost/kddi.rb +6 -8
  131. data/lib/sisimai/rhost/{exchangeonline.rb → microsoft.rb} +115 -114
  132. data/lib/sisimai/rhost/mimecast.rb +42 -40
  133. data/lib/sisimai/rhost/nttdocomo.rb +12 -12
  134. data/lib/sisimai/rhost/spectrum.rb +10 -12
  135. data/lib/sisimai/rhost/{tencentqq.rb → tencent.rb} +7 -8
  136. data/lib/sisimai/rhost.rb +23 -31
  137. data/lib/sisimai/smtp/command.rb +59 -0
  138. data/lib/sisimai/smtp/error.rb +4 -7
  139. data/lib/sisimai/smtp/reply.rb +161 -74
  140. data/lib/sisimai/smtp/status.rb +504 -393
  141. data/lib/sisimai/smtp/transcript.rb +124 -0
  142. data/lib/sisimai/smtp.rb +0 -1
  143. data/lib/sisimai/string.rb +74 -5
  144. data/lib/sisimai/time.rb +1 -2
  145. data/lib/sisimai/version.rb +1 -1
  146. data/lib/sisimai.rb +35 -21
  147. data/set-of-emails/maildir/bsd/lhost-domino-02.eml +6 -3
  148. data/set-of-emails/maildir/bsd/lhost-googlegroups-15.eml +174 -0
  149. data/set-of-emails/maildir/bsd/lhost-gsuite-15.eml +229 -0
  150. data/set-of-emails/maildir/bsd/lhost-postfix-75.eml +51 -0
  151. data/set-of-emails/maildir/bsd/lhost-postfix-76.eml +101 -0
  152. data/set-of-emails/maildir/bsd/lhost-postfix-77.eml +74 -0
  153. data/set-of-emails/maildir/bsd/lhost-postfix-78.eml +91 -0
  154. data/set-of-emails/maildir/bsd/lhost-receivingses-08.eml +88 -0
  155. data/set-of-emails/maildir/bsd/rfc3464-43.eml +88 -0
  156. data/set-of-emails/maildir/bsd/rhost-google-03.eml +101 -0
  157. data/set-of-emails/maildir/bsd/rhost-google-04.eml +102 -0
  158. data/set-of-emails/maildir/bsd/rhost-google-05.eml +82 -0
  159. data/set-of-emails/maildir/bsd/rhost-google-06.eml +102 -0
  160. data/set-of-emails/maildir/bsd/rhost-google-07.eml +69 -0
  161. data/set-of-emails/maildir/bsd/rhost-google-08.eml +99 -0
  162. data/sisimai-java.gemspec +1 -1
  163. data/sisimai.gemspec +1 -1
  164. metadata +41 -20
  165. data/.rspec +0 -2
  166. data/lib/sisimai/data/yaml.rb +0 -33
  167. data/lib/sisimai/data.rb +0 -411
  168. data/lib/sisimai/mime.rb +0 -456
  169. /data/set-of-emails/maildir/bsd/{rfc3464-41.eml → rfc3834-05.eml} +0 -0
  170. /data/set-of-emails/maildir/bsd/{rhost-googleapps-01.eml → rhost-google-01.eml} +0 -0
  171. /data/set-of-emails/maildir/bsd/{rhost-googleapps-02.eml → rhost-google-02.eml} +0 -0
  172. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-01.eml → rhost-microsoft-01.eml} +0 -0
  173. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-02.eml → rhost-microsoft-02.eml} +0 -0
  174. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-03.eml → rhost-microsoft-03.eml} +0 -0
  175. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-01.eml → rhost-tencent-01.eml} +0 -0
  176. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-02.eml → rhost-tencent-02.eml} +0 -0
  177. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-03.eml → rhost-tencent-03.eml} +0 -0
@@ -1,24 +1,21 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::Exim parses a bounce email which created by Exim.
3
- # Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::Exim parses a bounce email which created by Exim. Methods in the module are called
3
+ # from only Sisimai::Message.
4
4
  module Exim
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/Exim.pm
7
6
  require 'sisimai/lhost'
8
7
 
9
8
  Indicators = Sisimai::Lhost.INDICATORS
10
- # deliver.c:6423| if (bounce_return_body) fprintf(f,
11
- # deliver.c:6424|"------ This is a copy of the message, including all the headers. ------\n");
12
- # deliver.c:6425| else fprintf(f,
13
- # deliver.c:6426|"------ This is a copy of the message's headers. ------\n");
14
- ReBackbone = %r{^(?:
15
- [-]+[ ]This[ ]is[ ]a[ ]copy[ ]of[ ](?:the|your)[ ]message,[ ]including[ ]all[ ]the[ ]headers[.][ ][-]+
16
- |Content-Type:[ ]*message/rfc822\n(?:[\s\t]+.*?\n\n)?
17
- )
18
- }x.freeze
19
- StartingOf = { deliverystatus: ['Content-type: message/delivery-status'] }.freeze
20
- MarkingsOf = {
21
- # Error text regular expressions which defined in exim/src/deliver.c
9
+ Boundaries = [
10
+ # deliver.c:6423| if (bounce_return_body) fprintf(f,
11
+ # deliver.c:6424|"------ This is a copy of the message, including all the headers. ------\n");
12
+ # deliver.c:6425| else fprintf(f,
13
+ # deliver.c:6426|"------ This is a copy of the message's headers. ------\n");
14
+ '------ This is a copy of the message, including all the headers. ------',
15
+ 'Content-Type: message/rfc822',
16
+ ].freeze
17
+ StartingOf = {
18
+ # Error text strings which defined in exim/src/deliver.c
22
19
  #
23
20
  # deliver.c:6292| fprintf(f,
24
21
  # deliver.c:6293|"This message was created automatically by mail delivery software.\n");
@@ -35,19 +32,19 @@ module Sisimai::Lhost
35
32
  # deliver.c:6304|"could not be delivered to one or more of its recipients. The following\n"
36
33
  # deliver.c:6305|"address(es) failed:\n", sender_address);
37
34
  # deliver.c:6306| }
38
- alias: %r/\A([ ]+an undisclosed address)\z/,
39
- frozen: %r/\AMessage [^ ]+ (?:has been frozen|was frozen on arrival)/,
40
- message: %r{\A(?>
41
- This[ ]message[ ]was[ ]created[ ]automatically[ ]by[ ]mail[ ]delivery[ ]software[.]
42
- |A[ ]message[ ]that[ ]you[ ]sent[ ]was[ ]rejected[ ]by[ ]the[ ]local[ ]scanning[ ]code
43
- |A[ ]message[ ]that[ ]you[ ]sent[ ]contained[ ]one[ ]or[ ]more[ ]recipient[ ]addresses[ ]
44
- |A[ ]message[ ]that[ ]you[ ]sent[ ]could[ ]not[ ]be[ ]delivered[ ]to[ ]all[ ]of[ ]its[ ]recipients
45
- |Message[ ][^ ]+[ ](?:has[ ]been[ ]frozen|was[ ]frozen[ ]on[ ]arrival)
46
- |The[ ][^ ]+[ ]router[ ]encountered[ ]the[ ]following[ ]error[(]s[)]:
47
- )
48
- }x,
35
+ deliverystatus: ['Content-Type: message/delivery-status'],
36
+ frozen: [' has been frozen', ' was frozen on arrival'],
37
+ message: [
38
+ 'This message was created automatically by mail delivery software.',
39
+ 'A message that you sent was rejected by the local scannning code',
40
+ 'A message that you sent contained one or more recipient addresses ',
41
+ 'A message that you sent could not be delivered to all of its recipients',
42
+ ' has been frozen',
43
+ ' was frozen on arrival',
44
+ ' router encountered the following error(s):',
45
+ ],
49
46
  }.freeze
50
-
47
+ MarkingsOf = { alias: ' an undisclosed address' }.freeze
51
48
  ReCommands = [
52
49
  # transports/smtp.c:564| *message = US string_sprintf("SMTP error from remote mail server after %s%s: "
53
50
  # transports/smtp.c:837| string_sprintf("SMTP error from remote mail server after RCPT TO:<%s>: "
@@ -125,30 +122,28 @@ module Sisimai::Lhost
125
122
  # @param [String] mbody Message body of a bounce email
126
123
  # @return [Hash] Bounce data list and message/rfc822 part
127
124
  # @return [Nil] it failed to parse or the arguments are missing
128
- def make(mhead, mbody)
129
- return nil if mhead['from'] =~ /[@].+[.]mail[.]ru[>]?/
125
+ def inquire(mhead, mbody)
126
+ return nil if mhead['from'].include?('.mail.ru')
130
127
 
131
128
  # Message-Id: <E1P1YNN-0003AD-Ga@example.org>
132
129
  # X-Failed-Recipients: kijitora@example.ed.jp
133
130
  match = 0
131
+ msgid = mhead['message-id'] || ''
134
132
  match += 1 if mhead['from'].start_with?('Mail Delivery System')
135
- match += 1 if mhead['message-id'].to_s =~ %r/\A[<]\w{7}[-]\w{6}[-]\w{2}[@]/
136
- match += 1 if mhead['subject'] =~ %r{(?:
137
- Mail[ ]delivery[ ]failed(:[ ]returning[ ]message[ ]to[ ]sender)?
138
- |Warning:[ ]message[ ][^ ]+[ ]delayed[ ]+
139
- |Delivery[ ]Status[ ]Notification
140
- |Mail[ ]failure
141
- |Message[ ]frozen
142
- |error[(]s[)][ ]in[ ]forwarding[ ]or[ ]filtering
143
- )
144
- }x
133
+ match += 1 if msgid.start_with?('<') && msgid.index('-') == 8 && msgid.index('@') == 18
134
+ match += 1 if %w[
135
+ 'Delivery Status Notification',
136
+ 'Mail delivery failed',
137
+ 'Mail failure',
138
+ 'Message frozen',
139
+ 'Warning: message ',
140
+ 'error(s) in forwarding or filtering'].any? { |a| mhead['subject'].include?(a) }
145
141
  return nil if match < 2
146
142
 
147
- require 'sisimai/rfc1894'
148
143
  fieldtable = Sisimai::RFC1894.FIELDTABLE
149
144
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
150
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
151
- bodyslices = emailsteak[0].split("\n")
145
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
146
+ bodyslices = emailparts[0].split("\n")
152
147
  readcursor = 0 # (Integer) Points the current cursor position
153
148
  nextcursor = 0
154
149
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
@@ -157,19 +152,18 @@ module Sisimai::Lhost
157
152
  v = nil
158
153
 
159
154
  if mhead['content-type']
160
- # Get the boundary string and set regular expression for matching with
161
- # the boundary string.
162
- boundary00 = Sisimai::MIME.boundary(mhead['content-type']) || ''
155
+ # Get the boundary string and set regular expression for matching with the boundary string.
156
+ boundary00 = Sisimai::RFC2045.boundary(mhead['content-type']) || ''
163
157
  end
164
158
 
165
159
  while e = bodyslices.shift do
166
- # Read error messages and delivery status lines from the head of the email
167
- # to the previous line of the beginning of the original message.
160
+ # Read error messages and delivery status lines from the head of the email to the previous
161
+ # line of the beginning of the original message.
168
162
  if readcursor == 0
169
163
  # Beginning of the bounce message or message/delivery-status part
170
- if e =~ MarkingsOf[:message]
164
+ if StartingOf[:message].any? { |a| e.include?(a) }
171
165
  readcursor |= Indicators[:deliverystatus]
172
- next unless e =~ MarkingsOf[:frozen]
166
+ next unless StartingOf[:frozen].any? { |a| e.include?(a) }
173
167
  end
174
168
  end
175
169
  next if (readcursor & Indicators[:deliverystatus]) == 0
@@ -185,109 +179,130 @@ module Sisimai::Lhost
185
179
  # host neko.example.jp [192.0.2.222]: 550 5.1.1 <kijitora@example.jp>... User Unknown
186
180
  v = dscontents[-1]
187
181
 
188
- if cv = e.match(/\A[ \t]{2}([^ \t]+[@][^ \t]+[.]?[a-zA-Z]+)(:.+)?\z/) ||
189
- e.match(/\A[ \t]{2}[^ \t]+[@][^ \t]+[.][a-zA-Z]+[ ]<(.+?[@].+?)>:.+\z/) ||
190
- e.match(MarkingsOf[:alias])
191
- # kijitora@example.jp
192
- # sabineko@example.jp: forced freeze
193
- # mikeneko@example.jp <nekochan@example.org>: ...
194
- #
195
- # deliver.c:4549| printed = US"an undisclosed address";
196
- # an undisclosed address
197
- # (generated from kijitora@example.jp)
198
- r = cv[1]
182
+ cv = ''
183
+ ce = false
184
+ while true
185
+ # Check if the line matche the following patterns:
186
+ break unless e.start_with?(' ') # The line should start with " " (2 spaces)
187
+ break unless e.include?('@') # "@" should be included (email)
188
+ break unless e.include?('.') # "." should be included (domain part)
189
+ break unless e.include?('pipe to |') == false # Exclude "pipe to /path/to/prog" line
190
+
191
+ break unless e[2, 1] != ' '
192
+ break unless e[2, 1] != '<'
193
+ ce = true
194
+ break
195
+ end
199
196
 
197
+ if ce || e.include?(MarkingsOf[:alias])
198
+ # The line is including an email address
200
199
  if v['recipient']
201
200
  # There are multiple recipient addresses in the message body.
202
201
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
203
202
  v = dscontents[-1]
204
203
  end
205
- # v['recipient'] = cv[1]
206
-
207
- if cv = e.match(/\A[ \t]+[^ \t]+[@][^ \t]+[.][a-zA-Z]+[ ]<(.+?[@].+?)>:.+\z/)
208
- # parser.c:743| while (bracket_count-- > 0) if (*s++ != '>')
209
- # parser.c:744| {
210
- # parser.c:745| *errorptr = s[-1] == 0
211
- # parser.c:746| ? US"'>' missing at end of address"
212
- # parser.c:747| : string_sprintf("malformed address: %.32s may not follow %.*s",
213
- # parser.c:748| s-1, (int)(s - US mailbox - 1), mailbox);
214
- # parser.c:749| goto PARSE_FAILED;
215
- # parser.c:750| }
216
- r = cv[1]
217
- v['diagnosis'] = e
204
+
205
+ if e.include?(MarkingsOf[:alias])
206
+ # The line does not include an email address
207
+ # deliver.c:4549| printed = US"an undisclosed address";
208
+ # an undisclosed address
209
+ # (generated from kijitora@example.jp)
210
+ cv = e[2, e.size]
211
+ else
212
+ # kijitora@example.jp
213
+ # sabineko@example.jp: forced freeze
214
+ # mikeneko@example.jp <nekochan@example.org>: ...
215
+ p1 = e.index(' <') || -1
216
+ p2 = e.index('>:') || -1
217
+
218
+ if p1 > 1 && p2 > 1
219
+ # There are an email address and an error message in the line
220
+ # parser.c:743| while (bracket_count-- > 0) if (*s++ != '>')
221
+ # parser.c:744| {
222
+ # parser.c:745| *errorptr = s[-1] == 0
223
+ # parser.c:746| ? US"'>' missing at end of address"
224
+ # parser.c:747| : string_sprintf("malformed address: %.32s may not follow %.*s",
225
+ # parser.c:748| s-1, (int)(s - US mailbox - 1), mailbox);
226
+ # parser.c:749| goto PARSE_FAILED;
227
+ # parser.c:750| }
228
+ cv = Sisimai::Address.s3s4(e[p1 + 1, p2 - p1 - 1])
229
+ v['diagnosis'] = Sisimai::String.sweep(e[p2 + 1, e.size])
230
+ else
231
+ # There is an email address only in the line
232
+ # kijitora@example.jp
233
+ cv = Sisimai::Address.s3s4(e[2, e.size])
234
+ end
218
235
  end
219
- v['recipient'] = r
236
+ v['recipient'] = cv
220
237
  recipients += 1
221
238
 
222
- elsif cv = e.match(/\A[ ]+[(]generated[ ]from[ ](.+)[)]\z/) ||
223
- e.match(/\A[ ]+generated[ ]by[ ]([^ \t]+[@][^ \t]+)/)
239
+ elsif e.include?(' (generated from ') || e.include?(' generated by ')
224
240
  # (generated from kijitora@example.jp)
225
241
  # pipe to |/bin/echo "Some pipe output"
226
242
  # generated by userx@myhost.test.ex
227
- v['alias'] = cv[1]
243
+ v['alias'] = Sisimai::Address.s3s4(e[e.rindex(' ') + 1, e.size])
244
+
228
245
  else
229
246
  next if e.empty?
230
247
 
231
- if e =~ MarkingsOf[:frozen]
248
+ if StartingOf[:frozen].any? { |a| e.include?(a) }
232
249
  # Message *** has been frozen by the system filter.
233
250
  # Message *** was frozen on arrival by ACL.
234
251
  v['alterrors'] ||= ''
235
252
  v['alterrors'] << e + ' '
236
- else
237
- if !boundary00.empty?
238
- # --NNNNNNNNNN-eximdsn-MMMMMMMMMM
239
- # Content-type: message/delivery-status
240
- # ...
241
- if Sisimai::RFC1894.match(e)
242
- # "e" matched with any field defined in RFC3464
243
- next unless o = Sisimai::RFC1894.field(e)
244
-
245
- if o[-1] == 'addr'
246
- # Final-Recipient: rfc822; kijitora@example.jp
247
- # X-Actual-Recipient: rfc822; kijitora@example.co.jp
248
- next unless o[0] == 'final-recipient'
249
- v['spec'] ||= o[2].include?('@') ? 'SMTP' : 'X-UNIX'
250
-
251
- elsif o[-1] == 'code'
252
- # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
253
- v['spec'] = o[1]
254
- v['diagnosis'] = o[2]
255
-
256
- else
257
- # Other DSN fields defined in RFC3464
258
- next unless fieldtable[o[0]]
259
- v[fieldtable[o[0]]] = o[2]
260
- end
253
+ elsif !boundary00.empty?
254
+ # --NNNNNNNNNN-eximdsn-MMMMMMMMMM
255
+ # Content-type: message/delivery-status
256
+ # ...
257
+ if Sisimai::RFC1894.match(e)
258
+ # "e" matched with any field defined in RFC3464
259
+ next unless o = Sisimai::RFC1894.field(e)
260
+
261
+ if o[-1] == 'addr'
262
+ # Final-Recipient: rfc822; kijitora@example.jp
263
+ # X-Actual-Recipient: rfc822; kijitora@example.co.jp
264
+ next unless o[0] == 'final-recipient'
265
+ v['spec'] ||= o[2].include?('@') ? 'SMTP' : 'X-UNIX'
266
+
267
+ elsif o[-1] == 'code'
268
+ # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
269
+ v['spec'] = o[1]
270
+ v['diagnosis'] = o[2]
271
+
261
272
  else
262
- # Error message ?
263
- next if nextcursor == 1
273
+ # Other DSN fields defined in RFC3464
274
+ next unless fieldtable[o[0]]
275
+ v[fieldtable[o[0]]] = o[2]
276
+ end
277
+ else
278
+ # Error message ?
279
+ next if nextcursor == 1
264
280
 
265
- # Content-type: message/delivery-status
266
- nextcursor = 1 if e.start_with?(StartingOf[:deliverystatus][0])
267
- v['alterrors'] ||= ''
268
- if e.start_with?("\s", "\t")
269
- e.sub!(/\A[\s\t]+/, '')
270
- v['alterrors'] << e + ' ' unless v['alterrors'].include?(e)
271
- end
281
+ # Content-type: message/delivery-status
282
+ nextcursor = 1 if e.start_with?(StartingOf[:deliverystatus][0])
283
+ v['alterrors'] ||= ''
284
+ if e.start_with?("\s", "\t")
285
+ e.sub!(/\A[\s\t]+/, '')
286
+ v['alterrors'] << e + ' ' unless v['alterrors'].include?(e)
272
287
  end
288
+ end
289
+ else
290
+ # There is no boundary string in $boundary00
291
+ if dscontents.size == recipients
292
+ # Error message
293
+ next if e.empty?
294
+ v['diagnosis'] ||= ''
295
+ v['diagnosis'] << e + ' '
273
296
  else
274
- if dscontents.size == recipients
275
- # Error message
276
- next if e.empty?
277
- v['diagnosis'] ||= ''
278
- v['diagnosis'] << e + ' '
297
+ # Error message when email address above does not include '@' and domain part.
298
+ if e.include?(' pipe to |/')
299
+ # pipe to |/path/to/prog ...
300
+ # generated by kijitora@example.com
301
+ v['diagnosis'] = e
279
302
  else
280
- # Error message when email address above does not include '@'
281
- # and domain part.
282
- if e =~ %r<\A[ ]+pipe[ ]to[ ][|]/[^ ]+>
283
- # pipe to |/path/to/prog ...
284
- # generated by kijitora@example.com
285
- v['diagnosis'] = e
286
- else
287
- next unless e.start_with?(' ')
288
- v['alterrors'] ||= ''
289
- v['alterrors'] << e + ' '
290
- end
303
+ next unless e.start_with?(' ')
304
+ v['alterrors'] ||= ''
305
+ v['alterrors'] << e + ' '
291
306
  end
292
307
  end
293
308
  end
@@ -330,7 +345,9 @@ module Sisimai::Lhost
330
345
  unless mhead['received'].empty?
331
346
  # Get the name of local MTA
332
347
  # Received: from marutamachi.example.org (c192128.example.net [192.0.2.128])
333
- if cv = mhead['received'][-1].match(/from[ \t]([^ ]+)/) then localhost0 = cv[1] end
348
+ p1 = mhead['received'][-1].index('from ') || -1
349
+ p2 = mhead['received'][-1].index(' ', p1 + 5) || -1
350
+ localhost0 = mhead['received'][-1][p1 + 5, p2 - p1 - 5] if p1 > -1 && p2 > -1
334
351
  end
335
352
 
336
353
  dscontents.each do |e|
@@ -371,25 +388,23 @@ module Sisimai::Lhost
371
388
  if e['diagnosis'].start_with?('-') || e['diagnosis'].end_with?('__')
372
389
  # Override the value of diagnostic code message
373
390
  e['diagnosis'] = e['alterrors'] unless e['alterrors'].empty?
374
- else
375
- # Check the both value and try to match
376
- if e['diagnosis'].size < e['alterrors'].size
377
- # Check the value of alterrors
378
- rxdiagnosis = %r/e['diagnosis']/i
379
- # Override the value of diagnostic code message because
380
- # the value of alterrors includes the value of diagnosis.
381
- e['diagnosis'] = e['alterrors'] if e['alterrors'].downcase.include?(e['diagnosis'].downcase)
382
- end
391
+
392
+ elsif e['diagnosis'].size < e['alterrors'].size
393
+ # Override the value of diagnostic code message because the value of alterrors includes
394
+ # the value of diagnosis.
395
+ e['diagnosis'] = e['alterrors'] if e['alterrors'].downcase.include?(e['diagnosis'].downcase)
383
396
  end
384
397
  e.delete('alterrors')
385
398
  end
386
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''
387
- e['diagnosis'].sub!(/\b__.+\z/, '')
399
+ e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''; p1 = e['diagnosis'].index('__') || -1
400
+ e['diagnosis'] = e['diagnosis'][0, p1] if p1 > 1
388
401
 
389
402
  unless e['rhost']
390
403
  # Get the remote host name
391
404
  # host neko.example.jp [192.0.2.222]: 550 5.1.1 <kijitora@example.jp>... User Unknown
392
- if cv = e['diagnosis'].match(/host[ \t]+([^ \t]+)[ \t]\[.+\]:[ \t]/) then e['rhost'] = cv[1] end
405
+ p1 = e['diagnosis'].index('host ') || -1
406
+ p2 = e['diagnosis'].index(' ', p1 + 5) || -1
407
+ e['rhost'] = e['diagnosis'][p1 + 5, p2 - p1 - 5] if p1 > -1
393
408
 
394
409
  unless e['rhost']
395
410
  # Get localhost and remote host name from Received header.
@@ -426,55 +441,54 @@ module Sisimai::Lhost
426
441
 
427
442
  unless e['reason']
428
443
  # The reason "expired"
429
- e['reason'] = 'expired' if DelayedFor.any? { |a| e['diagnosis'].include?(a) }
444
+ e['reason'] = 'expired' if DelayedFor.any? { |a| e['diagnosis'].include?(a) }
445
+ e['reason'] ||= 'mailererror' if e['diagnosis'].include?('pipe to |')
430
446
  end
431
447
  end
432
448
  end
433
449
 
434
450
  # Prefer the value of smtp reply code in Diagnostic-Code:
435
- # See eg/maildir-as-a-sample/new/exim-20.eml
436
451
  # Action: failed
437
452
  # Final-Recipient: rfc822;userx@test.ex
438
453
  # Status: 5.0.0
439
454
  # Remote-MTA: dns; 127.0.0.1
440
455
  # Diagnostic-Code: smtp; 450 TEMPERROR: retry timeout exceeded
441
- # The value of "Status:" indicates permanent error but the value
442
- # of SMTP reply code in Diagnostic-Code: field is "TEMPERROR"!!!!
443
- sv = e['status'] || Sisimai::SMTP::Status.find(e['diagnosis'])
444
- rv = e['replycode'] || Sisimai::SMTP::Reply.find(e['diagnosis'])
456
+ #
457
+ # The value of "Status:" indicates permanent error but the value of SMTP reply code in
458
+ # Diagnostic-Code: field is "TEMPERROR"!!!!
459
+ cs = e['status'] || Sisimai::SMTP::Status.find(e['diagnosis'])
460
+ cr = e['replycode'] || Sisimai::SMTP::Reply.find(e['diagnosis'])
445
461
  s1 = 0 # First character of Status as integer
446
462
  r1 = 0 # First character of SMTP reply code as integer
447
463
 
448
464
  while true
449
465
  # "Status:" field did not exist in the bounce message
450
- break if sv
451
- break unless rv
466
+ break if cs
467
+ break unless cr
452
468
 
453
- # Check SMTP reply code
454
- # Generate pseudo DSN code from SMTP reply code
455
- r1 = rv[0, 1].to_i
469
+ # Check SMTP reply code, Generate pseudo DSN code from SMTP reply code
470
+ r1 = cr[0, 1].to_i
456
471
  if r1 == 4
457
472
  # Get the internal DSN(temporary error)
458
- sv = Sisimai::SMTP::Status.code(e['reason'], true)
473
+ cs = Sisimai::SMTP::Status.code(e['reason'], true)
459
474
 
460
475
  elsif r1 == 5
461
476
  # Get the internal DSN(permanent error)
462
- sv = Sisimai::SMTP::Status.code(e['reason'], false)
477
+ cs = Sisimai::SMTP::Status.code(e['reason'], false)
463
478
  end
464
479
  break
465
480
  end
466
481
 
467
- s1 = sv[0, 1].to_i if sv
482
+ s1 = cs[0, 1].to_i if cs
468
483
  v1 = s1 + r1
469
484
  v1 << e['status'][0, 1].to_i if e['status']
470
485
 
471
486
  if v1 > 0
472
- # Status or SMTP reply code exists
473
- # Set pseudo DSN into the value of "status" accessor
474
- e['status'] = sv if r1 > 0
487
+ # Status or SMTP reply code exists, Set pseudo DSN into the value of "status" accessor
488
+ e['status'] = cs if r1 > 0
475
489
  else
476
490
  # Neither Status nor SMTP reply code exist
477
- sv = if %w[expired mailboxfull].include?(e['reason'])
491
+ cs = if %w[expired mailboxfull].include?(e['reason'])
478
492
  # Set pseudo DSN (temporary error)
479
493
  Sisimai::SMTP::Status.code(e['reason'], true)
480
494
  else
@@ -482,10 +496,10 @@ module Sisimai::Lhost
482
496
  Sisimai::SMTP::Status.code(e['reason'], false)
483
497
  end
484
498
  end
485
- e['status'] ||= sv.to_s
499
+ e['status'] ||= cs.to_s
486
500
  end
487
501
 
488
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
502
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
489
503
  end
490
504
  def description; return 'Exim'; end
491
505
  end