diff options
author | Brion Vibber <brion@pobox.com> | 2010-09-15 14:09:23 -0700 |
---|---|---|
committer | Brion Vibber <brion@pobox.com> | 2010-09-15 14:10:18 -0700 |
commit | 95b534c5cee56602dffcc11121908c0f518cab6d (patch) | |
tree | 0a6a3cab7b1f6f881c51101bff7742dcb1d05cb6 /plugins/Linkback | |
parent | 2d4c0f9a47f6534c578a37abe79670cec699caae (diff) |
Move standalone plugin files into subdirectories so they can have locale data, etc added to them at leisure.
Diffstat (limited to 'plugins/Linkback')
-rw-r--r-- | plugins/Linkback/LinkbackPlugin.php | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/plugins/Linkback/LinkbackPlugin.php b/plugins/Linkback/LinkbackPlugin.php new file mode 100644 index 000000000..8e44beae1 --- /dev/null +++ b/plugins/Linkback/LinkbackPlugin.php @@ -0,0 +1,249 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Plugin to do linkbacks for notices containing links + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Plugin + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once('Auth/Yadis/Yadis.php'); + +define('LINKBACKPLUGIN_VERSION', '0.1'); + +/** + * Plugin to do linkbacks for notices containing URLs + * + * After new notices are saved, we check their text for URLs. If there + * are URLs, we test each URL to see if it supports any + * + * @category Plugin + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + * @see Event + */ + +class LinkbackPlugin extends Plugin +{ + var $notice = null; + + function __construct() + { + parent::__construct(); + } + + function onHandleQueuedNotice($notice) + { + if ($notice->is_local == 1) { + // Try to avoid actually mucking with the + // notice content + $c = $notice->content; + $this->notice = $notice; + // Ignoring results + common_replace_urls_callback($c, + array($this, 'linkbackUrl')); + } + return true; + } + + function linkbackUrl($url) + { + common_log(LOG_DEBUG,"Attempting linkback for " . $url); + + $orig = $url; + $url = htmlspecialchars_decode($orig); + $scheme = parse_url($url, PHP_URL_SCHEME); + if (!in_array($scheme, array('http', 'https'))) { + return $orig; + } + + // XXX: Do a HEAD first to save some time/bandwidth + + $fetcher = Auth_Yadis_Yadis::getHTTPFetcher(); + + $result = $fetcher->get($url, + array('User-Agent: ' . $this->userAgent(), + 'Accept: application/html+xml,text/html')); + + if (!in_array($result->status, array('200', '206'))) { + return $orig; + } + + $pb = null; + $tb = null; + + if (array_key_exists('X-Pingback', $result->headers)) { + $pb = $result->headers['X-Pingback']; + } else if (preg_match('/<link rel="pingback" href="([^"]+)" ?\/?>/', + $result->body, + $match)) { + $pb = $match[1]; + } + + if (!empty($pb)) { + $this->pingback($result->final_url, $pb); + } else { + $tb = $this->getTrackback($result->body, $result->final_url); + if (!empty($tb)) { + $this->trackback($result->final_url, $tb); + } + } + + return $orig; + } + + function pingback($url, $endpoint) + { + $args = array($this->notice->uri, $url); + + if (!extension_loaded('xmlrpc')) { + if (!dl('xmlrpc.so')) { + common_log(LOG_ERR, "Can't pingback; xmlrpc extension not available."); + return; + } + } + + $request = HTTPClient::start(); + try { + $response = $request->post($endpoint, + array('Content-Type: text/xml'), + xmlrpc_encode_request('pingback.ping', $args)); + $response = xmlrpc_decode($response->getBody()); + if (xmlrpc_is_fault($response)) { + common_log(LOG_WARNING, + "Pingback error for '$url' ($endpoint): ". + "$response[faultString] ($response[faultCode])"); + } else { + common_log(LOG_INFO, + "Pingback success for '$url' ($endpoint): ". + "'$response'"); + } + } catch (HTTP_Request2_Exception $e) { + common_log(LOG_WARNING, + "Pingback request failed for '$url' ($endpoint)"); + } + } + + // Largely cadged from trackback_cls.php by + // Ran Aroussi <ran@blogish.org>, GPL2 or any later version + // http://phptrackback.sourceforge.net/ + + function getTrackback($text, $url) + { + if (preg_match_all('/(<rdf:RDF.*?<\/rdf:RDF>)/sm', $text, $match, PREG_SET_ORDER)) { + for ($i = 0; $i < count($match); $i++) { + if (preg_match('|dc:identifier="' . preg_quote($url) . '"|ms', $match[$i][1])) { + $rdf_array[] = trim($match[$i][1]); + } + } + + // Loop through the RDFs array and extract trackback URIs + + $tb_array = array(); // <- holds list of trackback URIs + + if (!empty($rdf_array)) { + + for ($i = 0; $i < count($rdf_array); $i++) { + if (preg_match('/trackback:ping="([^"]+)"/', $rdf_array[$i], $array)) { + $tb_array[] = trim($array[1]); + break; + } + } + } + + // Return Trackbacks + + if (empty($tb_array)) { + return null; + } else { + return $tb_array[0]; + } + } + + if (preg_match_all('/(<a[^>]*?rel=[\'"]trackback[\'"][^>]*?>)/', $text, $match)) { + foreach ($match[1] as $atag) { + if (preg_match('/href=[\'"]([^\'"]*?)[\'"]/', $atag, $url)) { + return $url[1]; + } + } + } + + return null; + + } + + function trackback($url, $endpoint) + { + $profile = $this->notice->getProfile(); + + $args = array('title' => sprintf(_('%1$s\'s status on %2$s'), + $profile->nickname, + common_exact_date($this->notice->created)), + 'excerpt' => $this->notice->content, + 'url' => $this->notice->uri, + 'blog_name' => $profile->nickname); + + $fetcher = Auth_Yadis_Yadis::getHTTPFetcher(); + + $result = $fetcher->post($endpoint, + http_build_query($args), + array('User-Agent: ' . $this->userAgent())); + + if ($result->status != '200') { + common_log(LOG_WARNING, + "Trackback error for '$url' ($endpoint): ". + "$result->body"); + } else { + common_log(LOG_INFO, + "Trackback success for '$url' ($endpoint): ". + "'$result->body'"); + } + } + + function userAgent() + { + return 'LinkbackPlugin/'.LINKBACKPLUGIN_VERSION . + ' StatusNet/' . STATUSNET_VERSION; + } + + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'Linkback', + 'version' => LINKBACKPLUGIN_VERSION, + 'author' => 'Evan Prodromou', + 'homepage' => 'http://status.net/wiki/Plugin:Linkback', + 'rawdescription' => + _m('Notify blog authors when their posts have been linked in '. + 'microblog notices using '. + '<a href="http://www.hixie.ch/specs/pingback/pingback">Pingback</a> '. + 'or <a href="http://www.movabletype.org/docs/mttrackback.html">Trackback</a> protocols.')); + return true; + } +} |