mail 2.7.1 → 2.9.1

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 (126) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +135 -59
  3. data/lib/mail/attachments_list.rb +5 -6
  4. data/lib/mail/body.rb +51 -74
  5. data/lib/mail/constants.rb +27 -5
  6. data/lib/mail/elements/address.rb +27 -27
  7. data/lib/mail/elements/address_list.rb +1 -1
  8. data/lib/mail/elements/content_disposition_element.rb +1 -1
  9. data/lib/mail/elements/content_location_element.rb +1 -1
  10. data/lib/mail/elements/content_transfer_encoding_element.rb +1 -1
  11. data/lib/mail/elements/content_type_element.rb +8 -4
  12. data/lib/mail/elements/date_time_element.rb +1 -1
  13. data/lib/mail/elements/envelope_from_element.rb +13 -7
  14. data/lib/mail/elements/message_ids_element.rb +14 -5
  15. data/lib/mail/elements/mime_version_element.rb +1 -1
  16. data/lib/mail/elements/phrase_list.rb +7 -2
  17. data/lib/mail/elements/received_element.rb +20 -6
  18. data/lib/mail/encodings/7bit.rb +5 -0
  19. data/lib/mail/encodings/base64.rb +2 -2
  20. data/lib/mail/encodings/quoted_printable.rb +2 -2
  21. data/lib/mail/encodings/unix_to_unix.rb +1 -1
  22. data/lib/mail/encodings.rb +32 -61
  23. data/lib/mail/envelope.rb +11 -14
  24. data/lib/mail/field.rb +100 -86
  25. data/lib/mail/field_list.rb +61 -8
  26. data/lib/mail/fields/bcc_field.rb +33 -52
  27. data/lib/mail/fields/cc_field.rb +27 -49
  28. data/lib/mail/fields/comments_field.rb +26 -37
  29. data/lib/mail/fields/common_address_field.rb +162 -0
  30. data/lib/mail/fields/common_date_field.rb +56 -0
  31. data/lib/mail/fields/common_field.rb +77 -0
  32. data/lib/mail/fields/common_message_id_field.rb +41 -0
  33. data/lib/mail/fields/content_description_field.rb +6 -14
  34. data/lib/mail/fields/content_disposition_field.rb +11 -38
  35. data/lib/mail/fields/content_id_field.rb +23 -51
  36. data/lib/mail/fields/content_location_field.rb +10 -25
  37. data/lib/mail/fields/content_transfer_encoding_field.rb +30 -31
  38. data/lib/mail/fields/content_type_field.rb +49 -75
  39. data/lib/mail/fields/date_field.rb +22 -51
  40. data/lib/mail/fields/from_field.rb +27 -49
  41. data/lib/mail/fields/in_reply_to_field.rb +37 -49
  42. data/lib/mail/fields/keywords_field.rb +17 -31
  43. data/lib/mail/fields/message_id_field.rb +24 -71
  44. data/lib/mail/fields/mime_version_field.rb +18 -30
  45. data/lib/mail/fields/named_structured_field.rb +10 -0
  46. data/lib/mail/fields/named_unstructured_field.rb +10 -0
  47. data/lib/mail/fields/optional_field.rb +5 -7
  48. data/lib/mail/fields/{common/parameter_hash.rb → parameter_hash.rb} +12 -10
  49. data/lib/mail/fields/received_field.rb +42 -57
  50. data/lib/mail/fields/references_field.rb +34 -49
  51. data/lib/mail/fields/reply_to_field.rb +27 -49
  52. data/lib/mail/fields/resent_bcc_field.rb +27 -49
  53. data/lib/mail/fields/resent_cc_field.rb +27 -49
  54. data/lib/mail/fields/resent_date_field.rb +4 -29
  55. data/lib/mail/fields/resent_from_field.rb +27 -49
  56. data/lib/mail/fields/resent_message_id_field.rb +4 -29
  57. data/lib/mail/fields/resent_sender_field.rb +26 -56
  58. data/lib/mail/fields/resent_to_field.rb +27 -49
  59. data/lib/mail/fields/return_path_field.rb +49 -54
  60. data/lib/mail/fields/sender_field.rb +33 -55
  61. data/lib/mail/fields/structured_field.rb +2 -30
  62. data/lib/mail/fields/subject_field.rb +8 -11
  63. data/lib/mail/fields/to_field.rb +27 -49
  64. data/lib/mail/fields/unstructured_field.rb +15 -48
  65. data/lib/mail/fields.rb +9 -0
  66. data/lib/mail/header.rb +69 -110
  67. data/lib/mail/mail.rb +32 -27
  68. data/lib/mail/matchers/attachment_matchers.rb +15 -0
  69. data/lib/mail/message.rb +67 -78
  70. data/lib/mail/multibyte/chars.rb +9 -167
  71. data/lib/mail/multibyte/unicode.rb +1 -1
  72. data/lib/mail/multibyte/utils.rb +26 -43
  73. data/lib/mail/multibyte.rb +1 -11
  74. data/lib/mail/network/delivery_methods/exim.rb +5 -4
  75. data/lib/mail/network/delivery_methods/file_delivery.rb +13 -12
  76. data/lib/mail/network/delivery_methods/logger_delivery.rb +2 -5
  77. data/lib/mail/network/delivery_methods/sendmail.rb +27 -35
  78. data/lib/mail/network/delivery_methods/smtp.rb +81 -30
  79. data/lib/mail/network/delivery_methods/smtp_connection.rb +10 -19
  80. data/lib/mail/network/delivery_methods/test_mailer.rb +8 -6
  81. data/lib/mail/network/retriever_methods/base.rb +13 -13
  82. data/lib/mail/network/retriever_methods/imap.rb +8 -5
  83. data/lib/mail/network/retriever_methods/pop3.rb +22 -22
  84. data/lib/mail/network/retriever_methods/test_retriever.rb +2 -1
  85. data/lib/mail/parsers/address_lists_parser.rb +33073 -33064
  86. data/lib/mail/parsers/address_lists_parser.rl +11 -0
  87. data/lib/mail/parsers/content_disposition_parser.rb +837 -828
  88. data/lib/mail/parsers/content_disposition_parser.rl +11 -0
  89. data/lib/mail/parsers/content_location_parser.rb +773 -764
  90. data/lib/mail/parsers/content_location_parser.rl +12 -1
  91. data/lib/mail/parsers/content_transfer_encoding_parser.rb +477 -468
  92. data/lib/mail/parsers/content_transfer_encoding_parser.rl +11 -0
  93. data/lib/mail/parsers/content_type_parser.rb +974 -965
  94. data/lib/mail/parsers/content_type_parser.rl +11 -0
  95. data/lib/mail/parsers/date_time_parser.rb +841 -832
  96. data/lib/mail/parsers/date_time_parser.rl +11 -0
  97. data/lib/mail/parsers/envelope_from_parser.rb +3627 -3530
  98. data/lib/mail/parsers/envelope_from_parser.rl +11 -0
  99. data/lib/mail/parsers/message_ids_parser.rb +5110 -2800
  100. data/lib/mail/parsers/message_ids_parser.rl +16 -1
  101. data/lib/mail/parsers/mime_version_parser.rb +466 -457
  102. data/lib/mail/parsers/mime_version_parser.rl +11 -0
  103. data/lib/mail/parsers/phrase_lists_parser.rb +839 -830
  104. data/lib/mail/parsers/phrase_lists_parser.rl +12 -1
  105. data/lib/mail/parsers/received_parser.rb +8692 -8683
  106. data/lib/mail/parsers/received_parser.rl +11 -0
  107. data/lib/mail/parsers/rfc5322.rl +28 -13
  108. data/lib/mail/parsers.rb +11 -17
  109. data/lib/mail/part.rb +24 -28
  110. data/lib/mail/parts_list.rb +57 -0
  111. data/lib/mail/smtp_envelope.rb +57 -0
  112. data/lib/mail/utilities.rb +321 -69
  113. data/lib/mail/version.rb +1 -1
  114. data/lib/mail/yaml.rb +30 -0
  115. data/lib/mail.rb +2 -22
  116. metadata +83 -19
  117. data/lib/mail/check_delivery_params.rb +0 -60
  118. data/lib/mail/core_extensions/smtp.rb +0 -28
  119. data/lib/mail/core_extensions/string.rb +0 -17
  120. data/lib/mail/fields/common/address_container.rb +0 -17
  121. data/lib/mail/fields/common/common_address.rb +0 -161
  122. data/lib/mail/fields/common/common_date.rb +0 -36
  123. data/lib/mail/fields/common/common_field.rb +0 -52
  124. data/lib/mail/fields/common/common_message_id.rb +0 -49
  125. data/lib/mail/version_specific/ruby_1_8.rb +0 -163
  126. data/lib/mail/version_specific/ruby_1_9.rb +0 -278
