actionmailer 0.9.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionmailer might be problematic. Click here for more details.

Files changed (41) hide show
  1. data/CHANGELOG +51 -2
  2. data/README +5 -6
  3. data/lib/action_mailer.rb +10 -3
  4. data/lib/action_mailer/adv_attr_accessor.rb +56 -0
  5. data/lib/action_mailer/base.rb +244 -120
  6. data/lib/action_mailer/helpers.rb +115 -0
  7. data/lib/action_mailer/part.rb +76 -0
  8. data/lib/action_mailer/part_container.rb +25 -0
  9. data/lib/action_mailer/quoting.rb +99 -0
  10. data/lib/action_mailer/utils.rb +8 -0
  11. data/lib/action_mailer/vendor/tmail/attachments.rb +6 -5
  12. data/lib/action_mailer/vendor/tmail/encode.rb +1 -0
  13. data/lib/action_mailer/vendor/tmail/facade.rb +2 -1
  14. data/lib/action_mailer/vendor/tmail/mail.rb +4 -0
  15. data/lib/action_mailer/vendor/tmail/net.rb +1 -1
  16. data/lib/action_mailer/vendor/tmail/quoting.rb +103 -80
  17. data/lib/action_mailer/vendor/tmail/scanner_r.rb +2 -2
  18. data/lib/action_mailer/vendor/tmail/utils.rb +9 -5
  19. data/rakefile +4 -4
  20. data/test/fixtures/helper_mailer/use_helper.rhtml +1 -0
  21. data/test/fixtures/helper_mailer/use_helper_method.rhtml +1 -0
  22. data/test/fixtures/helper_mailer/use_mail_helper.rhtml +5 -0
  23. data/test/fixtures/helper_mailer/use_test_helper.rhtml +1 -0
  24. data/test/fixtures/helpers/test_helper.rb +5 -0
  25. data/test/fixtures/raw_email +14 -0
  26. data/test/fixtures/raw_email10 +20 -0
  27. data/test/fixtures/raw_email11 +34 -0
  28. data/test/fixtures/raw_email2 +114 -0
  29. data/test/fixtures/raw_email3 +70 -0
  30. data/test/fixtures/raw_email4 +59 -0
  31. data/test/fixtures/raw_email5 +19 -0
  32. data/test/fixtures/raw_email6 +20 -0
  33. data/test/fixtures/raw_email7 +56 -0
  34. data/test/fixtures/raw_email8 +47 -0
  35. data/test/fixtures/raw_email9 +28 -0
  36. data/test/fixtures/test_mailer/implicitly_multipart_example.text.html.rhtml +10 -0
  37. data/test/fixtures/test_mailer/implicitly_multipart_example.text.plain.rhtml +2 -0
  38. data/test/fixtures/test_mailer/implicitly_multipart_example.text.yaml.rhtml +1 -0
  39. data/test/mail_helper_test.rb +97 -0
  40. data/test/mail_service_test.rb +391 -30
  41. metadata +32 -4
