Class: Mail::Body

Inherits:
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 it’s 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 Method Summary collapse

Constructor Details

#initialize(string = '') ⇒ Body

Returns a new instance of Body.



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/mail/body.rb', line 29

def initialize(string = '')
  @boundary = nil
  @preamble = nil
  @epilogue = nil
  @charset  = nil
  @part_sort_order = [ "text/plain", "text/enriched", "text/html" ]
  @parts = Mail::PartsList.new
  if string.blank?
    @raw_source = ''
  else
    # Do join first incase we have been given an Array in Ruby 1.9
    if string.respond_to?(:join)
      @raw_source = string.join('')
    elsif string.respond_to?(:to_s)
      @raw_source = 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 = (only_us_ascii? ? '7bit' : '8bit')
  set_charset
end

Instance Method Details

#<<(val) ⇒ Object



250
251
252
253
254
255
256
# File 'lib/mail/body.rb', line 250

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


66
67
68
69
70
71
72
# File 'lib/mail/body.rb', line 66

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


84
85
86
# File 'lib/mail/body.rb', line 84

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

#boundaryObject

Returns the boundary used by the body



237
238
239
# File 'lib/mail/body.rb', line 237

def boundary
  @boundary
end

#boundary=(val) ⇒ Object

Allows you to change the boundary of this Body object



242
243
244
# File 'lib/mail/body.rb', line 242

def boundary=( val )
  @boundary = val
end

#charsetObject



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

def charset
  @charset
end

#charset=(val) ⇒ Object



191
192
193
# File 'lib/mail/body.rb', line 191

def charset=( val )
  @charset = val
end

#decodedObject



175
176
177
178
179
180
181
# File 'lib/mail/body.rb', line 175

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

#empty?Boolean

Returns:

  • (Boolean)


273
274
275
# File 'lib/mail/body.rb', line 273

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

#encoded(transfer_encoding = '8bit') ⇒ 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



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/mail/body.rb', line 150

def encoded(transfer_encoding = '8bit')
  if multipart?
    self.sort_parts!
    encoded_parts = parts.map { |p| p.encoded }
    ([preamble] + encoded_parts).join(crlf_boundary) + end_boundary + epilogue.to_s
  else
    be = get_best_encoding(transfer_encoding)
    dec = Mail::Encodings::get_encoding(encoding)
    enc = Mail::Encodings::get_encoding(be)
    if transfer_encoding == encoding and 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.encode!(charset)
          decoded.force_encoding('BINARY') unless Encoding.find(charset).ascii_compatible?
        end
        enc.encode(decoded)
    end
  end
end

#encoding(val = nil) ⇒ Object



195
196
197
198
199
200
201
# File 'lib/mail/body.rb', line 195

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

#encoding=(val) ⇒ Object



203
204
205
206
207
208
209
# File 'lib/mail/body.rb', line 203

def encoding=( val )
  @encoding = if val == "text" || val.blank?
      (only_us_ascii? ? '7bit' : '8bit')
  else
      val
  end
end

#epilogueObject

Returns the epilogue (any text that is after the last MIME boundary)



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

def epilogue
  @epilogue
end

#epilogue=(val) ⇒ Object

Sets the epilogue to a string (adds text after the last MIME boundary)



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

def epilogue=( val )
  @epilogue = val
end

#get_best_encoding(target) ⇒ Object



141
142
143
144
# File 'lib/mail/body.rb', line 141

def get_best_encoding(target)
  target_encoding = Mail::Encodings.get_encoding(target)
  target_encoding.get_best_compatible(encoding, raw_source)
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)


112
113
114
# File 'lib/mail/body.rb', line 112

def include?(other)
  self.decoded.include?(other.to_s)
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">


98
99
100
# File 'lib/mail/body.rb', line 98

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

#multipart?Boolean

Returns true if there are parts defined in the body

Returns:

  • (Boolean)


232
233
234
# File 'lib/mail/body.rb', line 232

def multipart?
  true unless parts.empty?
end

#only_us_ascii?Boolean

Returns:

  • (Boolean)


269
270
271
# File 'lib/mail/body.rb', line 269

def only_us_ascii?
  !(raw_source =~ /[^\x01-\x7f]/)
end

#partsObject



246
247
248
# File 'lib/mail/body.rb', line 246

def parts
  @parts
end

#preambleObject

Returns the preamble (any text that is before the first MIME boundary)



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

def preamble
  @preamble
end

#preamble=(val) ⇒ Object

Sets the preamble to a string (adds text before the first MIME boundary)



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

def preamble=( val )
  @preamble = val
end

#raw_sourceObject

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



137
138
139
# File 'lib/mail/body.rb', line 137

def raw_source
  @raw_source
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’ with any other content type coming after.



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

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



127
128
129
130
131
132
133
# File 'lib/mail/body.rb', line 127

def sort_parts!
  @parts.each do |p|
    p.body.set_sort_order(@part_sort_order)
    @parts.sort!(@part_sort_order)
    p.body.sort_parts!
  end
end

#split!(boundary) ⇒ Object



258
259
260
261
262
263
264
265
266
267
# File 'lib/mail/body.rb', line 258

def split!(boundary)
  self.boundary = boundary
  parts = raw_source.split(/--#{Regexp.escape(boundary)}(?=(?:--)?\s*$)/)
  # 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.sub('--', '').strip
  parts[1...-1].to_a.each { |part| @parts << Mail::Part.new(part) }
  self
end

#to_sObject



183
184
185
# File 'lib/mail/body.rb', line 183

def to_s
  decoded
end