From b255b26a3bc243b0a1affc12596715a23cabcd3c Mon Sep 17 00:00:00 2001 From: nobody Date: Thu, 15 Apr 2021 13:43:46 -0700 Subject: [PATCH 01/13] disable floc by default --- boot.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/boot.php b/boot.php index 46914b41f..aa18a44e5 100755 --- a/boot.php +++ b/boot.php @@ -2323,6 +2323,9 @@ function construct_page() { header("X-Content-Type-Options: nosniff"); } + // Create a preference for this if you wish. But make sure the default is privacy-respecting. + header("Permissions-Policy: interest-cohort=()"); + if (isset(App::$config['system']['public_key_pins']) && App::$config['system']['public_key_pins']) { header("Public-Key-Pins: " . App::$config['system']['public_key_pins']); } From eb394e6b4238fa740b2c7c16cfc5444f91eb9519 Mon Sep 17 00:00:00 2001 From: nobody Date: Thu, 15 Apr 2021 16:12:13 -0700 Subject: [PATCH 02/13] make FLoC participation configurable - opt-out by default --- boot.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/boot.php b/boot.php index aa18a44e5..e5d457a52 100755 --- a/boot.php +++ b/boot.php @@ -2323,8 +2323,14 @@ function construct_page() { header("X-Content-Type-Options: nosniff"); } - // Create a preference for this if you wish. But make sure the default is privacy-respecting. - header("Permissions-Policy: interest-cohort=()"); + + if (isset(App::$config['system']['perm_policy_header']) && App::$config['system']['perm_policy_header']) { + header("Permissions-Policy: " . App::$config['system']['perm_policy_header']); + } + else { + // opt-out this site from federated browser surveillance + header("Permissions-Policy: interest-cohort=()"); + } if (isset(App::$config['system']['public_key_pins']) && App::$config['system']['public_key_pins']) { header("Public-Key-Pins: " . App::$config['system']['public_key_pins']); From 577d63a5ed7af384764954338874aa6fcb03ba88 Mon Sep 17 00:00:00 2001 From: nobody Date: Thu, 15 Apr 2021 17:07:20 -0700 Subject: [PATCH 03/13] prettify E2EE content --- include/bbcode.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/include/bbcode.php b/include/bbcode.php index 74614de88..8839c2f55 100644 --- a/include/bbcode.php +++ b/include/bbcode.php @@ -282,7 +282,7 @@ function bb_parse_crypt($match) { $onclick = 'onclick="' . $f . '(\'' . $algorithm . '\',\'' . $hint . '\',\'' . $match[2] . '\',\'#' . $x . '\');"'; $label = t('Encrypted content'); - $Text = '
' . $label . '

'; + $Text = '
' . $label . '


