lib/roda/plugins/sinatra_helpers.rb in roda-3.43.1 vs lib/roda/plugins/sinatra_helpers.rb in roda-3.44.0
- old
+ new
@@ -209,10 +209,14 @@
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
module SinatraHelpers
+ UTF8_ENCODING = Encoding.find('UTF-8')
+ ISO88591_ENCODING = Encoding.find('ISO-8859-1')
+ BINARY_ENCODING = Encoding.find('BINARY')
+
# Depend on the status_303 plugin.
def self.load_dependencies(app, _opts = nil)
app.plugin :status_303
end
@@ -430,18 +434,36 @@
# Set the Content-Disposition to "attachment" with the specified filename,
# instructing the user agents to prompt to save.
def attachment(filename = nil, disposition='attachment')
if filename
- params = "; filename=#{File.basename(filename).inspect}"
+ param_filename = File.basename(filename)
+ encoding = param_filename.encoding
+
+ needs_encoding = param_filename.gsub!(/[^ 0-9a-zA-Z!\#$&\+\.\^_`\|~]+/, '-')
+ params = "; filename=#{param_filename.inspect}"
+
+ if needs_encoding && (encoding == UTF8_ENCODING || encoding == ISO88591_ENCODING)
+ # File name contains non attr-char characters from RFC 5987 Section 3.2.1
+
+ encoded_filename = File.basename(filename).force_encoding(BINARY_ENCODING)
+ # Similar regexp as above, but treat each byte separately, and encode
+ # space characters, since those aren't allowed in attr-char
+ encoded_filename.gsub!(/[^0-9a-zA-Z!\#$&\+\.\^_`\|~]/) do |c|
+ "%%%X" % c.ord
+ end
+
+ encoded_params = "; filename*=#{encoding.to_s}''#{encoded_filename}"
+ end
+
unless @headers["Content-Type"]
ext = File.extname(filename)
unless ext.empty?
content_type(ext)
end
end
end
- @headers["Content-Disposition"] = "#{disposition}#{params}"
+ @headers["Content-Disposition"] = "#{disposition}#{params}#{encoded_params}"
end
# Whether or not the status is set to 1xx. Returns nil if status not yet set.
def informational?
@status.between?(100, 199) if @status