blob: 583c6d28d4b1e226cdc60994bfa29973387287cd [file] [log] [blame]
adminb0dd10f2006-08-25 17:25:49 +00001<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
2/**
3 * Code Igniter
4 *
5 * An open source application development framework for PHP 4.3.2 or newer
6 *
7 * @package CodeIgniter
8 * @author Rick Ellis
9 * @copyright Copyright (c) 2006, pMachine, Inc.
10 * @license http://www.codeignitor.com/user_guide/license.html
11 * @link http://www.codeigniter.com
12 * @since Version 1.0
13 * @filesource
14 */
15
16// ------------------------------------------------------------------------
17
18/**
19 * Trackback Class
20 *
21 * Trackback Sending/Receiving Class
22 *
23 * @package CodeIgniter
24 * @subpackage Libraries
25 * @category Trackbacks
26 * @author Paul Burdick
27 * @link http://www.codeigniter.com/user_guide/libraries/trackback.html
28 */
29class CI_Trackback {
30
31 var $time_format = 'local';
32 var $charset = 'UTF-8';
33 var $data = array('url' => '', 'title' => '', 'excerpt' => '', 'blog_name' => '', 'charset' => '');
34 var $convert_ascii = TRUE;
35 var $response = '';
36 var $error_msg = array();
37
38 /**
39 * Constructor
40 *
41 * @access public
42 */
43 function CI_Trackback()
44 {
45 log_message('debug', "Trackback Class Initialized");
46 }
47
48 // --------------------------------------------------------------------
49
50 /**
51 * Send Trackback
52 *
53 * @access public
54 * @param array
55 * @return bool
56 */
57 function send($tb_data)
58 {
59 if ( ! is_array($tb_data))
60 {
61 $this->set_error('The send() method must be passed an array');
62 return FALSE;
63 }
64
65 // Pre-process the Trackback Data
66 foreach (array('url', 'title', 'excerpt', 'blog_name', 'ping_url') as $item)
67 {
68 if ( ! isset($tb_data[$item]))
69 {
70 $this->set_error('Required item missing: '.$item);
71 return FALSE;
72 }
73
74 switch ($item)
75 {
76 case 'ping_url' : $$item = $this->extract_urls($tb_data[$item]);
77 break;
78 case 'excerpt' : $$item = $this->limit_characters($this->convert_xml(strip_tags(stripslashes($tb_data[$item]))));
79 break;
80 case 'url' : $$item = str_replace('&#45;', '-', $this->convert_xml(strip_tags(stripslashes($tb_data[$item]))));
81 break;
82 default : $$item = $this->convert_xml(strip_tags(stripslashes($tb_data[$item])));
83 break;
84 }
85
86 // Convert High ASCII Characters
87 if ($this->convert_ascii == TRUE)
88 {
89 if ($item == 'excerpt')
90 {
91 $$item = $this->convert_ascii($$item);
92 }
93 elseif ($item == 'title')
94 {
95 $$item = $this->convert_ascii($$item);
96 }
97 elseif($item == 'blog_name')
98 {
99 $$item = $this->convert_ascii($$item);
100 }
101 }
102 }
103
104 // Build the Trackback data string
105 $charset = ( ! isset($tb_data['charset'])) ? $this->charset : $tb_data['charset'];
106
107 $data = "url=".rawurlencode($url)."&title=".rawurlencode($title)."&blog_name=".rawurlencode($blog_name)."&excerpt=".rawurlencode($excerpt)."&charset=".rawurlencode($charset);
108
109 // Send Trackback(s)
110 $return = TRUE;
111 if (count($ping_url) > 0)
112 {
113 foreach ($ping_url as $url)
114 {
115 if ($this->process($url, $data) == FALSE)
116 {
117 $return = FALSE;
118 }
119 }
120 }
121
122 return $return;
123 }
124 // END send()
125
126 // --------------------------------------------------------------------
127
128 /**
129 * Receive Trackback Data
130 *
131 * This function simply validates the incoming TB data.
132 * It returns false on failure and true on success.
133 * If the data is valid it is set to the $this->data array
134 * so that it can be inserted into a database.
135 *
136 * @access public
137 * @return bool
138 */
139 function receive()
140 {
141 foreach (array('url', 'title', 'blog_name', 'excerpt') as $val)
142 {
143 if ( ! isset($_POST[$val]) OR $_POST[$val] == '')
144 {
145 $this->set_error('The following required POST variable is missing: '.$val);
146 return FALSE;
147 }
148
149 $this->data['charset'] = ( ! isset($_POST['charset'])) ? 'auto' : strtoupper(trim($_POST['charset']));
150
151 if ($val != 'url' && function_exists('mb_convert_encoding'))
152 {
153 $_POST[$val] = mb_convert_encoding($_POST[$val], $this->charset, $this->data['charset']);
154 }
155
156 $_POST[$val] = ($val != 'url') ? $this->convert_xml(strip_tags($_POST[$val])) : strip_tags($_POST[$val]);
157
158 if ($val == 'excerpt')
159 {
160 $_POST['excerpt'] = $this->limit_characters($_POST['excerpt']);
161 }
162
163 $this->data[$val] = $_POST[$val];
164 }
165
166 return TRUE;
167 }
168 // END receive()
169
170 // --------------------------------------------------------------------
171
172 /**
173 * Send Trackback Error Message
174 *
175 * Allows custom errros to be set. By default it
176 * sends the "incomplete information" error, as that's
177 * the most common one.
178 *
179 * @access public
180 * @param string
181 * @return void
182 */
183 function send_error($message = 'Incomplete Information')
184 {
185 echo "<?xml version=\"1.0\" encoding=\"utf-8\"?".">\n<response>\n<error>1</error>\n<message>".$message."</message>\n</response>";
186 exit;
187 }
188 // END send_error()
189
190 // --------------------------------------------------------------------
191
192 /**
193 * Send Trackback Success Message
194 *
195 * This should be called when a trackback has been
196 * successfully received and inserted.
197 *
198 * @access public
199 * @return void
200 */
201 function send_success()
202 {
203 echo "<?xml version=\"1.0\" encoding=\"utf-8\"?".">\n<response>\n<error>0</error>\n</response>";
204 exit;
205 }
206 // END send_success()
207
208 // --------------------------------------------------------------------
209
210 /**
211 * Fetch a particular item
212 *
213 * @access public
214 * @param string
215 * @return string
216 */
217 function data($item)
218 {
219 return ( ! isset($this->data[$item])) ? '' : $this->data[$item];
220 }
221 // END data()
222
223 // --------------------------------------------------------------------
224
225 /**
226 * Process Trackback
227 *
228 * Opens a socket connection and passes the data to
229 * the server. Returns true on success, false on failure
230 *
231 * @access public
232 * @param string
233 * @param string
234 * @return bool
235 */
236 function process($url, $data)
237 {
238 $target = parse_url($url);
239
240 // Open the socket
241 if ( ! $fp = @fsockopen($target['host'], 80))
242 {
243 $this->set_error('Invalid Connection: '.$url);
244 return FALSE;
245 }
246
247 // Build the path
248 $ppath = ( ! isset($target['path'])) ? $url : $target['path'];
249
250 $path = (isset($target['query']) && $target['query'] != "") ? $ppath.'?'.$target['query'] : $ppath;
251
252 // Add the Trackback ID to the data string
253 if ($id = $this->get_id($url))
254 {
255 $data = "tb_id=".$id."&".$data;
256 }
257
258 // Transfer the data
259 fputs ($fp, "POST " . $path . " HTTP/1.0\r\n" );
260 fputs ($fp, "Host: " . $target['host'] . "\r\n" );
261 fputs ($fp, "Content-type: application/x-www-form-urlencoded\r\n" );
262 fputs ($fp, "Content-length: " . strlen($data) . "\r\n" );
263 fputs ($fp, "Connection: close\r\n\r\n" );
264 fputs ($fp, $data);
265
266 // Was it successful?
267 $this->response = "";
268
269 while(!feof($fp))
270 {
271 $this->response .= fgets($fp, 128);
272 }
273 @fclose($fp);
274
275 if ( ! eregi("<error>0</error>", $this->response))
276 {
277 $message = 'An unknown error was encountered';
278
279 if (preg_match("/<message>(.*?)<\/message>/is", $this->response, $match))
280 {
281 $message = trim($match['1']);
282 }
283
284 $this->set_error($message);
285 return FALSE;
286 }
287
288 return TRUE;
289 }
290 // END process()
291
292 // --------------------------------------------------------------------
293
294 /**
295 * Extract Trackback URLs
296 *
297 * This function lets multiple trackbacks be sent.
298 * It takes a string of URLs (separated by comma or
299 * space) and puts each URL into an array
300 *
301 * @access public
302 * @param string
303 * @return string
304 */
305 function extract_urls($urls)
306 {
307 // Remove the pesky white space and replace with a comma.
308 $urls = preg_replace("/\s*(\S+)\s*/", "\\1,", $urls);
309
310 // If they use commas get rid of the doubles.
311 $urls = str_replace(",,", ",", $urls);
312
313 // Remove any comma that might be at the end
314 if (substr($urls, -1) == ",")
315 {
316 $urls = substr($urls, 0, -1);
317 }
318
319 // Break into an array via commas
320 $urls = preg_split('/[,]/', $urls);
321
322 // Removes duplicates
323 $urls = array_unique($urls);
324
325 array_walk($urls, array($this, 'validate_url'));
326
327 return $urls;
328 }
329 // END extract_urls()
330
331 // --------------------------------------------------------------------
332
333 /**
334 * Validate URL
335 *
336 * Simply adds "http://" if missing
337 *
338 * @access public
339 * @param string
340 * @return string
341 */
342 function validate_url($url)
343 {
344 $url = trim($url);
345
346 if (substr($url, 0, 4) != "http")
347 {
348 $url = "http://".$url;
349 }
350 }
351 // END validate_url()
352
353 // --------------------------------------------------------------------
354
355 /**
356 * Find the Trackback URL's ID
357 *
358 * @access public
359 * @param string
360 * @return string
361 */
362 function get_id($url)
363 {
364 $tb_id = "";
365
366 if (strstr($url, '?'))
367 {
368 $tb_array = explode('/', $url);
369 $tb_end = $tb_array[count($tb_array)-1];
370
371 if ( ! ctype_digit($tb_end))
372 {
373 $tb_end = $tb_array[count($tb_array)-2];
374 }
375
376 $tb_array = explode('=', $tb_end);
377 $tb_id = $tb_array[count($tb_array)-1];
378 }
379 else
380 {
381 if (ereg("/$", $url))
382 {
383 $url = substr($url, 0, -1);
384 }
385
386 $tb_array = explode('/', $url);
387 $tb_id = $tb_array[count($tb_array)-1];
388
389 if ( ! ctype_digit($tb_id))
390 {
391 $tb_id = $tb_array[count($tb_array)-2];
392 }
393 }
394
395 if ( ! preg_match ("/^([0-9]+)$/", $tb_id))
396 {
397 return false;
398 }
399 else
400 {
401 return $tb_id;
402 }
403 }
404 // END get_id()
405
406 // --------------------------------------------------------------------
407
408 /**
409 * Convert Reserved XML characters to Entities
410 *
411 * @access public
412 * @param string
413 * @return string
414 */
415 function convert_xml($str)
416 {
417 $temp = '__TEMP_AMPERSANDS__';
418
419 $str = preg_replace("/&#(\d+);/", "$temp\\1;", $str);
420 $str = preg_replace("/&(\w+);/", "$temp\\1;", $str);
421
422 $str = str_replace(array("&","<",">","\"", "'", "-"),
423 array("&amp;", "&lt;", "&gt;", "&quot;", "&#39;", "&#45;"),
424 $str);
425
426 $str = preg_replace("/$temp(\d+);/","&#\\1;",$str);
427 $str = preg_replace("/$temp(\w+);/","&\\1;", $str);
428
429 return $str;
430 }
431 // END get_id()
432
433 // --------------------------------------------------------------------
434
435 /**
436 * Character limiter
437 *
438 * Limits the string based on the character count. Will preserve complete words.
439 *
440 * @access public
441 * @param string
442 * @param integer
443 * @param string
444 * @return string
445 */
446 function limit_characters($str, $n = 500, $end_char = '&#8230;')
447 {
448 if (strlen($str) < $n)
449 {
450 return $str;
451 }
452
453 $str = preg_replace("/\s+/", ' ', preg_replace("/(\r\n|\r|\n)/", " ", $str));
454
455 if (strlen($str) <= $n)
456 {
457 return $str;
458 }
459
460 $out = "";
461 foreach (explode(' ', trim($str)) as $val)
462 {
463 $out .= $val.' ';
464 if (strlen($out) >= $n)
465 {
466 return trim($out).$end_char;
467 }
468 }
469 }
470 // END get_id()
471
472 // --------------------------------------------------------------------
473
474 /**
475 * High ASCII to Entities
476 *
477 * Converts Hight ascii text and MS Word special chars
478 * to character entities
479 *
480 * @access public
481 * @param string
482 * @return string
483 */
484 function convert_ascii($str)
485 {
486 $count = 1;
487 $out = '';
488 $temp = array();
489
490 for ($i = 0, $s = strlen($str); $i < $s; $i++)
491 {
492 $ordinal = ord($str[$i]);
493
494 if ($ordinal < 128)
495 {
496 $out .= $str[$i];
497 }
498 else
499 {
500 if (count($temp) == 0)
501 {
502 $count = ($ordinal < 224) ? 2 : 3;
503 }
504
505 $temp[] = $ordinal;
506
507 if (count($temp) == $count)
508 {
509 $number = ($count == 3) ? (($temp['0'] % 16) * 4096) + (($temp['1'] % 64) * 64) + ($temp['2'] % 64) : (($temp['0'] % 32) * 64) + ($temp['1'] % 64);
510
511 $out .= '&#'.$number.';';
512 $count = 1;
513 $temp = array();
514 }
515 }
516 }
517
518 return $out;
519 }
520 // END convert_ascii()
521
522 // --------------------------------------------------------------------
523
524 /**
525 * Set error message
526 *
527 * @access public
528 * @param string
529 * @return void
530 */
531 function set_error($msg)
532 {
533 log_message('error', $msg);
534 $this->error_msg[] = $msg;
535 }
536 // END convert_ascii()
537
538 // --------------------------------------------------------------------
539
540 /**
541 * Show error messages
542 *
543 * @access public
544 * @param string
545 * @param string
546 * @return string
547 */
548 function display_errors($open = '<p>', $close = '</p>')
549 {
550 $str = '';
551 foreach ($this->error_msg as $val)
552 {
553 $str .= $open.$val.$close;
554 }
555
556 return $str;
557 }
558 // END display_errors()
559}
560// END Trackback Class
561?>