Parent

Class 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
53:   def initialize()
54:     @headers = Array.new()
55:     @attachments = Array.new()
56:     @attachmentboundary = generate_boundary()
57:     @bodyboundary = generate_boundary()
58:     @html = nil
59:     @text = nil
60:     @charset = 'utf-8'
61:   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
244:   def add_attachment(filename, type=nil, attachmentheaders = nil)
245:     attachment = Hash.new()
246:     attachment['filename'] = Pathname.new(filename).basename
247:     if(type == nil)
248:       attachment['mimetype'] = MIME::Types.type_for(filename).to_s
249:     else
250:       attachment['mimetype'] = type
251:     end 
252:     
253:     # Open in rb mode to handle Windows, which mangles binary files opened in a text mode
254:     File.open(filename, "rb") { |fp|
255:       attachment['attachment'] = file_encode(fp.read())
256:     }
257: 
258:     if(attachmentheaders != nil)
259:       if(!attachmentheaders.kind_of?(Array))
260:         attachmentheaders = attachmentheaders.split(/\r?\n/)
261:       end
262:       attachment['headers'] = attachmentheaders
263:     end
264: 
265:     @attachments << attachment
266:   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
275:   def add_attachment_as(file, emailfilename, type=nil, attachmentheaders = nil)
276:     attachment = Hash.new()
277:     attachment['filename'] = emailfilename
278: 
279:     if(type != nil)
280:       attachment['mimetype'] = type.to_s()
281:     elsif(file.kind_of?(String) or file.kind_of?(Pathname))
282:       attachment['mimetype'] = MIME::Types.type_for(file.to_s()).to_s
283:     else
284:       attachment['mimetype'] = ''
285:     end
286:     
287:     if(file.kind_of?(String) or file.kind_of?(Pathname))    
288:       # Open in rb mode to handle Windows, which mangles binary files opened in a text mode
289:       File.open(file.to_s(), "rb") { |fp|
290:         attachment['attachment'] = file_encode(fp.read())
291:       }
292:     elsif(file.respond_to?(:read))
293:       attachment['attachment'] = file_encode(file.read())
294:     else
295:       raise(Exception, "file is not a supported type (must be a String, Pathnamem, or support read method)")
296:     end
297:     
298:     if(attachmentheaders != nil)
299:       if(!attachmentheaders.kind_of?(Array))
300:         attachmentheaders = attachmentheaders.split(/\r?\n/)
301:       end
302:       attachment['headers'] = attachmentheaders
303:     end
304:     
305:     @attachments << attachment
306:   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
65:   def add_header(header, value)
66:     value = quoted_printable_with_instruction(value, @charset) if header == 'subject'
67:     value = quote_address_if_necessary(value, @charset) if ]from to cc bcc reply-to].include?(header.downcase)
68:     @headers << "#{header}: #{value}"
69:   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
175:   def construct(options = Hash.new)
176:     if(options[:date] == nil)
177:       options[:date] = true
178:     end
179:     
180:     if(options[:messageid])
181:       # add a unique message-id
182:       remove_header("Message-ID")
183:       sendingdomain = get_header('from')[0].to_s()[/@([-a-zA-Z0-9._]+)/,1].to_s()
184:       add_header("Message-ID", "<#{Time.now.to_f()}.#{Process.euid()}.#{String.new.object_id()}@#{sendingdomain}>")
185:     end
186: 
187:     if(options[:date])
188:       if(get_header("Date").length == 0)
189:         add_header("Date", Time.now.strftime("%a, %d %b %Y %H:%M:%S %z"))
190:       end
191:     end
192: 
193:     # Add a mime header if we don't already have one and we have multiple parts
194:     if(multipart?())
195:       if(get_header("MIME-Version").length == 0)
196:         add_header("MIME-Version", "1.0")
197:       end
198:       
199:       if(get_header("Content-Type").length == 0)
200:         if(@attachments.length == 0)
201:           add_header("Content-Type", "multipart/alternative;boundary=\"#{@bodyboundary}\"")
202:         else
203:           add_header("Content-Type", "multipart/mixed; boundary=\"#{@attachmentboundary}\"")
204:         end
205:       end
206:     end
207:     
208:     return("#{headers_to_s()}#{body_to_s()}")
209:   end
generate_boundary() click to toggle source

