mail 2.2.4 → 2.2.5

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,9 @@
1
+ == Thu 17 Jun 2010 22:13:18 UTC Mikel Lindsaar <mikel@rubyx.com>
2
+
3
+ * Added Mail::POP3.delete_all, including specs (Martijn Storck)
4
+ * Lars Pind updates on header folding
5
+ * Version bump to 2.2.5
6
+
1
7
  == Tue Jun 8 01:55:21 UTC 2010 Mikel Lindsaar <mikel@rubyx.com>
2
8
 
3
9
  * Added inline attachment support to mail (mikel)
@@ -1,4 +1,4 @@
1
- patch:4
1
+ patch:5
2
2
  major:2
3
3
  build:
4
4
  minor:2
@@ -113,24 +113,34 @@ module Mail
113
113
  #
114
114
  # String has to be of the format =?<encoding>?[QB]?<string>?=
115
115
  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 =~ /\?==\?/
116
+ # Optimization: If there's no encoded-words in the string, just return it
117
+ return str unless str.index("=?")
120
118
 
121
- str.gsub(/(.*?)(=\?.*?\?.\?.*?\?=)|$/m) do # Grab the insides of each encoded-word
122
- before = $1.to_s
123
- text = $2.to_s
119
+ str = str.gsub(/\?=(\s*)=\?/, '?==?') # Remove whitespaces between 'encoded-word's
124
120
 
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)
121
+ # Split on white-space boundaries with capture, so we capture the white-space as well
122
+ str.split(/([ \t])/).map do |text|
123
+ if text.index('=?') != 0
124
+ text
130
125
  else
131
- before + text
126
+ # Join QP encoded-words that are adjacent to avoid decoding partial chars
127
+ text.gsub!(/\?\=\=\?.+?\?[Qq]\?/m, '') if text =~ /\?==\?/
128
+
129
+ # Separate encoded-words with a space, so we can treat them one by one
130
+ text.gsub!(/\?\=\=\?/, '?= =?')
131
+
132
+ text.split(/ /).map do |word|
133
+ case
134
+ when word.to_str =~ /=\?.+\?[Bb]\?/m
135
+ b_value_decode(word)
136
+ when text.to_str =~ /=\?.+\?[Qq]\?/m
137
+ q_value_decode(word)
138
+ else
139
+ word.to_str
140
+ end
141
+ end
132
142
  end
133
- end
143
+ end.join("")
134
144
  end
135
145
 
136
146
  # Takes an encoded string of the format =?<encoding>?[QB]?<string>?=
@@ -214,7 +224,7 @@ module Mail
214
224
  def Encodings.q_value_encode(str, encoding = nil)
215
225
  return str if str.to_s.ascii_only?
216
226
  string, encoding = RubyVer.q_value_encode(str, encoding)
217
- string.gsub!("=\r\n=", '=') # We already have limited the string to the length we want
227
+ string.gsub!("=\r\n", '') # We already have limited the string to the length we want
218
228
  string.each_line.map do |str|
219
229
  "=?#{encoding}?Q?#{str.chomp.gsub(/ /, '_')}?="
220
230
  end.join(" ")
@@ -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\s"
119
- index += 1
120
- result[index] = line
121
- end
122
- end
123
- result.join
122
+ result.concat(folded_lines)
123
+ result.join("\r\n\s")
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 \s
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
@@ -170,6 +170,13 @@ module Mail
170
170
  Mail.new(File.read(filename))
171
171
  end
172
172
 
173
+ # Delete all emails from a POP3 server.
174
+ # See Mail::POP3 for a complete documentation.
175
+ def Mail.delete_all(*args, &block)
176
+ retriever_method.delete_all(*args, &block)
177
+ end
178
+
179
+
173
180
  # Initialize the observers and interceptors arrays
174
181
  @@delivery_notification_observers = []
175
182
  @@delivery_interceptors = []
@@ -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
@@ -118,6 +118,16 @@ module Mail
118
118
  end
119
119
  end
120
120
 
121
+ # Delete all emails from a POP3 server
122
+ def delete_all
123
+ start do |pop3|
124
+ unless pop3.mails.empty?
125
+ pop3.delete_all
126
+ pop3.finish
127
+ end
128
+ end
129
+ end
130
+
121
131
  private
122
132
 
123
133
  # Set default options
@@ -14,8 +14,17 @@ module Mail
14
14
  to_a
15
15
  end
16
16
  end
17
+
18
+ undef :map
19
+ alias_method :map, :collect
17
20
 
18
- alias :collect :map
21
+ def map!
22
+ raise NoMethodError, "#map! is not defined, please call #collect and create a new PartsList"
23
+ end
24
+
25
+ def collect!
26
+ raise NoMethodError, "#collect! is not defined, please call #collect and create a new PartsList"
27
+ end
19
28
 
20
29
  def sort!(order)
21
30
  sorted = self.sort do |a, b|
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 2
7
7
  - 2
8
- - 4
9
- version: 2.2.4
8
+ - 5
9
+ version: 2.2.5
10
10
  platform: ruby
11
11
  authors:
12
12
  - Mikel Lindsaar
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-06-12 00:00:00 -07:00
17
+ date: 2010-06-17 00:00:00 -07:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency