function getAttachmentFilename($filename, $attachment_id, $dir = null, $new = false, $file_hash = '') { global $modSettings, $smcFunc; // Just make up a nice hash... if ($new) { return sha1(md5($filename . time()) . mt_rand()); } // Grab the file hash if it wasn't added. if ($file_hash === '') { $request = $smcFunc['db_query']('', ' SELECT file_hash FROM {db_prefix}attachments WHERE id_attach = {int:id_attach}', array('id_attach' => $attachment_id)); if ($smcFunc['db_num_rows']($request) === 0) { return false; } list($file_hash) = $smcFunc['db_fetch_row']($request); $smcFunc['db_free_result']($request); } // In case of files from the old system, do a legacy call. if (empty($file_hash)) { return getLegacyAttachmentFilename($filename, $attachment_id, $dir, $new); } // Are we using multiple directories? if (!empty($modSettings['currentAttachmentUploadDir'])) { if (!is_array($modSettings['attachmentUploadDir'])) { $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']); } $path = $modSettings['attachmentUploadDir'][$dir]; } else { $path = $modSettings['attachmentUploadDir']; } return $path . '/' . $attachment_id . '_' . $file_hash; }
function RepairAttachments() { global $modSettings, $context, $txt, $smcFunc; checkSession('get'); // If we choose cancel, redirect right back. if (isset($_POST['cancel'])) { redirectexit('action=admin;area=manageattachments;sa=maintenance'); } // Try give us a while to sort this out... @set_time_limit(600); $_GET['step'] = empty($_GET['step']) ? 0 : (int) $_GET['step']; $_GET['substep'] = empty($_GET['substep']) ? 0 : (int) $_GET['substep']; // Don't recall the session just in case. if ($_GET['step'] == 0 && $_GET['substep'] == 0) { unset($_SESSION['attachments_to_fix'], $_SESSION['attachments_to_fix2']); // If we're actually fixing stuff - work out what. if (isset($_GET['fixErrors'])) { // Nothing? if (empty($_POST['to_fix'])) { redirectexit('action=admin;area=manageattachments;sa=maintenance'); } $_SESSION['attachments_to_fix'] = array(); //!!! No need to do this I think. foreach ($_POST['to_fix'] as $key => $value) { $_SESSION['attachments_to_fix'][] = $value; } } } // All the valid problems are here: $context['repair_errors'] = array('missing_thumbnail_parent' => 0, 'parent_missing_thumbnail' => 0, 'file_missing_on_disk' => 0, 'file_wrong_size' => 0, 'file_size_of_zero' => 0, 'attachment_no_msg' => 0, 'avatar_no_member' => 0, 'wrong_folder' => 0); $to_fix = !empty($_SESSION['attachments_to_fix']) ? $_SESSION['attachments_to_fix'] : array(); $context['repair_errors'] = isset($_SESSION['attachments_to_fix2']) ? $_SESSION['attachments_to_fix2'] : $context['repair_errors']; $fix_errors = isset($_GET['fixErrors']) ? true : false; // Get stranded thumbnails. if ($_GET['step'] <= 0) { $result = $smcFunc['db_query']('', ' SELECT MAX(id_attach) FROM {db_prefix}attachments WHERE attachment_type = {int:thumbnail}', array('thumbnail' => 3)); list($thumbnails) = $smcFunc['db_fetch_row']($result); $smcFunc['db_free_result']($result); for (; $_GET['substep'] < $thumbnails; $_GET['substep'] += 500) { $to_remove = array(); $result = $smcFunc['db_query']('', ' SELECT thumb.id_attach, thumb.id_folder, thumb.filename, thumb.file_hash FROM {db_prefix}attachments AS thumb LEFT JOIN {db_prefix}attachments AS tparent ON (tparent.id_thumb = thumb.id_attach) WHERE thumb.id_attach BETWEEN {int:substep} AND {int:substep} + 499 AND thumb.attachment_type = {int:thumbnail} AND tparent.id_attach IS NULL', array('thumbnail' => 3, 'substep' => $_GET['substep'])); while ($row = $smcFunc['db_fetch_assoc']($result)) { // Only do anything once... just in case if (!isset($to_remove[$row['id_attach']])) { $to_remove[$row['id_attach']] = $row['id_attach']; $context['repair_errors']['missing_thumbnail_parent']++; // If we are repairing remove the file from disk now. if ($fix_errors && in_array('missing_thumbnail_parent', $to_fix)) { $filename = getAttachmentFilename($row['filename'], $row['id_attach'], $row['id_folder'], false, $row['file_hash']); @unlink($filename); } } } if ($smcFunc['db_num_rows']($result) != 0) { $to_fix[] = 'missing_thumbnail_parent'; } $smcFunc['db_free_result']($result); // Do we need to delete what we have? if ($fix_errors && !empty($to_remove) && in_array('missing_thumbnail_parent', $to_fix)) { $smcFunc['db_query']('', ' DELETE FROM {db_prefix}attachments WHERE id_attach IN ({array_int:to_remove}) AND attachment_type = {int:attachment_type}', array('to_remove' => $to_remove, 'attachment_type' => 3)); } pauseAttachmentMaintenance($to_fix, $thumbnails); } $_GET['step'] = 1; $_GET['substep'] = 0; pauseAttachmentMaintenance($to_fix); } // Find parents which think they have thumbnails, but actually, don't. if ($_GET['step'] <= 1) { $result = $smcFunc['db_query']('', ' SELECT MAX(id_attach) FROM {db_prefix}attachments WHERE id_thumb != {int:no_thumb}', array('no_thumb' => 0)); list($thumbnails) = $smcFunc['db_fetch_row']($result); $smcFunc['db_free_result']($result); for (; $_GET['substep'] < $thumbnails; $_GET['substep'] += 500) { $to_update = array(); $result = $smcFunc['db_query']('', ' SELECT a.id_attach FROM {db_prefix}attachments AS a LEFT JOIN {db_prefix}attachments AS thumb ON (thumb.id_attach = a.id_thumb) WHERE a.id_attach BETWEEN {int:substep} AND {int:substep} + 499 AND a.id_thumb != {int:no_thumb} AND thumb.id_attach IS NULL', array('no_thumb' => 0, 'substep' => $_GET['substep'])); while ($row = $smcFunc['db_fetch_assoc']($result)) { $to_update[] = $row['id_attach']; $context['repair_errors']['parent_missing_thumbnail']++; } if ($smcFunc['db_num_rows']($result) != 0) { $to_fix[] = 'parent_missing_thumbnail'; } $smcFunc['db_free_result']($result); // Do we need to delete what we have? if ($fix_errors && !empty($to_update) && in_array('parent_missing_thumbnail', $to_fix)) { $smcFunc['db_query']('', ' UPDATE {db_prefix}attachments SET id_thumb = {int:no_thumb} WHERE id_attach IN ({array_int:to_update})', array('to_update' => $to_update, 'no_thumb' => 0)); } pauseAttachmentMaintenance($to_fix, $thumbnails); } $_GET['step'] = 2; $_GET['substep'] = 0; pauseAttachmentMaintenance($to_fix); } // This may take forever I'm afraid, but life sucks... recount EVERY attachments! if ($_GET['step'] <= 2) { $result = $smcFunc['db_query']('', ' SELECT MAX(id_attach) FROM {db_prefix}attachments', array()); list($thumbnails) = $smcFunc['db_fetch_row']($result); $smcFunc['db_free_result']($result); for (; $_GET['substep'] < $thumbnails; $_GET['substep'] += 250) { $to_remove = array(); $errors_found = array(); $result = $smcFunc['db_query']('', ' SELECT id_attach, id_folder, filename, file_hash, size, attachment_type FROM {db_prefix}attachments WHERE id_attach BETWEEN {int:substep} AND {int:substep} + 249', array('substep' => $_GET['substep'])); while ($row = $smcFunc['db_fetch_assoc']($result)) { // Get the filename. if ($row['attachment_type'] == 1) { $filename = $modSettings['custom_avatar_dir'] . '/' . $row['filename']; } else { $filename = getAttachmentFilename($row['filename'], $row['id_attach'], $row['id_folder'], false, $row['file_hash']); } // File doesn't exist? if (!file_exists($filename)) { // If we're lucky it might just be in a different folder. if (!empty($modSettings['currentAttachmentUploadDir'])) { // Get the attachment name with out the folder. $attachment_name = !empty($row['file_hash']) ? $row['id_attach'] . '_' . $row['file_hash'] : getLegacyAttachmentFilename($row['filename'], $row['id_attach'], null, true); if (!is_array($modSettings['attachmentUploadDir'])) { $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']); } // Loop through the other folders. foreach ($modSettings['attachmentUploadDir'] as $id => $dir) { if (file_exists($dir . '/' . $attachment_name)) { $context['repair_errors']['wrong_folder']++; $errors_found[] = 'wrong_folder'; // Are we going to fix this now? if ($fix_errors && in_array('wrong_folder', $to_fix)) { $smcFunc['db_query']('', ' UPDATE {db_prefix}attachments SET id_folder = {int:new_folder} WHERE id_attach = {int:id_attach}', array('new_folder' => $id, 'id_attach' => $row['id_attach'])); } continue 2; } } } $to_remove[] = $row['id_attach']; $context['repair_errors']['file_missing_on_disk']++; $errors_found[] = 'file_missing_on_disk'; } elseif (filesize($filename) == 0) { $context['repair_errors']['file_size_of_zero']++; $errors_found[] = 'file_size_of_zero'; // Fixing? if ($fix_errors && in_array('file_size_of_zero', $to_fix)) { $to_remove[] = $row['id_attach']; @unlink($filename); } } elseif (filesize($filename) != $row['size']) { $context['repair_errors']['file_wrong_size']++; $errors_found[] = 'file_wrong_size'; // Fix it here? if ($fix_errors && in_array('file_wrong_size', $to_fix)) { $smcFunc['db_query']('', ' UPDATE {db_prefix}attachments SET size = {int:filesize} WHERE id_attach = {int:id_attach}', array('filesize' => filesize($filename), 'id_attach' => $row['id_attach'])); } } } if (in_array('file_missing_on_disk', $errors_found)) { $to_fix[] = 'file_missing_on_disk'; } if (in_array('file_size_of_zero', $errors_found)) { $to_fix[] = 'file_size_of_zero'; } if (in_array('file_wrong_size', $errors_found)) { $to_fix[] = 'file_wrong_size'; } if (in_array('wrong_folder', $errors_found)) { $to_fix[] = 'wrong_folder'; } $smcFunc['db_free_result']($result); // Do we need to delete what we have? if ($fix_errors && !empty($to_remove)) { $smcFunc['db_query']('', ' DELETE FROM {db_prefix}attachments WHERE id_attach IN ({array_int:to_remove})', array('to_remove' => $to_remove)); $smcFunc['db_query']('', ' UPDATE {db_prefix}attachments SET id_thumb = {int:no_thumb} WHERE id_thumb IN ({array_int:to_remove})', array('to_remove' => $to_remove, 'no_thumb' => 0)); } pauseAttachmentMaintenance($to_fix, $thumbnails); } $_GET['step'] = 3; $_GET['substep'] = 0; pauseAttachmentMaintenance($to_fix); } // Get avatars with no members associated with them. if ($_GET['step'] <= 3) { $result = $smcFunc['db_query']('', ' SELECT MAX(id_attach) FROM {db_prefix}attachments', array()); list($thumbnails) = $smcFunc['db_fetch_row']($result); $smcFunc['db_free_result']($result); for (; $_GET['substep'] < $thumbnails; $_GET['substep'] += 500) { $to_remove = array(); $result = $smcFunc['db_query']('', ' SELECT a.id_attach, a.id_folder, a.filename, a.file_hash, a.attachment_type FROM {db_prefix}attachments AS a LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = a.id_member) WHERE a.id_attach BETWEEN {int:substep} AND {int:substep} + 499 AND a.id_member != {int:no_member} AND a.id_msg = {int:no_msg} AND mem.id_member IS NULL', array('no_member' => 0, 'no_msg' => 0, 'substep' => $_GET['substep'])); while ($row = $smcFunc['db_fetch_assoc']($result)) { $to_remove[] = $row['id_attach']; $context['repair_errors']['avatar_no_member']++; // If we are repairing remove the file from disk now. if ($fix_errors && in_array('avatar_no_member', $to_fix)) { if ($row['attachment_type'] == 1) { $filename = $modSettings['custom_avatar_dir'] . '/' . $row['filename']; } else { $filename = getAttachmentFilename($row['filename'], $row['id_attach'], $row['id_folder'], false, $row['file_hash']); } @unlink($filename); } } if ($smcFunc['db_num_rows']($result) != 0) { $to_fix[] = 'avatar_no_member'; } $smcFunc['db_free_result']($result); // Do we need to delete what we have? if ($fix_errors && !empty($to_remove) && in_array('avatar_no_member', $to_fix)) { $smcFunc['db_query']('', ' DELETE FROM {db_prefix}attachments WHERE id_attach IN ({array_int:to_remove}) AND id_member != {int:no_member} AND id_msg = {int:no_msg}', array('to_remove' => $to_remove, 'no_member' => 0, 'no_msg' => 0)); } pauseAttachmentMaintenance($to_fix, $thumbnails); } $_GET['step'] = 4; $_GET['substep'] = 0; pauseAttachmentMaintenance($to_fix); } // What about attachments, who are missing a message :'( if ($_GET['step'] <= 4) { $result = $smcFunc['db_query']('', ' SELECT MAX(id_attach) FROM {db_prefix}attachments', array()); list($thumbnails) = $smcFunc['db_fetch_row']($result); $smcFunc['db_free_result']($result); for (; $_GET['substep'] < $thumbnails; $_GET['substep'] += 500) { $to_remove = array(); $result = $smcFunc['db_query']('', ' SELECT a.id_attach, a.id_folder, a.filename, a.file_hash FROM {db_prefix}attachments AS a LEFT JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg) WHERE a.id_attach BETWEEN {int:substep} AND {int:substep} + 499 AND a.id_member = {int:no_member} AND a.id_msg != {int:no_msg} AND m.id_msg IS NULL', array('no_member' => 0, 'no_msg' => 0, 'substep' => $_GET['substep'])); while ($row = $smcFunc['db_fetch_assoc']($result)) { $to_remove[] = $row['id_attach']; $context['repair_errors']['attachment_no_msg']++; // If we are repairing remove the file from disk now. if ($fix_errors && in_array('attachment_no_msg', $to_fix)) { $filename = getAttachmentFilename($row['filename'], $row['id_attach'], $row['id_folder'], false, $row['file_hash']); @unlink($filename); } } if ($smcFunc['db_num_rows']($result) != 0) { $to_fix[] = 'attachment_no_msg'; } $smcFunc['db_free_result']($result); // Do we need to delete what we have? if ($fix_errors && !empty($to_remove) && in_array('attachment_no_msg', $to_fix)) { $smcFunc['db_query']('', ' DELETE FROM {db_prefix}attachments WHERE id_attach IN ({array_int:to_remove}) AND id_member = {int:no_member} AND id_msg != {int:no_msg}', array('to_remove' => $to_remove, 'no_member' => 0, 'no_msg' => 0)); } pauseAttachmentMaintenance($to_fix, $thumbnails); } $_GET['step'] = 5; $_GET['substep'] = 0; pauseAttachmentMaintenance($to_fix); } // Got here we must be doing well - just the template! :D $context['page_title'] = $txt['repair_attachments']; $context[$context['admin_menu_name']]['current_subsection'] = 'maintenance'; $context['sub_template'] = 'attachment_repair'; // What stage are we at? $context['completed'] = $fix_errors ? true : false; $context['errors_found'] = !empty($to_fix) ? true : false; }
/** * Goes thought all the attachments and checks that they exist * * - Goes in increments of 250 * - if $fix_errors is true will remove empty files, update wrong filesizes in the DB and * - remove DB entries if the file can not be found. * * @package Attachments * @param int $start * @param boolean $fix_errors * @param string[] $to_fix */ function repairAttachmentData($start, $fix_errors, $to_fix) { global $modSettings; $db = database(); $repair_errors = array('wrong_folder' => 0, 'missing_extension' => 0, 'file_missing_on_disk' => 0, 'file_size_of_zero' => 0, 'file_wrong_size' => 0); $result = $db->query('', ' SELECT id_attach, id_folder, filename, file_hash, size, attachment_type FROM {db_prefix}attachments WHERE id_attach BETWEEN {int:substep} AND {int:substep} + 249', array('substep' => $start)); $to_remove = array(); while ($row = $db->fetch_assoc($result)) { // Get the filename. if ($row['attachment_type'] == 1) { $filename = $modSettings['custom_avatar_dir'] . '/' . $row['filename']; } else { $filename = getAttachmentFilename($row['filename'], $row['id_attach'], $row['id_folder'], false, $row['file_hash']); } // File doesn't exist? if (!file_exists($filename)) { // If we're lucky it might just be in a different folder. if (!empty($modSettings['currentAttachmentUploadDir'])) { // Get the attachment name without the folder. $attachment_name = !empty($row['file_hash']) ? $row['id_attach'] . '_' . $row['file_hash'] . '.elk' : getLegacyAttachmentFilename($row['filename'], $row['id_attach'], null, true); if (!is_array($modSettings['attachmentUploadDir'])) { $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']); } // Loop through the other folders looking for this file foreach ($modSettings['attachmentUploadDir'] as $id => $dir) { if (file_exists($dir . '/' . $attachment_name)) { $repair_errors['wrong_folder']++; // Are we going to fix this now? if ($fix_errors && in_array('wrong_folder', $to_fix)) { attachment_folder($row['id_attach'], $id); } // Found it, on to the next attachment continue 2; } } if (!empty($row['file_hash'])) { // It may be without the elk extension (something wrong during upgrade/conversion) $attachment_name = $row['id_attach'] . '_' . $row['file_hash']; if (!is_array($modSettings['attachmentUploadDir'])) { $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']); } // Loop through the other folders looking for this file foreach ($modSettings['attachmentUploadDir'] as $id => $dir) { if (file_exists($dir . '/' . $attachment_name)) { $repair_errors['missing_extension']++; // Are we going to fix this now? if ($fix_errors && in_array('missing_extension', $to_fix)) { rename($dir . '/' . $attachment_name, $dir . '/' . $attachment_name . '.elk'); attachment_folder($row['id_attach'], $id); } // Found it, on to the next attachment continue 2; } } } } // Could not find it anywhere $to_remove[] = $row['id_attach']; $repair_errors['file_missing_on_disk']++; } elseif (filesize($filename) == 0) { $repair_errors['file_size_of_zero']++; // Fixing? if ($fix_errors && in_array('file_size_of_zero', $to_fix)) { $to_remove[] = $row['id_attach']; @unlink($filename); } } elseif (filesize($filename) != $row['size']) { $repair_errors['file_wrong_size']++; // Fix it here? if ($fix_errors && in_array('file_wrong_size', $to_fix)) { attachment_filesize($row['id_attach'], filesize($filename)); } } } $db->free_result($result); // Do we need to delete what we have? if ($fix_errors && !empty($to_remove) && in_array('file_missing_on_disk', $to_fix)) { removeOrphanAttachments($to_remove); } return $repair_errors; }
function getAttachmentFilename($filename, $attachment_id, $new = false, $file_hash = '') { global $modSettings, $db_prefix; // Just make up a nice hash... if ($new) { return sha1(md5($filename . time()) . mt_rand()); } // Grab the file hash if it wasn't added. if ($file_hash === '') { $request = db_query("\n\t\t\tSELECT file_hash\n\t\t\tFROM {$db_prefix}attachments\n\t\t\tWHERE ID_ATTACH = " . (int) $attachment_id, __FILE__, __LINE__); if (mysql_num_rows($request) === 0) { return false; } list($file_hash) = mysql_fetch_row($request); mysql_free_result($request); } // In case of files from the old system, do a legacy call. if (empty($file_hash)) { return getLegacyAttachmentFilename($filename, $attachment_id, $new); } return $modSettings['attachmentUploadDir'] . '/' . $attachment_id . '_' . $file_hash; }
/** * Get an attachment's encrypted filename. If $new is true, won't check for file existence. * @todo this currently returns the hash if new, and the full filename otherwise. * Something messy like that. * @todo and of course everything relies on this behavior and work around it. :P. * Converters included. * * @param string $filename * @param int $attachment_id * @param string|null $dir * @param bool $new * @param string $file_hash */ function getAttachmentFilename($filename, $attachment_id, $dir = null, $new = false, $file_hash = '') { global $modSettings; // Just make up a nice hash... if ($new) { return sha1(md5($filename . time()) . mt_rand()); } // In case of files from the old system, do a legacy call. if (empty($file_hash)) { require_once SUBSDIR . '/Attachments.subs.php'; return getLegacyAttachmentFilename($filename, $attachment_id, $dir, $new); } // Are we using multiple directories? if (!empty($modSettings['currentAttachmentUploadDir'])) { if (!is_array($modSettings['attachmentUploadDir'])) { $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']); } $path = isset($modSettings['attachmentUploadDir'][$dir]) ? $modSettings['attachmentUploadDir'][$dir] : $modSettings['basedirectory_for_attachments']; } else { $path = $modSettings['attachmentUploadDir']; } return $path . '/' . $attachment_id . '_' . $file_hash . '.elk'; }