#!/usr/bin/perl

#---------------------------------------------------------------------
# twitter API を用いprofile icon および backgroundの変更を行う
#
# version  : 0.01
# created  : 2011/08/16 KOBAYASHI Jun.
# modified :
#
# memo     : icon 変更後は tweetならびに背景の変更などアクションが
#          : あるまでiconは変更されない(twitterの仕様)
#---------------------------------------------------------------------

#
# 感謝
#
# http://www.magicvox.net/ --> ほとんどソースを使用しました。
# http://nabe.blog.abk.nu/ --> base64encode ルーチンを使用しました。
#


#
# packages.
#
use strict;

# hetemlのcronに登録する場合 配置先ディレクトリを
# 追加しておく必要がある
BEGIN { unshift(@INC,"./tool"); } 

use Digest::HMAC_SHA1;
use HTTP::Request::Common;
use LWP::UserAgent;



#
# constant
#
use constant {
	REQUEST_METHOD  => 'POST',

	CONSUMER_KEY    => 'xxxxxxxxxxxxxxxxxxxxxx',
	CONSUMER_SECRET => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',

	ACCESS_TOKEN    => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
	ACCESS_SECRET   => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',

};


#
# argment check.
#
if($#ARGV < 1 or $#ARGV > 2){
	&usage('argment err.');
}

my $request_uri;
my $tile;
my $category;

# API select.
if($ARGV[0] eq '-i'){
	$request_uri = 'http://api.twitter.com/1/account/update_profile_image.xml';
	$category = 'icon';
}elsif($ARGV[0] eq '-b'){
	$request_uri = 'http://api.twitter.com/1/account/update_profile_background_image.xml';
	$category = 'background';
	if($ARGV[2] eq 'tile'){
		$tile = 'true';
	}
}else{
	&usage('parameter err.');
}

# file check.
my $img_file = $ARGV[1];
if(!-e "$img_file"){
	&usage('file not found.');
}

# size check.
my $filesize = -s "$img_file";
if($ARGV[0] eq '-i'){
	if($filesize >= 700000 or $filesize == 0){
		&usage('icon image size err.');
	}
}elsif($ARGV[0] eq '-b'){
	if($filesize >= 800000 or $filesize == 0){
		&usage('background image size err.');
	}
}


#
# oAuth params
#
my %oauth = (
    oauth_consumer_key     => CONSUMER_KEY,
    oauth_token            => ACCESS_TOKEN,
    oauth_signature_method => 'HMAC-SHA1',
    oauth_timestamp        => time,
    oauth_nonce            => &generate_nonce(),
    oauth_version          => '1.0',
);


#
# twitter extra params
#
my %param;

if($tile eq 'true'){
	%param = (
		'tile' => _encode_url ('true'),
	);
}else{
	%param =();
}


#
# oAuth signature generate
# tile を含むextra parameterは、multipart/form-dataの場合signatureに含めない
#
$oauth{oauth_signature} = get_signature (REQUEST_METHOD, $request_uri, %oauth);


#
# http request generate
#

# parameterにupするファイルを追加
$param{image} = ["$img_file"];

my $req = POST( $request_uri,
       'Authorization'  => get_oauth_header (%oauth),
       'Content-Type'   => 'form-data',
       'Content'        => [ %param ] );


#
# http request(POST)
#
my $ua = LWP::UserAgent->new
    or die 'Failed to initialize LWP::UserAgent';

my $res = $ua->request($req);


#
# result(debug)
#
# print STDERR "(debug)request string:-----------------------------------------------------------------\n\n". $req->as_string."\n";
# print STDERR "(debug)content type: ". $res->content_type."\n\n";
# print STDERR "(debug)header = ". $res->headers_as_string()."\n\n";
# print STDERR "(debug)result = ". $res->status_line."\n\n";

if(! $res->is_success) { 
	print STDERR "\nErr : $res->status_line\n"; 
}else{
	if(defined($tile)){
		print STDERR "\n change $category image : $img_file - tiled\n\n";
	}else{
		print STDERR "\n change $category image : $img_file\n\n";
	}
}

