mail 1.1.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of mail might be problematic. Click here for more details.
- data/CHANGELOG.rdoc +14 -1
- data/README.rdoc +37 -13
- data/lib/mail/configuration.rb +4 -0
- data/lib/mail/elements/address.rb +11 -0
- data/lib/mail/encodings/base64.rb +1 -0
- data/lib/mail/encodings/encodings.rb +133 -6
- data/lib/mail/encodings/quoted_printable.rb +1 -1
- data/lib/mail/field.rb +34 -34
- data/lib/mail/fields/bcc_field.rb +10 -1
- data/lib/mail/fields/cc_field.rb +10 -1
- data/lib/mail/fields/comments_field.rb +2 -1
- data/lib/mail/fields/common/common_address.rb +25 -1
- data/lib/mail/fields/common/common_date.rb +8 -5
- data/lib/mail/fields/common/common_field.rb +2 -74
- data/lib/mail/fields/common/common_message_id.rb +10 -0
- data/lib/mail/fields/common/parameter_hash.rb +10 -0
- data/lib/mail/fields/content_description_field.rb +3 -2
- data/lib/mail/fields/content_disposition_field.rb +12 -2
- data/lib/mail/fields/content_id_field.rb +12 -2
- data/lib/mail/fields/content_location_field.rb +12 -2
- data/lib/mail/fields/content_transfer_encoding_field.rb +12 -2
- data/lib/mail/fields/content_type_field.rb +26 -5
- data/lib/mail/fields/date_field.rb +11 -2
- data/lib/mail/fields/from_field.rb +10 -1
- data/lib/mail/fields/in_reply_to_field.rb +10 -1
- data/lib/mail/fields/keywords_field.rb +10 -1
- data/lib/mail/fields/message_id_field.rb +11 -2
- data/lib/mail/fields/mime_version_field.rb +11 -2
- data/lib/mail/fields/received_field.rb +10 -1
- data/lib/mail/fields/references_field.rb +10 -1
- data/lib/mail/fields/reply_to_field.rb +10 -1
- data/lib/mail/fields/resent_bcc_field.rb +10 -1
- data/lib/mail/fields/resent_cc_field.rb +10 -1
- data/lib/mail/fields/resent_date_field.rb +10 -1
- data/lib/mail/fields/resent_from_field.rb +10 -1
- data/lib/mail/fields/resent_message_id_field.rb +10 -1
- data/lib/mail/fields/resent_sender_field.rb +10 -1
- data/lib/mail/fields/resent_to_field.rb +10 -1
- data/lib/mail/fields/return_path_field.rb +10 -1
- data/lib/mail/fields/sender_field.rb +10 -1
- data/lib/mail/fields/subject_field.rb +2 -1
- data/lib/mail/fields/to_field.rb +10 -1
- data/lib/mail/fields/unstructured_field.rb +82 -0
- data/lib/mail/header.rb +2 -2
- data/lib/mail/message.rb +25 -9
- data/lib/mail/utilities.rb +7 -1
- data/lib/mail/version.rb +2 -2
- data/lib/mail/version_specific/ruby_1_8.rb +30 -10
- data/lib/mail/version_specific/ruby_1_9.rb +30 -8
- metadata +1 -1
data/lib/mail/fields/cc_field.rb
CHANGED
@@ -31,9 +31,18 @@ module Mail
|
|
31
31
|
include Mail::CommonAddress
|
32
32
|
|
33
33
|
FIELD_NAME = 'cc'
|
34
|
+
CAPITALIZED_FIELD = 'Cc'
|
34
35
|
|
35
36
|
def initialize(*args)
|
36
|
-
super(
|
37
|
+
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, args.last))
|
38
|
+
end
|
39
|
+
|
40
|
+
def encoded
|
41
|
+
do_encode(CAPITALIZED_FIELD)
|
42
|
+
end
|
43
|
+
|
44
|
+
def decoded
|
45
|
+
do_decode
|
37
46
|
end
|
38
47
|
|
39
48
|
end
|
@@ -32,9 +32,10 @@ module Mail
|
|
32
32
|
class CommentsField < UnstructuredField
|
33
33
|
|
34
34
|
FIELD_NAME = 'comments'
|
35
|
+
CAPITALIZED_FIELD = 'Comments'
|
35
36
|
|
36
37
|
def initialize(*args)
|
37
|
-
super(
|
38
|
+
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, args.last))
|
38
39
|
end
|
39
40
|
|
40
41
|
end
|
@@ -33,14 +33,38 @@ module Mail
|
|
33
33
|
end
|
34
34
|
@groups
|
35
35
|
end
|
36
|
+
|
37
|
+
# Returns the addresses that are part of groups
|
38
|
+
def group_addresses
|
39
|
+
groups.map { |k,v| v.map { |a| a.format } }.flatten
|
40
|
+
end
|
36
41
|
|
37
42
|
# Returns the name of all the groups in a string
|
38
43
|
def group_names # :nodoc:
|
39
44
|
tree.group_names
|
40
45
|
end
|
41
|
-
|
46
|
+
|
42
47
|
private
|
43
48
|
|
49
|
+
def do_encode(field_name)
|
50
|
+
return '' unless value
|
51
|
+
address_array = tree.addresses.reject { |a| group_addresses.include?(a.encoded) }.compact.map { |a| a.encoded }
|
52
|
+
address_text = address_array.join(", \r\n\t")
|
53
|
+
group_array = groups.map { |k,v| "#{k}: #{v.map { |a| a.encoded }.join(", \r\n\t")};" }
|
54
|
+
group_text = group_array.join(" \r\n\t")
|
55
|
+
return_array = [address_text, group_text].reject { |a| a.blank? }
|
56
|
+
"#{field_name}: #{return_array.join(", \r\n\t")}\r\n"
|
57
|
+
end
|
58
|
+
|
59
|
+
def do_decode
|
60
|
+
address_array = tree.addresses.reject { |a| group_addresses.include?(a.decoded) }.map { |a| a.decoded }
|
61
|
+
address_text = address_array.join(", ")
|
62
|
+
group_array = groups.map { |k,v| "#{k}: #{v.map { |a| a.decoded }.join(", ")};" }
|
63
|
+
group_text = group_array.join(" ")
|
64
|
+
return_array = [address_text, group_text].reject { |a| a.blank? }
|
65
|
+
return_array.join(", ")
|
66
|
+
end
|
67
|
+
|
44
68
|
# Returns the syntax tree of the Addresses
|
45
69
|
def tree # :nodoc:
|
46
70
|
@tree ||= AddressList.new(value)
|
@@ -2,10 +2,6 @@
|
|
2
2
|
module Mail
|
3
3
|
module CommonDate # :nodoc:
|
4
4
|
|
5
|
-
module ClassMethods # :nodoc:
|
6
|
-
|
7
|
-
end
|
8
|
-
|
9
5
|
module InstanceMethods # :doc:
|
10
6
|
|
11
7
|
# Returns a date time object of the parsed date
|
@@ -14,6 +10,14 @@ module Mail
|
|
14
10
|
end
|
15
11
|
|
16
12
|
private
|
13
|
+
|
14
|
+
def do_encode(field_name)
|
15
|
+
"#{field_name}: #{value}\r\n"
|
16
|
+
end
|
17
|
+
|
18
|
+
def do_decode
|
19
|
+
"#{value}"
|
20
|
+
end
|
17
21
|
|
18
22
|
def element
|
19
23
|
@element ||= Mail::DateTimeElement.new(value)
|
@@ -27,7 +31,6 @@ module Mail
|
|
27
31
|
end
|
28
32
|
|
29
33
|
def self.included(receiver) # :nodoc:
|
30
|
-
receiver.extend ClassMethods
|
31
34
|
receiver.send :include, InstanceMethods
|
32
35
|
end
|
33
36
|
|
@@ -9,7 +9,7 @@ module Mail
|
|
9
9
|
module InstanceMethods # :doc:
|
10
10
|
|
11
11
|
def name=(value)
|
12
|
-
@name =
|
12
|
+
@name = value
|
13
13
|
end
|
14
14
|
|
15
15
|
def name
|
@@ -27,16 +27,8 @@ module Mail
|
|
27
27
|
@value
|
28
28
|
end
|
29
29
|
|
30
|
-
def encoded
|
31
|
-
value.blank? ? nil : "#{wrapped_value}\r\n"
|
32
|
-
end
|
33
|
-
|
34
|
-
def decoded
|
35
|
-
value.blank? ? nil : "#{name}: #{value}\r\n"
|
36
|
-
end
|
37
|
-
|
38
30
|
def to_s
|
39
|
-
|
31
|
+
encoded
|
40
32
|
end
|
41
33
|
|
42
34
|
def field_length
|
@@ -53,70 +45,6 @@ module Mail
|
|
53
45
|
string.to_s.gsub(/#{field_name}:\s+/i, '')
|
54
46
|
end
|
55
47
|
|
56
|
-
|
57
|
-
# 2.2.3. Long Header Fields
|
58
|
-
#
|
59
|
-
# Each header field is logically a single line of characters comprising
|
60
|
-
# the field name, the colon, and the field body. For convenience
|
61
|
-
# however, and to deal with the 998/78 character limitations per line,
|
62
|
-
# the field body portion of a header field can be split into a multiple
|
63
|
-
# line representation; this is called "folding". The general rule is
|
64
|
-
# that wherever this standard allows for folding white space (not
|
65
|
-
# simply WSP characters), a CRLF may be inserted before any WSP. For
|
66
|
-
# example, the header field:
|
67
|
-
#
|
68
|
-
# Subject: This is a test
|
69
|
-
#
|
70
|
-
# can be represented as:
|
71
|
-
#
|
72
|
-
# Subject: This
|
73
|
-
# is a test
|
74
|
-
#
|
75
|
-
# Note: Though structured field bodies are defined in such a way that
|
76
|
-
# folding can take place between many of the lexical tokens (and even
|
77
|
-
# within some of the lexical tokens), folding SHOULD be limited to
|
78
|
-
# placing the CRLF at higher-level syntactic breaks. For instance, if
|
79
|
-
# a field body is defined as comma-separated values, it is recommended
|
80
|
-
# that folding occur after the comma separating the structured items in
|
81
|
-
# preference to other places where the field could be folded, even if
|
82
|
-
# it is allowed elsewhere.
|
83
|
-
def wrapped_value # :nodoc:
|
84
|
-
case
|
85
|
-
when decoded.ascii_only? && field_length <= 78
|
86
|
-
"#{name}: #{value}"
|
87
|
-
when field_length <= 40 # Allow for =?ISO-8859-1?B?=...=
|
88
|
-
"#{name}: #{encode(value)}"
|
89
|
-
else
|
90
|
-
@folded_line = []
|
91
|
-
@unfolded_line = value.clone
|
92
|
-
fold("#{name}: ".length)
|
93
|
-
folded = @folded_line.compact.join("\r\n\t")
|
94
|
-
"#{name}: #{folded}"
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
def fold(prepend = 0) # :nodoc:
|
99
|
-
# Get the last whitespace character, OR we'll just choose
|
100
|
-
# 78 if there is no whitespace
|
101
|
-
@unfolded_line.ascii_only? ? (limit = 78 - prepend) : (limit = 40 - prepend)
|
102
|
-
wspp = @unfolded_line.slice(0..limit) =~ /[ \t][^ \T]*$/ || limit
|
103
|
-
wspp = limit if wspp == 0
|
104
|
-
@folded_line << encode(@unfolded_line.slice!(0...wspp))
|
105
|
-
if @unfolded_line.length > limit
|
106
|
-
fold
|
107
|
-
else
|
108
|
-
@folded_line << encode(@unfolded_line)
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
def encode(value)
|
113
|
-
if RUBY_VERSION < '1.9'
|
114
|
-
Encodings.b_encode(value, $KCODE)
|
115
|
-
else
|
116
|
-
Encodings.b_encode(value, @value.encoding)
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
48
|
end
|
121
49
|
|
122
50
|
def self.included(receiver) # :nodoc:
|
@@ -24,6 +24,16 @@ module Mail
|
|
24
24
|
element.message_ids
|
25
25
|
end
|
26
26
|
|
27
|
+
private
|
28
|
+
|
29
|
+
def do_encode(field_name)
|
30
|
+
%Q{#{field_name}: #{message_ids.map { |m| "<#{m}>" }.join(', ')}\r\n}
|
31
|
+
end
|
32
|
+
|
33
|
+
def do_decode
|
34
|
+
"#{message_ids.map { |m| "<#{m}>" }.join(', ')}"
|
35
|
+
end
|
36
|
+
|
27
37
|
end
|
28
38
|
|
29
39
|
def self.included(receiver) # :nodoc:
|
@@ -27,5 +27,15 @@ module Mail
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
+
def encoded
|
31
|
+
map.sort { |a,b| a.first <=> b.first }.map do |key_name, value|
|
32
|
+
unless value.ascii_only?
|
33
|
+
value = Mail::Encodings.param_encode(value)
|
34
|
+
key_name = "#{key_name}*"
|
35
|
+
end
|
36
|
+
%Q{#{key_name}="#{value}"}
|
37
|
+
end.join(";\r\n\t")
|
38
|
+
end
|
39
|
+
|
30
40
|
end
|
31
41
|
end
|
@@ -6,9 +6,10 @@ module Mail
|
|
6
6
|
class ContentDescriptionField < UnstructuredField
|
7
7
|
|
8
8
|
FIELD_NAME = 'content-description'
|
9
|
-
|
9
|
+
CAPITALIZED_FIELD = 'Content-Description'
|
10
|
+
|
10
11
|
def initialize(*args)
|
11
|
-
super(
|
12
|
+
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, args.last))
|
12
13
|
end
|
13
14
|
|
14
15
|
end
|
@@ -6,9 +6,10 @@ module Mail
|
|
6
6
|
class ContentDispositionField < StructuredField
|
7
7
|
|
8
8
|
FIELD_NAME = 'content-disposition'
|
9
|
-
|
9
|
+
CAPITALIZED_FIELD = 'Content-Disposition'
|
10
|
+
|
10
11
|
def initialize(*args)
|
11
|
-
super(
|
12
|
+
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, args.last))
|
12
13
|
end
|
13
14
|
|
14
15
|
def tree
|
@@ -42,5 +43,14 @@ module Mail
|
|
42
43
|
@filename
|
43
44
|
end
|
44
45
|
|
46
|
+
# TODO: Fix this up
|
47
|
+
def encoded
|
48
|
+
"#{CAPITALIZED_FIELD}: #{value}\r\n"
|
49
|
+
end
|
50
|
+
|
51
|
+
def decoded
|
52
|
+
value
|
53
|
+
end
|
54
|
+
|
45
55
|
end
|
46
56
|
end
|
@@ -6,15 +6,16 @@ module Mail
|
|
6
6
|
class ContentIdField < StructuredField
|
7
7
|
|
8
8
|
FIELD_NAME = 'content-id'
|
9
|
+
CAPITALIZED_FIELD = "Content-ID"
|
9
10
|
|
10
11
|
def initialize(*args)
|
11
12
|
@uniq = 1
|
12
13
|
if args.last.blank?
|
13
|
-
self.name =
|
14
|
+
self.name = CAPITALIZED_FIELD
|
14
15
|
self.value = generate_content_id
|
15
16
|
self
|
16
17
|
else
|
17
|
-
super(
|
18
|
+
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, args.last))
|
18
19
|
end
|
19
20
|
end
|
20
21
|
|
@@ -39,6 +40,15 @@ module Mail
|
|
39
40
|
"<#{content_id}>"
|
40
41
|
end
|
41
42
|
|
43
|
+
# TODO: Fix this up
|
44
|
+
def encoded
|
45
|
+
"#{CAPITALIZED_FIELD}: #{to_s}\r\n"
|
46
|
+
end
|
47
|
+
|
48
|
+
def decoded
|
49
|
+
"#{to_s}"
|
50
|
+
end
|
51
|
+
|
42
52
|
private
|
43
53
|
|
44
54
|
def generate_content_id
|
@@ -6,9 +6,10 @@ module Mail
|
|
6
6
|
class ContentLocationField < StructuredField
|
7
7
|
|
8
8
|
FIELD_NAME = 'content-location'
|
9
|
-
|
9
|
+
CAPITALIZED_FIELD = 'Content-Location'
|
10
|
+
|
10
11
|
def initialize(*args)
|
11
|
-
super(
|
12
|
+
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, args.last))
|
12
13
|
end
|
13
14
|
|
14
15
|
def tree
|
@@ -24,5 +25,14 @@ module Mail
|
|
24
25
|
element.location
|
25
26
|
end
|
26
27
|
|
28
|
+
# TODO: Fix this up
|
29
|
+
def encoded
|
30
|
+
"#{CAPITALIZED_FIELD}: #{value}\r\n"
|
31
|
+
end
|
32
|
+
|
33
|
+
def decoded
|
34
|
+
value
|
35
|
+
end
|
36
|
+
|
27
37
|
end
|
28
38
|
end
|
@@ -6,9 +6,10 @@ module Mail
|
|
6
6
|
class ContentTransferEncodingField < StructuredField
|
7
7
|
|
8
8
|
FIELD_NAME = 'content-transfer-encoding'
|
9
|
-
|
9
|
+
CAPITALIZED_FIELD = 'Content-Transfer-Encoding'
|
10
|
+
|
10
11
|
def initialize(*args)
|
11
|
-
super(
|
12
|
+
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, args.last.to_s.downcase))
|
12
13
|
end
|
13
14
|
|
14
15
|
def tree
|
@@ -24,5 +25,14 @@ module Mail
|
|
24
25
|
element.encoding
|
25
26
|
end
|
26
27
|
|
28
|
+
# TODO: Fix this up
|
29
|
+
def encoded
|
30
|
+
"#{CAPITALIZED_FIELD}: #{value}\r\n"
|
31
|
+
end
|
32
|
+
|
33
|
+
def decoded
|
34
|
+
value
|
35
|
+
end
|
36
|
+
|
27
37
|
end
|
28
38
|
end
|
@@ -6,18 +6,19 @@ module Mail
|
|
6
6
|
class ContentTypeField < StructuredField
|
7
7
|
|
8
8
|
FIELD_NAME = 'content-type'
|
9
|
-
|
9
|
+
CAPITALIZED_FIELD = 'Content-Type'
|
10
|
+
|
10
11
|
def initialize(*args)
|
11
12
|
if args.last.class == Array
|
12
13
|
@main_type = args.last[0]
|
13
14
|
@sub_type = args.last[1]
|
14
|
-
@parameters = args.last.last
|
15
|
-
super(
|
15
|
+
@parameters = ParameterHash.new.merge!(args.last.last)
|
16
|
+
super(CAPITALIZED_FIELD, args.last)
|
16
17
|
else
|
17
18
|
@main_type = nil
|
18
19
|
@sub_type = nil
|
19
20
|
@parameters = nil
|
20
|
-
super(
|
21
|
+
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, args.last))
|
21
22
|
end
|
22
23
|
end
|
23
24
|
|
@@ -67,7 +68,7 @@ module Mail
|
|
67
68
|
end
|
68
69
|
|
69
70
|
def stringify(params)
|
70
|
-
params.map { |k,v| "#{k}=#{v}" }.join("; ")
|
71
|
+
params.map { |k,v| "#{k}=#{Encodings.param_encode(v)}" }.join("; ")
|
71
72
|
end
|
72
73
|
|
73
74
|
def filename
|
@@ -81,6 +82,26 @@ module Mail
|
|
81
82
|
end
|
82
83
|
@filename
|
83
84
|
end
|
85
|
+
|
86
|
+
# TODO: Fix this up
|
87
|
+
def encoded
|
88
|
+
"#{CAPITALIZED_FIELD}: #{content_type};\r\n\t#{parameters.encoded};\r\n"
|
89
|
+
end
|
90
|
+
|
91
|
+
def decoded
|
92
|
+
value
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def method_missing(name, *args, &block)
|
98
|
+
if name.to_s =~ /([\w_]+)=/
|
99
|
+
self.parameters[$1] = args.first
|
100
|
+
@value = "#{content_type}; #{stringify(parameters)}"
|
101
|
+
else
|
102
|
+
super
|
103
|
+
end
|
104
|
+
end
|
84
105
|
|
85
106
|
end
|
86
107
|
end
|
@@ -29,16 +29,25 @@ module Mail
|
|
29
29
|
include Mail::CommonDate
|
30
30
|
|
31
31
|
FIELD_NAME = 'date'
|
32
|
+
CAPITALIZED_FIELD = "Date"
|
32
33
|
|
33
34
|
def initialize(*args)
|
34
35
|
if args.last.blank?
|
35
|
-
self.name =
|
36
|
+
self.name = CAPITALIZED_FIELD
|
36
37
|
self.value = Time.now.strftime('%a, %d %b %Y %H:%M:%S %z')
|
37
38
|
self
|
38
39
|
else
|
39
|
-
super(
|
40
|
+
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, args.last))
|
40
41
|
end
|
41
42
|
end
|
42
43
|
|
44
|
+
def encoded
|
45
|
+
do_encode(CAPITALIZED_FIELD)
|
46
|
+
end
|
47
|
+
|
48
|
+
def decoded
|
49
|
+
do_decode
|
50
|
+
end
|
51
|
+
|
43
52
|
end
|
44
53
|
end
|