Merge branch 'webmention-rocks' into 'nightly'
webmention.rocks I have improved the webmention handling so that all but two of the webmention.rocks compliance tests pass now. Also improved parsing of time/authors on incoming webmentions. See merge request !128
This commit is contained in:
commit
d4295cfb25
@ -248,10 +248,10 @@ class Notice extends Managed_DataObject
|
||||
return common_local_url('shownotice', array('notice' => $this->id), null, null, false);
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
public function getTitle($imply=true)
|
||||
{
|
||||
$title = null;
|
||||
if (Event::handle('GetNoticeTitle', array($this, &$title))) {
|
||||
if (Event::handle('GetNoticeTitle', array($this, &$title)) && $imply) {
|
||||
// TRANS: Title of a notice posted without a title value.
|
||||
// TRANS: %1$s is a user name, %2$s is the notice creation date/time.
|
||||
$title = sprintf(_('%1$s\'s status on %2$s'),
|
||||
|
@ -179,8 +179,9 @@ class NoticeListItem extends Widget
|
||||
function showNoticeTitle()
|
||||
{
|
||||
if (Event::handle('StartShowNoticeTitle', array($this))) {
|
||||
$nameClass = $this->notice->getTitle(false) ? 'p-name ' : '';
|
||||
$this->element('a', array('href' => $this->notice->getUri(),
|
||||
'class' => 'p-name u-uid'),
|
||||
'class' => $nameClass . 'u-uid'),
|
||||
$this->notice->getTitle());
|
||||
Event::handle('EndShowNoticeTitle', array($this));
|
||||
}
|
||||
@ -348,7 +349,8 @@ class NoticeListItem extends Widget
|
||||
function showContent()
|
||||
{
|
||||
// FIXME: URL, image, video, audio
|
||||
$this->out->elementStart('article', array('class' => 'e-content'));
|
||||
$nameClass = $this->notice->getTitle(false) ? '' : 'p-name ';
|
||||
$this->out->elementStart('article', array('class' => $nameClass . 'e-content'));
|
||||
if (Event::handle('StartShowNoticeContent', array($this->notice, $this->out, $this->out->getScoped()))) {
|
||||
if ($this->maxchars > 0 && mb_strlen($this->notice->content) > $this->maxchars) {
|
||||
$this->out->text(mb_substr($this->notice->content, 0, $this->maxchars) . '[…]');
|
||||
|
@ -101,14 +101,28 @@ class LinkbackPlugin extends Plugin
|
||||
return true;
|
||||
}
|
||||
|
||||
function unparse_url($parsed_url)
|
||||
{
|
||||
$scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '';
|
||||
$host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
|
||||
$port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '';
|
||||
$user = isset($parsed_url['user']) ? $parsed_url['user'] : '';
|
||||
$pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : '';
|
||||
$pass = ($user || $pass) ? "$pass@" : '';
|
||||
$path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
|
||||
$query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
|
||||
$fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
|
||||
return "$scheme$user$pass$host$port$path$query$fragment";
|
||||
}
|
||||
|
||||
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'))) {
|
||||
$base = parse_url($url);
|
||||
if (!in_array($base['scheme'], array('http', 'https'))) {
|
||||
return $orig;
|
||||
}
|
||||
|
||||
@ -126,13 +140,17 @@ class LinkbackPlugin extends Plugin
|
||||
return $orig;
|
||||
}
|
||||
|
||||
// XXX: Should handle relative-URI resolution in these detections
|
||||
|
||||
$wm = $this->getWebmention($response);
|
||||
if(!empty($wm)) {
|
||||
if(!is_null($wm)) {
|
||||
$wm = parse_url($wm);
|
||||
if(!$wm) $wm = array();
|
||||
if(!$wm['host']) $wm['host'] = $base['host'];
|
||||
if(!$wm['scheme']) $wm['scheme'] = $base['scheme'];
|
||||
if(!$wm['path']) $wm['path'] = $base['path'];
|
||||
|
||||
// It is the webmention receiver's job to resolve source
|
||||
// Ref: https://github.com/converspace/webmention/issues/43
|
||||
$this->webmention($url, $wm);
|
||||
$this->webmention($url, $this->unparse_url($wm));
|
||||
} else {
|
||||
$pb = $this->getPingback($response);
|
||||
if (!empty($pb)) {
|
||||
@ -156,26 +174,26 @@ class LinkbackPlugin extends Plugin
|
||||
$link = $response->getHeader('Link');
|
||||
if (!is_null($link)) {
|
||||
// XXX: the fetcher gives back a comma-separated string of all Link headers, I hope the parsing works reliably
|
||||
if (preg_match('~<((?:https?://)?[^>]+)>; rel="webmention"~', $link, $match)) {
|
||||
return $match[1];
|
||||
} elseif (preg_match('~<((?:https?://)?[^>]+)>; rel="http://webmention.org/?"~', $link, $match)) {
|
||||
if (preg_match('~<([^>]+)>; rel="?(?:[^" ]* )*(?:http://webmention.org/|webmention)(?: [^" ]*)*"?~', $link, $match)) {
|
||||
return $match[1];
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Do proper DOM traversal
|
||||
if(preg_match('/<(?:link|a)[ ]+href="([^"]+)"[ ]+rel="[^" ]* ?webmention ?[^" ]*"[ ]*\/?>/i', $response->getBody(), $match)
|
||||
|| preg_match('/<(?:link|a)[ ]+rel="[^" ]* ?webmention ?[^" ]*"[ ]+href="([^"]+)"[ ]*\/?>/i', $response->getBody(), $match)) {
|
||||
return $match[1];
|
||||
} elseif (preg_match('/<(?:link|a)[ ]+href="([^"]+)"[ ]+rel="http:\/\/webmention\.org\/?"[ ]*\/?>/i', $response->getBody(), $match)
|
||||
|| preg_match('/<(?:link|a)[ ]+rel="http:\/\/webmention\.org\/?"[ ]+href="([^"]+)"[ ]*\/?>/i', $response->getBody(), $match)) {
|
||||
// Currently fails https://webmention.rocks/test/13, https://webmention.rocks/test/17
|
||||
if(preg_match('~<(?:link|a)[ ]+href="([^"]*)"[ ]+rel="(?:[^" ]* )*(?:http://webmention.org/|webmention)(?: [^" ]*)*"[ ]*/?>~i', $response->getBody(), $match)
|
||||
|| preg_match('~<(?:link|a)[ ]+rel="(?:[^" ]* )*(?:http://webmention.org/|webmention)(?: [^" ]*)*"[ ]+href="([^"]*)"[ ]*/?>~i', $response->getBody(), $match)) {
|
||||
return $match[1];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
function webmention($url, $endpoint) {
|
||||
$source = $this->notice->getUrl();
|
||||
|
||||
common_log(LOG_DEBUG,"Attempting webmention to $endpoint for $url from $source");
|
||||
|
||||
$payload = array(
|
||||
'source' => $source,
|
||||
'target' => $url
|
||||
@ -191,7 +209,7 @@ class LinkbackPlugin extends Plugin
|
||||
$payload
|
||||
);
|
||||
|
||||
if(!in_array($response->getStatus(), array(200,202))) {
|
||||
if(!in_array($response->getStatus(), array(200,201,202))) {
|
||||
common_log(LOG_WARNING,
|
||||
"Webmention request failed for '$url' ($endpoint)");
|
||||
}
|
||||
|
@ -40,30 +40,30 @@ class WebmentionAction extends Action
|
||||
|
||||
if(!$source) {
|
||||
echo _m('"source" is missing')."\n";
|
||||
throw new ServerException(_m('"source" is missing'), 400);
|
||||
throw new ClientException(_m('"source" is missing'), 400);
|
||||
}
|
||||
|
||||
if(!$target) {
|
||||
echo _m('"target" is missing')."\n";
|
||||
throw new ServerException(_m('"target" is missing'), 400);
|
||||
throw new ClientException(_m('"target" is missing'), 400);
|
||||
}
|
||||
|
||||
$response = linkback_get_source($source, $target);
|
||||
if(!$response) {
|
||||
echo _m('Source does not link to target.')."\n";
|
||||
throw new ServerException(_m('Source does not link to target.'), 400);
|
||||
throw new ClientException(_m('Source does not link to target.'), 400);
|
||||
}
|
||||
|
||||
$notice = linkback_get_target($target);
|
||||
if(!$notice) {
|
||||
echo _m('Target not found')."\n";
|
||||
throw new ServerException(_m('Target not found'), 404);
|
||||
throw new ClientException(_m('Target not found'), 404);
|
||||
}
|
||||
|
||||
$url = linkback_save($source, $target, $response, $notice);
|
||||
if(!$url) {
|
||||
echo _m('An error occured while saving.')."\n";
|
||||
throw new ServerException(_m('An error occured while saving.'), 500);
|
||||
throw new ClientException(_m('An error occured while saving.'), 500);
|
||||
}
|
||||
|
||||
echo $url."\n";
|
||||
|
@ -7,7 +7,7 @@ function linkback_lenient_target_match($body, $target) {
|
||||
function linkback_get_source($source, $target) {
|
||||
// Check if we are pinging ourselves and ignore
|
||||
$localprefix = common_config('site', 'server') . '/' . common_config('site', 'path');
|
||||
if(linkback_lenient_target_match($source, $localprefix)) {
|
||||
if(linkback_lenient_target_match($source, $localprefix) === 0) {
|
||||
common_debug('Ignoring self ping from ' . $source . ' to ' . $target);
|
||||
return NULL;
|
||||
}
|
||||
@ -22,7 +22,7 @@ function linkback_get_source($source, $target) {
|
||||
|
||||
$body = htmlspecialchars_decode($response->getBody());
|
||||
// We're slightly more lenient in our link detection than the spec requires
|
||||
if(!linkback_lenient_target_match($body, $target)) {
|
||||
if(linkback_lenient_target_match($body, $target) === FALSE) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -56,7 +56,7 @@ function linkback_get_target($target) {
|
||||
}
|
||||
if(!$user) {
|
||||
preg_match('/\/([^\/\?#]+)(?:#.*)?$/', $response->getEffectiveUrl(), $match);
|
||||
if(linkback_lenient_target_match(common_profile_url($match[1]), $response->getEffectiveUrl())) {
|
||||
if(linkback_lenient_target_match(common_profile_url($match[1]), $response->getEffectiveUrl()) !== FALSE) {
|
||||
$user = User::getKV('nickname', $match[1]);
|
||||
}
|
||||
}
|
||||
@ -70,7 +70,7 @@ function linkback_get_target($target) {
|
||||
|
||||
function linkback_is_contained_in($entry, $target) {
|
||||
foreach ((array)$entry['properties'] as $key => $values) {
|
||||
if(count(array_filter($values, function($x) use ($target) { return linkback_lenient_target_match($x, $target); })) > 0) {
|
||||
if(count(array_filter($values, function($x) use ($target) { return linkback_lenient_target_match($x, $target) !== FALSE; })) > 0) {
|
||||
return $entry['properties'];
|
||||
}
|
||||
|
||||
@ -79,7 +79,7 @@ function linkback_is_contained_in($entry, $target) {
|
||||
if(isset($obj['type']) && array_intersect(array('h-cite', 'h-entry'), $obj['type']) &&
|
||||
isset($obj['properties']) && isset($obj['properties']['url']) &&
|
||||
count(array_filter($obj['properties']['url'],
|
||||
function($x) use ($target) { return linkback_lenient_target_match($x, $target); })) > 0
|
||||
function($x) use ($target) { return linkback_lenient_target_match($x, $target) !== FALSE; })) > 0
|
||||
) {
|
||||
return $entry['properties'];
|
||||
}
|
||||
@ -130,7 +130,7 @@ function linkback_entry_type($entry, $mf2, $target) {
|
||||
|
||||
if($mf2['rels'] && $mf2['rels']['in-reply-to']) {
|
||||
foreach($mf2['rels']['in-reply-to'] as $url) {
|
||||
if(linkback_lenient_target_match($url, $target)) {
|
||||
if(linkback_lenient_target_match($url, $target) !== FALSE) {
|
||||
return 'reply';
|
||||
}
|
||||
}
|
||||
@ -144,7 +144,7 @@ function linkback_entry_type($entry, $mf2, $target) {
|
||||
);
|
||||
|
||||
foreach((array)$entry as $key => $values) {
|
||||
if(count(array_filter($values, function($x) use ($target) { return linkback_lenient_target_match($x, $target); })) > 0) {
|
||||
if(count(array_filter($values, function($x) use ($target) { return linkback_lenient_target_match($x, $target) != FALSE; })) > 0) {
|
||||
if($classes[$key]) { return $classes[$key]; }
|
||||
}
|
||||
|
||||
@ -152,7 +152,7 @@ function linkback_entry_type($entry, $mf2, $target) {
|
||||
if(isset($obj['type']) && array_intersect(array('h-cite', 'h-entry'), $obj['type']) &&
|
||||
isset($obj['properties']) && isset($obj['properties']['url']) &&
|
||||
count(array_filter($obj['properties']['url'],
|
||||
function($x) use ($target) { return linkback_lenient_target_match($x, $target); })) > 0
|
||||
function($x) use ($target) { return linkback_lenient_target_match($x, $target) != FALSE; })) > 0
|
||||
) {
|
||||
if($classes[$key]) { return $classes[$key]; }
|
||||
}
|
||||
@ -243,8 +243,8 @@ function linkback_notice($source, $notice_or_user, $entry, $author, $mf2) {
|
||||
|
||||
if (isset($entry['published']) || isset($entry['updated'])) {
|
||||
$options['created'] = isset($entry['published'])
|
||||
? common_sql_date($entry['published'][0])
|
||||
: common_sql_date($entry['updated'][0]);
|
||||
? common_sql_date(strtotime($entry['published'][0]))
|
||||
: common_sql_date(strtotime($entry['updated'][0]));
|
||||
}
|
||||
|
||||
if (isset($entry['photo']) && common_valid_http_url($entry['photo'])) {
|
||||
@ -280,9 +280,42 @@ function linkback_notice($source, $notice_or_user, $entry, $author, $mf2) {
|
||||
return array($content, $options);
|
||||
}
|
||||
|
||||
function linkback_avatar($profile, $url) {
|
||||
// Ripped from OStatus plugin for now
|
||||
$temp_filename = tempnam(sys_get_temp_dir(), 'linback_avatar');
|
||||
try {
|
||||
$imgData = HTTPClient::quickGet($url);
|
||||
// Make sure it's at least an image file. ImageFile can do the rest.
|
||||
if (false === getimagesizefromstring($imgData)) {
|
||||
return false;
|
||||
}
|
||||
file_put_contents($temp_filename, $imgData);
|
||||
unset($imgData); // No need to carry this in memory.
|
||||
|
||||
$imagefile = new ImageFile(null, $temp_filename);
|
||||
$filename = Avatar::filename($profile->id,
|
||||
image_type_to_extension($imagefile->type),
|
||||
null,
|
||||
common_timestamp());
|
||||
rename($temp_filename, Avatar::path($filename));
|
||||
} catch (Exception $e) {
|
||||
unlink($temp_filename);
|
||||
throw $e;
|
||||
}
|
||||
// @todo FIXME: Hardcoded chmod is lame, but seems to be necessary to
|
||||
// keep from accidentally saving images from command-line (queues)
|
||||
// that can't be read from web server, which causes hard-to-notice
|
||||
// problems later on:
|
||||
//
|
||||
// http://status.net/open-source/issues/2663
|
||||
chmod(Avatar::path($filename), 0644);
|
||||
|
||||
$profile->setOriginal($filename);
|
||||
}
|
||||
|
||||
function linkback_profile($entry, $mf2, $response, $target) {
|
||||
if(isset($entry['properties']['author']) && isset($entry['properties']['author'][0]['properties'])) {
|
||||
$author = $entry['properties']['author'][0]['properties'];
|
||||
if(isset($entry['author']) && isset($entry['author'][0]['properties'])) {
|
||||
$author = $entry['author'][0]['properties'];
|
||||
} else {
|
||||
$author = linkback_hcard($mf2, $response->getEffectiveUrl());
|
||||
}
|
||||
@ -315,6 +348,10 @@ function linkback_profile($entry, $mf2, $response, $target) {
|
||||
$profile->nickname = isset($author['nickname']) ? $author['nickname'][0] : str_replace(' ', '', $author['name'][0]);
|
||||
$profile->created = common_sql_now();
|
||||
$profile->insert();
|
||||
|
||||
if($author['photo'] && $author['photo'][0]) {
|
||||
linkback_avatar($profile, $author['photo'][0]);
|
||||
}
|
||||
}
|
||||
|
||||
return array($profile, $author);
|
||||
|
Loading…
Reference in New Issue
Block a user