mail 2.4.4 → 2.6.3

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.
Files changed (163) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.rdoc +156 -0
  3. data/CONTRIBUTING.md +21 -6
  4. data/Dependencies.txt +0 -1
  5. data/Gemfile +10 -21
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +52 -32
  8. data/Rakefile +11 -22
  9. data/lib/mail/attachments_list.rb +2 -2
  10. data/lib/mail/body.rb +33 -9
  11. data/lib/mail/check_delivery_params.rb +20 -0
  12. data/lib/mail/configuration.rb +1 -1
  13. data/lib/mail/{patterns.rb → constants.rb} +26 -6
  14. data/lib/mail/core_extensions/nil.rb +4 -2
  15. data/lib/mail/core_extensions/object.rb +8 -8
  16. data/lib/mail/core_extensions/smtp.rb +12 -13
  17. data/lib/mail/core_extensions/string.rb +19 -6
  18. data/lib/mail/elements/address.rb +82 -141
  19. data/lib/mail/elements/address_list.rb +9 -42
  20. data/lib/mail/elements/content_disposition_element.rb +3 -7
  21. data/lib/mail/elements/content_location_element.rb +2 -6
  22. data/lib/mail/elements/content_transfer_encoding_element.rb +3 -10
  23. data/lib/mail/elements/content_type_element.rb +4 -8
  24. data/lib/mail/elements/date_time_element.rb +3 -7
  25. data/lib/mail/elements/envelope_from_element.rb +18 -13
  26. data/lib/mail/elements/message_ids_element.rb +1 -6
  27. data/lib/mail/elements/mime_version_element.rb +3 -7
  28. data/lib/mail/elements/phrase_list.rb +2 -7
  29. data/lib/mail/elements/received_element.rb +3 -7
  30. data/lib/mail/elements.rb +12 -12
  31. data/lib/mail/encodings/quoted_printable.rb +4 -3
  32. data/lib/mail/encodings/unix_to_unix.rb +17 -0
  33. data/lib/mail/encodings.rb +53 -38
  34. data/lib/mail/envelope.rb +0 -5
  35. data/lib/mail/field.rb +131 -118
  36. data/lib/mail/field_list.rb +18 -18
  37. data/lib/mail/fields/bcc_field.rb +2 -2
  38. data/lib/mail/fields/cc_field.rb +2 -2
  39. data/lib/mail/fields/comments_field.rb +1 -1
  40. data/lib/mail/fields/common/common_address.rb +32 -22
  41. data/lib/mail/fields/common/common_date.rb +0 -7
  42. data/lib/mail/fields/common/common_field.rb +10 -3
  43. data/lib/mail/fields/common/common_message_id.rb +9 -5
  44. data/lib/mail/fields/common/parameter_hash.rb +3 -3
  45. data/lib/mail/fields/content_disposition_field.rb +1 -0
  46. data/lib/mail/fields/content_id_field.rb +1 -2
  47. data/lib/mail/fields/content_transfer_encoding_field.rb +2 -8
  48. data/lib/mail/fields/content_type_field.rb +5 -2
  49. data/lib/mail/fields/date_field.rb +14 -14
  50. data/lib/mail/fields/from_field.rb +2 -2
  51. data/lib/mail/fields/in_reply_to_field.rb +2 -1
  52. data/lib/mail/fields/keywords_field.rb +1 -1
  53. data/lib/mail/fields/message_id_field.rb +2 -3
  54. data/lib/mail/fields/references_field.rb +2 -1
  55. data/lib/mail/fields/reply_to_field.rb +2 -2
  56. data/lib/mail/fields/resent_bcc_field.rb +2 -2
  57. data/lib/mail/fields/resent_cc_field.rb +2 -2
  58. data/lib/mail/fields/resent_from_field.rb +2 -2
  59. data/lib/mail/fields/resent_sender_field.rb +3 -3
  60. data/lib/mail/fields/resent_to_field.rb +2 -2
  61. data/lib/mail/fields/sender_field.rb +8 -8
  62. data/lib/mail/fields/to_field.rb +2 -2
  63. data/lib/mail/fields/unstructured_field.rb +44 -31
  64. data/lib/mail/fields.rb +32 -32
  65. data/lib/mail/header.rb +47 -37
  66. data/lib/mail/mail.rb +15 -1
  67. data/lib/mail/matchers/has_sent_mail.rb +36 -3
  68. data/lib/mail/message.rb +164 -57
  69. data/lib/mail/multibyte/chars.rb +4 -4
  70. data/lib/mail/multibyte/unicode.rb +9 -1
  71. data/lib/mail/network/delivery_methods/exim.rb +2 -3
  72. data/lib/mail/network/delivery_methods/file_delivery.rb +5 -0
  73. data/lib/mail/network/delivery_methods/sendmail.rb +35 -8
  74. data/lib/mail/network/delivery_methods/smtp.rb +34 -45
  75. data/lib/mail/network/delivery_methods/smtp_connection.rb +9 -22
  76. data/lib/mail/network/delivery_methods/test_mailer.rb +6 -2
  77. data/lib/mail/network/retriever_methods/imap.rb +32 -19
  78. data/lib/mail/network/retriever_methods/pop3.rb +2 -2
  79. data/lib/mail/network/retriever_methods/test_retriever.rb +11 -15
  80. data/lib/mail/network.rb +9 -9
  81. data/lib/mail/parsers/address_lists_parser.rb +132 -0
  82. data/lib/mail/parsers/content_disposition_parser.rb +67 -0
  83. data/lib/mail/parsers/content_location_parser.rb +35 -0
  84. data/lib/mail/parsers/content_transfer_encoding_parser.rb +33 -0
  85. data/lib/mail/parsers/content_type_parser.rb +66 -0
  86. data/lib/mail/parsers/date_time_parser.rb +36 -0
  87. data/lib/mail/parsers/envelope_from_parser.rb +45 -0
  88. data/lib/mail/parsers/message_ids_parser.rb +39 -0
  89. data/lib/mail/parsers/mime_version_parser.rb +41 -0
  90. data/lib/mail/parsers/phrase_lists_parser.rb +33 -0
  91. data/lib/mail/parsers/ragel/common.rl +185 -0
  92. data/lib/mail/parsers/ragel/date_time.rl +30 -0
  93. data/lib/mail/parsers/ragel/parser_info.rb +61 -0
  94. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +14864 -0
  95. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +37 -0
  96. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +751 -0
  97. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +37 -0
  98. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +614 -0
  99. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +37 -0
  100. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +447 -0
  101. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +37 -0
  102. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +825 -0
  103. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +37 -0
  104. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +817 -0
  105. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +37 -0
  106. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +2149 -0
  107. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +37 -0
  108. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +1570 -0
  109. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +37 -0
  110. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +440 -0
  111. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +37 -0
  112. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +564 -0
  113. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +37 -0
  114. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +51 -0
  115. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +5144 -0
  116. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +37 -0
  117. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +37 -0
  118. data/lib/mail/parsers/ragel/ruby.rb +40 -0
  119. data/lib/mail/parsers/ragel.rb +17 -0
  120. data/lib/mail/parsers/received_parser.rb +47 -0
  121. data/lib/mail/parsers.rb +26 -0
  122. data/lib/mail/part.rb +6 -2
  123. data/lib/mail/parts_list.rb +5 -3
  124. data/lib/mail/utilities.rb +70 -60
  125. data/lib/mail/values/unicode_tables.dat +0 -0
  126. data/lib/mail/version.rb +7 -15
  127. data/lib/mail/version_specific/ruby_1_8.rb +25 -4
  128. data/lib/mail/version_specific/ruby_1_9.rb +110 -30
  129. data/lib/mail.rb +20 -19
  130. metadata +129 -73
  131. data/Gemfile.lock +0 -36
  132. data/lib/VERSION +0 -4
  133. data/lib/mail/core_extensions/shell_escape.rb +0 -56
  134. data/lib/mail/parsers/address_lists.rb +0 -64
  135. data/lib/mail/parsers/address_lists.treetop +0 -19
  136. data/lib/mail/parsers/content_disposition.rb +0 -535
  137. data/lib/mail/parsers/content_disposition.treetop +0 -46
  138. data/lib/mail/parsers/content_location.rb +0 -139
  139. data/lib/mail/parsers/content_location.treetop +0 -20
  140. data/lib/mail/parsers/content_transfer_encoding.rb +0 -162
  141. data/lib/mail/parsers/content_transfer_encoding.treetop +0 -20
  142. data/lib/mail/parsers/content_type.rb +0 -967
  143. data/lib/mail/parsers/content_type.treetop +0 -68
  144. data/lib/mail/parsers/date_time.rb +0 -114
  145. data/lib/mail/parsers/date_time.treetop +0 -11
  146. data/lib/mail/parsers/envelope_from.rb +0 -194
  147. data/lib/mail/parsers/envelope_from.treetop +0 -32
  148. data/lib/mail/parsers/message_ids.rb +0 -45
  149. data/lib/mail/parsers/message_ids.treetop +0 -15
  150. data/lib/mail/parsers/mime_version.rb +0 -144
  151. data/lib/mail/parsers/mime_version.treetop +0 -19
  152. data/lib/mail/parsers/phrase_lists.rb +0 -45
  153. data/lib/mail/parsers/phrase_lists.treetop +0 -15
  154. data/lib/mail/parsers/received.rb +0 -71
  155. data/lib/mail/parsers/received.treetop +0 -11
  156. data/lib/mail/parsers/rfc2045.rb +0 -464
  157. data/lib/mail/parsers/rfc2045.treetop +0 -36
  158. data/lib/mail/parsers/rfc2822.rb +0 -5341
  159. data/lib/mail/parsers/rfc2822.treetop +0 -410
  160. data/lib/mail/parsers/rfc2822_obsolete.rb +0 -3768
  161. data/lib/mail/parsers/rfc2822_obsolete.treetop +0 -241
  162. data/lib/tasks/corpus.rake +0 -125
  163. data/lib/tasks/treetop.rake +0 -10
