Parent

Class/Module Index [+]

Quicksearch

MailFactory

An easy class for creating a mail message

Constants

VERSION

Public Class Methods

new() click to toggle source
# File lib/mailfactory.rb, line 53
def initialize()
  @headers = Array.new()
  @attachments = Array.new()
  @attachmentboundary = generate_boundary()
  @bodyboundary = generate_boundary()
  @html = nil
  @text = nil
  @charset = 'utf-8'
end

Public Instance Methods

add_attachment(filename, type=nil, attachmentheaders = nil) click to toggle source

adds an attachment to the mail. Type may be given as a mime type. If it is left off and the MIME::Types module is available it will be determined automagically. If the optional attachemntheaders is given, then they will be added to the attachment boundary in the email, which can be used to produce Content-ID markers. attachmentheaders can be given as an Array or a String.

# File lib/mailfactory.rb, line 244
def add_attachment(filename, type=nil, attachmentheaders = nil)
  attachment = Hash.new()
  attachment['filename'] = Pathname.new(filename).basename
  if(type == nil)
    attachment['mimetype'] = MIME::Types.type_for(filename).to_s
  else
    attachment['mimetype'] = type
  end 
  
  # Open in rb mode to handle Windows, which mangles binary files opened in a text mode
  File.open(filename, "rb") { |fp|
    attachment['attachment'] = file_encode(fp.read())
  }

  if(attachmentheaders != nil)
    if(!attachmentheaders.kind_of?(Array))
      attachmentheaders = attachmentheaders.split(/\r?\n/)
    end
    attachment['headers'] = attachmentheaders
  end

  @attachments << attachment
end
Also aliased as: attach
add_attachment_as(file, emailfilename, type=nil, attachmentheaders = nil) click to toggle source

adds an attachment to the mail as emailfilename. Type may be given as a mime type. If it is left off and the MIME::Types module is available it will be determined automagically. file may be given as an IO stream (which will be read until the end) or as a filename. If the optional attachemntheaders is given, then they will be added to the attachment boundary in the email, which can be used to produce Content-ID markers. attachmentheaders can be given as an Array of a String.

# File lib/mailfactory.rb, line 275
def add_attachment_as(file, emailfilename, type=nil, attachmentheaders = nil)
  attachment = Hash.new()
  attachment['filename'] = emailfilename

  if(type != nil)
    attachment['mimetype'] = type.to_s()
  elsif(file.kind_of?(String) or file.kind_of?(Pathname))
    attachment['mimetype'] = MIME::Types.type_for(file.to_s()).to_s
  else
    attachment['mimetype'] = ''
  end
  
  if(file.kind_of?(String) or file.kind_of?(Pathname))    
    # Open in rb mode to handle Windows, which mangles binary files opened in a text mode
    File.open(file.to_s(), "rb") { |fp|
      attachment['attachment'] = file_encode(fp.read())
    }
  elsif(file.respond_to?(:read))
    attachment['attachment'] = file_encode(file.read())
  else
    raise(Exception, "file is not a supported type (must be a String, Pathnamem, or support read method)")
  end
  
  if(attachmentheaders != nil)
    if(!attachmentheaders.kind_of?(Array))
      attachmentheaders = attachmentheaders.split(/\r?\n/)
    end
    attachment['headers'] = attachmentheaders
  end
  
  @attachments << attachment
end
Also aliased as: attach_as
add_header(header, value) click to toggle source

adds a header to the bottom of the headers

# File lib/mailfactory.rb, line 65
def add_header(header, value)
  value = quoted_printable_with_instruction(value, @charset) if header == 'subject'
  value = quote_address_if_necessary(value, @charset) if ]from to cc bcc reply-to].include?(header.downcase)
  @headers << "#{header}: #{value}"
end
attach(filename, type=nil, attachmentheaders = nil) click to toggle source
Alias for: add_attachment
attach_as(file, emailfilename, type=nil, attachmentheaders = nil) click to toggle source
Alias for: add_attachment_as
construct(options = Hash.new) click to toggle source

builds an email and returns it as a string. Takes the following options:

:messageid

Adds a message id to the message based on the from header (defaults to false)

:date

Adds a date to the message if one is not present (defaults to true)

# File lib/mailfactory.rb, line 175
def construct(options = Hash.new)
  if(options[:date] == nil)
    options[:date] = true
  end
  
  if(options[:messageid])
    # add a unique message-id
    remove_header("Message-ID")
    sendingdomain = get_header('from')[0].to_s()[/@([-a-zA-Z0-9._]+)/,1].to_s()
    add_header("Message-ID", "<#{Time.now.to_f()}.#{Process.euid()}.#{String.new.object_id()}@#{sendingdomain}>")
  end

  if(options[:date])
    if(get_header("Date").length == 0)
      add_header("Date", Time.now.strftime("%a, %d %b %Y %H:%M:%S %z"))
    end
  end

  # Add a mime header if we don't already have one and we have multiple parts
  if(multipart?())
    if(get_header("MIME-Version").length == 0)
      add_header("MIME-Version", "1.0")
    end
    
    if(get_header("Content-Type").length == 0)
      if(@attachments.length == 0)
        add_header("Content-Type", "multipart/alternative;boundary=\"#{@bodyboundary}\"")
      else
        add_header("Content-Type", "multipart/mixed; boundary=\"#{@attachmentboundary}\"")
      end
    end
  end
  
  return("#{headers_to_s()}#{body_to_s()}")
