1 $:.unshift File.join(File.dirname(__FILE__), 'lib')
14 @lockfile ||= Lockfile.new "#{File.expand_path(@path)}.lock", :timeout => 4
19 yield @initial_content
20 @initial_content = nil
21 until (buf = @file.read(BUFFER_LEN)).nil?
22 yield @cipher.update(buf)
43 (class << self; Application; end)
46 @depot = Depot.new(settings.depot_path) if @depot.nil? || settings.depot_path != @depot.path
51 class Application < Sinatra::Base
52 def self.authentication_method(method,options={})
53 require "coquelicot/auth/#{method}"
54 set :auth_method, method
55 include (eval "Coquelicot::Auth::#{method.to_s.capitalize}")
56 options.each{|k,v| set k,v }
59 set :app_file, __FILE__
60 set :default_expire, 60
61 set :maximum_expire, 60 * 24 * 30 # 1 month
62 set :gone_period, 10080
63 set :filename_length, 20
64 set :random_pass_length, 16
65 set :depot_path, Proc.new { File.join(root, 'files') }
67 authentication_method :simplepass, :upload_password => 'a94a8fe5ccb19ba61c4c0873d391e987982fbbd3'
69 GetText::bindtextdomain('coquelicot')
71 GetText::set_current_locale(params[:lang] || request.env['HTTP_ACCEPT_LANGUAGE'] || 'en')
75 content_type 'text/css', :charset => 'utf-8'
84 "#{Coquelicot.gen_random_pass}"
87 get '/ready/:link' do |link|
88 link, pass = link.split '-' if link.include? '-'
90 file = Coquelicot.depot.get_file(link, nil)
91 rescue Errno::ENOENT => ex
94 @expire_at = file.expire_at
95 @base = request.url.gsub(/\/ready\/[^\/]*$/, '')
101 @url = "#{@base}/#{@name}"
105 post '/authenticate' do
106 pass unless request.xhr?
107 unless authenticate(params) then
108 error 403, "Forbidden"
114 # if JS is disabled upload_token might be nil
115 params['upload_token'] = JSON.parse(params['upload_token']) unless params['upload_token'].nil?
116 unless authenticate(params) then
119 if params[:file] then
120 tmpfile = params[:file][:tempfile]
121 name = params[:file][:filename]
123 if tmpfile.nil? || name.nil? then
124 @error = "No file selected"
127 if tmpfile.lstat.size == 0 then
128 @error = "#{name} is empty"
131 if params[:expire].nil? or params[:expire].to_i == 0 then
132 params[:expire] = settings.default_expire
133 elsif params[:expire].to_i > settings.maximum_expire then
136 expire_at = Time.now + 60 * params[:expire].to_i
137 one_time_only = params[:one_time] and params[:one_time] == 'true'
138 if params[:file_key].nil? or params[:file_key].empty?then
139 pass = Coquelicot.gen_random_pass
141 pass = params[:file_key]
143 src = params[:file][:tempfile]
144 link = Coquelicot.depot.add_file(
146 { "Expire-at" => expire_at.to_i,
147 "One-time-only" => one_time_only,
148 "Filename" => params[:file][:filename],
149 "Length" => src.stat.size,
150 "Content-Type" => params[:file][:type],
152 redirect "ready/#{link}-#{pass}" if params[:file_key].nil? or params[:file_key].empty?
153 redirect "ready/#{link}"
157 throw :halt, [410, haml(:expired)]
160 def send_stored_file(file)
161 last_modified file.created_at.httpdate
162 attachment file.meta['Filename']
163 response['Content-Length'] = "#{file.meta['Length']}"
164 response['Content-Type'] = file.meta['Content-Type'] || 'application/octet-stream'
165 throw :halt, [200, file]
168 def send_link(link, pass)
169 file = Coquelicot.depot.get_file(link, pass)
170 return false if file.nil?
171 return expired if file.expired?
173 if file.one_time_only?
175 # unlocking done in file.close
177 rescue Lockfile::TimeoutLockError
178 error 409, "Download currently in progress"
181 send_stored_file(file)
184 get '/:link-:pass' do |link, pass|
185 link = Coquelicot.remap_base32_extra_characters(link)
186 pass = Coquelicot.remap_base32_extra_characters(pass)
187 not_found unless send_link(link, pass)
190 get '/:link' do |link|
191 link = Coquelicot.remap_base32_extra_characters(link)
192 not_found unless Coquelicot.depot.file_exists? link
197 post '/:link' do |link|
198 pass = params[:file_key]
199 return 403 if pass.nil? or pass.empty?
201 # send Forbidden even if file is not found
202 return 403 unless send_link(link, pass)
203 rescue Coquelicot::BadKey => ex
210 url = request.scheme + "://"
212 if request.scheme == "https" && request.port != 443 ||
213 request.scheme == "http" && request.port != 80
214 url << ":#{request.port}"
216 url << request.script_name
221 Coquelicot.settings.auth_method
227 Coquelicot::Application.run! if __FILE__ == $0