return false;
}
- if (Contact::isLocal($ret['url'])) {
+ if (self::isLocal($ret['url'])) {
Logger::info('Local contacts are not updated here.');
return true;
}
if (empty($item['event-id'])) {
unset($item['event-id']);
+
+ $ev = Event::fromBBCode($item['body']);
+ if ((!empty($ev['desc']) || !empty($ev['summary'])) && !empty($ev['start'])) {
+ Logger::info('Event found.');
+ $ev['cid'] = $item['contact-id'];
+ $ev['uid'] = $item['uid'];
+ $ev['uri'] = $item['uri'];
+ $ev['edited'] = $item['edited'];
+ $ev['private'] = $item['private'];
+ $ev['guid'] = $item['guid'];
+ $ev['plink'] = $item['plink'];
+ $ev['network'] = $item['network'];
+ $ev['protocol'] = $item['protocol'];
+ $ev['direction'] = $item['direction'];
+ $ev['source'] = $item['source'];
+
+ $event = DBA::selectFirst('event', ['id'], ['uri' => $item['uri'], 'uid' => $item['uid']]);
+ if (DBA::isResult($event)) {
+ $ev['id'] = $event['id'];
+ }
+
+ $item['event-id'] = Event::store($ev);
+ Logger::info('Event was stored', ['id' => $item['event-id']]);
+ }
}
if (empty($item['causer-id'])) {
/**
* Store a public item defined by their URI-ID for the given users
*
- * @param integer $uri_id URI-ID of the given item
- * @param integer $uid The user that will receive the item entry
- * @param array $fields Additional fields to be stored
+ * @param integer $uri_id URI-ID of the given item
+ * @param integer $uid The user that will receive the item entry
+ * @param array $fields Additional fields to be stored
+ * @param integer $source_uid User id of the source post
* @return integer stored item id
*/
- public static function storeForUserByUriId(int $uri_id, int $uid, array $fields = [])
+ public static function storeForUserByUriId(int $uri_id, int $uid, array $fields = [], int $source_uid = 0)
{
- $item = Post::selectFirst(self::ITEM_FIELDLIST, ['uri-id' => $uri_id, 'uid' => 0]);
+ if ($uid == $source_uid) {
+ Logger::warning('target UID must be be equal to the source UID', ['uri-id' => $uri_id, 'uid' => $uid]);
+ return 0;
+ }
+
+ $item = Post::selectFirst(self::ITEM_FIELDLIST, ['uri-id' => $uri_id, 'uid' => $source_uid]);
if (!DBA::isResult($item)) {
+ Logger::warning('Item could not be fetched', ['uri-id' => $uri_id, 'uid' => $source_uid]);
return 0;
}
- if (($item['private'] == self::PRIVATE) || !in_array($item['network'], Protocol::FEDERATED)) {
+ if (($source_uid == 0) && (($item['private'] == self::PRIVATE) || !in_array($item['network'], Protocol::FEDERATED))) {
Logger::notice('Item is private or not from a federated network. It will not be stored for the user.', ['uri-id' => $uri_id, 'uid' => $uid, 'private' => $item['private'], 'network' => $item['network']]);
return 0;
}
$item = array_merge($item, $fields);
+ $is_reshare = ($item['gravity'] == GRAVITY_ACTIVITY) && ($item['verb'] == Activity::ANNOUNCE);
+
+ if ((($item['gravity'] == GRAVITY_PARENT) || $is_reshare) &&
+ DI::pConfig()->get($uid, 'system', 'accept_only_sharer') &&
+ !Contact::isSharingByURL($item['author-link'], $uid) &&
+ !Contact::isSharingByURL($item['owner-link'], $uid)) {
+ Logger::info('Contact is not a follower, thread will not be stored', ['author' => $item['author-link'], 'uid' => $uid]);
+ return 0;
+ }
+
+ if ((($item['gravity'] == GRAVITY_COMMENT) || $is_reshare) && !Post::exists(['uri-id' => $item['thr-parent-id'], 'uid' => $uid])) {
+ // Only do an auto complete with the source uid "0" to prevent privavy problems
+ $result = self::storeForUserByUriId($item['thr-parent-id'], $uid);
+ Logger::info('Fetched thread parent', ['uri-id' => $item['thr-parent-id'], 'uid' => $uid, 'result' => $result]);
+ }
+
$stored = self::storeForUser($item, $uid);
- Logger::info('Public item stored for user', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'stored' => $stored]);
+ Logger::info('Item stored for user', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'source-uid' => $source_uid, 'stored' => $stored]);
return $stored;
}
}
unset($item['id']);
- unset($item['parent']);
unset($item['mention']);
unset($item['starred']);
unset($item['unseen']);
unset($item['psid']);
+ unset($item['pinned']);
+ unset($item['ignored']);
+ unset($item['pubmail']);
+ unset($item['forum_mode']);
+
+ //unset($item['post-reason']);
+ //unset($item['protocol']);
+ unset($item['event-id']);
+ unset($item['hidden']);
+ unset($item['notification-type']);
+ //unset($item['resource-id']);
$item['uid'] = $uid;
$item['origin'] = 0;
$item['contact-id'] = $self['id'];
}
- /// @todo Handling of "event-id"
-
$notify = false;
if ($item['gravity'] == GRAVITY_PARENT) {
$contact = DBA::selectFirst('contact', [], ['id' => $item['contact-id'], 'self' => false]);
$distributed = self::insert($item, $notify, true);
if (!$distributed) {
- Logger::info("Distributed public item wasn't stored", ['uri-id' => $item['uri-id'], 'user' => $uid]);
+ Logger::info("Distributed item wasn't stored", ['uri-id' => $item['uri-id'], 'user' => $uid]);
} else {
- Logger::info('Distributed public item was stored', ['uri-id' => $item['uri-id'], 'user' => $uid, 'stored' => $distributed]);
+ Logger::info('Distributed item was stored', ['uri-id' => $item['uri-id'], 'user' => $uid, 'stored' => $distributed]);
}
return $distributed;
}
{
$shared = BBCode::fetchShareAttributes($item['body']);
if (empty($shared['link'])) {
- return $item['body'];
+ return $item['body'];
}
-
+
$id = self::fetchByLink($shared['link']);
Logger::info('Fetched shared post', ['uri-id' => $item['uri-id'], 'id' => $id, 'author' => $shared['profile'], 'url' => $shared['link'], 'guid' => $shared['guid'], 'callstack' => System::callstack()]);
if (!$id) {
- return $item['body'];
+ return $item['body'];
}
$shared_item = Post::selectFirst(['author-name', 'author-link', 'author-avatar', 'plink', 'created', 'guid', 'title', 'body'], ['id' => $id]);
if (!DBA::isResult($shared_item)) {
- return $item['body'];
+ return $item['body'];
}
$shared_content = BBCode::getShareOpeningTag($shared_item['author-name'], $shared_item['author-link'], $shared_item['author-avatar'], $shared_item['plink'], $shared_item['created'], $shared_item['guid']);
* @param array $fields
* @param array $condition
* @param array $params
+ * @param bool $user_mode true = post-user-view, false = post-view
* @return bool|array
* @throws \Exception
* @see DBA::select
*/
- public static function selectFirst(array $fields = [], array $condition = [], $params = [])
+ public static function selectFirst(array $fields = [], array $condition = [], $params = [], bool $user_mode = true)
{
$params['limit'] = 1;
- $result = self::select($fields, $condition, $params);
+ $result = self::select($fields, $condition, $params, $user_mode);
if (is_bool($result)) {
return $result;
public static function getIdForURL(string $url)
{
// Avoid any database requests when the hostname isn't even part of the url.
- if (!strpos($url, DI::baseUrl()->getHostname())) {
+ if (!Contact::isLocal($url)) {
return 0;
}
$contacts = DBA::select('contact', ['id', 'url', 'network', 'protocol', 'gsid'], $condition);
while ($contact = DBA::fetch($contacts)) {
- if (Contact::isLocal($contact['url'])) {
- continue;
- }
-
if (!self::isAPContact($contact, $networks)) {
continue;
}
$profile = APContact::getByURL($contact['url'], false);
if (!empty($profile)) {
- if (empty($profile['sharedinbox']) || $personal) {
+ if (empty($profile['sharedinbox']) || $personal || Contact::isLocal($contact['url'])) {
$target = $profile['inbox'];
} else {
$target = $profile['sharedinbox'];
if ($item_profile && ($receiver == $item_profile['followers']) && ($uid == $profile_uid)) {
$inboxes = array_merge($inboxes, self::fetchTargetInboxesforUser($uid, $personal, self::isAPPost($last_id)));
} else {
- if (Contact::isLocal($receiver)) {
- continue;
- }
-
$profile = APContact::getByURL($receiver, false);
if (!empty($profile)) {
$contact = Contact::getByURLForUser($receiver, $uid, false, ['id']);
- if (empty($profile['sharedinbox']) || $personal || $blindcopy) {
+ if (empty($profile['sharedinbox']) || $personal || $blindcopy || Contact::isLocal($receiver)) {
$target = $profile['inbox'];
} else {
$target = $profile['sharedinbox'];
exit;
}
}
+
+ /**
+ * Check if the given URL is a local link
+ *
+ * @param string $url
+ * @return bool
+ */
+ public static function isLocalLink(string $url)
+ {
+ return (strpos(Strings::normaliseLink($url), Strings::normaliseLink(DI::baseUrl())) !== false);
+ }
}
use Friendica\Protocol\Diaspora;
use Friendica\Protocol\Email;
use Friendica\Protocol\Activity;
-use Friendica\Util\Strings;
use Friendica\Util\Network;
use Friendica\Core\Worker;
use Friendica\Model\Conversation;
use Friendica\Model\FContact;
use Friendica\Model\Item;
-use Friendica\Model\Post;
use Friendica\Protocol\Relay;
class Delivery
$contact['network'] = Protocol::DIASPORA;
}
- // Ensure that local contacts are delivered locally
- if (Model\Contact::isLocal($contact['url'])) {
- $contact['network'] = Protocol::DFRN;
- }
-
Logger::notice('Delivering', ['cmd' => $cmd, 'uri-id' => $post_uriid, 'followup' => $followup, 'network' => $contact['network']]);
switch ($contact['network']) {
Logger::debug('Notifier entry: ' . $contact["url"] . ' ' . (($target_item['guid'] ?? '') ?: $target_item['id']) . ' entry: ' . $atom);
- // perform local delivery if we are on the same site
- if (Model\Contact::isLocal($contact['url'])) {
- $condition = ['nurl' => Strings::normaliseLink($contact['url']), 'self' => true];
- $target_self = DBA::selectFirst('contact', ['uid'], $condition);
- if (!DBA::isResult($target_self)) {
- return;
- }
- $target_uid = $target_self['uid'];
-
- // Check if the user has got this contact
- $cid = Model\Contact::getIdForURL($owner['url'], $target_uid);
- if (!$cid) {
- // Otherwise there should be a public contact
- $cid = Model\Contact::getIdForURL($owner['url']);
- if (!$cid) {
- return;
- }
- }
-
- $target_importer = DFRN::getImporter($cid, $target_uid);
- if (empty($target_importer)) {
- // This should never happen
- return;
- }
-
- DFRN::import($atom, $target_importer, Conversation::PARCEL_LOCAL_DFRN, Conversation::PUSH);
-
- if (in_array($cmd, [Delivery::POST, Delivery::POKE])) {
- Model\Post\DeliveryData::incrementQueueDone($target_item['uri-id'], Model\Post\DeliveryData::DFRN);
- }
-
- return;
- }
-
$protocol = Model\Post\DeliveryData::DFRN;
// We don't have a relationship with contacts on a public post.
use Friendica\Protocol\OStatus;
use Friendica\Protocol\Relay;
use Friendica\Protocol\Salmon;
+use Friendica\Util\Network;
/*
* The notifier is typically called with:
/// @todo Possibly we should not uplink when the author is the forum itself?
if ((intval($parent['forum_mode']) == 1) && !$top_level && ($cmd !== Delivery::UPLINK)
- && ($target_item['verb'] != Activity::ANNOUNCE)) {
+ && ($target_item['verb'] != Activity::ANNOUNCE)) {
Worker::add($a->queue['priority'], 'Notifier', Delivery::UPLINK, $post_uriid, $sender_uid);
}
/**
* Deliver the message to the contacts
*
- * @param string $cmd
- * @param int $post_uriid
+ * @param string $cmd
+ * @param int $post_uriid
* @param int $sender_uid
- * @param array $target_item
- * @param array $thr_parent
- * @param array $owner
- * @param bool $batch_delivery
- * @param array $contacts
- * @param array $ap_contacts
- * @param array $conversants
- * @return int
- * @throws InternalServerErrorException
- * @throws Exception
+ * @param array $target_item
+ * @param array $thr_parent
+ * @param array $owner
+ * @param bool $batch_delivery
+ * @param array $contacts
+ * @param array $ap_contacts
+ * @param array $conversants
+ * @return int
+ * @throws InternalServerErrorException
+ * @throws Exception
*/
private static function delivery(string $cmd, int $post_uriid, int $sender_uid, array $target_item, array $thr_parent, array $owner, bool $batch_delivery, bool $in_batch, array $contacts, array $ap_contacts, array $conversants = [])
{
- $a = DI::app();
+ $a = DI::app();
$delivery_queue_count = 0;
foreach ($contacts as $contact) {
- // Ensure that local contacts are delivered via DFRN
- if (Contact::isLocal($contact['url'])) {
- $contact['network'] = Protocol::DFRN;
+ // Direct delivery of local contacts
+ if ($target_uid = User::getIdForURL($contact['url'])) {
+ Logger::info('Direct delivery', ['uri-id' => $target_item['uri-id'], 'target' => $target_uid]);
+ $fields = ['protocol' => Conversation::PARCEL_LOCAL_DFRN, 'direction' => Conversation::PUSH];
+ Item::storeForUserByUriId($target_item['uri-id'], $target_uid, $fields, $target_item['uid']);
+ continue;
}
// Deletions are always sent via DFRN as well.
/**
* Deliver the message via OStatus
*
- * @param int $target_id
- * @param array $target_item
- * @param array $owner
- * @param array $url_recipients
- * @param bool $public_message
- * @param bool $push_notify
- * @return int
- * @throws InternalServerErrorException
- * @throws Exception
+ * @param int $target_id
+ * @param array $target_item
+ * @param array $owner
+ * @param array $url_recipients
+ * @param bool $public_message
+ * @param bool $push_notify
+ * @return int
+ * @throws InternalServerErrorException
+ * @throws Exception
*/
private static function deliverOStatus(int $target_id, array $target_item, array $owner, array $url_recipients, bool $public_message, bool $push_notify)
{
- $a = DI::app();
+ $a = DI::app();
$delivery_queue_count = 0;
$url_recipients = array_filter($url_recipients);
foreach ($inboxes as $inbox => $receivers) {
$contacts = array_merge($contacts, $receivers);
+ if ((count($receivers) == 1) && Network::isLocalLink($inbox)) {
+ $contact = Contact::getById($receivers[0], ['url']);
+ if ($target_uid = User::getIdForURL($contact['url'])) {
+ $fields = ['protocol' => Conversation::PARCEL_LOCAL_DFRN, 'direction' => Conversation::PUSH];
+ Item::storeForUserByUriId($target_item['uri-id'], $target_uid, $fields, $target_item['uid']);
+ Logger::info('Delivered locally', ['cmd' => $cmd, 'id' => $target_item['id'], 'inbox' => $inbox]);
+ continue;
+ }
+ }
+
Logger::info('Delivery via ActivityPub', ['cmd' => $cmd, 'id' => $target_item['id'], 'inbox' => $inbox]);
if (Worker::add(['priority' => $priority, 'created' => $created, 'dont_fork' => true],