aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorzotlabs <mike@macgirvin.com>2020-05-05 19:42:00 -0700
committerzotlabs <mike@macgirvin.com>2020-05-05 19:42:00 -0700
commit79968ea07aa205e9c93993724d3f37362220b9d4 (patch)
tree053bbcc9c213bd2e1da2b4a1c464be771929b2c3
parent7a0d9a449492ff3b5ef97b997310ed26ec1f53e4 (diff)
parent06d1cf83d2b1acfef5529fe388d2502bea381881 (diff)
downloadvolse-hubzilla-79968ea07aa205e9c93993724d3f37362220b9d4.tar.gz
volse-hubzilla-79968ea07aa205e9c93993724d3f37362220b9d4.tar.bz2
volse-hubzilla-79968ea07aa205e9c93993724d3f37362220b9d4.zip
Merge branch 'dev' of https://framagit.org/hubzilla/core into dev
-rw-r--r--.homeinstall/README.md3
-rwxr-xr-x.homeinstall/hubzilla-setup.sh24
-rw-r--r--Zotlabs/Daemon/Cron.php5
-rw-r--r--Zotlabs/Daemon/Onepoll.php8
-rw-r--r--Zotlabs/Lib/Activity.php238
-rw-r--r--Zotlabs/Lib/ActivityStreams.php13
-rw-r--r--Zotlabs/Lib/Connect.php2
-rw-r--r--Zotlabs/Lib/Enotify.php30
-rw-r--r--Zotlabs/Lib/NativeWiki.php2
-rw-r--r--Zotlabs/Lib/Share.php3
-rw-r--r--Zotlabs/Lib/ThreadItem.php7
-rw-r--r--Zotlabs/Module/Activity.php2
-rw-r--r--Zotlabs/Module/Channel.php8
-rw-r--r--Zotlabs/Module/Display.php5
-rw-r--r--Zotlabs/Module/Filestorage.php8
-rw-r--r--Zotlabs/Module/Item.php120
-rw-r--r--Zotlabs/Module/Photo.php4
-rw-r--r--Zotlabs/Module/Sharedwithme.php99
-rw-r--r--Zotlabs/Module/Sse_bs.php23
-rw-r--r--Zotlabs/Module/Viewconnections.php6
-rwxr-xr-xboot.php2
-rw-r--r--include/attach.php200
-rw-r--r--include/bbcode.php135
-rw-r--r--include/channel.php2
-rw-r--r--include/conversation.php16
-rw-r--r--include/event.php2
-rwxr-xr-xinclude/items.php10
-rw-r--r--include/network.php4
-rw-r--r--include/photos.php4
-rw-r--r--include/text.php12
-rw-r--r--include/zid.php5
-rw-r--r--include/zot.php2
-rw-r--r--spec/HTTPSignatures/Home.md34
-rw-r--r--spec/OpenWebAuth/Home.md57
-rw-r--r--spec/Zot6/Changelog.md16
-rw-r--r--spec/Zot6/Content.md15
-rw-r--r--spec/Zot6/Discovery.md112
-rw-r--r--spec/Zot6/Encryption+Signatures.md142
-rw-r--r--spec/Zot6/Home.md92
-rw-r--r--spec/Zot6/Message Types.md111
-rw-r--r--spec/Zot6/Messages.md135
-rw-r--r--spec/Zot6/Nomadic Identity.md7
-rw-r--r--view/css/widgets.css5
-rw-r--r--view/js/main.js9
-rwxr-xr-xview/tpl/contact_template.tpl2
-rwxr-xr-xview/tpl/conv_item.tpl2
-rw-r--r--view/tpl/micropro_card.tpl2
-rwxr-xr-xview/tpl/micropro_img.tpl2
-rw-r--r--view/tpl/notifications_widget.tpl18
49 files changed, 1497 insertions, 268 deletions
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
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
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/Daemon/Onepoll.php b/Zotlabs/Daemon/Onepoll.php
index 2f06ec125..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');
@@ -76,7 +78,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/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php
index 3c16a5367..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'],
@@ -419,7 +418,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 +819,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) {
-
- $private = false;
- $list = [];
- $x = collect_recipients($i,$private);
- if($x) {
- stringify_array_elms($x);
- if(! $x)
- return;
+ // Returns an array of all recipients targeted by private item array $i.
- $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 . ") and hubloc_id_url != ''");
+ 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 hubloc_network, hubloc_hash, hubloc_id_url 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) {
@@ -969,7 +1130,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',
@@ -977,7 +1137,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);
@@ -1005,7 +1168,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',
@@ -1013,7 +1175,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);
@@ -2077,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;
@@ -2093,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) {
@@ -2177,8 +2340,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/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.');
diff --git a/Zotlabs/Lib/Enotify.php b/Zotlabs/Lib/Enotify.php
index 85e90d67c..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(! $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_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');
}
@@ -818,6 +825,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/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'];
diff --git a/Zotlabs/Lib/ThreadItem.php b/Zotlabs/Lib/ThreadItem.php
index dee7cda56..426f88688 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;
}
@@ -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/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/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/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']);
}
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/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php
index fcc040e01..95359ccad 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;
@@ -43,9 +44,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 +69,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 +106,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 +117,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 +128,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);
}
@@ -194,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) {
@@ -712,6 +676,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') {
@@ -723,16 +689,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);
@@ -746,7 +712,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']) {
@@ -759,6 +724,7 @@ class Item extends Controller {
);
}
}
+
}
if(($str_contact_allow) && (! $str_group_allow)) {
@@ -990,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']) {
@@ -1055,10 +1022,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
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);
}
diff --git a/Zotlabs/Module/Sharedwithme.php b/Zotlabs/Module/Sharedwithme.php
index c986f6695..4211a3af8 100644
--- a/Zotlabs/Module/Sharedwithme.php
+++ b/Zotlabs/Module/Sharedwithme.php
@@ -1,5 +1,8 @@
<?php
namespace Zotlabs\Module;
+
+use Zotlabs\Web\Controller;
+
require_once('include/conversation.php');
require_once('include/text.php');
@@ -9,7 +12,7 @@ require_once('include/text.php');
*
*/
-class Sharedwithme extends \Zotlabs\Web\Controller {
+class Sharedwithme extends Controller {
function get() {
if(! local_channel()) {
@@ -20,81 +23,80 @@ class Sharedwithme extends \Zotlabs\Web\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 = '';
@@ -114,5 +116,22 @@ class Sharedwithme extends \Zotlabs\Web\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 89e852120..23bc3c96b 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 CASE WHEN verb = '" . ACTIVITY_SHARE . "' THEN owner_xchan ELSE author_xchan END IN (" . self::$xchans . ") ";
$item_normal = item_normal();
@@ -128,6 +128,7 @@ class Sse_bs extends Controller {
WHERE uid = %d
AND created <= '%s'
AND item_unseen = 1 AND item_wall = 0
+ AND obj_type NOT IN ('Document', 'Video', 'Audio', 'Image')
AND author_xchan != '%s'
$item_normal
$sql_extra
@@ -183,7 +184,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 CASE WHEN verb = '" . ACTIVITY_SHARE . "' THEN owner_xchan ELSE author_xchan END IN (" . self::$xchans . ") ";
$item_normal = item_normal();
@@ -193,6 +194,7 @@ class Sse_bs extends Controller {
WHERE uid = %d
AND created <= '%s'
AND item_unseen = 1 AND item_wall = 1
+ AND obj_type NOT IN ('Document', 'Video', 'Audio', 'Image')
AND author_xchan != '%s'
$item_normal
$sql_extra
@@ -259,7 +261,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 CASE WHEN verb = '" . ACTIVITY_SHARE . "' THEN owner_xchan ELSE author_xchan END IN (" . self::$xchans . ") ";
$item_normal = item_normal();
@@ -268,6 +270,7 @@ class Sse_bs extends Controller {
WHERE uid = %d
AND created <= '%s'
AND item_unseen = 1
+ AND obj_type NOT IN ('Document', 'Video', 'Audio', 'Image')
AND author_xchan != '%s'
AND created > '%s'
$item_normal
@@ -324,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);
@@ -446,21 +450,24 @@ 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 = '%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
+ ORDER BY created DESC",
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/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/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 );
diff --git a/include/attach.php b/include/attach.php
index d986f4af1..80f71b9ea 100644
--- a/include/attach.php
+++ b/include/attach.php
@@ -12,6 +12,9 @@
*/
use Zotlabs\Lib\Libsync;
+use Zotlabs\Lib\Activity;
+use Zotlabs\Access\PermissionLimits;
+use Zotlabs\Daemon\Master;
require_once('include/permissions.php');
require_once('include/security.php');
@@ -1024,9 +1027,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);
+ attach_store_item($channel, $observer, $r[0]);
}
return $ret;
@@ -1452,9 +1453,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 +1496,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 +1518,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 +1552,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/.
@@ -1754,6 +1768,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 +1817,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 +1916,148 @@ function file_activity($channel_id, $object, $allow_cid, $allow_gid, $deny_cid,
return;
}
+*/
+
+
+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];
+
+ }
+
+ $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.
+ // 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']) {
+
+ /* 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]) {
+ 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['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_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);
+ $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=' . $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 = item_store($arr);
+
+ $item_id = $post['item_id'];
+
+ if($item_id) {
+ Master::Summon(['Notifier', 'activity', $item_id]);
+ }
+
+}
+
/**
* @brief Create file activity object.
@@ -1917,17 +2074,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/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=\&quot\;(.*?)\&quot\;/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=\&quot\;(.*?)\&quot\;/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=\&quot\;(.*?)\&quot\;/ism', $attributes, $matches);
+ if (x($matches, 1)) {
+ $title = $matches[1];
+ }
+ if ($title != "") {
+ $title = html_entity_decode($title, ENT_QUOTES, 'UTF-8');
+ $title = str_replace(["[", "]"], ["&#91;", "&#93;"], $title);
+ $data["title"] = $title;
+ }
+
+ $image = "";
+ preg_match("/image='(.*?)'/ism", $attributes, $matches);
+ if (x($matches, 1)) {
+ $image = $matches[1];
+ }
+
+ preg_match('/image=\&quot\;(.*?)\&quot\;/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=\&quot\;(.*?)\&quot\;/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.
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/conversation.php b/include/conversation.php
index 49dd411fb..b0e81b7e2 100644
--- a/include/conversation.php
+++ b/include/conversation.php
@@ -423,10 +423,18 @@ function visible_activity($item) {
}
}
-
-
- if(is_edit_activity($item))
- return false;
+ // 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;
}
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/items.php b/include/items.php
index 6fe5e1f0b..87ae5c6a5 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;
@@ -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;
diff --git a/include/network.php b/include/network.php
index c2edb4f8a..80d19797b 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)
@@ -257,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)
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]'
diff --git a/include/text.php b/include/text.php
index 5d1cf6eff..a2e5ce37a 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];
@@ -2237,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;
}
@@ -3732,7 +3738,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 {
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 @@
<?php
+use Zotlabs\Lib\Libzot;
use Zotlabs\Lib\Verify;
use Zotlabs\Zot\Finger;
@@ -402,9 +403,7 @@ function observer_auth($ob_hash) {
return;
}
- // Note: this has no Libzot namespace so prefers zot over zot6
-
- $hubloc = zot_record_preferred($r);
+ $hubloc = Libzot::zot_record_preferred($r);
$_SESSION['authenticated'] = 1;
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'];
}
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
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 94fd940b2..49c0bed97 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
@@ -1806,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();
@@ -1817,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/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 @@
<div class="contact-entry-wrapper" id="contact-entry-wrapper-{{$contact.id}}" >
<div class="contact-entry-photo-wrapper" >
<a href="{{$contact.link}}" title="{{$contact.img_hover}}" ><img class="contact-block-img" src="{{$contact.thumb}}" alt="{{$contact.name}}" /></a>
- {{include "connstatus.tpl" perminfo=$contact.perminfo}}
+ {{if $contact.perminfo}}{{include "connstatus.tpl" perminfo=$contact.perminfo}}{{/if}}
</div>
<div class="contact-entry-photo-end" ></div>
<div class="contact-entry-name" id="contact-entry-name-{{$contact.id}}" >{{$contact.name}}</div>
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 @@
<a href="{{$item.profile_url}}" title="{{$item.linktitle}}" class="wall-item-name-link u-url"><span class="wall-item-name{{$item.sparkle}}" id="wall-item-name-{{$item.id}}" ><bdi>{{$item.name}}</bdi></span></a>{{if $item.owner_url}}&nbsp;{{$item.via}}&nbsp;<a href="{{$item.owner_url}}" title="{{$item.olinktitle}}" class="wall-item-name-link"><span class="wall-item-name{{$item.osparkle}}" id="wall-item-ownername-{{$item.id}}"><bdi>{{$item.owner_name}}</bdi></span></a>{{/if}}
</div>
<div class="wall-item-ago" id="wall-item-ago-{{$item.id}}">
- {{if $item.verified}}<i class="fa fa-check item-verified" title="{{$item.verified}}"></i>&nbsp;{{elseif $item.forged}}<i class="fa fa-exclamation item-forged" title="{{$item.forged}}"></i>&nbsp;{{/if}}{{if $item.location}}<span class="wall-item-location p-location" id="wall-item-location-{{$item.id}}">{{$item.location}},&nbsp;</span>{{/if}}<span class="autotime" title="{{$item.isotime}}"><time class="dt-published" datetime="{{$item.isotime}}">{{$item.localtime}}</time>{{if $item.editedtime}}&nbsp;{{$item.editedtime}}{{/if}}{{if $item.expiretime}}&nbsp;{{$item.expiretime}}{{/if}}</span>{{if $item.editedtime}}&nbsp;<i class="fa fa-pencil"></i>{{/if}}&nbsp;{{if $item.app}}<span class="item.app">{{$item.str_app}}</span>{{/if}}
+ {{if $item.verified}}<i class="fa fa-check item-verified" title="{{$item.verified}}"></i>&nbsp;{{elseif $item.forged}}<i class="fa fa-exclamation item-forged" title="{{$item.forged}}"></i>&nbsp;{{/if}}{{if $item.location}}<span class="wall-item-location p-location" id="wall-item-location-{{$item.id}}">{{$item.location}},&nbsp;</span>{{/if}}<span class="autotime" title="{{$item.isotime}}"><time class="dt-published" datetime="{{$item.isotime}}">{{$item.localtime}}</time>{{if $item.editedtime}}&nbsp;{{$item.editedtime}}{{/if}}{{if $item.expiretime}}&nbsp;{{$item.expiretime}}{{/if}}</span>&nbsp;{{if $item.delayed}}<i class="fa fa-clock-o"></i>{{/if}}{{if $item.editedtime}}&nbsp;<i class="fa fa-pencil"></i>{{/if}}&nbsp;{{if $item.app}}<span class="item.app">{{$item.str_app}}</span>{{/if}}
</div>
</div>
{{if $item.divider}}
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 @@
<a class="list-group-item{{if $class}} {{$class}}{{/if}} fakelink" href="{{if $click}}#{{else}}{{$url}}{{/if}}" {{if $click}}onclick="{{$click}}"{{/if}}>
- <img class="menu-img-3" src="{{$photo}}" title="{{$title}}" alt="" />{{include "connstatus.tpl"}}
+ <img class="menu-img-3" src="{{$photo}}" title="{{$title}}" alt="" />{{if $perminfo}}{{include "connstatus.tpl"}}{{/if}}
<span class="contactname">{{$name}}</span>
<span class="dropdown-sub-text">{{$addr}}<br>{{$network}}</span>
</a>
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 @@
-<div class="contact-block-div{{if $class}} {{$class}}{{/if}}"><a class="contact-block-link{{if $class}} {{$class}}{{/if}}{{if $click}} fakelink{{/if}}" href="{{if $click}}#{{else}}{{$url}}{{/if}}" {{if $click}}onclick="{{$click}}"{{/if}}><img class="contact-block-img{{if $class}} {{$class}}{{/if}}" src="{{$photo}}" title="{{$title}}" alt="" />{{include "connstatus.tpl"}}</a></div>
+<div class="contact-block-div{{if $class}} {{$class}}{{/if}}"><a class="contact-block-link{{if $class}} {{$class}}{{/if}}{{if $click}} fakelink{{/if}}" href="{{if $click}}#{{else}}{{$url}}{{/if}}" {{if $click}}onclick="{{$click}}"{{/if}}><img class="contact-block-img{{if $class}} {{$class}}{{/if}}" src="{{$photo}}" title="{{$title}}" alt="" />{{if $perminfo}}{{include "connstatus.tpl"}}{{/if}}</a></div>
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}}