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,14 +1,12 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::SurfControl parses a bounce email which created by
3
- # WebSense SurfControl.
2
+ # Sisimai::Lhost::SurfControl parses a bounce email which created by WebSense SurfControl.
4
3
  # Methods in the module are called from only Sisimai::Message.
5
4
  module SurfControl
6
5
  class << self
7
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/SurfControl.pm
8
6
  require 'sisimai/lhost'
9
7
 
10
8
  Indicators = Sisimai::Lhost.INDICATORS
11
- ReBackbone = %r|^Content-Type:[ ]message/rfc822|.freeze
9
+ Boundaries = ['Content-Type: message/rfc822'].freeze
12
10
  StartingOf = { message: ['Your message could not be sent.'] }.freeze
13
11
 
14
12
  # Parse bounce messages from SurfControl
@@ -16,7 +14,7 @@ module Sisimai::Lhost
16
14
  # @param [String] mbody Message body of a bounce email
17
15
  # @return [Hash] Bounce data list and message/rfc822 part
18
16
  # @return [Nil] it failed to parse or the arguments are missing
19
- def make(mhead, mbody)
17
+ def inquire(mhead, mbody)
20
18
  # X-SEF-ZeroHour-RefID: fgs=000000000
21
19
  # X-SEF-Processed: 0_0_0_000__2010_04_29_23_34_45
22
20
  # X-Mailer: SurfControl E-mail Filter
@@ -24,19 +22,18 @@ module Sisimai::Lhost
24
22
  return nil unless mhead['x-mailer']
25
23
  return nil unless mhead['x-mailer'] == 'SurfControl E-mail Filter'
26
24
 
27
- require 'sisimai/rfc1894'
28
25
  fieldtable = Sisimai::RFC1894.FIELDTABLE
29
26
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
30
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
31
- bodyslices = emailsteak[0].split("\n")
27
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
28
+ bodyslices = emailparts[0].split("\n")
32
29
  readslices = ['']
33
30
  readcursor = 0 # (Integer) Points the current cursor position
34
31
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
35
32
  v = nil
36
33
 
37
34
  while e = bodyslices.shift do
38
- # Read error messages and delivery status lines from the head of the email
39
- # 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.
40
37
  readslices << e # Save the current line for the next loop
41
38
 
42
39
  if readcursor == 0
@@ -58,24 +55,27 @@ module Sisimai::Lhost
58
55
  # --- Message non-deliverable.
59
56
  v = dscontents[-1]
60
57
 
61
- if cv = e.match(/\AAddressed To:[ ]*([^ ]+?[@][^ ]+?)\z/)
58
+ if e.start_with?('Addressed To:') && e.index('@') > 1
62
59
  # Addressed To: kijitora@example.com
63
60
  if v['recipient']
64
61
  # There are multiple recipient addresses in the message body.
65
62
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
66
63
  v = dscontents[-1]
67
64
  end
68
- v['recipient'] = cv[1]
65
+ v['recipient'] = Sisimai::Address.s3s4(e[e.index(':') + 2, e.size])
69
66
  recipients += 1
70
67
 
71
- elsif e =~ /\A(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)[ ,]/
68
+ elsif %w[Sun Mon Tue Wed Thu Fri Sat].any? { |a| e.start_with?(a) }
72
69
  # Thu 29 Apr 2010 23:34:45 +0900
73
70
  v['date'] = e
74
71
 
75
- elsif cv = e.match(/\A[^ ]+[@][^ ]+:[ ]*\[(\d+[.]\d+[.]\d+[.]\d)\],[ ]*(.+)\z/)
72
+ elsif Sisimai::String.aligned(e, ['@', ':', ' ', '[', '],', '...'])
76
73
  # kijitora@example.com: [192.0.2.5], 550 kijitora@example.com... No such user
77
- v['rhost'] = cv[1]
78
- v['diagnosis'] = cv[2]
74
+ p1 = e.index('[')
75
+ p2 = e.index('],', p1 + 1)
76
+ v['rhost'] = e[p1 + 1, p2 - p1 - 1]
77
+ v['diagnosis'] = Sisimai::String.sweep(e[p2 + 2, e.size])
78
+
79
79
  else
80
80
  # Fallback, parse RFC3464 headers.
81
81
  if f = Sisimai::RFC1894.match(e)
