Class: Mailit::Mail

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

Overview

Overview:

A simple to use class to generate RFC compliant MIME email.

MailIt is a fork of MailFactory and provides a mostly identical API but has been cleaned up, simplified, and made compliant to common Ruby idioms.

Copyright © 2005-2008 David Powers. Copyright © 2009 Michael Fellinger.

This program is free software. You can re-distribute and/or modify this program under the same terms as Ruby itself.

Usage:

require 'net/smtp'
require 'mailit'

mail = Mailit::Mail.new
mail.to = '[email protected]'
mail.from = '[email protected]'
mail.subject = 'Here are some files for you!'
mail.text = 'This is what people with plain text mail readers will see'
mail.html = 'A little something <b>special</b> for people with HTML readers'
mail.attach('/etc/fstab')
mail.attach('/home/manveru/.vimrc')

server = 'smtp1.testmailer.com'
port = 25
domain = 'mail.from.domain'
password = 'foo'

Net::SMTP.start(server, port, domain, mail.from, password, :cram_md5) do |smtp|
  smtp.send_message(mail.to_s, mail.from, mail.to)
end

Todo:

  • MailFactory has a method_missing that handles getting and setting of arbitrary headers. I went for the less magical #[] and #[]= methods. Maybe someone can add the MailFactory behaviour.

Constant Summary collapse

VERSION =
'2009.03.02'
BOUNDARY_CHARS =
[*'a'..'z'] + [*'A'..'Z'] + [*'0'..'9'] + ['.', '_']
BOUNDARY_PREFIX =
"----=_NextPart_"
BODY_BOUNDARY =

body_boundary, encoding

"--%s\r\nContent-Type: %s\r\nContent-Transfer-Encoding: %s"
ATTACHMENT_BOUNDARY =

attachment_boundary, mimetype, filename, filename

"--%s\r\nContent-Type: %s; name=%p\r\nContent-Transfer-Encoding: base64\r\nContent-Disposition: inline; filename=%p"
HTML_BODY =
<<BODY.strip
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=%s">
  </head>
  <body bgcolor="#ffffff" text="#000000">
  %s
  </body>
</html>
BODY
OPTIONS =
{
  :date => true,
  :message_id => lambda{|mail|
    time = Time.now
    domain = mail['from'].first.to_s[/@[^>]+/] || 'localhost'
    message_id = "<%f.%d.%d@%s>" % [time, $$, time.object_id, domain]
  }
}
MIME_INDICATOR =
"This is a multi-part message in MIME format.\r\n\r\n--%s\r\nContent-Type: multipart/alternative; boundary=%p"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Mail

Create an instance of Mailit::Mailer.

Parameters:

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • :to (String)
  • :from (String)
  • :subject (String)
  • :text (String)
  • :html (String)

Author:

  • manveru



93
94
95
96
97
98
99
100
101
102
# File 'lib/mailit/mail.rb', line 93

def initialize(options = {})
  @headers = []
  @attachments = []
  @attachment_boundary = self.class.generate_boundary
  @body_boundary = self.class.generate_boundary
  @charset = 'utf-8'
  @html = @text = nil

  options.each{|key, value| __send__("#{key}=", value) }
end

Instance Attribute Details

#attachment_boundaryObject

Returns the value of attribute attachment_boundary.



82
83
84
# File 'lib/mailit/mail.rb', line 82

def attachment_boundary
  @attachment_boundary
end

#attachmentsObject (readonly)

Returns the value of attribute attachments.



83
84
85
# File 'lib/mailit/mail.rb', line 83

def attachments
  @attachments
end

#body_boundaryObject

Returns the value of attribute body_boundary.



82
83
84
# File 'lib/mailit/mail.rb', line 82

def body_boundary
  @body_boundary
end

#charsetObject

Returns the value of attribute charset.



82
83
84
# File 'lib/mailit/mail.rb', line 82

def charset
  @charset
end

#headersObject (readonly)

Returns the value of attribute headers.



83
84
85
# File 'lib/mailit/mail.rb', line 83

def headers
  @headers
end

#htmlObject

Returns the value of attribute html.



82
83
84
# File 'lib/mailit/mail.rb', line 82

def html
  @html
end

#textObject

Returns the value of attribute text.



82
83
84
# File 'lib/mailit/mail.rb', line 82

def text
  @text
end

Instance Method Details

#add_attachment(filename, mimetype = nil, headers = nil) ⇒ Object Also known as: attach

Attachments



136
137
138
139
140
141
142
143
# File 'lib/mailit/mail.rb', line 136

