##// END OF EJS Templates
Fix that AttachmentsController#show don't close the file after reading....
Jean-Philippe Lang -
r14907:3e1d2c09243b
parent child
Show More
@@ -1,191 +1,191
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class AttachmentsController < ApplicationController
18 class AttachmentsController < ApplicationController
19 before_filter :find_attachment, :only => [:show, :download, :thumbnail, :destroy]
19 before_filter :find_attachment, :only => [:show, :download, :thumbnail, :destroy]
20 before_filter :find_editable_attachments, :only => [:edit, :update]
20 before_filter :find_editable_attachments, :only => [:edit, :update]
21 before_filter :file_readable, :read_authorize, :only => [:show, :download, :thumbnail]
21 before_filter :file_readable, :read_authorize, :only => [:show, :download, :thumbnail]
22 before_filter :delete_authorize, :only => :destroy
22 before_filter :delete_authorize, :only => :destroy
23 before_filter :authorize_global, :only => :upload
23 before_filter :authorize_global, :only => :upload
24
24
25 accept_api_auth :show, :download, :thumbnail, :upload
25 accept_api_auth :show, :download, :thumbnail, :upload
26
26
27 def show
27 def show
28 respond_to do |format|
28 respond_to do |format|
29 format.html {
29 format.html {
30 if @attachment.is_diff?
30 if @attachment.is_diff?
31 @diff = File.new(@attachment.diskfile, "rb").read
31 @diff = File.read(@attachment.diskfile, :mode => "rb")
32 @diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
32 @diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
33 @diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
33 @diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
34 # Save diff type as user preference
34 # Save diff type as user preference
35 if User.current.logged? && @diff_type != User.current.pref[:diff_type]
35 if User.current.logged? && @diff_type != User.current.pref[:diff_type]
36 User.current.pref[:diff_type] = @diff_type
36 User.current.pref[:diff_type] = @diff_type
37 User.current.preference.save
37 User.current.preference.save
38 end
38 end
39 render :action => 'diff'
39 render :action => 'diff'
40 elsif @attachment.is_text? && @attachment.filesize <= Setting.file_max_size_displayed.to_i.kilobyte
40 elsif @attachment.is_text? && @attachment.filesize <= Setting.file_max_size_displayed.to_i.kilobyte
41 @content = File.new(@attachment.diskfile, "rb").read
41 @content = File.read(@attachment.diskfile, :mode => "rb")
42 render :action => 'file'
42 render :action => 'file'
43 else
43 else
44 download
44 download
45 end
45 end
46 }
46 }
47 format.api
47 format.api
48 end
48 end
49 end
49 end
50
50
51 def download
51 def download
52 if @attachment.container.is_a?(Version) || @attachment.container.is_a?(Project)
52 if @attachment.container.is_a?(Version) || @attachment.container.is_a?(Project)
53 @attachment.increment_download
53 @attachment.increment_download
54 end
54 end
55
55
56 if stale?(:etag => @attachment.digest)
56 if stale?(:etag => @attachment.digest)
57 # images are sent inline
57 # images are sent inline
58 send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename),
58 send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename),
59 :type => detect_content_type(@attachment),
59 :type => detect_content_type(@attachment),
60 :disposition => (@attachment.image? ? 'inline' : 'attachment')
60 :disposition => (@attachment.image? ? 'inline' : 'attachment')
61 end
61 end
62 end
62 end
63
63
64 def thumbnail
64 def thumbnail
65 if @attachment.thumbnailable? && tbnail = @attachment.thumbnail(:size => params[:size])
65 if @attachment.thumbnailable? && tbnail = @attachment.thumbnail(:size => params[:size])
66 if stale?(:etag => tbnail)
66 if stale?(:etag => tbnail)
67 send_file tbnail,
67 send_file tbnail,
68 :filename => filename_for_content_disposition(@attachment.filename),
68 :filename => filename_for_content_disposition(@attachment.filename),
69 :type => detect_content_type(@attachment),
69 :type => detect_content_type(@attachment),
70 :disposition => 'inline'
70 :disposition => 'inline'
71 end
71 end
72 else
72 else
73 # No thumbnail for the attachment or thumbnail could not be created
73 # No thumbnail for the attachment or thumbnail could not be created
74 render :nothing => true, :status => 404
74 render :nothing => true, :status => 404
75 end
75 end
76 end
76 end
77
77
78 def upload
78 def upload
79 # Make sure that API users get used to set this content type
79 # Make sure that API users get used to set this content type
80 # as it won't trigger Rails' automatic parsing of the request body for parameters
80 # as it won't trigger Rails' automatic parsing of the request body for parameters
81 unless request.content_type == 'application/octet-stream'
81 unless request.content_type == 'application/octet-stream'
82 render :nothing => true, :status => 406
82 render :nothing => true, :status => 406
83 return
83 return
84 end
84 end
85
85
86 @attachment = Attachment.new(:file => request.raw_post)
86 @attachment = Attachment.new(:file => request.raw_post)
87 @attachment.author = User.current
87 @attachment.author = User.current
88 @attachment.filename = params[:filename].presence || Redmine::Utils.random_hex(16)
88 @attachment.filename = params[:filename].presence || Redmine::Utils.random_hex(16)
89 @attachment.content_type = params[:content_type].presence
89 @attachment.content_type = params[:content_type].presence
90 saved = @attachment.save
90 saved = @attachment.save
91
91
92 respond_to do |format|
92 respond_to do |format|
93 format.js
93 format.js
94 format.api {
94 format.api {
95 if saved
95 if saved
96 render :action => 'upload', :status => :created
96 render :action => 'upload', :status => :created
97 else
97 else
98 render_validation_errors(@attachment)
98 render_validation_errors(@attachment)
99 end
99 end
100 }
100 }
101 end
101 end
102 end
102 end
103
103
104 def edit
104 def edit
105 end
105 end
106
106
107 def update
107 def update
108 if params[:attachments].is_a?(Hash)
108 if params[:attachments].is_a?(Hash)
109 if Attachment.update_attachments(@attachments, params[:attachments])
109 if Attachment.update_attachments(@attachments, params[:attachments])
110 redirect_back_or_default home_path
110 redirect_back_or_default home_path
111 return
111 return
112 end
112 end
113 end
113 end
114 render :action => 'edit'
114 render :action => 'edit'
115 end
115 end
116
116
117 def destroy
117 def destroy
118 if @attachment.container.respond_to?(:init_journal)
118 if @attachment.container.respond_to?(:init_journal)
119 @attachment.container.init_journal(User.current)
119 @attachment.container.init_journal(User.current)
120 end
120 end
121 if @attachment.container
121 if @attachment.container
122 # Make sure association callbacks are called
122 # Make sure association callbacks are called
123 @attachment.container.attachments.delete(@attachment)
123 @attachment.container.attachments.delete(@attachment)
124 else
124 else
125 @attachment.destroy
125 @attachment.destroy
126 end
126 end
127
127
128 respond_to do |format|
128 respond_to do |format|
129 format.html { redirect_to_referer_or project_path(@project) }
129 format.html { redirect_to_referer_or project_path(@project) }
130 format.js
130 format.js
131 end
131 end
132 end
132 end
133
133
134 private
134 private
135
135
136 def find_attachment
136 def find_attachment
137 @attachment = Attachment.find(params[:id])
137 @attachment = Attachment.find(params[:id])
138 # Show 404 if the filename in the url is wrong
138 # Show 404 if the filename in the url is wrong
139 raise ActiveRecord::RecordNotFound if params[:filename] && params[:filename] != @attachment.filename
139 raise ActiveRecord::RecordNotFound if params[:filename] && params[:filename] != @attachment.filename
140 @project = @attachment.project
140 @project = @attachment.project
141 rescue ActiveRecord::RecordNotFound
141 rescue ActiveRecord::RecordNotFound
142 render_404
142 render_404
143 end
143 end
144
144
145 def find_editable_attachments
145 def find_editable_attachments
146 klass = params[:object_type].to_s.singularize.classify.constantize rescue nil
146 klass = params[:object_type].to_s.singularize.classify.constantize rescue nil
147 unless klass && klass.reflect_on_association(:attachments)
147 unless klass && klass.reflect_on_association(:attachments)
148 render_404
148 render_404
149 return
149 return
150 end
150 end
151
151
152 @container = klass.find(params[:object_id])
152 @container = klass.find(params[:object_id])
153 if @container.respond_to?(:visible?) && !@container.visible?
153 if @container.respond_to?(:visible?) && !@container.visible?
154 render_403
154 render_403
155 return
155 return
156 end
156 end
157 @attachments = @container.attachments.select(&:editable?)
157 @attachments = @container.attachments.select(&:editable?)
158 if @container.respond_to?(:project)
158 if @container.respond_to?(:project)
159 @project = @container.project
159 @project = @container.project
160 end
160 end
161 render_404 if @attachments.empty?
161 render_404 if @attachments.empty?
162 rescue ActiveRecord::RecordNotFound
162 rescue ActiveRecord::RecordNotFound
163 render_404
163 render_404
164 end
164 end
165
165
166 # Checks that the file exists and is readable
166 # Checks that the file exists and is readable
167 def file_readable
167 def file_readable
168 if @attachment.readable?
168 if @attachment.readable?
169 true
169 true
170 else
170 else
171 logger.error "Cannot send attachment, #{@attachment.diskfile} does not exist or is unreadable."
171 logger.error "Cannot send attachment, #{@attachment.diskfile} does not exist or is unreadable."
172 render_404
172 render_404
173 end
173 end
174 end
174 end
175
175
176 def read_authorize
176 def read_authorize
177 @attachment.visible? ? true : deny_access
177 @attachment.visible? ? true : deny_access
178 end
178 end
179
179
180 def delete_authorize
180 def delete_authorize
181 @attachment.deletable? ? true : deny_access
181 @attachment.deletable? ? true : deny_access
182 end
182 end
183
183
184 def detect_content_type(attachment)
184 def detect_content_type(attachment)
185 content_type = attachment.content_type
185 content_type = attachment.content_type
186 if content_type.blank? || content_type == "application/octet-stream"
186 if content_type.blank? || content_type == "application/octet-stream"
187 content_type = Redmine::MimeType.of(attachment.filename)
187 content_type = Redmine::MimeType.of(attachment.filename)
188 end
188 end
189 content_type.to_s
189 content_type.to_s
190 end
190 end
191 end
191 end
General Comments 0
You need to be logged in to leave comments. Login now