OSDN Git Service

modieifed setting path only.
[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     rdonly_p = true
45     case mode
46     when String
47       arg[1] = mode[/[^:]+/] if RUBY_VERSION < "1.8.7" && mode.include?(':')
48       rdonly_p = /\A[^:]*[wa+]/ !~ mode
49     when Numeric
50       rdonly_p = !(mode & (IO::WRONY | IO::RDWR))
51     end
52     open(*arg) do |f|
53       f.flock(rdonly_p ? File::LOCK_SH : File::LOCK_EX)
54       return yield(f)
55     end
56   end
57 end
58
59 class NilClass
60   def blank?
61     nil?
62   end
63 end
64
65 class Array
66   def blank?
67     empty?
68   end
69 end
70
71 class String
72   def blank?
73     empty?
74   end
75 end
76
77 # = WebSecurityExceptionクラス
78 #
79 # 独自定義の例外クラスです。WebFilerインスタンス内の処理でセキュリティ違反が発生した場合にthrowされます。
80 class WebSecurityException < Exception
81 end
82
83 # = FileExistedExceptionクラス
84 #
85 # 独自定義の例外クラスです。WebFileインスタンスの処理で、既に存在するファイルに上書きしようとした場合にthrowされます。
86 class FileExistedException < Exception
87 end
88
89 # = HtmlWriterクラス
90
91 # テンプレートファイル(*.erb)を読み込み、管理するクラスです
92 class HtmlWriter
93   # 初期化メソッドです
94   #
95   # _template_ :: テンプレートファイル(*.erb)のパス
96   # _binding_ :: binding変数
97   def initialize(template, binding)
98     @erb = ERB.new(myopen(template, "r:utf-8") {|f| f.read}, nil, "-")
99     @binding = binding
100   end
101   
102   # テンプレートファイルの文字列を返却するメソッドです
103   def to_code
104     @erb.result(@binding)
105   end
106 end
107
108 # = WebFilerクラス
109 #
110 # Web上にローカルと同じフォルダ管理機能を利用できるクラスです
111 class WebFiler
112   # 初期化メソッドです
113   #
114   # _basepath_ :: クラス内で扱う最上位のフォルダ(root)とする実パス
115   def initialize(basepath)
116     @basepath = basepath
117     @basepath << "/" unless @basepath[-1..-1] == "/"
118     @relpath_list = []
119     
120     raise "Target dir not found." unless File.directory?(pwd)
121   end
122   
123   # ファイルに使用できない文字列を調べるための正規表現を定義します
124   FILEREGEXP = /\A[-_+~^0-9a-zA-Z]+(\.[-_+~^0-9a-zA-Z]+)*\z/
125   
126   attr_reader :basepath
127   attr_accessor :relpath_list
128   
129   # ディレクトリ内部のファイル・フォルダ一覧を取得するメソッド
130   def ls
131     filelist = Dir::entries(pwd)
132     filelist.delete(".")
133     filelist.delete("..")
134     
135     filelist.sort!
136   end
137   
138   # ディレクトリ内部のファイル・フォルダ一覧の詳細情報を取得するメソッド
139   def lsinfo
140     filelist = Dir::entries(pwd)
141     filelist.delete(".")
142     filelist.delete("..")
143     
144     fileinfo = {}
145     filelist.each { |fname| 
146       fileinfo[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
152     }
153     
154     fileinfo
155   end
156   
157   # ファイルタイプを取得するメソッド
158   def ftype(fname)
159     File.ftype(pwd + File.basename(fname))
160   end
161   
162   # ディレクトリを移動するメソッド
163   def cd(pathname)
164     if pathname == ".."
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)
169       else
170         raise FileExistedException
171       end
172     else
173       raise WebSecurityException
174     end
175   end
176   
177   # ディレクトリを絶対指定で移動するメソッド
178   def cd_abs(relpath_arr)
179     relpath_arr.each { |name|
180       unless name.match(FILEREGEXP)
181         raise WebSecurityException
182       end
183     }
184     @relpath_list = relpath_arr
185   end
186   
187   # 現在のディレクトリを表示するメソッド
188   def pwd
189     @basepath + relpath
190   end
191   
192   # 相対パスを算出するメソッド
193   def relpath
194     if @relpath_list.length == 0
195       ""
196     else
197       @relpath_list.join("/") << "/"
198     end
199   end
200   
201   # ファイルをアップロードするメソッド
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|
208         f.binmode
209         f.write file.read
210       end
211     else
212       raise WebSecurityException
213     end
214   end
215   
216   # ファイルを消去するメソッド
217   def delete(fname)
218     if File.exist?(pwd + File.basename(fname)) && fname.match(FILEREGEXP)
219       File.delete(pwd + File.basename(fname))
220     else
221       raise WebSecurityException
222     end
223   end
224   
225   # ディレクトリを製作するメソッド
226   def mkdir(dirname)
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))
232     else
233       raise WebSecurityException
234     end
235   end
236   
237   # 内部が空のディレクトリを消去するメソッド
238   def rmdir(dirname)
239     if File.exist?(pwd + File.basename(dirname)) && dirname.match(FILEREGEXP)
240       Dir.rmdir(pwd + File.basename(dirname))
241     else
242       raise WebSecurityException
243     end
244   end
245   
246 end
247
248 # = Controllerクラス
249 #
250 # コントローラ部分に相当する処理を受け持つクラスです
251 class Controller
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("/")
257   end
258   
259   def Controller.MultiForm(cgi, session, params, db)
260     db.transaction do
261       case params["action"]
262         # アップロード時
263         when "upload"
264         filer = WebFiler.new(IMGPATH)
265         filer.relpath_list = params["relpath_list"].split("/")
266         Controller.update_session(session, filer);
267         
268         if (cgi["updata"].size <= UPLOADLIMIT)
269           begin
270             filer.upload(cgi["updata"], cgi["updata"].original_filename)
271           rescue FileExistedException
272             session["error"] = "既に同名のファイルが存在します!"
273           rescue WebSecurityException
274             session["error"] = "ファイル名に使用できない文字列が含まれています!"
275           end
276         else
277           session["error"] = "ファイルの容量が大きすぎます!"
278         end
279         
280         Controller.update_session(session, filer);
281         
282         session["info"] = "正常にアップロードが完了しました。" if session["error"] == ""
283         
284         # 削除時
285         when "delete"
286         filer = WebFiler.new(IMGPATH)
287         filer.relpath_list = params["relpath_list"].split("/")
288         Controller.update_session(session, filer);
289         
290         session["dellist"] = []
291         count = 0
292         session["filelist"].each do |file|
293           if params["filename_" + file] == "delete" && filer.ftype(file) == "file"
294             filer.delete(file) 
295             count = count + 1
296           elsif params["filename_" + file] == "delete" && filer.ftype(file) == "directory"
297             begin
298               filer.rmdir(file)
299               count = count + 1
300             rescue
301               mes = "対象のディレクトリは空ではありません!<br>"
302               mes << "ディレクトリを削除する場合は、事前にディレクトリ内部の全てのファイルを削除してください。"
303               session["error"] = mes
304             end
305           end
306         end
307         
308         Controller.update_session(session, filer);
309         
310         session["info"] = "正常にファイルの削除が完了しました。" if session["error"] == "" && count != 0
311         
312         # ディレクトリ製作時
313         when "mkdir"
314         filer = WebFiler.new(IMGPATH)
315         filer.relpath_list = params["relpath_list"].split("/")
316         Controller.update_session(session, filer);
317         
318         begin
319           filer.mkdir(params["dirname"])
320         rescue FileExistedException
321           session["error"] = "既に同名のディレクトリが存在します!"
322         rescue WebSecurityException
323           session["error"] = "ディレクトリ名に使用できない文字列が含まれています!"
324         end
325         
326         Controller.update_session(session, filer);
327         
328         session["info"] = "正常にディレクトリの作成が完了しました。" if session["error"] == ""
329         
330         # ディレクトリ移動時
331         when "cd"
332         filer = WebFiler.new(IMGPATH)
333         filer.relpath_list = params["relpath_list"].split("/")
334         Controller.update_session(session, filer);
335         
336         begin
337           filer.cd(params["arg"])
338         rescue
339           session["error"] = "移動先のディレクトリが見つかりません!"
340         end
341         
342         Controller.update_session(session, filer);
343         
344         # 絶対位置でのディレクトリ移動時
345         when "cd_abs"
346         filer = WebFiler.new(IMGPATH)
347         filer.relpath_list = params["relpath_list"].split("/")
348         Controller.update_session(session, filer);
349         
350         if params["arg"].to_i >= 0
351           begin
352             movepath = []
353             params["arg"].to_i.times { |i|
354               movepath << params["relpath_list"].split("/")[i]
355             }
356             filer.cd_abs(movepath)
357           rescue
358             session["error"] = "移動先のディレクトリが見つかりません!"
359           end
360         else
361           session["error"] = "移動先のディレクトリが見つかりません!"
362         end
363         
364         Controller.update_session(session, filer);
365         
366         # 表示更新時
367         when "refresh"
368         filer = WebFiler.new(IMGPATH)
369         filer.relpath_list = params["relpath_list"].split("/")
370         Controller.update_session(session, filer);
371         
372         # 初期表示
373       else
374         filer = WebFiler.new(IMGPATH)
375         Controller.update_session(session, filer);
376       end
377     end
378   end
379 end
380
381 def main
382   # SESSION変数、パラメータなどを取得します
383   cgi = CGI.new
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"}
387   
388   # ログアウト処理
389   if params["mode"] == "logout"
390     session["login"] = nil
391     session["logini"] = nil
392     session["password"] = nil
393     session.delete
394   end
395   
396   # メインコントローラー
397   # セッションが有効な場合のも実行します
398   if session["login"] == "true"
399     db = PStore.new("./work/#{session.session_id}_file.dat")
400     session["info"] = ""
401     session["error"] = ""
402     
403     # コントローラ部分
404     Controller.MultiForm(cgi, session, params, db)
405   end
406   
407   # ビュー部分
408   if session["login"] != "true"
409     # if session["login"] == "true"
410     # セッションが存在しない場合は強制的にエラーページに遷移
411     htmlwriter = HtmlWriter.new("./erbtemp/fileindex.html.erb", binding)
412   else
413     htmlwriter = HtmlWriter.new("./erbtemp/filemanager.html.erb", binding)
414   end
415   
416   # 実際にHTMLを出力します
417   begin
418     cgi.out{htmlwriter.to_code}
419   rescue => exception
420     # エラーが発生した場合、それを画面に表示します
421     htmlwriter = HtmlWriter.new("./erbtemp/exception.html.erb", binding)
422     cgi.out{ htmlwriter.to_code }
423   end
424 end
425
426 begin
427   main
428 rescue => evar
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
434 end