exit 0;



#-----------------------------------------------------------------------------------------
# oauth_nonce を生成
#-----------------------------------------------------------------------------------------
sub generate_nonce {
    my $str = &base64encode(pack 'C*', map { int rand 256 } 1..12 );
    # 記号は除く
    $str =~ tr!+=/!012!;
    $str;
}

#-----------------------------------------------------------------------------------------
# 送信するパラメータから oauth_signature を生成
#-----------------------------------------------------------------------------------------
sub get_signature {
    my ($method, $uri, %params) = @_;

    my $params = join '&', map {
        join '=', $_, $params{$_};
    } sort keys %params;

    my $base_string = join '&',
            $method,
            &_encode_url ($uri),
            &_encode_url ($params);
    my $key = join '&',
            CONSUMER_SECRET,
            ACCESS_SECRET;
     &base64encode(Digest::HMAC_SHA1::hmac_sha1 ($base_string, $key));
}

#-----------------------------------------------------------------------------------------
# OAuth HTTP ヘッダ(Authorize)文字列の生成する
#-----------------------------------------------------------------------------------------
sub get_oauth_header {
    my (%param) = @_;
    'OAuth '. join ', ', map {
        sprintf '%s="%s"', $_, &_encode_url($param{$_});
    } keys %param;
}

#-----------------------------------------------------------------------------------------
# POST するデータ文字列(extra params)を生成する
#-----------------------------------------------------------------------------------------
sub get_content {
    my (%param) = @_;
    join '&', map {
        sprintf '%s=%s', $_, $param{$_};
    } keys %param;
}

#-----------------------------------------------------------------------------------------
# url encode
#-----------------------------------------------------------------------------------------
sub _encode_url {
    my ($str) = @_;
    $str =~ s!([^a-zA-Z0-9_.~-])!sprintf '%%%02X',ord($1)!ge;
    $str;
}




#===============================================================================
# base64 encode
#===============================================================================
sub base64encode {
	my $str = shift;
	my $table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
	my $ret;

	# 2 : 0000_0000 1111_1100
	# 4 : 0000_0011 1111_0000
	# 6 : 0000_1111 1100_0000
	my ($i, $j, $x, $y);
	for($i=$x=0, $j=2; $i<length($str); $i++) {
		$x    = ($x<<8) + ord(substr($str,$i,1));
		$ret .= substr($table, ($x>>$j) & 0x3f, 1);

		if ($j != 6) { $j+=2; next; }
		# j==6
		$ret .= substr($table, $x & 0x3f, 1);
		$j    = 2;
	}
	if ($j != 2)    { $ret .= substr($table, ($x<<(8-$j)) & 0x3f, 1); }
	if ($j == 4)    { $ret .= '=='; }
	elsif ($j == 6) { $ret .= '=';  }

    	$ret =~ s/^\s+|\s+$//g;
	return $ret;
}


#===============================================================================
# usage
#===============================================================================
sub usage($) {

 my $msg = shift;

 print STDERR "\n",
	      "twitter API / icon,background change script.\n",
	      "usage: $0 (-i or -b) imgefile [tile] \n",
	      "\n",
	      "   -i      : profile icon change.\n",
	      "   -b      : profile background change.\n",
	      "   imgfile : (-i) GIF, JPG, or PNG image of less than 700 kilobytes in size.\n", 
	      "           :      Images with width larger than 500 pixels will be scaled down. \n",
	      "           :      Animated GIFs will be converted to a static GIF of the first frame,\n",
	      "           :      removing the animation.\n",
	      "\n",
	      "           : (-b) GIF, JPG, or PNG image of less than 800 kilobytes in size. \n",
	      "           :      Images with width larger than 2048 pixels will be forcibly scaled down\n",
	      "\n",
              "   tile    : tile is tile option use,(-b only)\n\n\n";
        
	if(defined($msg)){
		print " Err : $msg\n\n";
	}


	exit(-1);
}
