From ea9a42259df530d280f4a4331a9fca7737a79de1 Mon Sep 17 00:00:00 2001 From: Michael Vogel Date: Fri, 22 Jan 2016 01:58:36 +0100 Subject: [PATCH] First version of dfrn.php - unfinished --- include/dfrn.php | 323 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 323 insertions(+) create mode 100644 include/dfrn.php diff --git a/include/dfrn.php b/include/dfrn.php new file mode 100644 index 0000000000..4fdc991721 --- /dev/null +++ b/include/dfrn.php @@ -0,0 +1,323 @@ +argc > 2) { + for($x = 2; $x < $a->argc; $x++) { + if($a->argv[$x] == 'converse') + $converse = true; + if($a->argv[$x] == 'starred') + $starred = true; + if($a->argv[$x] === 'category' && $a->argc > ($x + 1) && strlen($a->argv[$x+1])) + $category = $a->argv[$x+1]; + } + } + + + + // default permissions - anonymous user + + $sql_extra = " AND `item`.`allow_cid` = '' AND `item`.`allow_gid` = '' AND `item`.`deny_cid` = '' AND `item`.`deny_gid` = '' "; + + $r = q("SELECT `contact`.*, `user`.`uid` AS `user_uid`, `user`.`nickname`, `user`.`timezone`, `user`.`page-flags` + FROM `contact` INNER 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']); + + $sql_post_table = ""; + $visibility = ""; + + 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]; + require_once('include/security.php'); + $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'; + + $date_field = "`changed`"; + $sql_order = "`item`.`parent` ".$sort.", `item`.`created` ASC"; + + if(! strlen($last_update)) + $last_update = 'now -30 days'; + + if(isset($category)) { + $sql_post_table = sprintf("INNER JOIN (SELECT `oid` FROM `term` WHERE `term` = '%s' AND `otype` = %d AND `type` = %d AND `uid` = %d ORDER BY `tid` DESC) AS `term` ON `item`.`id` = `term`.`oid` ", + dbesc(protect_sprintf($category)), intval(TERM_OBJ_POST), intval(TERM_CATEGORY), intval($owner_id)); + //$sql_extra .= file_tag_file_query('item',$category,'category'); + } + + 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'); + + // AND ( `item`.`edited` > '%s' OR `item`.`changed` > '%s' ) + // dbesc($check_date), + + $r = q("SELECT STRAIGHT_JOIN `item`.*, `item`.`id` AS `item_id`, + `contact`.`name`, `contact`.`network`, `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` $sql_post_table + INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id` + AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0 + LEFT JOIN `sign` ON `sign`.`iid` = `item`.`id` + WHERE `item`.`uid` = %d AND `item`.`visible` = 1 and `item`.`moderated` = 0 AND `item`.`parent` != 0 + AND ((`item`.`wall` = 1) $visibility) AND `item`.$date_field > '%s' + $sql_extra + ORDER BY $sql_order LIMIT 0, 300", + intval($owner_id), + 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 = ''; + $doc = new DOMDocument('1.0', 'utf-8'); + $doc->formatOutput = true; + + $xpath = new DomXPath($doc); + $xpath->registerNamespace('atom', "http://www.w3.org/2005/Atom"); + $xpath->registerNamespace('thr', "http://purl.org/syndication/thread/1.0"); + $xpath->registerNamespace('georss', "http://www.georss.org/georss"); + $xpath->registerNamespace('activity', "http://activitystrea.ms/spec/1.0/"); + $xpath->registerNamespace('media', "http://purl.org/syndication/atommedia"); + $xpath->registerNamespace('poco', "http://portablecontacts.net/spec/1.0"); + $xpath->registerNamespace('ostatus', "http://ostatus.org/schema/1.0"); + $xpath->registerNamespace('statusnet', "http://status.net/schema/api/1/"); + + $root = ostatus_add_header($doc, $owner); + dfrn_add_header($root, $doc, $xpath, $owner); + + // Todo $hubxml = feed_hublinks(); + + // Todo $salmon = feed_salmonlinks($owner_nick); + + // todo $alternatelink = $owner['url']; + +/* + if(isset($category)) + $alternatelink .= "/category/".$category; + + $atom .= replace_macros($feed_template, array( + '$version' => xmlify(FRIENDICA_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, + '$alternatelink' => xmlify($alternatelink), + '$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)) ? '' . xmlify($birthday) . '' : ''), + '$community' => (($owner['page-flags'] == PAGE_COMMUNITY) ? '1' : '') + )); +*/ + call_hooks('atom_feed', $atom); + + if(! count($items)) { + + call_hooks('atom_feed_end', $atom); + + //$atom .= '' . "\r\n"; + //return $atom; + return(trim($doc->saveXML())); + } + + foreach($items as $item) { + + // prevent private email from leaking. + if($item['network'] === NETWORK_MAIL) + continue; + + // 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); + $entry = ostatus_entry($doc, $item, $owner); + dfrn_entry($entry, $doc, $xpath, $item, $owner); + $root->appendChild($entry); + + } + + call_hooks('atom_feed_end', $atom); + + //$atom .= '' . "\r\n"; + + //return $atom; + return(trim($doc->saveXML())); +} + +/** + * @brief Adds the header elements for thr DFRN protocol + * + * We use the XML from OStatus as a base and are adding the DFRN parts to it. + * + * @root Class XML root element + * @doc Class XML document + * @xpath Class XML xpath + * @owner array Owner record + * + */ +function dfrn_add_header(&$root, $doc, $xpath, $owner) { + + $root->setAttribute("xmlns:at", "http://purl.org/atompub/tombstones/1.0"); + $root->setAttribute("xmlns:xmlns:dfrn", "http://purl.org/macgirvin/dfrn/1.0"); + + $attributes = array("href" => "http://creativecommons.org/licenses/by/3.0/", "rel" => "license"); + xml_add_element($doc, $root, "link", "", $attributes); + + xml_replace_element($doc, $root, $xpath, "title", $owner["name"]); + xml_remove_element($root, $xpath, "/atom:feed/subtitle"); + xml_remove_element($root, $xpath, "/atom:feed/logo"); + xml_remove_element($root, $xpath, "/atom:feed/author"); + + $author = dfrn_add_author($doc, $owner); + $root->appendChild($author); +} + +function dfrn_add_author($doc, $owner) { + $a = get_app(); + + $author = $doc->createElement("author"); + + $namdate = datetime_convert('UTC', 'UTC', $owner['name-date'].'+00:00' , ATOM_TIME); + $uridate = datetime_convert('UTC', 'UTC', $owner['uri-date'].'+00:00', ATOM_TIME); + $picdate = datetime_convert('UTC', 'UTC', $owner['avatar-date'].'+00:00', ATOM_TIME); + + $attributes = array("dfrn:updated" => $namdate); + xml_add_element($doc, $author, "name", $owner["name"], $attributes); + + $attributes = array("dfrn:updated" => $namdate); + xml_add_element($doc, $author, "uri", $a->get_baseurl().'/profile/'.$owner["nickname"], $attributes); + + $attributes = array("rel" => "photo", "type" => "image/jpeg", "dfrn:updated" => $picdate, + "media:width" => 175, "media:height" => 175, "href" => $owner['photo']); + xml_add_element($doc, $author, "link", "", $attributes); + + $attributes = array("rel" => "avatar", "type" => "image/jpeg", "dfrn:updated" => $picdate, + "media:width" => 175, "media:height" => 175, "href" => $owner['photo']); + xml_add_element($doc, $author, "link", "", $attributes); + + return $author; +} + +function dfrn_entry($entry, $doc, $xpath, $item, $owner) { + $a = get_app(); + + $author = ostatus_add_author($doc, $owner); + $entry->appendChild($author); + +} + +function xml_replace_element($doc, $parent, $xpath, $element, $value = "", $attributes = array()) { + $old_element = $xpath->query("/atom:feed/".$element)->item(0); + + $element = $doc->createElement($element, xmlify($value)); + + foreach ($attributes AS $key => $value) { + $attribute = $doc->createAttribute($key); + $attribute->value = xmlify($value); + $element->appendChild($attribute); + } + + $parent->replaceChild($element, $old_element); +} + +function xml_remove_element($parent, $xpath, $element) { + $old_element = $xpath->query($element)->item(0); + $parent->removeChild($old_element); +}