data/CHANGELOG CHANGED
@@ -1,3 +1,52 @@
1
+ *1.0.0* (6 July, 2005)
2
+
3
+ * Avoid adding nil header values #1392
4
+
5
+ * Better multipart support with implicit multipart/alternative and sorting of subparts [John Long]
6
+
7
+ * Allow for nested parts in multipart mails #1570 [Flurin Egger]
8
+
9
+ * Normalize line endings in outgoing mail bodies to "\n" #1536 [John Long]
10
+
11
+ * Allow template to be explicitly specified #1448 [tuxie@dekadance.se]
12
+
13
+ * Allow specific "multipart/xxx" content-type to be set on multipart messages #1412 [Flurin Egger]
14
+
15
+ * Unquoted @ characters in headers are now accepted in spite of RFC 822 #1206
16
+
17
+ * Helper support (borrowed from ActionPack)
18
+
19
+ * Silently ignore Errno::EINVAL errors when converting text.
20
+
21
+ * Don't cause an error when parsing an encoded attachment name #1340 [lon@speedymac.com]
22
+
23
+ * Nested multipart message parts are correctly processed in TMail::Mail#body
24
+
25
+ * BCC headers are removed when sending via SMTP #1402
26
+
27
+ * Added 'content_type' accessor, to allow content type to be set on a per-message basis. content_type defaults to "text/plain".
28
+
29
+ * Silently ignore Iconv::IllegalSequence errors when converting text #1341 [lon@speedymac.com]
30
+
31
+ * Support attachments and multipart messages.
32
+
33
+ * Added new accessors for the various mail properties.
34
+
35
+ * Fix to only perform the charset conversion if a 'from' and a 'to' charset are given (make no assumptions about what the charset was) #1276 [Jamis Buck]
36
+
37
+ * Fix attachments and content-type problems #1276 [Jamis Buck]
38
+
39
+ * Fixed the TMail#body method to look at the content-transfer-encoding header and unquote the body according to the rules it specifies #1265 [Jamis Buck]
40
+
41
+ * Added unquoting even if the iconv lib can't be loaded--in that case, only the charset conversion is skipped #1265 [Jamis Buck]
42
+
43
+ * Added automatic decoding of base64 bodies #1214 [Jamis Buck]
44
+
45
+ * Added that delivery errors are caught in a way so the mail is still returned whether the delivery was successful or not
46
+
47
+ * Fixed that email address like "Jamis Buck, M.D." <wild.medicine@example.net> would cause the quoter to generate emails resulting in "bad address" errors from the mail server #1220 [Jamis Buck]
48
+
49
+
1
50
  *0.9.1* (20th April, 2005)
2
51
 
3
52
  * Depend on Action Pack 1.8.1
@@ -87,7 +136,7 @@
87
136
  * Consolidated the server configuration options into Base#server_settings= and expanded that with controls for authentication and more [Marten]
88
137
  NOTE: This is an API change that could potentially break your application if you used the old application form. Please do change!
89
138
 
90
- * Added Base#deliveries as an accessor for an array of emails sent out through that ActionMailer class when using the :test delivery option. [bitsweat]
139
+ * Added Base#deliveries as an accessor for an array of emails sent out through that ActionMailer class when using the :test delivery option. [Jeremy Kemper]
91
140
 
92
141
  * Added Base#perform_deliveries= which can be set to false to turn off the actual delivery of the email through smtp or sendmail.
93
142
  This is especially useful for functional testing that shouldn't send off real emails, but still trigger delivery_* methods.
@@ -100,4 +149,4 @@
100
149
 
101
150
  *0.3*
102
151
 
103
- * First release
152
+ * First release
data/README CHANGED
@@ -16,16 +16,15 @@ in methods on the service layer. Subject, recipients, sender, and timestamp
16
16
  are all set up this way. An example of such a method:
17
17
 
18
18
  def signed_up(recipient)
19
- @recipients = recipient
20
- @subject = "[Signed up] Welcome #{recipient}"
21
- @from = "system@loudthinking.com"
22
- @sent_on = Time.local(2004, 12, 12)
19
+ recipients recipient
20
+ subject "[Signed up] Welcome #{recipient}"
21
+ from "system@loudthinking.com"
23
22
 
24
- @body["recipient"] = recipient
23
+ body(:recipient => recipient)
25
24
  end
26
25
 
27
26
  The body of the email is created by using an Action View template (regular
28
- ERb) that has the content of the @body hash available as instance variables.
27
+ ERb) that has the content of the body hash parameter available as instance variables.
29
28
  So the corresponding body template for the method above could look like this:
30
29
 
31
30
  Hello there,
@@ -28,19 +28,26 @@ rescue LoadError
28
28
  require File.dirname(__FILE__) + '/../../actionpack/lib/action_controller'
29
29
  rescue LoadError
30
30
  require 'rubygems'
31
- require_gem 'actionpack', '>= 1.6.0'
31
+ require_gem 'actionpack', '>= 1.9.0'
32
32
  end
33
33
  end
34
34
 
35
35
  $:.unshift(File.dirname(__FILE__) + "/action_mailer/vendor/")
36
36
 
37
37
  require 'action_mailer/base'
38
+ require 'action_mailer/helpers'
38
39
  require 'action_mailer/mail_helper'
