sisimai 4.25.17-java → 5.0.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 (178) hide show
  1. checksums.yaml +5 -5
  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 +42 -23
  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::GMX parses a bounce email which created by GMX.
3
- # Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::GMX parses a bounce email which created by GMX. Methods in the module are called
3
+ # from only Sisimai::Message.
4
4
  module GMX
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/GMX.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 = { message: ['This message was created automatically by mail delivery software'] }.freeze
12
11
  MessagesOf = { 'expired' => ['delivery retry timeout exceeded'] }.freeze
13
12
 
@@ -16,23 +15,24 @@ module Sisimai::Lhost
16
15
  # @param [String] mbody Message body of a bounce email
17
16
  # @return [Hash] Bounce data list and message/rfc822 part
18
17
  # @return [Nil] it failed to parse or the arguments are missing
19
- def make(mhead, mbody)
18
+ def inquire(mhead, mbody)
20
19
  # Envelope-To: <kijitora@mail.example.com>
21
20
  # X-GMX-Antispam: 0 (Mail was not recognized as spam); Detail=V3;
22
21
  # X-GMX-Antivirus: 0 (no virus found)
23
22
  # X-UI-Out-Filterresults: unknown:0;
24
23
  return nil unless mhead['x-gmx-antispam']
25
24
 
25
+ require 'sisimai/smtp/command'
26
26
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
27
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
28
- bodyslices = emailsteak[0].split("\n")
27
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
28
+ bodyslices = emailparts[0].split("\n")
29
29
  readcursor = 0 # (Integer) Points the current cursor position
30
30
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
31
31
  v = nil
32
32
 
33
33
  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.
34
+ # Read error messages and delivery status lines from the head of the email to the previous
35
+ # line of the beginning of the original message.
36
36
  if readcursor == 0
37
37
  # Beginning of the bounce message or delivery status part
38
38
  readcursor |= Indicators[:deliverystatus] if e.start_with?(StartingOf[:message][0])
@@ -53,7 +53,7 @@ module Sisimai::Lhost
53
53
  # 5.1.1 <shironeko@example.jp>... User Unknown
54
54
  v = dscontents[-1]
55
55
 
56
- if cv = e.match(/\A["]([^ ]+[@][^ ]+)["]:\z/) || e.match(/\A[<]([^ ]+[@][^ ]+)[>]\z/)
56
+ if e.include?('@') && ( e.start_with?('"') || e.start_with?('<') )
57
57
  # "shironeko@example.jp":
58
58
  # ---- OR ----
59
59
  # <kijitora@6jo.example.co.jp>
@@ -65,19 +65,20 @@ module Sisimai::Lhost
65
65
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
66
66
  v = dscontents[-1]
67
67
  end
68
- v['recipient'] = cv[1]
68
+ v['recipient'] = Sisimai::Address.s3s4(e)
69
69
  recipients += 1
70
70
 
71
- elsif cv = e.match(/\ASMTP error .+ ([A-Z]{4}) command:\z/)
71
+ elsif e.start_with?('SMTP error ')
72
72
  # SMTP error from remote server after RCPT command:
73
- v['command'] = cv[1]
73
+ v['command'] = Sisimai::SMTP::Command.find(e)
74
74
 
75
- elsif cv = e.match(/\Ahost:[ \t]*(.+)\z/)
75
+ elsif e.start_with?('host:')
76
76
  # host: mx.example.jp
77
- v['rhost'] = cv[1]
77
+ v['rhost'] = e[6, e.size]
78
78
  else
79
79
  # Get error message
80
- if e =~ /\b[45][.]\d[.]\d\b/ || e =~ /[<][^ ]+[@][^ ]+[>]/ || e =~ /\b[45]\d{2}\b/
80
+ if Sisimai::SMTP::Status.find(e) || Sisimai::String.aligned(e, ['<', '@', '>'])
81
+ # 5.1.1 <shironeko@example.jp>... User Unknown
81
82
  v['diagnosis'] ||= e
82
83
  else
83
84
  next if e.empty?
