Merge branch 'dev' into 'dev'

basic support for nomadic content, fix for permitted guests deleting their own files in cloud space of other channel,urlencode category widget links

See merge request hubzilla/core!1523
This commit is contained in:
Mario 2019-02-20 08:52:13 +01:00
commit 65e8ed6871
8 changed files with 295 additions and 38 deletions

View File

@ -2,10 +2,7 @@
namespace Zotlabs\Lib;
use Zotlabs\Lib\Libzot;
use Zotlabs\Lib\Libsync;
use Zotlabs\Lib\ActivityStreams;
use Zotlabs\Lib\Group;
use Zotlabs\Zot6\HTTPSig;
class Activity {
@ -43,6 +40,46 @@ class Activity {
}
static function fetch($url,$channel = null) {
$redirects = 0;
if(! check_siteallowed($url)) {
logger('blacklisted: ' . $url);
return null;
}
if(! $channel) {
$channel = get_sys_channel();
}
logger('fetch: ' . $url, LOGGER_DEBUG);
if(strpos($url,'x-zot:') === 0) {
$x = ZotURL::fetch($url,$channel);
}
else {
$m = parse_url($url);
$headers = [
'Accept' => 'application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
'Host' => $m['host'],
'(request-target)' => 'get ' . get_request_string($url),
'Date' => datetime_convert('UTC','UTC','now','D, d M Y H:i:s') . ' UTC'
];
$h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel),false);
$x = z_fetch_url($url, true, $redirects, [ 'headers' => $h ] );
}
if($x['success']) {
$y = json_decode($x['body'],true);
logger('returned: ' . json_encode($y,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));
return json_decode($x['body'], true);
}
else {
logger('fetch failed: ' . $url);
}
return null;
}
static function fetch_person($x) {
return self::fetch_profile($x);

View File

@ -263,24 +263,8 @@ class ActivityStreams {
return self::fetch($url);
}
static function fetch($url) {
$redirects = 0;
if(! check_siteallowed($url)) {
logger('blacklisted: ' . $url);
return null;
}
logger('fetch: ' . $url, LOGGER_DEBUG);
$x = z_fetch_url($url, true, $redirects,
[ 'headers' => [ 'Accept: application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams"' ]]);
if($x['success']) {
$y = json_decode($x['body'],true);
logger('returned: ' . json_encode($y,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));
return json_decode($x['body'], true);
}
else {
logger('fetch failed: ' . $url);
}
return null;
static function fetch($url,$channel = null) {
return Activity::fetch($url,$channel);
}
static function is_an_actor($s) {

91
Zotlabs/Lib/ZotURL.php Normal file
View File

@ -0,0 +1,91 @@
<?php
namespace Zotlabs\Lib;
use Zotlabs\Zot6\HTTPSig;
class ZotURL {
static public function fetch($url,$channel) {
$ret = [ 'success' => false ];
if(strpos($url,'x-zot:') !== 0) {
return $ret;
}
if(! $url) {
return $ret;
}
$portable_url = substr($url,6);
$u = explode('/',$portable_url);
$portable_id = $u[0];
$hosts = self::lookup($portable_id);
if(! $hosts) {
return $ret;
}
foreach($hosts as $h) {
$newurl = $h . '/id/' . $portable_url;
$m = parse_url($newurl);
$data = json_encode([ 'zot_token' => random_string() ]);
if($channel && $m) {
$headers = [
'Accept' => 'application/x-zot+json',
'Content-Type' => 'application/x-zot+json',
'X-Zot-Token' => random_string(),
'Digest' => HTTPSig::generate_digest_header($data),
'Host' => $m['host'],
'(request-target)' => 'post ' . get_request_string($newurl)
];
$h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel),false);
}
else {
$h = [ 'Accept: application/x-zot+json' ];
}
$result = [];
$redirects = 0;
$x = z_post_url($newurl,$data,$redirects, [ 'headers' => $h ] );
if($x['success']) {
return $x;
}
}
return $ret;
}
static public function is_zoturl($s) {
if(strpos($url,'x-zot:') === 0) {
return true;
}
return false;
}
static public function lookup($portable_id) {
$r = q("select * from hubloc left join site on hubloc_url = site_url where hubloc_hash = '%s' and site_dead = 0 order by hubloc_primary desc",
dbesc($portable_id)
);
if(! $r) {
// extend to network lookup
return false;
}
return ids_to_array($r,'hubloc_url');
}
}

View File

@ -5,6 +5,8 @@ namespace Zotlabs\Module;
*
*/
class Filestorage extends \Zotlabs\Web\Controller {
function post() {
@ -71,14 +73,6 @@ class Filestorage extends \Zotlabs\Web\Controller {
return;
}
// Since we have ACL'd files in the wild, but don't have ACL here yet, we
// need to return for anyone other than the owner, despite the perms check for now.
$is_owner = (((local_channel()) && ($owner == local_channel())) ? true : false);
if(! ($is_owner || is_site_admin())){
info( t('Permission Denied.') . EOL );
return;
}
if(argc() > 3 && argv(3) === 'delete') {
@ -101,18 +95,31 @@ class Filestorage extends \Zotlabs\Web\Controller {
}
$file = intval(argv(2));
$r = q("SELECT hash FROM attach WHERE id = %d AND uid = %d LIMIT 1",
$r = q("SELECT hash, creator FROM attach WHERE id = %d AND uid = %d LIMIT 1",
dbesc($file),
intval($owner)
);
if(! $r) {
notice( t('File not found.') . EOL);
if($json_return)
json_return_and_die([ 'success' => false ]);
notice( t('File not found.') . EOL);
goaway(z_root() . '/cloud/' . $which);
}
if(local_channel() !== $owner) {
if($r[0]['creator'] && $r[0]['creator'] !== $ob_hash) {
notice( t('Permission denied.') . EOL);
if($json_return)
json_return_and_die([ 'success' => false ]);
goaway(z_root() . '/cloud/' . $which);
}
}
$f = $r[0];
$channel = channelx_by_n($owner);
@ -134,6 +141,19 @@ class Filestorage extends \Zotlabs\Web\Controller {
goaway(dirname($url));
}
// Since we have ACL'd files in the wild, but don't have ACL here yet, we
// need to return for anyone other than the owner, despite the perms check for now.
$is_owner = (((local_channel()) && ($owner == local_channel())) ? true : false);
if(! ($is_owner || is_site_admin())){
info( t('Permission Denied.') . EOL );
return;
}
if(argc() > 3 && argv(3) === 'edit') {
require_once('include/acl_selectors.php');
if(! $perms['write_storage']) {

119
Zotlabs/Module/Id.php Normal file
View File

@ -0,0 +1,119 @@
<?php
namespace Zotlabs\Module;
/**
*
* Controller for responding to x-zot: protocol requests
* x-zot:_jkfRG85nJ-714zn-LW_VbTFW8jSjGAhAydOcJzHxqHkvEHWG2E0RbA_pbch-h4R63RG1YJZifaNzgccoLa3MQ/453c1678-1a79-4af7-ab65-6b012f6cab77
*
*/
use Zotlabs\Lib\Activity;
use Zotlabs\Lib\ActivityStreams;
use Zotlabs\Lib\LDSignatures;
use Zotlabs\Zot6\HTTPSig;
use Zotlabs\Web\Controller;
use Zotlabs\Lib\Libzot;
use Zotlabs\Lib\ThreadListener;
use Zotlabs\Lib\IConfig;
use Zotlabs\Lib\Enotify;
use App;
require_once('include/attach.php');
require_once('include/bbcode.php');
require_once('include/security.php');
class Id extends Controller {
function init() {
if(Libzot::is_zot_request()) {
$conversation = false;
$request_portable_id = argv(1);
if(argc() > 2) {
$item_id = argv(2);
}
$portable_id = EMPTY_STR;
$sigdata = HTTPSig::verify(EMPTY_STR);
if($sigdata['portable_id'] && $sigdata['header_valid']) {
$portable_id = $sigdata['portable_id'];
}
$chan = channelx_by_hash($request_portable_id);
if($chan) {
$channel_id = $chan['channel_id'];
if(! $item_id) {
$handler = new Channel();
App::$argc = 2;
App::$argv[0] = 'channel';
App::$argv[1] = $chan['channel_address'];
$handler->init();
}
}
else {
http_status_exit(404, 'Not found');
}
$item_normal = " and item.item_hidden = 0 and item.item_type = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_blocked = 0 ";
$sql_extra = item_permissions_sql(0);
$r = q("select * from item where uuid = '%s' $item_normal $sql_extra and uid = %d limit 1",
dbesc($item_id),
intval($channel_id)
);
if(! $r) {
$r = q("select * from item where uuid = '%s' $item_normal and uid = %d limit 1",
dbesc($item_id),
intval($channel_id)
);
if($r) {
http_status_exit(403, 'Forbidden');
}
http_status_exit(404, 'Not found');
}
if(! perm_is_allowed($chan['channel_id'],get_observer_hash(),'view_stream'))
http_status_exit(403, 'Forbidden');
xchan_query($r,true);
$items = fetch_post_tags($r,true);
$i = Activity::encode_item($items[0]);
if(! $i)
http_status_exit(404, 'Not found');
$x = array_merge(['@context' => [
ACTIVITYSTREAMS_JSONLD_REV,
'https://w3id.org/security/v1',
z_root() . ZOT_APSCHEMA_REV
]], $i);
$headers = [];
$headers['Content-Type'] = 'application/x-zot+json' ;
$ret = json_encode($x, JSON_UNESCAPED_SLASHES);
$headers['Digest'] = HTTPSig::generate_digest_header($ret);
$headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI'];
$h = HTTPSig::create_sig($headers,$chan['channel_prvkey'],channel_url($chan));
HTTPSig::set_headers($h);
echo $ret;
killme();
}
}
}

View File

@ -205,17 +205,21 @@ class Browser extends DAV\Browser\Plugin {
// upload access. system.thumbnail_security should be set to 1 if you want to include these
// types
$is_creator = false;
$photo_icon = '';
$preview_style = intval(get_config('system','thumbnail_security',0));
$r = q("select content from attach where hash = '%s' and uid = %d limit 1",
$r = q("select content, creator from attach where hash = '%s' and uid = %d limit 1",
dbesc($attachHash),
intval($owner)
);
if($r && file_exists(dbunescbin($r[0]['content']) . '.thumb')) {
$photo_icon = 'data:image/jpeg;base64,' . base64_encode(file_get_contents(dbunescbin($r[0]['content']) . '.thumb'));
// logger('found thumb: ' . $photo_icon);
if($r) {
$is_creator = (($r[0]['creator'] === get_observer_hash()) ? true : false);
if(file_exists(dbunescbin($r[0]['content']) . '.thumb')) {
$photo_icon = 'data:image/jpeg;base64,' . base64_encode(file_get_contents(dbunescbin($r[0]['content']) . '.thumb'));
// logger('found thumb: ' . $photo_icon);
}
}
if(strpos($type,'image/') === 0 && $attachHash) {
@ -247,6 +251,7 @@ class Browser extends DAV\Browser\Plugin {
$ft['attachIcon'] = (($size) ? $attachIcon : '');
// @todo Should this be an item value, not a global one?
$ft['is_owner'] = $is_owner;
$ft['is_creator'] = $is_creator;
$ft['fullPath'] = $fullPath;
$ft['displayName'] = $displayName;
$ft['type'] = $type;
@ -256,6 +261,7 @@ class Browser extends DAV\Browser\Plugin {
$ft['iconFromType'] = getIconFromType($type);
$f[] = $ft;
}

View File

@ -5,7 +5,7 @@
<ul class="nav nav-pills flex-column">
<li class="nav-item"><a href="{{$base}}" class="nav-link{{if $sel_all}} active{{/if}}">{{$all}}</a></li>
{{foreach $terms as $term}}
<li class="nav-item"><a href="{{$base}}?f=&cat={{$term.name}}" class="nav-link{{if $term.selected}} active{{/if}}">{{$term.name}}</a></li>
<li class="nav-item"><a href="{{$base}}?f=&cat={{$term.name|urlencode}}" class="nav-link{{if $term.selected}} active{{/if}}">{{$term.name}}</a></li>
{{/foreach}}
</ul>

View File

@ -68,7 +68,7 @@
<td class="cloud-index-tool"><a href="#" title="{{$delete}}" onclick="dropItem('{{$item.fileStorageUrl}}/{{$item.attachId}}/delete/json', '#cloud-index-{{$item.attachId}},#cloud-tools-{{$item.attachId}}'); return false;"><i class="fa fa-trash-o drop-icons"></i></a></td>
{{else}}
<td></td><td></td><td></td>{{if $is_admin}}<td class="cloud-index-tool"><a href="#" title="{{$admin_delete}}" onclick="dropItem('{{$item.fileStorageUrl}}/{{$item.attachId}}/delete/json', '#cloud-index-{{$item.attachId}},#cloud-tools-{{$item.attachId}}'); return false;"><i class="fa fa-trash-o drop-icons"></i></a>{{else}}<td>{{/if}}</td>
<td></td><td></td><td></td>{{if $is_admin || $item.is_creator}}<td class="cloud-index-tool"><a href="#" title="{{if $is_admin}}{{$admin_delete}}{{else}}{{$delete}}{{/if}}" onclick="dropItem('{{$item.fileStorageUrl}}/{{$item.attachId}}/delete/json', '#cloud-index-{{$item.attachId}},#cloud-tools-{{$item.attachId}}'); return false;"><i class="fa fa-trash-o drop-icons"></i></a>{{else}}<td>{{/if}}</td>
{{/if}}
<td>{{*{{$item.type}}*}}</td>
<td class="d-none d-md-table-cell">{{$item.sizeFormatted}}</td>