OSDN Git Service

- Fix plugin bug (add param "mode")
[feedblog/feedgenerator.git] / filemanager.rb
index a57cb2c..f7275df 100644 (file)
@@ -18,13 +18,16 @@ require "fileutils"
 # インターフェースのテーブルの幅
 TABLEWIDTH = 800
 # 画像フォルダの場所を定義
-IMGPATH = "./../lunardial/xml/img"
+IMGPATH = "./../lunardial/xml/img/"
 
 # バージョン情報を示す文字列です
 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>)"
 # タイトル領域に表示される文字列です
 APPTITLE = "FileManager for Ruby version 2.0.0.0"
 
+# アップロード可能な最大ファイルサイズ
+UPLOADLIMIT = 2 * 1024 * 1024
+
 # = Objectクラス
 #
 # 基本クラスのオーバーライドを行います
@@ -38,9 +41,16 @@ class Object
   # _arg[1]_ :: モードの指定。例 : w:utf-8(書き込みモード・UTF-8エンコードでファイルを開く)
   def myopen(*arg)
     mode = arg[1]
-    arg[1] = mode[/[^:]+/] if mode && RUBY_VERSION < "1.8.7" && mode.include?(':')
+    rdonly_p = true
+    case mode
+    when String
+      arg[1] = mode[/[^:]+/] if RUBY_VERSION < "1.8.7" && mode.include?(':')
+      rdonly_p = /\A[^:]*[wa+]/ !~ mode
+    when Numeric
+      rdonly_p = !(mode & (IO::WRONY | IO::RDWR))
+    end
     open(*arg) do |f|
-      f.flock(/\A\w+r/ =~ mode ? File::LOCK_SH : File::LOCK_EX)
+      f.flock(rdonly_p ? File::LOCK_SH : File::LOCK_EX)
       return yield(f)
     end
   end
@@ -64,6 +74,18 @@ class String
   end
 end
 
+# = WebSecurityExceptionクラス
+#
+# 独自定義の例外クラスです。WebFilerインスタンス内の処理でセキュリティ違反が発生した場合にthrowされます。
+class WebSecurityException < Exception
+end
+
+# = FileExistedExceptionクラス
+#
+# 独自定義の例外クラスです。WebFileインスタンスの処理で、既に存在するファイルに上書きしようとした場合にthrowされます。
+class FileExistedException < Exception
+end
+
 # = HtmlWriterクラス
 # 
 # テンプレートファイル(*.erb)を読み込み、管理するクラスです
@@ -73,7 +95,7 @@ class HtmlWriter
   # _template_ :: テンプレートファイル(*.erb)のパス
   # _binding_ :: binding変数
   def initialize(template, binding)
-    @erb = ERB.new(myopen(template, "r") {|f| f.read}, nil, "-")
+    @erb = ERB.new(myopen(template, "r:utf-8") {|f| f.read}, nil, "-")
     @binding = binding
   end
   
@@ -92,26 +114,29 @@ class WebFiler
   # _basepath_ :: クラス内で扱う最上位のフォルダ(root)とする実パス
   def initialize(basepath)
     @basepath = basepath
-    @basepath << "/" unless @basepath[-1] == "/"
+    @basepath << "/" unless @basepath[-1..-1] == "/"
     @relpath_list = []
     
-    raise "Target dir not found." unless check_dir_exist?(pwd)
+    raise "Target dir not found." unless File.directory?(pwd)
   end
   
-  attr_reader :basepath, :relpath_list
+  # ファイルに使用できない文字列を調べるための正規表現を定義します
+  FILEREGEXP = /\A[-_+~^0-9a-zA-Z]+(\.[-_+~^0-9a-zA-Z]+)*\z/
+  
+  attr_reader :basepath
   attr_accessor :relpath_list
   
   # ディレクトリ内部のファイル・フォルダ一覧を取得するメソッド
-  def dir
+  def ls
     filelist = Dir::entries(pwd)
     filelist.delete(".")
     filelist.delete("..")
     
-    filelist
+    filelist.sort!
   end
   
   # ディレクトリ内部のファイル・フォルダ一覧の詳細情報を取得するメソッド
-  def ls
+  def lsinfo
     filelist = Dir::entries(pwd)
     filelist.delete(".")
     filelist.delete("..")
@@ -138,17 +163,27 @@ class WebFiler
   def cd(pathname)
     if pathname == ".."
       @relpath_list.delete_at(-1) unless @relpath_list.length == 0
-    elsif pathname.match(/([^\w]|\.\.|\/|\.)/) == nil
-      if check_dir_exist?(pwd + "/" + File.basename(pathname))
+    elsif pathname.match(FILEREGEXP)
+      if File.directory?(pwd + "/" + File.basename(pathname))
         @relpath_list << File.basename(pathname)
       else
