Проблемы с русскими именами файлов и участников

Автор erm, 18 февраля 2006, 14:27:40

« назад - далее »

0 Пользователи и 2 гостей просматривают эту тему.

erm

Тема отчасти отностится к проблемам русификации, а отчасти к проблемам движка.

Не работает отправка Личных Сообщений участникам с русским именем регистрации. При попытке отправки имя участника преобразуется к верхнему регистру и выдаётся сообщение "Не могу найти пользователя 'АЛЕКСАНДР ЮРЬЕВИЧ'." Хотя при нажатии ссылки для отправки сообщения в профиле пользователя изначально имя верное "Александр Юрьевич". Участникам с английскими именами сообщения отправляются нормально.

Вложения с русскими именами файлов присутствуют в сообщении к которому прикреплены и видны в списке файлов в разделе администрирования, но при нажатии ссылки на такое вложение получаем "404 - Вложение не найдено", также, если это изображение, то его не видно в сообщении, к которому оно прикреплено, видно только ссылку на файл.

ОС FreeBSD 5.4 и локаль ru_RU.KOI8-R. Но форум в cp1251. Общая причина появления пользователей с русскими именами и русских названий файлов - перенос содержимого формума на движке Invision Board 1.3.1.
Пользователей и файлов много, поэтому переименовать сложно, но даже если и возможно, то основная сложность в том, чтобы не нарушить связность между файлами на диске и их внутренними цифровыми обозначениями. Поэтому предпочтительно научить движок форума корректно работать с русскими символами.

erm

#1
С файлами происходит следующее:
Форум был перенесён на версию 1.0.6, т.к. конвертер не работает с версией 1.1
Все имена файлов приобрели вид ID_имя_расширениеMD5 в том числе и с русскими именами, например, 1_ПЕЙНПД_1001_jpg19fbf15ba6e00c9e67e40ce27236928b
Но в версии 1.1 RC2 в функции getAttachmentFilename в файле Subs.php для русских имён файлов предполагается другое кодирование, а именно dhaeidha_1001.jpg для "чистых" имён и 1_dhaeidha_1001_jpg19fbf15ba6e00c9e67e40ce27236928b для кодированных.

Исправить можно пропустив имена файлов через "фильтр" взятый из вышеуказанной функции.

Скрипт запускать через web интерфейс из корневого каталога форума, один раз.

<?php
// Get the bad filename in the old format
function filter1($filename, $attachment_id)
{
       
// Sorry, no spaces, dots, or anything else but letters allowed.
       
$clean_name = preg_replace(array('/\s/', '/[^\w_\.\-]/'), array('_', ''), $filename);

       
$enc_name = $attachment_id . '_' . strtr($clean_name, '.', '_') . md5($clean_name);
       
$filename = 'attachments/' . $enc_name;

       return
$filename;
}

// Get the right filename in the new format
function filter2($filename, $attachment_id)
{
       
// Remove special accented characters - ie. sМ.
       
$clean_name = strtr($filename, '~J~N~Z~^~_юабцдегхийклмноярстужьызшэщЮАБЦДЕГХИЙКЛМНОЯРСТУЖЬЫЗШЭЩЪ', 'SZszYAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiii
noooooouuuuyy'
);
       
$clean_name = strtr($clean_name, array('ч' => 'TH', 'Ч' => 'th', 'п' => 'DH', 'П' => 'dh', 'ъ' => 'ss', '~L' => 'OE', '~\' => 'oe', 'ф' => 'AE', 'Ф'
=> '
ae', '' => 'u'));

       // Sorry, no spaces, dots, or anything else but letters allowed.
       $clean_name = preg_replace(array('
/\s/', '/[^\w_\.\-]/'), array('_', ''), $clean_name);

       $enc_name = $attachment_id . '
_' . strtr($clean_name, '.', '_') . md5($clean_name);

       $filename = '
attachments/' . $enc_name;

       return $filename;
}

define('
SMF', 1);
// Load the settings...
require_once(dirname(__FILE__) . '
/Settings.php');
require_once($sourcedir . '
/Subs.php');

// Connect to the MySQL database.
if (empty($db_persist))
       $db_connection = @mysql_connect($db_server, $db_user, $db_passwd);
else
       $db_connection = @mysql_pconnect($db_server, $db_user, $db_passwd);

// Show an error if the connection couldn'
t be made.
if (!
$db_connection || !@mysql_select_db($db_name, $db_connection))
       
db_fatal_error();

// Get the all attachments from the MySQL database
$request = db_query("SELECT a.filename, a.ID_ATTACH, a.attachmentType, a.size FROM {$db_prefix}attachments AS a", __FILE__, __LINE__);
if (
mysql_num_rows($request) == 0) { echo 'error1'; exit(1);}

print(
'<html><head><title>attachments</title></head><body><table>');

while(
$row=mysql_fetch_row($request)) {
       
$filename1 = filter1($row[0],$row[1]);
       
$filename2 = filter2($row[0],$row[1]);
       if(
file_exists($filename1)) rename($filename1,$filename2);
       print(
"<tr><td>$row[0]</td><td>$filename1</td><td>$filename2</td></tr>");
}
print(
'</table></body></html>');
?>

erm

#2
Проблема русских Личных Сообщений гораздо глубже и следует из активного применения функции strtolower. А странное поведение достигается работой функции IN в SELECT запросе MySQL, она находит запись в одном случае и не находит в другом, казалось бы похожем случае. На что надеялись разработчики данного php кода не понятно и по-хорошему он требует глобального переписывания.

В общем, исправил для себя, буду смотреть на счёт глюков.
Основной смысл исправления - убрать strtolower
Для этого в файле Sources/Subs-Auth.php находим строку в функции findMembers()
$names[$i] = addslashes(trim(strtolower($name)));
и меняем на
$names[$i] = addslashes(trim($name));
В файле Sources/PersonalMessage.php находим и меняем строки в функции MessagePost2()
$input[$rec_type][$index] = preg_replace('~&amp;#(\d{4,5}|[2-9]\d{2,4}|1[2-9]\d);~', '&#$1;', htmlspecialchars(strtolower(stripslashes(trim($member)))));
меняем на
$input[$rec_type][$index] = preg_replace('~&amp;#(\d{4,5}|[2-9]\d{2,4}|1[2-9]\d);~', '&#$1;', htmlspecialchars(stripslashes(trim($member))));
Строку
if (array_intersect(array(strtolower($member['username']), strtolower($member['name']), strtolower($member['email'])), $to_members))
меняем на
if (array_intersect(array($member['username'], $member['name'], strtolower($member['email'])), $to_members))
И наконец строку
$input[$rec_type] = array_diff($input[$rec_type], array(strtolower($member['username']), strtolower($member['name'])
, strtolower($member['email'])));

меняем на
$input[$rec_type] = array_diff($input[$rec_type], array($member['username'], $member['name'], strtolower($member['em
ail'])));

Вызов функции findMembers() имеется также в функции JSMembers() и используется для поиска пользователей, но работа этой функции на первый взгляд корректна и поэтому я там ничего не менял.

Mavn

SimpleMachines Russian Community Team
п.1 Пройду курсы гадалок для определения исходного кода по скриншоту.

п.2 У вас нет желания читать правила раздела, у меня нет желания одобрять темы, которые не соответствуют этим правилам.

эНДжей

Цитата: erm от 19 февраля 2006, 00:10:27
В общем, исправил для себя, буду смотреть на счёт глюков.
Огромное спасибо!!! Помогло.