actionmailer 0.7.1 → 0.8.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.

data/CHANGELOG CHANGED
@@ -1,3 +1,8 @@
1
+ *0.8.0* (22th March, 2005)
2
+
3
+ * Added framework support for processing incoming emails with an Action Mailer class. See example in README.
4
+
5
+
1
6
  *0.7.1* (7th March, 2005)
2
7
 
3
8
  * Bind to newest Action Pack (1.5.1)
data/README CHANGED
@@ -5,6 +5,12 @@ are used to consolidate code for sending out forgotten passwords, welcoming
5
5
  wishes on signup, invoices for billing, and any other use case that requires
6
6
  a written notification to either a person or another system.
7
7
 
8
+ Additionally, an Action Mailer class can be used to process incoming email,
9
+ such as allowing a weblog to accept new posts from an email (which could even
10
+ have been sent from a phone).
11
+
12
+ == Sending emails
13
+
8
14
  The framework works by setting up all the email details, except the body,
9
15
  in methods on the service layer. Subject, recipients, sender, and timestamp
10
16
  are all set up this way. An example of such a method:
@@ -47,6 +53,36 @@ ApplicationMailer, it would look like this:
47
53
  ApplicationMailer.deliver_signed_up("david@loudthinking.com") # sends the email
48
54
  ApplicationMailer.new.signed_up("david@loudthinking.com") # won't work!
49
55
 
56
+ == Receiving emails
57
+
58
+ To receive emails, you need to implement a public instance method called receive that takes a
59
+ tmail object as its single parameter. The Action Mailer framework has a corresponding class method,
60
+ which is also called receive, that accepts a raw, unprocessed email as a string, which it then turns
61
+ into the tmail object and calls the receive instance method.
62
+
63
+ Example:
64
+
65
+ class Mailman < ActionMailer::Base
66
+ def receive(email)
67
+ page = Page.find_by_address(email.to.first)
68
+ page.emails.create(
69
+ :subject => email.subject, :body => email.body
70
+ )
71
+
72
+ if email.has_attachments?
73
+ for attachment in email.attachments
74
+ page.attachments.create({
75
+ :file => attachment, :description => email.subject
76
+ })
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ This Mailman can be the target for Postfix. In Rails, you would use the runner like this:
83
+
84
+ ./script/runner 'Mailman.receive(STDIN.read)'
85
+
50
86
 
51
87
  == Dependencies
52
88
 
data/lib/action_mailer.rb CHANGED
@@ -28,7 +28,7 @@ rescue LoadError
28
28
  require File.dirname(__FILE__) + '/../../actionpack/lib/action_controller'
29
29
  rescue LoadError
30
30
  require 'rubygems'
31
- require_gem 'actionpack', '>= 0.9.0'
31
+ require_gem 'actionpack', '>= 1.6.0'
32
32
  end
33
33
  end
34
34
 
@@ -114,7 +114,7 @@ module ActionMailer #:nodoc:
114
114
  rescue Object => e
115
115
  raise e if raise_delivery_errors
116
116
  end
117
- end
117
+ end
118
118
  end
119
119
 
