2022-09-13 13:14:49 +00:00
|
|
|
<?php
|
|
|
|
namespace Misuzu;
|
|
|
|
|
|
|
|
use ZipArchive;
|
2023-07-17 19:28:13 +00:00
|
|
|
use Index\XString;
|
2023-08-02 22:12:47 +00:00
|
|
|
use Misuzu\Users\UserInfo;
|
2022-09-13 13:14:49 +00:00
|
|
|
|
2024-12-02 02:28:08 +00:00
|
|
|
if(!isset($msz) || !($msz instanceof \Misuzu\MisuzuContext))
|
|
|
|
die('Script must be called through the Misuzu route dispatcher.');
|
|
|
|
|
2024-11-30 04:09:29 +00:00
|
|
|
if(!$msz->authInfo->isLoggedIn)
|
2023-08-31 15:59:53 +00:00
|
|
|
Template::throwError(401);
|
2022-09-13 13:14:49 +00:00
|
|
|
|
2024-11-30 04:09:29 +00:00
|
|
|
$dbConn = $msz->dbConn;
|
2022-09-13 13:14:49 +00:00
|
|
|
|
2024-12-02 21:33:15 +00:00
|
|
|
/** @param string[] $fieldInfos */
|
|
|
|
function db_to_zip(
|
|
|
|
ZipArchive $archive,
|
|
|
|
UserInfo $userInfo,
|
|
|
|
string $baseName,
|
|
|
|
array $fieldInfos,
|
|
|
|
string $userIdField = 'user_id'
|
|
|
|
): string {
|
2023-07-17 19:28:13 +00:00
|
|
|
global $dbConn;
|
|
|
|
|
2024-11-30 04:20:20 +00:00
|
|
|
$userId = $userInfo->id;
|
2023-07-17 19:28:13 +00:00
|
|
|
$fields = [];
|
|
|
|
|
|
|
|
foreach($fieldInfos as $key => $fieldInfo) {
|
|
|
|
$fieldInfo = explode(':', $fieldInfo);
|
|
|
|
$fieldInfo = [
|
|
|
|
'name' => $fieldInfo[0],
|
|
|
|
'type' => $fieldInfo[1] ?? 'n',
|
|
|
|
'nullable' => ($fieldInfo[2] ?? '') === 'n',
|
|
|
|
];
|
|
|
|
$fieldInfo['is_null'] = $fieldInfo['type'] === 'n';
|
|
|
|
|
|
|
|
if(!$fieldInfo['is_null']) {
|
|
|
|
$selectField = $fieldInfo['name'];
|
|
|
|
|
|
|
|
if($fieldInfo['type'] === 'a')
|
|
|
|
$selectField = sprintf('INET6_NTOA(%s)', $selectField);
|
|
|
|
elseif($fieldInfo['type'] === 't')
|
|
|
|
$selectField = sprintf('UNIX_TIMESTAMP(%s)', $selectField);
|
|
|
|
|
|
|
|
$fields[] = $selectField;
|
|
|
|
}
|
|
|
|
|
|
|
|
$fieldInfos[$key] = $fieldInfo;
|
|
|
|
}
|
|
|
|
|
2023-08-02 22:12:47 +00:00
|
|
|
$tmpName = sys_get_temp_dir() . DIRECTORY_SEPARATOR . sprintf('msz-user-data-%s-%s-%s.tmp', $userId, $baseName, XString::random(8));
|
2024-10-05 02:40:29 +00:00
|
|
|
$tmpHandle = fopen($tmpName, 'wb');
|
2023-07-17 19:28:13 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
$stmt = $dbConn->prepare(sprintf('SELECT %s FROM msz_%s WHERE %s = ?', implode(', ', $fields), $baseName, $userIdField));
|
|
|
|
$stmt->addParameter(1, $userId);
|
|
|
|
$stmt->execute();
|
|
|
|
$result = $stmt->getResult();
|
|
|
|
|
|
|
|
while($result->next()) {
|
|
|
|
$args = -1;
|
|
|
|
$row = [];
|
|
|
|
|
|
|
|
foreach($fieldInfos as $fieldInfo) {
|
|
|
|
$fieldValue = null;
|
|
|
|
|
|
|
|
if(!$fieldInfo['is_null']) {
|
|
|
|
++$args;
|
|
|
|
|
|
|
|
if(!$fieldInfo['nullable'] || !$result->isNull($args)) {
|
|
|
|
if($fieldInfo['type'] === 's' || $fieldInfo['type'] === 'a' || $fieldInfo['type'] === 'j') {
|
|
|
|
$fieldValue = $result->getString($args);
|
|
|
|
|
|
|
|
if($fieldInfo['type'] === 'j')
|
|
|
|
$fieldValue = json_decode($fieldValue);
|
|
|
|
} elseif($fieldInfo['type'] === 't' || $fieldInfo['type'] === 'b' || $fieldInfo['type'] === 'i') {
|
|
|
|
$fieldValue = $result->getInteger($args);
|
|
|
|
|
|
|
|
if($fieldInfo['type'] === 'b')
|
|
|
|
$fieldValue = $fieldValue !== 0;
|
|
|
|
elseif($fieldInfo['type'] === 't') {
|
|
|
|
$fieldValue = date(DATE_ATOM, $fieldValue);
|
|
|
|
if(str_ends_with($fieldValue, '+00:00'))
|
|
|
|
$fieldValue = substr($fieldValue, 0, -6) . 'Z';
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$fieldValue = '?';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$row[$fieldInfo['name']] = $fieldValue;
|
|
|
|
}
|
|
|
|
|
2024-10-05 02:40:29 +00:00
|
|
|
fwrite($tmpHandle, json_encode($row, JSON_INVALID_UTF8_SUBSTITUTE));
|
|
|
|
fwrite($tmpHandle, "\n");
|
2022-09-13 13:14:49 +00:00
|
|
|
}
|
2023-07-17 19:28:13 +00:00
|
|
|
} finally {
|
2024-10-05 02:40:29 +00:00
|
|
|
fflush($tmpHandle);
|
|
|
|
fclose($tmpHandle);
|
2022-09-13 13:14:49 +00:00
|
|
|
}
|
|
|
|
|
2023-07-17 19:28:13 +00:00
|
|
|
$archive->addFile($tmpName, $baseName . '.jsonl');
|
|
|
|
|
|
|
|
return $tmpName;
|
2022-09-13 13:14:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
$errors = [];
|
2024-11-30 04:09:29 +00:00
|
|
|
$userInfo = $msz->authInfo->userInfo;
|
2022-09-13 13:14:49 +00:00
|
|
|
|
|
|
|
if(isset($_POST['action']) && is_string($_POST['action'])) {
|
|
|
|
if(isset($_POST['password']) && is_string($_POST['password'])
|
2023-08-02 22:12:47 +00:00
|
|
|
&& ($userInfo->verifyPassword($_POST['password'] ?? ''))) {
|
2022-09-13 13:14:49 +00:00
|
|
|
switch($_POST['action']) {
|
|
|
|
case 'data':
|
2023-07-17 17:43:17 +00:00
|
|
|
$msz->createAuditLog('PERSONAL_DATA_DOWNLOAD');
|
2022-09-13 13:14:49 +00:00
|
|
|
|
|
|
|
$timeStamp = floor(time() / 3600) * 3600;
|
2024-11-30 04:20:20 +00:00
|
|
|
$fileName = sprintf('msz-user-data-%d-%d.zip', $userInfo->id, $timeStamp);
|
2022-09-13 13:14:49 +00:00
|
|
|
$filePath = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $fileName;
|
|
|
|
$archive = new ZipArchive;
|
|
|
|
|
|
|
|
if(!is_file($filePath)) {
|
|
|
|
if($archive->open($filePath, ZipArchive::CREATE | ZIPARCHIVE::OVERWRITE) === true) {
|
2023-07-17 19:28:13 +00:00
|
|
|
$tmpFiles = [];
|
|
|
|
|
|
|
|
try {
|
2023-08-02 22:12:47 +00:00
|
|
|
$tmpFiles[] = db_to_zip($archive, $userInfo, 'audit_log', ['user_id:s:n', 'log_action:s', 'log_params:j', 'log_created:t', 'log_ip:a:n', 'log_country:s']);
|
|
|
|
$tmpFiles[] = db_to_zip($archive, $userInfo, 'auth_tfa', ['user_id:s', 'tfa_token:n', 'tfa_created:t']);
|
|
|
|
$tmpFiles[] = db_to_zip($archive, $userInfo, 'changelog_changes', ['change_id:s', 'user_id:s:n', 'change_action:s:n', 'change_created:t', 'change_log:s', 'change_text:s:n']);
|
|
|
|
$tmpFiles[] = db_to_zip($archive, $userInfo, 'comments_categories', ['category_id:s', 'category_name:s', 'owner_id:s:n', 'category_created:t', 'category_locked:t:n'], 'owner_id');
|
|
|
|
$tmpFiles[] = db_to_zip($archive, $userInfo, 'comments_posts', ['comment_id:s', 'category_id:s', 'user_id:s:n', 'comment_reply_to:s:n', 'comment_text:s', 'comment_created:t', 'comment_pinned:t:n', 'comment_edited:t:n', 'comment_deleted:t:n']);
|
|
|
|
$tmpFiles[] = db_to_zip($archive, $userInfo, 'comments_votes', ['comment_id:s', 'user_id:s', 'comment_vote:i']);
|
|
|
|
$tmpFiles[] = db_to_zip($archive, $userInfo, 'forum_posts', ['post_id:s', 'topic_id:s', 'forum_id:s', 'user_id:s:n', 'post_ip:a', 'post_text:s', 'post_parse:i', 'post_display_signature:b', 'post_created:t', 'post_edited:t:n', 'post_deleted:t:n']);
|
|
|
|
$tmpFiles[] = db_to_zip($archive, $userInfo, 'forum_topics', ['topic_id:s', 'forum_id:s', 'user_id:s:n', 'topic_type:i', 'topic_title:s', 'topic_count_views:i', 'topic_created:t', 'topic_bumped:t', 'topic_deleted:t:n', 'topic_locked:t:n']);
|
|
|
|
$tmpFiles[] = db_to_zip($archive, $userInfo, 'forum_topics_redirects', ['topic_id:s', 'user_id:s:n', 'topic_redir_url:s', 'topic_redir_created:t']);
|
|
|
|
$tmpFiles[] = db_to_zip($archive, $userInfo, 'forum_topics_track', ['user_id:s', 'topic_id:s', 'forum_id:s', 'track_last_read:t']);
|
|
|
|
$tmpFiles[] = db_to_zip($archive, $userInfo, 'login_attempts', ['user_id:s:n', 'attempt_success:b', 'attempt_ip:a', 'attempt_country:s', 'attempt_created:t', 'attempt_user_agent:s']);
|
2024-02-05 22:56:51 +00:00
|
|
|
$tmpFiles[] = db_to_zip($archive, $userInfo, 'messages', ['msg_id:s', 'msg_owner_id:s', 'msg_author_id:s:n', 'msg_recipient_id:s:n', 'msg_reply_to:s:n', 'msg_title:s', 'msg_body:s', 'msg_parser:i', 'msg_created:t', 'msg_sent:t:n', 'msg_read:t:n', 'msg_deleted:t:n'], 'msg_owner_id');
|
2023-08-02 22:12:47 +00:00
|
|
|
$tmpFiles[] = db_to_zip($archive, $userInfo, 'news_posts', ['post_id:s', 'category_id:s', 'user_id:s:n', 'comment_section_id:s:n', 'post_is_featured:b', 'post_title:s', 'post_text:s', 'post_scheduled:t', 'post_created:t', 'post_updated:t', 'post_deleted:t:n']);
|
2023-08-30 22:37:21 +00:00
|
|
|
$tmpFiles[] = db_to_zip($archive, $userInfo, 'perms', ['user_id:s:n', 'role_id:s:n', 'forum_id:s:n', 'perms_category:s', 'perms_allow:i', 'perms_deny:i']);
|
|
|
|
$tmpFiles[] = db_to_zip($archive, $userInfo, 'perms_calculated', ['user_id:s:n', 'forum_id:s:n', 'perms_category:s', 'perms_calculated:i']);
|
2023-08-02 22:12:47 +00:00
|
|
|
$tmpFiles[] = db_to_zip($archive, $userInfo, 'profile_fields_values', ['field_id:s', 'user_id:s', 'format_id:s', 'field_value:s']);
|
|
|
|
$tmpFiles[] = db_to_zip($archive, $userInfo, 'sessions', ['session_id:s', 'user_id:s', 'session_key:n', 'session_ip:a', 'session_ip_last:a:n', 'session_user_agent:s', 'session_country:s', 'session_expires:t', 'session_expires_bump:b', 'session_created:t', 'session_active:t:n']);
|
|
|
|
$tmpFiles[] = db_to_zip($archive, $userInfo, 'users', ['user_id:s', 'username:s', 'password:n', 'email:s', 'register_ip:a', 'last_ip:a', 'user_super:b', 'user_country:s', 'user_colour:i:n', 'user_created:t', 'user_active:t:n', 'user_deleted:t:n', 'display_role:s:n', 'user_totp_key:n', 'user_about_content:s:n', 'user_about_parser:i', 'user_signature_content:s:n', 'user_signature_parser:i', 'user_birthdate:s:n', 'user_background_settings:i:n', 'user_title:s:n']);
|
|
|
|
$tmpFiles[] = db_to_zip($archive, $userInfo, 'users_bans', ['ban_id:s', 'user_id:s', 'mod_id:n', 'ban_severity:i', 'ban_reason_public:s', 'ban_reason_private:s', 'ban_created:t', 'ban_expires:t:n']);
|
|
|
|
$tmpFiles[] = db_to_zip($archive, $userInfo, 'users_password_resets', ['user_id:s', 'reset_ip:a', 'reset_requested:t', 'verification_code:n']);
|
|
|
|
$tmpFiles[] = db_to_zip($archive, $userInfo, 'users_warnings', ['warn_id:s', 'user_id:s', 'mod_id:n', 'warn_body:s', 'warn_created:t']);
|
|
|
|
$tmpFiles[] = db_to_zip($archive, $userInfo, 'users_roles', ['user_id:s', 'role_id:s']);
|
2023-07-17 19:28:13 +00:00
|
|
|
|
|
|
|
$archive->close();
|
|
|
|
} finally {
|
|
|
|
foreach($tmpFiles as $tmpFile)
|
|
|
|
if(is_file($tmpFile))
|
|
|
|
unlink($tmpFile);
|
|
|
|
}
|
2022-09-13 13:14:49 +00:00
|
|
|
} else {
|
|
|
|
$errors[] = 'Something went wrong while creating your account archive.';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
header('Content-Type: application/zip');
|
|
|
|
header(sprintf('Content-Disposition: inline; filename="%s"', $fileName));
|
|
|
|
echo file_get_contents($filePath);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 'deactivate':
|
|
|
|
// deactivation
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$errors[] = 'Incorrect password.';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Template::render('settings.data', [
|
|
|
|
'errors' => $errors,
|
|
|
|
]);
|