ページの作成や編集にはユーザ登録が必要です。
特定のページ名のページにおいて、sage や参照権限変更ができない。
- 投稿者: ぐうます
- カテゴリ: 本体
- 優先度: 低
- 状態: リリース済
- 日時: 2008年12月11日 18時53分43秒
内容
ある特定のページ名のページにおいて、次の不具合が発生します。
- ページ修正後、「タイムスタンプを更新しない」にチェックして保存しても、タイムスタンプが更新される。
- 編集ページから「参照権限を変更」ボタンで参照権限を変更しようとしても、常に「全員に公開」になる。
ある特定のページ名とは、次のようなページ名です。
- 先頭が 1 個以上の連続した '\' であるページ名
- 末尾が 1 個以上の連続した '\' であるページ名
現象確認した FSWiki バージョン
FreeStyle Wiki 3.6.3dev3
ただし、バグ対象のバージョン範囲(後述)はもっと広いです。
原因
Util::load_config_hash() によるファイル読込み処理時に使用される Util::_unescape() が、アンエスケープ処理を誤るため。
詳細
注:以下では、perl ソースファイル上の表記にあわせて、1 文字の '\' を '\\' と表記することにします。
_unescape() は、エスケープ処理してファイルに保存された文字列をアンエスケープする処理です。Util::save_config_hash() によるファイル保存時にはエスケープ処理により、
- '\\' → '\\\\'
- '\n' → '\\n'
- '\r' → '\\r'
という変換が施されますが、_unescape() は、Util::load_config_hash() によるファイル読込み時にこれを元に戻すために使用されます。よって、与えられた引数の文字列に '\\\\' がある場合、それを '\\' に戻すのが、_unescape() の期待される動作です。
さて、現状のプログラムにおいて、_unescape() の処理概要は次のようになっています。
- 引数で与えられた文字列を、'\\\\' で split し、得られるリストの各要素について、
- 次の置換を実施。
- '\\n' → '\n'
- '\\r' → '\r'
- 次の置換を実施。
- 置換結果を '\\' で連結。($buf が初期値である空文字列と等しくなければ、2 個目以降の要素とみなして '\\' で連結)。
Util::_unescape() のソース
sub _unescape { my $value = shift; my $buf = ''; foreach my $item (split(/\\\\/,$value)){ $item =~ s/\\n/\n/g; $item =~ s/\\r/\r/g; if($buf ne ''){ $buf .= '\\'; } $buf .= $item; } return $buf; }
引数文字列の先頭が '\\\\' である場合、split して得られるリストの先頭要素は空文字列になるので、foreach ループの 2 回目の処理時も $buf ne '' が成り立ちません。このため、必要な '\\' が連結されず、結果的に '\\\\' のアンエスケープ処理としては誤った結果を返します。
引数文字列の末尾が '\\\\' である場合、split した結果のリストの末尾要素は空文字列になるはずですが、perl の組み込み関数 split() の第 3 引数を明示的に指定しない default 動作は、結果リストの末尾に連続した空文字列要素を返却しません。すなわち、例題で確認すると次のようになります。このため、必要な '\\' が連結されず、結果的に '\\\\' のアンエスケープ処理としては誤った結果を返します。
% cat test.pl #!/usr/local/bin/perl my $str = 'a,b,c,d,,,'; foreach $i (split(/,/, $str)) { print "'$i'\n"; } % ./test.pl 'a' 'b' 'c' 'd' %
以上のようなアンエスケープ処理誤りが、config/modtime.dat (ページの更新日時保存ファイル), config/showlevel.log (ページの参照権限保存ファイル) の読み込み時にページ名に対して発生するので、これらのファイルを読み込んで得られるハッシュでは、キーとして誤ったページ名でデータが登録されてしまいます。よって、これらのハッシュからこれらのページの更新日時や参照権限を読み出そうとしても登録がないことになり、更新日時は Wiki ページソースファイルの物理的更新日時となり、参照権限は「全員に公開」になってしまいます。
なお、ページ名の表示時に誤らないのは、ページ名自体は log/pagelist.cache にキャッシュされており、Util::_unescape() を使用しない Util::load_config_text() によって読み出されている(そもそも、保存時にエスケープ処理されていない)ためです。
修正案
Util::escapeHTML() で採用されている、変換内容を定義したハッシュテーブルを用いる方法がシンプルでよいのではないでしょうか。
{ my %table = ("\\\\" => "\\", "\\n" => "\n", "\\r" => "\r"); sub _unescape { my $value = shift; $value =~ s/(\\[\\nr])/$table{$1}/go; return $value; } }
対象となる FSWiki バージョン
FreeStyle Wiki 3.6.0dev1 以降の全てのバージョン(3.6.0 を含む)。
パッチ
以上の内容のパッチファイル Util.pm.patch(518) を添付致します。なお、_unescape() に対するコメントに誤記があるようですので、その修正も入っています。
--- ../wiki3.6.3dev3orig/lib/Util.pm Mon Dec 31 12:05:37 2007 +++ lib/Util.pm Thu Dec 11 15:08:34 2008 @@ -268,20 +268,16 @@ } #=============================================================================== -# save_config_hash関数で使用するアンエスケープ用関数 +# load_config_hash関数で使用するアンエスケープ用関数 #=============================================================================== -sub _unescape { - my $value = shift; - my $buf = ''; - foreach my $item (split(/\\\\/,$value)){ - $item =~ s/\\n/\n/g; - $item =~ s/\\r/\r/g; - if($buf ne ''){ - $buf .= '\\'; - } - $buf .= $item; - } - return $buf; +{ + my %table = ("\\\\" => "\\", "\\n" => "\n", "\\r" => "\r"); + + sub _unescape { + my $value = shift; + $value =~ s/(\\[\\nr])/$table{$1}/go; + return $value; + } } #===============================================================================
コメント
- 詳細な説明ありがとうございます。取り込ませていただこうと思います。 - たけぞう (2008年12月12日 11時09分21秒)
- パッチをコミットしました。 - たけぞう (2008年12月14日 15時58分24秒)
- コミットしていただき、ありがとうございます。 - ぐうます (2008年12月14日 17時48分29秒)
- 3.6.3でリリースしました。 - たけぞう (2008年12月14日 23時30分47秒)
最終更新時間:2008年12月14日 23時30分47秒