@@ -0,0 +1,37 @@
1
+ %%{
2
+ machine received;
3
+
4
+ include rb_actions "rb_actions.rl";
5
+ include common "../../common.rl";
6
+
7
+ getkey data_unpacked[p];
8
+
9
+ main := received;
10
+ }%%
11
+
12
+ module Mail
13
+ module Parsers
14
+ module Ragel
15
+ module ReceivedMachine
16
+ %%write data;
17
+
18
+ def self.parse(data)
19
+ p = 0
20
+ eof = data.length
21
+ stack = []
22
+
23
+ actions = []
24
+ data_unpacked = data.bytes.to_a
25
+ %%write init;
26
+ %%write exec;
27
+
28
+ if p == eof && cs >= %%{ write first_final; }%%
29
+ return actions, nil
30
+ else
31
+ return [], "Only able to parse up to #{data[0..p]}"
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,37 @@
1
+ %%{
2
+ machine <%= parser_name %>;
3
+
4
+ include rb_actions "rb_actions.rl";
5
+ include common "../../common.rl";
6
+
7
+ getkey data_unpacked[p];
8
+
9
+ main := <%= parser_name %>;
10
+ }%%
11
+
12
+ module Mail
13
+ module Parsers
14
+ module Ragel
15
+ module <%= parser_name_cc %>
16
+ %%write data;
17
+
18
+ def self.parse(data)
19
+ p = 0
20
+ eof = data.length
21
+ stack = []
22
+
23
+ actions = []
24
+ data_unpacked = data.bytes.to_a
25
+ %%write init;
26
+ %%write exec;
27
+
28
+ if p == eof && cs >= %%{ write first_final; }%%
29
+ return actions, nil
30
+ else
31
+ return [], "Only able to parse up to #{data[0..p]}"
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,40 @@
1
+ module Mail
2
+ module Parsers
3
+ module Ragel
4
+ module Ruby
5
+ def self.silence_warnings
6
+ old, $VERBOSE = $VERBOSE, nil
7
+ yield
8
+ ensure
9
+ $VERBOSE = old
10
+ end
11
+
12
+ # Ragel-generated parsers give a lot of warnings
13
+ # and may cause logs to balloon in size
14
+ silence_warnings do
15
+ Mail::Parsers::Ragel::FIELD_PARSERS.each do |field_parser|
16
+ require "mail/parsers/ragel/ruby/machines/#{field_parser}_machine"
17
+ end
18
+ end
19
+
20
+ MACHINE_LIST = {
21
+ :address_lists => AddressListsMachine,
22
+ :phrase_lists => PhraseListsMachine,
23
+ :date_time => DateTimeMachine,
24
+ :received => ReceivedMachine,
25
+ :message_ids => MessageIdsMachine,
26
+ :envelope_from => EnvelopeFromMachine,
27
+ :mime_version => MimeVersionMachine,
28
+ :content_type => ContentTypeMachine,
29
+ :content_disposition => ContentDispositionMachine,
30
+ :content_transfer_encoding => ContentTransferEncodingMachine,
31
+ :content_location => ContentLocationMachine
32
+ }
33
+
34
+ def self.parse(machine, string)
35
+ MACHINE_LIST[machine].parse(string)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,17 @@
1
+ module Mail
2
+ module Parsers
3
+ module Ragel
4
+ require 'mail/parsers/ragel/parser_info'
5
+ require "mail/parsers/ragel/ruby"
6
+
7
+ def self.parse(machine, string)
8
+ @machine_module ||= Ruby
9
+ @machine_module.parse(machine, string)
10
+ end
11
+
12
+ def self.machine_module=(m)
13
+ @machine_module = m
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,47 @@
1
+ module Mail::Parsers
2
+ class ReceivedParser
3
+
4
+ def parse(s)
5
+ actions, error = Ragel.parse(:received, s)
6
+ if error
7
+ raise Mail::Field::ParseError.new(Mail::ReceivedElement, s, error)
8
+ end
9
+
10
+ received = ReceivedStruct.new
11
+
12
+ received_tokens_s = date_s = time_s = nil
13
+ actions.each_slice(2) do |action_id, p|
14
+ action = Mail::Parsers::Ragel::ACTIONS[action_id]
15
+ case action
16
+
17
+ # Received Tokens:
18
+ when :received_tokens_s then received_tokens_s = p
19
+ when :received_tokens_e
20
+ received.info = s[received_tokens_s..(p-1)]
21
+
22
+ # Date
23
+ when :date_s then date_s = p
24
+ when :date_e
25
+ received.date = s[date_s..(p-1)].strip
26
+
27
+ # Time
28
+ when :time_s then time_s = p
29
+ when :time_e
30
+ received.time = s[time_s..(p-1)]
31
+
32
+ when :angle_addr_s, :comment_e, :comment_s,
33
+ :domain_e, :domain_s, :local_dot_atom_e,
34
+ :local_dot_atom_pre_comment_e,
35
+ :local_dot_atom_pre_comment_s,
36
+ :local_dot_atom_s, :qstr_e, :qstr_s,
37
+ :local_quoted_string_s, :local_quoted_string_e
38
+ # ignored actions
39
+
40
+ else
41
+ raise Mail::Field::ParseError.new(Mail::ReceivedElement, s, "Failed to process unknown action: #{action}")
42
+ end
43
+ end
44
+ received
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,26 @@
1
+ module Mail
2
+ module Parsers
3
+
4
+ # Low-level ragel based parsers
5
+ require 'mail/parsers/ragel'
6
+
7
+ AddressListStruct = Struct.new(:addresses, :group_names, :error)
8
+ AddressStruct = Struct.new(:raw, :domain, :comments, :local,
9
+ :obs_domain_list, :display_name, :group, :error)
10
+ ContentDispositionStruct = Struct.new(:disposition_type, :parameters, :error)
11
+ ContentLocationStruct = Struct.new(:location, :error)
12
+ ContentTransferEncodingStruct = Struct.new(:encoding, :error)
13
+ ContentTypeStruct = Struct.new(:main_type, :sub_type, :parameters, :error)
14
+ DateTimeStruct = Struct.new(:date_string, :time_string, :error)
15
+ EnvelopeFromStruct = Struct.new(:address, :ctime_date, :error)
16
+ MessageIdsStruct = Struct.new(:message_ids, :error)
17
+ MimeVersionStruct = Struct.new(:major, :minor, :error)
18
+ PhraseListsStruct = Struct.new(:phrases, :error)
19
+ ReceivedStruct = Struct.new(:date, :time, :info, :error)
20
+
21
+ require 'mail/parsers/ragel/parser_info'
22
+ Ragel::FIELD_PARSERS.each do |field_parser|
23
+ require "mail/parsers/#{field_parser}_parser"
24
+ end
25
+ end
26
+ end
data/lib/mail/part.rb CHANGED
@@ -38,8 +38,12 @@ module Mail
38
38
  end
