sisimai 4.25.17 → 5.0.0

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 (178) 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 +406 -407
  7. data/Developers.mk +5 -6
  8. data/Gemfile +1 -1
  9. data/Makefile +12 -12
  10. data/README-JA.md +142 -94
  11. data/README.md +282 -150
  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 +20 -16
  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 -325
  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 -45
  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 +13 -18
  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 -21
  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/mac/reported-from-nick4tech-san-01.eml +0 -6
  170. /data/set-of-emails/maildir/bsd/{rfc3464-41.eml → rfc3834-05.eml} +0 -0
  171. /data/set-of-emails/maildir/bsd/{rhost-googleapps-01.eml → rhost-google-01.eml} +0 -0
  172. /data/set-of-emails/maildir/bsd/{rhost-googleapps-02.eml → rhost-google-02.eml} +0 -0
  173. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-01.eml → rhost-microsoft-01.eml} +0 -0
  174. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-02.eml → rhost-microsoft-02.eml} +0 -0
  175. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-03.eml → rhost-microsoft-03.eml} +0 -0
  176. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-01.eml → rhost-tencent-01.eml} +0 -0
  177. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-02.eml → rhost-tencent-02.eml} +0 -0
  178. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-03.eml → rhost-tencent-03.eml} +0 -0
@@ -1,13 +1,12 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::Barracuda parses a bounce email which created by Barracuda.
3
- # Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::Barracuda parses a bounce email which created by Barracuda. Methods in the module
3
+ # are called from only Sisimai::Message.
4
4
  module Barracuda
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/Barracuda.pm
7
6
  require 'sisimai/lhost'
8
7
 
9
8
  Indicators = Sisimai::Lhost.INDICATORS
10
- ReBackbone = %r|^Content-Type:[ ]text/rfc822-headers|.freeze
9
+ Boundaries = ['Content-Type: text/rfc822-headers'].freeze
11
10
  StartingOf = { message: ['Your message to:'] }.freeze
12
11
 
13
12
  # Parse bounce messages from Barracuda
@@ -16,24 +15,23 @@ module Sisimai::Lhost
16
15
  # @return [Hash] Bounce data list and message/rfc822 part
17
16
  # @return [Nil] it failed to parse or the arguments are missing
18
17
  # @since v4.25.6
19
- def make(mhead, mbody)
18
+ def inquire(mhead, mbody)
20
19
  # Subject: **Message you sent blocked by our bulk email filter**
21
20
  return nil unless mhead['subject'].to_s.end_with?('our bulk email filter**')
22
21
 
23
- require 'sisimai/rfc1894'
24
22
  fieldtable = Sisimai::RFC1894.FIELDTABLE
25
23
  permessage = {} # (Hash) Store values of each Per-Message field
26
24
 
27
25
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
28
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
29
- bodyslices = emailsteak[0].split("\n")
26
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
27
+ bodyslices = emailparts[0].split("\n")
30
28
  readcursor = 0 # (Integer) Points the current cursor position
31
29
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
32
30
  v = nil
33
31
 
34
32
  while e = bodyslices.shift do
35
- # Read error messages and delivery status lines from the head of the email
36
- # to the previous line of the beginning of the original message.
33
+ # Read error messages and delivery status lines from the head of the email to the previous
34
+ # line of the beginning of the original message.
37
35
  if readcursor == 0
38
36
  # Beginning of the bounce message or message/delivery-status part
39
37
  readcursor |= Indicators[:deliverystatus] if e.start_with?(StartingOf[:message][0])
@@ -72,7 +70,7 @@ module Sisimai::Lhost
72
70
  next unless fieldtable[o[0]]
73
71
  v[fieldtable[o[0]]] = o[2]
74
72
 
75
- next unless f == 1
73
+ next unless f
76
74
  permessage[fieldtable[o[0]]] = o[2]
77
75
  end
78
76
  end
@@ -85,7 +83,7 @@ module Sisimai::Lhost
85
83
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'].to_s.tr("\n", ' '))
86
84
  end
87
85
 
88
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
86
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
89
87
  end
90
88
  def description; return 'Barracuda: https://www.barracuda.com'; end
