PageRenderTime 65ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/code/thirdparty/Akismet.php

https://github.com/lenix/silverstripe-comments
PHP | 398 lines | 183 code | 44 blank | 171 comment | 10 complexity | 92a6f2c7c884a5ad803cfdbe31da4302 MD5 | raw file
  1. <?php
  2. /**
  3. * Akismet anti-comment spam service
  4. *
  5. * The class in this package allows use of the {@link http://akismet.com Akismet} anti-comment spam service in any PHP5 application.
  6. *
  7. * This service performs a number of checks on submitted data and returns whether or not the data is likely to be spam.
  8. *
  9. * Please note that in order to use this class, you must have a vaild {@link http://wordpress.com/api-keys/ WordPress API key}. They are free for non/small-profit types and getting one will only take a couple of minutes.
  10. *
  11. * For commercial use, please {@link http://akismet.com/commercial/ visit the Akismet commercial licensing page}.
  12. *
  13. * Please be aware that this class is PHP5 only. Attempts to run it under PHP4 will most likely fail.
  14. *
  15. * See the Akismet class documentation page linked to below for usage information.
  16. *
  17. * @package comments
  18. * @author Alex Potsides, {@link http://www.achingbrain.net http://www.achingbrain.net}
  19. * @version 0.1
  20. * @copyright Alex Potsides, {@link http://www.achingbrain.net http://www.achingbrain.net}
  21. * @license http://www.gnu.org/licenses/gpl.html GNU General Public License
  22. */
  23. /**
  24. * The Akismet PHP5 Class
  25. *
  26. * This class takes the functionality from the Akismet WordPress plugin written by {@link http://photomatt.net/ Matt Mullenweg} and allows it to be integrated into any PHP5 application or website.
  27. *
  28. * The original plugin is {@link http://akismet.com/download/ available on the Akismet website}.
  29. *
  30. * <b>Usage:</b>
  31. * <code>
  32. * $akismet = new Akismet('http://www.example.com/blog/', 'aoeu1aoue');
  33. * $akismet->setCommentAuthor($name);
  34. * $akismet->setCommentAuthorEmail($email);
  35. * $akismet->setCommentAuthorURL($url);
  36. * $akismet->setCommentContent($comment);
  37. * $akismet->setPermalink('http://www.example.com/blog/alex/someurl/');
  38. * if($akismet->isCommentSpam())
  39. * // store the comment but mark it as spam (in case of a mis-diagnosis)
  40. * else
  41. * // store the comment normally
  42. * </code>
  43. *
  44. * @version 0.2
  45. * @author Alex Potsides
  46. * @link http://www.achingbrain.net/
  47. * @package cms
  48. * @subpackage comments
  49. */
  50. class Akismet
  51. {
  52. private $version = '0.2';
  53. private $wordPressAPIKey;
  54. private $blogURL;
  55. private $comment;
  56. private $apiPort;
  57. private $akismetServer;
  58. private $akismetVersion;
  59. // This prevents some potentially sensitive information from being sent accross the wire.
  60. private $ignore = array('HTTP_COOKIE',
  61. 'HTTP_X_FORWARDED_FOR',
  62. 'HTTP_X_FORWARDED_HOST',
  63. 'HTTP_MAX_FORWARDS',
  64. 'HTTP_X_FORWARDED_SERVER',
  65. 'REDIRECT_STATUS',
  66. 'SERVER_PORT',
  67. 'PATH',
  68. 'DOCUMENT_ROOT',
  69. 'SERVER_ADMIN',
  70. 'QUERY_STRING',
  71. 'PHP_SELF' );
  72. /**
  73. * @throws Exception An exception is thrown if your API key is invalid.
  74. * @param string Your WordPress API key.
  75. * @param string $blogURL The URL of your blog.
  76. */
  77. public function __construct($blogURL, $wordPressAPIKey)
  78. {
  79. $this->blogURL = $blogURL;
  80. $this->wordPressAPIKey = $wordPressAPIKey;
  81. // Set some default values
  82. $this->apiPort = 80;
  83. $this->akismetServer = 'rest.akismet.com';
  84. $this->akismetVersion = '1.1';
  85. // Start to populate the comment data
  86. $this->comment['blog'] = $blogURL;
  87. $this->comment['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
  88. $this->comment['referrer'] = $_SERVER['HTTP_REFERER'];
  89. // This is necessary if the server PHP5 is running on has been set up to run PHP4 and
  90. // PHP5 concurently and is actually running through a separate proxy al a these instructions:
  91. // http://www.schlitt.info/applications/blog/archives/83_How_to_run_PHP4_and_PHP_5_parallel.html
  92. // and http://wiki.coggeshall.org/37.html
  93. // Otherwise the user_ip appears as the IP address of the PHP4 server passing the requests to the
  94. // PHP5 one...
  95. $this->comment['user_ip'] = $_SERVER['REMOTE_ADDR'] != getenv('SERVER_ADDR') ? $_SERVER['REMOTE_ADDR'] : getenv('HTTP_X_FORWARDED_FOR');
  96. // Check to see if the key is valid
  97. $response = $this->http_post('key=' . $this->wordPressAPIKey . '&blog=' . $this->blogURL, $this->akismetServer, '/' . $this->akismetVersion . '/verify-key');
  98. if($response[1] != 'valid')
  99. {
  100. // Whoops, no it's not. Throw an exception as we can't proceed without a valid API key.
  101. throw new Exception('Invalid API key. Please obtain one from http://wordpress.com/api-keys/');
  102. }
  103. }
  104. private function http_post($request, $host, $path)
  105. {
  106. $http_request = "POST " . $path . " HTTP/1.1\r\n";
  107. $http_request .= "Host: " . $host . "\r\n";
  108. $http_request .= "Content-Type: application/x-www-form-urlencoded; charset=utf-8\r\n";
  109. $http_request .= "Content-Length: " . strlen($request) . "\r\n";
  110. $http_request .= "User-Agent: Akismet PHP5 Class " . $this->version . " | Akismet/1.11\r\n";
  111. $http_request .= "\r\n";
  112. $http_request .= $request;
  113. $socketWriteRead = new SocketWriteRead($host, $this->apiPort, $http_request);
  114. $socketWriteRead->send();
  115. return explode("\r\n\r\n", $socketWriteRead->getResponse(), 2);
  116. }
  117. // Formats the data for transmission echo $sql;
  118. private function getQueryString()
  119. {
  120. foreach($_SERVER as $key => $value)
  121. {
  122. if(!in_array($key, $this->ignore))
  123. {
  124. if($key == 'REMOTE_ADDR')
  125. {
  126. $this->comment[$key] = $this->comment['user_ip'];
  127. }
  128. else
  129. {
  130. $this->comment[$key] = $value;
  131. }
  132. }
  133. }
  134. $query_string = '';
  135. foreach($this->comment as $key => $data)
  136. {
  137. @$query_string .= $key . '=' . urlencode(stripslashes($data)) . '&';
  138. }
  139. return $query_string;
  140. }
  141. /**
  142. * Tests for spam.
  143. *
  144. * Uses the web service provided by {@link http://www.akismet.com Akismet} to see whether or not the submitted comment is spam. Returns a boolean value.
  145. *
  146. * @return bool True if the comment is spam, false if not
  147. */
  148. public function isCommentSpam()
  149. {
  150. $response = $this->http_post($this->getQueryString(), $this->wordPressAPIKey . '.rest.akismet.com', '/' . $this->akismetVersion . '/comment-check');
  151. return ($response[1] == 'true');
  152. }
  153. /**
  154. * Submit spam that is incorrectly tagged as ham.
  155. *
  156. * Using this function will make you a good citizen as it helps Akismet to learn from its mistakes. This will improve the service for everybody.
  157. */
  158. public function submitSpam()
  159. {
  160. $this->http_post($this->getQueryString(), $this->wordPressAPIKey . '.' . $this->akismetServer, '/' . $this->akismetVersion . '/submit-spam');
  161. }
  162. /**
  163. * Submit ham that is incorrectly tagged as spam.
  164. *
  165. * Using this function will make you a good citizen as it helps Akismet to learn from its mistakes. This will improve the service for everybody.
  166. */
  167. public function submitHam()
  168. {
  169. $this->http_post($this->getQueryString(), $this->wordPressAPIKey . '.' . $this->akismetServer, '/' . $this->akismetVersion . '/submit-ham');
  170. }
  171. /**
  172. * To override the user IP address when submitting spam/ham later on
  173. *
  174. * @param string $userip An IP address. Optional.
  175. */
  176. public function setUserIP($userip)
  177. {
  178. $this->comment['user_ip'] = $userip;
  179. }
  180. /**
  181. * To override the referring page when submitting spam/ham later on
  182. *
  183. * @param string $referrer The referring page. Optional.
  184. */
  185. public function setReferrer($referrer)
  186. {
  187. $this->comment['referrer'] = $referrer;
  188. }
  189. /**
  190. * A permanent URL referencing the blog post the comment was submitted to.
  191. *
  192. * @param string $permalink The URL. Optional.
  193. */
  194. public function setPermalink($permalink)
  195. {
  196. $this->comment['permalink'] = $permalink;
  197. }
  198. /**
  199. * The type of comment being submitted.
  200. *
  201. * May be blank, comment, trackback, pingback, or a made up value like "registration" or "wiki".
  202. */
  203. public function setCommentType($commentType)
  204. {
  205. $this->comment['comment_type'] = $commentType;
  206. }
  207. /**
  208. * The name that the author submitted with the comment.
  209. */
  210. public function setCommentAuthor($commentAuthor)
  211. {
  212. $this->comment['comment_author'] = $commentAuthor;
  213. }
  214. /**
  215. * The email address that the author submitted with the comment.
  216. *
  217. * The address is assumed to be valid.
  218. */
  219. public function setCommentAuthorEmail($authorEmail)
  220. {
  221. $this->comment['comment_author_email'] = $authorEmail;
  222. }
  223. /**
  224. * The URL that the author submitted with the comment.
  225. */
  226. public function setCommentAuthorURL($authorURL)
  227. {
  228. $this->comment['comment_author_url'] = $authorURL;
  229. }
  230. /**
  231. * The comment's body text.
  232. */
  233. public function setCommentContent($commentBody)
  234. {
  235. $this->comment['comment_content'] = $commentBody;
  236. }
  237. /**
  238. * Defaults to 80
  239. */
  240. public function setAPIPort($apiPort)
  241. {
  242. $this->apiPort = $apiPort;
  243. }
  244. /**
  245. * Defaults to rest.akismet.com
  246. */
  247. public function setAkismetServer($akismetServer)
  248. {
  249. $this->akismetServer = $akismetServer;
  250. }
  251. /**
  252. * Defaults to '1.1'
  253. */
  254. public function setAkismetVersion($akismetVersion)
  255. {
  256. $this->akismetVersion = $akismetVersion;
  257. }
  258. }
  259. /**
  260. * Utility class used by Akismet
  261. *
  262. * This class is used by Akismet to do the actual sending and receiving of data. It opens a connection to a remote host, sends some data and the reads the response and makes it available to the calling program.
  263. *
  264. * The code that makes up this class originates in the Akismet WordPress plugin, which is {@link http://akismet.com/download/ available on the Akismet website}.
  265. *
  266. * N.B. It is not necessary to call this class directly to use the Akismet class. This is included here mainly out of a sense of completeness.
  267. *
  268. * @name SocketWriteRead
  269. * @version 0.1
  270. * @author Alex Potsides
  271. * @link http://www.achingbrain.net/
  272. * @package cms
  273. * @subpackage comments
  274. */
  275. class SocketWriteRead
  276. {
  277. private $host;
  278. private $port;
  279. private $request;
  280. private $response;
  281. private $responseLength;
  282. private $errorNumber;
  283. private $errorString;
  284. /**
  285. * @param string $host The host to send/receive data.
  286. * @param int $port The port on the remote host.
  287. * @param string $request The data to send.
  288. * @param int $responseLength The amount of data to read. Defaults to 1160 bytes.
  289. */
  290. public function __construct($host, $port, $request, $responseLength = 1160)
  291. {
  292. $this->host = $host;
  293. $this->port = $port;
  294. $this->request = $request;
  295. $this->responseLength = $responseLength;
  296. $this->errorNumber = 0;
  297. $this->errorString = '';
  298. }
  299. /**
  300. * Sends the data to the remote host.
  301. *
  302. * @throws An exception is thrown if a connection cannot be made to the remote host.
  303. */
  304. public function send()
  305. {
  306. $this->response = '';
  307. $fs = fsockopen($this->host, $this->port, $this->errorNumber, $this->errorString, 3);
  308. if($this->errorNumber != 0)
  309. {
  310. throw new Exception('Error connecting to host: ' . $this->host . ' Error number: ' . $this->errorNumber . ' Error message: ' . $this->errorString);
  311. }
  312. if($fs !== false)
  313. {
  314. @fwrite($fs, $this->request);
  315. while(!feof($fs))
  316. {
  317. $this->response .= fgets($fs, $this->responseLength);
  318. }
  319. fclose($fs);
  320. }
  321. }
  322. /**
  323. * Returns the server response text
  324. *
  325. * @return string
  326. */
  327. public function getResponse()
  328. {
  329. return $this->response;
  330. }
  331. /**
  332. * Returns the error number
  333. *
  334. * If there was no error, 0 will be returned.
  335. *
  336. * @return int
  337. */
  338. public function getErrorNumner()
  339. {
  340. return $this->errorNumber;
  341. }
  342. /**
  343. * Returns the error string
  344. *
  345. * If there was no error, an empty string will be returned.
  346. *
  347. * @return string
  348. */
  349. public function getErrorString()
  350. {
  351. return $this->errorString;
  352. }
  353. }
  354. ?>