Dotclear

source: inc/core/class.dc.trackback.php @ 3874:ab8368569446

Revision 3874:ab8368569446, 27.0 KB checked in by franck <carnet.franck.paul@…>, 7 years ago (diff)

short notation for array (array() → [])

Line 
1<?php
2/**
3 * @brief Trackbacks/Pingbacks sender and server
4 *
5 * Sends and receives trackbacks/pingbacks.
6 * Also handles trackbacks/pingbacks auto discovery.
7 *
8 * @package Dotclear
9 * @subpackage Core
10 *
11 * @copyright Olivier Meunier & Association Dotclear
12 * @copyright GPL-2.0-only
13 */
14
15if (!defined('DC_RC_PATH')) {return;}
16
17class dcTrackback
18{
19    public $core;  ///< <b>dcCore</b> dcCore instance
20    public $table; ///< <b>string</b> done pings table name
21
22    /**
23    Object constructor
24
25    @param    core        <b>dcCore</b>        dcCore instance
26     */
27    public function __construct($core)
28    {
29        $this->core  = &$core;
30        $this->con   = &$this->core->con;
31        $this->table = $this->core->prefix . 'ping';
32    }
33
34    /// @name Send
35    //@{
36    /**
37    Get all pings sent for a given post.
38
39    @param    post_id    <b>integer</b>        Post ID
40    @return    <b>record</b>
41     */
42    public function getPostPings($post_id)
43    {
44        $strReq = 'SELECT ping_url, ping_dt ' .
45        'FROM ' . $this->table . ' ' .
46        'WHERE post_id = ' . (integer) $post_id;
47
48        return $this->con->select($strReq);
49    }
50
51    /**
52    Sends a ping to given <var>$url</var>.
53
54    @param    url            <b>string</b>        URL to ping
55    @param    post_id        <b>integer</b>        Post ID
56    @param    post_title    <b>string</b>        Post title
57    @param    post_excerpt    <b>string</b>        Post excerpt
58    @param    post_url        <b>string</b>        Post URL
59     */
60    public function ping($url, $post_id, $post_title, $post_excerpt, $post_url)
61    {
62        if ($this->core->blog === null) {
63            return false;
64        }
65
66        $post_id = (integer) $post_id;
67
68        # Check for previously done trackback
69        $strReq = 'SELECT post_id, ping_url FROM ' . $this->table . ' ' .
70        'WHERE post_id = ' . $post_id . ' ' .
71        "AND ping_url = '" . $this->con->escape($url) . "' ";
72
73        $rs = $this->con->select($strReq);
74
75        if (!$rs->isEmpty()) {
76            throw new Exception(sprintf(__('%s has still been pinged'), $url));
77        }
78
79        $ping_parts = explode('|', $url);
80        # Maybe a webmention
81        if (count($ping_parts) == 3) {
82            $payload = http_build_query([
83                'source' => $post_url,
84                'target' => $ping_parts[1]
85            ]);
86
87            try {
88                $http = self::initHttp($ping_parts[0], $path);
89                $http->setMoreHeader('Content-Type: application/x-www-form-urlencoded');
90                $http->post($path, $payload, 'UTF-8');
91
92                # Read response status
93                $status     = $http->getStatus();
94                $ping_error = '0';
95            } catch (Exception $e) {
96                throw new Exception(__('Unable to ping URL'));
97            }
98
99            if (!in_array($status, ['200', '201', '202'])) {
100                $ping_error = $http->getStatus();
101                $ping_msg   = __('Bad server response code');
102            }
103        }
104        # No, let's walk by the trackback way
105        elseif (count($ping_parts) < 2) {
106            $data = [
107                'title'     => $post_title,
108                'excerpt'   => $post_excerpt,
109                'url'       => $post_url,
110                'blog_name' => trim(html::escapeHTML(html::clean($this->core->blog->name)))
111                //,'__debug' => false
112            ];
113
114            # Ping
115            try {
116                $http = self::initHttp($url, $path);
117                $http->post($path, $data, 'UTF-8');
118                $res = $http->getContent();
119            } catch (Exception $e) {
120                throw new Exception(__('Unable to ping URL'));
121            }
122
123            $pattern =
124                '|<response>.*<error>(.*)</error>(.*)' .
125                '(<message>(.*)</message>(.*))?' .
126                '</response>|msU';
127
128            if (!preg_match($pattern, $res, $match)) {
129                throw new Exception(sprintf(__('%s is not a ping URL'), $url));
130            }
131
132            $ping_error = trim($match[1]);
133            $ping_msg   = (!empty($match[4])) ? $match[4] : '';
134        }
135        # Damnit ! Let's play pingback
136        else {
137            try {
138                $xmlrpc     = new xmlrpcClient($ping_parts[0]);
139                $res        = $xmlrpc->query('pingback.ping', $post_url, $ping_parts[1]);
140                $ping_error = '0';
141            } catch (xmlrpcException $e) {
142                $ping_error = $e->getCode();
143                $ping_msg   = $e->getMessage();
144            } catch (Exception $e) {
145                throw new Exception(__('Unable to ping URL'));
146            }
147        }
148
149        if ($ping_error != '0') {
150            throw new Exception(sprintf(__('%s, ping error:'), $url) . ' ' . $ping_msg);
151        } else {
152            # Notify ping result in database
153            $cur           = $this->con->openCursor($this->table);
154            $cur->post_id  = $post_id;
155            $cur->ping_url = $url;
156            $cur->ping_dt  = date('Y-m-d H:i:s');
157
158            $cur->insert();
159        }
160    }
161    //@}
162
163    /// @name Receive
164    //@{
165    /**
166    Receives a trackback and insert it as a comment of given post.
167
168    @param    post_id        <b>integer</b>        Post ID
169     */
170    public function receiveTrackback($post_id)
171    {
172        header('Content-Type: text/xml; charset=UTF-8');
173        if (empty($_POST)) {
174            http::head(405, 'Method Not Allowed');
175            echo
176                '<?xml version="1.0" encoding="utf-8"?>' . "\n" .
177                "<response>\n" .
178                "  <error>1</error>\n" .
179                "  <message>POST request needed</message>\n" .
180                "</response>";
181            return;
182        }
183
184        $post_id = (integer) $post_id;
185
186        $title     = !empty($_POST['title']) ? $_POST['title'] : '';
187        $excerpt   = !empty($_POST['excerpt']) ? $_POST['excerpt'] : '';
188        $url       = !empty($_POST['url']) ? $_POST['url'] : '';
189        $blog_name = !empty($_POST['blog_name']) ? $_POST['blog_name'] : '';
190        $charset   = '';
191        $comment   = '';
192
193        $err = false;
194        $msg = '';
195
196        if ($this->core->blog === null) {
197            $err = true;
198            $msg = 'No blog.';
199        } elseif ($url == '') {
200            $err = true;
201            $msg = 'URL parameter is required.';
202        } elseif ($blog_name == '') {
203            $err = true;
204            $msg = 'Blog name is required.';
205        }
206
207        if (!$err) {
208            $post = $this->core->blog->getPosts(['post_id' => $post_id, 'post_type' => '']);
209
210            if ($post->isEmpty()) {
211                $err = true;
212                $msg = 'No such post.';
213            } elseif (!$post->trackbacksActive()) {
214                $err = true;
215                $msg = 'Trackbacks are not allowed for this post or weblog.';
216            }
217
218            $url = trim(html::clean($url));
219            if ($this->pingAlreadyDone($post->post_id, $url)) {
220                $err = true;
221                $msg = 'The trackback has already been registered';
222            }
223        }
224
225        if (!$err) {
226            $charset = self::getCharsetFromRequest();
227
228            if (!$charset) {
229                $charset = self::detectCharset($title . ' ' . $excerpt . ' ' . $blog_name);
230            }
231
232            if (strtolower($charset) != 'utf-8') {
233                $title     = iconv($charset, 'UTF-8', $title);
234                $excerpt   = iconv($charset, 'UTF-8', $excerpt);
235                $blog_name = iconv($charset, 'UTF-8', $blog_name);
236            }
237
238            $title = trim(html::clean($title));
239            $title = html::decodeEntities($title);
240            $title = html::escapeHTML($title);
241            $title = text::cutString($title, 60);
242
243            $excerpt = trim(html::clean($excerpt));
244            $excerpt = html::decodeEntities($excerpt);
245            $excerpt = preg_replace('/\s+/ms', ' ', $excerpt);
246            $excerpt = text::cutString($excerpt, 252);
247            $excerpt = html::escapeHTML($excerpt) . '...';
248
249            $blog_name = trim(html::clean($blog_name));
250            $blog_name = html::decodeEntities($blog_name);
251            $blog_name = html::escapeHTML($blog_name);
252            $blog_name = text::cutString($blog_name, 60);
253
254            try {
255                $this->addBacklink($post_id, $url, $blog_name, $title, $excerpt, $comment);
256            } catch (Exception $e) {
257                $err = 1;
258                $msg = 'Something went wrong : ' . $e->getMessage();
259            }
260        }
261
262        $resp =
263        '<?xml version="1.0" encoding="utf-8"?>' . "\n" .
264        "<response>\n" .
265        '  <error>' . (integer) $err . "</error>\n";
266
267        if ($msg) {
268            $resp .= '  <message>' . $msg . "</message>\n";
269        }
270
271        if (!empty($_POST['__debug'])) {
272            $resp .=
273                "  <debug>\n" .
274                '    <title>' . $title . "</title>\n" .
275                '    <excerpt>' . $excerpt . "</excerpt>\n" .
276                '    <url>' . $url . "</url>\n" .
277                '    <blog_name>' . $blog_name . "</blog_name>\n" .
278                '    <charset>' . $charset . "</charset>\n" .
279                '    <comment>' . $comment . "</comment>\n" .
280                "  </debug>\n";
281        }
282
283        echo $resp . "</response>";
284    }
285
286    /**
287    Receives a pingback and insert it as a comment of given post.
288
289    @param    from_url        <b>string</b>        Source URL
290    @param    to_url            <b>string</b>        Target URL
291     */
292    public function receivePingback($from_url, $to_url)
293    {
294        try {
295            $posts = $this->getTargetPost($to_url);
296
297            if ($this->pingAlreadyDone($posts->post_id, $from_url)) {
298                throw new Exception(__('Don\'t repeat yourself, please.'), 48);
299            }
300
301            $remote_content = $this->getRemoteContent($from_url);
302
303            # We want a title...
304            if (!preg_match('!<title>([^<].*?)</title>!mis', $remote_content, $m)) {
305                throw new Exception(__('Where\'s your title?'), 0);
306            }
307            $title = trim(html::clean($m[1]));
308            $title = html::decodeEntities($title);
309            $title = html::escapeHTML($title);
310            $title = text::cutString($title, 60);
311
312            preg_match('!<body[^>]*?>(.*)?</body>!msi', $remote_content, $m);
313            $source = $m[1];
314            $source = preg_replace('![\r\n\s]+!ms', ' ', $source);
315            $source = preg_replace("/<\/*(h\d|p|th|td|li|dt|dd|pre|caption|input|textarea|button)[^>]*>/", "\n\n", $source);
316            $source = strip_tags($source, '<a>');
317            $source = explode("\n\n", $source);
318
319            $excerpt = '';
320            foreach ($source as $line) {
321                if (strpos($line, $to_url) !== false) {
322                    if (preg_match("!<a[^>]+?" . $to_url . "[^>]*>([^>]+?)</a>!", $line, $m)) {
323                        $excerpt = strip_tags($line);
324                        break;
325                    }
326                }
327            }
328            if ($excerpt) {
329                $excerpt = '(&#8230;) ' . text::cutString(html::escapeHTML($excerpt), 200) . ' (&#8230;)';
330            } else {
331                $excerpt = '(&#8230;)';
332            }
333
334            $this->addBacklink($posts->post_id, $from_url, '', $title, $excerpt, $comment);
335        } catch (Exception $e) {
336            throw new Exception(__('Sorry, an internal problem has occured.'), 0);
337        }
338
339        return __('Thanks, mate. It was a pleasure.');
340    }
341
342    /**
343    Receives a webmention and insert it as a comment of given post.
344
345    NB: plugin Fair Trackback check source content to find url.
346
347    @return    <b>null</b>    Null on success, else throw an exception
348     */
349    public function receiveWebmention()
350    {
351        $err = $post_id = false;
352        header('Content-Type: text/html; charset=UTF-8');
353
354        try {
355            # Check if post and target are valid URL
356            if (empty($_POST['source']) || empty($_POST['target'])) {
357                throw new Exception('Source or target is not valid', 0);
358            }
359
360            $from_url = urldecode($_POST['source']);
361            $to_url   = urldecode($_POST['target']);
362
363            self::checkURLs($from_url, $to_url);
364
365            # Try to find post
366            $posts   = $this->getTargetPost($to_url);
367            $post_id = $posts->post_id;
368
369            # Check if it's an updated mention
370            if ($this->pingAlreadyDone($post_id, $from_url)) {
371                $this->delBacklink($post_id, $from_url);
372            }
373
374            # Create a comment for received webmention
375            $remote_content = $this->getRemoteContent($from_url);
376
377            # We want a title...
378            if (!preg_match('!<title>([^<].*?)</title>!mis', $remote_content, $m)) {
379                throw new Exception(__('Where\'s your title?'), 0);
380            }
381            $title = trim(html::clean($m[1]));
382            $title = html::decodeEntities($title);
383            $title = html::escapeHTML($title);
384            $title = text::cutString($title, 60);
385
386            preg_match('!<body[^>]*?>(.*)?</body>!msi', $remote_content, $m);
387            $source = $m[1];
388            $source = preg_replace('![\r\n\s]+!ms', ' ', $source);
389            $source = preg_replace("/<\/*(h\d|p|th|td|li|dt|dd|pre|caption|input|textarea|button)[^>]*>/", "\n\n", $source);
390            $source = strip_tags($source, '<a>');
391            $source = explode("\n\n", $source);
392
393            $excerpt = '';
394            foreach ($source as $line) {
395                if (strpos($line, $to_url) !== false) {
396                    if (preg_match("!<a[^>]+?" . $to_url . "[^>]*>([^>]+?)</a>!", $line, $m)) {
397                        $excerpt = strip_tags($line);
398                        break;
399                    }
400                }
401            }
402            if ($excerpt) {
403                $excerpt = '(&#8230;) ' . text::cutString(html::escapeHTML($excerpt), 200) . ' (&#8230;)';
404            } else {
405                $excerpt = '(&#8230;)';
406            }
407
408            $this->addBacklink($post_id, $from_url, '', $title, $excerpt, $comment);
409
410            # All done, thanks
411            $code = $this->core->blog->settings->system->trackbacks_pub ? 200 : 202;
412            http::head($code);
413            return;
414        } catch (Exception $e) {
415            $err = $e->getMessage();
416        }
417
418        http::head(400);
419        echo $err ?: 'Something went wrong.';
420        return;
421    }
422
423    /**
424    Check if a post previously received a ping a from an URL.
425
426    @param    post_id    <b>integer</b>        Post ID
427    @param    from_url    <b>string</b>        Source URL
428    @return    <b>boolean</b>
429     */
430    private function pingAlreadyDone($post_id, $from_url)
431    {
432        $params = [
433            'post_id'           => $post_id,
434            'comment_site'      => $from_url,
435            'comment_trackback' => 1
436        ];
437
438        $rs = $this->core->blog->getComments($params, true);
439        if ($rs && !$rs->isEmpty()) {
440            return ($rs->f(0));
441        }
442
443        return false;
444    }
445
446    /**
447    Create a comment marked as trackback for a given post.
448
449    @param    post_id    <b>integer</b>        Post ID
450    @param    url        <b>string</b>        Discovered URL
451    @param    blog name    <b>string</b>        Source blog name
452    @param    title    <b>string</b>        Comment title
453    @param    excerpt    <b>string</b>        Source excerpt
454    @param    comment    <b>string</b>        Comment content
455     */
456    private function addBacklink($post_id, $url, $blog_name, $title, $excerpt, &$comment)
457    {
458        if (empty($blog_name)) {
459            // Let use title as text link for this backlink
460            $blog_name = ($title ?: 'Anonymous blog');
461        }
462
463        $comment =
464            "<!-- TB -->\n" .
465            '<p><strong>' . ($title ?: $blog_name) . "</strong></p>\n" .
466            '<p>' . $excerpt . '</p>';
467
468        $cur                    = $this->core->con->openCursor($this->core->prefix . 'comment');
469        $cur->comment_author    = (string) $blog_name;
470        $cur->comment_site      = (string) $url;
471        $cur->comment_content   = (string) $comment;
472        $cur->post_id           = $post_id;
473        $cur->comment_trackback = 1;
474        $cur->comment_status    = $this->core->blog->settings->system->trackbacks_pub ? 1 : -1;
475        $cur->comment_ip        = http::realIP();
476
477        # --BEHAVIOR-- publicBeforeTrackbackCreate
478        $this->core->callBehavior('publicBeforeTrackbackCreate', $cur);
479        if ($cur->post_id) {
480            $comment_id = $this->core->blog->addComment($cur);
481
482            # --BEHAVIOR-- publicAfterTrackbackCreate
483            $this->core->callBehavior('publicAfterTrackbackCreate', $cur, $comment_id);
484        }
485    }
486
487    /**
488    Delete previously received comment made from an URL for a given post.
489
490    @param    post_id    <b>integer</b>        Post ID
491    @param    url        <b>string</b>        Source URL
492     */
493    private function delBacklink($post_id, $url)
494    {
495        $this->con->execute(
496            'DELETE FROM ' . $this->core->prefix . 'comment ' .
497            'WHERE post_id = ' . ((integer) $post_id) . ' ' .
498            "AND comment_site = '" . $this->core->con->escape((string) $url) . "' " .
499            'AND comment_trackback = 1 '
500        );
501    }
502
503    /**
504    Find Charset from HTTP headers.
505
506    @param    header    <b>string</b>        Source header
507    @return    <b>string</b>
508     */
509    private static function getCharsetFromRequest($header = '')
510    {
511        if (!$header && isset($_SERVER['CONTENT_TYPE'])) {
512            $header = $_SERVER['CONTENT_TYPE'];
513        }
514
515        if ($header) {
516            if (preg_match('|charset=([a-zA-Z0-9-]+)|', $header, $m)) {
517                return $m[1];
518            }
519        }
520
521        return;
522    }
523
524    /**
525    Detect encoding.
526
527    @param    content        <b>string</b>        Source URL
528    @return    <b>string</b>
529     */
530    private static function detectCharset($content)
531    {
532        return mb_detect_encoding($content,
533            'UTF-8,ISO-8859-1,ISO-8859-2,ISO-8859-3,' .
534            'ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,' .
535            'ISO-8859-9,ISO-8859-10,ISO-8859-13,ISO-8859-14,ISO-8859-15');
536    }
537
538    /**
539    Retreive local post from a given URL
540
541    @param    to_url        <b>string</b>        Target URL
542    @return    <b>string</b>
543     */
544    private function getTargetPost($to_url)
545    {
546        $reg  = '!^' . preg_quote($this->core->blog->url) . '(.*)!';
547        $type = $args = $next = '';
548
549        # Are you dumb?
550        if (!preg_match($reg, $to_url, $m)) {
551            throw new Exception(__('Any chance you ping one of my contents? No? Really?'), 0);
552        }
553
554        # Does the targeted URL look like a registered post type?
555        $url_part   = $m[1];
556        $p_type     = '';
557        $post_types = $this->core->getPostTypes();
558        foreach ($post_types as $k => $v) {
559            $reg = '!^' . preg_quote(str_replace('%s', '', $v['public_url'])) . '(.*)!';
560            if (preg_match($reg, $url_part, $n)) {
561                $p_type   = $k;
562                $post_url = $n[1];
563                break;
564            }
565        }
566
567        if (empty($p_type)) {
568            throw new Exception(__('Sorry but you can not ping this type of content.'), 33);
569        }
570
571        # Time to see if we've got a winner...
572        $params = [
573            'post_type' => $p_type,
574            'post_url'  => $post_url
575        ];
576        $posts = $this->core->blog->getPosts($params);
577
578        # Missed!
579        if ($posts->isEmpty()) {
580            throw new Exception(__('Oops. Kinda "not found" stuff. Please check the target URL twice.'), 33);
581        }
582
583        # Nice try. But, sorry, no.
584        if (!$posts->trackbacksActive()) {
585            throw new Exception(__('Sorry, dude. This entry does not accept pingback at the moment.'), 33);
586        }
587
588        return $posts;
589    }
590
591    /**
592    Returns content of a distant page
593
594    @param    from_url        <b>string</b>        Target URL
595    @return    <b>string</b>
596     */
597    private function getRemoteContent($from_url)
598    {
599        $http = self::initHttp($from_url, $from_path);
600
601        # First round : just to be sure the ping comes from an acceptable resource type.
602        $http->setHeadersOnly(true);
603        $http->get($from_path);
604        $c_type = explode(';', $http->getHeader('content-type'));
605
606        # Bad luck. Bye, bye...
607        if (!in_array($c_type[0], ['text/html', 'application/xhtml+xml'])) {
608            throw new Exception(__('Your source URL does not look like a supported content type. Sorry. Bye, bye!'), 0);
609        }
610
611        # Second round : let's go fetch and parse the remote content
612        $http->setHeadersOnly(false);
613        $http->get($from_path);
614        $remote_content = $http->getContent();
615
616        # Convert content charset
617        $charset = self::getCharsetFromRequest($http->getHeader('content-type'));
618        if (!$charset) {
619            $charset = self::detectCharset($remote_content);
620        }
621        if (strtolower($charset) != 'utf-8') {
622            $remote_content = iconv($charset, 'UTF-8', $remote_content);
623        }
624
625        return $remote_content;
626    }
627    //@}
628
629    /// @name Discover
630    //@{
631    /**
632    Returns an array containing all discovered trackbacks URLs in
633    <var>$text</var>.
634
635    @param    text        <b>string</b>        Input text
636    @return    <b>array</b>
637     */
638    public function discover($text)
639    {
640        $res = [];
641
642        foreach ($this->getTextLinks($text) as $link) {
643            if (($url = $this->getPingURL($link)) !== null) {
644                $res[] = $url;
645            }
646        }
647
648        return $res;
649    }
650
651    /**
652    Find links into a text.
653
654    @param    text        <b>string</b>        Text to scan
655    @return    <b>array</b>
656     */
657    private function getTextLinks($text)
658    {
659        $res = [];
660
661        # href attribute on "a" tags
662        if (preg_match_all('/<a ([^>]+)>/ms', $text, $match, PREG_SET_ORDER)) {
663            for ($i = 0; $i < count($match); $i++) {
664                if (preg_match('/href="((https?:\/)?\/[^"]+)"/ms', $match[$i][1], $matches)) {
665                    $res[$matches[1]] = 1;
666                }
667            }
668        }
669        unset($match);
670
671        # cite attributes on "blockquote" and "q" tags
672        if (preg_match_all('/<(blockquote|q) ([^>]+)>/ms', $text, $match, PREG_SET_ORDER)) {
673            for ($i = 0; $i < count($match); $i++) {
674                if (preg_match('/cite="((https?:\/)?\/[^"]+)"/ms', $match[$i][2], $matches)) {
675                    $res[$matches[1]] = 1;
676                }
677            }
678        }
679
680        return array_keys($res);
681    }
682
683    /**
684    Check remote header/content to find api trace.
685
686    @param    url        <b>string</b>        URL to scan
687    @return    <b>string</b>
688     */
689    private function getPingURL($url)
690    {
691        if (strpos($url, '/') === 0) {
692            $url = http::getHost() . $url;
693        }
694
695        try {
696            $http = self::initHttp($url, $path);
697            $http->get($path);
698            $page_content = $http->getContent();
699            $pb_url       = $http->getHeader('x-pingback');
700            $wm_url       = $http->getHeader('link');
701        } catch (Exception $e) {
702            return false;
703        }
704
705        # Let's check for an elderly trackback data chunk...
706        $pattern_rdf =
707            '/<rdf:RDF.*?>.*?' .
708            '<rdf:Description\s+(.*?)\/>' .
709            '.*?<\/rdf:RDF>' .
710            '/msi';
711
712        preg_match_all($pattern_rdf, $page_content, $rdf_all, PREG_SET_ORDER);
713
714        $url_path      = parse_url($url, PHP_URL_PATH);
715        $sanitized_url = str_replace($url_path, html::sanitizeURL($url_path), $url);
716
717        for ($i = 0; $i < count($rdf_all); $i++) {
718            $rdf = $rdf_all[$i][1];
719            if (preg_match('/dc:identifier="' . preg_quote($url, '/') . '"/msi', $rdf) ||
720                preg_match('/dc:identifier="' . preg_quote($sanitized_url, '/') . '"/msi', $rdf)) {
721                if (preg_match('/trackback:ping="(.*?)"/msi', $rdf, $tb_link)) {
722                    return $tb_link[1];
723                }
724            }
725        }
726
727        # No trackback ? OK, let see if we've got a X-Pingback header and it's a valid URL, it will be enough
728        if ($pb_url && filter_var($pb_url, FILTER_VALIDATE_URL) && preg_match('!^https?:!', $pb_url)) {
729            return $pb_url . '|' . $url;
730        }
731
732        # No X-Pingback header. A link rel=pingback, maybe ?
733        $pattern_pingback = '!<link rel="pingback" href="(.*?)"( /)?>!msi';
734
735        if (preg_match($pattern_pingback, $page_content, $m)) {
736            $pb_url = $m[1];
737            if (filter_var($pb_url, FILTER_VALIDATE_URL) && preg_match('!^https?:!', $pb_url)) {
738                return $pb_url . '|' . $url;
739            }
740        }
741
742        # Nothing, let's try webmention. Only support x/html content
743        if ($wm_url) {
744            $type = explode(';', $http->getHeader('content-type'));
745            if (!in_array($type[0], ['text/html', 'application/xhtml+xml'])) {
746                $wm_url = false;
747            }
748        }
749
750        # Check HTTP headers for a Link: <ENDPOINT_URL>; rel="webmention"
751        $wm_api = false;
752        if ($wm_url) {
753            if (preg_match('~<((?:https?://)?[^>]+)>; rel="?(?:https?://webmention.org/?|webmention)"?~', $wm_url, $match)) {
754                if (filter_var($match[1], FILTER_VALIDATE_URL) && preg_match('!^https?:!', $match[1])) {
755                    $wm_api = $match[1];
756                }
757            }
758        }
759
760        # Else check content for <link href="ENDPOINT_URL" rel="webmention" />
761        if ($wm_url && !$wm_api) {
762            $content = preg_replace('/<!--(.*)-->/Us', '', $page_content);
763            if (preg_match('/<(?:link|a)[ ]+href="([^"]*)"[ ]+rel="[^" ]* ?webmention ?[^" ]*"[ ]*\/?>/i', $content, $match)
764                || preg_match('/<(?:link|a)[ ]+rel="[^" ]* ?webmention ?[^" ]*"[ ]+href="([^"]*)"[ ]*\/?>/i', $content, $match)) {
765                $wm_api = $match[1];
766            }
767        }
768
769        # We have a winner, let's add some tricks to make diference
770        if ($wm_api) {
771            return $wm_api . '|' . $url . '|webmention';
772        }
773
774        return;
775    }
776    //@}
777
778    /**
779    HTTP helper.
780
781    @param    url        <b>string</b>        URL
782    @param    path        <b>string</b>        Path
783    @return    <b>object</b>
784     */
785    private static function initHttp($url, &$path)
786    {
787        $client = netHttp::initClient($url, $path);
788        $client->setTimeout(5);
789        $client->setUserAgent('Dotclear - http://www.dotclear.org/');
790        $client->useGzip(false);
791        $client->setPersistReferers(false);
792
793        return $client;
794    }
795
796    /**
797    URL helper.
798
799    @param    from_url        <b>string</b>        URL a
800    @param    to_url        <b>string</b>        URL b
801     */
802    public static function checkURLs($from_url, $to_url)
803    {
804        if (!(filter_var($from_url, FILTER_VALIDATE_URL) && preg_match('!^https?://!', $from_url))) {
805            throw new Exception(__('No valid source URL provided? Try again!'), 0);
806        }
807
808        if (!(filter_var($to_url, FILTER_VALIDATE_URL) && preg_match('!^https?://!', $to_url))) {
809            throw new Exception(__('No valid target URL provided? Try again!'), 0);
810        }
811
812        if (html::sanitizeURL(urldecode($from_url)) == html::sanitizeURL(urldecode($to_url))) {
813            throw new Exception(__('LOL!'), 0);
814        }
815    }
816}
Note: See TracBrowser for help on using the repository browser.

Sites map