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.
- data/CHANGELOG +51 -2
- data/README +5 -6
- data/lib/action_mailer.rb +10 -3
- data/lib/action_mailer/adv_attr_accessor.rb +56 -0
- data/lib/action_mailer/base.rb +244 -120
- data/lib/action_mailer/helpers.rb +115 -0
- data/lib/action_mailer/part.rb +76 -0
- data/lib/action_mailer/part_container.rb +25 -0
- data/lib/action_mailer/quoting.rb +99 -0
- data/lib/action_mailer/utils.rb +8 -0
- data/lib/action_mailer/vendor/tmail/attachments.rb +6 -5
- data/lib/action_mailer/vendor/tmail/encode.rb +1 -0
- data/lib/action_mailer/vendor/tmail/facade.rb +2 -1
- data/lib/action_mailer/vendor/tmail/mail.rb +4 -0
- data/lib/action_mailer/vendor/tmail/net.rb +1 -1
- data/lib/action_mailer/vendor/tmail/quoting.rb +103 -80
- data/lib/action_mailer/vendor/tmail/scanner_r.rb +2 -2
- data/lib/action_mailer/vendor/tmail/utils.rb +9 -5
- data/rakefile +4 -4
- data/test/fixtures/helper_mailer/use_helper.rhtml +1 -0
- data/test/fixtures/helper_mailer/use_helper_method.rhtml +1 -0
- data/test/fixtures/helper_mailer/use_mail_helper.rhtml +5 -0
- data/test/fixtures/helper_mailer/use_test_helper.rhtml +1 -0
- data/test/fixtures/helpers/test_helper.rb +5 -0
- data/test/fixtures/raw_email +14 -0
- data/test/fixtures/raw_email10 +20 -0
- data/test/fixtures/raw_email11 +34 -0
- data/test/fixtures/raw_email2 +114 -0
- data/test/fixtures/raw_email3 +70 -0
- data/test/fixtures/raw_email4 +59 -0
- data/test/fixtures/raw_email5 +19 -0
- data/test/fixtures/raw_email6 +20 -0
- data/test/fixtures/raw_email7 +56 -0
- data/test/fixtures/raw_email8 +47 -0
- data/test/fixtures/raw_email9 +28 -0
- data/test/fixtures/test_mailer/implicitly_multipart_example.text.html.rhtml +10 -0
- data/test/fixtures/test_mailer/implicitly_multipart_example.text.plain.rhtml +2 -0
- data/test/fixtures/test_mailer/implicitly_multipart_example.text.yaml.rhtml +1 -0
- data/test/mail_helper_test.rb +97 -0
- data/test/mail_service_test.rb +391 -30
- 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
|
@@ -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
|
18
|
-
|
19
|
-
|
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
|
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
|
@@ -1,101 +1,124 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
27
|
-
end
|
21
|
+
quoted_body
|
28
22
|
end
|
29
23
|
end
|
30
24
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
-
|
39
|
+
attachment_presenter.call(header["name"] || "(unnamed)")
|
49
40
|
end
|
50
|
-
|
51
|
-
|
52
|
-
|
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
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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
|
74
|
-
|
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
|
80
|
-
|
81
|
-
|
82
|
-
|
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
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
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
|