91
89
  end
@@ -1,49 +1,49 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::Bigfoot parses a bounce email which created by Bigfoot.
3
- # Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::Bigfoot parses a bounce email which created by Bigfoot. Methods in the module
3
+ # are called from only Sisimai::Message.
4
4
  module Bigfoot
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/Bigfoot.pm
7
6
  require 'sisimai/lhost'
8
7
 
9
8
  Indicators = Sisimai::Lhost.INDICATORS
10
- ReBackbone = %r|^Content-Type:[ ]message/partial|.freeze
11
- MarkingsOf = { message: %r/\A[ \t]+[-]+[ \t]*Transcript of session follows/ }.freeze
9
+ Boundaries = ['Content-Type: message/partial'].freeze
10
+ MarkingsOf = { message: ' ----- Transcript of session follows -----' }.freeze
12
11
 
13
12
  # Parse bounce messages from Bigfoot
14
13
  # @param [Hash] mhead Message headers of a bounce email
15
14
  # @param [String] mbody Message body of a bounce email
16
15
  # @return [Hash] Bounce data list and message/rfc822 part
17
16
  # @return [Nil] it failed to parse or the arguments are missing
18
- def make(mhead, mbody)
17
+ def inquire(mhead, mbody)
19
18
  # :subject => %r/\AReturned mail: /,
20
19
  match = 0
21
20
  match += 1 if mhead['from'].include?('@bigfoot.com>')
22
21
  match += 1 if mhead['received'].any? { |a| a.include?('.bigfoot.com') }
23
22
  return nil unless match > 0
24
23
 
24
+ require 'sisimai/smtp/command'
25
25
  require 'sisimai/rfc1894'
26
26
  fieldtable = Sisimai::RFC1894.FIELDTABLE
27
27
  permessage = {} # (Hash) Store values of each Per-Message field
28
28
 
29
29
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
30
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
31
- bodyslices = emailsteak[0].split("\n")
30
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
31
+ bodyslices = emailparts[0].split("\n")
32
32
  readslices = ['']
33
33
  readcursor = 0 # (Integer) Points the current cursor position
34
34
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
35
- commandtxt = '' # (String) SMTP Command name begin with the string '>>>'
35
+ thecommand = '' # (String) SMTP Command name begin with the string '>>>'
36
36
  esmtpreply = '' # (String) Reply from remote server on SMTP session
37
37
  v = nil
38
38
 
39
39
  while e = bodyslices.shift do
40
- # Read error messages and delivery status lines from the head of the email
41
- # to the previous line of the beginning of the original message.
40
+ # Read error messages and delivery status lines from the head of the email to the previous
41
+ # line of the beginning of the original message.
42
42
  readslices << e # Save the current line for the next loop
43
43
 
44
44
  if readcursor == 0
45
45
  # Beginning of the bounce message or message/delivery-status part
46
- readcursor |= Indicators[:deliverystatus] if e =~ MarkingsOf[:message]
46
+ readcursor |= Indicators[:deliverystatus] if e.start_with?(MarkingsOf[:message])
47
47
  next
48
48
  end
49
49
  next if (readcursor & Indicators[:deliverystatus]) == 0
@@ -79,7 +79,7 @@ module Sisimai::Lhost
79
79
  next unless fieldtable[o[0]]
80
80
  v[fieldtable[o[0]]] = o[2]
81
81
 
82
- next unless f == 1
82
+ next unless f
83
83
  permessage[fieldtable[o[0]]] = o[2]
84
84
  end
85
85
  else
@@ -88,18 +88,18 @@ module Sisimai::Lhost
88
88
  # ----- Transcript of session follows -----
89
89
  # >>> RCPT TO:<destinaion@example.net>
90
90
  # <<< 553 Invalid recipient destinaion@example.net (Mode: normal)
91
- if cv = e.match(/\A[>]{3}[ ]+([A-Z]{4})[ ]?/)
91
+ if e.start_with?('>>> ')
92
92
  # >>> DATA
