X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=include%2Fitems.php;h=7b0ff2ce6b09e0fbd936012674c6f1fe61585335;hb=2fc89deaeae0341aa12b6b811927e793011055d1;hp=fa07727de1850d100a21787e72f5ceb6b11c0b81;hpb=eb5e6aae7273fef06a06f983fe78aaad737aa729;p=friendica.git diff --git a/include/items.php b/include/items.php index fa07727de1..7b0ff2ce6b 100644 --- a/include/items.php +++ b/include/items.php @@ -2,7 +2,7 @@ require_once('bbcode.php'); -function get_feed_for(&$a, $dfrn_id, $owner_id, $last_update, $direction = 0) { +function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0) { // default permissions - anonymous user @@ -14,24 +14,66 @@ function get_feed_for(&$a, $dfrn_id, $owner_id, $last_update, $direction = 0) { AND `deny_gid` = '' "; - if(strlen($owner_id) && ! intval($owner_id)) { - $r = q("SELECT `uid`, `nickname` FROM `user` WHERE `nickname` = '%s' LIMIT 1", - dbesc($owner_id) + if(strlen($owner_nick) && ! intval($owner_nick)) { + $r = q("SELECT `uid`, `nickname`, `timezone` FROM `user` WHERE `nickname` = '%s' LIMIT 1", + dbesc($owner_nick) ); if(count($r)) { $owner_id = $r[0]['uid']; $owner_nick = $r[0]['nickname']; + $owner_tz = $r[0]['timezone']; } } $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1", intval($owner_id) ); - if(count($r)) + if(count($r)) { $owner = $r[0]; + $owner['nickname'] = $owner_nick; + } else killme(); + + /** + * + * Determine the next birthday, but only if the birthday is published + * in the default profile. We _could_ also look for a private profile that the + * recipient can see, but somebody could get mad at us if they start getting + * public birthday greetings when they haven't made this info public. + * + * Assuming we are able to publish this info, we are then going to convert + * the start time from the owner's timezone to UTC. + * + * This will potentially solve the problem found with some social networks + * where birthdays are converted to the viewer's timezone and salutations from + * elsewhere in the world show up on the wrong day. We will convert it to the + * viewer's timezone also, but first we are going to convert it from the birthday + * person's timezone to GMT - so the viewer may find the birthday starting at + * 6:00PM the day before, but that will correspond to midnight to the birthday person. + * + */ + + $birthday = ''; + + $p = q("SELECT `dob` FROM `profile` WHERE `is-default` = 1 AND `uid` = %d LIMIT 1", + intval($owner_id) + ); + + if($p && count($p)) { + $tmp_dob = substr($p[0]['dob'],5); + if(intval($tmp_dob)) { + $y = datetime_convert($owner_tz,$owner_tz,'now','Y'); + $bd = $y . '-' . $tmp_dob . ' 00:00'; + $t_dob = strtotime($bd); + $now = strtotime(datetime_convert($owner_tz,$owner_tz,'now')); + if($t_dob < $now) + $bd = $y + 1 . '-' . $tmp_dob . ' 00:00'; + $birthday = datetime_convert($owner_tz,'UTC',$bd,ATOM_TIME); + } + } + if($dfrn_id && $dfrn_id != '*') { $sql_extra = ''; @@ -90,7 +132,7 @@ function get_feed_for(&$a, $dfrn_id, $owner_id, $last_update, $direction = 0) { $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'); @@ -117,9 +159,6 @@ function get_feed_for(&$a, $dfrn_id, $owner_id, $last_update, $direction = 0) { $items = $r; $feed_template = load_view_file('view/atom_feed.tpl'); - $tomb_template = load_view_file('view/atom_tomb.tpl'); - $item_template = load_view_file('view/atom_item.tpl'); - $cmnt_template = load_view_file('view/atom_cmnt.tpl'); $atom = ''; @@ -144,9 +183,10 @@ function get_feed_for(&$a, $dfrn_id, $owner_id, $last_update, $direction = 0) { $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', $updated . '+00:00' , ATOM_TIME)) , + '$feed_updated' => xmlify(datetime_convert('UTC', 'UTC', 'now' , ATOM_TIME)) , '$hub' => $hubxml, '$salmon' => $salmon, '$name' => xmlify($owner['name']), @@ -155,11 +195,16 @@ function get_feed_for(&$a, $dfrn_id, $owner_id, $last_update, $direction = 0) { '$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)) + '$namdate' => xmlify(datetime_convert('UTC','UTC',$owner['name-date'] . '+00:00' , ATOM_TIME)) , + '$birthday' => ((strlen($birthday)) ? '' . xmlify($birthday) . '' : '') )); + call_hooks('atom_feed', $atom); if(! count($items)) { + + call_hooks('atom_feed_end', $atom); + $atom .= '' . "\r\n"; return $atom; } @@ -169,69 +214,19 @@ function get_feed_for(&$a, $dfrn_id, $owner_id, $last_update, $direction = 0) { // public feeds get html, our own nodes use bbcode if($dfrn_id === '*') { - $allow = (($item['last-child']) ? 1 : 0); - $item['body'] = bbcode($item['body']); $type = 'html'; } else { - $allow = ((($item['last-child']) && ($contact['rel']) && ($contact['rel'] != REL_FAN)) ? 1 : 0); $type = 'text'; } - if($item['deleted']) { - $atom .= replace_macros($tomb_template, array( - '$id' => xmlify($item['uri']), - '$updated' => xmlify(datetime_convert('UTC', 'UTC', $item['edited'] . '+00:00' , ATOM_TIME)) - )); - } - else { - $verb = construct_verb($item); - $actobj = construct_activity($item); - - 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' , ATOM_TIME)), - '$updated' => xmlify(datetime_convert('UTC', 'UTC', $item['edited'] . '+00:00' , ATOM_TIME)), - '$location' => xmlify($item['location']), - '$coord' => xmlify($item['coord']), - '$type' => $type, - '$alt' => xmlify($a->get_baseurl() . '/display/' . $owner_nick . '/' . $item['id']), - '$content' => xmlify($item['body']), - '$verb' => xmlify($verb), - '$actobj' => $actobj, // do not xmlify - '$comment_allow' => $allow - )); - } - 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' , ATOM_TIME)), - '$updated' => xmlify(datetime_convert('UTC', 'UTC', $item['edited'] . '+00:00' , ATOM_TIME)), - '$type' => $type, - '$content' => xmlify($item['body']), - '$alt' => xmlify($a->get_baseurl() . '/display/' . $owner_nick . '/' . $item['id']), - '$verb' => xmlify($verb), - '$actobj' => $actobj, // do not xmlify - '$parent_id' => xmlify($item['parent-uri']), - '$comment_allow' => $allow - )); - } - } + $atom .= atom_entry($item,$type,null,$owner,true); } + call_hooks('atom_feed_end', $atom); + $atom .= '' . "\r\n"; + return $atom; } @@ -242,7 +237,7 @@ function construct_verb($item) { return ACTIVITY_POST; } -function construct_activity($item) { +function construct_activity_object($item) { if($item['object']) { $o = '' . "\r\n"; @@ -251,10 +246,14 @@ function construct_activity($item) { $o .= '' . xmlify($r->type) . '' . "\r\n"; if($r->id) $o .= '' . xmlify($r->id) . '' . "\r\n"; - if($r->link) - $o .= '' . "\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"; @@ -264,6 +263,32 @@ function construct_activity($item) { return ''; } +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 ''; +} + @@ -301,7 +326,7 @@ function get_atom_elements($feed,$item) { $rawactor = $item->get_item_tags(NAMESPACE_ACTIVITY, 'actor'); - if($rawactor && $rawactor[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'] === ACTIVITY_OBJ_PERSON) { + 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) { @@ -317,7 +342,7 @@ function get_atom_elements($feed,$item) { // No photo/profile-link on the item - look at the feed level - if((! $res['author-link']) || (! $res['author-avatar'])) { + 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']; @@ -333,14 +358,14 @@ function get_atom_elements($feed,$item) { $rawactor = $feed->get_feed_tags(NAMESPACE_ACTIVITY, 'subject'); - if($rawactor && $rawactor[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'] === ACTIVITY_OBJ_PERSON) { + 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(! (x($res,'author-avatar'))) { if($link['attribs']['']['rel'] === 'avatar' || $link['attribs']['']['rel'] === 'photo') $res['author-avatar'] = unxmlify($link['attribs']['']['href']); } @@ -356,7 +381,7 @@ function get_atom_elements($feed,$item) { // 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 + // 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 @@ -365,7 +390,7 @@ function get_atom_elements($feed,$item) { // html. - if(strpos($res['body'],'<')) { + if((strpos($res['body'],'<')) || (strpos($res['body'],'>'))) { $res['body'] = preg_replace('#]+>.+?' . 'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?#s', '[youtube]$1[/youtube]', $res['body']); @@ -379,11 +404,12 @@ function get_atom_elements($feed,$item) { $purifier = new HTMLPurifier($config); $res['body'] = $purifier->purify($res['body']); - } + $res['body'] = html2bbcode($res['body']); + } + else + $res['body'] = escape_tags($res['body']); - $res['body'] = html2bbcode($res['body']); - $allow = $item->get_item_tags(NAMESPACE_DFRN,'comment-allow'); if($allow && $allow[0]['data'] == 1) @@ -391,19 +417,35 @@ function get_atom_elements($feed,$item) { else $res['last-child'] = 0; - $rawcreated = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'published'); - if($rawcreated) - $res['created'] = unxmlify($rawcreated[0]['data']); + $private = $item->get_item_tags(NAMESPACE_DFRN,'private'); + if($private && $private[0]['data'] == 1) + $res['private'] = 1; + else + $res['private'] = 0; + $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($rawcreated[0]['data']); + + if(! $res['created']) + $res['created'] = $item->get_date(); + + if(! $res['edited']) + $res['edited'] = $item->get_date(); + + $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']); @@ -431,9 +473,19 @@ function get_atom_elements($feed,$item) { $rawverb = $item->get_item_tags(NAMESPACE_ACTIVITY, 'verb'); + // select between supported verbs - if($rawverb) + + 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; + + $rawobj = $item->get_item_tags(NAMESPACE_ACTIVITY, 'object'); @@ -445,16 +497,17 @@ function get_atom_elements($feed,$item) { } 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'][0]['attribs']['']['rel'] === 'alternate') - $res['object'] .= '' . $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'][0]['attribs']['']['href'] . '' . "\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,'<')) { + // preserve a copy of the original body content in case we later need to parse out any microformat information, e.g. events + $res['object'] .= '' . xmlify($body) . '' . "\n"; + if((strpos($body,'<')) || (strpos($body,'>'))) { $body = preg_replace('#]+>.+?' . 'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?#s', '[youtube]$1[/youtube]', $body); @@ -464,110 +517,226 @@ function get_atom_elements($feed,$item) { $purifier = new HTMLPurifier($config); $body = $purifier->purify($body); + $body = html2bbcode($body); } + else + $body = escape_tags($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']; + // preserve a copy of the original body content in case we later need to parse out any microformat information, e.g. events + $res['object'] .= '' . xmlify($body) . '' . "\n"; + if((strpos($body,'<')) || (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); + } + else + $body = escape_tags($body); + + $res['target'] .= '' . $body . '' . "\n"; + } + + $res['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 .= ' may have been double encoded, depending on which filter chain + // they came through. + + $arr['body'] = str_replace( + array('&amp;', '&gt;', '&lt;', '&quot;'), + 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; + } + } + + call_hooks('post_remote',$arr); dbesc_array($arr); - $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 parent and snarf the item id and ACL's + // find the item we just created - $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", - dbesc($arr['parent-uri']), + $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", + $arr['uri'], // already dbesc'd intval($arr['uid']) ); - if(count($r)) { - $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']; + $current_post = $r[0]['id']; + logger('item_store: created item ' . $current_post); } else { - $parent_missing = true; + 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'], // already dbesc'd - 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 - return 0; - - if($parent_missing) { - - // perhaps the parent was deleted, but in any case, this thread is dead - // and unfortunately our brand new item now has to be destroyed - - q("DELETE FROM `item` WHERE `id` = %d LIMIT 1", - intval($current_post) - ); - return 0; - } + $private = $arr['private']; - // Set parent id - all of the parent's ACL's are also inherited by this post + // 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' WHERE `id` = %d LIMIT 1", + `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) ); @@ -587,8 +756,9 @@ function get_item_contact($item,$contacts) { } -function dfrn_deliver($owner,$contact,$atom,$debugging = false) { +function dfrn_deliver($owner,$contact,$atom, $dissolve = false) { + $a = get_app(); if((! strlen($contact['dfrn-id'])) && (! $contact['duplex']) && (! ($owner['page-flags'] == PAGE_COMMUNITY))) return 3; @@ -600,15 +770,24 @@ function dfrn_deliver($owner,$contact,$atom,$debugging = false) { if($contact['duplex'] && $contact['issued-id']) $idtosend = '1:' . $orig_id; - $url = $contact['notify'] . '?dfrn_id=' . $idtosend . '&dfrn_version=' . DFRN_PROTOCOL_VERSION ; + $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' : ''); - if($debugging) - echo "URL: $url\n"; + logger('dfrn_deliver: ' . $url); $xml = fetch_url($url); - if($debugging) - echo $xml; + $curl_stat = $a->get_curl_code(); + if(! $curl_stat) + return(-1); // timed out + + logger('dfrn_deliver: ' . $xml); if(! $xml) return 3; @@ -619,13 +798,13 @@ function dfrn_deliver($owner,$contact,$atom,$debugging = false) { return (($res->status) ? $res->status : 3); $postvars = array(); - $sent_dfrn_id = hex2bin($res->dfrn_id); - $challenge = hex2bin($res->challenge); + $sent_dfrn_id = hex2bin((string) $res->dfrn_id); + $challenge = hex2bin((string) $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']); @@ -641,14 +820,15 @@ function dfrn_deliver($owner,$contact,$atom,$debugging = false) { $final_dfrn_id = substr($final_dfrn_id,2); if($final_dfrn_id != $orig_id) { - if($debugging) - echo "Wrong ID - did not decode\n"; + 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($dissolve) + $postvars['dissolve'] = '1'; if(($contact['rel']) && ($contact['rel'] != REL_FAN) && (! $contact['blocked']) && (! $contact['readonly'])) { $postvars['data'] = $atom; @@ -660,13 +840,34 @@ function dfrn_deliver($owner,$contact,$atom,$debugging = false) { $postvars['data'] = str_replace('1','0',$atom); } - $xml = post_url($contact['notify'],$postvars); + if($rino && $rino_allowed && (! $dissolve)) { + $key = substr(random_string(),0,16); + $data = bin2hex(aes_encrypt($postvars['data'],$key)); + $postvars['data'] = $data; + logger('rino: sent key = ' . $key); - if($debugging) { - echo "SENDING: " . print_r($postvars,true) . "\n"; - echo "RECEIVING: " . $xml; + 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; @@ -685,28 +886,30 @@ function dfrn_deliver($owner,$contact,$atom,$debugging = false) { * $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 wefind ahub declation in the feed, pass it back to our calling process, who might (or + * $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) { +function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0) { require_once('simplepie/simplepie.inc'); $feed = new SimplePie(); $feed->set_raw_data($xml); - $feed->enable_order_by_date(false); + 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 - $debugging = get_config('system','debugging'); $name_updated = ''; $new_name = ''; $photo_timestamp = ''; $photo_url = ''; - + $birthday = ''; $hubs = $feed->get_links('hub'); @@ -720,22 +923,21 @@ function consume_feed($xml,$importer,$contact, &$hub) { $name_updated = $elems['name'][0]['attribs'][NAMESPACE_DFRN]['updated']; $new_name = $elems['name'][0]['data']; } - if(($elems['link'][0]['attribs']['']['rel'] === 'photo') && ($elems['link'][0]['attribs'][NAMESPACE_DFRN]['updated'])) { + 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(! $photo_timestamp) { - $photo_rawupdate = $feed->get_feed_tags(NAMESPACE_DFRN,'icon-updated'); - if($photo_rawupdate) { - $photo_timestamp = datetime_convert('UTC','UTC',$photo_rawupdate[0]['data']); - $photo_url = $feed->get_image_url(); + + if((x($rawtags[0]['child'], NAMESPACE_DFRN)) && (x($rawtags[0]['child'][NAMESPACE_DFRN],'birthday'))) { + $birthday = datetime_convert('UTC','UTC', $rawtags[0]['child'][NAMESPACE_DFRN]['birthday'][0]['data']); } } - if((is_array($contact)) && ($photo_timestamp) && (strlen($photo_url)) && ($photo_timestamp > $contact['avatar-date'])) { + 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']), @@ -743,29 +945,45 @@ function consume_feed($xml,$importer,$contact, &$hub) { ); if(count($r)) { $resource_id = $r[0]['resource-id']; - $img_str = fetch_url($photo_url,true); - $img = new Photo($img_str); - if($img->is_valid()) { - q("DELETE FROM `photo` WHERE `resource-id` = '%s' AND contact-id` = %d AND `uid` = %d", + $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->scaleImageSquare(175); - $img->scaleImage(80); - $r = $img->store($contact['uid'], $contact['id'], $hash, basename($photo_url), t('Contact Photos') , 5); - if($r) - q("UPDATE `contact` SET `avatar-date` = '%s' WHERE `uid` = %d AND `id` = %d LIMIT 1", - dbesc(datetime_convert()), - intval($contact['uid']), - intval($contact['id']) - ); - } + $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']) + ); } } @@ -778,9 +996,63 @@ function consume_feed($xml,$importer,$contact, &$hub) { ); } + if(strlen($birthday)) { + if(substr($birthday,0,4) != $contact['bdyear']) { + logger('consume_feed: updating birthday: ' . $birthday); + + /** + * + * Add new birthday event for this person + * + * $bdtext is just a readable placeholder in case the event is shared + * with others. We will replace it during presentation to our $importer + * to contain a sparkle link and perhaps a photo. + * + */ + + $bdtext = t('Birthday:') . ' [url=' . $contact['url'] . ']' . $contact['name'] . '[/url]' ; + + + $r = q("INSERT INTO `event` (`uid`,`cid`,`created`,`edited`,`start`,`finish`,`desc`,`type`) + VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s' ) ", + intval($contact['uid']), + intval($contact['id']), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + dbesc(datetime_convert('UTC','UTC', $birthday)), + dbesc(datetime_convert('UTC','UTC', $birthday . ' + 1 day ')), + dbesc($bdtext), + dbesc('birthday') + ); + + + // update bdyear + + q("UPDATE `contact` SET `bdyear` = '%s' WHERE `uid` = %d AND `id` = %d LIMIT 1", + dbesc(substr($birthday,0,4)), + intval($contact['uid']), + intval($contact['id']) + ); + + // This function is called twice without reloading the contact + // Make sure we only create one event. This is why &$contact + // is a reference var in this function + + $contact['bdyear'] = substr($birthday,0,4); + } + + } + // Now process the feed if($feed->get_item_quantity()) { - foreach($feed->get_items() as $item) { + + // in inverse date order + if ($datedir) + $items = array_reverse($feed->get_items()); + else + $items = $feed->get_items(); + + foreach($items as $item) { $deleted = false; @@ -869,7 +1141,7 @@ function consume_feed($xml,$importer,$contact, &$hub) { // 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']) { + 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), @@ -881,17 +1153,28 @@ function consume_feed($xml,$importer,$contact, &$hub) { dbesc($item_id), intval($importer['uid']) ); - } continue; } $datarray = get_atom_elements($feed,$item); - if($contact['network'] === 'stat' && strlen($datarray['title'])) - unset($datarray['title']); + 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; + } + if(($contact['network'] === 'feed') || (! strlen($contact['notify']))) { + // one way feed - no remote comment ability + $datarray['last-child'] = 0; + } $datarray['parent-uri'] = $parent_uri; $datarray['uid'] = $importer['uid']; $datarray['contact-id'] = $contact['id']; - if(($datarray['verb'] === ACTIVITY_LIKE) || ($datarray['verb'] === ACTIVITY_DISLIKE)) { + if((activity_match($datarray['verb'],ACTIVITY_LIKE)) || (activity_match($datarray['verb'],ACTIVITY_DISLIKE))) { $datarray['type'] = 'activity'; $datarray['gravity'] = GRAVITY_LIKE; } @@ -922,13 +1205,12 @@ function consume_feed($xml,$importer,$contact, &$hub) { } $datarray = get_atom_elements($feed,$item); - if($datarray['verb'] === ACTIVITY_FOLLOW) { - if($debugging) - file_put_contents('salmon.out',"\n" . 'New follower.' . "\n", FILE_APPEND); + if(activity_match($datarray['verb'],ACTIVITY_FOLLOW)) { + logger('consume-feed: New follower'); new_follower($importer,$contact,$datarray,$item); return; } - if($datarray['verb'] === ACTIVITY_UNFOLLOW) { + if(activity_match($datarray['verb'],ACTIVITY_UNFOLLOW)) { lose_follower($importer,$contact,$datarray,$item); return; } @@ -938,9 +1220,14 @@ function consume_feed($xml,$importer,$contact, &$hub) { if($contact['network'] === 'stat') { if(strlen($datarray['title'])) unset($datarray['title']); - if(($contact['rel'] == REL_VIP) || ($contact['rel'] == REL_BUD)) - $datarray['last-child'] = 1; + $datarray['last-child'] = 1; + } + + if(($contact['network'] === 'feed') || (! strlen($contact['notify']))) { + // one way feed - no remote comment ability + $datarray['last-child'] = 0; } + $datarray['parent-uri'] = $item_id; $datarray['uid'] = $importer['uid']; $datarray['contact-id'] = $contact['id']; @@ -950,7 +1237,6 @@ function consume_feed($xml,$importer,$contact, &$hub) { } } } - } function new_follower($importer,$contact,$datarray,$item) { @@ -1009,6 +1295,27 @@ function new_follower($importer,$contact,$datarray,$item) { dbesc(datetime_convert()) ); } + $r = q("SELECT * FROM `user` WHERE `uid` = %d LIMIT 1", + intval($importer['uid']) + ); + $a = get_app(); + 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'] ); + + } + } } } @@ -1044,6 +1351,8 @@ function subscribe_to_hub($url,$importer,$contact) { $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), @@ -1054,4 +1363,88 @@ function subscribe_to_hub($url,$importer,$contact) { post_url($url,$params); return; -} \ No newline at end of file +} + + +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"; + + call_hooks('atom_author', $o); + + $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; + + call_hooks('atom_entry', $o); + + $o .= '' . "\r\n"; + + return $o; +} +