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: 9 inserted, 5 deleted

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