40
+ require 'action_mailer/quoting'
39
41
  require 'action_mailer/vendor/tmail'
40
42
  require 'net/smtp'
41
43
 
42
- ActionView::Base.class_eval { include MailHelper }
44
+ ActionMailer::Base.class_eval do
45
+ include ActionMailer::Quoting
46
+ include ActionMailer::Helpers
47
+
48
+ helper MailHelper
49
+ end
43
50
 
44
51
  old_verbose, $VERBOSE = $VERBOSE, nil
45
52
  TMail::Encoder.const_set("MAX_LINE_LEN", 200)
46
- $VERBOSE = old_verbose
53
+ $VERBOSE = old_verbose
@@ -0,0 +1,56 @@
1
+ module ActionMailer
2
+ module AdvAttrAccessor #:nodoc:
3
+ def self.append_features(base)
4
+ super
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods #:nodoc:
9
+ def adv_attr_accessor(*names)
10
+ names.each do |name|
11
+ define_method("#{name}=") do |value|
12
+ instance_variable_set("@#{name}", value)
13
+ end
14
+
15
+ define_method(name) do |*parameters|
16
+ raise ArgumentError, "expected 0 or 1 parameters" unless parameters.length <= 1
17
+ if parameters.empty?
18
+ instance_variable_get("@#{name}")
19
+ else
20
+ instance_variable_set("@#{name}", parameters.first)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ module ActionMailer
30
+ module AdvAttrAccessor #:nodoc:
31
+ def self.append_features(base)
32
+ super
33
+ base.extend(ClassMethods)
34
+ end
35
+
36
+ module ClassMethods #:nodoc:
37
+ def adv_attr_accessor(*names)
38
+ names.each do |name|
39
+ define_method("#{name}=") do |value|
40
+ instance_variable_set("@#{name}", value)
41
+ end
42
+
43
+ define_method(name) do |*parameters|
44
+ raise ArgumentError, "expected 0 or 1 parameters" unless parameters.length <= 1
45
+ if parameters.empty?
46
+ instance_variable_get("@#{name}")
47
+ else
48
+ instance_variable_set("@#{name}", parameters.first)
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ end
55
+ end
56
+ end
@@ -1,23 +1,73 @@
1
+ require 'action_mailer/adv_attr_accessor'
2
+ require 'action_mailer/part'
3
+ require 'action_mailer/part_container'
4
+ require 'action_mailer/utils'
5
+ require 'tmail/net'
6
+
1
7
  module ActionMailer #:nodoc:
2
8
  # Usage:
3
9
  #
4
10
  # class ApplicationMailer < ActionMailer::Base
5
- # def post_notification(recipients, post)
6
- # @recipients = recipients
7
- # @from = post.author.email_address_with_name
8
- # @headers["bcc"] = SYSTEM_ADMINISTRATOR_EMAIL
9
- # @headers["reply-to"] = "notifications@example.com"
10
- # @subject = "[#{post.account.name} #{post.title}]"
11
- # @body["post"] = post
11
+ # # Set up properties
12
+ # # (Properties can also be specified via accessor methods
13
+ # # i.e. self.subject = "foo") and instance variables (@subject = "foo").
14
+ # def signup_notification(recipient)
15
+ # recipients recipient.email_address_with_name
16
+ # subject "New account information"
17
+ # body { "account" => recipient }
18
+ # from "system@example.com"
19
+ # end
20
+ #
21
+ # # explicitly specify multipart messages
22
+ # def signup_notification(recipient)
23
+ # recipients recipient.email_address_with_name
24
+ # subject "New account information"
25
+ # from "system@example.com"
26
+ #
27
+ # part :content_type => "text/html",
28
+ # :body => render_message("signup-as-html", :account => recipient)
29
+ #
30
+ # part "text/plain" do |p|
31
+ # p.body = render_message("signup-as-plain", :account => recipient)
32
+ # p.transfer_encoding = "base64"
33
+ # end
34
+ # end
35
+ #
36
+ # # attachments
37
+ # def signup_notification(recipient)
38
+ # recipients recipient.email_address_with_name
39
+ # subject "New account information"
40
+ # from "system@example.com"
41
+ #
42
+ # attachment :content_type => "image/jpeg",
43
+ # :body => File.read("an-image.jpg")
44
+ #
45
+ # attachment "application/pdf" do |a|
46
+ # a.body = generate_your_pdf_here()
47
+ # end
12
48
  # end
