#!/usr/local/bin/ruby
# -*- coding: utf-8 -*-
#
#= Atom Feed 1.0を管理するWEBアプリケーション
#
#Autohr::    Kureha Hisame (http://lunardial.sakura.ne.jp/) & Yui Naruse (http://airemix.com/)
#Version::   2.0.0.0
#Copyright:: Copyright 2009 FeedBlog Project (http://sourceforge.jp/projects/feedblog/)
#License::   GPLv3

require "cgi"
require "cgi/session"
require "erb"
require "rexml/document"
require "pstore"
require 'time'
require "date"
require "fileutils"

# ログイン情報配列
LOGININFO = [
{:id => "login", :password => "password", :name => "テストユーザ"}
]
# インターフェースのテーブルの幅
TABLEWIDTH = 1000
# XMLファイル格納先までの相対パス
XMLPATH = "./../lunardial/xml/"
# FeedBlogを設置したディレクトリのURL
HOMEBASE = "https://lunardial.sakura.ne.jp/"
# 入力されたフルパスURL(HOMEBASE)を置換する文字列
RELAYPATH = "./"
# loglist.xmlファイルの定義
LISTXMLPATH = "#{XMLPATH}loglist.xml"
# FeedBlog上の表示ページからログ格納ディレクトリまでのパス
FEEDXMLDIR = "./xml/"
# デバッガモード
DEBUG = false
# ファイルマネージャー機能を使用するならtrue
USEFILEMANAGER = true
# ファイルマネージャー機能スクリプト(filemanager.rb)のパス
FILEMANAGER = "./filemanager.rb"
# XMLに書き込む際、改行部分を<br>のまま保持するか、改行記号に直すか
REPLACEBRTAG = false
# ファイルの書き込み時にENTRYのIDおよびURLを、FEEDオブジェクトから自動生成した値に置換するか否か
REPLACEENTRYIDANDURL = false
# プラグインディレクトリ
PLUGINDIR = "./plugins/"
# 記事用初期ファイル名
INITIALXML = "diary.xml"

# バージョン情報を示す文字列です
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>)"
# タイトル領域に表示される文字列です
APPTITLE = "FeedGenerator for Ruby version 2.0.0.0"

# = Objectクラス
#
# 基本クラスのオーバーライドを行います
class Object
  # myopenメソッド
  #
  # ruby-1.9.x以降ではファイルを開いた際、エンコードの指定を行わないとエラーの原因になります。
  # ただしruby-1.8.6以前はエンコードの指定に対応していないため、独自メソッドを定義してファイルの入出力を行います。
  #
  # _arg[0]_ :: 入出力を行うファイルのパス
  # _arg[1]_ :: モードの指定。例 : w:utf-8(書き込みモード・UTF-8エンコードでファイルを開く)
  def myopen(*arg)
    mode = arg[1]
    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(rdonly_p ? File::LOCK_SH : File::LOCK_EX)
      return yield(f)
    end
  end
end

class NilClass
  def blank?
    nil?
  end
end

class Array
  def blank?
    empty?
  end
end

class String
  def blank?
    empty?
  end
end

# = Feed/Entryのスーパークラス
# 
# 入出力用のメソッドなど、共通する機能を提供します
class AbstractEntry
  # 初期化メソッドです
  #
  # _hash_ :: 値を格納したhash配列。superfeed.new(CGI::Session.new(new CGI).params)のようにして使う。
  def initialize(hash)
    # 内部データ保持用のハッシュ配列です
    @attr = {}
    
    # 引数を格納します
    @paramlist.each do |key|
      val = hash[key.to_sym] || hash[key.to_s]
      if val
        val.strip!
        val.gsub!(/\r\n|\r/, "\n")
        @attr[key.to_sym] = CGI.escapeHTML(val)
      else
        # 空の場合の対策
        @attr[key.to_sym] = ""
      end
    end
    
    # 可視・不可視を示すハッシュキーを格納する配列です
    @display = {}
    
    # ハッシュキーの日本語説明を格納する配列です
    @name = []
  end
  
  # Accessor
  attr_reader :attr, :paramlist, :name, :display
  
  # 内部の@attrハッシュにアクセスする手段を提供するメソッドです
  #
  # AbstractEntry.attr[:title]にアクセスしたい場合はAbstractEntry.titleで可能になります。
  # AbstractEntry.send("title")という方法でもアクセス可能です。
  def method_missing(methname, *args)
    methname = methname.to_s
    
    if methname[-1] == ?=
      # setter
      raise ArgumentError, "wrong number of arguments (#{args.length} for 1)" unless args.length == 1
      methname.chop!
      methname = @paramlist.find{|par|par == methname}
      return @attr[methname.to_sym] = args[0] if methname
    else
      # getter
      raise ArgumentError, "wrong number of arguments (#{args.length} for 0)" unless args.empty?
      return @attr[methname.to_sym] if @attr.key?(methname.to_sym)
    end
    # attr上にキーがない値を入れようとした場合はNoMethodError
    raise NoMethodError
  end
  
  def [](key)
    @attr[key.to_sym]
  end
  
  def []=(key, value)
    @attr[key.to_sym] = value
  end
end

