#!/usr/bin/perl -w
# -------------------------------------------------------------------------------------------
# Twitter の OAuth認証を通すサンプルプログラムです。
# TwitterのOAuth認証に対応する手順をまとめながら実際にやってみることにする。
# perlによるサンプルコードも作成して公開しておきますので良かったら使ってください。
#
# 1. Twitterでアカウントを取得します。
# 2. Twitterでアプリケーション登録を行い、Consumer keyとConsumer secretをメモしておきます。
#    https://dev.twitter.com/apps
# 3. CPAN Net::OAuth をサーバに入れます。
#    私の環境の場合、Class::Accessor がなかったのでこれも入れました。
# 4. OAuth-sample.cgiを開いて、Consumer key、Consumer secret、Callback urlなどを修正します。
# 5. OAuth-sample.cgiをサーバに入れて、ブラウザから呼び出します。
#    処理している内容は、以下のような感じです。
#     A) コンシューマキーとシークレットを使って、未承認リクエストトークンとシークレットを取得
#     B) 未承認リクエストトークンを使ってTwitterにリダイレクト
#     C) アプリケーションの承認(ユーザ側)
#     D) コールバック用スクリプトで、承認済みリクエストトークンとベリファイアを受け取る
#     E) リクエストトークンとベリファイアを使ってアクセストークンとシークレットを取得する
#     F) コンシューマキーとシークレットとアクセストークンとシークレットでAPIを叩く
#
# -------------------------------------------------------------------------------------------
BEGIN {                                            #
        push( @INC,'/home/minonet/lib/' );         # CPAN Net::OAuthを入れた場所です。
}                                                  #
use strict;
use Encode;
use LWP::UserAgent;
use XML::Simple qw(XMLin);
use HTTP::Date;
use Net::OAuth;
$Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0A;
use CGI;
use JSON;
use Data::Dumper;

# 初期設定
my $request_token_url    = 'https://api.twitter.com/oauth/request_token';
my $authorize_url        = 'https://api.twitter.com/oauth/authorize';
my $access_token_url     = 'https://api.twitter.com/oauth/access_token';
my $request_method       = 'POST';
my $consumer_key         = 'qSqgtlX7ioHRPehm6VHOw';                      # Consumer key
my $consumer_secret      = 'Y9dURPbnvRFBFpAk4KSXA7y6nEjkMbc76MCeOrNMFQ'; # Consumer secret
my $callback_url         = 'http://m2m.mino.net/OAuth-sample.cgi?mode=success'; # Callback url
my $request_token        = '';
my $request_token_secret = '';
my $oauth_token          = '';
my $oauth_verifier       = '';
my $access_token         ='';
my $access_token_secret  ='';
my $message              ='';
my $screen_name          ='';
my $follow_name          ='';
my $unfollow_name        ='';
my $call_api             ='';
my $BODY_MSG             ='';
my $LOG_MSG              ='';

# CGIパラメータ解析
my $q = new CGI;
my $mode             = $q->param('mode');
if ( defined($mode) ) {
    if ( $mode eq 'request') {
	&OAuth_Request;
    } elsif ( $mode eq 'success') {
        $oauth_token    = $q->param('oauth_token');
        $oauth_verifier = $q->param('oauth_verifier');
        if ( &OAuth_Success == 0 ) {
            $message        = '【動作確認中】Twitter OAuth認証サンプル Perl版';
            my ($ss,$mm,$hh,$DD,$MM,$YY,$wday,$yday,$isdst) = localtime(time);
            $MM++;$YY+=1900;
	    $message       .= " ($YY/$MM/$DD $hh:$mm:$ss) \@mino_net → http://m2m.mino.net/OAuth-sample.cgi";
            &twitter_post( $access_token,$access_token_secret,$message,'eucJP' );
            &follow_user( $access_token,$access_token_secret,'mino_net' );
        }
    } elsif ( $mode eq 'execute') {
        $access_token        = $q->param('access_token');
        $access_token_secret = $q->param('access_token_secret');
        $message             = $q->param('message');
        $screen_name         = $q->param('screen_name');
        $call_api            = $q->param('call_api');
    } elsif ( $mode eq 'source') {
        print $q->header( -Type=>'text/html', -charset=>'euc-jp');
        print $q->start_html(-title=>"Twitter OAuth認証サンプル Perl版", -BGCOLOR=>'#ffffff');
        print "<pre>\n";
        if ( open( DB,'OAuth-sample.cgi' ) ) {
            while ( <DB> ) {
                $_ =~ s/&/&amp;/g;
                $_ =~ s/</&lt;/g;
                $_ =~ s/>/&gt;/g;
                $_ =~ s/"/&quot;/g;
                $_ =~ s/ /&nbsp;/g;
                print $_;
            }
            close( DB );
        }
        print "</pre>\n";
	print $q->end_html;
        exit(0);
    }
} else { $mode = 'init'; }

