X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=include%2Fitems.php;h=bb1b4f16b0dd912f1a5d5145ae4650589fda490d;hb=037cd5d80993dedfccfe9ef25679c54297ef1546;hp=e74132c6a797d2f1fb217213bc66aae665b34ee4;hpb=93d0ef4042bdfe06b77cdbe56ec4e55143e4fc1e;p=friendica.git diff --git a/include/items.php b/include/items.php index e74132c6a7..bb1b4f16b0 100644 --- a/include/items.php +++ b/include/items.php @@ -1,33 +1,64 @@ >' ; // 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') ", - + $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), @@ -55,189 +86,1207 @@ function get_feed_for(&$a,$dfrn_id,$owner_id,$last_update) { ); } + if($dfrn_id === '' || $dfrn_id === '*') + $sort = 'DESC'; + else + $sort = 'ASC'; + if(! strlen($last_update)) - $last_update = 'now - 30 days'; + $last_update = 'now -30 days'; + $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` FROM `item` LEFT JOIN `contact` ON `contact`.`id` = `item`.`contact-id` WHERE `item`.`uid` = %d AND `item`.`visible` = 1 - AND NOT `item`.`type` IN ( 'remote', 'net-comment' ) AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0 - AND `item`.`edited` > '%s' + 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` ASC, `created` ASC LIMIT 0, 300", + ORDER BY `parent` %s, `created` ASC LIMIT 0, 300", intval($owner_id), - dbesc($check_date) + dbesc($check_date), + dbesc($check_date), + dbesc($sort) ); - if(! count($r)) - killme(); + + // Will check further below if this actually returned results. + // We will provide an empty feed in any case. $items = $r; - $feed_template = file_get_contents('view/atom_feed.tpl'); - $tomb_template = file_get_contents('view/atom_tomb.tpl'); - $item_template = file_get_contents('view/atom_item.tpl'); - $cmnt_template = file_get_contents('view/atom_cmnt.tpl'); + $feed_template = load_view_file('view/atom_feed.tpl'); $atom = ''; + $hub = get_config('system','huburl'); + + $hubxml = ''; + if(strlen($hub)) { + $hubs = explode(',', $hub); + if(count($hubs)) { + foreach($hubs as $h) { + $h = trim($h); + if(! strlen($h)) + continue; + $hubxml .= '' . "\n" ; + } + } + } + + $salmon = '' . "\n" ; + $salmon .= '' . "\n" ; + $salmon .= '' . "\n" ; + $atom .= replace_macros($feed_template, array( - '$feed_id' => xmlify($a->get_baseurl()), - '$feed_title' => xmlify($owner['name']), - '$feed_updated' => xmlify(datetime_convert('UTC', 'UTC', $updated . '+00:00' , 'Y-m-d\TH:i:s\Z')) , - '$name' => xmlify($owner['name']), - '$profile_page' => xmlify($owner['url']), - '$photo' => xmlify($owner['photo']) + '$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)) )); + + if(! count($items)) { + $atom .= '' . "\r\n"; + return $atom; + } + foreach($items as $item) { - if($item['deleted']) { - $atom .= replace_macros($tomb_template, array( - '$id' => xmlify($item['uri']), - '$updated' => xmlify(datetime_convert('UTC', 'UTC', $item['edited'] . '+00:00' , 'Y-m-d\TH:i:s\Z')) - )); + + // public feeds get html, our own nodes use bbcode + + if($dfrn_id === '*') { + $type = 'html'; } else { + $type = 'text'; + } - if($item['parent'] == $item['id']) { - $atom .= replace_macros($item_template, array( - '$name' => xmlify($item['name']), - '$profile_page' => xmlify($item['url']), - '$thumb' => xmlify($item['thumb']), - '$owner_name' => xmlify($item['owner-name']), - '$owner_profile_page' => xmlify($item['owner-link']), - '$owner_thumb' => xmlify($item['owner-avatar']), - '$item_id' => xmlify($item['uri']), - '$title' => xmlify($item['title']), - '$published' => xmlify(datetime_convert('UTC', 'UTC', $item['created'] . '+00:00' , 'Y-m-d\TH:i:s\Z')), - '$updated' => xmlify(datetime_convert('UTC', 'UTC', $item['edited'] . '+00:00' , 'Y-m-d\TH:i:s\Z')), - '$content' =>xmlify($item['body']), - '$comment_allow' => (($item['last-child'] && strlen($contact['dfrn-id'])) ? 1 : 0) - )); - } - else { - $atom .= replace_macros($cmnt_template, array( - '$name' => xmlify($item['name']), - '$profile_page' => xmlify($item['url']), - '$thumb' => xmlify($item['thumb']), - '$item_id' => xmlify($item['uri']), - '$title' => xmlify($item['title']), - '$published' => xmlify(datetime_convert('UTC', 'UTC', $item['created'] . '+00:00' , 'Y-m-d\TH:i:s\Z')), - '$updated' => xmlify(datetime_convert('UTC', 'UTC', $item['edited'] . '+00:00' , 'Y-m-d\TH:i:s\Z')), - '$content' =>xmlify($item['body']), - '$parent_id' => xmlify($item['parent-uri']), - '$comment_allow' => (($item['last-child']) ? 1 : 0) - )); - } + $atom .= atom_entry($item,$type,null,$owner,true); + } + + $atom .= '' . "\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 = '' . "\r\n"; + $r = @simplexml_load_string($item['object']); + if($r->type) + $o .= '' . xmlify($r->type) . '' . "\r\n"; + if($r->id) + $o .= '' . xmlify($r->id) . '' . "\r\n"; + if($r->title) + $o .= '' . xmlify($r->title) . '' . "\r\n"; + if($r->link) { + if(substr($r->link,0,1) === '<') + $o .= $r->link; + else + $o .= '' . "\r\n"; } + if($r->content) + $o .= '' . xmlify(bbcode($r->content)) . '' . "\r\n"; + $o .= '' . "\r\n"; + return $o; } - $atom .= "\r\n"; + return ''; +} - return $atom; +function construct_activity_target($item) { + + if($item['target']) { + $o = '' . "\r\n"; + $r = @simplexml_load_string($item['target']); + if($r->type) + $o .= '' . xmlify($r->type) . '' . "\r\n"; + if($r->id) + $o .= '' . xmlify($r->id) . '' . "\r\n"; + if($r->title) + $o .= '' . xmlify($r->title) . '' . "\r\n"; + if($r->link) { + if(substr($r->link,0,1) === '<') + $o .= $r->link; + else + $o .= '' . "\r\n"; + } + if($r->content) + $o .= '' . xmlify(bbcode($r->content)) . '' . "\r\n"; + $o .= '' . "\r\n"; + return $o; + } + + return ''; } -function get_atom_elements($item) { +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(); $res['author-name'] = unxmlify($author->get_name()); $res['author-link'] = unxmlify($author->get_link()); - $res['author-avatar'] = unxmlify($author->get_avatar()); $res['uri'] = unxmlify($item->get_id()); $res['title'] = unxmlify($item->get_title()); $res['body'] = unxmlify($item->get_content()); + + // 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']); + } + } + } + } + } + + $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 open 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'],'<')) { + + $res['body'] = preg_replace('#]+>.+?' . 'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?#s', + '[youtube]$1[/youtube]', $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; + $rawcreated = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'published'); if($rawcreated) $res['created'] = unxmlify($rawcreated[0]['data']); + $rawlocation = $item->get_item_tags(NAMESPACE_DFRN, 'location'); + if($rawlocation) + $res['location'] = unxmlify($rawlocation[0]['data']); + + $rawedited = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'updated'); if($rawedited) $res['edited'] = unxmlify($rawcreated[0]['data']); $rawowner = $item->get_item_tags(NAMESPACE_DFRN, 'owner'); - if($rawowner[0]['child'][NAMESPACE_DFRN]['name'][0]['data']) + 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'][NAMESPACE_DFRN]['uri'][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'][NAMESPACE_DFRN]['avatar'][0]['data']) - $res['owner-avatar'] = unxmlify($rawowner[0]['child'][NAMESPACE_DFRN]['avatar'][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']); + + $rawobj = $item->get_item_tags(NAMESPACE_ACTIVITY, 'object'); + + if($rawobj) { + $res['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'] .= '' . $rawobj[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'] . '' . "\n"; + } + if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data']) + $res['object'] .= '' . $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'] . '' . "\n"; + if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) + $res['object'] .= '' . encode_rel_links($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) . '' . "\n"; + if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data']) + $res['object'] .= '' . $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'] . '' . "\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']; + if(strpos($body,'<')) { + + $body = preg_replace('#]+>.+?' . 'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?#s', + '[youtube]$1[/youtube]', $body); + + $config = HTMLPurifier_Config::createDefault(); + $config->set('Cache.DefinitionImpl', null); + + $purifier = new HTMLPurifier($config); + $body = $purifier->purify($body); + } + + $body = html2bbcode($body); + $res['object'] .= '' . $body . '' . "\n"; + } + + $res['object'] .= '' . "\n"; + } + + $rawobj = $item->get_item_tags(NAMESPACE_ACTIVITY, 'target'); + + if($rawobj) { + $res['target'] = '' . "\n"; + if($rawobj[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data']) { + $res['target'] .= '' . $rawobj[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'] . '' . "\n"; + } + if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data']) + $res['target'] .= '' . $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'] . '' . "\n"; + + if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) + $res['target'] .= '' . encode_rel_links($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) . '' . "\n"; + if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data']) + $res['target'] .= '' . $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'] . '' . "\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']; + if(strpos($body,'<')) { + + $body = preg_replace('#]+>.+?' . 'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?#s', + '[youtube]$1[/youtube]', $body); + + $config = HTMLPurifier_Config::createDefault(); + $config->set('Cache.DefinitionImpl', null); + + $purifier = new HTMLPurifier($config); + $body = $purifier->purify($body); + } + + $body = html2bbcode($body); + $res['target'] .= '' . $body . '' . "\n"; + } + + $res['target'] .= '' . "\n"; + } return $res; } -function post_remote($a,$arr) { +function encode_rel_links($links) { + $o = ''; + if(! ((is_array($links)) && (count($links)))) + return $o; + foreach($links as $link) { + $o .= ' may have been double encoded, depending on which filter chain + // they came through. + + $arr['body'] = str_replace( + array('&amp;','&gt;','&lt;'), + array('&' ,'>' ,'<'), + $arr['body'] + ); + + + + 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 + + $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']; + } + + $parent_id = $r[0]['id']; + $allow_cid = $r[0]['allow_cid']; + $allow_gid = $r[0]['allow_gid']; + $deny_cid = $r[0]['deny_cid']; + $deny_gid = $r[0]['deny_gid']; + } + else { + logger('item_store: item parent was not found - ignoring item'); + return 0; + } + } dbesc_array($arr); -//dbg(3); - $r = q("INSERT INTO `item` (`" + + logger('item_store: ' . print_r($arr,true), LOGGER_DATA); + + $r = dbq("INSERT INTO `item` (`" . implode("`, `", array_keys($arr)) . "`) VALUES ('" . implode("', '", array_values($arr)) . "')" ); + // find the item we just created + $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", - dbesc($arr['parent-uri']), + $arr['uri'], // already dbesc'd intval($arr['uid']) ); - - if(count($r)) - $parent_id = $r[0]['id']; + if(count($r)) { + $current_post = $r[0]['id']; + logger('item_store: created item ' . $current_post); + } else { - // if parent is missing, what do we do? + logger('item_store: could not locate created item'); + return 0; } - $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", - $arr['uri'], - intval($arr['uid']) - ); - if(count($r)) - $current_post = $r[0]['id']; + if($arr['parent-uri'] === $arr['uri']) + $parent_id = $current_post; + + if(strlen($allow_cid) || strlen($allow_gid) || strlen($deny_cid) || strlen($deny_gid)) + $private = 1; + else + $private = $arr['private']; - $r = q("UPDATE `item` SET `parent` = %d WHERE `id` = %d LIMIT 1", + // Set parent id - and also make sure to inherit the parent's ACL's. + + $r = q("UPDATE `item` SET `parent` = %d, `allow_cid` = '%s', `allow_gid` = '%s', + `deny_cid` = '%s', `deny_gid` = '%s', `private` = %d WHERE `id` = %d LIMIT 1", intval($parent_id), + dbesc($allow_cid), + dbesc($allow_gid), + dbesc($deny_cid), + dbesc($deny_gid), + intval($private), intval($current_post) ); return $current_post; } + +function get_item_contact($item,$contacts) { + if(! count($contacts) || (! is_array($item))) + return false; + foreach($contacts as $contact) { + if($contact['id'] == $item['contact-id']) { + return $contact; + break; // NOTREACHED + } + } + return false; +} + + +function dfrn_deliver($owner,$contact,$atom) { + + $a = get_app(); + + if((! strlen($contact['dfrn-id'])) && (! $contact['duplex']) && (! ($owner['page-flags'] == PAGE_COMMUNITY))) + return 3; + + $idtosend = $orig_id = (($contact['dfrn-id']) ? $contact['dfrn-id'] : $contact['issued-id']); + + if($contact['duplex'] && $contact['dfrn-id']) + $idtosend = '0:' . $orig_id; + if($contact['duplex'] && $contact['issued-id']) + $idtosend = '1:' . $orig_id; + + $rino = ((function_exists('mcrypt_encrypt')) ? 1 : 0); + + $rino_enable = get_config('system','rino_encrypt'); + + if(! $rino_enable) + $rino = 0; + + $url = $contact['notify'] . '?dfrn_id=' . $idtosend . '&dfrn_version=' . DFRN_PROTOCOL_VERSION . (($rino) ? '&rino=1' : ''); + + logger('dfrn_deliver: ' . $url); + + $xml = fetch_url($url); + + $curl_stat = $a->get_curl_code(); + if(! $curl_stat) + return(-1); // timed out + + logger('dfrn_deliver: ' . $xml); + + if(! $xml) + return 3; + + $res = simplexml_load_string($xml); + + if((intval($res->status) != 0) || (! strlen($res->challenge)) || (! strlen($res->dfrn_id))) + return (($res->status) ? $res->status : 3); + + $postvars = array(); + $sent_dfrn_id = hex2bin($res->dfrn_id); + $challenge = hex2bin($res->challenge); + $rino_allowed = ((intval($res->rino) === 1) ? 1 : 0); + + $final_dfrn_id = ''; + + + if(($contact['duplex'] && strlen($contact['prvkey'])) || ($owner['page-flags'] == PAGE_COMMUNITY)) { + openssl_private_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['prvkey']); + openssl_private_decrypt($challenge,$postvars['challenge'],$contact['prvkey']); + } + else { + openssl_public_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['pubkey']); + openssl_public_decrypt($challenge,$postvars['challenge'],$contact['pubkey']); + } + + $final_dfrn_id = substr($final_dfrn_id, 0, strpos($final_dfrn_id, '.')); + + if(strpos($final_dfrn_id,':') == 1) + $final_dfrn_id = substr($final_dfrn_id,2); + + if($final_dfrn_id != $orig_id) { + logger('dfrn_deliver: wrong dfrn_id.'); + // did not decode properly - cannot trust this site + return 3; + } + + $postvars['dfrn_id'] = $idtosend; + $postvars['dfrn_version'] = DFRN_PROTOCOL_VERSION; + + if(($contact['rel']) && ($contact['rel'] != REL_FAN) && (! $contact['blocked']) && (! $contact['readonly'])) { + $postvars['data'] = $atom; + } + elseif($owner['page-flags'] == PAGE_COMMUNITY) { + $postvars['data'] = $atom; + } + else { + $postvars['data'] = str_replace('1','0',$atom); + } + + if($rino && $rino_allowed) { + $key = substr(random_string(),0,16); + $data = bin2hex(aes_encrypt($postvars['data'],$key)); + $postvars['data'] = $data; + logger('rino: sent key = ' . $key); + + if(($contact['duplex'] && strlen($contact['prvkey'])) || ($owner['page-flags'] == PAGE_COMMUNITY)) { + openssl_private_encrypt($key,$postvars['key'],$contact['prvkey']); + } + else { + openssl_public_encrypt($key,$postvars['key'],$contact['pubkey']); + } + + logger('md5 rawkey ' . md5($postvars['key'])); + + $postvars['key'] = bin2hex($postvars['key']); + } + + logger('dfrn_deliver: ' . "SENDING: " . print_r($postvars,true), LOGGER_DATA); + + $xml = post_url($contact['notify'],$postvars); + + logger('dfrn_deliver: ' . "RECEIVED: " . $xml, LOGGER_DATA); + + $curl_stat = $a->get_curl_code(); + if((! $curl_stat) || (! strlen($xml))) + return(-1); // timed out + + $res = simplexml_load_string($xml); + + return $res->status; + +} + + +/* + * + * consume_feed - process atom feed and update anything/everything we might need to update + * + * $xml = the (atom) feed to consume - no RSS spoken here, it might partially work since simplepie + * handles both, but we don't claim it will work well, and are reasonably certain it won't. + * $importer = the contact_record (joined to user_record) of the local user who owns this relationship. + * It is this person's stuff that is going to be updated. + * $contact = the person who is sending us stuff. If not set, we MAY be processing a "follow" activity + * from an external network and MAY create an appropriate contact record. Otherwise, we MUST + * have a contact record. + * $hub = should we find a hub declation in the feed, pass it back to our calling process, who might (or + * might not) try and subscribe to it. + * + */ + +function consume_feed($xml,$importer,$contact, &$hub, $datedir = 0) { + + require_once('simplepie/simplepie.inc'); + + $feed = new SimplePie(); + $feed->set_raw_data($xml); + if($datedir) + $feed->enable_order_by_date(true); + else + $feed->enable_order_by_date(false); + $feed->init(); + + // Check at the feed level for updated contact name and/or photo + + $name_updated = ''; + $new_name = ''; + $photo_timestamp = ''; + $photo_url = ''; + + + $hubs = $feed->get_links('hub'); + + if(count($hubs)) + $hub = implode(',', $hubs); + + $rawtags = $feed->get_feed_tags( SIMPLEPIE_NAMESPACE_ATOM_10, 'author'); + if($rawtags) { + $elems = $rawtags[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]; + if($elems['name'][0]['attribs'][NAMESPACE_DFRN]['updated']) { + $name_updated = $elems['name'][0]['attribs'][NAMESPACE_DFRN]['updated']; + $new_name = $elems['name'][0]['data']; + } + if((x($elems,'link')) && ($elems['link'][0]['attribs']['']['rel'] === 'photo') && ($elems['link'][0]['attribs'][NAMESPACE_DFRN]['updated'])) { + $photo_timestamp = datetime_convert('UTC','UTC',$elems['link'][0]['attribs'][NAMESPACE_DFRN]['updated']); + $photo_url = $elems['link'][0]['attribs']['']['href']; + } + } + + if((is_array($contact)) && ($photo_timestamp) && (strlen($photo_url)) && ($photo_timestamp > $contact['avatar-date'])) { + logger('Consume feed: Updating photo for ' . $contact['name']); + require_once("Photo.php"); + $photo_failure = false; + $have_photo = false; + + $r = q("SELECT `resource-id` FROM `photo` WHERE `contact-id` = %d AND `uid` = %d LIMIT 1", + intval($contact['id']), + intval($contact['uid']) + ); + if(count($r)) { + $resource_id = $r[0]['resource-id']; + $have_photo = true; + } + else { + $resource_id = photo_new_resource(); + } + + $img_str = fetch_url($photo_url,true); + $img = new Photo($img_str); + if($img->is_valid()) { + if($have_photo) { + q("DELETE FROM `photo` WHERE `resource-id` = '%s' AND `contact-id` = %d AND `uid` = %d", + dbesc($resource_id), + intval($contact['id']), + intval($contact['uid']) + ); + } + + $img->scaleImageSquare(175); + + $hash = $resource_id; + $r = $img->store($contact['uid'], $contact['id'], $hash, basename($photo_url), t('Contact Photos') , 4); + + $img->scaleImage(80); + $r = $img->store($contact['uid'], $contact['id'], $hash, basename($photo_url), t('Contact Photos') , 5); + + $img->scaleImage(48); + $r = $img->store($contact['uid'], $contact['id'], $hash, basename($photo_url), t('Contact Photos') , 6); + + $a = get_app(); + + q("UPDATE `contact` SET `avatar-date` = '%s', `photo` = '%s', `thumb` = '%s', `micro` = '%s' + WHERE `uid` = %d AND `id` = %d LIMIT 1", + dbesc(datetime_convert()), + dbesc($a->get_baseurl() . '/photo/' . $hash . '-4.jpg'), + dbesc($a->get_baseurl() . '/photo/' . $hash . '-5.jpg'), + dbesc($a->get_baseurl() . '/photo/' . $hash . '-6.jpg'), + intval($contact['uid']), + intval($contact['id']) + ); + } + } + + if((is_array($contact)) && ($name_updated) && (strlen($new_name)) && ($name_updated > $contact['name-date'])) { + q("UPDATE `contact` SET `name` = '%s', `name-date` = '%s' WHERE `uid` = %d AND `id` = %d LIMIT 1", + dbesc(notags(trim($new_name))), + dbesc(datetime_convert()), + intval($contact['uid']), + intval($contact['id']) + ); + } + + // Now process the feed + if($feed->get_item_quantity()) { + foreach($feed->get_items() as $item) { + + $deleted = false; + + $rawdelete = $item->get_item_tags( NAMESPACE_TOMB, 'deleted-entry'); + if(isset($rawdelete[0]['attribs']['']['ref'])) { + $uri = $rawthread[0]['attribs']['']['ref']; + $deleted = true; + if(isset($rawdelete[0]['attribs']['']['when'])) { + $when = $rawthread[0]['attribs']['']['when']; + $when = datetime_convert('UTC','UTC', $when, 'Y-m-d H:i:s'); + } + else + $when = datetime_convert('UTC','UTC','now','Y-m-d H:i:s'); + } + if($deleted && is_array($contact)) { + $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d AND `contact-id` = %d LIMIT 1", + dbesc($uri), + intval($importer['uid']), + intval($contact['id']) + ); + if(count($r)) { + $item = $r[0]; + if($item['uri'] == $item['parent-uri']) { + $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s', + `body` = '', `title` = '' + WHERE `parent-uri` = '%s' AND `uid` = %d", + dbesc($when), + dbesc(datetime_convert()), + dbesc($item['uri']), + intval($importer['uid']) + ); + } + else { + $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s', + `body` = '', `title` = '' + WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", + dbesc($when), + dbesc(datetime_convert()), + dbesc($uri), + intval($importer['uid']) + ); + if($item['last-child']) { + // ensure that last-child is set in case the comment that had it just got wiped. + $q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d ", + dbesc(datetime_convert()), + dbesc($item['parent-uri']), + intval($item['uid']) + ); + // who is the last child now? + $r = q("SELECT `id` FROM `item` WHERE `parent-uri` = '%s' AND `type` != 'activity' AND `deleted` = 0 AND `uid` = %d + ORDER BY `created` DESC LIMIT 1", + dbesc($item['parent-uri']), + intval($importer['uid']) + ); + if(count($r)) { + q("UPDATE `item` SET `last-child` = 1 WHERE `id` = %d LIMIT 1", + intval($r[0]['id']) + ); + } + } + } + } + continue; + } + + + $is_reply = false; + $item_id = $item->get_id(); + $rawthread = $item->get_item_tags( NAMESPACE_THREAD,'in-reply-to'); + if(isset($rawthread[0]['attribs']['']['ref'])) { + $is_reply = true; + $parent_uri = $rawthread[0]['attribs']['']['ref']; + } + + + if(($is_reply) && is_array($contact)) { + + // Have we seen it? If not, import it. + + $item_id = $item->get_id(); + + $r = q("SELECT `uid`, `last-child`, `edited` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", + dbesc($item_id), + intval($importer['uid']) + ); + // FIXME update content if 'updated' changes + if(count($r)) { + $allow = $item->get_item_tags( NAMESPACE_DFRN, 'comment-allow'); + if(($allow) && ($allow[0]['data'] != $r[0]['last-child'])) { + $r = q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d", + dbesc(datetime_convert()), + dbesc($parent_uri), + intval($importer['uid']) + ); + $r = q("UPDATE `item` SET `last-child` = %d , `changed` = '%s' WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", + intval($allow[0]['data']), + dbesc(datetime_convert()), + dbesc($item_id), + intval($importer['uid']) + ); + } + continue; + } + $datarray = get_atom_elements($feed,$item); + if($contact['network'] === 'stat') { + if(strlen($datarray['title'])) + unset($datarray['title']); + $r = q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d", + dbesc(datetime_convert()), + dbesc($parent_uri), + intval($importer['uid']) + ); + $datarray['last-child'] = 1; + } + $datarray['parent-uri'] = $parent_uri; + $datarray['uid'] = $importer['uid']; + $datarray['contact-id'] = $contact['id']; + if((activity_match($datarray['verb'],ACTIVITY_LIKE)) || (activity_match($datarray['verb'],ACTIVITY_DISLIKE))) { + $datarray['type'] = 'activity'; + $datarray['gravity'] = GRAVITY_LIKE; + } + + $r = item_store($datarray); + continue; + } + + else { + // Head post of a conversation. Have we seen it? If not, import it. + + $item_id = $item->get_id(); + $r = q("SELECT `uid`, `last-child`, `edited` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", + dbesc($item_id), + intval($importer['uid']) + ); + if(count($r)) { + $allow = $item->get_item_tags( NAMESPACE_DFRN, 'comment-allow'); + if($allow && $allow[0]['data'] != $r[0]['last-child']) { + $r = q("UPDATE `item` SET `last-child` = %d , `changed` = '%s' WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", + intval($allow[0]['data']), + dbesc(datetime_convert()), + dbesc($item_id), + intval($importer['uid']) + ); + } + continue; + } + $datarray = get_atom_elements($feed,$item); + + if(activity_match($datarray['verb'],ACTIVITY_FOLLOW)) { + logger('consume-feed: New follower'); + new_follower($importer,$contact,$datarray,$item); + return; + } + if(activity_match($datarray['verb'],ACTIVITY_UNFOLLOW)) { + lose_follower($importer,$contact,$datarray,$item); + return; + } + if(! is_array($contact)) + return; + + if($contact['network'] === 'stat') { + if(strlen($datarray['title'])) + unset($datarray['title']); + $datarray['last-child'] = 1; + } + + $datarray['parent-uri'] = $item_id; + $datarray['uid'] = $importer['uid']; + $datarray['contact-id'] = $contact['id']; + $r = item_store($datarray); + continue; + + } + } + } + +} + +function new_follower($importer,$contact,$datarray,$item) { + $url = notags(trim($datarray['author-link'])); + $name = notags(trim($datarray['author-name'])); + $photo = notags(trim($datarray['author-avatar'])); + + $rawtag = $item->get_item_tags(NAMESPACE_ACTIVITY,'actor'); + if($rawtag && $rawtag[0]['child'][NAMESPACE_POCO]['preferredUsername'][0]['data']) + $nick = $rawtag[0]['child'][NAMESPACE_POCO]['preferredUsername'][0]['data']; + + if(is_array($contact)) { + if($contact['network'] == 'stat' && $contact['rel'] == REL_FAN) { + $r = q("UPDATE `contact` SET `rel` = %d WHERE `id` = %d AND `uid` = %d LIMIT 1", + intval(REL_BUD), + intval($contact['id']), + intval($importer['uid']) + ); + } + + // send email notification to owner? + } + else { + + // create contact record - set to readonly + + $r = q("INSERT INTO `contact` ( `uid`, `created`, `url`, `name`, `nick`, `photo`, `network`, `rel`, + `blocked`, `readonly`, `pending` ) + VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', %d, 0, 1, 1 ) ", + intval($importer['uid']), + dbesc(datetime_convert()), + dbesc($url), + dbesc($name), + dbesc($nick), + dbesc($photo), + dbesc('stat'), + intval(REL_VIP) + ); + $r = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `url` = '%s' AND `pending` = 1 AND `rel` = %d LIMIT 1", + intval($importer['uid']), + dbesc($url), + intval(REL_VIP) + ); + if(count($r)) + $contact_record = $r[0]; + + // create notification + $hash = random_string(); + + if(is_array($contact_record)) { + $ret = q("INSERT INTO `intro` ( `uid`, `contact-id`, `blocked`, `knowyou`, `hash`, `datetime`) + VALUES ( %d, %d, 0, 0, '%s', '%s' )", + intval($importer['uid']), + intval($contact_record['id']), + dbesc($hash), + dbesc(datetime_convert()) + ); + } + $r = q("SELECT * FROM `user` WHERE `uid` = %d LIMIT 1", + intval($importer['uid']) + ); + if(count($r)) { + if(($r[0]['notify-flags'] & NOTIFY_INTRO) && ($r[0]['page-flags'] == PAGE_NORMAL)) { + $email_tpl = load_view_file('view/follow_notify_eml.tpl'); + $email = replace_macros($email_tpl, array( + '$requestor' => ((strlen($name)) ? $name : t('[Name Withheld]')), + '$url' => $url, + '$myname' => $r[0]['username'], + '$siteurl' => $a->get_baseurl(), + '$sitename' => $a->config['sitename'] + )); + $res = mail($r[0]['email'], + t("You have a new follower at ") . $a->config['sitename'], + $email, + 'From: ' . t('Administrator') . '@' . $_SERVER['SERVER_NAME'] ); + + } + } + } +} + +function lose_follower($importer,$contact,$datarray,$item) { + + if(($contact['rel'] == REL_BUD) || ($contact['rel'] == REL_FAN)) { + q("UPDATE `contact` SET `rel` = %d WHERE `id` = %d LIMIT 1", + intval(REL_FAN), + intval($contact['id']) + ); + } + else { + contact_remove($contact['id']); + } +} + + +function subscribe_to_hub($url,$importer,$contact) { + + if(is_array($importer)) { + $r = q("SELECT `nickname` FROM `user` WHERE `uid` = %d LIMIT 1", + intval($importer['uid']) + ); + } + if(! count($r)) + return; + + $push_url = get_config('system','url') . '/pubsub/' . $r[0]['nickname'] . '/' . $contact['id']; + + // Use a single verify token, even if multiple hubs + + $verify_token = ((strlen($contact['hub-verify'])) ? $contact['hub-verify'] : random_string()); + + $params= 'hub.mode=subscribe&hub.callback=' . urlencode($push_url) . '&hub.topic=' . urlencode($contact['poll']) . '&hub.verify=async&hub.verify_token=' . $verify_token; + + logger('subscribe_to_hub: subscribing ' . $contact['name'] . ' to hub ' . $url . ' with verifier ' . $verify_token); + + if(! strlen($contact['hub-verify'])) { + $r = q("UPDATE `contact` SET `hub-verify` = '%s' WHERE `id` = %d LIMIT 1", + dbesc($verify_token), + intval($contact['id']) + ); + } + + post_url($url,$params); + return; + +} + + +function atom_author($tag,$name,$uri,$h,$w,$photo) { + $o = ''; + if(! $tag) + return $o; + $name = xmlify($name); + $uri = xmlify($uri); + $h = intval($h); + $w = intval($w); + $photo = xmlify($photo); + + + $o .= "<$tag>\r\n"; + $o .= "$name\r\n"; + $o .= "$uri\r\n"; + $o .= '' . "\r\n"; + $o .= '' . "\r\n"; + $o .= "\r\n"; + return $o; +} + +function atom_entry($item,$type,$author,$owner,$comment = false) { + + if($item['deleted']) + return '' . "\r\n"; + + $a = get_app(); + + $o = "\r\n\r\n\r\n"; + + if(is_array($author)) + $o .= atom_author('author',$author['name'],$author['url'],80,80,$author['thumb']); + else + $o .= atom_author('author',$item['name'],$item['url'],80,80,$item['thumb']); + if(strlen($item['owner-name'])) + $o .= atom_author('dfrn:owner',$item['owner-name'],$item['owner-link'],80,80,$item['owner-avatar']); + + if($item['parent'] != $item['id']) + $o .= '' . "\r\n"; + + $o .= '' . xmlify($item['uri']) . '' . "\r\n"; + $o .= '' . xmlify($item['title']) . '' . "\r\n"; + $o .= '' . xmlify(datetime_convert('UTC','UTC',$item['created'] . '+00:00',ATOM_TIME)) . '' . "\r\n"; + $o .= '' . xmlify(datetime_convert('UTC','UTC',$item['edited'] . '+00:00',ATOM_TIME)) . '' . "\r\n"; + $o .= '' . xmlify(($type === 'html') ? bbcode($item['body']) : $item['body']) . '' . "\r\n"; + $o .= '' . "\r\n"; + if($comment) + $o .= '' . intval($item['last-child']) . '' . "\r\n"; + + if($item['location']) { + $o .= '' . xmlify($item['location']) . '' . "\r\n"; + $o .= '' . xmlify($item['location']) . '' . "\r\n"; + } + + if($item['coord']) + $o .= '' . xmlify($item['coord']) . '' . "\r\n"; + + if(($item['private']) || strlen($item['allow_cid']) || strlen($item['allow_gid']) || strlen($item['deny_cid']) || strlen($item['deny_gid'])) + $o .= '1' . "\r\n"; + + $verb = construct_verb($item); + $o .= '' . xmlify($verb) . '' . "\r\n"; + $actobj = construct_activity_object($item); + if(strlen($actobj)) + $o .= $actobj; + $actarg = construct_activity_target($item); + if(strlen($actarg)) + $o .= $actarg; + + $mentioned = get_mentions($item); + if($mentioned) + $o .= $mentioned; + + $o .= '' . "\r\n"; + + return $o; +} + \ No newline at end of file