13
- #
14
- # def comment_notification(recipient, comment)
15
- # @recipients = recipient.email_address_with_name
16
- # @subject = "[#{comment.post.project.client.firm.account.name}]" +
17
- # " Re: #{comment.post.title}"
18
- # @body["comment"] = comment
19
- # @from = comment.author.email_address_with_name
20
- # @sent_on = comment.posted_on
49
+ #
50
+ # # implicitly multipart messages
51
+ # def signup_notification(recipient)
52
+ # recipients recipient.email_address_with_name
53
+ # subject "New account information"
54
+ # from "system@example.com"
55
+ # body(:account => "recipient")
56
+ #
57
+ # # ActionMailer will automatically detect and use multipart templates,
58
+ # # where each template is named after the name of the action, followed
59
+ # # by the content type. Each such detected template will be added as
60
+ # # a separate part to the message.
61
+ # #
62
+ # # for example, if the following templates existed:
63
+ # # * signup_notification.text.plain.rhtml
64
+ # # * signup_notification.text.html.rhtml
65
+ # # * signup_notification.text.xml.rxml
66
+ # # * signup_notification.text.x-yaml.rhtml
67
+ # #
68
+ # # Each would be rendered and added as a separate part to the message,
69
+ # # with the corresponding content type. The same body hash is passed to
70
+ # # each template.
21
71
  # end
22
72
  # end
23
73
  #
@@ -57,8 +107,18 @@ module ActionMailer #:nodoc:
57
107
  # for unit and functional testing.
58
108
  #
59
109
  # * <tt>default_charset</tt> - The default charset used for the body and to encode the subject. Defaults to UTF-8. You can also
60
- # pick a different charset from inside a method with <tt>@encoding</tt>.
110
+ # pick a different charset from inside a method with <tt>@charset</tt>.
111
+ # * <tt>default_content_type</tt> - The default content type used for main part of the message. Defaults to "text/plain". You
112
+ # can also pick a different content type from inside a method with <tt>@content_type</tt>.
113
+ # * <tt>default_implicit_parts_order</tt> - When a message is built implicitly (i.e. multiple parts are assemble from templates
114
+ # which specify the content type in their filenames) this variable controls how the parts are ordered. Defaults to
115
+ # ["text/html", "text/enriched", "text/plain"]. Items that appear first in the array have higher priority in the mail client
116
+ # and appear last in the mime encoded message. You can also pick a different order from inside a method with
117
+ # <tt>@implicit_parts_order</tt>.
61
118
  class Base
119
+ include ActionMailer::AdvAttrAccessor
120
+ include ActionMailer::PartContainer
121
+
62
122
  private_class_method :new #:nodoc:
63
123
 
64
124
  cattr_accessor :template_root
@@ -89,146 +149,210 @@ module ActionMailer #:nodoc:
89
149
  @@default_charset = "utf-8"
90
150
  cattr_accessor :default_charset
91
151
 
92
- attr_accessor :recipients, :subject, :body, :from, :sent_on, :headers, :bcc, :cc, :charset
152
+ @@default_content_type = "text/plain"
153
+ cattr_accessor :default_content_type
154
+
155
+ @@default_implicit_parts_order = [ "text/html", "text/enriched", "text/plain" ]
156
+ cattr_accessor :default_implicit_parts_order
157
+
158
+ adv_attr_accessor :recipients, :subject, :body, :from, :sent_on, :headers,
159
+ :bcc, :cc, :charset, :content_type, :implicit_parts_order,
160
+ :template
93
161
 
