sisimai 4.25.16 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (177) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -3
  3. data/ANALYTICAL-PRECISION +2 -2
  4. data/Benchmarks.mk +3 -3
  5. data/CONTRIBUTING +1 -1
  6. data/ChangeLog.md +412 -393
  7. data/Developers.mk +5 -6
  8. data/Gemfile +1 -1
  9. data/Makefile +15 -15
  10. data/README-JA.md +140 -78
  11. data/README.md +290 -143
  12. data/Rakefile +9 -3
  13. data/Repository.mk +2 -3
  14. data/lib/sisimai/address.rb +118 -74
  15. data/lib/sisimai/arf.rb +84 -82
  16. data/lib/sisimai/datetime.rb +5 -52
  17. data/lib/sisimai/{data → fact}/json.rb +7 -9
  18. data/lib/sisimai/fact/yaml.rb +31 -0
  19. data/lib/sisimai/fact.rb +468 -0
  20. data/lib/sisimai/lhost/activehunter.rb +12 -14
  21. data/lib/sisimai/lhost/amavis.rb +11 -14
  22. data/lib/sisimai/lhost/amazonses.rb +37 -41
  23. data/lib/sisimai/lhost/amazonworkmail.rb +15 -18
  24. data/lib/sisimai/lhost/aol.rb +12 -14
  25. data/lib/sisimai/lhost/apachejames.rb +19 -21
  26. data/lib/sisimai/lhost/barracuda.rb +10 -12
  27. data/lib/sisimai/lhost/bigfoot.rb +21 -21
  28. data/lib/sisimai/lhost/biglobe.rb +15 -16
  29. data/lib/sisimai/lhost/courier.rb +20 -20
  30. data/lib/sisimai/lhost/domino.rb +23 -19
  31. data/lib/sisimai/lhost/einsundeins.rb +23 -18
  32. data/lib/sisimai/lhost/exchange2003.rb +30 -29
  33. data/lib/sisimai/lhost/exchange2007.rb +70 -58
  34. data/lib/sisimai/lhost/exim.rb +175 -161
  35. data/lib/sisimai/lhost/ezweb.rb +31 -56
  36. data/lib/sisimai/lhost/facebook.rb +21 -33
  37. data/lib/sisimai/lhost/fml.rb +43 -48
  38. data/lib/sisimai/lhost/gmail.rb +29 -29
  39. data/lib/sisimai/lhost/gmx.rb +18 -17
  40. data/lib/sisimai/lhost/googlegroups.rb +9 -10
  41. data/lib/sisimai/lhost/gsuite.rb +21 -27
  42. data/lib/sisimai/lhost/imailserver.rb +25 -39
  43. data/lib/sisimai/lhost/interscanmss.rb +28 -31
  44. data/lib/sisimai/lhost/kddi.rb +22 -28
  45. data/lib/sisimai/lhost/mailfoundry.rb +11 -12
  46. data/lib/sisimai/lhost/mailmarshalsmtp.rb +25 -29
  47. data/lib/sisimai/lhost/mailru.rb +33 -27
  48. data/lib/sisimai/lhost/mcafee.rb +21 -31
  49. data/lib/sisimai/lhost/messagelabs.rb +17 -20
  50. data/lib/sisimai/lhost/messagingserver.rb +40 -37
  51. data/lib/sisimai/lhost/mfilter.rb +15 -16
  52. data/lib/sisimai/lhost/mxlogic.rb +24 -23
  53. data/lib/sisimai/lhost/notes.rb +17 -17
  54. data/lib/sisimai/lhost/office365.rb +63 -27
  55. data/lib/sisimai/lhost/opensmtpd.rb +12 -13
  56. data/lib/sisimai/lhost/outlook.rb +12 -15
  57. data/lib/sisimai/lhost/postfix.rb +179 -129
  58. data/lib/sisimai/lhost/powermta.rb +12 -14
  59. data/lib/sisimai/lhost/qmail.rb +44 -47
  60. data/lib/sisimai/lhost/receivingses.rb +15 -20
  61. data/lib/sisimai/lhost/sendgrid.rb +34 -32
  62. data/lib/sisimai/lhost/sendmail.rb +66 -53
  63. data/lib/sisimai/lhost/surfcontrol.rb +19 -19
  64. data/lib/sisimai/lhost/v5sendmail.rb +45 -39
  65. data/lib/sisimai/lhost/verizon.rb +35 -39
  66. data/lib/sisimai/lhost/x1.rb +18 -17
  67. data/lib/sisimai/lhost/x2.rb +17 -14
  68. data/lib/sisimai/lhost/x3.rb +19 -19
  69. data/lib/sisimai/lhost/x4.rb +72 -57
  70. data/lib/sisimai/lhost/x5.rb +17 -19
  71. data/lib/sisimai/lhost/x6.rb +41 -17
  72. data/lib/sisimai/lhost/yahoo.rb +17 -16
  73. data/lib/sisimai/lhost/yandex.rb +16 -20
  74. data/lib/sisimai/lhost/zoho.rb +16 -15
  75. data/lib/sisimai/lhost.rb +8 -10
  76. data/lib/sisimai/mail/maildir.rb +1 -3
  77. data/lib/sisimai/mail/mbox.rb +3 -4
  78. data/lib/sisimai/mail/memory.rb +0 -1
  79. data/lib/sisimai/mail/stdin.rb +1 -3
  80. data/lib/sisimai/mail.rb +3 -7
  81. data/lib/sisimai/mda.rb +28 -42
  82. data/lib/sisimai/message.rb +435 -326
  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 +12 -12
  134. data/lib/sisimai/rhost/spectrum.rb +10 -12
  135. data/lib/sisimai/rhost/{tencentqq.rb → tencent.rb} +7 -8
  136. data/lib/sisimai/rhost.rb +23 -31
  137. data/lib/sisimai/smtp/command.rb +59 -0
  138. data/lib/sisimai/smtp/error.rb +4 -7
  139. data/lib/sisimai/smtp/reply.rb +161 -74
  140. data/lib/sisimai/smtp/status.rb +504 -393
  141. data/lib/sisimai/smtp/transcript.rb +124 -0
  142. data/lib/sisimai/smtp.rb +0 -1
  143. data/lib/sisimai/string.rb +74 -5
  144. data/lib/sisimai/time.rb +1 -2
  145. data/lib/sisimai/version.rb +1 -1
  146. data/lib/sisimai.rb +35 -21
  147. data/set-of-emails/maildir/bsd/lhost-domino-02.eml +6 -3
  148. data/set-of-emails/maildir/bsd/lhost-googlegroups-15.eml +174 -0
  149. data/set-of-emails/maildir/bsd/lhost-gsuite-15.eml +229 -0
  150. data/set-of-emails/maildir/bsd/lhost-postfix-75.eml +51 -0
  151. data/set-of-emails/maildir/bsd/lhost-postfix-76.eml +101 -0
  152. data/set-of-emails/maildir/bsd/lhost-postfix-77.eml +74 -0
  153. data/set-of-emails/maildir/bsd/lhost-postfix-78.eml +91 -0
  154. data/set-of-emails/maildir/bsd/lhost-receivingses-08.eml +88 -0
  155. data/set-of-emails/maildir/bsd/rfc3464-43.eml +88 -0
  156. data/set-of-emails/maildir/bsd/rhost-google-03.eml +101 -0
  157. data/set-of-emails/maildir/bsd/rhost-google-04.eml +102 -0
  158. data/set-of-emails/maildir/bsd/rhost-google-05.eml +82 -0
  159. data/set-of-emails/maildir/bsd/rhost-google-06.eml +102 -0
  160. data/set-of-emails/maildir/bsd/rhost-google-07.eml +69 -0
  161. data/set-of-emails/maildir/bsd/rhost-google-08.eml +99 -0
  162. data/sisimai-java.gemspec +1 -1
  163. data/sisimai.gemspec +1 -1
  164. metadata +41 -20
  165. data/.rspec +0 -2
  166. data/lib/sisimai/data/yaml.rb +0 -33
  167. data/lib/sisimai/data.rb +0 -411
  168. data/lib/sisimai/mime.rb +0 -456
  169. /data/set-of-emails/maildir/bsd/{rfc3464-41.eml → rfc3834-05.eml} +0 -0
  170. /data/set-of-emails/maildir/bsd/{rhost-googleapps-01.eml → rhost-google-01.eml} +0 -0
  171. /data/set-of-emails/maildir/bsd/{rhost-googleapps-02.eml → rhost-google-02.eml} +0 -0
  172. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-01.eml → rhost-microsoft-01.eml} +0 -0
  173. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-02.eml → rhost-microsoft-02.eml} +0 -0
  174. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-03.eml → rhost-microsoft-03.eml} +0 -0
  175. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-01.eml → rhost-tencent-01.eml} +0 -0
  176. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-02.eml → rhost-tencent-02.eml} +0 -0
  177. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-03.eml → rhost-tencent-03.eml} +0 -0
@@ -1,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