いろいろな端末の為の着メロサイト3
scm4.cgiを公開します
デモとダウンロードはこちらへ
『簡単な内容でいいから、とにかくi-mode、J-sky(J-PHON)、EzWebなどの機種に対応した携帯用着メロサイトを作りたい!』
という方のためのCGIを作成しました。
PC用ブラウザのためのHTMLのタグ4種類と本ツール用似非タグ4種類を使って元ページデータファイルというものを各ページ毎に
1ファイルずつ作るだけでCGIがアクセスしてきた端末に合わせて表示用データを書き換えて送るようにするものです。
着メロを増やしたり、テキストデータを変更したりするのも着メロファイルの準備の他は元ページデータファイルを書き換えるだけなのでメンテが楽になる
と思います。私が自分で携帯サイトを作って着メロダウンロードページを設ける為に作ったCGIです。
scm4.cgiソースファイルに突っ込みを入れる(笑)!!
取説を真面目に書いてしまったので、週報に書くネタが有りません。なので、今回の週報は、自分で作ったscm4.cgiに対して
自己突っ込みや解説を入れたい部分を取り上げて、つべこべ申し述べてみることにしました。大抵の方に不要なインフォメーション…(笑)。
# jcode.plの取り込み
@dmy=<${jcode_path}*>;
if($dmy[0] eq $jcode_path){require $jcode_path;}else{$message="jcode.plのパス設定が違っています。";}
これはjcode.plを取り込むときにパスが違っていると全く原因の分からないエラーメッセージがでてビックリするのを避ける為、PCでアクセスした場合に原因を明確にしたエラーメッセージを出そうという試みです。
# アクセスされたページネームの取得
%form = &read_input('sjis');
$page =$form{'page'};
$agent =$form{'agent'};
これは、ページのリンクと、アクセスして来た端末の情報を得る為の入力部分です。
# XHTMLダウンロード画面
$kyokuban=-1;
if(index($page." ","@")>0){
@dmy=split("@",$page);
$page=$dmy[0];
$kyokuban=$dmy[1];
}
XHTMLでのダウンロードのタグが一曲につき膨大になる為、選択された曲のみに対してダウンロードの操作をするページを表示させようという試みです。曲選択で、$pageにファイル名@曲番号が引数として渡されてくるのです。
# 表示頁のパスの分離
$pos1=rindex($page,"/");
if($pos1>0){
$base=substr($page,0,$pos1+1);
}else{
$base="";
}
元ページデータファイルの相対アドレスの内、ディレクトリに当る部分を$baseに格納しています。
# ページタイトルの分離
$pos1=index($honbun,"<!-title\"");
if($pos1>=0){
$pos2=index($honbun,"\"-->",$pos1);
$title=substr($honbun,$pos1+9,$pos2-$pos1-9);
$honbun=substr($honbun,0,$pos1).$atag.substr($honbun,$pos2+4,length($honbun)-$pos2-4);
}
# データタイプの分離
$pos1=index($honbun,"<!-Dtype\"");
if($pos1>=0){
$pos2=index($honbun,"\"-->",$pos1);
$Dtype=substr($honbun,$pos1+9,$pos2-$pos1-9);
$honbun=substr($honbun,0,$pos1).$atag.substr($honbun,$pos2+4,length($honbun)-$pos2-4);
}
@data_type=split(",",$Dtype);
この当りはローカルルール似非タグの処理です。コメントタグにscm4.cgi独自の役割を加えた部分を取り出しています。
$base1=$base;
$path=$path[$i];
while(substr($path,0,3) eq "../" && substr($base1,length($base1)-3,3) ne "../"){
$pos=rindex($base1,"/",length($base1)-2);
if($pos<0){$base1="";}else{$base1=substr($base1,0,$pos+1);}
$path=substr($path,3,length($path)-3);
}
$path[$i]=$base1.$path;
ここ分かりにくいと思うのですが、
$baseはCGIから着メロファイルダウンロードページの有るディレクトリへの相対アドレス
$pathは着メロファイルダウンロードページから着メロファイルへの相対アドレス
なので、CGIからの着メロファイルへの相対アドレスは$base.$pathと合体させれば役目を果たします。
それでは、whileの部分で何をやっているかというと、単純に合体すると
$base="../html/" : $path="../melo" の場合に $base.$path="../html/../melo" となったりするのが嫌で、
間の "html/.." の部分を相殺して $base.$path="../melo" の様に短縮するというだけのことをしています(笑)。
だってほら、携帯って文字数に合わせてパケット代がかかるのかな〜と思ったので。PC相手だったらこんなことしません。
# 相対アドレス指定のファイルへのLINKをCGI経由でのアクセスに変更する。
$pos3=0;
while(index($indata[0],"<a href=",$pos3)>=0){
$pos1=index($indata[0],"<a href=",$pos3);
$pos2=index($indata[0],">",$pos1);
$pos3=index($indata[0],"</a>",$pos2);
$atag=substr($indata[0],$pos1,$pos2-$pos1+1);
if(index($atag,"http:")<0){
$atag=~ s/<a href=\"/<a href=\"$me\?page=$base/g;
$indata[0]=substr($indata[0],0,$pos1).$atag.substr($indata[0],$pos2+1,length($indata[0])-$pos2);
}
}
これは、<a href="melo1.html">などと書いてある相対アドレスのLINKを、<a href="scm4.cgi?page=melo1.html">の様に書き直す部分です。$meというのはscm4.cgiのこと、ファイル名を変えたときにはそのファイル名に合わせた文字列が入っているはずの変数です。
# エージェントを取得して、表示の切り替えをする。
if($agent eq ""){ $agent=$ENV{'HTTP_USER_AGENT'}; }
if(index($agent,"DoCoMo")>=0){
$pagedata=&page0;
}elsif(index($agent,"J-PHON")>=0){
$pagedata=&page1;
}elsif(index($agent,"UP.Browser")>0){
$pagedata=&page2;
}elsif(index($agent,"UP.Browser")==0){
$pagedata=&page3;
}else{
$pagedata=&page4;
}
print $pagedata;
今まで読み取ってきた元ページデータファイルからの情報を使って、いよいよ端末毎の表現を振り分ける部分です。
各々の表示データは、サブルーチンpage0〜page4までの間に記述しています。
# アクセスログの処理
if($axcess_log == 1){&axcess_check;}
アクセスログを取るサブルーチンを呼んでいます。$axcess_logはCGIのトップの方でユーザー様が1か0で指定するようお願いします!!
sub page0{
# i-modeソースを作成する
$axcess_page="I";
$out=$out."<html><head><title>$title</title></head><body>\n".$indata[0];
for($i=0;$i<$dnno;$i++){$out=$out."<a href=\"".$path[$i]."I_$filename[$i].mld\">$label[$i]</a><br>\n";}
$out=$out."</body></html>";
$out="Content-Type: text/html\nContent-Length: ".length($out)."\n\n".$out;
return $out;
}
i-mode用のページ出力。$axcess_pageはアクセスログに残すページ種別です。
$outは実際にクライアントに送信されるデータの全てです。
html自体はPC用のブラウザと余り代わりが無いのですが、
"Content-Type: text/html\nContent-Length: ".length($out)."\n\n"
という部分のみが特殊です。HTMLの文字数length($out)をカウントして出力する必要が有るそうです。無くても大丈夫な端末の方が多いらしいですが。
sub page1{
# J-skyソースを作成する
$axcess_page="J";
$out="Content-type:text/html\n\n";
$out=$out."<html><head><title>$title</title></head><body>\n".$indata[0];
for($i=0;$i<$dnno;$i++){$out=$out."<a href=\"".$path[$i]."J_$filename[$i].mmf\">$label[$i]</a><br>\n";}
$out=$out."</body></html>";
return $out;
}
J-sky用のページ出力。$axcess_pageはアクセスログに残すページ種別です。
$outは実際にクライアントに送信されるデータの全てです。
html自体はPC用のブラウザと余り代わりが無くて、PC用のブラウザのタグに慣れた方に一番分かりやすいHTMLだと思います。
sub page2{
# XHMLソースを作成する
$axcess_page="X";
$out="content-type:text/html\n\n";
$out=$out."<?xml version=\"1.0\" encoding=\"Shift_JIS\"?>\n<!DOCTYPE html PUBLIC \"-//OPENWAVE//DTD XHTML 1.0//EN\"";
$out=$out." http://www.openwave.com/DTD/xhtml-basic.dtd>\n<html>\n<head><title>$title</title></head>\n<body>\n";
if($kyokuban<0){
$out=$out.$indata[0]."\n";
for($i=0;$i<$dnno;$i++){
$out=$out."<a href=\"$me?page=$page".'@'."$i\">$label[$i]</a><br />\n";
}
}else{
$out=$out."$label[$kyokuban]<br>の着メロをダウンロードしますか?<br /><br />"."\n";
$out=$out."<center><object data=\"".$path[$kyokuban]."J_$filename[$kyokuban].mmf\" type=\"$data_type[1]\" standby=\" OK \">\n";
$out=$out."<param name=\"title\" value=\"$filename[$kyokuban]\" valuetype=\"data\" />\n";
$out=$out."<param name=\"size\" value=\"$size[$kyokuban]\" valuetype=\"data\" />\n";
$out=$out."<param name=\"disposition\" value=\"$data_type[0]\" valuetype=\"data\" />\n</object></center>\n";
$out=$out."<center><a href=\"$me?page=$page\"> 戻る </a></center>\n";
}
$out=$out."</body></html>";
$out=~ s/<br>/<br \/>/g;
return $out;
}
EzWeb(WAP2.0端末)用のページ出力。$axcess_pageはアクセスログに残すページ種別です。
$outは実際にクライアントに送信されるデータの全てです。
$kyokubanに曲の番号が指定されていない場合は、選曲リストページの表示になります。
$kyokubanに曲の番号が指定されている場合は、その番号の着メロファイルをダウンロードする操作のページを表示します。
sub page3{
$axcess_page="E";
# HDMLソースを作成する
$out="content-type:text/x-hdml;charset=shift_jis\n\n";
# <font></font>タグを削除する
$pos2=0;
while(index($indata[0],"<font",$pos2)>=0){
$pos1=index($indata[0],"<font",$pos2);
$pos2=index($indata[0],">",$pos1);
$indata[0]=substr($indata[0],0,$pos1).substr($indata[0],$pos2+1,length($indata[0])-$pos2);
$indata[0]=~ s/<\/font>//g;
}
# HDMLを作成する
if($dnno == 0){
$out=$out."<hdml version=\"3.0\" markable=\"TRUE\">\n<display title=\"$title\">\n".$indata[0]."\n</display></hdml>";
}else{
$out=$out."<hdml version=\"3.0\" markable=\"TRUE\">\n<choice key=\"url\" title=\"$title\" method=\"alpha\">\n";
$out=$out."<action type=\"accept\" task=\"gosub\" dest=\"device:data/dnld?url=\$url\" label=\"OK\">\n".$indata[0];
for($i=0;$i<$dnno;$i++){
$out=$out."<ce value=\"$download_cgi&name=".$path[$i]."E_$filename[$i].mmf&size=$checksum[$i]&disposition=$data_type[0]&title=$filename[$i]\">$label[$i]\n";
}
$out=$out."</choice></hdml>";
}
$out=~ s/<\/center>/<br>/g;
$out=~ s/<a href\=\"/<a type\=\"accept\" task\=\"go\" label\=\"OK\" dest\=\"/g;
return $out;
}
EzWeb(HDML端末)用のページ出力。$axcess_pageはアクセスログに残すページ種別です。
$outは実際にクライアントに送信されるデータの全てです。
<font>タグを削ったり、テキストページの場合は<display>カード、着メロページの場合は<choice>カードを使用するとか、linkを<a type="accept" task="go" label="OK" dest="パス">に書き直したりとか、やること満載です(笑)。
<choice>カードでのdownload.cgiの呼び方については済みません、見様見真似です。
<choice key="url" title="着メロサイトDEMO" method="alpha">で<ce>で選択したものを"url"というキーで表しますよ、と宣言して、
<action type="accept" task="gosub" dest="device:data/dnld?url=$url" label="OK">でそのurlを使って実際にする作業を指定しているのかなぁと漠然と思うだけです…。
<ce value="download.cgi&name=melody/E_beyer60.mmf&size=738&disposition=devmfan&title=beyer60">♪バイエル60番
ここでは本当にcgiへのパスと引数の指定だけしかしてませんし…。HDMLいずれちゃんと調べたいと思ってます。downloa.cgiの中身も読めてません、どなたか解説して下さると嬉しいのですが…。
sub page4{
$axcess_page="P";
# PC用ソースを作成する
$out="content-type:text/html\n\n";
$out=$out."<html><head><title>$title</title><meta http-equiv=\"content-type\" content=\"text/html;charset=SHIFT_JIS\"></head>";
$out=$out."<bodey style=\"line-height:150%;margin:1.5cm\">\n".$indata[0];
if($dnno>=0){$out=$out."<table>";
for($i=0;$i<$dnno;$i++){$out=$out."<tr><td>$label[$i]</td><td><a href=\"".$path[$i]."I_$filename[$i].mld\">[i-mode用]</a></td><td><a href=\"".$path[$i]."J_$filename[$i].mmf\">[J-sky・EzWeb用]</a></td></tr>\n";}
$out=$out."</table>"
}
$out=$out."<hr><font size=\"+3\">$message</font>ここは携帯用サイトです。i-mode、J-sky、EzWeb(Wap2.0)、EzWeb(HDML)の4種類の端末に対して
着メロダウンロードページを表\示できるツール、<a href=\"http://www3.kcn.ne.jp/~tomate/o_gate/scm4.html\">scm4.cgi</a>を利用しています。<hr>";
$out=$out."</body></html>";
return $out;
}
PC用のページ出力。$axcess_pageはアクセスログに残すページ種別です。
$outは実際にクライアントに送信されるデータの全てです。
PCでアクセスされた時にPCの画面に何を表示させるかをここで指定しているのです。
このソースの場合は、PCでのアクセスを許可し、更に着メロダウンロードページを呼ばれた時にはi-mode用とJ-sky用&EzWeb用の着メロをちゃんとPC上に
ダウンロードできるようにしています。
そうしたくない場合は、
$out="content-type:text/html\n\n";
$out=$out."<html><head><title>$title</title><meta http-equiv=\"content-type\" content=\"text/html;charset=SHIFT_JIS\"></head>";
$out=$out."<bodey style=\"line-height:150%;margin:1.5cm\">\n"
$out=$out."恐れ入りますが、携帯電話でのアクセスをお願いいたします。\n";
$out=$out."<hr><font size=\"+3\">$message</font>ここは携帯用サイトです。i-mode、J-sky、EzWeb(Wap2.0)、EzWeb(HDML)の4種類の端末に対して
着メロダウンロードページを表\示できるツール、<a href=\"http://www3.kcn.ne.jp/~tomate/o_gate/scm4.html\">scm4.cgi</a>を利用しています。<hr>";
$out=$out."</body></html>";
return $out;
}
などと書き換えたらOKです。
$out=$out."<hr><font size=\"+3\">$message</font>ここは携帯用サイトです。i-mode、J-sky、EzWeb(Wap2.0)、EzWeb(HDML)の4種類の端末に対して
着メロダウンロードページを表\示できるツール、<a href=\"http://www3.kcn.ne.jp/~tomate/o_gate/scm4.html\">scm4.cgi</a>を利用しています。<hr>";
この部分は極力書き換えないようにお願いしたいのですが、PCアクセスを禁止した場合、PC用のサイトへの自動ジャンプを入れるのも有りですよね(笑)。
sub axcess_check{
# 日付のセット
($sec , $min , $hour, $mday, $mon, $year ,$wday) = localtime();
$today=sprintf("%04d/%02d/%02d %02d:%02d:%02d",$year + 1900, $mon + 1, $mday,$hour,$min,$sec);
# エージェントの取得
$agent1=$ENV{'HTTP_USER_AGENT'};
# ホストの取得
$host=$ENV{'REMOTE_HOST'};
if($host eq ''){$host=$ENV{'REMOTE_ADDR'};}
# アクセスログファイル入力
@axcess=();
&fileinput($axelog,'axcess');
$dmy=$axcess[0];
$dmy=~s/\n/<br>/g;
$dmy=$axcess[@axcess-1];
@dmy=split("\,",$dmy);
$dmy=$dmy[3].','.$dmy[4];
if($dmy ne "\"$agent1\",\"$host\""){
# カウントUP(5桁まで)
$count=substr($dmy[0],0,5)+1;
# アクセスログ更新
$newaxcess=sprintf("%05d",$count).",$today,($axcess_page),\"$agent1\",\"$host\"";
push(@axcess,$newaxcess);
if(@axcess>$axcess_max){$dmy=shift(@axcess);}
if ( !open (FILEOUT, ">$axelog") ){
$message="<HR>ファイル書き込みに失敗しました<HR>";
}else{
print FILEOUT join("\n",@axcess);
close(FILEOUT);
}
}
}
ここではアクセスログの入出力のサブルーチンです。『アクセスログいらな〜い!』と仰る方は、この部分削ってしまっても大丈夫ですよ!
2003.10.18
[HOME][とまて週報TOP]