end
generate_boundary() click to toggle source

generates a unique boundary string

# File lib/mailfactory.rb, line 219
def generate_boundary()
  randomstring = Array.new()
  1.upto(25) {
    whichglyph = rand(100)
    if(whichglyph < 40)
      randomstring << (rand(25) + 65).chr()
    elsif(whichglyph < 70)
      randomstring << (rand(25) + 97).chr()
    elsif(whichglyph < 90)
      randomstring << (rand(10) + 48).chr()
    elsif(whichglyph < 95)
      randomstring << '.'
    else
      randomstring << '_'
    end
  }
  return("----=_NextPart_#{randomstring.join()}")
end
get_header(header) click to toggle source

returns the value (or values) of the named header in an array

# File lib/mailfactory.rb, line 149
def get_header(header)
  headers = Array.new()
  headerregex = /^#{Regexp.escape(header)}:/
  @headers.each() { |h|
    if(headerregex.match(h))
      headers << h[/^[^:]+:(.*)/, 1].strip()
    end
  }
  
  return(headers)
end
html=(newhtml) click to toggle source

sets the HTML body of the message. Only the body of the html should be provided

# File lib/mailfactory.rb, line 108
def html=(newhtml)
  @html = "<html>\n<head>\n<meta content=\"text/html;charset=#{@charset}\" http-equiv=\"Content-Type\">\n</head>\n<body bgcolor=\"#ffffff\" text=\"#000000\">\n#{newhtml}\n</body>\n</html>"
end
method_missing(methId, *args) click to toggle source

implement method missing to provide helper methods for setting and getting headers. Headers with '-' characters may be set/gotten as 'x_mailer' or 'XMailer' (splitting will occur between capital letters or on '_' chracters)

# File lib/mailfactory.rb, line 122
def method_missing(methId, *args)
  name = methId.id2name()
  
  # mangle the name if we have to
  if(name =~ /_/)
    name = name.gsub(/_/, '-')
  elsif(name =~ /[A-Z]/)
    name = name.gsub(/([a-zA-Z])([A-Z])/, '\1-\2')
  end
  
  # determine if it sets or gets, and do the right thing
  if(name =~ /=$/)
    if(args.length != 1)
      super(methId, args)
    end
    set_header(name[/^(.*)=$/, 1], args[0])     
  else
    if(args.length != 0)
      super(methId, args)
    end
    headers = get_header(name)
    return(get_header(name))
  end
end
multipart?() click to toggle source

returns true if the email is multipart

# File lib/mailfactory.rb, line 163
def multipart?()
  if(@attachments.length > 0 or @html != nil)
    return(true)
  else
    return(false)
  end
end
rawhtml=(newhtml) click to toggle source

sets the HTML body of the message. The entire HTML section should be provided

# File lib/mailfactory.rb, line 114
def rawhtml=(newhtml)
  @html = newhtml
end
remove_header(header) click to toggle source

removes the named header - case insensitive

