X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FProtocol%2FDiaspora.php;h=7ab41eb52b08ea6bd1b2d9bdbf41e442df4b5f65;hb=32f70abf9a7913df1ddb28b9745eb37941d09d80;hp=f3b95db68f2fb1c5756cb494e73b96b51b121de5;hpb=e6cd5a4d663c74213a572a5528c32f721b677c9e;p=friendica.git diff --git a/src/Protocol/Diaspora.php b/src/Protocol/Diaspora.php index f3b95db68f..7ab41eb52b 100644 --- a/src/Protocol/Diaspora.php +++ b/src/Protocol/Diaspora.php @@ -34,7 +34,7 @@ use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\Conversation; -use Friendica\Model\GContact; +use Friendica\Model\FContact; use Friendica\Model\Item; use Friendica\Model\ItemURI; use Friendica\Model\Mail; @@ -56,196 +56,6 @@ use SimpleXMLElement; */ class Diaspora { - /** - * Mark the relay contact of the given contact for archival - * This is called whenever there is a communication issue with the server. - * It avoids sending stuff to servers who don't exist anymore. - * The relay contact is a technical contact entry that exists once per server. - * - * @param array $contact of the relay contact - */ - public static function markRelayForArchival(array $contact) - { - if (!empty($contact['contact-type']) && ($contact['contact-type'] == Contact::TYPE_RELAY)) { - // This is already the relay contact, we don't need to fetch it - $relay_contact = $contact; - } elseif (empty($contact['baseurl'])) { - if (!empty($contact['batch'])) { - $condition = ['uid' => 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 * @@ -639,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) { @@ -659,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; } @@ -670,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") @@ -708,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) { @@ -761,7 +573,7 @@ class Diaspora 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); @@ -937,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"]; } @@ -945,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 * @@ -1067,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 * @@ -1107,20 +818,7 @@ class Diaspora */ private static function contactByHandle($uid, $handle) { - $cid = Contact::getIdForURL($handle, $uid); - if (!$cid) { - Logger::log("Haven't found a contact for user " . $uid . " and handle " . $handle, Logger::DEBUG); - return false; - } - - $contact = DBA::selectFirst('contact', [], ['id' => $cid]); - if (!DBA::isResult($contact)) { - // This here shouldn't happen at all - Logger::log("Haven't found a contact for user " . $uid . " and handle " . $handle, Logger::DEBUG); - return false; - } - - return $contact; + return Contact::getByURL($handle, null, [], $uid); } /** @@ -1134,7 +832,7 @@ class Diaspora */ public static function isSupportedByContactUrl($url, $update = null) { - return !empty(self::personByHandle($url, $update)); + return !empty(FContact::getByURL($url, $update)); } /** @@ -1286,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]'; @@ -1352,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); } /** @@ -1493,7 +1191,7 @@ class Diaspora $item = Item::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 @@ -1656,7 +1354,7 @@ class Diaspora // Update the profile self::receiveProfile($importer, $data->profile); - // change the technical stuff in contact and gcontact + // change the technical stuff in contact $data = Probe::uri($new_handle); if ($data['network'] == Protocol::PHANTOM) { Logger::log('Account for '.$new_handle." couldn't be probed."); @@ -1671,14 +1369,6 @@ class Diaspora DBA::update('contact', $fields, ['addr' => $old_handle]); - $fields = ['url' => $data['url'], 'nurl' => Strings::normaliseLink($data['url']), - 'name' => $data['name'], 'nick' => $data['nick'], - 'addr' => $data['addr'], 'connect' => $data['addr'], - 'notify' => $data['notify'], 'photo' => $data['photo'], - 'server_url' => $data['baseurl'], 'network' => $data['network']]; - - DBA::update('gcontact', $fields, ['addr' => $old_handle]); - Logger::log('Contacts are updated.'); return true; @@ -1702,8 +1392,6 @@ class Diaspora } DBA::close($contacts); - DBA::delete('gcontact', ['addr' => $author]); - Logger::log('Removed contacts for ' . $author); return true; @@ -1726,7 +1414,7 @@ class Diaspora 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']); @@ -1757,27 +1445,6 @@ class Diaspora } } - /** - * 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 * @@ -1803,7 +1470,7 @@ class Diaspora continue; } - $person = self::personByHandle($match[3]); + $person = FContact::getByURL($match[3]); if (empty($person)) { continue; } @@ -1819,12 +1486,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)); @@ -1839,9 +1507,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); @@ -1854,12 +1522,12 @@ class Diaspora 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; @@ -1880,6 +1548,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-type"] = Item::PT_FETCHED; + } elseif ($datarray["uid"] == 0) { + $datarray["post-type"] = Item::PT_GLOBAL; + } else { + $datarray["post-type"] = Item::PT_COMMENT; + } + $datarray["guid"] = $guid; $datarray["uri"] = self::getUriFromGuid($author, $guid); $datarray['uri-id'] = ItemURI::insert(['uri' => $datarray['uri'], 'guid' => $datarray['guid']]); @@ -1887,11 +1564,7 @@ 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; @@ -1900,7 +1573,7 @@ class Diaspora $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"]); @@ -1912,7 +1585,7 @@ 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); } @@ -1974,7 +1647,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'], @@ -2086,12 +1759,12 @@ class Diaspora 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; @@ -2124,7 +1797,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; @@ -2134,11 +1807,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 = Item::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. @@ -2197,7 +1870,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; @@ -2249,21 +1922,21 @@ class Diaspora 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; @@ -2288,7 +1961,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; @@ -2303,7 +1976,7 @@ class Diaspora // 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]]); + ['parent' => $toplevel_parent_item['id'], 'gravity' => [GRAVITY_COMMENT, GRAVITY_ACTIVITY]]); while ($comment = Item::fetch($comments)) { if (in_array($comment['verb'], [Activity::FOLLOW, Activity::TAG])) { Logger::info('participation messages are not relayed', ['item' => $comment['id']]); @@ -2438,18 +2111,6 @@ class Diaspora DBA::update('contact', $fields, ['id' => $contact['id']]); - // @todo Update the public contact, then update the gcontact from that - - $gcontact = ["url" => $contact["url"], "network" => Protocol::DIASPORA, "generation" => 2, - "photo" => $image_url, "name" => $name, "location" => $location, - "about" => $about, "birthday" => $birthday, - "addr" => $author, "nick" => $nick, "keywords" => $keywords, - "hide" => !$searchable, "nsfw" => $nsfw]; - - $gcid = GContact::update($gcontact); - - GContact::link($gcid, $importer["uid"], $contact["id"]); - Logger::log("Profile of contact ".$contact["id"]." stored for user ".$importer["uid"], Logger::DEBUG); return true; @@ -2549,7 +2210,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); @@ -2603,8 +2264,8 @@ 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); @@ -2647,8 +2308,8 @@ 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); @@ -2692,7 +2353,7 @@ 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; @@ -2763,7 +2424,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; @@ -2791,7 +2452,7 @@ class Diaspora Tag::storeFromBody($datarray['uri-id'], $datarray["body"]); - $datarray["attach"] = $original_item["attach"]; + Post\Media::copy($original_item['uri-id'], $datarray['uri-id']); $datarray["app"] = $original_item["app"]; $datarray["plink"] = self::plink($author, $guid); @@ -2837,7 +2498,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; @@ -2848,7 +2509,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', 'file']; // When we receive a public retraction, we delete every item that we find. if ($importer['uid'] == 0) { @@ -2932,18 +2593,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)); @@ -2969,13 +2673,18 @@ 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) { + self::storePhotoAsMedia($datarray['uri-id'], $photo); $body = "[img]".XML::unescape($photo->remote_photo_path). XML::unescape($photo->remote_photo_name)."[/img]\n".$body; } @@ -3009,21 +2718,29 @@ 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; + if ($fetched) { + $datarray["post-type"] = Item::PT_FETCHED; + } elseif ($datarray["uid"] == 0) { + $datarray["post-type"] = Item::PT_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; } @@ -3240,7 +2957,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"]); } @@ -3667,13 +3384,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"; } } @@ -3772,12 +3487,12 @@ class Diaspora */ private static function constructLike(array $item, array $owner) { - $parent = Item::selectFirst(['guid', 'uri', 'parent-uri'], ['uri' => $item["thr-parent"]]); + $parent = Item::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"; @@ -3804,7 +3519,7 @@ class Diaspora */ private static function constructAttend(array $item, array $owner) { - $parent = Item::selectFirst(['guid', 'uri', 'parent-uri'], ['uri' => $item["thr-parent"]]); + $parent = Item::selectFirst(['guid'], ['uri' => $item['thr-parent']]); if (!DBA::isResult($parent)) { return false; } @@ -4333,10 +4048,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 = Item::selectFirst(['parent-uri'], ['uri' => $item['thr-parent']]); if (!DBA::isResult($parent)) { return; }