mail 2.2.0 → 2.2.1

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.

@@ -1,3 +1,21 @@
1
+ == Thu May 13 02:49:47 UTC 2010 Mikel Lindsaar <raasdnil@gmail.com>
2
+
3
+ * Fixed Ruby 1.8.6 and 1.9.x incompatibilities because we were using each_with_index and each_line
4
+ * Redid the folding to make it much much faster for long strings (the old one really crumbled with headers longer than 100,000 characters (Lars Pind <lars@pinds.com>)
5
+ * Mail now only returns one encoded-word per line (Lars Pind <lars@pinds.com>)
6
+ * Fixed all the previous issues with extra white-space and an extra = at the end of the encoded-words. (Lars Pind <lars@pinds.com>)
7
+ * Make sure we can handle decoding of very long strings efficiently (Lars Pind <lars@pinds.com>)
8
+ * Handle setting of charset through []= method, so we don't get the warning (Lars Pind <lars@pinds.com>)
9
+ * Remove the trailing =\n that pack('M') adds (Lars Pind <lars@pinds.com>)
10
+ * Handle multiple quoted words in Encodings.unquote_and_convert_to (Eric Kidd <git@randomhacks.net>)
11
+ * Ruby 1.9: mark source encoding so it's usable with -Ks, -Ke, etc (Jeremy Kemper <jeremy@bitsweat.net>)
12
+ * Add #include? to mail body for convenience (Maxim Chernyak <max@bitsonnet.com>)
13
+ * Use Mail::TestMailer.deliveries in README example (John Trupiano <jtrupiano@gmail.com>)
14
+ * Allow bundler to automatically build a gem directly from git (Eric Kidd <git@randomhacks.net>)
15
+ * Added recursive parsing of attachments inside message/rfc822 parts (Ubiratan Pires Alberton <u.alberton@gmail.com>)
16
+ * Adding #inline_content_id for attachments and parts (mikel)
17
+ * Updating readme (mikel)
18
+
1
19
  == Sun Apr 11 07:49:15 UTC 2010 Mikel Lindsaar <raasdnil@gmail.com>
2
20
 
3
21
  * Lots of updates on encoding and decoding of headers and unstructured fields. This
@@ -27,17 +27,15 @@ you are doing, you can fiddle with every last bit of your email directly.
27
27
 
28
28
  Mail is tested and works on the following platforms:
29
29
 
30
- * ree-1.8.7-2009.10 [ x86_64 ]
31
- * ruby-1.8.6-p383 [ x86_64 ]
32
- * ruby-1.8.7-p248 [ x86_64 ]
33
- * ruby-1.9.1-head [ x86_64 ]
34
- * ruby-1.9.1-p376 [ x86_64 ]
30
+ * jruby-1.4.0 [ [x86_64-java] ]
31
+ * ree-1.8.7-2010.01 [ x86_64 ]
32
+ * ruby-1.8.7-p249 [ x86_64 ]
33
+ * ruby-1.9.1-head [ x86_64 ] (10 April 2010)
34
+ * ruby-1.9.2-head [ x86_64 ] (10 April 2010)
35
35
 
36
- Mail seems to work fine on JRuby as well, however, the Base64 and Quoted-Printable
37
- encoding methods are implemented differently and return different values so 6
38
- specs fail. This needs to be triple checked if all OK.
39
-
40
- * jruby-1.4.0 [x86_64-java]
36
+ Mail seems to work fine on JRuby as well, however there is some weird QP
37
+ problem that only shows up while running the specs, running it directly
38
+ works fine.... weird. Any jRuby gurus who want to help out, please do.
41
39
 
42
40
  == Discussion
43
41
 
@@ -52,6 +50,8 @@ the Google Group http://groups.google.com/group/mail-ruby
52
50
  * Support for reading multipart/report emails & getting details from such
53
51
  * Support for multibyte emails - needs quite a lot of work and testing
54
52
  * Wrappers for File, Net/POP3, Net/SMTP
53
+ * Auto encoding of non US-ASCII header fields
54
+ * Auto encoding of non US-ASCII bodies
55
55
 
56
56
  Mail is RFC2822 compliant now, that is, it can parse and generate valid US-ASCII
57
57
  emails. There are a few obsoleted syntax emails that it will have problems with, but
@@ -108,7 +108,7 @@ may or may not be a problem for you.
108
108
 
109
109
  If you want to install mail manually, you can download the gem from github and do:
110
110
 
111
- # gem install mail-1.2.1.gem
111
+ # gem install mail-2.2.0.gem
112
112
 
113
113
  == Encodings
114
114
 
@@ -512,7 +512,7 @@ sending emails, the TestMailer can do this for you.
512
512
  delivery_method :test
513
513
  end
514
514
  => #<Mail::Configuration:0x19345a8 @delivery_method=Mail::TestMailer>
515
- Mail.deliveries
515
+ Mail::TestMailer.deliveries
516
516
  => []
517
517
  Mail.deliver do
518
518
  to 'mikel@me.com'
@@ -521,11 +521,11 @@ sending emails, the TestMailer can do this for you.
521
521
  body 'hello'
522
522
  end
523
523
  => #<Mail::Message:0x19284ec ...
524
- Mail.deliveries.length
524
+ Mail::TestMailer.deliveries.length
525
525
  => 1
526
- Mail.deliveries.first
526
+ Mail::TestMailer.deliveries.first
527
527
  => #<Mail::Message:0x19284ec ...
528
- Mail.deliveries.clear
528
+ Mail::TestMailer.deliveries.clear
529
529
  => []
530
530
 
531
531
  == Excerpts from TREC Spam Corpus 2005
data/Rakefile CHANGED
@@ -1,34 +1,13 @@
1
1
  require File.expand_path('../spec/environment', __FILE__)
2
2
 
3
3
  require 'rake/rdoctask'
4
- require 'rake/gempackagetask'
5
4
  require 'rake/testtask'
6
5
  require 'spec/rake/spectask'
7
6
  require 'cucumber/rake/task'
8
7
 
9
- spec = Gem::Specification.new do |s|
10
- s.name = "mail"
11
- s.version = "2.2.0"
12
- s.author = "Mike Lindsaar"
13
- s.email = "raasdnil@gmail.com"
14
- s.homepage = "http://github.com/mikel/mail"
15
- s.description = "A really Ruby Mail handler."
16
- s.summary = "Mail provides a nice Ruby DSL for making, sending and reading emails."
17
-
18
- s.platform = Gem::Platform::RUBY
19
- s.has_rdoc = true
20
- s.extra_rdoc_files = ["README.rdoc", "CHANGELOG.rdoc", "TODO.rdoc"]
21
-
22
- s.add_dependency('activesupport', ">= 2.3.4")
23
- s.add_dependency('mime-types')
24
- s.add_dependency('treetop', '>= 1.4.5')
25
-
26
- s.require_path = 'lib'
27
- s.files = %w(README.rdoc Rakefile TODO.rdoc) + Dir.glob("lib/**/*")
28
- end
29
-
30
- Rake::GemPackageTask.new(spec) do |pkg|
31
- pkg.gem_spec = spec
8
+ desc "Build a gem file"
9
+ task :build do
10
+ system "gem build mail.gemspec"
32
11
  end
33
12
 
34
13
  task :default => :spec
@@ -50,13 +29,6 @@ Spec::Rake::SpecTask.new(:spec) do |t|
50
29
  t.libs << "#{File.dirname(__FILE__)}/spec/mail"
51
30
  end
52
31
 
53
- Rake::TestTask.new(:test) do |t|
54
- t.libs << 'test'
55
- t.pattern = 'test/**/tc_*.rb'
56
- t.verbose = true
57
- t.warning = false
58
- end
59
-
60
32
  Rake::RDocTask.new(:rdoc) do |rdoc|
61
33
  rdoc.rdoc_dir = 'rdoc'
62
34
  rdoc.title = 'Mail'
@@ -1,10 +1,12 @@
1
1
  module Mail
2
2
  class AttachmentsList < Array
3
-
3
+
4
4
  def initialize(parts_list)
5
5
  @parts_list = parts_list
6
6
  parts_list.map { |p|
7
- if p.parts.empty?
7
+ if p.content_type == "message/rfc822"
8
+ Mail.new(p.body).attachments
9
+ elsif p.parts.empty?
8
10
  p if p.attachment?
9
11
  else
10
12
  p.attachments
@@ -12,12 +14,12 @@ module Mail
12
14
  }.flatten.compact.each { |a| self << a }
13
15
  self
14
16
  end
15
-
17
+
16
18
  # Returns the attachment by filename or at index.
17
- #
19
+ #
18
20
  # mail.attachments['test.png'] = File.read('test.png')
19
21
  # mail.attachments['test.jpg'] = File.read('test.jpg')
20
- #
22
+ #
21
23
  # mail.attachments['test.png'].filename #=> 'test.png'
22
24
  # mail.attachments[1].filename #=> 'test.jpg'
23
25
  def [](index_value)
@@ -65,10 +67,13 @@ module Mail
65
67
  hash[:body].force_encoding("BINARY")
66
68
  end
67
69
  end
70
+
71
+ attachment = Part.new(hash)
72
+ attachment.add_content_id(hash[:content_id])
68
73
 
69
- @parts_list << Part.new(hash)
74
+ @parts_list << attachment
70
75
  end
71
-
76
+
72
77
  # Uses the mime type to try and guess the encoding, if it is a binary type, or unknown, then we
73
78
  # set it to binary, otherwise as set to plain text
74
79
  def guess_encoding
@@ -77,8 +82,8 @@ module Mail
77
82
  else
78
83
  "binary"
79
84
  end
80
- end
81
-
85
+ end
86
+
82
87
  def set_mime_type(filename)
83
88
  # Have to do this because MIME::Types is not Ruby 1.9 safe yet
84
89
  if RUBY_VERSION >= '1.9'
@@ -88,7 +93,7 @@ module Mail
88
93
  end
89
94
  @mime_type = MIME::Types.type_for(filename).first
90
95
  end
91
-
96
+
92
97
  end
93
98
  end
94
99
 
@@ -99,6 +99,20 @@ module Mail
99
99
  self.decoded.match(regexp)
100
100
  end
101
101
 
102
+ # Accepts anything that responds to #to_s and checks if it's a substring of the decoded text
103
+ #
104
+ # Examples:
105
+ #
106
+ # body = Mail::Body.new('The body')
107
+ # body.include?('The') #=> true
108
+ #
109
+ # body = Mail::Body.new("VGhlIGJvZHk=\n")
110
+ # body.encoding = 'base64'
111
+ # body.include?('The') #=> true
112
+ def include?(other)
113
+ self.decoded.include?(other.to_s)
114
+ end
115
+
102
116
  # Allows you to set the sort order of the parts, overriding the default sort order.
103
117
  # Defaults to 'text/plain', then 'text/enriched', then 'text/html' with any other content
104
118
  # type coming after.
@@ -24,4 +24,16 @@ class String #:nodoc:
24
24
  unless method_defined?(:bytesize)
25
25
  alias :bytesize :length
26
26
  end
27
+
28
+ unless ''.respond_to?(:lines)
29
+ require "enumerator"
30
+
31
+ def lines
32
+ to_a.enum_for(:each)
33
+ end
34
+
35
+ end
36
+
27
37
  end
38
+
39
+
@@ -7,6 +7,7 @@ module Mail
7
7
 
8
8
  module Encodings
9
9
 
10
+ require "enumerator"
10
11
  include Mail::Patterns
11
12
  extend Mail::Utilities
12
13
 
@@ -113,24 +114,34 @@ module Mail
113
114
  #
114
115
  # String has to be of the format =?<encoding>?[QB]?<string>?=
115
116
  def Encodings.value_decode(str)
116
- str = str.gsub(/\?=(\s*)=\?/, '?==?') # Remove whitespaces between 'encoded-word's
117
-
118
- # Join QP encoded-words that are adjacent to avoid decoding partial chars
119
- str.gsub!( /=\?==\?.+?\?[Qq]\?/m, '' ) if str =~ /\?==\?/
117
+ # Optimization: If there's no encoded-words in the string, just return it
118
+ return str unless str.index("=?")
120
119
 
121
- str.gsub(/(.*?)(=\?.*?\?.\?.*?\?=)|$/m) do # Grab the insides of each encoded-word
122
- before = $1.to_s
123
- text = $2.to_s
120
+ str = str.gsub(/\?=(\s*)=\?/, '?==?') # Remove whitespaces between 'encoded-word's
124
121
 
125
- case
126
- when text.to_str =~ /=\?.+\?[Bb]\?/m
127
- before + b_value_decode(text)
128
- when text.to_str =~ /=\?.+\?[Qq]\?/m
129
- before + q_value_decode(text)
122
+ # Split on white-space boundaries with capture, so we capture the white-space as well
123
+ str.split(/([ \t])/).map do |text|
124
+ if text.index('=?') != 0
125
+ text
130
126
  else
131
- before + text
127
+ # Join QP encoded-words that are adjacent to avoid decoding partial chars
128
+ text.gsub!(/\?\=\=\?.+?\?[Qq]\?/m, '') if text =~ /\?==\?/
129
+
130
+ # Separate encoded-words with a space, so we can treat them one by one
131
+ text.gsub!(/\?\=\=\?/, '?= =?')
132
+
133
+ text.split(/ /).map do |word|
134
+ case
135
+ when word.to_str =~ /=\?.+\?[Bb]\?/m
136
+ b_value_decode(word)
137
+ when text.to_str =~ /=\?.+\?[Qq]\?/m
138
+ q_value_decode(word)
139
+ else
140
+ word.to_str
141
+ end
142
+ end
132
143
  end
133
- end
144
+ end.join("")
134
145
  end
135
146
 
136
147
  # Takes an encoded string of the format =?<encoding>?[QB]?<string>?=
@@ -176,7 +187,8 @@ module Mail
176
187
  address.gsub!(/(".*?[^#{us_ascii}].+?")/) { |s| Encodings.b_value_encode(unquote(s), charset) }
177
188
  # Then loop through all remaining items and encode as needed
178
189
  tokens = address.split(/\s/)
179
- tokens.each_with_index.map do |word, i|
190
+ # Need to use enum_for to stay 1.8.6 compatible
191
+ tokens.enum_for(:each_with_index).map do |word, i|
180
192
  if word.ascii_only?
181
193
  word
182
194
  else
@@ -199,7 +211,7 @@ module Mail
199
211
  def Encodings.b_value_encode(str, encoding = nil)
200
212
  return str if str.to_s.ascii_only?
201
213
  string, encoding = RubyVer.b_value_encode(str, encoding)
202
- string.each_line.map do |str|
214
+ string.lines.map do |str|
203
215
  "=?#{encoding}?B?#{str.chomp}?="
204
216
  end.join(" ")
205
217
  end
@@ -214,8 +226,8 @@ module Mail
214
226
  def Encodings.q_value_encode(str, encoding = nil)
215
227
  return str if str.to_s.ascii_only?
216
228
  string, encoding = RubyVer.q_value_encode(str, encoding)
217
- string.gsub!("=\r\n=", '=') # We already have limited the string to the length we want
218
- string.each_line.map do |str|
229
+ string.gsub!("=\r\n", '') # We already have limited the string to the length we want
230
+ string.lines.map do |str|
219
231
  "=?#{encoding}?Q?#{str.chomp.gsub(/ /, '_')}?="
220
232
  end.join(" ")
221
233
  end
@@ -243,7 +255,7 @@ module Mail
243
255
  end
244
256
 
245
257
  def Encodings.split_encoding_from_string( str )
246
- match = str.match(/\=\?(.+)?\?[QB]\?(.+)?\?\=/mi)
258
+ match = str.match(/\=\?([^?]+)?\?[QB]\?(.+)?\?\=/mi)
247
259
  if match
248
260
  [match[1], match[2]]
249
261
  else
@@ -102,64 +102,77 @@ module Mail
102
102
  # it is allowed elsewhere.
103
103
  def wrapped_value # :nodoc:
104
104
  @folded_line = []
105
- @unfolded_line = decoded.to_s.clone
105
+ @unfolded_line = decoded.to_s.split(/[ \t]/)
106
106
  fold("#{name}: ".length)
107
107
  wrap_lines(name, @folded_line)
108
108
  end
109
-
109
+
110
+ # 6.2. Display of 'encoded-word's
111
+ #
112
+ # When displaying a particular header field that contains multiple
113
+ # 'encoded-word's, any 'linear-white-space' that separates a pair of
114
+ # adjacent 'encoded-word's is ignored. (This is to allow the use of
115
+ # multiple 'encoded-word's to represent long strings of unencoded text,
116
+ # without having to separate 'encoded-word's where spaces occur in the
117
+ # unencoded text.)
110
118
  def wrap_lines(name, folded_lines)
111
119
  result = []
112
120
  index = 0
113
121
  result[index] = "#{name}: #{folded_lines.shift}"
114
- folded_lines.each do |line|
115
- if (result[index] + line).length < 77
116
- result[index] << " " + line
117
- else
118
- result[index] << "\r\n\t"
119
- index += 1
120
- result[index] = line
121
- end
122
- end
123
- result.join
122
+ result.concat(folded_lines)
123
+ result.join("\r\n\t")
124
124
  end
125
125
 
126
126
  def fold(prepend = 0) # :nodoc:
127
- # Get the last whitespace character, OR we'll just choose
128
- # 78 if there is no whitespace, or 23 for non ascii:
129
- # Each QP byte is 6 chars (=0A)
130
- # Plus 18 for the =?encoding?Q?= ... ?=
131
- # Plus 2 for the \r\n and 1 for the \t
132
- # 80 - 2 - 1 - 18 = 59 / 6 ~= 10
133
- @unfolded_line.ascii_only? ? (limit = 78 - prepend) : (limit = 10 - prepend)
134
- # find the last white space character within the limit
135
- if wspp = @unfolded_line.mb_chars.slice(0..limit) =~ /[ \t][^ \t]*$/
136
- wrap = true
137
- wspp = limit if wspp == 0
138
- @folded_line << encode(@unfolded_line.mb_chars.slice!(0...wspp).strip.to_str)
139
- @folded_line.flatten!
140
- # if no last whitespace before the limit, find the first
141
- elsif wspp = @unfolded_line.mb_chars =~ /[ \t][^ \t]/
142
- wrap = true
143
- wspp = limit if wspp == 0
144
- @folded_line << encode(@unfolded_line.mb_chars.slice!(0...wspp).strip.to_str)
145
- @folded_line.flatten!
146
- # if no whitespace, don't wrap
147
- else
148
- wrap = false
149
- end
150
-
151
- if wrap && @unfolded_line.length > limit
152
- fold
153
- else
154
- @folded_line << encode(@unfolded_line)
155
- @folded_line.flatten!
127
+ encoding = @charset.to_s.upcase.gsub('_', '-')
128
+ while !@unfolded_line.empty?
129
+ encoded = false
130
+ limit = 78 - prepend
131
+ line = ""
132
+ while !@unfolded_line.empty?
133
+ break unless word = @unfolded_line.first.dup
134
+ # Remember whether it was non-ascii before we encode it ('cause then we can't tell anymore)
135
+ non_ascii = word.not_ascii_only?
136
+ encoded_word = encode(word)
137
+ # Skip to next line if we're going to go past the limit
138
+ # Unless this is the first word, in which case we're going to add it anyway
139
+ # Note: This means that a word that's longer than 998 characters is going to break the spec. Please fix if this is a problem for you.
140
+ # (The fix, it seems, would be to use encoded-word encoding on it, because that way you can break it across multiple lines and
141
+ # the linebreak will be ignored)
142
+ break if !line.empty? && (line.length + encoded_word.length + 1 > limit)
143
+ # If word was the first non-ascii word, we're going to make the entire line encoded and we're going to reduce the limit accordingly
144
+ if non_ascii && !encoded
145
+ encoded = true
146
+ encoded_word_safify!(line)
147
+ limit = limit - 8 - encoding.length # minus the =?...?Q?...?= part, the possible leading white-space, and the name of the encoding
148
+ end
149
+ # Remove the word from the queue ...
150
+ @unfolded_line.shift
151
+ # ... add it in encoded form to the current line
152
+ line << " " unless line.empty?
153
+ encoded_word_safify!(encoded_word) if encoded
154
+ line << encoded_word
155
+ end
156
+ # Add leading whitespace if both this and the last line were encoded, because whitespace between two encoded-words is ignored when decoding
157
+ line = " " + line if encoded && @folded_line.last && @folded_line.last.index('=?') == 0
158
+ # Encode the line if necessary
159
+ line = "=?#{encoding}?Q?#{line.gsub(/ /, '_')}?=" if encoded
160
+ # Add the line to the output and reset the prepend
161
+ @folded_line << line
162
+ prepend = 0
156
163
  end
157
164
  end
158
-
165
+
159
166
  def encode(value)
160
- value.gsub!("\r", "=0D")
161
- value.gsub!("\n", "=0A")
162
- Encodings.q_value_encode(value, @charset).split(" ")
167
+ (value.not_ascii_only? ? [value].pack("M").gsub("=\n", '') : value).gsub("\r", "=0D").gsub("\n", "=0A")
168
+ end
169
+
170
+ def encoded_word_safify!(value)
171
+ value.gsub!(/"/, '=22')
172
+ value.gsub!(/\(/, '=28')
173
+ value.gsub!(/\)/, '=29')
174
+ value.gsub!(/\?/, '=3F')
175
+ value.gsub!(/_/, '=5F')
163
176
  end
164
177
 
165
178
  end
@@ -1180,6 +1180,8 @@ module Mail
1180
1180
  self.body = value
1181
1181
  elsif name.to_s =~ /content[-_]type/i
1182
1182
  header[name] = value
1183
+ elsif name.to_s == 'charset'
1184
+ self.charset = value
1183
1185
  else
1184
1186
  header[name] = value
1185
1187
  end
@@ -17,7 +17,12 @@ module Mail
17
17
  def has_content_id?
18
18
  header.has_content_id?
19
19
  end
20
-
20
+
21
+ def inline_content_id
22
+ add_content_id unless has_content_id?
23
+ uri_escape(unbracket(content_id))
24
+ end
25
+
21
26
  def add_required_fields
22
27
  add_content_id unless has_content_id?
23
28
  super
@@ -1,3 +1,4 @@
1
+ # encoding: us-ascii
1
2
  module Mail
2
3
  module Patterns
3
4
  white_space = %Q|\x9\x20|
@@ -69,7 +69,7 @@ module Mail
69
69
  #
70
70
  # Example:
71
71
  #
72
- # string
72
+ # paren( 'This is a string' ) #=> '(This is a string)'
73
73
  def paren( str )
74
74
  RubyVer.paren( str )
75
75
  end
@@ -85,6 +85,26 @@ module Mail
85
85
  match ? match[1] : str
86
86
  end
87
87
 
88
+ # Wraps a string in angle brackets and escapes any that are in the string itself
89
+ #
90
+ # Example:
91
+ #
92
+ # bracket( 'This is a string' ) #=> '<This is a string>'
93
+ def bracket( str )
94
+ RubyVer.bracket( str )
95
+ end
96
+
97
+ # Unwraps a string from being wrapped in parenthesis
98
+ #
99
+ # Example:
100
+ #
101
+ # str = '<This is a string>'
102
+ # unbracket( str ) #=> 'This is a string'
103
+ def unbracket( str )
104
+ match = str.match(/^\<(.*?)\>$/)
105
+ match ? match[1] : str
106
+ end
107
+
88
108
  # Escape parenthesies in a string
89
109
  #
90
110
  # Example:
@@ -95,6 +115,14 @@ module Mail
95
115
  RubyVer.escape_paren( str )
96
116
  end
97
117
 
118
+ def uri_escape( str )
119
+ URI.escape(str)
120
+ end
121
+
122
+ def uri_unescape( str )
123
+ URI.unescape(str)
124
+ end
125
+
98
126
  # Matches two objects with their to_s values case insensitively
99
127
  #
100
128
  # Example:
@@ -3,7 +3,7 @@ module Mail
3
3
  module VERSION
4
4
  MAJOR = 2
5
5
  MINOR = 2
6
- TINY = 0
6
+ TINY = 1
7
7
 
8
8
  STRING = [MAJOR, MINOR, TINY].join('.')
9
9
  end
@@ -21,6 +21,21 @@ module Mail
21
21
  '(' + str + ')'
22
22
  end
23
23
 
24
+ def Ruby18.escape_bracket( str )
25
+ re = /\\\>/
26
+ str = str.gsub(re) { |s| '>'}
27
+ re = /\\\</
28
+ str = str.gsub(re) { |s| '<'}
29
+ re = /([\<\>])/ # Only match unescaped parens
30
+ str.gsub(re) { |s| '\\' + s }
31
+ end
32
+
33
+ def Ruby18.bracket( str )
34
+ str = $1 if str =~ /^\<(.*)?\>$/
35
+ str = escape_bracket( str )
36
+ '<' + str + '>'
37
+ end
38
+
24
39
  def Ruby18.decode_base64(str)
25
40
  Base64.decode64(str)
26
41
  end
@@ -15,6 +15,17 @@ module Mail
15
15
  '(' + str + ')'
16
16
  end
17
17
 
18
+ def Ruby19.escape_bracket( str )
19
+ re = /(?<!\\)([\<\>])/ # Only match unescaped brackets
20
+ str.gsub(re) { |s| '\\' + s }
21
+ end
22
+
23
+ def Ruby19.bracket( str )
24
+ str = $1 if str =~ /^\<(.*)?\>$/
25
+ str = escape_bracket( str )
26
+ '<' + str + '>'
27
+ end
28
+
18
29
  def Ruby19.decode_base64(str)
19
30
  str.unpack( 'm' ).first.force_encoding(Encoding::BINARY)
20
31
  end
metadata CHANGED
@@ -5,22 +5,22 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 2
7
7
  - 2
8
- - 0
9
- version: 2.2.0
8
+ - 1
9
+ version: 2.2.1
10
10
  platform: ruby
11
11
  authors:
12
- - Mike Lindsaar
12
+ - Mikel Lindsaar
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-04-11 00:00:00 +10:00
17
+ date: 2010-05-13 00:00:00 +10:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
- type: :runtime
22
21
  name: activesupport
23
- version_requirements: &id001 !ruby/object:Gem::Requirement
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
24
  requirements:
25
25
  - - ">="
26
26
  - !ruby/object:Gem::Version
@@ -29,24 +29,24 @@ dependencies:
29
29
  - 3
30
30
  - 4
31
31
  version: 2.3.4
32
- requirement: *id001
33
- prerelease: false
34
- - !ruby/object:Gem::Dependency
35
32
  type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
36
35
  name: mime-types
37
- version_requirements: &id002 !ruby/object:Gem::Requirement
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
38
  requirements:
39
39
  - - ">="
40
40
  - !ruby/object:Gem::Version
41
41
  segments:
42
42
  - 0
43
43
  version: "0"
44
- requirement: *id002
45
- prerelease: false
46
- - !ruby/object:Gem::Dependency
47
44
  type: :runtime
45
+ version_requirements: *id002
46
+ - !ruby/object:Gem::Dependency
48
47
  name: treetop
49
- version_requirements: &id003 !ruby/object:Gem::Requirement
48
+ prerelease: false
49
+ requirement: &id003 !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - ">="
52
52
  - !ruby/object:Gem::Version
@@ -55,8 +55,8 @@ dependencies:
55
55
  - 4
56
56
  - 5
57
57
  version: 1.4.5
58
- requirement: *id003
59
- prerelease: false
58
+ type: :runtime
59
+ version_requirements: *id003
60
60
  description: A really Ruby Mail handler.
61
61
  email: raasdnil@gmail.com
62
62
  executables: []