diff options
Diffstat (limited to 'Zotlabs/Lib/ActivityStreams.php')
-rw-r--r-- | Zotlabs/Lib/ActivityStreams.php | 191 |
1 files changed, 168 insertions, 23 deletions
diff --git a/Zotlabs/Lib/ActivityStreams.php b/Zotlabs/Lib/ActivityStreams.php index 37e717f58..49978031e 100644 --- a/Zotlabs/Lib/ActivityStreams.php +++ b/Zotlabs/Lib/ActivityStreams.php @@ -7,22 +7,25 @@ namespace Zotlabs\Lib; * * Parses an ActivityStream JSON string. */ + class ActivityStreams { - public $raw = null; - public $data; - public $valid = false; - public $id = ''; - public $type = ''; - public $actor = null; - public $obj = null; - public $tgt = null; - public $origin = null; - public $owner = null; - public $signer = null; - public $ldsig = null; - public $sigok = false; - public $recips = null; + public $raw = null; + public $data = null; + public $valid = false; + public $deleted = false; + public $id = ''; + public $parent_id = ''; + public $type = ''; + public $actor = null; + public $obj = null; + public $tgt = null; + public $origin = null; + public $owner = null; + public $signer = null; + public $ldsig = null; + public $sigok = false; + public $recips = null; public $raw_recips = null; /** @@ -35,16 +38,49 @@ class ActivityStreams { function __construct($string) { $this->raw = $string; - $this->data = json_decode($string, true); + + if(is_array($string)) { + $this->data = $string; + } + else { + $this->data = json_decode($string, true); + } if($this->data) { + + // verify and unpack JSalmon signature if present + + 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']) { + $saved = json_encode($this->data,JSON_UNESCAPED_SLASHES); + $this->data = $tmp; + $this->data['signer'] = $ret['signer']; + $this->data['signed_data'] = $saved; + if($ret['hubloc']) { + $this->data['hubloc'] = $ret['hubloc']; + } + } + } + } + $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']) { + $this->deleted = $this->data['actor']; + $this->valid = false; + } + } + } if($this->is_valid()) { $this->id = $this->get_property_obj('id'); $this->type = $this->get_primary_type(); - $this->actor = $this->get_compound_property('actor'); + $this->actor = $this->get_actor('actor','',''); $this->obj = $this->get_compound_property('object'); $this->tgt = $this->get_compound_property('target'); $this->origin = $this->get_compound_property('origin'); @@ -53,14 +89,31 @@ class ActivityStreams { $this->ldsig = $this->get_compound_property('signature'); if($this->ldsig) { $this->signer = $this->get_compound_property('creator',$this->ldsig); - if($this->signer && $this->signer['publicKey'] && $this->signer['publicKey']['publicKeyPem']) { - $this->sigok = \Zotlabs\Lib\LDSignatures::verify($this->data,$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->type === 'Note') && (! $this->obj)) { + if(! $this->obj) { $this->obj = $this->data; $this->type = 'Create'; + 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); + + $this->parent_id = $this->get_property_obj('inReplyTo'); + + if((! $this->parent_id) && is_array($this->obj)) { + $this->parent_id = $this->obj['inReplyTo']; + } + if((! $this->parent_id) && is_array($this->obj)) { + $this->parent_id = $this->obj['id']; } } } @@ -190,44 +243,122 @@ class ActivityStreams { $base = (($base) ? $base : $this->data); $propname = (($prefix) ? $prefix . ':' : '') . $property; + if(! is_array($base)) { + btlogger('not an array: ' . print_r($base,true)); + return null; + } + return ((array_key_exists($propname, $base)) ? $base[$propname] : null); } + /** * @brief Fetches a property from an URL. * * @param string $url * @return NULL|mixed */ + function fetch_property($url) { + 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']) + [ '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 is_an_actor($s) { + return(in_array($s,[ 'Application','Group','Organization','Person','Service' ])); + } + + /** + * @brief + * + * @param string $property + * @param array $base + * @param string $namespace (optional) default empty + * @return NULL|mixed + */ + + function get_actor($property,$base='',$namespace = '') { + $x = $this->get_property_obj($property, $base, $namespace); + 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 + $r = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_id_url = '%s' limit 1", + dbesc($x) + ); + if($r) { + $y = Activity::encode_person($r[0]); + $y['cached'] = true; + return $y; + } + } + $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))) { + $actor = $this->fetch_property($actor['id']); + } + return $actor; + } return null; } + /** * @brief * * @param string $property * @param array $base * @param string $namespace (optional) default empty + * @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 = '') { + function get_compound_property($property, $base = '', $namespace = '', $first = false) { $x = $this->get_property_obj($property, $base, $namespace); if($this->is_url($x)) { $x = $this->fetch_property($x); } + // verify and unpack JSalmon signature if present + + 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']) { + $saved = json_encode($x,JSON_UNESCAPED_SLASHES); + $x = $tmp; + $x['signer'] = $ret['signer']; + $x['signed_data'] = $saved; + if($ret['hubloc']) { + $x['hubloc'] = $ret['hubloc']; + } + } + } + } + if($first && is_array($x) && array_key_exists(0,$x)) { + return $x[0]; + } + return $x; } @@ -273,4 +404,18 @@ class ActivityStreams { return $x; } + + static function is_as_request() { + + $x = getBestSupportedMimeType([ + 'application/ld+json;profile="https://www.w3.org/ns/activitystreams"', + 'application/activity+json', + 'application/ld+json;profile="http://www.w3.org/ns/activitystreams"' + ]); + + return(($x) ? true : false); + + } + + }
\ No newline at end of file |