2 # -*- coding: utf-8 -*-
4 #= FeedGeneratorから起動するファイルマネージャWEBアプリケーション
6 #Autohr:: Kureha Hisame (http://lunardial.sakura.ne.jp/) & Yui Naruse (http://airemix.com/)
8 #Copyright:: Copyright 2009 FeedBlog Project (http://sourceforge.jp/projects/feedblog/)
14 require "rexml/document"
21 IMGPATH = "./../lunardial/xml/img/"
24 APPVERSION = "- FileManager for Ruby version 2.0.0.0 -<br>Copyright(c) 2009 Kureha.H (<a href=\"http://lunardial.sakura.ne.jp/\" target=\"_blank\">http://lunardial.sakura.ne.jp/</a>) & Yui Naruse (<a href=\"http://airemix.com/\" target=\"_blank\">http://airemix.com/</a>)"
26 APPTITLE = "FileManager for Ruby version 2.0.0.0"
29 UPLOADLIMIT = 2 * 1024 * 1024
37 # ruby-1.9.x以降ではファイルを開いた際、エンコードの指定を行わないとエラーの原因になります。
38 # ただしruby-1.8.6以前はエンコードの指定に対応していないため、独自メソッドを定義してファイルの入出力を行います。
40 # _arg[0]_ :: 入出力を行うファイルのパス
41 # _arg[1]_ :: モードの指定。例 : w:utf-8(書き込みモード・UTF-8エンコードでファイルを開く)
47 arg[1] = mode[/[^:]+/] if RUBY_VERSION < "1.8.7" && mode.include?(':')
48 rdonly_p = /\A[^:]*[wa+]/ !~ mode
50 rdonly_p = !(mode & (IO::WRONY | IO::RDWR))
53 f.flock(rdonly_p ? File::LOCK_SH : File::LOCK_EX)
77 # = WebSecurityExceptionクラス
79 # 独自定義の例外クラスです。WebFilerインスタンス内の処理でセキュリティ違反が発生した場合にthrowされます。
80 class WebSecurityException < Exception
83 # = FileExistedExceptionクラス
85 # 独自定義の例外クラスです。WebFileインスタンスの処理で、既に存在するファイルに上書きしようとした場合にthrowされます。
86 class FileExistedException < Exception
91 # テンプレートファイル(*.erb)を読み込み、管理するクラスです
95 # _template_ :: テンプレートファイル(*.erb)のパス
96 # _binding_ :: binding変数
97 def initialize(template, binding)
98 @erb = ERB.new(myopen(template, "r:utf-8") {|f| f.read}, nil, "-")
102 # テンプレートファイルの文字列を返却するメソッドです
104 @erb.result(@binding)
110 # Web上にローカルと同じフォルダ管理機能を利用できるクラスです
114 # _basepath_ :: クラス内で扱う最上位のフォルダ(root)とする実パス
115 def initialize(basepath)
117 @basepath << "/" unless @basepath[-1..-1] == "/"
120 raise "Target dir not found." unless File.directory?(pwd)
123 # ファイルに使用できない文字列を調べるための正規表現を定義します
124 FILEREGEXP = /\A[-_+~^0-9a-zA-Z]+(\.[-_+~^0-9a-zA-Z]+)*\z/
126 attr_reader :basepath
127 attr_accessor :relpath_list
129 # ディレクトリ内部のファイル・フォルダ一覧を取得するメソッド
131 filelist = Dir::entries(pwd)
133 filelist.delete("..")
138 # ディレクトリ内部のファイル・フォルダ一覧の詳細情報を取得するメソッド
140 filelist = Dir::entries(pwd)
142 filelist.delete("..")
145 filelist.each { |fname|
147 fileinfo[fname][:ftype] = File.ftype(pwd + "/" + fname)
148 fileinfo[fname][:atime] = File.atime(pwd + "/" + fname)
149 fileinfo[fname][:ctime] = File.ctime(pwd + "/" + fname)
150 fileinfo[fname][:mtime] = File.mtime(pwd + "/" + fname)
151 fileinfo[fname][:size] = File.size(pwd + "/" + fname) / 1000
159 File.ftype(pwd + File.basename(fname))
165 @relpath_list.delete_at(-1) unless @relpath_list.length == 0
166 elsif pathname.match(FILEREGEXP)
167 if File.directory?(pwd + "/" + File.basename(pathname))
168 @relpath_list << File.basename(pathname)
170 raise FileExistedException
173 raise WebSecurityException
177 # ディレクトリを絶対指定で移動するメソッド
178 def cd_abs(relpath_arr)
179 relpath_arr.each { |name|
180 unless name.match(FILEREGEXP)
181 raise WebSecurityException
184 @relpath_list = relpath_arr
194 if @relpath_list.length == 0
197 @relpath_list.join("/") << "/"
202 def upload(file, fname)
203 fname.gsub!(/( | )/, "_")
204 if File.exist?(pwd + File.basename(fname))
205 raise FileExistedException
206 elsif File.basename(fname).match(FILEREGEXP)
207 open(pwd + File.basename(fname), "w") do |f|
212 raise WebSecurityException
218 if File.exist?(pwd + File.basename(fname)) && fname.match(FILEREGEXP)
219 File.delete(pwd + File.basename(fname))
221 raise WebSecurityException
227 dirname.gsub!(/( | )/, "_")
228 if File.exist?(pwd + File.basename(dirname))
229 raise FileExistedException
230 elsif dirname.match(FILEREGEXP)
231 Dir.mkdir(pwd + File.basename(dirname))
233 raise WebSecurityException
237 # 内部が空のディレクトリを消去するメソッド
239 if File.exist?(pwd + File.basename(dirname)) && dirname.match(FILEREGEXP)
240 Dir.rmdir(pwd + File.basename(dirname))
242 raise WebSecurityException
250 # コントローラ部分に相当する処理を受け持つクラスです
252 def self.update_session(session, filer)
253 session["filelist"] = filer.ls.reverse
254 session["fileinfo"] = filer.lsinfo
255 session["pwd"] = filer.pwd
256 session["relpath_list"] = filer.relpath_list.join("/")
259 def Controller.MultiForm(cgi, session, params, db)
261 case params["action"]
264 filer = WebFiler.new(IMGPATH)
265 filer.relpath_list = params["relpath_list"].split("/")
266 Controller.update_session(session, filer);
268 if (cgi["updata"].size <= UPLOADLIMIT)
270 filer.upload(cgi["updata"], cgi["updata"].original_filename)
271 rescue FileExistedException
272 session["error"] = "既に同名のファイルが存在します!"
273 rescue WebSecurityException
274 session["error"] = "ファイル名に使用できない文字列が含まれています!"
277 session["error"] = "ファイルの容量が大きすぎます!"
280 Controller.update_session(session, filer);
282 session["info"] = "正常にアップロードが完了しました。" if session["error"] == ""
286 filer = WebFiler.new(IMGPATH)
287 filer.relpath_list = params["relpath_list"].split("/")
288 Controller.update_session(session, filer);
290 session["dellist"] = []
292 session["filelist"].each do |file|
293 if params["filename_" + file] == "delete" && filer.ftype(file) == "file"
296 elsif params["filename_" + file] == "delete" && filer.ftype(file) == "directory"
301 mes = "対象のディレクトリは空ではありません!<br>"
302 mes << "ディレクトリを削除する場合は、事前にディレクトリ内部の全てのファイルを削除してください。"
303 session["error"] = mes
308 Controller.update_session(session, filer);
310 session["info"] = "正常にファイルの削除が完了しました。" if session["error"] == "" && count != 0
314 filer = WebFiler.new(IMGPATH)
315 filer.relpath_list = params["relpath_list"].split("/")
316 Controller.update_session(session, filer);
319 filer.mkdir(params["dirname"])
320 rescue FileExistedException
321 session["error"] = "既に同名のディレクトリが存在します!"
322 rescue WebSecurityException
323 session["error"] = "ディレクトリ名に使用できない文字列が含まれています!"
326 Controller.update_session(session, filer);
328 session["info"] = "正常にディレクトリの作成が完了しました。" if session["error"] == ""
332 filer = WebFiler.new(IMGPATH)
333 filer.relpath_list = params["relpath_list"].split("/")
334 Controller.update_session(session, filer);
337 filer.cd(params["arg"])
339 session["error"] = "移動先のディレクトリが見つかりません!"
342 Controller.update_session(session, filer);
346 filer = WebFiler.new(IMGPATH)
347 filer.relpath_list = params["relpath_list"].split("/")
348 Controller.update_session(session, filer);
350 if params["arg"].to_i >= 0
353 params["arg"].to_i.times { |i|
354 movepath << params["relpath_list"].split("/")[i]
356 filer.cd_abs(movepath)
358 session["error"] = "移動先のディレクトリが見つかりません!"
361 session["error"] = "移動先のディレクトリが見つかりません!"
364 Controller.update_session(session, filer);
368 filer = WebFiler.new(IMGPATH)
369 filer.relpath_list = params["relpath_list"].split("/")
370 Controller.update_session(session, filer);
374 filer = WebFiler.new(IMGPATH)
375 Controller.update_session(session, filer);
382 # SESSION変数、パラメータなどを取得します
384 session = CGI::Session.new(cgi)
385 params = Hash[*cgi.params.to_a.map{|k, v| [k, v[0].to_s]}.flatten]
386 params.each { |k, v| params[k] = cgi[k].read if cgi[k].respond_to?(:read) && k != "updata"}
389 if params["mode"] == "logout"
390 session["login"] = nil
391 session["logini"] = nil
392 session["password"] = nil
398 if session["login"] == "true"
399 db = PStore.new("./work/#{session.session_id}_file.dat")
401 session["error"] = ""
404 Controller.MultiForm(cgi, session, params, db)
408 if session["login"] != "true"
409 # if session["login"] == "true"
410 # セッションが存在しない場合は強制的にエラーページに遷移
411 htmlwriter = HtmlWriter.new("./erbtemp/fileindex.html.erb", binding)
413 htmlwriter = HtmlWriter.new("./erbtemp/filemanager.html.erb", binding)
418 cgi.out{htmlwriter.to_code}
420 # エラーが発生した場合、それを画面に表示します
421 htmlwriter = HtmlWriter.new("./erbtemp/exception.html.erb", binding)
422 cgi.out{ htmlwriter.to_code }
429 # エラーが発生した場合、それを画面に表示します
430 detail = ("%s: %s (%s)\n" %
431 [evar.backtrace[0], evar.message, evar.send('class')]) +
432 evar.backtrace[1..-1].join("\n")
433 puts "content-type: text/html\n\n<plaintext>\n" + detail