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
@@ -0,0 +1,115 @@
1
+ module ActionMailer #:nodoc:
2
+ module Helpers #:nodoc:
3
+ def self.append_features(base)
4
+ super
5
+
6
+ # Initialize the base module to aggregate its helpers.
7
+ base.class_inheritable_accessor :master_helper_module
8
+ base.master_helper_module = Module.new
9
+
10
+ # Extend base with class methods to declare helpers.
11
+ base.extend(ClassMethods)
12
+
13
+ base.class_eval do
14
+ # Wrap inherited to create a new master helper module for subclasses.
15
+ class << self
16
+ alias_method :inherited_without_helper, :inherited
17
+ alias_method :inherited, :inherited_with_helper
18
+ end
19
+
20
+ # Wrap initialize_template_class to extend new template class
21
+ # instances with the master helper module.
22
+ alias_method :initialize_template_class_without_helper, :initialize_template_class
23
+ alias_method :initialize_template_class, :initialize_template_class_with_helper
24
+ end
25
+ end
26
+
27
+ module ClassMethods #:nodoc:
28
+ # Makes all the (instance) methods in the helper module available to templates rendered through this controller.
29
+ # See ActionView::Helpers (link:classes/ActionView/Helpers.html) for more about making your own helper modules
30
+ # available to the templates.
31
+ def add_template_helper(helper_module) #:nodoc:
32
+ master_helper_module.module_eval "include #{helper_module}"
33
+ end
34
+
35
+ # Declare a helper:
36
+ # helper :foo
37
+ # requires 'foo_helper' and includes FooHelper in the template class.
38
+ # helper FooHelper
39
+ # includes FooHelper in the template class.
40
+ # helper { def foo() "#{bar} is the very best" end }
41
+ # evaluates the block in the template class, adding method #foo.
42
+ # helper(:three, BlindHelper) { def mice() 'mice' end }
43
+ # does all three.
44
+ def helper(*args, &block)
45
+ args.flatten.each do |arg|
46
+ case arg
47
+ when Module
48
+ add_template_helper(arg)
49
+ when String, Symbol
50
+ file_name = arg.to_s.underscore + '_helper'
51
+ class_name = file_name.camelize
52
+
53
+ begin
54
+ require_dependency(file_name)
55
+ rescue LoadError => load_error
56
+ requiree = / -- (.*?)(\.rb)?$/.match(load_error).to_a[1]
57
+ msg = (requiree == file_name) ? "Missing helper file helpers/#{file_name}.rb" : "Can't load file: #{requiree}"
58
+ raise LoadError.new(msg).copy_blame!(load_error)
59
+ end
60
+
61
+ add_template_helper(class_name.constantize)
62
+ else
63
+ raise ArgumentError, 'helper expects String, Symbol, or Module argument'
64
+ end
65
+ end
66
+
67
+ # Evaluate block in template class if given.
68
+ master_helper_module.module_eval(&block) if block_given?
69
+ end
70
+
71
+ # Declare a controller method as a helper. For example,
72
+ # helper_method :link_to
73
+ # def link_to(name, options) ... end
74
+ # makes the link_to controller method available in the view.
75
+ def helper_method(*methods)
76
+ methods.flatten.each do |method|
77
+ master_helper_module.module_eval <<-end_eval
78
+ def #{method}(*args, &block)
79
+ controller.send(%(#{method}), *args, &block)
80
+ end
81
+ end_eval
82
+ end
83
+ end
84
+
85
+ # Declare a controller attribute as a helper. For example,
86
+ # helper_attr :name
87
+ # attr_accessor :name
88
+ # makes the name and name= controller methods available in the view.
89
+ # The is a convenience wrapper for helper_method.
90
+ def helper_attr(*attrs)
91
+ attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") }
92
+ end
93
+
94
+ private
95
+ def inherited_with_helper(child)
96
+ inherited_without_helper(child)
97
+ begin
98
+ child.master_helper_module = Module.new
99
+ child.master_helper_module.send :include, master_helper_module
100
+ child.helper child.name.underscore
101
+ rescue MissingSourceFile => e
102
+ raise unless e.is_missing?("helpers/#{child.name.underscore}_helper")
103
+ end
104
+ end
105
+ end
106
+
107
+ private
108
+ # Extend the template class instance with our controller's helper module.
109
+ def initialize_template_class_with_helper(assigns)
110
+ returning(template = initialize_template_class_without_helper(assigns)) do
111
+ template.extend self.class.master_helper_module
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,76 @@
1
+ require 'action_mailer/adv_attr_accessor'
2
+ require 'action_mailer/part_container'
3
+ require 'action_mailer/utils'
4
+
5
+ module ActionMailer
6
+ class Part #:nodoc:
7
+ include ActionMailer::AdvAttrAccessor
8
+ include ActionMailer::PartContainer
9
+
10
+ adv_attr_accessor :content_type, :content_disposition, :charset, :body
11
+ adv_attr_accessor :filename, :transfer_encoding, :headers
12
+
13
+ def initialize(params)
14
+ @content_type = params[:content_type]
15
+ @content_disposition = params[:disposition] || "inline"
16
+ @charset = params[:charset]
17
+ @body = params[:body]
18
+ @filename = params[:filename]
19
+ @transfer_encoding = params[:transfer_encoding] || "quoted-printable"
20
+ @headers = params[:headers] || {}
21
+ @parts = []
22
+ end
23
+
24
+ def to_mail(defaults)
25
+ part = TMail::Mail.new
26
+
27
+ if @parts.empty?
28
+ part.content_transfer_encoding = transfer_encoding || "quoted-printable"
29
+ case (transfer_encoding || "").downcase
30
+ when "base64" then
31
+ part.body = TMail::Base64.folding_encode(body)
32
+ when "quoted-printable"
33
+ part.body = [Utils.normalize_new_lines(body)].pack("M*")
34
+ else
35
+ part.body = body
36
+ end
37
+
38
+ # Always set the content_type after setting the body and or parts!
39
+ # Also don't set filename and name when there is none (like in
40
+ # non-attachment parts)
41
+ if content_disposition == "attachment"
42
+ part.set_content_type(content_type || defaults.content_type, nil,
43
+ squish("charset" => nil, "name" => filename))
44
+ part.set_content_disposition(content_disposition,
45
+ squish("filename" => filename))
46
+ else
47
+ part.set_content_type(content_type || defaults.content_type, nil,
48
+ "charset" => (charset || defaults.charset))
49
+ part.set_content_disposition(content_disposition)
50
+ end
51
+ else
52
+ if String === body
53
+ part = TMail::Mail.new
54
+ part.body = body
55
+ part.set_content_type content_type, nil, { "charset" => charset }
56
+ part.set_content_disposition "inline"
57
+ m.parts << part
58
+ end
59
+
60
+ @parts.each do |p|
61
+ prt = (TMail::Mail === p ? p : p.to_mail(defaults))
62
+ part.parts << prt
63
+ end
64
+
65
+ part.set_content_type(content_type, nil, { "charset" => charset }) if content_type =~ /multipart/
66
+ end
67
+
68
+ part
69
+ end
70
+
71
+ private
72
+ def squish(values={})
73
+ values.delete_if { |k,v| v.nil? }
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,25 @@
1
+ module ActionMailer
2
+ module PartContainer #:nodoc:
3
+ attr_reader :parts
4
+
5
+ # Add a part to a multipart message, with the given content-type. The
6
+ # part itself is yielded to the block, so that other properties (charset,
7
+ # body, headers, etc.) can be set on it.
8
+ def part(params)
9
+ params = {:content_type => params} if String === params
10
+ part = Part.new(params)
11
+ yield part if block_given?
12
+ @parts << part
13
+ end
14
+
15
+ # Add an attachment to a multipart message. This is simply a part with the
16
+ # content-disposition set to "attachment".
17
+ def attachment(params, &block)
18
+ params = { :content_type => params } if String === params
19
+ params = { :disposition => "attachment",
20
+ :transfer_encoding => "base64" }.merge(params)
21
+ part(params, &block)
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,99 @@
1
+ module ActionMailer
2
+ module Quoting #:nodoc:
3
+ # Convert the given text into quoted printable format, with an instruction
4
+ # that the text be eventually interpreted in the given charset.
5
+ def quoted_printable(text, charset)
6
+ text = text.gsub( /[^a-z ]/i ) { "=%02x" % $&[0] }.gsub( / /, "_" )
7
+ "=?#{charset}?Q?#{text}?="
8
+ end
9
+
10
+ # A quick-and-dirty regexp for determining whether a string contains any
11
+ # characters that need escaping.
12
+ CHARS_NEEDING_QUOTING = /[\000-\011\013\014\016-\037\177-\377]/
13
+
14
+ # Quote the given text if it contains any "illegal" characters
15
+ def quote_if_necessary(text, charset)
16
+ (text =~ CHARS_NEEDING_QUOTING) ?
17
+ quoted_printable(text, charset) :
18
+ text
19
+ end
20
+
21
+ # Quote any of the given strings if they contain any "illegal" characters
22
+ def quote_any_if_necessary(charset, *args)
23
+ args.map { |v| quote_if_necessary(v, charset) }
24
+ end
25
+
26
+ # Quote the given address if it needs to be. The address may be a
27
+ # regular email address, or it can be a phrase followed by an address in
28
+ # brackets. The phrase is the only part that will be quoted, and only if
29
+ # it needs to be. This allows extended characters to be used in the
30
+ # "to", "from", "cc", and "bcc" headers.
31
+ def quote_address_if_necessary(address, charset)
32
+ if Array === address
33
+ address.map { |a| quote_address_if_necessary(a, charset) }
34
+ elsif address =~ /^(\S.*)\s+(<.*>)$/
35
+ address = $2
36
+ phrase = quote_if_necessary($1.gsub(/^['"](.*)['"]$/, '\1'), charset)
37
+ "\"#{phrase}\" #{address}"
38
+ else
39
+ address
40
+ end
41
+ end
42
+
43
+ # Quote any of the given addresses, if they need to be.
44
+ def quote_any_address_if_necessary(charset, *args)
45
+ args.map { |v| quote_address_if_necessary(v, charset) }
46
+ end
47
+ end
48
+ end
49
+
50
+ module ActionMailer
51
+ module Quoting #:nodoc:
52
+ # Convert the given text into quoted printable format, with an instruction
53
+ # that the text be eventually interpreted in the given charset.
54
+ def quoted_printable(text, charset)
55
+ text = text.gsub( /[^a-z ]/i ) { "=%02x" % $&[0] }.gsub( / /, "_" )
56
+ "=?#{charset}?Q?#{text}?="
57
+ end
58
+
59
+ # A quick-and-dirty regexp for determining whether a string contains any
60
+ # characters that need escaping.
61
+ if !defined?(CHARS_NEEDING_QUOTING)
62
+ CHARS_NEEDING_QUOTING = /[\000-\011\013\014\016-\037\177-\377]/
63
+ end
64
+
65
+ # Quote the given text if it contains any "illegal" characters
66
+ def quote_if_necessary(text, charset)
67
+ (text =~ CHARS_NEEDING_QUOTING) ?
68
+ quoted_printable(text, charset) :
69
+ text
70
+ end
71
+
72
+ # Quote any of the given strings if they contain any "illegal" characters
73
+ def quote_any_if_necessary(charset, *args)
74
+ args.map { |v| quote_if_necessary(v, charset) }
75
+ end
76
+
77
+ # Quote the given address if it needs to be. The address may be a
78
+ # regular email address, or it can be a phrase followed by an address in
79
+ # brackets. The phrase is the only part that will be quoted, and only if
80
+ # it needs to be. This allows extended characters to be used in the
81
+ # "to", "from", "cc", and "bcc" headers.
82
+ def quote_address_if_necessary(address, charset)
83
+ if Array === address
84
+ address.map { |a| quote_address_if_necessary(a, charset) }
85
+ elsif address =~ /^(\S.*)\s+(<.*>)$/
86
+ address = $2
87
+ phrase = quote_if_necessary($1.gsub(/^['"](.*)['"]$/, '\1'), charset)
88
+ "\"#{phrase}\" #{address}"
89
+ else
90
+ address
91
+ end
92
+ end
93
+
94
+ # Quote any of the given addresses, if they need to be.
95
+ def quote_any_address_if_necessary(charset, *args)
96
+ args.map { |v| quote_address_if_necessary(v, charset) }
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,8 @@
1
+ module ActionMailer
2
+ module Utils #:nodoc:
3
+ def normalize_new_lines(text)
4
+ text.to_s.gsub(/\r\n?/, "\n")
5
+ end
6
+ module_function :normalize_new_lines
7
+ end
8
+ end
@@ -14,18 +14,19 @@ module TMail
14
14
  if multipart?
15
15
  parts.collect { |part|
16
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"]
17
+ content = part.body # unquoted automatically by TMail#body
18
+ file_name = part.sub_header("content-type", "name") ||
19
+ part.sub_header("content-disposition", "filename")
20
20
 
21
21
  next if file_name.blank? || content.blank?
22
22
 
23
23
  attachment = Attachment.new(content)
24
- attachment.original_filename = file_name.strip.dup
24
+ attachment.original_filename = file_name.strip
25
+ attachment.content_type = part.content_type
25
26
  attachment
26
27
  end
27
28
  }.compact
28
29
  end
29
30
  end
30
31
  end
31
- end
32
+ end
@@ -253,6 +253,7 @@ module TMail
253
253
  # FIXME: implement line folding
254
254
  #
255
255
  def kv_pair( k, v )
256
+ return if v.nil?
256
257
  v = normalize_encoding(v)
257
258
  if token_safe?(v)
258
259
  add_text k + '=' + v
@@ -442,7 +442,8 @@ module TMail
442
442
  h.disposition = str
443
443
  h.params.clear
444
444
  else
445
- h = store('Content-Disposition', str)
445
+ store('Content-Disposition', str)
446
+ h = @header['content-disposition']
446
447
  end
447
448
  h.params.replace params if params
448
449
  end
@@ -145,6 +145,10 @@ module TMail
145
145
  @header[key.downcase]
146
146
  end
147
147
 
148
+ def sub_header(key, param)
149
+ (hdr = self[key]) ? hdr[param] : nil
150
+ end
151
+
148
152
  alias fetch []
149
153
 
150
154
  def []=( key, val )
@@ -61,7 +61,7 @@ module TMail
61
61
  end
62
62
 
63
63
  def add_message_id( fqdn = nil )
64
- self.message_id = ::TMail::new_msgid(fqdn)
64
+ self.message_id = ::TMail::new_message_id(fqdn)
65
65
  end
66
66
 
67
67
  def add_date
@@ -1,101 +1,124 @@
1
- begin
2
- require 'iconv'
3
- require 'base64'
4
-
5
- module TMail
6
- class Mail
7
- def subject(to_charset = 'utf-8')
8
- Unquoter.unquote_and_convert_to(quoted_subject, to_charset)
9
- end
10
-
11
- def unquoted_body(to_charset = 'utf-8')
12
- from_charset = header['content-type']['charset'] rescue 'us-ascii'
13
- Unquoter.unquote_and_convert_to(quoted_body, to_charset, from_charset)
14
- end
1
+ module TMail
2
+ class Mail
3
+ def subject(to_charset = 'utf-8')
4
+ Unquoter.unquote_and_convert_to(quoted_subject, to_charset)
5
+ end
15
6
 
16
- def body(to_charset = 'utf-8', &block)
17
- attachment_presenter = block || Proc.new { |file_name| "Attachment: #{file_name}\n" }
18
-
19
- if multipart?
20
- parts.collect { |part|
21
- part.header["content-type"].main_type == "text" ?
22
- part.unquoted_body(to_charset) :
23
- attachment_presenter.call(part.header["content-type"].params["name"])
24
- }.join
7
+ def unquoted_body(to_charset = 'utf-8')
8
+ from_charset = sub_header("content-type", "charset")
9
+ case (content_transfer_encoding || "7bit").downcase
10
+ when "quoted-printable"
11
+ Unquoter.unquote_quoted_printable_and_convert_to(quoted_body,
12
+ to_charset, from_charset)
13
+ when "base64"
14
+ Unquoter.unquote_base64_and_convert_to(quoted_body, to_charset,
15
+ from_charset)
16
+ when "7bit", "8bit"
17
+ Unquoter.convert_to(quoted_body, to_charset, from_charset)
18
+ when "binary"
19
+ quoted_body
25
20
  else
26
- unquoted_body(to_charset)
27
- end
21
+ quoted_body
28
22
  end
29
23
  end
30
24
 
31
- class Unquoter
32
- class << self
33
- def unquote_and_convert_to(text, to_charset, from_charset = "iso-8859-1")
34
- return "" if text.nil?
35
- if text =~ /^=\?(.*?)\?(.)\?(.*)\?=$/
36
- from_charset = $1
37
- quoting_method = $2
38
- text = $3
39
- case quoting_method.upcase
40
- when "Q" then
41
- unquote_quoted_printable_and_convert_to(text, from_charset, to_charset)
42
- when "B" then
43
- unquote_base64_and_convert_to(text, from_charset, to_charset)
44
- else
45
- raise "unknown quoting method #{quoting_method.inspect}"
46
- end
25
+ def body(to_charset = 'utf-8', &block)
26
+ attachment_presenter = block || Proc.new { |file_name| "Attachment: #{file_name}\n" }
27
+
28
+ if multipart?
29
+ parts.collect { |part|
30
+ header = part["content-type"]
31
+
32
+ if part.multipart?
33
+ part.body(to_charset, &attachment_presenter)
34
+ elsif header.nil?
35
+ ""
36
+ elsif header.main_type == "text"
37
+ part.unquoted_body(to_charset)
47
38
  else
48
- unquote_quoted_printable_and_convert_to(text, from_charset, to_charset)
39
+ attachment_presenter.call(header["name"] || "(unnamed)")
49
40
  end
50
- end
51
-
52
- def unquote_quoted_printable_and_convert_to(text, from, to)
53
- text ? Iconv.iconv(to, from || "ISO-8859-1", text.gsub(/_/," ").unpack("M*").first).first : ""
54
- end
55
-
56
- def unquote_base64_and_convert_to(text, from, to)
57
- text ? Iconv.iconv(to, from || "ISO-8859-1", Base64.decode64(text)).first : ""
58
- end
41
+ }.join
42
+ else
43
+ unquoted_body(to_charset)
59
44
  end
60
45
  end
61
46
  end
62
47
 
63
- if __FILE__ == $0
64
- require 'test/unit'
65
-
66
- class TC_Unquoter < Test::Unit::TestCase
67
- def test_unquote_quoted_printable
68
- a ="=?ISO-8859-1?Q?[166417]_Bekr=E6ftelse_fra_Rejsefeber?="
69
- b = TMail::Unquoter.unquote_and_convert_to(a, 'utf-8')
70
- assert_equal "[166417] Bekr\303\246ftelse fra Rejsefeber", b
48
+ class Unquoter
49
+ class << self
50
+ def unquote_and_convert_to(text, to_charset, from_charset = "iso-8859-1")
51
+ return "" if text.nil?
52
+ if text =~ /^=\?(.*?)\?(.)\?(.*)\?=$/
53
+ from_charset = $1
54
+ quoting_method = $2
55
+ text = $3
56
+ case quoting_method.upcase
57
+ when "Q" then
58
+ unquote_quoted_printable_and_convert_to(text, to_charset, from_charset)
59
+ when "B" then
60
+ unquote_base64_and_convert_to(text, to_charset, from_charset)
61
+ else
62
+ raise "unknown quoting method #{quoting_method.inspect}"
63
+ end
64
+ else
65
+ convert_to(text, to_charset, from_charset)
66
+ end
71
67
  end
72
68
 
73
- def test_unquote_base64
74
- a ="=?ISO-8859-1?B?WzE2NjQxN10gQmVrcuZmdGVsc2UgZnJhIFJlanNlZmViZXI=?="
75
- b = TMail::Unquoter.unquote_and_convert_to(a, 'utf-8')
76
- assert_equal "[166417] Bekr\303\246ftelse fra Rejsefeber", b
69
+ def unquote_quoted_printable_and_convert_to(text, to, from)
70
+ convert_to(text.gsub(/_/," ").unpack("M*").first, to, from)
77
71
  end
78
72
 
79
- def test_unquote_without_charset
80
- a ="[166417]_Bekr=E6ftelse_fra_Rejsefeber"
81
- b = TMail::Unquoter.unquote_and_convert_to(a, 'utf-8')
82
- assert_equal "[166417] Bekr\303\246ftelse fra Rejsefeber", b
73
+ def unquote_base64_and_convert_to(text, to, from)
74
+ convert_to(Base64.decode(text).first, to, from)
75
+ end
76
+
77
+ begin
78
+ require 'iconv'
79
+ def convert_to(text, to, from)
80
+ return text unless to && from
81
+ text ? Iconv.iconv(to, from, text).first : ""
82
+ rescue Iconv::IllegalSequence, Errno::EINVAL
83
+ # the 'from' parameter specifies a charset other than what the text
84
+ # actually is...not much we can do in this case but just return the
85
+ # unconverted text.
86
+ #
87
+ # Ditto if either parameter represents an unknown charset, like
88
+ # X-UNKNOWN.
89
+ text
90
+ end
91
+ rescue LoadError
92
+ # Not providing quoting support
93
+ def convert_to(text, to, from)
94
+ warn "Action Mailer: iconv not loaded; ignoring conversion from #{from} to #{to} (#{__FILE__}:#{__LINE__})"
95
+ text
96
+ end
83
97
  end
84
98
  end
85
99
  end
86
- rescue LoadError => e
87
- # Not providing quoting support
88
- module TMail
89
- class Mail
90
- def subject
91
- warn "Action Mailer: iconv couldn't be required, so the charset conversion is skipped"
92
- quoted_subject
93
- end
94
-
95
- def body
96
- warn "Action Mailer: iconv couldn't be required, so the charset conversion is skipped"
97
- quoted_body
98
- end
100
+ end
101
+
102
+ if __FILE__ == $0
103
+ require 'test/unit'
104
+
105
+ class TC_Unquoter < Test::Unit::TestCase
106
+ def test_unquote_quoted_printable
107
+ a ="=?ISO-8859-1?Q?[166417]_Bekr=E6ftelse_fra_Rejsefeber?="
108
+ b = TMail::Unquoter.unquote_and_convert_to(a, 'utf-8')
109
+ assert_equal "[166417] Bekr\303\246ftelse fra Rejsefeber", b
110
+ end
111
+
112
+ def test_unquote_base64
113
+ a ="=?ISO-8859-1?B?WzE2NjQxN10gQmVrcuZmdGVsc2UgZnJhIFJlanNlZmViZXI=?="
114
+ b = TMail::Unquoter.unquote_and_convert_to(a, 'utf-8')
115
+ assert_equal "[166417] Bekr\303\246ftelse fra Rejsefeber", b
116
+ end
117
+
118
+ def test_unquote_without_charset
119
+ a ="[166417]_Bekr=E6ftelse_fra_Rejsefeber"
120
+ b = TMail::Unquoter.unquote_and_convert_to(a, 'utf-8')
121
+ assert_equal "[166417]_Bekr=E6ftelse_fra_Rejsefeber", b
99
122
  end
100
123
  end
101
124
  end