# ------------------------------------------------------------------------- # # Finance::SE::PPM.pm - A module for fetching account information from the swedish PPM # # $Id: PPM.pm,v 1.3 2008-08-26 10:30:35 erwan Exp $ # # Erwan Lemonnier - 2004 # # ------------------------------------------------------------------------- # TODO: parse amount not yet invested while changing fund profile package Finance::SE::PPM; die "Do NOT use this module"; # Note: Crypt::SSLeay requires: # # MIME-Base64-3.00.tar.gz # URI-1.30.tar.gz # HTML-Tagset-3.03.tar.gz # HTML-Parser-3.35.tar.gz # Crypt-SSLeay-0.51.tar.gz # libwww-perl-5.76.tar.gz # # installed crypt::ssleay with: # perl5 Makefile.PL PREFIX=/usr/home/USERNAME/usr/local # make # make test # make install # make clean use 5.006; use strict; use warnings; use Data::Dumper; use Carp qw(croak confess); use HTTP::Request::Common qw(POST GET); use HTTP::Headers; use HTTP::Cookies; use LWP::UserAgent; use Crypt::SSLeay; use HTML::TreeBuilder; use Class::XPath; require Exporter; our @ISA = qw(Exporter); our %EXPORT_TAGS = ( 'all' => [ qw() ] ); our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = qw(); our $VERSION = '0.04'; #--------------------------------------------------------------------- # # Parameters # # correspondance html form's inputs / PPM object fields my $HTML_INPUT_PPM = { "personnummer" => "pnr", "pin" => "pin", }; my $URL_LOGIN = "https://secure.ppm.nu/tpp/securelogin/3:11;x;:1:1;1101;:"; my $URL_LOGOUT = "https://secure.ppm.nu/tpp/securelogin/3:20;:1:1;1102;:"; my $SERVER = "secure.ppm.nu"; my $PROTO = "https://"; # default debug/verbose level my $DEBUG = 0; #--------------------------------------------------------------------- # # _init_xpath - add Class::XPath routines to HTML::Element # sub _init_xpath { Class::XPath->add_methods(target => 'HTML::Element', get_parent => 'parent', get_name => 'tag', get_attr_names => sub { my %attr = shift->all_external_attr; return keys %attr; }, get_attr_value => sub { my %attr = shift->all_external_attr; return $attr{$_[0]}; }, get_children => sub { grep { ref $_ } shift->content_list }, get_content => sub { grep { not ref $_ } shift->content_list }, get_root => sub { local $_=shift; while($_->parent) { $_ = $_->parent } return $_; }, ); } &_init_xpath(); ###################################################################### # # # Public methods # # ###################################################################### #--------------------------------------------------------------------- # # new Finance::PPM - create PPM object to handle 1 account # # parameters: personnummer -> pnr # pincode -> pin # debug -> int sub new { my($class,%args) = @_; my $PPM = {}; bless($PPM,$class); $PPM->{pnr} = $args{personnummer} if (exists $args{personnummer}); $PPM->{pin} = $args{pincode} if (exists $args{pincode}); if (exists $args{debug}) { croak "ERROR: debug must be a number [".$args{debug}."]" if ($args{debug} !~ /^\d+$/); $DEBUG = $args{debug}; } $PPM->{referer} = ""; # initialise PPM user agent my $ua = new LWP::UserAgent; $ua->timeout(10); $ua->agent("Mozilla/5.0"); $ua->cookie_jar(HTTP::Cookies->new(file => ".cookies.txt")); $ua->requests_redirectable(['GET','POST','HEAD']); $PPM->{useragent} = $ua; return $PPM; } #--------------------------------------------------------------------- # # getFundInfo - get info about one fund # # parameters: fundid -> PPM fund id # returns: a hash describing this fund (name, value, etc) # sub getFundInfo { croak "Not implemented yet."; } #--------------------------------------------------------------------- # # fetchAccountStatus - retrieve this person's account # sub fetchAccountStatus { my($PPM,$pnr,$pin) = @_; $PPM->_chk_pnr_pin(); $PPM->_set_current_date(); _debug(5,"content of PPM object before loading account:\n".Dumper($PPM)); $PPM->_account_login(); $PPM->_account_logout(); $PPM->_extract_fund_info(); } #--------------------------------------------------------------------- # # getAccountFunds - returns an array of hashes describing # each fund's status # sub getAccountFunds { my $PPM = shift; croak "ERROR: getAccountFundObjects without calling loadAccountStatus first" if (!exists $PPM->{account}); return @{$PPM->{account}}; } #--------------------------------------------------------------------- # # isChangingFunds() - check if funds are being changed on account # sub isChangingFunds { my $PPM = shift; croak "ERROR: getAccountFundObjects without calling loadAccountStatus first" if (!exists $PPM->{html}); return $PPM->{html}->as_string() =~ /fondhandelpagar.gif/g; } #--------------------------------------------------------------------- # # setProxy # sub setProxy { my($PPM,$proxy) = @_; $ENV{HTTPS_PROXY} = $proxy; } ###################################################################### # # # Private methods # # ###################################################################### #--------------------------------------------------------------------- # # debug - intern log function # sub _debug { my($lvl,$msg) = @_; if ($DEBUG >= $lvl) { print "PPM.PM DEBUG[$lvl]: $msg\n"; } } #--------------------------------------------------------------------- # # _chk_pnr_pin # # verify that this PPM object has received a valid pincode & personnummer # sub _chk_pnr_pin { my $PPM = shift; croak "ERROR: no personnummer specified" if (!defined $PPM->{pnr}); croak "ERROR: no pin code specified" if (!defined $PPM->{pin}); croak "ERROR: invalid personnummer [".$PPM->{pnr}."]" if ($PPM->{pnr} !~ /^\d{12}$/); croak "ERROR: invalid pin [".$PPM->{pin}."]" if ($PPM->{pin} !~ /^\d{5}$/); } #--------------------------------------------------------------------- # # _set_current_date() - set PPM object to today's date # sub _set_current_date { my $PPM = shift; my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); $PPM->{date} = sprintf("%04d-%02d-%02d", $year+1900, $mon+1, $mday); } #--------------------------------------------------------------------- # # _account_logout() # # retrieve PPM login page, login and return identification headers # sub _account_logout { my $PPM = shift; return $PPM->_http_fetch($URL_LOGOUT,'GET'); } #--------------------------------------------------------------------- # # _account_login() # # retrieve PPM login page, login and return identification headers # sub _account_login { my $PPM = shift; my $pg_login = $PPM->_http_fetch($URL_LOGIN,'GET')->as_string; _debug(5,"HTML login page:\n[$pg_login]\n"); if ($pg_login =~ /^\s*$/) { croak "ERROR: got an empty page [$URL_LOGIN]"; } # extract the