Class: Mail::Body

Inherits:
Object
  • Object
show all
Defined in:
lib/mail/body.rb

Overview

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.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(string = '') ⇒ Body

Returns a new instance of Body.


30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 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

Instance Attribute Details

#boundaryObject

Returns and sets the boundary used by the body Allows you to change the boundary of this Body object


226
227
228
# File 'lib/mail/body.rb', line 226

def boundary
  @boundary
end

#charsetObject

Returns and sets the original character encoding


216
217
218
# File 'lib/mail/body.rb', line 216

def charset
  @charset
end

#epilogueObject

Returns and sets the epilogue as a string (any text that is after the last MIME boundary)


222
223
224
# File 'lib/mail/body.rb', line 222

def epilogue
  @epilogue
end

#partsObject (readonly)

Returns parts of the body


213
214
215
# File 'lib/mail/body.rb', line 213

def parts
  @parts
end

#preambleObject

Returns and sets the preamble as a string (any text that is before the first MIME boundary)


219
220
221
# File 'lib/mail/body.rb', line 219

def preamble
  @preamble
end

#raw_sourceObject (readonly)

Returns the raw source that the body was initialized with, without any tampering


210
211
212
# File 'lib/mail/body.rb', line 210

def raw_source
  @raw_source
end

Instance Method Details

#<<(val) ⇒ Object


233
234
235
236
237
238
239
# File 'lib/mail/body.rb', line 233

def <<( val )
  if @parts
    @parts << val
  else
    @parts = Mail::PartsList.new[val]
  end
end

#==(other) ⇒ Object

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

72
73
74
75
76
77
78
# File 'lib/mail/body.rb', line 72

def ==(other)
  if other.class == String
    self.decoded == other
  else
    super
  end
end

#=~(regexp) ⇒ Object

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

90
91
92
# File 'lib/mail/body.rb', line 90

def =~(regexp)
  self.decoded =~ regexp
end

#ascii_only?Boolean

Returns:

  • (Boolean)

253
254
255
256
257
258
# File 'lib/mail/body.rb', line 253

def ascii_only?
  unless defined? @ascii_only
    @ascii_only = raw_source.ascii_only?
  end
  @ascii_only
end

#decodedObject


179
180
181
182
183
184
185
# 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

#default_encodingObject


264
265
266
# File 'lib/mail/body.rb', line 264

def default_encoding
  ascii_only? ? '7bit' : '8bit'
end

#empty?Boolean

Returns:

  • (Boolean)

260
261
262
# File 'lib/mail/body.rb', line 260

def empty?
  !!raw_source.to_s.empty?
end

#encoded(transfer_encoding = nil) ⇒ Object

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


149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# 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

#encoding(val = nil) ⇒ Object


191
192
193
194
195
196
197
# File 'lib/mail/body.rb', line 191

def encoding(val = nil)
  if val
    self.encoding = val
  else
    @encoding
  end
end

#encoding=(val) ⇒ Object


199
200
201
202
203
204
205
206
# File 'lib/mail/body.rb', line 199

def encoding=( val )
  @encoding =
    if val == "text" || Utilities.blank?(val)
      default_encoding
    else
      val
    end
end

#include?(other) ⇒ Boolean

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

Returns:

  • (Boolean)

118
119
120
# File 'lib/mail/body.rb', line 118

def include?(other)
  self.decoded.include?(other.to_s)
end

#init_with(coder) ⇒ Object


53
54
55
56
# 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

#match(regexp) ⇒ Object

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">

104
105
106
# File 'lib/mail/body.rb', line 104

def match(regexp)
  self.decoded.match(regexp)
end

#multipart?Boolean

Returns true if there are parts defined in the body

Returns:

  • (Boolean)

229
230
231
# File 'lib/mail/body.rb', line 229

def multipart?
  true unless parts.empty?
end

#negotiate_best_encoding(message_encoding, allowed_encodings = nil) ⇒ Object


141
142
143
# 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

#set_sort_order(order) ⇒ Object

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.


125
126
127
# File 'lib/mail/body.rb', line 125

def set_sort_order(order)
  @part_sort_order = order
end

#sort_parts!Object

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


133
134
135
136
137
138
139
# 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

#split!(boundary) ⇒ Object


241
242
243
244
245
246
247
248
249
250
251
# 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

#to_sObject


187
188
189
# File 'lib/mail/body.rb', line 187

def to_s
  decoded
end