aboutsummaryrefslogtreecommitdiffstats
path: root/Zotlabs
diff options
context:
space:
mode:
Diffstat (limited to 'Zotlabs')
-rw-r--r--Zotlabs/Lib/Enotify.php14
-rw-r--r--Zotlabs/Lib/Libzot.php401
-rw-r--r--Zotlabs/Lib/PConfig.php107
-rw-r--r--Zotlabs/Module/Owa.php10
-rw-r--r--Zotlabs/Module/Photo.php78
-rw-r--r--Zotlabs/Module/Photos.php12
-rw-r--r--Zotlabs/Module/Search.php10
-rw-r--r--Zotlabs/Update/_1225.php26
8 files changed, 479 insertions, 179 deletions
diff --git a/Zotlabs/Lib/Enotify.php b/Zotlabs/Lib/Enotify.php
index cfb0bd344..25c96d9cc 100644
--- a/Zotlabs/Lib/Enotify.php
+++ b/Zotlabs/Lib/Enotify.php
@@ -825,7 +825,7 @@ class Enotify {
// convert this logic into a json array just like the system notifications
- return array(
+ $x = array(
'notify_link' => $item['llink'],
'name' => $item['author']['xchan_name'],
'url' => $item['author']['xchan_url'],
@@ -835,9 +835,19 @@ class Enotify {
'b64mid' => ((in_array($item['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) ? 'b64.' . base64url_encode($item['thr_parent']) : 'b64.' . base64url_encode($item['mid'])),
'notify_id' => 'undefined',
'thread_top' => (($item['item_thread_top']) ? true : false),
- 'message' => strip_tags(bbcode($itemem_text))
+ 'message' => strip_tags(bbcode($itemem_text)),
+ // these are for the superblock addon
+ 'hash' => $item['author']['xchan_hash'],
+ 'uid' => local_channel(),
+ 'display' => true
);
+ call_hooks('enotify_format',$x);
+ if(! $x['display']) {
+ return [];
+ }
+
+ return $x;
}
}
diff --git a/Zotlabs/Lib/Libzot.php b/Zotlabs/Lib/Libzot.php
index ec9db4ce1..2c726aff4 100644
--- a/Zotlabs/Lib/Libzot.php
+++ b/Zotlabs/Lib/Libzot.php
@@ -7,16 +7,10 @@ namespace Zotlabs\Lib;
*
*/
-use Zotlabs\Lib\DReport;
-use Zotlabs\Lib\Enotify;
-use Zotlabs\Lib\Group;
-use Zotlabs\Lib\Libsync;
-use Zotlabs\Lib\Libzotdir;
-use Zotlabs\Lib\System;
-use Zotlabs\Lib\MessageFilter;
-use Zotlabs\Lib\Queue;
-use Zotlabs\Lib\Zotfinger;
-use Zotlabs\Web\HTTPSig;
+use Zotlabs\Zot6\HTTPSig;
+use Zotlabs\Access\Permissions;
+use Zotlabs\Access\PermissionLimits;
+use Zotlabs\Daemon\Master;
require_once('include/crypto.php');
@@ -211,9 +205,10 @@ class Libzot {
if($channel) {
$headers = [
- 'X-Zot-Token' => random_string(),
- 'Digest' => HTTPSig::generate_digest_header($data),
- 'Content-type' => 'application/x-zot+json'
+ 'X-Zot-Token' => random_string(),
+ 'Digest' => HTTPSig::generate_digest_header($data),
+ 'Content-type' => 'application/x-zot+json',
+ '(request-target)' => 'post ' . get_request_string($url)
];
$h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel),false,'sha512',
@@ -378,13 +373,13 @@ class Libzot {
else {
// if we were just granted read stream permission and didn't have it before, try to pull in some posts
if((! $old_read_stream_perm) && (intval($permissions['view_stream'])))
- \Zotlabs\Daemon\Master::Summon(array('Onepoll',$r[0]['abook_id']));
+ Master::Summon([ 'Onepoll', $r[0]['abook_id'] ]);
}
}
else {
- $p = \Zotlabs\Access\Permissions::connect_perms($channel['channel_id']);
- $my_perms = \Zotlabs\Access\Permissions::serialise($p['perms']);
+ $p = Permissions::connect_perms($channel['channel_id']);
+ $my_perms = Permissions::serialise($p['perms']);
$automatic = $p['automatic'];
@@ -424,8 +419,8 @@ class Libzot {
);
if($new_connection) {
- if(! \Zotlabs\Access\Permissions::PermsCompare($new_perms,$previous_perms))
- \Zotlabs\Daemon\Master::Summon(array('Notifier','permissions_create',$new_connection[0]['abook_id']));
+ if(! Permissions::PermsCompare($new_perms,$previous_perms))
+ Master::Summon([ 'Notifier', 'permissions_create', $new_connection[0]['abook_id'] ]);
Enotify::submit(
[
'type' => NOTIFY_INTRO,
@@ -438,7 +433,7 @@ class Libzot {
if(intval($permissions['view_stream'])) {
if(intval(get_pconfig($channel['channel_id'],'perm_limits','send_stream') & PERMS_PENDING)
|| (! intval($new_connection[0]['abook_pending'])))
- \Zotlabs\Daemon\Master::Summon(array('Onepoll',$new_connection[0]['abook_id']));
+ Master::Summon([ 'Onepoll', $new_connection[0]['abook_id'] ]);
}
@@ -975,39 +970,45 @@ class Libzot {
$x = json_decode($x,true);
}
- if(! $x['success']) {
+ if(! is_array($x)) {
+ btlogger('failed communication - no response');
+ }
- // handle remote validation issues
+ if($x) {
+ if(! $x['success']) {
- $b = q("update dreport set dreport_result = '%s', dreport_time = '%s' where dreport_queue = '%s'",
- dbesc(($x['message']) ? $x['message'] : 'unknown delivery error'),
- dbesc(datetime_convert()),
- dbesc($outq['outq_hash'])
- );
- }
+ // handle remote validation issues
+
+ $b = q("update dreport set dreport_result = '%s', dreport_time = '%s' where dreport_queue = '%s'",
+ dbesc(($x['message']) ? $x['message'] : 'unknown delivery error'),
+ dbesc(datetime_convert()),
+ dbesc($outq['outq_hash'])
+ );
+ }
- if(array_key_exists('delivery_report',$x) && is_array($x['delivery_report'])) {
- foreach($x['delivery_report'] as $xx) {
- if(is_array($xx) && array_key_exists('message_id',$xx) && DReport::is_storable($xx)) {
- q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_name, dreport_result, dreport_time, dreport_xchan ) values ( '%s', '%s', '%s','%s','%s','%s','%s' ) ",
- dbesc($xx['message_id']),
- dbesc($xx['location']),
- dbesc($xx['recipient']),
- dbesc($xx['name']),
- dbesc($xx['status']),
- dbesc(datetime_convert($xx['date'])),
- dbesc($xx['sender'])
- );
+ if(is_array($x) && array_key_exists('delivery_report',$x) && is_array($x['delivery_report'])) {
+ foreach($x['delivery_report'] as $xx) {
+ call_hooks('dreport_process',$xx);
+ if(is_array($xx) && array_key_exists('message_id',$xx) && DReport::is_storable($xx)) {
+ q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_name, dreport_result, dreport_time, dreport_xchan ) values ( '%s', '%s', '%s','%s','%s','%s','%s' ) ",
+ dbesc($xx['message_id']),
+ dbesc($xx['location']),
+ dbesc($xx['recipient']),
+ dbesc($xx['name']),
+ dbesc($xx['status']),
+ dbesc(datetime_convert($xx['date'])),
+ dbesc($xx['sender'])
+ );
+ }
}
- }
- // we have a more descriptive delivery report, so discard the per hub 'queue' report.
+ // we have a more descriptive delivery report, so discard the per hub 'queue' report.
- q("delete from dreport where dreport_queue = '%s' ",
- dbesc($outq['outq_hash'])
- );
+ q("delete from dreport where dreport_queue = '%s' ",
+ dbesc($outq['outq_hash'])
+ );
+ }
}
-
// update the timestamp for this site
q("update site set site_dead = 0, site_update = '%s' where site_url = '%s'",
@@ -1092,13 +1093,27 @@ class Libzot {
return;
}
- $message_request = ((array_key_exists('message_id',$env)) ? true : false);
- if($message_request)
- logger('processing message request');
+ $message_request = false;
+
$has_data = array_key_exists('data',$env) && $env['data'];
$data = (($has_data) ? $env['data'] : false);
-
+
+ $AS = null;
+
+ if($env['encoding'] === 'activitystreams') {
+
+ $AS = new ActivityStreams($data);
+ if(! $AS->is_valid()) {
+ logger('Activity rejected: ' . print_r($data,true));
+ return;
+ }
+ $arr = Activity::decode_note($AS);
+
+ logger($AS->debug());
+ }
+
+
$deliveries = null;
if(array_key_exists('recipients',$env) && count($env['recipients'])) {
@@ -1140,7 +1155,7 @@ class Libzot {
// and who are allowed to see them based on the sender's permissions
// @fixme;
- $deliveries = self::public_recips($env);
+ $deliveries = self::public_recips($env,$AS);
}
@@ -1157,48 +1172,42 @@ class Libzot {
if(in_array($env['type'],['activity','response'])) {
- if($env['encoding'] === 'zot') {
- $arr = get_item_elements($data);
-
- $v = validate_item_elements($data,$arr);
-
- if(! $v['success']) {
- logger('Activity rejected: ' . $v['message'] . ' ' . print_r($data,true));
- return;
- }
- }
- elseif($env['encoding'] === 'activitystreams') {
+ $arr = Activity::decode_note($AS);
- $AS = new \Zotlabs\Lib\ActivityStreams($data);
- if(! $AS->is_valid()) {
- logger('Activity rejected: ' . print_r($data,true));
- return;
- }
- $arr = \Zotlabs\Lib\Activity::decode_note($AS);
+ //logger($AS->debug());
- logger($AS->debug());
+ $r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1",
+ dbesc($AS->actor['id'])
+ );
- $r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1",
- dbesc($AS->actor['id'])
- );
+ if($r) {
+ $arr['author_xchan'] = $r[0]['hubloc_hash'];
+ }
- if($r) {
- $arr['author_xchan'] = $r[0]['hubloc_hash'];
- }
- // @fixme (in individual delivery, change owner if needed)
+
+ $s = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1",
+ dbesc($env['sender'])
+ );
+
+ // in individual delivery, change owner if needed
+ if($s) {
+ $arr['owner_xchan'] = $s[0]['hubloc_hash'];
+ }
+ else {
$arr['owner_xchan'] = $env['sender'];
- if($private) {
- $arr['item_private'] = true;
- }
- // @fixme - spoofable
- if($AS->data['hubloc']) {
- $arr['item_verified'] = true;
- }
- if($AS->data['signed_data']) {
- IConfig::Set($arr,'activitystreams','signed_data',$AS->data['signed_data'],false);
- }
+ }
+ if($private) {
+ $arr['item_private'] = true;
+ }
+ // @fixme - spoofable
+ if($AS->data['hubloc']) {
+ $arr['item_verified'] = true;
}
+ if($AS->data['signed_data']) {
+ IConfig::Set($arr,'activitystreams','signed_data',$AS->data['signed_data'],false);
+ }
+
logger('Activity received: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG);
logger('Activity recipients: ' . print_r($deliveries,true), LOGGER_DATA, LOG_DEBUG);
@@ -1225,15 +1234,31 @@ class Libzot {
}
- static function is_top_level($env) {
+ static function is_top_level($env,$act) {
if($env['encoding'] === 'zot' && array_key_exists('flags',$env) && in_array('thread_parent', $env['flags'])) {
return true;
}
- if($env['encoding'] === 'activitystreams') {
- if(array_key_exists('inReplyTo',$env['data']) && $env['data']['inReplyTo']) {
+ if($act) {
+ if(in_array($act->type, ['Like','Dislike'])) {
return false;
}
- return true;
+ $x = self::find_parent($env,$act);
+ if($x === $act->id || $x === $act->obj['id']) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ static function find_parent($env,$act) {
+ if($act) {
+ if(in_array($act->type, ['Like','Dislike'])) {
+ return $act->obj['id'];
+ }
+ if($act->parent_id) {
+ return $act->parent_id;
+ }
}
return false;
}
@@ -1255,7 +1280,7 @@ class Libzot {
* @return NULL|array
*/
- static function public_recips($msg) {
+ static function public_recips($msg, $act) {
require_once('include/channel.php');
@@ -1269,7 +1294,7 @@ class Libzot {
$perm = 'send_stream';
- if(self::is_top_level($msg)) {
+ if(self::is_top_level($msg,$act)) {
$check_mentions = true;
}
}
@@ -1301,9 +1326,9 @@ class Libzot {
if($check_mentions) {
// It's a top level post. Look at the tags. See if any of them are mentions and are on this hub.
- if(array_path_exists('data/object/tag',$msg)) {
- if(is_array($msg['data']['object']['tag']) && $msg['data']['object']['tag']) {
- foreach($msg['data']['object']['tag'] as $tag) {
+ if($act && $act->obj) {
+ if(is_array($act->obj['tag']) && $act->obj['tag']) {
+ foreach($act->obj['tag'] as $tag) {
if($tag['type'] === 'Mention' && (strpos($tag['href'],z_root()) !== false)) {
$address = basename($tag['href']);
if($address) {
@@ -1325,9 +1350,12 @@ class Libzot {
// everybody that stored a copy of the parent. This way we know we're covered. We'll check the
// comment permissions when we deliver them.
- if(array_path_exists('data/inReplyTo',$msg)) {
- $z = q("select owner_xchan as hash from item where parent_mid = '%s' ",
- dbesc($msg['data']['inReplyTo'])
+ $thread_parent = self::find_parent($msg,$act);
+
+ if($thread_parent) {
+ $z = q("select channel_hash as hash from channel left join item on channel.channel_id = item.uid where ( item.thr_parent = '%s' OR item.parent_mid = '%s' ) ",
+ dbesc($thread_parent),
+ dbesc($thread_parent)
);
if($z) {
foreach($z as $zv) {
@@ -1341,7 +1369,7 @@ class Libzot {
// It's a bit of work since it's a multi-dimensional array
if($r) {
- $r = array_unique($r);
+ $r = array_values(array_unique($r));
}
logger('public_recips: ' . print_r($r,true), LOGGER_DATA, LOG_DEBUG);
@@ -1378,7 +1406,7 @@ class Libzot {
$local_public = $public;
- $DR = new \Zotlabs\Lib\DReport(z_root(),$sender,$d,$arr['mid']);
+ $DR = new DReport(z_root(),$sender,$d,$arr['mid']);
$channel = channelx_by_hash($d);
@@ -1413,7 +1441,7 @@ class Libzot {
$local_public = true;
$r = q("select xchan_selfcensored from xchan where xchan_hash = '%s' limit 1",
- dbesc($sender['hash'])
+ dbesc($sender)
);
// don't import sys channel posts from selfcensored authors
if($r && (intval($r[0]['xchan_selfcensored']))) {
@@ -1441,11 +1469,30 @@ class Libzot {
$arr['item_wall'] = 0;
}
- if((! perm_is_allowed($channel['channel_id'],$sender,$perm)) && (! $tag_delivery) && (! $local_public)) {
- logger("permission denied for delivery to channel {$channel['channel_id']} {$channel['channel_address']}");
- $DR->update('permission denied');
- $result[] = $DR->get();
- continue;
+ $friendofriend = false;
+
+ if ((! $tag_delivery) && (! $local_public)) {
+ $allowed = (perm_is_allowed($channel['channel_id'],$sender,$perm));
+ if((! $allowed) && $perm === 'post_comments') {
+ $parent = q("select * from item where mid = '%s' and uid = %d limit 1",
+ dbesc($arr['parent_mid']),
+ intval($channel['channel_id'])
+ );
+ if ($parent) {
+ $allowed = can_comment_on_post($d,$parent[0]);
+ }
+ }
+ if($request) {
+ $allowed = true;
+ $friendofriend = true;
+ }
+
+ if (! $allowed) {
+ logger("permission denied for delivery to channel {$channel['channel_id']} {$channel['channel_address']}");
+ $DR->update('permission denied');
+ $result[] = $DR->get();
+ continue;
+ }
}
if($arr['mid'] != $arr['parent_mid']) {
@@ -1456,7 +1503,7 @@ class Libzot {
// As a side effect we will also do a preliminary check that we have the top-level-post, otherwise
// processing it is pointless.
- $r = q("select route, id from item where mid = '%s' and uid = %d limit 1",
+ $r = q("select route, id, owner_xchan, item_private from item where mid = '%s' and uid = %d limit 1",
dbesc($arr['parent_mid']),
intval($channel['channel_id'])
);
@@ -1480,15 +1527,21 @@ class Libzot {
if((! $relay) && (! $request) && (! $local_public)
&& perm_is_allowed($channel['channel_id'],$sender,'send_stream')) {
- \Zotlabs\Daemon\Master::Summon(array('Notifier', 'request', $channel['channel_id'], $sender, $arr['parent_mid']));
+ self::fetch_conversation($channel,$arr['parent_mid']);
}
continue;
}
- if($relay) {
+
+ if($relay || $friendofriend || (intval($r[0]['item_private']) === 0 && intval($arr['item_private']) === 0)) {
// reset the route in case it travelled a great distance upstream
// use our parent's route so when we go back downstream we'll match
// with whatever route our parent has.
+ // Also friend-of-friend conversations may have been imported without a route,
+ // but we are now getting comments via listener delivery
+ // and if there is no privacy on this or the parent, we don't care about the route,
+ // so just set the owner and route accordingly.
$arr['route'] = $r[0]['route'];
+ $arr['owner_xchan'] = $r[0]['owner_xchan'];
}
else {
@@ -1546,13 +1599,13 @@ class Libzot {
$arr['aid'] = $channel['channel_account_id'];
$arr['uid'] = $channel['channel_id'];
- $item_id = delete_imported_item($sender,$arr,$channel['channel_id'],$relay);
+ $item_id = self::delete_imported_item($sender,$arr,$channel['channel_id'],$relay);
$DR->update(($item_id) ? 'deleted' : 'delete_failed');
$result[] = $DR->get();
if($relay && $item_id) {
logger('process_delivery: invoking relay');
- \Zotlabs\Daemon\Master::Summon(array('Notifier','relay',intval($item_id)));
+ Master::Summon([ 'Notifier', 'relay', intval($item_id) ]);
$DR->update('relayed');
$result[] = $DR->get();
}
@@ -1662,7 +1715,7 @@ class Libzot {
if($relay && $item_id) {
logger('Invoking relay');
- \Zotlabs\Daemon\Master::Summon(array('Notifier','relay',intval($item_id)));
+ Master::Summon([ 'Notifier', 'relay', intval($item_id) ]);
$DR->addto_update('relayed');
$result[] = $DR->get();
}
@@ -1676,6 +1729,98 @@ class Libzot {
return $result;
}
+ static public function fetch_conversation($channel,$mid) {
+
+ // Use Zotfinger to create a signed request
+
+ $a = Zotfinger::exec($mid,$channel);
+
+ logger('received conversation: ' . print_r($a,true), LOGGER_DATA);
+
+ if($a['data']['type'] !== 'OrderedCollection') {
+ return;
+ }
+
+ if(! intval($a['data']['totalItems'])) {
+ return;
+ }
+
+ $ret = [];
+
+ foreach($a['data']['orderedItems'] as $activity) {
+
+ $AS = new ActivityStreams($activity);
+ if(! $AS->is_valid()) {
+ logger('FOF Activity rejected: ' . print_r($activity,true));
+ continue;
+ }
+ $arr = Activity::decode_note($AS);
+
+ logger($AS->debug());
+
+
+ $r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1",
+ dbesc($AS->actor['id'])
+ );
+
+ if(! $r) {
+ $y = import_author_xchan([ 'url' => $AS->actor['id'] ]);
+ if($y) {
+ $r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1",
+ dbesc($AS->actor['id'])
+ );
+ }
+ if(! $r) {
+ logger('FOF Activity: no actor');
+ continue;
+ }
+ }
+
+ if($AS->obj['actor'] && $AS->obj['actor']['id'] && $AS->obj['actor']['id'] !== $AS->actor['id']) {
+ $y = import_author_xchan([ 'url' => $AS->obj['actor']['id'] ]);
+ if(! $y) {
+ logger('FOF Activity: no object actor');
+ continue;
+ }
+ }
+
+
+ if($r) {
+ $arr['author_xchan'] = $r[0]['hubloc_hash'];
+ }
+
+ $s = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1",
+ dbesc($a['signature']['signer'])
+ );
+
+ if($s) {
+ $arr['owner_xchan'] = $s[0]['hubloc_hash'];
+ }
+ else {
+ $arr['owner_xchan'] = $a['signature']['signer'];
+ }
+
+ // @fixme - spoofable
+ if($AS->data['hubloc']) {
+ $arr['item_verified'] = true;
+ }
+ if($AS->data['signed_data']) {
+ IConfig::Set($arr,'activitystreams','signed_data',$AS->data['signed_data'],false);
+ }
+
+ logger('FOF Activity received: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG);
+ logger('FOF Activity recipient: ' . $channel['channel_hash'], LOGGER_DATA, LOG_DEBUG);
+
+ $result = self::process_delivery($arr['owner_xchan'],$arr, [ $channel['channel_hash'] ],false,false,true);
+ if ($result) {
+ $ret = array_merge($ret, $result);
+ }
+ }
+
+ return $ret;
+ }
+
+
/**
* @brief Remove community tag.
*
@@ -1900,7 +2045,7 @@ class Libzot {
foreach($deliveries as $d) {
- $DR = new \Zotlabs\Lib\DReport(z_root(),$sender,$d,$arr['mid']);
+ $DR = new DReport(z_root(),$sender,$d,$arr['mid']);
$r = q("select * from channel where channel_hash = '%s' limit 1",
dbesc($d['hash'])
@@ -2112,7 +2257,8 @@ class Libzot {
if(intval($channel['channel_removed']) && $hub['hubloc_url'] === z_root())
$hub['hubloc_deleted'] = 1;
- $ret[] = [
+
+ $z = [
'host' => $hub['hubloc_host'],
'address' => $hub['hubloc_addr'],
'id_url' => $hub['hubloc_id_url'],
@@ -2120,10 +2266,25 @@ class Libzot {
'url' => $hub['hubloc_url'],
'url_sig' => $hub['hubloc_url_sig'],
'site_id' => $hub['hubloc_site_id'],
- 'callback' => $hub['hubloc_callback'],
+ 'callback' => $hub['hubloc_url'] . '/zot',
'sitekey' => $hub['hubloc_sitekey'],
'deleted' => (intval($hub['hubloc_deleted']) ? true : false)
];
+
+ // version compatibility tweaks
+
+ if(! strpos($z['url_sig'],'.')) {
+ $z['url_sig'] = 'sha256.' . $z['url_sig'];
+ }
+
+ if(! $z['id_url']) {
+ $z['id_url'] = $z['url'] . '/channel/' . substr($z['address'],0,strpos($z['address'],'@'));
+ }
+ if(! $z['site_id']) {
+ $z['site_id'] = Libzot::make_xchan_hash($z['url'],$z['sitekey']);
+ }
+
+ $ret[] = $z;
}
}
@@ -2331,6 +2492,10 @@ class Libzot {
// we may only end up with one; which results in posts with no author name or photo and are a bit
// of a hassle to repair. If either or both are missing, do a full discovery probe.
+ if(! array_key_exists('id',$x)) {
+ return import_author_activitypub($x);
+ }
+
$hash = self::make_xchan_hash($x['id'],$x['key']);
$desturl = $x['url'];
@@ -2502,7 +2667,7 @@ class Libzot {
}
else {
// check if it has characteristics of a public forum based on custom permissions.
- $m = \Zotlabs\Access\Permissions::FilledAutoperms($e['channel_id']);
+ $m = Permissions::FilledAutoperms($e['channel_id']);
if($m) {
foreach($m as $k => $v) {
if($k == 'tag_deliver' && intval($v) == 1)
@@ -2584,13 +2749,13 @@ class Libzot {
];
$ret['channel_role'] = get_pconfig($e['channel_id'],'system','permissions_role','custom');
-
+ $ret['protocols'] = [ 'zot6' ];
$ret['searchable'] = $searchable;
$ret['adult_content'] = $adult_channel;
$ret['public_forum'] = $public_forum;
- $ret['comments'] = map_scope(\Zotlabs\Access\PermissionLimits::Get($e['channel_id'],'post_comments'));
- $ret['mail'] = map_scope(\Zotlabs\Access\PermissionLimits::Get($e['channel_id'],'post_mail'));
+ $ret['comments'] = map_scope(PermissionLimits::Get($e['channel_id'],'post_comments'));
+ $ret['mail'] = map_scope(PermissionLimits::Get($e['channel_id'],'post_mail'));
if($deleted)
$ret['deleted'] = $deleted;
diff --git a/Zotlabs/Lib/PConfig.php b/Zotlabs/Lib/PConfig.php
index ec0792ce1..b9384cf6b 100644
--- a/Zotlabs/Lib/PConfig.php
+++ b/Zotlabs/Lib/PConfig.php
@@ -57,6 +57,7 @@ class PConfig {
\App::$config[$uid][$c]['config_loaded'] = true;
}
\App::$config[$uid][$c][$k] = $rr['v'];
+ \App::$config[$uid][$c]['pcfgud:'.$k] = $rr['updated'];
}
}
}
@@ -113,7 +114,7 @@ class PConfig {
* The value to store
* @return mixed Stored $value or false
*/
- static public function Set($uid, $family, $key, $value) {
+ static public function Set($uid, $family, $key, $value, $updated=NULL) {
// this catches subtle errors where this function has been called
// with local_channel() when not logged in (which returns false)
@@ -130,29 +131,74 @@ class PConfig {
$dbvalue = ((is_array($value)) ? serialize($value) : $value);
$dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue);
+ if (! $updated) {
+ $updated = datetime_convert();
+ }
+
+ $hash = hash('sha256',$family.':'.$key);
+
+ if (self::Get($uid, 'hz_delpconfig', $hash) !== false) {
+ if (Get($uid, 'hz_delpconfig', $hash) > $updated) {
+ logger('Refusing to update pconfig with outdated info (Item deleted more recently).', LOGGER_NORMAL, LOG_ERR);
+ return self::Get($uid,$family,$key);
+ } else {
+ self::Delete($uid,'hz_delpconfig',$hash);
+ }
+ }
+
if(self::Get($uid, $family, $key) === false) {
if(! array_key_exists($uid, \App::$config))
\App::$config[$uid] = array();
if(! array_key_exists($family, \App::$config[$uid]))
\App::$config[$uid][$family] = array();
- $ret = q("INSERT INTO pconfig ( uid, cat, k, v ) VALUES ( %d, '%s', '%s', '%s' ) ",
+
+ $ret = q("INSERT INTO pconfig ( uid, cat, k, v, updated ) VALUES ( %d, '%s', '%s', '%s', '%s' ) ",
intval($uid),
dbesc($family),
dbesc($key),
- dbesc($dbvalue)
+ dbesc($dbvalue),
+ dbesc($updated)
);
+
+ // There is a possible race condition if another process happens
+ // to insert something after this thread has Loaded and now. We should
+ // at least make a note of it if it happens.
+
+ if (!$ret) {
+ logger("Error: Insert to pconfig failed.",LOGGER_NORMAL, LOG_ERR);
+ }
+
+ \App::$config[$uid][$family]['pcfgud:'.$key] = $updated;
+
}
else {
+ $new = (\App::$config[$uid][$family]['pcfgud:'.$key] < $updated);
- $ret = q("UPDATE pconfig SET v = '%s' WHERE uid = %d and cat = '%s' AND k = '%s'",
- dbesc($dbvalue),
- intval($uid),
- dbesc($family),
- dbesc($key)
- );
+ if ($new) {
+
+ // @NOTE There is still a possible race condition under limited circumstances
+ // where a value will be updated by another thread with more current data than
+ // we have. At this point there is no easy way to test for it, so we update
+ // and hope for the best.
+
+ $ret = q("UPDATE pconfig SET v = '%s', updated = '%s' WHERE uid = %d and cat = '%s' AND k = '%s' ",
+ dbesc($dbvalue),
+ dbesc($updated),
+ intval($uid),
+ dbesc($family),
+ dbesc($key)
+ );
+
+ \App::$config[$uid][$family]['pcfgud:'.$key] = $updated;
+
+ } else {
+ logger('Refusing to update pconfig with outdated info.', LOGGER_NORMAL, LOG_ERR);
+ return self::Get($uid, $family, $key);
+ }
}
+
// keep a separate copy for all variables which were
// set in the life of this page. We need this to
// synchronise channel clones.
@@ -163,7 +209,11 @@ class PConfig {
\App::$config[$uid]['transient'][$family] = array();
\App::$config[$uid][$family][$key] = $value;
- \App::$config[$uid]['transient'][$family][$key] = $value;
+
+ if ($new) {
+ \App::$config[$uid]['transient'][$family][$key] = $value;
+ \App::$config[$uid]['transient'][$family]['pcfgud:'.$key] = $updated;
+ }
if($ret)
return $value;
@@ -186,18 +236,29 @@ class PConfig {
* The configuration key to delete
* @return mixed
*/
- static public function Delete($uid, $family, $key) {
+ static public function Delete($uid, $family, $key, $updated = NULL) {
if(is_null($uid) || $uid === false)
return false;
+ $updated = ($updated) ? $updated : datetime_convert();
+
+ $newer = (\App::$config[$uid][$family]['pcfgud:'.$key] < $updated);
+
+ if (! $newer) {
+ logger('Refusing to delete pconfig with outdated delete request.', LOGGER_NORMAL, LOG_ERR);
+ return false;
+ }
+
$ret = false;
- if(array_key_exists($uid,\App::$config)
- && is_array(\App::$config['uid'])
- && array_key_exists($family,\App::$config['uid'])
- && array_key_exists($key, \App::$config[$uid][$family]))
+ if (isset(\App::$config[$uid][$family][$key])) {
unset(\App::$config[$uid][$family][$key]);
+ }
+
+ if (isset(\App::$config[$uid][$family]['pcfgud:'.$key])) {
+ unset(\App::$config[$uid][$family]['pcfgud:'.$key]);
+ }
$ret = q("DELETE FROM pconfig WHERE uid = %d AND cat = '%s' AND k = '%s'",
intval($uid),
@@ -205,6 +266,22 @@ class PConfig {
dbesc($key)
);
+ if ($family != 'hz_delpconfig') {
+ $hash = hash('sha256',$family.':'.$key);
+ set_pconfig($uid,'hz_delpconfig',$hash,$updated);
+ }
+
+ // Synchronize delete with clones.
+
+ if(! array_key_exists('transient', \App::$config[$uid]))
+ \App::$config[$uid]['transient'] = array();
+ if(! array_key_exists($family, \App::$config[$uid]['transient']))
+ \App::$config[$uid]['transient'][$family] = array();
+
+ if ($new) {
+ \App::$config[$uid]['transient'][$family]['pcfgdel:'.$key] = $updated;
+ }
+
return $ret;
}
diff --git a/Zotlabs/Module/Owa.php b/Zotlabs/Module/Owa.php
index 4a488086f..ad57f883c 100644
--- a/Zotlabs/Module/Owa.php
+++ b/Zotlabs/Module/Owa.php
@@ -31,15 +31,17 @@ class Owa extends \Zotlabs\Web\Controller {
if($keyId) {
$r = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash
- where hubloc_addr = '%s' ",
- dbesc(str_replace('acct:','',$keyId))
+ where ( hubloc_addr = '%s' or hubloc_id_url = '%s' ) ",
+ dbesc(str_replace('acct:','',$keyId)),
+ dbesc($keyId)
);
if(! $r) {
$found = discover_by_webbie(str_replace('acct:','',$keyId));
if($found) {
$r = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash
- where hubloc_addr = '%s' ",
- dbesc(str_replace('acct:','',$keyId))
+ where ( hubloc_addr = '%s' or hubloc_id_url = '%s' ) ",
+ dbesc(str_replace('acct:','',$keyId)),
+ dbesc($keyId)
);
}
}
diff --git a/Zotlabs/Module/Photo.php b/Zotlabs/Module/Photo.php
index 8efc00707..30e8340e2 100644
--- a/Zotlabs/Module/Photo.php
+++ b/Zotlabs/Module/Photo.php
@@ -4,13 +4,12 @@ namespace Zotlabs\Module;
require_once('include/security.php');
require_once('include/attach.php');
require_once('include/photo/photo_driver.php');
-require_once('include/photos.php');
class Photo extends \Zotlabs\Web\Controller {
function init() {
-
+
$prvcachecontrol = false;
$streaming = null;
$channel = null;
@@ -32,26 +31,26 @@ class Photo extends \Zotlabs\Web\Controller {
}
$observer_xchan = get_observer_hash();
+ $ismodified = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
- $default = z_root() . '/' . get_default_profile_photo();
-
if(isset($type)) {
/**
* Profile photos - Access controls on default profile photos are not honoured since they need to be exchanged with remote sites.
*
*/
-
+
+ $default = get_default_profile_photo();
+
if($type === 'profile') {
switch($res) {
-
case 'm':
$resolution = 5;
- $default = z_root() . '/' . get_default_profile_photo(80);
+ $default = get_default_profile_photo(80);
break;
case 's':
$resolution = 6;
- $default = z_root() . '/' . get_default_profile_photo(48);
+ $default = get_default_profile_photo(48);
break;
case 'l':
default:
@@ -60,6 +59,8 @@ class Photo extends \Zotlabs\Web\Controller {
}
}
+ $modified = filemtime($default);
+ $default = z_root() . '/' . $default;
$uid = $person;
$d = [ 'imgscale' => $resolution, 'channel_id' => $uid, 'default' => $default, 'data' => '', 'mimetype' => '' ];
@@ -78,17 +79,18 @@ class Photo extends \Zotlabs\Web\Controller {
intval(PHOTO_PROFILE)
);
if($r) {
+ $modified = strtotime($r[0]['edited'] . "Z");
$data = dbunescbin($r[0]['content']);
$mimetype = $r[0]['mimetype'];
}
if(intval($r[0]['os_storage']))
$data = file_get_contents($data);
}
+
if(! $data) {
- $data = fetch_image_from_url($default,$mimetype);
- }
- if(! $mimetype) {
- $mimetype = 'image/png';
+ $x = z_fetch_url($default,true,0,[ 'novalidate' => true ]);
+ $data = ($x['success'] ? $x['body'] : EMPTY_STR);
+ $mimetype = 'image/png';
}
}
else {
@@ -124,9 +126,7 @@ class Photo extends \Zotlabs\Web\Controller {
$photo = substr($photo,0,-2);
// If viewing on a high-res screen, attempt to serve a higher resolution image:
if ($resolution == 2 && ($cookie_value > 1))
- {
$resolution = 1;
- }
}
$r = q("SELECT uid, photo_usage FROM photo WHERE resource_id = '%s' AND imgscale = %d LIMIT 1",
@@ -163,10 +163,13 @@ class Photo extends \Zotlabs\Web\Controller {
if($exists && $allowed) {
$data = dbunescbin($e[0]['content']);
+ $filesize = $e[0]['filesize'];
$mimetype = $e[0]['mimetype'];
- if(intval($e[0]['os_storage'])) {
+ $modified = strtotime($e[0]['edited'] . 'Z');
+ if(intval($e[0]['os_storage']))
$streaming = $data;
- }
+ if($e[0]['allow_cid'] != '' || $e[0]['allow_gid'] != '' || $e[0]['deny_gid'] != '' || $e[0]['deny_gid'] != '')
+ $prvcachecontrol = true;
}
else {
if(! $allowed) {
@@ -177,27 +180,40 @@ class Photo extends \Zotlabs\Web\Controller {
}
}
+ } else {
+ http_status_exit(404,'not found');
}
}
-
+
+ header_remove('Pragma');
+
+ if($ismodified === gmdate("D, d M Y H:i:s", $modified) . " GMT") {
+ header_remove('Expires');
+ header_remove('Cache-Control');
+ header_remove('Set-Cookie');
+ http_status_exit(304,'not modified');
+ }
+
if(! isset($data)) {
if(isset($resolution)) {
switch($resolution) {
-
case 4:
- $data = fetch_image_from_url(z_root() . '/' . get_default_profile_photo(),$mimetype);
+ $default = get_default_profile_photo();
break;
case 5:
- $data = fetch_image_from_url(z_root() . '/' . get_default_profile_photo(80),$mimetype);
+ $default = get_default_profile_photo(80);
break;
case 6:
- $data = fetch_image_from_url(z_root() . '/' . get_default_profile_photo(48),$mimetype);
+ $default = get_default_profile_photo(48);
break;
default:
killme();
// NOTREACHED
break;
}
+ $x = z_fetch_url(z_root() . '/' . $default,true,0,[ 'novalidate' => true ]);
+ $data = ($x['success'] ? $x['body'] : EMPTY_STR);
+ $mimetype = 'image/png';
}
}
@@ -210,15 +226,14 @@ class Photo extends \Zotlabs\Web\Controller {
}
}
+ // @FIXME Seems never invoked
// Writing in cachefile
- if (isset($cachefile) && $cachefile != '')
+ if (isset($cachefile) && $cachefile != '') {
file_put_contents($cachefile, $data);
-
- if(function_exists('header_remove')) {
- header_remove('Pragma');
- header_remove('pragma');
+ $modified = filemtime($cachefile);
}
-
+
+
header("Content-type: " . $mimetype);
if($prvcachecontrol) {
@@ -240,15 +255,16 @@ class Photo extends \Zotlabs\Web\Controller {
// This has performance considerations but we highly recommend you
// leave it alone.
- $cache = get_config('system','photo_cache_time');
- if(! $cache)
- $cache = (3600 * 24); // 1 day
-
+ $cache = get_config('system','photo_cache_time', 86400); // 1 day by default
+
header("Expires: " . gmdate("D, d M Y H:i:s", time() + $cache) . " GMT");
header("Cache-Control: max-age=" . $cache);
}
+ header("Last-Modified: " . gmdate("D, d M Y H:i:s", $modified) . " GMT");
+ header("Content-Length: " . (isset($filesize) ? $filesize : strlen($data)));
+
// If it's a file resource, stream it.
if($streaming && $channel) {
diff --git a/Zotlabs/Module/Photos.php b/Zotlabs/Module/Photos.php
index 78bfb1f09..03fd8a53d 100644
--- a/Zotlabs/Module/Photos.php
+++ b/Zotlabs/Module/Photos.php
@@ -263,7 +263,8 @@ class Photos extends \Zotlabs\Web\Controller {
$fsize = strlen($data);
}
- $x = q("update photo set content = '%s', filesize = %d, height = %d, width = %d where resource_id = '%s' and uid = %d and imgscale = 0",
+ $x = q("update photo set edited = '%s', content = '%s', filesize = %d, height = %d, width = %d where resource_id = '%s' and uid = %d and imgscale = 0",
+ dbesc(datetime_convert()),
dbescbin($data),
intval($fsize),
intval($height),
@@ -278,7 +279,8 @@ class Photos extends \Zotlabs\Web\Controller {
$width = $ph->getWidth();
$height = $ph->getHeight();
- $x = q("update photo set content = '%s', height = %d, width = %d where resource_id = '%s' and uid = %d and imgscale = 1",
+ $x = q("update photo set edited = '%s', content = '%s', height = %d, width = %d where resource_id = '%s' and uid = %d and imgscale = 1",
+ dbesc(datetime_convert()),
dbescbin($ph->imageString()),
intval($height),
intval($width),
@@ -293,7 +295,8 @@ class Photos extends \Zotlabs\Web\Controller {
$width = $ph->getWidth();
$height = $ph->getHeight();
- $x = q("update photo set content = '%s', height = %d, width = %d where resource_id = '%s' and uid = %d and imgscale = 2",
+ $x = q("update photo set edited = '%s', content = '%s', height = %d, width = %d where resource_id = '%s' and uid = %d and imgscale = 2",
+ dbesc(datetime_convert()),
dbescbin($ph->imageString()),
intval($height),
intval($width),
@@ -308,7 +311,8 @@ class Photos extends \Zotlabs\Web\Controller {
$width = $ph->getWidth();
$height = $ph->getHeight();
- $x = q("update photo set content = '%s', height = %d, width = %d where resource_id = '%s' and uid = %d and imgscale = 3",
+ $x = q("update photo set edited = '%s', content = '%s', height = %d, width = %d where resource_id = '%s' and uid = %d and imgscale = 3",
+ dbesc(datetime_convert()),
dbescbin($ph->imageString()),
intval($height),
intval($width),
diff --git a/Zotlabs/Module/Search.php b/Zotlabs/Module/Search.php
index e520c671d..838f9d6b9 100644
--- a/Zotlabs/Module/Search.php
+++ b/Zotlabs/Module/Search.php
@@ -6,7 +6,7 @@ class Search extends \Zotlabs\Web\Controller {
function init() {
if(x($_REQUEST,'search'))
- \App::$data['search'] = $_REQUEST['search'];
+ \App::$data['search'] = escape_tags($_REQUEST['search']);
}
@@ -46,12 +46,12 @@ class Search extends \Zotlabs\Web\Controller {
if(x(\App::$data,'search'))
$search = trim(\App::$data['search']);
else
- $search = ((x($_GET,'search')) ? trim(rawurldecode($_GET['search'])) : '');
+ $search = ((x($_GET,'search')) ? trim(escape_tags(rawurldecode($_GET['search']))) : '');
$tag = false;
if(x($_GET,'tag')) {
$tag = true;
- $search = ((x($_GET,'tag')) ? trim(rawurldecode($_GET['tag'])) : '');
+ $search = ((x($_GET,'tag')) ? trim(escape_tags(rawurldecode($_GET['tag']))) : '');
}
$static = ((array_key_exists('static',$_REQUEST)) ? intval($_REQUEST['static']) : 0);
@@ -227,9 +227,9 @@ class Search extends \Zotlabs\Web\Controller {
}
if($tag)
- $o .= '<h2>' . sprintf( t('Items tagged with: %s'),htmlspecialchars($search, ENT_COMPAT,'UTF-8')) . '</h2>';
+ $o .= '<h2>' . sprintf( t('Items tagged with: %s'),$search) . '</h2>';
else
- $o .= '<h2>' . sprintf( t('Search results for: %s'),htmlspecialchars($search, ENT_COMPAT,'UTF-8')) . '</h2>';
+ $o .= '<h2>' . sprintf( t('Search results for: %s'),$search) . '</h2>';
$o .= conversation($items,'search',$update,'client');
diff --git a/Zotlabs/Update/_1225.php b/Zotlabs/Update/_1225.php
new file mode 100644
index 000000000..a7d866154
--- /dev/null
+++ b/Zotlabs/Update/_1225.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace Zotlabs\Update;
+
+class _1225 {
+
+ function run() {
+
+ if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) {
+ $r1 = q("ALTER TABLE pconfig ADD updated timestamp NOT NULL DEFAULT '0001-01-01 00:00:00' ");
+ $r2 = q("create index \"pconfig_updated_idx\" on pconfig (\"updated\")");
+
+ $r = ($r1 && $r2);
+ }
+ else {
+ $r = q("ALTER TABLE `pconfig` ADD `updated` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' ,
+ ADD INDEX `pconfig_updated` (`updated`)");
+ }
+
+ if($r)
+ return UPDATE_SUCCESS;
+ return UPDATE_FAILED;
+
+ }
+
+}