OSDN Git Service

40924b21bbac10745790a8191a1ec0a113880a52
[feedblog/feedgenerator.git] / feedgenerator.rb
1 #!/usr/local/bin/ruby
2 # -*- coding: utf-8 -*-
3 #
4 #= Atom Feed 1.0を管理する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 'time'
17 require "date"
18 require "fileutils"
19
20 # ログインID
21 LOGINID = "login"
22 # ログインパスワード
23 PASSWORD = "password"
24 # インターフェースのテーブルの幅
25 TABLEWIDTH = 800
26 # XMLファイル格納先までの相対パス
27 XMLPATH = "./../lunardial/xml/"
28 # loglist.xmlファイルの定義
29 LISTXMLPATH = "#{XMLPATH}loglist.xml"
30 # FeedBlog上の表示ページからログ格納ディレクトリまでのパス
31 FEEDXMLDIR = "./xml/"
32 # デバッガモード
33 DEBUG = false
34 # ファイルマネージャー機能を使用するならtrue
35 USEFILEMANAGER = true
36 # ファイルマネージャー機能スクリプト(filemanager.rb)のパス
37 FILEMANAGER = "./filemanager.rb"
38 # XMLに書き込む際、改行部分を<br>のまま保持するか、改行記号に直すか
39 REPLACEBRTAG = false
40
41 # バージョン情報を示す文字列です
42 APPVERSION = "- FeedGenerator 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>)"
43 # タイトル領域に表示される文字列です
44 APPTITLE = "FeedGenerator for Ruby version 2.0.0.0"
45
46 # = Objectクラス
47 #
48 # 基本クラスのオーバーライドを行います
49 class Object
50   # myopenメソッド
51   #
52   # ruby-1.9.x以降ではファイルを開いた際、エンコードの指定を行わないとエラーの原因になります。
53   # ただしruby-1.8.6以前はエンコードの指定に対応していないため、独自メソッドを定義してファイルの入出力を行います。
54   #
55   # _arg[0]_ :: 入出力を行うファイルのパス
56   # _arg[1]_ :: モードの指定。例 : w:utf-8(書き込みモード・UTF-8エンコードでファイルを開く)
57   def myopen(*arg)
58     mode = arg[1]
59     arg[1] = mode[/[^:]+/] if mode && RUBY_VERSION < "1.8.7" && mode.include?(':')
60     open(*arg) do |f|
61       f.flock(/\A\w+r/ =~ mode ? File::LOCK_SH : File::LOCK_EX)
62       return yield(f)
63     end
64   end
65 end
66
67 class NilClass
68   def blank?
69     nil?
70   end
71 end
72
73 class Array
74   def blank?
75     empty?
76   end
77 end
78
79 class String
80   def blank?
81     empty?
82   end
83 end
84
85 # = Feed/Entryのスーパークラス
86
87 # 入出力用のメソッドなど、共通する機能を提供します
88 class AbstractEntry
89   # 初期化メソッドです
90   #
91   # _hash_ :: 値を格納したhash配列。superfeed.new(CGI::Session.new(new CGI).params)のようにして使う。
92   def initialize(hash)
93     # 内部データ保持用のハッシュ配列です
94     @attr = {}
95     
96     # 引数を格納します
97     @paramlist.each do |key|
98       val = hash[key.to_sym] || hash[key.to_s]
99       if val
100         val.strip!
101         val.gsub!(/\r\n|\r/, "\n")
102         @attr[key.to_sym] = CGI.escapeHTML(val)
103       else
104         # 空の場合の対策
105         @attr[key.to_sym] = ""
106       end
107     end
108     
109     # 可視・不可視を示すハッシュキーを格納する配列です
110     @display = {}
111     
112     # ハッシュキーの日本語説明を格納する配列です
113     @name = []
114   end
115   
116   # Accessor
117   attr_reader :attr, :paramlist, :name, :display
118   
119   # 内部の@attrハッシュにアクセスする手段を提供するメソッドです
120   #
121   # AbstractEntry.attr[:title]にアクセスしたい場合はAbstractEntry.titleで可能になります。
122   # AbstractEntry.send("title")という方法でもアクセス可能です。
123   def method_missing(methname, *args)
124     methname = methname.to_s
125     
126     if methname[-1] == ?=
127       # setter
128       raise ArgumentError, "wrong number of arguments (#{args.length} for 1)" unless args.length == 1
129       methname.chop!
130       methname = @paramlist.find{|par|par == methname}
131       return @attr[methname.to_sym] = args[0] if methname
132     else
133       # getter
134       raise ArgumentError, "wrong number of arguments (#{args.length} for 0)" unless args.empty?
135       return @attr[methname.to_sym] if @attr.key?(methname.to_sym)
136     end
137     # attr上にキーがない値を入れようとした場合はNoMethodError
138     raise NoMethodError
139   end
140   
141   def [](key)
142     @attr[key.to_sym]
143   end
144   
145   def []=(key, value)
146     @attr[key.to_sym] = value
147   end
148 end
149
150 # = Feedクラス
151 #
152 # Feedの基礎情報を保有するクラスです
153 class Feed < AbstractEntry
154   # 初期化メソッドです
155   #
156   # _hash_ :: 値を格納したhash配列。feed.new(CGI::Session.new(new CGI).params)のようにして使います。
157   def initialize(hash)
158     # 内部データ保持用のハッシュ配列です
159     @attr = {}
160     
161     # 内部データの項目名を格納する配列です
162     @paramlist = ["feedattr", "title", "subtitle", "self", "url", "updated", "feedid", "rights", "aname", "amail", "others"]
163     
164     # AbstractEntryのinitializeメソッドを呼び出し、値を@attr配列に格納します
165     super(hash)
166     
167     # 可視・不可視を示すハッシュキーを格納する配列です
168     @display = {"feedattr" => "none", "title" => "", "subtitle" => "", 
169       "self" => "", "url" => "", 
170       "updated" => "none", "feedid" => "", 
171       "rights" => "", "aname" => "", 
172       "amail" => "", "others" => "none"}
173     
174     # デバッグモードの場合、全ての入力要素を表示します
175     if DEBUG == true
176       @display.each do |key, val|
177         @display[key] = ""
178       end
179     end
180     
181     # ハッシュキーの日本語説明を格納する配列です
182     @name = {"feedattr" => "feedの保持している属性", "title" => "ウェブページのタイトル", "subtitle" => "ウェブページの簡単な説明", 
183       "self" => "このXMLファイルのURL", "url" => "ウェブページのURL", 
184       "updated" => "XMLファイルの最終更新日", "feedid" => "あなたのページ独自のID", 
185       "rights" => "ページの著作権表記", "aname" => "ページの製作者", 
186       "amail" => "ページ製作者のメールアドレス", "others" => "その他の要素"}
187     
188   end
189   
190   # Atom XMLファイルを読み込んで解析し、等価なFeedオブジェクトを返却するメソッドです
191   #
192   # _path_ :: Atom XMLファイルのパス
193   def self.readxml(path)
194     
195     # ファイルを読み込みます
196     doc = REXML::Document.new(myopen(path, "r:utf-8"){|f|f.read})
197     xml = {}
198     others = []
199     
200     # Feedの属性値を取得します
201     xmlattr = []
202     doc.elements["feed"].attributes.each_attribute do |attr|
203       xmlattr.push("#{attr.to_string} ")
204     end
205     xml[:feedattr] = xmlattr.join("\n").gsub(/"/, "'")
206     
207     # XML解析部分です。各element毎に判定を行います
208     doc.elements.each("feed") do |elm|
209       elm.elements.each { |child|
210         begin
211           case child.name
212             when "id"
213             xml[:feedid] = child.text
214             when "title", "subtitle", "updated", "rights"
215             xml[child.name.to_sym] = child.text
216             when "author"
217             child.elements.each do |gchild|
218               case gchild.name
219                 when "name"
220                 xml[:aname] = gchild.text
221                 when "email"
222                 xml[:amail] = gchild.text
223               end
224             end
225             when "link"
226             child.attributes.each do |k, v|
227               if k == "rel"
228                 if v == "self"
229                   xml[:self] = child.attributes["href"]
230                 elsif v == "alternate"
231                   xml[:url] = child.attributes["href"]
232                 end
233               end
234             end
235             when "entry"
236             # Entry要素は無視します
237           else
238             # 上記判定以外の全要素は配列に格納します
239             others.push(child.to_s)
240           end
241         rescue NoMethodError
242         end
243         
244       }
245     end
246     
247     # Others要素を結合して代入します
248     xml[:others] = others.join("\n")
249     feed = Feed.new(xml)
250     return feed
251   end
252   
253   # 内部に保持している情報を、Atom Feed1.0形式の文字列に出力するメソッドです
254   def to_s
255     buf = []
256     
257     buf.push("<feed #{@attr[:feedattr]}>")
258     buf.push("<title type=\"text\">#{@attr[:title]}</title>")
259     buf.push("<subtitle type=\"text\">#{@attr[:subtitle]}</subtitle>")
260     buf.push("<link rel=\"self\" type=\"application/atom+xml\" href=\"#{@attr[:self]}\" />")
261     buf.push("<link rel=\"alternate\" type=\"text/html\" href=\"#{@attr[:url]}\" />")
262     buf.push("<updated>#{Time.now.iso8601}</updated>")
263     buf.push("<id>#{@attr[:feedid]}</id>")
264     buf.push("<rights type=\"text\">#{@attr[:rights]}</rights>")
265     buf.push("<author>")
266     buf.push("\t<name>#{@attr[:aname]}</name>")
267     buf.push("\t<email>#{@attr[:amail]}</email>")
268     buf.push("</author>")
269     buf.push("#{CGI.unescapeHTML(@attr[:others])}") if @attr[:others] != ""
270     
271     return buf.join("\n")
272   end
273   
274   # Feed情報更新メソッド
275   def self.update(path, feed)
276     entrylist = Entry.readxml(path)
277     Feed.to_xml(path, feed, entrylist)
278     
279     true
280   end
281   
282   # XMLファイル出力用メソッドです
283   #
284   # _feed_ :: Feedオブジェクト
285   # _entry_ :: Entryオブジェクトの配列
286   def self.to_xml(path, feed, entrylist)
287     buf = []
288     buf.push("<?xml version=\"1.0\" encoding=\"utf-8\"?>")
289     buf.push("#{feed.to_s}\n")
290     entrylist.each { |entry|
291       buf.push("#{entry.to_s}\n")
292     }
293     buf.push("</feed>")
294     
295     myopen(path, "w") do |f|
296       f.print buf.join("\n")
297     end
298   end
299   
300 end
301
302 # = Entryクラス
303 #
304 # Entryの基礎情報を保有するクラスです
305 class Entry < AbstractEntry
306   # 初期化メソッドです
307   #
308   # _hash_ :: 値を格納したhash配列。entry.new(CGI::Session.new(new CGI).params)のようにして使います。
309   def initialize(hash)
310     # 内部データ保持用のハッシュ配列です
311     @attr = {}
312     
313     # 内部データの項目名を格納する配列です
314     @paramlist = ["entryid", "title", "summary", "published", "updated", "url", "content", "others"]
315     
316     # AbstractEntryのinitializeメソッドを呼び出し、値を@attr配列に格納します
317     super(hash)
318     
319     # 可視・不可視を示すハッシュキーを格納する配列です
320     @display = {"entryid" => "none", "title" => "",
321       "summary" => "", "published" => "none",
322       "updated" => "none", "url" => "none",
323       "content" => "", "others"=>"none"}
324     
325     # デバッグモードの場合、全ての入力要素を表示します
326     if DEBUG == true
327       @display.each do |key, val|
328         @display[key] = ""
329       end
330     end
331     
332     # ハッシュキーの日本語説明を格納する配列です
333     @name = {"entryid" => "記事固有のID", "title" => "記事のタイトル",
334       "summary" => "記事の簡単な説明", "published" => "記事の出版時刻",
335       "updated" => "記事の更新時刻", "url" => "記事へのURLアドレス",
336       "content" => "記事の本文", "others"=>"その他の項目"}
337   end
338   
339   # Atom XMLファイルを読み込んで解析し、等価なEntryオブジェクト配列を返却するメソッドです
340   #
341   # _path_ :: Atom XMLファイルのパス
342   def self.readxml(path)
343     
344     # ファイルを読み込みます
345     doc = REXML::Document.new(myopen(path, "r:utf-8"){|f|f.read})
346     entrylist = []
347     xml = {}
348     
349     # XML解析部分です。各element毎に判定を行います
350     doc.elements.each("feed/entry") do |elm|
351       xml = {}
352       others = []
353       elm.elements.each do |child|
354         begin
355           case child.name
356             when "id"
357             xml[:entryid] = child.text
358             when "link"
359             xml[:url] = child.attributes["href"]
360             when "title", "summary", "summary", "published", "updated", "content"
361             xml[child.name.to_sym] = child.text
362           else
363             # 上記判定以外の全要素は配列に格納します
364             others.push(child.to_s)
365           end
366         rescue NoMethodError
367         end
368       end
369       # Others要素を結合して代入します
370       xml[:others] = others.join("\n")
371       entrylist.push(Entry.new(xml))
372     end
373     
374     return entrylist
375   end
376   
377   # Entry挿入メソッド
378   def self.insert(path, entry)
379     feed = Feed.readxml(path)
380     entrylist = Entry.readxml(path)
381     entrylist.unshift entry
382     
383     Feed.to_xml(path, feed, entrylist)
384     
385     true
386   end
387   
388   # Entry更新メソッド
389   def self.update(path, entry)
390     feed = Feed.readxml(path)
391     entrylist = Entry.readxml(path)
392     
393     successed = false
394     entrylist.each_with_index { |e, i|
395       if e.entryid == entry.entryid
396         entrylist[i] = entry 
397         successed = true
398       end
399     }
400     Feed.to_xml(path, feed, entrylist) if successed
401     
402     successed
403   end
404   
405   # Entryロードメソッド
406   def self.select(path, entryid)
407     feed = Feed.readxml(path)
408     entrylist = Entry.readxml(path)
409     
410     entry = nil
411     entrylist.each_with_index { |e, i|
412       entry = entrylist[i].dup if e.entryid == entryid
413     }
414     
415     entry
416   end
417   
418   # Entry消去メソッド
419   def self.delete(path, entryid)
420     feed = Feed.readxml(path)
421     entrylist = Entry.readxml(path)
422     
423     successed = false
424     delete_index = -1
425     entrylist.each_with_index { |e, i|
426       if e.entryid == entryid
427         delete_index = i
428         successed = true
429       end
430     }
431     
432     if successed
433       entrylist.delete_at delete_index
434       Feed.to_xml(path, feed, entrylist)
435     end
436     
437     successed
438   end
439   
440   # データソースから読み取ったHTMLを、エディタで編集可能な形式に変換するメソッドです
441   def content_for_generator
442     str = @attr[:content].dup
443     str.strip!
444     str.gsub!(/(&lt;\/(?:p|h\d|div)(?:&gt;|>))\n/i, '\1')
445     str.gsub!(/\n/, '&lt;br&gt;') if REPLACEBRTAG
446     str.gsub!(/(&lt;(?:(?!&gt;).)*?)#{Regexp.escape(FEEDXMLDIR)}/) { "#$1#{XMLPATH}" }
447     str
448   end
449   
450   # エディタで編集されたCONTENT要素を、データソースに書き込める形式に変換するメソッドです
451   def content_for_blog
452     str = @attr[:content].dup
453     str = CGI.unescapeHTML(str)
454     str.strip!
455     str.gsub!(/(\r\n|\n)/, "")
456     str.gsub!(/<br>/i, "\n") if REPLACEBRTAG
457     str.gsub!(/(<br>|<\/p>|<\/h\d>|<\/div>)(?=[^\n])/i) { "#$1\n" } unless REPLACEBRTAG
458     str.gsub!(/(<[^>]*?)#{Regexp.escape(XMLPATH)}/) { "#$1#{FEEDXMLDIR}" }
459     CGI.escapeHTML(str)
460   end
461   
462   # 確認画面で表示されるCONTENT要素を生成するメソッドです
463   def content_for_view
464     str = @attr[:content].dup
465     str = CGI.unescapeHTML(str)
466     str.strip!
467     str.gsub!(/<br>/i, "\n") if REPLACEBRTAG
468     str.gsub!(/(<[^>]*?)#{Regexp.escape(FEEDXMLDIR)}/) { "#$1#{XMLPATH}" }
469     str
470   end
471   
472   # 内部に保持している情報を、Atom Feed1.0形式の文字列に出力するメソッドです
473   def to_s
474     buf = []
475     buf.push("<entry>")
476     buf.push("<id>#{@attr[:entryid]}</id>")
477     buf.push("<title>#{@attr[:title]}</title>")
478     buf.push("<summary>#{@attr[:summary]}</summary>")
479     buf.push("<published>#{@attr[:published]}</published>")
480     buf.push("<updated>#{@attr[:updated]}</updated>")
481     buf.push("<link href=\"#{@attr[:url]}\" />")
482     buf.push("<content type=\"html\">#{@attr[:content]}</content>")
483     buf.push("#{CGI.unescapeHTML(@attr[:others])}") if @attr[:others] != ""
484     buf.push("</entry>")
485     
486     return buf.join("\n")
487   end
488 end
489
490 # = HtmlWriterクラス
491
492 # テンプレートファイル(*.erb)を読み込み、管理するクラスです
493 class HtmlWriter
494   # 初期化メソッドです
495   #
496   # _template_ :: テンプレートファイル(*.erb)のパス
497   # _binding_ :: binding変数
498   def initialize(template, binding)
499     @erb = ERB.new(myopen(template, "r:utf-8") {|f| f.read}, nil, "-")
500     @binding = binding
501   end
502   
503   # テンプレートファイルの文字列を返却するメソッドです
504   def to_code
505     @erb.result(@binding)
506   end
507 end
508
509 # = LogListクラス
510 #
511 # loglist.xmlにかかわる入出力を行います
512 class LogList
513   # 初期化メソッドです
514   #
515   # _display_ :: 画面表示用文字の配列
516   # _path_ :: XMLファイルパスの配列
517   # _logpath_ :: loglist.xmlのパス
518   def initialize(display, path, logpath)
519     @display = display
520     @path = path
521     @logpath = logpath
522   end
523   
524   attr_accessor :display, :path, :logpath
525   
526   # loglist.xmlファイルを読み込んで解析し、LogListオブジェクトを返却するメソッドです
527   #
528   # _logpath_ :: loglist.xmlへのパス
529   def LogList.readxml(logpath)
530     
531     # ファイルを読み込みます
532     lines = []
533     myopen(logpath, "r:utf-8") { |f|
534       lines = f.readlines
535     }
536     
537     doc = REXML::Document.new(lines.join("\n"))
538     @display = []
539     @path = []
540     
541     doc.elements.each("list/file") { |elm|
542       elm.elements.each {|child|
543         case child.name
544           when "display"
545           @display.push(child.text)
546           when "path"
547           @path.push(child.text)
548         else 
549           # With no action
550         end
551       }
552     }
553     
554     return LogList.new(@display, @path, logpath)
555   end
556   
557   # loglist.xmlにオブジェクトの内容を反映するメソッドです
558   def to_xml
559     buf = []
560     buf.push("<list>")
561     path.each_with_index do |path, i|
562       buf.push("<file>")
563       buf.push("<display>#{@display[i]}</display>")
564       buf.push("<path>#{@path[i]}</path>")
565       buf.push("</file>")
566     end
567     buf.push("</list>")
568     
569     myopen(@logpath, "w") do |f|
570       f.print buf.join("\n")
571     end
572   end
573   
574 end
575
576 # = FileUploaderクラス
577 #
578 # 画像の管理を行うクラスです
579 class FileUploader
580   # 初期化メソッドです
581   def initialize
582   end
583   
584   # ファイルの一覧を取得し、ファイル名称を格納した配列を返却します
585   def filelist
586     arr = Dir::entries(XMLPATH).sort
587     arr.delete(".")
588     arr.delete("..")
589     
590     return arr
591   end
592   
593   # ファイルの消去を実行します
594   #
595   # _name_ :: 消去するファイル名
596   def delete(name)
597     File.delete(XMLPATH + name.match(/[^\/]*?$/).to_a[0])
598   end
599   
600   # ファイルのアップロードを実行します
601   #
602   # _name_ :: アップロードファイルの名称
603   # _img_ :: アップロードファイル
604   def upload(name, img)
605     open(XMLPATH + File.basename(name), "w") do |f|
606       f.binmode
607       f.write img
608     end
609   end
610 end
611
612 # = Controllerクラス
613 #
614 # コントローラ部分に相当する処理を受け持つクラスです
615 class Controller
616   def Controller.NormalForm(cgi, session, params, db)
617     db.transaction do
618       # modeとactionの相関図は以下のようになります。
619       # [mode] + [action]
620       #        + [action]
621       #        + [action]
622       #        + ... and more
623       case params["mode"]
624         # ログ選択画面
625         when "logselect"
626         session["filepath"] = params["logpath"]
627         db["loglist"] = LogList.readxml(LISTXMLPATH)
628         # 初期状態で選択されるログは「loglist.xml」の最上に位置するログになります
629         session["filepath"] = db["loglist"].path[0] if session["filepath"] == nil
630         db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
631         db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
632         
633         # 新規記事追加部分
634         when "newentry"
635         case params["action"]
636           # 確認画面
637           when "confirm"
638           db["newentry"] = Entry.new(params)
639           db["newentry"].content = db["newentry"].content_for_blog
640           # 記事の追記を実際にファイルに反映
641           when "exec"
642           successed = Entry.insert(XMLPATH + File.basename(session["filepath"]), Entry.new(params))
643           unless successed
644             db["error"] = "日記の新規追加に失敗しました。" 
645             params["mode"] = "error"
646           end
647           # 画面を戻った際の処理
648           when "back"
649           db["newentry"] = Entry.new(params)
650         else 
651           # New Diary - Default
652           db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
653           db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
654         end
655         
656         # 記事編集部分
657         when "editentry"
658         case params["action"]
659           # 編集画面
660           when "edit"
661           session["editid"] = cgi["editid"].to_s
662           db["editentry"] = Entry.select(XMLPATH + File.basename(session["filepath"]), session["editid"])
663           # 確認画面
664           when "confirm"
665           session["editid"] = cgi["editid"].to_s
666           db["editentry"] = Entry.new(params)
667           db["editentry"].content = db["editentry"].content_for_blog
668           # 記事の変更を実際にファイルに反映
669           when "exec"
670           successed = Entry.update(XMLPATH + File.basename(session["filepath"]), Entry.new(params))
671           unless successed
672             db["error"] = "日記の編集処理に失敗しました。<br>該当の日記が既に存在しない可能性があります。" 
673             params["mode"] = "error"
674           end
675           when "back"
676           db["editentry"] = Entry.new(params)
677         else
678           # Edit Diary - Default
679           db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
680           db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
681         end
682         
683         # 記事削除部分
684         when "delentry"
685         case params["action"]
686           # 確認画面
687           when "confirm"
688           session["delid"] = cgi["delid"].to_s
689           db["delentry"] = Entry.select(XMLPATH + File.basename(session["filepath"]), session["delid"])
690           # 記事の削除を実際にファイルに反映
691           when "exec"
692           successed = Entry.delete(XMLPATH + File.basename(session["filepath"]), cgi["delid"].to_s)
693           unless successed
694             db["error"] = "日記の編集処理に失敗しました。<br>該当の日記が既に存在しない可能性があります。" 
695             params["mode"] = "error"
696           end
697         else
698           # Delete Diary - Default
699           db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
700           db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
701         end
702         
703         # Feed情報変更部分
704         when "editfeed"
705         case params["action"]
706           # 確認画面
707           when "confirm"
708           db["feed"] = Feed.new(params)
709           # 実際にFeed情報の変更をファイルに反映
710           when "back"
711           db["feed"] = Feed.new(params)
712           when "exec"
713           Feed.update(XMLPATH + File.basename(session["filepath"]), Feed.new(params))
714         else
715           
716         end
717         
718         # ログ編集モード
719         when "log"
720         # 必ず内部データをリフレッシュする
721         db["loglist"] = LogList.readxml(LISTXMLPATH)
722         case params["action"]
723           # ログファイルの編集を実際にファイルに反映
724           when "addexec"
725           # エラーチェック。この段階のエラーは強度のエラーを発する。
726           db["loglist"].path.each do |val|
727             if val == cgi["logpath"]
728               # 重複していた場合エラーメッセージを表示し、処理を行わない
729               params["action"] = ""
730               params["mode"] = "error"
731               db["error"] = "ログファイルの追加中に重大なエラーが発生しました。<br>環境を見直してください。"
732               return
733             end
734           end
735           
736           # 入力された内容を保持する配列に追加
737           db["loglist"].path[1, 0] = cgi["logpath"]
738           db["loglist"].display[1, 0] = cgi["logdisplay"]
739           db["loglist"].to_xml
740           # 既存のdiary.xmlを指定された名称でコピーして保存
741           FileUtils.copy_file(XMLPATH + File.basename(db["loglist"].path[0]), XMLPATH + File.basename(db["logpath"]), true)
742           # 保持している情報を更新する
743           db["loglist"] = LogList.readxml(LISTXMLPATH)
744           db["entry"] = []
745           # 新たなdiary.xmlを生成。この際保持する情報は同一のFeedオブジェクトnew()
746           Feed.to_xml(XMLPATH + File.basename(db["loglist"].path[0]), db["feed"], [])
747           # 確認画面
748           when "addconfirm"
749           # 入力されたログが既に存在するかを確認
750           db["loglist"].path.each do |val|
751             if val == cgi["logpath"]
752               # 重複していた場合エラーメッセージを表示し、処理を行わない
753               params["action"] = ""
754               db["error"] = "<span style='color: #ff0000'>同一のファイルが存在します!別の名前を指定してください。</span><br>"
755               return
756             end
757           end
758           
759           db["logpath"] = cgi["logpath"]
760           db["logdisplay"] = cgi["logdisplay"]
761           
762           if db["logpath"].blank? || db["logdisplay"].blank?
763             params["action"] = ""
764           end
765           when "back"
766           
767           # 削除確認画面
768           when "delconfirm"
769           db["logdelindex"] = params["logdelindex"].to_i
770           
771           if db["logdelindex"] < 1
772             db["error"] = "<span style='color: #ff0000'>ログファイルの削除パラメタが不正です。</span><br>"
773             params["action"] = ""
774           end
775           # 削除処理
776           when "delexec"
777           if cgi["logdelindex"].to_i < 1
778             params["action"] = ""
779             params["mode"] = "error"
780             db["error"] = "ログファイルの削除中に重大なエラーが発生しました。<br>環境を見直してください。"
781             return
782           else
783             # 記事ファイルを削除します
784             File.delete(XMLPATH + File.basename(db["loglist"].path[db["logdelindex"]]))
785             # ログリストから削除します
786             db["loglist"].path.delete_at(cgi["logdelindex"].to_i)
787             db["loglist"].display.delete_at(cgi["logdelindex"].to_i)
788             db["loglist"].to_xml
789             # 保持している情報を更新する
790             db["loglist"] = LogList.readxml(LISTXMLPATH)
791             db["entry"] = []
792           end
793           
794           # 編集画面
795           when "edit"
796           db["logeditindex"] = params["logdelindex"].to_i
797           
798           db["logpath"] = db["loglist"].path[db["logeditindex"]]
799           db["logdisplay"] = db["loglist"].display[db["logeditindex"]]
800           
801           if db["logeditindex"] == 0
802             db["error"] = "<span style='color: #ff0000'>ログファイルの編集パラメタが不正です。</span><br>"
803             params["action"] = ""
804           end
805           # 編集確認画面
806           when "editconfirm"
807           checkflag = true
808           db["loglist"].path.each_with_index do |val, i|
809             if db["logeditindex"] != i
810               if params["logpath"].to_s == db["loglist"].path[i].to_s
811                 checkflag = false
812               end
813             end
814           end
815           
816           if checkflag == false
817             params["action"] = "edit"
818             db["error"] = "<span style='color: #ff0000'>同一のファイルが存在します!別の名前を指定してください。</span><br>"
819           else
820             db["loginsertindex"] = params["loginsertindex"].to_i
821             
822             db["logpath"] = params["logpath"].to_s
823             db["logdisplay"] = params["logdisplay"].to_s
824           end
825           # 編集実行
826           when "editexec"
827           checkflag = true
828           db["loglist"].path.each_with_index do |val, i|
829             if db["logeditindex"] != i
830               if params["logpath"].to_s == db["loglist"].path[i].to_s
831                 checkflag = false
832               end
833             end
834           end
835           
836           if checkflag == false
837             params["action"] = ""
838             params["mode"] = "error"
839             db["error"] = "ログファイルの編集中に重大なエラーが発生しました。<br>環境を見直してください。"
840             return
841           else
842             db["loginsertindex"] = params["loginsertindex"].to_i
843             
844             db["logpath"] = params["logpath"].to_s
845             db["logdisplay"] = params["logdisplay"].to_s
846             
847             # ファイルを移動します
848             if XMLPATH + File.basename(db["loglist"].path[db["logeditindex"]]) != XMLPATH + File.basename(db["logpath"])
849               FileUtils.move(XMLPATH + File.basename(db["loglist"].path[db["logeditindex"]]), XMLPATH + File.basename(db["logpath"]))
850               # ログリストを更新します
851               db["loglist"].path.delete_at(db["logeditindex"])
852               db["loglist"].display.delete_at(db["logeditindex"])
853               db["loglist"].path.insert(db["loginsertindex"] + 1, db["logpath"])
854               db["loglist"].display.insert(db["loginsertindex"] + 1, db["logdisplay"])
855               db["loglist"].to_xml
856             end
857           end
858           
859           # 初期表示画面
860         else
861           # 現在編集中のデータを強制的に最上のファイルパスに変更
862           session["filepath"] = db["loglist"].path[0]
863           
864           # 前月の時刻を作成します
865           prevmonth = (DateTime.now << 1)
866           
867           # 前月の時刻を元に、ディフォルト書式を生成します
868           db["logpath"] = FEEDXMLDIR + prevmonth.strftime("%Y%m") + ".xml"
869           db["logdisplay"] = prevmonth.strftime("%Y年%m月").gsub("年0", "年")
870         end
871         
872         # インポートモードの場合の処理
873         when "import"
874         db["loglist"] = LogList.readxml(LISTXMLPATH)
875         
876         # リセットモードの場合の処理
877         when "reset"
878         case params["action"]
879           # リセット実行時の処理
880           when "exec"
881           file = FileUploader.new
882           file.filelist().each { |fname|
883             file.delete(fname) if File.ftype(XMLPATH + fname) == "file"
884           }
885           
886           # 全ファイルの初期化を実行する
887           # loglist.xmlの初期化
888           loglist = LogList.new(["最新の記事"], ["#{FEEDXMLDIR}diary.xml"], LISTXMLPATH)
889           loglist.to_xml
890           
891           db["loglist"] = LogList.readxml(LISTXMLPATH)
892           
893           # diary.xmlの初期化
894           Feed.to_xml(XMLPATH + db["loglist"].path[0].match(/[^\/]*?$/).to_a[0], Feed.new({}), [])
895           
896           # 初期の編集ファイルはloglist.xml上の最も上のファイル
897           session["filepath"] = db["loglist"].path[0]
898           db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
899           db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
900           
901           # 画面の遷移先をトップページにする
902           params["mode"] = ""
903           params["action"] = ""
904           # パラメタ無しの処理
905         else
906           # ファイル一覧を取得する
907           filelist = FileUploader.new.filelist()
908           
909           # タイプがファイルのみリストとして取得する
910           db["filelist"] = []
911           filelist.each { |fname| db["filelist"] << fname if File.ftype(XMLPATH + fname) == "file"}
912         end
913         
914         # ログイン時の画面
915       else
916         # loglist.xmlが存在するかチェック
917         if File.exist?(LISTXMLPATH) == false
918           # なかった場合はloglist.xmlを自動生成
919           loglist = LogList.new(["最新の記事"], ["#{FEEDXMLDIR}diary.xml"], LISTXMLPATH)
920           loglist.to_xml
921           Feed.to_xml(XMLPATH + File.basename(loglist.path[0]), Feed.new({}), [])
922         end
923         
924         db["loglist"] = LogList.readxml(LISTXMLPATH)
925         
926         # diary.xmlが存在するかチェック
927         if File.exist?(XMLPATH + File.basename(db["loglist"].path[0])) == false
928           # なかった場合はdiary.xmlを自動生成
929           Feed.to_xml(XMLPATH + File.basename(db["loglist"].path[0]), Feed.new({}), [])
930         end
931         
932         # 初期の編集ファイルはloglist.xml上の最も上のファイル
933         session["filepath"] = db["loglist"].path[0] if session["filepath"] == nil
934         db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
935         db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
936       end
937     end
938   end
939   
940   # マルチパートフォームの場合の処理です
941   def Controller.MultiForm(cgi, session, params, db)
942     db.transaction do
943       
944       # loglist.xmlをロードします
945       db["loglist"] = LogList.readxml(LISTXMLPATH)
946       
947       case params["mode"]
948         # 特定位置に挿入する場合の処理
949         when "insert"
950         case params["action"]
951           when "exec"
952           # この段階のエラーに対しては強度のエラーを発する
953           checkflag = true
954           db["logpath"] = Controller.get_mpart_value(cgi["logpath"])
955           db["loglist"].path.each do |val|
956             if val == db["logpath"]
957               # 重複していた場合エラーメッセージを表示し、処理を行わない
958               db["logpath"] = ""
959               params["action"] = ""
960               params["mode"] = "error"
961               db["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
962               return
963             end
964           end
965           
966           db["loginsertindex"] = cgi["loginsertindex"].read.to_i
967           db["logdisplay"] = Controller.get_mpart_value(cgi["logdisplay"])
968           
969           # 0位置への挿入防止
970           if db["loginsertindex"] == 0
971             params["action"] = ""
972             params["mode"] = "error"
973             db["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
974             return
975           end
976           
977           if File.basename(db["logpath"]).blank? || db["logdisplay"].blank?
978             params["action"] = ""
979             params["mode"] = "error"
980             db["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
981             return
982           end
983           
984           # ファイルを実際にアップロードします
985           file = FileUploader.new
986           file.upload(db["logpath"], db["importxml"])
987           
988           # loglist.xmlを更新します
989           db["loglist"].path[db["loginsertindex"], 0] = db["logpath"]
990           db["loglist"].display[db["loginsertindex"], 0] = db["logdisplay"]
991           db["loglist"].to_xml
992           
993           # 情報を更新します
994           db["loglist"] = LogList.readxml(LISTXMLPATH)
995           session["filepath"] = db["loglist"].path[0] if session["filepath"] == nil
996           db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
997           db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
998           
999           when "confirm"
1000           # 入力されたログファイルパスが既に存在するかを確認
1001           checkflag = true
1002           db["logpath"] = Controller.get_mpart_value(cgi["logpath"])
1003           db["loglist"].path.each do |val|
1004             if val == db["logpath"]
1005               # 重複していた場合エラーメッセージを表示し、処理を行わない
1006               db["logpath"] = ""
1007               params["action"] = ""
1008               db["error"] = "<br><span style='color: #ff0000'>同一のファイルが存在します!別の名前を指定してください。</span><br>"
1009               checkflag = false
1010               return
1011             end
1012           end
1013           
1014           if checkflag
1015             db["loginsertindex"] = cgi["loginsertindex"].read.to_i
1016             db["logdisplay"] = Controller.get_mpart_value(cgi["logdisplay"])
1017             db["importxml"] = cgi["updata"].read
1018             
1019             # XMLの整合性をチェックします
1020             begin
1021               REXML::Document.new(Controller.fix_updata_enc(db["importxml"].to_s))
1022             rescue => exception
1023               db["error"] = "<br><span style='color: #ff0000'>不正な整形のXMLです!ファイルを見直してください。</span><br>"
1024               db["error"] << "<div class='divstyle' style='width: 560px; color: #ff0000; text-align: left;'>"
1025               db["error"] << CGI.escapeHTML(exception.to_s).gsub("\n", "<br>")
1026               db["error"] << "</div>"
1027               params["action"] = ""
1028               return
1029             end
1030           end
1031           
1032           # 0位置への挿入防止
1033           if db["loginsertindex"] == 0
1034             params["action"] = ""
1035             db["error"] = "<br><span style='color: #ff0000'>ラジオボックスでログの挿入位置を選択してください!</span><br>"
1036             return
1037           end
1038           
1039           if db["logpath"] == FEEDXMLDIR || db["logdisplay"].blank?
1040             params["action"] = ""
1041             db["error"] = "<br><span style='color: #ff0000'>インポートファイル、及び、ログの表示名は空欄にできません。</span><br>"
1042             return
1043           end
1044           
1045         else
1046         end
1047         
1048         # diary.xmlを入れ替える処理
1049         when "replace"
1050         case params["action"]
1051           when "exec"
1052           # この段階のエラーに対しては強度のエラーを発する
1053           checkflag = true
1054           db["logpath"] = Controller.get_mpart_value(cgi["logpath"])
1055           db["loglist"].path.each do |val|
1056             if val == db["logpath"]
1057               # 重複していた場合エラーメッセージを表示し、処理を行わない
1058               params["action"] = ""
1059               params["mode"] = "error"
1060               db["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
1061               return
1062             end
1063           end
1064           
1065           db["logdisplay"] = Controller.get_mpart_value(cgi["logdisplay"])
1066           
1067           if File.basename(db["logpath"]).blank? || db["logdisplay"].blank? || db["importxml"].blank?
1068             params["action"] = ""
1069             params["mode"] = "error"
1070             db["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
1071             return
1072           end
1073           
1074           # diary.xmlを移動します
1075           FileUtils.move(XMLPATH + "diary.xml", XMLPATH + File.basename(db["logpath"]))
1076           # ファイルをアップロードします
1077           file = FileUploader.new
1078           file.upload("diary.xml", db["importxml"])
1079           
1080           # loglist.xmlを更新します
1081           db["loglist"].path[1, 0] = db["logpath"]
1082           db["loglist"].display[1, 0] = db["logdisplay"]
1083           db["loglist"].to_xml
1084           
1085           # 情報を更新します
1086           db["loglist"] = LogList.readxml(LISTXMLPATH)
1087           session["filepath"] = db["loglist"].path[0] if session["filepath"] == nil
1088           db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
1089           db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
1090           
1091           when "confirm"
1092           # 入力されたログファイルパスが既に存在するかを確認
1093           checkflag = true
1094           db["logpath"] = Controller.get_mpart_value(cgi["logpath"])
1095           db["loglist"].path.each do |val|
1096             if val == db["logpath"]
1097               # 重複していた場合エラーメッセージを表示し、処理を行わない
1098               params["action"] = ""
1099               db["error"] = "<br><span style='color: #ff0000'>同一のファイルが存在します!別の名前を指定してください。</span><br>"
1100               checkflag = false
1101               return
1102             end
1103           end
1104           
1105           if checkflag == true
1106             db["logdisplay"] = Controller.get_mpart_value(cgi["logdisplay"])
1107             db["importxml"] = cgi["updata"].read
1108             
1109             # XMLの整合性をチェックします
1110             begin
1111               REXML::Document.new(Controller.fix_updata_enc(db["importxml"].to_s))
1112             rescue => exception
1113               db["error"] = "<br><span style='color: #ff0000'>不正な整形のXMLです!ファイルを見直してください。</span><br>"
1114               db["error"] << "<div class='divstyle' style='width: 560px; color: #ff0000; text-align: left;'>"
1115               db["error"] << CGI.escapeHTML(exception.to_s).gsub("\n", "<br>")
1116               db["error"] << "</div>"
1117               params["action"] = ""
1118               return
1119             end
1120           end
1121           
1122           if db["logpath"].blank? || db["logdisplay"].blank? || db["importxml"].blank?
1123             params["action"] = ""
1124             db["error"] = "<br><span style='color: #ff0000'>インポートファイル、及び、ログの表示名、ログのパスは空欄にできません。</span><br>"
1125             return
1126           end
1127           
1128         else
1129         end
1130         
1131       else
1132       end
1133     end
1134   end
1135   
1136   # Formのenctypeがmultypartだった場合、入力された値をrubyバージョンに左右されずに読み取るメソッドです。
1137   def Controller.get_mpart_value(cgi_param)
1138     if RUBY_VERSION >= "1.9.0"
1139       cgi_param[0..cgi_param.length].to_s
1140     else
1141       cgi_param.read.to_s
1142     end
1143   end
1144   
1145   # アップロードされたXMLをバージョンに左右されずに読み取るために、ruby-1.9.1以上ではエンコーディングを強制指定します
1146   def Controller.fix_updata_enc(file_to_s)
1147     if RUBY_VERSION >= "1.9.0"
1148       file_to_s.force_encoding("UTF-8")
1149     else
1150       file_to_s
1151     end
1152   end
1153   
1154 end
1155
1156 def main
1157   # SESSION変数、パラメータなどを取得します
1158   cgi = CGI.new
1159   session = CGI::Session.new(cgi)
1160   params = Hash[*cgi.params.to_a.map{|k, v| [k, v[0].to_s]}.flatten]
1161   
1162   # コントローラー部分
1163   # セッション管理
1164   if session["login"] != "true"
1165     if (cgi["loginid"] == LOGINID && cgi["password"] == PASSWORD)
1166       session["login"] = "true"
1167       
1168       # ワークフォルダの中をクリーンアップします
1169       filelist = Dir::entries("./work")
1170       # 削除条件 : 最終変更日時から1日(60*60*24sec)かつ、ファイルタイプがファイルの場合
1171       filelist.each do |file|
1172         File.delete("./work/#{file}") if Time.now - File.ctime("./work/#{file}") > 86400 && File.ftype("./work/#{file}") == "file"
1173       end
1174     end
1175   end
1176   
1177   # ログアウト処理
1178   if params["mode"] == "logout"
1179     session["login"] = nil
1180     session["logini"] = nil
1181     session["password"] = nil
1182     session.delete
1183   end
1184   
1185   begin
1186     # メインコントローラー
1187     # セッションが有効な場合のも実行します
1188     if session["login"] == "true"
1189       # PStore破損チェック!
1190       begin
1191         db = PStore.new("./work/#{session.session_id}.dat")
1192         db.transaction do
1193           db["error"] = ""
1194         end
1195       rescue
1196         # PStoreファイルを破棄する
1197         File.delete("./work/#{session.session_id}.dat")
1198         # PStoreが破損していた場合はセッション情報を破棄する
1199         session["login"] = nil
1200         session["logini"] = nil
1201         session["password"] = nil
1202         session.delete
1203       end
1204       
1205       # フォームによって挙動を変更します
1206       if cgi["mode"].respond_to?(:read) && cgi["action"].respond_to?(:read)
1207         params["mode"] = cgi["mode"].read
1208         params["action"] = cgi["action"].read
1209         Controller.MultiForm(cgi, session, params, db)
1210       else
1211         Controller.NormalForm(cgi, session, params, db)
1212       end
1213       
1214       # エラー画面移行時はセッション情報を破棄する
1215       if params["mode"] == "error"
1216         session["login"] = nil
1217         session["logini"] = nil
1218         session["password"] = nil
1219         session.delete
1220       end
1221     end
1222     
1223     # メニューとして表示されるHTML文字列です
1224     if USEFILEMANAGER == true
1225       menu = "<div class=\"divstyle\" style=\"width: #{TABLEWIDTH}px;\"><div class=\"divstyle\" align=\"center\" style=\"margin-bottom: 5px;\">現在編集中のファイル : #{session["filepath"]} [ <a href=\"#{cgi.script_name}?mode=selectlog\">他のファイルを選択</a> ]</div>[ <a href=\"#{cgi.script_name}\">トップページ</a> | 記事管理 ( <a href=\"#{cgi.script_name}?mode=newentry\">作成</a> | <a href=\"#{cgi.script_name}?mode=editentry\">編集</a> | <a href=\"#{cgi.script_name}?mode=delentry\">消去</a> )  | <a href=\"#{cgi.script_name}?mode=editfeed\">XML情報編集</a> | <a href=\"#{cgi.script_name}?mode=log\">ログ管理</a> | <a href=\"#{cgi.script_name}?mode=import\">インポート</a> | <a href=\"#{FILEMANAGER}\" target=\"_blank\">ファイル管理</a> | <a href=\"#{cgi.script_name}?mode=reset\">初期化</a> | <a href=\"#{cgi.script_name}?mode=logout\">ログアウト</a> ]</div>"
1226     else
1227       menu = "<div class=\"divstyle\" style=\"width: #{TABLEWIDTH}px;\"><div class=\"divstyle\" align=\"center\" style=\"margin-bottom: 5px;\">現在編集中のファイル : #{session["filepath"]} [ <a href=\"#{cgi.script_name}?mode=selectlog\">他のファイルを選択</a> ]</div>[ <a href=\"#{cgi.script_name}\">トップページ</a> | 記事管理 ( <a href=\"#{cgi.script_name}?mode=newentry\">作成</a> | <a href=\"#{cgi.script_name}?mode=editentry\">編集</a> | <a href=\"#{cgi.script_name}?mode=delentry\">消去</a> )  | <a href=\"#{cgi.script_name}?mode=editfeed\">XML情報編集</a> | <a href=\"#{cgi.script_name}?mode=log\">ログ管理</a> | <a href=\"#{cgi.script_name}?mode=import\">インポート</a> | <a href=\"#{cgi.script_name}?mode=reset\">初期化</a> | <a href=\"#{cgi.script_name}?mode=logout\">ログアウト</a> ]</div>"
1228     end
1229     
1230     # ビュー部分
1231     # modeとactionの相関図は以下のようになります。
1232     # [mode] + [action]
1233     #        + [action]
1234     #        + [action]
1235     #        + ... and more
1236     if session["login"] != "true"
1237       # セッションが存在しない場合は強制的にトップページに遷移
1238       htmlwriter = HtmlWriter.new("./erbtemp/login.html.erb", binding)
1239     else
1240       case params["mode"]
1241         when "selectlog"
1242         htmlwriter = HtmlWriter.new("./erbtemp/select.html.erb", binding)
1243         when "newentry"
1244         htmlwriter = HtmlWriter.new("./erbtemp/newentry.html.erb", binding)
1245         when "editentry"
1246         htmlwriter = HtmlWriter.new("./erbtemp/editentry.html.erb", binding)
1247         when "delentry"
1248         htmlwriter = HtmlWriter.new("./erbtemp/delentry.html.erb", binding)
1249         when "editfeed"
1250         htmlwriter = HtmlWriter.new("./erbtemp/editfeed.html.erb", binding)
1251         when "log"
1252         htmlwriter = HtmlWriter.new("./erbtemp/log.html.erb", binding)
1253         when "insert"
1254         htmlwriter = HtmlWriter.new("./erbtemp/insertfeed.html.erb", binding)
1255         when "replace"
1256         htmlwriter = HtmlWriter.new("./erbtemp/replacefeed.html.erb", binding)
1257         when "import"
1258         htmlwriter = HtmlWriter.new("./erbtemp/indeximport.html.erb", binding)
1259         when "reset"
1260         htmlwriter = HtmlWriter.new("./erbtemp/reset.html.erb", binding)
1261         when "error"
1262         htmlwriter = HtmlWriter.new("./erbtemp/error.html.erb", binding)
1263       else
1264         htmlwriter = HtmlWriter.new("./erbtemp/index.html.erb", binding)
1265       end
1266     end
1267   rescue => exception
1268     # エラーが発生した場合、それを画面に表示します
1269     htmlwriter = HtmlWriter.new("./erbtemp/exception.html.erb", binding)
1270   end
1271   
1272   # 実際にHTMLを出力します
1273   begin
1274     cgi.out{htmlwriter.to_code}
1275   rescue => exception
1276     # エラーが発生した場合、それを画面に表示します
1277     htmlwriter = HtmlWriter.new("./erbtemp/exception.html.erb", binding)
1278     cgi.out{htmlwriter.to_code}
1279   end
1280 end
1281
1282 begin
1283   main
1284 rescue => evar
1285   # エラーが発生した場合、それを画面に表示します
1286   detail = ("%s: %s (%s)\n" %
1287   [evar.backtrace[0], evar.message, evar.send('class')]) +
1288   evar.backtrace[1..-1].join("\n")
1289   puts "content-type: text/html\n\n<plaintext>\n" + detail
1290 end