-        raise "Target dir not found."
+        raise FileExistedException
       end
     else
-      
+      raise WebSecurityException
     end
   end
   
+  # ディレクトリを絶対指定で移動するメソッド
+  def cd_abs(relpath_arr)
+    relpath_arr.each { |name|
+      unless name.match(FILEREGEXP)
+        raise WebSecurityException
+      end
+    }
+    @relpath_list = relpath_arr
+  end
+  
   # 現在のディレクトリを表示するメソッド
   def pwd
     @basepath + relpath
@@ -165,52 +200,46 @@ class WebFiler
   
   # ファイルをアップロードするメソッド
   def upload(file, fname)
-    if File.exist?(pwd + fname)
-      raise "Target file exist."
-    elsif
-      fname.match(/([^\w]|\.\.)/) == nil
-      raise "Wrong file name."
-    else
+    fname.gsub!(/( | )/, "_")
+    if File.exist?(pwd + File.basename(fname))
+      raise FileExistedException
+    elsif File.basename(fname).match(FILEREGEXP)
       open(pwd + File.basename(fname), "w") do |f|
         f.binmode
-        f.write file
+        f.write file.read
       end
+    else
+      raise WebSecurityException
     end
   end
   
   # ファイルを消去するメソッド
   def delete(fname)
-    if File.exist?(pwd + File.basename(fname))
+    if File.exist?(pwd + File.basename(fname)) && fname.match(FILEREGEXP)
       File.delete(pwd + File.basename(fname))
     else
-      raise "Target file is not exist."
+      raise WebSecurityException
     end
   end
   
   # ディレクトリを製作するメソッド
   def mkdir(dirname)
+    dirname.gsub!(/( | )/, "_")
     if File.exist?(pwd + File.basename(dirname))
-      raise "Target directory is exist."
-    else
+      raise FileExistedException
+    elsif dirname.match(FILEREGEXP)
       Dir.mkdir(pwd + File.basename(dirname))
+    else
+      raise WebSecurityException
     end
   end
   
   # 内部が空のディレクトリを消去するメソッド
   def rmdir(dirname)
-    if File.exist?(pwd + File.basename(dirname))
+    if File.exist?(pwd + File.basename(dirname)) && dirname.match(FILEREGEXP)
       Dir.rmdir(pwd + File.basename(dirname))
     else
-      raise "Target directory is not exist."
-    end
-  end
-  
-  # 対象ディレクトリが存在するかを確認するメソッド
-  def check_dir_exist?(target_dir)
-    unless (File.exist?(target_dir) || File.ftype(target_dir) != "directory")
-      false
-    else
-      true
+      raise WebSecurityException
     end
   end
   
@@ -220,78 +249,130 @@ end
 #
 # コントローラ部分に相当する処理を受け持つクラスです
 class Controller
+  def self.update_session(session, filer)
+    session["filelist"] = filer.ls.reverse
+    session["fileinfo"] = filer.lsinfo
+    session["pwd"] = filer.pwd
+    session["relpath_list"] = filer.relpath_list.join("/")
+  end
+  
   def Controller.MultiForm(cgi, session, params, db)
     db.transaction do
       case params["action"]
         # アップロード時
         when "upload"
         filer = WebFiler.new(IMGPATH)
-        filer.relpath_list = db["relpath_list"]
+        filer.relpath_list = params["relpath_list"].split("/")
+        Controller.update_session(session, filer);
         
-        filer.upload(params["updata"], cgi["updata"].original_filename)
+        if (cgi["updata"].size <= UPLOADLIMIT)
+          begin
+            filer.upload(cgi["updata"], cgi["updata"].original_filename)
+          rescue FileExistedException
+            session["error"] = "既に同名のファイルが存在します!"
+          rescue WebSecurityException
+            session["error"] = "ファイル名に使用できない文字列が含まれています!"
+          end
+        else
+          session["error"] = "ファイルの容量が大きすぎます!"
+        end
         
-        db["filelist"] = filer.dir
-        db["fileinfo"] = filer.ls
+        Controller.update_session(session, filer);
         
-        db["info"] = "正常にアップロードが完了しました。" if db["error"] == ""
+        session["info"] = "正常にアップロードが完了しました。" if session["error"] == ""
         
         # 削除時
         when "delete"
         filer = WebFiler.new(IMGPATH)
-        filer.relpath_list = db["relpath_list"]
+        filer.relpath_list = params["relpath_list"].split("/")
+        Controller.update_session(session, filer);
         
-        db["dellist"] = []
-        db["filelist"].each do |file|
+        session["dellist"] = []
+        count = 0
+        session["filelist"].each do |file|
           if params["filename_" + file] == "delete" && filer.ftype(file) == "file"
             filer.delete(file) 
+            count = count + 1
           elsif params["filename_" + file] == "delete" && filer.ftype(file) == "directory"
             begin
               filer.rmdir(file)
