File Coverage

blib/lib/Business/PhoneBill/Allopass.pm
Criterion Covered Total %
statement 25 206 12.1
branch 2 104 1.9
condition 3 31 9.6
subroutine 6 18 33.3
pod 7 7 100.0
total 43 366 11.7


line stmt bran cond sub pod time code
1             package Business::PhoneBill::Allopass;
2              
3 1     1   10135 use vars qw/$VERSION/;
  1         3  
  1         74  
4             $VERSION = "1.09";
5              
6             =head1 NAME
7              
8             Business::PhoneBill::Allopass - A class for micro-payment system from Allopass
9              
10             =head1 SYNOPSIS
11              
12             use Business::PhoneBill::Allopass;
13            
14             my $allopass=Business::PhoneBill::Allopass->new($session_file, [$ttl]);
15             die "Cann't create class: ".$allopass unless ref $allopass;
16            
17             # Check access
18             if ($allopass->check($document_id, [$RECALL])){
19             print "OK\n";
20             } else {
21             print $allopass->get_last_error;
22             }
23            
24             # No further access for this user
25             $allopass->end_session($document_id);
26            
27             =head1 DESCRIPTION
28              
29             This class provides you an easy api to the allopass.com system. It automatically handles user sessions.
30              
31             =head1 SEE ALSO
32              
33             Please consider using Business::PhoneBill::Allopass::Simple if you don't need session management.
34              
35             See I for more informations on their system and how it basically works.
36              
37             =cut
38              
39 1     1   6 use strict;
  1         2  
  1         37  
40 1     1   1215 use HTTP::Request::Common qw(POST);
  1         53728  
  1         99  
41 1     1   1175 use LWP::UserAgent;
  1         30866  
  1         39  
42 1     1   1266 use CGI::Cookie;
  1         10827  
  1         3340  
