|
|
|
<?php
|
|
|
|
|
|
|
|
require_once('include/bbcode.php');
|
|
|
|
require_once('include/oembed.php');
|
|
|
|
require_once('include/salmon.php');
|
|
|
|
require_once('include/crypto.php');
|
|
|
|
|
|
|
|
function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0) {
|
|
|
|
|
|
|
|
// default permissions - anonymous user
|
|
|
|
|
|
|
|
if(! strlen($owner_nick))
|
|
|
|
killme();
|
|
|
|
|
|
|
|
$public_feed = (($dfrn_id) ? false : true);
|
|
|
|
$starred = false;
|
|
|
|
$converse = false;
|
|
|
|
|
|
|
|
if($public_feed && $a->argc > 2) {
|
|
|
|
for($x = 2; $x < $a->argc; $x++) {
|
|
|
|
if($a->argv[$x] == 'converse')
|
|
|
|
$converse = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$sql_extra = " AND `allow_cid` = '' AND `allow_gid` = '' AND `deny_cid` = '' AND `deny_gid` = '' ";
|
|
|
|
|
|
|
|
$r = q("SELECT `contact`.*, `user`.`uid` AS `user_uid`, `user`.`nickname`, `user`.`timezone`
|
|
|
|
FROM `contact` LEFT JOIN `user` ON `user`.`uid` = `contact`.`uid`
|
|
|
|
WHERE `contact`.`self` = 1 AND `user`.`nickname` = '%s' LIMIT 1",
|
|
|
|
dbesc($owner_nick)
|
|
|
|
);
|
|
|
|
|
|
|
|
if(! count($r))
|
|
|
|
killme();
|
|
|
|
|
|
|
|
$owner = $r[0];
|
|
|
|
$owner_id = $owner['user_uid'];
|
|
|
|
$owner_nick = $owner['nickname'];
|
|
|
|
|
|
|
|
$birthday = feed_birthday($owner_id,$owner['timezone']);
|
|
|
|
|
|
|
|
if(! $public_feed) {
|
|
|
|
|
|
|
|
$sql_extra = '';
|
|
|
|
switch($direction) {
|
|
|
|
case (-1):
|
|
|
|
$sql_extra = sprintf(" AND `issued-id` = '%s' ", dbesc($dfrn_id));
|
|
|
|
$my_id = $dfrn_id;
|
|
|
|
break;
|
|
|
|
case 0:
|
|
|
|
$sql_extra = sprintf(" AND `issued-id` = '%s' AND `duplex` = 1 ", dbesc($dfrn_id));
|
|
|
|
$my_id = '1:' . $dfrn_id;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
$sql_extra = sprintf(" AND `dfrn-id` = '%s' AND `duplex` = 1 ", dbesc($dfrn_id));
|
|
|
|
$my_id = '0:' . $dfrn_id;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
break; // NOTREACHED
|
|
|
|
}
|
|
|
|
|
|
|
|
$r = q("SELECT * FROM `contact` WHERE `blocked` = 0 AND `pending` = 0 AND `contact`.`uid` = %d $sql_extra LIMIT 1",
|
|
|
|
intval($owner_id)
|
|
|
|
);
|
|
|
|
|
|
|
|
if(! count($r))
|
|
|
|
killme();
|
|
|
|
|
|
|
|
$contact = $r[0];
|
|
|
|
$groups = init_groups_visitor($contact['id']);
|
|
|
|
|
|
|
|
if(count($groups)) {
|
|
|
|
for($x = 0; $x < count($groups); $x ++)
|
|
|
|
$groups[$x] = '<' . intval($groups[$x]) . '>' ;
|
|
|
|
$gs = implode('|', $groups);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
$gs = '<<>>' ; // Impossible to match
|
|
|
|
|
|
|
|
$sql_extra = sprintf("
|
|
|
|
AND ( `allow_cid` = '' OR `allow_cid` REGEXP '<%d>' )
|
|
|
|
AND ( `deny_cid` = '' OR NOT `deny_cid` REGEXP '<%d>' )
|
|
|
|
AND ( `allow_gid` = '' OR `allow_gid` REGEXP '%s' )
|
|
|
|
AND ( `deny_gid` = '' OR NOT `deny_gid` REGEXP '%s')
|
|
|
|
",
|
|
|
|
intval($contact['id']),
|
|
|
|
intval($contact['id']),
|
|
|
|
dbesc($gs),
|
|
|
|
dbesc($gs)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if($public_feed)
|
|
|
|
$sort = 'DESC';
|
|
|
|
else
|
|
|
|
$sort = 'ASC';
|
|
|
|
|
|
|
|
if(! strlen($last_update))
|
|
|
|
$last_update = 'now -30 days';
|
|
|
|
|
|
|
|
if($public_feed) {
|
|
|
|
if(! $converse)
|
|
|
|
$sql_extra .= " AND `contact`.`self` = 1 ";
|
|
|
|
}
|
|
|
|
|
|
|
|
$check_date = datetime_convert('UTC','UTC',$last_update,'Y-m-d H:i:s');
|
|
|
|
|
|
|
|
$r = q("SELECT `item`.*, `item`.`id` AS `item_id`,
|
|
|
|
`contact`.`name`, `contact`.`photo`, `contact`.`url`,
|
|
|
|
`contact`.`name-date`, `contact`.`uri-date`, `contact`.`avatar-date`,
|
|
|
|
`contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`,
|
|
|
|
`contact`.`id` AS `contact-id`, `contact`.`uid` AS `contact-uid`,
|
|
|
|
`sign`.`signed_text`, `sign`.`signature`, `sign`.`signer`
|
|
|
|
FROM `item` LEFT JOIN `contact` ON `contact`.`id` = `item`.`contact-id`
|
|
|
|
LEFT JOIN `sign` ON `sign`.`iid` = `item`.`id`
|
|
|
|
WHERE `item`.`uid` = %d AND `item`.`visible` = 1 AND `item`.`parent` != 0
|
|
|
|
AND `item`.`wall` = 1 AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0
|
|
|
|
AND ( `item`.`edited` > '%s' OR `item`.`changed` > '%s' )
|
|
|
|
$sql_extra
|
|
|
|
ORDER BY `parent` %s, `created` ASC LIMIT 0, 300",
|
|
|
|
intval($owner_id),
|
|
|
|
dbesc($check_date),
|
|
|
|
dbesc($check_date),
|
|
|
|
dbesc($sort)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Will check further below if this actually returned results.
|
|
|
|
// We will provide an empty feed if that is the case.
|
|
|
|
|
|
|
|
$items = $r;
|
|
|
|
|
|
|
|
$feed_template = get_markup_template(($dfrn_id) ? 'atom_feed_dfrn.tpl' : 'atom_feed.tpl');
|
|
|
|
|
|
|
|
$atom = '';
|
|
|
|
|
|
|
|
$hubxml = feed_hublinks();
|
|
|
|
|
|
|
|
$salmon = feed_salmonlinks($owner_nick);
|
|
|
|
|
|
|
|
$atom .= replace_macros($feed_template, array(
|
|
|
|
'$version' => xmlify(FRIENDIKA_VERSION),
|
|
|
|
'$feed_id' => xmlify($a->get_baseurl() . '/profile/' . $owner_nick),
|
|
|
|
'$feed_title' => xmlify($owner['name']),
|
|
|
|
'$feed_updated' => xmlify(datetime_convert('UTC', 'UTC', 'now' , ATOM_TIME)) ,
|
|
|
|
'$hub' => $hubxml,
|
|
|
|
'$salmon' => $salmon,
|
|
|
|
'$name' => xmlify($owner['name']),
|
|
|
|
'$profile_page' => xmlify($owner['url']),
|
|
|
|
'$photo' => xmlify($owner['photo']),
|
|
|
|
'$thumb' => xmlify($owner['thumb']),
|
|
|
|
'$picdate' => xmlify(datetime_convert('UTC','UTC',$owner['avatar-date'] . '+00:00' , ATOM_TIME)) ,
|
|
|
|
'$uridate' => xmlify(datetime_convert('UTC','UTC',$owner['uri-date'] . '+00:00' , ATOM_TIME)) ,
|
|
|
|
'$namdate' => xmlify(datetime_convert('UTC','UTC',$owner['name-date'] . '+00:00' , ATOM_TIME)) ,
|
|
|
|
'$birthday' => ((strlen($birthday)) ? '<dfrn:birthday>' . xmlify($birthday) . '</dfrn:birthday>' : '')
|
|
|
|
));
|
|
|
|
|
|
|
|
call_hooks('atom_feed', $atom);
|
|
|
|
|
|
|
|
if(! count($items)) {
|
|
|
|
|
|
|
|
call_hooks('atom_feed_end', $atom);
|
|
|
|
|
|
|
|
$atom .= '</feed>' . "\r\n";
|
|
|
|
return $atom;
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach($items as $item) {
|
|
|
|
|
|
|
|
// public feeds get html, our own nodes use bbcode
|
|
|
|
|
|
|
|
if($public_feed) {
|
|
|
|
$type = 'html';
|
|
|
|
// catch any email that's in a public conversation and make sure it doesn't leak
|
|
|
|
if($item['private'])
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$type = 'text';
|
|
|
|
}
|
|
|
|
|
|
|
|
$atom .= atom_entry($item,$type,null,$owner,true);
|
|
|
|
}
|
|
|
|
|
|
|
|
call_hooks('atom_feed_end', $atom);
|
|
|
|
|
|
|
|
$atom .= '</feed>' . "\r\n";
|
|
|
|
|
|
|
|
return $atom;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function construct_verb($item) {
|
|
|
|
if($item['verb'])
|
|
|
|
return $item['verb'];
|
|
|
|
return ACTIVITY_POST;
|
|
|
|
}
|
|
|
|
|
|
|
|
function construct_activity_object($item) {
|
|
|
|
|
|
|
|
if($item['object']) {
|
|
|
|
$o = '<as:object>' . "\r\n";
|
|
|
|
$r = parse_xml_string($item['object'],false);
|
|
|
|
|
|
|
|
|
|
|
|
if(! $r)
|
|
|
|
return '';
|
|
|
|
if($r->type)
|
|
|
|
$o .= '<as:object-type>' . xmlify($r->type) . '</as:object-type>' . "\r\n";
|
|
|
|
if($r->id)
|
|
|
|
$o .= '<id>' . xmlify($r->id) . '</id>' . "\r\n";
|
|
|
|
if($r->title)
|
|
|
|
$o .= '<title>' . xmlify($r->title) . '</title>' . "\r\n";
|
|
|
|
if($r->link) {
|
|
|
|
if(substr($r->link,0,1) === '<') {
|
|
|
|
// patch up some facebook "like" activity objects that got stored incorrectly
|
|
|
|
// for a couple of months prior to 9-Jun-2011 and generated bad XML.
|
|
|
|
// we can probably remove this hack here and in the following function in a few months time.
|
|
|
|
if(strstr($r->link,'&') && (! strstr($r->link,'&')))
|
|
|
|
$r->link = str_replace('&','&', $r->link);
|
|
|
|
$r->link = preg_replace('/\<link(.*?)\"\>/','<link$1"/>',$r->link);
|
|
|
|
$o .= $r->link;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
$o .= '<link rel="alternate" type="text/html" href="' . xmlify($r->link) . '" />' . "\r\n";
|
|
|
|
}
|
|
|
|
if($r->content)
|
|
|
|
$o .= '<content type="html" >' . xmlify(bbcode($r->content)) . '</content>' . "\r\n";
|
|
|
|
$o .= '</as:object>' . "\r\n";
|
|
|
|
return $o;
|
|
|
|
}
|
|
|
|
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
|
|
|
function construct_activity_target($item) {
|
|
|
|
|
|
|
|
if($item['target']) {
|
|
|
|
$o = '<as:target>' . "\r\n";
|
|
|
|
$r = parse_xml_string($item['target'],false);
|
|
|
|
if(! $r)
|
|
|
|
return '';
|
|
|
|
if($r->type)
|
|
|
|
$o .= '<as:object-type>' . xmlify($r->type) . '</as:object-type>' . "\r\n";
|
|
|
|
if($r->id)
|
|
|
|
$o .= '<id>' . xmlify($r->id) . '</id>' . "\r\n";
|
|
|
|
if($r->title)
|
|
|
|
$o .= '<title>' . xmlify($r->title) . '</title>' . "\r\n";
|
|
|
|
if($r->link) {
|
|
|
|
if(substr($r->link,0,1) === '<') {
|
|
|
|
if(strstr($r->link,'&') && (! strstr($r->link,'&')))
|
|
|
|
$r->link = str_replace('&','&', $r->link);
|
|
|
|
$r->link = preg_replace('/\<link(.*?)\"\>/','<link$1"/>',$r->link);
|
|
|
|
$o .= $r->link;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
$o .= '<link rel="alternate" type="text/html" href="' . xmlify($r->link) . '" />' . "\r\n";
|
|
|
|
}
|
|
|
|
if($r->content)
|
|
|
|
$o .= '<content type="html" >' . xmlify(bbcode($r->content)) . '</content>' . "\r\n";
|
|
|
|
$o .= '</as:target>' . "\r\n";
|
|
|
|
return $o;
|
|
|
|
}
|
|
|
|
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function get_atom_elements($feed,$item) {
|
|
|
|
|
|
|
|
require_once('library/HTMLPurifier.auto.php');
|
|
|
|
require_once('include/html2bbcode.php');
|
|
|
|
|
|
|
|
$best_photo = array();
|
|
|
|
|
|
|
|
$res = array();
|
|
|
|
|
|
|
|
$author = $item->get_author();
|
|
|
|
if($author) {
|
|
|
|
$res['author-name'] = unxmlify($author->get_name());
|
|
|
|
$res['author-link'] = unxmlify($author->get_link());
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$res['author-name'] = unxmlify($feed->get_title());
|
|
|
|
$res['author-link'] = unxmlify($feed->get_permalink());
|
|
|
|
}
|
|
|
|
$res['uri'] = unxmlify($item->get_id());
|
|
|
|
$res['title'] = unxmlify($item->get_title());
|
|
|
|
$res['body'] = unxmlify($item->get_content());
|
|
|
|
$res['plink'] = unxmlify($item->get_link(0));
|
|
|
|
|
|
|
|
// look for a photo. We should check media size and find the best one,
|
|
|
|
// but for now let's just find any author photo
|
|
|
|
|
|
|
|
$rawauthor = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'author');
|
|
|
|
|
|
|
|
if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) {
|
|
|
|
$base = $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
|
|
|
|
foreach($base as $link) {
|
|
|
|
if(! $res['author-avatar']) {
|
|
|
|
if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar')
|
|
|
|
$res['author-avatar'] = unxmlify($link['attribs']['']['href']);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$rawactor = $item->get_item_tags(NAMESPACE_ACTIVITY, 'actor');
|
|
|
|
|
|
|
|
if($rawactor && activity_match($rawactor[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'],ACTIVITY_OBJ_PERSON)) {
|
|
|
|
$base = $rawactor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
|
|
|
|
if($base && count($base)) {
|
|
|
|
foreach($base as $link) {
|
|
|
|
if($link['attribs']['']['rel'] === 'alternate' && (! $res['author-link']))
|
|
|
|
$res['author-link'] = unxmlify($link['attribs']['']['href']);
|
|
|
|
if(! $res['author-avatar']) {
|
|
|
|
if($link['attribs']['']['rel'] === 'avatar' || $link['attribs']['']['rel'] === 'photo')
|
|
|
|
$res['author-avatar'] = unxmlify($link['attribs']['']['href']);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// No photo/profile-link on the item - look at the feed level
|
|
|
|
|
|
|
|
if((! (x($res,'author-link'))) || (! (x($res,'author-avatar')))) {
|
|
|
|
$rawauthor = $feed->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'author');
|
|
|
|
if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) {
|
|
|
|
$base = $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
|
|
|
|
foreach($base as $link) {
|
|
|
|
if($link['attribs']['']['rel'] === 'alternate' && (! $res['author-link']))
|
|
|
|
$res['author-link'] = unxmlify($link['attribs']['']['href']);
|
|
|
|
if(! $res['author-avatar']) {
|
|
|
|
if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar')
|
|
|
|
$res['author-avatar'] = unxmlify($link['attribs']['']['href']);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$rawactor = $feed->get_feed_tags(NAMESPACE_ACTIVITY, 'subject');
|
|
|
|
|
|
|
|
if($rawactor && activity_match($rawactor[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'],ACTIVITY_OBJ_PERSON)) {
|
|
|
|
$base = $rawactor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
|
|
|
|
|
|
|
|
if($base && count($base)) {
|
|
|
|
foreach($base as $link) {
|
|
|
|
if($link['attribs']['']['rel'] === 'alternate' && (! $res['author-link']))
|
|
|
|
$res['author-link'] = unxmlify($link['attribs']['']['href']);
|
|
|
|
if(! (x($res,'author-avatar'))) {
|
|
|
|
if($link['attribs']['']['rel'] === 'avatar' || $link['attribs']['']['rel'] === 'photo')
|
|
|
|
$res['author-avatar'] = unxmlify($link['attribs']['']['href']);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$apps = $item->get_item_tags(NAMESPACE_STATUSNET,'notice_info');
|
|
|
|
if($apps && $apps[0]['attribs']['']['source']) {
|
|
|
|
$res['app'] = strip_tags(unxmlify($apps[0]['attribs']['']['source']));
|
|
|
|
if($res['app'] === 'web')
|
|
|
|
$res['app'] = 'OStatus';
|
|
|
|
}
|
|
|
|
|
|
|
|
// base64 encoded json structure representing Diaspora signature
|
|
|
|
|
|
|
|
$dsig = $item->get_item_tags(NAMESPACE_DFRN,'diaspora_signature');
|
|
|
|
if($dsig) {
|
|
|
|
$res['dsprsig'] = unxmlify($dsig[0]['data']);
|
|
|
|
}
|
|
|
|
|
|
|
|
$dguid = $item->get_item_tags(NAMESPACE_DFRN,'diaspora_guid');
|
|
|
|
if($dguid)
|
|
|
|
$res['guid'] = unxmlify($dguid[0]['data']);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If there's a copy of the body content which is guaranteed to have survived mangling in transit, use it.
|
|
|
|
*/
|
|
|
|
|
|
|
|
$have_real_body = false;
|
|
|
|
|
|
|
|
$rawenv = $item->get_item_tags(NAMESPACE_DFRN, 'env');
|
|
|
|
if($rawenv) {
|
|
|
|
$have_real_body = true;
|
|
|
|
$res['body'] = $rawenv[0]['data'];
|
|
|
|
$res['body'] = str_replace(array(' ',"\t","\r","\n"), array('','','',''),$res['body']);
|
|
|
|
// make sure nobody is trying to sneak some html tags by us
|
|
|
|
$res['body'] = notags(base64url_decode($res['body']));
|
|
|
|
}
|
|
|
|
|
|
|
|
$maxlen = get_max_import_size();
|
|
|
|
if($maxlen && (strlen($res['body']) > $maxlen))
|
|
|
|
$res['body'] = substr($res['body'],0, $maxlen);
|
|
|
|
|
|
|
|
// It isn't certain at this point whether our content is plaintext or html and we'd be foolish to trust
|
|
|
|
// the content type. Our own network only emits text normally, though it might have been converted to
|
|
|
|
// html if we used a pubsubhubbub transport. But if we see even one html tag in our text, we will
|
|
|
|
// have to assume it is all html and needs to be purified.
|
|
|
|
|
|
|
|
// It doesn't matter all that much security wise - because before this content is used anywhere, we are
|
|
|
|
// going to escape any tags we find regardless, but this lets us import a limited subset of html from
|
|
|
|
// the wild, by sanitising it and converting supported tags to bbcode before we rip out any remaining
|
|
|
|
// html.
|
|
|
|
|
|
|
|
if((strpos($res['body'],'<') !== false) || (strpos($res['body'],'>') !== false)) {
|
|
|
|
|
|
|
|
$res['body'] = preg_replace('#<object[^>]+>.+?' . 'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?</object>#s',
|
|
|
|
'[youtube]$1[/youtube]', $res['body']);
|
|
|
|
|
|
|
|
$res['body'] = preg_replace('#<iframe[^>].+?' . 'http://www.youtube.com/embed/([A-Za-z0-9\-_=]+).+?</iframe>#s',
|
|
|
|
'[youtube]$1[/youtube]', $res['body']);
|
|
|
|
|
|
|
|
$res['body'] = oembed_html2bbcode($res['body']);
|
|
|
|
|
|
|
|
$config = HTMLPurifier_Config::createDefault();
|
|
|
|
$config->set('Cache.DefinitionImpl', null);
|
|
|
|
|
|
|
|
// we shouldn't need a whitelist, because the bbcode converter
|
|
|
|
// will strip out any unsupported tags.
|
|
|
|
// $config->set('HTML.Allowed', 'p,b,a[href],i');
|
|
|
|
|
|
|
|
$purifier = new HTMLPurifier($config);
|
|
|
|
$res['body'] = $purifier->purify($res['body']);
|
|
|
|
|
|
|
|
$res['body'] = html2bbcode($res['body']);
|
|
|
|
}
|
|
|
|
|
|
|
|
$allow = $item->get_item_tags(NAMESPACE_DFRN,'comment-allow');
|
|
|
|
if($allow && $allow[0]['data'] == 1)
|
|
|
|
$res['last-child'] = 1;
|
|
|
|
else
|
|
|
|
$res['last-child'] = 0;
|
|
|
|
|
|
|
|
$private = $item->get_item_tags(NAMESPACE_DFRN,'private');
|
|
|
|
if($private && $private[0]['data'] == 1)
|
|
|
|
$res['private'] = 1;
|
|
|
|
else
|
|
|
|
$res['private'] = 0;
|
|
|
|
|
|
|
|
$extid = $item->get_item_tags(NAMESPACE_DFRN,'extid');
|
|
|
|
if($extid && $extid[0]['data'])
|
|
|
|
$res['extid'] = $extid[0]['data'];
|
|
|
|
|
|
|
|
$rawlocation = $item->get_item_tags(NAMESPACE_DFRN, 'location');
|
|
|
|
if($rawlocation)
|
|
|
|
$res['location'] = unxmlify($rawlocation[0]['data']);
|
|
|
|
|
|
|
|
|
|
|
|
$rawcreated = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'published');
|
|
|
|
if($rawcreated)
|
|
|
|
$res['created'] = unxmlify($rawcreated[0]['data']);
|
|
|
|
|
|
|
|
|
|
|
|
$rawedited = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'updated');
|
|
|
|
if($rawedited)
|
|
|
|
$res['edited'] = unxmlify($rawedited[0]['data']);
|
|
|
|
|
|
|
|
if((x($res,'edited')) && (! (x($res,'created'))))
|
|
|
|
$res['created'] = $res['edited'];
|
|
|
|
|
|
|
|
if(! $res['created'])
|
|
|
|
$res['created'] = $item->get_date('c');
|
|
|
|
|
|
|
|
if(! $res['edited'])
|
|
|
|
$res['edited'] = $item->get_date('c');
|
|
|
|
|
|
|
|
|
|
|
|
// Disallow time travelling posts
|
|
|
|
|
|
|
|
$d1 = strtotime($res['created']);
|
|
|
|
$d2 = strtotime($res['edited']);
|
|
|
|
$d3 = strtotime('now');
|
|
|
|
|
|
|
|
if($d1 > $d3)
|
|
|
|
$res['created'] = datetime_convert();
|
|
|
|
if($d2 > $d3)
|
|
|
|
$res['edited'] = datetime_convert();
|
|
|
|
|
|
|
|
$rawowner = $item->get_item_tags(NAMESPACE_DFRN, 'owner');
|
|
|
|
if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])
|
|
|
|
$res['owner-name'] = unxmlify($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']);
|
|
|
|
elseif($rawowner[0]['child'][NAMESPACE_DFRN]['name'][0]['data'])
|
|
|
|
$res['owner-name'] = unxmlify($rawowner[0]['child'][NAMESPACE_DFRN]['name'][0]['data']);
|
|
|
|
if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])
|
|
|
|
$res['owner-link'] = unxmlify($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']);
|
|
|
|
elseif($rawowner[0]['child'][NAMESPACE_DFRN]['uri'][0]['data'])
|
|
|
|
$res['owner-link'] = unxmlify($rawowner[0]['child'][NAMESPACE_DFRN]['uri'][0]['data']);
|
|
|
|
|
|
|
|
if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) {
|
|
|
|
$base = $rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
|
|
|
|
|
|
|
|
foreach($base as $link) {
|
|
|
|
if(! $res['owner-avatar']) {
|
|
|
|
if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar')
|
|
|
|
$res['owner-avatar'] = unxmlify($link['attribs']['']['href']);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$rawgeo = $item->get_item_tags(NAMESPACE_GEORSS,'point');
|
|
|
|
if($rawgeo)
|
|
|
|
$res['coord'] = unxmlify($rawgeo[0]['data']);
|
|
|
|
|
|
|
|
|
|
|
|
$rawverb = $item->get_item_tags(NAMESPACE_ACTIVITY, 'verb');
|
|
|
|
|
|
|
|
// select between supported verbs
|
|
|
|
|
|
|
|
if($rawverb) {
|
|
|
|
$res['verb'] = unxmlify($rawverb[0]['data']);
|
|
|
|
}
|
|
|
|
|
|
|
|
// translate OStatus unfollow to activity streams if it happened to get selected
|
|
|
|
|
|
|
|
if((x($res,'verb')) && ($res['verb'] === 'http://ostatus.org/schema/1.0/unfollow'))
|
|
|
|
$res['verb'] = ACTIVITY_UNFOLLOW;
|
|
|
|
|
|
|
|
$cats = $item->get_categories();
|
|
|
|
if($cats) {
|
|
|
|
$tag_arr = array();
|
|
|
|
foreach($cats as $cat) {
|
|
|
|
$term = $cat->get_term();
|
|
|
|
if(! $term)
|
|
|
|
$term = $cat->get_label();
|
|
|
|
$scheme = $cat->get_scheme();
|
|
|
|
if($scheme && $term && stristr($scheme,'X-DFRN:'))
|
|
|
|
$tag_arr[] = substr($scheme,7,1) . '[url=' . unxmlify(substr($scheme,9)) . ']' . unxmlify($term) . '[/url]';
|
|
|
|
elseif($term)
|
|
|
|
$tag_arr[] = notags(trim($term));
|
|
|
|
}
|
|
|
|
$res['tag'] = implode(',', $tag_arr);
|
|
|
|
}
|
|
|
|
|
|
|
|
$attach = $item->get_enclosures();
|
|
|
|
if($attach) {
|
|
|
|
$att_arr = array();
|
|
|
|
foreach($attach as $att) {
|
|
|
|
$len = intval($att->get_length());
|
|
|
|
$link = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att->get_link()))));
|
|
|
|
$title = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att->get_title()))));
|
|
|
|
$type = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att->get_type()))));
|
|
|
|
if(strpos($type,';'))
|
|
|
|
$type = substr($type,0,strpos($type,';'));
|
|
|
|
if((! $link) || (strpos($link,'http') !== 0))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if(! $title)
|
|
|
|
$title = ' ';
|
|
|
|
if(! $type)
|
|
|
|
$type = 'application/octet-stream';
|
|
|
|
|
|
|
|
$att_arr[] = '[attach]href="' . $link . '" length="' . $len . '" type="' . $type . '" title="' . $title . '"[/attach]';
|
|
|
|
}
|
|
|
|
$res['attach'] = implode(',', $att_arr);
|
|
|
|
}
|
|
|
|
|
|
|
|
$rawobj = $item->get_item_tags(NAMESPACE_ACTIVITY, 'object');
|
|
|
|
|
|
|
|
if($rawobj) {
|
|
|
|
$res['object'] = '<object>' . "\n";
|
|
|
|
if($rawobj[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data']) {
|
|
|
|
$res['object-type'] = $rawobj[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'];
|
|
|
|
$res['object'] .= '<type>' . $rawobj[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'] . '</type>' . "\n";
|
|
|
|
}
|
|
|
|
if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'])
|
|
|
|
$res['object'] .= '<id>' . $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'] . '</id>' . "\n";
|
|
|
|
if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'])
|
|
|
|
$res['object'] .= '<link>' . encode_rel_links($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) . '</link>' . "\n";
|
|
|
|
if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'])
|
|
|
|
$res['object'] .= '<title>' . $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'] . '</title>' . "\n";
|
|
|
|
if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data']) {
|
|
|
|
$body = $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data'];
|
|
|
|
if(! $body)
|
|
|
|
$body = $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['summary'][0]['data'];
|
|
|
|
// preserve a copy of the original body content in case we later need to parse out any microformat information, e.g. events
|
|
|
|
$res['object'] .= '<orig>' . xmlify($body) . '</orig>' . "\n";
|
|
|
|
if((strpos($body,'<') !== false) || (strpos($body,'>') !== false)) {
|
|
|
|
|
|
|
|
$body = preg_replace('#<object[^>]+>.+?' . 'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?</object>#s',
|
|
|
|
'[youtube]$1[/youtube]', $body);
|
|
|
|
|
|
|
|
$res['body'] = preg_replace('#<iframe[^>].+?' . 'http://www.youtube.com/embed/([A-Za-z0-9\-_=]+).+?</iframe>#s',
|
|
|
|
'[youtube]$1[/youtube]', $res['body']);
|
|
|
|
|
|
|
|
|
|
|
|
$config = HTMLPurifier_Config::createDefault();
|
|
|
|
$config->set('Cache.DefinitionImpl', null);
|
|
|
|
|
|
|
|
$purifier = new HTMLPurifier($config);
|
|
|
|
$body = $purifier->purify($body);
|
|
|
|
$body = html2bbcode($body);
|
|
|
|
}
|
|
|
|
|
|
|
|
$res['object'] .= '<content>' . $body . '</content>' . "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
$res['object'] .= '</object>' . "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
$rawobj = $item->get_item_tags(NAMESPACE_ACTIVITY, 'target');
|
|
|
|
|
|
|
|
if($rawobj) {
|
|
|
|
$res['target'] = '<target>' . "\n";
|
|
|
|
if($rawobj[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data']) {
|
|
|
|
$res['target'] .= '<type>' . $rawobj[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'] . '</type>' . "\n";
|
|
|
|
}
|
|
|
|
if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'])
|
|
|
|
$res['target'] .= '<id>' . $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'] . '</id>' . "\n";
|
|
|
|
|
|
|
|
if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'])
|
|
|
|
$res['target'] .= '<link>' . encode_rel_links($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) . '</link>' . "\n";
|
|
|
|
if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'])
|
|
|
|
$res['target'] .= '<title>' . $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'] . '</title>' . "\n";
|
|
|
|
if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data']) {
|
|
|
|
$body = $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data'];
|
|
|
|
if(! $body)
|
|
|
|
$body = $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['summary'][0]['data'];
|
|
|
|
// preserve a copy of the original body content in case we later need to parse out any microformat information, e.g. events
|
|
|
|
$res['object'] .= '<orig>' . xmlify($body) . '</orig>' . "\n";
|
|
|
|
if((strpos($body,'<') !== false) || (strpos($body,'>') !== false)) {
|
|
|
|
|
|
|
|
$body = preg_replace('#<object[^>]+>.+?' . 'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?</object>#s',
|
|
|
|
'[youtube]$1[/youtube]', $body);
|
|
|
|
|
|
|
|
$res['body'] = preg_replace('#<iframe[^>].+?' . 'http://www.youtube.com/embed/([A-Za-z0-9\-_=]+).+?</iframe>#s',
|
|
|
|
'[youtube]$1[/youtube]', $res['body']);
|
|
|
|
|
|
|
|
$config = HTMLPurifier_Config::createDefault();
|
|
|
|
$config->set('Cache.DefinitionImpl', null);
|
|
|
|
|
|
|
|
$purifier = new HTMLPurifier($config);
|
|
|
|
$body = $purifier->purify($body);
|
|
|
|
$body = html2bbcode($body);
|
|
|
|
}
|
|
|
|
|
|
|
|
$res['target'] .= '<content>' . $body . '</content>' . "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
$res['target'] .= '</target>' . "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
$arr = array('feed' => $feed, 'item' => $item, 'result' => $res);
|
|
|
|
|
|
|
|
call_hooks('parse_atom', $arr);
|
|
|
|
|
|
|
|
return $res;
|
|
|
|
}
|
|
|
|
|
|
|
|
function encode_rel_links($links) {
|
|
|
|
$o = '';
|
|
|
|
if(! ((is_array($links)) && (count($links))))
|
|
|
|
return $o;
|
|
|
|
foreach($links as $link) {
|
|
|
|
$o .= '<link ';
|
|
|
|
if($link['attribs']['']['rel'])
|
|
|
|
$o .= 'rel="' . $link['attribs']['']['rel'] . '" ';
|
|
|
|
if($link['attribs']['']['type'])
|
|
|
|
$o .= 'type="' . $link['attribs']['']['type'] . '" ';
|
|
|
|
if($link['attribs']['']['href'])
|
|
|
|
$o .= 'href="' . $link['attribs']['']['href'] . '" ';
|
|
|
|
if( (x($link['attribs'],NAMESPACE_MEDIA)) && $link['attribs'][NAMESPACE_MEDIA]['width'])
|
|
|
|
$o .= 'media:width="' . $link['attribs'][NAMESPACE_MEDIA]['width'] . '" ';
|
|
|
|
if( (x($link['attribs'],NAMESPACE_MEDIA)) && $link['attribs'][NAMESPACE_MEDIA]['height'])
|
|
|
|
$o .= 'media:height="' . $link['attribs'][NAMESPACE_MEDIA]['height'] . '" ';
|
|
|
|
$o .= ' />' . "\n" ;
|
|
|
|
}
|
|
|
|
return xmlify($o);
|
|
|
|
}
|
|
|
|
|
|
|
|
function item_store($arr,$force_parent = false) {
|
|
|
|
|
|
|
|
// If a Diaspora signature structure was passed in, pull it out of the
|
|
|
|
// item array and set it aside for later storage.
|
|
|
|
|
|
|
|
$dsprsig = null;
|
|
|
|
if(x($arr,'dsprsig')) {
|
|
|
|
$dsprsig = json_decode(base64_decode($arr['dsprsig']));
|
|
|
|
unset($arr['dsprsig']);
|
|
|
|
}
|
|
|
|
|
|
|
|
if($arr['gravity'])
|
|
|
|
$arr['gravity'] = intval($arr['gravity']);
|
|
|
|
elseif($arr['parent-uri'] == $arr['uri'])
|
|
|
|
$arr['gravity'] = 0;
|
|
|
|
elseif(activity_match($arr['verb'],ACTIVITY_POST))
|
|
|
|
$arr['gravity'] = 6;
|
|
|
|
else
|
|
|
|
$arr['gravity'] = 6; // extensible catchall
|
|
|
|
|
|
|
|
if(! x($arr,'type'))
|
|
|
|
$arr['type'] = 'remote';
|
|
|
|
|
|
|
|
// Shouldn't happen but we want to make absolutely sure it doesn't leak from a plugin.
|
|
|
|
|
|
|
|
if((strpos($arr['body'],'<') !== false) || (strpos($arr['body'],'>') !== false))
|
|
|
|
$arr['body'] = strip_tags($arr['body']);
|
|
|
|
|
|
|
|
|
|
|
|
$arr['wall'] = ((x($arr,'wall')) ? intval($arr['wall']) : 0);
|
|
|
|
$arr['uri'] = ((x($arr,'uri')) ? notags(trim($arr['uri'])) : random_string());
|
|
|
|
$arr['extid'] = ((x($arr,'extid')) ? notags(trim($arr['extid'])) : '');
|
|
|
|
$arr['author-name'] = ((x($arr,'author-name')) ? notags(trim($arr['author-name'])) : '');
|
|
|
|
$arr['author-link'] = ((x($arr,'author-link')) ? notags(trim($arr['author-link'])) : '');
|
|
|
|
$arr['author-avatar'] = ((x($arr,'author-avatar')) ? notags(trim($arr['author-avatar'])) : '');
|
|
|
|
$arr['owner-name'] = ((x($arr,'owner-name')) ? notags(trim($arr['owner-name'])) : '');
|
|
|
|
$arr['owner-link'] = ((x($arr,'owner-link')) ? notags(trim($arr['owner-link'])) : '');
|
|
|
|
$arr['owner-avatar'] = ((x($arr,'owner-avatar')) ? notags(trim($arr['owner-avatar'])) : '');
|
|
|
|
$arr['created'] = ((x($arr,'created') !== false) ? datetime_convert('UTC','UTC',$arr['created']) : datetime_convert());
|
|
|
|
$arr['edited'] = ((x($arr,'edited') !== false) ? datetime_convert('UTC','UTC',$arr['edited']) : datetime_convert());
|
|
|
|
$arr['received'] = datetime_convert();
|
|
|
|
$arr['changed'] = datetime_convert();
|
|
|
|
$arr['title'] = ((x($arr,'title')) ? notags(trim($arr['title'])) : '');
|
|
|
|
$arr['location'] = ((x($arr,'location')) ? notags(trim($arr['location'])) : '');
|
|
|
|
$arr['coord'] = ((x($arr,'coord')) ? notags(trim($arr['coord'])) : '');
|
|
|
|
$arr['last-child'] = ((x($arr,'last-child')) ? intval($arr['last-child']) : 0 );
|
|
|
|
$arr['visible'] = ((x($arr,'visible') !== false) ? intval($arr['visible']) : 1 );
|
|
|
|
$arr['deleted'] = 0;
|
|
|
|
$arr['parent-uri'] = ((x($arr,'parent-uri')) ? notags(trim($arr['parent-uri'])) : '');
|
|
|
|
$arr['verb'] = ((x($arr,'verb')) ? notags(trim($arr['verb'])) : '');
|
|
|
|
$arr['object-type'] = ((x($arr,'object-type')) ? notags(trim($arr['object-type'])) : '');
|
|
|
|
$arr['object'] = ((x($arr,'object')) ? trim($arr['object']) : '');
|
|
|
|
$arr['target-type'] = ((x($arr,'target-type')) ? notags(trim($arr['target-type'])) : '');
|
|
|
|
$arr['target'] = ((x($arr,'target')) ? trim($arr['target']) : '');
|
|
|
|
$arr['plink'] = ((x($arr,'plink')) ? notags(trim($arr['plink'])) : '');
|
|
|
|
$arr['allow_cid'] = ((x($arr,'allow_cid')) ? trim($arr['allow_cid']) : '');
|
|
|
|
$arr['allow_gid'] = ((x($arr,'allow_gid')) ? trim($arr['allow_gid']) : '');
|
|
|
|
$arr['deny_cid'] = ((x($arr,'deny_cid')) ? trim($arr['deny_cid']) : '');
|
|
|
|
$arr['deny_gid'] = ((x($arr,'deny_gid')) ? trim($arr['deny_gid']) : '');
|
|
|
|
$arr['private'] = ((x($arr,'private')) ? intval($arr['private']) : 0 );
|
|
|
|
$arr['body'] = ((x($arr,'body')) ? trim($arr['body']) : '');
|
|
|
|
$arr['tag'] = ((x($arr,'tag')) ? notags(trim($arr['tag'])) : '');
|
|
|
|
$arr['attach'] = ((x($arr,'attach')) ? notags(trim($arr['attach'])) : '');
|
|
|
|
$arr['app'] = ((x($arr,'app')) ? notags(trim($arr['app'])) : '');
|
|
|
|
$arr['guid'] = ((x($arr,'guid')) ? notags(trim($arr['guid'])) : get_guid());
|
|
|
|
|
|
|
|
if($arr['parent-uri'] === $arr['uri']) {
|
|
|
|
$parent_id = 0;
|
|
|
|
$allow_cid = $arr['allow_cid'];
|
|
|
|
$allow_gid = $arr['allow_gid'];
|
|
|
|
$deny_cid = $arr['deny_cid'];
|
|
|
|
$deny_gid = $arr['deny_gid'];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
|
|
|
|
// find the parent and snarf the item id and ACL's
|
|
|
|
// and anything else we need to inherit
|
|
|
|
|
|
|
|
$r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
|
|
|
|
dbesc($arr['parent-uri']),
|
|
|
|
intval($arr['uid'])
|
|
|
|
);
|
|
|
|
|
|
|
|
if(count($r)) {
|
|
|
|
|
|
|
|
// is the new message multi-level threaded?
|
|
|
|
// even though we don't support it now, preserve the info
|
|
|
|
// and re-attach to the conversation parent.
|
|
|
|
|
|
|
|
if($r[0]['uri'] != $r[0]['parent-uri']) {
|
|
|
|
$arr['thr-parent'] = $arr['parent-uri'];
|
|
|
|
$arr['parent-uri'] = $r[0]['parent-uri'];
|
|
|
|
$z = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `parent-uri` = '%s' AND `uid` = %d LIMIT 1",
|
|
|
|
dbesc($r[0]['parent-uri']),
|
|
|
|
dbesc($r[0]['parent-uri']),
|
|
|
|
intval($arr['uid'])
|
|
|
|
);
|
|
|
|
if($z && count($z))
|
|
|
|
$r = $z;
|
|
|
|
}
|
|
|
|
|
|
|
|
$parent_id = $r[0]['id'];
|
|
|
|
$parent_deleted = $r[0]['deleted'];
|
|
|
|
$allow_cid = $r[0]['allow_cid'];
|
|
|
|
$allow_gid = $r[0]['allow_gid'];
|
|
|
|
$deny_cid = $r[0]['deny_cid'];
|
|
|
|
$deny_gid = $r[0]['deny_gid'];
|
|
|
|
$arr['wall'] = $r[0]['wall'];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
|
|
|
|
// Allow one to see reply tweets from status.net even when
|
|
|
|
// we don't have or can't see the original post.
|
|
|
|
|
|
|
|
if($force_parent) {
|
|
|
|
logger('item_store: $force_parent=true, reply converted to top-level post.');
|
|
|
|
$parent_id = 0;
|
|
|
|
$arr['thr-parent'] = $arr['parent-uri'];
|
|
|
|
$arr['parent-uri'] = $arr['uri'];
|
|
|
|
$arr['gravity'] = 0;
|
|
|
|
}
|
|
|
|
else {
|
|