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