@@ -2,6 +2,9 @@
2
2
  require 'mail/utilities'
3
3
  require 'mail/parser_tools'
4
4
 
5
+ begin
6
+ original_verbose, $VERBOSE = $VERBOSE, nil
7
+
5
8
  %%{
6
9
  machine date_time;
7
10
  alphtype int;
@@ -74,6 +77,10 @@ module Mail::Parsers
74
77
  %%write init;
75
78
  %%write exec;
76
79
 
80
+ if false
81
+ testEof
82
+ end
83
+
77
84
  if p != eof || cs < %%{ write first_final; }%%
78
85
  raise Mail::Field::IncompleteParseError.new(Mail::ReceivedElement, data, p)
79
86
  end
@@ -82,3 +89,7 @@ module Mail::Parsers
82
89
  end
83
90
  end
84
91
  end
92
+
93
+ ensure
94
+ $VERBOSE = original_verbose
95
+ end
@@ -30,19 +30,35 @@
30
30
 
31
31
  # 3.6.4. Identification Fields
32
32
  obs_id_left = local_part;
33
- id_left = dot_atom_text | obs_id_left;
34
- # id_right modifications to support multiple '@' in msg_id.
35
- msg_id_atext = ALPHA | DIGIT | "!" | "#" | "$" | "%" | "&" | "'" | "*" |
36
- "+" | "-" | "/" | "=" | "?" | "^" | "_" | "`" | "{" | "|" |
37
- "}" | "~" | "@";
38
- msg_id_dot_atom_text = (msg_id_atext+ "."?)+;
39
33
  obs_id_right = domain;
40
34
  no_fold_literal = "[" (dtext)* "]";
41
- id_right = msg_id_dot_atom_text | no_fold_literal | obs_id_right;
42
- msg_id = (CFWS)?
43
- (("<" id_left "@" id_right ">") >msg_id_s %msg_id_e)
44
- (CFWS)?;
45
- message_ids = msg_id (CFWS? msg_id)*;
35
+
36
+ msg_id_atext = rfc5322_atext | ":" | "," | "." | " ";
37
+
38
+ id_left = msg_id_atext+ | obs_id_left;
39
+ id_left_ns = ( msg_id_atext - ( " " | "," ) )+;
40
+
41
+ # id_right modifications to support multiple '@' in msg_id.
42
+ id_right = ( msg_id_atext | "@" )+ | no_fold_literal | obs_id_right;
43
+ id_right_ns = ( msg_id_atext - ( " " | "," ) | "@" )+ | no_fold_literal;
44
+
45
+ # Handle various message-id formats:
46
+ # <id_left@id_right>
47
+ # <id_left@id_right...
48
+ # <id_left@>
49
+ # <id_left>
50
+ # <id_left...
51
+ # id_left@id_right
52
+ # id_left
53
+ # Handle comma-separated message_ids.
54
+ msg_id = (CFWS)? (
55
+ (("<" id_left "@" id_right? ">") >msg_id_s %msg_id_e) |
56
+ (("<" id_left "@" id_right? :>> "...") >msg_id_s %msg_id_e) |
57
+ (("<" id_left ">") >msg_id_s %msg_id_e) |
58
+ (("<" id_left :>> "..." ) >msg_id_s %msg_id_e) |
59
+ ((id_left_ns ("@" id_right_ns)? ) >msg_id_s %msg_id_e)
60
+ ) (CFWS)? <: ","?;
61
+ message_ids = msg_id**;
46
62
 
47
63
 
48
64
  # 3.6.7 Trace Fields
@@ -54,6 +70,5 @@
54
70
  # Envelope From
55
71
  ctime_date = day_name " "+ month " "+ day " " time_of_day " " year;
56
72
  null_sender = ('<>' ' '{0,1});
57
- envelope_from = (addr_spec_no_angle_brackets | null_sender) >address_s %address_e " "
58
- (ctime_date >ctime_date_s %ctime_date_e);
73
+ envelope_from = (addr_spec_no_angle_brackets | null_sender) >address_s %address_e (" " (ctime_date >ctime_date_s %ctime_date_e))?;
59
74
  }%%