93
- commandtxt = cv[1]
94
- elsif cv = e.match(/\A[<]{3}[ ]+(.+)\z/)
93
+ thecommand = Sisimai::SMTP::Command.find(e)
94
+ elsif e.start_with?('<<< ')
95
95
  # <<< Response
96
- esmtpreply = cv[1]
96
+ esmtpreply = e[4, e.size - 4]
97
97
  end
98
98
  else
99
99
  # Continued line of the value of Diagnostic-Code field
100
100
  next unless readslices[-2].start_with?('Diagnostic-Code:')
101
- next unless cv = e.match(/\A[ \t]+(.+)\z/)
102
- v['diagnosis'] << ' ' << cv[1]
101
+ next unless e.start_with?(' ')
102
+ v['diagnosis'] << ' ' << Sisimai::String.sweep(e[1, e.size])
103
103
  readslices[-1] = 'Diagnostic-Code: ' << e
104
104
  end
105
105
  end
@@ -111,13 +111,13 @@ module Sisimai::Lhost
111
111
  e['lhost'] ||= permessage['rhost']
112
112
  permessage.each_key { |a| e[a] ||= permessage[a] || '' }
113
113
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
114
- e['command'] = commandtxt
114
+ e['command'] = thecommand || ''
115
115
  if e['command'].empty?
116
116
  e['command'] = 'EHLO' unless esmtpreply.empty?
117
117
  end
118
118
  end
119
119
 
120
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
120
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
121
121
  end
122
122
  def description; return 'Bigfoot: http://www.bigfoot.com'; end
123
123
  end
@@ -1,13 +1,12 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::Biglobe parses a bounce email which created by BIGLOBE.
3
- # Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::Biglobe parses a bounce email which created by BIGLOBE. Methods in the module
3
+ # are called from only Sisimai::Message.
4
4
  module Biglobe
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/Biglobe.pm
7
6
  require 'sisimai/lhost'
8
7
 
9
8
  Indicators = Sisimai::Lhost.INDICATORS
