Files
Piwigo_Bratonien_Tools/include/album_shares.inc.php

298 lines
10 KiB
PHP

<?php
if (!defined('PHPWG_ROOT_PATH'))
{
die('Hacking attempt!');
}
function bratonien_tools_shares_table()
{
return $GLOBALS['prefixeTable'].'bratonien_tools_album_shares';
}
function bratonien_tools_create_album_shares_table()
{
$table = bratonien_tools_shares_table();
pwg_query("CREATE TABLE IF NOT EXISTS `$table` (
id int(11) NOT NULL AUTO_INCREMENT,
category_id int(11) NOT NULL,
user_id mediumint(8) unsigned NOT NULL,
token_hash char(64) NOT NULL,
password_hash varchar(255) NOT NULL,
created_by mediumint(8) unsigned NOT NULL,
created_at datetime NOT NULL,
expires_at datetime DEFAULT NULL,
active tinyint(1) NOT NULL DEFAULT 1,
PRIMARY KEY (id),
UNIQUE KEY token_hash (token_hash),
KEY category_id (category_id),
KEY user_id (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
}
function bratonien_tools_drop_album_shares_table()
{
pwg_query('DROP TABLE IF EXISTS `'.bratonien_tools_shares_table().'`');
}
function bratonien_tools_album_shares_init()
{
if (!isset($_GET['brshare']))
{
return;
}
$token = strtolower(trim((string)$_GET['brshare']));
if (!preg_match('/^[a-f0-9]{48}$/', $token))
{
bratonien_tools_share_access_page('Ungültiger Freigabelink.');
}
$share = bratonien_tools_get_share_by_token($token);
if (!$share)
{
bratonien_tools_share_access_page('Diese Freigabe existiert nicht oder wurde widerrufen.');
}
if (!empty($share['expires_at']) && strtotime($share['expires_at']) < time())
{
bratonien_tools_share_access_page('Diese Freigabe ist abgelaufen.');
}
$session_key = 'bratonien_share_'.(int)$share['id'];
$authorized = !empty($_SESSION[$session_key]);
$error = '';
if (!$authorized && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['bratonien_share_password']))
{
if (password_verify((string)$_POST['bratonien_share_password'], $share['password_hash']))
{
$_SESSION[$session_key] = true;
$authorized = true;
}
else
{
$error = 'Das Passwort ist nicht korrekt.';
}
}
if (!$authorized)
{
bratonien_tools_share_access_page($error, $token, true);
}
if (function_exists('is_a_guest') && is_a_guest())
{
log_user((int)$share['user_id'], false);
}
redirect(PHPWG_ROOT_PATH.'index.php?/category/'.(int)$share['category_id']);
}
function bratonien_tools_get_share_by_token($token)
{
$hash = hash('sha256', $token);
$query = 'SELECT s.*, c.name AS category_name FROM '.bratonien_tools_shares_table().' s '
.'JOIN '.CATEGORIES_TABLE.' c ON c.id = s.category_id '
."WHERE s.token_hash = '".pwg_db_real_escape_string($hash)."' AND s.active = 1 LIMIT 1";
$result = pwg_query($query);
return pwg_db_num_rows($result) ? pwg_db_fetch_assoc($result) : null;
}
function bratonien_tools_get_album_shares()
{
$query = 'SELECT s.*, c.name AS category_name, u.username AS created_by_name '
.'FROM '.bratonien_tools_shares_table().' s '
.'LEFT JOIN '.CATEGORIES_TABLE.' c ON c.id = s.category_id '
.'LEFT JOIN '.USERS_TABLE.' u ON u.id = s.created_by '
.'ORDER BY s.created_at DESC';
return query2array($query);
}
function bratonien_tools_get_private_albums()
{
$query = "SELECT id, name, uppercats FROM ".CATEGORIES_TABLE." WHERE status = 'private' ORDER BY global_rank ASC, name ASC";
return query2array($query);
}
function bratonien_tools_create_album_share()
{
global $user;
$category_id = isset($_POST['share_category_id']) ? (int)$_POST['share_category_id'] : 0;
$password = (string)($_POST['share_password'] ?? '');
$expires = trim((string)($_POST['share_expires_at'] ?? ''));
if ($category_id < 1)
{
throw new Exception('Bitte ein privates Album auswählen.');
}
if ($password === '')
{
throw new Exception('Für eine geschützte Freigabe ist ein Passwort erforderlich.');
}
$query = 'SELECT id FROM '.CATEGORIES_TABLE." WHERE id = $category_id AND status = 'private' LIMIT 1";
if (pwg_db_num_rows(pwg_query($query)) === 0)
{
throw new Exception('Das gewählte Album ist nicht privat oder existiert nicht.');
}
$expires_at = null;
if ($expires !== '')
{
$ts = strtotime($expires);
if ($ts === false || $ts <= time())
{
throw new Exception('Das Ablaufdatum muss in der Zukunft liegen.');
}
$expires_at = date('Y-m-d H:i:s', $ts);
}
$username = 'brshare_'.bin2hex(random_bytes(5));
$random_password = bin2hex(random_bytes(16));
$errors = array();
$new_user_id = register_user($username, $random_password, null, 0, $errors, false);
if (!$new_user_id || !empty($errors))
{
throw new Exception('Der Freigabebenutzer konnte nicht erstellt werden.');
}
pwg_query('UPDATE '.USER_INFOS_TABLE." SET status = 'generic' WHERE user_id = ".(int)$new_user_id);
bratonien_tools_grant_album_access((int)$new_user_id, $category_id);
$token = bin2hex(random_bytes(24));
$token_hash = hash('sha256', $token);
$password_hash = password_hash($password, PASSWORD_DEFAULT);
$expires_sql = $expires_at === null ? 'NULL' : "'".pwg_db_real_escape_string($expires_at)."'";
$query = 'INSERT INTO '.bratonien_tools_shares_table()
.' (category_id, user_id, token_hash, password_hash, created_by, created_at, expires_at, active) VALUES ('
.(int)$category_id.', '.(int)$new_user_id.", '".pwg_db_real_escape_string($token_hash)."', '"
.pwg_db_real_escape_string($password_hash)."', ".(int)$user['id'].', NOW(), '.$expires_sql.', 1)';
pwg_query($query);
invalidate_user_cache();
return array(
'message' => 'Geschützte Albumfreigabe erstellt: '.get_absolute_root_url().'?brshare='.$token,
);
}
function bratonien_tools_revoke_album_share()
{
$share_id = isset($_POST['share_id']) ? (int)$_POST['share_id'] : 0;
if ($share_id < 1)
{
throw new Exception('Ungültige Freigabe.');
}
$query = 'SELECT user_id FROM '.bratonien_tools_shares_table().' WHERE id = '.$share_id.' LIMIT 1';
$result = pwg_query($query);
if (!pwg_db_num_rows($result))
{
throw new Exception('Freigabe nicht gefunden.');
}
$row = pwg_db_fetch_assoc($result);
bratonien_tools_delete_share_user((int)$row['user_id']);
pwg_query('DELETE FROM '.bratonien_tools_shares_table().' WHERE id = '.$share_id.' LIMIT 1');
invalidate_user_cache();
return array('message' => 'Albumfreigabe wurde widerrufen.');
}
function bratonien_tools_delete_share_user($user_id)
{
$user_id = (int)$user_id;
if ($user_id < 1)
{
return;
}
pwg_query('DELETE FROM '.USER_ACCESS_TABLE.' WHERE user_id = '.$user_id);
if (defined('USER_GROUP_TABLE'))
{
pwg_query('DELETE FROM '.USER_GROUP_TABLE.' WHERE user_id = '.$user_id);
}
pwg_query('DELETE FROM '.USER_INFOS_TABLE.' WHERE user_id = '.$user_id);
pwg_query('DELETE FROM '.USERS_TABLE.' WHERE id = '.$user_id);
if (function_exists('delete_user_sessions'))
{
delete_user_sessions($user_id);
}
}
function bratonien_tools_grant_album_access($user_id, $category_id)
{
$user_id = (int)$user_id;
$category_id = (int)$category_id;
$query = 'SELECT 1 FROM '.USER_ACCESS_TABLE.' WHERE user_id = '.$user_id.' AND cat_id = '.$category_id.' LIMIT 1';
if (pwg_db_num_rows(pwg_query($query)) === 0)
{
pwg_query('INSERT INTO '.USER_ACCESS_TABLE.' (user_id, cat_id) VALUES ('.$user_id.', '.$category_id.')');
}
}
function bratonien_tools_ensure_private_album_access($category_id, $user_id = null)
{
global $user;
$uid = $user_id === null ? (int)$user['id'] : (int)$user_id;
if ($uid < 1 || (int)$category_id < 1)
{
return;
}
bratonien_tools_grant_album_access($uid, (int)$category_id);
}
function bratonien_tools_preserve_private_album_access()
{
if (!defined('IN_ADMIN') || $_SERVER['REQUEST_METHOD'] !== 'POST')
{
return;
}
if ((string)($_GET['page'] ?? '') !== 'cat_options' || (string)($_GET['section'] ?? '') !== 'status')
{
return;
}
if (!isset($_POST['falsify']) || empty($_POST['cat_true']) || !is_array($_POST['cat_true']))
{
return;
}
check_pwg_token();
foreach ($_POST['cat_true'] as $category_id)
{
if (preg_match('/^\d+$/', (string)$category_id))
{
bratonien_tools_ensure_private_album_access((int)$category_id);
}
}
}
function bratonien_tools_album_shares_on_delete_categories($category_ids)
{
foreach ((array)$category_ids as $category_id)
{
$result = pwg_query('SELECT id, user_id FROM '.bratonien_tools_shares_table().' WHERE category_id = '.(int)$category_id);
while ($row = pwg_db_fetch_assoc($result))
{
bratonien_tools_delete_share_user((int)$row['user_id']);
pwg_query('DELETE FROM '.bratonien_tools_shares_table().' WHERE id = '.(int)$row['id'].' LIMIT 1');
}
}
}
function bratonien_tools_share_access_page($error = '', $token = '', $show_form = false)
{
header('Content-Type: text/html; charset=UTF-8');
$action = htmlspecialchars(get_absolute_root_url().'?brshare='.$token, ENT_QUOTES, 'UTF-8');
$message = $error !== '' ? '<p class="brshare-error">'.htmlspecialchars($error, ENT_QUOTES, 'UTF-8').'</p>' : '';
$form = '';
if ($show_form)
{
$form = '<form method="post" action="'.$action.'"><label for="brshare-password">Passwort</label><input id="brshare-password" type="password" name="bratonien_share_password" required autofocus><button type="submit">Album öffnen</button></form>';
}
echo '<!doctype html><html lang="de"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Geschützte Albumfreigabe</title><style>body{margin:0;min-height:100vh;display:grid;place-items:center;background:#202428;color:#eee;font-family:system-ui,sans-serif}.brshare{width:min(92vw,460px);padding:28px;border:1px solid #555;background:#2b3035;box-shadow:0 12px 30px rgba(0,0,0,.35)}h1{font-size:1.4rem;margin:0 0 14px}p{line-height:1.5}.brshare-error{color:#ffb4b4}label{display:block;margin:16px 0 6px}input{box-sizing:border-box;width:100%;padding:11px;border:1px solid #666;background:#181b1e;color:#fff}button{margin-top:14px;padding:10px 16px;cursor:pointer}</style></head><body><main class="brshare"><h1>Geschütztes Album</h1><p>Für diese Freigabe ist ein Passwort erforderlich.</p>'.$message.$form.'</main></body></html>';
exit;
}