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