| 1 | <?php | 
|---|
| 2 | # -- BEGIN LICENSE BLOCK --------------------------------------- | 
|---|
| 3 | # | 
|---|
| 4 | # This file is part of Dotclear 2. | 
|---|
| 5 | # | 
|---|
| 6 | # Copyright (c) 2003-2013 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 ----------------------------------------- | 
|---|
| 12 | if (!defined('DC_RC_PATH')) { return; } | 
|---|
| 13 |  | 
|---|
| 14 | /** | 
|---|
| 15 | @ingroup DC_CORE | 
|---|
| 16 | @brief Trackbacks/Pingbacks sender and server | 
|---|
| 17 |  | 
|---|
| 18 | Sends and receives trackbacks/pingbacks. Also handles trackbacks/pingbacks auto discovery. | 
|---|
| 19 | */ | 
|---|
| 20 | class 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 |           $ping_parts = explode('|',$url); | 
|---|
| 83 |            | 
|---|
| 84 |           # Let's walk by the trackback way | 
|---|
| 85 |           if (count($ping_parts) < 2) { | 
|---|
| 86 |                $data = array( | 
|---|
| 87 |                     'title' => $post_title, | 
|---|
| 88 |                     'excerpt' => $post_excerpt, | 
|---|
| 89 |                     'url' => $post_url, | 
|---|
| 90 |                     'blog_name' => trim(html::escapeHTML(html::clean($this->core->blog->name))) | 
|---|
| 91 |                     //,'__debug' => false | 
|---|
| 92 |                ); | 
|---|
| 93 |                 | 
|---|
| 94 |                # Ping | 
|---|
| 95 |                try | 
|---|
| 96 |                { | 
|---|
| 97 |                     $http = self::initHttp($url,$path); | 
|---|
| 98 |                     $http->post($path,$data,'UTF-8'); | 
|---|
| 99 |                     $res = $http->getContent(); | 
|---|
| 100 |                } | 
|---|
| 101 |                catch (Exception $e) | 
|---|
| 102 |                { | 
|---|
| 103 |                     throw new Exception(__('Unable to ping URL')); | 
|---|
| 104 |                } | 
|---|
| 105 |                 | 
|---|
| 106 |                $pattern = | 
|---|
| 107 |                '|<response>.*<error>(.*)</error>(.*)'. | 
|---|
| 108 |                '(<message>(.*)</message>(.*))?'. | 
|---|
| 109 |                '</response>|msU'; | 
|---|
| 110 |                 | 
|---|
| 111 |                if (!preg_match($pattern,$res,$match)) | 
|---|
| 112 |                { | 
|---|
| 113 |                     throw new Exception(sprintf(__('%s is not a ping URL'),$url)); | 
|---|
| 114 |                } | 
|---|
| 115 |                 | 
|---|
| 116 |                $ping_error = trim($match[1]); | 
|---|
| 117 |                $ping_msg = (!empty($match[4])) ? $match[4] : ''; | 
|---|
| 118 |           } | 
|---|
| 119 |           # Damnit ! Let's play pingback | 
|---|
| 120 |           else { | 
|---|
| 121 |                try { | 
|---|
| 122 |                     $xmlrpc = new xmlrpcClient($ping_parts[0]); | 
|---|
| 123 |                     $res = $xmlrpc->query('pingback.ping', $post_url, $ping_parts[1]); | 
|---|
| 124 |                     $ping_error = '0'; | 
|---|
| 125 |                } | 
|---|
| 126 |                catch (xmlrpcException $e) { | 
|---|
| 127 |                     $ping_error = $e->getCode(); | 
|---|
| 128 |                     $ping_msg = $e->getMessage();  | 
|---|
| 129 |                } | 
|---|
| 130 |                catch (Exception $e) { | 
|---|
| 131 |                     throw new Exception(__('Unable to ping URL')); | 
|---|
| 132 |                } | 
|---|
| 133 |           } | 
|---|
| 134 |            | 
|---|
| 135 |           if ($ping_error != '0') { | 
|---|
| 136 |                throw new Exception(sprintf(__('%s, ping error:'),$url).' '.$ping_msg); | 
|---|
| 137 |           } else { | 
|---|
| 138 |                # Notify ping result in database | 
|---|
| 139 |                $cur = $this->con->openCursor($this->table); | 
|---|
| 140 |                $cur->post_id = $post_id; | 
|---|
| 141 |                $cur->ping_url = $url; | 
|---|
| 142 |                $cur->ping_dt = date('Y-m-d H:i:s'); | 
|---|
| 143 |                 | 
|---|
| 144 |                $cur->insert(); | 
|---|
| 145 |           } | 
|---|
| 146 |      } | 
|---|
| 147 |      //@} | 
|---|
| 148 |       | 
|---|
| 149 |      private function pingAlreadyDone($post_id, $from_url) | 
|---|
| 150 |      { | 
|---|
| 151 |           $params = array( | 
|---|
| 152 |                'post_id' => $post_id, | 
|---|
| 153 |                'comment_site' => $from_url, | 
|---|
| 154 |                'comment_trackback' => 1, | 
|---|
| 155 |           ); | 
|---|
| 156 |            | 
|---|
| 157 |           $rs = $this->core->blog->getComments($params, true); | 
|---|
| 158 |           if ($rs && !$rs->isEmpty()) { | 
|---|
| 159 |                return ($rs->f(0)); | 
|---|
| 160 |           } | 
|---|
| 161 |            | 
|---|
| 162 |           return false; | 
|---|
| 163 |      } | 
|---|
| 164 |       | 
|---|
| 165 |      private function addBacklink($post_id, $url, $blog_name, $title, $excerpt, &$comment) | 
|---|
| 166 |      { | 
|---|
| 167 |           if (empty($blog_name)) { | 
|---|
| 168 |                $blog_name = 'Anonymous blog'; | 
|---|
| 169 |           } | 
|---|
| 170 |            | 
|---|
| 171 |           $comment = | 
|---|
| 172 |           "<!-- TB -->\n". | 
|---|
| 173 |           '<p><strong>'.($title ? $title : $blog_name)."</strong></p>\n". | 
|---|
| 174 |           '<p>'.$excerpt.'</p>'; | 
|---|
| 175 |            | 
|---|
| 176 |           $cur = $this->core->con->openCursor($this->core->prefix.'comment'); | 
|---|
| 177 |           $cur->comment_author = (string) $blog_name; | 
|---|
| 178 |           $cur->comment_site = (string) $url; | 
|---|
| 179 |           $cur->comment_content = (string) $comment; | 
|---|
| 180 |           $cur->post_id = $post_id; | 
|---|
| 181 |           $cur->comment_trackback = 1; | 
|---|
| 182 |           $cur->comment_status = $this->core->blog->settings->system->trackbacks_pub ? 1 : -1; | 
|---|
| 183 |           $cur->comment_ip = http::realIP(); | 
|---|
| 184 |            | 
|---|
| 185 |           # --BEHAVIOR-- publicBeforeTrackbackCreate | 
|---|
| 186 |           $this->core->callBehavior('publicBeforeTrackbackCreate',$cur); | 
|---|
| 187 |           if ($cur->post_id) { | 
|---|
| 188 |                $comment_id = $this->core->blog->addComment($cur); | 
|---|
| 189 |                 | 
|---|
| 190 |                # --BEHAVIOR-- publicAfterTrackbackCreate | 
|---|
| 191 |                $this->core->callBehavior('publicAfterTrackbackCreate',$cur,$comment_id); | 
|---|
| 192 |           } | 
|---|
| 193 |      } | 
|---|
| 194 |       | 
|---|
| 195 |      /// @name Receive trackbacks | 
|---|
| 196 |      //@{ | 
|---|
| 197 |      /** | 
|---|
| 198 |      Receives a trackback and insert it as a comment of given post. | 
|---|
| 199 |       | 
|---|
| 200 |      @param    post_id        <b>integer</b>      Post ID | 
|---|
| 201 |      */ | 
|---|
| 202 |      public function receive($post_id) | 
|---|
| 203 |      { | 
|---|
| 204 |           header('Content-Type: text/xml; charset=UTF-8'); | 
|---|
| 205 |           if (empty($_POST)) { | 
|---|
| 206 |                http::head(405,'Method Not Allowed'); | 
|---|
| 207 |                echo | 
|---|
| 208 |                '<?xml version="1.0" encoding="utf-8"?>'."\n". | 
|---|
| 209 |                "<response>\n". | 
|---|
| 210 |                "  <error>1</error>\n". | 
|---|
| 211 |                "  <message>POST request needed</message>\n". | 
|---|
| 212 |                "</response>"; | 
|---|
| 213 |                return; | 
|---|
| 214 |           } | 
|---|
| 215 |            | 
|---|
| 216 |           $post_id = (integer) $post_id; | 
|---|
| 217 |            | 
|---|
| 218 |           $title = !empty($_POST['title']) ? $_POST['title'] : ''; | 
|---|
| 219 |           $excerpt = !empty($_POST['excerpt']) ? $_POST['excerpt'] : ''; | 
|---|
| 220 |           $url = !empty($_POST['url']) ? $_POST['url'] : ''; | 
|---|
| 221 |           $blog_name = !empty($_POST['blog_name']) ? $_POST['blog_name'] : ''; | 
|---|
| 222 |           $charset = ''; | 
|---|
| 223 |           $comment = ''; | 
|---|
| 224 |                      | 
|---|
| 225 |           $err = false; | 
|---|
| 226 |           $msg = ''; | 
|---|
| 227 |            | 
|---|
| 228 |           if ($this->core->blog === null) | 
|---|
| 229 |           { | 
|---|
| 230 |                $err = true; | 
|---|
| 231 |                $msg = 'No blog.'; | 
|---|
| 232 |           } | 
|---|
| 233 |           elseif ($url == '') | 
|---|
| 234 |           { | 
|---|
| 235 |                $err = true; | 
|---|
| 236 |                $msg = 'URL parameter is required.'; | 
|---|
| 237 |           } | 
|---|
| 238 |           elseif ($blog_name == '') { | 
|---|
| 239 |                $err = true; | 
|---|
| 240 |                $msg = 'Blog name is required.'; | 
|---|
| 241 |           } | 
|---|
| 242 |            | 
|---|
| 243 |           if (!$err) | 
|---|
| 244 |           { | 
|---|
| 245 |                $post = $this->core->blog->getPosts(array('post_id'=>$post_id,'post_type'=>'')); | 
|---|
| 246 |                 | 
|---|
| 247 |                if ($post->isEmpty()) | 
|---|
| 248 |                { | 
|---|
| 249 |                     $err = true; | 
|---|
| 250 |                     $msg = 'No such post.'; | 
|---|
| 251 |                } | 
|---|
| 252 |                elseif (!$post->trackbacksActive()) | 
|---|
| 253 |                { | 
|---|
| 254 |                     $err = true; | 
|---|
| 255 |                     $msg = 'Trackbacks are not allowed for this post or weblog.'; | 
|---|
| 256 |                } | 
|---|
| 257 |  | 
|---|
| 258 |                $url = trim(html::clean($url)); | 
|---|
| 259 |                if ($this->pingAlreadyDone($post->post_id, $url)) { | 
|---|
| 260 |                     $err = true; | 
|---|
| 261 |                     $msg = 'The trackback has already been registered'; | 
|---|
| 262 |                } | 
|---|
| 263 |           } | 
|---|
| 264 |            | 
|---|
| 265 |           if (!$err) | 
|---|
| 266 |           { | 
|---|
| 267 |                $charset = self::getCharsetFromRequest(); | 
|---|
| 268 |                 | 
|---|
| 269 |                if (!$charset) { | 
|---|
| 270 |                     $charset = self::detectCharset($title.' '.$excerpt.' '.$blog_name); | 
|---|
| 271 |                } | 
|---|
| 272 |                 | 
|---|
| 273 |                if (strtolower($charset) != 'utf-8') { | 
|---|
| 274 |                     $title = iconv($charset,'UTF-8',$title); | 
|---|
| 275 |                     $excerpt = iconv($charset,'UTF-8',$excerpt); | 
|---|
| 276 |                     $blog_name = iconv($charset,'UTF-8',$blog_name); | 
|---|
| 277 |                } | 
|---|
| 278 |                 | 
|---|
| 279 |                $title = trim(html::clean($title)); | 
|---|
| 280 |                $title = html::decodeEntities($title); | 
|---|
| 281 |                $title = html::escapeHTML($title); | 
|---|
| 282 |                $title = text::cutString($title,60); | 
|---|
| 283 |                 | 
|---|
| 284 |                $excerpt = trim(html::clean($excerpt)); | 
|---|
| 285 |                $excerpt = html::decodeEntities($excerpt); | 
|---|
| 286 |                $excerpt = preg_replace('/\s+/ms',' ',$excerpt); | 
|---|
| 287 |                $excerpt = text::cutString($excerpt,252);  | 
|---|
| 288 |                $excerpt = html::escapeHTML($excerpt).'...'; | 
|---|
| 289 |                 | 
|---|
| 290 |                $blog_name = trim(html::clean($blog_name)); | 
|---|
| 291 |                $blog_name = html::decodeEntities($blog_name); | 
|---|
| 292 |                $blog_name = html::escapeHTML($blog_name); | 
|---|
| 293 |                $blog_name = text::cutString($blog_name,60); | 
|---|
| 294 |                 | 
|---|
| 295 |                try | 
|---|
| 296 |                { | 
|---|
| 297 |                     $this->addBacklink($post_id, $url, $blog_name, $title, $excerpt, $comment); | 
|---|
| 298 |                } | 
|---|
| 299 |                catch (Exception $e) | 
|---|
| 300 |                { | 
|---|
| 301 |                     $err = 1; | 
|---|
| 302 |                     $msg = 'Something went wrong : '.$e->getMessage(); | 
|---|
| 303 |                } | 
|---|
| 304 |           } | 
|---|
| 305 |            | 
|---|
| 306 |           $resp = | 
|---|
| 307 |           '<?xml version="1.0" encoding="utf-8"?>'."\n". | 
|---|
| 308 |           "<response>\n". | 
|---|
| 309 |           '  <error>'.(integer) $err."</error>\n"; | 
|---|
| 310 |            | 
|---|
| 311 |           if ($msg) { | 
|---|
| 312 |                $resp .= '  <message>'.$msg."</message>\n"; | 
|---|
| 313 |           } | 
|---|
| 314 |            | 
|---|
| 315 |           if (!empty($_POST['__debug'])) { | 
|---|
| 316 |                $resp .=  | 
|---|
| 317 |                "  <debug>\n". | 
|---|
| 318 |                '    <title>'.$title."</title>\n". | 
|---|
| 319 |                '    <excerpt>'.$excerpt."</excerpt>\n". | 
|---|
| 320 |                '    <url>'.$url."</url>\n". | 
|---|
| 321 |                '    <blog_name>'.$blog_name."</blog_name>\n". | 
|---|
| 322 |                '    <charset>'.$charset."</charset>\n". | 
|---|
| 323 |                '    <comment>'.$comment."</comment>\n". | 
|---|
| 324 |                "  </debug>\n"; | 
|---|
| 325 |           } | 
|---|
| 326 |            | 
|---|
| 327 |           echo $resp."</response>"; | 
|---|
| 328 |      } | 
|---|
| 329 |      //@} | 
|---|
| 330 |  | 
|---|
| 331 |      /// @name Receive pingbacks | 
|---|
| 332 |      //@{ | 
|---|
| 333 |      /** | 
|---|
| 334 |      Receives a pingback and insert it as a comment of given post. | 
|---|
| 335 |       | 
|---|
| 336 |      @param    from_url       <b>string</b>       Source URL | 
|---|
| 337 |      @param    to_url              <b>string</b>       Target URL | 
|---|
| 338 |      */ | 
|---|
| 339 |      public function receive_pb($from_url, $to_url) | 
|---|
| 340 |      { | 
|---|
| 341 |           $reg = '!^'.preg_quote($this->core->blog->url).'(.*)!'; | 
|---|
| 342 |           $type = $args = $next = ''; | 
|---|
| 343 |            | 
|---|
| 344 |           # Are you dumb? | 
|---|
| 345 |           if (!preg_match($reg, $to_url, $m)) { | 
|---|
| 346 |                throw new Exception(__('Any chance you ping one of my contents? No? Really?'), 0); | 
|---|
| 347 |           } | 
|---|
| 348 |            | 
|---|
| 349 |           # Does the targeted URL look like a registered post type?  | 
|---|
| 350 |           $url_part = $m[1]; | 
|---|
| 351 |           $p_type = ''; | 
|---|
| 352 |           $post_types = $this->core->getPostTypes(); | 
|---|
| 353 |           foreach ($post_types as $k => $v) { | 
|---|
| 354 |                $reg = '!^'.preg_quote(str_replace('%s', '', $v['public_url'])).'(.*)!'; | 
|---|
| 355 |                if (preg_match($reg, $url_part, $n)) { | 
|---|
| 356 |                     $p_type = $k; | 
|---|
| 357 |                     $post_url = $n[1]; | 
|---|
| 358 |                     break; | 
|---|
| 359 |                } | 
|---|
| 360 |           } | 
|---|
| 361 |            | 
|---|
| 362 |           if (empty($p_type)) { | 
|---|
| 363 |                throw new Exception(__('Sorry but you can not ping this type of content.'), 33); | 
|---|
| 364 |           } | 
|---|
| 365 |  | 
|---|
| 366 |           # Time to see if we've got a winner... | 
|---|
| 367 |           $params = array( | 
|---|
| 368 |                'post_type' => $p_type, | 
|---|
| 369 |                'post_url' => $post_url, | 
|---|
| 370 |           ); | 
|---|
| 371 |           $posts = $this->core->blog->getPosts($params); | 
|---|
| 372 |            | 
|---|
| 373 |           # Missed!  | 
|---|
| 374 |           if ($posts->isEmpty()) { | 
|---|
| 375 |                throw new Exception(__('Oops. Kinda "not found" stuff. Please check the target URL twice.'), 33); | 
|---|
| 376 |           } | 
|---|
| 377 |            | 
|---|
| 378 |           # Nice try. But, sorry, no. | 
|---|
| 379 |           if (!$posts->trackbacksActive()) { | 
|---|
| 380 |                throw new Exception(__('Sorry, dude. This entry does not accept pingback at the moment.'), 33); | 
|---|
| 381 |           } | 
|---|
| 382 |  | 
|---|
| 383 |           if ($this->pingAlreadyDone($posts->post_id, $from_url)) { | 
|---|
| 384 |                throw new Exception(__('Don\'t repeat yourself, please.'), 48); | 
|---|
| 385 |           } | 
|---|
| 386 |            | 
|---|
| 387 |           # OK. We've found our champion. Time to check the remote part. | 
|---|
| 388 |           try { | 
|---|
| 389 |                $http = self::initHttp($from_url, $from_path); | 
|---|
| 390 |                 | 
|---|
| 391 |                # First round : just to be sure the ping comes from an acceptable resource type. | 
|---|
| 392 |                $http->setHeadersOnly(true); | 
|---|
| 393 |                $http->get($from_path); | 
|---|
| 394 |                $c_type = explode(';', $http->getHeader('content-type')); | 
|---|
| 395 |  | 
|---|
| 396 |                # Bad luck. Bye, bye... | 
|---|
| 397 |                if (!in_array($c_type[0],array('text/html', 'application/xhtml+xml'))) { | 
|---|
| 398 |                     throw new Exception(__('Your source URL does not look like a supported content type. Sorry. Bye, bye!'), 0); | 
|---|
| 399 |                } | 
|---|
| 400 |                 | 
|---|
| 401 |                # Second round : let's go fetch and parse the remote content | 
|---|
| 402 |                $http->setHeadersOnly(false); | 
|---|
| 403 |                $http->get($from_path); | 
|---|
| 404 |                $remote_content = $http->getContent(); | 
|---|
| 405 |  | 
|---|
| 406 |                $charset = self::getCharsetFromRequest($http->getHeader('content-type')); | 
|---|
| 407 |  | 
|---|
| 408 |                if (!$charset) { | 
|---|
| 409 |                     $charset = self::detectCharset($remote_content); | 
|---|
| 410 |                } | 
|---|
| 411 |                 | 
|---|
| 412 |                if (strtolower($charset) != 'utf-8') { | 
|---|
| 413 |                     $remote_content = iconv($charset,'UTF-8',$remote_content); | 
|---|
| 414 |                } | 
|---|
| 415 |                 | 
|---|
| 416 |                # We want a title... | 
|---|
| 417 |                if (!preg_match('!<title>([^<].*?)</title>!mis', $remote_content, $m)) { | 
|---|
| 418 |                     throw new Exception(__('Where\'s your title?'), 0); | 
|---|
| 419 |                } | 
|---|
| 420 |                $title = trim(html::clean($m[1])); | 
|---|
| 421 |                $title = html::decodeEntities($title); | 
|---|
| 422 |                $title = html::escapeHTML($title); | 
|---|
| 423 |                $title = text::cutString($title,60); | 
|---|
| 424 |                 | 
|---|
| 425 |                preg_match('!<body[^>]*?>(.*)?</body>!msi', $remote_content, $m); | 
|---|
| 426 |                $source = $m[1]; | 
|---|
| 427 |                $source = preg_replace('![\r\n\s]+!ms',' ',$source); | 
|---|
| 428 |                $source = preg_replace( "/<\/*(h\d|p|th|td|li|dt|dd|pre|caption|input|textarea|button)[^>]*>/", "\n\n", $source ); | 
|---|
| 429 |                $source = strip_tags($source, '<a>'); | 
|---|
| 430 |                $source = explode("\n\n",$source); | 
|---|
| 431 |                 | 
|---|
| 432 |                $excerpt = ''; | 
|---|
| 433 |                foreach ($source as $line) { | 
|---|
| 434 |                     if (strpos($line, $to_url) !== false) { | 
|---|
| 435 |                          if (preg_match("!<a[^>]+?".$to_url."[^>]*>([^>]+?)</a>!", $line, $m)) { | 
|---|
| 436 |                               $excerpt = strip_tags($line); | 
|---|
| 437 |                               break; | 
|---|
| 438 |                          } | 
|---|
| 439 |                     } | 
|---|
| 440 |                } | 
|---|
| 441 |                if ($excerpt) { | 
|---|
| 442 |                     $excerpt = '(…) '.text::cutString(html::escapeHTML($excerpt),200).' (…)'; | 
|---|
| 443 |                } | 
|---|
| 444 |                else { | 
|---|
| 445 |                     $excerpt = '(…)'; | 
|---|
| 446 |                } | 
|---|
| 447 |  | 
|---|
| 448 |                $this->addBacklink($posts->post_id, $from_url, '', $title, $excerpt, $comment); | 
|---|
| 449 |           } | 
|---|
| 450 |           catch (Exception $e) { | 
|---|
| 451 |                throw new Exception(__('Sorry, an internal problem has occured.'), 0); | 
|---|
| 452 |           } | 
|---|
| 453 |            | 
|---|
| 454 |           return __('Thanks, mate. It was a pleasure.'); | 
|---|
| 455 |      } | 
|---|
| 456 |      //@} | 
|---|
| 457 |       | 
|---|
| 458 |      private static function initHttp($url,&$path) | 
|---|
| 459 |      { | 
|---|
| 460 |           $client = netHttp::initClient($url,$path); | 
|---|
| 461 |           $client->setTimeout(5); | 
|---|
| 462 |           $client->setUserAgent('Dotclear - http://www.dotclear.org/'); | 
|---|
| 463 |           $client->useGzip(false); | 
|---|
| 464 |           $client->setPersistReferers(false); | 
|---|
| 465 |            | 
|---|
| 466 |           return $client; | 
|---|
| 467 |      } | 
|---|
| 468 |       | 
|---|
| 469 |      private static function getCharsetFromRequest($header = '') | 
|---|
| 470 |      { | 
|---|
| 471 |           if (!$header && isset($_SERVER['CONTENT_TYPE'])) { | 
|---|
| 472 |                $header = $_SERVER['CONTENT_TYPE']; | 
|---|
| 473 |           } | 
|---|
| 474 |            | 
|---|
| 475 |           if ($header) { | 
|---|
| 476 |                if (preg_match('|charset=([a-zA-Z0-9-]+)|',$header,$m)) { | 
|---|
| 477 |                     return $m[1]; | 
|---|
| 478 |                } | 
|---|
| 479 |           } | 
|---|
| 480 |            | 
|---|
| 481 |           return null; | 
|---|
| 482 |      } | 
|---|
| 483 |  | 
|---|
| 484 |      private static function detectCharset($string) | 
|---|
| 485 |      { | 
|---|
| 486 |           return mb_detect_encoding($remote_content, | 
|---|
| 487 |                     'UTF-8,ISO-8859-1,ISO-8859-2,ISO-8859-3,'. | 
|---|
| 488 |                     'ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,'. | 
|---|
| 489 |                     'ISO-8859-9,ISO-8859-10,ISO-8859-13,ISO-8859-14,ISO-8859-15'); | 
|---|
| 490 |      } | 
|---|
| 491 |            | 
|---|
| 492 |      /// @name Trackbacks auto discovery | 
|---|
| 493 |      //@{ | 
|---|
| 494 |      /** | 
|---|
| 495 |      Returns an array containing all discovered trackbacks URLs in | 
|---|
| 496 |      <var>$text</var>. | 
|---|
| 497 |       | 
|---|
| 498 |      @param    text      <b>string</b>       Input text | 
|---|
| 499 |      @return   <b>array</b> | 
|---|
| 500 |      */ | 
|---|
| 501 |      public function discover($text) | 
|---|
| 502 |      { | 
|---|
| 503 |           $res = array(); | 
|---|
| 504 |            | 
|---|
| 505 |           foreach ($this->getTextLinks($text) as $link) | 
|---|
| 506 |           { | 
|---|
| 507 |                if (($url = $this->getPingURL($link)) !== null) { | 
|---|
| 508 |                     $res[] = $url; | 
|---|
| 509 |                } | 
|---|
| 510 |           } | 
|---|
| 511 |            | 
|---|
| 512 |           return $res; | 
|---|
| 513 |      } | 
|---|
| 514 |      //@} | 
|---|
| 515 |       | 
|---|
| 516 |      private function getTextLinks($text) | 
|---|
| 517 |      { | 
|---|
| 518 |           $res = array(); | 
|---|
| 519 |            | 
|---|
| 520 |           # href attribute on "a" tags | 
|---|
| 521 |           if (preg_match_all('/<a ([^>]+)>/ms', $text, $match, PREG_SET_ORDER)) | 
|---|
| 522 |           { | 
|---|
| 523 |                for ($i = 0; $i<count($match); $i++) | 
|---|
| 524 |                { | 
|---|
| 525 |                     if (preg_match('/href="((https?:\/)?\/[^"]+)"/ms', $match[$i][1], $matches)) { | 
|---|
| 526 |                          $res[$matches[1]] = 1; | 
|---|
| 527 |                     } | 
|---|
| 528 |                } | 
|---|
| 529 |           } | 
|---|
| 530 |           unset($match); | 
|---|
| 531 |            | 
|---|
| 532 |           # cite attributes on "blockquote" and "q" tags | 
|---|
| 533 |           if (preg_match_all('/<(blockquote|q) ([^>]+)>/ms', $text, $match, PREG_SET_ORDER)) | 
|---|
| 534 |           { | 
|---|
| 535 |                for ($i = 0; $i<count($match); $i++) | 
|---|
| 536 |                { | 
|---|
| 537 |                     if (preg_match('/cite="((https?:\/)?\/[^"]+)"/ms', $match[$i][2], $matches)) { | 
|---|
| 538 |                          $res[$matches[1]] = 1; | 
|---|
| 539 |                     } | 
|---|
| 540 |                } | 
|---|
| 541 |           } | 
|---|
| 542 |            | 
|---|
| 543 |           return array_keys($res); | 
|---|
| 544 |      } | 
|---|
| 545 |       | 
|---|
| 546 |      private function getPingURL($url) | 
|---|
| 547 |      { | 
|---|
| 548 |           if (strpos($url,'/') === 0) { | 
|---|
| 549 |                $url = http::getHost().$url; | 
|---|
| 550 |           } | 
|---|
| 551 |            | 
|---|
| 552 |           try | 
|---|
| 553 |           { | 
|---|
| 554 |                $http = self::initHttp($url,$path); | 
|---|
| 555 |                $http->get($path); | 
|---|
| 556 |                $page_content = $http->getContent(); | 
|---|
| 557 |                $pb_url = $http->getHeader('x-pingback'); | 
|---|
| 558 |           } | 
|---|
| 559 |           catch (Exception $e) | 
|---|
| 560 |           { | 
|---|
| 561 |                return false; | 
|---|
| 562 |           } | 
|---|
| 563 |            | 
|---|
| 564 |           # If we've got a X-Pingback header and it's a valid URL, it will be enough | 
|---|
| 565 |           if ($pb_url && filter_var($pb_url,FILTER_VALIDATE_URL) && preg_match('!^https?:!',$pb_url)) { | 
|---|
| 566 |                return $pb_url.'|'.$url; | 
|---|
| 567 |           } | 
|---|
| 568 |            | 
|---|
| 569 |           # No X-Pingback header. A link rel=pingback, maybe ? | 
|---|
| 570 |           $pattern_pingback = '!<link rel="pingback" href="(.*?)"( /)?>!msi'; | 
|---|
| 571 |            | 
|---|
| 572 |           if (preg_match($pattern_pingback,$page_content,$m)) { | 
|---|
| 573 |                $pb_url = $m[1]; | 
|---|
| 574 |                if (filter_var($pb_url,FILTER_VALIDATE_URL) && preg_match('!^https?:!',$pb_url)) { | 
|---|
| 575 |                     return $pb_url.'|'.$url; | 
|---|
| 576 |                } | 
|---|
| 577 |           } | 
|---|
| 578 |  | 
|---|
| 579 |           # No pingback ? OK, let's check for a trackback data chunk... | 
|---|
| 580 |           $pattern_rdf = | 
|---|
| 581 |           '/<rdf:RDF.*?>.*?'. | 
|---|
| 582 |           '<rdf:Description\s+(.*?)\/>'. | 
|---|
| 583 |           '.*?<\/rdf:RDF>'. | 
|---|
| 584 |           '/msi'; | 
|---|
| 585 |            | 
|---|
| 586 |           preg_match_all($pattern_rdf,$page_content,$rdf_all,PREG_SET_ORDER); | 
|---|
| 587 |            | 
|---|
| 588 |           $url_path = parse_url($url, PHP_URL_PATH); | 
|---|
| 589 |           $sanitized_url = str_replace($url_path, html::sanitizeURL($url_path), $url); | 
|---|
| 590 |            | 
|---|
| 591 |           for ($i=0; $i<count($rdf_all); $i++) | 
|---|
| 592 |           { | 
|---|
| 593 |                $rdf = $rdf_all[$i][1]; | 
|---|
| 594 |                if (preg_match('/dc:identifier="'.preg_quote($url,'/').'"/msi',$rdf) || | 
|---|
| 595 |                     preg_match('/dc:identifier="'.preg_quote($sanitized_url,'/').'"/msi',$rdf)) { | 
|---|
| 596 |                     if (preg_match('/trackback:ping="(.*?)"/msi',$rdf,$tb_link)) { | 
|---|
| 597 |                          return $tb_link[1]; | 
|---|
| 598 |                     } | 
|---|
| 599 |                } | 
|---|
| 600 |           } | 
|---|
| 601 |            | 
|---|
| 602 |           return null; | 
|---|
| 603 |      } | 
|---|
| 604 | } | 
|---|
| 605 | ?> | 
|---|