@@ -87,8 +87,8 @@ module Sisimai::Lhost
87
87
  else
88
88
  # Continued line of the value of Diagnostic-Code field
89
89
  next unless readslices[-2].start_with?('Diagnostic-Code:')
90
- next unless cv = e.match(/\A[ \t]+(.+)\z/)
91
- v['diagnosis'] << ' ' << cv[1]
90
+ next unless e.start_with?(' ')
91
+ v['diagnosis'] << ' ' << Sisimai::String.sweep(e)
92
92
  readslices[-1] = 'Diagnostic-Code: ' << e
93
93
  end
94
94
  end
@@ -96,7 +96,7 @@ module Sisimai::Lhost
96
96
  return nil unless recipients > 0
97
97
 
98
98
  dscontents.each { |e| e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) }
99
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
99
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
100
100
  end
101
101
  def description; return 'WebSense SurfControl'; end
102
102
  end
@@ -1,16 +1,13 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::V5sendmail parses a bounce email which created by
3
- # Sendmail version 5.
4
- # Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::V5sendmail parses a bounce email which created by Sendmail version 5. Methods in
3
+ # the module are called from only Sisimai::Message.
5
4
  module V5sendmail
6
5
  class << self
7
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/V5sendmail.pm
8
6
  require 'sisimai/lhost'
9
7
 
10
8
  Indicators = Sisimai::Lhost.INDICATORS