' . bb_parse_b64_crypt($match); return $Text; } @@ -298,9 +298,11 @@ function bb_parse_b64_crypt($match) { if(empty($match[2])) return; - $r .= '----- ENCRYPTED CONTENT -----' . PHP_EOL; - $r .= $match[2] . PHP_EOL; - $r .= '----- END ENCRYPTED CONTENT -----'; + $r .= '----- ENCRYPTED CONTENT -----' . "\n"; + $r .= base64_encode($match[1]) . "." . $match[2] . "\n"; + $r .= '----- END ENCRYPTED CONTENT -----' . "\n"; + + $r = '' . str_replace("\n",'
',wordwrap($r,75,"\n",true)) . '
'; return $r; From e9a0a682c4a1d50922230b0d02dc814c362554cd Mon Sep 17 00:00:00 2001 From: nobody Date: Fri, 16 Apr 2021 04:34:35 -0700 Subject: [PATCH 04/13] only one entry showing on notifications page --- Zotlabs/Module/Notifications.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Zotlabs/Module/Notifications.php b/Zotlabs/Module/Notifications.php index 55268e0a2..0ef244804 100644 --- a/Zotlabs/Module/Notifications.php +++ b/Zotlabs/Module/Notifications.php @@ -16,23 +16,25 @@ class Notifications extends \Zotlabs\Web\Controller { $o = ''; - $r = q("select count(*) as total from notify where uid = %d and seen = 0", + $n = q("select count(*) as total from notify where uid = %d and seen = 0", intval(local_channel()) ); - if($r && intval($r[0]['total']) > 49) { + if($n && intval($n[0]['total']) > 49) { $r = q("select * from notify where uid = %d and seen = 0 order by created desc limit 50", intval(local_channel()) ); - } else { + } + else { $r1 = q("select * from notify where uid = %d and seen = 0 order by created desc limit 50", intval(local_channel()) ); + $r2 = q("select * from notify where uid = %d and seen = 1 order by created desc limit %d", intval(local_channel()), - intval(50 - intval($r[0]['total'])) + intval(50 - intval($n[0]['total'])) ); $r = array_merge($r1,$r2); } @@ -41,7 +43,7 @@ class Notifications extends \Zotlabs\Web\Controller { $notifications_available = 1; foreach ($r as $rr) { $x = strip_tags(bbcode($rr['msg'])); - $notif_content = replace_macros(get_markup_template('notify.tpl'),array( + $notif_content .= replace_macros(get_markup_template('notify.tpl'),array( '$item_link' => z_root().'/notify/view/'. $rr['id'], '$item_image' => $rr['photo'], '$item_text' => $x, From 93ab70787c76ff904e3f85e61649bedfb5f82612 Mon Sep 17 00:00:00 2001 From: Papa Dragon Date: Fri, 16 Apr 2021 16:48:58 +0200 Subject: [PATCH 05/13] Added a few lines in the home install setup script for Roadhouse --- .homeinstall/zotserver-setup.sh | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.homeinstall/zotserver-setup.sh b/.homeinstall/zotserver-setup.sh index 797ac354e..0321c14de 100755 --- a/.homeinstall/zotserver-setup.sh +++ b/.homeinstall/zotserver-setup.sh @@ -9,6 +9,7 @@ # - misty : https://zotlabs.com/misty/ # - osada : https://codeberg.org/zot/osada # - redmatrix : https://codeberg.org/zot/redmatrix +# - roadhouse : https://codeberg.org/zot/roadhouse # under Debian Linux "Buster" # # 1) Copy the file "zotserver-config.txt.template" to "zotserver-config.txt" @@ -532,8 +533,11 @@ function zotserver_name { elif git remote -v | grep -i "origin.*redmatrix.*" then zotserver=redmatrix + elif git remote -v | grep -i "origin.*roadhouse.*" + then + zotserver=roadhouse else - die "neither redmatrix, osada, misty, zap nor hubzilla repository > did not install redmatrix/osada/misty/zap/hubzilla" + die "neither roadhouse, redmatrix, osada, misty, zap nor hubzilla repository > did not install roadhouse/redmatrix/osada/misty/zap/hubzilla" fi } @@ -560,8 +564,12 @@ function install_zotserver { then print_info "redmatrix" util/add_addon_repo https://codeberg.org/zot/redmatrix-addons.git raddons + elif [ $zotserver = "roadhouse" ] + then + print_info "roadhouse" + util/add_addon_repo https://codeberg.org/zot/roadhouse-addons.git rhaddons else - die "neither redmatrix, osada, misty, zap nor hubzilla repository > did not install addons or redmatrix/osada/misty/zap/hubzilla" + die "neither roadhouse, redmatrix, osada, misty, zap nor hubzilla repository > did not install addons or roadhouse/redmatrix/osada/misty/zap/hubzilla" fi mkdir -p "cache/smarty3" mkdir -p "store" From 8bb42abfd6e9b8bafba19c33876f65fbf0fd4f71 Mon Sep 17 00:00:00 2001 From: nobody Date: Fri, 16 Apr 2021 15:44:20 -0700 Subject: [PATCH 06/13] update federation.md --- FEDERATION.md | 15 +++++++++++---- Zotlabs/Lib/Activity.php | 7 +++++-- include/api.php | 3 +-- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/FEDERATION.md b/FEDERATION.md index 88690b4cb..2be54fa76 100644 --- a/FEDERATION.md +++ b/FEDERATION.md @@ -61,11 +61,11 @@ The Zot permission system has years of historical use and is different than and Delivery model -This project uses the relay system pioneered by projects such as Friendica, Diaspora, and Hubzilla which attempts to keep entire conversations intact and keeps the conversation initiator in control of the privacy distribution. This is not guaranteed under ActivityPub where conversation members can cc: others who were not in the initial privacy group. We encourage projects to not allow additional recipients or not include their own followers in followups. Followups SHOULD have one recipient - the conversation owner or originator, and are relayed by the owner to the other conversation members. This normally requires the use of LD-Signatures but may also be accessible through authenticated fetch of the activity using HTTP signatures. +This project uses the relay system pioneered by projects such as Friendica, Diaspora, and Hubzilla which attempts to keep entire conversations intact and keeps the conversation initiator in control of the privacy distribution. This is not guaranteed under ActivityPub where conversation members can cc: others who were not in the initial privacy group. We encourage projects to not allow additional recipients or not include their own followers in followups. Followups SHOULD have one recipient - the conversation owner or originator, and are relayed by the owner to the other conversation members. This normally requires the use of LD-Signatures but may also be accessible through authenticated fetch of the activity using HTTP signatures. Content -Content may be rich multi-media and renders nicely as HTML. Bbcode is used internally due to its ease of purification while still providing rich multi-media support. Content is not obviously length-limited and authors MAY use up to the storage maximum of 24MB. In practice bbcode conversion limits the effective length to around 200KB and the default "maximum length of imported content" from other sites is 200KB. This can be changed on a per-site basis but this is rare. A Note may contain one or more images or links. The images are also added as an attachment for the benefit of Mastodon, but remain in the HTML source. When importing content from other sites, if the content contains an image attachment, the content is scanned to see if a link (a) or (img) tag containing that image is already present in the HTML. If not, an image tag is added inline to the end of the incoming content. Multiple images are supported using this mechanism. +Content may be rich multi-media and renders nicely as HTML. Multicode is used and stored internally. Multicode is html, markdown, and bbcode. The multicode interpreter allows you to switch between these or combine them at will. Content is not obviously length-limited and authors MAY use up to the storage maximum of 24MB. In practice markup conversion limits the effective length to around 200KB and the default "maximum length of imported content" from other sites is 200KB. This can be changed on a per-site basis but this is rare. A Note may contain one or more images, other media, and links. HTML purification primarily removes javascript and iframes and allows most legitimate tags and CSS styles through. Inline images are also added as attachments for the benefit of Mastodon (which strips most HTML), but remain in the HTML source. When importing content from other sites, if the content contains an image attachment, the content is scanned to see if a link (a) or (img) tag containing that image is already present in the HTML. If not, an image tag is added inline to the end of the incoming content. Multiple images are supported using this mechanism. Mastodon 'summary' does not invoke any special handling so 'summary' can be used for its intended purpose as a content summary. Mastodon 'sensitive' is honoured and results in invoking whatever mechanisms the user has selected to deal with this type of content. By default images are obscured and are 'click to view'. Sensitive text is not treated specially, but may be obscured using the NSFW plugin or filtered per connection based on string match, tags, patterns, languages, or other criteria. @@ -75,7 +75,9 @@ Edited posts and comments are sent with Update/Note and an 'updated' timestamp a Announce -Announce and relay activities are supported on the inbound side but are not generated. Instead a new message is generated with an embedded rendering of the shared content as the message content. This message may (should) contain additional commentary in order to comply with the Fair Use provisions of copyright law. +Announce and relay activities use two mechanisms. As wll as the Announce activity, a new message is generated with an embedded rendering of the shared content as the message content. This message may (should) contain additional commentary in order to comply with the Fair Use provisions of copyright law. The other reason is our use of comment permissions. Comments to Announce activities are sent to the author (who typically accepts comments only from connections). Comment to embedded forwards are sent to the sender. This difference in behaviour allows groups to work correctly in the presence of comment permissions. + +Discussion (2021-04-17): In the email world this type of conflict is resolved by the use of the reply-to header (e.g. in this case reply to the group rather than to the author) as well as the concept of a 'sender' which is different than 'from' (the author). We will soon be modelling the first one in ActivityPub with the use of 'replyTo'. If you see 'replyTo' in an activity it indicates that replies SHOULD go to that address rather than the author's inbox. We will implement this first and come up with a proposal for 'sender' if this gets any traction. If enough projects support these constructs we can eliminate the multiple Announce mechanisms and in the process make ActivityPub much more versatile when it comes to organisational and group communications. Our primary use case for 'sender' is to provide an ActivityPub origin to a message that was imported from another system entirely (such as Diaspora or from RSS source). In this case we would set 'attributedTo' to the remote identity that authored the content, and 'sender' to the person that posted it in ActivityPub. Mastodon Custom Emojis @@ -84,5 +86,10 @@ Mastodon Custom Emojis are only supported for post content. Display names and me Mentions and private mentions -By default the mention format is '@Display Name', but other options are available, such as '@username' and both '@Display Name (username)'. Mentions may also contain an exclamation character, for example '@!Display Name'. This indicates a Direct or private message to 'Display Name' and post addressing/privacy are altered accordingly. +By default the mention format is '@Display Name', but other options are available, such as '@username' and both '@Display Name (username)'. Mentions may also contain an exclamation character, for example '@!Display Name'. This indicates a Direct or private message to 'Display Name' and post addressing/privacy are altered accordingly. All incoming and outgoing content to your stream is re-written to display mentions in your chosen style. This mechanism may need to be adopted by other projects to provide consistency as there are strong feelings on both sides of the debate about which form should be prevalent in the fediverse. + + +(Mastodon) Comment Notifications + +Our projects send comment notifications if somebody replies to a post you either created or have previously interacted with in some way. They also are able to send a "mention" notification if you were mentioned in the post. This differs from Mastodon which does not appear to support comment notifications at all and only provides mention notifications. For this reason, Mastodon users don't typically get notified unless the author they are replying to is mentioned in the post. We provide this mention in the 'tag' field of the generated Activity, but normally don't include it in the message body, as we don't actually require mentions that were created for the sole purpose of triggering a notification. diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index f3c6cccce..e7f89866e 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -1483,9 +1483,10 @@ class Activity { $ret['following'] = z_root() . '/following/' . $c['channel_address']; $ret['endpoints'] = [ - 'sharedInbox' => z_root() . '/inbox', + 'sharedInbox' => z_root() . '/inbox', + 'oauthRegistrationEndpoint' => z_root() . '/api/client/register', 'oauthAuthorizationEndpoint' => z_root() . '/authorize', - 'oauthTokenEndpoint' => z_root() . '/token' + 'oauthTokenEndpoint' => z_root() . '/token' ]; $ret['discoverable'] = ((1 - intval($p['xchan_hidden'])) ? true : false); @@ -3790,8 +3791,10 @@ class Activity { 'toot' => 'http://joinmastodon.org/ns#', 'ostatus' => 'http://ostatus.org#', 'schema' => 'http://schema.org#', + 'litepub' => 'http://litepub.social/ns#', 'conversation' => 'ostatus:conversation', 'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers', + 'oauthRegistrationEndpoint' => 'litepub:oauthRegistrationEndpoint', 'sensitive' => 'as:sensitive', 'movedTo' => 'as:movedTo', 'copiedTo' => 'as:copiedTo', diff --git a/include/api.php b/include/api.php index 057f2f63e..ec95967db 100644 --- a/include/api.php +++ b/include/api.php @@ -196,8 +196,7 @@ require_once('include/api_zot.php'); $secret = random_string(16); $name = trim(escape_tags($_REQUEST['client_name'])); if (! $name) { - // json_return_and_die($ret); - $name = random_string(8); + json_return_and_die($ret); } if (is_array($_REQUEST['redirect_uris'])) { $redirect = trim($_REQUEST['redirect_uris'][0]); From 2bab9d7139f574b5f11aeb2d0a6c82beb340ca75 Mon Sep 17 00:00:00 2001 From: nobody Date: Fri, 16 Apr 2021 16:35:57 -0700 Subject: [PATCH 07/13] brainstorm in public --- FEDERATION.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/FEDERATION.md b/FEDERATION.md index 2be54fa76..ab2f18c34 100644 --- a/FEDERATION.md +++ b/FEDERATION.md @@ -93,3 +93,8 @@ By default the mention format is '@Display Name', but other options are availabl Our projects send comment notifications if somebody replies to a post you either created or have previously interacted with in some way. They also are able to send a "mention" notification if you were mentioned in the post. This differs from Mastodon which does not appear to support comment notifications at all and only provides mention notifications. For this reason, Mastodon users don't typically get notified unless the author they are replying to is mentioned in the post. We provide this mention in the 'tag' field of the generated Activity, but normally don't include it in the message body, as we don't actually require mentions that were created for the sole purpose of triggering a notification. +Conversation Completion + +(2021-04-17) It's easy to fetch missing pieces of a conversation going "upstream", but there is no agreed-on method to fetch a complete conversation from the viewpoint of the origin actor. We intend to use 'context' to return a collection containing the entire converstion and would like to see participation by other projects to provide the most complete conversations as practical as seen by the initator. This is expected in Q2 2021. Use of 'replies' for this purpose will be provided to ActivityPub viewers as well but that collection does not contain the origin post. It still requires traversing the conversation upstream, whereas carrying a 'context' field throughout the conversation would allow a single authoritative fetchable pointer of the thread head and all its descendants. + +So our plan is to re-use a supplied 'context' on all descendants, much as we do with ostatus:conversation, and provide a collection at the context endpoint. \ No newline at end of file From 29fcd158cb74b4846336d923ec19483190f0cb6d Mon Sep 17 00:00:00 2001 From: nobody Date: Fri, 16 Apr 2021 18:43:14 -0700 Subject: [PATCH 08/13] implement conversation endpoint --- Zotlabs/Module/Activity.php | 2 +- Zotlabs/Module/Conversation.php | 162 ++++++++++++++++++++++++++++++++ Zotlabs/Module/Item.php | 3 +- 3 files changed, 164 insertions(+), 3 deletions(-) create mode 100644 Zotlabs/Module/Conversation.php diff --git a/Zotlabs/Module/Activity.php b/Zotlabs/Module/Activity.php index 24564dc5a..2ad2c8373 100644 --- a/Zotlabs/Module/Activity.php +++ b/Zotlabs/Module/Activity.php @@ -232,7 +232,7 @@ class Activity extends Controller { if(! perm_is_allowed($chan['channel_id'],get_observer_hash(),'view_stream')) http_status_exit(403, 'Forbidden'); - $i = ZlibActivity::encode_item_collection($nitems,'conversation/' . $item_id,'OrderedCollection',true); + $i = ZlibActivity::encode_item_collection($nitems,'conversation/' . $item_id,'OrderedCollection',true, count($nitems)); if ($portable_id && (! intval($items[0]['item_private']))) { ThreadListener::store(z_root() . '/activity/' . $item_id,$portable_id); } diff --git a/Zotlabs/Module/Conversation.php b/Zotlabs/Module/Conversation.php new file mode 100644 index 000000000..c51ea1969 --- /dev/null +++ b/Zotlabs/Module/Conversation.php @@ -0,0 +1,162 @@ + Date: Fri, 16 Apr 2021 19:16:17 -0700 Subject: [PATCH 09/13] federation.md --- FEDERATION.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/FEDERATION.md b/FEDERATION.md index ab2f18c34..db41b6bab 100644 --- a/FEDERATION.md +++ b/FEDERATION.md @@ -97,4 +97,5 @@ Conversation Completion (2021-04-17) It's easy to fetch missing pieces of a conversation going "upstream", but there is no agreed-on method to fetch a complete conversation from the viewpoint of the origin actor. We intend to use 'context' to return a collection containing the entire converstion and would like to see participation by other projects to provide the most complete conversations as practical as seen by the initator. This is expected in Q2 2021. Use of 'replies' for this purpose will be provided to ActivityPub viewers as well but that collection does not contain the origin post. It still requires traversing the conversation upstream, whereas carrying a 'context' field throughout the conversation would allow a single authoritative fetchable pointer of the thread head and all its descendants. -So our plan is to re-use a supplied 'context' on all descendants, much as we do with ostatus:conversation, and provide a collection at the context endpoint. \ No newline at end of file +So our plan is to re-use a supplied 'context' on all descendants, much as we do with ostatus:conversation today, and provide a collection at the context endpoint. Once this is in place we will begin the process of deprecating ostatus:conversation. + From 87de776a4388553d26fc7493047cd33de541d0a0 Mon Sep 17 00:00:00 2001 From: nobody Date: Fri, 16 Apr 2021 23:53:35 -0700 Subject: [PATCH 10/13] context is a thing now --- FEDERATION.md | 6 ++---- Zotlabs/Lib/Activity.php | 36 +++++++++++++++++++++++++++--------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/FEDERATION.md b/FEDERATION.md index db41b6bab..927faeb04 100644 --- a/FEDERATION.md +++ b/FEDERATION.md @@ -91,11 +91,9 @@ By default the mention format is '@Display Name', but other options are availabl (Mastodon) Comment Notifications -Our projects send comment notifications if somebody replies to a post you either created or have previously interacted with in some way. They also are able to send a "mention" notification if you were mentioned in the post. This differs from Mastodon which does not appear to support comment notifications at all and only provides mention notifications. For this reason, Mastodon users don't typically get notified unless the author they are replying to is mentioned in the post. We provide this mention in the 'tag' field of the generated Activity, but normally don't include it in the message body, as we don't actually require mentions that were created for the sole purpose of triggering a notification. +Our projects send comment notifications if somebody replies to a post you either created or have previously interacted with in some way. They also are able to send a "mention" notification if you were mentioned in the post. This differs from Mastodon which does not appear to support comment notifications at all and only provides mention notifications. For this reason, Mastodon users don't typically get notified unless the author they are replying to is mentioned in the post. We provide this mention in the 'tag' field of the generated Activity, but normally don't include it in the message body, as we don't actually require mentions that were created for the sole purpose of triggering a comment notification. Conversation Completion -(2021-04-17) It's easy to fetch missing pieces of a conversation going "upstream", but there is no agreed-on method to fetch a complete conversation from the viewpoint of the origin actor. We intend to use 'context' to return a collection containing the entire converstion and would like to see participation by other projects to provide the most complete conversations as practical as seen by the initator. This is expected in Q2 2021. Use of 'replies' for this purpose will be provided to ActivityPub viewers as well but that collection does not contain the origin post. It still requires traversing the conversation upstream, whereas carrying a 'context' field throughout the conversation would allow a single authoritative fetchable pointer of the thread head and all its descendants. - -So our plan is to re-use a supplied 'context' on all descendants, much as we do with ostatus:conversation today, and provide a collection at the context endpoint. Once this is in place we will begin the process of deprecating ostatus:conversation. +(2021-04-17) It's easy to fetch missing pieces of a conversation going "upstream", but there is no agreed-on method to fetch a complete conversation from the viewpoint of the origin actor and upstream fetching only provides a single conversation branch, rather than the entire tree. We provide 'context' as a URL to a collection containing the entire conversation (all known branches) as seen by its creator. This requires special treatment and is very similar to ostatus:conversation in that if context is present, it needs to be replicated in conversation descendants. We still support ostatus:conversation but usage is deprecated. We do not use 'replies' to achieve the same purposes because 'replies' only applies to direct descendants at any point in the conversation tree. diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index e7f89866e..409b92501 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -745,7 +745,10 @@ class Activity { if (! in_array($ret['type'],[ 'Create','Update','Accept','Reject','TentativeAccept','TentativeReject' ])) { $ret['inReplyTo'] = $i['thr_parent']; - $cnv = get_iconfig($i['parent'],'ostatus','conversation'); + $cnv = get_iconfig($i['parent'],'activitypub','context'); + if (! $cnv) { + $cnv = get_iconfig($i['parent'],'ostatus','conversation'); + } if (! $cnv) { $cnv = $ret['parent_mid']; } @@ -753,15 +756,19 @@ class Activity { } if (! (isset($cnv) && $cnv)) { - // This method may be called before the item is actually saved - in which case there is no id and IConfig cannot be used - if ($i['id']) { + $cnv = get_iconfig($i,'activitypub','context'); + if (! $cnv) { $cnv = get_iconfig($i,'ostatus','conversation'); } - else { + if (! $cnv) { $cnv = $i['parent_mid']; } } if (isset($cnv) && $cnv) { + if (strpos($cnv,z_root()) === 0) { + $cnv = str_replace(['/item/','/activity/'],[ '/conversation/', '/conversation/' ], $cnv); + } + $ret['context'] = $cnv; $ret['conversation'] = $cnv; } @@ -1065,7 +1072,10 @@ class Activity { if ($i['mid'] !== $i['parent_mid']) { $ret['inReplyTo'] = $i['thr_parent']; - $cnv = get_iconfig($i['parent'],'ostatus','conversation'); + $cnv = get_iconfig($i['parent'],'activitypub','context'); + if (! $cnv) { + $cnv = get_iconfig($i['parent'],'ostatus','conversation'); + } if (! $cnv) { $cnv = $ret['parent_mid']; } @@ -1091,14 +1101,19 @@ class Activity { } } if (! isset($cnv)) { - if ($i['id']) { + $cnv = get_iconfig($i,'activitypub','context'); + if (! $cnv) { $cnv = get_iconfig($i,'ostatus','conversation'); } - else { + if (! $cnv) { $cnv = $i['parent_mid']; } } - if ($cnv) { + if (isset($cnv) && $cnv) { + if (strpos($cnv,z_root()) === 0) { + $cnv = str_replace(['/item/','/activity/'],[ '/conversation/', '/conversation/' ], $cnv); + } + $ret['context'] = $cnv; $ret['conversation'] = $cnv; } @@ -3318,11 +3333,14 @@ class Activity { return; } + if ($act->obj['context']) { + set_iconfig($item,'activitypub','context',$act->obj['context'],1); + } + if ($act->obj['conversation']) { set_iconfig($item,'ostatus','conversation',$act->obj['conversation'],1); } - set_iconfig($item,'activitypub','recips',$act->raw_recips); if (! (isset($act->data['inheritPrivacy']) && $act->data['inheritPrivacy'])) { From 1f2981418e3302caafaae0dbd667a31aa3bab98d Mon Sep 17 00:00:00 2001 From: nobody Date: Sat, 17 Apr 2021 16:34:36 -0700 Subject: [PATCH 11/13] move 'replyto' from initial concept to experimental stage --- Zotlabs/Daemon/Notifier.php | 24 ++++++++++++++++++++++++ Zotlabs/Lib/Activity.php | 29 ++++++++++++++++++----------- Zotlabs/Module/Item.php | 18 ++++++++++++++++-- include/items.php | 8 +++++++- 4 files changed, 65 insertions(+), 14 deletions(-) diff --git a/Zotlabs/Daemon/Notifier.php b/Zotlabs/Daemon/Notifier.php index 855d8e3cc..fb1c5fe11 100644 --- a/Zotlabs/Daemon/Notifier.php +++ b/Zotlabs/Daemon/Notifier.php @@ -400,7 +400,31 @@ class Notifier { if (($relay_to_owner || $uplink) && ($cmd !== 'relay')) { logger('followup relay (upstream delivery)', LOGGER_DEBUG); $sendto = ($uplink) ? $parent_item['source_xchan'] : $parent_item['owner_xchan']; +if (defined('X-REPLY-TO')) { +// experimental until debugging is completed + if ($parent_item['replyto']) { + $ptr = unserialise($parent_item['replyto']); + if (is_string($ptr)) { + $sendto = $ptr; + self::$recipients = [ $sendto ]; + } + elseif (is_array($tr)) { + $sendto = []; + foreach ($ptr as $rto) { + if (is_string($rto)) { + $sendto[] = $rto; + } + elseif (is_array($rto) && isset($rto['id'])) { + $sendto[] = $rto['id']; + } + } + self::$recipients = $sendto; + } + } +else // X-REPLY-TO self::$recipients = [ $sendto ]; +} +// END X-REPLY-TO self::$private = true; $upstream = true; self::$packet_type = 'response'; diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index 409b92501..12126cab5 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -782,10 +782,11 @@ class Activity { else return []; - $replyto = self::encode_person($i['owner'],false); -// if ($replyto) { -// $ret['replyTo'] = $replyto; -// } + + $replyto = unserialise($i['replyto']); + if ($replyto) { + $ret['replyTo'] = $replyto; + } if (! isset($ret['url'])) { $urls = []; @@ -1166,10 +1167,10 @@ class Activity { } } - $replyto = self::encode_person($i['owner'],false); -// if ($replyto) { -// $ret['replyTo'] = $replyto; -// } + $replyto = unserialise($i['replyto']); + if ($replyto) { + $ret['replyTo'] = $replyto; + } if (! isset($ret['url'])) { $urls = []; @@ -2515,6 +2516,15 @@ class Activity { $s['mid'] = $s['parent_mid'] = $act->id; } + if (isset($act->replyto) && ! empty($act->replyto)) { + if (is_array($act->replyto) && isset($act->replyto['id'])) { + $s['replyto'] = $act->replyto['id']; + } + else { + $s['replyto'] = $act->replyto; + } + } + if (ActivityStreams::is_response_activity($act->type)) { $response_activity = true; @@ -2522,9 +2532,6 @@ class Activity { $s['mid'] = $act->id; $s['parent_mid'] = $act->obj['id']; -// if (isset($act->replyto) && ! empty($act->replyto)) { -// $s['replyto'] = $act->replyto; -// } // over-ride the object timestamp with the activity diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php index 70f278cd5..76261034c 100644 --- a/Zotlabs/Module/Item.php +++ b/Zotlabs/Module/Item.php @@ -533,6 +533,10 @@ class Item extends Controller { } + if ($parent_item && isset($parent_item['replyto']) && $parent_item['replyto']) { + $replyto = unserialise($parent_item['replyto']); + } + $moderated = false; if (! $observer) { @@ -696,7 +700,17 @@ class Item extends Controller { } } } - + + if (! isset($replyto)) { + if (strpos($owner_xchan['xchan_hash'],'http') === 0) { + $replyto = $owner_xchan['xchan_hash']; + } + else { + $replyto = $owner_xchan['xchan_url']; + } + } + + $acl = new AccessControl($channel); $view_policy = PermissionLimits::Get($channel['channel_id'],'view_stream'); @@ -1340,7 +1354,7 @@ class Item extends Controller { $datarray['term'] = $post_tags; $datarray['plink'] = $plink; $datarray['route'] = $route; - + $datarray['replyto'] = $replyto; // A specific ACL over-rides public_policy completely diff --git a/include/items.php b/include/items.php index 9cc8371f0..6f62d24e2 100644 --- a/include/items.php +++ b/include/items.php @@ -724,6 +724,7 @@ function get_item_elements($x,$allow_code = false) { } $arr['attach'] = activity_sanitise($x['attach']); + $arr['replyto'] = activity_sanitise($c['replyto']); $arr['term'] = decode_tags($x['tags']); $arr['iconfig'] = decode_item_meta($x['meta']); @@ -1115,6 +1116,7 @@ function encode_item($item,$mirror = false) { $x['longlat'] = $item['coord']; $x['signature'] = $item['sig']; $x['route'] = $item['route']; + $x['replyto'] = $item['replyto']; $x['owner'] = encode_item_xchan($item['owner']); $x['author'] = encode_item_xchan($item['author']); @@ -3058,6 +3060,9 @@ function start_delivery_chain($channel, $item, $item_id, $parent, $group = false $arr['deny_gid'] = $channel['channel_deny_gid']; $arr['comment_policy'] = map_scope(PermissionLimits::Get($channel['channel_id'],'post_comments')); + $arr['replyto'] = z_root() . '/channel/' . $channel['channel_address']; + + if ($arr['id']) { $post = item_store_update($arr); } @@ -3117,12 +3122,13 @@ function start_delivery_chain($channel, $item, $item_id, $parent, $group = false $title = $item['title']; $body = $item['body']; - $r = q("update item set item_uplink = %d, item_nocomment = %d, item_flags = %d, owner_xchan = '%s', allow_cid = '%s', allow_gid = '%s', + $r = q("update item set item_uplink = %d, item_nocomment = %d, item_flags = %d, owner_xchan = '%s', replyto = '%s', allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s', item_private = %d, comment_policy = '%s', title = '%s', body = '%s', item_wall = %d, item_origin = %d where id = %d", intval($item_uplink), intval($item_nocomment), intval($flag_bits), dbesc($channel['channel_hash']), + dbesc(channel_url($channel)), dbesc($channel['channel_allow_cid']), dbesc($channel['channel_allow_gid']), dbesc($channel['channel_deny_cid']), From 220ee850f50a29343163bb007bac57c3517e75ef Mon Sep 17 00:00:00 2001 From: nobody Date: Sat, 17 Apr 2021 16:51:00 -0700 Subject: [PATCH 12/13] minor text mangling --- FEDERATION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FEDERATION.md b/FEDERATION.md index 927faeb04..d7a296daa 100644 --- a/FEDERATION.md +++ b/FEDERATION.md @@ -77,7 +77,7 @@ Announce Announce and relay activities use two mechanisms. As wll as the Announce activity, a new message is generated with an embedded rendering of the shared content as the message content. This message may (should) contain additional commentary in order to comply with the Fair Use provisions of copyright law. The other reason is our use of comment permissions. Comments to Announce activities are sent to the author (who typically accepts comments only from connections). Comment to embedded forwards are sent to the sender. This difference in behaviour allows groups to work correctly in the presence of comment permissions. -Discussion (2021-04-17): In the email world this type of conflict is resolved by the use of the reply-to header (e.g. in this case reply to the group rather than to the author) as well as the concept of a 'sender' which is different than 'from' (the author). We will soon be modelling the first one in ActivityPub with the use of 'replyTo'. If you see 'replyTo' in an activity it indicates that replies SHOULD go to that address rather than the author's inbox. We will implement this first and come up with a proposal for 'sender' if this gets any traction. If enough projects support these constructs we can eliminate the multiple Announce mechanisms and in the process make ActivityPub much more versatile when it comes to organisational and group communications. Our primary use case for 'sender' is to provide an ActivityPub origin to a message that was imported from another system entirely (such as Diaspora or from RSS source). In this case we would set 'attributedTo' to the remote identity that authored the content, and 'sender' to the person that posted it in ActivityPub. +Discussion (2021-04-17): In the email world this type of conflict is resolved by the use of the reply-to header (e.g. in this case reply to the group rather than to the author) as well as the concept of a 'sender' which is different than 'from' (the author). We will soon be modelling the first one in ActivityPub with the use of 'replyTo'. If you see 'replyTo' in an activity it indicates that replies SHOULD go to that address rather than the author's inbox. We will implement this first and come up with a proposal for 'sender' if this gets any traction. If enough projects support these constructs we can eliminate the multiple relay mechanisms and in the process make ActivityPub much more versatile when it comes to organisational and group communications. Our primary use case for 'sender' is to provide an ActivityPub origin to a message that was imported from another system entirely (such as Diaspora or from RSS source). In this case we would set 'attributedTo' to the remote identity that authored the content, and 'sender' to the person that posted it in ActivityPub. Mastodon Custom Emojis From 825614c7d35216a01b9ce8537785f1ff214cb28a Mon Sep 17 00:00:00 2001 From: nobody Date: Sat, 17 Apr 2021 19:06:41 -0700 Subject: [PATCH 13/13] replyto enhancements --- Zotlabs/Daemon/Notifier.php | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/Zotlabs/Daemon/Notifier.php b/Zotlabs/Daemon/Notifier.php index fb1c5fe11..cb1b20d57 100644 --- a/Zotlabs/Daemon/Notifier.php +++ b/Zotlabs/Daemon/Notifier.php @@ -400,15 +400,20 @@ class Notifier { if (($relay_to_owner || $uplink) && ($cmd !== 'relay')) { logger('followup relay (upstream delivery)', LOGGER_DEBUG); $sendto = ($uplink) ? $parent_item['source_xchan'] : $parent_item['owner_xchan']; + self::$recipients = [ $sendto ]; + if (defined('X-REPLY-TO')) { // experimental until debugging is completed - if ($parent_item['replyto']) { + + if ($parent_item['replyto'] && (! $uplink)) { $ptr = unserialise($parent_item['replyto']); if (is_string($ptr)) { - $sendto = $ptr; - self::$recipients = [ $sendto ]; + if (ActivityStreams::is_url($sendto)) { + $sendto = $ptr; + self::$recipients = [ $sendto ]; + } } - elseif (is_array($tr)) { + elseif (is_array($ptr)) { $sendto = []; foreach ($ptr as $rto) { if (is_string($rto)) { @@ -421,10 +426,10 @@ if (defined('X-REPLY-TO')) { self::$recipients = $sendto; } } -else // X-REPLY-TO - self::$recipients = [ $sendto ]; + } -// END X-REPLY-TO +// END defined('X-REPLY-TO') + self::$private = true; $upstream = true; self::$packet_type = 'response';