OSDN Git Service

fixed filename-bug of upload file contains space/
[feedblog/feedgenerator.git] / filemanager.rb
1 #!/usr/local/bin/ruby
2 # -*- coding: utf-8 -*-
3 #
4 #= FeedGeneratorから起動するファイルマネージャWEBアプリケーション
5 #
6 #Autohr::    Kureha Hisame (http://lunardial.sakura.ne.jp/) & Yui Naruse (http://airemix.com/)
7 #Version::   2.0.0.0
8 #Copyright:: Copyright 2009 FeedBlog Project (http://sourceforge.jp/projects/feedblog/)
9 #License::   GPLv3
10
11 require "cgi"
12 require "cgi/session"
13 require "erb"
14 require "rexml/document"
15 require "pstore"
16 require "fileutils"
17
18 # インターフェースのテーブルの幅
19 TABLEWIDTH = 800
20 # 画像フォルダの場所を定義
21 IMGPATH = "./../lunardial/xml/img"
22
23 # バージョン情報を示す文字列です
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>)"
25 # タイトル領域に表示される文字列です
26 APPTITLE = "FileManager for Ruby version 2.0.0.0"
27
28 # アップロード可能な最大ファイルサイズ
29 UPLOADLIMIT = 2 * 1024 * 1024
30
31 # = Objectクラス
32 #
33 # 基本クラスのオーバーライドを行います
34 class Object
35   # myopenメソッド
36   #
37   # ruby-1.9.x以降ではファイルを開いた際、エンコードの指定を行わないとエラーの原因になります。
38   # ただしruby-1.8.6以前はエンコードの指定に対応していないため、独自メソッドを定義してファイルの入出力を行います。
39   #
40   # _arg[0]_ :: 入出力を行うファイルのパス
41   # _arg[1]_ :: モードの指定。例 : w:utf-8(書き込みモード・UTF-8エンコードでファイルを開く)
42   def myopen(*arg)
43     mode = arg[1]
44     arg[1] = mode[/[^:]+/] if mode && RUBY_VERSION < "1.8.7" && mode.include?(':')
45     open(*arg) do |f|
46       f.flock(/\A\w+r/ =~ mode ? File::LOCK_SH : File::LOCK_EX)
47       return yield(f)
48     end
49   end
50 end
51
52 class NilClass
53   def blank?
54     nil?
55   end
56 end
57
58 class Array
59   def blank?
60     empty?
61   end
62 end
63
64 class String
65   def blank?
66     empty?
67   end
68 end
69
70 # = HtmlWriterクラス
71
72 # テンプレートファイル(*.erb)を読み込み、管理するクラスです
73 class HtmlWriter
74   # 初期化メソッドです
75   #
76   # _template_ :: テンプレートファイル(*.erb)のパス
77   # _binding_ :: binding変数
78   def initialize(template, binding)
79     @erb = ERB.new(myopen(template, "r") {|f| f.read}, nil, "-")
80     @binding = binding
81   end
82   
83   # テンプレートファイルの文字列を返却するメソッドです
84   def to_code
85     @erb.result(@binding)
86   end
87 end
88
89 # = WebFilerクラス
90 #
91 # Web上にローカルと同じフォルダ管理機能を利用できるクラスです
92 class WebFiler
93   # 初期化メソッドです
94   #
95   # _basepath_ :: クラス内で扱う最上位のフォルダ(root)とする実パス
96   def initialize(basepath)
97     @basepath = basepath
98     @basepath << "/" unless @basepath[-1] == "/"
99     @relpath_list = []
100     
101     raise "Target dir not found." unless check_dir_exist?(pwd)
102   end
103   
104   attr_reader :basepath
105   attr_accessor :relpath_list
106   
107   # ディレクトリ内部のファイル・フォルダ一覧を取得するメソッド
108   def ls
109     filelist = Dir::entries(pwd)
110     filelist.delete(".")
111     filelist.delete("..")
112     
113     filelist.sort!
114   end
115   
116   # ディレクトリ内部のファイル・フォルダ一覧の詳細情報を取得するメソッド
117   def lsinfo
118     filelist = Dir::entries(pwd)
119     filelist.delete(".")
120     filelist.delete("..")
121     
122     fileinfo = {}
123     filelist.each { |fname| 
124       fileinfo[fname] = {}
125       fileinfo[fname][:ftype] = File.ftype(pwd + "/" + fname)
126       fileinfo[fname][:atime] = File.atime(pwd + "/" + fname)
127       fileinfo[fname][:ctime] = File.ctime(pwd + "/" + fname)
128       fileinfo[fname][:mtime] = File.mtime(pwd + "/" + fname)
129       fileinfo[fname][:size] = File.size(pwd + "/" + fname) / 1000
130     }
131     
132     fileinfo
133   end
134   
135   # ファイルタイプを取得するメソッド
136   def ftype(fname)
137     File.ftype(pwd + File.basename(fname))
138   end
139   
140   # ディレクトリを移動するメソッド
141   def cd(pathname)
142     if pathname == ".."
143       @relpath_list.delete_at(-1) unless @relpath_list.length == 0
144     elsif pathname.match(/([^\w]|\.\.|\/|\.)/) == nil
145       if check_dir_exist?(pwd + "/" + File.basename(pathname))
146         @relpath_list << File.basename(pathname)
147       else
148         raise "Target dir not found."
149       end
150     else
151       
152     end
153   end
154   
155   # 現在のディレクトリを表示するメソッド
156   def pwd
157     @basepath + relpath
158   end
159   
160   # 相対パスを算出するメソッド
161   def relpath
162     if @relpath_list.length == 0
163       ""
164     else
165       @relpath_list.join("/") << "/"
166     end
167   end
168   
169   # ファイルをアップロードするメソッド
170   def upload(file, fname)
171     if File.exist?(pwd + fname)
172       raise "Target file exist."
173     elsif
174       fname.match(/([^\w]|\.\.)/) == nil
175       raise "Wrong file name."
176     else
177       open(pwd + File.basename(fname), "w") do |f|
178         f.binmode
179         f.write file.read
180       end
181     end
182   end
183   
184   # ファイルを消去するメソッド
185   def delete(fname)
186     if File.exist?(pwd + File.basename(fname))
187       File.delete(pwd + File.basename(fname))
188     else
189       raise "Target file is not exist."
190     end
191   end
192   
193   # ディレクトリを製作するメソッド
194   def mkdir(dirname)
195     if File.exist?(pwd + File.basename(dirname))
196       raise "Target directory is exist."
197     else
198       Dir.mkdir(pwd + File.basename(dirname))
199     end
200   end
201   
202   # 内部が空のディレクトリを消去するメソッド
203   def rmdir(dirname)
204     if File.exist?(pwd + File.basename(dirname))
205       Dir.rmdir(pwd + File.basename(dirname))
206     else
207       raise "Target directory is not exist."
208     end
209   end
210   
211   # 対象ディレクトリが存在するかを確認するメソッド
212   def check_dir_exist?(target_dir)
213     unless (File.exist?(target_dir) || File.ftype(target_dir) != "directory")
214       false
215     else
216       true
217     end
218   end
219   
220 end
221
222 # = Controllerクラス
223 #
224 # コントローラ部分に相当する処理を受け持つクラスです
225 class Controller
226   def Controller.MultiForm(cgi, session, params, db)
227     db.transaction do
228       case params["action"]
229         # アップロード時
230         when "upload"
231         filer = WebFiler.new(IMGPATH)
232         filer.relpath_list = db["relpath_list"]
233         
234         if (cgi["updata"].size <= UPLOADLIMIT)
235           filer.upload(cgi["updata"], cgi["updata"].original_filename.gsub(/ /, "_"))
236         else
237           db["error"] = "ファイルの容量が大きすぎます!"
238         end
239         
240         db["filelist"] = filer.ls.reverse
241         db["fileinfo"] = filer.lsinfo
242         
243         db["info"] = "正常にアップロードが完了しました。" if db["error"] == ""
244         
245         # 削除時
246         when "delete"
247         filer = WebFiler.new(IMGPATH)
248         filer.relpath_list = db["relpath_list"]
249         
250         db["dellist"] = []
251         db["filelist"].each do |file|
252           if params["filename_" + file] == "delete" && filer.ftype(file) == "file"
253             filer.delete(file) 
254           elsif params["filename_" + file] == "delete" && filer.ftype(file) == "directory"
255             begin
256               filer.rmdir(file)
257             rescue => ever
258               mes = "対象のディレクトリは空ではありません!<br>"
259               mes << "ディレクトリを削除する場合は、事前にディレクトリ内部の全てのファイルを削除してください。"
260               db["error"] = mes
261             end
262           end
263         end
264         
265         db["filelist"] = filer.ls.reverse
266         db["fileinfo"] = filer.lsinfo
267         
268         db["info"] = "正常にファイルの削除が完了しました。" if db["error"] == ""
269         
270         # ディレクトリ製作時
271         when "mkdir"
272         filer = WebFiler.new(IMGPATH)
273         filer.relpath_list = db["relpath_list"]
274         
275         filer.mkdir(params["dirname"])
276         
277         db["filelist"] = filer.ls.reverse
278         db["fileinfo"] = filer.lsinfo
279         
280         db["info"] = "正常にディレクトリの作成が完了しました。" if db["error"] == ""
281         
282         # ディレクトリ移動時
283         when "cd"
284         filer = WebFiler.new(IMGPATH)
285         filer.relpath_list = db["relpath_list"]
286         
287         filer.cd(params["arg"])
288         
289         db["filelist"] = filer.ls.reverse
290         db["fileinfo"] = filer.lsinfo
291         db["pwd"] = filer.pwd
292         db["relpath_list"] = filer.relpath_list
293         
294         # 初期表示
295       else
296         filer = WebFiler.new(IMGPATH)
297         
298         db["filelist"] = filer.ls.reverse
299         db["fileinfo"] = filer.lsinfo
300         db["pwd"] = filer.pwd
301         db["relpath_list"] = filer.relpath_list
302       end
303     end
304   end
305 end
306
307 def main
308   # SESSION変数、パラメータなどを取得します
309   cgi = CGI.new
310   session = CGI::Session.new(cgi)
311   params = Hash[*cgi.params.to_a.map{|k, v| [k, v[0].to_s]}.flatten]
312   params.each { |k, v| params[k] = cgi[k].read if cgi[k].respond_to?(:read) && k != "updata"}
313   
314   # ログアウト処理
315   if params["mode"] == "logout"
316     session["login"] = nil
317     session["logini"] = nil
318     session["password"] = nil
319     session.delete
320   end
321   
322   # メインコントローラー
323   # セッションが有効な場合のも実行します
324   if session["login"] == "true"
325     db = PStore.new("./work/#{session.session_id}_file.dat")
326     db.transaction do
327       db["info"] = ""
328       db["error"] = ""
329     end
330     
331     # コントローラ部分
332     Controller.MultiForm(cgi, session, params, db)
333   end
334   
335   # ビュー部分
336   if session["login"] != "true"
337     # if session["login"] == "true"
338     # セッションが存在しない場合は強制的にエラーページに遷移
339     htmlwriter = HtmlWriter.new("./erbtemp/fileindex.html.erb", binding)
340   else
341     htmlwriter = HtmlWriter.new("./erbtemp/filemanager.html.erb", binding)
342   end
343   
344   # 実際にHTMLを出力します
345   begin
346     cgi.out{htmlwriter.to_code}
347   rescue => exception
348     # エラーが発生した場合、それを画面に表示します
349     htmlwriter = HtmlWriter.new("./erbtemp/exception.html.erb", binding)
350     cgi.out{htmlwriter.to_code}
351   end
352 end
353
354 begin
355   main
356 rescue => evar
357   # エラーが発生した場合、それを画面に表示します
358   detail = ("%s: %s (%s)\n" %
359   [evar.backtrace[0], evar.message, evar.send('class')]) +
360   evar.backtrace[1..-1].join("\n")
361   puts "content-type: text/html\n\n<plaintext>\n" + detail
362 end