大体見てみたいものは作ったかな~と思うので,いよいよファイルを選べるようにする.
まず,modeが選択されてなかったら日付と処理モードを選ぶ初期画面を表示するのだ.最初,それを初期画面表示メソッドとしてinitというのを作っていたのだが,入力にエラーがあったときはエラー表示と共に,選んだモードなんかは保持した上でもう一度初期画面が出ると便利だなぁなどと考えていたら,条件やら何やらが面倒になってきた.
それと,「何月何日から何月何日までの範囲で入力してください」と表示させないとどこから見られるか分からないやと,過去ログファイルの表示に頭を悩ませる.Dir.entriesを使えるとラクなんだけど,セキュリティエラーがでるのでまた悩む.
どうにかロジックを組んで,画面を表示させていざ,過去ログ選んでみたら,おっとまたセキュリティエラー!!
そうなのである.外部からの入力を使ってのファイル操作はセーフレベル1ではできないのだ.だめだ,こればかりは他の方法って訳に行かない.
うんうん唸って,悩んだ挙句,怖かったけど,入力文字列をチェックした上で,禁断のuntaint.こうなると,上述の最古ファイル~最新ファイルを得るためのメソッドにDir.entriesを使わないのもなんだなぁと思い,結局,いろんなものを書き換えた.
#最新のログファイル(圧縮なし)を取得するメソッド
def lastlog
logfilename = Dir.entries(LOG_DIR).sort[-3]
if /^access_log_\d{8}$/ =~ logfilename
logfilename.untaint
logfilename = LOG_DIR + logfilename
end
return logfilename
end
となった.
一番古いログファイルを得る方はしたがって,
#もっとも古いログファイルを取得する
def oldestlog
logfilename = Dir.entries(LOG_DIR).sort[2]
if /^access_log_\d{8}\.gz$/ =~ logfilename
logfilename.untaint
logfilename = LOG_DIR + logfilename
end
return logfilename
end
となる.
最新・最古ファイルが決まると初期画面が決められる.
#初期画面表示関数
def init(log="", date="", mode="")
/(\d{4})(\d{2})(\d{2})/ =~ lastlog()
last = [$1, $2, $3]
/(\d{4})(\d{2})(\d{2})/ =~ oldestlog()
old = [$1, $2, $3]
print <<EOF
<form action="#{SCRIPT}" method="get">
<fieldset>
<legend>ログファイル選択</legend>
<p>#{old[0]}年#{old[1]}月#{old[2]}日~#{last[0]}年#{last[1]}月#{last[2]}日から選択</p>
EOF
if log == "last" || log == ""
print <<EOF
<label><input type="radio" name="log" value="last" checked>最新</label>
<label><input type="radio" name="log" value="old">過去ログ</label>
<label><input type="text" name="date" size="10">(半角数字,YYYYMMDDという形式で入力すること)</label>
EOF
else
print <<EOF
<label><input type="radio" name="log" value="last">最新</label>
<label><input type="radio" name="log" value="old" checked>過去ログ</label>
<label><input type="text" name="date" size="10" value="#{date}">(半角数字,YYYYMMDDという形式で入力すること)</label>
EOF
end
print <<EOF
</fieldset>
<fieldset>
<legend>モード選択</legend>
EOF
print '<label><input type="radio" name="mode" value="view"'
print " checked" if mode == "view" || mode == ""
print ">アクセスログ</label>\n"
print '<label><input type="radio" name="mode" value="error"'
print " checked" if mode == "error"
print ">エラーログ</label>\n"
print '<label><input type="radio" name="mode" value="trace"'
print " checked" if mode == "trace"
print ">サイト内遷移</label>\n"
print '<label><input type="radio" name="mode" value="robot"'
print " checked" if mode == "robot"
print ">クローラー挙動</label>\n"
print <<EOF
</fieldset>
<input type="submit" value="実行">
</form>
EOF
end
やっと部品ができたので,呼び出し側のメインプログラムを以下のように書き換える.
cgi = CGI.new
mode = cgi.params["mode"].to_s #モード取得
log = cgi.params["log"].to_s #最新ログかそうでないか
if mode == "" #もしモードがなかったら
header_out("ログ/モード選択")
init() #ファイル/モード選択画面
footer_out()
else
#モードがある場合,まず,表示するファイルをチェック
if log == "last" #最新を選択していたら最新ファイルをセット
file = lastlog()
else #過去ログを選択している場合
date = cgi.params["date"].to_s
unless /^\d{8}$/ =~ date
header_out("入力エラー")
print "<p>半角数字で8桁(YYYYMMDDの形式)を入力してください.</p>\n"
init(log, date, mode)
footer_out()
exit
else
date.untaint #8桁の数字だったら汚染を除去
end
file = "#{LOG_DIR}access_log_#{date}.gz"
unless FileTest.exist?(file)
header_out("ファイルがありません.")
print "<p>日時の指定が範囲内か確認してください.</p>\n"
init(date, mode)
footer_out()
exit
end
file = "|/usr/bin/gunzip -c #{file}"
end
case mode #モードがある場合の処理
when "view"
log_view(file) #生ログ表示
when "error"
errorlog_view(file) #エラーログ表示
when "trace"
trace_view(file) #サイト内遷移の様子を表示
when "robot"
trace_robot(file)
else
header_out("モードエラー")
print "<p>有効なモードを取得できません.</p>"
footer_out()
end
end
いちおう,動いてるみたい.
じゃ,あとは,集計のことを考えるか.