PHP、mixiのopenIDを使用して認証


WebサービスmixiopenIDを使用して認証したくていろいろ試しました。
結果もう少しでうまく感じですが参考サイトのようにうまく行きませんでした。

ソースはここを参考
http://blog.zuzara.com/2008/08/25/274/

mixiopenid.php をほとんどそのまま使いました。

証明書関係の設定はここを参考
http://d.hatena.ne.jp/botchy/20080820/1219255653

mixiの認証画面に遷移して戻ってくるのですが以下のようなエラーが出ます。
(数字)は自分のIDに対応した物が入っています。

OpenID authentication failed: No OpenID information found at https://id.mixi.jp/(数字)

finishedのこれが表示される。
die("OpenID authentication failed: " . $response->message);

switch ($action) { 
    case 'finished': 
        $consumer = new Auth_OpenID_Consumer(new Auth_OpenID_FileStore(FILE_STORE_DIR)); 
        $response = $consumer->complete(SERVER_URI_RETURNTO); 

        if ($response->status == Auth_OpenID_CANCEL) { 
            die('Verification cancelled.'); 
        } else if ($response->status == Auth_OpenID_FAILURE) { 
			fputs($fplog, "Auth_OpenID_FAILURE\n");	
            die("OpenID authentication failed: " . $response->message); 

一部編集していますが、アドレスバーは以下のようになって返って来ます。
値は取れてると思うのですが???です。

http://xxxx.com/xxx/openid/php-openid-2.1.2/mixiopenid.php?action=finished&janrain_nonce=2009-02-22T03:21:52ZcYjqAU&openid.mode=id_res&openid.claimed_id=https://id.mixi.jp/15xxx48&openid.identity=https://id.mixi.jp/1xxx9x8&openid.op_endpoint=https://mixi.jp/openid_server.pl&openid.return_to=http://xxxx.com/xxx/openid/php-openid-2.1.2/mixiopenid.php%3Faction%3Dfinished%26janrain_nonce%3D2009-02-22T03%253A21%253A52ZcYjqAU&openid.response_nonce=2

Cappuccino フレームワーク

ソフトウェアデザインの3月号で紹介されていたので試してみました。
といっても、本の通り入力してもうまくいかずチュートリアルをコピペして
動かしました。

サイト
http://cappuccino.org/

チュートリアル
http://cappuccino.org/learn/tutorials/starter-tutorial.php


ここで動作確認しました
http://anakureon.com/work/capp/

ボタンを押すとテキスト表示が変化します。

これで他と違うWebアプリが作れないかな?
cocoaも流行だし、勉強しておいて損はないと思っています。


AppController.j

/*
 * AppController.j
 *
 * Created by __Me__ on __Date__.
 * Copyright 2008 __MyCompanyName__. All rights reserved.
 */

@import <Foundation/CPObject.j>


@implementation AppController : CPObject
{
	CPTextField label;	//追加
}

- (void)applicationDidFinishLaunching:(CPNotification)aNotification
{

    var theWindow = [[CPWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessBridgeWindowMask],
        contentView = [theWindow contentView];

//    var label = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
    label = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];	//変更、変数宣言なし

    [label setStringValue:@"Hello World!"];
    [label setFont:[CPFont boldSystemFontOfSize:24.0]];

    [label sizeToFit];

    [label setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin | CPViewMinYMargin | CPViewMaxYMargin];
    [label setFrameOrigin:CGPointMake((CGRectGetWidth([contentView bounds]) - CGRectGetWidth([label frame])) / 2.0, (CGRectGetHeight([contentView bounds]) - CGRectGetHeight([label frame])) / 2.0)];

    [contentView addSubview:label];

//
var button = [[CPButton alloc] initWithFrame: CGRectMake(
                CGRectGetWidth([contentView bounds])/2.0 - 40,
                CGRectGetMaxY([label frame]) + 10,
                80, 18
             )];
                          
[button setAutoresizingMask:CPViewMinXMargin | 
                            CPViewMaxXMargin | 
                            CPViewMinYMargin | 
                            CPViewMaxYMargin];

[button setTitle:"swap"];

[button setTarget:self];
[button setAction:@selector(swap:)];                
              
[contentView addSubview:button];
//    
    
/*
    var button = [[CPButton alloc] initWithFrame: CGRectMark(
    	CGRectGetWidth([contentView bounds])/2.0 -40,
    	CGRectGetMaxY([label frame]) + 10, 80, 18)];


    [button setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin | CPViewMinYMargin | CPviewMaxYMargin];
    [button setTitle:"swap"];
	[button setTarget:self];
    [button setAction:@selector(swap:)];
    [contentView addSubview:button];
*/

    [theWindow orderFront:self];

    // Uncomment the following line to turn on the standard menu bar.
//    [CPMenu setMenuBarVisible:YES];
}

/*--- swap method追加 ---*/
- (void)swap:(id)sender
{
    if ([label stringValue] == "Hello World!")
        [label setStringValue:"Goodbye!"];
    else
        [label setStringValue:"Hello World!"];
}

@end

mixiのアカウントでWebサービスを認証したいのでOpenIDを試したのですがなかなかうまくいかない。
そこでソースデバッグの環境を構築してみました。

インストール

NetBeans6.5 と xampp のインストール

NetBeans6.5のダウンロード
ここのサポートテクノロジーPHPをダウンロードしてインストールしました。
http://www.netbeans.org/downloads/index.html

xammpのダウンロード
http://www.apachefriends.org/jp/xampp-windows.html

設定

NetBeans IDE での PHP ソースコードデバッグ
http://www.netbeans.org/kb/docs/php/debugging_ja.html

上記だけではうまく行かない。


PHPデバッグxdebugをxamppで有効にする
http://www.mapee.jp/wlh/phpxdebugxampp.html

zend_extension_ts を切り替えるために、コメントアウトとメントアウトを取りました。

;zend_extension_ts = "C:\xampp\php\zendOptimizer\lib\ZendExtensionManager.dll"

をしないとうまく動きません。

C:\xampp\apache\bin\php.ini

[Zend]
;zend_extension_ts = "C:\xampp\php\zendOptimizer\lib\ZendExtensionManager.dll"
zend_extension_manager.optimizer_ts = "C:\xampp\php\zendOptimizer\lib\Optimizer"
zend_optimizer.enable_loader = 0
zend_optimizer.optimization_level=15
;zend_optimizer.license_path =
; Local Variables:
; tab-width: 4
; End:

[XDebug]
;; Only Zend OR (!) XDebug
zend_extension_ts="C:\xampp\php\ext\php_xdebug.dll"
xdebug.remote_enable=true
xdebug.remote_host=127.0.0.1
xdebug.remote_port=9000
xdebug.remote_handler=dbgp
xdebug.profiler_enable=1
xdebug.profiler_output_dir="C:\xampp\tmp"


こんなエラーが出ます。

C:\xampp\apache\logs\error.log

PHP Fatal error:  [Zend Optimizer] Zend Optimizer 3.3.3 is incompatible with Xdebug 2.0.3 in Unknown on line 0
[Sun Feb 15 10:13:40 2009] [warn] pid file C:/xampp/apache/logs/httpd.pid overwritten -- Unclean shutdown of previous Apache run?
[Sun Feb 15 10:13:40 2009] [notice] Digest: generating secret for digest authentication ...
[Sun Feb 15 10:13:40 2009] [notice] Digest: done

php.iniを保存したらapacheを再起動します。
error.logを開き、エラーがないことを確認します。

NetBeansの操作

C:\work\openid を指定して openid プロジェクトを作成しました。

プロジェクトプロパティ、ソース c:\work\openid

プロジェクトプロパティ、実行構成
開始ファイルを
C:\work\openid\php-openid-2.1.2\mixiopenid.php
に設定しました。
このファイルがデバッグできます。

サーバ側PHP(S)を選択

デバッグ対象のPHPを実行して起動したブラウザ

NetBeansのソースデバッグ画面
$action = で停止中です

このようにNetBeansを使用してソースデバッグが出来るようになりました。
しかしOpenIDは本物のサーバ上に置かないと認証結果がもらえないみたいです。

ローカルサーバだけで動作するPHPデバッグについては便利だと思います。
NetBeansでソースをエディットするとリアルタイムでエラーを表示してくれたり
簡単に関数の実態へジャンプできるので大変便利です。

前回は添付ファイルを保存するところまでが出来ました。

今回は以下のことを実装しました。
・保存したファイル(jpg)を特定のフォルダーに保存。
・タイムスタンプ、送信元メールアドレス、ファイル名をデータベースへ保存。
・保存したファイルを表示。


コマンドラインPHPでファイルを作成すると属性が660になるので664に変更するようにソースを修正しました。

    $ret = chmod($filename2,0644); // $filename のパーミッション変更


受信したjpgファイルを縮小してサムネイルを作ります。

ここを参考にしました。
http://goodjob.boy.jp/chirashinoura/id/79.html
http://sandman.s6.xrea.com/nucleus/item-60.html

サムネイルの幅を固定するのに良いサンプルでした。
何箇所か誤記があったので直しています。

ImageCreateFromJPEG(), ImageCopyResized()を使用しています。
参考元ほとんどそのままでfunctionにしました。


resize.php

<?php

function resize($file_path, $file_path2){
	print "function resize {$file_path} {$file_path2} \n";
	// -- ?
	// 画像を読み込む。
	//$file_path = "ファイルパスまたはURL";
	$image = ImageCreateFromJPEG($file_path); //JPEGファイルを読み込む
	//$image = ImageCreateFromGIF($file_path); //GIFファイルを読み込む
	//$image = ImageCreateFromPNG($file_path); //PNGファイルを読み込む

	// -- ?
	// 画像のサイズを取得。
	$width = ImageSX($image); //横幅(ピクセル)
	$height = ImageSY($image); //縦幅(ピクセル)

	// -- ?
	// 縮小した画像のサイズを決める。
	// 例えば、幅を100ピクセルに固定したい場合は以下のとおり。
	$new_width = 240;
	$rate = $new_width / $width; //圧縮比
	$new_height = $rate * $height;

	// -- ?
	// 空の画像を作成する。
	$new_image = ImageCreateTrueColor($new_width, $new_height);

	// -- ?
	// 画像を普通にリサイズコピーする場合。
	ImageCopyResized($new_image,$image,0,0,0,0,$new_width,$new_height,$width,$height);
	// サンプリングしなおす場合。
	ImageCopyResampled($new_image,$image,0,0,0,0,$new_width,$new_height,$width,$height);

	// -- ?
	// ブラウザに出力する場合。
	//ImageJPEG($image);
	//ImageGIF($image); //環境によっては使えない
	//ImagePNG($image);
	// ファイルに保存する場合。
	ImageJPEG($new_image, $file_path2, 70); //3つ目の引数はクオリティー(0〜100)
	//ImageGIF($image, $file_path); //環境によっては使えない
	//ImagePNG($image, $file_path);

	// -- ?
	// ちゃんとメモリを解放する。※これを怠るとサーバ桝魔キるかもねーw
	imagedestroy ($image); //サムネイル用イメージIDの破棄 ※3
	imagedestroy ($new_image); //サムネイル元イメージIDの破棄 ※4
}

?>


メール受信からデータベース登録まで
完成したメール受信で起動するPHP example.php

#!/usr/local/bin/php


<?php
print "php start!!\n";


//---------------------------------------------------------------------
//標準出力をmail.txtに保存
//---------------------------------------------------------------------
$content = null;
$fp=fopen("php://stdin",'r') or die('File Open Error');
$fpw=fopen("mail.txt",'w');
$fplog=fopen("log.txt",'a');
$fplog2=fopen("log2.txt",'a');

//fputs($fplog, "log open \n");

while( !feof($fp) ){
    $content .= fgets( $fp ,1024);
}
fputs($fpw, $content);

//fputs($fplog, "mail.txt write \n");


//---------------------------------------------------------------------
//メール受信処理
//---------------------------------------------------------------------
require("resize.php");	
ini_set('include_path', '/home/xxx/www/xxx/pear/php');
require_once('ReceiptMailDecoder.class.php');

print "open mail.txt\n";

$fp = fopen("mail.txt","r");
$body = "";
while (!feof($fp)){
	$body .= fgets($fp,1024);
}

$decoder =& new ReceiptMailDecoder($body);
// To:アドレスのみを取得する

$toAddr = $decoder->getToAddr();	print "$toAddr <br>";
// To:ヘッダの値を取得する

$fromAddr = $decoder->getFromAddr();		print "$fromAddr <br>";
// from:ヘッダの値を取得する

$toString = $decoder->getDecodedHeader( 'to' );
// Subject:ヘッダの値を取得する

$subject = $decoder->getDecodedHeader( 'subject' );	print "$subject  <br>";
// text/planなメール本文を取得する

$body = mb_convert_encoding($decoder->body['text'],"eucjp-win","jis"); print "$body  <br>";
// text/htmlなメール本文を取得する

$body = mb_convert_encoding($decoder->body['html'],"eucjp-win","jis"); print "$body  <br>";
// マルチパートのデータを取得する
if ( $decoder->isMultipart() ) {
    $tempFiles = array();
    $num_of_attaches = $decoder->getNumOfAttach();
    for ( $i=0 ; $i < $num_of_attaches ; ++$i ) {
        /*
         * ファイルを一時ディレクトリ _TEMP_ATTACH_FILE_DIR_ に保存する
         * 一時ファイルには tempnam()を使用する
         * この部分は使用に合わせて変更して下せい
         */
//      $fpath = tempnam("./tmp", "todoattach_" );
//    	print "fpath=$fpath <br>\n";
//      $fpath = tempnam( _TEMP_ATTACH_FILE_DIR_, "todoattach_" );
        $mimetype = $decoder->attachments[$i]['mime_type'];
      	$filename = $decoder->attachments[$i]['file_name'];    	
    	$filename2 = "./"."$mimetype"."/"."$filename";
    	$filename3 = "./"."$mimetype"."/resized/"."$filename";
        if ( $decoder->saveAttachFile( $i, $filename2 ) ) {	//パス付きのファイル名
        	$ret = chmod($filename2,0644); // $filename のパーミッション変更
        	$created = date('Y-m-d H:i:s');
			fputs($fplog2, "$created,$fromAddr,$num_of_attaches,$filename,$mimetype,\n");

        	/*mysql insert*/
        	$cn = mysql_connect('mysql2.db.sakura.ne.jp', 'xxxx', 'xxxx');
			if (!$cn) {
    			die('Could not connect: ' . mysql_error());
			}
			mysql_select_db('anakureon');
			$sql = "insert into post_log(created,fromaddress,filename,mime) ";
			$sql = $sql."VALUES('$created','$fromAddr','$filename','$mimetype')";
//			print "sql = $sql \n";
			if (!(mysql_query($sql))) {
//				die('query err:t: ' . mysql_error());
			}
        	$file_path = $filename2;
        	$file_path2= $filename3;
        	resize($file_path,$file_path2);
        	$ret = chmod($file_path2,0644); // $file_path2 のパーミッション変更
        }
    }
}
/**/

$created = date('Y-m-d H:i:s');  
fputs($fplog, "$created,$fromAddr,$num_of_attaches,\n");

?>


保存したファイルをselect文を使用して最新50件表示します。
サムネイルを表示してサムネイルをクリックするとファイル本体を表示します。

select.php

<HTML>
<HEAD>
<META http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
<TITLE>mk18 photo</TITLE>
</HEAD>
<BODY>
<P align="center">mk18 photo</P>
<HR>
<DIV>

<?php

$cn = mysql_connect('xxx.db.sakura.ne.jp', 'xxx', 'xxx');
if (!$cn) {
    die('Could not connect: ' . mysql_error());
}
mysql_select_db('anakureon');

$sql = "SELECT * FROM  `post_log` ORDER BY  `post_log`.`id` DESC LIMIT 0 , 50";
//print "sql = $sql <BR>\n";


if (!($rs = mysql_query($sql))) {
	die('query err:t: ' . mysql_error());
}
//print '照会件数= ' . mysql_num_rows($rs) . '<BR>';

// MySQL レコード参照


//print "mysql_fetch_array <br>\n";
print "<br>\n";
print "<p>\n";
while ($item = mysql_fetch_array($rs)) {
	$filename = $item['filename'];
	$filename2= "./image/jpeg/{$filename}";
	$filename3= "./image/jpeg/resized/{$filename}";
	
	print "<a href='{$filename2}'><img src='${filename3}' alt='{$filename}' title='{$filename}' ></img></a>\n";
}
print "</p>\n";


mysql_close($cn);


//print "<P>正常終了</P>";
?>
</DIV>
<HR>
</BODY>
</HTML>

今回も先人の皆様のおかげでうまく行きました。
ありがとうございます。

受信メールでPHPの起動から添付ファイルの保存まで(さくらインターネット)


さくらインターネットレンタルサーバ環境にて、受信メールによるPHPプログラムの
起動に成功しましたので以下にメモします。

http://www.cpa-lab.com/tech/0143

を参考にsakuraエディターでテキストファイルを編集してftpでアップした。

このようにエラー発生

----- The following addresses had permanent fatal errors -----
<post@xxxx.com>
   (reason: 127)

  ----- Transcript of session follows -----
/home/xxxx/www/work/mail/example.php: not found
554 5.3.0 unknown mailer error 127

telnetコマンドラインでもエラー

%ls
example.php
%./example.php
./example.php: Command not found.


http://www.y-tti.com/blog/2007/06/mailfilter.php
ここを見て”保存設定 EUC LF(改行)としたら成功しました。

コマンドラインも成功

%ls
example.php
%./example.php
X-Powered-By: PHP/5.2.8
Content-type: text/html

example.php

#!/usr/local/bin/php

<?php

$content = null;
$fp=fopen("php://stdin",'r') or die('File Open Error');
$fpw=fopen("mail.txt",'w');

while( !feof($fp) ){
    $content .= fgets( $fp ,1024);
}
fputs($fpw,$content);

?>


実行結果 mail.txt が出力されました。

崖っぷちWEBデザイナーブログ で助かりました。
ありがとうございます。

添付ファイルの保存は、ここの class ReceiptMailDecoder を使うことにする。
http://d.hatena.ne.jp/ya--mada/20080415/1208318475

PEARのインストールが必要なのでここを見ながらインストール。
http://masha.maakikaku.jp/2008/05/gopearpear.php

Go-PEARは便利でした。
一番下にスクロールするところが判り難い。
Start Web Frontend of the PEAR Installer >> をクリックする必要がある。


サンプルをmail.txtを読み込んで処理するように修正。
デバッグしやすいように2つのPHPに分けて実行。

<?php
require_once('ReceiptMailDecoder.class.php');

$fp = fopen("mail.txt","r");
$body = "";
while (!feof($fp)){
	$body .= fgets($fp,1024);
}

$decoder =& new ReceiptMailDecoder($body);
// To:アドレスのみを取得する

$toAddr = $decoder->getToAddr();	print "$toAddr <br>";
// To:ヘッダの値を取得する

$fromAddr = $decoder->getFromAddr();		print "$fromAddr <br>";
// from:ヘッダの値を取得する

$toString = $decoder->getDecodedHeader( 'to' );
// Subject:ヘッダの値を取得する

$subject = $decoder->getDecodedHeader( 'subject' );	print "$subject  <br>";
// text/planなメール本文を取得する

$body = mb_convert_encoding($decoder->body['text'],"eucjp-win","jis"); print "$body  <br>";
// text/htmlなメール本文を取得する

$body = mb_convert_encoding($decoder->body['html'],"eucjp-win","jis"); print "$body  <br>";
// マルチパートのデータを取得する
if ( $decoder->isMultipart() ) {
    $tempFiles = array();
    $num_of_attaches = $decoder->getNumOfAttach();
    for ( $i=0 ; $i < $num_of_attaches ; ++$i ) {
        /*
         * ファイルを一時ディレクトリ _TEMP_ATTACH_FILE_DIR_ に保存する
         * 一時ファイルには tempnam()を使用する
         * この部分は使用に合わせて変更して下せい
         */
        $fpath = tempnam("./tmp", "todoattach_" );
    	print "fpath=$fpath <br>\n";
//        $fpath = tempnam( _TEMP_ATTACH_FILE_DIR_, "todoattach_" );
        if ( $decoder->saveAttachFile( $i, $fpath ) ) {
            $tempFiles["$fpath"] = $decoder->attachments[$i]['mime_type'];
        }
    }
}
?>


class ReceiptMailDecoder のサンプルは、テンポラリーのファイル名を使うようになっているので
受信メールよりファイル名を取得してそのファイル名で保存するように修正した。

$filename = $this->attachments[$index]['file_name'];
でファイル名が取得できる。

    function saveAttachFile ( $index, $str_path ) {
	
	if ( !file_exists($str_path) ) {
	    if ( !is_writable(dirname($str_path)) ) {
		return false;
	    }
	}
	else {
	    if ( !is_writable($str_path) ) {
		return false;
	    }
	}
	
	if ( !isset($this->attachments[$index]) ) {
	    return false;
	}
	
	$filename = $this->attachments[$index]['file_name'];    	
    print "filename = $filename <br>\n";
//	if ( $fp=fopen($str_path, "wb") ) {
	if ( $fp=fopen($filename, "wb") ) {		
	    fwrite($fp, $this->attachments[$index]['binary'] );
	    fclose($fp);
	    return true;
	}

    	
	return false;
    }


ここまでで、受信メールでPHPの起動から添付ファイルの保存までのプログラムが出来た。
私のアプリケーションの場合受信ファイル名が重要なのでこのあたりの調整が必要。


次に2つのPHPをくっつけたらうまく動かない。

2つのPHPをくっつけて実行したらうまくいかない。
telnetにて

%
%php -i >php.htm
%

を実行してパスを確認したら

Loaded Configuration File が /usr/local/php-5.2.8/lib/php.ini

になっていた。

ソースにini_setを追加してうまくいきました。

ini_set('include_path', '/home/xxxx/www/work/pear/php');
require_once('ReceiptMailDecoder.class.php');


先人たちの記事にてここまでくることが出来ました。
感謝します。


作成した、受信メールで起動するPHPのソース

#!/usr/local/bin/php

<?php
print "php start!!\n";

/*標準出力をmail.txtに保存*/
$content = null;
$fp=fopen("php://stdin",'r') or die('File Open Error');
$fpw=fopen("mail.txt",'w');
$fplog=fopen("log.txt",'a');

//fputs($fplog, "log open \n");

while( !feof($fp) ){
    $content .= fgets( $fp ,1024);
}
fputs($fpw, $content);

//fputs($fplog, "mail.txt write \n");


/**/
ini_set('include_path', '/home/anakureon/www/work/pear/php');
require_once('ReceiptMailDecoder.class.php');

print "open mail.txt\n";

$fp = fopen("mail.txt","r");
$body = "";
while (!feof($fp)){
	$body .= fgets($fp,1024);
}

$decoder =& new ReceiptMailDecoder($body);
// To:アドレスのみを取得する

$toAddr = $decoder->getToAddr();	print "$toAddr <br>";
// To:ヘッダの値を取得する

$fromAddr = $decoder->getFromAddr();		print "$fromAddr <br>";
// from:ヘッダの値を取得する

$toString = $decoder->getDecodedHeader( 'to' );
// Subject:ヘッダの値を取得する

$subject = $decoder->getDecodedHeader( 'subject' );	print "$subject  <br>";
// text/planなメール本文を取得する

$body = mb_convert_encoding($decoder->body['text'],"eucjp-win","jis"); print "$body  <br>";
// text/htmlなメール本文を取得する

$body = mb_convert_encoding($decoder->body['html'],"eucjp-win","jis"); print "$body  <br>";
// マルチパートのデータを取得する
if ( $decoder->isMultipart() ) {
    $tempFiles = array();
    $num_of_attaches = $decoder->getNumOfAttach();
    for ( $i=0 ; $i < $num_of_attaches ; ++$i ) {
        /*
         * ファイルを一時ディレクトリ _TEMP_ATTACH_FILE_DIR_ に保存する
         * 一時ファイルには tempnam()を使用する
         * この部分は使用に合わせて変更して下せい
         */
        $fpath = tempnam("./tmp", "todoattach_" );
    	print "fpath=$fpath <br>\n";
//        $fpath = tempnam( _TEMP_ATTACH_FILE_DIR_, "todoattach_" );
        if ( $decoder->saveAttachFile( $i, $fpath ) ) {
            $tempFiles["$fpath"] = $decoder->attachments[$i]['mime_type'];
        }
    }
}
/**/

$created = date('Y-m-d H:i:s');  
fputs($fplog, "$created,$fromAddr,\n");

?>

7月28日〜31日、青春18きっぷの旅

にこにこ動画にアップしたものを貼り付けます。


001

002

003


004


005

006

007

008

009

010

011

012

013

014

015

016

017

018

019

020

021

022
欠番

023

024

025

026

027

028

029

030

031

032

033

034

035

036

037