39
39
 
40
40
  def add_required_fields
41
- add_content_id unless has_content_id?
42
41
  super
42
+ add_content_id if !has_content_id? && inline?
43
+ end
44
+
45
+ def add_required_message_fields
46
+ # Override so we don't add Date, MIME-Version, or Message-ID.
43
47
  end
44
48
 
45
49
  def delivery_status_report_part?
@@ -113,4 +117,4 @@ module Mail
113
117
 
114
118
  end
115
119
 
116
- end
120
+ end
@@ -31,11 +31,13 @@ module Mail
31
31
  end
32
32
 
33
33
  def sort!(order)
34
- sorted = self.sort do |a, b|
34
+ # stable sort should be used to maintain the relative order as the parts are added
35
+ i = 0;
36
+ sorted = self.sort_by do |a|
35
37
  # OK, 10000 is arbitrary... if anyone actually wants to explicitly sort 10000 parts of a
36
38
  # single email message... please show me a use case and I'll put more work into this method,
37
39
  # in the meantime, it works :)
38
- get_order_value(a, order) <=> get_order_value(b, order)
40
+ [get_order_value(a, order), i += 1]
39
41
  end
40
42
  self.clear
41
43
  sorted.each { |p| self << p }
@@ -44,7 +46,7 @@ module Mail
44
46
  private