generates a unique boundary string

     # File lib/mailfactory.rb, line 219
219:   def generate_boundary()
220:     randomstring = Array.new()
221:     1.upto(25) {
222:       whichglyph = rand(100)
223:       if(whichglyph < 40)
224:         randomstring << (rand(25) + 65).chr()
225:       elsif(whichglyph < 70)
226:         randomstring << (rand(25) + 97).chr()
227:       elsif(whichglyph < 90)
228:         randomstring << (rand(10) + 48).chr()
229:       elsif(whichglyph < 95)
230:         randomstring << '.'
231:       else
232:         randomstring << '_'
233:       end
234:     }
235:     return("----=_NextPart_#{randomstring.join()}")
236:   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
149:   def get_header(header)
150:     headers = Array.new()
151:     headerregex = /^#{Regexp.escape(header)}:/
152:     @headers.each() { |h|
153:       if(headerregex.match(h))
154:         headers << h[/^[^:]+:(.*)/, 1].strip()
155:       end
156:     }
157:     
158:     return(headers)
159:   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
108:   def html=(newhtml)
109:     @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>"
110:   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
122:   def method_missing(methId, *args)
123:     name = methId.id2name()
124:     
125:     # mangle the name if we have to
126:     if(name =~ /_/)
127:       name = name.gsub(/_/, '-')
128:     elsif(name =~ /[A-Z]/)
129:       name = name.gsub(/([a-zA-Z])([A-Z])/, '\1-\2')
130:     end
131:     
132:     # determine if it sets or gets, and do the right thing
133:     if(name =~ /=$/)
134:       if(args.length != 1)
135:         super(methId, args)
136:       end
137:       set_header(name[/^(.*)=$/, 1], args[0])     
138:     else
139:       if(args.length != 0)
140:         super(methId, args)
141:       end
142:       headers = get_header(name)
143:       return(get_header(name))
144:     end
145:   end
multipart?() click to toggle source

returns true if the email is multipart

     # File lib/mailfactory.rb, line 163
163:   def multipart?()
164:     if(@attachments.length > 0 or @html != nil)
165:       return(true)
166:     else
167:       return(false)
168:     end
169:   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
114:   def rawhtml=(newhtml)
115:     @html = newhtml
116:   end
remove_header(header) click to toggle source

removes the named header - case insensitive

    # File lib/mailfactory.rb, line 73
