diff --git a/include/api.php b/include/api.php index eccd77675e..0ca0bf838c 100644 --- a/include/api.php +++ b/include/api.php @@ -361,7 +361,7 @@ function api_call(App $a) } } - Logger::warning(API_LOG_PREFIX . 'not implemented', ['module' => 'api', 'action' => 'call']); + Logger::warning(API_LOG_PREFIX . 'not implemented', ['module' => 'api', 'action' => 'call', 'query' => $a->query_string]); throw new NotImplementedException(); } catch (HTTPException $e) { header("HTTP/1.1 {$e->getCode()} {$e->httpdesc}"); diff --git a/src/Model/Contact.php b/src/Model/Contact.php index ce3aeac560..a0bbdd1049 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -2120,17 +2120,33 @@ class Contact extends BaseObject return $contact; } - public static function addRelationship($importer, $contact, $datarray, $item = '', $sharing = false, $note = '') { + /** + * @param array $importer Owner (local user) data + * @param array $contact Existing owner-specific contact data we want to expand the relationship with. Optional. + * @param array $datarray An item-like array with at least the 'author-id' and 'author-url' keys for the contact. Mandatory. + * @param bool $sharing True: Contact is now sharing with Owner; False: Contact is now following Owner (default) + * @param string $note Introduction additional message + * @return bool|null True: follow request is accepted; False: relationship is rejected; Null: relationship is pending + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @throws \ImagickException + */ + public static function addRelationship(array $importer, array $contact, array $datarray, $sharing = false, $note = '') + { // Should always be set if (empty($datarray['author-id'])) { - return; + return false; } - $fields = ['url', 'name', 'nick', 'photo', 'network']; + $fields = ['url', 'name', 'nick', 'photo', 'network', 'blocked']; $pub_contact = DBA::selectFirst('contact', $fields, ['id' => $datarray['author-id']]); if (!DBA::isResult($pub_contact)) { // Should never happen - return; + return false; + } + + // Contact is blocked at node-level + if (self::isBlocked($datarray['author-id'])) { + return false; } $url = defaults($datarray, 'author-link', $pub_contact['url']); @@ -2139,44 +2155,45 @@ class Contact extends BaseObject $nick = $pub_contact['nick']; $network = $pub_contact['network']; - if (is_array($contact)) { + if (!empty($contact)) { + // Contact is blocked at user-level + if (self::isBlockedByUser($contact['id'], $importer['id'])) { + return false; + } + // Make sure that the existing contact isn't archived self::unmarkForArchival($contact); - $protocol = self::getProtocol($url, $contact['network']); - if (($contact['rel'] == self::SHARING) || ($sharing && $contact['rel'] == self::FOLLOWER)) { DBA::update('contact', ['rel' => self::FRIEND, 'writable' => true, 'pending' => false], ['id' => $contact['id'], 'uid' => $importer['uid']]); } - if ($protocol == Protocol::ACTIVITYPUB) { - ActivityPub\Transmitter::sendContactAccept($contact['url'], $contact['hub-verify'], $importer['uid']); - } - - // send email notification to owner? + return true; } else { - $protocol = self::getProtocol($url, $network); - + // send email notification to owner? if (DBA::exists('contact', ['nurl' => Strings::normaliseLink($url), 'uid' => $importer['uid'], 'pending' => true])) { Logger::log('ignoring duplicated connection request from pending contact ' . $url); - return; + return null; } + // create contact record - q("INSERT INTO `contact` (`uid`, `created`, `url`, `nurl`, `name`, `nick`, `photo`, `network`, `rel`, - `blocked`, `readonly`, `pending`, `writable`) - VALUES (%d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, 0, 0, 1, 1)", - intval($importer['uid']), - DBA::escape(DateTimeFormat::utcNow()), - DBA::escape($url), - DBA::escape(Strings::normaliseLink($url)), - DBA::escape($name), - DBA::escape($nick), - DBA::escape($photo), - DBA::escape($network), - intval(self::FOLLOWER) - ); + DBA::insert('contact', [ + 'uid' => $importer['uid'], + 'created' => DateTimeFormat::utcNow(), + 'url' => $url, + 'nurl' => Strings::normaliseLink($url), + 'name' => $name, + 'nick' => $nick, + 'photo' => $photo, + 'network' => $network, + 'rel' => self::FOLLOWER, + 'blocked' => 0, + 'readonly' => 0, + 'pending' => 1, + 'writable' => 1, + ]); $contact_record = [ 'id' => DBA::lastInsertId(), @@ -2220,20 +2237,16 @@ class Contact extends BaseObject 'verb' => ($sharing ? ACTIVITY_FRIEND : ACTIVITY_FOLLOW), 'otype' => 'intro' ]); - } } elseif (DBA::isResult($user) && in_array($user['page-flags'], [User::PAGE_FLAGS_SOAPBOX, User::PAGE_FLAGS_FREELOVE, User::PAGE_FLAGS_COMMUNITY])) { $condition = ['uid' => $importer['uid'], 'url' => $url, 'pending' => true]; DBA::update('contact', ['pending' => false], $condition); - $contact = DBA::selectFirst('contact', ['url', 'network', 'hub-verify'], ['id' => $contact_record['id']]); - $protocol = self::getProtocol($contact['url'], $contact['network']); - - if ($protocol == Protocol::ACTIVITYPUB) { - ActivityPub\Transmitter::sendContactAccept($contact['url'], $contact['hub-verify'], $importer['uid']); - } + return true; } } + + return null; } public static function removeFollower($importer, $contact, array $datarray = [], $item = "") diff --git a/src/Protocol/ActivityPub/Processor.php b/src/Protocol/ActivityPub/Processor.php index 045c48a4c1..fd84f494e2 100644 --- a/src/Protocol/ActivityPub/Processor.php +++ b/src/Protocol/ActivityPub/Processor.php @@ -530,7 +530,7 @@ class Processor DBA::update('contact', ['hub-verify' => $activity['id'], 'protocol' => Protocol::ACTIVITYPUB], ['id' => $cid]); $contact = DBA::selectFirst('contact', [], ['id' => $cid, 'network' => Protocol::NATIVE_SUPPORT]); } else { - $contact = false; + $contact = []; } $item = ['author-id' => Contact::getIdForURL($activity['actor']), @@ -541,7 +541,11 @@ class Processor // Ensure that the contact has got the right network type self::switchContact($item['author-id']); - Contact::addRelationship($owner, $contact, $item, '', false, $note); + $result = Contact::addRelationship($owner, $contact, $item, false, $note); + if ($result === true) { + ActivityPub\Transmitter::sendContactAccept($item['author-link'], $item['author-id'], $owner['uid']); + } + $cid = Contact::getIdForURL($activity['actor'], $uid); if (empty($cid)) { return; diff --git a/src/Protocol/ActivityPub/Transmitter.php b/src/Protocol/ActivityPub/Transmitter.php index 7fc5e2df06..ed792db16e 100644 --- a/src/Protocol/ActivityPub/Transmitter.php +++ b/src/Protocol/ActivityPub/Transmitter.php @@ -1538,13 +1538,16 @@ class Transmitter 'id' => System::baseUrl() . '/activity/' . System::createGUID(), 'type' => 'Accept', 'actor' => $owner['url'], - 'object' => ['id' => $id, 'type' => 'Follow', + 'object' => [ + 'id' => (string)$id, + 'type' => 'Follow', 'actor' => $profile['url'], - 'object' => $owner['url']], + 'object' => $owner['url'] + ], 'instrument' => self::getService(), 'to' => [$profile['url']]]; - Logger::log('Sending accept to ' . $target . ' for user ' . $uid . ' with id ' . $id, Logger::DEBUG); + Logger::debug('Sending accept to ' . $target . ' for user ' . $uid . ' with id ' . $id); $signed = LDSignature::sign($data, $owner); HTTPSignature::transmit($signed, $profile['inbox'], $uid); @@ -1568,13 +1571,16 @@ class Transmitter 'id' => System::baseUrl() . '/activity/' . System::createGUID(), 'type' => 'Reject', 'actor' => $owner['url'], - 'object' => ['id' => $id, 'type' => 'Follow', + 'object' => [ + 'id' => (string)$id, + 'type' => 'Follow', 'actor' => $profile['url'], - 'object' => $owner['url']], + 'object' => $owner['url'] + ], 'instrument' => self::getService(), 'to' => [$profile['url']]]; - Logger::log('Sending reject to ' . $target . ' for user ' . $uid . ' with id ' . $id, Logger::DEBUG); + Logger::debug('Sending reject to ' . $target . ' for user ' . $uid . ' with id ' . $id); $signed = LDSignature::sign($data, $owner); HTTPSignature::transmit($signed, $profile['inbox'], $uid); diff --git a/src/Protocol/DFRN.php b/src/Protocol/DFRN.php index f74b572949..01159b1bae 100644 --- a/src/Protocol/DFRN.php +++ b/src/Protocol/DFRN.php @@ -2247,13 +2247,12 @@ class DFRN // The functions below are partly used by ostatus.php as well - where we have this variable $r = q("SELECT * FROM `contact` WHERE `id` = %d", intval($importer["id"])); $contact = $r[0]; - $nickname = $contact["nick"]; // Big question: Do we need these functions? They were part of the "consume_feed" function. // This function once was responsible for DFRN and OStatus. if (activity_match($item["verb"], ACTIVITY_FOLLOW)) { Logger::log("New follower"); - Contact::addRelationship($importer, $contact, $item, $nickname); + Contact::addRelationship($importer, $contact, $item); return false; } if (activity_match($item["verb"], ACTIVITY_UNFOLLOW)) { @@ -2263,7 +2262,7 @@ class DFRN } if (activity_match($item["verb"], ACTIVITY_REQ_FRIEND)) { Logger::log("New friend request"); - Contact::addRelationship($importer, $contact, $item, $nickname, true); + Contact::addRelationship($importer, $contact, $item, true); return false; } if (activity_match($item["verb"], ACTIVITY_UNFRIEND)) { diff --git a/src/Protocol/OStatus.php b/src/Protocol/OStatus.php index c7747e8cfc..81e5c19db8 100644 --- a/src/Protocol/OStatus.php +++ b/src/Protocol/OStatus.php @@ -417,13 +417,6 @@ class OStatus $author = self::fetchAuthor($xpath, $entry, $importer, $contact, $stored); } - $value = XML::getFirstNodeValue($xpath, 'atom:author/poco:preferredUsername/text()', $entry); - if ($value != "") { - $nickname = $value; - } else { - $nickname = $author["author-name"]; - } - $item = array_merge($header, $author); $item["uri"] = XML::getFirstNodeValue($xpath, 'atom:id/text()', $entry); @@ -463,7 +456,7 @@ class OStatus } if ($item["verb"] == ACTIVITY_FOLLOW) { - Contact::addRelationship($importer, $contact, $item, $nickname); + Contact::addRelationship($importer, $contact, $item); continue; } diff --git a/src/Util/JsonLD.php b/src/Util/JsonLD.php index 69973f4feb..926fa1437d 100644 --- a/src/Util/JsonLD.php +++ b/src/Util/JsonLD.php @@ -68,9 +68,16 @@ class JsonLD } catch (Exception $e) { $normalized = false; - Logger::error('normalise error'); - // Sooner or later we should log some details as well - but currently this leads to memory issues - // Logger::log('normalise error:' . substr(print_r($e, true), 0, 10000), Logger::DEBUG); + $messages = []; + $currentException = $e; + do { + $messages[] = $currentException->getMessage(); + } while($currentException = $currentException->getPrevious()); + + Logger::warning('JsonLD normalize error'); + Logger::notice('JsonLD normalize error', ['messages' => $messages]); + Logger::info('JsonLD normalize error', ['trace' => $e->getTraceAsString()]); + Logger::debug('JsonLD normalize error', ['jsonobj' => $jsonobj]); } return $normalized;