94
- def initialize
95
- @bcc = @cc = @from = @recipients = @sent_on = @subject = @body = nil
162
+ attr_reader :mail
163
+
164
+ # Instantiate a new mailer object. If +method_name+ is not +nil+, the mailer
165
+ # will be initialized according to the named method. If not, the mailer will
166
+ # remain uninitialized (useful when you only need to invoke the "receive"
167
+ # method, for instance).
168
+ def initialize(method_name=nil, *parameters) #:nodoc:
169
+ create!(method_name, *parameters) if method_name
170
+ end
171
+
172
+ # Initialize the mailer via the given +method_name+. The body will be
173
+ # rendered and a new TMail::Mail object created.
174
+ def create!(method_name, *parameters) #:nodoc:
175
+ @bcc = @cc = @from = @recipients = @sent_on = @subject = nil
96
176
  @charset = @@default_charset.dup
177
+ @content_type = @@default_content_type.dup
178
+ @implicit_parts_order = @@default_implicit_parts_order.dup
179
+ @template = method_name
180
+ @parts = []
97
181
  @headers = {}
98
- end
182
+ @body = {}
99
183
 
100
- class << self
101
- def method_missing(method_symbol, *parameters)#:nodoc:
102
- case method_symbol.id2name
103
- when /^create_([_a-z]\w*)/
104
- create_from_action($1, *parameters)
105
- when /^deliver_([_a-z]\w*)/
106
- begin
107
- deliver(send("create_" + $1, *parameters))
108
- rescue Object => e
109
- raise e if raise_delivery_errors
110
- end
184
+ send(method_name, *parameters)
185
+
186
+ # If an explicit, textual body has not been set, we check assumptions.
187
+ unless String === @body
188
+ # First, we look to see if there are any likely templates that match,
189
+ # which include the content-type in their file name (i.e.,
190
+ # "the_template_file.text.html.rhtml", etc.).
191
+ if @parts.empty?
192
+ templates = Dir.glob("#{template_path}/#{@template}.*")
193
+ templates.each do |path|
194
+ type = (File.basename(path).split(".")[1..-2] || []).join("/")
195
+ next if type.empty?
196
+ @parts << Part.new(:content_type => type,
197
+ :disposition => "inline", :charset => charset,
198
+ :body => render_message(File.basename(path).split(".")[0..-2].join('.'), @body))
199
+ end
200
+ unless @parts.empty?
201
+ @content_type = "multipart/alternative"
202
+ @parts = sort_parts(@parts, @implicit_parts_order)
203
+ end
111
204
  end
112
- end
113
205
 
114
- def mail(to, subject, body, from, timestamp = nil, headers = {}, charset = @@default_charset) #:nodoc:
115
- deliver(create(to, subject, body, from, timestamp, headers, charset))
206
+ # Then, if there were such templates, we check to see if we ought to
207
+ # also render a "normal" template (without the content type). If a
208
+ # normal template exists (or if there were no implicit parts) we render
209
+ # it.
210
+ template_exists = @parts.empty?
211
+ template_exists ||= Dir.glob("#{template_path}/#{@template}.*").any? { |i| i.split(".").length == 2 }
212
+ @body = render_message(@template, @body) if template_exists
213
+
214
+ # Finally, if there are other message parts and a textual body exists,
215
+ # we shift it onto the front of the parts and set the body to nil (so
216
+ # that create_mail doesn't try to render it in addition to the parts).
217
+ if !@parts.empty? && String === @body
218
+ @parts.unshift Part.new(:charset => charset, :body => @body)
219
+ @body = nil
220
+ end
116
221
  end
117
222
 
118
- def create(to, subject, body, from, timestamp = nil, headers = {}, charset = @@default_charset) #:nodoc:
119
- m = TMail::Mail.new
120
- m.body = body
121
- m.subject, = quote_any_if_necessary(charset, subject)
122
- m.to, m.from = quote_any_address_if_necessary(charset, to, from)
223
+ # build the mail object itself
224
+ @mail = create_mail
225
+ end
123
226
 
124
- m.date = timestamp.respond_to?("to_time") ? timestamp.to_time : (timestamp || Time.now)
227
+ # Delivers the cached TMail::Mail object. If no TMail::Mail object has been
228
+ # created (via the #create! method, for instance) this will fail.
229
+ def deliver! #:nodoc:
230
+ raise "no mail object available for delivery!" unless @mail
231
+ logger.info "Sent mail:\n #{mail.encoded}" unless logger.nil?
125
232
 