45
47
 
46
48
  def get_order_value(part, order)
47
- if part.respond_to?(:content_type)
49
+ if part.respond_to?(:content_type) && !part[:content_type].nil?
48
50
  order.index(part[:content_type].string.downcase) || 10000
49
51
  else
50
52
  10000
@@ -1,27 +1,29 @@
1
1
  # encoding: utf-8
2
2
  module Mail
3
3
  module Utilities
4
- include Patterns
5
-
4
+ include Constants
5
+
6
6
  # Returns true if the string supplied is free from characters not allowed as an ATOM
7
7
  def atom_safe?( str )
8
8
  not ATOM_UNSAFE === str
9
9
  end
10
10
 
11
- # If the string supplied has ATOM unsafe characters in it, will return the string quoted
11
+ # If the string supplied has ATOM unsafe characters in it, will return the string quoted
12
12
  # in double quotes, otherwise returns the string unmodified
13
13
  def quote_atom( str )
14
14
  atom_safe?( str ) ? str : dquote(str)
15
15
  end
16
16
 
17
- # If the string supplied has PHRASE unsafe characters in it, will return the string quoted
17
+ # If the string supplied has PHRASE unsafe characters in it, will return the string quoted
18
18
  # in double quotes, otherwise returns the string unmodified
19
19
  def quote_phrase( str )
