@@ -105,7 +105,8 @@ def wrap_lines(name, folded_lines)
105105 #
106106 # The list of individual words is then used to fill up the output lines without overflowing.
107107 # This is not always guaranteed to work, because there is a wide variation in the number of
108- # characters that are needed to encode a given character.
108+ # characters that are needed to encode a given character. If the resulting line would be too
109+ # long, divide the original word into two chunks and add the pieces separately.
109110 def fold ( prepend = 0 ) # :nodoc:
110111 # prepend is the length to allow for the header prefix on the first line (e.g. 'Subject: ')
111112 encoding = normalized_encoding
@@ -115,6 +116,7 @@ def fold(prepend = 0) # :nodoc:
115116 decoded_string = decoded . to_s
116117 words = decoded_string . split ( /[ \t ]/ )
117118 should_encode = !decoded_string . ascii_only? || words . any? { |word | word . length > max_safe_word }
119+ encoding_overhead = 0 unless should_encode
118120 if should_encode
119121 max_safe_re = Regexp . new ( ".{#{ max_safe_word } }|.+$" )
120122 first = true
@@ -134,13 +136,14 @@ def fold(prepend = 0) # :nodoc:
134136
135137 folded_lines = [ ]
136138 while !words . empty?
137- limit = 78 - prepend
138- limit = limit - encoding_overhead if should_encode
139+ limit = 78 - prepend - encoding_overhead
139140 line = String . new
140141 first_word = true
141142 while !words . empty?
142143 break unless word = words . first . dup
143144
145+ original_word = word # in case we need to try again
146+
144147 # Convert on 1.9+ only since we aren't sure of the current
145148 # charset encoding on 1.8. We'd need to track internal/external
146149 # charset on each field.
@@ -152,17 +155,29 @@ def fold(prepend = 0) # :nodoc:
152155 word = encode_crlf ( word )
153156 # Skip to next line if we're going to go past the limit
154157 # Unless this is the first word, in which case we're going to add it anyway
155- break if !line . empty? && ( line . length + word . length + 1 > limit )
158+ break if !line . empty? && ( line . length + word . length >= limit )
156159 # Remove the word from the queue ...
157160 words . shift
158161 # Add word separator
159162 if first_word
160163 first_word = false
161164 else
162- line << " " if ! should_encode
165+ line << " " unless should_encode
163166 end
164167
165168 # ... add it in encoded form to the current line
169+ # but first check if we have overflowed
170+ # should only happen for the first word on a line
171+ if should_encode && ( line . length + word . length > limit )
172+ word , remain = original_word . scan ( /.{3}|.+$/ ) # roughly half the original split
173+ words . unshift remain # put the unused bit back
174+ # re-encode shorter word
175+ if charset && word . respond_to? ( :encoding )
176+ word = Encodings . transcode_charset ( word , word . encoding , charset )
177+ end
178+ word = encode ( word )
179+ word = encode_crlf ( word )
180+ end
166181 line << word
167182 end
168183 # Mark the line as encoded if necessary
0 commit comments