data/lib/mail/parsers.rb CHANGED
@@ -1,19 +1,13 @@
1
1
  # frozen_string_literal: true
2
- # Ragel-generated parsers are full of known warnings. Suppress them.
3
- begin
4
- orig, $VERBOSE = $VERBOSE, nil
5
2
 
6
- require 'mail/parsers/address_lists_parser'
7
- require 'mail/parsers/content_disposition_parser'
8
- require 'mail/parsers/content_location_parser'
9
- require 'mail/parsers/content_transfer_encoding_parser'
10
- require 'mail/parsers/content_type_parser'
11
- require 'mail/parsers/date_time_parser'
12
- require 'mail/parsers/envelope_from_parser'
13
- require 'mail/parsers/message_ids_parser'
14
- require 'mail/parsers/mime_version_parser'
15
- require 'mail/parsers/phrase_lists_parser'
16
- require 'mail/parsers/received_parser'
17
- ensure
18
- $VERBOSE = orig
19
- end
3
+ require 'mail/parsers/address_lists_parser'
4
+ require 'mail/parsers/content_disposition_parser'
5
+ require 'mail/parsers/content_location_parser'
6
+ require 'mail/parsers/content_transfer_encoding_parser'
7
+ require 'mail/parsers/content_type_parser'
8
+ require 'mail/parsers/date_time_parser'
9
+ require 'mail/parsers/envelope_from_parser'
10
+ require 'mail/parsers/message_ids_parser'
11
+ require 'mail/parsers/mime_version_parser'
12
+ require 'mail/parsers/phrase_lists_parser'
13
+ require 'mail/parsers/received_parser'
data/lib/mail/part.rb CHANGED
@@ -1,43 +1,39 @@
1
1
  # encoding: utf-8