43              
44             my $baseurl = 'http://www.allopass.com/check/vf.php4';
45              
46             =head1 METHODS
47              
48             =over 4
49              
50             =item B Class constructor. Provides session-based access check.
51              
52             $allopass=Billing::Allopass->new($session_file, [$ttl]);
53              
54             $session_file is the physical location for the session file. The webserver must have write access to it.
55             If not, this constructor returns a text error message.
56              
57             $ttl is the number of minutes of inactivity for automatically removing sessions. Default : 60.
58              
59             You have to test if the returned value is a reference.
60              
61             =cut
62              
63             sub new {
64 1     1 1 21 my $class = shift;
65 1   50     6 my $ses_file= shift || return "You must provide writable session file name";
66 1 50       40 if (!-e $ses_file) {
67 1 50       17531 open TEMP, ">$ses_file" or return "Cann't create session file: ".$!; close TEMP;
  1         39  
68             }
69 1   50     13 my $ttl=shift || 60;
70            
71 1         4 my %arg = @_;
72 1   50     19 my $self = {
73             os => 0,
74             ttl => $ttl,
75             ses_file => $ses_file,
76             error => '',
77             hhttp => $arg{hhttp} || '',
78             code => '',
79             };
80 1         7 $self = bless $self, $class;
81 1         8 $self;
82             }
83              
84             =item B - Checks if a client have access to this document
85            
86             $ok=$allopass->check($document_id, [$RECALL]);
87              
88             The RECALL parameter is provided by Allopass.com when it redirects the visitor to your website, after the secret code verification.
89             Returns 1 if authorization is successfull.
90             Next accesses to the script will be protected by the session-based system, and you no longer need to provide the $RECALL argument..
91            
92             =cut
93              
94             sub check {
95 0     0 1   my $self=shift;
96 0   0       my $doc_id=shift || return 0;
97 0   0       my $code = shift || '';
98 0           my ($res, $ua, $req);
99 0 0         if ($self->_is_session($doc_id)) {
    0          
100 0           return 1;
101             } elsif ($code) {
102 0           $ua = LWP::UserAgent->new;
103 0           $ua->agent('Business::PhoneBill::Allopass/'.$VERSION);
104 0           $req = POST $baseurl,
105             [
106             'CODE' => $code ,
107             'AUTH' => $doc_id ,
108             ];
109 0           $res = $ua->simple_request($req)->as_string;
110 0 0         if($self->_is_res_ok($res)) {
111 0           $self->_add_session($doc_id, $code);
112 0           $self->_set_error('Allopass Recall OK');
113 0           return 1;
114             }
115             }
116 0           0;
117             }
118              
119             =item B - Ends user session for specified document.
120              
121             $allopass->end_session($document_id);
122              
123             =cut
124              
125             sub end_session {
126 0     0 1   shift->_end_session(@_);
127             }
128              
129             =item B - Returns last recorded error
130              
131             $allopass->get_last_error();
132              
133             =cut
134              
135             sub get_last_error {
136 0     0 1   shift->{error};
137             }
138              
139             =item B - Checks if a client have access to this document
140            
141             $ok=$allopass->check_code($document_id, $code, [$datas], [$ap_ca]);
142              
143             =cut
144              
145             sub check_code {
146 0     0 1   my $self=shift;
147 0           my ($docid, $code, $datas, $ap_ca) = @_;
148 0 0         if ($self->_is_session($docid)) {
    0          
149 0           return 1;
150             } elsif ($code) {
151 0           my ($site_id, $doc_id, $r)=split(/\//, $docid);
152 0           my ($res, $ua, $req);
153 0           my $baseurl = 'http://www.allopass.com/check/index.php4';
154 0           $ua = LWP::UserAgent->new;
155 0           $ua->agent('Business::PhoneBill::Allopass/'.$VERSION);
156 0           $req = POST $baseurl,
157             [
158             'SITE_ID' => $site_id ,
159             'DOC_ID' => $doc_id ,
160             'CODE0' => $code ,
161             'DATAS' => $datas ,
162             'AP_CA' => $ap_ca
163             ];
164 0           $res = $ua->simple_request($req)->as_string;
165 0 0         if ($res=~/Set-Cookie: AP_CHECK/) {
166 0           $self->_set_error('Allopass Check Code OK');
167 0           my $r = $self->_add_session($docid, $code);
168 0 0         if ($r) {
169 0           $self->_set_error($r);
170 0           return 0;
171             }
172 0           return 1;
173             }
174             }
175 0           0;
176             }
177              
178             =back
179              
180             =head1 PROPERTIES
181              
182             =over 4
183              
184             =item B - Session time to live property.
185              
186             $ttl=$allopass->ttl([$ttl]);
187              
188             Session expiration time, in minutes.
189              
190             =cut
191              
192             sub ttl {
193 0     0 1   my $self=shift;
194 0           my $val =shift;
195 0 0         $self->{ttl}=$val if $val;
196 0           $self->{ttl};
197             }
198              
199             =item B - Operating system property.
200              
201             $allopass->os(1);
202            
203             You need to set it to 1 only if your OS doesn't support flock (windoze ???).
204              
205             =cut
206              
207             sub os {
208 0     0 1   my $self=shift;
209 0           my $val =shift;
210 0 0         $self->{os}=1 if $val;
211 0           $self->{os};
212             }
213              
214             ### PRIVATE FUNCTIONS ==========================================================
215             sub _is_session {
216 0     0     my $self = shift;
217 0           my $doc_id = shift;
218              
219 0           my $ok=0;
220 0           my %cookies = fetch CGI::Cookie;
221 0           my $docid=$doc_id; $docid=~s/\//\./g;
  0            
222              
223 0 0         if (!$doc_id) {
224 0           $self->_set_error("No Document ID");
225 0           return 0
226             }
227 0 0         if (!$cookies{$docid}){
228 0           $self->_set_error("No Session Cookie");
229 0           return 0
230             }
231 0 0         return 0 if !ref $cookies{$docid};
232 0 0         return 0 if !defined $cookies{$docid}->value;
233            
234 0   0       my $code = $cookies{$docid}->value || $self->{code};
235            
236 0           my $a=time;
237 0           $self->_set_error("Error opening ".$self->{ses_file}." for read");
238 0 0         open (TEMP, $self->{ses_file}) or return 0;
239 0 0         if ($self->{os} == 0) {flock (TEMP, 2);}
  0            
240 0           my @index = ;
241 0 0         if ($self->{os} == 0) {flock (TEMP, 8);}
  0            
242 0           close (TEMP);
243 0           $self->_set_error("Error opening ".$self->{ses_file}." for write");
244 0 0         open (OUTPUT, ">".$self->{ses_file}) or return 0;
245 0           $self->_set_error('No session match found');
246 0 0         if ($self->{os} == 0) {flock (TEMP, 2);}
  0            
247 0           for (my $i = 0; $i < @index; $i++) {
248 0           chomp $index[$i];
249 0 0         next unless ($index[$i]);
250 0           my ($docid, $pass, $IP, $heure, @autres) = split (/\|/, $index[$i]);
251 0 0         next if ($a > ($heure + $self->{ttl} * 60));
252 0 0 0       if ($doc_id eq $docid && $code eq $pass){
253 0           print OUTPUT "$docid|$pass|$IP|" . $a . "||\n";
254 0           $self->_set_error('Session found');
255 0           $ok=1;
256             } else {
257 0           print OUTPUT "$docid|$pass|$IP|$heure||\n";
258             }
259             }
260 0 0         if ($self->{os} == 0) {flock (TEMP, 8);}
  0            
261 0           close (OUTPUT);
262 0           $ok;
263             }
264             sub _add_session {
265 0     0     my $self = shift;
266 0           my $doc_id = shift;
267 0           my $code = shift;
268 0           $self->{code}=$code;
269 0           foreach($doc_id, $code){
270 0           s/[\r\n]//g;
271 0           s/\|/|/g;
272             }
273 0           my $a=time;
274 0 0         open (TEMP, $self->{ses_file}) or return("Error opening ".$self->{ses_file}." for read : ".$!);
275 0 0         if ($self->{os} == 0) {flock (TEMP, 2);}
  0            
276 0           my @index = ;
277 0 0         if ($self->{os} == 0) {flock (TEMP, 8);}
  0            
278 0           close (TEMP);
279 0 0         open (OUTPUT, ">".$self->{ses_file}) or return("Error opening ".$self->{ses_file}." for write : ".$!);
280 0 0         if ($self->{os} == 0) {flock (OUTPUT, 2);}
  0            
281 0           for (my $i = 0; $i < @index; $i++) {
282 0           chomp $index[$i];
283 0 0         next unless ($index[$i]);
284 0           my ($docid, $pass, $IP, $heure, @autres) = split (/\|/, $index[$i]);
285 0 0         next if ($a > ($heure + $self->{ttl} * 60));
286 0 0 0       next if $docid eq $doc_id && $pass eq $code;
287 0           print OUTPUT "$docid|$pass|$IP|$heure||\n";
288             }
289 0           print OUTPUT "$doc_id|$code|$ENV{REMOTE_ADDR}|" . $a . "||\n";
290 0 0         if ($self->{os} == 0) {flock (OUTPUT, 8);}
  0            
291 0           close (OUTPUT);
292 0           $doc_id=~s/\//\./g;
293 0           my $cookie = new CGI::Cookie(-name=>$doc_id, -value=> $code );
294 0 0         if (ref $self->{hhttp}) {
295 0           $self->{hhttp}->add_cookie("Set-Cookie: ".$cookie->as_string);
296             } else {
297 0           print "Set-Cookie: ",$cookie->as_string,"\n";
298             }
299 0           0;
300             }
301             sub _end_session {
302 0     0     my $self=shift;
303 0           my $doc_id = shift;
304            
305 0           my %cookies = fetch CGI::Cookie;
306 0           my $docid=$doc_id; $docid=~s/\//\./g;
  0            
307              
308 0           my $code = $self->{code};
309 0 0         unless ($code) {
310 0 0         return("Unable to remove session : Undefined sid") if !ref $cookies{$docid};
311 0 0         return("Unable to remove session : Undefined sid") if !defined $cookies{$docid};
312 0 0         return("Unable to remove session : Undefined sid") if !defined $cookies{$docid}->value;
313 0 0         $code = $cookies{$docid}->value if defined $cookies{$docid}->value;
314             }
315            
316             # warn "Code :".$code;
317            
318 0           my $a=time;
319 0 0         open (TEMP, $self->{ses_file}) or return("Error opening ".$self->{ses_file}." for read : ".$!);
320 0 0         if ($self->{os} == 0) {flock (TEMP, 2);}
  0            
321 0           my @index = ;
322 0 0         if ($self->{os} == 0) {flock (TEMP, 8);}
  0            
323 0           close (TEMP);
324 0 0         open (OUTPUT, ">".$self->{ses_file}) or return("Error opening ".$self->{ses_file}." for write : ".$!);
325 0 0         if ($self->{os} == 0) {flock (TEMP, 2);}
  0            
326 0           for (my $i = 0; $i < @index; $i++) {
327 0           chomp $index[$i];
328 0 0         next unless ($index[$i]);
329 0           my ($ldocid, $pass, $IP, $heure, @autres) = split (/\|/, $index[$i]);
330 0 0         next if ($a > ($heure + $self->{ttl} * 60));
331 0 0         next if $pass eq $code;
332 0           print OUTPUT "$docid|$pass|$IP|$heure|$code|\n";
333             }
334 0 0         if ($self->{os} == 0) {flock (TEMP, 8);}
  0            
335 0           close (OUTPUT);
336 0           $doc_id=~s/\//\./g;
337 0           my $cookie = new CGI::Cookie(-name=>$docid, -value=> '-' );
338 0 0         if (ref $self->{hhttp}) {
339 0           $self->{hhttp}->add_cookie("Set-Cookie: ".$cookie->as_string);
340             } else {
341 0           print "Set-Cookie: ",$cookie->as_string,"\n";
342             }
343 0           0;
344             }
345             sub _is_res_ok {
346 0     0     my $self=shift;
347 0           my $res=shift;
348 0           my($h, $c, $a)=split(/\n\n/, $res); chomp $c;
  0            
349 0 0 0       if($res && $res!~/NOK/ && $res!~/ERR/ && $res!~/error/i && $c=~/OK/) {
      0        
      0        
      0        
350 0           $self->_set_error('Allopass Recall OK');
351 0           return 1;
352             }
353 0 0         if ($c =~/NOK/) {
    0          
354 0           $self->_set_error("Allopass.com says : This code is invalid")
355             } elsif ($c =~/ERR/) {
356 0           $self->_set_error("Allopass.com says : Invalid document id")
357             } else {
358 0           $res=~s/[\r\n]/ /g;
359 0           $self->_set_error("Invalid Allopass.com response code : $res")
360             }
361 0           0;
362             }
363             sub _get_new_uid {
364 0     0     my $id;
365 0           $id=crypt(rand(99999999999),'fi');
366 0           $id=crypt(rand(99999999999),'l2').$id;
367 0           $id=crypt(rand(99999999999),'la').$id;
368 0           $id=~s/[\|\/\\]/\-/g;
369 0           $id;
370             }
371             sub _set_error {
372 0     0     my $self=shift;
373 0           $self->{error}=shift;
374             }
375              
376             =back
377              
378             =head1 Other documentation
379              
380             Jetez un oeil sur I pour la documentation en français.
381              
382              
383             =head1 AUTHOR
384              
385             Bernard Nauwelaerts
386              
387             =head1 LICENSE
388              
389             GPL. Enjoy! See COPYING for further informations on the GPL.
390              
391             =cut
392              
393             1;