OSDN Git Service

BugTrack2/62: Do remove the whole design, 'Showing TrackBack-ping list by html'.
[pukiwiki/pukiwiki.git] / lib / trackback.php
1 <?php
2 // $Id: trackback.php,v 1.19 2005/04/29 11:24:20 henoheno Exp $
3 // Copyright (C)
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
7 //
8 // PukiWiki/TrackBack
9
10 /*
11  * NOTE:
12  *     To get TrackBack ID correctly, specify URI clearly like:
13  *     http://localhost/pukiwiki/pukiwiki.php?FrontPage
14  *
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
24  *                         // lib/pukiwiki.php
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
27  *
28  * == Referer related ==
29  * ref_save($page)         Save or update referer data // lib/pukiwiki.php
30  */
31
32 define('PLUGIN_TRACKBACK_VERSION', 'PukiWiki/TrackBack 0.3');
33
34 // Get TrackBack ID from page name
35 function tb_get_id($page)
36 {
37         return md5($page);
38 }
39
40 // Get page name from TrackBack ID
41 function tb_id2page($tb_id)
42 {
43         static $pages, $cache = array();
44
45         if (isset($cache[$tb_id])) return $cache[$tb_id];
46
47         if (! isset($pages)) $pages = get_existpages();
48         foreach ($pages as $page) {
49                 $_tb_id = tb_get_id($page);
50                 $cache[$_tb_id] = $page;
51                 unset($pages[$page]);
52                 if ($tb_id == $_tb_id) return $cache[$tb_id]; // Found
53         }
54
55         $cache[$tb_id] = FALSE;
56         return $cache[$tb_id]; // Not found
57 }
58
59 // Get file name of TrackBack ping data
60 function tb_get_filename($page, $ext = '.txt')
61 {
62         return TRACKBACK_DIR . encode($page) . $ext;
63 }
64
65 // Count the number of TrackBack pings included for the page
66 function tb_count($page, $ext = '.txt')
67 {
68         $filename = tb_get_filename($page, $ext);
69         return file_exists($filename) ? count(file($filename)) : 0;
70 }
71
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 = '')
76 {
77         global $trackback, $page_title;
78
79         if (! $trackback) return;
80
81         $script = get_script_uri();
82
83         // Disable 'max execution time' (php.ini: max_execution_time)
84         if (ini_get('safe_mode') == '0') set_time_limit(0);
85
86         // Get URLs from <a>(anchor) tag from convert_html()
87         $links = array();
88         $plus  = convert_html($plus); // WARNING: heavy and may cause side-effect
89         preg_match_all('#href="(https?://[^"]+)"#', $plus, $links, PREG_PATTERN_ORDER);
90         $links = array_unique($links[1]);
91
92         // Reject from minus list
93         if ($minus != '') {
94                 $links_m = array();
95                 $minus = convert_html($minus); // WARNING: heavy and may cause side-effect
96                 preg_match_all('#href="(https?://[^"]+)"#', $minus, $links_m, PREG_PATTERN_ORDER);
97                 $links_m = array_unique($links_m[1]);
98
99                 $links = array_diff($links, $links_m);
100         }
101
102         // Reject own URL (Pattern _NOT_ started with '$script' and '?')
103         $links = preg_grep('/^(?!' . preg_quote($script, '/') . '\?)./', $links);
104
105         // No link, END
106         if (! is_array($links) || empty($links)) return;
107
108         $r_page  = rawurlencode($page);
109         $excerpt = strip_htmltag(convert_html(get_source($page)));
110
111         // Sender's information
112         $putdata = array(
113                 'title'     => $page, // Title = It's page name
114                 'url'       => $script . '?' . $r_page, // will be rawurlencode() at send phase
115                 'excerpt'   => mb_strimwidth(preg_replace("/[\r\n]/", ' ', $excerpt), 0, 255, '...'),
116                 'blog_name' => $page_title . ' (' . PLUGIN_TRACKBACK_VERSION . ')',
117                 'charset'   => SOURCE_ENCODING // Ping text encoding (Not defined)
118         );
119
120         foreach ($links as $link) {
121                 $tb_id = tb_get_url($link);  // Get Trackback ID from the URL
122                 if (empty($tb_id)) continue; // Trackback is not supported
123
124                 $result = http_request($tb_id, 'POST', '', $putdata, 2, CONTENT_CHARSET);
125                 // FIXME: Create warning notification space at pukiwiki.skin!
126         }
127 }
128
129 // Remove TrackBack ping data
130 function tb_delete($page)
131 {
132         $filename = tb_get_filename($page);
133         if (file_exists($filename)) @unlink($filename);
134 }
135
136 // Import TrackBack ping data from file
137 function tb_get($file, $key = 1)
138 {
139         if (! file_exists($file)) return array();
140
141         $result = array();
142         $fp = @fopen($file, 'r');
143         set_file_buffer($fp, 0);
144         flock($fp, LOCK_EX);
145         rewind($fp);
146         while ($data = @fgetcsv($fp, 8192, ',')) {
147                 // $data[$key] = URL
148                 $result[rawurldecode($data[$key])] = $data;
149         }
150         flock($fp, LOCK_UN);
151         fclose ($fp);
152
153         return $result;
154 }
155
156 // Get a RDF comment to bury TrackBack-ping-URI under HTML(XHTML) output
157 function tb_get_rdf($page)
158 {
159         $_script = get_script_uri(); // Get absolute path
160         $r_page = rawurlencode($page);
161         $tb_id  = tb_get_id($page);
162         // $dcdate = substr_replace(get_date('Y-m-d\TH:i:sO', $time), ':', -2, 0);
163         // dc:date="$dcdate"
164
165         return <<<EOD
166 <!--
167 <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
168   xmlns:dc="http://purl.org/dc/elements/1.1/"
169   xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
170  <rdf:Description
171    rdf:about="$_script?$r_page"
172    dc:identifier="$_script?$r_page"
173    dc:title="$page"
174    trackback:ping="$_script?tb_id=$tb_id" />
175 </rdf:RDF>
176 -->
177 EOD;
178 }
179
180 // HTTP-GET from $uri, and reveal the TrackBack Ping URL
181 function tb_get_url($url)
182 {
183         // Don't go across HTTP-proxy server
184         $parse_url = parse_url($url);
185         if (empty($parse_url['host']) || via_proxy($parse_url['host']))
186                 return '';
187
188         $data = http_request($url);
189         if ($data['rc'] !== 200) return '';
190
191         $matches = array();
192         if (! preg_match_all('#<rdf:RDF[^>]*xmlns:trackback=[^>]*>(.*?)</rdf:RDF>#si', $data['data'],
193             $matches, PREG_PATTERN_ORDER))
194                 return '';
195
196         $obj = new TrackBack_XML();
197         foreach ($matches[1] as $body) {
198                 $tb_url = $obj->parse($body, $url);
199                 if ($tb_url !== FALSE) return $tb_url;
200         }
201
202         return '';
203 }
204
205 // Parse and reveal the TrackBack Ping URL from RDF(XML) data
206 class TrackBack_XML
207 {
208         var $url;
209         var $tb_url;
210
211         function parse($buf, $url)
212         {
213                 // Init
214                 $this->url    = $url;
215                 $this->tb_url = FALSE;
216
217                 $xml_parser = xml_parser_create();
218                 if ($xml_parser === FALSE) return FALSE;
219
220                 xml_set_element_handler($xml_parser, array(& $this, 'start_element'),
221                         array(& $this, 'end_element'));
222
223                 if (! xml_parse($xml_parser, $buf, TRUE)) {
224 /*                      die(sprintf('XML error: %s at line %d in %s',
225                                 xml_error_string(xml_get_error_code($xml_parser)),
226                                 xml_get_current_line_number($xml_parser),
227                                 $buf));
228 */
229                         return FALSE;
230                 }
231
232                 return $this->tb_url;
233         }
234
235         function start_element($parser, $name, $attrs)
236         {
237                 if ($name !== 'RDF:DESCRIPTION') return;
238
239                 $about = $url = $tb_url = '';
240                 foreach ($attrs as $key=>$value) {
241                         switch ($key) {
242                         case 'RDF:ABOUT'     : $about  = $value; break;
243                         case 'DC:IDENTIFER'  : /*FALLTHROUGH*/
244                         case 'DC:IDENTIFIER' : $url    = $value; break;
245                         case 'TRACKBACK:PING': $tb_url = $value; break;
246                         }
247                 }
248                 if ($about == $this->url || $url == $this->url)
249                         $this->tb_url = $tb_url;
250         }
251
252         function end_element($parser, $name) {}
253 }
254
255 // Save or update referer data
256 function ref_save($page)
257 {
258         global $referer;
259
260         if (PKWK_READONLY || ! $referer || empty($_SERVER['HTTP_REFERER'])) return TRUE;
261
262         $url = $_SERVER['HTTP_REFERER'];
263
264         // Validate URI (Ignore own)
265         $parse_url = parse_url($url);
266         if (empty($parse_url['host']) || $parse_url['host'] == $_SERVER['HTTP_HOST'])
267                 return TRUE;
268
269         if (! is_dir(TRACKBACK_DIR))      die('No such directory: TRACKBACK_DIR');
270         if (! is_writable(TRACKBACK_DIR)) die('Permission denied to write: TRACKBACK_DIR');
271
272         // Update referer data
273         if (ereg("[,\"\n\r]", $url))
274                 $url = '"' . str_replace('"', '""', $url) . '"';
275
276         $filename = tb_get_filename($page, '.ref');
277         $data     = tb_get($filename, 3);
278         $d_url    = rawurldecode($url);
279         if (! isset($data[$d_url])) {
280                 $data[$d_url] = array(
281                         '',    // [0]: Last update date
282                         UTIME, // [1]: Creation date
283                         0,     // [2]: Reference counter
284                         $url,  // [3]: Referer header
285                         1      // [4]: Enable / Disable flag (1 = enable)
286                 );
287         }
288         $data[$d_url][0] = UTIME;
289         $data[$d_url][2]++;
290
291         $fp = fopen($filename, 'w');
292         if ($fp === FALSE) return FALSE;        
293         set_file_buffer($fp, 0);
294         flock($fp, LOCK_EX);
295         rewind($fp);
296         foreach ($data as $line)
297                 fwrite($fp, join(',', $line) . "\n");
298         flock($fp, LOCK_UN);
299         fclose($fp);
300
301         return TRUE;
302 }
303 ?>