Class: MailFactory

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

Overview

An easy class for creating a mail message

Constant Summary collapse

VERSION =
'1.4.0'

Instance Method Summary collapse

Constructor Details

#initializeMailFactory

Returns a new instance of MailFactory.



53
54
55
56
57
58
59
60
61
# 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

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(methId, *args) ⇒ Object

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)



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# 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

Instance Method Details

#add_attachment(filename, type = nil, attachmentheaders = nil) ⇒ Object Also known as: attach

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.



244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# 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

#add_attachment_as(file, emailfilename, type = nil, attachmentheaders = nil) ⇒ Object Also known as: attach_as

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.



275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
# 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

#add_header(header, value) ⇒ Object

adds a header to the bottom of the headers



65
66
67
68
69
# 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 %w[from to cc bcc reply-to].include?(header.downcase)
  @headers << "#{header}: #{value}"
end

#construct(options = Hash.new) ⇒ Object

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)



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# 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_boundaryObject

generates a unique boundary string



219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# 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) ⇒ Object

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



149
150
151
152
153
154
155
156
157
158
159
# File 'lib/mailfactory.rb', line 149

def get_header(header)
  headers = Array.new()
  headerregex = /^#{Regexp.escape(header)}:/i
  @headers.each() { |h|
    if(headerregex.match(h))
      headers << h[/^[^:]+:(.*)/i, 1].strip()
    end
  }
  
  return(headers)
end

#html=(newhtml) ⇒ Object

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



108
109
110
# 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

#multipart?Boolean

returns true if the email is multipart

Returns:

  • (Boolean)


163
164
165
166
167
168
169
# File 'lib/mailfactory.rb', line 163

def multipart?()
  if(@attachments.length > 0 or @html != nil)
    return(true)
  else
    return(false)
  end
end

#rawhtml=(newhtml) ⇒ Object

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



114
115
116
# File 'lib/mailfactory.rb', line 114

def rawhtml=(newhtml)
  @html = newhtml
end

#remove_header(header) ⇒ Object

removes the named header - case insensitive



73
74
75
76
77
78
79
# File 'lib/mailfactory.rb', line 73

def remove_header(header)
  @headers.each_index() { |i|
    if(@headers[i] =~ /^#{Regexp.escape(header)}:/i)
      @headers.delete_at(i)
    end
  }
end

#replytoObject



95
96
97
# File 'lib/mailfactory.rb', line 95

def replyto()
  return(get_header("Reply-To")[0])
end

#replyto=(newreplyto) ⇒ Object



89
90
91
92
# File 'lib/mailfactory.rb', line 89

def replyto=(newreplyto)
  remove_header("Reply-To")
  add_header("Reply-To", newreplyto)
end

#set_header(header, value) ⇒ Object

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



83
84
85
86
# File 'lib/mailfactory.rb', line 83

def set_header(header, value)
  remove_header(header)
  add_header(header, value)
end

#text=(newtext) ⇒ Object

sets the plain text body of the message



101
102
103
# File 'lib/mailfactory.rb', line 101

def text=(newtext)
  @text = newtext
end

#to_sObject

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



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

def to_s()
  return(construct(:messageid => true))
end