# = Feedクラス
#
# Feedの基礎情報を保有するクラスです
class Feed < AbstractEntry
  # 初期化メソッドです
  #
  # _hash_ :: 値を格納したhash配列。feed.new(CGI::Session.new(new CGI).params)のようにして使います。
  def initialize(hash)
    # 内部データ保持用のハッシュ配列です
    @attr = {}
    
    # 内部データの項目名を格納する配列です
    @paramlist = ["feedattr", "title", "subtitle", "self", "url", "updated", "feedid", "rights", "aname", "amail", "others"]
    
    # AbstractEntryのinitializeメソッドを呼び出し、値を@attr配列に格納します
    super(hash)
    
    # 可視・不可視を示すハッシュキーを格納する配列です
    @display = {"feedattr" => "none", "title" => "", "subtitle" => "", 
      "self" => "", "url" => "", 
      "updated" => "none", "feedid" => "", 
      "rights" => "", "aname" => "", 
      "amail" => "", "others" => "none"}
    
    # デバッグモードの場合、全ての入力要素を表示します
    if DEBUG == true
      @display.each do |key, val|
        @display[key] = ""
      end
    end
    
    # ハッシュキーの日本語説明を格納する配列です
    @name = {"feedattr" => "feedの保持している属性", "title" => "ウェブページのタイトル", "subtitle" => "ウェブページの簡単な説明", 
      "self" => "このXMLファイルのURL", "url" => "ウェブページのURL", 
      "updated" => "XMLファイルの最終更新日", "feedid" => "あなたのページ独自のID", 
      "rights" => "ページの著作権表記", "aname" => "ページの製作者", 
      "amail" => "ページ製作者のメールアドレス", "others" => "その他の要素"}
    
  end
  
  # Atom XMLファイルを読み込んで解析し、等価なFeedオブジェクトを返却するメソッドです
  #
  # _path_ :: Atom XMLファイルのパス
  def self.readxml(path)
    
    # ファイルを読み込みます
    doc = REXML::Document.new(myopen(path, "r:utf-8"){|f|f.read})
    xml = {}
    others = []
    
    # Feedの属性値を取得します
    xmlattr = []
    doc.elements["feed"].attributes.each_attribute do |attr|
      xmlattr.push("#{attr.to_string} ")
    end
    xml[:feedattr] = xmlattr.join("\n").gsub(/"/, "'")
    
    # XML解析部分です。各element毎に判定を行います
    doc.elements.each("feed") do |elm|
      elm.elements.each { |child|
        begin
          case child.name
            when "id"
            xml[:feedid] = child.text
            when "title", "subtitle", "updated", "rights"
            xml[child.name.to_sym] = child.text
            when "author"
            child.elements.each do |gchild|
              case gchild.name
                when "name"
                xml[:aname] = gchild.text
                when "email"
                xml[:amail] = gchild.text
              end
            end
            when "link"
            child.attributes.each do |k, v|
              if k == "rel"
                if v == "self"
                  xml[:self] = child.attributes["href"]
                elsif v == "alternate"
                  xml[:url] = child.attributes["href"]
                end
              end
            end
            when "entry"
            # Entry要素は無視します
          else
            # 上記判定以外の全要素は配列に格納します
            others.push(child.to_s)
          end
        rescue NoMethodError
        end
        
      }
    end
    
    # Others要素を結合して代入します
    xml[:others] = others.join("\n")
    feed = Feed.new(xml)
    return feed
  end
  
  # 内部に保持している情報を、Atom Feed1.0形式の文字列に出力するメソッドです
  def to_s
    buf = []
    
    # buf.push("<feed #{@attr[:feedattr]}>")
    buf.push("<feed xml:lang='ja-jp' xmlns='http://www.w3.org/2005/Atom'>");
    buf.push("<title type=\"text\">#{@attr[:title]}</title>")
    buf.push("<subtitle type=\"text\">#{@attr[:subtitle]}</subtitle>")
    buf.push("<link rel=\"self\" type=\"application/atom+xml\" href=\"#{@attr[:self]}\" />")
    buf.push("<link rel=\"alternate\" type=\"text/html\" href=\"#{@attr[:url]}\" />")
    buf.push("<updated>#{Time.now.iso8601}</updated>")
    buf.push("<id>#{@attr[:feedid]}</id>")
    buf.push("<rights type=\"text\">#{@attr[:rights]}</rights>")
    buf.push("<author>")
    buf.push("\t<name>#{@attr[:aname]}</name>")
    buf.push("\t<email>#{@attr[:amail]}</email>")
    buf.push("</author>")
    buf.push("#{CGI.unescapeHTML(@attr[:others])}") if @attr[:others] != ""
    
    return buf.join("\n")
  end
  
  # Feed情報更新メソッド
  def self.update(path, feed)
    entrylist = Entry.readxml(path)
    Feed.to_xml(path, feed, entrylist)
    
    true
  end
  
  # XMLファイル出力用メソッドです
  #
  # _feed_ :: Feedオブジェクト
  # _entry_ :: Entryオブジェクトの配列
  def self.to_xml(path, feed, entrylist_tmp)
    buf = []
    entrylist = entrylist_tmp.dup
    buf.push("<?xml version=\"1.0\" encoding=\"utf-8\"?>")
    buf.push("#{feed.to_s}\n")
    entrylist.each { |entry|
      if REPLACEENTRYIDANDURL
        entry.entryid.gsub!(/^[^\?]*\?/, "")
        entry.entryid = feed.url + "?" + entry.entryid
        entry.url = feed.url + "#" + entry.entryid
      end
      buf.push("#{entry.to_s}\n")
    }
    buf.push("</feed>")
    
    myopen(path, "w") do |f|
      f.print buf.join("\n")
    end
  end
  
  # XMLファイル出力用メソッドです
  #
  # _feed_ :: Feedオブジェクト
  # _entry_ :: Entryオブジェクトの配列
  def self.to_xml_plain(path, feed, entrylist)
    buf = []
    buf.push("<?xml version=\"1.0\" encoding=\"utf-8\"?>")
    buf.push("#{feed.to_s}\n")
    entrylist.each { |entry|
      buf.push("#{entry.to_s}\n")
    }
    buf.push("</feed>")
    
    myopen(path, "w") do |f|
      f.print buf.join("\n")
    end
  end
  
end

# = Entryクラス
#
# Entryの基礎情報を保有するクラスです
class Entry < AbstractEntry
  # 初期化メソッドです
  #
  # _hash_ :: 値を格納したhash配列。entry.new(CGI::Session.new(new CGI).params)のようにして使います。
  def initialize(hash)
    # 内部データ保持用のハッシュ配列です
    @attr = {}
    
    # 内部データの項目名を格納する配列です
    @paramlist = ["entryid", "title", "summary", "published", "updated", "url", "content", "others"]
    
    # AbstractEntryのinitializeメソッドを呼び出し、値を@attr配列に格納します
    super(hash)
    
    # 可視・不可視を示すハッシュキーを格納する配列です
    @display = {"entryid" => "none", "title" => "",
      "summary" => "", "published" => "none",
      "updated" => "none", "url" => "",
      "content" => "", "others"=>"none"}
    
    # デバッグモードの場合、全ての入力要素を表示します
    if DEBUG == true
      @display.each do |key, val|
        @display[key] = ""
      end
    end
    
    # ハッシュキーの日本語説明を格納する配列です
    @name = {"entryid" => "記事固有のID", "title" => "記事のタイトル",
      "summary" => "記事の簡単な説明", "published" => "記事の出版時刻",
      "updated" => "記事の更新時刻", "url" => "記事へのURLアドレス",
      "content" => "記事の本文", "others"=>"その他の項目"}
  end
  
  # Atom XMLファイルを読み込んで解析し、等価なEntryオブジェクト配列を返却するメソッドです
  #
  # _path_ :: Atom XMLファイルのパス
  def self.readxml(path)
    
    # ファイルを読み込みます
    doc = REXML::Document.new(myopen(path, "r:utf-8"){|f|f.read})
    entrylist = []
    xml = {}
    
    # XML解析部分です。各element毎に判定を行います
    doc.elements.each("feed/entry") do |elm|
      xml = {}
      others = []
      elm.elements.each do |child|
        begin
          case child.name
            when "id"
            xml[:entryid] = child.text
            when "link"
            xml[:url] = child.attributes["href"]
            when "title", "summary", "summary", "published", "updated", "content"
            xml[child.name.to_sym] = child.text
          else
            # 上記判定以外の全要素は配列に格納します
            others.push(child.to_s)
          end
        rescue NoMethodError
        end
      end
      # Others要素を結合して代入します
      xml[:others] = others.join("\n")
      entrylist.push(Entry.new(xml))
    end
    
    return entrylist
  end
  
  # Atom XMLファイルを読み込んで解析し、テンプレートファイルにしたがってHTMLに変換するメソッドです
  def self.to_html(xmlpath, destpath, entry_temppath, html_temppath)
    # 引数チェック - 全必須
    if xmlpath.empty? or destpath.empty? or entry_temppath.empty? or html_temppath.empty?
      raise ArgumentError
    end
    
    # 必須ファイル存在チェック
    unless File.exist?(xmlpath) and File.exist?(entry_temppath) and File.exist?(html_temppath)
      raise IOError
    end
    
    # XML読み込み
    entrylist = Entry.readxml(xmlpath)
    
    body = ''
    entrylist.each { |e|
      # Entry毎のHTML表示部分を生成
      body << e.to_template(entry_temppath)
    }
    
    # HTML全体のテンプレートを生成
    html_temp = HtmlWriter.new(html_temppath, binding)
    
    # HTMLに書き込み
    myopen(destpath, 'w:utf-8') { |f|
      f.write(CGI.pretty(html_temp.to_code))
    }
  end
  
  # Entryをテンプレートに沿って変形するメソッド
  def to_template(temppath)
    erb = HtmlWriter.new(temppath, binding)
    title = CGI.unescapeHTML(@attr[:title])
    date = @attr[:published]
    content = CGI.unescapeHTML(@attr[:content])
    erb.to_code
  end
  
  # Entry挿入メソッド
  def self.insert(path, entry)
    feed = Feed.readxml(path)
    entrylist = Entry.readxml(path)
    entrylist.unshift entry
    
    Feed.to_xml(path, feed, entrylist)
    
    true
  end
  
  # Entry更新メソッド
  def self.update(path, entry)
    feed = Feed.readxml(path)
    entrylist = Entry.readxml(path)
    
    successed = false
    entrylist.each_with_index { |e, i|
      if e.entryid == entry.entryid
        entrylist[i] = entry 
        successed = true
      end
    }
    Feed.to_xml(path, feed, entrylist) if successed
    
    successed
  end
  
  # Entryロードメソッド
  def self.select(path, entryid)
    feed = Feed.readxml(path)
    entrylist = Entry.readxml(path)
    
    entry = nil
    entrylist.each_with_index { |e, i|
      entry = entrylist[i].dup if e.entryid == entryid
    }
    
    entry
  end
  
  # Entry消去メソッド
  def self.delete(path, entryid)
    feed = Feed.readxml(path)
    entrylist = Entry.readxml(path)
    
    successed = false
    delete_index = -1
    entrylist.each_with_index { |e, i|
      if e.entryid == entryid
        delete_index = i
        successed = true
      end
    }
    
    if successed
      entrylist.delete_at delete_index
      Feed.to_xml(path, feed, entrylist)
    end
    
    successed
  end
  
  # データソースから読み取ったHTMLを、エディタで編集可能な形式に変換するメソッドです
  def content_for_generator
    str = @attr[:content].dup
    str.strip!
    str.gsub!(/(&lt;\/(?:p|h\d|div)(?:&gt;|>))\n/i, '\1')
    str.gsub!(/\n/, '&lt;br&gt;') if REPLACEBRTAG
    str.gsub!(/(&lt;(?:(?!&gt;).)*?)#{Regexp.escape(RELAYPATH)}/) { "#$1#{HOMEBASE}" }
    str
  end
  
  # エディタで編集されたCONTENT要素を、データソースに書き込める形式に変換するメソッドです
  def content_for_blog
    str = @attr[:content].dup
    str = CGI.unescapeHTML(str)
    str.strip!
    str.gsub!(/(\r\n|\n)/, "")
    str.gsub!(/<br>|<br[ ]*\/>/i, "\n") if REPLACEBRTAG
    str.gsub!(/(<br>|<br[ ]*\/>|<\/p>|<\/h\d>|<\/div>)(?=[^\n])/i) { "#$1\n" } unless REPLACEBRTAG
    str.gsub!(/(<[^>]*?)#{Regexp.escape(HOMEBASE)}/) { "#$1#{RELAYPATH}" }
    CGI.escapeHTML(str)
  end
  
  # 確認画面で表示されるCONTENT要素を生成するメソッドです
  def content_for_view
    str = @attr[:content].dup
    str = CGI.unescapeHTML(str)
    str.strip!
    str.gsub!(/<br>|<br[ ]*\/>/i, "\n") if REPLACEBRTAG
    str.gsub!(/(<[^>]*?)#{Regexp.escape(RELAYPATH)}/) { "#$1#{HOMEBASE}" }
    str
  end
  
  # 内部に保持している情報を、Atom Feed1.0形式の文字列に出力するメソッドです
  def to_s
    buf = []
    buf.push("<entry>")
    buf.push("<id>#{@attr[:entryid]}</id>")
    buf.push("<title>#{@attr[:title]}</title>")
    buf.push("<summary>#{@attr[:summary]}</summary>")
    buf.push("<published>#{@attr[:published]}</published>")
    buf.push("<updated>#{@attr[:updated]}</updated>")
    buf.push("<link href=\"#{@attr[:url]}\" />")
    buf.push("<content type=\"html\">#{@attr[:content]}</content>")
    buf.push("#{CGI.unescapeHTML(@attr[:others])}") if @attr[:others] != ""
    buf.push("</entry>")
    
    return buf.join("\n")
  end
end

# = HtmlWriterクラス
# 
# テンプレートファイル(*.erb)を読み込み、管理するクラスです
class HtmlWriter
  # 初期化メソッドです
  #
  # _template_ :: テンプレートファイル(*.erb)のパス
  # _binding_ :: binding変数
  def initialize(template, binding)
    @erb = ERB.new(myopen(template, "r:utf-8") {|f| f.read}, nil, "-")
    @binding = binding
  end
  
  # テンプレートファイルの文字列を返却するメソッドです
  def to_code
    @erb.result(@binding)
  end
end

# = LogListクラス
#
# loglist.xmlにかかわる入出力を行います
class LogList
  # 初期化メソッドです
  #
  # _display_ :: 画面表示用文字の配列
  # _path_ :: XMLファイルパスの配列
  # _logpath_ :: loglist.xmlのパス
  def initialize(display, path, logpath)
    @display = display
    @path = path
    @logpath = logpath
  end
  
  attr_accessor :display, :path, :logpath
  
  # loglist.xmlファイルを読み込んで解析し、LogListオブジェクトを返却するメソッドです
  #
  # _logpath_ :: loglist.xmlへのパス
  def LogList.readxml(logpath)
    
    # ファイルを読み込みます
    lines = []
    myopen(logpath, "r:utf-8") { |f|
      lines = f.readlines
    }
    
    doc = REXML::Document.new(lines.join("\n"))
    @display = []
    @path = []
    
    doc.elements.each("list/file") { |elm|
      elm.elements.each {|child|
        case child.name
          when "display"
          @display.push(child.text)
          when "path"
          @path.push(child.text)
        else 
          # With no action
        end
      }
    }
    
    return LogList.new(@display, @path, logpath)
  end
  
  # loglist.xmlにオブジェクトの内容を反映するメソッドです
  def to_xml
    buf = []
    buf.push("<list>")
    path.each_with_index do |path, i|
      buf.push("<file>")
      buf.push("<display>#{@display[i]}</display>")
      buf.push("<path>#{@path[i]}</path>")
      buf.push("</file>")
    end
    buf.push("</list>")
    
    myopen(@logpath, "w") do |f|
      f.print buf.join("\n")
    end
  end
  
end

# = FileUploaderクラス
#
# 画像の管理を行うクラスです
class FileUploader
  # 初期化メソッドです
  def initialize
  end
  
  # ファイルの一覧を取得し、ファイル名称を格納した配列を返却します
  def filelist
    arr = Dir::entries(XMLPATH).sort
    arr.delete(".")
    arr.delete("..")
    
    return arr
  end
  
  # ファイルの消去を実行します
  #
  # _name_ :: 消去するファイル名
  def delete(name)
    File.delete(XMLPATH + name.match(/[^\/]*?$/).to_a[0])
  end
  
  # ファイルのアップロードを実行します
  #
  # _name_ :: アップロードファイルの名称
  # _img_ :: アップロードファイル
  def upload(name, img)
    open(XMLPATH + File.basename(name), "w") do |f|
      f.binmode
      f.write img
    end
  end
end

# = Pluginクラス
#
# プラグインの処理を行うクラスです
class FeedGenPluginManager
  def self.exec(mode, filepath)
    feed = Feed.readxml(XMLPATH + filepath)
    entries = Entry.readxml(XMLPATH + filepath)
    feed.freeze
    entries.freeze
    Dir.foreach(PLUGINDIR) do |fn|
      next unless File.extname(fn) == '.rb'
      require File.join(PLUGINDIR, fn)
      plugin_name = "FeedGenPlugins::"
      plugin_name << File.basename(fn).gsub(/\.rb\Z/, "")
      plugin_ins = plugin_name.split(/::/).inject(Object) { |c,name| c.const_get(name) }
      plugin_ins.new.exec(mode, feed, entries)
    end
  end
end

# = Controllerクラス
#
# コントローラ部分に相当する処理を受け持つクラスです
class Controller
  def Controller.NormalForm(cgi, session, params, db)
    db.transaction do
      # modeとactionの相関図は以下のようになります。
      # [mode] + [action]
      #        + [action]
      #        + [action]
      #        + ... and more
      case params["mode"]
        # ログ選択画面
        when "logselect"
        session["filepath"] = params["logpath"]
        db["loglist"] = LogList.readxml(LISTXMLPATH)
        # 初期状態で選択されるログは「loglist.xml」の最上に位置するログになります
        session["filepath"] = db["loglist"].path[0] if session["filepath"] == nil
        db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
        db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
        
        # 新規記事追加部分
        when "newentry"
        case params["action"]
          # 確認画面
          when "confirm"
          session["target_filepath"] = params["target_filepath"]
          db["newentry"] = Entry.new(params)
          db["newentry"].content = db["newentry"].content_for_blog
          # 記事の追記を実際にファイルに反映
          when "exec"
          session["target_filepath"] = params["target_filepath"]
          successed = Entry.insert(XMLPATH + File.basename(params["target_filepath"]), Entry.new(params))
          unless successed
            db["error"] = "日記の新規追加に失敗しました。" 
            params["mode"] = "error"
          else
            # 成功時はプラグイン処理を実施する
            FeedGenPluginManager.exec("newentry", File.basename(session["filepath"]))
          end
          # 画面を戻った際の処理
          when "back"
          session["target_filepath"] = params["target_filepath"]
          db["newentry"] = Entry.new(params)
        else 
          # New Diary - Default
          session["target_filepath"] = session["filepath"]
          db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
          db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
        end
        
        # 記事編集部分
        when "editentry"
        case params["action"]
          # 編集画面
          when "edit"
          session["target_filepath"] = params["target_filepath"]
          session["editid"] = cgi["editid"].to_s
          db["editentry"] = Entry.select(XMLPATH + File.basename(session["target_filepath"]), session["editid"])
          # 確認画面
          when "confirm"
          session["target_filepath"] = params["target_filepath"]
          session["editid"] = cgi["editid"].to_s
          db["editentry"] = Entry.new(params)
          db["editentry"].content = db["editentry"].content_for_blog
          # 記事の変更を実際にファイルに反映
          when "exec"
          session["target_filepath"] = params["target_filepath"]
          successed = Entry.update(XMLPATH + File.basename(params["target_filepath"]), Entry.new(params))
          unless successed
            db["error"] = "日記の編集処理に失敗しました。<br>該当の日記が既に存在しない可能性があります。" 
            params["mode"] = "error"
          else
            # 成功時はプラグイン処理を実施する
            FeedGenPluginManager.exec("editentry", File.basename(session["filepath"]))
          end
          when "back"
          session["target_filepath"] = params["target_filepath"]
          db["editentry"] = Entry.new(params)
        else
          # Edit Diary - Default
          session["target_filepath"] = session["filepath"]
          db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
          db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
        end
        
        # 記事削除部分
        when "delentry"
        case params["action"]
          # 確認画面
          when "confirm"
          session["target_filepath"] = params["target_filepath"]
          session["delid"] = cgi["delid"].to_s
          db["delentry"] = Entry.select(XMLPATH + File.basename(session["target_filepath"]), session["delid"])
          # 記事の削除を実際にファイルに反映
          when "exec"
          session["target_filepath"] = params["target_filepath"]
          successed = Entry.delete(XMLPATH + File.basename(params["target_filepath"]), cgi["delid"].to_s)
          unless successed
            db["error"] = "日記の編集処理に失敗しました。<br>該当の日記が既に存在しない可能性があります。" 
            params["mode"] = "error"
          else
            # 成功時はプラグイン処理を実施する
            FeedGenPluginManager.exec("delentry", File.basename(session["filepath"]))
          end
          when "back"
          session["target_filepath"] = params["target_filepath"]
          db["feed"] = Feed.readxml(XMLPATH + File.basename(session["target_filepath"]))
          db["entry"] = Entry.readxml(XMLPATH + File.basename(session["target_filepath"]))
        else
          # Delete Diary - Default
          session["target_filepath"] = session["filepath"]
          db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
          db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
        end
        
        # Feed情報変更部分
        when "editfeed"
        case params["action"]
          # 確認画面
          when "confirm"
          session["target_filepath"] = params["target_filepath"]
          db["feed"] = Feed.new(params)
          # 実際にFeed情報の変更をファイルに反映
          when "back"
          session["target_filepath"] = params["target_filepath"]
          db["feed"] = Feed.new(params)
          when "exec"
          session["target_filepath"] = params["target_filepath"]
          Feed.update(XMLPATH + File.basename(params["target_filepath"]), Feed.new(params))
        else
          session["target_filepath"] = session["filepath"]
          db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
        end
        
        # ログ編集モード
        when "log"
        # 必ず内部データをリフレッシュする
        db["loglist"] = LogList.readxml(LISTXMLPATH)
        case params["action"]
          # ログファイルの編集を実際にファイルに反映
          when "addexec"
          # エラーチェック。この段階のエラーは強度のエラーを発する。
          db["loglist"].path.each do |val|
            if val == cgi["logpath"]
              # 重複していた場合エラーメッセージを表示し、処理を行わない
              params["action"] = ""
              params["mode"] = "error"
              db["error"] = "ログファイルの追加中に重大なエラーが発生しました。<br>環境を見直してください。"
              return
            end
          end
          
          # 入力された内容を保持する配列に追加
          db["loglist"].path[1, 0] = cgi["logpath"]
          db["loglist"].display[1, 0] = cgi["logdisplay"]
          db["loglist"].to_xml
          # 既存のdiary.xmlを指定された名称でコピーして保存
          FileUtils.copy_file(XMLPATH + File.basename(db["loglist"].path[0]), XMLPATH + File.basename(db["logpath"]), true)
          # 保持している情報を更新する
          db["loglist"] = LogList.readxml(LISTXMLPATH)
          db["entry"] = []
          # 新たなdiary.xmlを生成。この際保持する情報は同一のFeedオブジェクトnew()
          Feed.to_xml(XMLPATH + File.basename(db["loglist"].path[0]), db["feed"], [])
          # 確認画面
          when "addconfirm"
          # 入力されたログが既に存在するかを確認
          db["loglist"].path.each do |val|
            if val == cgi["logpath"]
              # 重複していた場合エラーメッセージを表示し、処理を行わない
              params["action"] = ""
              db["error"] = "<span style='color: #ff0000'>同一のファイルが存在します！別の名前を指定してください。</span><br>"
              return
            end
          end
          
          db["logpath"] = cgi["logpath"]
          db["logdisplay"] = cgi["logdisplay"]
          
          if db["logpath"].blank? || db["logdisplay"].blank?
            params["action"] = ""
          end
          when "back"
          
          # 削除確認画面
          when "delconfirm"
          db["logdelindex"] = params["logdelindex"].to_i
          
          if db["logdelindex"] < 1
            db["error"] = "<span style='color: #ff0000'>ログファイルの削除パラメタが不正です。</span><br>"
            params["action"] = ""
          end
          # 削除処理
          when "delexec"
          if cgi["logdelindex"].to_i < 1
            params["action"] = ""
            params["mode"] = "error"
            db["error"] = "ログファイルの削除中に重大なエラーが発生しました。<br>環境を見直してください。"
            return
          else
            # 記事ファイルを削除します
            File.delete(XMLPATH + File.basename(db["loglist"].path[db["logdelindex"]]))
            # ログリストから削除します
            db["loglist"].path.delete_at(cgi["logdelindex"].to_i)
            db["loglist"].display.delete_at(cgi["logdelindex"].to_i)
            db["loglist"].to_xml
            # 保持している情報を更新する
            db["loglist"] = LogList.readxml(LISTXMLPATH)
            db["entry"] = []
          end
          
          # 編集画面
          when "edit"
          db["logeditindex"] = params["logdelindex"].to_i
          
          db["logpath"] = db["loglist"].path[db["logeditindex"]]
          db["logdisplay"] = db["loglist"].display[db["logeditindex"]]
          
          if db["logeditindex"] == 0
            db["error"] = "<span style='color: #ff0000'>ログファイルの編集パラメタが不正です。</span><br>"
            params["action"] = ""
          end
          # 編集確認画面
          when "editconfirm"
          checkflag = true
          db["loglist"].path.each_with_index do |val, i|
            if db["logeditindex"] != i
              if params["logpath"].to_s == db["loglist"].path[i].to_s
                checkflag = false
              end
            end
          end
          
          if checkflag == false
            params["action"] = "edit"
            db["error"] = "<span style='color: #ff0000'>同一のファイルが存在します！別の名前を指定してください。</span><br>"
          else
            db["loginsertindex"] = params["loginsertindex"].to_i
            
            db["logpath"] = params["logpath"].to_s
            db["logdisplay"] = params["logdisplay"].to_s
          end
          # 編集実行
          when "editexec"
          checkflag = true
          db["loglist"].path.each_with_index do |val, i|
            if db["logeditindex"] != i
              if params["logpath"].to_s == db["loglist"].path[i].to_s
                checkflag = false
              end
            end
          end
          
          if checkflag == false
            params["action"] = ""
            params["mode"] = "error"
            db["error"] = "ログファイルの編集中に重大なエラーが発生しました。<br>環境を見直してください。"
            return
          else
            db["loginsertindex"] = params["loginsertindex"].to_i
            
            db["logpath"] = params["logpath"].to_s
            db["logdisplay"] = params["logdisplay"].to_s
            
            # ファイルを移動します
            if XMLPATH + File.basename(db["loglist"].path[db["logeditindex"]]) != XMLPATH + File.basename(db["logpath"])
              FileUtils.move(XMLPATH + File.basename(db["loglist"].path[db["logeditindex"]]), XMLPATH + File.basename(db["logpath"]))
              # ログリストを更新します
              db["loglist"].path.delete_at(db["logeditindex"])
              db["loglist"].display.delete_at(db["logeditindex"])
              db["loglist"].path.insert(db["loginsertindex"] + 1, db["logpath"])
              db["loglist"].display.insert(db["loginsertindex"] + 1, db["logdisplay"])
              db["loglist"].to_xml
            end
          end
          
          # 初期表示画面
        else
          # 現在編集中のデータを強制的に最上のファイルパスに変更
          session["filepath"] = db["loglist"].path[0]
          
          # 前月の時刻を作成します
          prevmonth = (DateTime.now << 1)
          
          # 前月の時刻を元に、ディフォルト書式を生成します
          db["logpath"] = FEEDXMLDIR + prevmonth.strftime("%Y%m") + ".xml"
          db["logdisplay"] = prevmonth.strftime("%Y年%m月").gsub("年0", "年")
        end
        
        # インポートモードの場合の処理
        when "import"
        db["loglist"] = LogList.readxml(LISTXMLPATH)
        
        # リセットモードの場合の処理
        when "reset"
        case params["action"]
          # リセット実行時の処理
          when "exec"
          file = FileUploader.new
          file.filelist().each { |fname|
            file.delete(fname) if File.ftype(XMLPATH + fname) == "file"
          }
          
          # 全ファイルの初期化を実行する
          # loglist.xmlの初期化
          loglist = LogList.new(["最新の記事"], ["#{FEEDXMLDIR}#{INITIALXML}"], LISTXMLPATH)
          loglist.to_xml
          
          db["loglist"] = LogList.readxml(LISTXMLPATH)
          
          # diary.xmlの初期化
          Feed.to_xml(XMLPATH + db["loglist"].path[0].match(/[^\/]*?$/).to_a[0], Feed.new({}), [])
          
          # 初期の編集ファイルはloglist.xml上の最も上のファイル
          session["filepath"] = db["loglist"].path[0]
          db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
          db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
          
          # 画面の遷移先をトップページにする
          params["mode"] = ""
          params["action"] = ""
          # パラメタ無しの処理
        else
          # ファイル一覧を取得する
          filelist = FileUploader.new.filelist()
          
          # タイプがファイルのみリストとして取得する
          db["filelist"] = []
          filelist.each { |fname| db["filelist"] << fname if File.ftype(XMLPATH + fname) == "file"}
        end
        
        # ログイン時の画面
      else
        # loglist.xmlが存在するかチェック
        if File.exist?(LISTXMLPATH) == false
          # なかった場合はloglist.xmlを自動生成
          loglist = LogList.new(["最新の記事"], ["#{FEEDXMLDIR}#{INITIALXML}"], LISTXMLPATH)
          loglist.to_xml
          Feed.to_xml(XMLPATH + File.basename(loglist.path[0]), Feed.new({}), [])
        end
        
        db["loglist"] = LogList.readxml(LISTXMLPATH)
        
        # diary.xmlが存在するかチェック
        if File.exist?(XMLPATH + File.basename(db["loglist"].path[0])) == false
          # なかった場合はdiary.xmlを自動生成
          Feed.to_xml(XMLPATH + File.basename(db["loglist"].path[0]), Feed.new({}), [])
        end
        
        # 初期の編集ファイルはloglist.xml上の最も上のファイル
        session["filepath"] = db["loglist"].path[0] if session["filepath"] == nil
        db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
        db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
      end
    end
  end
  
  # マルチパートフォームの場合の処理です
  def Controller.MultiForm(cgi, session, params, db)
    db.transaction do
      # loglist.xmlをロードします
      db["loglist"] = LogList.readxml(LISTXMLPATH)
      case params["mode"]
        # 特定位置に挿入する場合の処理
        when "insert"
        case params["action"]
          when "exec"
          # この段階のエラーに対しては強度のエラーを発する
          checkflag = true
          db["logpath"] = Controller.get_mpart_value(cgi["logpath"])
          db["loglist"].path.each do |val|
            if val == db["logpath"]
              # 重複していた場合エラーメッセージを表示し、処理を行わない
              db["logpath"] = ""
              params["action"] = ""
              params["mode"] = "error"
              db["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
              return
            end
          end
          
          db["loginsertindex"] = cgi["loginsertindex"].read.to_i
          db["logdisplay"] = Controller.get_mpart_value(cgi["logdisplay"])
          
          # 0位置への挿入防止
          if db["loginsertindex"] == 0
            params["action"] = ""
            params["mode"] = "error"
            db["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
            return
          end
          
          if File.basename(db["logpath"]).blank? || db["logdisplay"].blank?
            params["action"] = ""
            params["mode"] = "error"
            db["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
            return
          end
          
          # ファイルを実際にアップロードします
          file = FileUploader.new
          file.upload(db["logpath"], db["importxml"])
          
          # loglist.xmlを更新します
          db["loglist"].path[db["loginsertindex"], 0] = db["logpath"]
          db["loglist"].display[db["loginsertindex"], 0] = db["logdisplay"]
          db["loglist"].to_xml
          
          # 情報を更新します
          db["loglist"] = LogList.readxml(LISTXMLPATH)
          session["filepath"] = db["loglist"].path[0] if session["filepath"] == nil
          db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
          db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
          
          when "confirm"
          # 入力されたログファイルパスが既に存在するかを確認
          checkflag = true
          db["logpath"] = Controller.get_mpart_value(cgi["logpath"])
          db["loglist"].path.each do |val|
            if val == db["logpath"]
              # 重複していた場合エラーメッセージを表示し、処理を行わない
              db["logpath"] = ""
              params["action"] = ""
              db["error"] = "<br><span style='color: #ff0000'>同一のファイルが存在します！別の名前を指定してください。</span><br>"
              checkflag = false
              return
            end
          end
          
          if checkflag
            db["loginsertindex"] = cgi["loginsertindex"].read.to_i
            db["logdisplay"] = Controller.get_mpart_value(cgi["logdisplay"])
            db["importxml"] = cgi["updata"].read
            
            # XMLの整合性をチェックします
            begin
              REXML::Document.new(Controller.fix_updata_enc(db["importxml"].to_s))
            rescue => exception
              db["error"] = "<br><span style='color: #ff0000'>不正な整形のXMLです！ファイルを見直してください。</span><br>"
              db["error"] << "<div class='divstyle' style='width: 560px; color: #ff0000; text-align: left;'>"
              db["error"] << CGI.escapeHTML(exception.to_s).gsub("\n", "<br>")
              db["error"] << "</div>"
              params["action"] = ""
              return
            end
          end
          
          # 0位置への挿入防止
          if db["loginsertindex"] == 0
            params["action"] = ""
            db["error"] = "<br><span style='color: #ff0000'>ラジオボックスでログの挿入位置を選択してください！</span><br>"
            return
          end
          
          if db["logpath"] == FEEDXMLDIR || db["logdisplay"].blank?
            params["action"] = ""
            db["error"] = "<br><span style='color: #ff0000'>インポートファイル、及び、ログの表示名は空欄にできません。</span><br>"
            return
          end
          
        else
        end
        
        # diary.xmlを入れ替える処理
        when "replace"
        case params["action"]
          when "exec"
          # この段階のエラーに対しては強度のエラーを発する
          checkflag = true
          db["logpath"] = Controller.get_mpart_value(cgi["logpath"])
          db["loglist"].path.each do |val|
            if val == db["logpath"]
              # 重複していた場合エラーメッセージを表示し、処理を行わない
              params["action"] = ""
              params["mode"] = "error"
              db["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
              return
            end
          end
          
          db["logdisplay"] = Controller.get_mpart_value(cgi["logdisplay"])
          
          if File.basename(db["logpath"]).blank? || db["logdisplay"].blank? || db["importxml"].blank?
            params["action"] = ""
            params["mode"] = "error"
            db["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
            return
          end
          
          # diary.xmlを移動します
          FileUtils.move(XMLPATH + INITIALXML, XMLPATH + File.basename(db["logpath"]))
          # ファイルをアップロードします
          file = FileUploader.new
          file.upload(INITIALXML, db["importxml"])
          
          # loglist.xmlを更新します
          db["loglist"].path[1, 0] = db["logpath"]
          db["loglist"].display[1, 0] = db["logdisplay"]
          db["loglist"].to_xml
          
          # 情報を更新します
          db["loglist"] = LogList.readxml(LISTXMLPATH)
          session["filepath"] = db["loglist"].path[0] if session["filepath"] == nil
          db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
          db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
          
          when "confirm"
          # 入力されたログファイルパスが既に存在するかを確認
          checkflag = true
          db["logpath"] = Controller.get_mpart_value(cgi["logpath"])
          db["loglist"].path.each do |val|
            if val == db["logpath"]
              # 重複していた場合エラーメッセージを表示し、処理を行わない
              params["action"] = ""
              db["error"] = "<br><span style='color: #ff0000'>同一のファイルが存在します！別の名前を指定してください。</span><br>"
              checkflag = false
              return
            end
          end
          
          if checkflag == true
            db["logdisplay"] = Controller.get_mpart_value(cgi["logdisplay"])
            db["importxml"] = cgi["updata"].read
            
            # XMLの整合性をチェックします
            begin
              REXML::Document.new(Controller.fix_updata_enc(db["importxml"].to_s))
            rescue => exception
              db["error"] = "<br><span style='color: #ff0000'>不正な整形のXMLです！ファイルを見直してください。</span><br>"
              db["error"] << "<div class='divstyle' style='width: 560px; color: #ff0000; text-align: left;'>"
              db["error"] << CGI.escapeHTML(exception.to_s).gsub("\n", "<br>")
              db["error"] << "</div>"
              params["action"] = ""
              return
            end
          end
          
          if db["logpath"].blank? || db["logdisplay"].blank? || db["importxml"].blank?
            params["action"] = ""
            db["error"] = "<br><span style='color: #ff0000'>インポートファイル、及び、ログの表示名、ログのパスは空欄にできません。</span><br>"
            return
          end
          
        else
        end
        
      else
      end
    end
  end
  
  # Formのenctypeがmultypartだった場合、入力された値をrubyバージョンに左右されずに読み取るメソッドです。
  def Controller.get_mpart_value(cgi_param)
    if RUBY_VERSION >= "1.9.0"
      cgi_param[0..cgi_param.length].to_s
    else
      cgi_param.read.to_s
    end
  end
  
  # アップロードされたXMLをバージョンに左右されずに読み取るために、ruby-1.9.1以上ではエンコーディングを強制指定します
  def Controller.fix_updata_enc(file_to_s)
    if RUBY_VERSION >= "1.9.0"
      file_to_s.force_encoding("UTF-8")
    else
      file_to_s
    end
  end
  
end

def main
  # SESSION変数、パラメータなどを取得します
  cgi = CGI.new
  session = CGI::Session.new(cgi)
  params = Hash[*cgi.params.to_a.map{|k, v| [k, v[0].to_s]}.flatten]
  
  # コントローラー部分
  # セッション管理
  if session["login"] != "true"
    
    # ログイン情報を確認
    LOGININFO.each {|h|
      if (cgi["loginid"] == h[:id] && cgi["password"] == h[:password])
        session["login"] = "true"
        session["name"] = h[:name]
        break
      end
    }
    
    if (session["login"] == "true")   
      # ワークフォルダの中をクリーンアップします
      filelist = Dir::entries("./work")
      # 削除条件 : 最終変更日時から1日(60*60*24sec)かつ、ファイルタイプがファイルの場合
      filelist.each do |file|
        File.delete("./work/#{file}") if Time.now - File.ctime("./work/#{file}") > 86400 && File.ftype("./work/#{file}") == "file"
      end
    end
  end
  
  # ログアウト処理
  if params["mode"] == "logout"
    session["login"] = nil
    session.delete
  end
  
  begin
    # メインコントローラー
    # セッションが有効な場合のも実行します
    if session["login"] == "true"
      # PStore破損チェック！
      begin
        db = PStore.new("./work/#{session.session_id}.dat")
        db.transaction do
          db["error"] = ""
        end
      rescue
        # PStoreファイルを破棄する
        File.delete("./work/#{session.session_id}.dat")
        # PStoreが破損していた場合はセッション情報を破棄する
        session["login"] = nil
        session.delete
      end
      
      # フォームによって挙動を変更します
      if cgi["mode"].respond_to?(:read) && cgi["action"].respond_to?(:read)
        params["mode"] = cgi["mode"].read
        params["action"] = cgi["action"].read
        Controller.MultiForm(cgi, session, params, db)
      else
        Controller.NormalForm(cgi, session, params, db)
      end
      
      # エラー画面移行時はセッション情報を破棄する
      if params["mode"] == "error"
        session["login"] = nil
        session.delete
      end
    end
    
    # メニューとして表示されるHTML文字列です
    if params["target_filepath"].blank?
      menu = "<div class=\"divstyle\" style=\"width: #{TABLEWIDTH}px;\"><div class=\"divstyle\" align=\"center\" style=\"margin-bottom: 5px;\">現在編集中のファイル : #{session["filepath"]} "
    else
      menu = "<div class=\"divstyle\" style=\"width: #{TABLEWIDTH}px;\"><div class=\"divstyle\" align=\"center\" style=\"margin-bottom: 5px;\">現在編集中のファイル : #{session["target_filepath"]} "
    end
    if USEFILEMANAGER == true
      menu += "[ <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>"
    else
      menu += "[ <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>"
    end
    
    # ビュー部分
    # modeとactionの相関図は以下のようになります。
    # [mode] + [action]
    #        + [action]
    #        + [action]
    #        + ... and more
    if session["login"] != "true"
      # セッションが存在しない場合は強制的にトップページに遷移
      htmlwriter = HtmlWriter.new("./erbtemp/login.html.erb", binding)
    else
      case params["mode"]
        when "selectlog"
        htmlwriter = HtmlWriter.new("./erbtemp/select.html.erb", binding)
        when "newentry"
        htmlwriter = HtmlWriter.new("./erbtemp/newentry.html.erb", binding)
        when "editentry"
        htmlwriter = HtmlWriter.new("./erbtemp/editentry.html.erb", binding)
        when "delentry"
        htmlwriter = HtmlWriter.new("./erbtemp/delentry.html.erb", binding)
        when "editfeed"
        htmlwriter = HtmlWriter.new("./erbtemp/editfeed.html.erb", binding)
        when "log"
        htmlwriter = HtmlWriter.new("./erbtemp/log.html.erb", binding)
        when "insert"
        htmlwriter = HtmlWriter.new("./erbtemp/insertfeed.html.erb", binding)
        when "replace"
        htmlwriter = HtmlWriter.new("./erbtemp/replacefeed.html.erb", binding)
        when "import"
        htmlwriter = HtmlWriter.new("./erbtemp/indeximport.html.erb", binding)
        when "reset"
        htmlwriter = HtmlWriter.new("./erbtemp/reset.html.erb", binding)
        when "error"
        htmlwriter = HtmlWriter.new("./erbtemp/error.html.erb", binding)
      else
        htmlwriter = HtmlWriter.new("./erbtemp/index.html.erb", binding)
      end
    end
  rescue => exception
    # エラーが発生した場合、それを画面に表示します
    htmlwriter = HtmlWriter.new("./erbtemp/exception.html.erb", binding)
  end
  
  # 実際にHTMLを出力します
  begin
    cgi.out{htmlwriter.to_code}
  rescue => exception
    # エラーが発生した場合、それを画面に表示します
    htmlwriter = HtmlWriter.new("./erbtemp/exception.html.erb", binding)
    cgi.out{htmlwriter.to_code}
  end
end

begin
  main
rescue => evar
  # エラーが発生した場合、それを画面に表示します
  detail = ("%s: %s (%s)\n" %
  [evar.backtrace[0], evar.message, evar.send('class')]) +
  evar.backtrace[1..-1].join("\n")
  puts "content-type: text/html\n\n<plaintext>\n" + detail
end
