PageRenderTime 33ms CodeModel.GetById 6ms RepoModel.GetById 0ms app.codeStats 0ms

/dada/DADA/perllib/WWW/StopForumSpam.pm

http://github.com/justingit/dada-mail
Perl | 309 lines | 229 code | 68 blank | 12 comment | 33 complexity | 00daceec118973b511aaefc17488dad8 MD5 | raw file
Possible License(s): GPL-2.0
  1. package WWW::StopForumSpam;
  2. use 5.010;
  3. use strict;
  4. use warnings;
  5. use autodie;
  6. use Carp qw(croak);
  7. use URI::Escape;
  8. use Digest::MD5 qw(md5_hex);
  9. use Socket;
  10. use WWW::Curl::Easy;
  11. use JSON qw(decode_json);
  12. our $VERSION = '0.02';
  13. sub new {
  14. my $class = shift;
  15. my $self = bless({}, $class);
  16. # parse params
  17. while(@_) {
  18. my $attr = shift;
  19. my $value = shift;
  20. if($attr eq "timeout") {
  21. $self->{timeout} = 0 + $value;
  22. } elsif($attr eq "api_key") {
  23. $self->{api_key} = "$value";
  24. } elsif($attr eq "api_url") {
  25. $self->{api_url} = "$value";
  26. } elsif($attr eq "dnsbl") {
  27. $self->{dnsbl} = "$value";
  28. } elsif($attr eq "treshold") {
  29. $self->{treshold} = 0 + $value;
  30. }
  31. }
  32. # validate / set defaults
  33. $self->{api_url} = "http://www.stopforumspam.com/api" unless exists $self->{api_url};
  34. $self->{dnsbl} = "sfs.dnsbl.st." unless exists $self->{dnsbl};
  35. $self->{timeout} = 4 unless exists $self->{timeout};
  36. $self->{connect_timeout} = $self->_ceil($self->{timeout} / 2);
  37. $self->{treshold} = 65 unless exists $self->{treshold};
  38. return $self;
  39. }
  40. sub check {
  41. my $self = shift;
  42. my @request_params = ();
  43. while(@_) {
  44. my $attr = shift;
  45. my $value = shift;
  46. if ($attr eq "ip" or $attr eq "email" or $attr eq "username") {
  47. push(@request_params, $attr . "=" . uri_escape($value));
  48. }
  49. }
  50. # add default params
  51. push(@request_params, "f=json");
  52. my ($http_code, $buffer) = $self->_query_api(join("&", @request_params));
  53. # if the api is not working, we don't want to allow potential spammers
  54. # signing up, so rather force the developers to check their logs...
  55. if (not defined $buffer) {
  56. return 1;
  57. }
  58. my $decoded_json = decode_json($buffer);
  59. if(not defined $decoded_json->{'success'}) {
  60. warn "unable to read json";
  61. return 1;
  62. } elsif($decoded_json->{'success'} == 0) {
  63. warn $decoded_json->{'error'};
  64. return 1;
  65. }
  66. if($self->_get_avg_confidence($decoded_json) > $self->{treshold}) {
  67. return 1;
  68. }
  69. return 0;
  70. }
  71. sub dns_check {
  72. my $self = shift;
  73. my $packed_ip;
  74. my $ip_address;
  75. while(@_) {
  76. my $attr = shift;
  77. my $value = shift;
  78. if ($attr eq "ip") {
  79. $packed_ip = gethostbyname(join('.', reverse split(/\./, $value)) . "." . $self->{dnsbl});
  80. if (not defined $packed_ip) {
  81. next;
  82. }
  83. $ip_address = inet_ntoa($packed_ip);
  84. if ($ip_address eq "127.0.0.2") {
  85. return 1;
  86. }
  87. } elsif ($attr eq "email") {
  88. $packed_ip = gethostbyname(md5_hex($value) . "." . $self->{dnsbl});
  89. if (not defined $packed_ip) {
  90. next;
  91. }
  92. $ip_address = inet_ntoa($packed_ip);
  93. if ($ip_address eq "127.0.0.3") {
  94. return 1;
  95. }
  96. }
  97. }
  98. return 0;
  99. }
  100. sub report {
  101. my $self = shift;
  102. my @request_params = ();
  103. if(not defined $self->{api_key}) {
  104. croak "apikey required.";
  105. }
  106. while(@_) {
  107. my $attr = shift;
  108. my $value = shift;
  109. if ($attr eq "username" or $attr eq "ip_addr" or $attr eq "evidence" or $attr eq "email") {
  110. if (length($value) > 0) {
  111. push(@request_params, $attr . "=" . uri_escape($value));
  112. }
  113. }
  114. }
  115. # add default params
  116. push(@request_params, "api_key=" . $self->{api_key});
  117. my ($http_code, $buffer) = $self->_query_api(join("&", @request_params), 1);
  118. if (not defined $buffer) {
  119. return 0;
  120. }
  121. if ($http_code == 200) {
  122. return 1;
  123. } else {
  124. warn $self->_strip_tags($buffer);
  125. return 0;
  126. }
  127. }
  128. sub _query_api {
  129. my ($self, $data, $is_submit) = @_;
  130. if (not defined $is_submit) {
  131. $is_submit = 0;
  132. }
  133. my $buffer = "";
  134. my $curl = WWW::Curl::Easy->new();
  135. if ($is_submit) {
  136. $curl->setopt(CURLOPT_URL, "http://www.stopforumspam.com/add.php");
  137. $curl->setopt(CURLOPT_POST, 1);
  138. $curl->setopt(CURLOPT_POSTFIELDS, $data);
  139. } else {
  140. $curl->setopt(CURLOPT_URL, $self->{api_url} . "?" . $data);
  141. }
  142. $curl->setopt(CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; WWW::StopForumSpam/0.1; +http://www.perlhipster.com/bot.html)");
  143. $curl->setopt(CURLOPT_ENCODING, "");
  144. $curl->setopt(CURLOPT_NOPROGRESS, 1);
  145. $curl->setopt(CURLOPT_FAILONERROR, 0);
  146. $curl->setopt(CURLOPT_TIMEOUT, $self->{timeout});
  147. $curl->setopt(CURLOPT_WRITEFUNCTION, sub {
  148. $buffer .= $_[0];
  149. return length($_[0]);
  150. });
  151. my $retcode = $curl->perform();
  152. if($retcode != 0) {
  153. warn $curl->errbuf;
  154. return;
  155. }
  156. return ($curl->getinfo(CURLINFO_HTTP_CODE), $buffer);
  157. }
  158. sub _get_avg_confidence {
  159. my ($self, $decoded_json) = @_;
  160. my $confidence_total = 0;
  161. my $confidence_num = 0;
  162. if(defined $decoded_json->{'username'}) {
  163. if (defined $decoded_json->{'username'}{'confidence'}) {
  164. $confidence_total += $decoded_json->{'username'}{'confidence'};
  165. }
  166. $confidence_num++;
  167. }
  168. if(defined $decoded_json->{'email'}) {
  169. if (defined $decoded_json->{'email'}{'confidence'}) {
  170. $confidence_total += $decoded_json->{'email'}{'confidence'};
  171. }
  172. $confidence_num++;
  173. }
  174. if(defined $decoded_json->{'ip'}) {
  175. if (defined $decoded_json->{'ip'}{'confidence'}) {
  176. $confidence_total += $decoded_json->{'ip'}{'confidence'};
  177. }
  178. $confidence_num++;
  179. }
  180. return $confidence_total / $confidence_num;
  181. }
  182. sub _ceil {
  183. my ($self, $num) = @_;
  184. return int($num) + ($num > int($num));
  185. }
  186. sub _strip_tags {
  187. my ($self, $string) = @_;
  188. while ($string =~ s/<\S[^<>]*(?:>|$)//gs) {};
  189. return $string;
  190. }
  191. 1;
  192. __END__
  193. =encoding utf8
  194. =head1 NAME
  195. WWW::StopForumSpam - Perl extension for the StopForumSpam.com API
  196. =head1 DESCRIPTION
  197. StopForumSpam is a Anti Spam Database for free usage. Even though aimed towards
  198. preventing registration of spambots on a forum, this extension can be used for
  199. any type of website (e.g. blog) as well.
  200. An API key is only needed for reporting a new case of spam registration.
  201. =head1 SYNOPSIS
  202. use WWW::StopForumSpam;
  203. my $sfs = WWW::StopForumSpam->new(
  204. api_key => "", # optional
  205. timeout => 4, # cURL timeout in seconds, defaults to 4
  206. treshold => 65, # defaults to 65
  207. );
  208. # Returns 1 if spammer (caution: it will return 1 also on errors, this is to
  209. # prevent mass spam registration due to services not working properly, you
  210. # should therefor always check logs)
  211. $sfs->check(
  212. ip => "127.0.0.1", # optional, recommended
  213. email => "test\@test.com", # optional, recommended
  214. username => "Foobar", # optional, not recommended
  215. );
  216. # Alternative api call via DNSBL. Does not support usernames.
  217. # Unlike check() this will NOT return 1 on server fail.
  218. $sfs->dns_check(
  219. ip => "127.0.0.1",
  220. email => "test\@test.com",
  221. );
  222. # Requires the setting of "api_key" in the constructor
  223. $sfs->report(
  224. username => "Foobar", # required
  225. ip_addr => "127.0.0.1", # required
  226. evidence => "", # optional (for example the forum-post)
  227. email => "test\@test.com", # required
  228. );
  229. );
  230. =head1 SEE ALSO
  231. API keys and more detail on StopForumSpam are available at L<http://www.stopforumspam.com>.
  232. Github: L<https://github.com/lifeofguenter/p5-stopforumspam>
  233. Website: L<http://www.perlhipster.com/p5-stopforumspam>
  234. DNSBL: L<http://sfs.dnsbl.st>
  235. =head1 AUTHOR
  236. Günter Grodotzki, E<lt>guenter@perlhipster.comE<gt>
  237. =head1 COPYRIGHT AND LICENSE
  238. Copyright (C) 2014 by Günter Grodotzki
  239. This module is free software; you can redistribute it and/or
  240. modify it under the same terms as Perl itself. See L<perlartistic>.
  241. =cut