#!/usr/bin/head -5
# -*- mode: shell-script -*-
# CGI Authentication Library for Shell Script
# Use this by source'ing.
# . ./cgiauthlib-sh
wasuretaword="wasureta"

type query >/dev/null 2>&1 || . ./cgilib2-sh	# query()未定義ならロードする

loginlink() {
  cat<<EOF
<p class="login"><a href="$myname?login">Login</a></p>
EOF
}
loginform() {
  htmlhead "${1:-Login}"
  cat<<EOF
<form action="$myname" method="POST" enctype="multipart/form-data">
 <table>
  <tr><td>ユーザ</td><td><input name="user"></td></tr>
  <tr><td>パスワード</td><td><input name="pswd" type="password"></td></tr>
 </table>
 <input type="submit" value="Login">
 <input type="reset" value="reset">
</form>
EOF
}
s256() {			# SHA256を探すがそれ以外でもよい
  if [ -z "$_sha256" ]; then
    if [ -x /bin/digest ]; then			# Solaris10
      _sha256="/bin/digest -a sha256"
    elif type openssl >/dev/null 2>&1; then	# Maybe Linux
      _sha256() {
	openssl sha256 $* | cut -d' ' -f2
      } ; _sha256=_sha256
    else
      echo "Abort(sha256)."; exit 255		# 見付からなければ中止
    fi
  fi
  $_sha256 "$@"
}
mycrypt() (			# crypt(3)関数的挙動をsha256で行なう
  key=$1 salt=$2		# $s256$ソルト$ハッシュ化文字列
  case $2 in
    '$'*'$'*)   salt=${salt#\$s256\$}
		salt=${salt%\$*} ;;
  esac
  echo -n '$s256$'"$salt"'$'
  echo "$salt$key" | s256
)
randomstr() {			# $1=columns (default: 10, should be <512)
  dd if=/dev/urandom count=1 2>/dev/null | nkf -wMB \
      | tr -d '+\n' | fold -w${1:-10} | sed -n 2p
}
mypwhash() {			# パスワードハッシュは標準入力から
  mycrypt `cat` `randomstr`
}
getcookie() {			# 指定したcookie値を得る
  _tag=cookie/$_tag getpar "$1"
}
postmail() {	# $1=rcpts $2=subject ($3=file)
  rcpts=`echo $1`
  subj=`echo $2|nkf -jM`
  (cat<<EOF; nkf -j $3) | sendmail -t
To: `echo $rcpts|sed 's/ /, /g'`
Subject: $subj
Content-type: text/plain; charset=iso-2022-jp
MIME-Version: 1.0

EOF
}
wasureta() {			# $userの新パスワードを発行
  newpswd=`randomstr`
  encoded=`echo "$newpswd"|mypwhash`
  local=${user%@*}		# ローカルパート
  domain=${user##*@}		# ドメインパート
  if ! host $domain >/dev/null 2>&1; then
    echo "Invalid domain"; exit 1	# DNSの索けないドメインは無効終了
  fi
  dbuser=`query "SELECT email FROM users WHERE email='$user';"`
  if [ -z "$dbuser" ]; then	# 新規登録
    query "INSERT INTO users VALUES('$user', '$encoded', NULL);"
  else
    query "UPDATE users SET pswd='$encoded' WHERE email='$user';"
  fi
  postmail "$user" "New Password for miniblog3" <<EOF
新しいパスワードを設定しました。
ユーザ名:	$user
パスワード:	$newpswd
EOF
  return 0
}
setcookie() (
  for kv; do
    echo "Set-Cookie: $kv; Max-Age=3600"
  done
)
cgiauth() {	# OKならグローバル変数 _session をセットして return 0
  query<<-EOF
	CREATE TABLE IF NOT EXISTS users(
	  email PRIMARY KEY, pswd TEXT NOT NULL, gecos,
 	  CHECK(pswd != '')
	);
	CREATE TABLE IF NOT EXISTS users_m(
	  email, key, val,
	  FOREIGN KEY(email) REFERENCES users(email)
		 ON DELETE CASCADE ON UPDATE CASCADE,
	  UNIQUE(email, key, val)
	);
	CREATE TABLE IF NOT EXISTS sessions(
	  user PRIMARY KEY TEXT, sesskey TEXT,
	  FOREIGN KEY(user) REFERENCES users(email)
		 ON DELETE CASCADE ON UPDATE CASCADE,
	  FOREIGN KEY(sesskey) REFERENCES tags(id)
		 ON DELETE CASCADE ON UPDATE CASCADE
	);
	EOF
  user=`getpar user`			# フォームからのユーザ名を取得
  user=${user:-`getcookie user`}	# フォーム値がなければcookie値取得
  user=`echo "$user" | sed "s/'/''/g"`	# SQLへ渡す変数なので ' をエスケープ
  pswd=`getpar pswd`			# フォームからのpswd
  skey=`getcookie skey`			# セッションキーはcookieから
  expsql="datetime('now', 'localtime', '$_exp')" # 有効期限の生成SQL文
  [ -z "$user" ] && return 1		# user未指定なら終了
  if [ -n "$pswd" ]; then		# pswdを入力した場合
    if [ x"$pswd" = x"$wasuretaword" ]; then	# パスワードリセット
      wasureta $user
      return 99
    fi
    dbpswd=`query "SELECT pswd FROM users WHERE email='$user';"`
    fromweb=`mycrypt "$pswd" "$dbpswd"`	# 送信されたパスワードからのハッシュ
    if [ x"$fromweb" = x"$dbpswd" ]; then # 正しいパスワードなら
      _session=`randomstr 50`		# 新しいセッションキー生成
      query<<-EOF
	REPLACE INTO tags VALUES('$_session', $expsql);
	REPLACE INTO sessions VALUES('$user', '$_session');
	EOF
      setcookie "user=$user" "skey=$_session"
      return 0				# 認証OKならreturn
    fi
  fi
  if [ -n "$skey" ]; then		# ブラウザからのCookie値を調べる
    dbskey=`query "SELECT sesskey FROM sessions WHERE user='$user';"`
    if [ x"$skey" = x"$dbskey" ]; then	# 送って来たセッションキーと同じならOK
      _session=$skey
      query "UPDATE tags SET expire=$expsql WHERE id='$skey';" # 期限更新
      setcookie "user=$user" "skey=$_session"
      return 0				# セッションキー確認OK
    fi
  fi
  return 1		# パスワードもセッションキーも不適合ならエラー戻り
}
_tag=cookie/$_tag storeparam "`echo $HTTP_COOKIE|sed 's/[;, ]/\&/g'`"