11
- ReBackbone = %r/^[ ]+-----[ ](?:Unsent[ ]message[ ]follows|No[ ]message[ ]was[ ]collected)[ ]-----/.freeze
12
- StartingOf = { message: ['----- Transcript of session follows -----'] }
13
- MarkingsOf = {
9
+ Boundaries = [' ----- Unsent message follows -----', ' ----- No message was collected -----'].freeze
10
+ StartingOf = {
14
11
  # Error text regular expressions which defined in src/savemail.c
15
12
  # savemail.c:485| (void) fflush(stdout);
16
13
  # savemail.c:486| p = queuename(e->e_parent, 'x');
@@ -27,7 +24,8 @@ module Sisimai::Lhost
27
24
  # savemail.c:497| while (fgets(buf, sizeof buf, xfile) != NULL)
28
25
  # savemail.c:498| putline(buf, fp, m);
29
26
  # savemail.c:499| (void) fclose(xfile);
30
- error: %r/\A[.]+ while talking to .+[:]\z/,
27
+ error: [' while talking to '],
28
+ message: ['----- Transcript of session follows -----'],
31
29
  }.freeze
32
30
 
33
31
  # Parse bounce messages from Sendmail version 5
@@ -35,26 +33,27 @@ module Sisimai::Lhost
35
33
  # @param [String] mbody Message body of a bounce email
36
34
  # @return [Hash] Bounce data list and message/rfc822 part
37
35
  # @return [Nil] it failed to parse or the arguments are missing
38
- def make(mhead, mbody)
36
+ def inquire(mhead, mbody)
39
37
  # :from => %r/\AMail Delivery Subsystem/,
40
- return nil unless mhead['subject'] =~ /\AReturned mail: [A-Z]/
38
+ return nil unless mhead['subject'].start_with?('Returned mail: ')
41
39
 
42
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
43
- return nil if emailsteak[1].empty?
40
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
41
+ return nil unless emailparts[1].size > 0
44
42
 
43
+ require 'sisimai/smtp/command'
45
44
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
46
- bodyslices = emailsteak[0].split("\n")
45
+ bodyslices = emailparts[0].split("\n")
47
46
  readcursor = 0 # (Integer) Points the current cursor position
48
47
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
48
+ anotherset = {} # (Hash) Another error information
49
49
  responding = [] # (Array) Responses from remote server
50
50
  commandset = [] # (Array) SMTP command which is sent to remote server
51
- anotherset = {} # (Hash) Another error information
52
51
  errorindex = -1 # (Integer)
53
52
  v = nil
54
53
 
55
54
  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.
55
+ # Read error messages and delivery status lines from the head of the email to the previous
56
+ # line of the beginning of the original message.
58
57
  if readcursor == 0
59
58
  # Beginning of the bounce message or delivery status part
60
59
  readcursor |= Indicators[:deliverystatus] if e.include?(StartingOf[:message][0])
@@ -71,57 +70,64 @@ module Sisimai::Lhost
71
70
  # 421 example.org (smtp)... Deferred: Connection timed out during user open with example.org
72
71
  v = dscontents[-1]
73
72
 
74
- if cv = e.match(/\A\d{3}[ ]+[<]([^ ]+[@][^ ]+)[>][.]{3}[ ]*(.+)\z/)
73
+ if e.start_with?('5', '4') && Sisimai::String.aligned(e, [' <', '@', '>...'])
75
74
  # 550 <kijitora@example.org>... User unknown
76
75
  if v['recipient']
77
76
  # There are multiple recipient addresses in the message body.
78
77
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
79
78
  v = dscontents[-1]
80
79
  end
81
- v['recipient'] = cv[1]
82
- v['diagnosis'] = cv[2]
80
+ p1 = e.index('<', 0)
81
+ p2 = e.index('>...')
82
+ v['recipient'] = e[p1 + 1, p2 - p1 - 1]
83
+ v['diagnosis'] = e[p2 + 5, e.size]
83
84
 
84
85
  # Concatenate the response of the server and error message
85
86
  v['diagnosis'] << ': ' << responding[recipients] if responding[recipients]
86
87
  recipients += 1
87
88
 
88
- elsif cv = e.match(/\A[>]{3}[ ]*([A-Z]{4})[ ]*/)
89
+ elsif e.start_with?('>>> ')
89
90
  # >>> RCPT To:<kijitora@example.org>
90
- commandset[recipients] = cv[1]
91
+ cv = Sisimai::SMTP::Command.find(e); commandset[recipients] = cv if cv
91
92
 
92
- elsif cv = e.match(/\A[<]{3}[ ]+(.+)\z/)
93
+ elsif e.start_with?('<<< ')
93
94
  # <<< Response
94
95
  # <<< 501 <shironeko@example.co.jp>... no access from mail server [192.0.2.55] which is an open relay.
95
96
  # <<< 550 Requested User Mailbox not found. No such user here.
96
- responding[recipients] = cv[1]
97
+ responding[recipients] = e[4, e.size]
98
+
97
99
  else
98
100
  # Detect SMTP session error or connection error
99
101
  next if v['sessionerr']
100
102
 
101
- if e =~ MarkingsOf[:error]
103
+ if e.include?(StartingOf[:error][0])
102
104
  # ----- Transcript of session follows -----
103
105
  # ... while talking to mta.example.org.:
104
106
  v['sessionerr'] = true
105
107
  next
106
108
  end
107
109
 
108
- if cv = e.match(/\A\d{3}[ ]+.+[.]{3}[ \t]*(.+)\z/)
110
+ if e.start_with?('4', '5') && e.include?('... ')
109
111
  # 421 example.org (smtp)... Deferred: Connection timed out during user open with example.org
110
- anotherset['diagnosis'] = cv[1]
112
+ anotherset['replycode'] = e[0, 3]
113
+ anotherset['diagnosis'] = e[e.index('... ') + 4, e.size]
111
114
  end
112
115
  end
113
116
  end
114
117
 
115
- if recipients == 0 && cv = emailsteak[1].match(/^To:[ ]*(.+)$/)
118
+ p1 = emailparts[1].index("\nTo: ") || -1
119
+ p2 = emailparts[1].index("\n", p1 + 6) || -1
120
+ if recipients == 0 && p1 > 0
116
121
  # Get the recipient address from "To:" header at the original message
117
- dscontents[0]['recipient'] = Sisimai::Address.s3s4(cv[1])
122
+ dscontents[0]['recipient'] = Sisimai::Address.s3s4(emailparts[1][p1, p2 - p1 - 5])
118
123
  recipients = 1
119
124
  end
120
125
  return nil unless recipients > 0
121
126
 
122
127
  dscontents.each do |e|
123
128
  errorindex += 1
124
- e['command'] = commandset[errorindex] || ''
129
+ e.delete('sessionerr')
130
+
125
131
  e['diagnosis'] ||= if anotherset['diagnosis'].to_s.size > 0
126
132
  # Copy alternative error message
127
133
  anotherset['diagnosis']
@@ -130,18 +136,18 @@ module Sisimai::Lhost
130
136
  responding[errorindex]
131
137
  end
132
138
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
133
-
134
- unless e['recipient'] =~ /\A[^ ]+[@][^ ]+\z/
135
- # @example.jp, no local part
136
- if cv = e['diagnosis'].match(/[<]([^ ]+[@][^ ]+)[>]/)
137
- # Get email address from the value of Diagnostic-Code header
138
- e['recipient'] = cv[1]
139
- end
140
- end
141
- e.delete('sessionerr')
139
+ e['replycode'] = Sisimai::SMTP::Reply.find(e['diagnosis']) || anotherset['replycode']
140
+ e['command'] = commandset[errorindex] || Sisimai::SMTP::Command.find(e['diagnosis']) || ''
141
+
142
+ # @example.jp, no local part
143
+ # Get email address from the value of Diagnostic-Code header
144
+ next if e['recipient'].include?('@')
145
+ p1 = e['diagnosis'].index('<'); next unless p1
146
+ p2 = e['diagnosis'].index('>'); next unless p2
147
+ e['recipient'] = Sisimai::Address.s3s4(e[p1, p2 - p1])
142
148
  end
143
149
 
144
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
150
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
145
151
  end
146
152
  def description; return 'Sendmail version 5'; end
147
153
  end
@@ -1,10 +1,8 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::Verizon parses a bounce email which created by
3
- # Verizon Wireless.
4
- # Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::Verizon parses a bounce email which created by Verizon Wireless. Methods in the
3
+ # module are called from only Sisimai::Message.
5
4
  module Verizon
6
5
  class << self
7
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/Verizon.pm
8
6
  require 'sisimai/lhost'
9
7
  Indicators = Sisimai::Lhost.INDICATORS
10
8
 
@@ -13,25 +11,25 @@ module Sisimai::Lhost
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
  match = -1
18
16
  while true
19
17
  # Check the value of "From" header
20
18
  # :'subject' => %r/Undeliverable Message/,
21
19
  break unless mhead['received'].any? { |a| a.include?('.vtext.com (') }
22
20
  match = 1 if mhead['from'] == 'post_master@vtext.com'
23
- match = 0 if mhead['from'] =~ /[<]?sysadmin[@].+[.]vzwpix[.]com[>]?\z/
21
+ match = 0 if Sisimai::String.aligned(mhead['from'], ['sysadmin@', '.vzwpix.com'])
24
22
  break
25
23
  end
26
24
  return nil if match < 0
27
25
 
26
+ boundaries = []
28
27
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
29
- rebackbone = /^__BOUNDARY_STRING_HERE__/
28
+ emailparts = []
30
29
  readcursor = 0 # (Integer) Points the current cursor position
31
30
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
32
31
  senderaddr = '' # (String) Sender address in the message body
33
32
  subjecttxt = '' # (String) Subject of the original message
34
-
35
33
  markingsof = {} # (Hash) Delimiter patterns
36
34
  startingof = {} # (Hash) Delimiter strings
37
35
  messagesof = {} # (Hash) Error message patterns
@@ -39,22 +37,21 @@ module Sisimai::Lhost
39
37
 
40
38
  if match == 1
41
39
  # vtext.com
42
- markingsof = { message: %r/\AError:[ \t]/ }
40
+ markingsof = { message: ['Error: '] }
43
41
  messagesof = {
44
42
  # The attempted recipient address does not exist.
45
43
  'userunknown' => ['550 - Requested action not taken: no such user here'],
46
44
  }
47
- boundary00 = Sisimai::MIME.boundary(mhead['content-type'], 1) || ''
48
- rebackbone = Regexp.new('^' << Regexp.escape(boundary00)) unless boundary00.empty?
49
- emailsteak = Sisimai::RFC5322.fillet(mbody, rebackbone)
50
- bodyslices = emailsteak[0].split("\n")
45
+ boundaries = [Sisimai::RFC2045.boundary(mhead['content-type'], 1)]
46
+ emailparts = Sisimai::RFC5322.part(mbody, boundaries)
47
+ bodyslices = emailparts[0].split("\n")
51
48
 
52
49
  while e = bodyslices.shift do
53
- # Read error messages and delivery status lines from the head of the email
54
- # to the previous line of the beginning of the original message.
50
+ # Read error messages and delivery status lines from the head of the email to the previous
51
+ # line of the beginning of the original message.
55
52
  if readcursor == 0
56
53
  # Beginning of the bounce message or delivery status part
57
- readcursor |= Indicators[:deliverystatus] if e =~ markingsof[:message]
54
+ readcursor |= Indicators[:deliverystatus] if e.start_with?(markingsof[:message][0])
58
55
  next
59
56
  end
60
57
  next if (readcursor & Indicators[:deliverystatus]) == 0
@@ -66,41 +63,40 @@ module Sisimai::Lhost
66
63
  # MAIL FROM: *******@hg.example.com
67
64
  # RCPT TO: *****@vtext.com
68
65
  v = dscontents[-1]
69
- if cv = e.match(/\A[ \t]+RCPT TO: (.*)\z/)
66
+ if e.start_with?(' RCPT TO: ')
70
67
  if v['recipient']
71
68
  # There are multiple recipient addresses in the message body.
72
69
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
73
70
  v = dscontents[-1]
74
71
  end
75
72
 
76
- v['recipient'] = cv[1]
73
+ v['recipient'] = e[11, e.size]
77
74
  recipients += 1
78
75
  next
79
76
 
80
- elsif cv = e.match(/\A[ \t]+MAIL FROM:[ \t](.+)\z/)
77
+ elsif e.start_with?(' MAIL FROM: ')
81
78
  # MAIL FROM: *******@hg.example.com
82
- senderaddr = cv[1] if senderaddr.empty?
79
+ senderaddr = e[13, e.size] if senderaddr.empty?
83
80
 
84
- elsif cv = e.match(/\A[ \t]+Subject:[ \t](.+)\z/)
81
+ elsif e.start_with?(' Subject: ')
85
82
  # Subject:
86
- subjecttxt = cv[1] if subjecttxt.empty?
83
+ subjecttxt = e[11, e.size] if subjecttxt.empty?
87
84
  else
88
85
  # 550 - Requested action not taken: no such user here
89
- v['diagnosis'] = e if e =~ /\A(\d{3})[ \t][-][ \t](.*)\z/
86
+ v['diagnosis'] = e if e.include?(' - ')
90
87
  end
91
88
  end
92
89
  else
93
90
  # vzwpix.com
94
91
  startingof = { message: ['Message could not be delivered to mobile'] }
95
92
  messagesof = { 'userunknown' => ['No valid recipients for this MM'] }
96
- boundary00 = Sisimai::MIME.boundary(mhead['content-type'], 1)
97
- rebackbone = Regexp.new('^' << Regexp.escape(boundary00)) unless boundary00.empty?
98
- emailsteak = Sisimai::RFC5322.fillet(mbody, rebackbone)
99
- bodyslices = emailsteak[0].split("\n")
93
+ boundaries = [Sisimai::RFC2045.boundary(mhead['content-type'], 1)]
94
+ emailparts = Sisimai::RFC5322.part(mbody, boundaries)
95
+ bodyslices = emailparts[0].split("\n")
100
96
 
101
97
  while e = bodyslices.shift do
102
- # Read error messages and delivery status lines from the head of the email
103
- # to the previous line of the beginning of the original message.
98
+ # Read error messages and delivery status lines from the head of the email to the previous
99
+ # line of the beginning of the original message.
104
100
  if readcursor == 0
105
101
  # Beginning of the bounce message or delivery status part
106
102
  readcursor |= Indicators[:deliverystatus] if e.start_with?(startingof[:message][0])
@@ -115,35 +111,35 @@ module Sisimai::Lhost
115
111
  # Subject: test for bounce
116
112
  # Date: Wed, 20 Jun 2013 10:29:52 +0000
117
113
  v = dscontents[-1]
118
- if cv = e.match(/\ATo:[ \t]+(.*)\z/)
114
+ if e.start_with?('To: ')
119
115
  if v['recipient']
120
116
  # There are multiple recipient addresses in the message body.
121
117
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
122
118
  v = dscontents[-1]
123
119
  end
124
- v['recipient'] = Sisimai::Address.s3s4(cv[1])
120
+ v['recipient'] = Sisimai::Address.s3s4(e[4, e.size])
125
121
  recipients += 1
126
122
  next
127
123
 
128
- elsif cv = e.match(/\AFrom:[ \t](.+)\z/)
124
+ elsif e.start_with?('From: ')
129
125
  # From: kijitora <kijitora@example.jp>
130
- senderaddr = Sisimai::Address.s3s4(cv[1]) if senderaddr.empty?
126
+ senderaddr = Sisimai::Address.s3s4(e[4, e.size]) if senderaddr.empty?
131
127
 
132
- elsif cv = e.match(/\ASubject:[ \t](.+)\z/)
128
+ elsif e.start_with?('Subject: ')
133
129
  # Subject:
134
- subjecttxt = cv[1] if subjecttxt.empty?
130
+ subjecttxt = e[9, e.size] if subjecttxt.empty?
135
131
  else
136
132
  # Message could not be delivered to mobile.
137
133
  # Error: No valid recipients for this MM
138
- v['diagnosis'] = e if e =~ /\AError:[ \t]+(.+)\z/
134
+ v['diagnosis'] = Sisimai::String.sweep(e[7, e.size]) if e.start_with?('Error: ')
139
135
  end
140
136
  end
141
137
  end
142
138
  return nil unless recipients > 0
143
139
 
144
140
  # Set the value of "MAIL FROM:" and "From:"
145
- emailsteak[1] << ('From: ' << senderaddr << "\n") unless emailsteak[1] =~ /^From: /
146
- emailsteak[1] << ('Subject: ' << subjecttxt << "\n") unless emailsteak[1] =~ /^Subject: /
141
+ emailparts[1] << ('From: ' << senderaddr << "\n") if emailparts[1].include?("\nFrom: ") == false
142
+ emailparts[1] << ('Subject: ' << subjecttxt << "\n") if emailparts[1].include?("\nSubject: ") == false
147
143
 
148
144
  dscontents.each do |e|
149
145
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
@@ -155,7 +151,7 @@ module Sisimai::Lhost
155
151
  end
156
152
  end
157
153
 
158
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
154
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
159
155
  end
160
156
  def description; return 'Verizon Wireless: https://www.verizonwireless.com'; end
161
157
  end
@@ -1,38 +1,37 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::X1 parses a bounce email which created by Unknown MTA #1.
3
- # Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::X1 parses a bounce email which created by Unknown MTA #1. Methods in the module
3
+ # are called from only Sisimai::Message.
4
4
  module X1
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/X1.pm
7
6
  require 'sisimai/lhost'
8
7
 
9
8
  Indicators = Sisimai::Lhost.INDICATORS
10
- ReBackbone = %r|^Received: from \d+[.]\d+[.]\d+[.]\d|.freeze
11
- MarkingsOf = { message: %r/\AThe original message was received at (.+)\z/ }.freeze
9
+ Boundaries = ['Received: from '].freeze
10
+ MarkingsOf = { message: ['The original message was received at '] }.freeze
12
11
 
13
12
  # Parse bounce messages from Unknown MTA #1
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
  return nil unless mhead['subject'].start_with?('Returned Mail: ')
20
19
  return nil unless mhead['from'].start_with?('"Mail Deliver System" ')
21
20
 
22
21
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
23
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
24
- bodyslices = emailsteak[0].split("\n")
22
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
23
+ bodyslices = emailparts[0].split("\n")
25
24
  readcursor = 0 # (Integer) Points the current cursor position
26
25
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
27
26
  datestring = '' # (String) Date string
28
27
  v = nil
29
28
 
30
29
  while e = bodyslices.shift do
31
- # Read error messages and delivery status lines from the head of the email
32
- # to the previous line of the beginning of the original message.
30
+ # Read error messages and delivery status lines from the head of the email to the previous
31
+ # line of the beginning of the original message.
33
32
  if readcursor == 0
34
33
  # Beginning of the bounce message or delivery status part
35
- readcursor |= Indicators[:deliverystatus] if e =~ MarkingsOf[:message]
34
+ readcursor |= Indicators[:deliverystatus] if e.start_with?(MarkingsOf[:message][0])
36
35
  next
37
36
  end
38
37
  next if (readcursor & Indicators[:deliverystatus]) == 0
@@ -46,20 +45,22 @@ module Sisimai::Lhost
46
45
  # kijitora@example.co.jp [User unknown]
47
46
  v = dscontents[-1]
48
47
 
49
- if cv = e.match(/\A([^ ]+?[@][^ ]+?)[ ]+\[(.+)\]\z/)
48
+ if Sisimai::String.aligned(e, ['@', ' [', ']'])
50
49
  # kijitora@example.co.jp [User unknown]
51
50
  if v['recipient']
52
51
  # There are multiple recipient addresses in the message body.
53
52
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
54
53
  v = dscontents[-1]
55
54
  end
56
- v['recipient'] = cv[1]
57
- v['diagnosis'] = cv[2]
55
+ p1 = e.index(' ')
56
+ p2 = e.index(']')
57
+ v['recipient'] = e[0, p1]
58
+ v['diagnosis'] = e[p1 + 2, p2 - p1 - 2]
58
59
  recipients += 1
59
60
 
60
- elsif cv = e.match(MarkingsOf[:message])
61
+ elsif e.start_with?(MarkingsOf[:message][0])
61
62
  # The original message was received at Thu, 29 Apr 2010 23:34:45 +0900 (JST)
62
- datestring = cv[1]
63
+ datestring = e[MarkingsOf[:message][0].size, e.size]
63
64
  end
64
65
  end
65
66
  return nil unless recipients > 0
@@ -69,7 +70,7 @@ module Sisimai::Lhost
69
70
  e['date'] = datestring || ''
70
71
  end
71
72
 
72
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
73
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
73
74
  end
74
75
  def description; return 'Unknown MTA #1'; end
75
76
  end
@@ -1,13 +1,12 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::X2 parses a bounce email which created by Unknown MTA #2.
3
- # Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::X2 parses a bounce email which created by Unknown MTA #2. Methods in the module
3
+ # are called from only Sisimai::Message.
4
4
  module X2
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/X2.pm
7
6
  require 'sisimai/lhost'
8
7
 
9
8
  Indicators = Sisimai::Lhost.INDICATORS
10
- ReBackbone = %r|^--- Original message follows[.]|.freeze
9
+ Boundaries = ['--- Original message follows.'].freeze
11
10
  StartingOf = { message: ['Unable to deliver message to the following address'] }.freeze
12
11
 
13
12
  # Parse bounce messages from Unknown MTA #2
@@ -15,20 +14,24 @@ module Sisimai::Lhost
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)
19
- return nil unless mhead['from'].include?('MAILER-DAEMON@')
20
- return nil unless mhead['subject'] =~ %r/\A(?>Delivery failure|fail(?:ure|ed) delivery)/
17
+ def inquire(mhead, mbody)
18
+ match = nil
19
+ match ||= 1 if mhead['from'].include?('MAILER-DAEMON@')
20
+ match ||= 1 if mhead['subject'].start_with?('Delivery failure')
21
+ match ||= 1 if mhead['subject'].start_with?('failure delivery')
22
+ match ||= 1 if mhead['subject'].start_with?('failed delivery')
23
+ return nil unless match
21
24
 