def add_attachment(filename, mimetype = nil, headers = nil)
  container = {
    :filename => Pathname.new(filename).basename,
    :mimetype => (mimetype || mime_type_for(filename)),
  }

  add_attachment_common(container, filename, headers)
end

#add_attachment_as(file, filename, mimetype = nil, headers = nil) ⇒ Object Also known as: attach_as



146
147
148
149
150
151
152
153
# File 'lib/mailit/mail.rb', line 146

def add_attachment_as(file, filename, mimetype = nil, headers = nil)
  container = {
    :filename => filename,
    :mimetype => (mimetype || mime_type_for(file))
  }

  add_attachment_common(container, file, headers)
end

#add_header(header, value) ⇒ Object

Header handling



184
185
186
187
188
189
190
191
192
193
# File 'lib/mailit/mail.rb', line 184

def add_header(header, value)
  case header.to_s.downcase
  when /^subject$/i
    value = quoted_printable_with_instruction(value)
  when /^(from|to|bcc|reply-to)$/i
    value = quote_address_if_necessary(value, charset)
  end

  @headers << [header, value]
end

#body_stringObject



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/mailit/mail.rb', line 220

def body_string
  return text unless multipart?

  body = [ MIME_INDICATOR % [attachment_boundary, body_boundary] ]
  body << build_body_boundary("text/plain; charset=#{charset} format=flowed")
  body << "\r\n\r\n" << quote_if_necessary(text, charset)

  if html
    body << build_body_boundary("text/html; charset=#{charset}")
    body << "\r\n\r\n" << quote_if_necessary(html, charset)
  end

  body << "--#{body_boundary}--"

  attachments.each do |attachment|
    body << build_attachment_boundary(attachment)
    body << "\r\n\r\n" << attachment[:attachment]
    body << "\r\n--#{attachment_boundary}--"
  end

  body.join("\r\n\r\n")
end

#construct(options = {}) ⇒ Object Also known as: to_s



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/mailit/mail.rb', line 104

def construct(options = {})
  options = OPTIONS.merge(options)
  time = Time.now

  if message_id = options[:message_id]
    self['Message-ID'] = message_id.call(self) unless self['Message-Id'].any?
  end

  if options[:date]
    self['Date'] = time.rfc2822 unless self['Date'].any?
  end

  if multipart?
    self['MIME-Version'] = '1.0' unless self['MIME-Version'].any?

    unless self['Content-Type'].any?
      if @attachments.any?
        content_type = ('multipart/alternative; boundary=%p' % body_boundary)
      else
        content_type = ('multipart/mixed; boundary=%p' % attachment_boundary)
      end

      self['Content-Type'] = content_type
    end
  end

  "#{header_string}#{body_string}"
end

#defer_send(options = {}) ⇒ Object



178
179
180
# File 'lib/mailit/mail.rb', line 178

def defer_send(options = {})
  Mailer.defer_send(self, options)
end

#get_header(header) ⇒ Object Also known as: []



207
208
209
210
211
# File 'lib/mailit/mail.rb', line 207

def get_header(header)
  regex = /^#{Regexp.escape(header)}/i

  @headers.map{|key, value| value if regex =~ key }.compact
end

#header_stringObject



214
215
216
# File 'lib/mailit/mail.rb', line 214

def header_string
  headers.map{|key,value| "#{key}: #{value}"}.join("\r\n") << "\r\n\r\n"
end

#message_id=(id) ⇒ Object



170
171
172
# File 'lib/mailit/mail.rb', line 170

def message_id=(id)
  self['Message-ID'] = id
end

#multipart?Boolean

Shortcuts

Returns:

  • (Boolean)


158
159
160
# File 'lib/mailit/mail.rb', line 158

def multipart?
  html || attachments.size > 0
end

#raw_html=(html) ⇒ Object



166
167
168
# File 'lib/mailit/mail.rb', line 166

def raw_html=(html)
  @html = html
end

#remove_header(header) ⇒ Object



201
202
203
204
205
# File 'lib/mailit/mail.rb', line 201

def remove_header(header)
  regex = /^#{Regexp.escape(header)}/i

  @headers.reject!{|key, value| key =~ regex }
end

#send(options = {}) ⇒ Object



174
175
176
# File 'lib/mailit/mail.rb', line 174

def send(options = {})
  Mailer.send(self, options)
end

#set_header(header, value) ⇒ Object Also known as: []=



195
196
197
198
# File 'lib/mailit/mail.rb', line 195

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