- PR -

ファイルアップロードCGI が Windows server 2003 で動かないのは?

投稿者投稿内容
himahima_san
会議室デビュー日: 2005/04/03
投稿数: 12
投稿日時: 2005-04-03 19:46
初めてスレを立ち上げます。
ITエキスパートではないのですが、仕事でどうしても急ぐので、皆様のお力を借りたいと思います。
さて問題は、次のファイルアップロードCGIです。

---------------------------------------------------------
$Ctitle='ファイルアップロード'; # タイトル
$Cbody='<body bgcolor=#ffffff text=#000000>'; # BODYタグ
$Cuploaddir=./nattokudb/images/s02'; # アップロードファイルを格納するディレクトリ
#$Cappendext='.upl'; # ファイル名に追加する拡張子
$Cappendext='';
$Callsize=1024*1024*4; # 認める総合計ファイルサイズ(ex:1024*100)
$Cmaxsize=1024*1024; # 認める最大ファイルサイズ(ex:1024*10)
@Cextlist=qw(jpg); # 認める拡張子(ext:gif jpg)
$Cerrmstagb='<font color=red><b><small>'; # 認証エラー時のタグ
$Cerrmstage='</small></font></b>'; # 認証エラー時のタグ
$Cinfomstagb='<font color=#6040ff><b><small>'; # 送信情報メッセージのタグ
$Cinfomstage='</small></font></b>'; # 送信情報メッセージのタグ
$Ctablecolor='#808080'; # ボーダー色
$Ccellcolor='#ffffe0'; # セル色


############### Init
$buf="";
$read_data="";
$remain=$ENV{'CONTENT_LENGTH'};

############### Out
&header;
if($remain>$Callsize){
print'送信合計サイズが大きすぎます。<br>';
print"送信サイズは、約",&intkp($Callsize),"キロバイトまでです。<br>";
&fooder;
exit;
}
print'<p align=center>下記のファイルを確認しました。</p>';
print"<div align=center><table bgcolor=$Ctablecolor cellspacing=2 cellpadding=4>";
binmode(STDIN);
while($remain){$remain-=sysread(STDIN,$buf,$remain);$read_data.=$buf;}
$pos1=$pos2=$pos3=0;
$delimiter="";
$max_count=0;
$sendfilecnt=0;
while(1){
$pos2=index($read_data,"\n\n",$pos1)+4;
@headers=split("\n",substr($read_data,$pos1,$pos2-$pos1));
$filename='';
$name='';
foreach(@headers){
if($delimiter eq''){
$delimiter=$_;
}elsif(/^Content-Disposition: ([^;]*); name="([^;]*)"; filename="([^;]*)"/i){
if($3){
$filename=$3;
if($filename=~/([^\\\/]+$)/){$filename=$1;}
}
}elsif(/^Content-Disposition: ([^;]*); name="([^;]*)"/i){$name=$2;}
}
$pos3=index($read_data,"\n$delimiter",$pos2);
$size=$pos3-$pos2;
$flcheck=1;
$extok=0;
if($filename){
print"<tr><td bgcolor=$Ccellcolor>";
print"<table><tr><td>●</td><td>";
print"<b>ファイル : $filename</b>";
print"</tr></td>";

$ext=(split(/\./,$filename))[1];
foreach(@Cextlist){if(/^$ext$/i){$extok=1;last;}}
if(!$extok){
print"<tr><td></td><td><p>$Cerrmstagb";
print"$filename 拡張子が認められていません。";
print"$Cerrmstage<br>";
print"<small>ファイルの種類は(",join ' / ',@Cextlist,")のみが有効です。</small>";
print"</p></tr></td>";
$flcheck=0;
}
if($size>$Cmaxsize){
print"<tr><td></td><td><p>$Cerrmstagb";
print"$filename ファイルサイズが大きすぎます。";
print"$Cerrmstage<br>";
print"<small>ファイルサイズは、約",$Cmaxsize,"バイトまでです。</small>";
print"</p></tr></td>";
$flcheck=0;
}
}
if(!$flcheck){
print"<tr><td></td><td><p>$Cinfomstagb";
print"このファイルの送信はキャンセルされました。";
print"$Cinfomstage</p>";
print"</tr></td></table>";
print'</td></tr>';
}
if($filename && $flcheck){
if(open(FH,">$Cuploaddir/$filename$Cappendext")){
binmode FH;
print FH substr($read_data,$pos2,$size);
close FH;
$sendfilecnt++;
print"<tr><td></td><td><p>$Cinfomstagb";
print"このファイルの送信は正常に完了しました。";
print"$Cinfomstage</p>";
print"</tr></td></table>";
print'</td></tr>';
}
}elsif($name){$FORM{$name}=substr($read_data,$pos2,$size);}
$pos1=$pos3+length("\n$delimiter");
if(substr($read_data,$pos1,4) eq"--\n"){last;}
else{
$pos1+=2;
if($max_count++ >16){last;}next;
}
}
print'</table></div>';
if($sendfilecnt){print"<p align=center>合計 $sendfilecnt つのファイルを送信しました。</p>";}
else{print"<p align=center>ファイルを送信できませんでした。前ページに戻り再度確認してください。</p>";}
&fooder;exit;
---------------------------------------------------------------------------

