diff --git a/src/Content/Item.php b/src/Content/Item.php index 28d980473a..6befe978ff 100644 --- a/src/Content/Item.php +++ b/src/Content/Item.php @@ -547,9 +547,9 @@ class Item $item['private'] = $private_group ? ItemModel::PRIVATE : ItemModel::UNLISTED; if ($only_to_group) { - $cdata = Contact::getPublicAndUserContactID($group_contact['id'], $item['uid']); - if (!empty($cdata['user'])) { - $item['owner-id'] = $cdata['user']; + $pcid = Contact::getPublicContactId($group_contact['id'], $item['uid']); + if ($pcid) { + $item['owner-id'] = $pcid; unset($item['owner-link']); unset($item['owner-name']); unset($item['owner-avatar']); diff --git a/src/Model/Circle.php b/src/Model/Circle.php index d5fa70fda9..af20103040 100644 --- a/src/Model/Circle.php +++ b/src/Model/Circle.php @@ -290,12 +290,12 @@ class Circle throw new HTTPException\NotFoundException('Circle not found.'); } - $cdata = Contact::getPublicAndUserContactID($cid, $circle['uid']); - if (empty($cdata['user'])) { + $ucid = Contact::getUserContactId($cid, $circle['uid']); + if (!$ucid) { throw new HTTPException\NotFoundException('Invalid contact.'); } - return DBA::insert('group_member', ['gid' => $gid, 'contact-id' => $cdata['user']], Database::INSERT_IGNORE); + return DBA::insert('group_member', ['gid' => $gid, 'contact-id' => $ucid], Database::INSERT_IGNORE); } /** @@ -318,12 +318,12 @@ class Circle throw new HTTPException\NotFoundException('Circle not found.'); } - $cdata = Contact::getPublicAndUserContactID($cid, $circle['uid']); - if (empty($cdata['user'])) { + $ucid = Contact::getUserContactId($cid, $circle['uid']); + if (!$ucid) { throw new HTTPException\NotFoundException('Invalid contact.'); } - return DBA::delete('group_member', ['gid' => $gid, 'contact-id' => $cid]); + return DBA::delete('group_member', ['gid' => $gid, 'contact-id' => $ucid]); } /** @@ -347,12 +347,12 @@ class Circle } foreach ($contacts as $cid) { - $cdata = Contact::getPublicAndUserContactID($cid, $circle['uid']); - if (empty($cdata['user'])) { + $ucid = Contact::getUserContactId($cid, $circle['uid']); + if (!$ucid) { throw new HTTPException\NotFoundException('Invalid contact.'); } - DBA::insert('group_member', ['gid' => $gid, 'contact-id' => $cdata['user']], Database::INSERT_IGNORE); + DBA::insert('group_member', ['gid' => $gid, 'contact-id' => $ucid], Database::INSERT_IGNORE); } } @@ -379,12 +379,12 @@ class Circle $contactIds = []; foreach ($contacts as $cid) { - $cdata = Contact::getPublicAndUserContactID($cid, $circle['uid']); - if (empty($cdata['user'])) { + $ucid = Contact::getUserContactId($cid, $circle['uid']); + if (!$ucid) { throw new HTTPException\NotFoundException('Invalid contact.'); } - $contactIds[] = $cdata['user']; + $contactIds[] = $ucid; } // Return status of deletion diff --git a/src/Model/Contact.php b/src/Model/Contact.php index cecd505bde..bd9273ea17 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -444,12 +444,12 @@ class Contact return false; } - $cdata = self::getPublicAndUserContactID($cid, $uid); - if (empty($cdata['user'])) { + $ucid = self::getUserContactId($cid, $uid); + if (!$ucid) { return false; } - $condition = ['id' => $cdata['user'], 'rel' => [self::FOLLOWER, self::FRIEND]]; + $condition = ['id' => $ucid, 'rel' => [self::FOLLOWER, self::FRIEND]]; if ($strict) { $condition = array_merge($condition, ['pending' => false, 'readonly' => false, 'blocked' => false]); } @@ -495,12 +495,12 @@ class Contact return false; } - $cdata = self::getPublicAndUserContactID($cid, $uid); - if (empty($cdata['user'])) { + $ucid = self::getUserContactId($cid, $uid); + if (!$ucid) { return false; } - $condition = ['id' => $cdata['user'], 'rel' => [self::SHARING, self::FRIEND]]; + $condition = ['id' => $ucid, 'rel' => [self::SHARING, self::FRIEND]]; if ($strict) { $condition = array_merge($condition, ['pending' => false, 'readonly' => false, 'blocked' => false]); } @@ -671,6 +671,32 @@ class Contact return ['public' => $pcid, 'user' => $ucid]; } + /** + * Returns the public contact id of a provided contact id + * + * @param integer $cid + * @param integer $uid + * @return integer + */ + public static function getPublicContactId(int $cid, int $uid): int + { + $contact = DBA::selectFirst('account-user-view', ['pid'], ['id' => $cid, 'uid' => [0, $uid]]); + return $contact['pid'] ?? 0; + } + + /** + * Returns the user contact id of a provided contact id + * + * @param integer $cid + * @param integer $uid + * @return integer + */ + public static function getUserContactId(int $cid, int $uid): int + { + $data = self::getPublicAndUserContactID($cid, $uid); + return $data['user'] ?? 0; + } + /** * Helper function for "getPublicAndUserContactID" * @@ -968,13 +994,13 @@ class Contact } if (in_array($contact['rel'], [self::SHARING, self::FRIEND])) { - $cdata = self::getPublicAndUserContactID($contact['id'], $contact['uid']); - if (!empty($cdata['public'])) { - Worker::add(Worker::PRIORITY_HIGH, 'Contact\Unfollow', $cdata['public'], $contact['uid']); + $pcid = self::getPublicContactId($contact['id'], $contact['uid']); + if ($pcid) { + Worker::add(Worker::PRIORITY_HIGH, 'Contact\Unfollow', $pcid, $contact['uid']); } } - self::removeSharer($contact); + self::removeSharer($contact, false); } /** @@ -998,13 +1024,13 @@ class Contact } if (in_array($contact['rel'], [self::FOLLOWER, self::FRIEND])) { - $cdata = self::getPublicAndUserContactID($contact['id'], $contact['uid']); - if (!empty($cdata['public'])) { - Worker::add(Worker::PRIORITY_HIGH, 'Contact\RevokeFollow', $cdata['public'], $contact['uid']); + $pcid = self::getPublicContactId($contact['id'], $contact['uid']); + if ($pcid) { + Worker::add(Worker::PRIORITY_HIGH, 'Contact\RevokeFollow', $pcid, $contact['uid']); } } - self::removeFollower($contact); + self::removeFollower($contact, false); } /** @@ -1025,14 +1051,14 @@ class Contact throw new \InvalidArgumentException('Unexpected public contact record'); } - $cdata = self::getPublicAndUserContactID($contact['id'], $contact['uid']); + $pcid = self::getPublicContactId($contact['id'], $contact['uid']); - if (in_array($contact['rel'], [self::SHARING, self::FRIEND]) && !empty($cdata['public'])) { - Worker::add(Worker::PRIORITY_HIGH, 'Contact\Unfollow', $cdata['public'], $contact['uid']); + if (in_array($contact['rel'], [self::SHARING, self::FRIEND]) && $pcid) { + Worker::add(Worker::PRIORITY_HIGH, 'Contact\Unfollow', $pcid, $contact['uid']); } - if (in_array($contact['rel'], [self::FOLLOWER, self::FRIEND]) && !empty($cdata['public'])) { - Worker::add(Worker::PRIORITY_HIGH, 'Contact\RevokeFollow', $cdata['public'], $contact['uid']); + if (in_array($contact['rel'], [self::FOLLOWER, self::FRIEND]) && $pcid) { + Worker::add(Worker::PRIORITY_HIGH, 'Contact\RevokeFollow', $pcid, $contact['uid']); } self::remove($contact['id']); @@ -3009,6 +3035,10 @@ class Contact */ public static function getProtocol(string $url, string $network): string { + if (self::isLocal($url)) { + return Protocol::ACTIVITYPUB; + } + if ($network != Protocol::DFRN) { return $network; } @@ -3400,16 +3430,21 @@ class Contact * Update the local relationship when a local user loses a follower * * @param array $contact User-specific contact (uid != 0) array + * @param bool $delete Delete if set, otherwise set relation to "nothing" when contact had been a follower * @return void * @throws HTTPException\InternalServerErrorException * @throws \ImagickException */ - public static function removeFollower(array $contact) + public static function removeFollower(array $contact, bool $delete = true) { if (in_array($contact['rel'] ?? [], [self::FRIEND, self::SHARING])) { self::update(['rel' => self::SHARING], ['id' => $contact['id']]); } elseif (!empty($contact['id'])) { - self::remove($contact['id']); + if ($delete) { + self::remove($contact['id']); + } else { + self::update(['rel' => self::NOTHING, 'pending' => false], ['id' => $contact['id']]); + } } else { DI::logger()->info('Couldn\'t remove follower because of invalid contact array', ['contact' => $contact]); return; @@ -3419,9 +3454,9 @@ class Contact self::clearFollowerFollowingEndpointCache($contact['uid']); - $cdata = self::getPublicAndUserContactID($contact['id'], $contact['uid']); - if (!empty($cdata['public'])) { - DI::notification()->deleteForUserByVerb($contact['uid'], Activity::FOLLOW, ['actor-id' => $cdata['public']]); + $pcid = self::getPublicContactId($contact['id'], $contact['uid']); + if ($pcid) { + DI::notification()->deleteForUserByVerb($contact['uid'], Activity::FOLLOW, ['actor-id' => $pcid]); } } @@ -3430,14 +3465,19 @@ class Contact * Removes the contact for sharing-only protocols (feed and mail). * * @param array $contact User-specific contact (uid != 0) array + * @param bool $delete Delete if set, otherwise set relation to "nothing" when contact had been a sharer * @throws HTTPException\InternalServerErrorException */ - public static function removeSharer(array $contact) + public static function removeSharer(array $contact, bool $delete = true) { self::clearFollowerFollowingEndpointCache($contact['uid']); - if ($contact['rel'] == self::SHARING || in_array($contact['network'], [Protocol::FEED, Protocol::MAIL])) { - self::remove($contact['id']); + if (in_array($contact['rel'], [self::SHARING, self::NOTHING]) || in_array($contact['network'], [Protocol::FEED, Protocol::MAIL])) { + if ($delete) { + self::remove($contact['id']); + } else { + self::update(['rel' => self::NOTHING, 'pending' => false], ['id' => $contact['id']]); + } } else { self::update(['rel' => self::FOLLOWER, 'pending' => false], ['id' => $contact['id']]); } diff --git a/src/Model/Item.php b/src/Model/Item.php index 9910da7f6e..0da392df63 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -556,9 +556,9 @@ class Item } if (!empty($item['causer-id']) && Contact::isSharing($item['causer-id'], $item['uid'], true)) { - $cdata = Contact::getPublicAndUserContactID($item['causer-id'], $item['uid']); - if (!empty($cdata['user'])) { - return $cdata['user']; + $ucid = Contact::getUserContactId($item['causer-id'], $item['uid']); + if ($ucid) { + return $ucid; } } @@ -2632,12 +2632,12 @@ class Item return; } - $cdata = Contact::getPublicAndUserContactID($item['author-id'], $item['uid']); - if (empty($cdata['user']) || ($cdata['user'] != $item['contact-id'])) { + $ucid = Contact::getUserContactId($item['author-id'], $item['uid']); + if (!$ucid || ($ucid != $item['contact-id'])) { return; } - if (!DBA::exists('contact', ['id' => $cdata['user'], 'remote_self' => LocalRelationship::MIRROR_NATIVE_RESHARE])) { + if (!DBA::exists('contact', ['id' => $ucid, 'remote_self' => LocalRelationship::MIRROR_NATIVE_RESHARE])) { return; } diff --git a/src/Model/User.php b/src/Model/User.php index 551699ef6e..5deccdef34 100644 --- a/src/Model/User.php +++ b/src/Model/User.php @@ -412,6 +412,29 @@ class User return 0; } + /** + * Returns the user id of a given contact id + * + * @param int $cid + * + * @return integer user id + * @throws Exception + */ + public static function getIdForContactId(int $cid): int + { + $account = Contact::selectFirstAccountUser(['pid', 'self', 'uid'], ['id' => $cid]); + if (empty($account['pid'])) { + return 0; + } + + if ($account['self']) { + return $account['uid']; + } + + $self = Contact::selectFirstAccountUser(['uid'], ['pid' => $cid, 'self' => true]); + return $self['uid'] ?? 0; + } + /** * Get a user based on its email * diff --git a/src/Module/Api/Mastodon/Accounts/Block.php b/src/Module/Api/Mastodon/Accounts/Block.php index 4c5abc2475..43cf322553 100644 --- a/src/Module/Api/Mastodon/Accounts/Block.php +++ b/src/Module/Api/Mastodon/Accounts/Block.php @@ -43,9 +43,9 @@ class Block extends BaseApi Contact\User::setBlocked($this->parameters['id'], $uid, true); - $cdata = Contact::getPublicAndUserContactID($this->parameters['id'], $uid); - if (!empty($cdata['user'])) { - $contact = Contact::getById($cdata['user']); + $ucid = Contact::getUserContactId($this->parameters['id'], $uid); + if ($ucid) { + $contact = Contact::getById($ucid); if (!empty($contact)) { // Mastodon-expected behavior: relationship is severed on block Contact::terminateFriendship($contact); diff --git a/src/Module/Api/Mastodon/Accounts/Lists.php b/src/Module/Api/Mastodon/Accounts/Lists.php index 6a15386307..3de5960612 100644 --- a/src/Module/Api/Mastodon/Accounts/Lists.php +++ b/src/Module/Api/Mastodon/Accounts/Lists.php @@ -51,9 +51,9 @@ class Lists extends BaseApi $lists = []; - $cdata = Contact::getPublicAndUserContactID($id, $uid); - if (!empty($cdata['user'])) { - $circles = DBA::select('group_member', ['gid'], ['contact-id' => $cdata['user']]); + $ucid = Contact::getUserContactId($id, $uid); + if ($ucid) { + $circles = DBA::select('group_member', ['gid'], ['contact-id' => $ucid]); while ($circle = DBA::fetch($circles)) { $lists[] = DI::mstdnList()->createFromCircleId($circle['gid']); } diff --git a/src/Module/Api/Mastodon/Accounts/Note.php b/src/Module/Api/Mastodon/Accounts/Note.php index a101c1519e..dbb5c6e033 100644 --- a/src/Module/Api/Mastodon/Accounts/Note.php +++ b/src/Module/Api/Mastodon/Accounts/Note.php @@ -45,12 +45,12 @@ class Note extends BaseApi 'comment' => '', ], $request); - $cdata = Contact::getPublicAndUserContactID($this->parameters['id'], $uid); - if (empty($cdata['user'])) { + $ucid = Contact::getUserContactId($this->parameters['id'], $uid); + if (!$ucid) { $this->logAndJsonError(404, $this->errorFactory->RecordNotFound()); } - Contact::update(['info' => $request['comment']], ['id' => $cdata['user']]); + Contact::update(['info' => $request['comment']], ['id' => $ucid]); $this->jsonExit(DI::mstdnRelationship()->createFromContactId($this->parameters['id'], $uid)->toArray()); } diff --git a/src/Module/Api/Mastodon/Accounts/Unfollow.php b/src/Module/Api/Mastodon/Accounts/Unfollow.php index b898f85f18..1c8c5a8921 100644 --- a/src/Module/Api/Mastodon/Accounts/Unfollow.php +++ b/src/Module/Api/Mastodon/Accounts/Unfollow.php @@ -40,12 +40,12 @@ class Unfollow extends BaseApi $this->logAndJsonError(422, $this->errorFactory->UnprocessableEntity()); } - $cdata = Contact::getPublicAndUserContactID($this->parameters['id'], $uid); - if (empty($cdata['user'])) { + $ucid = Contact::getUserContactId($this->parameters['id'], $uid); + if (!$ucid) { $this->logAndJsonError(404, $this->errorFactory->RecordNotFound()); } - $contact = Contact::getById($cdata['user']); + $contact = Contact::getById($ucid); Contact::unfollow($contact); diff --git a/src/Module/Api/Mastodon/FollowRequests.php b/src/Module/Api/Mastodon/FollowRequests.php index 1a6c247a6d..5d9ad2bdfd 100644 --- a/src/Module/Api/Mastodon/FollowRequests.php +++ b/src/Module/Api/Mastodon/FollowRequests.php @@ -21,7 +21,6 @@ namespace Friendica\Module\Api\Mastodon; -use Friendica\Core\System; use Friendica\DI; use Friendica\Model\Contact; use Friendica\Module\BaseApi; @@ -47,12 +46,12 @@ class FollowRequests extends BaseApi $this->checkAllowedScope(self::SCOPE_FOLLOW); $uid = self::getCurrentUserID(); - $cdata = Contact::getPublicAndUserContactID($this->parameters['id'], $uid); - if (empty($cdata['user'])) { + $ucid = Contact::getUserContactId($this->parameters['id'], $uid); + if (!$ucid) { throw new HTTPException\NotFoundException('Contact not found'); } - $introduction = DI::intro()->selectForContact($cdata['user']); + $introduction = DI::intro()->selectForContact($ucid); $contactId = $introduction->cid; diff --git a/src/Module/Api/Mastodon/Timelines/ListTimeline.php b/src/Module/Api/Mastodon/Timelines/ListTimeline.php index ea44ee821d..2dc0b7bb63 100644 --- a/src/Module/Api/Mastodon/Timelines/ListTimeline.php +++ b/src/Module/Api/Mastodon/Timelines/ListTimeline.php @@ -110,8 +110,7 @@ class ListTimeline extends BaseApi private function getStatusesForGroup(int $uid, array $request): array { - $cdata = Contact::getPublicAndUserContactID((int)substr($this->parameters['id'], 6), $uid); - $cid = $cdata['public']; + $cid = Contact::getPublicContactId((int)substr($this->parameters['id'], 6), $uid); $condition = ["(`uid` = ? OR (`uid` = ? AND NOT `global`))", 0, $uid]; diff --git a/src/Module/Api/Twitter/DirectMessages/NewDM.php b/src/Module/Api/Twitter/DirectMessages/NewDM.php index 29226c9a9c..49764304d3 100644 --- a/src/Module/Api/Twitter/DirectMessages/NewDM.php +++ b/src/Module/Api/Twitter/DirectMessages/NewDM.php @@ -81,9 +81,9 @@ class NewDM extends BaseApi } } - $cdata = Contact::getPublicAndUserContactID($cid, $uid); + $ucid = Contact::getUserContactId($cid, $uid); - $id = Mail::send($uid, $cdata['user'], $request['text'], $sub, $replyto); + $id = Mail::send($uid, $ucid, $request['text'], $sub, $replyto); if ($id > -1) { $ret = $this->directMessage->createFromMailId($id, $uid, $this->getRequestValue($request, 'getText', '')); diff --git a/src/Module/Api/Twitter/DirectMessagesEndpoint.php b/src/Module/Api/Twitter/DirectMessagesEndpoint.php index ec4ef65411..b94393eabf 100644 --- a/src/Module/Api/Twitter/DirectMessagesEndpoint.php +++ b/src/Module/Api/Twitter/DirectMessagesEndpoint.php @@ -88,9 +88,9 @@ abstract class DirectMessagesEndpoint extends BaseApi $cid = BaseApi::getContactIDForSearchterm($this->getRequestValue($request, 'screen_name', ''), $this->getRequestValue($request, 'profileurl', ''), $this->getRequestValue($request, 'user_id', 0), 0); if (!empty($cid)) { - $cdata = Contact::getPublicAndUserContactID($cid, $uid); - if (!empty($cdata['user'])) { - $condition = DBA::mergeConditions($condition, ["`contact-id` = ?", $cdata['user']]); + $ucid = Contact::getUserContactId($cid, $uid); + if ($ucid) { + $condition = DBA::mergeConditions($condition, ["`contact-id` = ?", $ucid]); } } diff --git a/src/Module/Api/Twitter/Friendships/Destroy.php b/src/Module/Api/Twitter/Friendships/Destroy.php index 619fbf3386..e02448f765 100644 --- a/src/Module/Api/Twitter/Friendships/Destroy.php +++ b/src/Module/Api/Twitter/Friendships/Destroy.php @@ -71,13 +71,13 @@ class Destroy extends ContactEndpoint } // Get Contact by given id - $cdata = Contact::getPublicAndUserContactID($contact_id, $uid); - if (!empty($cdata['user'])) { + $ucid = Contact::getUserContactId($contact_id, $uid); + if (!$ucid) { Logger::notice(BaseApi::LOG_PREFIX . 'Not following contact', ['module' => 'api', 'action' => 'friendships_destroy']); throw new HTTPException\NotFoundException('Not following Contact'); } - $contact = Contact::getById($cdata['user']); + $contact = Contact::getById($ucid); $user = $this->twitterUser->createFromContactId($contact_id, $uid, true)->toArray(); try { diff --git a/src/Module/Api/Twitter/Friendships/Show.php b/src/Module/Api/Twitter/Friendships/Show.php index 23af79d2ae..304b836097 100644 --- a/src/Module/Api/Twitter/Friendships/Show.php +++ b/src/Module/Api/Twitter/Friendships/Show.php @@ -55,9 +55,9 @@ class Show extends ContactEndpoint $following = false; if ($source_cid == Contact::getPublicIdByUserId($uid)) { - $cdata = Contact::getPublicAndUserContactID($target_cid, $uid); - if (!empty($cdata['user'])) { - $usercontact = Contact::getById($cdata['user'], ['rel']); + $ucid = Contact::getUserContactId($target_cid, $uid); + if ($ucid) { + $usercontact = Contact::getById($ucid, ['rel']); switch ($usercontact['rel'] ?? Contact::NOTHING) { case Contact::FOLLOWER: $follower = true; diff --git a/src/Module/Contact.php b/src/Module/Contact.php index d58fde0623..1455bd5e4f 100644 --- a/src/Module/Contact.php +++ b/src/Module/Contact.php @@ -253,7 +253,7 @@ class Contact extends BaseModule $sql_extra = " AND `archive` AND NOT `blocked` AND NOT `pending`"; break; case 'pending': - $sql_extra = " AND `pending` AND NOT `archive` AND NOT `failed` AND ((`rel` = ?) + $sql_extra = " AND `pending` AND NOT `archive` AND ((`rel` = ?) OR `id` IN (SELECT `contact-id` FROM `intro` WHERE `intro`.`uid` = ? AND NOT `ignore`))"; $sql_values[] = Model\Contact::SHARING; $sql_values[] = DI::userSession()->getLocalUserId(); diff --git a/src/Module/Contact/Follow.php b/src/Module/Contact/Follow.php index ac5f323be9..9aae85f85e 100644 --- a/src/Module/Contact/Follow.php +++ b/src/Module/Contact/Follow.php @@ -215,7 +215,7 @@ class Follow extends BaseModule $this->baseUrl->redirect($returnPath); } elseif (!empty($result['cid'])) { - $this->baseUrl->redirect('contact/' . $result['cid']); + $this->baseUrl->redirect('contact/' . Contact::getPublicContactId($result['cid'], $this->session->getLocalUserId())); } $this->sysMessages->addNotice($this->t('The contact could not be added.')); diff --git a/src/Module/Contact/Profile.php b/src/Module/Contact/Profile.php index 3531c1dafd..66076034b7 100644 --- a/src/Module/Contact/Profile.php +++ b/src/Module/Contact/Profile.php @@ -91,8 +91,8 @@ class Profile extends BaseModule // Backward compatibility: The update still needs a user-specific contact ID // Change to user-contact table check by version 2022.03 - $cdata = Contact::getPublicAndUserContactID($contact_id, $this->session->getLocalUserId()); - if (empty($cdata['user']) || !$this->db->exists('contact', ['id' => $cdata['user'], 'deleted' => false])) { + $ucid = Contact::getUserContactId($contact_id, $this->session->getLocalUserId()); + if (!$ucid || !$this->db->exists('contact', ['id' => $ucid, 'deleted' => false])) { return; } @@ -134,14 +134,14 @@ class Profile extends BaseModule } if (isset($request['channel_frequency'])) { - Contact\User::setChannelFrequency($cdata['user'], $this->session->getLocalUserId(), $request['channel_frequency']); + Contact\User::setChannelFrequency($ucid, $this->session->getLocalUserId(), $request['channel_frequency']); } if (isset($request['channel_only'])) { - Contact\User::setChannelOnly($cdata['user'], $this->session->getLocalUserId(), $request['channel_only']); + Contact\User::setChannelOnly($ucid, $this->session->getLocalUserId(), $request['channel_only']); } - if (!Contact::update($fields, ['id' => $cdata['user'], 'uid' => $this->session->getLocalUserId()])) { + if (!Contact::update($fields, ['id' => $ucid, 'uid' => $this->session->getLocalUserId()])) { $this->systemMessages->addNotice($this->t('Failed to update contact record.')); } } @@ -164,6 +164,18 @@ class Profile extends BaseModule throw new HTTPException\NotFoundException($this->t('Contact not found.')); } + // Fetch the protocol from the user's contact. + $usercontact = Contact::getById($data['user'], ['network', 'protocol']); + if ($this->db->isResult($usercontact)) { + $contact['network'] = $usercontact['network']; + $contact['protocol'] = $usercontact['protocol']; + } + + if (empty($contact['network']) && Contact::isLocal($contact['url']) ) { + $contact['network'] = Protocol::DFRN; + $contact['protocol'] = Protocol::ACTIVITYPUB; + } + // Don't display contacts that are about to be deleted if ($this->db->isResult($contact) && (!empty($contact['deleted']) || !empty($contact['network']) && $contact['network'] == Protocol::PHANTOM)) { throw new HTTPException\NotFoundException($this->t('Contact not found.')); diff --git a/src/Module/Contact/Revoke.php b/src/Module/Contact/Revoke.php index 1f3a85a476..1036e02aa5 100644 --- a/src/Module/Contact/Revoke.php +++ b/src/Module/Contact/Revoke.php @@ -30,6 +30,7 @@ use Friendica\Core\Renderer; use Friendica\Database\Database; use Friendica\DI; use Friendica\Model; +use Friendica\Model\Contact as ModelContact; use Friendica\Module\Contact; use Friendica\Module\Response; use Friendica\Module\Security\Login; @@ -90,7 +91,7 @@ class Revoke extends BaseModule DI::sysmsg()->addNotice($this->t('Follow was successfully revoked.')); - $this->baseUrl->redirect('contact/' . $this->parameters['id']); + $this->baseUrl->redirect('contact/' . ModelContact::getPublicContactId($this->parameters['id'], DI::userSession()->getLocalUserId())); } protected function content(array $request = []): string diff --git a/src/Module/Contact/Unfollow.php b/src/Module/Contact/Unfollow.php index eb855d7fb6..17e475e039 100644 --- a/src/Module/Contact/Unfollow.php +++ b/src/Module/Contact/Unfollow.php @@ -168,7 +168,7 @@ class Unfollow extends \Friendica\BaseModule $this->baseUrl->redirect($base_return_path); } - $return_path = $base_return_path . '/' . $contact['id']; + $return_path = $base_return_path . '/' . Contact::getPublicContactId($contact['id'], $uid); try { Contact::unfollow($contact); diff --git a/src/Util/HTTPSignature.php b/src/Util/HTTPSignature.php index 85a3ed396b..89a746017e 100644 --- a/src/Util/HTTPSignature.php +++ b/src/Util/HTTPSignature.php @@ -36,6 +36,7 @@ use Friendica\Network\HTTPClient\Capability\ICanHandleHttpResponses; use Friendica\Network\HTTPClient\Client\HttpClientAccept; use Friendica\Network\HTTPClient\Client\HttpClientOptions; use Friendica\Network\HTTPClient\Client\HttpClientRequest; +use Friendica\Protocol\ActivityPub\Receiver; /** * Implements HTTP Signatures per draft-cavage-http-signatures-07. @@ -315,6 +316,55 @@ class HTTPSignature return $postResult; } + /** + * Route activities locally + * + * @param array $data + * @param string $target + * @param array $owner + * @return boolean + */ + private static function routeLocal(array $data, string $target, array $owner): bool + { + $uid = self::getUserIdForInbox($target); + if (is_null($uid)) { + return false; + } + + $activity = JsonLD::compact($data); + $type = JsonLD::fetchElement($activity, '@type'); + $trust_source = true; + $object_data = Receiver::prepareObjectData($activity, $uid, true, $trust_source, $owner['url']); + if (empty($object_data)) { + return false; + } + + Logger::debug('Process directly', ['uid' => $uid, 'target' => $target, 'type' => $type]); + return Receiver::routeActivities($object_data, $type, true, true, $uid); + } + + /** + * Fetch the user id for a given inbox + * + * @param string $inbox + * @return integer|null + */ + private static function getUserIdForInbox(string $inbox): ?int + { + $gsid = GServer::getID(DI::baseUrl()); + if (!$gsid) { + return null; + } + if (DBA::exists('apcontact', ['gsid' => $gsid, 'sharedinbox' => $inbox])) { + return 0; + } + $apcontact = DBA::selectFirst('apcontact', ['url'], ['gsid' => $gsid, 'inbox' => $inbox]); + if (empty($apcontact['url'])) { + return null; + } + return User::getIdForURL($apcontact['url']); + } + /** * Transmit given data to a target for a user * @@ -326,6 +376,10 @@ class HTTPSignature */ public static function transmit(array $data, string $target, array $owner): bool { + if (DI::baseUrl()->isLocalUrl($target) && self::routeLocal($data, $target, $owner)) { + return true; + } + $postResult = self::post($data, $target, $owner); $return_code = $postResult->getReturnCode(); diff --git a/src/Worker/Contact/RevokeFollow.php b/src/Worker/Contact/RevokeFollow.php index e97dc1adaf..5a8e397173 100644 --- a/src/Worker/Contact/RevokeFollow.php +++ b/src/Worker/Contact/RevokeFollow.php @@ -42,7 +42,12 @@ class RevokeFollow */ public static function execute(int $cid, int $uid) { - $contact = Contact::getById($cid); + $ucid = Contact::getUserContactId($cid, $uid); + if (!$ucid) { + return; + } + + $contact = Contact::getById($ucid); if (empty($contact)) { return; } @@ -53,7 +58,11 @@ class RevokeFollow } if (!Protocol::revokeFollow($contact, $owner)) { - Worker::defer(self::WORKER_DEFER_LIMIT); + if (!Worker::defer(self::WORKER_DEFER_LIMIT)) { + Contact::removeFollower($contact); + } + } else { + Contact::removeFollower($contact); } } } diff --git a/src/Worker/Contact/Unfollow.php b/src/Worker/Contact/Unfollow.php index 48651b3e5b..0cc95274da 100644 --- a/src/Worker/Contact/Unfollow.php +++ b/src/Worker/Contact/Unfollow.php @@ -41,7 +41,12 @@ class Unfollow */ public static function execute(int $cid, int $uid) { - $contact = Contact::getById($cid); + $ucid = Contact::getUserContactId($cid, $uid); + if (!$ucid) { + return; + } + + $contact = Contact::getById($ucid); if (empty($contact)) { return; } @@ -53,7 +58,11 @@ class Unfollow $result = Protocol::unfollow($contact, $owner); if ($result === false) { - Worker::defer(self::WORKER_DEFER_LIMIT); + if (!Worker::defer(self::WORKER_DEFER_LIMIT)) { + Contact::removeSharer($contact); + } + } else { + Contact::removeSharer($contact); } } }