Wikiのページが時々,表示されない(別の原因)

Wikiのページが時々,表示されない(別の原因)

- Tatsuya Shirai の投稿
返信数: 8
同じく,表示がキャッシュヒット時にWiki本文が表示されない場合があることを発見しました.ただし,キャッシュ格納時に文字化けした文字列をMySQLに登録してしまうのが原因で,読み出しが正常に行なえない,というメカニズムは同じです.

今度の現象は,<a href="ほにゃらら">のURL欄に日本語(マルチバイト)を含む場合に発生します.前回の問題は単にキャッシュヒット時に表示できないだけでしたが,今回の問題はキャッシュヒット時だけでなく,そもそものデータがおかしな(=化けた)文字列に一部だけ書き換えられてしまうため,正常なハイパーリンクが実施できません.

いま確認しているのは,URLに”業”の字を含む場合です.UTF-8ではE6 A6 ADのようですので,単純なstr_replace()等による置換ではなさそうです.

具体的には,URL欄に,
http://www.suzuka-ct.ac.jp/mech/moodle/file.php/13/ECS/ECS_Wiki用データフォルダ/山口卒論/卒業論文 山口0227y.pdf
というアドレスを「閲覧」で選択してOKします.そしてWiki編集画面で”プレビュー”をクリックして表示された画面では,このクリッカブル文字列のアドレスの「卒業論文」の「業」の字が化けます.

同じようにHTMLエディタでURLをハイパーリンクさせても,”Webページの追加”あたりではこの現象は発生しません.何故かは分かりません.

いまのところ,
lib/weblib.php 中の function clean_text()の中から呼んでいる,

/// Clean up embedded scripts and , using kses
$text = cleanAttributes($text);
をコメントアウトすれば,”業”が文字化けしないことは確認しています.

cleanAttributes()は,
function cleanAttributes($str){
$result = preg_replace_callback(
'%(<[^>]*(>|$)|>)%m', #search for html tags
"cleanAttributes2",
$str
);
return $result;
}

コールバック関数cleanAttribute2()が正体です.
ちょっと複雑そうなので解析にもう少し時間が掛かります.

Tatsuya Shirai への返信

Re: Wikiのページが時々,表示されない(別の原因)

- Tatsuya Shirai の投稿
cleanAttribute2()関数内で使用されている,
$attrlist = $matches[3]; // the list of attributes as a string
$attrArray = kses_hair($attrlist, $ALLOWED_PROTOCOLS);
このkses_hair()関数を通すと,$attrlistでは化けていないマルチバイトを含むURLの一部が化けています.原因はkses_hair()関数内にありそうです.

kses_hair()関数はlib/kses.php内にあります.

kses_hair()関数から,
case 2: # attribute value, a URL after href= for instance

if (preg_match('/^"([^"]*)"(\s+|$)/', $attr, $match))
# "value"
{
$thisval = kses_bad_protocol($match[1], $allowed_protocols);

以上の箇所で呼ばれている,kses_bad_protocol()の戻り値,$thisvalで化けています.

確定しました.
function kses_bad_protocol($string, $allowed_protocols)
{
$string = kses_no_null($string);
$string = preg_replace('/\xad+/', '', $string); # deals with Opera "feature"
$string2 = $string.'a';
lib/kses.php中のkses_bad_protcol()の冒頭にある,2行目の"Opera"対応と思われる置換命令が,思いっきり,”業”のUTF-8コード E6 A5 ADの最後のADを破壊しています.

これは一体,Operaのどのような機能に関係しているのでしょうか...
手元のPHPの参考書では,preg_replace()はUTF-8に対応していると書いてあります.とはいえ,ASCII文字コードではない0x0ADを置換しろ,という命令に対しては文字区切りを考慮しないでバイト単位で置き換えを実行してしまうのでしょうか?

どなたか,このpreg_replace()の意味合い(Operaにどう関係するのか,UTF-8コードにどう関係するのか)と,安全性の高い対策方法を考えていただけないでしょうか.
私としては仕事に支障が出る本問題の簡単な対策(コメントアウトしてしまう)を施して本件終了,と言いたいところです...

Tatsuya Shirai への返信

Re: Wikiのページが時々,表示されない(別の原因)

- Haruhiko Okumura の投稿
http://sourceforge.net/project/shownotes.php?release_id=187629&group_id=81853

- For some reason, the Opera developers decided to make chr(173) a whitespace
  character in URL protocols, both when it occurs raw and in an entity. kses
  now handles this.

だそうです。それで ¥xad を消そうとしているようですが,これはUTF-8にとってはまずいので,ここはばっさり消すのが正しそうです。
Haruhiko Okumura への返信

Re: Wikiのページが時々,表示されない(別の原因)

- Tatsuya Shirai の投稿
なるほど.Opera独自の拡張なのですね.
mbstring系の関数で文字列を頭から順に一文字一文字チェックして行って,単独のADを発見したらそれだけを削除する,という関数を作れなくもないと思いますが,(Operaユーザーには申し訳ありませんが)他のブラウザーのユーザに不具合が発生すると怖いので,コメントアウトすることとしましょう.

#preg系の関数はUTF-8に対応している,と本には書いてあっても,実際には検索パターンの種類によってはマルチバイトを平気で破壊してしまうのですね.
Tatsuya Shirai への返信

Re: Wikiのページが時々,表示されない(別の原因)

- Haruhiko Okumura の投稿
preg系がUTF-8に対応しているということ自体あやしいですね。
mb_ereg_replaceとかだったらいいと思います。
Tatsuya Shirai への返信

Re: Wikiのページが時々,表示されない(別の原因)

- Tatsuya Shirai の投稿
同じコード,
$string2 = preg_replace('/\xad+/', '', $string2);
が,function kses_bad_protocol_once2()にもあります.今回はこちらにヒットしませんでしたが,やはり同じ問題を含んでいるので,これもコメントアウトの必要がありそうです.

Tatsuya Shirai への返信

Re: Wikiのページが時々,表示されない(別の原因)

- Haruhiko Okumura の投稿
ほんとですね,気がつきませんでした。ありがとうございます。
Haruhiko Okumura への返信

Re: Wikiのページが時々,表示されない(別の原因)

- Haruhiko Okumura の投稿
この件は解決済みのようですが,たまたま他のソフトでも問題になっていたので,メモしておきます:
kses.phpの日本語不具合 - WordPress 日本語版作成チーム | Google グループ
Haruhiko Okumura への返信

Re: Wikiのページが時々,表示されない(別の原因)

- Tatsuya Shirai の投稿
 なるほど、Latin-1におけるソフトハイフンという記号の意味だったのですか。もしOperaの特定のバージョンにおいて勝手にその文字を追加して自動折り返しでもする機能があるのなら、取り除く必要はありそうですね。
 他にも悩んでいるグループが居て安心しました。