# File lib/mailfactory.rb, line 73
def remove_header(header)
  @headers.each_index() { |i|
    if(@headers[i] =~ /^#{Regexp.escape(header)}:/)
      @headers.delete_at(i)
    end
  }
end
replyto() click to toggle source
# File lib/mailfactory.rb, line 95
def replyto()
  return(get_header("Reply-To")[0])
end
replyto=(newreplyto) click to toggle source
# File lib/mailfactory.rb, line 89
def replyto=(newreplyto)
  remove_header("Reply-To")
  add_header("Reply-To", newreplyto)
end
set_header(header, value) click to toggle source

sets a header (removing any other versions of that header)

# File lib/mailfactory.rb, line 83
def set_header(header, value)
  remove_header(header)
  add_header(header, value)
end
text=(newtext) click to toggle source

sets the plain text body of the message

# File lib/mailfactory.rb, line 101
def text=(newtext)
  @text = newtext
end
to_s() click to toggle source

returns a formatted email - equivalent to construct(:messageid => true)

# File lib/mailfactory.rb, line 213
def to_s()
  return(construct(:messageid => true))
end

Protected Instance Methods

body_to_s() click to toggle source

returns the body as a properly formatted string

# File lib/mailfactory.rb, line 321
def body_to_s()
  body = Array.new()
  
  # simple message with one part
  if(!multipart?())
    return(@text)
  else
    body << "This is a multi-part message in MIME format.\r\n\r\n--#{@attachmentboundary}\r\nContent-Type: multipart/alternative; boundary=\"#{@bodyboundary}\""
    
    if(@attachments.length > 0)
      # text part
      body << "#{buildbodyboundary("text/plain; charset=#{@charset}; format=flowed", 'quoted-printable')}\r\n\r\n#{quote_if_necessary(@text, @charset)}"
      
      # html part if one is provided
      if @html
        body << "#{buildbodyboundary("text/html; charset=#{@charset}", 'quoted-printable')}\r\n\r\n#{quote_if_necessary(@html, @charset)}"
      end
      
      body << "--#{@bodyboundary}--"
      
      # and, the attachments
      if(@attachments.length > 0)
        @attachments.each() { |attachment|
          body << "#{buildattachmentboundary(attachment)}\r\n\r\n#{attachment['attachment']}"
        }
        body << "\r\n--#{@attachmentboundary}--"
      end
    else
      # text part
      body << "#{buildbodyboundary("text/plain; charset=#{@charset}; format=flowed", 'quoted-printable')}\r\n\r\n#{quote_if_necessary(@text, @charset)}"
      
      # html part
      body << "#{buildbodyboundary("text/html; charset=#{@charset}", 'quoted-printable')}\r\n\r\n#{quote_if_necessary(@html, @charset)}"
      
      body << "--#{@bodyboundary}--"
    end
    
    return(body.join("\r\n\r\n"))
  end
end
buildattachmentboundary(attachment) click to toggle source

builds a boundary string for including attachments in the body, expects an attachment hash as built by add_attachment and add_attachment_as

# File lib/mailfactory.rb, line 365
def buildattachmentboundary(attachment)
  disposition = "Content-Disposition: inline; filename=\"#{attachment['filename']}\""
  boundary = "--#{@attachmentboundary}\r\nContent-Type: #{attachment['mimetype']}; name=\"#{attachment['filename']}\"\r\nContent-Transfer-Encoding: base64\r\n#{disposition}"
  if(attachment['headers'])
    boundary = boundary + "\r\n#{attachment['headers'].join("\r\n")}"
  end
  
  return(boundary)
end
buildbodyboundary(type, encoding) click to toggle source

builds a boundary string for inclusion in the body of a message

# File lib/mailfactory.rb, line 377
def buildbodyboundary(type, encoding)
  return("--#{@bodyboundary}\r\nContent-Type: #{type}\r\nContent-Transfer-Encoding: #{encoding}")
end
file_encode(str) click to toggle source

returns a base64 encoded version of the contents of str

# File lib/mailfactory.rb, line 383
def file_encode(str)
  collection = Array.new()
  enc = [str].pack('m')
  #  while(enc.length > 60)
  #    collection << enc.slice!(0..59)
  #  end
  #  collection << enc
  #  return(collection.join("\n"))
  return(enc)
end
headers_to_s() click to toggle source

returns the @headers as a properly formatted string

# File lib/mailfactory.rb, line 315
def headers_to_s()
  return("#{@headers.join("\r\n")}\r\n\r\n")
end
quote_address_if_necessary(address, charset) click to toggle source

Quote the given address if it needs to be. The address may be a regular email address, or it can be a phrase followed by an address in brackets. The phrase is the only part that will be quoted, and only if it needs to be. This allows extended characters to be used in the "to", "from", "cc", and "bcc" headers.

# File lib/mailfactory.rb, line 453
def quote_address_if_necessary(address, charset)
  if Array === address
    address.map { |a| quote_address_if_necessary(a, charset) }
  elsif address =~ /^(\S.*)\s+(<.*>)$/
    address = $2
    phrase = quote_if_necessary($1.gsub(/^['"](.*)['"]$/, '\1'), charset, true)
    "\"#{phrase}\" #{address}"
  else
    address
  end
end
quote_if_necessary(text, charset, instruction = false) click to toggle source

Quote the given text if it contains any "illegal" characters

# File lib/mailfactory.rb, line 440
def quote_if_necessary(text, charset, instruction = false)
  return unless text
  text = text.dup.force_encoding(Encoding::ASCII_8BIT) if text.respond_to?(:force_encoding)
  #(text =~ CHARS_NEEDING_QUOTING) ? (instruction ? quoted_printable_with_instruction(text, charset) : quoted_printable_encode(text)) : text
  instruction ? quoted_printable_with_instruction(text, charset) : quoted_printable_encode(text)
end
quoted_printable_encode(text) click to toggle source

rfc2045 compatible. use rfc2047 for headers (such as the Subject line) instead

# File lib/mailfactory.rb, line 404
def quoted_printable_encode(text)
  [text].pack('M').gsub(/\n/, "\r\n").chomp.gsub(/=$/, '')
end
quoted_printable_encode_header(text) click to toggle source
# File lib/mailfactory.rb, line 413
def quoted_printable_encode_header(text)
  text.enum_for(:each_byte).map do |ord|
    if ord < 128 and ord != 61 # 61 is ascii '='
      ord.chr
    else
      '=%X' % ord
    end
  end.join('').
      chomp.
      gsub(/=$/,'').
      gsub('?', '=3F').
      gsub('_', '=5F').
      gsub(/ /, '_')
end
quoted_printable_with_instruction(text, charset) click to toggle source

Convert the given text into quoted printable format, with an instruction that the text be eventually interpreted in the given charset.

# File lib/mailfactory.rb, line 398
def quoted_printable_with_instruction(text, charset)
  text = quoted_printable_encode_header(text)
  "=?#{charset}?Q?#{text}?="
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.