| 1 | <?php |
|---|
| 2 | |
|---|
| 3 | /** |
|---|
| 4 | * Jlog |
|---|
| 5 | * |
|---|
| 6 | * This program is free software; you can redistribute it and/or modify |
|---|
| 7 | * it under the terms of the GNU General Public License as published by |
|---|
| 8 | * the Free Software Foundation; either version 2 of the License, or |
|---|
| 9 | * (at your option) any later version. |
|---|
| 10 | * |
|---|
| 11 | * This program is distributed in the hope that it will be useful, |
|---|
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 14 | * GNU General Public License for more details. |
|---|
| 15 | * |
|---|
| 16 | * You should have received a copy of the GNU General Public License |
|---|
| 17 | * along with this program; if not, write to the Free Software |
|---|
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|---|
| 19 | * |
|---|
| 20 | * $HeadURL$ |
|---|
| 21 | * $Rev$ |
|---|
| 22 | * $Author$ |
|---|
| 23 | * $Date$ |
|---|
| 24 | **/ |
|---|
| 25 | |
|---|
| 26 | require_once('./config.inc.php'); |
|---|
| 27 | require_once(JLOG_LIBPATH . '/bootstrap.php'); |
|---|
| 28 | |
|---|
| 29 | |
|---|
| 30 | |
|---|
| 31 | $HTTP_RAW_POST_DATA = trim($HTTP_RAW_POST_DATA); |
|---|
| 32 | if(!defined('JLOG_BASEPATH')) require_once('.'.DIRECTORY_SEPARATOR.'scripts'.DIRECTORY_SEPARATOR.'prepend.inc.php'); |
|---|
| 33 | require_once(JLOG_BASEPATH.'scripts'.DIRECTORY_SEPARATOR.'ixr-library.inc.php'); |
|---|
| 34 | require_once(JLOG_BASEPATH.'scripts'.DIRECTORY_SEPARATOR.'jlogHTTP_Request.php'); |
|---|
| 35 | |
|---|
| 36 | |
|---|
| 37 | if(defined("JLOG_ADMIN") === false) { |
|---|
| 38 | function ping($args) { |
|---|
| 39 | |
|---|
| 40 | $pingback = new Jlog_GetPingback(JLOG_DB_CONTENT, JLOG_DB_COMMENTS, JLOG_PATH, new_sid()); |
|---|
| 41 | $pingback->get_ping($args); |
|---|
| 42 | if($pingback->validate()) { |
|---|
| 43 | $pingback->write_to_db(); |
|---|
| 44 | return "Thanks for your ping."; |
|---|
| 45 | } |
|---|
| 46 | } |
|---|
| 47 | $server = new IXR_Server(array('pingback.ping' => 'ping')); |
|---|
| 48 | } |
|---|
| 49 | |
|---|
| 50 | class Jlog_GetPingback { |
|---|
| 51 | |
|---|
| 52 | var $errors = array(); // array |
|---|
| 53 | var $method = ""; // string |
|---|
| 54 | var $sourceURI = ""; // string |
|---|
| 55 | var $targetURI = array(); // array incl: orginal, parsed [array from parse_url()], y, m, url |
|---|
| 56 | var $title = ""; // string |
|---|
| 57 | var $sid = ""; // string |
|---|
| 58 | |
|---|
| 59 | function Jlog_GetPingback($db_content, $db_comments, $path, $sid = NULL) { |
|---|
| 60 | $this->db_content = $db_content; |
|---|
| 61 | $this->db_comments = $db_comments; |
|---|
| 62 | $this->path = $path; |
|---|
| 63 | if($sid != NULL) $this->sid = $sid; |
|---|
| 64 | } |
|---|
| 65 | |
|---|
| 66 | function get_ping($uris) { |
|---|
| 67 | |
|---|
| 68 | $ymurls = array(); |
|---|
| 69 | $tmp_host_got = ""; |
|---|
| 70 | $tmp_host_path_parsed = array(); |
|---|
| 71 | $tmp_host_path = ""; |
|---|
| 72 | |
|---|
| 73 | $this->sourceURI = trim($uris[0]); |
|---|
| 74 | $this->targetURI['orginal'] = trim(str_replace(array('"','<', '>', '&'), array('"', '<', '>', '&'), $uris[1])); |
|---|
| 75 | $this->targetURI['parsed'] = parse_url($this->targetURI['orginal']); |
|---|
| 76 | $tmp_host_got = str_replace('www.', '', $this->targetURI['parsed']['host']).$this->targetURI['parsed']['path']; |
|---|
| 77 | $tmp_host_path_parsed = parse_url($this->path); |
|---|
| 78 | $tmp_host_path = str_replace('www.', '', $tmp_host_path_parsed['host']).'/log.php'; |
|---|
| 79 | |
|---|
| 80 | if(!empty($this->targetURI['parsed']['query']) AND ($tmp_host_got == $tmp_host_path)) { |
|---|
| 81 | |
|---|
| 82 | $ymurls = explode('&', $this->targetURI['parsed']['query']); |
|---|
| 83 | $this->_counter = count($ymurls); |
|---|
| 84 | |
|---|
| 85 | foreach($ymurls AS $ymurl) { |
|---|
| 86 | if(substr($ymurl, 0, 2) == 'y=') $this->targetURI['y'] = substr($ymurl, 2); |
|---|
| 87 | elseif(substr($ymurl, 0, 2) == 'm=') $this->targetURI['m'] = substr($ymurl, 2); |
|---|
| 88 | elseif(substr($ymurl, 0, 4) == 'url=') $this->targetURI['url'] = substr($ymurl, 4); |
|---|
| 89 | } |
|---|
| 90 | } |
|---|
| 91 | else { |
|---|
| 92 | ### Plugin Hook |
|---|
| 93 | global $plugins; |
|---|
| 94 | $tmp_URI = $plugins->callHook('xmlrpcPermalink', $this->targetURI['orginal']); |
|---|
| 95 | |
|---|
| 96 | $regex = "#^".$this->path."/([0-9]{4})/?([0-9]{2})/?([a-z0-9_\-]+)$#"; |
|---|
| 97 | preg_match($regex, $tmp_URI, $matches); |
|---|
| 98 | $this->targetURI['y'] = $matches[1]; |
|---|
| 99 | $this->targetURI['m'] = $matches[2]; |
|---|
| 100 | $this->targetURI['url'] = $matches[3]; |
|---|
| 101 | } |
|---|
| 102 | |
|---|
| 103 | } |
|---|
| 104 | |
|---|
| 105 | function validate() { |
|---|
| 106 | |
|---|
| 107 | |
|---|
| 108 | if(!strpos($this->targetURI['orginal'], str_replace(array('http://', 'https://'), '', str_replace('www.', '', $this->path)))) |
|---|
| 109 | $this->send_error(0, 'Target URI ('.$this->targetURI['orginal'].') is not this page: '.$this->path); |
|---|
| 110 | |
|---|
| 111 | |
|---|
| 112 | // is there such a post? |
|---|
| 113 | $sql = "SELECT id, allowpingback FROM ".$this->db_content." WHERE |
|---|
| 114 | YEAR(date) = '".escape_for_mysql($this->targetURI['y'])."' AND |
|---|
| 115 | MONTH(date) = '".escape_for_mysql($this->targetURI['m'])."' AND |
|---|
| 116 | url = '".escape_for_mysql($this->targetURI['url'])."' AND |
|---|
| 117 | section = 'weblog' |
|---|
| 118 | LIMIT 1"; |
|---|
| 119 | $blog = new Query($sql); |
|---|
| 120 | if($blog->error()) $this->send_error(0, 'Could not read my database.'); |
|---|
| 121 | $blogrow = $blog->fetch(); |
|---|
| 122 | |
|---|
| 123 | if($blog->numRows() != 1) $this->send_error(32, 'The specified target URI does not exist.'.$this->targetURI['orginal']); |
|---|
| 124 | if($blogrow['allowpingback'] === 0) $this->send_error(33, 'The specified target URI cannot be used as a target. It it is not a pingback-enabled resource.'); |
|---|
| 125 | else $this->reference = $blogrow['id']; |
|---|
| 126 | |
|---|
| 127 | $s =& new HTTP_Request($this->sourceURI); |
|---|
| 128 | if(PEAR::isError($s->sendRequest())) $this->send_error(16, 'The source URI does not exist.'); |
|---|
| 129 | else { |
|---|
| 130 | $source = $s->getResponseBody(); |
|---|
| 131 | $source = strip_tags(str_replace('<!DOCTYPE','<DOCTYPE', $source), '<title><a>'); |
|---|
| 132 | |
|---|
| 133 | if (!$this->isLinkInHTML($this->targetURI['orginal'], $source)) |
|---|
| 134 | $this->send_error(17, 'The source URI does not contain a link to the target URI, and so cannot be used as a source.'); |
|---|
| 135 | |
|---|
| 136 | preg_match('|<title>([^<]*?)</title>|is', $source, $title); |
|---|
| 137 | |
|---|
| 138 | if(! $utf8 = preg_match ('/charset\s*=\s*utf-8/i', $s->getResponseHeader("Content-Type"))) |
|---|
| 139 | $utf8 = 'application/xhtml+xml' == strtolower(trim($s->getResponseHeader("Content-Type"))); |
|---|
| 140 | |
|---|
| 141 | // since text in database is utf8 encoded, we need to *en*code the title to utf8 if it isn't already, not *de*code it |
|---|
| 142 | $this->title = empty($title[1]) ? $this->sourceURI : html_entity_decode($utf8 ? $title[1] : utf8_encode($title[1])); |
|---|
| 143 | } |
|---|
| 144 | |
|---|
| 145 | $sql = "SELECT COUNT(*) AS ping FROM ".$this->db_comments." WHERE |
|---|
| 146 | reference = '".escape_for_mysql($blogrow['id'])."' AND |
|---|
| 147 | homepage = '".escape_for_mysql($this->sourceURI)."' AND |
|---|
| 148 | name = '".escape_for_mysql($this->title)."' AND |
|---|
| 149 | type = 'pingback' |
|---|
| 150 | LIMIT 1"; |
|---|
| 151 | $p = new Query($sql); |
|---|
| 152 | if($p->error()) $this->send_error(0, 'Could not read my database.'); |
|---|
| 153 | $f = $p->fetch(); |
|---|
| 154 | |
|---|
| 155 | if($f['ping'] > 0) $this->send_error(48, 'The pingback has already been registered.'); |
|---|
| 156 | |
|---|
| 157 | if(count($this->errors) > 0) return false; |
|---|
| 158 | else return true; |
|---|
| 159 | } |
|---|
| 160 | |
|---|
| 161 | function write_to_db() { |
|---|
| 162 | $sql = "INSERT INTO ".$this->db_comments." ( |
|---|
| 163 | sid, |
|---|
| 164 | name, |
|---|
| 165 | homepage, |
|---|
| 166 | reference, |
|---|
| 167 | date, |
|---|
| 168 | type |
|---|
| 169 | ) |
|---|
| 170 | VALUES ( |
|---|
| 171 | '".escape_for_mysql($this->sid)."', |
|---|
| 172 | '".escape_for_mysql($this->title)."', |
|---|
| 173 | '".escape_for_mysql($this->sourceURI)."', |
|---|
| 174 | '".escape_for_mysql($this->reference)."', |
|---|
| 175 | NOW(), |
|---|
| 176 | 'pingback' |
|---|
| 177 | )"; |
|---|
| 178 | $ping = new Query($sql); |
|---|
| 179 | |
|---|
| 180 | if($ping->error()) $this->send_error(0, 'Could not write to database.'); |
|---|
| 181 | |
|---|
| 182 | } |
|---|
| 183 | |
|---|
| 184 | function send_error($nr, $string) { |
|---|
| 185 | $this->errors[] = $nr." ".$string; |
|---|
| 186 | $error = new IXR_Error($nr, $string); |
|---|
| 187 | $this->send_xml($error->getXml()); |
|---|
| 188 | } |
|---|
| 189 | |
|---|
| 190 | function get_errors() { |
|---|
| 191 | return $this->errors; |
|---|
| 192 | } |
|---|
| 193 | |
|---|
| 194 | function send_xml($xml) { |
|---|
| 195 | header('Connection: close'); |
|---|
| 196 | header('Content-Length: '.strlen($xml)); |
|---|
| 197 | header('Content-Type: text/xml'); |
|---|
| 198 | header('Date: '.date('r')); |
|---|
| 199 | echo $xml; |
|---|
| 200 | exit; |
|---|
| 201 | } |
|---|
| 202 | |
|---|
| 203 | function isLinkInHTML($search, $html) { |
|---|
| 204 | preg_match_all('#<a[^>]+href\s*=\s*("([^"]+)"|\'([^\']+)\')[^>]*>(.+)</a>#Ui', $html, $matches); |
|---|
| 205 | $links = array_unique(array_merge($matches[2], $matches[3])); |
|---|
| 206 | |
|---|
| 207 | foreach($links as $link) { |
|---|
| 208 | if($search === str_replace('&', '&', $link)) return true; |
|---|
| 209 | } |
|---|
| 210 | return false; |
|---|
| 211 | } |
|---|
| 212 | |
|---|
| 213 | } |
|---|
| 214 | |
|---|
| 215 | class Jlog_SendPingback { |
|---|
| 216 | |
|---|
| 217 | var $pageslinkedto = array(); |
|---|
| 218 | var $useragent = ""; |
|---|
| 219 | |
|---|
| 220 | function Jlog_SendPingback($html, $pagelinkedfrom, $useragent) { |
|---|
| 221 | // neet to prevent & in url |
|---|
| 222 | $this->pagelinkedfrom = htmlspecialchars_decode($pagelinkedfrom); |
|---|
| 223 | $this->useragent = $useragent; |
|---|
| 224 | |
|---|
| 225 | preg_match_all('#<a[^>]+href\s*=\s*("([^"]+)"|\'([^\']+)\')[^>]*>(.+)</a>#Ui', $html, $matches); |
|---|
| 226 | $pageslinkedto = array(); |
|---|
| 227 | $pageslinkedto = array_unique(array_merge($matches[2], $matches[3])); |
|---|
| 228 | $count = count($pageslinkedto); |
|---|
| 229 | for($i = 0; $count > $i; $i++) { |
|---|
| 230 | if(substr($pageslinkedto[$i], 0, 4) !== "http") unset($pageslinkedto[$i]); |
|---|
| 231 | // htmlspecialchars_decode is easier than str_replace |
|---|
| 232 | else $pageslinkedto[$i] = htmlspecialchars_decode($pageslinkedto[$i]); |
|---|
| 233 | } |
|---|
| 234 | $this->pageslinkedto = $pageslinkedto; |
|---|
| 235 | } |
|---|
| 236 | |
|---|
| 237 | function doPingbacks() { |
|---|
| 238 | foreach($this->pageslinkedto as $pagelinkedto) { |
|---|
| 239 | $feedback[] = $this->send($pagelinkedto); |
|---|
| 240 | } |
|---|
| 241 | return $feedback; |
|---|
| 242 | } |
|---|
| 243 | |
|---|
| 244 | function send($pagelinkedto) { |
|---|
| 245 | |
|---|
| 246 | $s =& new HTTP_Request($pagelinkedto); |
|---|
| 247 | if(PEAR::isError($s->sendRequest())) return $pagelinkedto." — Error: The source URI does not exist."; |
|---|
| 248 | else { |
|---|
| 249 | $xmlrpcserver = $s->getResponseHeader("X-Pingback"); |
|---|
| 250 | if(!empty($xmlrpcserver)); |
|---|
| 251 | else { |
|---|
| 252 | if(preg_match('<link rel="pingback" href="([^"]+)" ?/?>', $s->getResponseBody(), $matches)) { |
|---|
| 253 | $xmlrpcserver = $matches[1]; |
|---|
| 254 | } |
|---|
| 255 | else return $pagelinkedto." — This is not a pingback-enabled resource."; |
|---|
| 256 | } |
|---|
| 257 | |
|---|
| 258 | $client = new IXR_Client($xmlrpcserver); |
|---|
| 259 | $client->timeout = 3; |
|---|
| 260 | $client->useragent = $this->useragent; |
|---|
| 261 | |
|---|
| 262 | // when set to true, this outputs debug messages by itself |
|---|
| 263 | $client->debug = false; |
|---|
| 264 | |
|---|
| 265 | if (! $client->query('pingback.ping', $this->pagelinkedfrom, $pagelinkedto ) ) |
|---|
| 266 | return $pagelinkedto." — Error: ".$client->getErrorMessage(); |
|---|
| 267 | |
|---|
| 268 | else return $pagelinkedto." — ".$client->getResponse(); |
|---|
| 269 | } |
|---|
| 270 | } |
|---|
| 271 | |
|---|
| 272 | } |
|---|
| 273 | |
|---|
| 274 | // eof |
|---|