これを実行すると、エラーにこそなりませんが、ファイルは送信されず、

---------------------------------------------------------------------

下記のファイルを確認しました。

● ファイル : 12_1.jpg

ファイルを送信できませんでした。前ページに戻り再度確認してください。
---------------------------------------------------------------------

と表示されます。
どうやら、$filenameがヌルのようです。

Windows2003への移行ということで、\r\nをすべて\nに変更しました。
それ以外に、何をどうすればいいのか、さっぱり分かりません。
アクセス権の設定・・・というようなものも見かけましたが、さっぱりわかりません。
とりあえず、レンタルサーバーのコントロールパネルに「FTPユーザー」が追加できたので、追加してみました。

本当に困っています。
どなたか教えてください。
がるがる
ぬし
会議室デビュー日: 2002/04/12
投稿数: 873
投稿日時: 2005-04-04 16:09
どもです。がると申します。
…なんていうか、突っ込みどころ満載なのですが。
とりあえず「アクセス権の設定」が「さっぱりわかりません」という
状態で、CGIをお仕事として扱っていくのはちょっと至難か、と。

ただ、多分このままだとろくすっぽレスもつかない気がするので、
ちょいとだけ。
こういうときは、丁寧に問題点からさかのぼっていくのが基本。

まず、エラー(というか問題)は、$sendfilecnt が0であるがゆえに、
if文で「ファイルを送信できない」という風なブロックに
プログラムが流れています。
この変数は
$sendfilecnt=0;
と宣言された後、
if($filename && $flcheck){
if(open(FH,">$Cuploaddir/$filename$Cappendext")){
この二つのif文がともにtrueである場合に限って、ブロック内で
インクリメントされています。

つまり、上記のifのいずれかがfalseであるというのが原因に
なります。
…とまぁ、こうやって読み解いていきます。
まぁ、パーミッション(アクセス権)がどうのというお話を見ている限りですと、
「$Cuploaddirで設定されているディレクトリのパーミッションがおかしい」
んだろうなぁ、とか想像しますが :-P
# 多分、httpdデーモン(Windowsだとなんと呼ぶのだろう?)のユーザと
# 違う所有者の状態で644なんだろうなぁ、っと。

とりあえず、Googleあたりで
パーミッション 書き込み 読み込み 実行
とかいうキーワードでちゃんと調べて、「理解してから」パーミッション
の設定をされることをお勧めします。
# 曲がりなりにも、基礎以前のレベルの内容ですし…

ここで回答を述べても、ほぼ確実に「その場限りの対応」になる上に
「次回も同じ間違いで引っかかる」「セキュリティ的に危ない設定を
する」などの弊害が山のように予想できますので、私は回答しません。

きちんと調べられての質問であればまたちゃんと回答いたしますので。
この業界、やはり知識が重要で、つまりは調べるというスキルが大切
です :-P

ちょいときつい発言になりましたが。
がんばってください ^^

[余談]
ちなみに個人的には、こーゆー時のデバッグには、まず先頭に
print "Content-type : text/plain\n\n";
とかいうおまじないをぶち込みます :-P

しかる後に、適当な行ごとに
print "Trap 適当な連番 \n";
とかを入れてある程度問題箇所のあたりを付けて…ってパターン。
昔懐かしい由緒正しい手法です(笑

原始的ではありますが、相変わらず便利なんですよねぇ。
いやまぁ、コマンドライン使える環境ならストレートにコマンドライン
からCGIたたきますが :-P
[/余談]
Mattun
ぬし
会議室デビュー日: 2004/08/10
投稿数: 1391
投稿日時: 2005-04-04 17:28
引用:

# 多分、httpdデーモン(Windowsだとなんと呼ぶのだろう?)のユーザと
# 違う所有者の状態で644なんだろうなぁ、っと。


IIS6.0(WindowsServer2003標準のWebサーバ)だと、
・新規作成したWebサイトおよび仮想ディレクトリは読み取り専用
・さらにIISで匿名ユーザを利用してるなら、デフォルトのサイトフォルダは
 IISの匿名ユーザに対してはデフォルトで書き込み禁止権限が付与
って感じで、完全にフールプルーフ対策が取られてます。

ただ、Perl+IIS5ならまだしも、Perl+IIS6での資料って探しても少なかったりします。
IIS5はProfessionalなバージョンのWindowsに付与されてるけど、
IIS6はServerOSにしか付与されてないんで、個人レベルで情報公開してる人は少ないでしょうし。
Windows2000のIIS5だと、後者の設定はありませんから、後者の部分に関しては
PerlだけじゃなくCGI全般の問題として、Microsoftのドキュメントをあさる必要はありそうですね。
himahima_san
会議室デビュー日: 2005/04/03
投稿数: 12
投稿日時: 2005-04-04 18:20
早速のご教授、ありがとうございます。
わたしも、if($filename && $flcheck)の部分に行き着きましたので、
print文で、$filenameと$flcheckを表示させると、
$filenameがヌルだということに気がつきました。
アクセス権がわからない、と書いてあったのは、Windows2003serverのことで、
基礎の基礎のパーミッションの設定ぐらいは少し知っています。
といっても、素人の域ですが・・・。
Windows2000serverや、UNIX、Linux系サーバーなら、なんとかかんとかCGIをセットできるのですが、
Windows2003は初めてで、お手上げです。
それに、パーミッションの設定も、FTPではできませんし、プロバイダのFAQには、設定しなくてよいとのこと。


一応、知らないなりにももうかれこれ1週間ほど調べて、万策尽きてこちらのスレを立ち上げたのですが・・・。

Mattunさんのおっしゃるとおり、
書込み禁止になっている気がしてなりません。
プロバイダのいうとおり、レンタルサーバーのコンパネで、ユーザーの追加というものをやってみましたが、いったいそのユーザーでどうやってアクセスするのかがわかりません。

本当に、雲をつかむような話で申し訳ありません。

ですが、書込み禁止だと、アクセスカウンタすら動かないんです。
こちらも知識がないもんで、CGIが悪いのか、サーバーの特徴なのかがわからず、砂漠の砂の中から針を探している状態です。

がるがるさんの
print "Content-type : text/plain\n\n";
は参考になりました。
早速やってみます。

まあでも、いろいろ言い訳してみても、やはり私の勉強不足ですね。
すいません。お世話になりました。
もし何か他に情報があれば、お願いします。

Mattunさん、がるがるさん、ありがとうございました。
Mattun
ぬし
会議室デビュー日: 2004/08/10
投稿数: 1391
投稿日時: 2005-04-04 19:25
引用:

アクセス権がわからない、と書いてあったのは、Windows2003serverのことで、
基礎の基礎のパーミッションの設定ぐらいは少し知っています。
といっても、素人の域ですが・・・。


UNIXでのアクセス権の話は知ってても、WindowsのIISでのアクセス権のことを
知らない人は結構多いですから。
対象と権限で、3x3のアクセス権しか設定できない(ACL対応除く)UNIXに比べて、
Windowsのアクセス権は奥が深いですから。
それでいてしっかり理解している人も少ないですし。

IISでは、
・匿名認証を利用してる場合:
  IUSR_<コンピュータ名>というユーザでCGIが実行される
・基本認証やWindows統合認証を利用している場合:
  認証に利用したローカルユーザやドメインユーザでCGIが実行される
となります。
匿名認証を使ってる場合、そのCGIが書き込みを行うフォルダに対して、
IUSRが書き込みの権限をもっている必要があります。

で、
引用:

・新規作成したWebサイトおよび仮想ディレクトリは読み取り専用
・さらにIISで匿名ユーザを利用してるなら、デフォルトのサイトフォルダは
 IISの匿名ユーザに対してはデフォルトで書き込み禁止権限が付与


と書いたわけですが、
前者については、IISの管理ツール(インターネットサービスマネージャ)で、
該当するフォルダのプロパティを見れば分かります。
後者については、エクスプローラなどでそのフォルダのプロパティを開き、
セキュリティタブのアクセス権を見直してください。
wwwrootフォルダ以下であればIUSRへの書き込み拒否アクセス権が設定されてますし、
その他のフォルダであればIUSRが含まれるUSERSグループに対して
書き込みアクセス権が付与されてない(=書き込み拒否)となってます。


で、実際に書き込みに失敗しているかどうかについては、
ファイルアクセスの監査を設定すれば分かります。
この辺はヘルプファイルに載ってます。
himahima_san
会議室デビュー日: 2005/04/03
投稿数: 12
投稿日時: 2005-04-04 21:23
お世話になります。
早速のご教授ありがとうございます。

>前者については、IISの管理ツール(インターネットサービスマネージャ)で、
該当するフォルダのプロパティを見れば分かります。

とありますが、レンタルサーバーの場合、どうすればいいでしょうか。
とりあえず、レンタルサーバーのコントロールパネルをもう一回見直してみました。
すると、

「ACL Permissions」
というものがありましたので、「これだ!」と思い、ユーザーを見てみると、

1.レンタルサーバー申し込み時に設定した ユーザー名  のユーザー
2.ユーザーマネージャーで追加した ユーザー(ユーザーマネージャーで閲覧すると、レベルはFTPユーザーとなっています)

この2つのユーザーで、 fullcontrol となっていました。

危ないかな・・・と思ったのですが、3つめのユーザーとして、

3.everyone fullcontrol と設定し、CGIの動作確認をしました。

やはり、動きません。

ということは、アクセス権ではなかったのでしょうか???
がるがる
ぬし
会議室デビュー日: 2002/04/12
投稿数: 873
投稿日時: 2005-04-04 23:22
どもでふ。がるです。

引用:

わたしも、if($filename && $flcheck)の部分に行き着きましたので、
print文で、$filenameと$flcheckを表示させると、
$filenameがヌルだということに気がつきました。


NULLってか、多分0になってますね。初期設定で値に0が入っている
かと思うので。

引用:

Mattunさんのおっしゃるとおり、
書込み禁止になっている気がしてなりません。


単純にこれを調べるのであれば、例えば以下のようなコードをまず
でっち上げます。

コード:
#!/usr/bin/perl

open (IN, "> 適切なディレクトリ/適当なファイル名");
print IN "なんかまぁ適宜";
close IN;

print "Content-type: text\/plain\n\n";
print "できたかな?";


これをCGIとして動かして、該当するディレクトリにファイルが
出来上がるかどうか、をチェックするのが多分一番早いです。

コード:
ですが、書込み禁止だと、アクセスカウンタすら動かないんです。
こちらも知識がないもんで、CGIが悪いのか、サーバーの特徴なのかがわからず、砂漠の砂の中から針を探している状態です。


まぁ、砂漠の中から針を探すスキルは基礎であると同時にもっとも
難度の高いスキルでもあるので :-P
いかにして「針を探すスキルを上げるか」は重要だと思います。

コード:
print "Content-type : text/plain\n\n";
は参考になりました。
早速やってみます。


非常〜〜〜〜〜〜〜〜に原始的ですが。
つまりは「ほぼ環境を問わずに」使えるので、とても便利です。
私はvi使いなので、
・通常のHTML出力のprint文は行頭に書かず、最低2文字の空白を入れる
(もっともCGIソースコードにHTMLを書くこと自体最近は0ですが)
・デバッグ用のprint文は必ず行頭に書く
という規則を守ることで、
/^print
とかやると瞬時に検索ができます。無論
:%s・^print/\/\/print/g
という手段もありありです(逆もあり)。

さぁ、みんなでLet's vi !! (なんか違うだろ(笑

コード:
まあでも、いろいろ言い訳してみても、やはり私の勉強不足ですね。


まぁ、勉強不足なのはたぶん誰しも一緒です。あとは「如何にして
今の自分よりも高いスキルを得るか」なので。

長くなりましたが、以下、簡単にデバッグのコツを。
・状況の可能性を分析する
・細かく切り分ける
個人的にはこれに尽きます。

今回の場合、直接的な問題は
・ファイルが書き込めない
で、その論拠は
・$sendfilecntの値が0のまんまである
・ファイルが実際に書き込まれていない(んですよね? 多分)
となってます。

で、原因としては
・CGIのプログラム上の不具合
・ファイルが書き込めない
の2者の可能性があって。
後者は、前述したお手軽CGIでチェックできるかと思いますので、
ここでは前者のほうを。

プログラムをざっくりと見ますと、$sendfilecnt がインクリメント(++)
されている場所は一箇所だけです。
つまり、根本的には
コード:
if($filename && $flcheck){
if(open(FH,">$Cuploaddir/$filename$Cappendext")){


この2つのifか、もしくは「さらに手前のどこか」ではねられている
ことになります。
まぁ、この処理をしてるwhileループ、泣いちゃいそうなくらいに長いのですが(苦笑

とりあえず、このwhileループの随所に
print "Trap 番号\n";
を突っ込んで、プログラムが実際にどのような挙動で流れているのか
を把握されると良いかと思います。
ポイントは
・if、for、foreach、whileに入る直前の行
・入った直後の行
・出た直後の行
にとりあえずトラップを仕掛けましょう。つまり
コード:
print "Trap1\n";
  if () {
print "Trap2\n";
    ほにゃららなしょり
  }
print "Trap3\n";


こんな感じです。これで、
Trap1
Trap2
Trap3
とくれば「if文を通って次に行った」ことがわかりますし、
Trap1
Trap3
とくれば「if文はスルーして次に行った」ことがわかります。

こうやって問題点を絞り込んでいけば、今よりはもう少し見通しも
良くなるのではないかなぁ、とおもうのですが。

また進展があったら書き込みしてくださいませ。
時間の可能な範囲内でですが、お手伝いしますので。
himahima_san
会議室デビュー日: 2005/04/03
投稿数: 12
投稿日時: 2005-04-05 01:47
がるさん、ご教授ありがとうございます。

さて、まず
コード:
--------------------------------------------------------------------------------

#!/usr/bin/perl

open (IN, "> test.txt");
print IN "なんかまぁ適宜";
close IN;

print "Content-type: text\/plain\n\n";
print "できたかな?";

を作って実行したところ、test.txtファイルは更新されませんでした。
やはり書き込みが行われないのか・・・と思いつつ、

教えていただいたとおりトラップを仕掛けて調べたところ、

if($3){
  print "$filenameファイルネーム";
  $filename=$3;
  if($filename=~/([^\\\/]+$)/){$filename=$1;}
}

としておいたら、
やはり$filenameはヌルでした。

ということは、書き込みが禁止である上に、このCGIはこのサーバーでは動かない
ということでしょうか。

もう、私の手にはおえそうにありません。
Windows server2003恐るべし・・・

スキルアップ/キャリアアップ(JOB@IT)