2
2
  # frozen_string_literal: true
3
+ require 'mail/constants'
4
+ require 'mail/utilities'
5
+
3
6
  module Mail
4
7
  class Part < Message
5
-
6
8
  # Creates a new empty Content-ID field and inserts it in the correct order
7
9
  # into the Header. The ContentIdField object will automatically generate
8
10
  # a unique content ID if you try and encode it or output it to_s without
9
11
  # specifying a content id.
10
- #
12
+ #
11
13
  # It will preserve the content ID you specify if you do.
12
14
  def add_content_id(content_id_val = '')
13
15
  header['content-id'] = content_id_val
14
16
  end
15
-
17
+
16
18
  # Returns true if the part has a content ID field, the field may or may
17
19
  # not have a value, but the field exists or not.
18
20
  def has_content_id?
19
21
  header.has_content_id?
20
22
  end
21
-
22
- def inline_content_id
23
- # TODO: Deprecated in 2.2.2 - Remove in 2.3
24
- warn("Part#inline_content_id is deprecated, please call Part#cid instead")
25
- cid
26
- end
27
-
23
+
28
24
  def cid
29
25
  add_content_id unless has_content_id?
30
- uri_escape(unbracket(content_id))
26
+ Utilities.uri_escape(Utilities.unbracket(content_id))
31
27
  end