20
20
  if RUBY_VERSION >= '1.9'
21
21
  original_encoding = str.encoding
22
22
  str.force_encoding('ASCII-8BIT')
23
23
  if (PHRASE_UNSAFE === str)
24
- dquote(str).force_encoding(original_encoding)
24
+ quoted_str = dquote(str).force_encoding(original_encoding)
25
+ str.force_encoding(original_encoding)
26
+ quoted_str
25
27
  else
26
28
  str.force_encoding(original_encoding)
27
29
  end
@@ -35,150 +37,158 @@ module Mail
35
37
  not TOKEN_UNSAFE === str
36
38
  end
37
39
 
38
- # If the string supplied has TOKEN unsafe characters in it, will return the string quoted
40
+ # If the string supplied has TOKEN unsafe characters in it, will return the string quoted
39
41
  # in double quotes, otherwise returns the string unmodified
40
42
  def quote_token( str )
41
43
  token_safe?( str ) ? str : dquote(str)
42
44
  end
43
45
 
44
- # Wraps supplied string in double quotes unless it is already wrapped.
45
- #
46
- # Additionally will escape any double quotation marks in the string with a single
47
- # backslash in front of the '"' character.
46
+ # Wraps supplied string in double quotes and applies \-escaping as necessary,
47
+ # unless it is already wrapped.
48
+ #
49
+ # Example:
50
+ #
51
+ # string = 'This is a string'
52
+ # dquote(string) #=> '"This is a string"'
53
+ #
54
+ # string = 'This is "a string"'
55
+ # dquote(string #=> '"This is \"a string\"'
48
56
  def dquote( str )
