Now in own repository for remote checkouts
[core.git] / inc / classes / third_party / akismet / akismet.class.php
1 <?php
2 /**
3  * 01.07.2008 22:32:28est
4  * 
5  * Akismet PHP4 class
6  * 
7  * <b>Usage</b>
8  * <code>
9  *    $comment = array(
10  *           'author'    => 'viagra-test-123',
11  *           'email'     => 'test@example.com',
12  *           'website'   => 'http://www.example.com/',
13  *           'body'      => 'This is a test comment',
14  *           'permalink' => 'http://yourdomain.com/yourblogpost.url',
15  *        );
16  *
17  *    $akismet = new Akismet('http://www.yourdomain.com/', 'YOUR_WORDPRESS_API_KEY', $comment);
18  *
19  *    if($akismet->errorsExist()) {
20  *        echo"Couldn't connected to Akismet server!";
21  *    } else {
22  *        if($akismet->isSpam()) {
23  *            echo"Spam detected";
24  *        } else {
25  *            echo"yay, no spam!";
26  *        }
27  *    }
28  * </code>
29  * 
30  * @author Bret Kuhns {@link www.miphp.net}
31  * @link http://www.miphp.net/blog/view/new_akismet_class/
32  * @version 0.3.4
33  * @license http://www.opensource.org/licenses/mit-license.php MIT License
34  */
35
36
37
38 // Error constants
39 define("AKISMET_SERVER_NOT_FOUND",      0);
40 define("AKISMET_RESPONSE_FAILED",       1);
41 define("AKISMET_INVALID_KEY",           2);
42
43
44
45 // Base class to assist in error handling between Akismet classes
46 class AkismetObject {
47         var $errors = array();
48         
49         
50         /**
51          * Add a new error to the errors array in the object
52          *
53          * @param       String  $name   A name (array key) for the error
54          * @param       String  $string The error message
55          * @return void
56          */ 
57         // Set an error in the object
58         function setError($name, $message) {
59                 $this->errors[$name] = $message;
60         }
61         
62
63         /**
64          * Return a specific error message from the errors array
65          *
66          * @param       String  $name   The name of the error you want
67          * @return mixed        Returns a String if the error exists, a false boolean if it does not exist
68          */
69         function getError($name) {
70                 if($this->isError($name)) {
71                         return $this->errors[$name];
72                 } else {
73                         return false;
74                 }
75         }
76         
77         
78         /**
79          * Return all errors in the object
80          *
81          * @return String[]
82          */ 
83         function getErrors() {
84                 return (array)$this->errors;
85         }
86         
87         
88         /**
89          * Check if a certain error exists
90          *
91          * @param       String  $name   The name of the error you want
92          * @return boolean
93          */ 
94         function isError($name) {
95                 return isset($this->errors[$name]);
96         }
97         
98         
99         /**
100          * Check if any errors exist
101          *
102          * @return boolean
103          */
104         function errorsExist() {
105                 return (count($this->errors) > 0);
106         }
107         
108         
109 }
110
111
112
113
114
115 // Used by the Akismet class to communicate with the Akismet service
116 class AkismetHttpClient extends AkismetObject {
117         var $akismetVersion = '1.1';
118         var $con;
119         var $host;
120         var $port;
121         var $apiKey;
122         var $blogUrl;
123         var $errors = array();
124         
125         
126         // Constructor
127         function AkismetHttpClient($host, $blogUrl, $apiKey, $port = 80) {
128                 $this->host = $host;
129                 $this->port = $port;
130                 $this->blogUrl = $blogUrl;
131                 $this->apiKey = $apiKey;
132         }
133         
134         
135         // Use the connection active in $con to get a response from the server and return that response
136         function getResponse($request, $path, $type = "post", $responseLength = 1160) {
137                 $this->_connect();
138                 
139                 if($this->con && !$this->isError(AKISMET_SERVER_NOT_FOUND)) {
140                         $request  = 
141                                         strToUpper($type)." /{$this->akismetVersion}/$path HTTP/1.1\r\n" .
142                                         "Host: ".((!empty($this->apiKey)) ? $this->apiKey."." : null)."{$this->host}\r\n" .
143                                         "Content-Type: application/x-www-form-urlencoded; charset=utf-8\r\n" .
144                                         "Content-Length: ".strlen($request)."\r\n" .
145                                         "User-Agent: Akismet PHP4 Class\r\n" .
146                                         "\r\n" .
147                                         $request
148                                 ;
149                         $response = "";
150
151                         @fwrite($this->con, $request);
152
153                         while(!feof($this->con)) {
154                                 $response .= @fgets($this->con, $responseLength);
155                         }
156
157                         $response = explode("\r\n\r\n", $response, 2);
158                         return $response[1];
159                 } else {
160                         $this->setError(AKISMET_RESPONSE_FAILED, "The response could not be retrieved.");
161                 }
162                 
163                 $this->_disconnect();
164         }
165         
166         
167         // Connect to the Akismet server and store that connection in the instance variable $con
168         function _connect() {
169                 if(!($this->con = @fsockopen($this->host, $this->port))) {
170                         $this->setError(AKISMET_SERVER_NOT_FOUND, "Could not connect to akismet server.");
171                 }
172         }
173         
174         
175         // Close the connection to the Akismet server
176         function _disconnect() {
177                 @fclose($this->con);
178         }
179         
180         
181 }
182
183
184
185
186
187 // The controlling class. This is the ONLY class the user should instantiate in
188 // order to use the Akismet service!
189 class Akismet extends AkismetObject {
190         var $apiPort = 80;
191         var $akismetServer = 'rest.akismet.com';
192         var $akismetVersion = '1.1';
193         var $http;
194         
195         var $ignore = array(
196                         'HTTP_COOKIE',
197                         'HTTP_X_FORWARDED_FOR',
198                         'HTTP_X_FORWARDED_HOST',
199                         'HTTP_MAX_FORWARDS',
200                         'HTTP_X_FORWARDED_SERVER',
201                         'REDIRECT_STATUS',
202                         'SERVER_PORT',
203                         'PATH',
204                         'DOCUMENT_ROOT',
205                         'SERVER_ADMIN',
206                         'QUERY_STRING',
207                         'PHP_SELF',
208                         'argv'
209                 );
210         
211         var $blogUrl = "";
212         var $apiKey  = "";
213         var $comment = array();
214         
215         
216         /**
217          * Constructor
218          * 
219          * Set instance variables, connect to Akismet, and check API key
220          * 
221          * @param       String  $blogUrl        The URL to your own blog
222          * @param       String  $apiKey         Your wordpress API key
223          * @param       String[]        $comment        A formatted comment array to be examined by the Akismet service
224          * @return      Akismet
225          */
226         function Akismet($blogUrl, $apiKey, $comment = array()) {
227                 $this->blogUrl = $blogUrl;
228                 $this->apiKey  = $apiKey;
229                 $this->setComment($comment);
230                 
231                 // Connect to the Akismet server and populate errors if they exist
232                 $this->http = new AkismetHttpClient($this->akismetServer, $blogUrl, $apiKey);
233                 if($this->http->errorsExist()) {
234                         $this->errors = array_merge($this->errors, $this->http->getErrors());
235                 }
236                 
237                 // Check if the API key is valid
238                 if(!$this->_isValidApiKey($apiKey)) {
239                         $this->setError(AKISMET_INVALID_KEY, "Your Akismet API key is not valid.");
240                 }
241         }
242         
243         
244         /**
245          * Query the Akismet and determine if the comment is spam or not
246          * 
247          * @return      boolean
248          */
249         function isSpam() {
250                 $response = $this->http->getResponse($this->_getQueryString(), 'comment-check');
251                 
252                 return ($response == "true");
253         }
254         
255         
256         /**
257          * Submit this comment as an unchecked spam to the Akismet server
258          * 
259          * @return      void
260          */
261         function submitSpam() {
262                 $this->http->getResponse($this->_getQueryString(), 'submit-spam');
263         }
264         
265         
266         /**
267          * Submit a false-positive comment as "ham" to the Akismet server
268          *
269          * @return      void
270          */
271         function submitHam() {
272                 $this->http->getResponse($this->_getQueryString(), 'submit-ham');
273         }
274         
275         
276         /**
277          * Manually set the comment value of the instantiated object.
278          *
279          * @param       Array   $comment
280          * @return      void
281          */
282         function setComment($comment) {
283                 $this->comment = $comment;
284                 if(!empty($comment)) {
285                         $this->_formatCommentArray();
286                         $this->_fillCommentValues();
287                 }
288         }
289         
290         
291         /**
292          * Returns the current value of the object's comment array.
293          *
294          * @return      Array
295          */
296         function getComment() {
297                 return $this->comment;
298         }
299         
300         
301         /**
302          * Check with the Akismet server to determine if the API key is valid
303          *
304          * @access      Protected
305          * @param       String  $key    The Wordpress API key passed from the constructor argument
306          * @return      boolean
307          */
308         function _isValidApiKey($key) {
309                 $keyCheck = $this->http->getResponse("key=".$this->apiKey."&blog=".$this->blogUrl, 'verify-key');
310                         
311                 return ($keyCheck == "valid");
312         }
313         
314         
315         /**
316          * Format the comment array in accordance to the Akismet API
317          *
318          * @access      Protected
319          * @return      void
320          */
321         function _formatCommentArray() {
322                 $format = array(
323                                 'type' => 'comment_type',
324                                 'author' => 'comment_author',
325                                 'email' => 'comment_author_email',
326                                 'website' => 'comment_author_url',
327                                 'body' => 'comment_content'
328                         );
329                 
330                 foreach($format as $short => $long) {
331                         if(isset($this->comment[$short])) {
332                                 $this->comment[$long] = $this->comment[$short];
333                                 unset($this->comment[$short]);
334                         }
335                 }
336         }
337         
338         
339         /**
340          * Fill any values not provided by the developer with available values.
341          *
342          * @return      void
343          */
344         function _fillCommentValues() {
345                 if(!isset($this->comment['user_ip'])) {
346                         $this->comment['user_ip'] = ($_SERVER['REMOTE_ADDR'] != getenv('SERVER_ADDR')) ? $_SERVER['REMOTE_ADDR'] : getenv('HTTP_X_FORWARDED_FOR');
347                 }
348                 if(!isset($this->comment['user_agent'])) {
349                         $this->comment['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
350                 }
351                 if(!isset($this->comment['referrer'])) {
352                         $this->comment['referrer'] = $_SERVER['HTTP_REFERER'];
353                 }
354                 if(!isset($this->comment['blog'])) {
355                         $this->comment['blog'] = $this->blogUrl;
356                 }
357         }
358         
359         
360         /**
361          * Build a query string for use with HTTP requests
362          *
363          * @access      Protected
364          * @return      String
365          */
366         function _getQueryString() {
367                 foreach($_SERVER as $key => $value) {
368                         if(!in_array($key, $this->ignore)) {
369                                 if($key == 'REMOTE_ADDR') {
370                                         $this->comment[$key] = $this->comment['user_ip'];
371                                 } else {
372                                         $this->comment[$key] = $value;
373                                 }
374                         }
375                 }
376
377                 $query_string = '';
378
379                 foreach($this->comment as $key => $data) {
380                         $query_string .= $key . '=' . urlencode(stripslashes($data)) . '&';
381                 }
382
383                 return $query_string;
384         }
385         
386         
387 }
388 ?>