File Coverage

blib/lib/WWW/StopForumSpam.pm
Criterion Covered Total %
statement 25 27 92.5
branch n/a
condition n/a
subroutine 9 9 100.0
pod n/a
total 34 36 94.4


line stmt bran cond sub pod time code
1             package WWW::StopForumSpam;
2              
3 1     1   24210 use 5.010;
  1         3  
  1         39  
4 1     1   6 use strict;
  1         2  
  1         67  
5 1     1   12 use warnings;
  1         6  
  1         35  
6 1     1   885 use autodie;
  1         21627  
  1         6  
7 1     1   6087 use Carp qw(croak);
  1         2  
  1         79  
8 1     1   893 use URI::Escape;
  1         1419  
  1         70  
9 1     1   7 use Digest::MD5 qw(md5_hex);
  1         2  
  1         47  
10 1     1   1038 use Socket;
  1         4316  
  1         547  
11 1     1   399 use WWW::Curl::Easy;
  0            
  0            
12             use JSON qw(decode_json);
13              
14             our $VERSION = '0.02';
15              
16             sub new {
17             my $class = shift;
18             my $self = bless({}, $class);
19              
20             # parse params
21             while(@_) {
22             my $attr = shift;
23             my $value = shift;
24            
25             if($attr eq "timeout") {
26             $self->{timeout} = 0 + $value;
27             } elsif($attr eq "api_key") {
28             $self->{api_key} = "$value";
29             } elsif($attr eq "api_url") {
30             $self->{api_url} = "$value";
31             } elsif($attr eq "dnsbl") {
32             $self->{dnsbl} = "$value";
33             } elsif($attr eq "treshold") {
34             $self->{treshold} = 0 + $value;
35             }
36             }
37            
38             # validate / set defaults
39             $self->{api_url} = "http://www.stopforumspam.com/api" unless exists $self->{api_url};
40             $self->{dnsbl} = "sfs.dnsbl.st." unless exists $self->{dnsbl};
41             $self->{timeout} = 4 unless exists $self->{timeout};
42             $self->{connect_timeout} = $self->_ceil($self->{timeout} / 2);
43             $self->{treshold} = 65 unless exists $self->{treshold};
44             return $self;
45             }
46              
47             sub check {
48             my $self = shift;
49             my @request_params = ();
50            
51             while(@_) {
52             my $attr = shift;
53             my $value = shift;
54            
55             if ($attr eq "ip" or $attr eq "email" or $attr eq "username") {
56             push(@request_params, $attr . "=" . uri_escape($value));
57             }
58             }
59            
60             # add default params
61             push(@request_params, "f=json");
62            
63             my ($http_code, $buffer) = $self->_query_api(join("&", @request_params));
64            
65             # if the api is not working, we don't want to allow potential spammers
66             # signing up, so rather force the developers to check their logs...
67             if (not defined $buffer) {
68             return 1;
69             }
70            
71             my $decoded_json = decode_json($buffer);
72             if(not defined $decoded_json->{'success'}) {
73             warn "unable to read json";
74             return 1;
75             } elsif($decoded_json->{'success'} == 0) {
76             warn $decoded_json->{'error'};
77             return 1;
78             }
79            
80             if($self->_get_avg_confidence($decoded_json) > $self->{treshold}) {
81             return 1;
82             }
83            
84             return 0;
85             }
86              
87             sub dns_check {
88             my $self = shift;
89            
90             my $packed_ip;
91             my $ip_address;
92            
93             while(@_) {
94             my $attr = shift;
95             my $value = shift;
96            
97             if ($attr eq "ip") {
98             $packed_ip = gethostbyname(join('.', reverse split(/\./, $value)) . "." . $self->{dnsbl});
99             if (not defined $packed_ip) {
100             next;
101             }
102            
103             $ip_address = inet_ntoa($packed_ip);
104             if ($ip_address eq "127.0.0.2") {
105             return 1;
106             }
107            
108             } elsif ($attr eq "email") {
109             $packed_ip = gethostbyname(md5_hex($value) . "." . $self->{dnsbl});
110             if (not defined $packed_ip) {
111             next;
112             }
113            
114             $ip_address = inet_ntoa($packed_ip);
115             if ($ip_address eq "127.0.0.3") {
116             return 1;
117             }
118             }
119             }
120            
121             return 0;
122             }
123              
124             sub report {
125             my $self = shift;
126             my @request_params = ();
127            
128             if(not defined $self->{api_key}) {
129             croak "apikey required.";
130             }
131            
132             while(@_) {
133             my $attr = shift;
134             my $value = shift;
135            
136             if ($attr eq "username" or $attr eq "ip_addr" or $attr eq "evidence" or $attr eq "email") {
137             if (length($value) > 0) {
138             push(@request_params, $attr . "=" . uri_escape($value));
139             }
140             }
141             }
142            
143             # add default params
144             push(@request_params, "api_key=" . $self->{api_key});
145            
146             my ($http_code, $buffer) = $self->_query_api(join("&", @request_params), 1);
147            
148             if (not defined $buffer) {
149             return 0;
150             }
151            
152             if ($http_code == 200) {
153             return 1;
154             } else {
155             warn $self->_strip_tags($buffer);
156             return 0;
157             }
158             }
159              
160             sub _query_api {
161             my ($self, $data, $is_submit) = @_;
162            
163             if (not defined $is_submit) {
164             $is_submit = 0;
165             }
166            
167             my $buffer = "";
168             my $curl = WWW::Curl::Easy->new();
169            
170             if ($is_submit) {
171             $curl->setopt(CURLOPT_URL, "http://www.stopforumspam.com/add.php");
172             $curl->setopt(CURLOPT_POST, 1);
173             $curl->setopt(CURLOPT_POSTFIELDS, $data);
174             } else {
175             $curl->setopt(CURLOPT_URL, $self->{api_url} . "?" . $data);
176             }
177            
178             $curl->setopt(CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; WWW::StopForumSpam/0.1; +http://www.perlhipster.com/bot.html)");
179             $curl->setopt(CURLOPT_ENCODING, "");
180             $curl->setopt(CURLOPT_NOPROGRESS, 1);
181             $curl->setopt(CURLOPT_FAILONERROR, 0);
182             $curl->setopt(CURLOPT_TIMEOUT, $self->{timeout});
183             $curl->setopt(CURLOPT_WRITEFUNCTION, sub {
184             $buffer .= $_[0];
185             return length($_[0]);
186             });
187            
188             my $retcode = $curl->perform();
189            
190             if($retcode != 0) {
191             warn $curl->errbuf;
192             return;
193             }
194            
195             return ($curl->getinfo(CURLINFO_HTTP_CODE), $buffer);
196             }
197              
198             sub _get_avg_confidence {
199             my ($self, $decoded_json) = @_;
200             my $confidence_total = 0;
201             my $confidence_num = 0;
202            
203             if(defined $decoded_json->{'username'}) {
204             if (defined $decoded_json->{'username'}{'confidence'}) {
205             $confidence_total += $decoded_json->{'username'}{'confidence'};
206             }
207             $confidence_num++;
208             }
209             if(defined $decoded_json->{'email'}) {
210             if (defined $decoded_json->{'email'}{'confidence'}) {
211             $confidence_total += $decoded_json->{'email'}{'confidence'};
212             }
213             $confidence_num++;
214             }
215             if(defined $decoded_json->{'ip'}) {
216             if (defined $decoded_json->{'ip'}{'confidence'}) {
217             $confidence_total += $decoded_json->{'ip'}{'confidence'};
218             }
219             $confidence_num++;
220             }
221            
222             return $confidence_total / $confidence_num;
223             }
224              
225             sub _ceil {
226             my ($self, $num) = @_;
227             return int($num) + ($num > int($num));
228             }
229              
230             sub _strip_tags {
231             my ($self, $string) = @_;
232             while ($string =~ s/<\S[^<>]*(?:>|$)//gs) {};
233             return $string;
234             }
235              
236             1;
237             __END__