73:   def remove_header(header)
74:     @headers.each_index() { |i|
75:       if(@headers[i] =~ /^#{Regexp.escape(header)}:/)
76:         @headers.delete_at(i)
77:       end
78:     }
79:   end
replyto() click to toggle source
    # File lib/mailfactory.rb, line 95
95:   def replyto()
96:     return(get_header("Reply-To")[0])
97:   end
replyto=(newreplyto) click to toggle source
    # File lib/mailfactory.rb, line 89
89:   def replyto=(newreplyto)
90:     remove_header("Reply-To")
91:     add_header("Reply-To", newreplyto)
92:   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
83:   def set_header(header, value)
84:     remove_header(header)
85:     add_header(header, value)
86:   end
text=(newtext) click to toggle source

sets the plain text body of the message

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

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

     # File lib/mailfactory.rb, line 213
213:   def to_s()
214:     return(construct(:messageid => true))
215:   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
321:   def body_to_s()
322:     body = Array.new()
323:     
324:     # simple message with one part
325:     if(!multipart?())
326:       return(@text)
327:     else
328:       body << "This is a multi-part message in MIME format.\r\n\r\n--#{@attachmentboundary}\r\nContent-Type: multipart/alternative; boundary=\"#{@bodyboundary}\""
329:       
330:       if(@attachments.length > 0)
331:         # text part
332:         body << "#{buildbodyboundary("text/plain; charset=#{@charset}; format=flowed", 'quoted-printable')}\r\n\r\n#{quote_if_necessary(@text, @charset)}"
333:         
334:         # html part if one is provided
335:         if @html
336:           body << "#{buildbodyboundary("text/html; charset=#{@charset}", 'quoted-printable')}\r\n\r\n#{quote_if_necessary(@html, @charset)}"
337:         end
338:         
339:         body << "--#{@bodyboundary}--"
340:         
341:         # and, the attachments
342:         if(@attachments.length > 0)
343:           @attachments.each() { |attachment|
344:             body << "#{buildattachmentboundary(attachment)}\r\n\r\n#{attachment['attachment']}"
345:           }
346:           body << "\r\n--#{@attachmentboundary}--"
347:         end
348:       else
349:         # text part
350:         body << "#{buildbodyboundary("text/plain; charset=#{@charset}; format=flowed", 'quoted-printable')}\r\n\r\n#{quote_if_necessary(@text, @charset)}"
351:         
352:         # html part
353:         body << "#{buildbodyboundary("text/html; charset=#{@charset}", 'quoted-printable')}\r\n\r\n#{quote_if_necessary(@html, @charset)}"
354:         
355:         body << "--#{@bodyboundary}--"
356:       end
357:       
358:       return(body.join("\r\n\r\n"))
359:     end
360:   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
365:   def buildattachmentboundary(attachment)
366:     disposition = "Content-Disposition: inline; filename=\"#{attachment['filename']}\""
367:     boundary = "--#{@attachmentboundary}\r\nContent-Type: #{attachment['mimetype']}; name=\"#{attachment['filename']}\"\r\nContent-Transfer-Encoding: base64\r\n#{disposition}"
368:     if(attachment['headers'])
369:       boundary = boundary + "\r\n#{attachment['headers'].join("\r\n")}"
370:     end
371:     
372:     return(boundary)
373:   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
377:   def buildbodyboundary(type, encoding)
378:     return("--#{@bodyboundary}\r\nContent-Type: #{type}\r\nContent-Transfer-Encoding: #{encoding}")
379:   end
file_encode(str) click to toggle source

returns a base64 encoded version of the contents of str

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

returns the @headers as a properly formatted string

     # File lib/mailfactory.rb, line 315
315:   def headers_to_s()
316:     return("#{@headers.join("\r\n")}\r\n\r\n")
317:   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
453:   def quote_address_if_necessary(address, charset)
454:     if Array === address
455:       address.map { |a| quote_address_if_necessary(a, charset) }
456:     elsif address =~ /^(\S.*)\s+(<.*>)$/
457:       address = $2
458:       phrase = quote_if_necessary($1.gsub(/^['"](.*)['"]$/, '\1'), charset, true)
459:       "\"#{phrase}\" #{address}"
460:     else
461:       address
462:     end
463:   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
440:   def quote_if_necessary(text, charset, instruction = false)
441:     return unless text
442:     text = text.dup.force_encoding(Encoding::ASCII_8BIT) if text.respond_to?(:force_encoding)
443:     #(text =~ CHARS_NEEDING_QUOTING) ? (instruction ? quoted_printable_with_instruction(text, charset) : quoted_printable_encode(text)) : text
444:     instruction ? quoted_printable_with_instruction(text, charset) : quoted_printable_encode(text)
445:   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
404:   def quoted_printable_encode(text)
405:     [text].pack('M').gsub(/\n/, "\r\n").chomp.gsub(/=$/, '')
406:   end
quoted_printable_encode_header(text) click to toggle source
     # File lib/mailfactory.rb, line 413
413:   def quoted_printable_encode_header(text)
414:     text.enum_for(:each_byte).map do |ord|
415:       if ord < 128 and ord != 61 # 61 is ascii '='
416:         ord.chr
417:       else
418:         '=%X' % ord
419:       end
420:     end.join('').
421:         chomp.
422:         gsub(/=$/,'').
423:         gsub('?', '=3F').
424:         gsub('_', '=5F').
425:         gsub(/ /, '_')
426:   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
398:   def quoted_printable_with_instruction(text, charset)
399:     text = quoted_printable_encode_header(text)
400:     "=?#{charset}?Q?#{text}?="
401:   end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.