32
-
28
+
33
29
  def url
34
30
  "cid:#{cid}"
35
31
  end
36
-
32
+
37
33
  def inline?
38
34
  header[:content_disposition].disposition_type == 'inline' if header[:content_disposition].respond_to?(:disposition_type)
39
35
  end
40
-
36
+
41
37
  def add_required_fields
42
38
  super
43
39
  add_content_id if !has_content_id? && inline?
@@ -46,15 +42,15 @@ module Mail
46
42
  def add_required_message_fields
47
43
  # Override so we don't add Date, MIME-Version, or Message-ID.
48
44
  end
49
-
45
+
50
46
  def delivery_status_report_part?
51
47
  (main_type =~ /message/i && sub_type =~ /delivery-status/i) && body =~ /Status:/
52
48
  end
53
-
49
+
54
50
  def delivery_status_data
55
51
  delivery_status_report_part? ? parse_delivery_status_report : {}
56
52
  end
57
-
53
+
58
54
  def bounced?
59
55
  if action.is_a?(Array)
60
56
  !!(action.first =~ /failed/i)
@@ -62,18 +58,18 @@ module Mail
62
58
  !!(action =~ /failed/i)
63
59
  end
64
60
  end
65
-
66
-
61
+
62
+
67
63
  # Either returns the action if the message has just a single report, or an
68
64
  # array of all the actions, one for each report
69
65
  def action
70
66
  get_return_values('action')
71
67
  end
72
-
68
+
73
69
  def final_recipient
74
70
  get_return_values('final-recipient')
75
71
  end
76
-
72
+
77
73
  def error_status
78
74
  get_return_values('status')
79
75
  end
@@ -81,17 +77,17 @@ module Mail
81
77
  def diagnostic_code
82
78
  get_return_values('diagnostic-code')
83
79
  end
84
-
80
+
85
81
  def remote_mta
86
82
  get_return_values('remote-mta')
87
83
  end
88
-
84
+
89
85
  def retryable?
90
86
  !(error_status =~ /^5/)
91
87
  end
92
88
 
93
89
  private
94
-
90
+
95
91
  def get_return_values(key)
96
92
  if delivery_status_data[key].is_a?(Array)
97
93
  delivery_status_data[key].map { |a| a.value }
@@ -101,10 +97,10 @@ module Mail
101
97
  nil
102
98
  end
103
99
  end
104
-
100
+
105
101
  # A part may not have a header.... so, just init a body if no header
106
102
  def parse_message
