#!/usr/bin/env ruby22 # coding: utf-8 # fruits3.rb require 'sqlite3' require 'kconv' require 'cgi' $myname = 'fruits3.rb' $dbfile = "tmpdb/fruits.sq3" # セッションキーのPREFIXを、接続IPアドレス+日付にしていずれか一方でも # 変わった場合は使えないようにする。 skeyprefix = ENV["REMOTE_ADDR"] + Time.now.strftime("/%Y%m%d%H/") cgi = CGI.new(:accept_charset => "utf-8") db = SQLite3::Database.new($dbfile) db.execute("CREATE TABLE IF NOT EXISTS fruits(item, at, memo)") db.execute("CREATE TABLE IF NOT EXISTS kudamono(" + "id integer primary key autoincrement, item, at, memo)") # ログイン画面から来たものであれば,パスワード照合し,セッションキーを生成 # して表に格納する。 user = cgi['user'] # ログインフォームから来た場合値が入る authok = nil # 認証できたかどうか wasureta = "wasureta" # パスワード失念時の呪文 logininfo = "" # ログインプロンプトの追加メッセージ cookie = cgi.cookies # cgi.cookiesに配列のハッシュが入っている skey = cookie['skey'][0] # cookieにある(はずの)セッションキー if user && user > "" # ログインフォームからきた場合 pswd = cgi['pswd'] db.execute("CREATE TABLE IF NOT EXISTS " + "user(uid integer primary key autoincrement, " + "uname, pswd, email, name, skey);") r = db.execute("SELECT pswd FROM user WHERE uname = ?", user)[0] if r && pswd.crypt(r[0]) == r[0] # r[0]はdb内のpswd # パスワード照合OKなので新セッションキーを「IPアドレス+ランダム」で生成 skey = skeyprefix+ [*'a'..'z'].sample(14).join db.execute("UPDATE user SET skey = ? WHERE uname=?", skey, user) authok = true # 認証フラグON elsif pswd == wasureta if /(.*)@(.*)/ =~ user system "../useradm.rb #{user} #{$dbfile} | Mail -s newuser #{user}" logininfo = "指定したメイルアドレスに送信しました。" end end elsif skey && skey > "" user = cookie['user'][0] r=db.execute("SELECT skey FROM user WHERE uname = ?", user)[0] skeyinfo = [user, r] # デバッグ用 if r && r[0] && r[0] == skey # cookie skey == saved skey if skey.index(skeyprefix) == 0 # セッションキーが継続して使えるものか検査 authok = true # 認証フラグON else logininfo = "ログイン有効時間終了です。再ログインをお願いします。" end end end if authok # 認証成功ならcookie更新 (HTML本文より先に送出する) # 5時間有効 expires = (Time.now.gmtime+3600*5).strftime("%a, %d-%m-%Y %H:%M:%S GMT") printf("Set-Cookie: skey=%s; expires=%s\n", skey, expires) printf("Set-Cookie: user=%s; expires=%s\n", user, expires) end # HTMLヘッダの出力 (まで) print <<_EOF_ Content-type: text/html; charset=utf-8 YAOYA

yaoya DB

_EOF_ # user未確定ならログイン画面を出して終了 unless authok action = $myname if ARGV[0] then # 今回呼ばれた引数を入れて action="" を作成 action += "?"+ARGV.each{|c| CGI::escape(c)}.join("+") end print <<_EOF_

ログイン

User:
Password:
(wasuretaで初期化)

_EOF_ # ログインタイムアウトしたときに同じ値を再入力させないために # この回に送られた値をすべてhidden変数に書き込んでおく。 cgi.params.keys.each do |k| if cgi[k] && cgi[k] > "" printf("\n", k, cgi[k]) end end puts("

") if logininfo > "" printf("

%s

\n", logininfo) end puts("\n") exit(0) end # フォーム画面をロードした時刻を hidden 変数に埋め込む now = Time.now.to_i.to_s # 1970/1/1からの秒数が入る # 値入力フォームの初期値用の変数 fruit = at = memo = update = "" # ./fruits3.rb -edit 1 のように呼ばれたら入力フォームに初期値を入れる # -edit オプションは一覧表示のところで出している(※1) if ARGV[0] == "-edit" && ARGV[1] != nil id = ARGV[1] # 更新するレコードのid r = db.execute("SELECT * FROM kudamono WHERE id=?", id) fruit, at, memo = r[0][1..-1] # 第2カラム以降すべて memo = CGI::unescapeHTML(memo.gsub("
", "")) #
は取り除く update = "\n" end # (1)入力フォーム出力 if update > "" printf("

