diff --git a/README.md b/README.md index 193622506..e99776f55 100644 --- a/README.md +++ b/README.md @@ -28,9 +28,6 @@ https://codeberg.org/node9/streams.docker A Yunohost install is present in https://apps.yunohost.org/app/streams -Managed hosting is available from -https://knthost.com/streams - Language translation instructions are provided in https://codeberg.org/streams/streams/src/branch/release/util/README diff --git a/src/Daemon/Notifier.php b/src/Daemon/Notifier.php index 2d8ae4995..1b50ccd48 100644 --- a/src/Daemon/Notifier.php +++ b/src/Daemon/Notifier.php @@ -625,7 +625,6 @@ class Notifier implements DaemonInterface } logger('notifier: recipients (may be delivered to more if public): ' . print_r($recip_list, true), LOGGER_DEBUG); - // Now we have collected recipients (except for external mentions, @FIXME) // Let's reduce this to a set of hubs; checking that the site is not dead. @@ -661,7 +660,6 @@ class Notifier implements DaemonInterface } } } - if (! $hubs) { logger('notifier: no hubs', LOGGER_NORMAL, LOG_NOTICE); return; @@ -865,9 +863,7 @@ class Notifier implements DaemonInterface if ($parentItem['replyto']) { $ptr = unserialise($parentItem['replyto']); if (is_string($ptr)) { - if (ActivityStreams::is_url($ptr)) { - $audience[] = $ptr; - } + $audience[] = $ptr; } elseif (is_array($ptr)) { foreach ($ptr as $rto) { if (is_string($rto) && ActivityStreams::is_url($rto)) { diff --git a/src/Lib/Activity.php b/src/Lib/Activity.php index 34ebbfeec..7a6942fc8 100644 --- a/src/Lib/Activity.php +++ b/src/Lib/Activity.php @@ -2407,9 +2407,10 @@ class Activity $ap_hubloc = null; $hublocs = self::get_actor_hublocs($url); + if ($hublocs) { foreach ($hublocs as $hub) { - if ($hub['hubloc_network'] === 'activitypub') { + if (in_array($hub['hubloc_network'], ['activitypub', 'apnomadic'])) { $ap_hubloc = $hub; } if (in_array($hub['hubloc_network'],['zot6','nomad'])) { @@ -2847,6 +2848,15 @@ class Activity return; } + $hublocs = self::get_actor_hublocs($portableId); + if ($hublocs) { + foreach ($hublocs as $hubloc) { + if (in_array($hubloc['hubloc_network'], ['zot6', 'nomad'])) { + return; + } + } + } + $count = 0; foreach ($gateways as $gateway) { @@ -4650,6 +4660,20 @@ class Activity public static function find_best_identity($xchan) { + $actorId = new ActorId($xchan); + if ($actorId->getType() === ActorId::ACTORID_TYPE_DIDKEY) { + $query = q("select * from xchan where xchan_epubkey = '%s'", + dbesc(str_replace('did:key:', '', $actorId->getId())) + ); + if ($query) { + foreach ($query as $test) { + if (in_array($test['xchan_network'], ['zot6', 'nomad'])) { + return $test['xchan_hash']; + } + } + } + } + $r = q( "select hubloc_hash, hubloc_network from hubloc where hubloc_id_url = '%s' and hubloc_deleted = 0 order by hubloc_id desc", dbesc($xchan) @@ -4813,10 +4837,10 @@ class Activity if (isset($a['image'])) { if (self::media_not_in_body($a['image'], $item['body']) && self::media_not_in_body($a['href'], $item['body'])) { if (isset($a['name']) && $a['name']) { - $alt = htmlspecialchars($a['name'], ENT_QUOTES, 'UTF-8', false); // Escape brackets by converting to unicode full-width bracket since regular brackets will confuse multicode/bbcode parsing. // The full width bracket isn't quite as alien looking as most other unicode bracket replacements. - $alt = str_replace(['[', ']', '"'], ['[', ']', '\"'], $alt); + // Do the same for double-quotes; which may present issues with the HTML purifier and when rendered as HTML attributes. + $alt = str_replace(['[', ']', '\\"', '\"', '"', '"'], ['[', ']', '"', '"', '"', '"'], $a['name']); $alt = htmlspecialchars($alt, ENT_QUOTES, 'UTF-8', false); $item['body'] .= "\n\n" . '[img alt="' . $alt . '"]' . $a['href'] . '[/img]'; } else { $item['body'] .= "\n\n" . '[img]' . $a['href'] . '[/img]'; @@ -4826,10 +4850,10 @@ class Activity } elseif (self::media_not_in_body($a['href'], $item['body'])) { if (isset($a['name']) && $a['name']) { - $alt = htmlspecialchars($a['name'], ENT_QUOTES, 'UTF-8', false); // Escape brackets by converting to unicode full-width bracket since regular brackets will confuse multicode/bbcode parsing. // The full width bracket isn't quite as alien looking as most other unicode bracket replacements. - $alt = str_replace(['[', ']', '"'], ['[', ']', '\"'], $alt); + // Do the same for double-quotes; which may present issues with the HTML purifier and when rendered as HTML attributes. + $alt = str_replace(['[', ']', '\\"', '\"', '"', '"'], ['[', ']', '"', '"', '"', '"'], $a['name']); $alt = htmlspecialchars($alt, ENT_QUOTES, 'UTF-8', false); $item['body'] .= "\n\n" . '[img alt="' . $alt . '"]' . $a['href'] . '[/img]'; } else { $item['body'] .= "\n\n" . '[img]' . $a['href'] . '[/img]'; @@ -4839,8 +4863,7 @@ class Activity if (array_key_exists('type', $a) && stripos($a['type'], 'video') !== false) { if (self::media_not_in_body($a['href'], $item['body'])) { if (isset($a['name']) && $a['name']) { - $alt = htmlspecialchars($a['name'], ENT_QUOTES, 'UTF-8', false); - $alt = str_replace(['[', ']'], ['[', ']'], $alt); + $alt = str_replace(['[', ']', '\\"', '\"', '"', '"'], ['[', ']', '"', '"', '"', '"'], $a['name']); $alt = htmlspecialchars($alt, ENT_QUOTES, 'UTF-8', false); $item['body'] .= "\n\n" . '[video title="' . $alt . '"]' . $a['href'] . '[/video]'; } else { $item['body'] .= "\n\n" . '[video]' . $a['href'] . '[/video]'; @@ -4852,7 +4875,7 @@ class Activity $item['body'] .= "\n\n" . '[audio]' . $a['href'] . '[/audio]'; } } - if (!isset($a['type']) && ActivityStreams::is_url($a['href']) && !strpos($item['body'], $a['href'])) { + if ((!isset($a['type']) || $a['type'] === 'Link') && ActivityStreams::is_url($a['href']) && !strpos($item['body'], $a['href'])) { $li = Url::get(z_root() . '/linkinfo?binurl=' . bin2hex($a['href'])); if ($li['success'] && $li['body']) { $item['body'] .= "\n" . $li['body']; @@ -5160,6 +5183,26 @@ class Activity } } + // Edge case. Communications from an apnomadic URL, but they also have a zot6 or nomad hubloc record + // with a traditional hubloc_id_url. Pull out the xchan_hash of the zot6/nomad identity and use it + // instead of the URL that was passed. + + $actorId = new ActorId($url); + if ($actorId->getType() === ActorId::ACTORID_TYPE_DIDKEY) { + $ekey = str_replace('did:key:', '', $actorId->getId()); + $query = q("select * from xchan where epubkey = '%s'", + dbesc($ekey) + ); + if ($query) { + foreach ($query as $test) { + if (in_array($test['xchan_network'], ['nomad', 'zot6'])) { + $url = $test['xchan_hash']; + break; + } + } + } + } + return match (trim($options_arr[0])) { 'activitypub' => q( "select * from hubloc left join xchan on hubloc_hash = xchan_hash where hubloc_hash = '%s' $sql_options order by hubloc_id DESC ", diff --git a/src/Lib/ActivityStreams.php b/src/Lib/ActivityStreams.php index 81b17f5d1..0b796badd 100644 --- a/src/Lib/ActivityStreams.php +++ b/src/Lib/ActivityStreams.php @@ -117,6 +117,12 @@ class ActivityStreams if (!$this->replyto) { $this->replyto = $this->get_property_obj('replyTo'); } + if ($this->replyto) { + $actorId = new ActorId($this->replyto); + if ($actorId->getType() !== ActorId::ACTORID_TYPE_URL) { + $this->replyto = Activity::find_best_identity($actorId->getId()); + } + } $this->edsig = $this->get_compound_property('proof'); if ($this->edsig) { diff --git a/src/Lib/Channel.php b/src/Lib/Channel.php index 4ec7a6dc7..7b6997faf 100644 --- a/src/Lib/Channel.php +++ b/src/Lib/Channel.php @@ -2084,7 +2084,7 @@ class Channel { $pubkey = (new Multibase())->publicKey($channel['channel_epubkey']); $nomadic = PConfig::Get($channel['channel_id'], 'system', 'nomadicAP'); - if (!str_contains($id, '/.well-known/apgateway/')) { + if (!str_contains($id, '/.well-known/apgateway/') && !str_starts_with($id,'ap://')) { $nomadic = false; } return (($nomadic) ? Channel::getDid($channel) : Channel::url($channel) . '#' . $pubkey); diff --git a/src/Module/Inbox.php b/src/Module/Inbox.php index 0ac1f1113..8a6d21df3 100644 --- a/src/Module/Inbox.php +++ b/src/Module/Inbox.php @@ -6,6 +6,7 @@ namespace Code\Module; use App; +use Code\Lib\ActorId; use Code\Lib\Time; use Code\Web\HTTPSig; use Code\Lib\ActivityStreams; @@ -144,7 +145,14 @@ class Inbox extends Controller // if the sender has the ability to send messages over zot/nomad, ignore messages sent via activitypub // as observer aware features and client side markup will be unavailable - $test = Activity::get_actor_hublocs($hsig['portable_id'], 'all,not_deleted'); + if (str_starts_with($hsig['portable_id'], 'did:key:')) { + $test = q("select xchan_network as hubloc_network from xchan where xchan_epubkey = '%s'", + dbesc(str_replace('did:key:', '', $hsig['portable_id'])) + ); + } + else { + $test = Activity::get_actor_hublocs($hsig['portable_id'], 'all,not_deleted'); + } if ($test) { foreach ($test as $t) { if (in_array($t['hubloc_network'], ['zot6', 'nomad'])) { @@ -261,7 +269,7 @@ class Inbox extends Controller $collections = Activity::get_actor_collections($observer_hash); - if (is_array($collections) && in_array($collections['followers'], $AS->recips) + if ((is_array($collections) && in_array($collections['followers'], $AS->recips)) || in_array(ACTIVITY_PUBLIC_INBOX, $AS->recips) || in_array('Public', $AS->recips) || in_array('as:Public', $AS->recips)) { @@ -280,14 +288,23 @@ class Inbox extends Controller // deliver to anybody at this site directly addressed $channel_addr = ''; foreach($AS->recips as $recip) { - if (str_starts_with($recip, z_root())) { - $channel_addr .= '\'' . dbesc(basename($recip)) . '\','; + if (!str_starts_with($recip, z_root())) { + continue; + } + $actorId = new ActorId($recip); + if ($actorId->getType() === ActorId::ACTORID_TYPE_URL) { + $query = q("SELECT * from channel left join xchan on channel_hash = xchan_hash where xchan_url = '%s' and channel_removed = 0", + dbesc($recip) + ); + } + else { + $query = q("select * from channel left join xchan on channel_hash = xchan_hash where xchan_epubkey = '%s' and channel_removed = 0", + dbesc(str_replace('did:key:', '', $actorId->getId())) + ); + } + if ($query) { + $channels[] = array_shift($query); } - } - if ($channel_addr) { - $channel_addr = rtrim($channel_addr, ','); - $channels = dbq("SELECT * FROM channel - WHERE channel_address IN ($channel_addr) AND channel_removed = 0"); } } diff --git a/version.php b/version.php index 09bf15eff..e2f4fd87c 100644 --- a/version.php +++ b/version.php @@ -1,2 +1,2 @@ K&T Host - (www.knthost.com)' +version: 'Version: 0.26' +credits: 'Maintained by community' author: 'K&T Host' maintainer: - - 'K&T Host' + - 'Streams community'