22
25
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
23
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
24
- bodyslices = emailsteak[0].split("\n")
26
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
27
+ bodyslices = emailparts[0].split("\n")
25
28
  readcursor = 0 # (Integer) Points the current cursor position
26
29
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
27
30
  v = nil
28
31
 
29
32
  while e = bodyslices.shift do
30
- # Read error messages and delivery status lines from the head of the email
31
- # 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.
32
35
  if readcursor == 0
33
36
  # Beginning of the bounce message or delivery status part
34
37
  readcursor |= Indicators[:deliverystatus] if e.start_with?(StartingOf[:message][0])
@@ -44,14 +47,14 @@ module Sisimai::Lhost
44
47
  # This user doesn't have a example.com account (kijitora@example.com) [0]
45
48
  v = dscontents[-1]
46
49
 
47
- if cv = e.match(/\A[<]([^ ]+[@][^ ]+)[>]:\z/)
50
+ if e.start_with?('<') && Sisimai::String.aligned(e, ['<', '@', '>', ':'])
48
51
  # <kijitora@example.com>:
49
52
  if v['recipient']
50
53
  # There are multiple recipient addresses in the message body.
51
54
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
52
55
  v = dscontents[-1]
53
56
  end
54
- v['recipient'] = cv[1]
57
+ v['recipient'] = e[1, e.size - 3]
55
58
  recipients += 1
56
59
  else
57
60
  # This user doesn't have a example.com account (kijitora@example.com) [0]
@@ -62,7 +65,7 @@ module Sisimai::Lhost
62
65
  return nil unless recipients > 0
63
66
 
64
67
  dscontents.each { |e| e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) }
65
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
68
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
66
69
  end
67
70
  def description; return 'Unknown MTA #2'; end
68
71
  end