120
120
  def mail(to, subject, body, from, timestamp = nil, headers = {},
@@ -150,6 +150,11 @@ module ActionMailer #:nodoc:
150
150
  "=?#{charset}?Q?#{text}?="
151
151
  end
152
152
 
153
+ def receive(raw_email)
154
+ logger.info "Received mail:\n #{raw_email}" unless logger.nil?
155
+ new.receive(TMail::Mail.parse(raw_email))
156
+ end
157
+
153
158
  private
154
159
  def perform_delivery_smtp(mail)
155
160
  Net::SMTP.start(server_settings[:address], server_settings[:port], server_settings[:domain],
@@ -0,0 +1,31 @@
1
+ require 'stringio'
2
+
3
+ module TMail
4
+ class Attachment < StringIO
5
+ attr_accessor :original_filename, :content_type
6
+ end
7
+
8
+ class Mail
9
+ def has_attachments?
10
+ multipart? && parts.any? { |part| part.header["content-type"].main_type != "text" }
11
+ end
12
+
13
+ def attachments
14
+ if multipart?
15
+ parts.collect { |part|
16
+ if part.header["content-type"].main_type != "text"
17
+ content = part.body.unpack("m")[0]
18
+ content = part.body if content.blank?
19
+ file_name = part.header["content-type"].params["name"]
20
+
21
+ next if file_name.blank? || content.blank?
22
+
23
+ attachment = Attachment.new(content)
24
+ attachment.original_filename = file_name.strip.dup
25
+ attachment
26
+ end
27
+ }.compact
28
+ end
29
+ end
30
+ end
31
+ end
@@ -261,6 +261,7 @@ module TMail
261
261
  default
262
262
  end
263
263
  end
264
+ alias quoted_subject subject
264
265
 
265
266
  def subject=( str )
266
267
  set_string_attr 'Subject', str
@@ -14,6 +14,8 @@ require 'tmail/header'
14
14
  require 'tmail/port'
15
15
  require 'tmail/config'
16
16
  require 'tmail/utils'
17
+ require 'tmail/attachments'
18
+ require 'tmail/quoting'
17
19
  require 'socket'
18
20
 
19
21
 
@@ -320,7 +322,7 @@ module TMail
320
322
  body_port().ropen {|f| f.each(&block) }
321
323
  end
322
324
 
323
- def body
325
+ def quoted_body
324
326
  parse_body
325
327
  @body_port.ropen {|f|
326
328
  return f.read
@@ -0,0 +1,83 @@
1
+ require 'iconv'
2
+ require 'base64'
3
+
4
+ module TMail
5
+ class Mail
6
+ def subject(to_charset = 'utf-8')
7
+ Unquoter.unquote_and_convert_to(quoted_subject, to_charset)
8
+ end
9
+
10
+ def unquoted_body(to_charset = 'utf-8')
11
+ Unquoter.unquote_and_convert_to(quoted_body, to_charset, header["content-type"]["charset"])
12
+ end
13
+
14
+ def body(to_charset = 'utf-8', &block)
15
+ attachment_presenter = block || Proc.new { |file_name| "Attachment: #{file_name}\n" }
16
+
17
+ if multipart?
18
+ parts.collect { |part|
19
+ part.header["content-type"].main_type == "text" ?
20
+ part.unquoted_body(to_charset) :
21
+ attachment_presenter.call(part.header["content-type"].params["name"])
22
+ }.join
23
+ else
24
+ unquoted_body(to_charset)
25
+ end
26
+ end
27
+ end
28
+
29
+ class Unquoter
30
+ class << self
31
+ def unquote_and_convert_to(text, to_charset, from_charset = "iso-8859-1")
32
+ return "" if text.nil?
33
+ if text =~ /^=\?(.*?)\?(.)\?(.*)\?=$/
34
+ from_charset = $1
35
+ quoting_method = $2
36
+ text = $3
37
+ case quoting_method
38
+ when "Q" then
39
+ unquote_quoted_printable_and_convert_to(text, from_charset, to_charset)
40
+ when "B" then
41
+ unquote_base64_and_convert_to(text, from_charset, to_charset)
42
+ else
43
+ raise "unknown quoting method #{quoting_method.inspect}"
44
+ end
45
+ else
46
+ unquote_quoted_printable_and_convert_to(text, from_charset, to_charset)
47
+ end
48
+ end
49
+
50
+ def unquote_quoted_printable_and_convert_to(text, from, to)
51
+ text ? Iconv.iconv(to, from || "ISO-8859-1", text.gsub(/_/," ").unpack("M*").first).first : ""
52
+ end
53
+
54
+ def unquote_base64_and_convert_to(text, from, to)
55
+ text ? Iconv.iconv(to, from || "ISO-8859-1", Base64.decode64(text)).first : ""
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ if __FILE__ == $0
62
+ require 'test/unit'
63
+
64
+ class TC_Unquoter < Test::Unit::TestCase
65
+ def test_unquote_quoted_printable
66
+ a ="=?ISO-8859-1?Q?[166417]_Bekr=E6ftelse_fra_Rejsefeber?="
67
+ b = TMail::Unquoter.unquote_and_convert_to(a, 'utf-8')
68
+ assert_equal "[166417] Bekr\303\246ftelse fra Rejsefeber", b
69
+ end
70
+
71
+ def test_unquote_base64
72
+ a ="=?ISO-8859-1?B?WzE2NjQxN10gQmVrcuZmdGVsc2UgZnJhIFJlanNlZmViZXI=?="
73
+ b = TMail::Unquoter.unquote_and_convert_to(a, 'utf-8')
74
+ assert_equal "[166417] Bekr\303\246ftelse fra Rejsefeber", b
75
+ end
76
+
77
+ def test_unquote_without_charset
78
+ a ="[166417]_Bekr=E6ftelse_fra_Rejsefeber"
79
+ b = TMail::Unquoter.unquote_and_convert_to(a, 'utf-8')
80
+ assert_equal "[166417] Bekr\303\246ftelse fra Rejsefeber", b
81
+ end
82
+ end
83
+ end
data/rakefile CHANGED
@@ -8,7 +8,7 @@ require 'rake/contrib/rubyforgepublisher'
8
8
 
9
9
  PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
10
10
  PKG_NAME = 'actionmailer'
11
- PKG_VERSION = '0.7.1' + PKG_BUILD
11
+ PKG_VERSION = '0.8.0' + PKG_BUILD
12
12
  PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
13
13
 
14
14
  desc "Default Task"
@@ -50,7 +50,7 @@ spec = Gem::Specification.new do |s|
50
50
  s.rubyforge_project = "actionmailer"
51
51
  s.homepage = "http://www.rubyonrails.org"
52
52
 
53
- s.add_dependency('actionpack', '= 1.5.1' + PKG_BUILD)
53
+ s.add_dependency('actionpack', '= 1.6.0' + PKG_BUILD)
54
54
 
55
55
  s.has_rdoc = true
56
56
  s.requirements << 'none'
@@ -212,5 +212,21 @@ class ActionMailerTest < Test::Unit::TestCase
212
212
  assert_equal 1, ActionMailer::Base.deliveries.size
213
213
  end
214
214
 
215
+ def test_unquote_subject
216
+ msg = <<EOF
217
+ From: me@example.com
218
+ Subject: =?utf-8?Q?testing_testing_=D6=A4?=
219
+ Content-Type: text/plain; charset=iso-8859-1
220
+
221
+ This_is_a_test
222
+ 2 + 2 =3D 4
223
+ EOF
224
+ mail = TMail::Mail.parse(msg)
225
+ assert_equal "testing testing \326\244", mail.subject
226
+ assert_equal "=?utf-8?Q?testing_testing_=D6=A4?=", mail.quoted_subject
227
+ assert_equal "This is a test\n2 + 2 = 4\n", mail.body
228
+ assert_equal "This_is_a_test\n2 + 2 =3D 4\n", mail.quoted_body
229
+ end
230
+
215
231
  end
216
232
 
metadata CHANGED
@@ -1,10 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.8.6
2
+ rubygems_version: 0.8.8
3
3
  specification_version: 1
4
4
  name: actionmailer
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.7.1
7
- date: 2005-03-07
6
+ version: 0.8.0
7
+ date: 2005-03-22
8
8
  summary: Service layer for easy email delivery and testing.
9
9
  require_paths:
10
10
  - lib
@@ -42,6 +42,7 @@ files:
42
42
  - lib/action_mailer/vendor/tmail.rb
43
43
  - lib/action_mailer/vendor/text/format.rb
44
44
  - lib/action_mailer/vendor/tmail/address.rb
45
+ - lib/action_mailer/vendor/tmail/attachments.rb
45
46
  - lib/action_mailer/vendor/tmail/base64.rb
46
47
  - lib/action_mailer/vendor/tmail/config.rb
47
48
  - lib/action_mailer/vendor/tmail/encode.rb
@@ -56,6 +57,7 @@ files:
56
57
  - lib/action_mailer/vendor/tmail/obsolete.rb
57
58
  - lib/action_mailer/vendor/tmail/parser.rb
58
59
  - lib/action_mailer/vendor/tmail/port.rb
60
+ - lib/action_mailer/vendor/tmail/quoting.rb
59
61
  - lib/action_mailer/vendor/tmail/scanner.rb
60
62
  - lib/action_mailer/vendor/tmail/scanner_r.rb
61
63
  - lib/action_mailer/vendor/tmail/stringio.rb
@@ -83,5 +85,5 @@ dependencies:
83
85
  -
84
86
  - "="
85
87
  - !ruby/object:Gem::Version
86
- version: 1.5.1
88
+ version: 1.6.0
87
89
  version: