mail 2.2.20 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of mail might be problematic. Click here for more details.
- data/CHANGELOG.rdoc +4 -5
- data/Dependencies.txt +3 -0
- data/Gemfile +29 -0
- data/lib/VERSION +2 -2
- data/lib/mail.rb +12 -7
- data/lib/mail/body.rb +4 -3
- data/lib/mail/core_extensions/nil.rb +2 -0
- data/lib/mail/core_extensions/object.rb +13 -0
- data/lib/mail/core_extensions/shellwords.rb +2 -0
- data/lib/mail/core_extensions/smtp.rb +1 -0
- data/lib/mail/core_extensions/string.rb +5 -1
- data/lib/mail/core_extensions/string/access.rb +104 -0
- data/lib/mail/core_extensions/string/multibyte.rb +78 -0
- data/lib/mail/fields/common/parameter_hash.rb +5 -5
- data/lib/mail/indifferent_hash.rb +146 -0
- data/lib/mail/message.rb +8 -28
- data/lib/mail/multibyte.rb +42 -0
- data/lib/mail/multibyte/chars.rb +474 -0
- data/lib/mail/multibyte/exceptions.rb +8 -0
- data/lib/mail/multibyte/unicode.rb +392 -0
- data/lib/mail/multibyte/utils.rb +60 -0
- data/lib/mail/network/retriever_methods/test_retriever.rb +8 -1
- data/lib/mail/parts_list.rb +1 -1
- data/lib/mail/version_specific/ruby_1_8.rb +14 -13
- data/lib/mail/version_specific/ruby_1_9.rb +10 -9
- metadata +210 -183
data/CHANGELOG.rdoc
CHANGED
@@ -1,10 +1,9 @@
|
|
1
|
-
== Tue
|
2
|
-
|
3
|
-
* Backport 2.4.2 2.4.4 fixes (fractious)
|
4
|
-
|
5
|
-
== Tue Apr 26 09:49:54 UTC 2011 Mikel Lindsaar <mikel@rubyx.com>
|
1
|
+
== Tue Apr 26 09:59:56 UTC 2011 Mikel Lindsaar <mikel@rubyx.com>
|
6
2
|
|
3
|
+
* Remove ActiveSupport from the dependencies, load Active Support if present, or use internals if not
|
4
|
+
* Created v2.2 branch for all 2.2 related commits
|
7
5
|
* Update activesupport require to use inflector - closes #217
|
6
|
+
* Version bump to 2.3 and gem release
|
8
7
|
|
9
8
|
== Tue Apr 26 06:18:19 UTC 2011 Mikel Lindsaar <mikel@rubyx.com>
|
10
9
|
|
data/Dependencies.txt
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
source :rubygems
|
2
|
+
|
3
|
+
gem "activesupport", ">= 2.3.6"
|
4
|
+
gem "tlsmail" if RUBY_VERSION <= '1.8.6'
|
5
|
+
gem "mime-types", "~> 1.16"
|
6
|
+
gem "treetop", "~> 1.4.8"
|
7
|
+
gem "i18n", ">= 0.4.0"
|
8
|
+
|
9
|
+
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
|
10
|
+
gem 'jruby-openssl'
|
11
|
+
end
|
12
|
+
|
13
|
+
group :test do
|
14
|
+
gem "ZenTest", "~> 4.4.0"
|
15
|
+
gem "rake", "~> 0.8.7"
|
16
|
+
gem "bundler"
|
17
|
+
gem "rspec", "~> 1.3.0"
|
18
|
+
gem "diff-lcs"
|
19
|
+
case
|
20
|
+
when defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
|
21
|
+
# Skip it
|
22
|
+
when RUBY_PLATFORM == 'java'
|
23
|
+
# Skip it
|
24
|
+
when RUBY_VERSION < '1.9'
|
25
|
+
gem "ruby-debug"
|
26
|
+
else
|
27
|
+
# Skip it
|
28
|
+
end
|
29
|
+
end
|
data/lib/VERSION
CHANGED
data/lib/mail.rb
CHANGED
@@ -4,12 +4,6 @@ module Mail # :doc:
|
|
4
4
|
require 'date'
|
5
5
|
require 'shellwords'
|
6
6
|
|
7
|
-
require 'active_support'
|
8
|
-
require 'active_support/core_ext/class/attribute_accessors'
|
9
|
-
require 'active_support/core_ext/hash/indifferent_access'
|
10
|
-
require 'active_support/core_ext/object/blank'
|
11
|
-
require 'active_support/inflector'
|
12
|
-
|
13
7
|
require 'uri'
|
14
8
|
require 'net/smtp'
|
15
9
|
require 'mime/types'
|
@@ -22,7 +16,7 @@ module Mail # :doc:
|
|
22
16
|
end
|
23
17
|
end
|
24
18
|
|
25
|
-
if RUBY_VERSION >= "1.9.
|
19
|
+
if RUBY_VERSION >= "1.9.0"
|
26
20
|
require 'mail/version_specific/ruby_1_9'
|
27
21
|
RubyVer = Ruby19
|
28
22
|
else
|
@@ -33,9 +27,20 @@ module Mail # :doc:
|
|
33
27
|
require 'mail/version'
|
34
28
|
|
35
29
|
require 'mail/core_extensions/nil'
|
30
|
+
require 'mail/core_extensions/object'
|
36
31
|
require 'mail/core_extensions/string'
|
37
32
|
require 'mail/core_extensions/shellwords' unless String.new.respond_to?(:shellescape)
|
38
33
|
require 'mail/core_extensions/smtp' if RUBY_VERSION < '1.9.3'
|
34
|
+
require 'mail/indifferent_hash'
|
35
|
+
|
36
|
+
# Only load our multibyte extensions if AS is not already loaded
|
37
|
+
if defined?(ActiveSupport)
|
38
|
+
require 'active_support/inflector'
|
39
|
+
else
|
40
|
+
require 'mail/core_extensions/string/access'
|
41
|
+
require 'mail/core_extensions/string/multibyte'
|
42
|
+
require 'mail/multibyte'
|
43
|
+
end
|
39
44
|
|
40
45
|
require 'mail/patterns'
|
41
46
|
require 'mail/utilities'
|
data/lib/mail/body.rb
CHANGED
@@ -201,10 +201,11 @@ module Mail
|
|
201
201
|
end
|
202
202
|
|
203
203
|
def encoding=( val )
|
204
|
-
if val == "text" || val.blank?
|
205
|
-
|
204
|
+
@encoding = if val == "text" || val.blank?
|
205
|
+
(only_us_ascii? ? '7bit' : '8bit')
|
206
|
+
else
|
207
|
+
val
|
206
208
|
end
|
207
|
-
@encoding = (val == "text") ? "8bit" : val
|
208
209
|
end
|
209
210
|
|
210
211
|
# Returns the preamble (any text that is before the first MIME boundary)
|
@@ -8,6 +8,10 @@ class String #:nodoc:
|
|
8
8
|
gsub(/\n|\r\n|\r/) { "\n" }
|
9
9
|
end
|
10
10
|
|
11
|
+
def blank?
|
12
|
+
self !~ /\S/
|
13
|
+
end
|
14
|
+
|
11
15
|
unless method_defined?(:ascii_only?)
|
12
16
|
# Provides all strings with the Ruby 1.9 method of .ascii_only? and
|
13
17
|
# returns true or false
|
@@ -16,7 +20,7 @@ class String #:nodoc:
|
|
16
20
|
!(self =~ /[^#{US_ASCII_REGEXP}]/)
|
17
21
|
end
|
18
22
|
end
|
19
|
-
|
23
|
+
|
20
24
|
def not_ascii_only?
|
21
25
|
!ascii_only?
|
22
26
|
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# This is not loaded if ActiveSupport is already loaded
|
4
|
+
|
5
|
+
# This is an almost cut and paste from ActiveSupport v3.0.6, copied in here so that Mail
|
6
|
+
# itself does not depend on ActiveSupport to avoid versioning conflicts
|
7
|
+
|
8
|
+
class String
|
9
|
+
unless '1.9'.respond_to?(:force_encoding)
|
10
|
+
# Returns the character at the +position+ treating the string as an array (where 0 is the first character).
|
11
|
+
#
|
12
|
+
# Examples:
|
13
|
+
# "hello".at(0) # => "h"
|
14
|
+
# "hello".at(4) # => "o"
|
15
|
+
# "hello".at(10) # => ERROR if < 1.9, nil in 1.9
|
16
|
+
def at(position)
|
17
|
+
mb_chars[position, 1].to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns the remaining of the string from the +position+ treating the string as an array (where 0 is the first character).
|
21
|
+
#
|
22
|
+
# Examples:
|
23
|
+
# "hello".from(0) # => "hello"
|
24
|
+
# "hello".from(2) # => "llo"
|
25
|
+
# "hello".from(10) # => "" if < 1.9, nil in 1.9
|
26
|
+
def from(position)
|
27
|
+
mb_chars[position..-1].to_s
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns the beginning of the string up to the +position+ treating the string as an array (where 0 is the first character).
|
31
|
+
#
|
32
|
+
# Examples:
|
33
|
+
# "hello".to(0) # => "h"
|
34
|
+
# "hello".to(2) # => "hel"
|
35
|
+
# "hello".to(10) # => "hello"
|
36
|
+
def to(position)
|
37
|
+
mb_chars[0..position].to_s
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns the first character of the string or the first +limit+ characters.
|
41
|
+
#
|
42
|
+
# Examples:
|
43
|
+
# "hello".first # => "h"
|
44
|
+
# "hello".first(2) # => "he"
|
45
|
+
# "hello".first(10) # => "hello"
|
46
|
+
def first(limit = 1)
|
47
|
+
if limit == 0
|
48
|
+
''
|
49
|
+
elsif limit >= size
|
50
|
+
self
|
51
|
+
else
|
52
|
+
mb_chars[0...limit].to_s
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns the last character of the string or the last +limit+ characters.
|
57
|
+
#
|
58
|
+
# Examples:
|
59
|
+
# "hello".last # => "o"
|
60
|
+
# "hello".last(2) # => "lo"
|
61
|
+
# "hello".last(10) # => "hello"
|
62
|
+
def last(limit = 1)
|
63
|
+
if limit == 0
|
64
|
+
''
|
65
|
+
elsif limit >= size
|
66
|
+
self
|
67
|
+
else
|
68
|
+
mb_chars[(-limit)..-1].to_s
|
69
|
+
end
|
70
|
+
end
|
71
|
+
else
|
72
|
+
def at(position)
|
73
|
+
self[position]
|
74
|
+
end
|
75
|
+
|
76
|
+
def from(position)
|
77
|
+
self[position..-1]
|
78
|
+
end
|
79
|
+
|
80
|
+
def to(position)
|
81
|
+
self[0..position]
|
82
|
+
end
|
83
|
+
|
84
|
+
def first(limit = 1)
|
85
|
+
if limit == 0
|
86
|
+
''
|
87
|
+
elsif limit >= size
|
88
|
+
self
|
89
|
+
else
|
90
|
+
to(limit - 1)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def last(limit = 1)
|
95
|
+
if limit == 0
|
96
|
+
''
|
97
|
+
elsif limit >= size
|
98
|
+
self
|
99
|
+
else
|
100
|
+
from(-limit)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# This is not loaded if ActiveSupport is already loaded
|
4
|
+
|
5
|
+
# This is an almost cut and paste from ActiveSupport v3.0.6, copied in here so that Mail
|
6
|
+
# itself does not depend on ActiveSupport to avoid versioning conflicts
|
7
|
+
|
8
|
+
require 'mail/multibyte'
|
9
|
+
|
10
|
+
class String
|
11
|
+
if RUBY_VERSION >= "1.9"
|
12
|
+
# == Multibyte proxy
|
13
|
+
#
|
14
|
+
# +mb_chars+ is a multibyte safe proxy for string methods.
|
15
|
+
#
|
16
|
+
# In Ruby 1.8 and older it creates and returns an instance of the Mail::Multibyte::Chars class which
|
17
|
+
# encapsulates the original string. A Unicode safe version of all the String methods are defined on this proxy
|
18
|
+
# class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsuled string.
|
19
|
+
#
|
20
|
+
# name = 'Claus Müller'
|
21
|
+
# name.reverse # => "rell??M sualC"
|
22
|
+
# name.length # => 13
|
23
|
+
#
|
24
|
+
# name.mb_chars.reverse.to_s # => "rellüM sualC"
|
25
|
+
# name.mb_chars.length # => 12
|
26
|
+
#
|
27
|
+
# In Ruby 1.9 and newer +mb_chars+ returns +self+ because String is (mostly) encoding aware. This means that
|
28
|
+
# it becomes easy to run one version of your code on multiple Ruby versions.
|
29
|
+
#
|
30
|
+
# == Method chaining
|
31
|
+
#
|
32
|
+
# All the methods on the Chars proxy which normally return a string will return a Chars object. This allows
|
33
|
+
# method chaining on the result of any of these methods.
|
34
|
+
#
|
35
|
+
# name.mb_chars.reverse.length # => 12
|
36
|
+
#
|
37
|
+
# == Interoperability and configuration
|
38
|
+
#
|
39
|
+
# The Chars object tries to be as interchangeable with String objects as possible: sorting and comparing between
|
40
|
+
# String and Char work like expected. The bang! methods change the internal string representation in the Chars
|
41
|
+
# object. Interoperability problems can be resolved easily with a +to_s+ call.
|
42
|
+
#
|
43
|
+
# For more information about the methods defined on the Chars proxy see Mail::Multibyte::Chars. For
|
44
|
+
# information about how to change the default Multibyte behaviour see Mail::Multibyte.
|
45
|
+
def mb_chars
|
46
|
+
if Mail::Multibyte.proxy_class.consumes?(self)
|
47
|
+
Mail::Multibyte.proxy_class.new(self)
|
48
|
+
else
|
49
|
+
self
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def is_utf8? #:nodoc
|
54
|
+
case encoding
|
55
|
+
when Encoding::UTF_8
|
56
|
+
valid_encoding?
|
57
|
+
when Encoding::ASCII_8BIT, Encoding::US_ASCII
|
58
|
+
dup.force_encoding(Encoding::UTF_8).valid_encoding?
|
59
|
+
else
|
60
|
+
false
|
61
|
+
end
|
62
|
+
end
|
63
|
+
else
|
64
|
+
def mb_chars
|
65
|
+
if Mail::Multibyte.proxy_class.wants?(self)
|
66
|
+
Mail::Multibyte.proxy_class.new(self)
|
67
|
+
else
|
68
|
+
self
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns true if the string has UTF-8 semantics (a String used for purely byte resources is unlikely to have
|
73
|
+
# them), returns false otherwise.
|
74
|
+
def is_utf8?
|
75
|
+
Mail::Multibyte::Chars.consumes?(self)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -8,7 +8,7 @@ module Mail
|
|
8
8
|
# to make that happen
|
9
9
|
# Parameters are defined in RFC2045, split keys are in RFC2231
|
10
10
|
|
11
|
-
class ParameterHash <
|
11
|
+
class ParameterHash < IndifferentHash
|
12
12
|
|
13
13
|
include Mail::Utilities
|
14
14
|
|
@@ -28,8 +28,8 @@ module Mail
|
|
28
28
|
if pairs.empty? # Just dealing with a single value pair
|
29
29
|
super(exact || key_name)
|
30
30
|
else # Dealing with a multiple value pair or a single encoded value pair
|
31
|
-
string = pairs.sort { |a,b| a.first <=> b.first }.map { |v| v.last }.join('')
|
32
|
-
if mt = string.match(/([\w\-]+)'(\w\w)'(.*)/)
|
31
|
+
string = pairs.sort { |a,b| a.first.to_s <=> b.first.to_s }.map { |v| v.last }.join('')
|
32
|
+
if mt = string.match(/([\w\d\-]+)'(\w\w)'(.*)/)
|
33
33
|
string = mt[3]
|
34
34
|
encoding = mt[1]
|
35
35
|
else
|
@@ -40,7 +40,7 @@ module Mail
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def encoded
|
43
|
-
map.sort { |a,b| a.first <=> b.first }.map do |key_name, value|
|
43
|
+
map.sort { |a,b| a.first.to_s <=> b.first.to_s }.map do |key_name, value|
|
44
44
|
unless value.ascii_only?
|
45
45
|
value = Mail::Encodings.param_encode(value)
|
46
46
|
key_name = "#{key_name}*"
|
@@ -50,7 +50,7 @@ module Mail
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def decoded
|
53
|
-
map.sort { |a,b| a.first <=> b.first }.map do |key_name, value|
|
53
|
+
map.sort { |a,b| a.first.to_s <=> b.first.to_s }.map do |key_name, value|
|
54
54
|
%Q{#{key_name}=#{quote_token(value)}}
|
55
55
|
end.join("; ")
|
56
56
|
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# This is an almost cut and paste from ActiveSupport v3.0.6, copied in here so that Mail
|
4
|
+
# itself does not depend on ActiveSupport to avoid versioning conflicts
|
5
|
+
|
6
|
+
module Mail
|
7
|
+
class IndifferentHash < Hash
|
8
|
+
|
9
|
+
def initialize(constructor = {})
|
10
|
+
if constructor.is_a?(Hash)
|
11
|
+
super()
|
12
|
+
update(constructor)
|
13
|
+
else
|
14
|
+
super(constructor)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def default(key = nil)
|
19
|
+
if key.is_a?(Symbol) && include?(key = key.to_s)
|
20
|
+
self[key]
|
21
|
+
else
|
22
|
+
super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.new_from_hash_copying_default(hash)
|
27
|
+
IndifferentHash.new(hash).tap do |new_hash|
|
28
|
+
new_hash.default = hash.default
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
|
33
|
+
alias_method :regular_update, :update unless method_defined?(:regular_update)
|
34
|
+
|
35
|
+
# Assigns a new value to the hash:
|
36
|
+
#
|
37
|
+
# hash = HashWithIndifferentAccess.new
|
38
|
+
# hash[:key] = "value"
|
39
|
+
#
|
40
|
+
def []=(key, value)
|
41
|
+
regular_writer(convert_key(key), convert_value(value))
|
42
|
+
end
|
43
|
+
|
44
|
+
alias_method :store, :[]=
|
45
|
+
|
46
|
+
# Updates the instantized hash with values from the second:
|
47
|
+
#
|
48
|
+
# hash_1 = HashWithIndifferentAccess.new
|
49
|
+
# hash_1[:key] = "value"
|
50
|
+
#
|
51
|
+
# hash_2 = HashWithIndifferentAccess.new
|
52
|
+
# hash_2[:key] = "New Value!"
|
53
|
+
#
|
54
|
+
# hash_1.update(hash_2) # => {"key"=>"New Value!"}
|
55
|
+
#
|
56
|
+
def update(other_hash)
|
57
|
+
other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
|
58
|
+
self
|
59
|
+
end
|
60
|
+
|
61
|
+
alias_method :merge!, :update
|
62
|
+
|
63
|
+
# Checks the hash for a key matching the argument passed in:
|
64
|
+
#
|
65
|
+
# hash = HashWithIndifferentAccess.new
|
66
|
+
# hash["key"] = "value"
|
67
|
+
# hash.key? :key # => true
|
68
|
+
# hash.key? "key" # => true
|
69
|
+
#
|
70
|
+
def key?(key)
|
71
|
+
super(convert_key(key))
|
72
|
+
end
|
73
|
+
|
74
|
+
alias_method :include?, :key?
|
75
|
+
alias_method :has_key?, :key?
|
76
|
+
alias_method :member?, :key?
|
77
|
+
|
78
|
+
# Fetches the value for the specified key, same as doing hash[key]
|
79
|
+
def fetch(key, *extras)
|
80
|
+
super(convert_key(key), *extras)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns an array of the values at the specified indices:
|
84
|
+
#
|
85
|
+
# hash = HashWithIndifferentAccess.new
|
86
|
+
# hash[:a] = "x"
|
87
|
+
# hash[:b] = "y"
|
88
|
+
# hash.values_at("a", "b") # => ["x", "y"]
|
89
|
+
#
|
90
|
+
def values_at(*indices)
|
91
|
+
indices.collect {|key| self[convert_key(key)]}
|
92
|
+
end
|
93
|
+
|
94
|
+
# Returns an exact copy of the hash.
|
95
|
+
def dup
|
96
|
+
IndifferentHash.new(self)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Merges the instantized and the specified hashes together, giving precedence to the values from the second hash
|
100
|
+
# Does not overwrite the existing hash.
|
101
|
+
def merge(hash)
|
102
|
+
self.dup.update(hash)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
|
106
|
+
# This overloaded definition prevents returning a regular hash, if reverse_merge is called on a HashWithDifferentAccess.
|
107
|
+
def reverse_merge(other_hash)
|
108
|
+
super self.class.new_from_hash_copying_default(other_hash)
|
109
|
+
end
|
110
|
+
|
111
|
+
def reverse_merge!(other_hash)
|
112
|
+
replace(reverse_merge( other_hash ))
|
113
|
+
end
|
114
|
+
|
115
|
+
# Removes a specified key from the hash.
|
116
|
+
def delete(key)
|
117
|
+
super(convert_key(key))
|
118
|
+
end
|
119
|
+
|
120
|
+
def stringify_keys!; self end
|
121
|
+
def stringify_keys; dup end
|
122
|
+
def symbolize_keys; to_hash.symbolize_keys end
|
123
|
+
def to_options!; self end
|
124
|
+
|
125
|
+
def to_hash
|
126
|
+
Hash.new(default).merge!(self)
|
127
|
+
end
|
128
|
+
|
129
|
+
protected
|
130
|
+
|
131
|
+
def convert_key(key)
|
132
|
+
key.kind_of?(Symbol) ? key.to_s : key
|
133
|
+
end
|
134
|
+
|
135
|
+
def convert_value(value)
|
136
|
+
if value.class == Hash
|
137
|
+
self.class.new_from_hash_copying_default(value)
|
138
|
+
elsif value.is_a?(Array)
|
139
|
+
value.dup.replace(value.map { |e| convert_value(e) })
|
140
|
+
else
|
141
|
+
value
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
end
|