1<?php
2
3/**
4 * Pingback server for use with the DokuWiki Linkback Plugin.
5 *
6 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
7 * @author     Gina Haeussge <osd@foosel.net>
8 * @link       http://wiki.foosel.net/snippets/dokuwiki/linkback
9 */
10
11use IXR\Server\Server;
12use IXR\Message\Error;
13use dokuwiki\HTTP\DokuHTTPClient;
14
15if (!defined('DOKU_INC'))
16    define('DOKU_INC', realpath(dirname(__FILE__) . '/../../../../') . '/');
17
18require_once (DOKU_INC . 'inc/init.php');
19
20// Pingback Faultcodes
21const PINGBACK_ERROR_GENERIC = 0;
22const PINGBACK_ERROR_SOURCEURI_DOES_NOT_EXIST = 16;
23const PINGBACK_ERROR_SOURCEURI_DOES_NOT_CONTAIN_LINK = 17;
24const PINGBACK_ERROR_TARGETURI_DOES_NOT_EXIST = 32;
25const PINGBACK_ERROR_TARGETURI_CANNOT_BE_USED = 33;
26const PINGBACK_ERROR_PINGBACK_ALREADY_MADE = 48;
27const PINGBACK_ERROR_ACCESS_DENIED = 49;
28const PINGBACK_ERROR_NO_UPSTREAM = 50;
29
30/**
31 * Class PingbackServer
32 */
33class PingbackServer extends Server {
34
35    /** @var  helper_plugin_blogtng_linkback */
36    var $tools;
37
38    /**
39     * Register service and construct helper
40     */
41    function __construct() {
42        $this->tools = plugin_load('helper', 'blogtng_linkback');
43        parent::__construct(array('pingback.ping' => 'this:ping'));
44    }
45
46    /**
47     * Send a HTTP get request to @$sourceUri and extract linkback
48     * data for @$targetUri from the response.
49     *
50     * @param $sourceUri
51     * @param $targetUri
52     * @return Error|void
53     */
54    function ping($sourceUri, $targetUri) {
55        global $ID;
56        $ID = substr($_SERVER['PATH_INFO'], 1);
57
58        if (is_null($this->tools) || !$this->tools->linkbackAllowed()) {
59            return new Error(PINGBACK_ERROR_TARGETURI_CANNOT_BE_USED, '');
60        }
61
62        // Given URLs are no urls? Quit
63        if (!preg_match("#^([a-z0-9\-\.+]+?)://.*#i", $sourceUri))
64            return new Error(PINGBACK_ERROR_GENERIC, '');
65        if (!preg_match("#^([a-z0-9\-\.+]+?)://.*#i", $targetUri))
66            return new Error(PINGBACK_ERROR_GENERIC, '');
67
68        // Source URL does not exist? Quit
69        $http = new DokuHTTPClient;
70        $page = $http->get($sourceUri);
71        if ($page === false)
72            return new Error(PINGBACK_ERROR_SOURCEURI_DOES_NOT_EXIST, '');
73
74        // Target URL does not match with request? Quit
75        if ($targetUri != wl($ID, '', true))
76            return new Error(PINGBACK_ERROR_GENERIC, '');
77
78        // Retrieve data from source
79        $linkback = $this->_getTrackbackData($sourceUri, $targetUri, $page);
80
81        // Source URL does not contain link to target? Quit
82        if (!$linkback)
83            return new Error(PINGBACK_ERROR_SOURCEURI_DOES_NOT_CONTAIN_LINK, '');
84
85        if (!$this->tools->saveLinkback('pingback', $linkback['title'],
86                                        $sourceUri, $linkback['excerpt'], $ID)) {
87            return new Error(PINGBACK_ERROR_PINGBACK_ALREADY_MADE, '');
88        }
89    }
90
91    /**
92     * Constructs linkback data and checks if source contains a link to target and a title.
93     *
94     * @param $sourceUri
95     * @param $targetUri
96     * @param $page
97     * @return array|bool
98     */
99    function _getTrackbackData($sourceUri, $targetUri, $page) {
100        $linkback = array ();
101
102        $searchurl = preg_quote($targetUri, '!');
103        $regex = '!<a[^>]+?href="' . $searchurl . '"[^>]*?>(.*?)</a>!is';
104        $regex2 = '!\s(' . $searchurl . ')\s!is';
105        if (!preg_match($regex, $page, $match) && !preg_match($regex2, $page, $match)) {
106            // FIXME internal pings
107            if ((strstr($targetUri, DOKU_URL) == $targetUri)) {
108                $ID = substr($_SERVER['PATH_INFO'], 1);
109                $searchurl = preg_quote(wl($ID, '', false), '!');
110
111                $regex = '!<a[^>]+?href="' . $searchurl . '"[^>]*?>(.*?)</a>!is';
112                $regex2 = '!\s(' . $searchurl . ')\s!is';
113                if (!preg_match($regex, $page, $match) && !preg_match($regex2, $page, $match))
114                    return false;
115            } else {
116                return false;
117            }
118        }
119        $linkback['excerpt'] = '[…] ' . strip_tags($match[1]) . ' […]';
120
121        $regex = '!<title.*?>(.*?)</title>!is';
122        if (!preg_match($regex, $page, $match))
123            return false;
124        $linkback['title'] = strip_tags($match[1]);
125
126        return $linkback;
127    }
128}
129
130$server = new PingbackServer();
131