changed git call from https to git readonly
[atutor.git] / mods / wiki / plugins / feature / pingback.php
1 <?php
2
3 /*
4    PingBack [http://www.hixie.ch/specs/pingback/pingback] automatically
5    discovers when somebody inserts a URL into a page and pings the remote
6    server to take note about the mentioning of one of its articles. There
7    is a lot of auto-discovery involved, and this requires XML+RPC and the
8    HTTP extensions (see plugins/lib/) to be present.
9    This plugin implements client and server (Wiki can itself be pinged).
10    
11    GOTCHAS: may only work for ordinarily named WikiPages (no non-word
12    characters inside). You should prepare EWIKI_SCRIPT_URL to yield nice
13    links to your pages as usual.
14    If possible you should add <link rel="pingback" href=".../z.php"> to
15    your site layout script.
16 */
17
18
19
20 #-- plugin registration
21 $wikiapi["pingback.ping"] = "ewiki_pingback_rpc";
22 $ewiki_plugins["edit_save"][] = "ewiki_pingback_newurls";
23 $ewiki_plugins["init"][] = "ewiki_pingback_header";
24 @$ewiki_config["ua"] .= " PingBack/0.2";
25
26
27 #-- server part (adding URLs to our pages) -------------------------------
28 function ewiki_pingback_rpc($source_url, $target_url) {
29    global $ewiki_config;
30
31    #-- does the target URL refer to a known WikiPage ?
32    $id = ewiki_url2id($target_url);
33    if (!$id) {
34       xmlrpc_send_response(xmlrpc_error(0x0021, "Could not determine PageName for the given target URL."));
35    }
36    if (!($data = ewiki_db::GET($id))) {
37       xmlrpc_send_response(xmlrpc_error(0x0020, "The given target page does not exist."));
38    }
39
40    
41    #-- check if the caller really has a link as he claims
42    ini_set("user_agent", $ewiki_config["ua"]);
43    if ((strpos($source_url, "http://")===0) && ($test = ewiki_http_asis($source_url, 96256))) {
44       $test = strtolower($test);
45       $test_url = strtolower($target_url);
46       if (!strpos($test, $test_url)
47       and !strpos($test, htmlentities($test_url))) {
48          return xmlrpc_error(0x0011, "Sorry, but couldn't find a link to '$target_url' on your given '$source_url' page.");
49       }
50    }
51    else {
52       return xmlrpc_error(0x0010, "Your given source URL does not exist, could not be retrieved.");
53    }
54    
55    #-- reject other frivolous links
56    if (preg_match('#^http://[^/]+/?$#', $source_url)) {
57       return xmlrpc_error(0x0011, "Rejected '$source_url' as frivolous.");
58    }
59    #-- check write permissions
60    if ((EWIKI_DB_F_TEXT != $data["flags"] & EWIKI_DB_F_TYPE)
61    or ($data["flags"] & EWIKI_DB_F_READONLY)) {
62       return xmlrpc_error(0x0031, "Sorry, but this page is write-protected or not a system page.");
63    }
64    #-- already on page
65    if (strpos($data["content"], $source_url)) {
66       return xmlrpc_error(0x0030, "The given link does already exist on this page.");
67    }
68    #-- other go-away cases
69    if (function_exists("ewiki_banned_url") && ewiki_banned_url($source_url) || function_exists("ewiki_blocked_url") && ewiki_blocked_url($source_url)) {
70       return xmlrpc_error(0x0100, "Your link is unwanted here (registered on BlockedLinks or BannedLinks).");
71    }
72    
73    #-- else update page
74    $data["content"] = rtrim($data["content"])
75                     . "\n* $source_url (PingBack)\n";
76    ewiki_db::UPDATE($data);
77    $data["version"]++;
78    $ok = ewiki_db::WRITE($data);
79
80    #-- fin response
81    if ($ok) {
82       return("Link to '$source_url' was added to page '$id'.");
83    }
84    else {
85       return xmlrpc_error(0x0101, "Seems like a database/writing error occoured.");
86    }
87 }
88
89
90 #-- page id from absolute URL
91 function ewiki_url2id($url) {
92    if (strpos($url, EWIKI_SCRIPT_URL)===0) {
93       $id = substr($url, strlen(EWIKI_SCRIPT_URL));
94    }
95    elseif ($l = strpos($url, "?id=")) {
96       $id = strtok(substr($url, $l+4), "&");
97    }
98    elseif ($l = strpos($url, "?")) {
99       $id = strtok(substr($url, $l+1), "&");
100    }
101    elseif (($l = strrpos($url, "/")) > 10) {
102       $id = strtok(substr($url, $l+1), "?&.");
103    }
104    return($id);
105 }
106
107
108 #-- notify clients that we're also server
109 function ewiki_pingback_header() {
110    header("X-PingBack: " . EWIKI_BASE_URL . "z.php");
111 }
112
113
114
115
116 #-- client part (notify remote server of added URLs on current site) -----
117 function ewiki_pingback_ping($source, $target) {
118
119    #-- detect if $target URL is pingback-enabled, and go
120    if ($rpc_url = ewiki_pingback_discover($target)) {
121
122       $res = xmlrpc_request($rpc_url, "pingback.ping", array($source, $target));
123       // we don't care about the result, do we?
124    }
125 }
126
127
128 #-- short http request to discover X-Pingback header or <link> tag
129 function ewiki_pingback_discover($url) {
130    global $ewiki_config;
131
132    ini_set("user_agent", $ewiki_config["ua"]);
133    if ((strpos($url, "http://") === 0) && ($data = ewiki_http_asis($url, 4096))) {
134       if (preg_match('/\nX-Pingback:\s*([^\s,]+)/i', $data, $uu)) {
135          return($uu[1]);
136       }
137       elseif (preg_match('/<link[^>]+rel=["\']?pingback["\']?[^>]+href=["\']?([^>"\'\s]+)/i', $data, $uu)) {
138          return($uu[1]);
139       }
140    }
141 }
142
143
144 #-- undeciphered GET request
145 function ewiki_http_asis($url, $maxsize=8192) {
146    global $ewiki_config;
147    $c = parse_url($url);
148    extract($c);
149    $port = $port ? $port : 80;
150    $path .= $query ? "?$query" : "";
151    if ($f = fsockopen($host, $port, $errno, $errstr, $timeout=5)) {
152       fwrite($f, "GET $path HTTP/1.0\r\n"
153                . "Host: $host\r\n"
154                . "Connection: close\r\n"
155                . "Accept: text/html, application/xml, text/xml, application/xhtml+xml, text/plain\r\n"
156                . "User-Agent: $ewiki_config[ua]\r\n"
157                . "\r\n");
158       socket_set_blocking($f, true);
159       $data = false;
160       while (!feof($f) && (strlen($data) < $maxsize)) {
161          $data .= fread($f, $maxsize);
162       }
163       fclose($f);
164       return($data);
165    }
166 }
167
168
169
170 #-- check for newly added urls
171 function ewiki_pingback_newurls(&$save, &$old) {
172
173    global $ewiki_plugins, $ewiki_config;
174
175    #-- check newly added links
176    $newlinks = array_diff(explode("\n", trim($save["refs"])), explode("\n", trim($old["refs"])));
177    foreach ($newlinks as $link) if (strpos($link, "://")) {
178       $ewiki_config["pingback"][] = $link;
179    }
180    if (@$ewiki_config["pingback"]) {
181       register_shutdown_function("ewiki_pingback_start");
182    }
183 }
184
185
186 #-- registers all previously added URLs
187 function ewiki_pingback_start() {
188
189    global $ewiki_plugins, $ewiki_config, $ewiki_id;
190    $source_url = ewiki_script_url("", $ewiki_id);
191
192    foreach ($ewiki_config["pingback"] as $target_url) {
193       ewiki_pingback_ping($source_url, $target_url);
194    }
195 }
196
197
198 ?>