X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=include%2Fdfrn.php;h=9a821a9d9125fe6a7c414a9df0c4ce50a637279d;hb=c33aa2ccee25a139b75330c1cd86c6164a020276;hp=ddebf1e9bf60210d575a337a7e38f976b8472340;hpb=cc3bc3f785e7f3e5aba67cb24ca8dcbb833ed8d3;p=friendica.git diff --git a/include/dfrn.php b/include/dfrn.php index ddebf1e9bf..9a821a9d91 100644 --- a/include/dfrn.php +++ b/include/dfrn.php @@ -7,6 +7,9 @@ * https://github.com/friendica/friendica/blob/master/spec/dfrn2.pdf */ +use Friendica\App; +use Friendica\Core\System; + require_once("include/Contact.php"); require_once("include/ostatus.php"); require_once("include/enotify.php"); @@ -243,7 +246,7 @@ class dfrn { /// @TODO This hook can't work anymore // call_hooks('atom_feed', $atom); - if (!dbm::is_result($items) OR $onlyheader) { + if (!dbm::is_result($items) || $onlyheader) { $atom = trim($doc->saveXML()); call_hooks('atom_feed_end', $atom); @@ -282,6 +285,59 @@ class dfrn { return $atom; } + /** + * @brief Generate an atom entry for a given item id + * + * @param int $item_id The item id + * + * @return string DFRN feed entry + */ + public static function itemFeed($item_id) { + $r = q("SELECT `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`, + `sign`.`signed_text`, `sign`.`signature`, `sign`.`signer` + FROM `item` + STRAIGHT_JOIN `contact` ON `contact`.`id` = `item`.`contact-id` + AND (NOT `contact`.`blocked` OR `contact`.`pending`) + LEFT JOIN `sign` ON `sign`.`iid` = `item`.`id` + WHERE `item`.`id` = %d AND `item`.`visible` AND NOT `item`.`moderated` AND `item`.`parent` != 0 + AND NOT `item`.`private`", + intval($item_id) + ); + + if (!dbm::is_result($r)) { + killme(); + } + + $item = $r[0]; + + $r = q("SELECT `contact`.*, `user`.`nickname`, `user`.`timezone`, `user`.`page-flags`, `user`.`account-type` + FROM `contact` INNER JOIN `user` ON `user`.`uid` = `contact`.`uid` + WHERE `contact`.`self` AND `user`.`uid` = %d LIMIT 1", + intval($item['uid']) + ); + + if (!dbm::is_result($r)) { + killme(); + } + + $owner = $r[0]; + + $doc = new DOMDocument('1.0', 'utf-8'); + $doc->formatOutput = true; + + $alternatelink = $owner['url']; + + $type = 'html'; + + $root = self::entry($doc, $type, $item, $owner, true, 0, true); + + $atom = trim($doc->saveXML()); + return $atom; + } + /** * @brief Create XML text for DFRN mails * @@ -370,7 +426,7 @@ class dfrn { $ext = Photo::supportedTypes(); foreach ($rp as $p) { - $photos[$p['scale']] = app::get_baseurl().'/photo/'.$p['resource-id'].'-'.$p['scale'].'.'.$ext[$p['type']]; + $photos[$p['scale']] = System::baseUrl().'/photo/'.$p['resource-id'].'-'.$p['scale'].'.'.$ext[$p['type']]; } unset($rp, $ext); @@ -431,7 +487,7 @@ class dfrn { $root->setAttribute("xmlns:ostatus", NAMESPACE_OSTATUS); $root->setAttribute("xmlns:statusnet", NAMESPACE_STATUSNET); - xml::add_element($doc, $root, "id", app::get_baseurl()."/profile/".$owner["nick"]); + xml::add_element($doc, $root, "id", System::baseUrl()."/profile/".$owner["nick"]); xml::add_element($doc, $root, "title", $owner["name"]); $attributes = array("uri" => "https://friendi.ca", "version" => FRIENDICA_VERSION."-".DB_UPDATE_VERSION); @@ -446,15 +502,15 @@ class dfrn { if ($public) { // DFRN itself doesn't uses this. But maybe someone else wants to subscribe to the public feed. - ostatus::hublinks($doc, $root); + ostatus::hublinks($doc, $root, $owner["nick"]); - $attributes = array("rel" => "salmon", "href" => app::get_baseurl()."/salmon/".$owner["nick"]); + $attributes = array("rel" => "salmon", "href" => System::baseUrl()."/salmon/".$owner["nick"]); xml::add_element($doc, $root, "link", "", $attributes); - $attributes = array("rel" => "http://salmon-protocol.org/ns/salmon-replies", "href" => app::get_baseurl()."/salmon/".$owner["nick"]); + $attributes = array("rel" => "http://salmon-protocol.org/ns/salmon-replies", "href" => System::baseUrl()."/salmon/".$owner["nick"]); xml::add_element($doc, $root, "link", "", $attributes); - $attributes = array("rel" => "http://salmon-protocol.org/ns/salmon-mention", "href" => app::get_baseurl()."/salmon/".$owner["nick"]); + $attributes = array("rel" => "http://salmon-protocol.org/ns/salmon-mention", "href" => System::baseUrl()."/salmon/".$owner["nick"]); xml::add_element($doc, $root, "link", "", $attributes); } @@ -506,18 +562,18 @@ class dfrn { $attributes = array(); - if (!$public OR !$hidewall) { + if (!$public || !$hidewall) { $attributes = array("dfrn:updated" => $namdate); } xml::add_element($doc, $author, "name", $owner["name"], $attributes); - xml::add_element($doc, $author, "uri", app::get_baseurl().'/profile/'.$owner["nickname"], $attributes); + xml::add_element($doc, $author, "uri", System::baseUrl().'/profile/'.$owner["nickname"], $attributes); xml::add_element($doc, $author, "dfrn:handle", $owner["addr"], $attributes); $attributes = array("rel" => "photo", "type" => "image/jpeg", "media:width" => 175, "media:height" => 175, "href" => $owner['photo']); - if (!$public OR !$hidewall) { + if (!$public || !$hidewall) { $attributes["dfrn:updated"] = $picdate; } @@ -764,11 +820,12 @@ class dfrn { * @param array $owner Owner record * @param bool $comment Trigger the sending of the "comment" element * @param int $cid Contact ID of the recipient + * @param bool $single If set, the entry is created as an XML document with a single "entry" element * * @return object XML entry object * @todo Add type-hints */ - private static function entry($doc, $type, $item, $owner, $comment = false, $cid = 0) { + private static function entry($doc, $type, $item, $owner, $comment = false, $cid = 0, $single = false) { $mentioned = array(); @@ -781,7 +838,22 @@ class dfrn { return xml::create_element($doc, "at:deleted-entry", "", $attributes); } - $entry = $doc->createElement("entry"); + if (!$single) { + $entry = $doc->createElement("entry"); + } else { + $entry = $doc->createElementNS(NAMESPACE_ATOM1, 'entry'); + $doc->appendChild($entry); + + $entry->setAttribute("xmlns:thr", NAMESPACE_THREAD); + $entry->setAttribute("xmlns:at", NAMESPACE_TOMB); + $entry->setAttribute("xmlns:media", NAMESPACE_MEDIA); + $entry->setAttribute("xmlns:dfrn", NAMESPACE_DFRN); + $entry->setAttribute("xmlns:activity", NAMESPACE_ACTIVITY); + $entry->setAttribute("xmlns:georss", NAMESPACE_GEORSS); + $entry->setAttribute("xmlns:poco", NAMESPACE_POCO); + $entry->setAttribute("xmlns:ostatus", NAMESPACE_OSTATUS); + $entry->setAttribute("xmlns:statusnet", NAMESPACE_STATUSNET); + } if ($item['allow_cid'] || $item['allow_gid'] || $item['deny_cid'] || $item['deny_gid']) { $body = fix_private_photos($item['body'],$owner['uid'],$item,$cid); @@ -809,14 +881,36 @@ class dfrn { $entry->appendChild($dfrnowner); if (($item['parent'] != $item['id']) || ($item['parent-uri'] !== $item['uri']) || (($item['thr-parent'] !== '') && ($item['thr-parent'] !== $item['uri']))) { - $parent = q("SELECT `guid` FROM `item` WHERE `id` = %d", intval($item["parent"])); $parent_item = (($item['thr-parent']) ? $item['thr-parent'] : $item['parent-uri']); + $parent = q("SELECT `guid`,`plink` FROM `item` WHERE `uri` = '%s' AND `uid` = %d", dbesc($parent_item), intval($item['uid'])); $attributes = array("ref" => $parent_item, "type" => "text/html", - "href" => app::get_baseurl().'/display/'.$parent[0]['guid'], + "href" => $parent[0]['plink'], "dfrn:diaspora_guid" => $parent[0]['guid']); xml::add_element($doc, $entry, "thr:in-reply-to", "", $attributes); } + // Add conversation data. This is used for OStatus + $conversation_href = System::baseUrl()."/display/".$owner["nick"]."/".$item["parent"]; + $conversation_uri = $conversation_href; + + if (isset($parent_item)) { + $r = dba::fetch_first("SELECT `conversation-uri`, `conversation-href` FROM `conversation` WHERE `item-uri` = ?", $item['parent-uri']); + if (dbm::is_result($r)) { + if ($r['conversation-uri'] != '') { + $conversation_uri = $r['conversation-uri']; + } + if ($r['conversation-href'] != '') { + $conversation_href = $r['conversation-href']; + } + } + } + + $attributes = array( + "href" => $conversation_href, + "ref" => $conversation_uri); + + xml::add_element($doc, $entry, "ostatus:conversation", $conversation_uri, $attributes); + xml::add_element($doc, $entry, "id", $item["uri"]); xml::add_element($doc, $entry, "title", $item["title"]); @@ -832,7 +926,7 @@ class dfrn { // We save this value in "plink". Maybe we should read it from there as well? xml::add_element($doc, $entry, "link", "", array("rel" => "alternate", "type" => "text/html", - "href" => app::get_baseurl()."/display/".$item["guid"])); + "href" => System::baseUrl()."/display/".$item["guid"])); // "comment-allow" is some old fashioned stuff for old Friendica versions. // It is included in the rewritten code for completeness @@ -897,7 +991,7 @@ class dfrn { if (count($tags)) { foreach ($tags as $t) { - if (($type != 'html') OR ($t[0] != "@")) { + if (($type != 'html') || ($t[0] != "@")) { xml::add_element($doc, $entry, "category", "", array("scheme" => "X-DFRN:".$t[0].":".$t[1], "term" => $t[2])); } } @@ -916,7 +1010,7 @@ class dfrn { intval($owner["uid"]), dbesc(normalise_link($mention))); - if (dbm::is_result($r) AND ($r[0]["forum"] OR $r[0]["prv"])) { + if (dbm::is_result($r) && ($r[0]["forum"] || $r[0]["prv"])) { xml::add_element($doc, $entry, "link", "", array("rel" => "mentioned", "ostatus:object-type" => ACTIVITY_OBJ_GROUP, "href" => $mention)); @@ -1201,7 +1295,15 @@ class dfrn { $res = parse_xml_string($xml); - return $res->status; + if (!isset($res->status)) { + return -11; + } + + if (!empty($res->message)) { + logger('Delivery returned status '.$res->status.' - '.$res->message, LOGGER_DEBUG); + } + + return intval($res->status); } /** @@ -1299,7 +1401,7 @@ class dfrn { $contact["avatar-date"] = $attributes->textContent; } } - if (($width > 0) AND ($href != "")) { + if (($width > 0) && ($href != "")) { $avatarlist[$width] = $href; } } @@ -1308,7 +1410,7 @@ class dfrn { $author["avatar"] = current($avatarlist); } - if (dbm::is_result($r) AND !$onlyfetch) { + if (dbm::is_result($r) && !$onlyfetch) { logger("Check if contact details for contact " . $r[0]["id"] . " (" . $r[0]["nick"] . ") have to be updated.", LOGGER_DEBUG); $poco = array("url" => $contact["url"]); @@ -1567,9 +1669,7 @@ class dfrn { $msg["seen"] = 0; $msg["replied"] = 0; - dbm::esc_array($msg, true); - - $r = dbq("INSERT INTO `mail` (`".implode("`, `", array_keys($msg))."`) VALUES (".implode(", ", array_values($msg)).")"); + dba::insert('mail', $msg); // send notifications. /// @TODO Arange this mess @@ -1709,7 +1809,7 @@ class dfrn { "to_email" => $importer["email"], "uid" => $importer["importer_uid"], "item" => $suggest, - "link" => App::get_baseurl()."/notifications/intros", + "link" => System::baseUrl()."/notifications/intros", "source_name" => $importer["name"], "source_link" => $importer["url"], "source_photo" => $importer["photo"], @@ -1750,7 +1850,7 @@ class dfrn { $relocate["poll"] = $xpath->query("dfrn:poll/text()", $relocation)->item(0)->nodeValue; $relocate["sitepubkey"] = $xpath->query("dfrn:sitepubkey/text()", $relocation)->item(0)->nodeValue; - if (($relocate["avatar"] == "") AND ($relocate["photo"] != "")) { + if (($relocate["avatar"] == "") && ($relocate["photo"] != "")) { $relocate["avatar"] = $relocate["photo"]; } @@ -1878,7 +1978,7 @@ class dfrn { return false; } - $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `tag` = '%s', `edited` = '%s', `changed` = '%s' WHERE `uri` = '%s' AND `uid` = %d", + $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `tag` = '%s', `edited` = '%s', `changed` = '%s' WHERE `uri` = '%s' AND `uid` IN (0, %d)", dbesc($item["title"]), dbesc($item["body"]), dbesc($item["tag"]), @@ -1898,13 +1998,13 @@ class dfrn { } // update last-child if it changes - if ($item["last-child"] AND ($item["last-child"] != $current["last-child"])) { - $r = q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d", + if ($item["last-child"] && ($item["last-child"] != $current["last-child"])) { + $r = q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` IN (0, %d)", dbesc(datetime_convert()), dbesc($item["parent-uri"]), intval($importer["importer_uid"]) ); - $r = q("UPDATE `item` SET `last-child` = %d , `changed` = '%s' WHERE `uri` = '%s' AND `uid` = %d", + $r = q("UPDATE `item` SET `last-child` = %d , `changed` = '%s' WHERE `uri` = '%s' AND `uid` IN (0, %d)", intval($item["last-child"]), dbesc(datetime_convert()), dbesc($item["uri"]), @@ -2015,7 +2115,7 @@ class dfrn { } } - if ($Blink && link_compare($Blink, App::get_baseurl() . "/profile/" . $importer["nickname"])) { + if ($Blink && link_compare($Blink, System::baseUrl() . "/profile/" . $importer["nickname"])) { // send a notification notification(array( @@ -2026,7 +2126,7 @@ class dfrn { "to_email" => $importer["email"], "uid" => $importer["importer_uid"], "item" => $item, - "link" => App::get_baseurl()."/display/".urlencode(get_item_guid($posted_id)), + "link" => System::baseUrl()."/display/".urlencode(get_item_guid($posted_id)), "source_name" => stripslashes($item["author-name"]), "source_link" => $item["author-link"], "source_photo" => ((link_compare($item["author-link"],$importer["url"])) @@ -2182,7 +2282,7 @@ class dfrn { $title = $attributes->textContent; } } - if (($rel != "") AND ($href != "")) { + if (($rel != "") && ($href != "")) { switch ($rel) { case "alternate": $item["plink"] = $href; @@ -2209,12 +2309,16 @@ class dfrn { * @param array $importer Record of the importer user mixed with contact of the content * @todo Add type-hints */ - private static function process_entry($header, $xpath, $entry, $importer) { + private static function process_entry($header, $xpath, $entry, $importer, $xml) { logger("Processing entries"); $item = $header; + $item["protocol"] = PROTOCOL_DFRN; + + $item["source"] = $xml; + // Get the uri $item["uri"] = $xpath->query("atom:id/text()", $entry)->item(0)->nodeValue; @@ -2226,7 +2330,7 @@ class dfrn { ); // Is there an existing item? - if (dbm::is_result($current) AND edited_timestamp_is_newer($current[0], $item) AND + if (dbm::is_result($current) && edited_timestamp_is_newer($current[0], $item) && (datetime_convert("UTC","UTC",$item["edited"]) < $current[0]["edited"])) { logger("Item ".$item["uri"]." already existed.", LOGGER_DEBUG); return; @@ -2300,7 +2404,7 @@ class dfrn { } $notice_info = $xpath->query("statusnet:notice_info", $entry); - if ($notice_info AND ($notice_info->length > 0)) { + if ($notice_info && ($notice_info->length > 0)) { foreach ($notice_info->item(0)->attributes AS $attributes) { if ($attributes->name == "source") { $item["app"] = strip_tags($attributes->textContent); @@ -2350,9 +2454,9 @@ class dfrn { } } - if (($term != "") AND ($scheme != "")) { + if (($term != "") && ($scheme != "")) { $parts = explode(":", $scheme); - if ((count($parts) >= 4) AND (array_shift($parts) == "X-DFRN")) { + if ((count($parts) >= 4) && (array_shift($parts) == "X-DFRN")) { $termhash = array_shift($parts); $termurl = implode(":", $parts); @@ -2373,6 +2477,20 @@ class dfrn { self::parse_links($links, $item); } + $item['conversation-uri'] = $xpath->query('ostatus:conversation/text()', $entry)->item(0)->nodeValue; + + $conv = $xpath->query('ostatus:conversation', $entry); + if (is_object($conv->item(0))) { + foreach ($conv->item(0)->attributes AS $attributes) { + if ($attributes->name == "ref") { + $item['conversation-uri'] = $attributes->textContent; + } + if ($attributes->name == "href") { + $item['conversation-href'] = $attributes->textContent; + } + } + } + // Is it a reply or a top level posting? $item["parent-uri"] = $item["uri"]; @@ -2398,7 +2516,7 @@ class dfrn { $item["contact-id"] = $owner["contact-id"]; } - if (($item["network"] != $owner["network"]) AND ($owner["network"] != "")) { + if (($item["network"] != $owner["network"]) && ($owner["network"] != "")) { $item["network"] = $owner["network"]; } @@ -2406,7 +2524,7 @@ class dfrn { $item["contact-id"] = $author["contact-id"]; } - if (($item["network"] != $author["network"]) AND ($author["network"] != "")) { + if (($item["network"] != $author["network"]) && ($author["network"] != "")) { $item["network"] = $author["network"]; } @@ -2506,7 +2624,7 @@ class dfrn { ); } - if ($posted_id AND $parent AND ($entrytype == DFRN_REPLY_RC)) { + if ($posted_id && $parent && ($entrytype == DFRN_REPLY_RC)) { logger("Notifying followers about comment ".$posted_id, LOGGER_DEBUG); proc_run(PRIORITY_HIGH, "include/notifier.php", "comment-import", $posted_id); } @@ -2571,7 +2689,7 @@ class dfrn { $when = datetime_convert("UTC", "UTC", "now", "Y-m-d H:i:s"); } - if (!$uri OR !$importer["id"]) { + if (!$uri || !$importer["id"]) { return false; } @@ -2646,7 +2764,7 @@ class dfrn { if ($entrytype == DFRN_TOP_LEVEL) { $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s', `body` = '', `title` = '' - WHERE `parent-uri` = '%s' AND `uid` = %d", + WHERE `parent-uri` = '%s' AND `uid` IN (0, %d)", dbesc($when), dbesc(datetime_convert()), dbesc($uri), @@ -2658,7 +2776,7 @@ class dfrn { } else { $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s', `body` = '', `title` = '' - WHERE `uri` = '%s' AND `uid` = %d", + WHERE `uri` = '%s' AND `uid` IN (0, %d)", dbesc($when), dbesc(datetime_convert()), dbesc($uri), @@ -2669,7 +2787,7 @@ class dfrn { update_thread_uri($uri, $importer["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 ", + q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` IN (0, %d)", dbesc(datetime_convert()), dbesc($item["parent-uri"]), intval($item["uid"]) @@ -2702,19 +2820,20 @@ class dfrn { * @param text $xml The DFRN message * @param array $importer Record of the importer user mixed with contact of the content * @param bool $sort_by_date Is used when feeds are polled + * @return integer Import status * @todo set proper type-hints */ - public static function import($xml,$importer, $sort_by_date = false) { + public static function import($xml, $importer, $sort_by_date = false) { if ($xml == "") { - return; + return 400; } if ($importer["readonly"]) { // We aren't receiving stuff from this person. But we will quietly ignore them // rather than a blatant "go away" message. logger('ignoring contact '.$importer["id"]); - return; + return 403; } $doc = new DOMDocument(); @@ -2801,7 +2920,7 @@ class dfrn { if (!$sort_by_date) { $entries = $xpath->query("/atom:feed/atom:entry"); foreach ($entries AS $entry) { - self::process_entry($header, $xpath, $entry, $importer); + self::process_entry($header, $xpath, $entry, $importer, $xml); } } else { $newentries = array(); @@ -2815,9 +2934,10 @@ class dfrn { ksort($newentries); foreach ($newentries AS $entry) { - self::process_entry($header, $xpath, $entry, $importer); + self::process_entry($header, $xpath, $entry, $importer, $xml); } } logger("Import done for user " . $importer["uid"] . " from contact " . $importer["id"], LOGGER_DEBUG); + return 200; } }