2 // $Id: trackback.php,v 1.21 2005/07/05 12:51:08 henoheno Exp $
4 // 2003-2005 PukiWiki Developers Team
5 // 2003 Originally written by Katsumi Saito <katsumi@jo1upk.ymt.prug.or.jp>
6 // License: GPL v2 or (at your option) any later version
12 * To get TrackBack ID correctly, specify URI clearly like:
13 * http://localhost/pukiwiki/pukiwiki.php?FrontPage
15 * tb_get_id($page) Get TrackBack ID from page name
16 * tb_id2page($tb_id) Get page name from TrackBack ID
17 * tb_get_filename($page) Get file name of TrackBack ping data
18 * tb_count($page) Count the number of TrackBack pings included for the page
19 * // pukiwiki.skin.php
20 * tb_send($page, $data) Send TrackBack ping(s) automatically // file.php
21 * tb_delete($page) Remove TrackBack ping data // edit.inc.php
22 * tb_get($file, $key = 1) Import TrackBack ping data from file
23 * tb_get_rdf($page) Get a RDF comment to bury TrackBack-ping-URI under HTML(XHTML) output
25 * tb_get_url($url) HTTP-GET from $uri, and reveal the TrackBack Ping URL
26 * class TrackBack_XML Parse and reveal the TrackBack Ping URL from RDF data
28 * == Referer related ==
29 * ref_save($page) Save or update referer data // lib/pukiwiki.php
32 define('PLUGIN_TRACKBACK_VERSION', 'PukiWiki/TrackBack 0.3');
34 // Get TrackBack ID from page name
35 function tb_get_id($page)
40 // Get page name from TrackBack ID
41 function tb_id2page($tb_id)
43 static $pages, $cache = array();
45 if (isset($cache[$tb_id])) return $cache[$tb_id];
47 if (! isset($pages)) $pages = get_existpages();
48 foreach ($pages as $page) {
49 $_tb_id = tb_get_id($page);
50 $cache[$_tb_id] = $page;
52 if ($tb_id == $_tb_id) return $cache[$tb_id]; // Found
55 $cache[$tb_id] = FALSE;
56 return $cache[$tb_id]; // Not found
59 // Get file name of TrackBack ping data
60 function tb_get_filename($page, $ext = '.txt')
62 return TRACKBACK_DIR . encode($page) . $ext;
65 // Count the number of TrackBack pings included for the page
66 function tb_count($page, $ext = '.txt')
68 $filename = tb_get_filename($page, $ext);
69 return file_exists($filename) ? count(file($filename)) : 0;
72 // Send TrackBack ping(s) automatically
73 // $plus = Newly added lines may include URLs
74 // $minus = Removed lines may include URLs
75 function tb_send($page, $plus, $minus = '')
79 $script = get_script_uri();
81 // Disable 'max execution time' (php.ini: max_execution_time)
82 if (ini_get('safe_mode') == '0') set_time_limit(0);
84 // Get URLs from <a>(anchor) tag from convert_html()
86 $plus = convert_html($plus); // WARNING: heavy and may cause side-effect
87 preg_match_all('#href="(https?://[^"]+)"#', $plus, $links, PREG_PATTERN_ORDER);
88 $links = array_unique($links[1]);
90 // Reject from minus list
93 $minus = convert_html($minus); // WARNING: heavy and may cause side-effect
94 preg_match_all('#href="(https?://[^"]+)"#', $minus, $links_m, PREG_PATTERN_ORDER);
95 $links_m = array_unique($links_m[1]);
97 $links = array_diff($links, $links_m);
100 // Reject own URL (Pattern _NOT_ started with '$script' and '?')
101 $links = preg_grep('/^(?!' . preg_quote($script, '/') . '\?)./', $links);
104 if (! is_array($links) || empty($links)) return;
106 $r_page = rawurlencode($page);
107 $excerpt = strip_htmltag(convert_html(get_source($page)));
109 // Sender's information
111 'title' => $page, // Title = It's page name
112 'url' => $script . '?' . $r_page, // will be rawurlencode() at send phase
113 'excerpt' => mb_strimwidth(preg_replace("/[\r\n]/", ' ', $excerpt), 0, 255, '...'),
114 'blog_name' => $page_title . ' (' . PLUGIN_TRACKBACK_VERSION . ')',
115 'charset' => SOURCE_ENCODING // Ping text encoding (Not defined)
118 foreach ($links as $link) {
119 $tb_id = tb_get_url($link); // Get Trackback ID from the URL
120 if (empty($tb_id)) continue; // Trackback is not supported
122 $result = http_request($tb_id, 'POST', '', $putdata, 2, CONTENT_CHARSET);
123 // FIXME: Create warning notification space at pukiwiki.skin!
127 // Remove TrackBack ping data
128 function tb_delete($page)
130 $filename = tb_get_filename($page);
131 if (file_exists($filename)) @unlink($filename);
134 // Import TrackBack ping data from file
135 function tb_get($file, $key = 1)
137 if (! file_exists($file)) return array();
140 $fp = @fopen($file, 'r');
141 set_file_buffer($fp, 0);
144 while ($data = @fgetcsv($fp, 8192, ',')) {
146 $result[rawurldecode($data[$key])] = $data;
154 // Get a RDF comment to bury TrackBack-ping-URI under HTML(XHTML) output
155 function tb_get_rdf($page)
157 $_script = get_script_uri(); // Get absolute path
158 $r_page = rawurlencode($page);
159 $tb_id = tb_get_id($page);
160 // $dcdate = substr_replace(get_date('Y-m-d\TH:i:sO', $time), ':', -2, 0);
165 <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
166 xmlns:dc="http://purl.org/dc/elements/1.1/"
167 xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
169 rdf:about="$_script?$r_page"
170 dc:identifier="$_script?$r_page"
172 trackback:ping="$_script?tb_id=$tb_id" />
178 // HTTP-GET from $uri, and reveal the TrackBack Ping URL
179 function tb_get_url($url)
181 global $use_proxy, $no_proxy;
183 // Don't go across HTTP-proxy server
184 $parse_url = parse_url($url);
185 if (empty($parse_url['host']) ||
186 ($use_proxy && ! in_the_net($no_proxy, $parse_url['host'])))
189 $data = http_request($url);
190 if ($data['rc'] !== 200) return '';
193 if (! preg_match_all('#<rdf:RDF[^>]*xmlns:trackback=[^>]*>(.*?)</rdf:RDF>#si', $data['data'],
194 $matches, PREG_PATTERN_ORDER))
197 $obj = new TrackBack_XML();
198 foreach ($matches[1] as $body) {
199 $tb_url = $obj->parse($body, $url);
200 if ($tb_url !== FALSE) return $tb_url;
206 // Parse and reveal the TrackBack Ping URL from RDF(XML) data
212 function parse($buf, $url)
216 $this->tb_url = FALSE;
218 $xml_parser = xml_parser_create();
219 if ($xml_parser === FALSE) return FALSE;
221 xml_set_element_handler($xml_parser, array(& $this, 'start_element'),
222 array(& $this, 'end_element'));
224 if (! xml_parse($xml_parser, $buf, TRUE)) {
225 /* die(sprintf('XML error: %s at line %d in %s',
226 xml_error_string(xml_get_error_code($xml_parser)),
227 xml_get_current_line_number($xml_parser),
233 return $this->tb_url;
236 function start_element($parser, $name, $attrs)
238 if ($name !== 'RDF:DESCRIPTION') return;
240 $about = $url = $tb_url = '';
241 foreach ($attrs as $key=>$value) {
243 case 'RDF:ABOUT' : $about = $value; break;
244 case 'DC:IDENTIFER' : /*FALLTHROUGH*/
245 case 'DC:IDENTIFIER' : $url = $value; break;
246 case 'TRACKBACK:PING': $tb_url = $value; break;
249 if ($about == $this->url || $url == $this->url)
250 $this->tb_url = $tb_url;
253 function end_element($parser, $name) {}
256 // Save or update referer data
257 function ref_save($page)
261 if (PKWK_READONLY || ! $referer || empty($_SERVER['HTTP_REFERER'])) return TRUE;
263 $url = $_SERVER['HTTP_REFERER'];
265 // Validate URI (Ignore own)
266 $parse_url = parse_url($url);
267 if (empty($parse_url['host']) || $parse_url['host'] == $_SERVER['HTTP_HOST'])
270 if (! is_dir(TRACKBACK_DIR)) die('No such directory: TRACKBACK_DIR');
271 if (! is_writable(TRACKBACK_DIR)) die('Permission denied to write: TRACKBACK_DIR');
273 // Update referer data
274 if (ereg("[,\"\n\r]", $url))
275 $url = '"' . str_replace('"', '""', $url) . '"';
277 $filename = tb_get_filename($page, '.ref');
278 $data = tb_get($filename, 3);
279 $d_url = rawurldecode($url);
280 if (! isset($data[$d_url])) {
281 $data[$d_url] = array(
282 '', // [0]: Last update date
283 UTIME, // [1]: Creation date
284 0, // [2]: Reference counter
285 $url, // [3]: Referer header
286 1 // [4]: Enable / Disable flag (1 = enable)
289 $data[$d_url][0] = UTIME;
292 $fp = fopen($filename, 'w');
293 if ($fp === FALSE) return FALSE;
294 set_file_buffer($fp, 0);
297 foreach ($data as $line)
298 fwrite($fp, join(',', $line) . "\n");