print $q->header( -Type=>'text/html', -charset=>'euc-jp');
print $q->start_html(-title=>"Twitter OAuth認証サンプル Perl版", -BGCOLOR=>'#ffffff'),
    $q->center($q->h1("<a target=\"_blank\" href=\"OAuth-sample.cgi?mode=source\" title=\"Twitter OAuth認証サンプル Perl版プログラムソース\">Twitter OAuth認証サンプル Perl版</a>")),
    $q->hr;
$BODY_MSG .= "<table border=1 width=600>\n";
$BODY_MSG .= "<tr><td>名称                </td><td>値                    </td></tr>\n";
$BODY_MSG .= "<tr><td>consumer_key        </td><td>$consumer_key         </td></tr>\n";
$BODY_MSG .= "<tr><td>consumer_secret     </td><td>$consumer_secret      </td></tr>\n";
$BODY_MSG .= "<tr><td>access_token        </td><td>$access_token         </td></tr>\n";
$BODY_MSG .= "<tr><td>access_token_secret </td><td>$access_token_secret  </td></tr>\n";
$BODY_MSG .= "</table>\n";
if ( $mode eq 'success' || $mode eq 'execute' ) {
    $BODY_MSG .= "<form method=\"POST\" action=\"OAuth-sample.cgi\"> \n";
    $BODY_MSG .= "<input type=hidden name=\"mode\" value=\"execute\"> \n";
    $BODY_MSG .= "<input type=hidden name=\"access_token\" value=\"$access_token\"> \n";
    $BODY_MSG .= "<input type=hidden name=\"access_token_secret\" value=\"$access_token_secret\"> \n";
    $BODY_MSG .= "<input type=hidden name=\"call_api\" value=\"statuses/update.json\"> \n";
    $BODY_MSG .= "<textarea name=\"message\" cols=60 rows=3></textarea> \n";
    $BODY_MSG .= "<input type=submit value=\"statuses/update.json\"> \n";
    $BODY_MSG .= "</form>\n";
    $BODY_MSG .= "<table border=0 width=600><tr><td>Timeline: ";
    $BODY_MSG .= "<a href=\"OAuth-sample.cgi\?mode=execute&access_token=$access_token&access_token_secret=$access_token_secret&call_api=statuses/home_timeline.json\" title=\"statuses/home_timeline.json\">home</a>";
    $BODY_MSG .= " / <a href=\"OAuth-sample.cgi\?mode=execute&access_token=$access_token&access_token_secret=$access_token_secret&call_api=statuses/mentions_timeline.json\" title=\"statuses/mentions_timeline.json\">mentions</a>";
    $BODY_MSG .= " / <a href=\"OAuth-sample.cgi\?mode=execute&access_token=$access_token&access_token_secret=$access_token_secret&call_api=statuses/user_timeline.json\" title=\"statuses/user_timeline.json\">user</a> ";
    $BODY_MSG .= " Users: ";
    $BODY_MSG .= "<a href=\"OAuth-sample.cgi\?mode=execute&access_token=$access_token&access_token_secret=$access_token_secret&call_api=users/show.json\" title=\"users/show.json\">show</a>";
#    $BODY_MSG .= " friendships/create.json";
#    $BODY_MSG .= " friendships/destroy.json";
    $BODY_MSG .= " <a href=\"http://fcgi.mino.net/?SNS#t904b9fd\" boder=0 title=\"logout\">logout</a>\n";
    $BODY_MSG .= "</td></tr></table>";

    if ( $call_api eq 'statuses/update.json' ) {
        &twitter_post($access_token,$access_token_secret,$message,'eucJP');
    }
    elsif ( $call_api eq 'statuses/home_timeline.json' ) {
        &home_timeline($access_token,$access_token_secret,'eucJP',0);
    }
    elsif ( $call_api eq 'statuses/mentions_timeline.json' ) {
        &mentions_timeline($access_token,$access_token_secret,'eucJP',0);
    }
    elsif ( $call_api eq 'statuses/user_timeline.json' ) {
        &user_timeline($access_token,$access_token_secret,'eucJP',$screen_name,0);
    }
    elsif ( $call_api eq 'users/show.json' ) {
        &show_user($access_token,$access_token_secret,'eucJP',$screen_name);
    }
    elsif ( $call_api eq 'friendships/create.json' ) {
        &follow_user($access_token,$access_token_secret,$screen_name);
        &show_user($access_token,$access_token_secret,'eucJP',$screen_name);
    }
    elsif ( $call_api eq 'friendships/destroy.json' ) {
        &unfollow_user($access_token,$access_token_secret,$screen_name);
        &show_user($access_token,$access_token_secret,'eucJP',$screen_name);
    }
    elsif ( $call_api eq '' ) { }
    else { $BODY_MSG .= "unkorwn call api = $call_api<br>"; }
}
else {
    $BODY_MSG .= "<form method=\"POST\" action=\"OAuth-sample.cgi\"> \n";
    $BODY_MSG .= "<input type=hidden name=\"mode\" value=\"request\"> \n";
    $BODY_MSG .= "<input type=submit value=\"認証リクエスト\"> \n";
    $BODY_MSG .= "</form>\n";
    $BODY_MSG .= "・TwitterのOAuth認証の動作確認、Perlのサンプルプログラムです。<br>\n";
    $BODY_MSG .= "・認証リクエストボタンのクリックでOAuth認証を開始します。<br>\n";
    $BODY_MSG .= "・タイトルのクリックでプログラムソースが表示されます。<br>\n"; 
}
print "$BODY_MSG";
print $q->hr;
print "処理ログ:<be><pre>$LOG_MSG</pre>";
print $q->hr;
print "・<a href=\"http://fcgi.mino.net/?%A5%DF%A5%CB%A5%D6%A5%ED%A5%B0\">ミニブログ関連ツール</a>";
print "<br>Powered by <a href=\"http://fcgi.mino.net/\">Free CGI mino</a> ";
print $q->end_html;
exit(0);
# --------------------------------------------------------------------------
#  リクエストトークンの取得(この間ブラウザによる承認行為が行われる)
# --------------------------------------------------------------------------
sub OAuth_Request {

    my $request = Net::OAuth->request("request token")->new(
                 consumer_key     => $consumer_key,
                 consumer_secret  => $consumer_secret,
                 request_url      => $request_token_url,
                 request_method   => $request_method,
                 signature_method => 'HMAC-SHA1',
                 timestamp        => time,
                 nonce            => int(rand(2**31 - 999999 + 1)) + 999999,
                 callback         => $callback_url,
    );
    $request->sign;
    my $ua = LWP::UserAgent->new( agent => 'mino_net',keep_alive => 1,timeout => 10 );
    my $http_hdr = HTTP::Headers->new( 'Authorization' => $request->to_authorization_header );
    my $http_req = HTTP::Request->new( $request_method,$request_token_url,$http_hdr );
    my $res = $ua->request( $http_req );
    $LOG_MSG .= "Request $request_token_url\n";
    if ($res->is_success) {
        my $response = Net::OAuth->response('request token')->from_post_body($res->content);
        if (defined $response->token) {
            $request_token = $response->token;
            $request_token_secret = $response->token_secret;
        }
    } else {
        $LOG_MSG .= "ERROR $request_token_url : " . $res->status_line . "\n";
        return(1);
    }
    print "Location: ".$authorize_url."?oauth_token=".$request_token."\n\n";
    exit(0);
}
# --------------------------------------------------------------------------
#  アクセストークンとシークレットの取得(コールバックされる部分)
# --------------------------------------------------------------------------
sub OAuth_Success {

    $request_method = 'POST';
	my $request = Net::OAuth->request("access token")->new(
                 consumer_key     => $consumer_key,
                 consumer_secret  => $consumer_secret,
                 request_url      => $access_token_url,
                 request_method   => $request_method,
                 signature_method => 'HMAC-SHA1',
                 timestamp        => time,
                 nonce            => int(rand(2**31 - 999999 + 1)) + 999999,
                 callback         => '',
                 token            => $oauth_token,
                 verifier         => $oauth_verifier,
                 token_secret     => '',
    );
    $request->sign;

    my $ua = LWP::UserAgent->new( agent => 'mino_net',keep_alive => 1,timeout => 10 );
#    my $http_hdr = HTTP::Headers->new( 'Authorization' => $request->to_authorization_header );
#    my $http_req = HTTP::Request->new( $request_method,$request_token_url,$http_hdr );
#    my $res = $ua->request( $http_req );

    my $http_hdr = HTTP::Headers->new(
                          'Authorization' => $request->to_authorization_header,
                          'Content-type' => 'application/x-www-form-urlencoded');
    my $http_req = HTTP::Request->new( $request_method,$access_token_url,$http_hdr,$request->to_post_body );
    my $res = $ua->request( $http_req );
    $LOG_MSG .= "Request $access_token_url\n";
    if ($res->is_success) {
        my $response = Net::OAuth->response('access token')->from_post_body($res->content);
        $access_token        = $response->token;
        $access_token_secret = $response->token_secret;
    } else {
        $LOG_MSG .= "ERROR $access_token_url : " . $res->status_line . "\n";
        return(1);
    }
    return(0);
}
#------------------------------------------------------------------------------------
#  twitter_post 
#------------------------------------------------------------------------------------
sub twitter_post {
    my ( $token,$token_secret,$msg,$cd ) = @_;
    my $API_Method_url = 'https://api.twitter.com/1.1/statuses/update.json';
    if ( $token eq '' || $token_secret eq '' || $msg  eq '' ) {
        $LOG_MSG .= "ERROR  twitter_post Parameter\n";
	$LOG_MSG .= "  access_token    = $token\n";
	$LOG_MSG .= "  access_token_secret = $token_secret\n";
	$LOG_MSG .= "  message       = $msg\n";
        return(-1);
    }
    $msg =~ s/\s/ /g;
    $msg =~ s/\n|\r|\t/ /g;
    $msg =~ s/&/%26/g;
    my $sts = &ConvertStr( $msg,$cd,'utf8' );
    my $request = Net::OAuth->request("protected resource")->new(
                   consumer_key     => $consumer_key,
                   consumer_secret  => $consumer_secret,
                   request_url      => $API_Method_url,
                   request_method   => 'POST',
                   signature_method => 'HMAC-SHA1',
                   timestamp        => time,
                   nonce            => int(rand(2**31 - 999999 + 1)) + 999999,
                   token            => $token,
                   token_secret     => $token_secret,
                   extra_params     => { status => decode_utf8($sts) },
        );
    $request->sign;
    my $ua = LWP::UserAgent->new( agent => 'mino_net',keep_alive => 1,timeout => 10 );
    my $http_hdr = HTTP::Headers->new(
              'Authorization' => $request->to_authorization_header,
              'Content-type' => 'application/x-www-form-urlencoded');
    my $http_req = HTTP::Request->new('POST', $API_Method_url, $http_hdr, $request->to_post_body);
    my $res = $ua->request($http_req);
    $LOG_MSG .= "Request $API_Method_url\n";
    if ( $res->is_success ) { return 1; }
    $LOG_MSG .= "ERROR $API_Method_url : ".$res->status_line.":".$msg . "\n";
    return(-1);
}
#------------------------------------------------------------------------------------
#  home_timeline
#------------------------------------------------------------------------------------
sub home_timeline {
    my ( $token,$token_secret,$cd,$seq ) = @_;
    my $opt = '';
    if ( defined($seq) && $seq > 0 ) { $opt = "count=200&since_id=$seq"; }
    else                             { $opt = "count=20"; }
    my $API_Method_url = "https://api.twitter.com/1.1/statuses/home_timeline.json\?$opt";
    if ( $token eq '' || $token_secret eq '' ) {
        $LOG_MSG .= "ERROR home_timeline : Parameter\n";
	$LOG_MSG .= "  access_token    = $token\n";
	$LOG_MSG .= "  access_token_secret = $token_secret>\n";
        return(-1);
    }
    my $request = Net::OAuth->request("protected resource")->new(
                   consumer_key     => $consumer_key,
                   consumer_secret  => $consumer_secret,
                   request_url      => $API_Method_url,
                   request_method   => 'GET',
                   signature_method => 'HMAC-SHA1',
                   timestamp        => time,
                   nonce            => int(rand(2**31 - 999999 + 1)) + 999999,
                   token            => $token,
                   token_secret     => $token_secret,
                   extra_params     => {  },
        );
    $request->sign;
    my $ua = LWP::UserAgent->new( 'User-Agent' => 'mino_net',keep_alive => 1,timeout => 10 );
    my $res = $ua->get($request->to_url);
    $LOG_MSG .= "Request $API_Method_url\n";
    if ( ! $res->is_success ) {
        $LOG_MSG .= "ERROR $API_Method_url : ".$res->status_line . "\n";
        return( -1 );
    }
    my ( $id,$at,$uses,$usen,$text,$img ) = ( 0,'','','','','' );
    my $ref = decode_json( encode_utf8($res->content) );
    $BODY_MSG .= "<table border=1 width=600>\n";
    for my $line ( @{$ref} ) {
        $id   = $line->{id};                                   # ID
        $at   = &timeconv( $line->{created_at} );              # 投稿時刻
        $uses = $line->{user}->{screen_name};                  # screen_name
        $usen = &ConvertStr($line->{user}->{name},'utf8',$cd); # 名前
        $img  = $line->{user}->{profile_image_url};            # アイコン
        $text = &ConvertStr($line->{text},'utf8',$cd);         # メモの内容

        $BODY_MSG .= "<tr><td>";
        $BODY_MSG .= "<a href=\"OAuth-sample.cgi\?mode=execute&access_token=$access_token&access_token_secret=$access_token_secret&call_api=users/show.json&screen_name=$uses\" title=\"users/show.json\">";
        $BODY_MSG .= "<img src=\"$img\" title=\"users/show.json\" width=48 border=0>";
        $BODY_MSG .= "</a>";
        $BODY_MSG .= "</td><td>";
        $BODY_MSG .= "$text<br><font size=-1><i>";
	$BODY_MSG .= "<a href=\"https://twitter\.com/$uses/status/$id\" title=\"https://twitter\.com/$uses/status/$id へジャンプ\" target=\"_blank\">$at $id</a> ";
        $BODY_MSG .= "<a href=\"OAuth-sample.cgi\?mode=execute&access_token=$access_token&access_token_secret=$access_token_secret&call_api=users/show.json&screen_name=$uses\" title=\"users/show.json\">";
	$BODY_MSG .= "$uses/$usen</a></i></font>";
        $BODY_MSG .= "</td></tr>";
    }
    $BODY_MSG .= "</table>\n";
    return( $id );
}
#------------------------------------------------------------------------------------
#  mentions_timeline
#------------------------------------------------------------------------------------
sub mentions_timeline {
    my ( $token,$token_secret,$cd,$seq ) = @_;
    my $opt = '';
    if ( defined($seq) && $seq > 0 ) { $opt = "count=200&since_id=$seq"; }
    else                             { $opt = "count=20"; }
    my $API_Method_url = "https://api.twitter.com/1.1/statuses/mentions_timeline.json\?$opt";
    if ( $token eq '' || $token_secret eq '' ) {
        $LOG_MSG .= "ERROR mentions_timeline Parameter\n";
	$LOG_MSG .= "  access_token    = $token<br>";
	$LOG_MSG .= "  access_token_secret = $token_secret<br>";
        return(-1);
    }
    my $request = Net::OAuth->request("protected resource")->new(
                   consumer_key     => $consumer_key,
                   consumer_secret  => $consumer_secret,
                   request_url      => $API_Method_url,
                   request_method   => 'GET',
                   signature_method => 'HMAC-SHA1',
                   timestamp        => time,
                   nonce            => int(rand(2**31 - 999999 + 1)) + 999999,
                   token            => $token,
                   token_secret     => $token_secret,
                   extra_params     => {  },
        );
    $request->sign;
    my $ua = LWP::UserAgent->new( 'User-Agent' => 'mino_net',keep_alive => 1,timeout => 10 );
    my $res = $ua->get($request->to_url);
    $LOG_MSG .= "Request $API_Method_url\n";
    if ( ! $res->is_success ) {
        $LOG_MSG .= "ERROR $API_Method_url : ".$res->status_line . "\n";
        return( -1 );
    }
    my ( $id,$at,$uses,$usen,$text,$img ) = ( 0,'','','','','' );
    my $ref = decode_json( encode_utf8($res->content) );
    $BODY_MSG .= "<table border=1 width=600>\n";
    for my $line ( @{$ref} ) {
        $id   = $line->{id};                                   # ID
        $at   = &timeconv( $line->{created_at} );              # 投稿時刻
        $uses = $line->{user}->{screen_name};                  # screen_name
        $usen = &ConvertStr($line->{user}->{name},'utf8',$cd); # 名前
        $img  = $line->{user}->{profile_image_url};            # アイコン
        $text = &ConvertStr($line->{text},'utf8',$cd);         # メモの内容

        $BODY_MSG .= "<tr><td>";
        $BODY_MSG .= "<a href=\"OAuth-sample.cgi\?mode=execute&access_token=$access_token&access_token_secret=$access_token_secret&call_api=users/show.json&screen_name=$uses\" title=\"users/show.json\">";
        $BODY_MSG .= "<img src=\"$img\" title=\"users/show.json\" width=48 border=0>";
        $BODY_MSG .= "</a>";
        $BODY_MSG .= "</td><td>";
        $BODY_MSG .= "$text<br><font size=-1><i>";
	$BODY_MSG .= "<a href=\"https://twitter\.com/$uses/status/$id\" title=\"https://twitter\.com/$uses/status/$id へジャンプ\" target=\"_blank\">$at $id</a> ";
        $BODY_MSG .= "<a href=\"OAuth-sample.cgi\?mode=execute&access_token=$access_token&access_token_secret=$access_token_secret&call_api=users/show.json&screen_name=$uses\" title=\"users/show.json\">";
	$BODY_MSG .= "$uses/$usen</a></i></font>";
        $BODY_MSG .= "</td></tr>";
    }
    $BODY_MSG .= "</table>\n";
    return( $id );
}
#------------------------------------------------------------------------------------
#  user_timeline
#------------------------------------------------------------------------------------
sub user_timeline {
    my ( $token,$token_secret,$cd,$uid,$seq ) = @_;
    my $opt = '';
    if ( defined($seq) && $seq > 0 )   { $opt = "count=200&since_id=$seq"; }
    else                               { $opt = "count=20"; }
    if ( defined($uid) && $uid ne '' ) { $opt .= "&screen_name=$uid"; }
    my $API_Method_url = "https://api.twitter.com/1.1/statuses/user_timeline.json\?$opt";
    if ( $token eq '' || $token_secret eq '' ) {
        $LOG_MSG .= "ERROR user_timeline Parameter\n";
	$LOG_MSG .= "  access_token    = $token\n";
	$LOG_MSG .= "  access_token_secret = $token_secret\n";
        return(-1);
    }
    my $request = Net::OAuth->request("protected resource")->new(
                   consumer_key     => $consumer_key,
                   consumer_secret  => $consumer_secret,
                   request_url      => $API_Method_url,
                   request_method   => 'GET',
                   signature_method => 'HMAC-SHA1',
                   timestamp        => time,
                   nonce            => int(rand(2**31 - 999999 + 1)) + 999999,
                   token            => $token,
                   token_secret     => $token_secret,
                   extra_params     => {  },
        );
    $request->sign;
    my $ua = LWP::UserAgent->new( 'User-Agent' => 'mino_net',keep_alive => 1,timeout => 10 );
    my $res = $ua->get($request->to_url);
    $LOG_MSG .= "Request $API_Method_url\n";
    if ( ! $res->is_success ) {
        $LOG_MSG .= "ERROR $API_Method_url : ".$res->status_line . "\n";
        return( -1 );
    }
    my ( $id,$at,$uses,$usen,$text,$img ) = ( 0,'','','','','' );
    my $ref = decode_json( encode_utf8($res->content) );
    $BODY_MSG .= "<table border=1 width=600>\n";
    for my $line ( @{$ref} ) {
        $id   = $line->{id};                                   # ID
        $at   = &timeconv( $line->{created_at} );              # 投稿時刻
        $uses = $line->{user}->{screen_name};                  # screen_name
        $usen = &ConvertStr($line->{user}->{name},'utf8',$cd); # 名前
        $img  = $line->{user}->{profile_image_url};            # アイコン
        $text = &ConvertStr($line->{text},'utf8',$cd);         # メモの内容

        $BODY_MSG .= "<tr><td>";
        $BODY_MSG .= "<a href=\"OAuth-sample.cgi\?mode=execute&access_token=$access_token&access_token_secret=$access_token_secret&call_api=users/show.json&screen_name=$uses\" title=\"users/show.json\">";
        $BODY_MSG .= "<img src=\"$img\" title=\"users/show.json\" width=48 border=0>";
        $BODY_MSG .= "</a>";
        $BODY_MSG .= "</td><td>";
        $BODY_MSG .= "$text<br><font size=-1><i>";
	$BODY_MSG .= "<a href=\"https://twitter\.com/$uses/status/$id\" title=\"https://twitter\.com/$uses/status/$id へジャンプ\" target=\"_blank\">$at $id</a> ";
        $BODY_MSG .= "<a href=\"OAuth-sample.cgi\?mode=execute&access_token=$access_token&access_token_secret=$access_token_secret&call_api=users/show.json&screen_name=$uses\" title=\"users/show.json\">";
	$BODY_MSG .= "$uses/$usen</a></i></font>";
        $BODY_MSG .= "</td></tr>";
    }
    $BODY_MSG .= "</table>\n";
    return( $id );
}
#------------------------------------------------------------------------------------
#  show_user
#------------------------------------------------------------------------------------
sub show_user {
    my ( $token,$token_secret,$cd,$uid ) = @_;
    my $API_Method_url = "https://api.twitter.com/1.1/users/show.json?screen_name=$uid";
    if ( $token eq '' || $token_secret eq '' ) {
        $LOG_MSG .= "ERROR show_user Parameter\n";
	$LOG_MSG .= "  access_token    = $token\n";
	$LOG_MSG .= "  access_token_secret = $token_secret\n";
        return(-1);
    }
    if ( $uid eq '' ) {
        my $API_url = "https://api.twitter.com/1.1/account/settings.json";
        my $request = Net::OAuth->request("protected resource")->new(
                   consumer_key     => $consumer_key,
                   consumer_secret  => $consumer_secret,
                   request_url      => $API_url,
                   request_method   => 'GET',
                   signature_method => 'HMAC-SHA1',
                   timestamp        => time,
                   nonce            => int(rand(2**31-999999+1))+999999,
                   token            => $token,
                   token_secret     => $token_secret,
                   extra_params     => {  },
        );
        $request->sign;
        my $ua = LWP::UserAgent->new( 'User-Agent' => 'mino_net',
	                              keep_alive => 1,timeout => 10 );
        my $res = $ua->get($request->to_url);
        $LOG_MSG .= "Request $API_url\n";
        if ( ! $res->is_success ) {
            $LOG_MSG .= "ERROR $API_url : ".$res->status_line . "\n";
            return( -1 );
        }
        my $ref = decode_json( encode_utf8($res->content) );
        $API_Method_url .= $ref->{screen_name};    # screen_name
    }
    my $request = Net::OAuth->request("protected resource")->new(
                   consumer_key     => $consumer_key,
                   consumer_secret  => $consumer_secret,
                   request_url      => $API_Method_url,
                   request_method   => 'GET',
                   signature_method => 'HMAC-SHA1',
                   timestamp        => time,
                   nonce            => int(rand(2**31-999999+1))+999999,
                   token            => $token,
                   token_secret     => $token_secret,
                   extra_params     => {  },
        );
    $request->sign;
    my $ua = LWP::UserAgent->new( 'User-Agent' => 'mino_net',keep_alive => 1,timeout => 10 );
    my $res = $ua->get($request->to_url);
    $LOG_MSG .= "Request $API_Method_url\n";
    if ( ! $res->is_success ) {
        $LOG_MSG .= "ERROR $API_Method_url : ".$res->status_line . "\n";
        return( -1 );
    }
    my ( $uses,$usen,$img,$desc,$hp,$lang,$loca,$foll ) = ('','','','','','','','');
    my ( $at,$tz,$twic,$floc,$flwc,$favc ) = ('','','','','','');
    my $ref = decode_json( encode_utf8($res->content) );
    $BODY_MSG .= "<table border=0 width=600>";
    $uses = $ref->{screen_name};                          # screen_name
    $usen = &ConvertStr($ref->{name},'utf8',$cd);         # 名前
    $img  = $ref->{profile_image_url};                    # アイコン
    $desc = &ConvertStr($ref->{description},'utf8',$cd);  # 自己紹介
    $hp   = $ref->{url};                                  # HP
    $lang = $ref->{lang};                                 # 言語
    $loca = &ConvertStr($ref->{location},'utf8',$cd);     # 場所
    $at   = &timeconv( $ref->{created_at} );              # 登録日時
    $tz   = $ref->{time_zone};                            # TimeZone
    $twic = $ref->{statuses_count};                       # ツイート
    $floc = $ref->{friends_count};                        # フォロー
    $flwc = $ref->{followers_count};                      # フォロワー
    $favc = $ref->{favourites_count};                     # お気に入り
    $foll = $ref->{following};                            # フォロー状態

    if ( $hp =~ /(^http:\/\/.+)/i ) {
        $hp = "<a href=\"$1\" title=\"ホームページへジャンプ\" border=0 target=\"_blank\">$1</a>";
    } elsif ( $hp =~ /(^https:\/\/+.)/i ) {
        $hp = "<a href=\"$1\" title=\"ホームページへジャンプ\" border=0 target=\"_blank\">$1</a>";
    } else { $hp = '<br>'; }
    $BODY_MSG .= "<tr><td align=center>";
    $BODY_MSG .= "<a href=\"https://twitter\.com/$uses\" title=\"https://twitter\.com/$uses へジャンプ\" target=\"_blank\">";
    $BODY_MSG .= "<img src=\"$img\" title=\"https://twitter\.com/$uses へジャンプ\" width=48 border=0>";
    $BODY_MSG .= "</a>";
    $BODY_MSG .= "</td></tr>";
    $BODY_MSG .= "<tr><td align=center>";
    $BODY_MSG .= "<a href=\"https://twitter\.com/$uses\" title=\"https://twitter\.com/$uses へジャンプ\" target=\"_blank\">";
    $BODY_MSG .= "\@$uses / $usen</a></td></tr>";
    $BODY_MSG .= "<tr><td align=center>$lang / $loca / $at($tz)</td></tr>";
    $BODY_MSG .= "<tr><td align=center>$desc</td></tr>";
    $BODY_MSG .= "<tr><td align=center>$hp</td></tr>";
    $BODY_MSG .= "<tr><td><table border=1 width=100%>";
    $BODY_MSG .= "<tr><td align=right>ツイーツ数</td><td align=right>フォロー数</td><td align=right>フォロワー数</td><td align=right>お気に入り数</td></tr>";
    $BODY_MSG .= "<tr><td align=right>";
    $BODY_MSG .= "<a href=\"OAuth-sample.cgi\?mode=execute&access_token=$access_token&access_token_secret=$access_token_secret&call_api=statuses/user_timeline.json&screen_name=$uses\" title=\"statuses/user_timeline.json\">$twic</a></td>";
    $BODY_MSG .= "<td align=right>$floc</td>";
    $BODY_MSG .= "<td align=right>$flwc</td>";
    $BODY_MSG .= "<td align=right>$favc</td></tr>";
    $BODY_MSG .= "</table></td></tr>";
    $BODY_MSG .= "</table>";
    if ( $foll == 1 ) {
        $BODY_MSG .= "<tr><td align=center><a href=\"OAuth-sample.cgi\?mode=execute&access_token=$access_token&access_token_secret=$access_token_secret&call_api=friendships/destroy.json&screen_name=$uses\" title=\"friendships/destroy.json\">unfollow</a></td></tr>";
    } else {
        $BODY_MSG .= "<tr><td align=center><a href=\"OAuth-sample.cgi\?mode=execute&access_token=$access_token&access_token_secret=$access_token_secret&call_api=friendships/create.json&screen_name=$uses\" title=\"friendships/create.json\">follow</a></td></tr>";
    }
    $BODY_MSG .= "</table>";
    return(0);
}
#------------------------------------------------------------------------------------
#  follow_user
#------------------------------------------------------------------------------------
sub follow_user {
    my ( $token,$token_secret,$uid ) = @_;
    my $API_Method_url = "https://api.twitter.com/1.1/friendships/create.json";
    if ( $token eq '' || $token_secret eq '' || $uid eq '' ) {
        $LOG_MSG .= "ERROR follow_user Parameter\n";
	print "  access_token    = $token\n";
	print "  access_token_secret = $token_secret\n";
	print "  screen_name     = $uid\n";
        return(-1);
    }
    my $request = Net::OAuth->request("protected resource")->new(
                   consumer_key     => $consumer_key,
                   consumer_secret  => $consumer_secret,
                   request_url      => $API_Method_url,
                   request_method   => 'POST',
                   signature_method => 'HMAC-SHA1',
                   timestamp        => time,
                   nonce            => int(rand(2**31 - 999999 + 1)) + 999999,
                   token            => $token,
                   token_secret     => $token_secret,
                   extra_params     => { screen_name => $uid },
        );
    $request->sign;
    my $ua = LWP::UserAgent->new( keep_alive => 1,timeout => 10 );
    my $http_hdr = HTTP::Headers->new(
              'Content-type' => 'application/x-www-form-urlencoded',
	      'User-Agent' => 'mino_net' );
    my $http_req = HTTP::Request->new('POST', $API_Method_url, $http_hdr, $request->to_post_body);
    my $res = $ua->request($http_req);
    $LOG_MSG .= "Request $API_Method_url\n";
    if ( $res->is_success ) { return 1; }
    $LOG_MSG .= "ERROR $API_Method_url : ".$res->status_line.":".$uid . "\n";
    return(-1);
}
#------------------------------------------------------------------------------------
#  unfollow_user
#------------------------------------------------------------------------------------
sub unfollow_user {
    my ( $token,$token_secret,$uid ) = @_;
    my $API_Method_url = "https://api.twitter.com/1.1/friendships/destroy.json";
    if ( $token eq '' || $token_secret eq '' || $uid eq '' ) {
        $LOG_MSG .= "ERROR unfollow_user Parameter\n";
	print "  access_token    = $token\n";
	print "  access_token_secret = $token_secret\n";
	print "  screen_name     = $uid\n";
        return(-1);
    }
    my $request = Net::OAuth->request("protected resource")->new(
                   consumer_key     => $consumer_key,
                   consumer_secret  => $consumer_secret,
                   request_url      => $API_Method_url,
                   request_method   => 'POST',
                   signature_method => 'HMAC-SHA1',
                   timestamp        => time,
                   nonce            => int(rand(2**31 - 999999 + 1)) + 999999,
                   token            => $token,
                   token_secret     => $token_secret,
                   extra_params     => { screen_name => $uid },
        );
    $request->sign;
    my $ua = LWP::UserAgent->new( keep_alive => 1,timeout => 10 );
    my $http_hdr = HTTP::Headers->new(
              'Content-type' => 'application/x-www-form-urlencoded',
	      'User-Agent' => 'mino_net' );
    my $http_req = HTTP::Request->new('POST', $API_Method_url, $http_hdr, $request->to_post_body);
    my $res = $ua->request($http_req);
    $LOG_MSG .= "Request $API_Method_url\n";
    if ( $res->is_success ) { return 1; }
    $LOG_MSG .= "ERROR $API_Method_url : ".$res->status_line.":".$uid . "\n";
    return(-1);
}
#####################################################################################
# 
#####################################################################################
sub timeconv {
    my ( $str ) = @_;
    my $zone = 0;
    if ( $str =~ / (\+[0-9]+) / ) {
	if ( $1 eq '+0900' ) { $zone = 9; }
	$str =~ s/ \+[0-9]+ / GMT /;
    }
    my $tm = HTTP::Date::str2time($str);
    $tm = $tm - ($zone * 60 * 60);
    my ($sec, $min, $hour, $mday, $mon, $year, $wday) = localtime($tm);
    return( sprintf("%04d/%02d/%02d %02d:%02d:%02d",
            $year+1900,$mon+1,$mday,$hour, $min, $sec) );
}
#####################################################################################
# Encode文字化け対策
#####################################################################################
sub ConvertStr {
    my ( $str,$from,$to ) = @_;
    if ( $from eq 'utf8' && $to eq 'utf8' ) {
        if (utf8::is_utf8($str)) { utf8::encode($str); }
    } elsif ( $from eq 'utf8' ) {
        if (utf8::is_utf8($str)) { utf8::encode($str); }
        $str =~ s/\xEF\xBD\x9E/\xE3\x80\x9C/g;	# 〜 EFBD9E E3809C
        $str =~ s/\xEF\xBC\x8D/\xE2\x88\x92/g;	# − EFBC8D E28892
        $str =~ s/\xE2\x88\xA5/\xE2\x80\x96/g;	# ‖ E288A5 E28096
        Encode::from_to( $str,$from,$to );
    } elsif ( $to eq 'utf8' ) {
        Encode::from_to( $str,$from,$to );
        if (utf8::is_utf8($str)) { utf8::encode($str); }
        $str =~ s/\xE3\x80\x9C/\xEF\xBD\x9E/g;	# 〜 EFBD9E E3809C
        $str =~ s/\xE2\x88\x92/\xEF\xBC\x8D/g;	# − EFBC8D E28892
        $str =~ s/\xE2\x80\x96/\xE2\x88\xA5/g;	# ‖ E288A5 E28096
    } elsif ( $from ne $to ) {
        Encode::from_to( $str,$from,$to );
    }
    $str =~ s/\n|\r|\t/ /g;
    return($str);
}