From 52e698cae6794ce92c249fd365f6035f88382688 Mon Sep 17 00:00:00 2001 From: Mario Date: Sun, 12 Apr 2020 07:05:01 +0000 Subject: function is_edit_activity() is obsolete --- include/conversation.php | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/include/conversation.php b/include/conversation.php index 49dd411fb..a78ee1704 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -423,35 +423,9 @@ function visible_activity($item) { } } - - - if(is_edit_activity($item)) - return false; - return true; } -/** - * @brief Check if a given activity is an edit activity - * - * - * @param array $item - * @return boolean - */ - -function is_edit_activity($item) { - - $post_types = [ ACTIVITY_OBJ_NOTE, ACTIVITY_OBJ_COMMENT, basename(ACTIVITY_OBJ_NOTE), basename(ACTIVITY_OBJ_COMMENT)]; - - // In order to share edits with networks which have no concept of editing, we'll create - // separate activities to indicate the edit. Our network will not require them, since our - // edits are automatically applied and the activity indicated. - - if(($item['verb'] === ACTIVITY_UPDATE) && (in_array($item['obj_type'],$post_types))) - return true; - - return false; -} /** * @brief "Render" a conversation or list of items for HTML display. -- cgit v1.2.3 From 27ae9c9d343fec96f117d5c4940d2221bc7cfd55 Mon Sep 17 00:00:00 2001 From: Mario Date: Sun, 12 Apr 2020 07:35:17 +0000 Subject: just comment out is_edit_activity() call and add comments on why it is commented out --- include/conversation.php | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/include/conversation.php b/include/conversation.php index a78ee1704..b0e81b7e2 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -423,9 +423,43 @@ function visible_activity($item) { } } + // We only need edit activities for other federated protocols + // which do not support edits natively. While this does federate + // edits, it presents a number of issues locally - such as #757 and #758. + // The SQL check for an edit activity would not perform that well so to fix these issues + // requires an additional item flag (perhaps 'item_edit_activity') that we can add to the + // query for searches and notifications. + + // For now we'll just forget about trying to make edits work on network protocols that + // don't support them. + + // if(is_edit_activity($item)) + // return false; + return true; } +/** + * @brief Check if a given activity is an edit activity + * + * + * @param array $item + * @return boolean + */ + +function is_edit_activity($item) { + + $post_types = [ ACTIVITY_OBJ_NOTE, ACTIVITY_OBJ_COMMENT, basename(ACTIVITY_OBJ_NOTE), basename(ACTIVITY_OBJ_COMMENT)]; + + // In order to share edits with networks which have no concept of editing, we'll create + // separate activities to indicate the edit. Our network will not require them, since our + // edits are automatically applied and the activity indicated. + + if(($item['verb'] === ACTIVITY_UPDATE) && (in_array($item['obj_type'],$post_types))) + return true; + + return false; +} /** * @brief "Render" a conversation or list of items for HTML display. -- cgit v1.2.3 From 69878ed628735cf1ecca3b6a2480a648c1e89127 Mon Sep 17 00:00:00 2001 From: Mario Date: Sun, 12 Apr 2020 07:54:22 +0000 Subject: handle some basic friendica attachment bbcodes --- include/bbcode.php | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) diff --git a/include/bbcode.php b/include/bbcode.php index b2e3f1d3b..e846e38db 100644 --- a/include/bbcode.php +++ b/include/bbcode.php @@ -319,6 +319,139 @@ function translate_design_element($type) { return $ret; } +function bb_format_attachdata($body) { + + $data = getAttachmentData($body); + + if($data) { + $txt = ''; + if($data['url'] && $data['title']) { + $txt .= "\n\n" . '[url=' . $data['url'] . ']' . $data['title'] . '[/url]'; + } + else { + if($data['url']) { + $txt .= "\n\n" . $data['url']; + } + if($data['title']) { + $txt .= "\n\n" . $data['title']; + } + } + if($data['preview']) { + $txt .= "\n\n" . '[img]' . $data['preview'] . '[/img]'; + } + if($data['image']) { + $txt .= "\n\n" . '[img]' . $data['image'] . '[/img]'; + } + + + $txt .= "\n\n" . $data['text']; + return preg_replace('/\[attachment(.*?)\](.*?)\[\/attachment\]/ism',$txt,$body); + } + + return $body; +} + +function getAttachmentData($body) { + + $data = []; + + if (! preg_match("/\[attachment(.*?)\](.*?)\[\/attachment\]/ism", $body, $match)) { + return null; + } + + $attributes = $match[1]; + + $data["text"] = trim($match[2]); + + $type = ""; + preg_match("/type='(.*?)'/ism", $attributes, $matches); + + if (x($matches, 1)) { + $type = strtolower($matches[1]); + } + + preg_match('/type=\"\;(.*?)\"\;/ism', $attributes, $matches); + if (x($matches, 1)) { + $type = strtolower($matches[1]); + } + + if ($type == "") { + return []; + } + + if (!in_array($type, ["link", "audio", "photo", "video"])) { + return []; + } + + if ($type != "") { + $data["type"] = $type; + } + $url = ""; + preg_match("/url='(.*?)'/ism", $attributes, $matches); + if (x($matches, 1)) { + $url = $matches[1]; + } + + preg_match('/url=\"\;(.*?)\"\;/ism', $attributes, $matches); + if (x($matches, 1)) { + $url = $matches[1]; + } + + if ($url != "") { + $data["url"] = html_entity_decode($url, ENT_QUOTES, 'UTF-8'); + } + + $title = ""; + preg_match("/title='(.*?)'/ism", $attributes, $matches); + if (x($matches, 1)) { + $title = $matches[1]; + } + + preg_match('/title=\"\;(.*?)\"\;/ism', $attributes, $matches); + if (x($matches, 1)) { + $title = $matches[1]; + } + if ($title != "") { + $title = html_entity_decode($title, ENT_QUOTES, 'UTF-8'); + $title = str_replace(["[", "]"], ["[", "]"], $title); + $data["title"] = $title; + } + + $image = ""; + preg_match("/image='(.*?)'/ism", $attributes, $matches); + if (x($matches, 1)) { + $image = $matches[1]; + } + + preg_match('/image=\"\;(.*?)\"\;/ism', $attributes, $matches); + if (x($matches, 1)) { + $image = $matches[1]; + } + + if ($image != "") { + $data["image"] = html_entity_decode($image, ENT_QUOTES, 'UTF-8'); + } + + $preview = ""; + preg_match("/preview='(.*?)'/ism", $attributes, $matches); + if (x($matches, 1)) { + $preview = $matches[1]; + } + + preg_match('/preview=\"\;(.*?)\"\;/ism', $attributes, $matches); + if (x($matches, 1)) { + $preview = $matches[1]; + } + if ($preview != "") { + $data["preview"] = html_entity_decode($preview, ENT_QUOTES, 'UTF-8'); + } + + $data["description"] = trim($match[3]); + + $data["after"] = trim($match[4]); + + return $data; +} function bb_ShareAttributes($match) { @@ -935,6 +1068,8 @@ function bbcode($Text, $options = []) { $Text = preg_replace_callback("/\[pre\](.*?)\[\/pre\]/ism", 'bb_spacefy',$Text); } + $Text = bb_format_attachdata($Text); + // If we find any event code, turn it into an event. // After we're finished processing the bbcode we'll // replace all of the event code with a reformatted version. -- cgit v1.2.3 From 6de823b532fc526a321e6b2b4df2cdd6175b7eb7 Mon Sep 17 00:00:00 2001 From: Mario Date: Sun, 12 Apr 2020 08:59:42 +0000 Subject: discover_feed() does not exist yet --- Zotlabs/Lib/Connect.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zotlabs/Lib/Connect.php b/Zotlabs/Lib/Connect.php index 5fc0e3fe1..caac30f7a 100644 --- a/Zotlabs/Lib/Connect.php +++ b/Zotlabs/Lib/Connect.php @@ -97,7 +97,7 @@ class Connect { $feeds = get_config('system','feed_contacts'); if (($feeds) && (in_array($protocol, [ '', 'feed', 'rss' ]))) { - $d = discover_feed($url); + $d = discover_by_url($url); } else { $result['message'] = t('Remote channel or protocol unavailable.'); -- cgit v1.2.3 From 31e1e9cbfb3ad74f8470c79cec2b1b21d3739117 Mon Sep 17 00:00:00 2001 From: Max Kostikov Date: Sun, 12 Apr 2020 21:41:32 +0200 Subject: Remove unnecessary [summary] tag processing --- Zotlabs/Module/Item.php | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php index fcc040e01..321089644 100644 --- a/Zotlabs/Module/Item.php +++ b/Zotlabs/Module/Item.php @@ -721,18 +721,18 @@ class Item extends Controller { // BBCODE alert: the following functions assume bbcode input // and will require alternatives for alternative content-types (text/html, text/markdown, text/plain, etc.) // we may need virtual or template classes to implement the possible alternatives - + /* if(strpos($body,'[/summary]') !== false) { - $match = ''; - $cnt = preg_match("/\[summary\](.*?)\[\/summary\]/ism",$body,$match); - if($cnt) { - $summary .= $match[1]; - } - $body_content = preg_replace("/^(.*?)\[summary\](.*?)\[\/summary\](.*?)$/ism", '',$body); - $body = trim($body_content); - } - - $summary = cleanup_bbcode($summary); + $match = ''; + $cnt = preg_match("/\[summary\](.*?)\[\/summary\]/ism",$body,$match); + if($cnt) { + $summary .= $match[1]; + } + $body_content = preg_replace("/^(.*?)\[summary\](.*?)\[\/summary\](.*?)$/ism", '',$body); + $body = trim($body_content); + } + */ + $summary = cleanup_bbcode($summary); $body = cleanup_bbcode($body); -- cgit v1.2.3 From bbca6be0f94625f541b0d985fe66df54fc8f4be2 Mon Sep 17 00:00:00 2001 From: Max Kostikov Date: Sun, 12 Apr 2020 23:08:11 +0200 Subject: Update Item.php --- Zotlabs/Module/Item.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php index 321089644..cdfc3c9a8 100644 --- a/Zotlabs/Module/Item.php +++ b/Zotlabs/Module/Item.php @@ -721,17 +721,17 @@ class Item extends Controller { // BBCODE alert: the following functions assume bbcode input // and will require alternatives for alternative content-types (text/html, text/markdown, text/plain, etc.) // we may need virtual or template classes to implement the possible alternatives - /* + if(strpos($body,'[/summary]') !== false) { $match = ''; $cnt = preg_match("/\[summary\](.*?)\[\/summary\]/ism",$body,$match); if($cnt) { $summary .= $match[1]; } - $body_content = preg_replace("/^(.*?)\[summary\](.*?)\[\/summary\](.*?)$/ism", '',$body); + $body_content = preg_replace("/\[summary\](.*?)\[\/summary\]/ism", '',$body); $body = trim($body_content); } - */ + $summary = cleanup_bbcode($summary); $body = cleanup_bbcode($body); -- cgit v1.2.3 From 9d0f71bfcbc0d1080a9f93590118046d326cae70 Mon Sep 17 00:00:00 2001 From: Mario Date: Wed, 15 Apr 2020 08:21:07 +0000 Subject: keychange and request packets via zot6 --- Zotlabs/Daemon/Notifier.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Zotlabs/Daemon/Notifier.php b/Zotlabs/Daemon/Notifier.php index d66079216..d3b0a6b14 100644 --- a/Zotlabs/Daemon/Notifier.php +++ b/Zotlabs/Daemon/Notifier.php @@ -672,15 +672,17 @@ class Notifier { $packet = zot_build_packet($channel,$packet_type,(($packet_recips) ? $packet_recips : null)); } } - if($packet_type === 'keychange' && $hub['hubloc_network'] === 'zot') { + if($packet_type === 'keychange' && $hub['hubloc_network'] === 'zot6') { $pmsg = get_pconfig($channel['channel_id'],'system','keychange'); - $packet = zot_build_packet($channel,$packet_type,(($packet_recips) ? $packet_recips : null)); + $packet = Libzot::build_packet($channel, $packet_type, (($packet_recips) ? $packet_recips : null)); + // $packet = zot_build_packet($channel,$packet_type,(($packet_recips) ? $packet_recips : null)); + } - elseif($packet_type === 'request' && $hub['hubloc_network'] === 'zot') { + elseif($packet_type === 'request' && $hub['hubloc_network'] === 'zot6') { $env = (($hub_env && $hub_env[$hub['hubloc_host'] . $hub['hubloc_sitekey']]) ? $hub_env[$hub['hubloc_host'] . $hub['hubloc_sitekey']] : ''); - $packet = zot_build_packet($channel,$packet_type,$env,$hub['hubloc_sitekey'],$hub['site_crypto'], - $hash, array('message_id' => $request_message_id) - ); + $packet = Libzot::build_packet($channel, $packet_type, $env, ['message_id' => $request_message_id], 'activitystreams', $hub['hubloc_sitekey'], $hub['site_crypto']); + // $packet = zot_build_packet($channel,$packet_type,$env,$hub['hubloc_sitekey'],$hub['site_crypto'],$hash, array('message_id' => $request_message_id)); + } if($packet) { -- cgit v1.2.3 From b1f74f4ef470715144728bfd16d5f6b6a802046d Mon Sep 17 00:00:00 2001 From: Max Kostikov Date: Wed, 15 Apr 2020 10:22:54 +0200 Subject: Remove unnecessary [summary] tag processing --- Zotlabs/Module/Item.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php index fcc040e01..cdfc3c9a8 100644 --- a/Zotlabs/Module/Item.php +++ b/Zotlabs/Module/Item.php @@ -723,16 +723,16 @@ class Item extends Controller { // we may need virtual or template classes to implement the possible alternatives if(strpos($body,'[/summary]') !== false) { - $match = ''; - $cnt = preg_match("/\[summary\](.*?)\[\/summary\]/ism",$body,$match); - if($cnt) { - $summary .= $match[1]; - } - $body_content = preg_replace("/^(.*?)\[summary\](.*?)\[\/summary\](.*?)$/ism", '',$body); - $body = trim($body_content); - } - - $summary = cleanup_bbcode($summary); + $match = ''; + $cnt = preg_match("/\[summary\](.*?)\[\/summary\]/ism",$body,$match); + if($cnt) { + $summary .= $match[1]; + } + $body_content = preg_replace("/\[summary\](.*?)\[\/summary\]/ism", '',$body); + $body = trim($body_content); + } + + $summary = cleanup_bbcode($summary); $body = cleanup_bbcode($body); -- cgit v1.2.3 From d6b9c8b93d5f04d986b1c9e61be9146d556f403a Mon Sep 17 00:00:00 2001 From: Mario Date: Wed, 15 Apr 2020 08:43:28 +0000 Subject: =?UTF-8?q?=C3=83fix=20php=20warning?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Zotlabs/Lib/ThreadItem.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zotlabs/Lib/ThreadItem.php b/Zotlabs/Lib/ThreadItem.php index dee7cda56..a5dd81d40 100644 --- a/Zotlabs/Lib/ThreadItem.php +++ b/Zotlabs/Lib/ThreadItem.php @@ -113,7 +113,7 @@ class ThreadItem { if(intval($item['item_private']) && ($item['owner']['xchan_network'] === 'activitypub')) { $recips = get_iconfig($item['parent'], 'activitypub', 'recips'); - if(! in_array($observer['xchan_url'], $recips['to'])) + if(! is_array($recips['to']) || ! in_array($observer['xchan_url'], $recips['to'])) $privacy_warning = true; } -- cgit v1.2.3 From 91cad21d332154eba17b21d3a2dd6a64d55d0a28 Mon Sep 17 00:00:00 2001 From: Mario Date: Wed, 15 Apr 2020 11:56:56 +0000 Subject: fetch the item for asld in Lib/Share. This probably fixes addons issue #146 --- Zotlabs/Lib/Share.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Zotlabs/Lib/Share.php b/Zotlabs/Lib/Share.php index 3a2ab1783..f8b636c10 100644 --- a/Zotlabs/Lib/Share.php +++ b/Zotlabs/Lib/Share.php @@ -2,6 +2,7 @@ namespace Zotlabs\Lib; +use Zotlabs\Lib\Activity; class Share { @@ -54,7 +55,7 @@ class Share { if(! $this->item) return $obj; - $obj['asld'] = $this->item['mid']; + $obj['asld'] = Activity::fetch_item( [ 'id' => $this->item['mid'] ] ); $obj['type'] = $this->item['obj_type']; $obj['id'] = $this->item['mid']; $obj['content'] = $this->item['body']; -- cgit v1.2.3 From e588ea8a8b697605c40a13210ab2b4c485d45ade Mon Sep 17 00:00:00 2001 From: Mario Date: Wed, 15 Apr 2020 15:39:33 +0000 Subject: fix notification filtering --- Zotlabs/Module/Sse_bs.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Zotlabs/Module/Sse_bs.php b/Zotlabs/Module/Sse_bs.php index 89e852120..c139fd19b 100644 --- a/Zotlabs/Module/Sse_bs.php +++ b/Zotlabs/Module/Sse_bs.php @@ -119,7 +119,7 @@ class Sse_bs extends Controller { $sql_extra2 = ''; if(self::$xchans) - $sql_extra2 = " AND (author_xchan IN (" . self::$xchans . ") OR owner_xchan IN (" . self::$xchans . ")) "; + $sql_extra2 = " AND author_xchan IN (" . self::$xchans . ") "; $item_normal = item_normal(); @@ -183,7 +183,7 @@ class Sse_bs extends Controller { $sql_extra2 = ''; if(self::$xchans) - $sql_extra2 = " AND (author_xchan IN (" . self::$xchans . ") OR owner_xchan IN (" . self::$xchans . ")) "; + $sql_extra2 = " AND author_xchan IN (" . self::$xchans . ") "; $item_normal = item_normal(); @@ -259,7 +259,7 @@ class Sse_bs extends Controller { $sql_extra2 = ''; if(self::$xchans) - $sql_extra2 = " AND (author_xchan IN (" . self::$xchans . ") OR owner_xchan IN (" . self::$xchans . ")) "; + $sql_extra2 = " AND author_xchan IN (" . self::$xchans . ") "; $item_normal = item_normal(); -- cgit v1.2.3 From f7e925beaad5b42e659977e33f2473ce9aa0bbac Mon Sep 17 00:00:00 2001 From: Mario Date: Thu, 16 Apr 2020 12:33:02 +0200 Subject: Revert "keychange and request packets via zot6" This reverts commit 9d0f71bfcbc0d1080a9f93590118046d326cae70 --- Zotlabs/Daemon/Notifier.php | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Zotlabs/Daemon/Notifier.php b/Zotlabs/Daemon/Notifier.php index d3b0a6b14..d66079216 100644 --- a/Zotlabs/Daemon/Notifier.php +++ b/Zotlabs/Daemon/Notifier.php @@ -672,17 +672,15 @@ class Notifier { $packet = zot_build_packet($channel,$packet_type,(($packet_recips) ? $packet_recips : null)); } } - if($packet_type === 'keychange' && $hub['hubloc_network'] === 'zot6') { + if($packet_type === 'keychange' && $hub['hubloc_network'] === 'zot') { $pmsg = get_pconfig($channel['channel_id'],'system','keychange'); - $packet = Libzot::build_packet($channel, $packet_type, (($packet_recips) ? $packet_recips : null)); - // $packet = zot_build_packet($channel,$packet_type,(($packet_recips) ? $packet_recips : null)); - + $packet = zot_build_packet($channel,$packet_type,(($packet_recips) ? $packet_recips : null)); } - elseif($packet_type === 'request' && $hub['hubloc_network'] === 'zot6') { + elseif($packet_type === 'request' && $hub['hubloc_network'] === 'zot') { $env = (($hub_env && $hub_env[$hub['hubloc_host'] . $hub['hubloc_sitekey']]) ? $hub_env[$hub['hubloc_host'] . $hub['hubloc_sitekey']] : ''); - $packet = Libzot::build_packet($channel, $packet_type, $env, ['message_id' => $request_message_id], 'activitystreams', $hub['hubloc_sitekey'], $hub['site_crypto']); - // $packet = zot_build_packet($channel,$packet_type,$env,$hub['hubloc_sitekey'],$hub['site_crypto'],$hash, array('message_id' => $request_message_id)); - + $packet = zot_build_packet($channel,$packet_type,$env,$hub['hubloc_sitekey'],$hub['site_crypto'], + $hash, array('message_id' => $request_message_id) + ); } if($packet) { -- cgit v1.2.3 From 223c4c7b9a01f70106e0ae0b72da4a13235af56c Mon Sep 17 00:00:00 2001 From: Mario Date: Sat, 18 Apr 2020 09:23:48 +0000 Subject: implement addressing and fix conversation fetching --- Zotlabs/Lib/Activity.php | 210 +++++++++++++++++++++++++++++++++++++++++------ Zotlabs/Module/Item.php | 82 +++++------------- 2 files changed, 208 insertions(+), 84 deletions(-) diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index 3c16a5367..2ba693049 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -419,7 +419,71 @@ class Activity { $ret['attachment'] = $a; } + $public = (($i['item_private']) ? false : true); + $top_level = (($i['mid'] === $i['parent_mid']) ? true : false); + + if ($public) { + $ret['to'] = [ ACTIVITY_PUBLIC_INBOX ]; + $ret['cc'] = [ z_root() . '/followers/' . substr($i['author']['xchan_addr'],0,strpos($i['author']['xchan_addr'],'@')) ]; + } + else { + + // private activity + + if ($top_level) { + $ret['to'] = self::map_acl($i); + } + else { + $ret['to'] = []; + if ($ret['tag']) { + foreach ($ret['tag'] as $mention) { + if (is_array($mention) && array_key_exists('href',$mention) && $mention['href']) { + $h = q("select * from hubloc where hubloc_id_url = '%s' limit 1", + dbesc($mention['href']) + ); + if ($h) { + if ($h[0]['hubloc_network'] === 'activitypub') { + $addr = $h[0]['hubloc_hash']; + } + else { + $addr = $h[0]['hubloc_id_url']; + } + if (! in_array($addr,$ret['to'])) { + $ret['to'][] = $addr; + } + } + } + } + } + $d = q("select hubloc.* from hubloc left join item on hubloc_hash = owner_xchan where item.id = %d limit 1", + intval($i['parent']) + ); + if ($d) { + if ($d[0]['hubloc_network'] === 'activitypub') { + $addr = $d[0]['hubloc_hash']; + } + else { + $addr = $d[0]['hubloc_id_url']; + } + if (! in_array($addr,$ret['to'])) { + $ret['cc'][] = $addr; + } + } + } + } + + $mentions = self::map_mentions($i); + if (count($mentions) > 0) { + if (! $ret['to']) { + $ret['to'] = $mentions; + } + else { + $ret['to'] = array_values(array_unique(array_merge($ret['to'], $mentions))); + } + } + return $ret; + } static function decode_taxonomy($item) { @@ -756,57 +820,155 @@ class Activity { return []; } + $t = self::encode_taxonomy($i); + if ($t) { + $ret['tag'] = $t; + } + + // addressing madness + + $public = (($i['item_private']) ? false : true); + $top_level = (($reply) ? false : true); + + if ($public) { + $ret['to'] = [ ACTIVITY_PUBLIC_INBOX ]; + $ret['cc'] = [ z_root() . '/followers/' . substr($i['author']['xchan_addr'],0,strpos($i['author']['xchan_addr'],'@')) ]; + } + else { + + // private activity + + if ($top_level) { + $ret['to'] = self::map_acl($i); + } + else { + $ret['to'] = []; + if ($ret['tag']) { + foreach ($ret['tag'] as $mention) { + if (is_array($mention) && array_key_exists('href',$mention) && $mention['href']) { + $h = q("select * from hubloc where hubloc_id_url = '%s' limit 1", + dbesc($mention['href']) + ); + if ($h) { + if ($h[0]['hubloc_network'] === 'activitypub') { + $addr = $h[0]['hubloc_hash']; + } + else { + $addr = $h[0]['hubloc_id_url']; + } + if (! in_array($addr,$ret['to'])) { + $ret['to'][] = $addr; + } + } + } + } + } + + $d = q("select hubloc.* from hubloc left join item on hubloc_hash = owner_xchan where item.id = %d limit 1", + intval($i['parent']) + ); + if ($d) { + if ($d[0]['hubloc_network'] === 'activitypub') { + $addr = $d[0]['hubloc_hash']; + } + else { + $addr = $d[0]['hubloc_id_url']; + } + if (! in_array($addr,$ret['to'])) { + $ret['cc'][] = $addr; + } + } + } + } + + $mentions = self::map_mentions($i); + if (count($mentions) > 0) { + if (! $ret['to']) { + $ret['to'] = $mentions; + } + else { + $ret['to'] = array_values(array_unique(array_merge($ret['to'], $mentions))); + } + } + return $ret; } + // Returns an array of URLS for any mention tags found in the item array $i. + static function map_mentions($i) { - if(! $i['term']) { + + if (! $i['term']) { return []; } $list = []; foreach ($i['term'] as $t) { - if($t['ttype'] == TERM_MENTION) { - $list[] = $t['url']; + if (! $t['url']) { + continue; + } + if ($t['ttype'] == TERM_MENTION) { + $url = self::lookup_term_url($t['url']); + $list[] = (($url) ? $url : $t['url']); } } return $list; } - static function map_acl($i,$mentions = false) { + // Returns an array of all recipients targeted by private item array $i. - $private = false; - $list = []; - $x = collect_recipients($i,$private); - if($x) { - stringify_array_elms($x); - if(! $x) - return; - - $strict = (($mentions) ? true : get_config('activitypub','compliance')); + static function map_acl($i) { + $ret = []; - $sql_extra = (($strict) ? " and xchan_network = 'activitypub' " : ''); + if (! $i['item_private']) { + return $ret; + } - $details = q("select xchan_url, xchan_addr, xchan_name from xchan where xchan_hash in (" . implode(',',$x) . ") $sql_extra"); + if ($i['allow_gid']) { + $tmp = expand_acl($i['allow_gid']); + if ($tmp) { + foreach ($tmp as $t) { + $ret[] = z_root() . '/lists/' . $t; + } + } + } - if($details) { - foreach($details as $d) { - if($mentions) { - $list[] = [ 'type' => 'Mention', 'href' => $d['xchan_url'], 'name' => '@' . (($d['xchan_addr']) ? $d['xchan_addr'] : $d['xchan_name']) ]; - } - else { - $list[] = $d['xchan_url']; + if ($i['allow_cid']) { + $tmp = expand_acl($i['allow_cid']); + $list = stringify_array($tmp,true); + if ($list) { + $details = q("select hubloc_id_url from hubloc where hubloc_hash in (" . $list . ") "); + if ($details) { + foreach ($details as $d) { + $ret[] = $d['hubloc_id_url']; } } } } - return $list; - + return $ret; } + static function lookup_term_url($url) { + + // The xchan_url for mastodon is a text/html rendering. This is called from map_mentions where we need + // to convert the mention url to an ActivityPub id. If this fails for any reason, return the url we have + + $r = q("select * from hubloc where hubloc_id_url = '%s' limit 1", + dbesc($url) + ); + + if ($r) { + if ($r[0]['hubloc_network'] === 'activitypub') { + return $r[0]['hubloc_hash']; + } + return $r[0]['hubloc_id_url']; + } + + return $url; + } static function encode_person($p, $extended = true) { diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php index cdfc3c9a8..9002b708d 100644 --- a/Zotlabs/Module/Item.php +++ b/Zotlabs/Module/Item.php @@ -43,9 +43,11 @@ class Item extends Controller { if (Libzot::is_zot_request()) { + $conversation = false; + $item_id = argv(1); - if (! $item_id) + if(! $item_id) http_status_exit(404, 'Not found'); $portable_id = EMPTY_STR; @@ -66,32 +68,24 @@ class Item extends Controller { // process an authenticated fetch - $sigdata = HTTPSig::verify(EMPTY_STR); - if($sigdata['portable_id'] && $sigdata['header_valid']) { + $sigdata = HTTPSig::verify(($_SERVER['REQUEST_METHOD'] === 'POST') ? file_get_contents('php://input') : EMPTY_STR); + if ($sigdata['portable_id'] && $sigdata['header_valid']) { $portable_id = $sigdata['portable_id']; + if (! check_channelallowed($portable_id)) { + http_status_exit(403, 'Permission denied'); + } + if (! check_siteallowed($sigdata['signer'])) { + http_status_exit(403, 'Permission denied'); + } observer_auth($portable_id); - // first see if we have a copy of this item's parent owned by the current signer - // include xchans for all zot-like networks - these will have the same guid and public key - - $x = q("select * from xchan where xchan_hash = '%s'", - dbesc($sigdata['portable_id']) + $i = q("select id as item_id from item where mid = '%s' $item_normal and owner_xchan = '%s' limit 1", + dbesc($r[0]['parent_mid']), + dbesc($portable_id) ); - - if ($x) { - $xchans = q("select xchan_hash from xchan where xchan_hash = '%s' OR ( xchan_guid = '%s' AND xchan_pubkey = '%s' ) ", - dbesc($sigdata['portable_id']), - dbesc($x[0]['xchan_guid']), - dbesc($x[0]['xchan_pubkey']) - ); - - if ($xchans) { - $hashes = ids_to_querystr($xchans,'xchan_hash',true); - $i = q("select id as item_id from item where mid = '%s' $item_normal and owner_xchan in ( " . protect_sprintf($hashes) . " ) limit 1", - dbesc($r[0]['parent_mid']) - ); - } - } + } + elseif (Config::get('system','require_authenticated_fetch',false)) { + http_status_exit(403,'Permission denied'); } // if we don't have a parent id belonging to the signer see if we can obtain one as a visitor that we have permission to access @@ -111,7 +105,7 @@ class Item extends Controller { $parents_str = ids_to_querystr($i,'item_id'); - $items = q("SELECT item.*, item.id AS item_id FROM item WHERE item.parent IN ( %s ) $item_normal ", + $items = q("SELECT item.*, item.id AS item_id FROM item WHERE item.parent IN ( %s ) $item_normal order by item.id asc", dbesc($parents_str) ); @@ -122,43 +116,10 @@ class Item extends Controller { xchan_query($items,true); $items = fetch_post_tags($items,true); - $observer = App::get_observer(); - $parent = $items[0]; - $recips = (($parent['owner']['xchan_network'] === 'activitypub') ? get_iconfig($parent['id'],'activitypub','recips', []) : []); - $to = (($recips && array_key_exists('to',$recips) && is_array($recips['to'])) ? $recips['to'] : null); - $nitems = []; - foreach($items as $i) { - - $mids = []; - - if(intval($i['item_private'])) { - if(! $observer) { - continue; - } - // ignore private reshare, possibly from hubzilla - if($i['verb'] === 'Announce') { - if(! in_array($i['thr_parent'],$mids)) { - $mids[] = $i['thr_parent']; - } - continue; - } - // also ignore any children of the private reshares - if(in_array($i['thr_parent'],$mids)) { - continue; - } - - if((! $to) || (! in_array($observer['xchan_url'],$to))) { - continue; - } - - } - $nitems[] = $i; - } - - if(! $nitems) + if(! $items) http_status_exit(404, 'Not found'); - $chan = channelx_by_n($nitems[0]['uid']); + $chan = channelx_by_n($items[0]['uid']); if(! $chan) http_status_exit(404, 'Not found'); @@ -166,7 +127,8 @@ class Item extends Controller { if(! perm_is_allowed($chan['channel_id'],get_observer_hash(),'view_stream')) http_status_exit(403, 'Forbidden'); - $i = Activity::encode_item_collection($nitems,'conversation/' . $item_id,'OrderedCollection'); + + $i = Activity::encode_item_collection($items, 'conversation/' . $item_id, 'OrderedCollection'); if($portable_id) { ThreadListener::store(z_root() . '/item/' . $item_id,$portable_id); } -- cgit v1.2.3 From 3d5fdd086be63bb8e9a889bfe593b3aaa4b4a6a2 Mon Sep 17 00:00:00 2001 From: Mario Date: Sat, 18 Apr 2020 11:30:19 +0000 Subject: only return hublocs with hubloc_id_url set --- Zotlabs/Lib/Activity.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index 2ba693049..24214072a 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -939,7 +939,7 @@ class Activity { $tmp = expand_acl($i['allow_cid']); $list = stringify_array($tmp,true); if ($list) { - $details = q("select hubloc_id_url from hubloc where hubloc_hash in (" . $list . ") "); + $details = q("select hubloc_id_url from hubloc where hubloc_hash in (" . $list . ") and hubloc_id_url != ''"); if ($details) { foreach ($details as $d) { $ret[] = $d['hubloc_id_url']; -- cgit v1.2.3 From 4cfc0b1a6410193b65673c5d43180aa9cecaeea2 Mon Sep 17 00:00:00 2001 From: Mario Date: Sat, 18 Apr 2020 12:19:34 +0000 Subject: de-duplicate $post_tags --- Zotlabs/Lib/Activity.php | 2 +- Zotlabs/Module/Item.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index 24214072a..5882a0777 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -890,7 +890,7 @@ class Activity { $ret['to'] = array_values(array_unique(array_merge($ret['to'], $mentions))); } } - +hz_syslog(print_r($ret,true)); return $ret; } diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php index 9002b708d..6de8fe89f 100644 --- a/Zotlabs/Module/Item.php +++ b/Zotlabs/Module/Item.php @@ -721,6 +721,7 @@ class Item extends Controller { ); } } + } if(($str_contact_allow) && (! $str_group_allow)) { @@ -1017,10 +1018,9 @@ class Item extends Controller { $datarray['layout_mid'] = $layout_mid; $datarray['public_policy'] = $public_policy; $datarray['comment_policy'] = map_scope($comment_policy); - $datarray['term'] = $post_tags; + $datarray['term'] = array_unique($post_tags, SORT_REGULAR); $datarray['plink'] = $plink; $datarray['route'] = $route; - // A specific ACL over-rides public_policy completely -- cgit v1.2.3 From 602ca91700fa314265b4f65a04465347e68f53db Mon Sep 17 00:00:00 2001 From: Mario Date: Sat, 18 Apr 2020 13:17:37 +0000 Subject: remove debug code --- Zotlabs/Lib/Activity.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index 5882a0777..24214072a 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -890,7 +890,7 @@ class Activity { $ret['to'] = array_values(array_unique(array_merge($ret['to'], $mentions))); } } -hz_syslog(print_r($ret,true)); + return $ret; } -- cgit v1.2.3 From c7fdc5379595d8829f916032c73f3126abfd37b4 Mon Sep 17 00:00:00 2001 From: Mario Date: Sat, 18 Apr 2020 13:59:30 +0000 Subject: more do not include hublocs with no hubloc_id_url --- Zotlabs/Lib/Activity.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index 24214072a..f3421c3fa 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -956,7 +956,7 @@ class Activity { // The xchan_url for mastodon is a text/html rendering. This is called from map_mentions where we need // to convert the mention url to an ActivityPub id. If this fails for any reason, return the url we have - $r = q("select * from hubloc where hubloc_id_url = '%s' limit 1", + $r = q("select * from hubloc where hubloc_id_url = '%s' and hubloc_id_url != '' limit 1", dbesc($url) ); -- cgit v1.2.3 From 109f9eed611949bdd1d03183e8e05d1236a38ac7 Mon Sep 17 00:00:00 2001 From: Mario Date: Sat, 18 Apr 2020 14:09:08 +0000 Subject: just fetch the info we need --- Zotlabs/Lib/Activity.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index f3421c3fa..f80f71a74 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -956,7 +956,7 @@ class Activity { // The xchan_url for mastodon is a text/html rendering. This is called from map_mentions where we need // to convert the mention url to an ActivityPub id. If this fails for any reason, return the url we have - $r = q("select * from hubloc where hubloc_id_url = '%s' and hubloc_id_url != '' limit 1", + $r = q("select hubloc_network, hubloc_hash, hubloc_id_url from hubloc where hubloc_id_url = '%s' and hubloc_id_url != '' limit 1", dbesc($url) ); -- cgit v1.2.3 From 42631b5943551d025c95e0b76e85471f4bde7101 Mon Sep 17 00:00:00 2001 From: Mario Date: Sat, 18 Apr 2020 14:22:40 +0000 Subject: revert --- Zotlabs/Lib/Activity.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index f80f71a74..613f939ab 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -956,7 +956,7 @@ class Activity { // The xchan_url for mastodon is a text/html rendering. This is called from map_mentions where we need // to convert the mention url to an ActivityPub id. If this fails for any reason, return the url we have - $r = q("select hubloc_network, hubloc_hash, hubloc_id_url from hubloc where hubloc_id_url = '%s' and hubloc_id_url != '' limit 1", + $r = q("select hubloc_network, hubloc_hash, hubloc_id_url from hubloc where hubloc_id_url = '%s' limit 1", dbesc($url) ); -- cgit v1.2.3 From ecb1c78173466b80660f56d95539e0d358e71cd4 Mon Sep 17 00:00:00 2001 From: Mario Date: Sun, 19 Apr 2020 14:17:00 +0000 Subject: make sure $post_tags is defined somewhere outsite a clause to prevent PHP warnings --- Zotlabs/Module/Item.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php index 6de8fe89f..83f99eecd 100644 --- a/Zotlabs/Module/Item.php +++ b/Zotlabs/Module/Item.php @@ -674,6 +674,8 @@ class Item extends Controller { $str_group_allow = $gacl['allow_gid']; $str_contact_deny = $gacl['deny_cid']; $str_group_deny = $gacl['deny_gid']; + + $post_tags = []; if($mimetype === 'text/bbcode') { @@ -708,7 +710,6 @@ class Item extends Controller { // Set permissions based on tag replacements set_linkified_perms($results, $str_contact_allow, $str_group_allow, $profile_uid, $parent_item, $private); - $post_tags = array(); foreach($results as $result) { $success = $result['success']; if($success['replaced']) { -- cgit v1.2.3 From f430a24fa38ea9d47d2916243e33d61b08f50f0f Mon Sep 17 00:00:00 2001 From: Mario Date: Sun, 19 Apr 2020 14:24:52 +0000 Subject: missing lib import --- Zotlabs/Module/Item.php | 1 + 1 file changed, 1 insertion(+) diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php index 83f99eecd..aa81c11e7 100644 --- a/Zotlabs/Module/Item.php +++ b/Zotlabs/Module/Item.php @@ -2,6 +2,7 @@ namespace Zotlabs\Module; +use Zotlabs\Lib\Config; use Zotlabs\Lib\IConfig; use Zotlabs\Lib\Enotify; use Zotlabs\Web\Controller; -- cgit v1.2.3 From 244936b0fd3ac60defa9fa6042e6ee3be869721c Mon Sep 17 00:00:00 2001 From: Mario Date: Mon, 20 Apr 2020 09:34:00 +0000 Subject: some work on improving federation of item_private and make sure we deal with an array in array_path_exists() --- Zotlabs/Lib/Activity.php | 9 +++++++-- Zotlabs/Lib/ActivityStreams.php | 13 +++++++++---- include/text.php | 2 +- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index 613f939ab..322579610 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -2339,8 +2339,13 @@ class Activity { $s['plink'] = $s['mid']; } - if ($act->recips && (! in_array(ACTIVITY_PUBLIC_INBOX,$act->recips))) - $s['item_private'] = 1; + // assume this is private unless specifically told otherwise. + + $s['item_private'] = 1; + + if ($act->recips && in_array(ACTIVITY_PUBLIC_INBOX, $act->recips)) { + $s['item_private'] = 0; + } if (is_array($act->obj)) { if (array_key_exists('directMessage',$act->obj) && intval($act->obj['directMessage'])) { diff --git a/Zotlabs/Lib/ActivityStreams.php b/Zotlabs/Lib/ActivityStreams.php index d8bd72943..b1ef59364 100644 --- a/Zotlabs/Lib/ActivityStreams.php +++ b/Zotlabs/Lib/ActivityStreams.php @@ -146,15 +146,20 @@ class ActivityStreams { */ function collect_recips($base = '', $namespace = '') { $x = []; + $fields = [ 'to', 'cc', 'bto', 'bcc', 'audience']; foreach($fields as $f) { $y = $this->get_compound_property($f, $base, $namespace); if($y) { - $x = array_merge($x, $y); - if(! is_array($this->raw_recips)) + if (! is_array($this->raw_recips)) { $this->raw_recips = []; + } - $this->raw_recips[$f] = $x; + if (! is_array($y)) { + $y = [ $y ]; + } + $this->raw_recips[$f] = $y; + $x = array_merge($x, $y); } } // not yet ready for prime time @@ -411,4 +416,4 @@ class ActivityStreams { } -} \ No newline at end of file +} diff --git a/include/text.php b/include/text.php index 5d1cf6eff..6f56a0754 100644 --- a/include/text.php +++ b/include/text.php @@ -3732,7 +3732,7 @@ function array_path_exists($str,$arr) { if($search) { foreach($search as $s) { - if($ptr && array_key_exists($s,$ptr)) { + if(is_array($ptr) && array_key_exists($s,$ptr)) { $ptr = $ptr[$s]; } else { -- cgit v1.2.3 From 03506bd6cf70719819f35a4979a759a7afee7eab Mon Sep 17 00:00:00 2001 From: Mario Date: Mon, 20 Apr 2020 11:58:08 +0000 Subject: use $mid as plink to prevent to long plinks --- Zotlabs/Module/Activity.php | 2 ++ Zotlabs/Module/Item.php | 10 ++++++---- include/event.php | 2 +- include/photos.php | 4 ++-- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Zotlabs/Module/Activity.php b/Zotlabs/Module/Activity.php index 93b5a15fc..9971ee60f 100644 --- a/Zotlabs/Module/Activity.php +++ b/Zotlabs/Module/Activity.php @@ -170,6 +170,8 @@ class Activity extends Controller { } + goaway(z_root() . '/item/' . argv(1)); + } } diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php index aa81c11e7..95359ccad 100644 --- a/Zotlabs/Module/Item.php +++ b/Zotlabs/Module/Item.php @@ -157,8 +157,9 @@ class Item extends Controller { } if(argc() > 1 && argv(1) !== 'drop') { - $x = q("select uid, item_wall, llink, mid from item where mid = '%s' ", - dbesc(z_root() . '/item/' . argv(1)) + $x = q("select uid, item_wall, llink, mid from item where mid = '%s' or mid = '%s' ", + dbesc(z_root() . '/item/' . argv(1)), + dbesc(z_root() . '/activity/' . argv(1)) ); if($x) { foreach($x as $xv) { @@ -955,8 +956,9 @@ class Item extends Controller { } if ((! $plink) && ($item_thread_top)) { - $plink = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . gen_link_id($mid); - $plink = substr($plink,0,190); + // $plink = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . gen_link_id($mid); + // $plink = substr($plink,0,190); + $plink = $mid; } if ($datarray['obj']) { diff --git a/include/event.php b/include/event.php index 6161175f6..82f6ca81e 100644 --- a/include/event.php +++ b/include/event.php @@ -1275,7 +1275,7 @@ function event_store_item($arr, $event) { // otherwise we'll fallback to /display/$message_id if($wall) - $item_arr['plink'] = z_root() . '/channel/' . $z[0]['channel_address'] . '/?f=&mid=' . gen_link_id($item_arr['mid']); + $item_arr['plink'] = $item_arr['mid']; else $item_arr['plink'] = z_root() . '/display/' . gen_link_id($item_arr['mid']); diff --git a/include/photos.php b/include/photos.php index 631660d7a..11dd07586 100644 --- a/include/photos.php +++ b/include/photos.php @@ -469,7 +469,7 @@ function photo_upload($channel, $observer, $args) { 'body' => $summary ]; - $arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . urlencode($arr['mid']); + $arr['plink'] = $mid; if($lat && $lon) $arr['coord'] = $lat . ' ' . $lon; @@ -850,7 +850,7 @@ function photos_create_item($channel, $creator_hash, $photo, $visible = false) { $arr['deny_cid'] = $photo['deny_cid']; $arr['deny_gid'] = $photo['deny_gid']; - $arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . urlencode($arr['mid']); + $arr['plink'] = $mid; $arr['body'] = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo['resource_id'] . ']' . '[zmg]' . z_root() . '/photo/' . $photo['resource_id'] . '-' . $photo['imgscale'] . '[/zmg]' -- cgit v1.2.3 From c71422e909a8583fcdbea4f1c4ada8c4e85a8f1a Mon Sep 17 00:00:00 2001 From: Mario Date: Mon, 20 Apr 2020 15:53:43 +0000 Subject: add the spec folder --- spec/HTTPSignatures/Home.md | 34 +++++++++ spec/OpenWebAuth/Home.md | 57 +++++++++++++++ spec/Zot6/Changelog.md | 16 +++++ spec/Zot6/Content.md | 15 ++++ spec/Zot6/Discovery.md | 112 +++++++++++++++++++++++++++++ spec/Zot6/Encryption+Signatures.md | 142 +++++++++++++++++++++++++++++++++++++ spec/Zot6/Home.md | 92 ++++++++++++++++++++++++ spec/Zot6/Message Types.md | 111 +++++++++++++++++++++++++++++ spec/Zot6/Messages.md | 135 +++++++++++++++++++++++++++++++++++ spec/Zot6/Nomadic Identity.md | 7 ++ 10 files changed, 721 insertions(+) create mode 100644 spec/HTTPSignatures/Home.md create mode 100644 spec/OpenWebAuth/Home.md create mode 100644 spec/Zot6/Changelog.md create mode 100644 spec/Zot6/Content.md create mode 100644 spec/Zot6/Discovery.md create mode 100644 spec/Zot6/Encryption+Signatures.md create mode 100644 spec/Zot6/Home.md create mode 100644 spec/Zot6/Message Types.md create mode 100644 spec/Zot6/Messages.md create mode 100644 spec/Zot6/Nomadic Identity.md diff --git a/spec/HTTPSignatures/Home.md b/spec/HTTPSignatures/Home.md new file mode 100644 index 000000000..3c1874f67 --- /dev/null +++ b/spec/HTTPSignatures/Home.md @@ -0,0 +1,34 @@ +### Encrypted HTTP Signatures + +draft-cavage-http-signatures-09 describes a method for providing public key signatures and authentication for HTTP requests. + +A fundamental limitation (flaw) of HTTP signatures is the fact that they often leak metadata of the originator of a communication via the 'keyId'. + +Encrypted HTTP signatures corrects this by encrypting the signature header. + +Encryption uses the public key of the receiving site. The content of the Signature or Authorization header after creating an HTTP signature is passed through an encryption function f(header,key,algorithm) with the public key of the remote site and a mutually agreed encryption algorithm which returns an encrypted structure containing + +key: a "random" string encrypted with the RSA public key of the remote site and base64_url encoded +iv: a "random" string encrypted with the RSA public key of the remote site and base64_url encoded (optional) +alg: the encryption algorithm used +data: the encrypted data, base64_url encoded +hmac: base64_url encoded hmac (optional) + +The header is generated by applying each field name followed by '=' followed by the double-quoted field value and fields separated by commas (the same as draft-cavage-http-signatures) + +The encryption is performed by encrypting the header string with the chosen algorithm using key and iv. Key and iv **may** be of a greater length than the specified algorithm permits. These strings are truncated to the desired key length and initialisation vector length prior to encryption, but transmitted in their entirety. Typically the random string is of length 256 octets and the key and iv are generally restricted to 16 or 32 octets (depending on the encryption algorithm used). There are no restrictions on the characters of the "random" octet string. + +Resulting header: + +```` +Signature: iv="d-uqkRoeXCoL1T5DU74ywizSM2RgsI9ZXWREKVg3_Qjd-mUWTJVGLq2hOQi3XKaa9Q7R6uB6UzlRmLfxBMZhVIxHjdNgfSRQ_oXafiSv8bZzMVKLZCjw6PfxBcljFs5gaQ7vEGuOVZ5nUaNEU7QX7WFr7BQKlev_6GFruv7HOsehGCokpyHHkKwrQ_4WJxUZp7o1ZhS1masPqMrEtUxDGfKwHfiHILuMdWDBvv2Xk4iHzlCi9fRVUEvzzFvv1rXsanjbaypZMIfSNj31kvsGfs6IyHpIaKbFqRs_iCxfujKDYh-2Dsg02bJTF1qx9BHJqLKNpfc0iReVe_xV2Qom3-SrJe1K8mRzYQJuOyyDuQk04GBlw7ken698JcwuS0G0OMfvGh5okq_0wM_O09iYumnJlEZT2a5nJ8ifc-kZfu8zdIPyAjJvS3a3KGEsytLxuUekFPVIpEoV2rmgOWz0TzDg-mIgwFffcx3kDa_WWhPGCFwVOzGU9um0KKStThKNXrbjYEAHVxD0gYXPgwmL8KayCo2A2s4bE2W8FfSURGu4Noqr9VsZ69Bcygzitv3aWCeIAk0y7kjJ0yQDfuIOjK1GP4HECq5NJIf8L3LJKw8QIBKm_0nx4gV9rLSAKCe3S63-D1tp9hafeiKQvGSwR0ybhxTJrhkcxd2nieVAyoA",key="Ca14lvjZua-ED8kXbedNLmrk6mRMHZm9NugcphyBMKEBo8MXLLnTsZchkAP-auWa0iJFKRwtdYUW_IGO-WX_qKZ8VNOslViveTYY-ybLTjQUj--YCFuURLYUWYTEmDcOImPWc8cQYGjTL_PN5X7vo7t3cm6rdV2W4tio2Rrmg3-cjhXBBRElr3GQKQ7i9ljBPs2YffoRsJ7f8DycKeyTv1T9xwr5lDklWOcOMTD4_39cZN2BI-b3AcGhBG4oYabUavW3BLGX7-SnezUcbTP3RyCVGI0ylVS8FmHSBZmW0oWfrVmz0oc0UcZYQMk8rb2WL_2ZdnzV_yZsjbBTFHG1ytIYyMeJsUU-pv4b4TodZmuDKT5UGtXPhm8Lsh-JpFo8xj5Yl15T9H6yLVHMR7Wzx_r2SvlJUsyqzBpaZE8DMd0zzrNZwgHQZ08wVHieKKO-TIqdypZHkxGGM68u2NPPW8-mXHgd_w9fUNM5fZRKPL9GxoVqoe9hx2f6CXPD95GAwjer9hbJcOmvxA1veXpIQzlkd-kEc8EuECaC50aUZJZbUIghYFo9NAA-UgNb26TyuY1OwE55MstPA6OO1sFki2u1G1T5JGWWgIOAziCcZbDYl1NPFWD2I1sV__rYeZ6XaaW4GXIVqD3wyBpmBRIoFx43gVDTISyUjhjUjjVHbZE",alg="aes256ctr",data="CLBNNE-tR1lRm0QL5gS86HyfwMs_16xKSSHTBP7MUEmRhGR00s0cdOfLC-PCZKlpG3ZRvc_lxnd53GGycNiTskisAb1mTbTrUBvk7hpDGNciUEB_7-hehjRiztmfi_oR-H0sCsVK9qDJdYepr4BYIgznVcB0uEN-POm97H4cTTVD8xCxLeEX0ArgDzgv_-Bq-nMcyht2LdGFl4Ej3bhEOhzvd-Xs1m6Z3E55dw0Bx7QDtkorvoetgMJrhgPKjYkIUWGoyVqa8MssvYIT8w9mpPDm4_QuVSNiPLIrKwQ3vob_hxcvENY-l0vXihdnpMzg81Sdk0E4FS4uQ9HtYSWsjOaFDSWRlxc-C5RhIvnHST4uEy3tjI--OHYQo2mFG2fWM3h8bYPq6r41W79qxsfmdSydmV1G5rFIqaz7gOa2JGOtW19WPJ8FTNFLVDehrFD6FJUy185gYyXosonp2EF3qlC8k_fzmazrzUrx0YmQ941870LJAwtEC7P-XiHV3dj-tZRYPgiSp7m8cMm7Z8WGgN8lLb61t5di5XS8zAv3FU1EAvvyL7PQhDi1U-s2cQXk3hXTNhOIymUYRhSV8NZrk80EsOrbPevSNQyYKXWCeUbnyhUznZQ3Lwq-UWAufcwrVY5uIJKeNu2lZ42xzSHWW3hn0ymcXzBOz7_wip9pSPY1nsTwApqTaIjURMEHhPvgaKRzNmuKbWP-d5Ihjeqw6JGXoAw0beWPJ4rqOlpQtn63deyBR5ylcRe4Ok2n03fZBnzJAobfZuHkiW93Yvc_byF-rpMJ3C8BSFYhGNDzYeRea3d9BEsqz_sr2HNpJyLhPssiZlZdjGRfqQ5UvCIJgT_NY57FoRCx4RHRpSxkjyF5XaKXW0_uNK7Oxk30qOCbIsLkQJqB2JIVrFFDBPITZIQVq2OamcBVk09OPuIMvsNBUTt2sxcZ7LVAA61ubv0jU39TcYO_OCs2eL7WaH7zDs9wHmxlwvzrPclduY5Gx2pwkrI_nb42j4Nc5imUkvzkIAhbYOB-XBClNVjFdEqYH35lziqEl9I6_w" +```` + +Decrypting the header reverses this process. + +- base64url_decode the key and iv and data fields and if used, the hmac field. +- use the site private key to decrypt key and iv +- apply decryption algorithm 'alg' to 'data' using 'key' and 'iv', truncating 'key' and 'iv' if necessary for the chosen algorithm. +- The end result is an HTTP Signature as sepcified in draft-cavage-http-signatures, process according to that document. + +Discovery of site public keys and algorithm negotiation is outside the scope of this document. \ No newline at end of file diff --git a/spec/OpenWebAuth/Home.md b/spec/OpenWebAuth/Home.md new file mode 100644 index 000000000..7d5adf449 --- /dev/null +++ b/spec/OpenWebAuth/Home.md @@ -0,0 +1,57 @@ +### OpenWebAuth + +OpenWebAuth provides a light-weight form of cross-domain authentication between websites on the open web. The principals involved in the authentication may or may not have any pre-existing relationship. + +OpenWebAuth utilises webfinger (RFC7033) and HTTP Signatures (draft-cavage-http-signatures-09) with a simple token generation service to provide seamless and interaction free authentication between diverse websites. + +For example, on website podunk.edu a member has made a video available privately to bob@example.com. In order for bob@example.com to verify his identity and view the protected video, he must establish proof of his identity to podunk.edu. + +At a high level, for an actor to visit another site as an authenticated viewer, he/she first redirects to a service which can create digital signatures on their behalf and which is provided a destination URL. This service must have access to their private signing key. + +The public key is stored on a webfinger compatible service or provided directly as a webfinger property. The webfinger resource is provided as the keyID of an HTTP Signature 'Authorization' header. + +There is very little concensus on providing public keys in webfinger except for the salmon magic-public-key. For those that prefer a higher level key format a property name of 'https://w3id.org/security/v1#publicKeyPem' MAY be used and although unofficial, aligns with JSON-LD and ActivityPub. Servers MAY look at other webfinger resources if there is no usable public key found in the webfinger JRD document. These discovery mechanisms are outside the scope of this document. + +A webfinger request to the baseurl of the destination URL returns an entry for an OpenWebAuth service endpoint. For example: + +```` +rel: https://purl.org/openwebauth/v1 +type: application/json +href: https://example.com/openwebauth +```` + +The redirector signs an HTTPS GET request to the OpenWebAuth endpoint using HTTP Signatures, which returns a json document: + +```` +{ + 'success': true, + 'encrypted_token': 'bgU50kUhtlMV5gKo1ce' +} +```` + +The 'token' is a single use access token; generally a unique hash value of 16 to 56 chars in length (this is consistent with RSA OAEP encryption using a 1024-bit RSA key). The resulting token will very likely be used in a URL, so the characters MUST be in the range of [a-zA-Z0-9]. To generate the 'encrypted\_token' this token is first encrypted with the actor's public key using RSA encryption, and the result base64\_url encoded. + +If the Signature cannot be validated, the OpenWebAuth service returns + +```` +{ + 'success': false, + 'message': 'Reason' +} +```` + +'message' is not required. + +If an encrypted_token is returned, this token is decrypted: + +base64\_url decode the 'encrypted_token' +decrypt this result with the RSA private key belonging to the original actor, which results in a plaintext 'token'. + +added to the destination URL as a query parameter 'owt' (OpenWebToken). + +303 https://example.com/some/url?owt=abc123 + + +The user's browser session is now redirected to the destination URL (with the token provided) and is authenticated and allowed access to various web resources depending on the permissions granted to their webfinger identity. + +Notes: All interactions MUST take place over https: transport with valid certificates. HTTP Signatures offer no protection against replay attacks. The OpenWebAuth token service MUST discard tokens after first use and SHOULD discard unused tokens within a few minutes of generation. \ No newline at end of file diff --git a/spec/Zot6/Changelog.md b/spec/Zot6/Changelog.md new file mode 100644 index 000000000..301ad48fa --- /dev/null +++ b/spec/Zot6/Changelog.md @@ -0,0 +1,16 @@ +### Changes + +2018-09-14 +Remove 'request' message type. To request missing conversation contents fetch the message 'id' attribute with an Accept header of 'application/x-zot+json'. An OrderedCollection of the containing conversation will be returned. The fetch should be signed by the requestor and permissions checked by the target server. + +2018-08-28 +Moved linked identity paragraph which was incorrectly placed in the 'nomadic considerations' text block. + +2018-08-17 +Clarify that ActivityStreams objects are assumed to have a default @context of "https://www.w3.org/ns/activitystreams" in the absence of a specific @context declaration. + +2018-08-16 +Added reference to encrypted HTTP Signatures, added pointer to reference implementation. + +2018-08-01 +Removed the "mail" message type and documented the "sync" message. \ No newline at end of file diff --git a/spec/Zot6/Content.md b/spec/Zot6/Content.md new file mode 100644 index 000000000..a3179ff16 --- /dev/null +++ b/spec/Zot6/Content.md @@ -0,0 +1,15 @@ +## Content + +Some Zot applications/implementations are able to support complex content structures including embedded apps, identity-aware content, and authenticated links. Not all implementations are required to support, or may be able to support these types of content. Additionally, the data serialisation formats supported by the implementation may affect the ability to fully represent rich identity-aware content. + +### ActivityStreams + +An implementation which only supports ActivityStreams2 will receive "message" content as type 'Article', and which contains a generic HTML rendering of the source content in the "content" or "contentMap" elements. This generic rendering is suitable for any observer and some HTML links may be inaccessible due to permission and authentication requirements. + +The source for the rendered HTML is available in the "source" element. Implementations which wish to support identity-aware content and authenticated links should use this source content (especially if it is type "text/x-zot-bbcode") to dynamically generate an HTML rendering that is specific to the current observer. + +The exact feature set supported by the implementation and local security filtering MAY result in rendered content that is empty. Implementations MAY choose not to display rendered content that is empty or MAY indicate to the observer that the content could not be rendered sucessfully. Implementations SHOULD provide a menu option to 'view source' and provide the ability to access the original content if the rendering results in empty content and is a mimetype that is determined to be safe to display in source format. + +### Zot + +The same considerations apply to content which uses the 'zot' serialisation. In this case, the content source is the "body" element and is of type "mimetype". The observer-neutral HTML rendering will be provided in the "html" element. \ No newline at end of file diff --git a/spec/Zot6/Discovery.md b/spec/Zot6/Discovery.md new file mode 100644 index 000000000..a5f3f01c8 --- /dev/null +++ b/spec/Zot6/Discovery.md @@ -0,0 +1,112 @@ +### Discovery + +Channel discovery is accomplished primarily through webfinger (RFC7033). You will be looking for an entry with + +```` +rel: http://purl.org/zot/protocol/6.0 +type: application/x-zot+json +href: (discovery rhef) +```` + +Load the url from the href using and HTTP Accept header of + +```` +Accept: application/x-zot+json; +```` + +This will provide a channel discovery document. It also includes a site discovery section. +You may load the site discovery packet separately by accessing the domain top level with + +```` +Accept: application/x-zot+json; +```` + +Sample channel discovery document + +```` +{ + "id": "XSWtrP_U65k9ZXeBxoYbcBgy6cVteo3yLwmLy4ppkjXqAryky0pYBq8YWnj7rApoTSdgy-QeciQG7Yhz7QYt4g", + "id_sig": "sha256.yG_Biu8pTpV0DlKPzoZHi0PbpM3okDYB7v5z2UuB___6J85gSOiOdes1tLwSmFkjbYMZbL2oksIe2tmD32lJWxpycSWJlDNbK8oggAtMx1sfVwyZOX_O0QBde2SxWCp0EIrRTRacIyKBzJhPRxCsGkc0uWin6XesXVZuYEVCxESr0KMT35Y79keOXGjJGv822C-Z2Nb4vphpbpftllGjxXOV70PxTNF0uZTWeVSmv2O0FGhkqBeBvBZU0FaWdYZqZZbd2AN_bto-8P95KMw6Fdfl2NIeL6vpD3xSu59Qhztl8L5npU13S3yvywzSvNg8DVgpNqcRmMiebaspfcjttCEAKtB2H-uiPkeuvDUk_iMXGtSUulcsNt1VFtSTnLEG371O6kj3dsczCV4QrpKBdIWNF3_41xHhrLi4Pug5JQg_wncyBSXu6Uj9pkCiD-JPVfI0ViCPccJcCKB-kXpP2EQIoPMhjV5x3bruI0TFLxrJqKWuoY6m8KUYrlGRdewaPYJ7pOY2NSNeLb9z6PO3UHT0bnr3DLyNxypxiUo5Pg4BxnHeuVKmiTxULF06KSwLmPDGsscrBSX1wIbHP6rhcmh0vDP0af2ixluLJbcLbptI2d137tFDVT4lTWBZ8PRNPWi1rfSl_x-dzevF8Dd3vi0iWd7D-aK89rqmRfUKsWk", + "aliases": [ + "acct:zapper@zap.macgirvin.com", + "https://zap.macgirvin.com/channel/zapper" + ], + "primary_location": { + "address": "zapper@zap.macgirvin.com", + "url": "https://zap.macgirvin.com/channel/zapper", + "connections_url": "https://zap.macgirvin.com/poco/zapper", + "follow_url": "https://zap.macgirvin.com/follow?f=&url=%s" + }, + "public_key": "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA154VVJChRzsdm1Aba4su\nMLhhnuTELsQCIuDGB07lxTlHmJeeD9eImLXzQPDTNlLcCYVQkKH1uPhfhwnBFITu\noG7hr9QklhFZxfe9IFiAfr7w1+IRFXCYEwfe/eAaYDoprNqtMxonniOiVvnAdNs+\nRi1/Bfasd9d4BRBSj2KwuAeTke7gvHlACRKmauhFmhfG2fzj53AL2ixsraBzdccR\nFseICC99eldgWk2Jg16J1Euh51HV56jUWoz4ZYb1Kxri8zf55R2GiQJCHvSCLlgl\noFaiHLPS+yBIlCkNZ+47ee6DL7ePJ6kip6+ukAP5/1vOM0ahTPANoqaWmg19RsOu\n2q0LPyotWyLX1JX13rtLC7qFlSST31gxY082G8QIJfEbWgYoci0g2XOCDjAZ3JjI\neK/+tZwLzrV5l5Tcorf35Kbc/lHbMSm+wuyc6eQV14tj542nUftZAgpkPOImHsRm\nLqwiG6uxpfIW0TFuUse45F/faYbV4pbcaGm7Hwp/MbXl8vNiE0LpV9eqZu5ryVjZ\nT+Vt32ISWuTeyLqB1Wb/lWP9lGsMFDtsiZ5Wst1W8omrAgYfYo4RxOr14TUiJg3T\nmgTg10fBrcTC210Kl0lKNkHGi0qK8LaB2QwuWy7FKgx5I7yhgWKSWLVFhIOlca/X\nFo0mxCf8NPqOrhxRdS6UeucCAwEAAQ==\n-----END PUBLIC KEY-----\n", + "name": "Zapper", + "name_updated": "2018-06-04 03:22:19", + "username": "zapper", + "photo": { + "url": "https://zap.macgirvin.com/photo/profile/l/2", + "type": "image/png", + "updated": "2018-06-04 03:22:19" + }, + "channel_role": "social", + "searchable": true, + "adult_content": false, + "public_forum": false, + "profile": { + "description": "", + "birthday": "", + "gender": "", + "marital": "", + "sexual": "", + "locale": "", + "region": "", + "postcode": "", + "country": "", + "about": "", + "homepage": "", + "hometown": "" + }, + "permissions": "view_stream,view_profile,view_contacts,view_storage,view_pages,view_wiki", + "permissions_for": "", + "locations": [ + { + "host": "zap.macgirvin.com", + "address": "zapper@zap.macgirvin.com", + "id_url": "https://zap.macgirvin.com/channel/zapper", + "primary": true, + "url": "https://zap.macgirvin.com", + "url_sig": "sha256.qBKZU6tReyUkVcNgGldRfdINiPoBneN9wWc-RHN7CFj8z9GgRW26LDUgmWL6kNoobYvHO6VIdZLxJb6CGdTLs7pjYGMZeTxpHHTgo3uHdBBIJdWPAwyEoppKGR3qT3S5iYWW9P0dsMtGjQ_q2VdaiqguoG8Z3lnTWikT7ujPI4NXZP2R0PVzEmaefN4SXqTO22XhXO-SuK4EOHylGcusQCfO6hXji9KItfwH1rnPx588YNRQ9WvBkV95ArZYSELRoFuJfHWh4ABqqAwQ4BqTO4-Pv1LiN1bWoNwVTki79Lx2GhQlw-_7HcHtVpqW_TQ04G4iPXvWHLzKfErfbGnQ575sbsF1gc2MYOINCofOmTq8eU_HOWaGK8D10HxpCVMMZXK37i8b6QEk3wpCoGiStGe5nsytVepZwNhsdnmW5msyO2ew_jZo3t_lP7U3oRvJHyJ7JpZBZg4E-MkLa-00KtiVGIosCesmFbZ042OwwTH3iJeSgz-yxrOsg3xdCj3e7rx4E93ra91OdN-mL-x8K4iYjcPQ6UpjInx2qsc_B8qwiw7L4jqJan7SYRDlxSiAb3yd1NFqL5gEMBg7RkS9s9a92hU3R6_K6CPpP9fb4mzRAzxpgRMpzhmBKnzlWF_Uz6c0urQpJLqJxfV4OzFThyxuqi3UyPg_R59DY3JfDUI", + "site_id": "gcwJ1OzIZbwtfgDcBYVYhwlUmjaxsgPyJezd-F2IS1F3IrlVsyOesNpm3hvoWemIBxoHmgIlMYKkhFeYihsqBQ", + "callback": "https://zap.macgirvin.com/zot", + "sitekey": "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuOkQslfq9EjZJLVniWP1\n3anzfdASnlgUKUYK1zyxy/HbwxAXl0GupYpJEVNIpkrGtTUMWY7ppxH7y/EAiSTJ\nsMIFIy1AnHgS/ecx6N/tH6rzZ68jD8yJQxjZBUk7MfhfYOK8KUti/qmp859Pr3cA\n1K1woCtSLRx2HNzHED8LDTUCGSwneHA2m7Ffc1MNfII8Ia/VtoF7pBwOixayws2N\nlY5syuDqOO3LtMJnDMBRN5WbtTw5jobyaoK6o4+Kg7Kln0nymloD3knFFsPvIdJd\nl493ItBi6k5QR0PV8NtElZMWRy8gZjzI5c4yukXku0WK1lVJBpjl4/LfjulWSMaR\nzj+YZKTjw7G56EEB2drNUVn/ZYLYwvMn6Bv/8lYu4VwL873UbupITNGX/Wh7+EU3\ntJqvXvdh8scIAKR3+sS/SrNI6OMn34HKewaX8iMf6NgW5lrskr9dhOYuZVr63huc\nhXxdGv+6b5+ARYEOpTn5QQd89WSL4Vui+1VaO4FARt9oRiC9s3sd0swyTxFqJSY4\nYJcpuVMUKY54jOGpsT0+1DlYFZf+lOk7pRpuYY1Vv/AhWCpkt6Uamf5d11rnVikA\nuPFgFqaObFenM1u1EKF1xrNaQqy3NbhOb0yRatVPcnAwOlesHbM7tgKmyZSopJTW\nJ2ug8isS+vNI0q+4IwET5FMCAwEAAQ==\n-----END PUBLIC KEY-----\n", + "deleted": false + } + ], + "site": { + "url": "https://zap.macgirvin.com", + "site_sig": "sha256.OWCNR-OocRwk2Y4RwSHtE-y_bcWPkXYPQetvRO-8VjO_b0eqwKDjvB52WPKKl43UQRHRn0CN6Xc487zPY7bVqIaLcsE23R0JOacl9IO3_9k-RbeH6H8KV8E_GynM-6cucPI1Dh7s3rgcqk-GXwoRaFaraurYDHvoGEVHOa1jHpv75lT2COCi1sDGhDR3-KPJbug61y58CUu3bJj2VRAqBaoiMz5TwbUIY9Sb22d205X_UzoIF_TlPDMoZv-Mbrkcxn9kgIfatgVyKGKKyoAnvyJeFzjHm1xCY4sZtt4C_em0a5wpcVPl31KbI5BodKn910ChErHXMCedBPeYWhRA0a-9Y_vYGonun3jXqJZ33WxzG9P1Gllp4bhxK6tm9X1iRpUnB7j8g8RHSH4PukQKSl2ErZ2vPLdHMIkczX9YEhhCbeZIvcX6T_5s82Ua75rlVktJGHsh8yLw3iqCdWljpCVhTWpEK0NmhJB6TcadE9qlRN9Gun7keEV4Ov6Dl5O7I-0ssoWbhv7lHU6JcjhAuf2TDLod_Izka32ZZk_8s8ZmqFzEEG6g6pRyuzvqk4XNK6cTL6dvBGIua5D0bRTBn4XTELx8u4B3yK7_MArr_m5Z5KfXOm_ngGMCN-lIZuKhxAQpCVDD5jcHmnCzjcDiR5m5LvOfvBSxwtpgHZUr3AU", + "post": "https://zap.macgirvin.com/zot", + "openWebAuth": "https://zap.macgirvin.com/owa", + "authRedirect": "https://zap.macgirvin.com/magic", + "sitekey": "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuOkQslfq9EjZJLVniWP1\n3anzfdASnlgUKUYK1zyxy/HbwxAXl0GupYpJEVNIpkrGtTUMWY7ppxH7y/EAiSTJ\nsMIFIy1AnHgS/ecx6N/tH6rzZ68jD8yJQxjZBUk7MfhfYOK8KUti/qmp859Pr3cA\n1K1woCtSLRx2HNzHED8LDTUCGSwneHA2m7Ffc1MNfII8Ia/VtoF7pBwOixayws2N\nlY5syuDqOO3LtMJnDMBRN5WbtTw5jobyaoK6o4+Kg7Kln0nymloD3knFFsPvIdJd\nl493ItBi6k5QR0PV8NtElZMWRy8gZjzI5c4yukXku0WK1lVJBpjl4/LfjulWSMaR\nzj+YZKTjw7G56EEB2drNUVn/ZYLYwvMn6Bv/8lYu4VwL873UbupITNGX/Wh7+EU3\ntJqvXvdh8scIAKR3+sS/SrNI6OMn34HKewaX8iMf6NgW5lrskr9dhOYuZVr63huc\nhXxdGv+6b5+ARYEOpTn5QQd89WSL4Vui+1VaO4FARt9oRiC9s3sd0swyTxFqJSY4\nYJcpuVMUKY54jOGpsT0+1DlYFZf+lOk7pRpuYY1Vv/AhWCpkt6Uamf5d11rnVikA\nuPFgFqaObFenM1u1EKF1xrNaQqy3NbhOb0yRatVPcnAwOlesHbM7tgKmyZSopJTW\nJ2ug8isS+vNI0q+4IwET5FMCAwEAAQ==\n-----END PUBLIC KEY-----\n", + "directory_mode": "normal", + "encryption": [ + "aes256ctr.oaep", + "camellia256cfb.oaep", + "cast5cfb.oaep" + ], + "zot": "6.1", + "register_policy": "closed", + "access_policy": "private", + "accounts": 1, + "channels": 3, + "admin": "mike@macgirvin.com", + "plugins": [], + "sitehash": "c89e5a2b5059d04cc05078899c2083d4b89c190e6d6b247300256bfc66a930b3", + "sitename": "Zap Development", + "sellpage": "", + "location": "", + "realm": "RED_GLOBAL", + "project": "zap", + "version": "6.6" + } +} +```` \ No newline at end of file diff --git a/spec/Zot6/Encryption+Signatures.md b/spec/Zot6/Encryption+Signatures.md new file mode 100644 index 000000000..b1071fcb4 --- /dev/null +++ b/spec/Zot6/Encryption+Signatures.md @@ -0,0 +1,142 @@ +### Encryption + +Sites provide in their site discovery document an array containing 0 or more encryption algorithms which it is willing to accept in order of preference. Sites sending encrypted documents MUST use this list to determine the most suitable algorithm to both parties. If a suitable algorithm cannot be negotiated, the site MAY fall back to plaintext (unencrypted data) but if the communications channel is not secured with SSL the sending site MUST NOT use plaintext and a receiving site MAY ignore or reject the communication if it contains private or sensitive information. + +If the receiving site does not support the provided algorithm, it MUST return error 400. + +Encrypted information is encapsulated into a JSON array/object with the following components: + +```` +'encrypted' => true +'key' => The encryption key, base64urlencoded +'iv' => The encryption initialisation vector, base64urlencoded +'alg' => The encryption algorithm used +'data' => The encrypted payload, base64urlencoded +```` + +The 'encrypted' boolean flag indicates this is a cryptographic structure and requires decryption to extract the information. 'alg' is required. Other elements may be change as necessary to support different mechanisms/algorithms. For instance some mechanisms may require an 'hmac' field. The elements shown support a wide variety of standard encryption algorithms. + +The key and iv are psuedo-random byte sequences, encrypted with the RSA public key of the recipient prior to base64urlencoding. The recipient key used in most cases (by default) will be the remote site public key. In certain circumstances (where specified) the RSA public key will be that of the target channel or recipient. + +Both 'key' and 'iv' MAY be padded to 255 chars. The amount of padding necessary is dependent on the encryption algorithm. The incoming site MUST strip additional padding on both parameters to limit the maximum length supported by the chosen algorithm. For example, the aes256cbc algorithm (not recommended) uses a key length of 32 bytes and an iv of 16 bytes. + +Algorithm names for common algorithms are the lowercase algorithm names used by the openssl library with punctuation removed. The openssl 'aes-256-ctr' algorithm for example is designated as 'aes256ctr'. + +Uncommon algorithms which are unsupported by openssl may be used, but the exact algorithm names are undefined by this document. + + +### Signatures + +Identity provenance is provided using HTTP Signatures (draft-cavage-http-signatures-10 is the relevant specification currently). If using encrypted transport, the HTTP Signature MAY be encrypted using the same negotiated algorithm as is used in the message for envelope protection. See [HTTP Signatures](spec/HTTPSignatures/Home). If a site uses negotiated encryption as described in the preceding section, it MUST be capable of decrypting the HTTP Signatures. + +In several places of the communications where verification is associated with a third party which is not the sender of the relevant HTTP packet, signed data/objects are specified/required. Two signature methods may be used, depending on whether the signed data is a single value or a JSON object. The method used for single values is referred to here as SimpleSignatures. The object signature method used is traditionally known as salmon magic signatures, using the JSON serialisation. + +#### Simple Signatures + +A data value is signed with an appropriate RSA method and hash algorithm; for instance 'sha256' which indicates an RSA keypair signature using the designated RSA private key and the 'sha256' hash algorithm. The result is base64url encoded, prepended with the algorithm name and a period (0x2e) and sent as an additional data item where specified. + +```` +"foo_sig": "sha256.EvGSD2vi8qYcveHnb-rrlok07qnCXjn8YSeCDDXlbhILSabgvNsPpbe..." +```` + +To verify, the element content is separated at the first period to extract the algorithm and signature. The signature is base64urldecoded and verified using the appropriate method and hash algorithm using the designated RSA public key (in the case of RSA signatures). The appropriate key to use is defined elsewhere in the Zot protocol depending on the context of the signature. + +Implementations MUST support RSA-SHA256 signatures. They MAY support additional signature methods. + +#### Salmon "magic envelope" Signatures with JSON serialisation. + +```` +{ + "signed": true, + "data": "PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGl...", + "data_type": "application/x-zot+json", + "encoding": "base64url", + "alg": "RSA-SHA256", + "sigs": [ + { + "value": "EvGSD2vi8qYcveHnb-rrlok07qnCXjn8YSeCDDXlbhILSabgvNsPpbe...", + "key_id": "4k8ikoyC2Xh+8BiIeQ+ob7Hcd2J7/Vj3uM61dy9iRMI" + } + ] +} +```` + +The boolean "signed" element is not defined in the magic envelope specification. This is a boolean flag which indicates the current element is a signed object and requires verification and unpacking to retrieve the actual element content. + +The signed data is retrieved by unpacking the magic signature 'data' component. Unpacking is accomplished by stripping all whitespace characters (0x0d 0x0a 0x20 and 0x09) and applying base64 "url" decoding. + +The key_id is the base64urlencoded identifier of the signer, which when applied to the Zot 'Discovery' process will result in locating the public key. This is typically the server base url or the channel "home" url. Webfinger identifiers (acct:user@domain) MAY also be used if the resultant webfinger document contains a discoverable public key (salmon-public-key or Webid key). + +Verification is performed by using the magic envelope verification method. First remove all whitespace characters from the 'data' value. Append the following fields together separated with a period (0x2e). + +data . data_type . encoding . algorithm + +This is the "signed data". Verification will take place using the RSA verify function using the signed data, the base64urldecoded sigs.value, algorithm specified in 'alg' and the public key found by performing Zot discovery on the base64urldecoded sigs.key_id. + +When performing Zot discovery for keys, it is important to verify that the principal returned in the discovery response matches the principal in the key_id and that the discovery response is likewise signed and validated. + +Some historical variants of magic signatures emit base64 url encoding with or without padding. + +In this specification, encoding MUST be the string "base64url", indicating the url safe base64 encoding as described in RFC4648, and without any trailing padding using equals (=) characters + +The unpacked data (once verified) may contain a single value or a compound object. If it contains a single value, this becomes the value of the enclosing element. Otherwise it is merged back as a compound object. + +Example: source document + +```` +{ + "guid": { + "signed": true, + "data": "PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGl...", + "data_type": "application/x-zot+json", + "encoding": "base64url", + "alg": "RSA-SHA256", + "sigs": [ + { + "value": "EvGSD2vi8qYcveHnb-rrlok07qnCXjn8YSeCDDXlbhILSabgvNsPpbe...", + "key_id": "4k8ikoyC2Xh+8BiIeQ+ob7Hcd2J7/Vj3uM61dy9iRMI" + } + ] + }, + "address": "foo@bar" +} +```` + +Decoding the data parameter (and assuming successful signature verification) results in + +```` +"abc12345" +```` + +Merging with the original document produces + +```` +{ + "guid": "abc12345", + "address": "foo@bar" +} +```` + +Example using a signed object containing multiple elements: + + +Decoding the data parameter (and assuming successful signature verification) results in + +```` +{ + "guid": "abc12345", + "name": "Barbara Jenkins" +} +```` + +Merging this with the original document produces + +```` +{ + "guid": { + "guid": "abc12345", + "name": "Barbara Jenkins" + }, + "address": "foo@bar" +} +```` \ No newline at end of file diff --git a/spec/Zot6/Home.md b/spec/Zot6/Home.md new file mode 100644 index 000000000..8b563dc7b --- /dev/null +++ b/spec/Zot6/Home.md @@ -0,0 +1,92 @@ +## Zot/6 aka Zot 2018 + +This document describes version 6 of the Zot protocol. This is a living document, last modified on 2018-09-14. + +Zot is a **WebMTA** which provides a decentralised identity and communications protocol using HTTPS/JSON. + +Earlier revisions of the zot protocol dealt with the creation of nomadic identities and cross-domain authentication to enable a decentralised network with features rivaling large centralised providers. + +Zot/6 builds on those concepts and streamlines many of the interactions, applying lessons learned over decades of building decentralised systems. + +A reference implementation (a social media app) is provided at https://framagit.org/zot/zap ; the reference implementation is not yet 100% spec compliant or complete but may be used for basic compatibility testing and development purposes. + +### Differences from earlier Zot versions + +1. Streamlined communications using direct (push) transfer. Earlier versions used a 'notify/pickup' delivery model. +2. The authentication component (Magic-Auth) has been spun off into a separate and independent specification ['OpenWebAuth'](spec/OpenWebAuth/Home.md). +3. Inclusion of ActivityStreams (JSON-LD) as a supported (primary) serialisation. +4. Dropping the requirements for implementations to support secondary serialisations. +5. Moving service discovery to "Accept-header" based service endpoints; where different representations can be selected by modification of the HTTPS request Accept: header. +6. Separation of the "portable-id" from the signature algorithm used. + +### Differences between Zot and other WebMTA services + +Zot is architecturally different from other HTTPS-based "social communications" protocols such as OStatus, ActivityPub, and Diaspora. The primary differences are: + +1. Support for nomadic identity where an identity is not permanently bound to a DNS server. +2. MUAs built on top of Zot are able to use and express detailed cross-domain permissions. +3. Encryption negotation for additional message protection in addition to HTTPS +4. Zot does not define an absolute payload format for content. Implementations MUST support ActivityStreams. Additional message types and serialisation formats MAY provide vendor specific enhancements. +5. Federation between other WebMTA protocols is not hampered by unnecessary restrictions on 3rd party communications. Messages from incompatible systems may be relayed to other sites which do not support the 3rd party protocol. +6. Detailed delivery reporting is provided for auditing and troubleshooting; which is critical in a distributed communications service. + +## Fundamental Concepts + +### Identity + +In Zot/6 decentralised identity is a core attribute of the system. This is somewhat different than (and often incompatible with) typical decentralised or P2P communications projects where messaging is the key component and identity is merely an atribution on a message. + +An identity consists of two primary components. + +1. An identity "claim" which is essentially a text string +2. An RSA based key with which to verify that identity + +These will be described in detail later. A "valid" identity is an identity which has been verified using public key cryptographic techniques. Until verified, an identity is "claimed" (unverified). An "invalid" identity is an identity which failed verification or has been revoked. + +In Zot/6 claimed (unverified) identities are allowed to exist, and MAY be allowed permissions to perform operations. This allows cross communication to occur between Zot/6 and other communications systems with different concepts of identity and different methods of validation. + +An implementation MAY choose to block unverified identities. This may significantly reduce the ability to interact with foreign systems, protocols, and networks. + +Invalid identities MUST NOT be allowed any permissions. By definition they are forgeries or have been revoked. + +### Channels + +Channels are what would typically be referred to as "users" on other systems, although this notion is abstracted in Zot/6 and can take on other meanings. A channel is represented as an identity. + +### Location and Location Independence + +A location is an identity and follows all the rules associated with an identity. The identity claim is typically the fully qualified "base" URL of the web service providing Zot/6 services, lowercased, with trailing slashes removed. International domain names are converted to "punycode". + +A "location independent identity" is a valid channel identity paired with a valid location, with the additional property that the channel/location pair has been validated using the key of the channel identity. In layman's terms, the user signs his/her website with their own key. + +The location independent model allows mobility of a channel to different or even multiple locations; which may all be valid simultaneously. This is referred to elsewhere as a "nomadic" identity. + +For the benefit of those that are new to this concept, this means in basic terms that a channel can appear at any location at any time as long as the location independent identity is valid. + +### Linked identities + +One or more channels/identities can be linked. In simple terms, this allows us to define a persistent token which refers to an identity whose claim string or public key (or both) have changed or is being merged from another system. In order to link identities, in addition to the requirement that both identities MUST validate, each identity MUST sign the other linked identity and all of these signatures MUST be valid. + +In the case of providing controlled access to website resources, all linked identities SHOULD be folded or reduced to a common identifier so that an attempt to access a protected resource by any linked identity will succeed for a resource that has been made available to any of its linked identities. + +### Nomadic Considerations + +The location independent properties of Zot/6 place additional requirements on communications systems. For outgoing messages, delivery SHOULD be attempted to every linked and nomadic location associated with the identity being delivered to. The service MAY remove a valid linked or nomadic location from the list of delivery targets if the location is marked "dead" or "unreachable". A location MAY be marked "dead" or "unreachable" for a number of reasons; generally by a failure to communicate for more than 30 days, or by manual means if the site administrator has reason to believe the location has been shut down permanently. A site SHOULD retain the location record and automatically remove the "dead" or "unreachable" designation if valid communications are initiated from that location in the future. + + +## Transport and Protocol Basics + +Communications with Zot/6 take place primarily with https JSON requests. Requests are signed by the "sender" using HTTP-Signatures (draft-cavage-http-signatures-xx). If a payload is present, the signed headers MUST include a Digest header using SHA-256 or SHA-512 as specified in RFC5843. + +For HTTP requests which do not carry a payload any headers may be signed, but at least one header MUST be signed. + +Signed requests MAY be rejected if + +- Digest verification fails +- The request has a JSON payload and the content is not signed +- public-key retrieval fails + + +Public keys are retrieved by utilising Zot/6 Discovery on the signature data's 'keyId' property and retrieving the public key from that document. Zot Discovery is described in a later chapter. + +Receivers verifying these requests SHOULD verify that the signer has authority over the data provided; either by virtue of being the author or a relay agent (sender). \ No newline at end of file diff --git a/spec/Zot6/Message Types.md b/spec/Zot6/Message Types.md new file mode 100644 index 000000000..e4e0f1c22 --- /dev/null +++ b/spec/Zot6/Message Types.md @@ -0,0 +1,111 @@ +## Message Types + +### purge + +This message type has no payload or encoding. If the message has recipients, it is considered an "unfriend" action. The sender's relationship with the recipients is severed. The exact actions which are undertaken as a result are implementation specific. Generally, the sender's permissions to the receiver are revoked. Permissions granted to the sender by the receiver MAY be left intact. + +If the message has no recipients, it is considered a notification that the sender identity no longer exists. Receiving sites MUST mark the channel as unavailable and discontinue further communications. They SHOULD destroy all public content attributed to the sender and MAY remove the connection and private content from connected channels. + +The response to this message is + +```` +{ + 'success': true +} +```` + +or + +```` +{ + 'success': false, + 'message': 'optional error message or reason' +} +```` + +Servers SHOULD only provide a single recipient when using a targetted message as the single return response may be ambiguous. + + +### refresh + +This message has no payload or encoding. It is a message to the receiving server that important channel information has changed. The receiving server MUST process a zot discovery operation and update any locally stored information which has changed. If the message has recipients, this action should be accomplished by the receiver using a signed discovery fetch, signed by the recipient. Response is the same as for a purge message. + +A targetted refresh message (e.g. containing recipients) is commonly used to indicate a change in permissions granted to the recipient by the sender. If no permissions have previously been associated with this sender, it is considered to be a "friend request", meaning some permissions are available which may not have been available before. The receiving channel SHOULD store the updated permissions and SHOULD add the sender to the receiver's known connections. It MAY notify the recipient that a new friend exists and put the connection into a 'pending' state until the request has been reviewed/accepted/rejected by the recipient. + +### rekey + +The rekey message is sent with no recipients. The message indicates the sender has changed their public key. The key change MUST be signed with both the old and new private keys and these signatures MUST validate or the operation MUST fail. +The 'update' boolean flag (if present) indicates the old portable\_id associated with that key is to be changed, and the old portable\_id discarded. If 'update' is false, a new portable\_id is generated and the old and new identities are linked. This means that both portable\_id's are valid nomadic identifiers for the same channel. Response is the same as for the purge message. + +### activity + +Message encoding: activitystreams + +This message type is used for normal communications. If recipients are specified, the message is private. If no recipients are specified, the message is public. The recipient list MAY be filtered and contain only the recipients which are known to be available on the receiving website. A message MUST NOT be sent by the sender if the message is private and the recipient list for a particular receiving site is empty. + + +```` +{ + "type": "Create", + "id": "https://example.org/item/e8a20a21dd7e0d8d2207a5df62d2168d65db7db08f1a91ca", + "published": "2018-06-25T04:14:08Z", + "actor": "https://example.org/channel/zapper", + "object": { + "type": "Article", + "id": "https://example.org/item/e8a20a21dd7e0d8d2207a5df62d2168d65db7db08f1a91ca", + "published": "2018-06-25T04:14:08Z", + "content": "just another Zot6 message", + "actor": "https://example.org/channel/zapper", + }, +} +```` + +Upon delivery, a delivery report is generated and returned to the sending site. + +```` +{ + "success": true, + "delivery_report": [ + { + "location": "https://zap.macgirvin.com", + "sender": "T5ni0wUAlYmyqlibiTQiS54PqLKXCL7XAJhOSKeJMqEXeKn46AkDPdCJZ4JUA05Vlhux25OLTkBPyV7L60JmyQ", + "recipient": "xxWsqvZp3w-sr3FXrmb6wxmKZx6khMLjBCOafPdRT1lWzYmCPHeaDDBD9KwOqpOAt4lezIFQbyaLt9I3H54M9Q", + "name": "System", + "message_id": "https://zap.macgirvin.com/item/ebaa483a2e8331a21a68b9d9e4a72a079c260162d2e37edc", + "status": "posted", + "date": "2018-06-26 05:19:48" + }, + { + "location": "https://zap.macgirvin.com", + "sender": "T5ni0wUAlYmyqlibiTQiS54PqLKXCL7XAJhOSKeJMqEXeKn46AkDPdCJZ4JUA05Vlhux25OLTkBPyV7L60JmyQ", + "recipient": "wXDz7WR51QHwAORaVR8-0wff06LBtBWvhd_zfDWTYEzaqaPfJ_fsK7nRaM4aVeKPmZklUAgtqs09zUzitwNT2w", + "name": "Zapper", + "message_id": "https://zap.macgirvin.com/item/ebaa483a2e8331a21a68b9d9e4a72a079c260162d2e37edc", + "status": "posted", + "date": "2018-06-26 05:19:48" + }, + { + "location": "https://zap.macgirvin.com", + "sender": "T5ni0wUAlYmyqlibiTQiS54PqLKXCL7XAJhOSKeJMqEXeKn46AkDPdCJZ4JUA05Vlhux25OLTkBPyV7L60JmyQ", + "recipient": "T5ni0wUAlYmyqlibiTQiS54PqLKXCL7XAJhOSKeJMqEXeKn46AkDPdCJZ4JUA05Vlhux25OLTkBPyV7L60JmyQ", + "name": "Bopper", + "message_id": "https://zap.macgirvin.com/item/ebaa483a2e8331a21a68b9d9e4a72a079c260162d2e37edc", + "status": "update ignored", + "date": "2018-06-26 05:19:48" + } + ] +} +```` + +### response + +The response message is used to send a comment or reply "upstream" to the sender. The sender will then redeliver the message to all downstream recipients. It is the same as the activity message type except that it MUST have one and only one recipient - the sender of the activity that this activity references. This entire activity SHOULD be signed by the sender of the response and encapsulated as a json salmon magic envelope as described in [[Encryption/Signatures]]. + + +### sync + +Sync messages are used between nomadic clones to synchronise changed data structures. These messages are sent to nomadic instances of the sender as private messages and SHOULD be encrypted with additional encryption beyond HTTPS transport encryption as the messages may contain private keys. + +Implementations MAY provide sync messages and they MAY attempt to co-exist with sync packets created by other implementations. If their data synchronisation needs cannot be mapped to the sync structures provided by others, they MUST provide a unique encoding type (recommend the name of the implementation using only the letters [a-z] of the US-ASCII character set). If a site receives a sync message with an unknown encoding and data format it MUST be ignored. + +Basic sync information includes any local changes to personal settings, profile settings, and changes in the social graph. Implementations MAY support syncing of all available information including uploaded files and photos, events, and other application specific data structures. \ No newline at end of file diff --git a/spec/Zot6/Messages.md b/spec/Zot6/Messages.md new file mode 100644 index 000000000..744f0a4c0 --- /dev/null +++ b/spec/Zot6/Messages.md @@ -0,0 +1,135 @@ +### Messages + +Zot6 is primarily a transport and identification format. The semantics of message content are in many respects outside the scope of this document. Sites/servers MUST provide in their site discovery document what standardised message formats are acceptable. + +The message formats of primary concern to us are + +1. ActivityStreams (ActivityStreams JSON-LD) (format='activitystreams') +2. Zot (format='zot') + +When using ActivityStreams JSON-LD, a default @context of "https://www.w3.org/ns/activitystreams" is assumed. Activity objects only need to specify or provide a @context declaration if there are differences from the default. + +### Delivery + +Delivery is a POST of envelope+data to the zot endpoint. HTTP Signatures are used to validate the sender. + +Delivery reports for private messages SHOULD be encrypted and MUST include results for host blocking. + +Here is a sample activity... + +```` +{ + "type": "activity", + "encoding": "activitystreams", + "sender": "wXDz7WR51QHwAORaVR8-0wff06LBtBWvhd_zfDWTYEzaqaPfJ_fsK7nRaM4aVeKPmZklUAgtqs09zUzitwNT2w", + "site_id": "gcwJ1OzIZbwtfgDcBYVYhwlUmjaxsgPyJezd-F2IS1F3IrlVsyOesNpm3hvoWemIBxoHmgIlMYKkhFeYihsqBQ", + "recipients": { + "xxWsqvZp3w-sr3FXrmb6wxmKZx6khMLjBCOafPdRT1lWzYmCPHeaDDBD9KwOqpOAt4lezIFQbyaLt9I3H54M9Q", + "T5ni0wUAlYmyqlibiTQiS54PqLKXCL7XAJhOSKeJMqEXeKn46AkDPdCJZ4JUA05Vlhux25OLTkBPyV7L60JmyQ", + }, + "version": "6.0", + "data": { + "type": "Create", + "id": "https://example.org/item/e8a20a21dd7e0d8d2207a5df62d2168d65db7db08f1a91ca", + "published": "2018-06-25T04:14:08Z", + "actor": "https://example.org/channel/zapper", + "object": { + "type": "Article", + "id": "https://example.org/item/e8a20a21dd7e0d8d2207a5df62d2168d65db7db08f1a91ca", + "published": "2018-06-25T04:14:08Z", + "content": "just another Zot6 message", + "actor": "https://example.org/channel/zapper", + }, + }, +} +```` + +The sender\_id is the portable\_id of the sender. The site\_id is the portable\_id of the sender's website. +Recipients is a simple array of portable_id's of message recipients. This list MAY be filtered and contain only the recipients which are known to be available on the receiving website. + +Version is provided to resolve potential protocol version differences over time. Data contains the actual ActivityStreams (in this case) payload. + + +Receiving sites MUST verify the HTTP Signature provided and MUST reject (error 400) posts with no signature or invalid signatures. Discovery is used during the verification process and generates a locally stored portable\_id. If the portable\_id does not match the verified signer's portable\_id, the message MUST be rejected (error 400). + +If the recipients field is non-existent or empty, the post is considered public and may be delivered to any channel following the sender. If the recipient field has contents, the message MUST NOT be delivered to anybody except the listed recipients. + +Upon successful delivery, a delivery report is generated and returned to the sending site. + +```` + { + "success": true, + "delivery_report": [ + { + "location": "https://zap.macgirvin.com", + "sender": "T5ni0wUAlYmyqlibiTQiS54PqLKXCL7XAJhOSKeJMqEXeKn46AkDPdCJZ4JUA05Vlhux25OLTkBPyV7L60JmyQ", + "recipient": "xxWsqvZp3w-sr3FXrmb6wxmKZx6khMLjBCOafPdRT1lWzYmCPHeaDDBD9KwOqpOAt4lezIFQbyaLt9I3H54M9Q", + "name": "System ", + "message_id": "https://zap.macgirvin.com/item/ebaa483a2e8331a21a68b9d9e4a72a079c260162d2e37edc", + "status": "posted", + "date": "2018-06-26 05:19:48" + }, + { + "location": "https://zap.macgirvin.com", + "sender": "T5ni0wUAlYmyqlibiTQiS54PqLKXCL7XAJhOSKeJMqEXeKn46AkDPdCJZ4JUA05Vlhux25OLTkBPyV7L60JmyQ", + "recipient": "wXDz7WR51QHwAORaVR8-0wff06LBtBWvhd_zfDWTYEzaqaPfJ_fsK7nRaM4aVeKPmZklUAgtqs09zUzitwNT2w", + "name": "Zapper ", + "message_id": "https://zap.macgirvin.com/item/ebaa483a2e8331a21a68b9d9e4a72a079c260162d2e37edc", + "status": "posted", + "date": "2018-06-26 05:19:48" + }, + { + "location": "https://zap.macgirvin.com", + "sender": "T5ni0wUAlYmyqlibiTQiS54PqLKXCL7XAJhOSKeJMqEXeKn46AkDPdCJZ4JUA05Vlhux25OLTkBPyV7L60JmyQ", + "recipient": "T5ni0wUAlYmyqlibiTQiS54PqLKXCL7XAJhOSKeJMqEXeKn46AkDPdCJZ4JUA05Vlhux25OLTkBPyV7L60JmyQ", + "name": "Bopper ", + "message_id": "https://zap.macgirvin.com/item/ebaa483a2e8331a21a68b9d9e4a72a079c260162d2e37edc", + "status": "update ignored", + "date": "2018-06-26 05:19:48" + } + ] +} +```` + +### Followups + +Followups to any post (replies, likes, reactions, etc.) MUST be sent as a private activity (single recipient) to the sender of the original with a message type 'response'. This is referred to as an "upstream delivery". + +Additionally these activities MUST provide an 'inReplyTo' element set to the id of the activity that is the object of the response. Implementations SHOULD support multi-level likes. Servers MAY support multi-level comments. + + +The original sender MUST resend the followups to all of the original message recipients using message type 'activity'. This is referred to as a "downstream delivery". + +This single-source mechanism ensures the original sender's privacy settings are respected and conversations are kept intact for all recipients of the original message. + +### Multi-level support + +If multi-level comments are not supported, the receiver MUST re-parent the multi-level comment to the original conversation head, flattening the conversation to one level. The receiving server SHOULD store the correct target id, even if the comment is re-parented. + +If multi-level likes are not supported, the incoming "like" activity for a non-parent conversation node MAY be dropped or rejected, rather than being re-parented to a potentially unrelated activity than was intended. + + +### Portable IDs + +Portable IDs are intended to provide location-independent identifiers across the network. A portable ID can only be trusted if it has been verified or if it has been generated on this site. Verification is performed during the [[Discovery]] process. + +The portable ID uses an obtained public\_key. In order to ensure that the portable ID calculation is portable, the calculation MUST be performed on a PKCS#8 public\_key. If the source key is provided in another format (such as a salmon [modulus/exponent] key or PKCS#1), the key MUST be converted to PKCS#8 format prior to calculating the portable ID. + +Here are the steps for generating a portable\_id. + +1. Via [[Discovery]], obtain the zot-info packet for a network URI. The network URI will often be presented as the signer keyID in a signed message, but may also be provided as an acct: URI submitted by site users. +2. The zot-info packet contains everything necessary to verify an identity claim. +3. With the public\_key, verify the id\_sig against the id (claimed identity). +4. With the public\_key, verify the location->url\_sig against the location->url **for the discovery site**. +5. With site->sitekey, verify the site->site\_sig against the site->url. +6. Concatenate the id and the public\_key. Perform a whirlpool hash function on this concatenated string and base64_url encode the result. +7. This is the channel portable\_id. +8. Concatenate the url and site->sitekey. Perform a whirlpool hash function on this concatenated string and base64_url encode the result. +9. This is the portable site\_id. Check that it matches the location->site\_id in the discovery packet. +10. If any verification step fails, discard the results and return an error. + +This is generally considered an expensive calculation; hence the results should be stored as a channel/location pair. + +When receiving a Zot message, use the stored results (if available) for the signer's keyID when checking the HTTP Signature. If the HTTP Signature validates, and the keyID matches the location->id\_url for this location, and the sender portable\_id matches the calculated/stored portable\_id for this channel/location pair, the nomadic sender has been validated. + +If no stored results are available for the keyID, perform [[Discovery]] as described. \ No newline at end of file diff --git a/spec/Zot6/Nomadic Identity.md b/spec/Zot6/Nomadic Identity.md new file mode 100644 index 000000000..188707f62 --- /dev/null +++ b/spec/Zot6/Nomadic Identity.md @@ -0,0 +1,7 @@ +### Nomadic Identity + +One of the fundamental differences between Zot and other messaging systems/protocols is the support for nomadic identity. This simply means that your identity (who you are) is independent from the server you are posting from (where you are). + +As a consequence, a person can have any number of active locations. Implementations which support nomadic identity MUST send a copy of all communications destined for that identity to all known active locations. If sites receive a communication from the given identity from any location, they MUST validate that the identity has authorised this location and (if verification is successful) deliver it appropriately. They SHOULD store the newly verified location and MAY subsequently used the stored information rather than re-validating. + +When an identity modifies its location information, it MUST send a 'refresh' packet to all known sites where they maintain connections and which are capable of nomadic operation. The 'refresh' message informs the other site to perform network discovery and update all stored information related to the identity which may have changed. \ No newline at end of file -- cgit v1.2.3 From a9d52cd296f93a125ecfdb1e4e83774d002b7a31 Mon Sep 17 00:00:00 2001 From: OJ Random Date: Mon, 20 Apr 2020 17:56:52 +0200 Subject: fixes for real intallation for debian 10 --- .homeinstall/hubzilla-setup.sh | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/.homeinstall/hubzilla-setup.sh b/.homeinstall/hubzilla-setup.sh index f1395e8ce..063cfcea4 100755 --- a/.homeinstall/hubzilla-setup.sh +++ b/.homeinstall/hubzilla-setup.sh @@ -235,7 +235,7 @@ function install_sendmail { function install_php { # openssl and mbstring are included in libapache2-mod-php print_info "installing php..." - nocheck_install "libapache2-mod-php php php-pear php-curl php-gd php-mysqli php-mbstring php-xml php-zip" + nocheck_install "libapache2-mod-php php php-pear php-curl php-gd php-mbstring php-xml php-zip" sed -i "s/^upload_max_filesize =.*/upload_max_filesize = 100M/g" /etc/php/7.3/apache2/php.ini sed -i "s/^post_max_size =.*/post_max_size = 100M/g" /etc/php/7.3/apache2/php.ini } @@ -322,7 +322,7 @@ function run_freedns { then die "You can not use freeDNS AND selfHOST for dynamic IP updates ('freedns_key' AND 'selfhost_user' set in $configfile)" fi - wget --no-check-certificate -O - https://freedns.afraid.org/dynamic/update.php?$freedns_key + wget --no-check-certificate -O - http://freedns.afraid.org/dynamic/update.php?$freedns_key fi } @@ -389,8 +389,8 @@ function configure_cron_freedns { # - every 30 minutes if [ -z "`grep 'freedns.afraid.org' /etc/crontab`" ] then - echo "@reboot root https://freedns.afraid.org/dynamic/update.php?$freedns_key > /dev/null 2>&1" >> /etc/crontab - echo "*/30 * * * * root wget --no-check-certificate -O - https://freedns.afraid.org/dynamic/update.php?$freedns_key > /dev/null 2>&1" >> /etc/crontab + echo "@reboot root http://freedns.afraid.org/dynamic/update.php?$freedns_key > /dev/null 2>&1" >> /etc/crontab + echo "*/30 * * * * root wget --no-check-certificate -O - http://freedns.afraid.org/dynamic/update.php?$freedns_key > /dev/null 2>&1" >> /etc/crontab else print_info "cron for freedns was configured already" fi @@ -448,11 +448,11 @@ function check_https { function install_hubzilla { print_info "installing addons..." cd /var/www/html/ - if git remote -v | grep -i "origin.*hubzilla.*core" + if git remote -v | grep -i "origin.*hubzilla.*" then print_info "hubzilla" util/add_addon_repo https://framagit.org/hubzilla/addons hzaddons - elif git remote -v | grep -i "origin.*zap.*core" + elif git remote -v | grep -i "origin.*zap.*" then print_info "zap" util/add_addon_repo https://framagit.org/zot/zap-addons.git zaddons @@ -460,6 +460,7 @@ function install_hubzilla { die "neither zap nor hubzilla repository > did not install addons or zap/hubzilla" fi mkdir -p "store/[data]/smarty3" + mkdir -p "store" chmod -R 777 store touch .htconfig.php chmod ou+w .htconfig.php @@ -624,7 +625,6 @@ configure_cron_selfhost if [ "$le_domain" != "localhost" ] then install_letsencrypt - configure_apache_for_https check_https else print_info "is localhost - skipped installation of letsencrypt and configuration of apache for https" @@ -632,20 +632,12 @@ fi install_hubzilla -if [ "$le_domain" != "localhost" ] -then - rewrite_to_https - install_rsnapshot -else - print_info "is localhost - skipped rewrite to https and installation of rsnapshot" -fi - configure_cron_daily if [ "$le_domain" != "localhost" ] then install_cryptosetup - write_uninstall_script + install_rsync else print_info "is localhost - skipped installation of cryptosetup" fi -- cgit v1.2.3 From 579adb48978172ae26f8cb1c724b8b17a6a37b34 Mon Sep 17 00:00:00 2001 From: Mario Date: Tue, 21 Apr 2020 09:42:40 +0000 Subject: fix onepoll --- Zotlabs/Daemon/Onepoll.php | 6 +++++- include/zot.php | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Zotlabs/Daemon/Onepoll.php b/Zotlabs/Daemon/Onepoll.php index 2f06ec125..fdcb907d1 100644 --- a/Zotlabs/Daemon/Onepoll.php +++ b/Zotlabs/Daemon/Onepoll.php @@ -76,7 +76,11 @@ class Onepoll { // update permissions - $x = zot_refresh($contact,$importer); + if($contact['xchan_network'] === 'zot6') + $x = Libzot::refresh($contact,$importer); + + if($contact['xchan_network'] === 'zot') + $x = zot_refresh($contact,$importer); $responded = false; $updated = datetime_convert(); diff --git a/include/zot.php b/include/zot.php index 8b9cb0767..fb0804aa7 100644 --- a/include/zot.php +++ b/include/zot.php @@ -408,7 +408,7 @@ function zot_refresh($them, $channel = null, $force = false) { if($channel) { $postvars['target'] = $channel['xchan_guid']; - $postvars['target_sig'] = $channel['xchan_guid_sig']; + $postvars['target_sig'] = str_replace('sha256.', '', $channel['xchan_guid_sig']); $postvars['key'] = $channel['channel_pubkey']; } -- cgit v1.2.3 From b1b9dbe55f4cc0740b113514085da52f290853ee Mon Sep 17 00:00:00 2001 From: Mario Date: Wed, 22 Apr 2020 07:44:51 +0000 Subject: more prefer zot6 over zot --- include/channel.php | 2 +- include/zid.php | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/include/channel.php b/include/channel.php index be58f154a..742d9e3a7 100644 --- a/include/channel.php +++ b/include/channel.php @@ -1930,7 +1930,7 @@ function zid_init() { Master::Summon(array('Gprobe',bin2hex($tmp_str))); } if($r) { - $r = zot_record_preferred($r); + $r = Libzot::zot_record_preferred($r); } if($r && remote_channel() && remote_channel() === $r['hubloc_hash']) return; diff --git a/include/zid.php b/include/zid.php index 325af5580..10e09e212 100644 --- a/include/zid.php +++ b/include/zid.php @@ -1,5 +1,6 @@ Date: Wed, 22 Apr 2020 18:15:02 +0000 Subject: the parent id is required later in the process --- Zotlabs/Module/Display.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Zotlabs/Module/Display.php b/Zotlabs/Module/Display.php index 777d183e1..f45f37001 100644 --- a/Zotlabs/Module/Display.php +++ b/Zotlabs/Module/Display.php @@ -101,7 +101,7 @@ class Display extends \Zotlabs\Web\Controller { if($decoded) $item_hash = $decoded; - $r = q("select id, uid, mid, parent_mid, thr_parent, verb, item_type, item_deleted, author_xchan, item_blocked from item where mid like '%s' limit 1", + $r = q("select id, uid, mid, parent, parent_mid, thr_parent, verb, item_type, item_deleted, author_xchan, item_blocked from item where mid like '%s' limit 1", dbesc($item_hash . '%') ); @@ -159,14 +159,17 @@ class Display extends \Zotlabs\Web\Controller { } } if($target_item['item_type'] == ITEM_TYPE_CARD) { + $x = q("select * from channel where channel_id = %d limit 1", intval($target_item['uid']) ); + $y = q("select * from iconfig left join item on iconfig.iid = item.id where item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'CARD' and item.id = %d limit 1", intval($target_item['uid']), intval($target_item['parent']) ); + if($x && $y) { goaway(z_root() . '/cards/' . $x[0]['channel_address'] . '/' . $y[0]['v']); } -- cgit v1.2.3 From b7a655917ed1f7caa5b8b3d9b92fdd578c6252c4 Mon Sep 17 00:00:00 2001 From: Mario Date: Thu, 23 Apr 2020 18:19:25 +0000 Subject: some work on deprecating ACTIVITY_OBJ_FILE --- Zotlabs/Lib/Activity.php | 11 +++-- Zotlabs/Lib/Enotify.php | 3 ++ Zotlabs/Lib/NativeWiki.php | 2 +- Zotlabs/Module/Sharedwithme.php | 5 ++- Zotlabs/Module/Sse_bs.php | 8 ++-- include/attach.php | 92 +++++++++++++++++++++++++++++++++++------ include/items.php | 8 ++-- 7 files changed, 104 insertions(+), 25 deletions(-) diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index 322579610..9b79190bb 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -1131,7 +1131,6 @@ class Activity { 'http://activitystrea.ms/schema/1.0/photo' => 'Image', 'http://activitystrea.ms/schema/1.0/profile-photo' => 'Icon', 'http://activitystrea.ms/schema/1.0/event' => 'Event', - 'http://activitystrea.ms/schema/1.0/wiki' => 'Document', 'http://purl.org/zot/activity/location' => 'Place', 'http://purl.org/zot/activity/chessgame' => 'Game', 'http://purl.org/zot/activity/tagterm' => 'zot:Tag', @@ -1139,7 +1138,10 @@ class Activity { 'http://purl.org/zot/activity/file' => 'zot:File', 'http://purl.org/zot/activity/mood' => 'zot:Mood', 'Invite' => 'Invite', - 'Question' => 'Question' + 'Question' => 'Question', + 'Document' => 'Document', + 'Audio' => 'Audio', + 'Video' => 'Video' ]; call_hooks('activity_obj_decode_mapper',$objs); @@ -1167,7 +1169,6 @@ class Activity { 'http://activitystrea.ms/schema/1.0/photo' => 'Image', 'http://activitystrea.ms/schema/1.0/profile-photo' => 'Icon', 'http://activitystrea.ms/schema/1.0/event' => 'Event', - 'http://activitystrea.ms/schema/1.0/wiki' => 'Document', 'http://purl.org/zot/activity/location' => 'Place', 'http://purl.org/zot/activity/chessgame' => 'Game', 'http://purl.org/zot/activity/tagterm' => 'zot:Tag', @@ -1175,7 +1176,9 @@ class Activity { 'http://purl.org/zot/activity/file' => 'zot:File', 'http://purl.org/zot/activity/mood' => 'zot:Mood', 'Invite' => 'Invite', - 'Question' => 'Question' + 'Question' => 'Question', + 'Audio' => 'Audio', + 'Video' => 'Video' ]; call_hooks('activity_obj_mapper',$objs); diff --git a/Zotlabs/Lib/Enotify.php b/Zotlabs/Lib/Enotify.php index 85e90d67c..a4fc8aa75 100644 --- a/Zotlabs/Lib/Enotify.php +++ b/Zotlabs/Lib/Enotify.php @@ -818,6 +818,9 @@ class Enotify { $itemem_text = sprintf( t('repeated %s\'s post'), '[bdi]' . $item['author']['xchan_name'] . '[/bdi]'); } + if(in_array($item['obj_type'], ['Document', 'Video', 'Audio', 'Image'])) { + $itemem_text = t('shared a file with you'); + } } $edit = false; diff --git a/Zotlabs/Lib/NativeWiki.php b/Zotlabs/Lib/NativeWiki.php index 6bda76eee..3ec032075 100644 --- a/Zotlabs/Lib/NativeWiki.php +++ b/Zotlabs/Lib/NativeWiki.php @@ -73,7 +73,7 @@ class NativeWiki { $arr['item_thread_top'] = 1; $arr['item_private'] = intval($acl->is_private()); $arr['verb'] = ACTIVITY_CREATE; - $arr['obj_type'] = ACTIVITY_OBJ_WIKI; + $arr['obj_type'] = 'Document'; $arr['body'] = '[table][tr][td][h1]New Wiki[/h1][/td][/tr][tr][td][zrl=' . $wiki_url . ']' . $wiki['htmlName'] . '[/zrl][/td][/tr][/table]'; $arr['public_policy'] = map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_wiki'),true); diff --git a/Zotlabs/Module/Sharedwithme.php b/Zotlabs/Module/Sharedwithme.php index c986f6695..f9d242dd3 100644 --- a/Zotlabs/Module/Sharedwithme.php +++ b/Zotlabs/Module/Sharedwithme.php @@ -1,5 +1,8 @@ '%s' $item_normal @@ -448,19 +451,18 @@ class Sse_bs extends Controller { $r = q("SELECT * FROM item WHERE verb = '%s' - AND obj_type = '%s' + AND obj_type in ('Document', 'Video', 'Audio', 'Image') AND uid = %d AND owner_xchan != '%s' AND item_unseen = 1", dbesc(ACTIVITY_POST), - dbesc(ACTIVITY_OBJ_FILE), intval(self::$uid), dbesc(self::$ob_hash) ); if($r) { xchan_query($r); foreach($r as $rr) { - $result['files']['notifications'][] = Enotify::format_files($rr); + $result['files']['notifications'][] = Enotify::format($rr); } $result['files']['count'] = count($r); } diff --git a/include/attach.php b/include/attach.php index d986f4af1..7b5972525 100644 --- a/include/attach.php +++ b/include/attach.php @@ -12,6 +12,8 @@ */ use Zotlabs\Lib\Libsync; +use Zotlabs\Access\PermissionLimits; +use Zotlabs\Daemon\Master; require_once('include/permissions.php'); require_once('include/security.php'); @@ -1024,9 +1026,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { } if($notify) { - $cloudPath = z_root() . '/cloud/' . $channel['channel_address'] . '/' . $r['0']['display_path']; - $object = get_file_activity_object($channel['channel_id'], $r['0']['hash'], $cloudPath); - file_activity($channel['channel_id'], $object, $r['0']['allow_cid'], $r['0']['allow_gid'], $r['0']['deny_cid'], $r['0']['deny_gid'], 'post', $notify); + file_activity($channel, $observer, $r[0]); } return $ret; @@ -1517,7 +1517,7 @@ function attach_delete($channel_id, $resource, $is_photo = 0) { */ call_hooks('attach_delete', $arr); - file_activity($channel_id, $object, $object['allow_cid'], $object['allow_gid'], $object['deny_cid'], $object['deny_gid'], 'update', true); + //file_activity($channel_id, $object, $object['allow_cid'], $object['allow_gid'], $object['deny_cid'], $object['deny_gid'], 'update', true); return; } @@ -1754,6 +1754,7 @@ function pipe_streams($in, $out, $bufsize = 16384) { * @param string $verb * @param boolean $notify */ +/* function file_activity($channel_id, $object, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $verb, $notify) { require_once('include/items.php'); @@ -1802,7 +1803,7 @@ function file_activity($channel_id, $object, $allow_cid, $allow_gid, $deny_cid, $uuid = item_message_id(); $mid = z_root() . '/item/' . $uuid; - $objtype = ACTIVITY_OBJ_FILE; + $objtype = 'ACTIVITY_OBJ_FILE'; $arr = array(); $arr['aid'] = get_account_id(); @@ -1901,6 +1902,62 @@ function file_activity($channel_id, $object, $allow_cid, $allow_gid, $deny_cid, return; } +*/ + + +function file_activity($channel, $observer, $file) { + + $filetype_parts = explode('/', $file['filetype']); + + switch($filetype_parts[0]) { + case 'image': + $type = 'Image'; + break; + case 'audio': + $type = 'Audio'; + break; + case 'video': + $type = 'Video'; + break; + default: + $type = 'Document'; + } + + $resource_id = $file['hash']; + $uuid = new_uuid(); + + $mid = z_root() . '/item/' . $uuid; + + $arr = []; // Initialize the array of parameters for the post + $arr['aid'] = $channel['channel_account_id']; + $arr['uuid'] = $uuid; + $arr['uid'] = $channel['channel_id']; + $arr['mid'] = $mid; + $arr['parent_mid'] = $mid; + $arr['resource_type'] = 'attach'; + $arr['resource_id'] = $resource_id; + $arr['owner_xchan'] = $channel['channel_hash']; + $arr['author_xchan'] = $observer['xchan_hash']; + $arr['plink'] = z_root() . '/cloud/' . $channel['channel_address'] . '/' . $file['display_path']; + $arr['llink'] = $arr['plink']; + $arr['title'] = $file['filename']; + $arr['allow_cid'] = $file['allow_cid']; + $arr['allow_gid'] = $file['allow_gid']; + $arr['deny_cid'] = $file['deny_cid']; + $arr['deny_gid'] = $file['deny_gid']; + $arr['item_origin'] = 1; + $arr['item_thread_top'] = 1; + $arr['item_private'] = (($file['allow_cid'] || $file['allow_gid'] || $file['deny_cid'] || $file['deny_gid']) ? 1 : 0); + $arr['verb'] = ACTIVITY_CREATE; + $arr['obj_type'] = $type; + $arr['title'] = $file['filename']; + $body_str = sprintf(t('%s shared a %s with you'), '[zrl=' . $observer['xchan_url'] . ']' . $observer['xchan_name'] . '[/zrl]', '[zrl=' . $arr['plink'] . ']' . t('file') . '[/zrl]'); + $arr['body'] = $body_str; + + post_activity_item($arr); + +} + /** * @brief Create file activity object. @@ -1917,17 +1974,28 @@ function get_file_activity_object($channel_id, $hash, $url) { dbesc($hash) ); - $url = rawurlencode($url); - - $links = array(); - $links[] = array( + $links = []; + $links[] = [ 'rel' => 'alternate', - 'type' => 'text/html', + 'type' => $x[0]['filetype'], 'href' => $url - ); + ]; + + $filetype_parts = explode('/', $x[0]['filetype']); + + switch($filetype_parts[0]) { + case 'audio': + $type = 'Audio'; + break; + case 'video': + $type = 'Video'; + break; + default: + $type = 'Document'; + } $object = array( - 'type' => ACTIVITY_OBJ_FILE, + 'type' => $type, 'title' => $x[0]['filename'], 'id' => $url, 'link' => $links, diff --git a/include/items.php b/include/items.php index 6fe5e1f0b..dbb4a7623 100755 --- a/include/items.php +++ b/include/items.php @@ -239,19 +239,19 @@ function comments_are_now_closed($item) { function item_normal() { return " and item.item_hidden = 0 and item.item_type = 0 and item.item_deleted = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0 - and item.item_blocked = 0 and item.obj_type != '" . ACTIVITY_OBJ_FILE . "' "; + and item.item_blocked = 0 "; } function item_normal_search() { return " and item.item_hidden = 0 and item.item_type in (0,3,6,7) and item.item_deleted = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0 - and item.item_blocked = 0 and item.obj_type != '" . ACTIVITY_OBJ_FILE . "' "; + and item.item_blocked = 0 "; } function item_normal_update() { return " and item.item_hidden = 0 and item.item_type = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0 - and item.item_blocked = 0 and item.obj_type != '" . ACTIVITY_OBJ_FILE . "' "; + and item.item_blocked = 0 "; } @@ -267,7 +267,7 @@ function is_item_normal($item) { if(intval($item['item_hidden']) || intval($item['item_type']) || intval($item['item_deleted']) || intval($item['item_unpublished']) || intval($item['item_delayed']) || intval($item['item_pending_remove']) - || intval($item['item_blocked']) || ($item['obj_type'] == ACTIVITY_OBJ_FILE)) + || intval($item['item_blocked'])) return false; return true; -- cgit v1.2.3 From 77c87bcccf6fffca9975fd5a6c08cfddc1ca66e8 Mon Sep 17 00:00:00 2001 From: Mario Date: Thu, 23 Apr 2020 18:21:38 +0000 Subject: default item_wall to 0 --- include/items.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/items.php b/include/items.php index dbb4a7623..87ae5c6a5 100755 --- a/include/items.php +++ b/include/items.php @@ -423,7 +423,7 @@ function post_activity_item($arr, $allow_code = false, $deliver = true) { if(! array_key_exists('item_origin',$arr)) $arr['item_origin'] = 1; if(! array_key_exists('item_wall',$arr) && (! $is_comment)) - $arr['item_wall'] = 1; + $arr['item_wall'] = 0; if(! array_key_exists('item_thread_top',$arr) && (! $is_comment)) $arr['item_thread_top'] = 1; -- cgit v1.2.3 From 7e89d816d7cddbd378e7eaed8e38f9776f3ae167 Mon Sep 17 00:00:00 2001 From: Mario Date: Thu, 23 Apr 2020 19:14:43 +0000 Subject: more work on deprecating ACTIVITY_OBJ_FILE --- include/attach.php | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/include/attach.php b/include/attach.php index 7b5972525..7149be735 100644 --- a/include/attach.php +++ b/include/attach.php @@ -1026,7 +1026,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { } if($notify) { - file_activity($channel, $observer, $r[0]); + attach_store_item($channel, $observer, $r[0]); } return $ret; @@ -1452,9 +1452,6 @@ function attach_delete($channel_id, $resource, $is_photo = 0) { return; } - $url = get_cloud_url($channel_id, $channel_address, $resource); - $object = get_file_activity_object($channel_id, $resource, $url); - // If resource is a directory delete everything in the directory recursive if(intval($r[0]['is_dir'])) { $x = q("SELECT hash, os_storage, is_dir, flags FROM attach WHERE folder = '%s' AND uid = %d", @@ -1498,6 +1495,9 @@ function attach_delete($channel_id, $resource, $is_photo = 0) { if($r[0]['is_photo']) { attach_drop_photo($channel_id,$resource); } + else { + attach_drop_item($channel_id,$resource); + } // update the parent folder's lastmodified timestamp @@ -1517,8 +1517,6 @@ function attach_delete($channel_id, $resource, $is_photo = 0) { */ call_hooks('attach_delete', $arr); - //file_activity($channel_id, $object, $object['allow_cid'], $object['allow_gid'], $object['deny_cid'], $object['deny_gid'], 'update', true); - return; } @@ -1553,6 +1551,21 @@ function attach_drop_photo($channel_id,$resource) { } +function attach_drop_item($channel_id,$resource) { + + $x = q("select id, item_hidden from item where resource_id = '%s' and resource_type = 'attach' and uid = %d and item_deleted = 0", + dbesc($resource), + intval($channel_id) + ); + + if($x) { + $stage = (($x[0]['item_hidden']) ? DROPITEM_NORMAL : DROPITEM_PHASE1); + $interactive = (($x[0]['item_hidden']) ? false : true); + drop_item($x[0]['id'], $interactive, $stage); + } + +} + /** * @brief Returns path to file in cloud/. @@ -1905,7 +1918,7 @@ function file_activity($channel_id, $object, $allow_cid, $allow_gid, $deny_cid, */ -function file_activity($channel, $observer, $file) { +function attach_store_item($channel, $observer, $file) { $filetype_parts = explode('/', $file['filetype']); @@ -1945,6 +1958,7 @@ function file_activity($channel, $observer, $file) { $arr['allow_gid'] = $file['allow_gid']; $arr['deny_cid'] = $file['deny_cid']; $arr['deny_gid'] = $file['deny_gid']; + $arr['item_wall'] = 1; $arr['item_origin'] = 1; $arr['item_thread_top'] = 1; $arr['item_private'] = (($file['allow_cid'] || $file['allow_gid'] || $file['deny_cid'] || $file['deny_gid']) ? 1 : 0); -- cgit v1.2.3 From 2b8afd55800754aeb158d77940777ecc2c119d37 Mon Sep 17 00:00:00 2001 From: Mario Date: Thu, 23 Apr 2020 19:38:19 +0000 Subject: fix file notifications --- Zotlabs/Module/Sse_bs.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Zotlabs/Module/Sse_bs.php b/Zotlabs/Module/Sse_bs.php index e4394fe33..290c616a9 100644 --- a/Zotlabs/Module/Sse_bs.php +++ b/Zotlabs/Module/Sse_bs.php @@ -449,12 +449,15 @@ class Sse_bs extends Controller { if(! self::$uid) return $result; + $item_normal = item_normal(); + $r = q("SELECT * FROM item WHERE verb = '%s' AND obj_type in ('Document', 'Video', 'Audio', 'Image') AND uid = %d - AND owner_xchan != '%s' - AND item_unseen = 1", + AND author_xchan != '%s' + AND item_unseen = 1 + $item_normal", dbesc(ACTIVITY_POST), intval(self::$uid), dbesc(self::$ob_hash) -- cgit v1.2.3 From e2b10f52e02c4cee77af089a544ec55e62048aba Mon Sep 17 00:00:00 2001 From: Mario Date: Fri, 24 Apr 2020 14:25:49 +0000 Subject: more work on deprecating ACTIVITY_OBJ_FILE and adapt mod sharedwithme --- Zotlabs/Daemon/Cron.php | 5 --- Zotlabs/Module/Sharedwithme.php | 94 ++++++++++++++++++++++++----------------- Zotlabs/Module/Sse_bs.php | 5 ++- include/attach.php | 19 +++++++-- 4 files changed, 74 insertions(+), 49 deletions(-) diff --git a/Zotlabs/Daemon/Cron.php b/Zotlabs/Daemon/Cron.php index 9cdfa9a0f..46f4e4071 100644 --- a/Zotlabs/Daemon/Cron.php +++ b/Zotlabs/Daemon/Cron.php @@ -38,11 +38,6 @@ class Cron { Master::Summon(array('Poller')); - // maintenance for mod sharedwithme - check for updated items and remove them - - require_once('include/sharedwithme.php'); - apply_updates(); - /** * Chatpresence: if somebody hasn't pinged recently, they've most likely left the page * and shouldn't count as online anymore. We allow an expection for bots. diff --git a/Zotlabs/Module/Sharedwithme.php b/Zotlabs/Module/Sharedwithme.php index f9d242dd3..4211a3af8 100644 --- a/Zotlabs/Module/Sharedwithme.php +++ b/Zotlabs/Module/Sharedwithme.php @@ -23,81 +23,80 @@ class Sharedwithme extends Controller { $channel = \App::get_channel(); $is_owner = (local_channel() && (local_channel() == $channel['channel_id'])); - - //check for updated items and remove them - require_once('include/sharedwithme.php'); - apply_updates(); + + $item_normal = item_normal(); //drop single file - localuser if((argc() > 2) && (argv(2) === 'drop')) { - + $id = intval(argv(1)); - - q("DELETE FROM item WHERE id = %d AND uid = %d", - intval($id), - intval(local_channel()) - ); - + + drop_item($id); + goaway(z_root() . '/sharedwithme'); + } //drop all files - localuser if((argc() > 1) && (argv(1) === 'dropall')) { - - q("DELETE FROM item WHERE verb = '%s' AND obj_type = '%s' AND uid = %d", + + $r = q("SELECT id FROM item WHERE verb = '%s' AND obj_type IN ('Document', 'Video', 'Audio', 'Image') AND uid = %d AND owner_xchan != '%s' $item_normal", dbesc(ACTIVITY_POST), - dbesc(ACTIVITY_OBJ_FILE), - intval(local_channel()) + intval(local_channel()), + dbesc($channel['channel_hash']) ); - + + $ids = ids_to_array($r); + + if($ids) + drop_items($ids); + goaway(z_root() . '/sharedwithme'); + } - + //list files - $r = q("SELECT id, uid, obj, item_unseen FROM item WHERE verb = '%s' AND obj_type = '%s' AND uid = %d AND owner_xchan != '%s'", + $r = q("SELECT id, uid, obj, item_unseen FROM item WHERE verb = '%s' AND obj_type IN ('Document', 'Video', 'Audio', 'Image') AND uid = %d AND owner_xchan != '%s' $item_normal", dbesc(ACTIVITY_POST), - dbesc(ACTIVITY_OBJ_FILE), intval(local_channel()), dbesc($channel['channel_hash']) ); - - $items =array(); - $ids = ''; - + + $items = []; + $ids = []; + if($r) { foreach($r as $rr) { $object = json_decode($rr['obj'],true); - - $item = array(); + $meta = self::get_meta($object); + + $item = []; $item['id'] = $rr['id']; - $item['objfiletype'] = $object['filetype']; - $item['objfiletypeclass'] = getIconFromType($object['filetype']); - $item['objurl'] = rawurldecode(get_rel_link($object['link'],'alternate')) . '?f=&zid=' . $channel['xchan_addr']; - $item['objfilename'] = $object['filename']; - $item['objfilesize'] = userReadableSize($object['filesize']); - $item['objedited'] = $object['edited']; + $item['objfiletype'] = $meta['type']; + $item['objfiletypeclass'] = getIconFromType($meta['type']); + $item['objurl'] = $meta['path'] . '?f=&zid=' . $channel['xchan_addr']; + $item['objfilename'] = $object['name']; + $item['objfilesize'] = userReadableSize($meta['size']); + $item['objedited'] = $meta['edited']; $item['unseen'] = $rr['item_unseen']; $items[] = $item; - if($item['unseen'] > 0) { - $ids .= " '" . $rr['id'] . "',"; + if($item['unseen']) { + $ids[] = $rr['id']; } } } - + + $ids = implode(',', $ids); + if($ids) { - - //remove trailing , - $ids = rtrim($ids, ","); - q("UPDATE item SET item_unseen = 0 WHERE id IN ( $ids ) AND uid = %d", intval(local_channel()) ); - } $o = ''; @@ -117,5 +116,22 @@ class Sharedwithme extends Controller { } + function get_meta($object) { + + $ret = []; + + if(! is_array($object['attachment'])) + return; + + foreach($object['attachment'] as $a) { + if($a['name'] === 'zot.attach.meta') { + $ret = $a['value']; + break; + } + } + + return $ret; + + } } diff --git a/Zotlabs/Module/Sse_bs.php b/Zotlabs/Module/Sse_bs.php index 290c616a9..cb4c54961 100644 --- a/Zotlabs/Module/Sse_bs.php +++ b/Zotlabs/Module/Sse_bs.php @@ -453,11 +453,12 @@ class Sse_bs extends Controller { $r = q("SELECT * FROM item WHERE verb = '%s' - AND obj_type in ('Document', 'Video', 'Audio', 'Image') + AND obj_type IN ('Document', 'Video', 'Audio', 'Image') AND uid = %d AND author_xchan != '%s' AND item_unseen = 1 - $item_normal", + $item_normal + ORDER BY created DESC", dbesc(ACTIVITY_POST), intval(self::$uid), dbesc(self::$ob_hash) diff --git a/include/attach.php b/include/attach.php index 7149be735..fbf131fb3 100644 --- a/include/attach.php +++ b/include/attach.php @@ -12,6 +12,7 @@ */ use Zotlabs\Lib\Libsync; +use Zotlabs\Lib\Activity; use Zotlabs\Access\PermissionLimits; use Zotlabs\Daemon\Master; @@ -1940,6 +1941,7 @@ function attach_store_item($channel, $observer, $file) { $uuid = new_uuid(); $mid = z_root() . '/item/' . $uuid; + $path = z_root() . '/cloud/' . $channel['channel_address'] . '/' . $file['display_path']; $arr = []; // Initialize the array of parameters for the post $arr['aid'] = $channel['channel_account_id']; @@ -1951,8 +1953,6 @@ function attach_store_item($channel, $observer, $file) { $arr['resource_id'] = $resource_id; $arr['owner_xchan'] = $channel['channel_hash']; $arr['author_xchan'] = $observer['xchan_hash']; - $arr['plink'] = z_root() . '/cloud/' . $channel['channel_address'] . '/' . $file['display_path']; - $arr['llink'] = $arr['plink']; $arr['title'] = $file['filename']; $arr['allow_cid'] = $file['allow_cid']; $arr['allow_gid'] = $file['allow_gid']; @@ -1965,9 +1965,22 @@ function attach_store_item($channel, $observer, $file) { $arr['verb'] = ACTIVITY_CREATE; $arr['obj_type'] = $type; $arr['title'] = $file['filename']; - $body_str = sprintf(t('%s shared a %s with you'), '[zrl=' . $observer['xchan_url'] . ']' . $observer['xchan_name'] . '[/zrl]', '[zrl=' . $arr['plink'] . ']' . t('file') . '[/zrl]'); + $body_str = sprintf(t('%s shared a %s with you'), '[zrl=' . $observer['xchan_url'] . ']' . $observer['xchan_name'] . '[/zrl]', '[zrl=' . $path . ']' . t('file') . '[/zrl]'); $arr['body'] = $body_str; + $meta = [ + 'name' => $file['filename'], + 'type' => $file['filetype'], + 'size' => $file['filesize'], + 'revision' => $file['revision'], + 'size' => $file['filesize'], + 'created' => $file['created'], + 'edited' => $file['edited'], + 'path' => $path + ]; + + set_iconfig($arr, 'attach', 'meta' , $meta, true); + post_activity_item($arr); } -- cgit v1.2.3 From c229f058b4331e3b631d9ca725385ef78bf59778 Mon Sep 17 00:00:00 2001 From: Mario Date: Sat, 25 Apr 2020 09:15:20 +0000 Subject: fix mod filestorage and fetch the info from attach if only the hash is provided in attach_store_item() --- Zotlabs/Module/Filestorage.php | 8 ++++---- include/attach.php | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/Zotlabs/Module/Filestorage.php b/Zotlabs/Module/Filestorage.php index c40de2823..0c6233493 100644 --- a/Zotlabs/Module/Filestorage.php +++ b/Zotlabs/Module/Filestorage.php @@ -35,12 +35,12 @@ class Filestorage extends \Zotlabs\Web\Controller { $url = get_cloud_url($channel_id, $channel['channel_address'], $resource); - //get the object before permissions change so we can catch eventual former allowed members - $object = get_file_activity_object($channel_id, $resource, $url); - attach_change_permissions($channel_id, $resource, $x['allow_cid'], $x['allow_gid'], $x['deny_cid'], $x['deny_gid'], $recurse, true); - file_activity($channel_id, $object, $x['allow_cid'], $x['allow_gid'], $x['deny_cid'], $x['deny_gid'], 'post', $notify); + if($notify) { + $observer = \App::get_observer(); + attach_store_item($channel, $observer, $resource); + } goaway(dirname($url)); } diff --git a/include/attach.php b/include/attach.php index fbf131fb3..4c2459046 100644 --- a/include/attach.php +++ b/include/attach.php @@ -1921,6 +1921,20 @@ function file_activity($channel_id, $object, $allow_cid, $allow_gid, $deny_cid, function attach_store_item($channel, $observer, $file) { + + if(is_string($file)) { + $r = q("SELECT * FROM attach WHERE uid = %d AND hash = '%s' LIMIT 1", + intval($channel['channel_id']), + dbesc($file) + ); + + if(! $r) + return; + + $file = $r[0]; + + } + $filetype_parts = explode('/', $file['filetype']); switch($filetype_parts[0]) { -- cgit v1.2.3 From 7b8c85cf3f4095f77cff0618c11a6e474e9d0fa5 Mon Sep 17 00:00:00 2001 From: OJ Random Date: Mon, 27 Apr 2020 09:59:17 +0200 Subject: stress the importance of PHP mail() --- .homeinstall/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.homeinstall/README.md b/.homeinstall/README.md index 1ed2e07d2..43c5d14b3 100644 --- a/.homeinstall/README.md +++ b/.homeinstall/README.md @@ -17,6 +17,7 @@ Software + Fresh installation of Debian 10 (Stretch) + Router with open ports 80 and 443 for your web server ++ Some form of email server or email gateway such that PHP mail() works. ## How to run the script @@ -45,7 +46,7 @@ In Admin settings of hubzilla or via terminal ## Optional - Switch verification of email on/off -Do this just befor you register the user. +Do this just befor you register the user and you have no working PHP mail(). In Admin settings of hubzilla or via terminal -- cgit v1.2.3 From 079c13e6330bc3b4bc3928c55b89023be0af5ad7 Mon Sep 17 00:00:00 2001 From: Mario Date: Wed, 29 Apr 2020 08:21:33 +0000 Subject: more work on attach_store_item() --- include/attach.php | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/include/attach.php b/include/attach.php index 4c2459046..d648293ee 100644 --- a/include/attach.php +++ b/include/attach.php @@ -1935,6 +1935,59 @@ function attach_store_item($channel, $observer, $file) { } + $path = z_root() . '/cloud/' . $channel['channel_address'] . '/' . $file['display_path']; + + $r = q("SELECT * FROM item WHERE resource_id = '%s' AND resource_type = 'attach' and uid = %d LIMIT 1", + dbesc($file['hash']), + intval($channel['channel_id']) + ); + + if($r) { + + // At the moment only file permission edits are possible. + // If permissions did not change do nothing. Otherwise delete the item and create a new one with new permissions. + + if($r[0]['allow_cid'] === $file['allow_cid'] && $r[0]['allow_gid'] === $file['allow_gid'] && $r[0]['deny_cid'] === $file['deny_cid'] && $r[0]['deny_gid'] === $file['deny_gid']) { + + /* once possible, other edits (eg rename) can be done here. + + q("UPDATE item SET title = '%s' WHERE id = %d AND uid = %d", + dbesc($file['filename']) + ); + + $meta = [ + 'name' => $file['filename'], + 'type' => $file['filetype'], + 'size' => $file['filesize'], + 'revision' => $file['revision'], + 'size' => $file['filesize'], + 'created' => $file['created'], + 'edited' => $file['edited'], + 'path' => $path + ]; + + set_iconfig($r[0], 'attach', 'meta' , $meta, true); + + $post = item_store($arr); + + $item_id = $post['item_id']; + + if($item_id) { + Master::Summon(['Notifier', 'activity', $item_id]); + } + + */ + + return; + + } + + $stage = (($r[0]['item_hidden']) ? DROPITEM_NORMAL : DROPITEM_PHASE1); + $interactive = (($r[0]['item_hidden']) ? false : true); + drop_item($r[0]['id'], $interactive, $stage); + + } + $filetype_parts = explode('/', $file['filetype']); switch($filetype_parts[0]) { @@ -1955,7 +2008,6 @@ function attach_store_item($channel, $observer, $file) { $uuid = new_uuid(); $mid = z_root() . '/item/' . $uuid; - $path = z_root() . '/cloud/' . $channel['channel_address'] . '/' . $file['display_path']; $arr = []; // Initialize the array of parameters for the post $arr['aid'] = $channel['channel_account_id']; @@ -1994,8 +2046,14 @@ function attach_store_item($channel, $observer, $file) { ]; set_iconfig($arr, 'attach', 'meta' , $meta, true); + + $post = item_store($arr); - post_activity_item($arr); + $item_id = $post['item_id']; + + if($item_id) { + Master::Summon(['Notifier', 'activity', $item_id]); + } } -- cgit v1.2.3 From c9794439bcdf7bf738055fdecddc754609b8b9ab Mon Sep 17 00:00:00 2001 From: Mario Date: Wed, 29 Apr 2020 08:24:00 +0000 Subject: adjust code comments and whitespace --- include/attach.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/attach.php b/include/attach.php index d648293ee..80f71b9ea 100644 --- a/include/attach.php +++ b/include/attach.php @@ -1945,7 +1945,8 @@ function attach_store_item($channel, $observer, $file) { if($r) { // At the moment only file permission edits are possible. - // If permissions did not change do nothing. Otherwise delete the item and create a new one with new permissions. + // Since we do not support permission editing on posts yet, + // we will delete the item and create a new one with the new permissions for now. if($r[0]['allow_cid'] === $file['allow_cid'] && $r[0]['allow_gid'] === $file['allow_gid'] && $r[0]['deny_cid'] === $file['deny_cid'] && $r[0]['deny_gid'] === $file['deny_gid']) { @@ -1967,7 +1968,7 @@ function attach_store_item($channel, $observer, $file) { ]; set_iconfig($r[0], 'attach', 'meta' , $meta, true); - + $post = item_store($arr); $item_id = $post['item_id']; @@ -2046,7 +2047,7 @@ function attach_store_item($channel, $observer, $file) { ]; set_iconfig($arr, 'attach', 'meta' , $meta, true); - + $post = item_store($arr); $item_id = $post['item_id']; -- cgit v1.2.3 From 837dbb7a1414d9e14d588f1e425acd12a2dc87e3 Mon Sep 17 00:00:00 2001 From: Mario Date: Wed, 29 Apr 2020 11:10:54 +0000 Subject: =?UTF-8?q?=C3=83fix=20photo=20items?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Zotlabs/Lib/Activity.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index 9b79190bb..5c72a1175 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -178,7 +178,6 @@ class Activity { static function fetch_image($x) { - $ret = [ 'type' => 'Image', 'id' => $x['id'], @@ -2242,9 +2241,7 @@ class Activity { } - // avoid double images from hubzilla to zap/osada - - if($act->obj['type'] === 'Image' && strpos($s['body'],'zrl=') === false) { + if($act->obj['type'] === 'Image') { $ptr = null; @@ -2258,10 +2255,11 @@ class Activity { } foreach($ptr as $vurl) { if(strpos($s['body'],$vurl['href']) === false) { - $s['body'] .= '[zmg]' . $vurl['href'] . '[/zmg]' . "\n\n" . $s['body']; + $bb_imgs .= '[zmg]' . $vurl['href'] . '[/zmg]' . "\n\n"; break; } } + $s['body'] = $bb_imgs . $s['body']; } elseif(is_string($act->obj['url'])) { if(strpos($s['body'],$act->obj['url']) === false) { -- cgit v1.2.3 From 328900cf3b412b1e8ca7d1e447d2d0ffc19e045a Mon Sep 17 00:00:00 2001 From: Max Kostikov Date: Wed, 29 Apr 2020 14:38:11 +0200 Subject: Force browser photo revalidation --- Zotlabs/Module/Photo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Zotlabs/Module/Photo.php b/Zotlabs/Module/Photo.php index 48e2bf4a5..1cf082bdd 100644 --- a/Zotlabs/Module/Photo.php +++ b/Zotlabs/Module/Photo.php @@ -213,7 +213,7 @@ class Photo extends \Zotlabs\Web\Controller { if(! $data) killme(); - + $etag = '"' . md5($data . $modified) . '"'; if($modified == 0) @@ -269,7 +269,7 @@ class Photo extends \Zotlabs\Web\Controller { // in the event that infrastructure caching is present. $smaxage = intval($maxage/12); - header("Cache-Control: s-maxage=" . $smaxage . ", max-age=" . $maxage . $cachecontrol); + header("Cache-Control: no-cache, s-maxage=" . $smaxage . ", max-age=" . $maxage . $cachecontrol); } -- cgit v1.2.3 From 01334d761aa8219c50625efd4ba4b1e74744c101 Mon Sep 17 00:00:00 2001 From: Mario Date: Fri, 1 May 2020 10:24:00 +0000 Subject: set CURLOPT_ENCODING to empty string so that compressed content will be uncompressed --- include/network.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/network.php b/include/network.php index c2edb4f8a..29c5bbcb9 100644 --- a/include/network.php +++ b/include/network.php @@ -61,7 +61,8 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) { @curl_setopt($ch, CURLOPT_CAINFO, get_capath()); @curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); @curl_setopt($ch, CURLOPT_RETURNTRANSFER,true); - @curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; zot)"); + @curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; zot)'); + @curl_setopt($ch, CURLOPT_ENCODING, ''); $ciphers = @get_config('system','curl_ssl_ciphers'); if($ciphers) -- cgit v1.2.3 From 03c1419ad0f5ed62b00ee49b81927223daed4c50 Mon Sep 17 00:00:00 2001 From: Mario Date: Sat, 2 May 2020 11:48:11 +0000 Subject: missing lib include --- Zotlabs/Daemon/Onepoll.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Zotlabs/Daemon/Onepoll.php b/Zotlabs/Daemon/Onepoll.php index fdcb907d1..93a5412b0 100644 --- a/Zotlabs/Daemon/Onepoll.php +++ b/Zotlabs/Daemon/Onepoll.php @@ -2,6 +2,8 @@ namespace Zotlabs\Daemon; +use Zotlabs\Lib\Libzot; + require_once('include/zot.php'); require_once('include/socgraph.php'); -- cgit v1.2.3 From f4a71db42dec1daa5d3cbf305372bdf47b4add5f Mon Sep 17 00:00:00 2001 From: Max Kostikov Date: Sat, 2 May 2020 17:28:49 +0200 Subject: Display delayed posts on author's channel page --- Zotlabs/Lib/ThreadItem.php | 5 +++-- Zotlabs/Module/Channel.php | 8 ++++++-- view/js/main.js | 3 ++- view/tpl/conv_item.tpl | 2 +- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Zotlabs/Lib/ThreadItem.php b/Zotlabs/Lib/ThreadItem.php index a5dd81d40..426f88688 100644 --- a/Zotlabs/Lib/ThreadItem.php +++ b/Zotlabs/Lib/ThreadItem.php @@ -426,6 +426,7 @@ class ThreadItem { 'editedtime' => (($item['edited'] != $item['created']) ? sprintf( t('last edited: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['edited'], 'r')) : ''), 'expiretime' => (($item['expires'] > NULL_DATE) ? sprintf( t('Expires: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['expires'], 'r')):''), 'lock' => $lock, + 'delayed' => $item['item_delayed'], 'privacy_warning' => $privacy_warning, 'verified' => $verified, 'unverified' => $unverified, @@ -460,7 +461,7 @@ class ThreadItem { 'tagger' => ((feature_enabled($conv->get_profile_owner(),'commtag')) ? $tagger : ''), 'filer' => ((feature_enabled($conv->get_profile_owner(),'filing') && ($item['item_type'] == ITEM_TYPE_POST)) ? $filer : ''), 'pinned' => ($pinned ? t('Pinned post') : ''), - 'pinnable' => (($this->is_toplevel() && local_channel() && $item['owner_xchan'] == $observer['xchan_hash'] && $allowed_type && $item['item_private'] == 0) ? '1' : ''), + 'pinnable' => (($this->is_toplevel() && local_channel() && $item['owner_xchan'] == $observer['xchan_hash'] && $allowed_type && $item['item_private'] == 0 && $item['item_delayed'] == 0) ? '1' : ''), 'pinme' => ($pinned ? t('Unpin from the top') : t('Pin to the top')), 'bookmark' => (($conv->get_profile_owner() == local_channel() && local_channel() && $has_bookmarks) ? t('Save Bookmarks') : ''), 'addtocal' => (($has_event) ? t('Add to Calendar') : ''), @@ -488,7 +489,7 @@ class ThreadItem { 'modal_dismiss' => t('Close'), 'showlike' => $showlike, 'showdislike' => $showdislike, - 'comment' => $this->get_comment_box($indent), + 'comment' => ($item['item_delayed'] ? '' : $this->get_comment_box($indent)), 'previewing' => ($conv->is_preview() ? true : false ), 'preview_lbl' => t('This is an unsaved preview'), 'wait' => t('Please wait'), diff --git a/Zotlabs/Module/Channel.php b/Zotlabs/Module/Channel.php index 170b81787..08de059a8 100644 --- a/Zotlabs/Module/Channel.php +++ b/Zotlabs/Module/Channel.php @@ -239,8 +239,12 @@ class Channel extends Controller { /** * Get permissions SQL - if $remote_contact is true, our remote user has been pre-verified and we already have fetched his/her groups */ - - $item_normal = item_normal(); + + $item_normal = " and item.item_hidden = 0 and item.item_type = 0 and item.item_deleted = 0 + and item.item_unpublished = 0 and item.item_pending_remove = 0 + and item.item_blocked = 0 "; + if (! $is_owner) + $item_normal .= "and item.item_delayed = 0 "; $item_normal_update = item_normal_update(); $sql_extra = item_permissions_sql(App::$profile['profile_uid']); diff --git a/view/js/main.js b/view/js/main.js index 94fd940b2..5ba4282a6 100644 --- a/view/js/main.js +++ b/view/js/main.js @@ -88,7 +88,8 @@ $(document).ready(function() { wordSeparator : aStr['t16'], numbers : aStr['t17'], }; - + + jQuery.timeago.settings.allowFuture = true; if(typeof(window.SharedWorker) === 'undefined') { // notifications with multiple tabs open will not work very well in this scenario diff --git a/view/tpl/conv_item.tpl b/view/tpl/conv_item.tpl index 340807d02..f48e88006 100755 --- a/view/tpl/conv_item.tpl +++ b/view/tpl/conv_item.tpl @@ -57,7 +57,7 @@ {{$item.name}}{{if $item.owner_url}} {{$item.via}} {{$item.owner_name}}{{/if}}
- {{if $item.verified}} {{elseif $item.forged}} {{/if}}{{if $item.location}}{{$item.location}}, {{/if}}{{if $item.editedtime}} {{$item.editedtime}}{{/if}}{{if $item.expiretime}} {{$item.expiretime}}{{/if}}{{if $item.editedtime}} {{/if}} {{if $item.app}}{{$item.str_app}}{{/if}} + {{if $item.verified}} {{elseif $item.forged}} {{/if}}{{if $item.location}}{{$item.location}}, {{/if}}{{if $item.editedtime}} {{$item.editedtime}}{{/if}}{{if $item.expiretime}} {{$item.expiretime}}{{/if}} {{if $item.delayed}}{{/if}}{{if $item.editedtime}} {{/if}} {{if $item.app}}{{$item.str_app}}{{/if}}
{{if $item.divider}} -- cgit v1.2.3 From fd48f9d173a7a76f9ecaedf078272f5fd3bdd706 Mon Sep 17 00:00:00 2001 From: Mario Date: Sun, 3 May 2020 12:31:10 +0000 Subject: improve functionality of combined notification filters --- view/css/widgets.css | 5 +++++ view/js/main.js | 6 +++--- view/tpl/notifications_widget.tpl | 18 ++++++++++++------ 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/view/css/widgets.css b/view/css/widgets.css index ca7267189..30e7e6972 100644 --- a/view/css/widgets.css +++ b/view/css/widgets.css @@ -225,6 +225,11 @@ a.wikilist { margin-bottom: 1rem; } +.tt-filter-active, +.cn-filter-active { + display: none !important; +} + /* contact block */ .contact-block-div .oneway-overlay { font-size: 20px; diff --git a/view/js/main.js b/view/js/main.js index 5ba4282a6..49c0bed97 100644 --- a/view/js/main.js +++ b/view/js/main.js @@ -1807,7 +1807,7 @@ function sse_handleNotificationsItems(notifyType, data, replace, followup) { $("#nav-" + notifyType + "-menu .notifications-autotime").timeago(); if($('#tt-' + notifyType + '-only').hasClass('active')) - $('#nav-' + notifyType + '-menu [data-thread_top=false]').addClass('d-none'); + $('#nav-' + notifyType + '-menu [data-thread_top=false]').addClass('tt-filter-active'); if($('#cn-' + notifyType + '-input').length) { var filter = $('#cn-' + notifyType + '-input').val().toString().toLowerCase(); @@ -1818,9 +1818,9 @@ function sse_handleNotificationsItems(notifyType, data, replace, followup) { var cn = $(el).data('contact_name').toString().toLowerCase(); var ca = $(el).data('contact_addr').toString().toLowerCase(); if(cn.indexOf(filter) === -1 && ca.indexOf(filter) === -1) - $(el).addClass('d-none'); + $(el).addClass('cn-filter-active'); else - $(el).removeClass('d-none'); + $(el).removeClass('cn-filter-active'); }); } } diff --git a/view/tpl/notifications_widget.tpl b/view/tpl/notifications_widget.tpl index 146c510f1..001a202af 100644 --- a/view/tpl/notifications_widget.tpl +++ b/view/tpl/notifications_widget.tpl @@ -81,14 +81,20 @@ {{foreach $notifications as $notification}} {{if $notification.filter}} $(document).on('click', '#tt-{{$notification.type}}-only', function(e) { - e.preventDefault(); - $('#nav-{{$notification.type}}-menu [data-thread_top=false]').toggleClass('d-none'); - $(this).toggleClass('active sticky-top'); + if($(this).hasClass('active sticky-top')) { + $('#nav-{{$notification.type}}-menu .notification[data-thread_top=false]').removeClass('tt-filter-active'); + $(this).removeClass('active sticky-top'); + } + else { + $('#nav-{{$notification.type}}-menu .notification[data-thread_top=false]').addClass('tt-filter-active'); + $(this).addClass('active sticky-top'); + } + }); $(document).on('click', '#cn-{{$notification.type}}-input-clear', function(e) { $('#cn-{{$notification.type}}-input').val(''); $('#cn-{{$notification.type}}-only').removeClass('active sticky-top'); - $("#nav-{{$notification.type}}-menu .notification").removeClass('d-none'); + $("#nav-{{$notification.type}}-menu .notification").removeClass('cn-filter-active'); $('#cn-{{$notification.type}}-input-clear').addClass('d-none'); }); $(document).on('input', '#cn-{{$notification.type}}-input', function(e) { @@ -108,9 +114,9 @@ var ca = $(el).data('contact_addr').toString().toLowerCase(); if(cn.indexOf(val) === -1 && ca.indexOf(val) === -1) - $(this).addClass('d-none'); + $(this).addClass('cn-filter-active'); else - $(this).removeClass('d-none'); + $(this).removeClass('cn-filter-active'); }); }); {{/if}} -- cgit v1.2.3 From 4eaba326ceeaadf0bbe418e4b8a8d5b63e9672b4 Mon Sep 17 00:00:00 2001 From: Mario Date: Sun, 3 May 2020 12:33:42 +0000 Subject: version 4.7.6 --- boot.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boot.php b/boot.php index 4d641baf0..c574b84fb 100755 --- a/boot.php +++ b/boot.php @@ -50,7 +50,7 @@ require_once('include/attach.php'); require_once('include/bbcode.php'); define ( 'PLATFORM_NAME', 'hubzilla' ); -define ( 'STD_VERSION', '4.7.5' ); +define ( 'STD_VERSION', '4.7.6' ); define ( 'ZOT_REVISION', '6.0a' ); define ( 'DB_UPDATE_VERSION', 1236 ); -- cgit v1.2.3 From ad040a0b111539d6cdc4922539d550e21500be04 Mon Sep 17 00:00:00 2001 From: Mario Date: Mon, 4 May 2020 07:44:19 +0000 Subject: set CURLOPT_ENCODING in z_post_url() --- include/network.php | 1 + 1 file changed, 1 insertion(+) diff --git a/include/network.php b/include/network.php index 29c5bbcb9..80d19797b 100644 --- a/include/network.php +++ b/include/network.php @@ -258,6 +258,7 @@ function z_post_url($url, $params, $redirects = 0, $opts = array()) { @curl_setopt($ch, CURLOPT_POST,1); @curl_setopt($ch, CURLOPT_POSTFIELDS,$params); @curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; zot)"); + @curl_setopt($ch, CURLOPT_ENCODING, ''); $ciphers = @get_config('system','curl_ssl_ciphers'); if($ciphers) -- cgit v1.2.3 From 46242aeaae084191244008529703c964fa5b2d53 Mon Sep 17 00:00:00 2001 From: Mario Date: Mon, 4 May 2020 09:24:22 +0000 Subject: display complete perminfo only to owner --- Zotlabs/Module/Viewconnections.php | 6 ++---- include/text.php | 7 +++++-- view/tpl/contact_template.tpl | 2 +- view/tpl/micropro_card.tpl | 2 +- view/tpl/micropro_img.tpl | 2 +- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Zotlabs/Module/Viewconnections.php b/Zotlabs/Module/Viewconnections.php index 320a331d1..a0c293ddf 100644 --- a/Zotlabs/Module/Viewconnections.php +++ b/Zotlabs/Module/Viewconnections.php @@ -97,7 +97,6 @@ class Viewconnections extends \Zotlabs\Web\Controller { $perminfo['connperms'] .= t('Nothing'); } - $url = chanlink_hash($rr['xchan_hash']); if($url) { $contacts[] = array( @@ -111,13 +110,12 @@ class Viewconnections extends \Zotlabs\Web\Controller { 'sparkle' => '', 'itemurl' => $rr['url'], 'network' => '', - 'perminfo' => $perminfo, + 'perminfo' => (($is_owner) ? $perminfo : (($perminfo['connpermcount'] === 0) ? $perminfo : [])), 'oneway' => $oneway ); } } - - + if($_REQUEST['aj']) { if($contacts) { $o = replace_macros(get_markup_template('viewcontactsajax.tpl'),array( diff --git a/include/text.php b/include/text.php index 6f56a0754..0202061ab 100644 --- a/include/text.php +++ b/include/text.php @@ -1010,7 +1010,7 @@ function contact_block() { if(count($r)) { $contacts = t('Connections'); - $micropro = Array(); + $micropro = []; foreach($r as $rr) { // There is no setting to discover if you are bi-directionally connected @@ -1037,6 +1037,9 @@ function contact_block() { $rr['perminfo']['connperms'] .= t('Nothing'); } + if($is_owner && $rr['perminfo']['connpermcount'] !== 0) + unset($rr['perminfo']); + $micropro[] = micropro($rr,true,'mpfriend'); } } @@ -1047,7 +1050,7 @@ function contact_block() { '$contacts' => $contacts, '$nickname' => App::$profile['channel_address'], '$viewconnections' => (($total > $shown) ? sprintf(t('View all %s connections'),$total) : ''), - '$micropro' => $micropro, + '$micropro' => $micropro )); $arr = ['contacts' => $r, 'output' => $o]; diff --git a/view/tpl/contact_template.tpl b/view/tpl/contact_template.tpl index 40495b789..73fa5adde 100755 --- a/view/tpl/contact_template.tpl +++ b/view/tpl/contact_template.tpl @@ -1,7 +1,7 @@
{{$contact.name}} - {{include "connstatus.tpl" perminfo=$contact.perminfo}} + {{if $contact.perminfo}}{{include "connstatus.tpl" perminfo=$contact.perminfo}}{{/if}}
{{$contact.name}}
diff --git a/view/tpl/micropro_card.tpl b/view/tpl/micropro_card.tpl index 1bdf92da1..058bfc14c 100644 --- a/view/tpl/micropro_card.tpl +++ b/view/tpl/micropro_card.tpl @@ -1,5 +1,5 @@ - {{include "connstatus.tpl"}} + {{if $perminfo}}{{include "connstatus.tpl"}}{{/if}} {{$name}} {{$addr}}
{{$network}}
diff --git a/view/tpl/micropro_img.tpl b/view/tpl/micropro_img.tpl index f023a2d00..98f33d119 100755 --- a/view/tpl/micropro_img.tpl +++ b/view/tpl/micropro_img.tpl @@ -1 +1 @@ - + -- cgit v1.2.3 From a40b882d723166313bacb04b67110fb31d960778 Mon Sep 17 00:00:00 2001 From: Mario Date: Mon, 4 May 2020 09:26:06 +0000 Subject: typo --- include/text.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/text.php b/include/text.php index 0202061ab..b13458e99 100644 --- a/include/text.php +++ b/include/text.php @@ -1037,7 +1037,7 @@ function contact_block() { $rr['perminfo']['connperms'] .= t('Nothing'); } - if($is_owner && $rr['perminfo']['connpermcount'] !== 0) + if(!$is_owner && $rr['perminfo']['connpermcount'] !== 0) unset($rr['perminfo']); $micropro[] = micropro($rr,true,'mpfriend'); -- cgit v1.2.3 From de058901c2a8dd5fea09b1594c2ace0b8bd2c90f Mon Sep 17 00:00:00 2001 From: Mario Date: Tue, 5 May 2020 07:40:24 +0000 Subject: catch the owner_xchan for activity_share items in notifications filter --- Zotlabs/Module/Sse_bs.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Zotlabs/Module/Sse_bs.php b/Zotlabs/Module/Sse_bs.php index cb4c54961..a8c0b2299 100644 --- a/Zotlabs/Module/Sse_bs.php +++ b/Zotlabs/Module/Sse_bs.php @@ -119,7 +119,7 @@ class Sse_bs extends Controller { $sql_extra2 = ''; if(self::$xchans) - $sql_extra2 = " AND author_xchan IN (" . self::$xchans . ") "; + $sql_extra2 = " AND CASE WHEN verb = '" . ACTIVITY_SHARE . "' THEN owner_xchan ELSE author_xchan END IN (" . self::$xchans . ") "; $item_normal = item_normal(); @@ -184,7 +184,7 @@ class Sse_bs extends Controller { $sql_extra2 = ''; if(self::$xchans) - $sql_extra2 = " AND author_xchan IN (" . self::$xchans . ") "; + $sql_extra2 = " AND CASE WHEN verb = '" . ACTIVITY_SHARE . "' THEN owner_xchan ELSE author_xchan END IN (" . self::$xchans . ") "; $item_normal = item_normal(); @@ -261,7 +261,7 @@ class Sse_bs extends Controller { $sql_extra2 = ''; if(self::$xchans) - $sql_extra2 = " AND author_xchan IN (" . self::$xchans . ") "; + $sql_extra2 = " AND CASE WHEN verb = '" . ACTIVITY_SHARE . "' THEN owner_xchan ELSE author_xchan END IN (" . self::$xchans . ") "; $item_normal = item_normal(); -- cgit v1.2.3 From 06d1cf83d2b1acfef5529fe388d2502bea381881 Mon Sep 17 00:00:00 2001 From: Mario Date: Tue, 5 May 2020 14:51:00 +0000 Subject: deal with polls and votes in enotify --- Zotlabs/Lib/Enotify.php | 27 +++++++++++++++++---------- Zotlabs/Module/Sse_bs.php | 1 + include/text.php | 3 +++ 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/Zotlabs/Lib/Enotify.php b/Zotlabs/Lib/Enotify.php index a4fc8aa75..f706b0fb9 100644 --- a/Zotlabs/Lib/Enotify.php +++ b/Zotlabs/Lib/Enotify.php @@ -143,19 +143,26 @@ class Enotify { $action = t('commented on'); - if(array_key_exists('item',$params) && in_array($params['item']['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) { + if(array_key_exists('item',$params)) { - if(! $always_show_in_notices || !($vnotify & VNOTIFY_LIKE)) { - logger('notification: not a visible activity. Ignoring.'); - pop_lang(); - return; - } + if(in_array($params['item']['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) { - if(activity_match($params['verb'], ACTIVITY_LIKE)) - $action = t('liked'); + if(! $always_show_in_notices || !($vnotify & VNOTIFY_LIKE)) { + logger('notification: not a visible activity. Ignoring.'); + pop_lang(); + return; + } + + if(activity_match($params['verb'], ACTIVITY_LIKE)) + $action = t('liked'); + + if(activity_match($params['verb'], ACTIVITY_DISLIKE)) + $action = t('disliked'); + + } - if(activity_match($params['verb'], ACTIVITY_DISLIKE)) - $action = t('disliked'); + if($params['item']['obj_type'] === 'Answer') + $action = t('voted on'); } diff --git a/Zotlabs/Module/Sse_bs.php b/Zotlabs/Module/Sse_bs.php index a8c0b2299..23bc3c96b 100644 --- a/Zotlabs/Module/Sse_bs.php +++ b/Zotlabs/Module/Sse_bs.php @@ -327,6 +327,7 @@ class Sse_bs extends Controller { $r = q("SELECT * FROM notify WHERE uid = %d AND seen = 0 ORDER BY created DESC", intval(self::$uid) ); + if($r) { foreach($r as $rr) { $result['notify']['notifications'][] = Enotify::format_notify($rr); diff --git a/include/text.php b/include/text.php index b13458e99..a2e5ce37a 100644 --- a/include/text.php +++ b/include/text.php @@ -2240,6 +2240,9 @@ function item_post_type($item) { if(strlen($item['verb']) && (! activity_match($item['verb'],ACTIVITY_POST))) $post_type = t('activity'); + if($item['obj_type'] === 'Question') + $post_type = t('poll'); + return $post_type; } -- cgit v1.2.3