File Coverage

blib/lib/Business/MaxMind/HTTPBase.pm
Criterion Covered Total %
statement 81 119 68.0
branch 17 48 35.4
condition 2 5 40.0
subroutine 12 17 70.5
pod 4 11 36.3
total 116 200 58.0


line stmt bran cond sub pod time code
1             package Business::MaxMind::HTTPBase;
2              
3 2     2   50 use 5.006; # we use the utf8 pragma now.
  2         6  
  2         91  
4             # Older perl installations should use 1.50
5              
6 2     2   10 use strict;
  2         4  
  2         74  
7              
8 2     2   10 use vars qw($VERSION $API_VERSION);
  2         5  
  2         120  
9              
10 2     2   11 use LWP::UserAgent;
  2         3  
  2         45  
11 2     2   9 use URI::Escape;
  2         3  
  2         3268  
12              
13             $VERSION = '1.54';
14             $API_VERSION = join('/','Perl',$VERSION);
15              
16             # default minfraud servers
17             my @defaultservers = ( 'minfraud.maxmind.com', 'minfraud-us-east.maxmind.com',
18             'minfraud-us-west.maxmind.com' );
19              
20             sub new {
21 2     2 1 547 my $i = 0;
22 2         6 my ($class) = shift;
23 2 50       9 if ($class eq 'Business::MaxMind::HTTPBase') {
24 0         0 die "Business::MaxMind::HTTPBase is an abstract class - use a subclass instead";
25             }
26 2         10 my $self = { @_ };
27 2         6 bless $self, $class;
28 2         6 for my $server (@defaultservers){
29 6         21 $self->{servers}->[$i] = $server;
30 6         13 $i++;
31             }
32 2 50       8 unless ($self->{wsIpaddrRefreshTimeout}) {
33 2         5 $self->{wsIpaddrRefreshTimeout} = 18000; # default of 5 hours timeout
34             }
35 2   50     19 $self->{wsIpaddrCacheFile} ||= '/tmp/maxmind.ws.cache';
36 2         34 $self->{ua} = LWP::UserAgent->new( ssl_opts => { verify_hostname => 0 } );
37 2         6549 $self->_init;
38 2         13 return $self;
39             }
40              
41             sub getServers {
42 0 0   0 0 0 return [ @{ $_[0]->{servers} || [] } ];
  0         0  
43             }
44              
45             sub setServers {
46 0     0 0 0 my ( $self, $serverarrayref ) = @_;
47 0         0 $self->{servers} = [@$serverarrayref];
48             }
49              
50             sub writeIpAddressToCache {
51 0     0 0 0 my ($self, $filename, $ipstr) = @_;
52 0         0 my $datetime = time();
53 0 0       0 open my $fh, ">$filename" or return;
54 0         0 print $fh $ipstr . "\n";
55 0         0 print $fh $datetime . "\n";
56             }
57              
58             sub readIpAddressFromCache {
59 2     2 0 5 my ($self) = @_;
60 2         3 my $ipstr;
61             my $datetime;
62 2 50       61 if (-s $self->{wsIpaddrCacheFile} ) {
63 2         90 open my $fh, $self->{wsIpaddrCacheFile};
64 2         40 $ipstr = <$fh>;
65 2         7 chomp($ipstr);
66 2         4 $datetime = <$fh>;
67 2         23 chomp($datetime);
68             }
69              
70 2 50 33     33 unless ($ipstr && (time() - $datetime <= $self->{wsIpaddrRefreshTimeout})) {
71             # refresh cached IP addresses if no IP address in file, or if refresh timeout expired
72 0 0       0 if (my $tryIpstr = $self->readIpAddressFromWeb($ipstr)) {
73 0         0 $ipstr = $tryIpstr;
74             } else {
75 0 0       0 if ($self->{debug}) {
76 0         0 print STDERR "Warning, unable to get ws_ipaddr from www.maxmind.com\n";
77             }
78             }
79             # we write to cache whether or not we were able to get $tryIpStr, since
80             # in case DNS goes down, we don't want to check app/ws_ipaddr over and over
81 0         0 $self->writeIpAddressToCache($self->{wsIpaddrCacheFile}, $ipstr);
82             }
83 2         7 return $ipstr;
84             }
85              
86             sub readIpAddressFromWeb {
87 0     0 0 0 my ($self) = @_;
88 0         0 my $request = HTTP::Request->new('GET', "http://www.maxmind.com/app/ws_ipaddr");
89 0 0       0 if ($self->{"timeout"} > 0) {
90 0         0 $self->{ua}->timeout($self->{"timeout"});
91             }
92              
93 0         0 my $response = $self->{ua}->request($request);
94 0 0       0 if ($response->is_success) {
95 0         0 my $content = $response->content;
96 0         0 chomp($content);
97 0 0       0 if ($content =~ m!^(?:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3};)*\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$!) {
98             # is comma separated string of IP addresses
99 0         0 return $content;
100             }
101             }
102             }
103              
104             sub query {
105 2     2 1 16 my ($self) = @_;
106 2         5 my $s = $self->{servers};
107 2         3 my $ipstr;
108             my $datetime;
109              
110 2 50       30 unless ($self->{useDNS}) {
111 2         26 $ipstr = $self->readIpAddressFromCache;
112             }
113 2 50       9 if ($ipstr) {
114 2         10 my @ipaddr = split(";",$ipstr);
115 2         5 for my $ip (@ipaddr) {
116 4         28 my $result = $self->querySingleServer($ip);
117 4 50       22 return $result if $result;
118             }
119             }
120 2         10 for my $server (@$s) {
121 6         19 my $result = $self->querySingleServer($server);
122 6 50       33 return $result if $result;
123             }
124 2         7 return 0;
125             }
126              
127             sub input {
128 2     2 1 438 my $self = shift;
129 2         17 my %vars = @_;
130 2         12 while (my ($k, $v) = each %vars) {
131 14 50       35 unless (exists $self->{allowed_fields}->{$k}) {
132 0         0 die "invalid input $k - perhaps misspelled field?";
133             }
134 14         36 $self->{queries}->{$k} = $self->filter_field($k, $v);
135             }
136             }
137              
138             # sub-class should override this if it needs to filter inputs
139             sub filter_field {
140 0     0 0 0 my ($self, $name, $value) = @_;
141 0         0 return $value;
142             }
143              
144             sub output {
145 1     1 1 14 my $self = shift;
146 1         3 return $self->{output};
147             }
148              
149             # if possible send the escaped string as latin1 for backward compatibility.
150             # That makes a difference for chars 128..255
151             # otherwise use utf8 encoding.
152             #
153             sub _mm_uri_escape {
154 70 50   70   142 return uri_escape($_[0]) if $] < 5.007;
155 70 100       302 return utf8::downgrade( my $t = $_[0], 1 ) ? uri_escape($_[0]) : uri_escape_utf8($_[0]) ;
156             }
157              
158             sub querySingleServer {
159 10     10 0 24 my ($self, $server) = @_;
160 10 50       64 my $url = ($self->{isSecure} ? 'https' : 'http') . '://' . $server . '/' .
161             $self->{url};
162 10         19 my $check_field = $self->{check_field};
163 10         19 my $queries = $self->{queries};
164 10         40 my $query_string = join('&', map { "$_=" . _mm_uri_escape($queries->{$_}) } keys %$queries);
  70         915  
165 10         133 $query_string .= "&clientAPI=$API_VERSION";
166 10 50       30 if ($self->{"timeout"} > 0) {
167 10         44 $self->{ua}->timeout($self->{"timeout"});
168             }
169 10         145 my $request = HTTP::Request->new('POST', $url);
170 10         20913 $request->content_type('application/x-www-form-urlencoded');
171 10         259 $request->content($query_string);
172 10 50       164 if ($self->{debug}) {
173 10         35 print STDERR "sending HTTP::Request: " . $request->as_string;
174             }
175 10         1384 my $response = $self->{ua}->request($request);
176 10 50       19463 if ($response->is_success) {
177 0         0 my $content = $response->content;
178 0         0 my @kvpair = split(';',$content);
179 0         0 my %output;
180 0         0 for my $kvp (@kvpair) {
181 0         0 my ($key, $value) = split('=',$kvp,2);
182 0         0 $output{$key} = $value;
183             }
184 0 0       0 unless (exists $output{$check_field}) {
185 0         0 return 0;
186             }
187 0         0 $self->{output} = \%output;
188 0         0 return 1;
189             } else {
190 10 50       102 if ($self->{debug}) {
191 10         41 print STDERR "Error querying $server code: " . $response->code;
192             }
193 10         503 return 0;
194             }
195             }
196              
197             1;
198             __END__