ewikiのバイナリファイルのアップロードの不具合

ewikiのバイナリファイルのアップロードの不具合

- Tatsuya Shirai の投稿
返信数: 9
Wikiのプロパティ(?)で”バインリファイル有効”に設定しますと,編集時にバイナリファイルのアップロードが可能です.ところがいくら頑張ってもうまくアップロードできないファイル(画像)があります.ソースと睨めっこしながら調べた結果,

mod/wiki/ewiki/ewiki.phpの135行目にある,
define("EWIKI_IMAGE_MAXSIZE", 64 *1024);
によって,アップロードできるファイルサイズに制限があったようです.

2744行の
#-- reject image if too large
if (strlen($content) > EWIKI_IMAGE_MAXSIZE) {
ewiki_die(ewiki_t("BIN_IMGTOOLARGE"), $return);
return;
}
で,エラーを発生させている...のですが,このエラーメッセージが画面に表示されない(正確には真っ白なBODYの中身の無いWebページが正しく表示される)ため,原因が分からず随分と悩みました.

64KBよりも大きな画像はアップロードできないということは分かったのですが,この制限は何のためにあるのでしょう.勝手にサイズを大きくしてデータベースが壊れるのも怖い.既にファイルサイズの制限を大きくしているが問題は発生していない方,いらしたらご報告お願いします.64KBの制限はjpeg画像とはいえちょっと小さ過ぎます.


