bibis/data.php

213 行
5.0 KiB
PHP

<?php
// Data controller
// Post
require_once(__DIR__ . '/data-post.php');
// User
// ユーザー情報は USER_TSV (data/user.tsv) に保存される。
// 内容はタブ区切り・ヘッダー行は無い。
// {ID}<TAB>{名前}<TAB>{パスワードハッシュ値}<TAB>2chトリップ
$users_cache = null;
function load_users() {
global $users_cache;
if (isset($users_cache)) { return $users_cache; }
$users_cache = [];
$prev = file_get_contents(USERS_TSV) ?? '';
$rows = explode(PHP_EOL, $prev);
unset($prev);
foreach ($rows as $row) {
$user = parse_user_row($row);
if ($user['id'] == '') { continue; }
$user['hash_password'] = ''; // HIDE
$users_cache[$user['id']] = $user;
unset($user);
}
unset($rows);
return $users_cache;
}
function update_user($user) {
// 注意:ここを変更したら、必ず、ユーザーが消えない事をテストすること。
if (!$user) { return ['ユーザーが不正。']; }
if (!isset($user['id'])) { return ['ユーザーIDが不正。']; }
$id = $user['id'];
$username = $user['username'];
$password = $user['password'];
$trip = $user['trip'] ?? '';
$fp = fopen(USERS_TSV, 'r+');
if ($fp === false || !flock($fp, LOCK_EX)) { return ['ファイルの取得に失敗。']; }
$prev = fread($fp, filesize(USERS_TSV));
if ($prev === false) { $prev = ''; }
$rows = explode(PHP_EOL, $prev);
$prev = null;
$output = '';
$search_id = $id . "\t";
$changed = false;
foreach ($rows as $row) {
if ($row === '') { continue; }
if (stripos($row, $search_id) !== 0) {
$output .= $row . PHP_EOL;
continue;
}
if ($password != '') {
$hash_password = hash_password($password);
} else {
$array = explode("\t", $row);
$hash_password = $array[2];
unset($array);
}
$output .= "{$id}\t{$username}\t{$hash_password}\t{$trip}" . PHP_EOL;
unset($id);
unset($username);
unset($hash_password);
unset($trip);
$changed = true;
}
unset($rows);
if (
!ftruncate($fp, 0)
|| !rewind($fp)
|| !fwrite($fp, $output)
|| !fflush($fp)
) { return ['ファイルの書き込みに失敗。']; }
flock($fp, LOCK_UN);
fclose($fp);
return [];
}
function add_user($user) {
if (!file_exists(USERS_TSV)) {
die(USERS_TSV.': ファイルを見つけられません。');
}
$id = $user['id'];
$username = $user['username'];
$password = $user['password'];
$match = find_user_row($id);
if (isset($match)) { return ['ID が既存ユーザーと重複。']; }
unset($match);
$hash_password = hash_password($password);
$record = "{$id}\t{$username}\t{$hash_password}\t" . PHP_EOL;
$result = false;
if (is_writable(USERS_TSV)) {
$result = file_put_contents(USERS_TSV, $record, FILE_APPEND | LOCK_EX);
}
if ($result === false) { return ['ファイルの書き込みに失敗。']; }
chmod(USERS_TSV, 644);
return [];
}
// Profile (bio)
// プロフィールは PROFILE_DIR (data/profile) 配下に {ID}.txt のファイル名で保存される。
// 先頭に「-」だけの行があり、それより後の行がプロフィール本文になる。
// 将来的に「-」より前の行にユーザーごとの設定値などを記録する予定。
function load_profile($id) {
if (!file_exists(PROFILE_DIR)) {
if (!mkdir_p(PROFILE_DIR, 755)) die(PROFILE_DIR.'を作成に失敗。');
}
if (!file_exists(PROFILE_DIR.$id.'.txt')) {
touch(PROFILE_DIR.$id.'.txt', time());
}
$profile = '';
if (is_writable(PROFILE_DIR.$id.'.txt')) {
$profile = file_get_contents(PROFILE_DIR . $id . '.txt') ?? '';
}
if ($profile == '') { return null; }
$split = preg_split('/^-$/m', $profile, 2);
$bio = mbtrim($split[1] ?? '');
return ['bio' => $bio];
}
function save_profile($id, $profile) {
$bio = $profile['bio'] ?? '';
if (!mkdir_p(PROFILE_DIR)) { return ['フォルダーの作成に失敗。']; }
$result = file_put_contents(PROFILE_DIR . $id . '.txt', '-' . PHP_EOL . $bio . PHP_EOL);
if ($result === false) { return ['ファイルの書き込みに失敗。']; }
return [];
}
// Auth
function auth_user($id, $password) {
$id = trim($id);
$match = find_user_row($id);
if (!isset($match)) { return false; }
$user = parse_user_row($match);
if (hash_password($password) !== $user['hash_password']) { return false; }
return $user;
}
// Util
function find_user_row($id) {
$users = file_get_contents(USERS_TSV) ?? '';
$rows = explode(PHP_EOL, $users);
$search_id = $id . "\t";
foreach ($rows as $row) {
if (stripos($row, $search_id) === 0) {
return $row;
}
}
return null;
}
function parse_user_row($row) {
$array = explode("\t", $row);
$id = $array[0] ?? '';
$username_raw = $array[1] ?? '';
$hash_password = $array[2] ?? '';
$trip = $array[3] ?? '';
$username = $username_raw . $trip;
$array = [];
return compact('id', 'username_raw', 'hash_password', 'trip', 'username');
}
function hash_password($password) {
$i = 0;
$hash = $password;
do {
$hash = hash('sha512', $hash . PASSWORD_SOLT);
$i++;
} while ($i < PASSWORD_ITER);
return $hash;
}