126
- m.set_content_type "text", "plain", { "charset" => charset }
233
+ begin
234
+ send("perform_delivery_#{delivery_method}", @mail) if perform_deliveries
235
+ rescue Object => e
236
+ raise e if raise_delivery_errors
237
+ end
127
238
 
128
- headers.each do |k, v|
129
- m[k] = v
130
- end
239
+ return @mail
240
+ end
131
241
 
132
- return m
242
+ private
243
+ def render_message(method_name, body)
244
+ initialize_template_class(body).render_file(method_name)
133
245
  end
134
-
135
- def deliver(mail) #:nodoc:
136
- logger.info "Sent mail:\n #{mail.encoded}" unless logger.nil?
137
- send("perform_delivery_#{delivery_method}", mail) if perform_deliveries
138
- return mail
246
+
247
+ def template_path
248
+ template_root + "/" + Inflector.underscore(self.class.name)
139
249
  end
140
250
 
141
- def quoted_printable(text, charset)#:nodoc:
142
- text = text.gsub( /[^a-z ]/i ) { "=%02x" % $&[0] }.gsub( / /, "_" )
143
- "=?#{charset}?Q?#{text}?="
251
+ def initialize_template_class(assigns)
252
+ ActionView::Base.new(template_path, assigns, self)
144
253
  end
145
254
 
146
- CHARS_NEEDING_QUOTING = /[\000-\011\013\014\016-\037\177-\377]/
255
+ def sort_parts(parts, order = [])
256
+ order = order.collect { |s| s.downcase }
147
257
 
148
- # Quote the given text if it contains any "illegal" characters
149
- def quote_if_necessary(text, charset)
150
- (text =~ CHARS_NEEDING_QUOTING) ?
151
- quoted_printable(text, charset) :
152
- text
153
- end
258
+ parts = parts.sort do |a, b|
259
+ a_ct = a.content_type.downcase
260
+ b_ct = b.content_type.downcase
154
261
 
155
- # Quote any of the given strings if they contain any "illegal" characters
156
- def quote_any_if_necessary(charset, *args)
157
- args.map { |v| quote_if_necessary(v, charset) }
158
- end
262
+ a_in = order.include? a_ct
263
+ b_in = order.include? b_ct
159
264
 
