class SystemMail::Message
Manages compiling an email message from various attributes and files.
Constants
- BASE64_SIZE
- SETTINGS
- UTF8_SIZE
Public Class Methods
new(options={})
click to toggle source
Creates new message. Available options:
-
:text, String, Textual part of the message
-
:enriched, String, Enriched alternative of the message (RFC 1896)
-
:html, String, HTML alternative of the message
-
:from, String, 'From:' header for the message
-
:to, String or Array of Strings, 'To:' header, if Arrey, it gets joined by ', '
-
:subject, String, Subject of the message, it gets encoded automatically
-
:files, File or String of file path or Array of them, Attachments of the message
Options :text, :enriched and :html are interchangeable. Option :to is required.
Examples:
mail = Message.new( :from => 'user@example.com', :to => 'user@gmail.com', :subject => 'test subject', :text => 'big small normal', :html => File.read('test.html'), :attachments => [File.open('Gemfile'), 'attachment.zip'], )
# File lib/system_mail/message.rb, line 44 def initialize(options={}) @body = {} @to = [] @mutex = Mutex.new %W(text enriched html from to subject attachments).each do |option| name = option.to_sym send(name, options[name]) end end
Public Instance Methods
deliver()
click to toggle source
Delivers the message using sendmail.
Example:
mail.deliver #=> nil
# File lib/system_mail/message.rb, line 61 def deliver validate with_storage do write_headers write_message send_message end end
settings()
click to toggle source
# File lib/system_mail/message.rb, line 70 def settings @settings ||= SETTINGS.dup end
Private Instance Methods
add_file(name, path = nil)
click to toggle source
# File lib/system_mail/message.rb, line 104 def add_file(name, path = nil) path ||= name name = File.basename(name) files[name] = path end
attachments(input)
click to toggle source
# File lib/system_mail/message.rb, line 100 def attachments(input) input && input.each{ |file| add_file(*file) } end
encode_address(address)
click to toggle source
# File lib/system_mail/message.rb, line 261 def encode_address(address) if address.ascii_only? address else if matchdata = address.match(/(.+)\s\<(.+)\>/) "#{encode_utf8 matchdata[1]} <#{matchdata[2]}>" else address end end end
encode_from()
click to toggle source
# File lib/system_mail/message.rb, line 251 def encode_from encode_address(@from) end
encode_subject()
click to toggle source
# File lib/system_mail/message.rb, line 247 def encode_subject @subject.ascii_only? ? @subject : encode_utf8(@subject) end
encode_to()
click to toggle source
# File lib/system_mail/message.rb, line 255 def encode_to @to.map do |to| encode_address(to) end.join(', ') end
encode_utf8(input)
click to toggle source
# File lib/system_mail/message.rb, line 273 def encode_utf8(input) "=?UTF-8?B?#{Base64.strict_encode64 input}?=" end
enriched(input)
click to toggle source
# File lib/system_mail/message.rb, line 80 def enriched(input) @body['enriched'] = input end
files()
click to toggle source
# File lib/system_mail/message.rb, line 110 def files @files ||= {} end
from(input)
click to toggle source
# File lib/system_mail/message.rb, line 88 def from(input) @from = input end
html(input)
click to toggle source
# File lib/system_mail/message.rb, line 84 def html(input) @body['html'] = input end
multipart(type) { |boundary| ... }
click to toggle source
# File lib/system_mail/message.rb, line 164 def multipart(type) boundary = new_boundary(type) @storage.puts "Content-Type: multipart/#{type}; boundary=\"#{boundary}\"" yield boundary write_part boundary, :end end
new_boundary(type)
click to toggle source
# File lib/system_mail/message.rb, line 243 def new_boundary(type) rand(36**6).to_s(36).rjust(20,type.to_s) end
read_mime(file_path)
click to toggle source
# File lib/system_mail/message.rb, line 234 def read_mime(file_path) `#{settings[:file]} '#{file_path.shellescape}'`.strip end
send_message()
click to toggle source
# File lib/system_mail/message.rb, line 222 def send_message if @storage.file? @storage.capture do |message_path| `#{settings[:sendmail]} < #{message_path}` end else IO.popen(settings[:sendmail], 'w') do |io| io.puts @storage.read end end end
subject(input)
click to toggle source
# File lib/system_mail/message.rb, line 96 def subject(input) @subject = input end
text(input)
click to toggle source
# File lib/system_mail/message.rb, line 76 def text(input) @body['text'] = input end
to(input)
click to toggle source
# File lib/system_mail/message.rb, line 92 def to(input) @to += Array(input) end
validate()
click to toggle source
# File lib/system_mail/message.rb, line 238 def validate fail ArgumentError, "Header 'To:' is empty" if @to.empty? warn 'Message body is empty' if @body.empty? end
with_storage() { || ... }
click to toggle source
# File lib/system_mail/message.rb, line 114 def with_storage @mutex.synchronize do @storage = Storage.new settings[:storage] yield @storage.clear @storage = nil end end
write_8bit_data(data, content_type)
click to toggle source
# File lib/system_mail/message.rb, line 191 def write_8bit_data(data, content_type) @storage.puts "Content-Type: #{content_type}; charset=#{data.encoding}" @storage.puts "Content-Transfer-Encoding: 8bit" @storage.puts @storage.puts data end
write_base64_data(data, content_type)
click to toggle source
# File lib/system_mail/message.rb, line 182 def write_base64_data(data, content_type) @storage.puts "Content-Type: #{content_type}; charset=#{data.encoding}" @storage.puts "Content-Transfer-Encoding: base64" @storage.puts Base64.strict_encode64(data).scan(/.{1,#{BASE64_SIZE}}/).each do |line| @storage.puts line end end
write_base64_file(name, path)
click to toggle source
# File lib/system_mail/message.rb, line 212 def write_base64_file(name, path) @storage.puts "Content-Type: #{read_mime(path)}" @storage.puts "Content-Transfer-Encoding: base64" @storage.puts "Content-Disposition: attachment; filename=\"#{name}\"" @storage.puts @storage.capture do |message_path| `#{settings[:base64]} '#{path.shellescape}' >> #{message_path}` end end
write_body()
click to toggle source
# File lib/system_mail/message.rb, line 138 def write_body case @body.length when 0 nil when 1 data, type = @body.first write_content data, "text/#{type}" else multipart :alternative do |boundary| %w[text enriched html].each do |type| data = @body[type] || next write_part boundary write_content data, "text/#{type}" end end end end
write_content(data, content_type)
click to toggle source
# File lib/system_mail/message.rb, line 156 def write_content(data, content_type) if data.bytesize < UTF8_SIZE || !data.lines.any?{ |line| line.bytesize > UTF8_SIZE } write_8bit_data data, content_type else write_base64_data data, content_type end end
write_file(name, file)
click to toggle source
# File lib/system_mail/message.rb, line 198 def write_file(name, file) path = case file when String fail Errno::ENOENT, file unless File.file?(file) fail Errno::EACCES, file unless File.readable?(file) file when File file.path else fail ArgumentError, 'attachment must be File or String of file path' end write_base64_file name, path end
write_headers()
click to toggle source
# File lib/system_mail/message.rb, line 171 def write_headers @storage.puts "From: #{encode_from}" if @from @storage.puts "To: #{encode_to}" @storage.puts "Subject: #{encode_subject}" end
write_message()
click to toggle source
# File lib/system_mail/message.rb, line 123 def write_message if files.any? multipart :mixed do |boundary| write_part boundary write_body files.each_pair do |name, path| write_part boundary write_file name, path end end else write_body end end
write_part(boundary, type = :begin)
click to toggle source
# File lib/system_mail/message.rb, line 177 def write_part(boundary, type = :begin) @storage.puts @storage.puts "--#{boundary}#{type == :end ? '--' : ''}" end