more ocap stuff

This commit is contained in:
zotlabs 2019-09-29 19:20:09 -07:00
parent c8ab4adf76
commit 67c9632d0b
8 changed files with 206 additions and 583 deletions

View file

@ -1015,20 +1015,11 @@ class Activity {
}
}
else {
$ret['inbox'] = z_root() . '/nullbox';
if ($c) {
$ret['outbox'] = z_root() . '/outbox/' . $c['channel_address'];
}
else {
$ret['outbox'] = z_root() . '/nullbox';
}
$ret['publicKey'] = [
'id' => $p['xchan_url'],
'owner' => $p['xchan_url'],
'publicKeyPem' => $p['xchan_pubkey']
];
}
$arr = [ 'xchan' => $p, 'encoded' => $ret, 'activitypub' => $activitypub ];
@ -1603,219 +1594,6 @@ class Activity {
return (($a['width'] > $b['width']) ? -1 : 1);
}
static function create_note($channel,$observer_hash,$act) {
$s = [];
// Mastodon only allows visibility in public timelines if the public inbox is listed in the 'to' field.
// They are hidden in the public timeline if the public inbox is listed in the 'cc' field.
// This is not part of the activitypub protocol - we might change this to show all public posts in pubstream at some point.
$pubstream = ((is_array($act->obj) && array_key_exists('to', $act->obj) && in_array(ACTIVITY_PUBLIC_INBOX, $act->obj['to'])) ? true : false);
$is_sys_channel = is_sys_channel($channel['channel_id']);
$parent = ((array_key_exists('inReplyTo',$act->obj)) ? urldecode($act->obj['inReplyTo']) : '');
if ($parent) {
$r = q("select * from item where uid = %d and ( mid = '%s' or mid = '%s' ) limit 1",
intval($channel['channel_id']),
dbesc($parent),
dbesc(basename($parent))
);
if (! $r) {
logger('parent not found.');
return;
}
if ($r[0]['owner_xchan'] === $channel['channel_hash']) {
if (! perm_is_allowed($channel['channel_id'],$observer_hash,'send_stream') && ! ($is_sys_channel && $pubstream)) {
logger('no comment permission.');
return;
}
}
$s['parent_mid'] = $r[0]['mid'];
$s['owner_xchan'] = $r[0]['owner_xchan'];
$s['author_xchan'] = $observer_hash;
}
else {
if (! perm_is_allowed($channel['channel_id'],$observer_hash,'send_stream') && ! ($is_sys_channel && $pubstream)) {
logger('no permission');
return;
}
$s['owner_xchan'] = $s['author_xchan'] = $observer_hash;
}
$abook = q("select * from abook where abook_xchan = '%s' and abook_channel = %d limit 1",
dbesc($observer_hash),
intval($channel['channel_id'])
);
if (is_array($act->obj)) {
$content = self::get_content($act->obj);
}
if (! $content) {
logger('no content');
return;
}
$s['aid'] = $channel['channel_account_id'];
$s['uid'] = $channel['channel_id'];
$s['mid'] = urldecode($act->obj['id']);
$s['plink'] = urldecode($act->obj['id']);
if ($act->data['published']) {
$s['created'] = datetime_convert('UTC','UTC',$act->data['published']);
}
elseif ($act->obj['published']) {
$s['created'] = datetime_convert('UTC','UTC',$act->obj['published']);
}
if ($act->data['updated']) {
$s['edited'] = datetime_convert('UTC','UTC',$act->data['updated']);
}
elseif ($act->obj['updated']) {
$s['edited'] = datetime_convert('UTC','UTC',$act->obj['updated']);
}
if (! $s['created'])
$s['created'] = datetime_convert();
if (! $s['edited'])
$s['edited'] = $s['created'];
if (! $s['parent_mid'])
$s['parent_mid'] = $s['mid'];
$s['title'] = self::bb_content($content,'name');
$s['summary'] = self::bb_content($content,'summary');
$s['body'] = self::bb_content($content,'content');
$s['verb'] = ACTIVITY_POST;
$s['obj_type'] = ACTIVITY_OBJ_NOTE;
$generator = $act->get_property_obj('generator');
if (! $generator)
$generator = $act->get_property_obj('generator',$act->obj);
if ($generator && array_key_exists('type',$generator)
&& in_array($generator['type'], ['Application', 'Service' ] ) && array_key_exists('name',$generator)) {
$s['app'] = escape_tags($generator['name']);
}
if ($channel['channel_system']) {
if (! MessageFilter::evaluate($s,get_config('system','pubstream_incl'),get_config('system','pubstream_excl'))) {
logger('post is filtered');
return;
}
}
if (! post_is_importable($channel['channel_id'],$s,$abook[0])) {
logger('post is filtered');
return;
}
if ($act->obj['conversation']) {
set_iconfig($s,'ostatus','conversation',$act->obj['conversation'],1);
}
$a = self::decode_taxonomy($act->obj);
if ($a) {
$s['term'] = $a;
}
$a = self::decode_attachment($act->obj);
if ($a) {
$s['attach'] = $a;
}
if ($act->obj['type'] === 'Note' && $s['attach']) {
$s['body'] .= self::bb_attach($s['attach'],$s['body']);
}
// we will need a hook here to extract magnet links e.g. peertube
// right now just link to the largest mp4 we find that will fit in our
// standard content region
if ($act->obj['type'] === 'Video') {
$vtypes = [
'video/mp4',
'video/ogg',
'video/webm'
];
$mps = [];
if (array_key_exists('url',$act->obj) && is_array($act->obj['url'])) {
foreach ($act->obj['url'] as $vurl) {
if (in_array($vurl['mimeType'], $vtypes)) {
if (! array_key_exists('width',$vurl)) {
$vurl['width'] = 0;
}
$mps[] = $vurl;
}
}
}
if ($mps) {
usort($mps,[ __CLASS__, 'vid_sort' ]);
foreach ($mps as $m) {
if (intval($m['width']) < 500) {
$s['body'] .= "\n\n" . '[video]' . $m['href'] . '[/video]';
break;
}
}
}
}
if ($act->recips && (! in_array(ACTIVITY_PUBLIC_INBOX,$act->recips)))
$s['item_private'] = 1;
set_iconfig($s,'activitypub','recips',$act->raw_recips);
if ($parent) {
set_iconfig($s,'activitypub','rawmsg',$act->raw,1);
}
$x = null;
$r = q("select created, edited from item where mid = '%s' and uid = %d limit 1",
dbesc($s['mid']),
intval($s['uid'])
);
if ($r) {
if ($s['edited'] > $r[0]['edited']) {
$x = item_store_update($s);
}
else {
return;
}
}
else {
$x = item_store($s);
}
if (is_array($x) && $x['item_id']) {
if ($parent) {
if ($s['owner_xchan'] === $channel['channel_hash']) {
// We are the owner of this conversation, so send all received comments back downstream
Master::Summon(array('Notifier','comment-import',$x['item_id']));
}
$r = q("select * from item where id = %d limit 1",
intval($x['item_id'])
);
if ($r) {
send_status_notifications($x['item_id'],$r[0]);
}
}
sync_an_item($channel['channel_id'],$x['item_id']);
}
}
static function share_bb($obj) {
// @fixme - error check and set defaults
@ -2253,12 +2031,14 @@ class Activity {
intval($channel['channel_id'])
);
if ($p) {
$allowed = perm_is_allowed($channel['channel_id'],$observer_hash,'post_comments');
// check permissions against the author, not the sender
$allowed = perm_is_allowed($channel['channel_id'],$item['author_xchan'],'post_comments');
if (! $allowed) {
logger('rejected comment from ' . $observer_hash . ' for ' . $channel['channel_address']);
logger('rejected comment from ' . $item['author_xchan'] . ' for ' . $channel['channel_address']);
logger('rejected: ' . print_r($item,true), LOGGER_DATA);
// let the sender know we received their comment but we don't permit spam here.
self::send_rejection_activity($channel,$observer_hash,$item);
self::send_rejection_activity($channel,$item['author_xchan'],$item);
return;
}
}
else {
@ -2482,6 +2262,13 @@ class Activity {
break;
}
$a = new ActivityStreams($n);
if ($a->type === 'Announce' && is_array($a->obj)
&& array_key_exists('object',$a->obj) && array_key_exists('actor',$a->obj)) {
// This is a relayed/forwarded Activity (as opposed to a shared/boosted object)
// Reparse the encapsulated Activity and use that instead
logger('relayed activity',LOGGER_DEBUG);
$a = new ActivityStreams($a->obj);
}
logger($a->debug());
@ -2532,278 +2319,6 @@ class Activity {
return false;
}
static function announce_note($channel,$observer_hash,$act) {
$s = [];
$is_sys_channel = is_sys_channel($channel['channel_id']);
// Mastodon only allows visibility in public timelines if the public inbox is listed in the 'to' field.
// They are hidden in the public timeline if the public inbox is listed in the 'cc' field.
// This is not part of the activitypub protocol - we might change this to show all public posts in pubstream at some point.
$pubstream = ((is_array($act->obj) && array_key_exists('to', $act->obj) && in_array(ACTIVITY_PUBLIC_INBOX, $act->obj['to'])) ? true : false);
if (! perm_is_allowed($channel['channel_id'],$observer_hash,'send_stream') && ! ($is_sys_channel && $pubstream)) {
logger('no permission');
return;
}
if (is_array($act->obj)) {
$content = self::get_content($act->obj);
}
if (! $content) {
logger('no content');
return;
}
$s['owner_xchan'] = $s['author_xchan'] = $observer_hash;
$s['aid'] = $channel['channel_account_id'];
$s['uid'] = $channel['channel_id'];
$s['mid'] = urldecode($act->obj['id']);
$s['plink'] = urldecode($act->obj['id']);
if (! $s['created'])
$s['created'] = datetime_convert();
if (! $s['edited'])
$s['edited'] = $s['created'];
$s['parent_mid'] = $s['mid'];
$s['verb'] = ACTIVITY_POST;
$s['obj_type'] = ACTIVITY_OBJ_NOTE;
$s['app'] = t('ActivityPub');
if ($channel['channel_system']) {
if (! MessageFilter::evaluate($s,get_config('system','pubstream_incl'),get_config('system','pubstream_excl'))) {
logger('post is filtered');
return;
}
}
$abook = q("select * from abook where abook_xchan = '%s' and abook_channel = %d limit 1",
dbesc($observer_hash),
intval($channel['channel_id'])
);
if (! post_is_importable($channel['channel_id'],$s,$abook[0])) {
logger('post is filtered');
return;
}
if ($act->obj['conversation']) {
set_iconfig($s,'ostatus','conversation',$act->obj['conversation'],1);
}
$a = self::decode_taxonomy($act->obj);
if ($a) {
$s['term'] = $a;
}
$a = self::decode_attachment($act->obj);
if ($a) {
$s['attach'] = $a;
}
$body = "[share author='" . urlencode($act->sharee['name']) .
"' profile='" . $act->sharee['url'] .
"' avatar='" . $act->sharee['photo_s'] .
"' link='" . ((is_array($act->obj['url'])) ? $act->obj['url']['href'] : $act->obj['url']) .
"' auth='" . ((is_matrix_url($act->obj['url'])) ? 'true' : 'false' ) .
"' posted='" . $act->obj['published'] .
"' message_id='" . $act->obj['id'] .
"']";
if ($content['name'])
$body .= self::bb_content($content,'name') . "\r\n";
$body .= self::bb_content($content,'content');
if ($act->obj['type'] === 'Note' && $s['attach']) {
$body .= self::bb_attach($s['attach'],body);
}
$body .= "[/share]";
$s['title'] = self::bb_content($content,'name');
$s['body'] = $body;
if ($act->recips && (! in_array(ACTIVITY_PUBLIC_INBOX,$act->recips)))
$s['item_private'] = 1;
set_iconfig($s,'activitypub','recips',$act->raw_recips);
$r = q("select created, edited from item where mid = '%s' and uid = %d limit 1",
dbesc($s['mid']),
intval($s['uid'])
);
if ($r) {
if ($s['edited'] > $r[0]['edited']) {
$x = item_store_update($s);
}
else {
return;
}
}
else {
$x = item_store($s);
}
if (is_array($x) && $x['item_id']) {
if ($parent) {
if ($s['owner_xchan'] === $channel['channel_hash']) {
// We are the owner of this conversation, so send all received comments back downstream
Master::Summon(array('Notifier','comment-import',$x['item_id']));
}
$r = q("select * from item where id = %d limit 1",
intval($x['item_id'])
);
if ($r) {
send_status_notifications($x['item_id'],$r[0]);
}
}
sync_an_item($channel['channel_id'],$x['item_id']);
}
}
static function like_note($channel,$observer_hash,$act) {
$s = [];
$parent = $act->obj['id'];
if ($act->type === 'Like')
$s['verb'] = ACTIVITY_LIKE;
if ($act->type === 'Dislike')
$s['verb'] = ACTIVITY_DISLIKE;
if (! $parent)
return;
$r = q("select * from item where uid = %d and ( mid = '%s' or mid = '%s' ) limit 1",
intval($channel['channel_id']),
dbesc($parent),
dbesc(urldecode(basename($parent)))
);
if (! $r) {
logger('parent not found.');
return;
}
xchan_query($r);
$parent_item = $r[0];
if ($parent_item['owner_xchan'] === $channel['channel_hash']) {
if (! perm_is_allowed($channel['channel_id'],$observer_hash,'post_comments')) {
logger('no comment permission.');
return;
}
}
if ($parent_item['mid'] === $parent_item['parent_mid']) {
$s['parent_mid'] = $parent_item['mid'];
}
else {
$s['thr_parent'] = $parent_item['mid'];
$s['parent_mid'] = $parent_item['parent_mid'];
}
$s['owner_xchan'] = $parent_item['owner_xchan'];
$s['author_xchan'] = $observer_hash;
$s['aid'] = $channel['channel_account_id'];
$s['uid'] = $channel['channel_id'];
$s['mid'] = $act->id;
if (! $s['parent_mid'])
$s['parent_mid'] = $s['mid'];
$post_type = (($parent_item['resource_type'] === 'photo') ? t('photo') : t('status'));
$links = array(array('rel' => 'alternate','type' => 'text/html', 'href' => $parent_item['plink']));
$objtype = (($parent_item['resource_type'] === 'photo') ? ACTIVITY_OBJ_PHOTO : ACTIVITY_OBJ_NOTE );
$body = $parent_item['body'];
$z = q("select * from xchan where xchan_hash = '%s' limit 1",
dbesc($parent_item['author_xchan'])
);
if ($z)
$item_author = $z[0];
$object = json_encode(array(
'type' => $post_type,
'id' => $parent_item['mid'],
'parent' => (($parent_item['thr_parent']) ? $parent_item['thr_parent'] : $parent_item['parent_mid']),
'link' => $links,
'title' => $parent_item['title'],
'content' => $parent_item['body'],
'created' => $parent_item['created'],
'edited' => $parent_item['edited'],
'author' => array(
'name' => $item_author['xchan_name'],
'address' => $item_author['xchan_addr'],
'guid' => $item_author['xchan_guid'],
'guid_sig' => $item_author['xchan_guid_sig'],
'link' => array(
array('rel' => 'alternate', 'type' => 'text/html', 'href' => $item_author['xchan_url']),
array('rel' => 'photo', 'type' => $item_author['xchan_photo_mimetype'], 'href' => $item_author['xchan_photo_m'])),
),
), JSON_UNESCAPED_SLASHES
);
if ($act->type === 'Like')
$bodyverb = t('%1$s likes %2$s\'s %3$s');
if ($act->type === 'Dislike')
$bodyverb = t('%1$s doesn\'t like %2$s\'s %3$s');
$ulink = '[url=' . $item_author['xchan_url'] . ']' . $item_author['xchan_name'] . '[/url]';
$alink = '[url=' . $parent_item['author']['xchan_url'] . ']' . $parent_item['author']['xchan_name'] . '[/url]';
$plink = '[url='. z_root() . '/display/' . urlencode($act->id) . ']' . $post_type . '[/url]';
$s['body'] = sprintf( $bodyverb, $ulink, $alink, $plink );
$s['app'] = t('ActivityPub');
// set the route to that of the parent so downstream hubs won't reject it.
$s['route'] = $parent_item['route'];
$s['item_private'] = $parent_item['item_private'];
$s['obj_type'] = $objtype;
$s['obj'] = $object;
if ($act->obj['conversation']) {
set_iconfig($s,'ostatus','conversation',$act->obj['conversation'],1);
}
if ($act->recips && (! in_array(ACTIVITY_PUBLIC_INBOX,$act->recips)))
$s['item_private'] = 1;
set_iconfig($s,'activitypub','recips',$act->raw_recips);
$result = item_store($s);
if ($result['success']) {
// if the message isn't already being relayed, notify others
if (intval($parent_item['item_origin']))
Master::Summon(array('Notifier','comment-import',$result['item_id']));
sync_an_item($channel['channel_id'],$result['item_id']);
}
return;
}
static function bb_attach($attach,$body) {
$ret = false;
@ -2973,17 +2488,28 @@ class Activity {
}
static function bear_from_request() {
// Find either an Authorization: Bearer token or 'token' request variable
// in the current web request and return it
static function token_from_request() {
foreach ( [ 'REDIRECT_REMOTE_USER', 'HTTP_AUTHORIZATION' ] as $s ) {
$auth = ((array_key_exists($s,$_SERVER) && strpos($_SERVER[$s],'Bearer ') === 0)
? str_replace('Bearer ', EMPTY_STR, $_SERVER[$s])
: EMPTY_STR
);
break;
if ($auth) {
break;
}
}
return (($auth) ? 'bear:?u=' . z_root() . $_SERVER['REQUEST_URI'] . '&t=' . $auth : EMPTY_STR);
if (! $auth) {
if (array_key_exists('token',$_REQUEST) && $_REQUEST['token']) {
$auth = $_REQUEST['token'];
}
}
return $auth;
}

View file

@ -45,38 +45,43 @@ class ActivityStreams {
$this->raw = $string;
$this->hub = $hub;
if(is_array($string)) {
if (is_array($string)) {
$this->data = $string;
}
else {
$this->data = json_decode($string, true);
}
if($this->data) {
if ($this->data) {
// verify and unpack JSalmon signature if present
// This will only be the case for Zot6 packets
if(is_array($this->data) && array_key_exists('signed',$this->data)) {
if (is_array($this->data) && array_key_exists('signed',$this->data)) {
$ret = JSalmon::verify($this->data);
$tmp = JSalmon::unpack($this->data['data']);
if($ret && $ret['success']) {
if($ret['signer']) {
if ($ret && $ret['success']) {
if ($ret['signer']) {
logger('Unpacked: ' . json_encode($tmp,JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT),LOGGER_DATA,LOG_DEBUG);
$saved = json_encode($this->data,JSON_UNESCAPED_SLASHES);
$this->data = $tmp;
$this->data['signer'] = $ret['signer'];
$this->data['signed_data'] = $saved;
if($ret['hubloc']) {
if ($ret['hubloc']) {
$this->data['hubloc'] = $ret['hubloc'];
}
}
}
}
// This indicates only that we have sucessfully decoded JSON.
$this->valid = true;
if(array_key_exists('type',$this->data) && array_key_exists('actor',$this->data) && array_key_exists('object',$this->data)) {
if($this->data['type'] === 'Delete' && $this->data['actor'] === $this->data['object']) {
// Special handling for Mastodon "delete actor" activities which will often fail to verify
// because the key cannot be fetched. We will catch this condition elsewhere.
if (array_key_exists('type',$this->data) && array_key_exists('actor',$this->data) && array_key_exists('object',$this->data)) {
if ($this->data['type'] === 'Delete' && $this->data['actor'] === $this->data['object']) {
$this->deleted = $this->data['actor'];
$this->valid = false;
}
@ -84,7 +89,9 @@ class ActivityStreams {
}
if($this->is_valid()) {
// Attempt to assemble an Activity from what we were given.
if ($this->is_valid()) {
$this->id = $this->get_property_obj('id');
$this->type = $this->get_primary_type();
$this->actor = $this->get_actor('actor','','');
@ -95,32 +102,41 @@ class ActivityStreams {
$this->replyto = $this->get_compound_property('replyTo');
$this->ldsig = $this->get_compound_property('signature');
if($this->ldsig) {
if ($this->ldsig) {
$this->signer = $this->get_compound_property('creator',$this->ldsig);
if($this->signer && is_array($this->signer) && array_key_exists('publicKey',$this->signer) && is_array($this->signer['publicKey']) && $this->signer['publicKey']['publicKeyPem']) {
if ($this->signer && is_array($this->signer) && array_key_exists('publicKey',$this->signer)
&& is_array($this->signer['publicKey']) && $this->signer['publicKey']['publicKeyPem']) {
$this->sigok = LDSignatures::verify($this->data,$this->signer['publicKey']['publicKeyPem']);
}
}
if(! $this->obj) {
// Implied create activity required by specification if no object is present
if (! $this->obj) {
$this->obj = $this->data;
$this->type = 'Create';
if(! $this->actor) {
if (! $this->actor) {
$this->actor = $this->get_actor('attributedTo',$this->obj);
}
}
if($this->obj && is_array($this->obj) && $this->obj['actor'])
$this->obj['actor'] = $this->get_actor('actor',$this->obj);
if($this->tgt && is_array($this->tgt) && $this->tgt['actor'])
$this->tgt['actor'] = $this->get_actor('actor',$this->tgt);
// Enumerate and store actors in referenced objects
if ($this->obj && is_array($this->obj) && $this->obj['actor']) {
$this->obj['actor'] = $this->get_actor('actor',$this->obj);
}
if ($this->tgt && is_array($this->tgt) && $this->tgt['actor']) {
$this->tgt['actor'] = $this->get_actor('actor',$this->tgt);
}
// Determine if this is a followup or response activity
$this->parent_id = $this->get_property_obj('inReplyTo');
if((! $this->parent_id) && is_array($this->obj)) {
if ((! $this->parent_id) && is_array($this->obj)) {
$this->parent_id = $this->obj['inReplyTo'];
}
if((! $this->parent_id) && is_array($this->obj)) {
if ((! $this->parent_id) && is_array($this->obj)) {
$this->parent_id = $this->obj['id'];
}
}
@ -149,19 +165,21 @@ class ActivityStreams {
function collect_recips($base = '', $namespace = '') {
$x = [];
$fields = [ 'to', 'cc', 'bto', 'bcc', 'audience'];
foreach($fields as $f) {
foreach ($fields as $f) {
$y = $this->get_compound_property($f, $base, $namespace);
if($y) {
if(! is_array($y)) {
if ($y) {
if (! is_array($y)) {
$y = [ $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;
}
}
// not yet ready for prime time
// $x = $this->expand($x,$base,$namespace);
return $x;
@ -172,15 +190,15 @@ class ActivityStreams {
// right now use a hardwired recursion depth of 5
for($z = 0; $z < 5; $z ++) {
if(is_array($arr) && $arr) {
foreach($arr as $a) {
if(is_array($a)) {
for ($z = 0; $z < 5; $z ++) {
if (is_array($arr) && $arr) {
foreach ($arr as $a) {
if (is_array($a)) {
$ret[] = $a;
}
else {
$x = $this->get_compound_property($a,$base,$namespace);
if($x) {
if ($x) {
$ret = array_merge($ret,$x);
}
}
@ -200,35 +218,39 @@ class ActivityStreams {
* @param string $namespace if not set return empty string
* @return string|NULL
*/
function get_namespace($base, $namespace) {
if(! $namespace)
return '';
if (! $namespace) {
return EMPTY_STR;
}
$key = null;
foreach( [ $this->data, $base ] as $b ) {
if(! $b)
foreach ( [ $this->data, $base ] as $b ) {
if (! $b) {
continue;
}
if(array_key_exists('@context', $b)) {
if(is_array($b['@context'])) {
foreach($b['@context'] as $ns) {
if(is_array($ns)) {
foreach($ns as $k => $v) {
if($namespace === $v)
if (array_key_exists('@context', $b)) {
if (is_array($b['@context'])) {
foreach ($b['@context'] as $ns) {
if (is_array($ns)) {
foreach ($ns as $k => $v) {
if ($namespace === $v) {
$key = $k;
}
}
}
else {
if($namespace === $ns) {
if ($namespace === $ns) {
$key = '';
}
}
}
}
else {
if($namespace === $b['@context']) {
if ($namespace === $b['@context']) {
$key = '';
}
}
@ -246,15 +268,17 @@ class ActivityStreams {
* @param string $namespace (optional) default empty
* @return NULL|mixed
*/
function get_property_obj($property, $base = '', $namespace = '') {
$prefix = $this->get_namespace($base, $namespace);
if($prefix === null)
if ($prefix === null) {
return null;
}
$base = (($base) ? $base : $this->data);
$propname = (($prefix) ? $prefix . ':' : '') . $property;
if(! is_array($base)) {
if (! is_array($base)) {
btlogger('not an array: ' . print_r($base,true));
return null;
}
@ -293,7 +317,7 @@ class ActivityStreams {
function get_actor($property,$base='',$namespace = '') {
$x = $this->get_property_obj($property, $base, $namespace);
if($this->is_url($x)) {
if ($this->is_url($x)) {
// SECURITY: If we have already stored the actor profile, re-generate it
// from cached data - don't refetch it from the network
@ -301,7 +325,8 @@ class ActivityStreams {
$r = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_id_url = '%s' limit 1",
dbesc($x)
);
if($r) {
if ($r) {
// indicate that this is a cached record
$y = Activity::encode_person($r[0]);
$y['cached'] = true;
$y['updated'] = datetime_convert('UTC','UTC',$r[0]['hubloc_updated'],ATOM_TIME);
@ -309,8 +334,8 @@ class ActivityStreams {
}
}
$actor = $this->get_compound_property($property,$base,$namespace,true);
if(is_array($actor) && self::is_an_actor($actor['type'])) {
if(array_key_exists('id',$actor) && (! array_key_exists('inbox',$actor))) {
if (is_array($actor) && self::is_an_actor($actor['type'])) {
if (array_key_exists('id',$actor) && (! array_key_exists('inbox',$actor))) {
$actor = $this->fetch_property($actor['id']);
}
return $actor;
@ -328,9 +353,10 @@ class ActivityStreams {
* @param boolean $first (optional) default false, if true and result is a sequential array return only the first element
* @return NULL|mixed
*/
function get_compound_property($property, $base = '', $namespace = '', $first = false) {
$x = $this->get_property_obj($property, $base, $namespace);
if($this->is_url($x)) {
if ($this->is_url($x)) {
$y = $this->fetch_property($x);
if (is_array($y)) {
$x = $y;
@ -338,23 +364,25 @@ class ActivityStreams {
}
// verify and unpack JSalmon signature if present
if(is_array($x) && array_key_exists('signed',$x)) {
// This may be present in Zot6 packets
if (is_array($x) && array_key_exists('signed',$x)) {
$ret = JSalmon::verify($x);
$tmp = JSalmon::unpack($x['data']);
if($ret && $ret['success']) {
if($ret['signer']) {
if ($ret && $ret['success']) {
if ($ret['signer']) {
logger('Unpacked: ' . json_encode($tmp,JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT),LOGGER_DATA,LOG_DEBUG);
$saved = json_encode($x,JSON_UNESCAPED_SLASHES);
$x = $tmp;
$x['signer'] = $ret['signer'];
$x['signed_data'] = $saved;
if($ret['hubloc']) {
if ($ret['hubloc']) {
$x['hubloc'] = $ret['hubloc'];
}
}
}
}
if($first && is_array($x) && array_key_exists(0,$x)) {
if ($first && is_array($x) && array_key_exists(0,$x)) {
return $x[0];
}
@ -367,8 +395,9 @@ class ActivityStreams {
* @param string $url
* @return boolean
*/
function is_url($url) {
if(($url) && (! is_array($url)) && ((strpos($url, 'http') === 0) || (strpos($url,'x-zot') === 0))) {
if (($url) && (! is_array($url)) && ((strpos($url, 'http') === 0) || (strpos($url,'x-zot') === 0) || (strpos($url,'bear') === 0))) {
return true;
}
@ -382,14 +411,15 @@ class ActivityStreams {
* @param string $namespace (optional) default empty
* @return NULL|mixed
*/
function get_primary_type($base = '', $namespace = '') {
if(! $base)
if (! $base) {
$base = $this->data;
}
$x = $this->get_property_obj('type', $base, $namespace);
if(is_array($x)) {
foreach($x as $y) {
if(strpos($y, ':') === false) {
if (is_array($x)) {
foreach ($x as $y) {
if (strpos($y, ':') === false) {
return $y;
}
}
@ -413,8 +443,5 @@ class ActivityStreams {
]);
return(($x) ? true : false);
}
}

View file

@ -1183,6 +1183,14 @@ class Libzot {
if ($env['encoding'] === 'activitystreams') {
$AS = new ActivityStreams($data);
if ($AS->is_valid() && $AS->type === 'Announce' && is_array($AS->obj)
&& array_key_exists('object',$AS->obj) && array_key_exists('actor',$AS->obj)) {
// This is a relayed/forwarded Activity (as opposed to a shared/boosted object)
// Reparse the encapsulated Activity and use that instead
logger('relayed activity',LOGGER_DEBUG);
$AS = new ActivityStreams($AS->obj);
}
if (! $AS->is_valid()) {
logger('Activity rejected: ' . print_r($data,true));
return;
@ -2020,6 +2028,14 @@ class Libzot {
foreach ($a['data']['orderedItems'] as $activity) {
$AS = new ActivityStreams($activity);
if ($AS->is_valid() && $AS->type === 'Announce' && is_array($AS->obj)
&& array_key_exists('object',$AS->obj) && array_key_exists('actor',$AS->obj)) {
// This is a relayed/forwarded Activity (as opposed to a shared/boosted object)
// Reparse the encapsulated Activity and use that instead
logger('relayed activity',LOGGER_DEBUG);
$AS = new ActivityStreams($AS->obj);
}
if (! $AS->is_valid()) {
logger('FOF Activity rejected: ' . print_r($activity,true));
continue;

View file

@ -510,12 +510,24 @@ class ThreadItem {
if (get_config('system','activitypub') && local_channel() && get_pconfig(local_channel(),'system','activitypub',true)) {
// place to store all the author addresses (links if not available) in the thread so we can auto-mention them in JS.
$result['authors'] = [];
// fix to add in sub-replies if replying to a comment on your own post from the top level.
if ($observer && ($profile_addr === $observer['xchan_hash'] || $profile_addr === $observer['xchan_addr'])) {
// ignore it
}
else {
$result['authors'][] = $profile_addr;
}
if ($children) {
foreach ($children as $child) {
$cdata = $child->get_data();
if ($cdata['author']['xchan_addr']) {
if (! in_array($cdata['author']['xchan_addr'],$result['authors'])) {
$result['authors'][] = $cdata['author']['xchan_addr'];
}
}
}
}
// Add any mentions from the immediate parent, unless they are mentions of the current viewer or duplicates
if ($item['term']) {
foreach ($item['term'] as $t) {

View file

@ -17,18 +17,40 @@ class Activity extends Controller {
return;
}
$bear = ZlibActivity::bear_from_request();
$ob_authorise = false;
$item_uid = 0;
$bear = ZlibActivity::token_from_request();
if ($bear) {
logger('bear: ' . $bear, LOGGER_DEBUG);
$t = q("select item.uid, iconfig.v from iconfig left join item on iid = item.id where cat = 'ocaps' and item.uuid = '%s'",
dbesc($item_id)
);
if ($t) {
foreach ($t as $token) {
if ($token['v'] === $bear) {
$ob_authorize = true;
$item_uid = $token['uid'];
break;
}
}
}
}
$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 ";
// if passed an owner_id of 0, we force "guest access" or observer checking
$sql_extra = item_permissions_sql(0);
$r = q("select * from item where uuid = '%s' $item_normal $sql_extra limit 1",
// if passed an owner_id of 0 to item_permissions_sql(), we force "guest access" or observer checking
// Give ocap tokens priority
if ($ob_authorize) {
$sql_extra = " and item.uid = " . intval($token['uid']) . " ";
}
else {
$sql_extra = item_permissions_sql(0);
}
$r = q("select * from item where uuid = '%s' $item_normal $sql_extra and item_deleted = 0 limit 1",
dbesc($item_id)
);
if (! $r) {
@ -54,7 +76,6 @@ class Activity extends Controller {
]], ZlibActivity::encode_activity($items[0]));
$headers = [];
$headers['Content-Type'] = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' ;
$x['signature'] = LDSignatures::sign($x,$channel);

View file

@ -43,6 +43,13 @@ class Inbox extends Controller {
$hsig = HTTPSig::verify($data);
$AS = new ActivityStreams($data);
if ($AS->is_valid() && $AS->type === 'Announce' && is_array($AS->obj)
&& array_key_exists('object',$AS->obj) && array_key_exists('actor',$AS->obj)) {
// This is a relayed/forwarded Activity (as opposed to a shared/boosted object)
// Reparse the encapsulated Activity and use that instead
logger('relayed activity',LOGGER_DEBUG);
$AS = new ActivityStreams($AS->obj);
}
//logger('debug: ' . $AS->debug());
@ -59,6 +66,7 @@ class Inbox extends Controller {
}
return;
}
// $observer_hash in this case is the sender

View file

@ -138,6 +138,15 @@ class Linkinfo extends Controller {
$x = Activity::fetch($url);
if (is_array($x)) {
$y = new ActivityStreams($x);
if ($y->is_valid() && $y->type === 'Announce' && is_array($y->obj)
&& array_key_exists('object',$y->obj) && array_key_exists('actor',$y->obj)) {
// This is a relayed/forwarded Activity (as opposed to a shared/boosted object)
// Reparse the encapsulated Activity and use that instead
logger('relayed activity',LOGGER_DEBUG);
$y = new ActivityStreams($y->obj);
}
if ($y->is_valid()) {
$z = Activity::decode_note($y);
$r = q("select hubloc_hash, hubloc_network, hubloc_url from hubloc where hubloc_hash = '%s' OR hubloc_id_url = '%s'",

View file

@ -1,9 +1,12 @@
<?php
namespace Zotlabs\Module;
use App;
use Zotlabs\Web\Controller;
class Viewsrc extends \Zotlabs\Web\Controller {
class Viewsrc extends Controller {
function get() {
@ -15,45 +18,46 @@ class Viewsrc extends \Zotlabs\Web\Controller {
$json = ((argc() > 2 && argv(2) === 'json') ? true : false);
$dload = ((argc() > 2 && argv(2) === 'download') ? true : false);
if(! local_channel()) {
if (! local_channel()) {
notice( t('Permission denied.') . EOL);
}
if(! $item_id) {
\App::$error = 404;
if (! $item_id) {
App::$error = 404;
notice( t('Item not found.') . EOL);
}
$item_normal = item_normal_search();
if(local_channel() && $item_id) {
if (local_channel() && $item_id) {
$r = q("select id, item_flags, mimetype, item_obscured, body, llink, plink from item where uid in (%d , %d) and id = %d $item_normal limit 1",
intval(local_channel()),
intval($sys['channel_id']),
intval($item_id)
);
if($r) {
if(intval($r[0]['item_obscured']))
if ($r) {
if (intval($r[0]['item_obscured']))
$dload = true;
if($dload) {
if ($dload) {
header('Content-type: ' . $r[0]['mimetype']);
header('Content-disposition: attachment; filename="' . t('item') . '-' . $item_id . '"' );
echo $r[0]['body'];
killme();
}
$content = escape_tags($r[0]['body']);
$o = (($json) ? json_encode($content) : str_replace("\n",'<br />',$content));
}
}
if(is_ajax()) {
$inspect = ((is_site_admin()) ? '| <a href="' . z_root() . '/inspect/item/' . $r[0]['id'] . '" target="_blank">' . t('inspect') . '</a>' : EMPTY_STR);
if (is_ajax()) {
echo '<div class="p-1">';
echo '<div>id: ' . $r[0]['id'] . ' | <a href="' . $r[0]['plink'] . '" target="_blank">plink</a> | <a href="' . $r[0]['llink'] . '" target="_blank">llink</a></div>';
echo '<div>id: ' . $r[0]['id'] . ' | <a href="' . $r[0]['plink'] . '" target="_blank">plink</a> | <a href="' . $r[0]['llink'] . '" target="_blank">llink</a>' . $inspect . '</div>';
echo '<hr>';
echo '<pre class="p-1">' . $o . '</pre>';
echo '</div>';