Class: MailFactory

### Home Classes Methods

### In Files

- lib/mailfactory.rb

### Parent

Object

### Methods

- ::new
- #add_attachment
- #add_attachment_as
- #add_header
- #attach
- #attach_as
- #body_to_s
- #buildattachmentboundary
- #buildbodyboundary
- #construct
- #file_encode
- #generate_boundary
- #get_header
- #headers_to_s
- #html=
- #method_missing
- #multipart?
- #quote_address_if_necessary
- #quote_if_necessary
- #quoted_printable_encode
- #quoted_printable_encode_header
- #quoted_printable_with_instruction
- #rawhtml=
- #remove_header
- #replyto
- #replyto=
- #set_header
- #text=
- #to_s

### Class/Module Index

Quicksearch

- MIME
- MIME::Types
- MailFactory

No matching classes.

# 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.