2012年3月10日土曜日

https proxy 経由で Net::Google::Calender を使う

Aipo と Google カレンダー連携」で,perl スクリプトから python スクリプトを呼ぶという汚い実装をしていたのだけど,perl にも Net::Google::Calender があって,perl だけで完結して Google カレンダーを操作できそうなので,やってみたらハマった(汗

まず cygwin に Net::Google::Calendar 入れようとしたらうまくいかない.ここを見ながら無理やりインストール.

次に,Aipo を運用している某所はプロキシサーバ経由で http / https アクセスしなければならないので,まずここを見ながら,$ENV{HTTP_PROXY} $ENV{HTTPS_PROXY} を設定してみたら,「500 SSL negotiation failed:」とかで失敗した.
そこで,Net::Google::Calender が内部で使用している LWP の,proxy 経由の https 方法を検索して見つけたのがここ.LWP は proxy 経由の httpsをうまく扱えないので,ちょっと変わった設定をする必要がある.

ここで気を付けなければならないのは,$ENV{HTTPS_PROXY} = ... をした後 $ua->env_proxy; を呼んでしまうと,LWP が proxy 経由の https を処理しようとして (? ココらへんは仕組みがよくわからない),結局うまく動かない.
ここらへんのコードだと $ENV{HTTPS_PROXY} 設定後に $ua->env_proxy; を呼んでいるのだが,うちでは $ua->env_proxy; を外さないとダメだった.

で,そのように設定して Net::Google::Calender を動かしてみたら,
「500 Server closed connection without sending any data back at /usr/lib/perl5/site_perl/5.10/Net/Google/Calendar.pm line 619.」
とかで失敗.

んーと思って,Net::Google::Calender の new メソッドを見てみたら,
sub new {
    my ($class, %opts) = @_;
    $opts{_ua}   = LWP::UserAgent->new( max_redirect => 0 );
    $opts{_ua}->env_proxy; ←★★ここ★★
    $opts{_auth} = Net::Google::AuthSub->new( service => 'cl' );
    $opts{_cookie_jar} = HTTP::Cookies->new;
    $opts{no_event_modification} ||= 0;
    my $self = bless \%opts, $class;
    $self->_find_calendar_id if $opts{url};
    return $self;
}
変なところに env_proxy; が入ってる(;´д⊂)
という訳で,結局 Calender.pm の上記の行をコメントアウトしたら,うまく動いたヽ(´ー`)ノ

という訳で以下コード.
Net::Google::Calendar 部分のコードや設定はこちらを参考にさせていただいた.
以下のコードを utf-8 で保存して $UserName, $Password, $ProxyURL の設定を各自の設定に書き換えれば動くはず.
#!/usr/bin/perl -w

use encoding 'utf-8';
use Encode qw(from_to);
use DateTime;
use Net::Google::Calendar;
use strict 'vars';

# 設定
my $UserName    = 'hoge.fuga@gmail.com'; # xxxx@gmail.com
my $Password    = 'password';
my $ProxyURL    = 'http://proxy.server:8080';   # proxy がいらない場合は '' にする

# stdin を全部読み込んでいったん utf-8 に変換
$_ = join( '', <> );
from_to( $_, 'jis', 'utf-8' );
@_ = split( /\s*\x0D*\x0A/, $_ );

# メールのパース

my $Summery;
my $Location;
my $Description = '';
my $DateStart;
my $DateEnd;
my $AllDay  = 0;

while( $#_ >= 0 ){
    $_ = shift( @_ );
    
    if( $_ eq '[予定]' ){
        $Summery = shift( @_ );
    }elsif( $_ eq '[日時]' ){
        $_ = shift( @_ );
        if( m#(\d+)/(\d+)/(\d+)\s+(\d+):(\d+)[^\d]+(\d+):(\d+)# ){
            # 時間指定あり
            $DateStart = DateTime->new(
                time_zone   => 'local',
                year        => $1,
                month       => $2,
                day         => $3,
                hour        => $4,
                minute      => $5,
                second      => 0
            );
            
            $DateEnd = DateTime->new(
                time_zone   => 'local',
                year        => $1,
                month       => $2,
                day         => $3,
                hour        => $6,
                minute      => $7,
                second      => 0
            );
        }elsif( m#(\d+)/(\d+)/(\d+)# ){
            # 時間指定なし
            $DateStart = DateTime->new(
                time_zone   => 'local',
                year        => $1,
                month       => $2,
                day         => $3,
                hour        => 0,
                minute      => 0,
                second      => 0
            );
            
            $DateEnd = $DateStart->add( days => 1 );
            $AllDay = 1;
        }else{
            # よくわからない時間指定,とりあえず脱出
            exit( 0 );
        }
        
    }elsif( $_ eq '[場所]' ){
        $Location = shift( @_ );
    }elsif( $_ eq '[内容]' ){
        while( $#_ >= 0 ){
            $_ = shift( @_ );
            last if( $_ =~ /^\[/ );
            
            $Description .= "\n" if( $Description ne '' );
            $Description .= $_;
        }
    }
}

# ログイン
my $Cal = Net::Google::Calendar->new();
if( $ProxyURL ){
    # proxy 設定
    $Cal->{_ua}->proxy( 'http', $ProxyURL );
    $ENV{ 'HTTPS_PROXY' } = $ProxyURL;
}
$Cal->login( $UserName, $Password );

# イベントのエントリー
my $Entry = Net::Google::Calendar::Entry->new();
$Entry->title( $Summery );
$Entry->content( $Description ) if( $Description );
$Entry->status( 'confirmed' );
$Entry->location( $Location )   if( defined( $Location ));
$Entry->transparency( 'opaque' );
$Entry->visibility( 'private' );
$Entry->when( $DateStart, $DateEnd, $AllDay );
$Cal->add_entry( $Entry );

0 件のコメント:

コメントを投稿