id=%dのレコード更新

\n", id) else puts("

新規レコード入力

") end print <<_EOF_
品目:
単価:
メモ:

#{update}
_EOF_ if update > "" # update(更新値入力)ならここで出力終わり puts("\n") exit(0) end # (2)検索フォームの出力 print <<_EOF_

検索

品目検索:
メモ検索:

_EOF_ # フォーム出力終わり # (1)と(2)のどちらの[OK]が押されたかはCGI変数 mode で判断する # 一覧表示の前に,前回のCGI入力があったら先に登録する name = cgi['fruit'] at = cgi['at'].to_i memo = cgi['memo'] mode = cgi['mode'] # この値が newなら登録, searchなら検索 update = cgi['update'] # この値が "" でなければ更新(update) id = cgi['id'] rm = cgi['rm'] # [削除]ボタンが押された場合に代入される # 品目が入力してあったなら実際にINSERTにて登録 または UPDATEで更新 if mode=="new" && name && (name=name.strip) > "" # これが呼ばれた時点で新規書き込みがあればINSERT memo = CGI::escapeHTML(memo.chomp) # 記号をエスケープする memo.gsub!(/\r?\n/, "
\n") # 改行に
を足す if id && id > "" # id指定があるなら既存レコード更新 sql = "UPDATE kudamono SET item=?, at=?, memo=? WHERE id=#{id.to_i}" else # id指定がないなら新規レコード登録 sql = "INSERT INTO kudamono(item, at, memo) VALUES (?, ?, ?)" end db.execute(sql, name.toutf8, at, memo.toutf8) printf("

%s完了通知

\n", id > "" ? "更新" : "登録") printf("

[%s] が正常に登録されました。

\n", name.toutf8) # 新規書き込み登録/update ここまで elsif rm && rm > "" # 削除ボタンを押したとき cgi.keys.each do |k| # keysで変数一覧. rm_#{id} があるはず next unless /rm_(\d+)/ =~ k # CGI変数名が rm_X でなければスキップ db.execute("DELETE FROM kudamono WHERE id=?", $1.to_i) end end def mktable(heading, result) # select の結果を表形式で一覧表示するメソッド h = "ID品目単価メモ" n = -1 # 表示何行目かを数える printf("

%s

\n", heading) puts('
') puts('') result.each do |row| puts(h) if (n+=1)%10 == 0 # 10行に1回見出し行を出す # row[0]とrow[1]の間に削除マークのcheckboxを挿入する id = row[0].to_i rmmk = "" row.insert(1, rmmk) # Arrayのinsertメソッドでずらし挿入できる # ↑で品目が row[2] にずれ込む。 # row[2]を修正リンクに変更する(※1) row[2] = "" + row[2] + "" # これで品目の部分が # りんご # のようになる。これは「./fruits3.rb -edit 1」と起動する。 # CGIプログラムへの引数は ? で区切り, # 引数間の区切りの空白は + に置き換えて表す。 printf("\n", row.join("
%s
")) end puts('
') puts('
') end # 検索キーワード指定時の処理 if mode=="search" kwd1 = cgi['kwd1']; kwd2 = cgi['kwd2'] if kwd1 > "" h = sprintf("

品名検索結果: キーワード:[%s]", kwd1) r = db.execute("SELECT * FROM kudamono WHERE item like ?", "%"+kwd1+"%") mktable(h, r) end if kwd2 > "" h = sprintf("

メモ検索結果: キーワード:[%s]", kwd2) r = db.execute("SELECT * FROM kudamono WHERE memo like ?", "%#{kwd2}%") mktable(h, r) else # 指定語なしの場合は一覧表示 mktable("レコード一覧", db.execute("SELECT * FROM kudamono")) end else # (3)一覧表示(検索語指定がなかった場合) mktable("レコード一覧", db.execute("SELECT * FROM kudamono")) end print <<_EOF_ _EOF_