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