160
- # Quote the given address if it needs to be. The address may be a
161
- # regular email address, or it can be a phrase followed by an address in
162
- # brackets. The phrase is the only part that will be quoted, and only if
163
- # it needs to be. This allows extended characters to be used in the
164
- # "to", "from", "cc", and "bcc" headers.
165
- def quote_address_if_necessary(address, charset)
166
- if Array === address
167
- address.map { |a| quote_address_if_necessary(a, charset) }
168
- elsif address =~ /^(\S.*)\s+(<.*>)$/
169
- address = $2
170
- phrase = quote_if_necessary($1.gsub(/^['"](.*)['"]$/, '\1'), charset)
171
- "#{phrase} #{address}"
172
- else
173
- address
265
+ s = case
266
+ when a_in && b_in
267
+ order.index(a_ct) <=> order.index(b_ct)
268
+ when a_in
269
+ -1
270
+ when b_in
271
+ 1
272
+ else
273
+ a_ct <=> b_ct
274
+ end
275
+
276
+ # reverse the ordering because parts that come last are displayed
277
+ # first in mail clients
278
+ (s * -1)
174
279
  end
175
- end
176
280
 
177
- # Quote any of the given addresses, if they need to be.
178
- def quote_any_address_if_necessary(charset, *args)
179
- args.map { |v| quote_address_if_necessary(v, charset) }
281
+ parts
180
282
  end
181
283
 
182
- def receive(raw_email)
183
- logger.info "Received mail:\n #{raw_email}" unless logger.nil?
184
- new.receive(TMail::Mail.parse(raw_email))
185
- end
284
+ def create_mail
285
+ m = TMail::Mail.new
286
+
287
+ m.subject, = quote_any_if_necessary(charset, subject)
288
+ m.to, m.from = quote_any_address_if_necessary(charset, recipients, from)
289
+ m.bcc = quote_address_if_necessary(bcc, charset) unless bcc.nil?
290
+ m.cc = quote_address_if_necessary(cc, charset) unless cc.nil?
186
291
 
187
- private
188
- def perform_delivery_smtp(mail)
189
- Net::SMTP.start(server_settings[:address], server_settings[:port], server_settings[:domain],
190
- server_settings[:user_name], server_settings[:password], server_settings[:authentication]) do |smtp|
191
- smtp.sendmail(mail.encoded, mail.from, mail.destinations)
292
+ m.date = sent_on.to_time rescue sent_on if sent_on
293
+ headers.each { |k, v| m[k] = v }
294
+
295
+ if @parts.empty?
296
+ m.set_content_type content_type, nil, { "charset" => charset }
297
+ m.body = Utils.normalize_new_lines(body)
298
+ else
299
+ if String === body
300
+ part = TMail::Mail.new
301
+ part.body = Utils.normalize_new_lines(body)
302
+ part.set_content_type content_type, nil, { "charset" => charset }
303
+ part.set_content_disposition "inline"
304
+ m.parts << part
192
305
  end
193
- end
194
306
 
195
- def perform_delivery_sendmail(mail)
196
- IO.popen("/usr/sbin/sendmail -i -t","w+") do |sm|
197
- sm.print(mail.encoded)
198
- sm.flush
307
+ @parts.each do |p|
308
+ part = (TMail::Mail === p ? p : p.to_mail(self))
309
+ m.parts << part
199
310
  end
311
+
312
+ m.set_content_type(content_type, nil, { "charset" => charset }) if content_type =~ /multipart/
200
313
  end
201
314
 
202
- def perform_delivery_test(mail)
203
- deliveries << mail
204
- end
315
+ @mail = m
316
+ end
205
317
 
206
- def create_from_action(method_name, *parameters)
207
- mailer = new
208
- mailer.body = {}
209
- mailer.send(method_name, *parameters)
318
+ def perform_delivery_smtp(mail)
319
+ destinations = mail.destinations
320
+ mail.ready_to_send
210
321
 
211
- unless String === mailer.body then
212
- mailer.body = render_body mailer, method_name
213
- end
322
+ Net::SMTP.start(server_settings[:address], server_settings[:port], server_settings[:domain],
323
+ server_settings[:user_name], server_settings[:password], server_settings[:authentication]) do |smtp|
324
+ smtp.sendmail(mail.encoded, mail.from, destinations)
325
+ end
326
+ end
214
327
 
215
- mail = create(mailer.recipients, mailer.subject, mailer.body,
216
- mailer.from, mailer.sent_on, mailer.headers,
217
- mailer.charset)
328
+ def perform_delivery_sendmail(mail)
329
+ IO.popen("/usr/sbin/sendmail -i -t","w+") do |sm|
330
+ sm.print(mail.encoded)
331
+ sm.flush
332
+ end
333
+ end
218
334
 
219
- mail.bcc = quote_address_if_necessary(mailer.bcc, mailer.charset) unless mailer.bcc.nil?
220
- mail.cc = quote_address_if_necessary(mailer.cc, mailer.charset) unless mailer.cc.nil?
335
+ def perform_delivery_test(mail)
336
+ deliveries << mail
337
+ end
221
338
 
222
- return mail
223
- end
224
-
225
- def render_body(mailer, method_name)
226
- ActionView::Base.new(template_path, mailer.body).render_file(method_name)
227
- end
228
-
229
- def template_path
230
- template_root + "/" + Inflector.underscore(self.to_s)
339
+ class << self
340
+ def method_missing(method_symbol, *parameters)#:nodoc:
341
+ case method_symbol.id2name
342
+ when /^create_([_a-z]\w*)/ then new($1, *parameters).mail
343
+ when /^deliver_([_a-z]\w*)/ then new($1, *parameters).deliver!
344
+ when "new" then nil
345
+ else super
231
346
  end
347
+ end
348
+
349
+ def receive(raw_email)
350
+ logger.info "Received mail:\n #{raw_email}" unless logger.nil?
351
+ mail = TMail::Mail.parse(raw_email)
352
+ mail.base64_decode
353
+ new.receive(mail)
354
+ end
355
+
232
356
  end
233
357
  end
234
358
  end