107
- header_part, body_part = raw_source.split(/#{Constants::CRLF}#{Constants::WSP}*#{Constants::CRLF}/m, 2)
103
+ header_part, body_part = raw_source.split(/#{Constants::LAX_CRLF}#{Constants::WSP}*#{Constants::LAX_CRLF}/m, 2)
108
104
  if header_part =~ Constants::HEADER_LINE
109
105
  self.header = header_part
110
106
  self.body = body_part
@@ -113,11 +109,11 @@ module Mail
113
109
  self.body = raw_source
114
110
  end
115
111
  end
116
-
112
+
117
113
  def parse_delivery_status_report
118
114
  @delivery_status_data ||= Header.new(body.to_s.gsub("\r\n\r\n", "\r\n"))
119
115
  end
120
116
 
121
117
  end
122
-
118
+
123
119
  end
@@ -44,6 +44,63 @@ module Mail
44
44
  raise NoMethodError, "#collect! is not defined, please call #collect and create a new PartsList"
45
45
  end
46
46
 
47
+ def inspect_structure(parent_id = '')
48
+ enum_for(:map).with_index { |part, i|
49
+ i = i + 1 # Use 1-based indexes since this is for humans to read
50
+ id = parent_id.empty? ? "#{i}" : "#{parent_id}.#{i}"
51
+ if part.content_type == "message/rfc822"
52
+ sub_list = Mail.new(part.body).parts
53
+ else
54
+ sub_list = part.parts
55
+ end
56
+ id + '. ' + part.inspect +
57
+ if sub_list.any?
58
+ "\n" + sub_list.inspect_structure(id)
59
+ end.to_s
60
+ }.join("\n")
61
+ end
62
+
63
+ def recursive_each(&block)
64
+ each do |part|
65
+ if part.content_type == "message/rfc822"
66
+ sub_list = Mail.new(part.body).parts
67
+ else
68
+ sub_list = part.parts
69
+ end
70
+
71
+ yield part
72
+
73
+ sub_list.recursive_each(&block)
74
+ end
75
+ end
76
+
77
+ def recursive_size
78
+ i = 0
79
+ recursive_each {|p| i += 1 }
80
+ i
81
+ end
82
+
83
+ def recursive_delete_if
84
+ delete_if { |part|
85
+ if part.content_type == "message/rfc822"
86
+ sub_list = Mail.new(part.body).parts
87
+ else
88
+ sub_list = part.parts
89
+ end
90
+ (yield part).tap {
91
+ if sub_list.any?
92
+ sub_list.recursive_delete_if {|part| yield part }
93
+ end
94
+ }
95
+ }
96
+ end
97
+
98
+ def delete_attachments
99
+ recursive_delete_if { |part|
100
+ part.attachment?
101
+ }
102
+ end
103
+
47
104
  def sort
48
105
  self.class.new(@parts.sort)
49
106
  end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mail
4
+ class SmtpEnvelope #:nodoc:
5
+ # Reasonable cap on address length to avoid SMTP line length
6
+ # overflow on old SMTP servers.
7
+ MAX_ADDRESS_BYTESIZE = 2000
8
+
9
+ attr_reader :from, :to, :message
10
+
11
+ def initialize(mail)
12
+ self.from = mail.smtp_envelope_from
13
+ self.to = mail.smtp_envelope_to
14
+ self.message = mail.encoded
15
+ end
16
+
17
+ def from=(addr)
18
+ if Utilities.blank? addr
19
+ raise ArgumentError, "SMTP From address may not be blank: #{addr.inspect}"
20
+ end
21
+
22
+ @from = validate_addr 'From', addr
23
+ end
24
+
25
+ def to=(addr)
26
+ if Utilities.blank?(addr)
27
+ raise ArgumentError, "SMTP To address may not be blank: #{addr.inspect}"
28
+ end
29
+
30
+ @to = Array(addr).map do |addr|
31
+ validate_addr 'To', addr
32
+ end
33
+ end
34
+
35
+ def message=(message)
36
+ if Utilities.blank?(message)
37
+ raise ArgumentError, 'SMTP message may not be blank'
38
+ end
39
+
40
+ @message = message
41
+ end
42
+
43
+
44
+ private
45
+ def validate_addr(addr_name, addr)
46
+ if addr.bytesize > MAX_ADDRESS_BYTESIZE
47
+ raise ArgumentError, "SMTP #{addr_name} address may not exceed #{MAX_ADDRESS_BYTESIZE} bytes: #{addr.inspect}"
48
+ end
49
+
50
+ if /[\r\n]/.match?(addr)
51
+ raise ArgumentError, "SMTP #{addr_name} address may not contain CR or LF line breaks: #{addr.inspect}"
52
+ end
53
+
54
+ addr
55
+ end
56
+ end
57
+ end