File Coverage

blib/lib/Finance/Bank/Wachovia/DataObtainer/WWW.pm
Criterion Covered Total %
statement 102 173 58.9
branch 17 62 27.4
condition 0 9 0.0
subroutine 33 38 86.8
pod 0 15 0.0
total 152 297 51.1


line stmt bran cond sub pod time code
1             package Finance::Bank::Wachovia::DataObtainer::WWW;
2              
3 6     6   121645 use WWW::Mechanize;
  6         816203  
  6         220  
4 6     6   3041 use HTTP::Cookies;
  6         33855  
  6         160  
5 6     6   2653 use Finance::Bank::Wachovia::DataObtainer::WWW::Parser;
  6         945  
  6         175  
6 6     6   2305 use Finance::Bank::Wachovia::ErrorHandler;
  6         19  
  6         150  
7 6     6   31 use strict;
  6         9  
  6         91  
8 6     6   24 use warnings;
  6         10  
  6         864  
9              
10             our $VERSION = '0.2';
11             my $DEBUG = 1 if "@ARGV" =~ /--www-debug/;
12             my $CONFIRM_LOGIN = 1 if "@ARGV" =~ /--www-confirm/;
13             my @attrs;
14             our @ISA = qw/Finance::Bank::Wachovia::ErrorHandler/;
15              
16             BEGIN{
17 6     6   35 @attrs = qw(
18             customer_access_number
19             user_id
20             password
21             pin
22             code_word
23             cached_content
24             mech
25             start_url
26             logged_in
27             );
28            
29 6         18 my $x = @__SUPER__::ATTRIBUTES;
30 6         33 for( @attrs ){
31 54     104   1321 eval "sub _$_ { $x }";
  104     6   479  
  6     6   18  
  6     0   19  
  0     0   0  
  0     6   0  
  6     6   27  
  6     20   20  
  20     6   97  
  6         21  
32 54         596 $x++;
33             }
34             }
35              
36             sub new {
37 10     10 0 2719 my($class, %attrs) = @_;
38 10         21 my $self = [];
39 10         20 bless $self, $class;
40 10         29 foreach my $att ( keys %attrs ){
41 10         71 $self->$att( $attrs{$att} );
42             }
43 10         34 $self->init();
44 10         35 return $self;
45             }
46              
47             sub init {
48 6     6   36 no strict;
  6         10  
  6         402  
49 10     10 0 17 my $self = shift;
50 10 50       63 $self->start_url('https://onlineservices.wachovia.com/auth/AuthService?action=presentLogin')
51             unless $self->start_url;
52 10         20 $self->[ &{"_cached_content"} ] = {};
  10         196  
53             }
54              
55             sub AUTOLOAD {
56 6     6   34 no strict 'refs';
  6         11  
  6         3010  
57 144     144   1331 our $AUTOLOAD;
58 144         200 my $self = shift;
59 144         286 my $attr = lc $AUTOLOAD;
60 144         523 $attr =~ s/.*:://;
61 144 50       970 return $self->Error("$attr is not a valid attribute")
62             unless grep /$attr/, @attrs;
63             # get if no args passed
64 144 100       300 return $self->[ &{"_$attr"} ] unless @_;
  114         2350  
65             # set if args passed
66 30         41 $self->[ &{"_$attr"} ] = shift;
  30         625  
67 30         57 return $self;
68             }
69              
70             sub trash_cache {
71 0     0 0 0 my $self = shift;
72 0         0 $self->[ &{"_cached_content"} ] = {};
  0         0  
73             }
74              
75             sub get_account_numbers {
76 8     8 0 520 my $self = shift;
77 8         21 return Finance::Bank::Wachovia::DataObtainer::WWW::Parser
78             ->get_account_numbers( $self->get_summary_content() );
79             }
80              
81             sub get_credit_account_current_balance {
82 1     1 0 4 get_account_available_balance( @_ );
83             }
84              
85             sub get_credit_account_available_credit {
86 2     2 0 419 my $self = shift;
87 2 50       8 return $self->Error( "must pass credit account number" ) unless @_;
88 2         7 return Finance::Bank::Wachovia::DataObtainer::WWW::Parser
89             ->get_credit_account_available_credit( $self->get_detail_content( @_ ) );
90             }
91              
92             sub get_credit_account_limit {
93 2     2 0 427 my $self = shift;
94 2 50       8 return $self->Error( "must pass credit account number" ) unless @_;
95 2         6 return Finance::Bank::Wachovia::DataObtainer::WWW::Parser
96             ->get_credit_account_limit( $self->get_detail_content( @_ ) );
97              
98             }
99              
100             sub get_account_available_balance {
101 8     8 0 508 my $self = shift;
102 8 50       22 return $self->Error( "must pass account number" ) unless @_;
103 8         19 return Finance::Bank::Wachovia::DataObtainer::WWW::Parser
104             ->get_account_available_balance( $self->get_summary_content(), @_ );
105             }
106              
107             sub get_account_name {
108 9     9 0 730 my $self = shift;
109 9 50       48 return $self->Error( "must pass account number" ) unless @_;
110 9         25 return Finance::Bank::Wachovia::DataObtainer::WWW::Parser
111             ->get_account_name( $self->get_summary_content(), @_ );
112             }
113              
114             sub get_account_type {
115 4     4 0 1687 my($self) = shift;
116 4 50       826 return $self->Error( "must pass account number" ) unless @_;
117 4         16 return Finance::Bank::Wachovia::DataObtainer::WWW::Parser
118             ->get_account_type( $self->get_detail_content(@_) );
119             }
120              
121             sub get_account_posted_balance {
122 1     1 0 238 my $self = shift;
123 1 50       3 return $self->Error( "must pass account number" ) unless @_;
124 1         2 return Finance::Bank::Wachovia::DataObtainer::WWW::Parser
125             ->get_account_posted_balance( $self->get_detail_content(@_) );
126             }
127              
128             sub get_account_transactions {
129 3     3 0 247 my $self = shift;
130 3 50       10 return $self->Error( "must pass account number" ) unless @_;
131 3         10 return Finance::Bank::Wachovia::DataObtainer::WWW::Parser
132             ->get_account_transactions( $self->get_detail_content(@_) );
133             }
134              
135             sub get_summary_content {
136 6     6   42 no warnings;
  6         12  
  6         1383  
137 27     27 0 81 local $^W = 0;
138 27 50       71 print STDERR "Getting Summary Content:\n" if $DEBUG;
139 27         41 my $self = shift;
140 27 50       98 if( $self->cached_content->{'summary'} ){
141             print STDERR "Returning cached summary:\n",
142             "============== BEGIN SUMMARY =============\n",
143 27 50       60 $self->cached_content->{'summary'}, "\n",
144             "=============== END SUMMARY ==============\n" if $DEBUG;
145 27         91 return $self->cached_content->{'summary'};
146             }
147 0 0       0 if( ! $self->logged_in ){
148 0         0 $self->login;
149 0         0 return $self->get_summary_content();
150             }
151 0         0 my $mech = $self->mech();
152 0         0 $mech->form_number( 1 );
153 0         0 $mech->field( inputName => 'RelationshipSummary' );
154 0         0 $mech->submit();
155 0         0 $self->cached_content->{'summary'} = $mech->content();
156             print STDERR "Returning NOT cached summary:\n",
157             "============== BEGIN SUMMARY =============\n",
158 0 0       0 $self->cached_content->{'summary'}, "\n",
159             "=============== END SUMMARY ==============\n" if $DEBUG;
160 0         0 return $self->cached_content->{'summary'};
161             }
162              
163             sub get_detail_content {
164 6     6   38 no warnings;
  6         10  
  6         1639  
165 14     14 0 522 local $^W = 0;
166 14         30 my($self, $account_number) = @_;
167 14 50       31 return $self->Error( "get_detail_content in WWW must have account_number, got: '$account_number'" )
168             unless $account_number;
169 14 50       56 if( $self->cached_content->{'details'}{$account_number} ){
170             print STDERR "Returning cached details:\n",
171             "============ BEGIN DETAILS ===============\n",
172 14 50       32 $self->cached_content->{'details'}{$account_number}, "\n",
173             "============ END DETAILS =================\n" if $DEBUG;
174 14         49 return $self->cached_content->{'details'}->{$account_number};
175             }
176 0 0         unless( $self->cached_content->{'summary'} ){
177 0           $self->get_summary_content();
178             }
179 0 0         my $stmt_type = $account_number =~ /^\d{16}$/ ? 'AccountSummary' : 'AccountDetail';
180 0           my $mech = $self->mech();
181 0           $mech->form_number( 1 );
182 0           $mech->field( RelSumAcctSel => $account_number );
183 0           $mech->field( inputName => $stmt_type );
184 0           $mech->field( RelSumStmtType => $stmt_type );
185 0           $mech->submit();
186 0           $self->cached_content->{'details'}->{$account_number} = $mech->content();
187             # return to summary page
188 0           $mech->form_number( 1 );
189 0           $mech->field( inputName => 'RelationshipSummary' );
190 0           $mech->submit();
191             print STDERR "Returning NOT cached details:\n",
192             "============ BEGIN DETAILS ===============\n",
193 0 0         $self->cached_content->{'details'}{$account_number}, "\n",
194             "============ END DETAILS =================\n" if $DEBUG;
195 0           return $self->cached_content->{'details'}->{$account_number};
196             }
197              
198             # initilizes WWW::Mech object, uses it to get to summary page
199             # summary page is cached/overwritten
200             sub login {
201 6     6   36 no warnings;
  6         16  
  6         2848  
202 0     0 0   local $^W = 0;
203 0           my $self = shift;
204 0           my %p = @_;
205            
206 0   0       my $start = $p{'start_url'} || $self->start_url();
207 0 0         print STDERR "Starting Login (1)\n" if $DEBUG;
208              
209             # now we can get to business
210 0           my $mech = WWW::Mechanize->new(
211             autocheck => 1,
212             max_redirect => 1,
213             );
214            
215             # caches the mech object
216 0           $self->mech( $mech );
217              
218 0           $mech->cookie_jar(HTTP::Cookies->new()); # have to turn on cookies manually apparently
219 0           $mech->agent_alias( 'Mac Safari' ); # don't want the bank to know we are geniuses, (using perl)
220             # but we don't want them thinking we are dumb either (using MSIE).
221             # considering changing this to Firefox (mozilla), as a practice in trivialness
222              
223             # make first contact
224             # TODO: add in success checking
225 0           $mech->get( $start );
226 0 0         print STDERR "Login (2) Content:\n",
227             "============ BEGIN CONTENT ===============\n",
228             $mech->content(), "\n",
229             "============ END CONTENT =================\n" if $DEBUG;
230              
231             # the website uses javascript to set this cookie, so we have to do it manually.
232             # without this, an error is returned from the website about either javascript or cookies being turned off
233 0           $mech->cookie_jar->set_cookie( undef, 'CookiesAreEnabled', 'yes', '/', '.wachovia.com', undef, undef, 1 );
234             #$mech->max_redirect(1);
235             #$mech->requests_redirectable([]);
236 0 0         if( ! $self->user_id ){
237 0 0 0       print STDERR "Logging in via CAN method...\n" if $DEBUG || $CONFIRM_LOGIN;
238 0 0         print STDERR
239             "CAN => '", $self->customer_access_number, "'\n",
240             "PIN => '", $self->pin, "'\n",
241             "CODEWORD => '", $self->code_word, "'\n"
242             if $CONFIRM_LOGIN;
243 0           $mech->form_name( 'canAuthForm' );
244 0           $mech->field( action => 'canPinLogin' );
245 0           $mech->field( CAN => $self->customer_access_number );
246 0           $mech->field( PIN => $self->pin );
247 0           $mech->field( CODEWORD => $self->code_word );
248 0           $mech->field( systemtarget => 'gotoBanking' );
249 0           $mech->field( requestTimestamp => time() ); # the website uses javascript to set this value
250             }
251             else{
252 0 0 0       print STDERR "Logging in via USERID method...\n" if $DEBUG || $CONFIRM_LOGIN;
253 0 0         print STDERR
254             "userid => '", $self->user_id, "'\n",
255             "password => '", $self->password, "'\n",
256             if $CONFIRM_LOGIN;
257 0           $mech->form_name( 'uidAuthForm' );
258 0           $mech->field( action => 'uidLogin' );
259 0           $mech->field( userid => $self->user_id );
260 0           $mech->field( password => $self->password );
261 0           $mech->field( systemtarget => 'gotoBanking' );
262 0           $mech->field( requestTimestamp => time() ); # the website uses javascript to set this value
263             }
264 0           $mech->submit();
265 0 0         print STDERR "Login (3) Content:\n",
266             "============ BEGIN CONTENT ===============\n",
267             $mech->content(), "\n",
268             "============ END CONTENT =================\n" if $DEBUG;
269              
270              
271             # after the initial commit, there is what appears to be a bunch of redirects. While there are some, there are
272             # also some javascript onLoad submits. The following code emulates that behavior (just submits a form that
273             # has a bunch of hidden inputs )
274 0           $mech->form_name( 'authForm' );
275 0           $mech->submit();
276 0 0         print STDERR "Login (4) Content:\n",
277             "============ BEGIN CONTENT ===============\n",
278             $mech->content(), "\n",
279             "============ END CONTENT =================\n" if $DEBUG;
280              
281 0           $mech->form_name( 'autoposterForm' );
282 0           $mech->submit();
283 0 0         print STDERR "Login (5) Content:\n",
284             "============ BEGIN CONTENT ===============\n",
285             $mech->content(), "\n",
286             "============ END CONTENT =================\n" if $DEBUG;
287              
288            
289 0           $self->cached_content->{'summary'} = $mech->content();
290 0           $self->logged_in( 1 );
291 0           return $self;
292             }
293              
294       0     sub DESTROY {}