@@ -105,7 +106,7 @@ module Sisimai::Lhost
105
106
  end
106
107
  end
107
108
 
108
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
109
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
109
110
  end
110
111
  def description; return 'GMX: https://www.gmx.net'; end
111
112
  end
@@ -1,13 +1,12 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::GoogleGroups parses a bounce email which created by Google
3
- # Groups. Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::GoogleGroups parses a bounce email which created by Google Groups. Methods in the
3
+ # module are called from only Sisimai::Message.
4
4
  module GoogleGroups
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/GoogleGroups.pm
7
6
  require 'sisimai/lhost'
8
7
 
9
8
  Indicators = Sisimai::Lhost.INDICATORS
10
- ReBackbone = %r/^-----[ ]Original[ ]message[ ]-----$/.freeze
9
+ Boundaries = ['----- Original message -----'].freeze
11
10
 
12
11
  # Parse bounce messages from Google Groups
13
12
  # @param [Hash] mhead Message headers of a bounce email
@@ -15,7 +14,7 @@ module Sisimai::Lhost
15
14
  # @return [Hash] Bounce data list and message/rfc822 part
16
15
  # @return [Nil] it failed to parse or the arguments are missing
17
16
  # @since v4.25.6
18
- def make(mhead, mbody)
17
+ def inquire(mhead, mbody)
19
18
  return nil unless mhead['from'].end_with?('<mailer-daemon@googlemail.com>')
20
19
  return nil unless mhead['subject'].start_with?('Delivery Status Notification')
21
20
  return nil unless mhead['x-failed-recipients']
@@ -39,7 +38,7 @@ module Sisimai::Lhost
39
38
  #
40
39
  # Google Groups
41
40
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
42
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
41
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
43
42
  recordwide = { 'rhost' => '', 'reason' => '', 'diagnosis' => '' }
44
43
  recipients = 0
45
44
  v = dscontents[-1]
@@ -48,14 +47,14 @@ module Sisimai::Lhost
48
47
  # * The owner of the group may have removed this group.
49
48
  # * You may need to join the group before receiving permission to post.
50
49
  # * This group may not be open to posting.
51
- entiremesg = emailsteak[0].split(/\n\n/, 5).slice(0, 4).join(' ').tr("\n", ' ');
50
+ entiremesg = emailparts[0].split(/\n\n/, 5).slice(0, 4).join(' ').tr("\n", ' ');
52
51
  recordwide['diagnosis'] = Sisimai::String.sweep(entiremesg)
53
- recordwide['reason'] = emailsteak[0].scan(/^[ ]?[*][ ]?/).size == 4 ? 'rejected' : 'onhold'
52
+ recordwide['reason'] = emailparts[0].scan(/^[ ]?[*][ ]?/).size == 4 ? 'rejected' : 'onhold'
54
53
  recordwide['rhost'] = Sisimai::RFC5322.received(mhead['received'][0]).shift
55
54
 
56
55
  mhead['x-failed-recipients'].split(',').each do |e|
57
56
  # X-Failed-Recipients: neko@example.jp, nyaan@example.org, ...
58
- next unless Sisimai::RFC5322.is_emailaddress(e)
57
+ next unless Sisimai::Address.is_emailaddress(e)
59
58
 
60
59
  if v['recipient']
61
60
  # There are multiple recipient addresses in the message body.
@@ -67,7 +66,7 @@ module Sisimai::Lhost
67
66
  recordwide.each_key { |r| v[r] = recordwide[r] }
68
67
  end
69
68
  return nil unless recipients > 0
70
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
69
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
71
70
  end
72
71
  def description; return 'Google Groups: https://groups.google.com'; end
73
72
  end
@@ -1,17 +1,15 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::GSuite parses a bounce email which created by G Suite.
3
- # Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::GSuite parses a bounce email which created by G Suite. Methods in the module are
3
+ # called from only Sisimai::Message.
4
4
  module GSuite
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/GSuite.pm
7
6
  require 'sisimai/lhost'
8
7
 
9
8
  Indicators = Sisimai::Lhost.INDICATORS
