Dotclear

source: inc/core/class.dc.trackback.php @ 0:54703be25dd6

Revision 0:54703be25dd6, 9.4 KB checked in by Dsls <dsls@…>, 14 years ago (diff)

2.3 branch (trunk) first checkin

Line 
1<?php
2# -- BEGIN LICENSE BLOCK ---------------------------------------
3#
4# This file is part of Dotclear 2.
5#
6# Copyright (c) 2003-2010 Olivier Meunier & Association Dotclear
7# Licensed under the GPL version 2.0 license.
8# See LICENSE file or
9# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
10#
11# -- END LICENSE BLOCK -----------------------------------------
12if (!defined('DC_RC_PATH')) { return; }
13
14/**
15@ingroup DC_CORE
16@brief Trackbacks sender and server
17
18Sends and receives trackbacks. Also handles trackbacks auto discovery.
19*/
20class dcTrackback
21{
22     public $core;       ///< <b>dcCore</b> dcCore instance
23     public $table;      ///< <b>string</b> done pings table name
24     
25     /**
26     Object constructor
27     
28     @param    core      <b>dcCore</b>       dcCore instance
29     */
30     public function __construct($core)
31     {
32          $this->core =& $core;
33          $this->con =& $this->core->con;
34          $this->table = $this->core->prefix.'ping';
35     }
36     
37     /// @name Send trackbacks
38     //@{
39     /**
40     Get all pings sent for a given post.
41     
42     @param    post_id   <b>integer</b>      Post ID
43     @return   <b>record</b>
44     */
45     public function getPostPings($post_id)
46     {
47          $strReq = 'SELECT ping_url, ping_dt '.
48                    'FROM '.$this->table.' '.
49                    'WHERE post_id = '.(integer) $post_id;
50         
51          return $this->con->select($strReq);
52     }
53     
54     /**
55     Sends a ping to given <var>$url</var>.
56     
57     @param    url            <b>string</b>       URL to ping
58     @param    post_id        <b>integer</b>      Post ID
59     @param    post_title     <b>string</b>       Post title
60     @param    post_excerpt   <b>string</b>       Post excerpt
61     @param    post_url       <b>string</b>       Post URL
62     */
63     public function ping($url,$post_id,$post_title,$post_excerpt,$post_url)
64     {
65          if ($this->core->blog === null) {
66               return false;
67          }
68         
69          $post_id = (integer) $post_id;
70         
71          # Check for previously done trackback
72          $strReq = 'SELECT post_id, ping_url FROM '.$this->table.' '.
73                    'WHERE post_id = '.$post_id.' '.
74                    "AND ping_url = '".$this->con->escape($url)."' ";
75         
76          $rs = $this->con->select($strReq);
77         
78          if (!$rs->isEmpty()) {
79               throw new Exception(sprintf(__('%s has still been pinged'),$url));
80          }
81         
82          $data = array(
83               'title' => $post_title,
84               'excerpt' => $post_excerpt,
85               'url' => $post_url,
86               'blog_name' => trim(html::escapeHTML(html::clean($this->core->blog->name)))
87               //,'__debug' => false
88          );
89         
90          # Ping
91          try
92          {
93               $http = self::initHttp($url,$path);
94               $http->post($path,$data,'UTF-8');
95               $res = $http->getContent();
96          }
97          catch (Exception $e)
98          {
99               throw new Exception(__('Unable to ping URL'));
100          }
101         
102          $pattern =
103          '|<response>.*<error>(.*)</error>(.*)'.
104          '(<message>(.*)</message>(.*))?'.
105          '</response>|msU';
106         
107          if (!preg_match($pattern,$res,$match))
108          {
109               throw new Exception(sprintf(__('%s is not a ping URL'),$url));
110          }
111         
112          $ping_error = trim($match[1]);
113          $ping_msg = (!empty($match[4])) ? $match[4] : '';
114         
115          if ($ping_error != '0') {
116               throw new Exception(sprintf(__('%s, ping error:'),$url).' '.$ping_msg);
117          } else {
118               # Notify ping result in database
119               $cur = $this->con->openCursor($this->table);
120               $cur->post_id = $post_id;
121               $cur->ping_url = $url;
122               $cur->ping_dt = date('Y-m-d H:i:s');
123               
124               $cur->insert();
125          }
126     }
127     //@}
128     
129     /// @name Receive trackbacks
130     //@{
131     /**
132     Receives a trackback and insert it as a comment of given post.
133     
134     @param    post_id        <b>integer</b>      Post ID
135     */
136     public function receive($post_id)
137     {
138          header('Content-Type: text/xml; charset=UTF-8');
139          if (empty($_POST)) {
140               http::head(405,'Method Not Allowed');
141               echo
142               '<?xml version="1.0" encoding="utf-8"?>'."\n".
143               "<response>\n".
144               "  <error>1</error>\n".
145               "  <message>POST request needed</message>\n".
146               "</response>";
147               return;
148          }
149         
150          $post_id = (integer) $post_id;
151         
152          $title = !empty($_POST['title']) ? $_POST['title'] : '';
153          $excerpt = !empty($_POST['excerpt']) ? $_POST['excerpt'] : '';
154          $url = !empty($_POST['url']) ? $_POST['url'] : '';
155          $blog_name = !empty($_POST['blog_name']) ? $_POST['blog_name'] : '';
156          $charset = '';
157          $comment = '';
158         
159          $err = false;
160          $msg = '';
161         
162          if ($this->core->blog === null)
163          {
164               $err = true;
165               $msg = 'No blog.';
166          }
167          elseif ($url == '')
168          {
169               $err = true;
170               $msg = 'URL parameter is required.';
171          }
172          elseif ($blog_name == '') {
173               $err = true;
174               $msg = 'Blog name is required.';
175          }
176         
177          if (!$err)
178          {
179               $post = $this->core->blog->getPosts(array('post_id'=>$post_id,'post_type'=>''));
180               
181               if ($post->isEmpty())
182               {
183                    $err = true;
184                    $msg = 'No such post.';
185               }
186               elseif (!$post->trackbacksActive())
187               {
188                    $err = true;
189                    $msg = 'Trackbacks are not allowed for this post or weblog.';
190               }
191          }
192         
193          if (!$err)
194          {
195               $charset = self::getCharsetFromRequest();
196               
197               if (!$charset) {
198                    $charset = mb_detect_encoding($title.' '.$excerpt.' '.$blog_name,
199                    'UTF-8,ISO-8859-1,ISO-8859-2,ISO-8859-3,'.
200                    'ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,'.
201                    'ISO-8859-9,ISO-8859-10,ISO-8859-13,ISO-8859-14,ISO-8859-15');
202               }
203               
204               if (strtolower($charset) != 'utf-8') {
205                    $title = iconv($charset,'UTF-8',$title);
206                    $excerpt = iconv($charset,'UTF-8',$excerpt);
207                    $blog_name = iconv($charset,'UTF-8',$blog_name);
208               }
209               
210               $title = trim(html::clean($title));
211               $title = html::decodeEntities($title);
212               $title = html::escapeHTML($title);
213               $title = text::cutString($title,60);
214               
215               $excerpt = trim(html::clean($excerpt));
216               $excerpt = html::decodeEntities($excerpt);
217               $excerpt = preg_replace('/\s+/ms',' ',$excerpt);
218               $excerpt = text::cutString($excerpt,252); 
219               $excerpt = html::escapeHTML($excerpt).'...';
220               
221               $blog_name = trim(html::clean($blog_name));
222               $blog_name = html::decodeEntities($blog_name);
223               $blog_name = html::escapeHTML($blog_name);
224               $blog_name = text::cutString($blog_name,60);
225               
226               $url = trim(html::clean($url));
227               
228               if (!$blog_name) {
229                    $blog_name = 'Anonymous blog';
230               }
231               
232               $comment =
233               "<!-- TB -->\n".
234               '<p><strong>'.($title ? $title : $blog_name)."</strong></p>\n".
235               '<p>'.$excerpt.'</p>';
236               
237               $cur = $this->core->con->openCursor($this->core->prefix.'comment');
238               $cur->comment_author = (string) $blog_name;
239               $cur->comment_site = (string) $url;
240               $cur->comment_content = (string) $comment;
241               $cur->post_id = $post_id;
242               $cur->comment_trackback = 1;
243               $cur->comment_status = $this->core->blog->settings->system->trackbacks_pub ? 1 : -1;
244               $cur->comment_ip = http::realIP();
245               
246               try
247               {
248                    # --BEHAVIOR-- publicBeforeTrackbackCreate
249                    $this->core->callBehavior('publicBeforeTrackbackCreate',$cur);
250                    if ($cur->post_id) {
251                         $comment_id = $this->core->blog->addComment($cur);
252                         
253                         # --BEHAVIOR-- publicAfterTrackbackCreate
254                         $this->core->callBehavior('publicAfterTrackbackCreate',$cur,$comment_id);
255                    }
256               }
257               catch (Exception $e)
258               {
259                    $err = 1;
260                    $msg = 'Something went wrong : '.$e->getMessage();
261               }
262          }
263         
264         
265          $debug_trace =
266          "  <debug>\n".
267          '    <title>'.$title."</title>\n".
268          '    <excerpt>'.$excerpt."</excerpt>\n".
269          '    <url>'.$url."</url>\n".
270          '    <blog_name>'.$blog_name."</blog_name>\n".
271          '    <charset>'.$charset."</charset>\n".
272          '    <comment>'.$comment."</comment>\n".
273          "  </debug>\n";
274         
275          $resp =
276          '<?xml version="1.0" encoding="utf-8"?>'."\n".
277          "<response>\n".
278          '  <error>'.(integer) $err."</error>\n";
279         
280          if ($msg) {
281               $resp .= '  <message>'.$msg."</message>\n";
282          }
283         
284          if (!empty($_POST['__debug'])) {
285               $resp .= $debug_trace;
286          }
287         
288          echo $resp."</response>";
289     }
290     //@}
291     
292     private static function initHttp($url,&$path)
293     {
294          $client = netHttp::initClient($url,$path);
295          $client->setTimeout(5);
296          $client->setUserAgent('Dotclear - http://www.dotclear.org/');
297          $client->useGzip(false);
298          $client->setPersistReferers(false);
299         
300          return $client;
301     }
302     
303     private static function getCharsetFromRequest()
304     {
305          if (isset($_SERVER['CONTENT_TYPE']))
306          {
307               if (preg_match('|charset=([a-zA-Z0-9-]+)|',$_SERVER['CONTENT_TYPE'],$m)) {
308                    return $m[1];
309               }
310          }
311         
312          return null;
313     }
314     
315     /// @name Trackbacks auto discovery
316     //@{
317     /**
318     Returns an array containing all discovered trackbacks URLs in
319     <var>$text</var>.
320     
321     @param    text      <b>string</b>       Input text
322     @return   <b>array</b>
323     */
324     public function discover($text)
325     {
326          $res = array();
327         
328          foreach ($this->getTextLinks($text) as $link)
329          {
330               if (($url = $this->getPingURL($link)) !== null) {
331                    $res[] = $url;
332               }
333          }
334         
335          return $res;
336     }
337     //@}
338     
339     private function getTextLinks($text)
340     {
341          $res = array();
342         
343          # href attribute on "a" tags
344          if (preg_match_all('/<a ([^>]+)>/ms', $text, $match, PREG_SET_ORDER))
345          {
346               for ($i = 0; $i<count($match); $i++)
347               {
348                    if (preg_match('/href="(http:\/\/[^"]+)"/ms', $match[$i][1], $matches)) {
349                         $res[$matches[1]] = 1;
350                    }
351               }
352          }
353          unset($match);
354         
355          # cite attributes on "blockquote" and "q" tags
356          if (preg_match_all('/<(blockquote|q) ([^>]+)>/ms', $text, $match, PREG_SET_ORDER))
357          {
358               for ($i = 0; $i<count($match); $i++)
359               {
360                    if (preg_match('/cite="(http:\/\/[^"]+)"/ms', $match[$i][2], $matches)) {
361                         $res[$matches[1]] = 1;
362                    }
363               }
364          }
365         
366          return array_keys($res);
367     }
368     
369     private function getPingURL($url)
370     {
371          try
372          {
373               $http = self::initHttp($url,$path);
374               $http->get($path);
375               $page_content = $http->getContent();
376          }
377          catch (Exception $e)
378          {
379               return false;
380          }
381         
382          $pattern_rdf =
383          '/<rdf:RDF.*?>.*?'.
384          '<rdf:Description\s+(.*?)\/>'.
385          '.*?<\/rdf:RDF>'.
386          '/msi';
387         
388          preg_match_all($pattern_rdf,$page_content,$rdf_all,PREG_SET_ORDER);
389         
390          for ($i=0; $i<count($rdf_all); $i++)
391          {
392               $rdf = $rdf_all[$i][1];
393               
394               if (preg_match('/dc:identifier="'.preg_quote($url,'/').'"/msi',$rdf)) {
395                    if (preg_match('/trackback:ping="(.*?)"/msi',$rdf,$tb_link)) {
396                         return $tb_link[1];
397                    }
398               }
399          }
400         
401          return null;
402     }
403}
404?>
Note: See TracBrowser for help on using the repository browser.

Sites map