X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=include%2Fdfrn.php;h=a8f7644ff7dbf955b2c1b50237b1d8457bcaebcc;hb=1b5f1effa0ed7d7774f34ebb199fcbf9e6fd9f4b;hp=887dba9a112b7de1f364013634f2c8346f20767f;hpb=d45e79dfe95c150d6846ecbdca271bd5577cefc5;p=friendica.git diff --git a/include/dfrn.php b/include/dfrn.php index 887dba9a11..a8f7644ff7 100644 --- a/include/dfrn.php +++ b/include/dfrn.php @@ -7,6 +7,8 @@ * https://github.com/friendica/friendica/blob/master/spec/dfrn2.pdf */ +use Friendica\App; + require_once("include/Contact.php"); require_once("include/ostatus.php"); require_once("include/enotify.php"); @@ -216,13 +218,10 @@ class dfrn { dbesc($sort) ); - if (!dbm::is_result($r)) { - /// @TODO Some logging? - killme(); - } - - // Will check further below if this actually returned results. - // We will provide an empty feed if that is the case. + /* + * Will check further below if this actually returned results. + * We will provide an empty feed if that is the case. + */ $items = $r; @@ -243,10 +242,10 @@ class dfrn { $root = self::add_header($doc, $owner, $author, $alternatelink, true); - // This hook can't work anymore + /// @TODO This hook can't work anymore // call_hooks('atom_feed', $atom); - if (!count($items) OR $onlyheader) { + if (!dbm::is_result($items) OR $onlyheader) { $atom = trim($doc->saveXML()); call_hooks('atom_feed_end', $atom); @@ -285,6 +284,62 @@ 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 `item`.`wall` 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']; + + $root = self::add_header($doc, $owner, 'dfrn:owner', $alternatelink, true); + + $type = 'html'; + + $entry = self::entry($doc, $type, $item, $owner, true); + $root->appendChild($entry); + + $atom = trim($doc->saveXML()); + return $atom; + } + /** * @brief Create XML text for DFRN mails * @@ -373,7 +428,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']] = App::get_baseurl().'/photo/'.$p['resource-id'].'-'.$p['scale'].'.'.$ext[$p['type']]; } unset($rp, $ext); @@ -434,7 +489,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", App::get_baseurl()."/profile/".$owner["nick"]); xml::add_element($doc, $root, "title", $owner["name"]); $attributes = array("uri" => "https://friendi.ca", "version" => FRIENDICA_VERSION."-".DB_UPDATE_VERSION); @@ -451,13 +506,13 @@ class dfrn { // DFRN itself doesn't uses this. But maybe someone else wants to subscribe to the public feed. ostatus::hublinks($doc, $root); - $attributes = array("rel" => "salmon", "href" => app::get_baseurl()."/salmon/".$owner["nick"]); + $attributes = array("rel" => "salmon", "href" => App::get_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" => App::get_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" => App::get_baseurl()."/salmon/".$owner["nick"]); xml::add_element($doc, $root, "link", "", $attributes); } @@ -514,7 +569,7 @@ class dfrn { } 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", App::get_baseurl().'/profile/'.$owner["nickname"], $attributes); xml::add_element($doc, $author, "dfrn:handle", $owner["addr"], $attributes); $attributes = array("rel" => "photo", "type" => "image/jpeg", @@ -557,8 +612,9 @@ class dfrn { xml::add_element($doc, $author, "poco:displayName", $profile["name"]); xml::add_element($doc, $author, "poco:updated", $namdate); - if (trim($profile["dob"]) != "0000-00-00") + if (trim($profile["dob"]) > '0001-01-01') { xml::add_element($doc, $author, "poco:birthday", "0000-".date("m-d", strtotime($profile["dob"]))); + } xml::add_element($doc, $author, "poco:note", $profile["about"]); xml::add_element($doc, $author, "poco:preferredUsername", $profile["nickname"]); @@ -814,11 +870,33 @@ class dfrn { $parent = q("SELECT `guid` FROM `item` WHERE `id` = %d", intval($item["parent"])); $parent_item = (($item['thr-parent']) ? $item['thr-parent'] : $item['parent-uri']); $attributes = array("ref" => $parent_item, "type" => "text/html", - "href" => app::get_baseurl().'/display/'.$parent[0]['guid'], + "href" => App::get_baseurl().'/display/'.$parent[0]['guid'], "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 = App::get_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"]); @@ -834,7 +912,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" => App::get_baseurl()."/display/".$item["guid"])); // "comment-allow" is some old fashioned stuff for old Friendica versions. // It is included in the rewritten code for completeness @@ -870,7 +948,7 @@ class dfrn { // The signed text contains the content in Markdown, the sender handle and the signatur for the content // It is needed for relayed comments to Diaspora. - if($item['signed_text']) { + if ($item['signed_text']) { $sign = base64_encode(json_encode(array('signed_text' => $item['signed_text'],'signature' => $item['signature'],'signer' => $item['signer']))); xml::add_element($doc, $entry, "dfrn:diaspora_signature", $sign); } @@ -918,12 +996,7 @@ class dfrn { intval($owner["uid"]), dbesc(normalise_link($mention))); - if (!dbm::is_result($r)) { - /// @TODO Maybe some logging? - killme(); - } - - if ($r[0]["forum"] OR $r[0]["prv"]) { + if (dbm::is_result($r) AND ($r[0]["forum"] OR $r[0]["prv"])) { xml::add_element($doc, $entry, "link", "", array("rel" => "mentioned", "ostatus:object-type" => ACTIVITY_OBJ_GROUP, "href" => $mention)); @@ -939,6 +1012,30 @@ class dfrn { return $entry; } + /** + * @brief encrypts data via AES + * + * @param string $data The data that is to be encrypted + * @param string $key The AES key + * + * @return string encrypted data + */ + private static function aes_encrypt($data, $key) { + return openssl_encrypt($data, 'aes-128-ecb', $key, OPENSSL_RAW_DATA); + } + + /** + * @brief decrypts data via AES + * + * @param string $encrypted The encrypted data + * @param string $key The AES key + * + * @return string decrypted data + */ + public static function aes_decrypt($encrypted, $key) { + return openssl_decrypt($encrypted, 'aes-128-ecb', $key, OPENSSL_RAW_DATA); + } + /** * @brief Delivers the atom content to the contacts * @@ -963,14 +1060,9 @@ class dfrn { $idtosend = '1:' . $orig_id; } - $rino = get_config('system','rino_encrypt'); + $rino = get_config('system', 'rino_encrypt'); $rino = intval($rino); - // use RINO1 if mcrypt isn't installed and RINO2 was selected - if ($rino == 2 and !function_exists('mcrypt_create_iv')) { - $rino = 1; - } - logger("Local rino version: ". $rino, LOGGER_DEBUG); $ssl_val = intval(get_config('system','ssl_policy')); @@ -1101,8 +1193,8 @@ class dfrn { switch ($rino_remote_version) { case 1: // Deprecated rino version! - $key = substr(random_string(), 0, 16); - $data = aes_encrypt($postvars['data'],$key); + $key = openssl_random_pseudo_bytes(16); + $data = self::aes_encrypt($postvars['data'], $key); break; case 2: // RINO 2 based on php-encryption @@ -1137,10 +1229,9 @@ class dfrn { if ($dfrn_version >= 2.1) { - if (($contact['duplex'] && strlen($contact['pubkey'])) { - || ($owner['page-flags'] == PAGE_COMMUNITY && strlen($contact['pubkey'])) - || ($contact['rel'] == CONTACT_IS_SHARING && strlen($contact['pubkey']))) - + if (($contact['duplex'] && strlen($contact['pubkey'])) + || ($owner['page-flags'] == PAGE_COMMUNITY && strlen($contact['pubkey'])) + || ($contact['rel'] == CONTACT_IS_SHARING && strlen($contact['pubkey']))) { openssl_public_encrypt($key,$postvars['key'],$contact['pubkey']); } else { openssl_private_encrypt($key,$postvars['key'],$contact['prvkey']); @@ -1277,7 +1368,7 @@ class dfrn { $href = ""; $width = 0; foreach ($avatar->attributes AS $attributes) { - /// @TODO Rewrite these similar if() to one switch + /// @TODO Rewrite these similar if () to one switch if ($attributes->name == "href") { $href = $attributes->textContent; } @@ -1392,7 +1483,7 @@ class dfrn { // "poco:birthday" is the birthday in the format "yyyy-mm-dd" $value = $xpath->evaluate($element . "/poco:birthday/text()", $context)->item(0)->nodeValue; - if (!in_array($value, array("", "0000-00-00"))) { + if (!in_array($value, array("", "0000-00-00", "0001-01-01"))) { $bdyear = date("Y"); $value = str_replace("0000", $bdyear, $value); @@ -1449,7 +1540,7 @@ class dfrn { dbesc($contact["name"]), dbesc($contact["nick"]), dbesc($contact["about"]), dbesc($contact["location"]), dbesc($contact["addr"]), dbesc($contact["keywords"]), dbesc($contact["bdyear"]), dbesc($contact["bd"]), intval($contact["hidden"]), dbesc($contact["xmpp"]), - dbesc($contact["name-date"]), dbesc($contact["uri-date"]), + dbesc(dbm::date($contact["name-date"])), dbesc(dbm::date($contact["uri-date"])), intval($contact["id"]), dbesc($contact["network"])); } @@ -1467,7 +1558,9 @@ class dfrn { $poco["photo"] = $author["avatar"]; $poco["hide"] = $hide; $poco["contact-type"] = $contact["contact-type"]; - update_gcontact($poco); + $gcid = update_gcontact($poco); + + link_gcontact($gcid, $importer["uid"], $contact["id"]); } return($author); @@ -1751,11 +1844,7 @@ class dfrn { intval($importer["importer_uid"])); if (!dbm::is_result($r)) { - /* - * @TODO maybe one day: logger("Query failed to execute, no result returned in " . __FUNCTION__); - killme(); - */ return false; } @@ -1813,7 +1902,6 @@ class dfrn { update_contact_avatar($relocate["avatar"], $importer["importer_uid"], $importer["id"], true); - /// @TODO decide to use dbm::is_result() here if ($x === false) { return false; } @@ -1837,7 +1925,6 @@ class dfrn { $n, dbesc($f[0]), intval($importer["importer_uid"])); - /// @TODO decide to use dbm::is_result() here if ($x === false) { return false; } @@ -1868,7 +1955,7 @@ class dfrn { // do not accept (ignore) an earlier edit than one we currently have. if (datetime_convert("UTC","UTC",$item["edited"]) < $current["edited"]) { - return(false); + return false; } $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `tag` = '%s', `edited` = '%s', `changed` = '%s' WHERE `uri` = '%s' AND `uid` = %d", @@ -1963,11 +2050,9 @@ class dfrn { */ /// @TODO Maybe merge these if() blocks into one? - if ($is_a_remote_action && $community) { - if ((!$r[0]["forum_mode"]) && (!$r[0]["wall"])) { - $is_a_remote_action = false; - logger("not a community action"); - } + if ($is_a_remote_action && $community && (!$r[0]["forum_mode"]) && (!$r[0]["wall"])) { + $is_a_remote_action = false; + logger("not a community action"); } if ($is_a_remote_action) { @@ -2125,11 +2210,7 @@ class dfrn { ); if (!dbm::is_result($r)) { - /* - * @TODO maybe one day: logger("Query failed to execute, no result returned in " . __FUNCTION__); - killme(); - */ return false; } @@ -2164,7 +2245,7 @@ class dfrn { $title = ""; foreach ($links AS $link) { foreach ($link->attributes AS $attributes) { - /// @TODO Rewrite these repeated (same) if() statements to a switch() + /// @TODO Rewrite these repeated (same) if () statements to a switch() if ($attributes->name == "href") { $href = $attributes->textContent; } @@ -2208,12 +2289,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; @@ -2372,6 +2457,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"]; @@ -2414,7 +2513,7 @@ class dfrn { // When activated, forums don't work. // And: Why should we disallow commenting by followers? // the behaviour is now similar to the Diaspora part. - //if($importer["rel"] == CONTACT_IS_FOLLOWER) { + //if ($importer["rel"] == CONTACT_IS_FOLLOWER) { // logger("Contact ".$importer["id"]." is only follower. Quitting", LOGGER_DEBUG); // return; //} @@ -2539,7 +2638,7 @@ class dfrn { logger("Item was stored with id ".$posted_id, LOGGER_DEBUG); - if(stristr($item["verb"],ACTIVITY_POKE)) + if (stristr($item["verb"],ACTIVITY_POKE)) self::do_poke($item, $importer, $posted_id); } } @@ -2666,7 +2765,7 @@ class dfrn { create_tags_from_itemuri($uri, $importer["uid"]); create_files_from_itemuri($uri, $importer["uid"]); update_thread_uri($uri, $importer["importer_uid"]); - if($item["last-child"]) { + 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()), @@ -2800,7 +2899,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(); @@ -2814,7 +2913,7 @@ 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);