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.

Files changed (50) hide show
  1. data/CHANGELOG.rdoc +14 -1
  2. data/README.rdoc +37 -13
  3. data/lib/mail/configuration.rb +4 -0
  4. data/lib/mail/elements/address.rb +11 -0
  5. data/lib/mail/encodings/base64.rb +1 -0
  6. data/lib/mail/encodings/encodings.rb +133 -6
  7. data/lib/mail/encodings/quoted_printable.rb +1 -1
  8. data/lib/mail/field.rb +34 -34
  9. data/lib/mail/fields/bcc_field.rb +10 -1
  10. data/lib/mail/fields/cc_field.rb +10 -1
  11. data/lib/mail/fields/comments_field.rb +2 -1
  12. data/lib/mail/fields/common/common_address.rb +25 -1
  13. data/lib/mail/fields/common/common_date.rb +8 -5
  14. data/lib/mail/fields/common/common_field.rb +2 -74
  15. data/lib/mail/fields/common/common_message_id.rb +10 -0
  16. data/lib/mail/fields/common/parameter_hash.rb +10 -0
  17. data/lib/mail/fields/content_description_field.rb +3 -2
  18. data/lib/mail/fields/content_disposition_field.rb +12 -2
  19. data/lib/mail/fields/content_id_field.rb +12 -2
  20. data/lib/mail/fields/content_location_field.rb +12 -2
  21. data/lib/mail/fields/content_transfer_encoding_field.rb +12 -2
  22. data/lib/mail/fields/content_type_field.rb +26 -5
  23. data/lib/mail/fields/date_field.rb +11 -2
  24. data/lib/mail/fields/from_field.rb +10 -1
  25. data/lib/mail/fields/in_reply_to_field.rb +10 -1
  26. data/lib/mail/fields/keywords_field.rb +10 -1
  27. data/lib/mail/fields/message_id_field.rb +11 -2
  28. data/lib/mail/fields/mime_version_field.rb +11 -2
  29. data/lib/mail/fields/received_field.rb +10 -1
  30. data/lib/mail/fields/references_field.rb +10 -1
  31. data/lib/mail/fields/reply_to_field.rb +10 -1
  32. data/lib/mail/fields/resent_bcc_field.rb +10 -1
  33. data/lib/mail/fields/resent_cc_field.rb +10 -1
  34. data/lib/mail/fields/resent_date_field.rb +10 -1
  35. data/lib/mail/fields/resent_from_field.rb +10 -1
  36. data/lib/mail/fields/resent_message_id_field.rb +10 -1
  37. data/lib/mail/fields/resent_sender_field.rb +10 -1
  38. data/lib/mail/fields/resent_to_field.rb +10 -1
  39. data/lib/mail/fields/return_path_field.rb +10 -1
  40. data/lib/mail/fields/sender_field.rb +10 -1
  41. data/lib/mail/fields/subject_field.rb +2 -1
  42. data/lib/mail/fields/to_field.rb +10 -1
  43. data/lib/mail/fields/unstructured_field.rb +82 -0
  44. data/lib/mail/header.rb +2 -2
  45. data/lib/mail/message.rb +25 -9
  46. data/lib/mail/utilities.rb +7 -1
  47. data/lib/mail/version.rb +2 -2
  48. data/lib/mail/version_specific/ruby_1_8.rb +30 -10
  49. data/lib/mail/version_specific/ruby_1_9.rb +30 -8
  50. metadata +1 -1
@@ -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(FIELD_NAME, strip_field(FIELD_NAME, args.last))
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(FIELD_NAME, strip_field(FIELD_NAME, args.last))
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 = capitalize_field(value)
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
- decoded.to_s
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(FIELD_NAME, strip_field(FIELD_NAME, args.last))
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(FIELD_NAME, strip_field(FIELD_NAME, args.last))
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 = FIELD_NAME
14
+ self.name = CAPITALIZED_FIELD
14
15
  self.value = generate_content_id
15
16
  self
16
17
  else
17
- super(FIELD_NAME, strip_field(FIELD_NAME, args.last))
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(FIELD_NAME, strip_field(FIELD_NAME, args.last))
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(FIELD_NAME, strip_field(FIELD_NAME, args.last.to_s.downcase))
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(FIELD_NAME, args.last)
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(FIELD_NAME, strip_field(FIELD_NAME, args.last))
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 = FIELD_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(FIELD_NAME, strip_field(FIELD_NAME, args.last))
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