10
- ReBackbone = %r<^Content-Type:[ ](?:message/rfc822|text/rfc822-headers)>.freeze
9
+ Boundaries = ['Content-Type: message/rfc822', 'Content-Type: text/rfc822-headers'].freeze
11
10
  MarkingsOf = {
12
- message: %r/\A[*][*][ ].+[ ][*][*]\z/,
13
- error: %r/\AThe[ ]response([ ]from[ ]the[ ]remote[ ]server)?[ ]was:\z/,
14
- html: %r{\AContent-Type:[ ]*text/html;[ ]*charset=['"]?(?:UTF|utf)[-]8['"]?\z},
11
+ message: ['** '],
12
+ error: ['The response was:', 'The response from the remote server was:'],
15
13
  }.freeze
16
14
  MessagesOf = {
17
15
  'userunknown' => ["because the address couldn't be found. Check for typos or unnecessary spaces and try again."],
@@ -24,18 +22,17 @@ module Sisimai::Lhost
24
22
  # @param [String] mbody Message body of a bounce email
25
23
  # @return [Hash] Bounce data list and message/rfc822 part
26
24
  # @return [Nil] it failed to parse or the arguments are missing
27
- def make(mhead, mbody)
25
+ def inquire(mhead, mbody)
28
26
  return nil unless mhead['from'].end_with?('<mailer-daemon@googlemail.com>')
29
27
  return nil unless mhead['subject'].start_with?('Delivery Status Notification')
30
28
  return nil unless mhead['x-gm-message-state']
31
29
 
32
- require 'sisimai/rfc1894'
33
30
  fieldtable = Sisimai::RFC1894.FIELDTABLE
34
31
  permessage = {} # (Hash) Store values of each Per-Message field
35
32
 
36
33
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
37
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
38
- bodyslices = emailsteak[0].split("\n")
34
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
35
+ bodyslices = emailparts[0].split("\n")
39
36
  readcursor = 0 # (Integer) Points the current cursor position
40
37
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
41
38
  endoferror = false # (Integer) Flag for a blank line after error messages
@@ -44,11 +41,11 @@ module Sisimai::Lhost
44
41
  v = nil
45
42
 
46
43
  while e = bodyslices.shift do
47
- # Read error messages and delivery status lines from the head of the email
48
- # 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.
49
46
  if readcursor == 0
50
47
  # Beginning of the bounce message or message/delivery-status part
51
- readcursor |= Indicators[:deliverystatus] if e =~ MarkingsOf[:message]
48
+ readcursor |= Indicators[:deliverystatus] if MarkingsOf[:message].any? { |a| e.start_with?(a) }
52
49
  end
53
50
  next if (readcursor & Indicators[:deliverystatus]) == 0
54
51
 
@@ -87,12 +84,12 @@ module Sisimai::Lhost
87
84
  v['lhost'] = '' if v['lhost'].include?('@')
88
85
  end
89
86
 
90
- next unless f == 1
87
+ next unless f
91
88
  permessage[fieldtable[o[0]]] = o[2]
92
89
  end
93
90
  else
94
- # The line does not begin with a DSN field defined in RFC3464
95
- # Append error messages continued from the previous line
91
+ # The line does not begin with a DSN field defined in RFC3464 Append error messages continued
92
+ # from the previous line
96
93
  if endoferror == false && v && ! v['diagnosis'].to_s.empty?
97
94
  endoferror ||= true if e.empty?
98
95
 
@@ -100,10 +97,10 @@ module Sisimai::Lhost
100
97
  next unless e.start_with?(' ')
101
98
  v['diagnosis'] << e
102
99
 
103
- elsif e =~ MarkingsOf[:error]
100
+ elsif MarkingsOf[:error].any? { |a| e.start_with?(a) }
104
101
  # Detect SMTP session error or connection error
105
102
  # The response from the remote server was:
106
- anotherset['diagnosis'] << e
103
+ anotherset['diagnosis'] << ' ' << e
107
104
  else
108
105
  # ** Address not found **
109
106
  #
@@ -112,12 +109,9 @@ module Sisimai::Lhost
112
109
  #
113
110
  # The response from the remote server was:
114
111
  # 550 #5.1.0 Address rejected.
115
- next if e =~ MarkingsOf[:html]
116
-
112
+ next if e.start_with?('Content-Type:')
117
113
  if anotherset['diagnosis']
118
- # Continued error messages from the previous line like
119
- # "550 #5.1.0 Address rejected."
120
- next if e =~ /\AContent-Type:/
114
+ # Continued error messages from the previous line like "550 #5.1.0 Address rejected."
121
115
  next if emptylines > 5
122
116
  if e.empty?
123
117
  # Count and next()
@@ -131,7 +125,7 @@ module Sisimai::Lhost
131
125
  # Your message wasn't delivered to * because the address couldn't be found.
132
126
  # Check for typos or unnecessary spaces and try again.
133
127
  next if e.empty?
134
- next unless e =~ MarkingsOf[:message]
128
+ next unless MarkingsOf[:message].any? { |a| e.start_with?(a) }
135
129
  anotherset['diagnosis'] = e
136
130
  end
137
131
  end
@@ -148,7 +142,7 @@ module Sisimai::Lhost
148
142
  # Copy alternative error message
149
143
  e['diagnosis'] = anotherset['diagnosis'] unless e['diagnosis']
150
144
 
151
- if e['diagnosis'] =~ /\A\d+\z/
145
+ if e['diagnosis'].include?(' ') == false && e['diagnosis'].to_i > 0
152
146
  e['diagnosis'] = anotherset['diagnosis']
153
147
  else
154
148
  # More detailed error message is in "anotherset"
@@ -192,7 +186,7 @@ module Sisimai::Lhost
192
186
  end
193
187
  end
194
188
 
195
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
189
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
196
190
  end
197
191
  def description; return 'G Suite: https://gsuite.google.com'; end
198
192
  end
@@ -1,29 +1,19 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::IMailServer parses a bounce email which created by
3
- # Ipswitch IMail Server.
2
+ # Sisimai::Lhost::IMailServer parses a bounce email which created by Ipswitch IMail Server.
4
3
  # Methods in the module are called from only Sisimai::Message.
5
4
  module IMailServer
6
5
  class << self
7
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/IMailServer.pm
8
6
  require 'sisimai/lhost'
9
7
 
10
- ReBackbone = %r|^Original[ ]message[ ]follows[.]|.freeze
8
+ Boundaries = ['Original message follows.'].freeze
11
9
  StartingOf = { error: ['Body of message generated response:'] }.freeze
12
-
13
- ReSMTP = {
14
- 'conn' => %r/(?:SMTP connection failed,|Unexpected connection response from server:)/,
15
- 'ehlo' => %r|Unexpected response to EHLO/HELO:|,
16
- 'mail' => %r|Server response to MAIL FROM:|,
17
- 'rcpt' => %r|Additional RCPT TO generated following response:|,
18
- 'data' => %r|DATA command generated response:|,
19
- }.freeze
20
10
  ReFailures = {
21
- 'hostunknown' => %r/Unknown host/,
22
- 'userunknown' => %r/\A(?:Unknown user|Invalid final delivery userid)/,
23
- 'mailboxfull' => %r/\AUser mailbox exceeds allowed size/,
24
- 'virusdetected' => %r/\ARequested action not taken: virus detected/,
25
- 'undefined' => %r/\Aundeliverable to /,
26
- 'expired' => %r/\ADelivery failed \d+ attempts/,
11
+ 'hostunknown' => ['Unknown host'],
12
+ 'userunknown' => ['Unknown user', 'Invalid final delivery userid'],
13
+ 'mailboxfull' => ['User mailbox exceeds allowed size'],
14
+ 'virusdetected' => ['Requested action not taken: virus detected'],
15
+ 'undefined' => ['undeliverable to'],
16
+ 'expired' => ['Delivery failed '],
27
17
  }.freeze
28
18
 
29
19
  # Parse bounce messages from IMailServer
@@ -31,47 +21,48 @@ module Sisimai::Lhost
31
21
  # @param [String] mbody Message body of a bounce email
32
22
  # @return [Hash] Bounce data list and message/rfc822 part
33
23
  # @return [Nil] it failed to parse or the arguments are missing
34
- def make(mhead, mbody)
24
+ def inquire(mhead, mbody)
35
25
  # X-Mailer: <SMTP32 v8.22>
36
26
  match = 0
37
- match += 1 if mhead['subject'] =~ /\AUndeliverable Mail[ ]*\z/
27
+ match += 1 if mhead['subject'].start_with?('Undeliverable Mail ')
38
28
  match += 1 if mhead['x-mailer'].to_s.start_with?('<SMTP32 v')
39
29
  return nil unless match > 0
40
30
 
41
31
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
42
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
43
- bodyslices = emailsteak[0].split("\n")
32
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
33
+ bodyslices = emailparts[0].split("\n")
44
34
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
45
35
  v = nil
46
36
 
47
37
  while e = bodyslices.shift do
48
- # Read error messages and delivery status lines from the head of the email
49
- # to the previous line of the beginning of the original message.
38
+ # Read error messages and delivery status lines from the head of the email to the previous
39
+ # line of the beginning of the original message.
50
40
 
51
41
  # Unknown user: kijitora@example.com
52
42
  #
53
43
  # Original message follows.
54
44
  v = dscontents[-1]
55
45
 
56
- if cv = e.match(/\A([^ ]+)[ ](.+)[:][ \t]*([^ ]+[@][^ ]+)/)
46
+ p0 = e.index(': ') || -1
47
+ if p0 > 8 && Sisimai::String.aligned(e, [': ', '@'])
57
48
  # Unknown user: kijitora@example.com
58
49
  if v['recipient']
59
50
  # There are multiple recipient addresses in the message body.
60
51
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
61
52
  v = dscontents[-1]
62
53
  end
63
- v['diagnosis'] = cv[1] + ' ' + cv[2]
64
- v['recipient'] = cv[3]
54
+ v['diagnosis'] = e
55
+ v['recipient'] = Sisimai::Address.s3s4(e[p0 + 2, e.size])
65
56
  recipients += 1
66
57
 
67
- elsif cv = e.match(/\Aundeliverable[ ]+to[ ]+(.+)\z/)
58
+ elsif e.start_with?('undeliverable ')
68
59
  # undeliverable to kijitora@example.com
69
60
  if v['recipient']
70
61
  # There are multiple recipient addresses in the message body.
71
62
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
72
63
  v = dscontents[-1]
73
64
  end
74
- v['recipient'] = Sisimai::Address.s3s4(cv[1])
65
+ v['recipient'] = Sisimai::Address.s3s4(e)
75
66
  recipients += 1
76
67
  else
77
68
  # Other error message text
@@ -81,6 +72,7 @@ module Sisimai::Lhost
81
72
  end
82
73
  return nil unless recipients > 0
83
74
 
75
+ require 'sisimai/smtp/command'
84
76
  dscontents.each do |e|
85
77
  unless e['alterrors'].to_s.empty?
86
78
  # Copy alternative error message
@@ -92,24 +84,18 @@ module Sisimai::Lhost
92
84
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
93
85
  e.delete('alterrors')
94
86
  end
95
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
96
-
97
- ReSMTP.each_key do |r|
98
- # Detect SMTP command from the message
99
- next unless e['diagnosis'] =~ ReSMTP[r]
100
- e['command'] = r.upcase
101
- break
102
- end
87
+ e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''
88
+ e['command'] = Sisimai::SMTP::Command.find(e['diagnosis'])
103
89
 
104
90
  ReFailures.each_key do |r|
105
91
  # Verify each regular expression of session errors
106
- next unless e['diagnosis'] =~ ReFailures[r]
92
+ next unless ReFailures[r].any? { |a| e['diagnosis'].include?(a) }
107
93
  e['reason'] = r
108
94
  break
109
95
  end
110
96
  end
111
97
 
112
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
98
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
113
99
  end
114
100
  def description; return 'IPSWITCH IMail Server'; end
115
101
  end
@@ -1,19 +1,17 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::InterScanMSS parses a bounce email which created by
3
- # Trend Micro InterScan Messaging Security Suite. Methods in the module are
4
- # called from only Sisimai::Message.
2
+ # Sisimai::Lhost::InterScanMSS parses a bounce email which created by Trend Micro InterScan Messaging
3
+ # Security Suite. Methods in the module are called from only Sisimai::Message.
5
4
  module InterScanMSS
6
5
  class << self
7
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/InterScanMSS.pm
8
6
  require 'sisimai/lhost'
9
- ReBackbone = %r|^Content-type:[ ]message/rfc822|.freeze
7
+ Boundaries = ['Content-Type: message/rfc822'].freeze
10
8
 
11
9
  # Parse bounce messages from InterScanMSS
12
10
  # @param [Hash] mhead Message headers of a bounce email
13
11
  # @param [String] mbody Message body of a bounce email
14
12
  # @return [Hash] Bounce data list and message/rfc822 part
15
13
  # @return [Nil] it failed to parse or the arguments are missing
16
- def make(mhead, mbody)
14
+ def inquire(mhead, mbody)
17
15
  # :received => %r/[ ][(]InterScanMSS[)][ ]with[ ]/,
18
16
  match = 0
19
17
  tryto = [
@@ -26,60 +24,59 @@ module Sisimai::Lhost
26
24
  match += 1 if tryto.any? { |a| mhead['subject'] == a }
27
25
  return nil unless match > 0
28
26
 
27
+ require 'sisimai/smtp/command'
29
28
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
30
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
31
- bodyslices = emailsteak[0].split("\n")
29
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
30
+ bodyslices = emailparts[0].split("\n")
32
31
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
33
32
  v = nil
34
33
 
35
34
  while e = bodyslices.shift do
36
- # Read error messages and delivery status lines from the head of the email
37
- # to the previous line of the beginning of the original message.
35
+ # Read error messages and delivery status lines from the head of the email to the previous
36
+ # line of the beginning of the original message.
38
37
  next if e.empty?
39
38
 
40
- v = dscontents[-1]
41
- if cv = e.match(/\A.+[<>]{3}[ \t]+.+[<]([^ ]+[@][^ ]+)[>]\z/) ||
42
- e.match(/\A.+[<>]{3}[ \t]+.+[<]([^ ]+[@][^ ]+)[>]/) ||
43
- e.match(/\A(?:Reason:[ ]+)?Unable[ ]to[ ]deliver[ ]message[ ]to[ ][<](.+)[>]/)
39
+ v = dscontents[-1]
40
+ p1 = e.index(' <<< ') || -1 # Sent <<< ...
41
+ p2 = e.index(' >>> ') || -1 # Received >>> ...
42
+ if e.include?('@') && e.include?(' <') && ( p1 > 1 || p2 > 1 || e.include?('Unable to deliver ') )
44
43
  # Sent <<< RCPT TO:<kijitora@example.co.jp>
45
44
  # Received >>> 550 5.1.1 <kijitora@example.co.jp>... user unknown
46
45
  # Unable to deliver message to <kijitora@neko.example.jp>
47
- if v['recipient'] && cv[1] != v['recipient']
46
+ cr = e[e.rindex('<') + 1, e.rindex('>') - e.rindex('<') - 1]
47
+
48
+ if v['recipient'] && cr != v['recipient']
48
49
  # There are multiple recipient addresses in the message body.
49
50
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
50
51
  v = dscontents[-1]
51
52
  end
52
- v['recipient'] = cv[1]
53
- v['diagnosis'] = e if e =~ /Unable[ ]to[ ]deliver[ ]/
53
+ v['recipient'] = cr
54
+ v['diagnosis'] = e if e.include?('Unable to deliver ')
54
55
  recipients = dscontents.size
55
56
  end
56
57
 
57
- if cv = e.match(/\ASent[ ]+[<]{3}[ ]+([A-Z]{4})[ ]/)
58
+ if e.start_with?('Sent <<< ')
58
59
  # Sent <<< RCPT TO:<kijitora@example.co.jp>
59
- v['command'] = cv[1]
60
+ v['command'] = Sisimai::SMTP::Command.find(e)
60
61
 
61
- elsif cv = e.match(/\AReceived[ ]+[>]{3}[ ]+(\d{3}[ ]+.+)\z/)
62
+ elsif e.start_with?('Received >>> ')
62
63
  # Received >>> 550 5.1.1 <kijitora@example.co.jp>... user unknown
63
- v['diagnosis'] = cv[1]
64
+ v['diagnosis'] = e[e.index(' >>> ') + 4, e.size]
64
65
  else
65
66
  # Error message in non-English
66
- if cv = e.match(/[ ][>]{3}[ ]([A-Z]{4})/)
67
- # >>> RCPT TO ...
68
- v['command'] = cv[1]
69
-
70
- elsif cv = e.match(/[ ][<]{3}[ ](.+)/)
71
- # <<< 550 5.1.1 User unknown
72
- v['diagnosis'] = cv[1]
73
- end
67
+ v['command'] = Sisimai::SMTP::Command.find(e) if e.include?(' >>> ')
68
+ p3 = e.index(' <<< ')
69
+ next unless p3
70
+ v['diagnosis'] = e[p3 + 4, e.size]
74
71
  end
75
72
  end
76
73
  return nil unless recipients > 0
77
74
 
78
75
  dscontents.each do |e|
79
76
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
80
- e['reason'] = 'userunknown' if e['diagnosis'] =~ /Unable[ ]to[ ]deliver/
77
+ e['reason'] = 'userunknown' if e['diagnosis'].include?('Unable to deliver')
81
78
  end
82
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
79
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
83
80
  end
84
81
  def description; return 'Trend Micro InterScan Messaging Security Suite'; end
85
82
  end
@@ -1,20 +1,13 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::KDDI parses a bounce email which created by au by KDDI.
3
- # Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::KDDI parses a bounce email which created by au by KDDI. Methods in the module are
3
+ # called from only Sisimai::Message.
4
4
  module KDDI
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/KDDI.pm
7
6
  require 'sisimai/lhost'
8
7
 
9
8
  Indicators = Sisimai::Lhost.INDICATORS
10
- ReBackbone = %r|^Content-Type:[ ]message/rfc822|.freeze
11
- MarkingsOf = {
12
- message: %r/\AYour[ ]mail[ ](?:
13
- sent[ ]on:?[ ][A-Z][a-z]{2}[,]
14
- |attempted[ ]to[ ]be[ ]delivered[ ]on:?[ ][A-Z][a-z]{2}[,]
15
- )
16
- /x,
17
- }.freeze
9
+ Boundaries = ['Content-Type: message/rfc822'].freeze
10
+ MarkingsOf = { message: ['Your mail sent on:', 'Your mail attempted to be delivered on:'] }.freeze
18
11
  MessagesOf = {
19
12
  'mailboxfull' => ['As their mailbox is full'],
20
13
  'norelaying' => ['Due to the following SMTP relay error'],
@@ -26,35 +19,35 @@ module Sisimai::Lhost
26
19
  # @param [String] mbody Message body of a bounce email
27
20
  # @return [Hash] Bounce data list and message/rfc822 part
28
21
  # @return [Nil] it failed to parse or the arguments are missing
29
- def make(mhead, mbody)
22
+ def inquire(mhead, mbody)
30
23
  # :'message-id' => %r/[@].+[.]ezweb[.]ne[.]jp[>]\z/,
31
24
  match = 0
32
- match += 1 if mhead['from'] =~ /no-reply[@].+[.]dion[.]ne[.]jp/
25
+ match += 1 if Sisimai::String.aligned(mhead['from'], ['no-reply@.', '.dion.ne.jp'])
33
26
  match += 1 if mhead['reply-to'].to_s == 'no-reply@app.auone-net.jp'
34
27
  match += 1 if mhead['received'].any? { |a| a.include?('ezweb.ne.jp (') }
35
28
  match += 1 if mhead['received'].any? { |a| a.include?('.au.com (') }
36
29
  return nil unless match > 0
37
30
 
38
31
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
39
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
40
- bodyslices = emailsteak[0].split("\n")
32
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
33
+ bodyslices = emailparts[0].split("\n")
41
34
  readcursor = 0 # (Integer) Points the current cursor position
42
35
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
43
36
  v = nil
44
37
 
45
38
  while e = bodyslices.shift do
46
- # Read error messages and delivery status lines from the head of the email
47
- # to the previous line of the beginning of the original message.
39
+ # Read error messages and delivery status lines from the head of the email to the previous
40
+ # line of the beginning of the original message.
48
41
  if readcursor == 0
49
42
  # Beginning of the bounce message or delivery status part
50
- readcursor |= Indicators[:deliverystatus] if e =~ MarkingsOf[:message]
43
+ readcursor |= Indicators[:deliverystatus] if MarkingsOf[:message].any? { |a| e.start_with?(a) }
51
44
  next
52
45
  end
53
46
  next if (readcursor & Indicators[:deliverystatus]) == 0
54
47
  next if e.empty?
55
48
 
56
49
  v = dscontents[-1]
57
- if cv = e.match(/\A[ \t]+Could not be delivered to: [<]([^ ]+[@][^ ]+)[>]/)
50
+ if e.include?(' Could not be delivered to: <')
58
51
  # Your mail sent on: Thu, 29 Apr 2010 11:04:47 +0900
59
52
  # Could not be delivered to: <******@**.***.**>
60
53
  # As their mailbox is full.
@@ -63,24 +56,26 @@ module Sisimai::Lhost
63
56
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
64
57
  v = dscontents[-1]
65
58
  end
66
- r = Sisimai::Address.s3s4(cv[1])
67
- next unless Sisimai::RFC5322.is_emailaddress(r)
59
+ r = Sisimai::Address.s3s4(e[e.index('<') + 1, e.size])
60
+ next unless Sisimai::Address.is_emailaddress(r)
68
61
  v['recipient'] = r
69
62
  recipients += 1
70
63
 
71
- elsif cv = e.match(/Your mail sent on: (.+)\z/)
64
+ elsif e.include?('Your mail sent on: ')
72
65
  # Your mail sent on: Thu, 29 Apr 2010 11:04:47 +0900
73
- v['date'] = cv[1]
66
+ v['date'] = e[19, e.size]
74
67
  else
75
68
  # As their mailbox is full.
76
69
  v['diagnosis'] ||= ''
77
- v['diagnosis'] << e + ' ' if e.start_with?(' ', "\t")
70
+ v['diagnosis'] << e + ' ' if e.start_with?(' ')
78
71
  end
79
72
  end
80
73
  return nil unless recipients > 0
81
74
 
75
+ require 'sisimai/smtp/command'
82
76
  dscontents.each do |e|
83
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
77
+ e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''
78
+ e['command'] = Sisimai::SMTP::Command.find(e['diagnosis'])
84
79
 
85
80
  if mhead['x-spasign'].to_s == 'NG'
86
81
  # Content-Type: text/plain; ..., X-SPASIGN: NG (spamghetti, au by KDDI)
@@ -88,8 +83,7 @@ module Sisimai::Lhost
88
83
  e['reason'] = 'filtered'
89
84
  else
90
85
  if e['command'] == 'RCPT'
91
- # set "userunknown" when the remote server rejected after RCPT
92
- # command.
86
+ # set "userunknown" when the remote server rejected after RCPT command.
93
87
  e['reason'] = 'userunknown'
94
88
  else
95
89
  # SMTP command is not RCPT
@@ -104,7 +98,7 @@ module Sisimai::Lhost
104
98
 
105
99
  end
106
100
 
107
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
101
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
108
102
  end
109
103
  def description; return 'au by KDDI: https://www.au.kddi.com'; end
110
104
  end