49
- match = str.match(/^"(.*)?"$/)
50
- str = match[1] if match
51
- # First remove all escaped double quotes:
52
- str = str.gsub(/\\"/, '"')
53
- # Then wrap and re-escape all double quotes
54
- '"' + str.gsub(/["]/n) {|s| '\\' + s } + '"'
55
- end
56
-
57
- # Unwraps supplied string from inside double quotes.
58
- #
57
+ '"' + unquote(str).gsub(/[\\"]/n) {|s| '\\' + s } + '"'
58
+ end
59
+
60
+ # Unwraps supplied string from inside double quotes and
61
+ # removes any \-escaping.
62
+ #
59
63
  # Example:
60
- #
64
+ #
61
65
  # string = '"This is a string"'
62
66
  # unquote(string) #=> 'This is a string'
67
+ #
68
+ # string = '"This is \"a string\""'
69
+ # unqoute(string) #=> 'This is "a string"'
63
70
  def unquote( str )
64
- match = str.match(/^"(.*?)"$/)
65
- match ? match[1] : str
71
+ if str =~ /^"(.*?)"$/
72
+ $1.gsub(/\\(.)/, '\1')
73
+ else
74
+ str
75
+ end
66
76
  end
67
-
77
+
68
78
  # Wraps a string in parenthesis and escapes any that are in the string itself.
69
- #
79
+ #
70
80
  # Example:
71
- #
81
+ #
72
82
  # paren( 'This is a string' ) #=> '(This is a string)'
73
83
  def paren( str )
74
84
  RubyVer.paren( str )
75
85
  end
76
-
86
+
77
87
  # Unwraps a string from being wrapped in parenthesis
78
- #
88
+ #
79
89
  # Example:
80
- #
90
+ #
81
91
  # str = '(This is a string)'
82
92
  # unparen( str ) #=> 'This is a string'
83
93
  def unparen( str )
84
94
  match = str.match(/^\((.*?)\)$/)
85
95
  match ? match[1] : str
86
96
  end
87
-
97
+
88
98
  # Wraps a string in angle brackets and escapes any that are in the string itself
89
- #
99
+ #
90
100
  # Example:
91
- #
101
+ #
92
102
  # bracket( 'This is a string' ) #=> '<This is a string>'
93
103
  def bracket( str )
94
104
  RubyVer.bracket( str )
95
105
  end
96
-
106
+
97
107
  # Unwraps a string from being wrapped in parenthesis
98
- #
108
+ #
99
109
  # Example:
100
- #
110
+ #
101
111
  # str = '<This is a string>'
102
112
  # unbracket( str ) #=> 'This is a string'
103
113
  def unbracket( str )
104
114
  match = str.match(/^\<(.*?)\>$/)
105
115
  match ? match[1] : str
106
116
  end
107
-
117
+
108
118
  # Escape parenthesies in a string
109
- #
119
+ #
110
120
  # Example:
111
- #
121
+ #
112
122
  # str = 'This is (a) string'
113
123
  # escape_paren( str ) #=> 'This is \(a\) string'
114
124
  def escape_paren( str )
115
125
  RubyVer.escape_paren( str )
116
126
  end
117
-
127
+
118
128
  def uri_escape( str )
119
129
  uri_parser.escape(str)
120
130
  end
121
-
131
+
122
132
  def uri_unescape( str )
123
133
  uri_parser.unescape(str)
124
134
  end
125
-
135
+
126
136
  def uri_parser
127
137
  @uri_parser ||= URI.const_defined?(:Parser) ? URI::Parser.new : URI
128
138
  end
129
-
139
+
130
140
  # Matches two objects with their to_s values case insensitively
131
- #
141
+ #
132
142
  # Example:
133
- #
143
+ #
134
144
  # obj2 = "This_is_An_object"
135
145
  # obj1 = :this_IS_an_object
136
146
  # match_to_s( obj1, obj2 ) #=> true
137
147
  def match_to_s( obj1, obj2 )
138
- obj1.to_s.downcase == obj2.to_s.downcase
148
+ obj1.to_s.casecmp(obj2.to_s) == 0
139
149
  end
140
-
150
+
141
151
  # Capitalizes a string that is joined by hyphens correctly.
142
- #
152
+ #
143
153
  # Example:
144
- #
154
+ #
145
155
  # string = 'resent-from-field'
146
156
  # capitalize_field( string ) #=> 'Resent-From-Field'
147
157
  def capitalize_field( str )
148
158
  str.to_s.split("-").map { |v| v.capitalize }.join("-")
149
159
  end
150
-
160
+
151
161
  # Takes an underscored word and turns it into a class name
152
- #
162
+ #
153
163
  # Example:
154
- #
164
+ #
155
165
  # constantize("hello") #=> "Hello"
156
166
  # constantize("hello-there") #=> "HelloThere"
157
167
  # constantize("hello-there-mate") #=> "HelloThereMate"
158
168
  def constantize( str )
159
169
  str.to_s.split(/[-_]/).map { |v| v.capitalize }.to_s
160
170
  end
161
-
171
+
162
172
  # Swaps out all underscores (_) for hyphens (-) good for stringing from symbols
163
173
  # a field name.
164
- #
174
+ #
165
175
  # Example:
166
- #
176
+ #
167
177
  # string = :resent_from_field
168
178
  # dasherize ( string ) #=> 'resent_from_field'
169
179
  def dasherize( str )
170
- str.to_s.gsub('_', '-')
180
+ str.to_s.tr(UNDERSCORE, HYPHEN)
171
181
  end
172
182
 
173
183
  # Swaps out all hyphens (-) for underscores (_) good for stringing to symbols
174
184
  # a field name.
175
- #
185
+ #
176
186
  # Example:
177
- #
187
+ #
178
188
  # string = :resent_from_field
179
189
  # underscoreize ( string ) #=> 'resent_from_field'
180
190
  def underscoreize( str )
181
- str.to_s.downcase.gsub('-', '_')
191
+ str.to_s.downcase.tr(HYPHEN, UNDERSCORE)
182
192
  end
183
193
 
184
194
  if RUBY_VERSION <= '1.8.6'
@@ -190,7 +200,7 @@ module Mail
190
200
  end
191
201
  results
192
202
  end
193
-
203
+
194
204
  def map_with_index( enum, &block )
195
205
  results = []
196
206
  enum.each_with_index do |token, i|
@@ -198,7 +208,7 @@ module Mail
198
208
  end
199
209
  results
200
210
  end
201
-
211
+
202
212
  else
203
213
 
204
214
  def map_lines( str, &block )
Binary file
data/lib/mail/version.rb CHANGED
@@ -1,24 +1,16 @@
1
- # encoding: utf-8
2
1
  module Mail
3
2
  module VERSION
4
-
5
- version = {}
6
- File.read(File.join(File.dirname(__FILE__), '../', 'VERSION')).each_line do |line|
7
- type, value = line.chomp.split(":")
8
- next if type =~ /^\s+$/ || value =~ /^\s+$/
9
- version[type] = value
10
- end
11
-
12
- MAJOR = version['major']
13
- MINOR = version['minor']
14
- PATCH = version['patch']
15
- BUILD = version['build']
3
+
4
+ MAJOR = 2
5
+ MINOR = 6
6
+ PATCH = 3
7
+ BUILD = nil
16
8
 
17
9
  STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.')
18
-
10
+
19
11
  def self.version
20
12
  STRING
21
13
  end
22
-
14
+
23
15
  end
24
16
  end
@@ -3,6 +3,7 @@
3
3
  module Mail
4
4
  class Ruby18
5
5
  require 'base64'
6
+ require 'iconv'
6
7
 
7
8
  # Escapes any parenthesis in a string that are unescaped. This can't
8
9
  # use the Ruby 1.9.1 regexp feature of negative look behind so we have
@@ -57,14 +58,15 @@ module Mail
57
58
  # Ruby 1.8 requires an encoding to work
58
59
  raise ArgumentError, "Must supply an encoding" if encoding.nil?
59
60
  encoding = encoding.to_s.upcase.gsub('_', '-')
60
- [Encodings::Base64.encode(str), encoding]
61
+ [Encodings::Base64.encode(str), fix_encoding(encoding)]
61
62
  end
62
63
 
63
64
  def Ruby18.b_value_decode(str)
64
- match = str.match(/\=\?(.+)?\?[Bb]\?(.+)?\?\=/m)
65
+ match = str.match(/\=\?(.+)?\?[Bb]\?(.*)\?\=/m)
65
66
  if match
66
67
  encoding = match[1]
67
68
  str = Ruby18.decode_base64(match[2])
69
+ str = Iconv.conv('UTF-8//IGNORE', fix_encoding(encoding), str)
68
70
  end
69
71
  str
70
72
  end
@@ -77,10 +79,14 @@ module Mail
77
79
  end
78
80
 
79
81
  def Ruby18.q_value_decode(str)
80
- match = str.match(/\=\?(.+)?\?[Qq]\?(.+)?\?\=/m)
82
+ match = str.match(/\=\?(.+)?\?[Qq]\?(.*)\?\=/m)
81
83
  if match
82
84
  encoding = match[1]
83
- str = Encodings::QuotedPrintable.decode(match[2].gsub(/_/, '=20'))
85
+ string = match[2].gsub(/_/, '=20')
86
+ # Remove trailing = if it exists in a Q encoding
87
+ string = string.sub(/\=$/, '')
88
+ str = Encodings::QuotedPrintable.decode(string)
89
+ str = Iconv.conv('UTF-8//IGNORE', fix_encoding(encoding), str)
84
90
  end
85
91
  str
86
92
  end
@@ -94,5 +100,20 @@ module Mail
94
100
  language = Configuration.instance.param_encode_language
95
101
  "#{encoding}'#{language}'#{URI.escape(str)}"
96
102
  end
103
+
104
+ private
105
+
106
+ def Ruby18.fix_encoding(encoding)
107
+ case encoding.upcase
108
+ when 'UTF8', 'UTF_8'
109
+ 'UTF-8'
110
+ when 'UTF16', 'UTF-16'
111
+ 'UTF-16BE'
112
+ when 'UTF32', 'UTF-32'
113
+ 'UTF-32BE'
114
+ else
115
+ encoding
116
+ end
117
+ end
97
118
  end
98
119
  end