Description:
fixed X-Sendfile restriction on output download
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r291:43d936feb73b - - 1 file changed: 4 inserted, 0 deleted

@@ -1,345 +1,349
1 1 class MainController < ApplicationController
2 2
3 3 before_filter :authenticate, :except => [:index, :login]
4 4 before_filter :check_viewability, :except => [:index, :login]
5 5
6 6 append_before_filter :update_user_start_time, :except => [:index, :login]
7 7
8 8 # to prevent log in box to be shown when user logged out of the
9 9 # system only in some tab
10 10 prepend_before_filter :reject_announcement_refresh_when_logged_out, :only => [:announcements]
11 11
12 12 # COMMENTED OUT: filter in each action instead
13 13 # before_filter :verify_time_limit, :only => [:submit]
14 14
15 15 verify :method => :post, :only => [:submit],
16 16 :redirect_to => { :action => :index }
17 17
18 18 # COMMENT OUT: only need when having high load
19 19 # caches_action :index, :login
20 20
21 21 # NOTE: This method is not actually needed, 'config/routes.rb' has
22 22 # assigned action login as a default action.
23 23 def index
24 24 redirect_to :action => 'login'
25 25 end
26 26
27 27 def login
28 28 saved_notice = flash[:notice]
29 29 reset_session
30 30 flash.now[:notice] = saved_notice
31 31
32 32 # EXPERIMENT:
33 33 # Hide login if in single user mode and the url does not
34 34 # explicitly specify /login
35 35 #
36 36 # logger.info "PATH: #{request.path}"
37 37 # if Configuration['system.single_user_mode'] and
38 38 # request.path!='/main/login'
39 39 # @hidelogin = true
40 40 # end
41 41
42 42 @announcements = Announcement.find_for_frontpage
43 43 render :action => 'login', :layout => 'empty'
44 44 end
45 45
46 46 def list
47 47 prepare_list_information
48 48 end
49 49
50 50 def help
51 51 @user = User.find(session[:user_id])
52 52 end
53 53
54 54 def submit
55 55 user = User.find(session[:user_id])
56 56
57 57 @submission = Submission.new(params[:submission])
58 58 @submission.user = user
59 59 @submission.language_id = 0
60 60 if (params['file']) and (params['file']!='')
61 61 @submission.source = params['file'].read
62 62 @submission.source_filename = params['file'].original_filename
63 63 end
64 64 @submission.submitted_at = Time.new.gmtime
65 65
66 66 if Configuration.time_limit_mode? and user.contest_finished?
67 67 @submission.errors.add_to_base "The contest is over."
68 68 prepare_list_information
69 69 render :action => 'list' and return
70 70 end
71 71
72 72 if @submission.valid?
73 73 if @submission.save == false
74 74 flash[:notice] = 'Error saving your submission'
75 75 elsif Task.create(:submission_id => @submission.id,
76 76 :status => Task::STATUS_INQUEUE) == false
77 77 flash[:notice] = 'Error adding your submission to task queue'
78 78 end
79 79 else
80 80 prepare_list_information
81 81 render :action => 'list' and return
82 82 end
83 83 redirect_to :action => 'list'
84 84 end
85 85
86 86 def source
87 87 submission = Submission.find(params[:id])
88 88 if submission.user_id == session[:user_id]
89 89 send_data(submission.source,
90 90 {:filename => submission.download_filename,
91 91 :type => 'text/plain'})
92 92 else
93 93 flash[:notice] = 'Error viewing source'
94 94 redirect_to :action => 'list'
95 95 end
96 96 end
97 97
98 98 def compiler_msg
99 99 @submission = Submission.find(params[:id])
100 100 if @submission.user_id == session[:user_id]
101 101 render :action => 'compiler_msg', :layout => 'empty'
102 102 else
103 103 flash[:notice] = 'Error viewing source'
104 104 redirect_to :action => 'list'
105 105 end
106 106 end
107 107
108 108 def submission
109 109 @user = User.find(session[:user_id])
110 110 @problems = @user.available_problems
111 111 if params[:id]==nil
112 112 @problem = nil
113 113 @submissions = nil
114 114 else
115 115 @problem = Problem.find_by_name(params[:id])
116 116 if not @problem.available
117 117 redirect_to :action => 'list'
118 118 flash[:notice] = 'Error: submissions for that problem are not viewable.'
119 119 return
120 120 end
121 121 @submissions = Submission.find_all_by_user_problem(@user.id, @problem.id)
122 122 end
123 123 end
124 124
125 125 def result
126 126 if !Configuration.show_grading_result
127 127 redirect_to :action => 'list' and return
128 128 end
129 129 @user = User.find(session[:user_id])
130 130 @submission = Submission.find(params[:id])
131 131 if @submission.user!=@user
132 132 flash[:notice] = 'You are not allowed to view result of other users.'
133 133 redirect_to :action => 'list' and return
134 134 end
135 135 prepare_grading_result(@submission)
136 136 end
137 137
138 138 def load_output
139 139 if !Configuration.show_grading_result or params[:num]==nil
140 140 redirect_to :action => 'list' and return
141 141 end
142 142 @user = User.find(session[:user_id])
143 143 @submission = Submission.find(params[:id])
144 144 if @submission.user!=@user
145 145 flash[:notice] = 'You are not allowed to view result of other users.'
146 146 redirect_to :action => 'list' and return
147 147 end
148 148 case_num = params[:num].to_i
149 149 out_filename = output_filename(@user.login,
150 150 @submission.problem.name,
151 151 @submission.id,
152 152 case_num)
153 153 if !FileTest.exists?(out_filename)
154 154 flash[:notice] = 'Output not found.'
155 155 redirect_to :action => 'list' and return
156 156 end
157 157
158 + if defined?(USE_APACHE_XSENDFILE) and USE_APACHE_XSENDFILE
158 159 response.headers['Content-Type'] = "application/force-download"
159 160 response.headers['Content-Disposition'] = "attachment; filename=\"output-#{case_num}.txt\""
160 161 response.headers["X-Sendfile"] = out_filename
161 162 response.headers['Content-length'] = File.size(out_filename)
162 163 render :nothing => true
164 + else
165 + send_file out_filename, :stream => false, :filename => "output-#{case_num}.txt", :type => "text/plain"
166 + end
163 167 end
164 168
165 169 def error
166 170 @user = User.find(session[:user_id])
167 171 end
168 172
169 173 # announcement refreshing and hiding methods
170 174
171 175 def announcements
172 176 if params.has_key? 'recent'
173 177 prepare_announcements(params[:recent])
174 178 else
175 179 prepare_announcements
176 180 end
177 181 render(:partial => 'announcement',
178 182 :collection => @announcements,
179 183 :locals => {:announcement_effect => true})
180 184 end
181 185
182 186 protected
183 187
184 188 def prepare_announcements(recent=nil)
185 189 if Configuration.show_tasks_to?(@user)
186 190 @announcements = Announcement.find_published(true)
187 191 else
188 192 @announcements = Announcement.find_published
189 193 end
190 194 if recent!=nil
191 195 recent_id = recent.to_i
192 196 @announcements = @announcements.find_all { |a| a.id > recent_id }
193 197 end
194 198 end
195 199
196 200 def prepare_list_information
197 201 @user = User.find(session[:user_id])
198 202 if not Configuration.multicontests?
199 203 @problems = problem_list_for_user(@user)
200 204 else
201 205 @contest_problems = @user.available_problems_group_by_contests
202 206 @problems = @user.available_problems
203 207 end
204 208 @prob_submissions = {}
205 209 @problems.each do |p|
206 210 sub = Submission.find_last_by_user_and_problem(@user.id,p.id)
207 211 if sub!=nil
208 212 @prob_submissions[p.id] = { :count => sub.number, :submission => sub }
209 213 else
210 214 @prob_submissions[p.id] = { :count => 0, :submission => nil }
211 215 end
212 216 end
213 217 prepare_announcements
214 218 end
215 219
216 220 def check_viewability
217 221 @user = User.find(session[:user_id])
218 222 if (!Configuration.show_tasks_to?(@user)) and
219 223 ((action_name=='submission') or (action_name=='submit'))
220 224 redirect_to :action => 'list' and return
221 225 end
222 226 end
223 227
224 228 def prepare_grading_result(submission)
225 229 if Configuration.task_grading_info.has_key? submission.problem.name
226 230 grading_info = Configuration.task_grading_info[submission.problem.name]
227 231 else
228 232 # guess task info from problem.full_score
229 233 cases = submission.problem.full_score / 10
230 234 grading_info = {
231 235 'testruns' => cases,
232 236 'testcases' => cases
233 237 }
234 238 end
235 239 @test_runs = []
236 240 if grading_info['testruns'].is_a? Integer
237 241 trun_count = grading_info['testruns']
238 242 trun_count.times do |i|
239 243 @test_runs << [ read_grading_result(@user.login,
240 244 submission.problem.name,
241 245 submission.id,
242 246 i+1) ]
243 247 end
244 248 else
245 249 grading_info['testruns'].keys.sort.each do |num|
246 250 run = []
247 251 testrun = grading_info['testruns'][num]
248 252 testrun.each do |c|
249 253 run << read_grading_result(@user.login,
250 254 submission.problem.name,
251 255 submission.id,
252 256 c)
253 257 end
254 258 @test_runs << run
255 259 end
256 260 end
257 261 end
258 262
259 263 def grading_result_dir(user_name, problem_name, submission_id, case_num)
260 264 return "#{GRADING_RESULT_DIR}/#{user_name}/#{problem_name}/#{submission_id}/test-result/#{case_num}"
261 265 end
262 266
263 267 def output_filename(user_name, problem_name, submission_id, case_num)
264 268 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
265 269 return "#{dir}/output.txt"
266 270 end
267 271
268 272 def read_grading_result(user_name, problem_name, submission_id, case_num)
269 273 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
270 274 result_file_name = "#{dir}/result"
271 275 if !FileTest.exists?(result_file_name)
272 276 return {:num => case_num, :msg => 'program did not run'}
273 277 else
274 278 results = File.open(result_file_name).readlines
275 279 run_stat = extract_running_stat(results)
276 280 output_filename = "#{dir}/output.txt"
277 281 if FileTest.exists?(output_filename)
278 282 output_file = true
279 283 output_size = File.size(output_filename)
280 284 else
281 285 output_file = false
282 286 output_size = 0
283 287 end
284 288
285 289 return {
286 290 :num => case_num,
287 291 :msg => results[0],
288 292 :run_stat => run_stat,
289 293 :output => output_file,
290 294 :output_size => output_size
291 295 }
292 296 end
293 297 end
294 298
295 299 # copied from grader/script/lib/test_request_helper.rb
296 300 def extract_running_stat(results)
297 301 running_stat_line = results[-1]
298 302
299 303 # extract exit status line
300 304 run_stat = ""
301 305 if !(/[Cc]orrect/.match(results[0]))
302 306 run_stat = results[0].chomp
303 307 else
304 308 run_stat = 'Program exited normally'
305 309 end
306 310
307 311 logger.info "Stat line: #{running_stat_line}"
308 312
309 313 # extract running time
310 314 if res = /r(.*)u(.*)s/.match(running_stat_line)
311 315 seconds = (res[1].to_f + res[2].to_f)
312 316 time_stat = "Time used: #{seconds} sec."
313 317 else
314 318 seconds = nil
315 319 time_stat = "Time used: n/a sec."
316 320 end
317 321
318 322 # extract memory usage
319 323 if res = /s(.*)m/.match(running_stat_line)
320 324 memory_used = res[1].to_i
321 325 else
322 326 memory_used = -1
323 327 end
324 328
325 329 return {
326 330 :msg => "#{run_stat}\n#{time_stat}",
327 331 :running_time => seconds,
328 332 :exit_status => run_stat,
329 333 :memory_usage => memory_used
330 334 }
331 335 end
332 336
333 337 def update_user_start_time
334 338 user = User.find(session[:user_id])
335 339 user.update_start_time
336 340 end
337 341
338 342 def reject_announcement_refresh_when_logged_out
339 343 if not session[:user_id]
340 344 render :text => 'Access forbidden', :status => 403
341 345 end
342 346 end
343 347
344 348 end
345 349
You need to be logged in to leave comments. Login now