-            rescue => ever
+              count = count + 1
+            rescue
               mes = "対象のディレクトリは空ではありません!<br>"
               mes << "ディレクトリを削除する場合は、事前にディレクトリ内部の全てのファイルを削除してください。"
-              db["error"] = mes
+              session["error"] = mes
             end
           end
         end
         
-        db["filelist"] = filer.dir
-        db["fileinfo"] = filer.ls
+        Controller.update_session(session, filer);
         
-        db["info"] = "正常にファイルの削除が完了しました。" if db["error"] == ""
+        session["info"] = "正常にファイルの削除が完了しました。" if session["error"] == "" && count != 0
         
         # ディレクトリ製作時
         when "mkdir"
         filer = WebFiler.new(IMGPATH)
-        filer.relpath_list = db["relpath_list"]
+        filer.relpath_list = params["relpath_list"].split("/")
+        Controller.update_session(session, filer);
         
-        filer.mkdir(params["dirname"])
+        begin
+          filer.mkdir(params["dirname"])
+        rescue FileExistedException
+          session["error"] = "既に同名のディレクトリが存在します!"
+        rescue WebSecurityException
+          session["error"] = "ディレクトリ名に使用できない文字列が含まれています!"
+        end
         
-        db["filelist"] = filer.dir
-        db["fileinfo"] = filer.ls
+        Controller.update_session(session, filer);
         
-        db["info"] = "正常にディレクトリの作成が完了しました。" if db["error"] == ""
+        session["info"] = "正常にディレクトリの作成が完了しました。" if session["error"] == ""
         
         # ディレクトリ移動時
         when "cd"
         filer = WebFiler.new(IMGPATH)
-        filer.relpath_list = db["relpath_list"]
+        filer.relpath_list = params["relpath_list"].split("/")
+        Controller.update_session(session, filer);
+        
+        begin
+          filer.cd(params["arg"])
+        rescue
+          session["error"] = "移動先のディレクトリが見つかりません!"
+        end
+        
+        Controller.update_session(session, filer);
+        
+        # 絶対位置でのディレクトリ移動時
+        when "cd_abs"
+        filer = WebFiler.new(IMGPATH)
+        filer.relpath_list = params["relpath_list"].split("/")
+        Controller.update_session(session, filer);
         
-        filer.cd(params["arg"])
+        if params["arg"].to_i >= 0
+          begin
+            movepath = []
+            params["arg"].to_i.times { |i|
+              movepath << params["relpath_list"].split("/")[i]
+            }
+            filer.cd_abs(movepath)
+          rescue
+            session["error"] = "移動先のディレクトリが見つかりません!"
+          end
+        else
+          session["error"] = "移動先のディレクトリが見つかりません!"
+        end
         
-        db["filelist"] = filer.dir
-        db["fileinfo"] = filer.ls
-        db["pwd"] = filer.pwd
-        db["relpath_list"] = filer.relpath_list
+        Controller.update_session(session, filer);
+        
+        # 表示更新時
+        when "refresh"
+        filer = WebFiler.new(IMGPATH)
+        filer.relpath_list = params["relpath_list"].split("/")
+        Controller.update_session(session, filer);
         
         # 初期表示
       else
         filer = WebFiler.new(IMGPATH)
-        
-        db["filelist"] = filer.dir
-        db["fileinfo"] = filer.ls
-        db["pwd"] = filer.pwd
-        db["relpath_list"] = filer.relpath_list
+        Controller.update_session(session, filer);
       end
     end
   end
@@ -302,7 +383,7 @@ def main
   cgi = CGI.new
   session = CGI::Session.new(cgi)
   params = Hash[*cgi.params.to_a.map{|k, v| [k, v[0].to_s]}.flatten]
-  params.each { |k, v| params[k] = cgi[k].read if cgi[k].respond_to?(:read) }
+  params.each { |k, v| params[k] = cgi[k].read if cgi[k].respond_to?(:read) && k != "updata"}
   
   # ログアウト処理
   if params["mode"] == "logout"
@@ -316,10 +397,8 @@ def main
   # セッションが有効な場合のも実行します
   if session["login"] == "true"
     db = PStore.new("./work/#{session.session_id}_file.dat")
-    db.transaction do
-      db["info"] = ""
-      db["error"] = ""
-    end
+    session["info"] = ""
+    session["error"] = ""
     
     # コントローラ部分
     Controller.MultiForm(cgi, session, params, db)
@@ -340,7 +419,7 @@ def main
   rescue => exception
     # エラーが発生した場合、それを画面に表示します
     htmlwriter = HtmlWriter.new("./erbtemp/exception.html.erb", binding)
-    cgi.out{htmlwriter.to_code}
+    cgi.out{ htmlwriter.to_code }
   end
 end