addChunk('Settings', function() use ($ui)
{
// First some settings...
$dumpvars = array('mbname', 'boardurl', 'boarddir', 'db_server', 'db_name', 'db_prefix');
$settings = array();
$settings[0] = array('Variable','Value');
foreach($dumpvars AS $var)
{
if ($ui->getSettingsFileVal($var) == null)
$value = 'NOT SET';
else
$value = $ui->getSettingsFileVal($var);
$settings[] = array($var, $value);
}
$ui->dumpTable($settings);
// Some settings table stuff...
$settings = array();
$settings[0] = array('Variable','Value');
foreach (array('smfVersion', 'attachmentCheckExtensions', 'attachmentDirSizeLimit', 'attachmentEnable', 'attachmentExtensions', 'attachmentNumPerPostLimit', 'attachmentPostLimit', 'attachmentShowImages', 'automanage_attachments', 'attachmentSizeLimit', 'basedirectory_for_attachments', 'attachment_basedirectories', 'attachmentUploadDir', 'custom_avatar_dir', 'currentAttachmentUploadDir', 'attachments_21_done', 'json_done') AS $var)
{
if ($ui->getSetting($var) == null)
$settings[] = array($var, 'NOT SET');
else
$settings[] = array($var, $ui->getSetting($var));
}
$ui->dumpTable($settings);
});
$ui->addChunk('Attachment Directories - attachmentUploadDir Decoded', function() use ($ui)
{
// Only for 2.1 & 3.0
if (!isset($ui->smfVersion) || !in_array($ui->smfVersion, array('2.1', '3.0')))
{
$ui->addError('This utility only works for SMF 2.1 & 3.0.');
return;
}
// Decode as array
$att_dir_json = $ui->getSetting('attachmentUploadDir');
$att_dirs = json_decode($att_dir_json, true);
if (empty($att_dirs))
$att_dirs = array();
$folders = array();
$folders[-1] = array('Folder ID', 'Folder', 'Valid Folder?');
$folder_err = false;
foreach ($att_dirs as $num => $dir)
{
$valid = file_exists($dir) && is_dir($dir);
$folders[] = array($num, $dir, $valid ? 'True' : 'False');
if (!$valid)
$folder_err = true;
}
if ($folder_err)
$ui->addError('One or more folders in attachmentUploadDir is invalid.');
$ui->dumpTable($folders);
// Lop off the header & Save this off for lookups later...
unset($folders[-1]);
$ui->att_dirs = array();
foreach ($folders as $folder)
$ui->att_dirs[$folder[0]] = strtr($folder[1], '\\', '/');
// Show count of attachments by folder
$counts = array();
$counts[] = array('Folder ID', 'Attachment Subtype', 'Count in DB');
$result = $ui->db->query('
SELECT id_folder,
CASE WHEN id_member = 0 THEN \'Attachment\'
ELSE \'Avatar Attachment\' END AS att_subtype,
count(*) as att_count
FROM ' . $ui->db->db_prefix . 'attachments
WHERE attachment_type != 1
GROUP BY id_folder, att_subtype
ORDER BY id_folder, att_subtype'
);
while ($row = $ui->db->fetch_assoc($result))
{
$row['att_count'] = number_format($row['att_count']);
$counts[] = $row;
}
$ui->db->free($result);
$ui->dumpTable($counts);
echo 'Avatars excluded.
';
});
$ui->addChunk('Attachment Directories - File System', function() use ($ui)
{
// Only for 2.1 & 3.0
if (!isset($ui->smfVersion) || !in_array($ui->smfVersion, array('2.1', '3.0')))
return;
// Recursively return all directories under boarddir that have 'att' somewhere in dir name...
function inspect_dir($dir, &$result)
{
$files = 0;
$bytes = 0;
foreach (glob($dir . '/*') as $entry)
{
if (is_dir($entry))
{
$result[] = inspect_dir($entry, $result);
}
else
{
$filename = basename($entry);
if ($filename == 'index.php')
continue;
if (substr($filename, 0, 1) == '.')
continue;
$files++;
$bytes += filesize($entry);
}
}
$files = number_format($files);
$bytes = number_format($bytes);
return array($dir, $files, $bytes);
}
$folders = array();
$folders[0] = array('Folders Found', 'Files', 'Size');
foreach (glob($ui->getSettingsFileVal('boarddir') . '/att*', GLOB_ONLYDIR) as $dir)
$folders[] = inspect_dir($dir, $folders);
$ui->dumpTable($folders);
echo 'index.php & files starting with a \'.\' are excluded.
';
});
$ui->addChunk('Rerunning 2.1 Upgrader Attachment Process', function() use ($ui)
{
// Only for 2.1 & 3.0; also skip if any errors found above
if (!isset($ui->smfVersion) || !in_array($ui->smfVersion, array('2.1', '3.0')) || !empty($ui->errors))
return;
// Some stats to report out on completion...
$avatars_renamed = 0;
$atts_renamed = 0;
// The below logic is adapted from /other/upgrade_2-1_MySQL.sql
// Converting legacy attachments.
// Need to know a few things first.
$custom_av_dir = !empty($ui->getSetting('custom_avatar_dir')) ? $ui->getSetting('custom_avatar_dir') : $ui->getSettingsFileVal('boarddir') .'/custom_avatar';
// This little fellow has to cooperate...
if (!is_writable($custom_av_dir))
{
// Try 755 and 775 first since 777 doesn't always work and could be a risk...
$chmod_values = array(0755, 0775, 0777);
foreach($chmod_values as $val)
{
// If it's writable, break out of the loop
if (is_writable($custom_av_dir))
break;
else
@chmod($custom_av_dir, $val);
}
}
// If we already are using a custom dir, delete the predefined one.
if (realpath($custom_av_dir) != realpath($ui->getSettingsFileVal('boarddir') . '/custom_avatar'))
{
// Borrow custom_avatars index.php file.
if (!file_exists($custom_av_dir . '/index.php'))
@rename($ui->getSettingsFileVal('boarddir') . '/custom_avatar/index.php', $custom_av_dir .'/index.php');
else
@unlink($ui->getSettingsFileVal('boarddir') . '/custom_avatar/index.php');
// Borrow blank.png as well
if (!file_exists($custom_av_dir . '/blank.png'))
@rename($ui->getSettingsFileVal('boarddir') . '/custom_avatar/blank.png', $custom_av_dir . '/blank.png');
else
@unlink($ui->getSettingsFileVal('boarddir') . '/custom_avatar/blank.png');
// Attempt to delete the directory.
@rmdir($ui->getSettingsFileVal('boarddir') . '/custom_avatar');
}
// We may be using multiple attachment directories.
// It's gotta be json at this point...
$attachmentUploadDir = @json_decode($ui->getSetting('attachmentUploadDir'), true);
$request = $ui->db->query('
SELECT id_attach, id_member, id_folder, filename, file_hash, mime_type
FROM ' . $ui->db->db_prefix . 'attachments
WHERE attachment_type != 1
ORDER BY id_attach');
while ($row = $ui->db->fetch_assoc($request))
{
if (is_array($attachmentUploadDir))
{
if (array_key_exists($row['id_folder'], $attachmentUploadDir) && is_dir($attachmentUploadDir[$row['id_folder']]))
$currentFolder = $attachmentUploadDir[$row['id_folder']];
else
{
$ui->addError('Invalid folder number for attach: ' . $row['id_attach'] . ' folder id: ' . $row['id_folder']);
// Can't do this one...
continue;
}
}
else
{
if (is_string($attachmentUploadDir) && is_dir($attachmentUploadDir))
$currentFolder = $attachmentUploadDir;
else
{
$ui->addError('Invalid attachment folder: ' . $attachmentUploadDir);
// No sense in going any further...
break;
}
}
$fileHash = '';
// Old School?
if (empty($row['file_hash']))
{
// The old logic removed diacritics before naming the file in the attachments folder.
// Single byte Windows-1252 characters were assumed, basically all of xC?, xD?, xE? and
// xF?, plus a few other alphabetic characters with diacritics.
// ***DB should now be utf8, so a slightly different technique must be used.***
// strtr breaks things because it is single-byte, our source is now multibyte.
$from_chars = array('Š','Ž','š','ž','Ÿ',
'À','Á','Â','Ã','Ä','Å','Ç','È','É','Ê','Ë','Ì','Í','Î','Ï',
'Ñ','Ò','Ó','Ô','Õ','Ö','Ø','Ù','Ú','Û','Ü','Ý',
'à','á','â','ã','ä','å','ç','è','é','ê','ë','ì','í','î','ï',
'ñ','ò','ó','ô','õ','ö','ø','ù','ú','û','ü','ý','ÿ',
'Þ', 'þ', 'Ð', 'ð', 'ß', 'Œ', 'œ', 'Æ', 'æ', 'µ');
$to_chars = array('S','Z','s','z','Y',
'A','A','A','A','A','A','C','E','E','E','E','I','I','I','I',
'N','O','O','O','O','O','O','U','U','U','U','Y',
'a','a','a','a','a','a','c','e','e','e','e','i','i','i','i',
'n','o','o','o','o','o','o','u','u','u','u','y','y',
'TH', 'th', 'DH', 'dh', 'ss', 'OE', 'oe', 'AE', 'ae', 'u');
$row['filename'] = str_replace($from_chars, $to_chars, $row['filename']);
// Sorry, no spaces, dots, or anything else but letters allowed.
$row['filename'] = preg_replace(array('/\s/', '/[^\w_\.\-]/'), array('_', ''), $row['filename']);
// Create a nice hash.
$fileHash = sha1(md5($row['filename'] . time()) . mt_rand());
// Iterate through the possible attachment names until we find the one that exists
$oldFile = $currentFolder . '/' . $row['id_attach']. '_' . strtr($row['filename'], '.', '_') . md5($row['filename']);
if (!file_exists($oldFile))
{
$oldFile = $currentFolder . '/' . $row['filename'];
if (!file_exists($oldFile)) $oldFile = false;
}
// Build the new file.
$newFile = $currentFolder . '/' . $row['id_attach'] . '_' . $fileHash .'.dat';
}
// Just rename the file.
else
{
$oldFile = $currentFolder . '/' . $row['id_attach'] . '_' . $row['file_hash'];
$newFile = $currentFolder . '/' . $row['id_attach'] . '_' . $row['file_hash'] .'.dat';
// Make sure it exists...
if (!file_exists($oldFile))
$oldFile = false;
}
if (!$oldFile)
{
// Existing attachment could not be found. Just skip it...
continue;
}
// Check if the av is an attachment
if ($row['id_member'] != 0)
{
if (rename($oldFile, $custom_av_dir . '/' . $row['filename']))
{
$ui->db->query('
UPDATE ' . $ui->db->db_prefix . 'attachments
SET file_hash = \'\', attachment_type = 1
WHERE id_attach = ' . $row['id_attach']);
$avatars_renamed++;
}
}
// Just a regular attachment.
else
{
rename($oldFile, $newFile);
$atts_renamed++;
}
// Only update this if it was successful and the file was using the old system.
if (empty($row['file_hash']) && !empty($fileHash) && file_exists($newFile) && !file_exists($oldFile))
$ui->db->query('
UPDATE ' . $ui->db->db_prefix . 'attachments
SET file_hash = \'' . $fileHash . '\'
WHERE id_attach = ' . $row['id_attach']);
// While we're here, do we need to update the mime_type?
if (empty($row['mime_type']) && file_exists($newFile))
{
$size = @getimagesize($newFile);
if (!empty($size['mime']))
$ui->db->query('
UPDATE ' . $ui->db->db_prefix . 'attachments
SET mime_type = \'' . substr($size['mime'], 0, 20) . '\'
WHERE id_attach = ' . $row['id_attach']);
}
}
$ui->db->free($request);
// Note attachment conversion complete
// Don't have an upsert, so just delete & add...
$ui->db->query('DELETE FROM ' . $ui->db->db_prefix . 'settings WHERE variable = \'attachments_21_done\'');
$ui->db->query('INSERT INTO ' . $ui->db->db_prefix . 'settings (variable, value) VALUES (\'attachments_21_done\', \'1\')');
// Display some basic stats...
$stats = array();
$stats[] = array('Stat', '#');
$stats[] = array('Avatars renamed', $avatars_renamed);
$stats[] = array('Attachments renamed', $atts_renamed);
$ui->dumpTable($stats);
});
$ui->go();
/**
* SimpleSmfUI
*
* A simple basic abstracted UI for utilities.
*
* Copyright 2021-2025 Shawn Bulen
*
* This file is part of the sjrbTools library.
*
* SimpleSmfUI is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SimpleSmfUI is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with SimpleSmfUI. If not, see