少しだけ困ったことなのですが,バイナリファイルがアップロードされた後に,そのデータベース内の参照用のリンクが表示されます(:このリンクをWiki内に記述すると,そこに画像が表示される.[internal://画像の名前]という形式)

この所在を教えてくれるページがポップアップする際に,FIrefoxならば問題無いのですが,IEだとランタイムエラー(';'が無い)が発生します.

opener.document.forms["ewiki"].elements["content"].value += "\nUPLOADED PICTURE: [internal://xampp.jpg"xampp.jpg"];\n";

このJavascriptに問題があるというのですが,私の乏しいJavascriptの知識では,どこに問題があるのか分かりません.IEの場合は”デバッグしますか?”に”いいえ”と答えさえすれば問題ないとは言え,結構,鬱陶しいですね.

Tatsuya Shirai への返信

Re: ewikiのバイナリファイルのアップロードの不具合

- Takayuki Fukuyama の投稿

こんにちわ、福山と申します。最近ewiki周りをさわっていたので、わかる範囲で
フォローさせていただきます。

> エラーメッセージが画面に表示されない
> (正確には真っ白なBODYの中身の無いWebページが正しく表示される)

これは、おそらくewiki_die()の関数まで到達できていない可能性が高いと思われます。

ewikiのファイルサイズ制限がデフォルトで64Kになっているのですが、さらにファイルの
アップロードフォームのhiddenフィールドにこのような記述がみられると思います
<input type="hidden" name="MAX_FILE_SIZE" value="65536" />
これにひっかかると、PHP側の$_FILESに何も入ってこないという状況になると思います。
(詳細は以下のurlを参照ください
http://jp.php.net/manual/ja/features.file-upload.php )

このような事から、おそらく、ewiki_binary_save_imageの第一引数である$filename
をプリントデバッグしてみると空になっていると思います。空の場合は


   #-- break on empty files
   if (!filesize($filename)) {
      return(false);
   }

ここでfalseを返してしまっていますから

$id = ewiki_binary_save_image(略);

ここで$idfalseがつめこまれます。
ですから本来は if($id === false){} のような感じでfalseだったときのエラー処理でも
いれるべきなのでしょうが、そのような処理は入っておらず、そのまま処理が進んでいって、
      if ($id) {
        // 略
      }

このifブロックに入れず、結局「何も出力しない」といった状態になっています。
ページが空白になるのはこういった理由かと思います。


> 64KBよりも大きな画像はアップロードできないということは分かったのですが,
> この制限は何のためにあるのでしょう.

この制限ですが、このewikiというものはどうやら「ErfurtWiki」というwikiエンジン、
といいますかプログラムを結構強引に乗せているようで、オフィシャルが
http://erfurtwiki.sourceforge.net/
ここにあるのですが、ここで最初から定数に64Kの値をいれているようです。
ですので64K制限というのはmoodleの制限ではなく、単純にこのプログラムが決め打ち
しているだけですので、ここを変更する事で制限を緩くする事ができます
(フォームのMAX_FILE_SIZEも変更されます)

> 勝手にサイズを大きくしてデータベースが壊れるのも怖い.
> 既にファイルサイズの制限を大きくしているが問題は発生していない方,
> いらしたらご報告お願いします.64KBの制限はjpeg画像とはいえちょっと小さ過ぎます.

私は本格運用の経験はありませんので、はっきりとした事は申し上げられませんが、
ファイル自体はバックエンドDBに直接格納されるわけではなく、moddata/wiki/のよう
なディレクトリの下に格納されるようですので、基本的にはディスクの空き容量と
相談という事になるような気がしています。

なお、DBに入っているページのデータ自体は「mdl_wiki_pages」のようなテーブルに
入っているので、一度参照してみてください。


> この所在を教えてくれるページがポップアップする際に,FIrefoxならば問題無い
> のですが,IEだとランタイムエラー(';'が無い)が発生します.
> opener.document.forms["ewiki"].elements["content"].value += "\nUPLOADED PICTURE: [internal://xampp.jpg"xampp.jpg"];\n";

これは

"\nUPLOADED PICTURE: [internal://xampp.jpg"xampp.jpg"];

この部分ですが、ダブルクォーテーションの処理に問題があります(この場合クオートが必要かと思います)。

とはいえ、この部分のjavascriptは、ewiki本来のフォームを使う場合であれば
有効なのですがmoodleにくっつけたものだとほとんど意味がないように思います。
(なお正しくjavascriptを動かすようにすると
window.setTimeout("self.close()", 5000);
これが効いてポップアップウィンドウが勝手に閉じます)
いちいちエラーが出て面倒な場合は<script></script>の部分をコメントアウトしてしま
うなりで対応するのが良いかと思います。

Takayuki Fukuyama への返信

Re: ewikiのバイナリファイルのアップロードの不具合

- Tatsuya Shirai の投稿

情報提供ありがとうございます.

>このifブロックに入れず、結局「何も出力しない」といった状態になっています。
>ページが空白になるのはこういった理由かと思います。
> エラーメッセージが画面に表示されない
> (正確には真っ白なBODYの中身の無いWebページが正しく表示される)

面白いことに(?)ファイルサイズが64KBギリギリの場合は別のエラーが表示されることが稀にあります.エラーを検出する段階が2段階ぐらいあって,ewiki寄りの場合にはMoodleで想定したエラーを発生できていないのだろうなぁと漫然と考えていました.
なるほど,とてもロジカルな説明でよく分かりました.時間があったらどちらでも同じエラーがでるように細工してみようと思います.でも,Moodleが表示するエラーも,「ファイルサイズが大きすぎる」といったズバリなエラーではなく,(うろ覚えで申し訳ありません)「ファイル1のログをアップロードします」といったような,「な,なんだ?」というようなエラーだったと思います...

> 64KBよりも大きな画像はアップロードできないということは分かったのですが,
> この制限は何のためにあるのでしょう.
そうですね.moddata/wikiの下に実体のバイナリーファイルは保存されているので,データベースを破壊しないような気がしたのですが,ちょっと不安でした.試された方が居るということは,即異常発生,というわけでは無さそうですので,こちらでも試して見ます.

これは

"\nUPLOADED PICTURE: [internal://xampp.jpg"xampp.jpg"];

この部分ですが、ダブルクォーテーションの処理に問題があります(この場合クオートが必要かと思います)。

すみません.これはダブルクォートの代わりにシングルクォートにする,という意味でしょうか?それともさらにシングルクォートで囲う,という意味でしょうか?


Tatsuya Shirai への返信

Re: ewikiのバイナリファイルのアップロードの不具合

- Tatsuya Shirai の投稿
なるほど,いまよーく見直して見たら原因が確かに分かりました.
opener.document.forms["ewiki"].elements["content"].value += "\\nUPLOADED PICTURE: [$id$title]\\n";
これがmod/wiki/ewiki/ewiki.phpの2590行目近辺にあるのですが,$idはともかく,$titleはダブルクォートで繰られたファイル名(="xampp.jpg")が入っているようです.したがって,
"\nUPLOADED PICTURE: [internal://xampp.jpg"xampp.jpg"];
この赤で示して頂いたダブルクォート同士がペアとなってしまい,xampp.jpgの前に;が無いので警告が出る,という意味ですね.なるほどー.ところで試しに$titleを取り除いてダブルクォートのペアが入れ子にならないようにしてみたら,別のエラーが出ました...うーむ.

そこで,御指摘の通り,この一行は削除しました.なるほど,確かに何の問題も無いようですね!

Tatsuya Shirai への返信

Re: ewikiのバイナリファイルのアップロードの不具合

- Takayuki Fukuyama の投稿

福山です。別のエラーはなかなか興味深いですね。当方でもいろいろとサイズを変えたりして試してみましたが、発生しませんでした。
なお、ewikiのメッセージも結局の所moodleのメッセージカタログを呼び出すように改造されているようですので、/lang/ja_utf8/wiki.phpのメッセージカタログが参考になるかもしれません(arrayのキーは多少加工されて送信されてしまっていますが)

なお、その後も少し調べたのですが以下のコードはどうも全く機能していないようです。

      #-- reject image if too large
      if (strlen($content) > EWIKI_IMAGE_MAXSIZE) {
         ewiki_die(ewiki_t("BIN_IMGTOOLARGE"), $return);
         return;
      }

前後を隈無く見回したのですが$contentという変数はどこにも現われておりませんし、global宣言もされていないので、おそらく常にNULLだと思います。これはおかしいなと思ってewikiの最新版(R1.02b)の該当箇所を見ました所以下のように変更されていました。

      #-- reject image if too large
      if(filesize($filename) > EWIKI_IMAGE_MAXSIZE) {
         ewiki_die(ewiki_t("BIN_IMGTOOLARGE"), $return);
         return;
      }

つまる所単純にfilesizeを見るようにしたようです。moodleのlatestでも古いewiki(R1.01d)を使っているのでこの辺は改善されていないようです。

ewiki側で潰したバグがmoodle側にマージされていないといった事が他にもあるかもしれませんので、追いかけるのも大変ですね。

# javascriptのsyntax errorの件は説明下手で申し訳ございませんでした...

Takayuki Fukuyama への返信

Re: ewikiのバイナリファイルのアップロードの不具合

- Tatsuya Shirai の投稿
”別のエラー”ですが,
ファイル 1 のログをアップロードする。 (xampp92a.jpg) :」
です.いま再現できました.これはja_utf8/moodle.phpの中にある,$string['uploadfilelog'] が出力されているようです.uplodafilelogは,lib/uploadlib.phpでしか利用されておらず,使用箇所はfunction print_upload_log()です.
このメッセージですが,エラーなのか報告なのか分かりません.昔は何か意味のあった関数だったのが使われなくなり,そのまま生き残っているだけなのでしょうか.”ファイル1”の部分は1,2,3,..と渡されたファイルリスト(Array)の要素数によって増えていくような感じです.なぜ使われなくなった関数だと思うのかと言うと,print_upload_log()関数は,mod/wiki/ewiki/plugins/moodle/moodle_binary_store.phpからしか呼ばれていないためです.
function moodle_binary_store_file()関数の中で,以下のように利用されています.
require_once($CFG->dirroot.'/lib/uploadlib.php');
$um = new upload_manager('upload',false,false,$course,false,0,true,true);
if ($um->process_file_uploads("$course->id/$CFG->moddata/wiki/$wiki->id/$entry->id/$ewiki_title")) {
$filename = ''; // this to make sure we don't keep processing in the parent function
if(!$id) {
$newfilename = $um->get_new_filename();
$id = EWIKI_IDF_INTERNAL.$newfilename;
}
return true;
}
error($um->print_upload_log(true));
return false;
}

アップロードに失敗した理由は多分,ファイルサイズの問題だとは思うのですが...

アップロードした画像ファイルは64.2KB (65,804Byte) ,添付します.70KBくらいの大きさのファイルをアップロードしようとすると,真っ白な画面になります.

添付 xampp92a.jpg
Tatsuya Shirai への返信

Re: ewikiのバイナリファイルのアップロードの不具合

- Tatsuya Shirai の投稿
取り敢えず,2種類のエラー(Moodleのアップロード機能とフォーム内での制限)に対応するために,以下の2箇所のエラーメッセージを仕込んで見ました.一箇所で済めば一番良いですし,アップロードしたファイルサイズが原因でNGである,とズバリ示すことができれば望ましいのですが,「エラーですよ.ファイルサイズをチェックして下さいね」と示すだけに留めました.

(1) mod/wiki/ewiki/ewiki.php
function ewiki_binary(): 2582行近辺,以下のブロックを追加.

#-- upload an image
if ($do == "upload"){

if (empty($upload_file["tmp_name"])) {
ewiki_die("バイナリファイルのアップロードに失敗しました.<BR>ファイルサイズを確認して下さい(最大サイズ:".EWIKI_IMAGE_MAXSIZE."バイト)", $return);
return;
}
$id = ewiki_binary_save_image($upload_file["tmp_name"], "", $return=0, $add_meta);

(2) mod/wiki/ewiki/plugins/moodle/moodle_binary_store.php
function moodle_binary_store_file()関数: 84行近辺

require_once($CFG->dirroot.'/lib/uploadlib.php');
$um = new upload_manager('upload',false,false,$course,false,0,true,true);
if ($um->process_file_uploads("$course->id/$CFG->moddata/wiki/$wiki->id/$entry->id/$ewiki_title")) {
$filename = ''; // this to make sure we don't keep processing in the parent function
if(!$id) {
$newfilename = $um->get_new_filename();
$id = EWIKI_IDF_INTERNAL.$newfilename;
}
return true;
}
// error($um->print_upload_log(true));
ewiki_die("バイナリファイルのアップロードに失敗しました.<BR>ファイルサイズを確認して下さい(最大サイズ:".EWIKI_IMAGE_MAXSIZE."バイト)", $return);
return false;
}


returnは不要かも?
これで「ダンマリ」にはならずに済みます.

Tatsuya Shirai への返信

Re: ewikiのバイナリファイルのアップロードの不具合

- Takayuki Fukuyama の投稿

当方でも再現しました。なるほど、この問題はかなり奥が深いですね...

とにかくいろいろな要素が絡んでいます。

まず第一にPHP側がMAX_FILE_SIZEのPOSTを(若干)越えたもののアップロードを
許容している点があります。これにより$_FILESに該当ファイルが入って処理が
続行されます。(PHPのバグでしょうか)

そして先にも書きましたが、以下のコードが正常に動作していないので処理が
続行されます

      if (strlen($content) > EWIKI_IMAGE_MAXSIZE) {
         ewiki_die(ewiki_t("BIN_IMGTOOLARGE"), $return);
         return;
      }

これは$contentが空なので常に無視されるのですが、それよりも問題なのが
その前の処理です。

      #-- resize image
      if (strpos($mime,"image/")!==false) {
      if ($pf_a = $ewiki_plugins["image_resize"]) {
      foreach ($pf_a as $pf) {
      if (EWIKI_IMAGE_RESIZE && (filesize($filename) > EWIKI_IMAGE_MAXSIZE)) {
         $pf($filename, $mime, $return);
         clearstatcache();
      }}}}

ここでGDを使ったリサイズを行っているのは良いのですが、tmpファイルを
勝手にリネームしてしまっています(resize imageが走る前と後で$filename
プリントしてみるとわかりやすいです)

/tmp/phpoIHD4E こういったtmpファイルが
/tmp/phpoIHD4E.jpeg このように.jpegの拡張子を付けられてリネームされて
しまっています。

リサイズした後でファイルサイズのチェックをしても正確な値が取れる
わけがないのはおいておいて、tmpファイルがリネームされてしまって
いますから、実際にアップロードされたファイルを処理するmove_uploaded_fileという
関数がlib/uploadlib.phpにあるのですが、これが正常に動作せず、falseを返しています。
(指定されたtmpfileを取りにいっているにもかかわらず見つけられないためと思われます)

if (move_uploaded_file($this->files[$i]['tmp_name'], $destination.'/'.$this->files[$i]['name'])) {}
これによりエラーになっているようですが、エラーメッセージがログうんぬんのような
あやしい原因までは見れていません。

解決策という解決策も見当たらないですが、とりあえずewikiのイメージリサイズは
moodleと相当相性がよくないようなのでこれをコメントアウトしてしまっても良い
かもしれません。

さらには、filesizeを正確に調べるのであればstrlen()ではなくfilesize()
使うように修正します(本来はPHPが提供している$_FILESの中身の数値を見た方が
楽なのですが、この関数内だとキーが取れないのでちょっと厳しそうです)

      #-- resize image
      /*
      if (strpos($mime,"image/")!==false) {
      if ($pf_a = $ewiki_plugins["image_resize"]) {
      foreach ($pf_a as $pf) {
      if (EWIKI_IMAGE_RESIZE && (filesize($filename) > EWIKI_IMAGE_MAXSIZE)) {
         $pf($filename, $mime, $return);
         clearstatcache();
      }}}}
      */
     
      #-- reject image if too large
      if (filesize($filename) > EWIKI_IMAGE_MAXSIZE) {
         ewiki_die(ewiki_t("BIN_IMGTOOLARGE"), $return);
         return;
      }

とりあえず応急処置的ですが、もうちょっとスマートなfixがあればよろしくお願いします。

Takayuki Fukuyama への返信

Re: ewikiのバイナリファイルのアップロードの不具合

- Tatsuya Shirai の投稿
福山さん,すばらしい!

なるほど,仕組みがやっと分かりました.リサイズはリサイズで悪い機能じゃなさそうですよ.ただ,勝手に裏でコソコソと作動しているのは気持ちが悪いのでメッセージを出しましょう.

それと,リサイズ前とリサイズ後でファイル名の後ろに,mod/wiki/ewiki/plugins/feature/resizeimage.phpで,勝手にmimetypeに従って拡張子(.jpeg)を付けてしまうことも確認しました.ということは,乱暴な話ではありますが,元の名前を覚えていて,それにリネームしてしまえばmove_uploaded_file()は騙されるのではないか? そう考えて試したところ,うまくリサイズされた画像がアップロードされるようになりました.
ちなみに原因不明でアップロードに失敗したと思われる画像ファイルがtmpフォルダに山のように残っている...誰か(cron.phpあたり?)が処分してくれるのかも知れませんが,ちょっと気持ち悪いですね.意味があるかどうか分かりませんが,それも削除するようにしてみたのが,以下のパッチです.

なお,勝手にリサイズされるのが気に食わないという場合は,139行目の
define("EWIKI_IMAGE_RESIZE", 1);

define("EWIKI_IMAGE_RESIZE", 0);
としても良いでしょうね.未確認ですが.

それとmove_uploaded_file()が,アップロードされたファイルサイズとリサイズされたファイルのファイルサイズの違いによって影響を受けるかなぁ?と少し心配していますが,単にファイルを一時フォルダ(tmp)から指定されたフォルダへコピーしているだけであると仮定しています.

mod/wiki/ewiki/ewiki.phpのfunction ewiki_binary_save_image()関数,2740行近辺.

#-- resize image
if (strpos($mime,"image/")!==false) {
if ($pf_a = $ewiki_plugins["image_resize"]) {
foreach ($pf_a as $pf) {
if (EWIKI_IMAGE_RESIZE && (filesize($filename) > EWIKI_IMAGE_MAXSIZE)) {
$original_filename = $filename; // (ADD)
echo "アップロードされた画像が大き過ぎます(".filesize($filename)."byte>".EWIKI_IMAGE_MAXSIZE."byte)..."; // (ADD)
$pf($filename, $mime, $return);
echo "リサイズ終了(->".filesize($filename)."byte)<BR>"; // (ADD)
clearstatcache();
}}}}

#-- reject image if too large
// if (strlen($content) > EWIKI_IMAGE_MAXSIZE) {
if (filesize($filename) > EWIKI_IMAGE_MAXSIZE) {
@unlink($filename); // (ADD)
ewiki_die(ewiki_t("BIN_IMGTOOLARGE"), $return);
return;
} else {
if ($original_filename != $filename) {
@unlink($original_filename);
@rename($filename, $original_filename);
}
}

#-- again check mime type and image sizes

Tatsuya Shirai への返信

Re: ewikiのバイナリファイルのアップロードの不具合

- Tatsuya Shirai の投稿
<input type="hidden" name="MAX_FILE_SIZE" value="65536" />

これの影響で,先にポストしたパッチでは,64KBよりも明らかに大きなファイルのアップロードができないことに気付きました.
せっかくリサイズを有効にしても意味がないですね.
mod/wiki/ewiki/ewiki.phpのfunction ewiki_page_edit_form_final_imgupload()関数,1626行近辺
function ewiki_page_edit_form_final_imgupload(&$o, &$id, &$data, &$action) {
if (EWIKI_SCRIPT_BINARY && EWIKI_UP_UPLOAD && EWIKI_IMAGE_MAXSIZE) {
$o .= "\n<br />\n". '<div class="image-upload">'
. '<form action='
. '"'. ewiki_script_binary("", EWIKI_IDF_INTERNAL, "", "_UPLOAD=1") .'"'
. ' method="post" enctype="multipart/form-data" target="_upload">'
. '<fieldset class="invisiblefieldset">'
// . '<input type="hidden" name="MAX_FILE_SIZE" value="'.EWIKI_IMAGE_MAXSIZE.'" />'
. '<input type="file" name="'.EWIKI_UP_UPLOAD.'"'
. (defined("EWIKI_IMAGE_ACCEPT") ? ' accept="'.EWIKI_IMAGE_ACCEPT.'" />' : " />")
. '<input type="hidden" name="'.EWIKI_UP_BINARY.'" value="'.EWIKI_IDF_INTERNAL.'" />'
. '&nbsp;&nbsp;&nbsp;'
. '<input type="submit" value="'.ewiki_t("UPLOAD_PICTURE_BUTTON").'" />'
. '</fieldset></form></div>'. "\n";
}
}

上記の通りにコメントアウトすればアップロード可能なファイルサイズの制限は無くなるようです.これは安全な対策でしょうか?

ただ,リサイズされたファイルのサイズが35KBくらい.私はてっきり64KBギリギリにリサイズしてくれると期待していたのですが...

define("EWIKI_IMAGE_MAXSIZE", 64 *1024);
define("EWIKI_IMAGE_MAXWIDTH", 3072);
define("EWIKI_IMAGE_MAXHEIGHT", 2048);
define("EWIKI_IMAGE_MAXALLOC", 1<<19);
define("EWIKI_IMAGE_RESIZE", 1);

MAXWIDTHやMAXHEIGHTの制限に引っかかっているようでもないので,何ゆえにここまで小さくしてくれるのか.
まぁ,簡便にWikiを使う,という立場でならばリサイズは有効な機能のように感じます.
もし「これ,小さすぎるよー.文字読めない」という場合はお手元で64KBに収まるように四苦八苦して頂く,ということでどうでしょう.