class Mail::Body
Body
¶ ↑
The body is where the text of the email is stored. Mail
treats the body as a single object. The body itself has no information about boundaries used in the MIME standard, it just looks at its content as either a single block of text, or (if it is a multipart message) as an array of blocks of text.
A body has to be told to split itself up into a multipart message by calling split with the correct boundary. This is because the body object has no way of knowing what the correct boundary is for itself (there could be many boundaries in a body in the case of a nested MIME text).
Once split is called, Mail::Body
will slice itself up on this boundary, assigning anything that appears before the first part to the preamble, and anything that appears after the closing boundary to the epilogue, then each part gets initialized into a Mail::Part
object.
The boundary that is used to split up the Body
is also stored in the Body
object for use on encoding itself back out to a string. You can overwrite this if it needs to be changed.
On encoding, the body will return the preamble, then each part joined by the boundary, followed by a closing boundary string and then the epilogue.
Attributes
Returns and sets the boundary used by the body Allows you to change the boundary of this Body
object
Returns and sets the original character encoding
Returns and sets the epilogue as a string (any text that is after the last MIME boundary)
Returns parts of the body
Returns and sets the preamble as a string (any text that is before the first MIME boundary)
Returns the raw source that the body was initialized with, without any tampering
Public Class Methods
# File lib/mail/body.rb, line 30 def initialize(string = '') @boundary = nil @preamble = nil @epilogue = nil @charset = nil @part_sort_order = [ "text/plain", "text/enriched", "text/html", "multipart/alternative" ] @parts = Mail::PartsList.new if Utilities.blank?(string) @raw_source = '' else # Do join first incase we have been given an Array in Ruby 1.9 if string.respond_to?(:join) @raw_source = ::Mail::Utilities.to_crlf(string.join('')) elsif string.respond_to?(:to_s) @raw_source = ::Mail::Utilities.to_crlf(string.to_s) else raise "You can only assign a string or an object that responds_to? :join or :to_s to a body." end end @encoding = default_encoding set_charset end
Public Instance Methods
# File lib/mail/body.rb, line 233 def <<( val ) if @parts @parts << val else @parts = Mail::PartsList.new[val] end end
Matches this body with another body. Also matches the decoded value of this body with a string.
Examples:
body = Mail::Body.new('The body') body == body #=> true body = Mail::Body.new('The body') body == 'The body' #=> true body = Mail::Body.new("VGhlIGJvZHk=\n") body.encoding = 'base64' body == "The body" #=> true
# File lib/mail/body.rb, line 72 def ==(other) if other.class == String self.decoded == other else super end end
Accepts a string and performs a regular expression against the decoded text
Examples:
body = Mail::Body.new('The body') body =~ /The/ #=> 0 body = Mail::Body.new("VGhlIGJvZHk=\n") body.encoding = 'base64' body =~ /The/ #=> 0
# File lib/mail/body.rb, line 90 def =~(regexp) self.decoded =~ regexp end
# File lib/mail/body.rb, line 253 def ascii_only? unless defined? @ascii_only @ascii_only = raw_source.ascii_only? end @ascii_only end
# File lib/mail/body.rb, line 179 def decoded if !Encodings.defined?(encoding) raise UnknownEncodingType, "Don't know how to decode #{encoding}, please call #encoded and decode it yourself." else Encodings.get_encoding(encoding).decode(raw_source) end end
# File lib/mail/body.rb, line 264 def default_encoding ascii_only? ? '7bit' : '8bit' end
# File lib/mail/body.rb, line 260 def empty? !!raw_source.to_s.empty? end
Returns a body encoded using transfer_encoding. Multipart always uses an identiy encoding (i.e. no encoding). Calling this directly is not a good idea, but supported for compatibility TODO: Validate that preamble and epilogue are valid for requested encoding
# File lib/mail/body.rb, line 149 def encoded(transfer_encoding = nil) if multipart? self.sort_parts! encoded_parts = parts.map { |p| p.encoded } ([preamble] + encoded_parts).join(crlf_boundary) + end_boundary + epilogue.to_s else dec = Mail::Encodings.get_encoding(encoding) enc = if Utilities.blank?(transfer_encoding) dec else negotiate_best_encoding(transfer_encoding) end if dec.nil? # Cannot decode, so skip normalization raw_source else # Decode then encode to normalize and allow transforming # from base64 to Q-P and vice versa decoded = dec.decode(raw_source) if defined?(Encoding) && charset && charset != "US-ASCII" decoded = decoded.encode(charset) decoded.force_encoding('BINARY') unless Encoding.find(charset).ascii_compatible? end enc.encode(decoded) end end end
# File lib/mail/body.rb, line 191 def encoding(val = nil) if val self.encoding = val else @encoding end end
# File lib/mail/body.rb, line 199 def encoding=( val ) @encoding = if val == "text" || Utilities.blank?(val) default_encoding else val end end
Accepts anything that responds to to_s
and checks if it’s a substring of the decoded text
Examples:
body = Mail::Body.new('The body') body.include?('The') #=> true body = Mail::Body.new("VGhlIGJvZHk=\n") body.encoding = 'base64' body.include?('The') #=> true
# File lib/mail/body.rb, line 118 def include?(other) self.decoded.include?(other.to_s) end
# File lib/mail/body.rb, line 53 def init_with(coder) coder.map.each { |k, v| instance_variable_set(:"@#{k}", v) } @parts = Mail::PartsList.new(coder['parts']) end
Accepts a string and performs a regular expression against the decoded text
Examples:
body = Mail::Body.new('The body') body.match(/The/) #=> #<MatchData "The"> body = Mail::Body.new("VGhlIGJvZHk=\n") body.encoding = 'base64' body.match(/The/) #=> #<MatchData "The">
# File lib/mail/body.rb, line 104 def match(regexp) self.decoded.match(regexp) end
Returns true if there are parts defined in the body
# File lib/mail/body.rb, line 229 def multipart? true unless parts.empty? end
# File lib/mail/body.rb, line 141 def negotiate_best_encoding(message_encoding, allowed_encodings = nil) Mail::Encodings::TransferEncoding.negotiate(message_encoding, encoding, raw_source, allowed_encodings) end
Allows you to set the sort order of the parts, overriding the default sort order. Defaults to ‘text/plain’, then ‘text/enriched’, then ‘text/html’, then ‘multipart/alternative’ with any other content type coming after.
# File lib/mail/body.rb, line 125 def set_sort_order(order) @part_sort_order = order end
Allows you to sort the parts according to the default sort order, or the sort order you set with :set_sort_order.
sort_parts! is also called from :encode, so there is no need for you to call this explicitly
# File lib/mail/body.rb, line 133 def sort_parts! @parts.each do |p| p.body.set_sort_order(@part_sort_order) p.body.sort_parts! end @parts.sort!(@part_sort_order) end
# File lib/mail/body.rb, line 241 def split!(boundary) self.boundary = boundary parts = extract_parts # Make the preamble equal to the preamble (if any) self.preamble = parts[0].to_s.strip # Make the epilogue equal to the epilogue (if any) self.epilogue = parts[-1].to_s.strip parts[1...-1].to_a.each { |part| @parts << Mail::Part.new(part) } self end
# File lib/mail/body.rb, line 187 def to_s decoded end
Private Instance Methods
# File lib/mail/body.rb, line 293 def crlf_boundary "\r\n--#{boundary}\r\n" end
# File lib/mail/body.rb, line 297 def end_boundary "\r\n--#{boundary}--\r\n" end
split parts by boundary, ignore first part if empty, append final part when closing boundary was missing
# File lib/mail/body.rb, line 271 def extract_parts parts_regex = / (?: # non-capturing group \A | # start of string OR \r\n # line break ) ( --#{Regexp.escape(boundary || "")} # boundary delimiter (?:--)? # with non-capturing optional closing ) (?=\s*$) # lookahead matching zero or more spaces followed by line-ending /x parts = raw_source.split(parts_regex).each_slice(2).to_a parts.each_with_index { |(part, _), index| parts.delete_at(index) if index > 0 && Utilities.blank?(part) } if parts.size > 1 final_separator = parts[-2][1] parts << [""] if final_separator != "--#{boundary}--" end parts.map(&:first) end
# File lib/mail/body.rb, line 301 def set_charset @charset = ascii_only? ? 'US-ASCII' : nil end