X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FProtocol%2FDiaspora.php;h=00da7d3915d2dd68d0f7c6013af0472c5f4b3c8e;hb=e7884b14092b5f5f2c5656d622a625006bffb165;hp=5e4ed9ee17e6daed9293ac7667334eb6f1771eda;hpb=1fb47b96aa80d33b062fad42320065dd405be5e7;p=friendica.git diff --git a/src/Protocol/Diaspora.php b/src/Protocol/Diaspora.php index 5e4ed9ee17..00da7d3915 100644 --- a/src/Protocol/Diaspora.php +++ b/src/Protocol/Diaspora.php @@ -1,6 +1,6 @@ 0, 'network' => Protocol::FEDERATED, 'batch' => $contact['batch'], 'contact-type' => Contact::TYPE_RELAY]; - $relay_contact = DBA::selectFirst('contact', [], $condition); - } else { - return; - } - } else { - $relay_contact = self::getRelayContact($contact['baseurl'], []); - } - - if (!empty($relay_contact)) { - Logger::info('Relay contact will be marked for archival', ['id' => $relay_contact['id'], 'url' => $relay_contact['url']]); - Contact::markForArchival($relay_contact); - } - } - - /** - * Return a list of relay servers - * - * The list contains not only the official relays but also servers that we serve directly - * - * @param integer $item_id The id of the item that is sent - * @param array $contacts The previously fetched contacts - * - * @return array of relay servers - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ - public static function relayList($item_id, array $contacts = []) - { - $serverlist = []; - - // Fetching relay servers - $serverdata = DI::config()->get("system", "relay_server"); - - if (!empty($serverdata)) { - $servers = explode(",", $serverdata); - foreach ($servers as $server) { - $serverlist[$server] = trim($server); - } - } - - if (DI::config()->get("system", "relay_directly", false)) { - // We distribute our stuff based on the parent to ensure that the thread will be complete - $parent = Item::selectFirst(['uri-id'], ['id' => $item_id]); - if (!DBA::isResult($parent)) { - return; - } - - // Servers that want to get all content - $servers = DBA::select('gserver', ['url'], ['relay-subscribe' => true, 'relay-scope' => 'all']); - while ($server = DBA::fetch($servers)) { - $serverlist[$server['url']] = $server['url']; - } - DBA::close($servers); - - // All tags of the current post - $tags = DBA::select('tag-view', ['name'], ['uri-id' => $parent['uri-id'], 'type' => Tag::HASHTAG]); - $taglist = []; - while ($tag = DBA::fetch($tags)) { - $taglist[] = $tag['name']; - } - DBA::close($tags); - - // All servers who wants content with this tag - $tagserverlist = []; - if (!empty($taglist)) { - $tagserver = DBA::select('gserver-tag', ['gserver-id'], ['tag' => $taglist]); - while ($server = DBA::fetch($tagserver)) { - $tagserverlist[] = $server['gserver-id']; - } - DBA::close($tagserver); - } - - // All adresses with the given id - if (!empty($tagserverlist)) { - $servers = DBA::select('gserver', ['url'], ['relay-subscribe' => true, 'relay-scope' => 'tags', 'id' => $tagserverlist]); - while ($server = DBA::fetch($servers)) { - $serverlist[$server['url']] = $server['url']; - } - DBA::close($servers); - } - } - - // Now we are collecting all relay contacts - foreach ($serverlist as $server_url) { - // We don't send messages to ourselves - if (Strings::compareLink($server_url, DI::baseUrl())) { - continue; - } - $contact = self::getRelayContact($server_url); - if (is_bool($contact)) { - continue; - } - - $exists = false; - foreach ($contacts as $entry) { - if ($entry['batch'] == $contact['batch']) { - $exists = true; - } - } - - if (!$exists) { - $contacts[] = $contact; - } - } - - return $contacts; - } - - /** - * Return a contact for a given server address or creates a dummy entry - * - * @param string $server_url The url of the server - * @param array $fields Fieldlist - * @return array with the contact - * @throws \Exception - */ - private static function getRelayContact(string $server_url, array $fields = ['batch', 'id', 'url', 'name', 'network', 'protocol', 'archive', 'blocked']) - { - // Fetch the relay contact - $condition = ['uid' => 0, 'nurl' => Strings::normaliseLink($server_url), - 'contact-type' => Contact::TYPE_RELAY]; - $contact = DBA::selectFirst('contact', $fields, $condition); - - if (DBA::isResult($contact)) { - if ($contact['archive'] || $contact['blocked']) { - return false; - } - return $contact; - } else { - self::setRelayContact($server_url); - - $contact = DBA::selectFirst('contact', $fields, $condition); - if (DBA::isResult($contact)) { - return $contact; - } - } - - // It should never happen that we arrive here - return []; - } - - /** - * Update or insert a relay contact - * - * @param string $server_url The url of the server - * @param array $network_fields Optional network specific fields - * @throws \Exception - */ - public static function setRelayContact($server_url, array $network_fields = []) - { - $fields = ['created' => DateTimeFormat::utcNow(), - 'name' => 'relay', 'nick' => 'relay', 'url' => $server_url, - 'nurl' => Strings::normaliseLink($server_url), - 'network' => Protocol::DIASPORA, 'uid' => 0, - 'batch' => $server_url . '/receive/public', - 'rel' => Contact::FOLLOWER, 'blocked' => false, - 'pending' => false, 'writable' => true, - 'baseurl' => $server_url, 'contact-type' => Contact::TYPE_RELAY]; - - $fields = array_merge($fields, $network_fields); - - $condition = ['uid' => 0, 'nurl' => Strings::normaliseLink($server_url)]; - $old = DBA::selectFirst('contact', [], $condition); - if (DBA::isResult($old)) { - unset($fields['created']); - $condition = ['id' => $old['id']]; - - Logger::info('Update relay contact', ['fields' => $fields, 'condition' => $condition]); - DBA::update('contact', $fields, $condition, $old); - } else { - Logger::info('Create relay contact', ['fields' => $fields]); - Contact::insert($fields); - } - } - /** * Return a list of participating contacts for a thread * @@ -265,9 +76,9 @@ class Diaspora return $contacts; } - $items = Item::select(['author-id', 'author-link', 'parent-author-link', 'parent-guid', 'guid'], + $items = Post::select(['author-id', 'author-link', 'parent-author-link', 'parent-guid', 'guid'], ['parent' => $item['parent'], 'gravity' => [GRAVITY_COMMENT, GRAVITY_ACTIVITY]]); - while ($item = DBA::fetch($items)) { + while ($item = Post::fetch($items)) { $contact = DBA::selectFirst('contact', ['id', 'url', 'name', 'protocol', 'batch', 'network'], ['id' => $item['author-id']]); if (!DBA::isResult($contact) || empty($contact['batch']) || @@ -638,13 +449,14 @@ class Diaspora /** * Dispatches public messages and find the fitting receivers * - * @param array $msg The post that will be dispatched + * @param array $msg The post that will be dispatched + * @param bool $fetched The message had been fetched (default "false") * * @return int The message id of the generated message, "true" or "false" if there was an error * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ - public static function dispatchPublic($msg) + public static function dispatchPublic($msg, bool $fetched = false) { $enabled = intval(DI::config()->get("system", "diaspora_enabled")); if (!$enabled) { @@ -658,7 +470,7 @@ class Diaspora } $importer = ["uid" => 0, "page-flags" => User::PAGE_FLAGS_FREELOVE]; - $success = self::dispatch($importer, $msg, $fields); + $success = self::dispatch($importer, $msg, $fields, $fetched); return $success; } @@ -669,12 +481,13 @@ class Diaspora * @param array $importer Array of the importer user * @param array $msg The post that will be dispatched * @param SimpleXMLElement $fields SimpleXML object that contains the message + * @param bool $fetched The message had been fetched (default "false") * * @return int The message id of the generated message, "true" or "false" if there was an error * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ - public static function dispatch(array $importer, $msg, SimpleXMLElement $fields = null) + public static function dispatch(array $importer, $msg, SimpleXMLElement $fields = null, bool $fetched = false) { // The sender is the handle of the contact that sent the message. // This will often be different with relayed messages (for example "like" and "comment") @@ -707,7 +520,7 @@ class Diaspora return self::receiveAccountDeletion($fields); case "comment": - return self::receiveComment($importer, $sender, $fields, $msg["message"]); + return self::receiveComment($importer, $sender, $fields, $msg["message"], $fetched); case "contact": if (!$private) { @@ -724,7 +537,7 @@ class Diaspora return self::receiveConversation($importer, $msg, $fields); case "like": - return self::receiveLike($importer, $sender, $fields); + return self::receiveLike($importer, $sender, $fields, $fetched); case "message": if (!$private) { @@ -738,7 +551,7 @@ class Diaspora Logger::log('Message with type ' . $type . ' is not private, quitting.'); return false; } - return self::receiveParticipation($importer, $fields); + return self::receiveParticipation($importer, $fields, $fetched); case "photo": // Not implemented return self::receivePhoto($importer, $fields); @@ -754,13 +567,13 @@ class Diaspora return self::receiveProfile($importer, $fields); case "reshare": - return self::receiveReshare($importer, $fields, $msg["message"]); + return self::receiveReshare($importer, $fields, $msg["message"], $fetched); case "retraction": return self::receiveRetraction($importer, $sender, $fields); case "status_message": - return self::receiveStatusMessage($importer, $fields, $msg["message"]); + return self::receiveStatusMessage($importer, $fields, $msg["message"], $fetched); default: Logger::log("Unknown message type ".$type); @@ -936,7 +749,7 @@ class Diaspora Logger::log("Fetching diaspora key for: ".$handle); - $r = self::personByHandle($handle); + $r = FContact::getByURL($handle); if ($r) { return $r["pubkey"]; } @@ -944,81 +757,6 @@ class Diaspora return ""; } - /** - * Fetches data for a given handle - * - * @param string $handle The handle - * @param boolean $update true = always update, false = never update, null = update when not found or outdated - * - * @return array the queried data - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException - */ - public static function personByHandle($handle, $update = null) - { - $person = DBA::selectFirst('fcontact', [], ['network' => Protocol::DIASPORA, 'addr' => $handle]); - if (!DBA::isResult($person)) { - $urls = [$handle, str_replace('http://', 'https://', $handle), Strings::normaliseLink($handle)]; - $person = DBA::selectFirst('fcontact', [], ['network' => Protocol::DIASPORA, 'url' => $urls]); - } - - if (DBA::isResult($person)) { - Logger::debug('In cache', ['person' => $person]); - - if (is_null($update)) { - // update record occasionally so it doesn't get stale - $d = strtotime($person["updated"]." +00:00"); - if ($d < strtotime("now - 14 days")) { - $update = true; - } - - if ($person["guid"] == "") { - $update = true; - } - } - } elseif (is_null($update)) { - $update = !DBA::isResult($person); - } else { - $person = []; - } - - if ($update) { - Logger::log("create or refresh", Logger::DEBUG); - $r = Probe::uri($handle, Protocol::DIASPORA); - - // Note that Friendica contacts will return a "Diaspora person" - // if Diaspora connectivity is enabled on their server - if ($r && ($r["network"] === Protocol::DIASPORA)) { - self::updateFContact($r); - - $person = self::personByHandle($handle, false); - } - } - - return $person; - } - - /** - * Updates the fcontact table - * - * @param array $arr The fcontact data - * @throws \Exception - */ - private static function updateFContact($arr) - { - $fields = ['name' => $arr["name"], 'photo' => $arr["photo"], - 'request' => $arr["request"], 'nick' => $arr["nick"], - 'addr' => strtolower($arr["addr"]), 'guid' => $arr["guid"], - 'batch' => $arr["batch"], 'notify' => $arr["notify"], - 'poll' => $arr["poll"], 'confirm' => $arr["confirm"], - 'alias' => $arr["alias"], 'pubkey' => $arr["pubkey"], - 'updated' => DateTimeFormat::utcNow()]; - - $condition = ['url' => $arr["url"], 'network' => $arr["network"]]; - - DBA::update('fcontact', $fields, $condition, true); - } - /** * get a handle (user@domain.tld) from a given contact id * @@ -1066,32 +804,6 @@ class Diaspora return strtolower($handle); } - /** - * get a url (scheme://domain.tld/u/user) from a given Diaspora* - * fcontact guid - * - * @param mixed $fcontact_guid Hexadecimal string guid - * - * @return string the contact url or null - * @throws \Exception - */ - public static function urlFromContactGuid($fcontact_guid) - { - Logger::info('fcontact', ['guid' => $fcontact_guid]); - - $r = q( - "SELECT `url` FROM `fcontact` WHERE `url` != '' AND `network` = '%s' AND `guid` = '%s'", - DBA::escape(Protocol::DIASPORA), - DBA::escape($fcontact_guid) - ); - - if (DBA::isResult($r)) { - return $r[0]['url']; - } - - return null; - } - /** * Get a contact id for a given handle * @@ -1120,7 +832,7 @@ class Diaspora */ public static function isSupportedByContactUrl($url, $update = null) { - return !empty(self::personByHandle($url, $update)); + return !empty(FContact::getByURL($url, $update)); } /** @@ -1219,7 +931,7 @@ class Diaspora */ private static function messageExists($uid, $guid) { - $item = Item::selectFirst(['id'], ['uid' => $uid, 'guid' => $guid]); + $item = Post::selectFirst(['id'], ['uid' => $uid, 'guid' => $guid]); if (DBA::isResult($item)) { Logger::log("message ".$guid." already exists for user ".$uid); return $item["id"]; @@ -1272,7 +984,7 @@ class Diaspora // 0 => '[url=/people/0123456789abcdef]Foo Bar[/url]' // 1 => '0123456789abcdef' // 2 => 'Foo Bar' - $handle = self::urlFromContactGuid($match[1]); + $handle = FContact::getUrlByGuid($match[1]); if ($handle) { $return = '@[url='.$handle.']'.$match[2].'[/url]'; @@ -1338,7 +1050,7 @@ class Diaspora Logger::log("Successfully fetched item ".$guid." from ".$server, Logger::DEBUG); // Now call the dispatcher - return self::dispatchPublic($msg); + return self::dispatchPublic($msg, true); } /** @@ -1354,7 +1066,7 @@ class Diaspora * 'key' => The public key of the author * @throws \Exception */ - private static function message($guid, $server, $level = 0) + public static function message($guid, $server, $level = 0) { if ($level > 5) { return false; @@ -1439,7 +1151,7 @@ class Diaspora $guid = urldecode($matches[2]); - $item = Item::selectFirst(['id'], ['guid' => $guid, 'uid' => $uid]); + $item = Post::selectFirst(['id'], ['guid' => $guid, 'uid' => $uid]); if (DBA::isResult($item)) { Logger::info('Found', ['id' => $item['id']]); return $item['id']; @@ -1449,7 +1161,7 @@ class Diaspora $ret = self::storeByGuid($guid, $matches[1], $uid); Logger::info('Result', ['ret' => $ret]); - $item = Item::selectFirst(['id'], ['guid' => $guid, 'uid' => $uid]); + $item = Post::selectFirst(['id'], ['guid' => $guid, 'uid' => $uid]); if (DBA::isResult($item)) { Logger::info('Found', ['id' => $item['id']]); return $item['id']; @@ -1476,10 +1188,10 @@ class Diaspora 'author-name', 'author-link', 'author-avatar', 'gravity', 'owner-name', 'owner-link', 'owner-avatar']; $condition = ['uid' => $uid, 'guid' => $guid]; - $item = Item::selectFirst($fields, $condition); + $item = Post::selectFirst($fields, $condition); if (!DBA::isResult($item)) { - $person = self::personByHandle($author); + $person = FContact::getByURL($author); $result = self::storeByGuid($guid, $person["url"], $uid); // We don't have an url for items that arrived at the public dispatcher @@ -1490,7 +1202,7 @@ class Diaspora if ($result) { Logger::log("Fetched missing item ".$guid." - result: ".$result, Logger::DEBUG); - $item = Item::selectFirst($fields, $condition); + $item = Post::selectFirst($fields, $condition); } } @@ -1553,7 +1265,7 @@ class Diaspora * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ - private static function plink($addr, $guid, $parent_guid = '') + private static function plink(string $addr, string $guid, string $parent_guid = '') { $contact = Contact::getByURL($addr); if (empty($contact)) { @@ -1698,11 +1410,11 @@ class Diaspora */ private static function getUriFromGuid($author, $guid, $onlyfound = false) { - $item = Item::selectFirst(['uri'], ['guid' => $guid]); + $item = Post::selectFirst(['uri'], ['guid' => $guid]); if (DBA::isResult($item)) { return $item["uri"]; } elseif (!$onlyfound) { - $person = self::personByHandle($author); + $person = FContact::getByURL($author); $parts = parse_url($person['url']); unset($parts['path']); @@ -1714,46 +1426,6 @@ class Diaspora return ""; } - /** - * Fetch the guid from our database with a given uri - * - * @param string $uri Message uri - * @param string $uid Author handle - * - * @return string The post guid - * @throws \Exception - */ - private static function getGuidFromUri($uri, $uid) - { - $item = Item::selectFirst(['guid'], ['uri' => $uri, 'uid' => $uid]); - if (DBA::isResult($item)) { - return $item["guid"]; - } else { - return false; - } - } - - /** - * Find the best importer for a comment, like, ... - * - * @param string $guid The guid of the item - * - * @return array|boolean the origin owner of that post - or false - * @throws \Exception - */ - private static function importerForGuid($guid) - { - $item = Item::selectFirst(['uid'], ['origin' => true, 'guid' => $guid]); - if (DBA::isResult($item)) { - Logger::log("Found user ".$item['uid']." as owner of item ".$guid, Logger::DEBUG); - $contact = DBA::selectFirst('contact', [], ['self' => true, 'uid' => $item['uid']]); - if (DBA::isResult($contact)) { - return $contact; - } - } - return false; - } - /** * Store the mentions in the tag table * @@ -1779,7 +1451,7 @@ class Diaspora continue; } - $person = self::personByHandle($match[3]); + $person = FContact::getByURL($match[3]); if (empty($person)) { continue; } @@ -1795,12 +1467,13 @@ class Diaspora * @param string $sender The sender of the message * @param object $data The message object * @param string $xml The original XML of the message + * @param bool $fetched The message had been fetched and not pushed * * @return int The message id of the generated comment or "false" if there was an error * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ - private static function receiveComment(array $importer, $sender, $data, $xml) + private static function receiveComment(array $importer, $sender, $data, $xml, bool $fetched) { $author = Strings::escapeTags(XML::unescape($data->author)); $guid = Strings::escapeTags(XML::unescape($data->guid)); @@ -1815,9 +1488,9 @@ class Diaspora if (isset($data->thread_parent_guid)) { $thread_parent_guid = Strings::escapeTags(XML::unescape($data->thread_parent_guid)); - $thr_uri = self::getUriFromGuid("", $thread_parent_guid, true); + $thr_parent = self::getUriFromGuid("", $thread_parent_guid, true); } else { - $thr_uri = ""; + $thr_parent = ""; } $contact = self::allowedContactByHandle($importer, $sender, true); @@ -1825,17 +1498,21 @@ class Diaspora return false; } + if (!empty($contact['gsid'])) { + GServer::setProtocol($contact['gsid'], Post\DeliveryData::DIASPORA); + } + $message_id = self::messageExists($importer["uid"], $guid); if ($message_id) { return true; } - $parent_item = self::parentItem($importer["uid"], $parent_guid, $author, $contact); - if (!$parent_item) { + $toplevel_parent_item = self::parentItem($importer["uid"], $parent_guid, $author, $contact); + if (!$toplevel_parent_item) { return false; } - $person = self::personByHandle($author); + $person = FContact::getByURL($author); if (!is_array($person)) { Logger::log("unable to find author details"); return false; @@ -1856,6 +1533,15 @@ class Diaspora $datarray["owner-link"] = $contact["url"]; $datarray["owner-id"] = Contact::getIdForURL($contact["url"], 0); + // Will be overwritten for sharing accounts in Item::insert + if ($fetched) { + $datarray["post-reason"] = Item::PR_FETCHED; + } elseif ($datarray["uid"] == 0) { + $datarray["post-reason"] = Item::PR_GLOBAL; + } else { + $datarray["post-reason"] = Item::PR_COMMENT; + } + $datarray["guid"] = $guid; $datarray["uri"] = self::getUriFromGuid($author, $guid); $datarray['uri-id'] = ItemURI::insert(['uri' => $datarray['uri'], 'guid' => $datarray['guid']]); @@ -1863,20 +1549,18 @@ class Diaspora $datarray["verb"] = Activity::POST; $datarray["gravity"] = GRAVITY_COMMENT; - if ($thr_uri != "") { - $datarray["parent-uri"] = $thr_uri; - } else { - $datarray["parent-uri"] = $parent_item["uri"]; - } + $datarray['thr-parent'] = $thr_parent ?: $toplevel_parent_item['uri']; $datarray["object-type"] = Activity\ObjectType::COMMENT; + $datarray["post-type"] = Item::PT_NOTE; $datarray["protocol"] = Conversation::PARCEL_DIASPORA; $datarray["source"] = $xml; + $datarray["direction"] = $fetched ? Conversation::PULL : Conversation::PUSH; $datarray["changed"] = $datarray["created"] = $datarray["edited"] = $created_at; - $datarray["plink"] = self::plink($author, $guid, $parent_item['guid']); + $datarray["plink"] = self::plink($author, $guid, $toplevel_parent_item['guid']); $body = Markdown::toBBCode($text); $datarray["body"] = self::replacePeopleGuid($body, $person["url"]); @@ -1888,10 +1572,15 @@ class Diaspora // If we are the origin of the parent we store the original data. // We notify our followers during the item storage. - if ($parent_item["origin"]) { + if ($toplevel_parent_item["origin"]) { $datarray['diaspora_signed_text'] = json_encode($data); } + if (Item::isTooOld($datarray)) { + Logger::info('Comment is too old', ['created' => $datarray['created'], 'uid' => $datarray['uid'], 'guid' => $datarray['guid']]); + return false; + } + $message_id = Item::insert($datarray); if ($message_id <= 0) { @@ -1950,7 +1639,7 @@ class Diaspora $body = Markdown::toBBCode($msg_text); $message_uri = $msg_author.":".$msg_guid; - $person = self::personByHandle($msg_author); + $person = FContact::getByURL($msg_author); return Mail::insert([ 'uid' => $importer['uid'], @@ -1998,6 +1687,10 @@ class Diaspora return false; } + if (!empty($contact['gsid'])) { + GServer::setProtocol($contact['gsid'], Post\DeliveryData::DIASPORA); + } + $conversation = DBA::selectFirst('conv', [], ['uid' => $importer["uid"], 'guid' => $guid]); if (!DBA::isResult($conversation)) { $r = q( @@ -2038,7 +1731,7 @@ class Diaspora * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ - private static function receiveLike(array $importer, $sender, $data) + private static function receiveLike(array $importer, $sender, $data, bool $fetched) { $author = Strings::escapeTags(XML::unescape($data->author)); $guid = Strings::escapeTags(XML::unescape($data->guid)); @@ -2057,17 +1750,21 @@ class Diaspora return false; } + if (!empty($contact['gsid'])) { + GServer::setProtocol($contact['gsid'], Post\DeliveryData::DIASPORA); + } + $message_id = self::messageExists($importer["uid"], $guid); if ($message_id) { return true; } - $parent_item = self::parentItem($importer["uid"], $parent_guid, $author, $contact); - if (!$parent_item) { + $toplevel_parent_item = self::parentItem($importer["uid"], $parent_guid, $author, $contact); + if (!$toplevel_parent_item) { return false; } - $person = self::personByHandle($author); + $person = FContact::getByURL($author); if (!is_array($person)) { Logger::log("unable to find author details"); return false; @@ -2087,6 +1784,7 @@ class Diaspora $datarray = []; $datarray["protocol"] = Conversation::PARCEL_DIASPORA; + $datarray["direction"] = $fetched ? Conversation::PULL : Conversation::PUSH; $datarray["uid"] = $importer["uid"]; $datarray["contact-id"] = $author_contact["cid"]; @@ -2100,7 +1798,7 @@ class Diaspora $datarray["verb"] = $verb; $datarray["gravity"] = GRAVITY_ACTIVITY; - $datarray["parent-uri"] = $parent_item["uri"]; + $datarray['thr-parent'] = $toplevel_parent_item['uri']; $datarray["object-type"] = Activity\ObjectType::NOTE; @@ -2110,11 +1808,11 @@ class Diaspora $datarray["changed"] = $datarray["created"] = $datarray["edited"] = DateTimeFormat::utcNow(); // like on comments have the comment as parent. So we need to fetch the toplevel parent - if ($parent_item['gravity'] != GRAVITY_PARENT) { - $toplevel = Item::selectFirst(['origin'], ['id' => $parent_item['parent']]); + if ($toplevel_parent_item['gravity'] != GRAVITY_PARENT) { + $toplevel = Post::selectFirst(['origin'], ['id' => $toplevel_parent_item['parent']]); $origin = $toplevel["origin"]; } else { - $origin = $parent_item["origin"]; + $origin = $toplevel_parent_item["origin"]; } // If we are the origin of the parent we store the original data. @@ -2123,6 +1821,11 @@ class Diaspora $datarray['diaspora_signed_text'] = json_encode($data); } + if (Item::isTooOld($datarray)) { + Logger::info('Like is too old', ['created' => $datarray['created'], 'uid' => $datarray['uid'], 'guid' => $datarray['guid']]); + return false; + } + $message_id = Item::insert($datarray); if ($message_id <= 0) { @@ -2161,6 +1864,10 @@ class Diaspora return false; } + if (!empty($contact['gsid'])) { + GServer::setProtocol($contact['gsid'], Post\DeliveryData::DIASPORA); + } + $conversation = null; $condition = ['uid' => $importer["uid"], 'guid' => $conversation_guid]; @@ -2173,7 +1880,7 @@ class Diaspora $message_uri = $author.":".$guid; - $person = self::personByHandle($author); + $person = FContact::getByURL($author); if (!$person) { Logger::log("unable to find author details"); return false; @@ -2210,7 +1917,7 @@ class Diaspora * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ - private static function receiveParticipation(array $importer, $data) + private static function receiveParticipation(array $importer, $data, bool $fetched) { $author = strtolower(Strings::escapeTags(XML::unescape($data->author))); $guid = Strings::escapeTags(XML::unescape($data->guid)); @@ -2221,25 +1928,29 @@ class Diaspora return false; } + if (!empty($contact['gsid'])) { + GServer::setProtocol($contact['gsid'], Post\DeliveryData::DIASPORA); + } + if (self::messageExists($importer["uid"], $guid)) { return true; } - $parent_item = self::parentItem($importer["uid"], $parent_guid, $author, $contact); - if (!$parent_item) { + $toplevel_parent_item = self::parentItem($importer["uid"], $parent_guid, $author, $contact); + if (!$toplevel_parent_item) { return false; } - if (!$parent_item['origin']) { + if (!$toplevel_parent_item['origin']) { Logger::info('Not our origin. Participation is ignored', ['parent_guid' => $parent_guid, 'guid' => $guid, 'author' => $author]); } - if (!in_array($parent_item['private'], [Item::PUBLIC, Item::UNLISTED])) { + if (!in_array($toplevel_parent_item['private'], [Item::PUBLIC, Item::UNLISTED])) { Logger::info('Item is not public, participation is ignored', ['parent_guid' => $parent_guid, 'guid' => $guid, 'author' => $author]); return false; } - $person = self::personByHandle($author); + $person = FContact::getByURL($author); if (!is_array($person)) { Logger::log("Person not found: ".$author); return false; @@ -2251,6 +1962,7 @@ class Diaspora $datarray = []; $datarray["protocol"] = Conversation::PARCEL_DIASPORA; + $datarray["direction"] = $fetched ? Conversation::PULL : Conversation::PUSH; $datarray["uid"] = $importer["uid"]; $datarray["contact-id"] = $author_contact["cid"]; @@ -2264,7 +1976,7 @@ class Diaspora $datarray["verb"] = Activity::FOLLOW; $datarray["gravity"] = GRAVITY_ACTIVITY; - $datarray["parent-uri"] = $parent_item["uri"]; + $datarray['thr-parent'] = $toplevel_parent_item['uri']; $datarray["object-type"] = Activity\ObjectType::NOTE; @@ -2273,14 +1985,19 @@ class Diaspora // Diaspora doesn't provide a date for a participation $datarray["changed"] = $datarray["created"] = $datarray["edited"] = DateTimeFormat::utcNow(); + if (Item::isTooOld($datarray)) { + Logger::info('Participation is too old', ['created' => $datarray['created'], 'uid' => $datarray['uid'], 'guid' => $datarray['guid']]); + return false; + } + $message_id = Item::insert($datarray); Logger::info('Participation stored', ['id' => $message_id, 'guid' => $guid, 'parent_guid' => $parent_guid, 'author' => $author]); // Send all existing comments and likes to the requesting server - $comments = Item::select(['id', 'uri-id', 'parent-author-network', 'author-network', 'verb'], - ['parent' => $parent_item['id'], 'gravity' => [GRAVITY_COMMENT, GRAVITY_ACTIVITY]]); - while ($comment = Item::fetch($comments)) { + $comments = Post::select(['id', 'uri-id', 'parent-author-network', 'author-network', 'verb'], + ['parent' => $toplevel_parent_item['id'], 'gravity' => [GRAVITY_COMMENT, GRAVITY_ACTIVITY]]); + while ($comment = Post::fetch($comments)) { if (in_array($comment['verb'], [Activity::FOLLOW, Activity::TAG])) { Logger::info('participation messages are not relayed', ['item' => $comment['id']]); continue; @@ -2513,7 +2230,7 @@ class Diaspora Logger::log("Author ".$author." wants to listen to us.", Logger::DEBUG); } - $ret = self::personByHandle($author); + $ret = FContact::getByURL($author); if (!$ret || ($ret["network"] != Protocol::DIASPORA)) { Logger::log("Cannot resolve diaspora handle ".$author." for ".$recipient); @@ -2567,10 +2284,10 @@ class Diaspora } // Do we already have this item? - $fields = ['body', 'title', 'attach', 'app', 'created', 'object-type', 'uri', 'guid', - 'author-name', 'author-link', 'author-avatar', 'plink']; + $fields = ['body', 'title', 'app', 'created', 'object-type', 'uri', 'guid', + 'author-name', 'author-link', 'author-avatar', 'plink', 'uri-id']; $condition = ['guid' => $guid, 'visible' => true, 'deleted' => false, 'private' => [Item::PUBLIC, Item::UNLISTED]]; - $item = Item::selectFirst($fields, $condition); + $item = Post::selectFirst($fields, $condition); if (DBA::isResult($item)) { Logger::log("reshared message ".$guid." already exists on system."); @@ -2585,9 +2302,6 @@ class Diaspora $item["body"] = self::replacePeopleGuid($item["body"], $item["author-link"]); - // Add OEmbed and other information to the body - $item["body"] = PageInfo::searchAndAppendToBody($item["body"], false, true); - return $item; } else { return $item; @@ -2611,10 +2325,10 @@ class Diaspora } if ($stored) { - $fields = ['body', 'title', 'attach', 'app', 'created', 'object-type', 'uri', 'guid', - 'author-name', 'author-link', 'author-avatar', 'plink']; + $fields = ['body', 'title', 'app', 'created', 'object-type', 'uri', 'guid', + 'author-name', 'author-link', 'author-avatar', 'plink', 'uri-id']; $condition = ['guid' => $guid, 'visible' => true, 'deleted' => false, 'private' => [Item::PUBLIC, Item::UNLISTED]]; - $item = Item::selectFirst($fields, $condition); + $item = Post::selectFirst($fields, $condition); if (DBA::isResult($item)) { // If it is a reshared post from another network then reformat to avoid display problems with two share elements @@ -2640,7 +2354,7 @@ class Diaspora */ private static function addReshareActivity($item, $parent_message_id, $guid, $author) { - $parent = Item::selectFirst(['uri', 'guid'], ['id' => $parent_message_id]); + $parent = Post::selectFirst(['uri', 'guid'], ['id' => $parent_message_id]); $datarray = []; @@ -2656,18 +2370,25 @@ class Diaspora $datarray['guid'] = $parent['guid'] . '-' . $guid; $datarray['uri'] = self::getUriFromGuid($author, $datarray['guid']); - $datarray['parent-uri'] = $parent['uri']; + $datarray['thr-parent'] = $parent['uri']; $datarray['verb'] = $datarray['body'] = Activity::ANNOUNCE; $datarray['gravity'] = GRAVITY_ACTIVITY; $datarray['object-type'] = Activity\ObjectType::NOTE; $datarray['protocol'] = $item['protocol']; + $datarray['source'] = $item['source']; + $datarray['direction'] = $item['direction']; $datarray['plink'] = self::plink($author, $datarray['guid']); $datarray['private'] = $item['private']; $datarray['changed'] = $datarray['created'] = $datarray['edited'] = $item['created']; + if (Item::isTooOld($datarray)) { + Logger::info('Reshare activity is too old', ['created' => $datarray['created'], 'uid' => $datarray['uid'], 'guid' => $datarray['guid']]); + return false; + } + $message_id = Item::insert($datarray); if ($message_id) { @@ -2689,7 +2410,7 @@ class Diaspora * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ - private static function receiveReshare(array $importer, $data, $xml) + private static function receiveReshare(array $importer, $data, $xml, bool $fetched) { $author = Strings::escapeTags(XML::unescape($data->author)); $guid = Strings::escapeTags(XML::unescape($data->guid)); @@ -2704,6 +2425,10 @@ class Diaspora return false; } + if (!empty($contact['gsid'])) { + GServer::setProtocol($contact['gsid'], Post\DeliveryData::DIASPORA); + } + $message_id = self::messageExists($importer["uid"], $guid); if ($message_id) { return true; @@ -2714,6 +2439,10 @@ class Diaspora return false; } + if (empty($original_item['plink'])) { + $original_item['plink'] = self::plink($root_author, $root_guid); + } + $datarray = []; $datarray["uid"] = $importer["uid"]; @@ -2727,7 +2456,7 @@ class Diaspora $datarray["owner-id"] = $datarray["author-id"]; $datarray["guid"] = $guid; - $datarray["uri"] = $datarray["parent-uri"] = self::getUriFromGuid($author, $guid); + $datarray["uri"] = $datarray["thr-parent"] = self::getUriFromGuid($author, $guid); $datarray['uri-id'] = ItemURI::insert(['uri' => $datarray['uri'], 'guid' => $datarray['guid']]); $datarray["verb"] = Activity::POST; @@ -2735,6 +2464,7 @@ class Diaspora $datarray["protocol"] = Conversation::PARCEL_DIASPORA; $datarray["source"] = $xml; + $datarray["direction"] = $fetched ? Conversation::PULL : Conversation::PUSH; /// @todo Copy tag data from original post @@ -2755,7 +2485,6 @@ class Diaspora Tag::storeFromBody($datarray['uri-id'], $datarray["body"]); - $datarray["attach"] = $original_item["attach"]; $datarray["app"] = $original_item["app"]; $datarray["plink"] = self::plink($author, $guid); @@ -2765,6 +2494,12 @@ class Diaspora $datarray["object-type"] = $original_item["object-type"]; self::fetchGuid($datarray); + + if (Item::isTooOld($datarray)) { + Logger::info('Reshare is too old', ['created' => $datarray['created'], 'uid' => $datarray['uid'], 'guid' => $datarray['guid']]); + return false; + } + $message_id = Item::insert($datarray); self::sendParticipation($contact, $datarray); @@ -2801,7 +2536,7 @@ class Diaspora $target_guid = Strings::escapeTags(XML::unescape($data->target_guid)); $target_type = Strings::escapeTags(XML::unescape($data->target_type)); - $person = self::personByHandle($author); + $person = FContact::getByURL($author); if (!is_array($person)) { Logger::log("unable to find author detail for ".$author); return false; @@ -2812,7 +2547,7 @@ class Diaspora } // Fetch items that are about to be deleted - $fields = ['uid', 'id', 'parent', 'parent-uri', 'author-link', 'file']; + $fields = ['uid', 'id', 'parent', 'author-link', 'uri-id']; // When we receive a public retraction, we delete every item that we find. if ($importer['uid'] == 0) { @@ -2821,20 +2556,20 @@ class Diaspora $condition = ['guid' => $target_guid, 'deleted' => false, 'uid' => $importer['uid']]; } - $r = Item::select($fields, $condition); + $r = Post::select($fields, $condition); if (!DBA::isResult($r)) { Logger::log("Target guid ".$target_guid." was not found on this system for user ".$importer['uid']."."); return false; } - while ($item = Item::fetch($r)) { - if (strstr($item['file'], '[')) { + while ($item = Post::fetch($r)) { + if (DBA::exists('post-category', ['uri-id' => $item['uri-id'], 'uid' => $item['uid'], 'type' => Post\Category::FILE])) { Logger::log("Target guid " . $target_guid . " for user " . $item['uid'] . " is filed. So it won't be deleted.", Logger::DEBUG); continue; } // Fetch the parent item - $parent = Item::selectFirst(['author-link'], ['id' => $item['parent']]); + $parent = Post::selectFirst(['author-link'], ['id' => $item['parent']]); // Only delete it if the parent author really fits if (!Strings::compareLink($parent["author-link"], $contact["url"]) && !Strings::compareLink($item["author-link"], $contact["url"])) { @@ -2846,6 +2581,7 @@ class Diaspora Logger::log("Deleted target ".$target_guid." (".$item["id"].") from user ".$item["uid"]." parent: ".$item['parent'], Logger::DEBUG); } + DBA::close($r); return true; } @@ -2896,18 +2632,61 @@ class Diaspora return true; } + /** + * Checks if an incoming message is wanted + * + * @param string $url + * @param integer $uriid + * @param string $author + * @param string $body + * @return boolean Is the message wanted? + */ + private static function isSolicitedMessage(string $url, int $uriid, string $author, string $body) + { + $contact = Contact::getByURL($author); + if (DBA::exists('contact', ["`nurl` = ? AND `uid` != ? AND `rel` IN (?, ?)", + $contact['nurl'], 0, Contact::FRIEND, Contact::SHARING])) { + Logger::info('Author has got followers - accepted', ['url' => $url, 'author' => $author]); + return true; + } + + $taglist = Tag::getByURIId($uriid, [Tag::HASHTAG]); + $tags = array_column($taglist, 'name'); + return Relay::isSolicitedPost($tags, $body, $contact['id'], $url, Protocol::DIASPORA); + } + + /** + * Store an attached photo in the post-media table + * + * @param int $uriid + * @param object $photo + * @return void + */ + private static function storePhotoAsMedia(int $uriid, $photo) + { + $data = []; + $data['uri-id'] = $uriid; + $data['type'] = Post\Media::IMAGE; + $data['url'] = XML::unescape($photo->remote_photo_path) . XML::unescape($photo->remote_photo_name); + $data['height'] = (int)XML::unescape($photo->height ?? 0); + $data['width'] = (int)XML::unescape($photo->width ?? 0); + $data['description'] = XML::unescape($photo->text ?? ''); + + Post\Media::insert($data); + } + /** * Receives status messages * * @param array $importer Array of the importer user * @param SimpleXMLElement $data The message object * @param string $xml The original XML of the message - * + * @param bool $fetched The message had been fetched and not pushed * @return int The message id of the newly created item * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ - private static function receiveStatusMessage(array $importer, SimpleXMLElement $data, $xml) + private static function receiveStatusMessage(array $importer, SimpleXMLElement $data, $xml, bool $fetched) { $author = Strings::escapeTags(XML::unescape($data->author)); $guid = Strings::escapeTags(XML::unescape($data->guid)); @@ -2921,6 +2700,10 @@ class Diaspora return false; } + if (!empty($contact['gsid'])) { + GServer::setProtocol($contact['gsid'], Post\DeliveryData::DIASPORA); + } + $message_id = self::messageExists($importer["uid"], $guid); if ($message_id) { return true; @@ -2933,25 +2716,25 @@ class Diaspora } } - $body = Markdown::toBBCode($text); + $raw_body = $body = Markdown::toBBCode($text); $datarray = []; + $datarray["guid"] = $guid; + $datarray["uri"] = $datarray["thr-parent"] = self::getUriFromGuid($author, $guid); + $datarray['uri-id'] = ItemURI::insert(['uri' => $datarray['uri'], 'guid' => $datarray['guid']]); + // Attach embedded pictures to the body if ($data->photo) { foreach ($data->photo as $photo) { - $body = "[img]".XML::unescape($photo->remote_photo_path). - XML::unescape($photo->remote_photo_name)."[/img]\n".$body; + self::storePhotoAsMedia($datarray['uri-id'], $photo); } $datarray["object-type"] = Activity\ObjectType::IMAGE; + $datarray["post-type"] = Item::PT_IMAGE; } else { $datarray["object-type"] = Activity\ObjectType::NOTE; - - // Add OEmbed and other information to the body - if (!self::isHubzilla($contact["url"])) { - $body = PageInfo::searchAndAppendToBody($body, false, true); - } + $datarray["post-type"] = Item::PT_NOTE; } /// @todo enable support for polls @@ -2973,21 +2756,30 @@ class Diaspora $datarray["owner-link"] = $datarray["author-link"]; $datarray["owner-id"] = $datarray["author-id"]; - $datarray["guid"] = $guid; - $datarray["uri"] = $datarray["parent-uri"] = self::getUriFromGuid($author, $guid); - $datarray['uri-id'] = ItemURI::insert(['uri' => $datarray['uri'], 'guid' => $datarray['guid']]); - $datarray["verb"] = Activity::POST; $datarray["gravity"] = GRAVITY_PARENT; $datarray["protocol"] = Conversation::PARCEL_DIASPORA; $datarray["source"] = $xml; + $datarray["direction"] = $fetched ? Conversation::PULL : Conversation::PUSH; + + if ($fetched) { + $datarray["post-reason"] = Item::PR_FETCHED; + } elseif ($datarray["uid"] == 0) { + $datarray["post-reason"] = Item::PR_GLOBAL; + } $datarray["body"] = self::replacePeopleGuid($body, $contact["url"]); + $datarray["raw-body"] = self::replacePeopleGuid($raw_body, $contact["url"]); self::storeMentions($datarray['uri-id'], $text); Tag::storeRawTagsFromBody($datarray['uri-id'], $datarray["body"]); + if (!$fetched && !self::isSolicitedMessage($datarray["uri"], $datarray['uri-id'], $author, $body)) { + DBA::delete('item-uri', ['uri' => $datarray['uri']]); + return false; + } + if ($provider_display_name != "") { $datarray["app"] = $provider_display_name; } @@ -3005,6 +2797,12 @@ class Diaspora } self::fetchGuid($datarray); + + if (Item::isTooOld($datarray)) { + Logger::info('Status is too old', ['created' => $datarray['created'], 'uid' => $datarray['uid'], 'guid' => $datarray['guid']]); + return false; + } + $message_id = Item::insert($datarray); self::sendParticipation($contact, $datarray); @@ -3072,9 +2870,9 @@ class Diaspora return false; } - $aes_key = openssl_random_pseudo_bytes(32); + $aes_key = random_bytes(32); $b_aes_key = base64_encode($aes_key); - $iv = openssl_random_pseudo_bytes(16); + $iv = random_bytes(16); $b_iv = base64_encode($iv); $ciphertext = self::aesEncrypt($aes_key, $iv, $msg); @@ -3204,7 +3002,7 @@ class Diaspora // We always try to use the data from the fcontact table. // This is important for transmitting data to Friendica servers. if (!empty($contact['addr'])) { - $fcontact = self::personByHandle($contact['addr']); + $fcontact = FContact::getByURL($contact['addr']); if (!empty($fcontact)) { $dest_url = ($public_batch ? $fcontact["batch"] : $fcontact["notify"]); } @@ -3278,7 +3076,18 @@ class Diaspora $owner['uprvkey'] = $owner['prvkey']; } - $envelope = self::buildMessage($msg, $owner, $contact, $owner['uprvkey'], $contact['pubkey'], $public_batch); + // When sending content to Friendica contacts using the Diaspora protocol + // we have to fetch the public key from the fcontact. + // This is due to the fact that legacy DFRN had unique keys for every contact. + $pubkey = $contact['pubkey']; + if (!empty($contact['addr'])) { + $fcontact = FContact::getByURL($contact['addr']); + if (!empty($fcontact)) { + $pubkey = $fcontact['pubkey']; + } + } + + $envelope = self::buildMessage($msg, $owner, $contact, $owner['uprvkey'], $pubkey, $public_batch); $return_code = self::transmit($owner, $contact, $envelope, $public_batch, $guid); @@ -3310,17 +3119,7 @@ class Diaspora return; } - // Fetch some user id to have a valid handle to transmit the participation. - // In fact it doesn't matter which user sends this - but it is needed by the protocol. - // If the item belongs to a user, we take this user id. - if ($item['uid'] == 0) { - $condition = ['verified' => true, 'blocked' => false, 'account_removed' => false, 'account_expired' => false]; - $first_user = DBA::selectFirst('user', ['uid'], $condition); - $owner = User::getOwnerDataById($first_user['uid']); - } else { - $owner = User::getOwnerDataById($item['uid']); - } - + $owner = User::getOwnerDataById($item['uid']); $author = self::myHandle($owner); $message = ["author" => $author, @@ -3456,7 +3255,7 @@ class Diaspora if (!empty($reshared['guid']) && $complete) { $condition = ['guid' => $reshared['guid'], 'network' => [Protocol::DFRN, Protocol::DIASPORA]]; - $item = Item::selectFirst(['contact-id'], $condition); + $item = Post::selectFirst(['contact-id'], $condition); if (DBA::isResult($item)) { $ret = []; $ret["root_handle"] = self::handleFromContact($item["contact-id"]); @@ -3607,7 +3406,7 @@ class Diaspora $type = "reshare"; } else { $title = $item["title"]; - $body = $item["body"]; + $body = Post\Media::addAttachmentsToBody($item['uri-id'], $item['body']); // Fetch the title from an attached link - if there is one if (empty($item["title"]) && DI::pConfig()->get($owner['uid'], 'system', 'attach_link_title')) { @@ -3618,7 +3417,6 @@ class Diaspora } if ($item['author-link'] != $item['owner-link']) { - require_once 'mod/share.php'; $body = BBCode::getShareOpeningTag($item['author-name'], $item['author-link'], $item['author-avatar'], $item['plink'], $item['created']) . $body . '[/share]'; } @@ -3631,13 +3429,11 @@ class Diaspora $body = "### ".html_entity_decode($title)."\n\n".$body; } - if ($item["attach"]) { - $cnt = preg_match_all('/href=\"(.*?)\"(.*?)title=\"(.*?)\"/ism', $item["attach"], $matches, PREG_SET_ORDER); - if ($cnt) { - $body .= "\n".DI::l10n()->t("Attachments:")."\n"; - foreach ($matches as $mtch) { - $body .= "[".$mtch[3]."](".$mtch[1].")\n"; - } + $attachments = Post\Media::getByURIId($item['uri-id'], [Post\Media::DOCUMENT, Post\Media::TORRENT, Post\Media::UNKNOWN]); + if (!empty($attachments)) { + $body .= "\n".DI::l10n()->t("Attachments:")."\n"; + foreach ($attachments as $attachment) { + $body .= "[" . $attachment['description'] . "](" . $attachment['url'] . ")\n"; } } @@ -3736,12 +3532,12 @@ class Diaspora */ private static function constructLike(array $item, array $owner) { - $parent = Item::selectFirst(['guid', 'uri', 'parent-uri'], ['uri' => $item["thr-parent"]]); + $parent = Post::selectFirst(['guid', 'uri', 'thr-parent'], ['uri' => $item["thr-parent"]]); if (!DBA::isResult($parent)) { return false; } - $target_type = ($parent["uri"] === $parent["parent-uri"] ? "Post" : "Comment"); + $target_type = ($parent["uri"] === $parent["thr-parent"] ? "Post" : "Comment"); $positive = null; if ($item['verb'] === Activity::LIKE) { $positive = "true"; @@ -3768,7 +3564,7 @@ class Diaspora */ private static function constructAttend(array $item, array $owner) { - $parent = Item::selectFirst(['guid', 'uri', 'parent-uri'], ['uri' => $item["thr-parent"]]); + $parent = Post::selectFirst(['guid'], ['uri' => $item['thr-parent']]); if (!DBA::isResult($parent)) { return false; } @@ -3813,7 +3609,7 @@ class Diaspora return $result; } - $toplevel_item = Item::selectFirst(['guid', 'author-id', 'author-link'], ['id' => $item['parent'], 'parent' => $item['parent']]); + $toplevel_item = Post::selectFirst(['guid', 'author-id', 'author-link', 'gravity'], ['id' => $item['parent'], 'parent' => $item['parent']]); if (!DBA::isResult($toplevel_item)) { Logger::error('Missing parent conversation item', ['parent' => $item['parent']]); return false; @@ -3821,10 +3617,10 @@ class Diaspora $thread_parent_item = $toplevel_item; if ($item['thr-parent'] != $item['parent-uri']) { - $thread_parent_item = Item::selectFirst(['guid', 'author-id', 'author-link'], ['uri' => $item['thr-parent'], 'uid' => $item['uid']]); + $thread_parent_item = Post::selectFirst(['guid', 'author-id', 'author-link', 'gravity'], ['uri' => $item['thr-parent'], 'uid' => $item['uid']]); } - $body = $item["body"]; + $body = Post\Media::addAttachmentsToBody($item['uri-id'], $item['body']); // The replied to autor mention is prepended for clarity if: // - Item replied isn't yours @@ -3832,6 +3628,7 @@ class Diaspora // - Implicit mentions are enabled if ( $item['author-id'] != $thread_parent_item['author-id'] + && ($thread_parent_item['gravity'] != GRAVITY_PARENT) && (empty($item['uid']) || !Feature::isEnabled($item['uid'], 'explicit_mentions')) && !DI::config()->get('system', 'disable_implicit_mentions') ) { @@ -3896,51 +3693,6 @@ class Diaspora return self::buildAndTransmit($owner, $contact, $type, $message, $public_batch, $item["guid"]); } - /** - * Creates a message from a signature record entry - * - * @param array $item The item that will be exported - * @return array The message - */ - private static function messageFromSignature(array $item) - { - // Split the signed text - $signed_parts = explode(";", $item['signed_text']); - - if ($item["deleted"]) { - $message = ["author" => $item['signer'], - "target_guid" => $signed_parts[0], - "target_type" => $signed_parts[1]]; - } elseif (in_array($item["verb"], [Activity::LIKE, Activity::DISLIKE])) { - $message = ["author" => $signed_parts[4], - "guid" => $signed_parts[1], - "parent_guid" => $signed_parts[3], - "parent_type" => $signed_parts[2], - "positive" => $signed_parts[0], - "author_signature" => $item['signature'], - "parent_author_signature" => ""]; - } else { - // Remove the comment guid - $guid = array_shift($signed_parts); - - // Remove the parent guid - $parent_guid = array_shift($signed_parts); - - // Remove the handle - $handle = array_pop($signed_parts); - - $message = [ - "author" => $handle, - "guid" => $guid, - "parent_guid" => $parent_guid, - "text" => implode(";", $signed_parts), - "author_signature" => $item['signature'], - "parent_author_signature" => "" - ]; - } - return $message; - } - /** * Relays messages (like, comment, retraction) to other servers if we are the thread owner * @@ -4297,10 +4049,7 @@ class Diaspora return false; } - // This is a workaround for the behaviour of the "insert" function, see mod/item.php - $item['thr-parent'] = $item['parent-uri']; - - $parent = Item::selectFirst(['parent-uri'], ['uri' => $item['parent-uri']]); + $parent = Post::selectFirst(['parent-uri'], ['uri' => $item['thr-parent']]); if (!DBA::isResult($parent)) { return; }