10
- ReBackbone = %r|^Content-Type:[ ]message/rfc822|.freeze
9
+ Boundaries = ['Content-Type: message/rfc822'].freeze
11
10
  StartingOf = {
12
11
  message: [' ----- The following addresses had delivery problems -----'],
13
12
  error: [' ----- Non-delivered information -----'],
@@ -22,20 +21,21 @@ module Sisimai::Lhost
22
21
  # @param [String] mbody Message body of a bounce email
23
22
  # @return [Hash] Bounce data list and message/rfc822 part
24
23
  # @return [Nil] it failed to parse or the arguments are missing
25
- def make(mhead, mbody)
26
- return nil unless mhead['from'] =~ /postmaster[@](?:biglobe|inacatv|tmtv|ttv)[.]ne[.]jp/
24
+ def inquire(mhead, mbody)
25
+ return nil unless mhead['from'].include?('postmaster@')
26
+ return nil unless %w[biglobe inacatv tmtv ttv].any? { |a| mhead['from'].include?('@' + a + '.ne.jp') }
27
27
  return nil unless mhead['subject'].start_with?('Returned mail:')
28
28
 
29
29
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
30
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
31
- bodyslices = emailsteak[0].split("\n")
30
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
31
+ bodyslices = emailparts[0].split("\n")
32
32
  readcursor = 0 # (Integer) Points the current cursor position
33
33
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
34
34
  v = nil
35
35
 
36
36
  while e = bodyslices.shift do
37
- # Read error messages and delivery status lines from the head of the email
38
- # to the previous line of the beginning of the original message.
37
+ # Read error messages and delivery status lines from the head of the email to the previous
38
+ # line of the beginning of the original message.
39
39
  if readcursor == 0
40
40
  # Beginning of the bounce message or delivery status part
41
41
  readcursor |= Indicators[:deliverystatus] if e == StartingOf[:message][0]
@@ -60,7 +60,7 @@ module Sisimai::Lhost
60
60
  #
61
61
  v = dscontents[-1]
62
62
 
63
- if cv = e.match(/\A([^ ]+[@][^ ]+)\z/)
63
+ if e.include?('@') && e.include?(' ') == false
64
64
  # ----- The following addresses had delivery problems -----
65
65
  # ********@***.biglobe.ne.jp
66
66
  if v['recipient']
@@ -69,12 +69,11 @@ module Sisimai::Lhost
69
69
  v = dscontents[-1]
70
70
  end
71
71
 
72
- r = Sisimai::Address.s3s4(cv[1])
73
- next unless Sisimai::RFC5322.is_emailaddress(r)
74
- v['recipient'] = r
72
+ next unless Sisimai::Address.is_emailaddress(e)
73
+ v['recipient'] = e
75
74
  recipients += 1
76
75
  else
77
- next if e =~ /\A[^\w]/
76
+ next if e.include?('--')
78
77
  v['diagnosis'] ||= ''
79
78
  v['diagnosis'] << e + ' '
80
79
  end
@@ -92,7 +91,7 @@ module Sisimai::Lhost
92
91
  end
93
92
  end
94
93
 
95
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
94
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
96
95
  end
97
96
  def description; return 'BIGLOBE: https://www.biglobe.ne.jp'; end
98
97
  end
@@ -1,14 +1,13 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::Courier parses a bounce email which created by Courier
3
- # MTA. Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::Courier parses a bounce email which created by Courier MTA. Methods in the module
3
+ # are called from only Sisimai::Message.
4
4
  module Courier
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/Courier.pm
7
6
  require 'sisimai/lhost'
8
7
 
9
8
  # https://www.courier-mta.org/courierdsn.html
10
9
  Indicators = Sisimai::Lhost.INDICATORS
11
- ReBackbone = %r<^Content-Type:[ ](?:message/rfc822|text/rfc822-headers)>.freeze
10
+ Boundaries = ['Content-Type: message/rfc822', 'Content-Type: text/rfc822-headers'].freeze
12
11
  StartingOf = {
13
12
  # courier/module.dsn/dsn*.txt
14
13
  message: ['DELAYS IN DELIVERING YOUR MESSAGE', 'UNDELIVERABLE MAIL'],
@@ -29,32 +28,34 @@ module Sisimai::Lhost
29
28
  # @param [String] mbody Message body of a bounce email
30
29
  # @return [Hash] Bounce data list and message/rfc822 part
31
30
  # @return [Nil] it failed to parse or the arguments are missing
32
- def make(mhead, mbody)
31
+ def inquire(mhead, mbody)
33
32
  match = 0
34
33
  match += 1 if mhead['from'].include?('Courier mail server at ')
35
- match += 1 if mhead['subject'] =~ /(?:NOTICE: mail delivery status[.]|WARNING: delayed mail[.])/
34
+ match += 1 if mhead['subject'].include?('NOTICE: mail delivery status.')
35
+ match += 1 if mhead['subject'].include?('WARNING: delayed mail.')
36
36
  if mhead['message-id']
37
37
  # Message-ID: <courier.4D025E3A.00001792@5jo.example.org>
38
- match += 1 if mhead['message-id'] =~ /\A[<]courier[.][0-9A-F]+[.]/
38
+ match += 1 if mhead['message-id'].start_with?('<courier.')
39
39
  end
40
40
  return nil unless match > 0
41
41
 
42
+ require 'sisimai/smtp/command'
42
43
  require 'sisimai/rfc1894'
43
44
  fieldtable = Sisimai::RFC1894.FIELDTABLE
44
45
  permessage = {} # (Hash) Store values of each Per-Message field
45
46
 
46
47
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
47
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
48
- bodyslices = emailsteak[0].split("\n")
48
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
49
+ bodyslices = emailparts[0].split("\n")
49
50
  readslices = ['']
50
51
  readcursor = 0 # (Integer) Points the current cursor position
51
52
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
52
- commandtxt = '' # (String) SMTP Command name begin with the string '>>>'
53
+ thecommand = '' # (String) SMTP Command name begin with the string '>>>'
53
54
  v = nil
54
55
 
55
56
  while e = bodyslices.shift do
56
- # Read error messages and delivery status lines from the head of the email
57
- # to the previous line of the beginning of the original message.
57
+ # Read error messages and delivery status lines from the head of the email to the previous
58
+ # line of the beginning of the original message.
58
59
  readslices << e # Save the current line for the next loop
59
60
 
60
61
  if readcursor == 0
@@ -97,12 +98,12 @@ module Sisimai::Lhost
97
98
  next unless fieldtable[o[0]]
98
99
  v[fieldtable[o[0]]] = o[2]
99
100
 
100
- next unless f == 1
101
+ next unless f
101
102
  permessage[fieldtable[o[0]]] = o[2]
102
103
  end
103
104
  else
104
105
  # The line does not begin with a DSN field defined in RFC3464
105
- if cv = e.match(/\A[>]{3}[ ]+([A-Z]{4})[ ]?/)
106
+ if e.start_with?('>>> ')
106
107
  # Your message to the following recipients cannot be delivered:
107
108
  #
108
109
  # <kijitora@example.co.jp>:
@@ -110,13 +111,12 @@ module Sisimai::Lhost
110
111
  # >>> RCPT TO:<kijitora@example.co.jp>
111
112
  # <<< 550 5.1.1 <kijitora@example.co.jp>... User Unknown
112
113
  #
113
- next unless commandtxt.empty?
114
- commandtxt = cv[1]
114
+ thecommand = Sisimai::SMTP::Command.find(e)
115
115
  else
116
116
  # Continued line of the value of Diagnostic-Code field
117
117
  next unless readslices[-2].start_with?('Diagnostic-Code:')
118
- next unless cv = e.match(/\A[ \t]+(.+)\z/)
119
- v['diagnosis'] << ' ' << cv[1]
118
+ next unless e.start_with?(' ')
119
+ v['diagnosis'] << ' ' << Sisimai::String.sweep(e)
120
120
  readslices[-1] = 'Diagnostic-Code: ' << e
121
121
  end
122
122
  end
@@ -126,7 +126,7 @@ module Sisimai::Lhost
126
126
  dscontents.each do |e|
127
127
  # Set default values if each value is empty.
128
128
  permessage.each_key { |a| e[a] ||= permessage[a] || '' }
129
- e['command'] ||= commandtxt || ''
129
+ e['command'] ||= thecommand || ''
130
130
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''
131
131
 
132
132
  MessagesOf.each_key do |r|
@@ -137,7 +137,7 @@ module Sisimai::Lhost
137
137
  end
138
138
  end
139
139
 
140
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
140
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
141
141
  end
142
142
  def description; return 'Courier MTA'; end
143
143
  end
@@ -1,19 +1,19 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::Domino parses a bounce email which created by IBM
3
- # Domino Server. Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::Domino parses a bounce email which created by IBM Domino Server. Methods in the
3
+ # module are called from only Sisimai::Message.
4
4
  module Domino
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/Domino.pm
7
6
  require 'sisimai/lhost'
8
7
 
9
8
  Indicators = Sisimai::Lhost.INDICATORS
10
- ReBackbone = %r|^Content-Type:[ ]message/rfc822|.freeze
9
+ Boundaries = ['Content-Type: message/rfc822'].freeze
11
10
  StartingOf = { message: ['Your message'] }.freeze
12
11
  MessagesOf = {
13
12
  'userunknown' => [
14
13
  'not listed in Domino Directory',
15
14
  'not listed in public Name & Address Book',
16
- "dans l'annuaire Domino", # TODO: "non répertorié dans l'annuaire Domino",
15
+ "non répertorié dans l'annuaire Domino",
16
+ 'no se encuentra en el Directorio de Domino',
17
17
  'Domino ディレクトリには見つかりません',
18
18
  ],
19
19
  'filtered' => ['Cannot route mail to user'],
@@ -25,7 +25,7 @@ module Sisimai::Lhost
25
25
  # @param [String] mbody Message body of a bounce email
26
26
  # @return [Hash] Bounce data list and message/rfc822 part
27
27
  # @return [Nil] it failed to parse or the arguments are missing
28
- def make(mhead, mbody)
28
+ def inquire(mhead, mbody)
29
29
  return nil unless mhead['subject'].start_with?('DELIVERY FAILURE:', 'DELIVERY_FAILURE:')
30
30
 
31
31
  require 'sisimai/rfc1894'
@@ -33,16 +33,16 @@ module Sisimai::Lhost
33
33
  permessage = {} # (Hash) Store values of each Per-Message field
34
34
 
35
35
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
36
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
37
- bodyslices = emailsteak[0].split("\n")
36
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
37
+ bodyslices = emailparts[0].split("\n")
38
38
  readcursor = 0 # (Integer) Points the current cursor position
39
39
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
40
40
  subjecttxt = '' # (String) The value of Subject:
41
41
  v = nil
42
42
 
43
43
  while e = bodyslices.shift do
44
- # Read error messages and delivery status lines from the head of the email
45
- # to the previous line of the beginning of the original message.
44
+ # Read error messages and delivery status lines from the head of the email to the previous
45
+ # line of the beginning of the original message.
46
46
  next if e.empty?
47
47
 
48
48
  if readcursor == 0
@@ -76,10 +76,10 @@ module Sisimai::Lhost
76
76
  v['recipient'] ||= e
77
77
  recipients += 1
78
78
 
79
- elsif cv = e.match(/\A[ ][ ]([^ ]+[@][^ ]+)\z/)
79
+ elsif e.start_with?(' ') && e.include?('@') && e.index(' ', 3).nil?
80
80
  # Continued from the line "was not delivered to:"
81
81
  # kijitora@example.net
82
- v['recipient'] = Sisimai::Address.s3s4(cv[1])
82
+ v['recipient'] = Sisimai::Address.s3s4(e[2, e.size])
83
83
 
84
84
  elsif e.start_with?('because:')
85
85
  # because:
@@ -89,9 +89,9 @@ module Sisimai::Lhost
89
89
  # Error message, continued from the line "because:"
90
90
  v['diagnosis'] = e
91
91
 
92
- elsif cv = e.match(/\A[ ][ ]Subject: (.+)\z/)
92
+ elsif e.start_with?(' Subject: ')
93
93
  # Subject: Nyaa
94
- subjecttxt = cv[1]
94
+ subjecttxt = e[11, e.size]
95
95
 
96
96
  elsif f = Sisimai::RFC1894.match(e)
97
97
  # There are some fields defined in RFC3464, try to match
@@ -107,9 +107,14 @@ module Sisimai::Lhost
107
107
  next unless fieldtable[o[0]]
108
108
  v[fieldtable[o[0]]] = o[2]
109
109
 
110
- next unless f == 1
110
+ next unless f
111
111
  permessage[fieldtable[o[0]]] = o[2]
112
112
  end
113
+ else
114
+ if v['diagnosis'] && e.start_with?("\s", "\t")
115
+ # The line is a continued line of "Diagnostic-Code:" field
116
+ v['diagnosis'] += e.sub(/\A[\s\t]+/, '')
117
+ end
113
118
  end
114
119
  end
115
120
  end
@@ -130,11 +135,10 @@ module Sisimai::Lhost
130
135
  end
131
136
  end
132
137
 
133
- # Set the value of subjecttxt as a Subject if there is no original
134
- # message in the bounce mail.
135
- emailsteak[1] << ('Subject: ' << subjecttxt << "\n") unless emailsteak[1] =~ /^Subject: /
138
+ # Set the value of subjecttxt as a Subject if there is no original message in the bounce mail.
139
+ emailparts[1] << ('Subject: ' << subjecttxt << "\n") unless emailparts[1].include?("\nSubject:")
136
140
 
137
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
141
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
138
142
  end
139
143
  def description; return 'IBM Domino Server'; end
140
144
  end
@@ -1,13 +1,12 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::EinsUndEins parses a bounce email which created by
3
- # 1&1. Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::EinsUndEins parses a bounce email which created by 1&1. Methods in the module are
3
+ # called from only Sisimai::Message.
4
4
  module EinsUndEins
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/EinsUndEins.pm
7
6
  require 'sisimai/lhost'
8
7
 
9
8
  Indicators = Sisimai::Lhost.INDICATORS
10
- ReBackbone = %r|^---[ ]The[ ]header[ ]of[ ]the[ ]original[ ]message[ ]is[ ]following[.][ ]---|.freeze
9
+ Boundaries = ['--- The header of the original message is following. ---'].freeze
11
10
  StartingOf = {
12
11
  message: ['This message was created automatically by mail delivery software'],
13
12
  error: ['For the following reason:'],
@@ -19,20 +18,20 @@ module Sisimai::Lhost
19
18
  # @param [String] mbody Message body of a bounce email
20
19
  # @return [Hash] Bounce data list and message/rfc822 part
21
20
  # @return [Nil] it failed to parse or the arguments are missing
22
- def make(mhead, mbody)
21
+ def inquire(mhead, mbody)
23
22
  return nil unless mhead['from'].start_with?('"Mail Delivery System"')
24
23
  return nil unless mhead['subject'] == 'Mail delivery failed: returning message to sender'
25
24
 
26
25
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
27
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
28
- bodyslices = emailsteak[0].split("\n")
26
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
27
+ bodyslices = emailparts[0].split("\n")
29
28
  readcursor = 0 # (Integer) Points the current cursor position
30
29
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
31
30
  v = nil
32
31
 
33
32
  while e = bodyslices.shift do
34
- # Read error messages and delivery status lines from the head of the email
35
- # to the previous line of the beginning of the original message.
33
+ # Read error messages and delivery status lines from the head of the email to the previous
34
+ # line of the beginning of the original message.
36
35
 
37
36
  if readcursor == 0
38
37
  # Beginning of the bounce message or delivery status part
@@ -60,7 +59,7 @@ module Sisimai::Lhost
60
59
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
61
60
  v = dscontents[-1]
62
61
  end
63
- v['recipient'] = cv[1]
62
+ v['recipient'] = Sisimai::Address.s3s4(e)
64
63
  recipients += 1
65
64
 
66
65
  elsif e.start_with?(StartingOf[:error][0])
@@ -81,21 +80,26 @@ module Sisimai::Lhost
81
80
  end
82
81
  return nil unless recipients > 0
83
82
 
83
+ require 'sisimai/smtp/command'
84
84
  dscontents.each do |e|
85
85
  e['diagnosis'] ||= ''
86
86
  e['diagnosis'] = e['alterrors'] if e['diagnosis'].empty?
87
+ e['command'] = Sisimai::SMTP::Command.find(e['diagnosis'])
87
88
 
88
- if cv = e['diagnosis'].match(/host:[ ]+(.+?)[ ]+.+[ ]+reason:.+/)
89
+ if Sisimai::String.aligned(e['diagnosis'], ['host: ', ' reason:'])
89
90
  # SMTP error from remote server for TEXT command,
90
91
  # host: smtp-in.orange.fr (193.252.22.65)
91
92
  # reason: 550 5.2.0 Mail rejete. Mail rejected. ofr_506 [506]
92
- e['rhost'] = cv[1]
93
- e['command'] = 'DATA' if e['diagnosis'] =~ /for TEXT command/
94
- e['spec'] = 'SMTP' if e['diagnosis'] =~ /SMTP error/
93
+ p1 = e['diagnosis'].index('host: ')
94
+ p2 = e['diagnosis'].index(' reason:')
95
+
96
+ e['rhost'] = Sisimai::String.sweep(e['diagnosis'][p1 + 6, p2 - p1 - 6])
97
+ e['command'] = 'DATA' if e['diagnosis'].include?('for TEXT command')
98
+ e['spec'] = 'SMTP' if e['diagnosis'].include?('SMTP error')
95
99
  e['status'] = Sisimai::SMTP::Status.find(e['diagnosis'])
96
100
  else
97
101
  # For the following reason:
98
- e['diagnosis'].gsub(/\A#{StartingOf[:error][0]}/, '')
102
+ e['diagnosis'][0, StartingOf[:error][0].size] = ''
99
103
  end
100
104
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
101
105
 
@@ -107,7 +111,7 @@ module Sisimai::Lhost
107
111
  end
108
112
  end
109
113
 
110
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
114
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
111
115
  end
112
116
  def description; return '1&1: https://www.1und1.de'; end
113
117
  end