diff options
Diffstat (limited to 'Zotlabs/Lib')
-rw-r--r-- | Zotlabs/Lib/Activity.php | 112 | ||||
-rw-r--r-- | Zotlabs/Lib/Apps.php | 375 | ||||
-rw-r--r-- | Zotlabs/Lib/DReport.php | 18 | ||||
-rw-r--r-- | Zotlabs/Lib/Libzot.php | 270 | ||||
-rw-r--r-- | Zotlabs/Lib/PConfig.php | 38 | ||||
-rw-r--r-- | Zotlabs/Lib/Share.php | 1 | ||||
-rw-r--r-- | Zotlabs/Lib/ThreadItem.php | 15 |
7 files changed, 542 insertions, 287 deletions
diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index ef6ee6c3e..8cfa18997 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -32,6 +32,12 @@ class Activity { if($x['type'] === ACTIVITY_OBJ_THING) { return self::fetch_thing($x); } + if($x['type'] === ACTIVITY_OBJ_EVENT) { + return self::fetch_event($x); + } + if($x['type'] === ACTIVITY_OBJ_PHOTO) { + return self::fetch_image($x); + } return $x; @@ -99,6 +105,63 @@ class Activity { } } + + static function fetch_image($x) { + + + $ret = [ + 'type' => 'Image', + 'id' => $x['id'], + 'name' => $x['title'], + 'content' => bbcode($x['body']), + 'source' => [ 'mediaType' => 'text/bbcode', 'content' => $x['body'] ], + 'published' => datetime_convert('UTC','UTC',$x['created'],ATOM_TIME), + 'updated' => datetime_convert('UTC','UTC', $x['edited'],ATOM_TIME), + 'url' => [ + 'type' => 'Link', + 'mediaType' => $x['link'][0]['type'], + 'href' => $x['link'][0]['href'], + 'width' => $x['link'][0]['width'], + 'height' => $x['link'][0]['height'] + ] + ]; + return $ret; + } + + static function fetch_event($x) { + + // convert old Zot event objects to ActivityStreams Event objects + + if (array_key_exists('content',$x) && array_key_exists('dtstart',$x)) { + $ev = bbtoevent($x['content']); + if($ev) { + + $actor = null; + if(array_key_exists('author',$x) && array_key_exists('link',$x['author'])) { + $actor = $x['author']['link'][0]['href']; + } + $y = [ + 'type' => 'Event', + 'id' => z_root() . '/event/' . $ev['event_hash'], + 'summary' => bbcode($ev['summary']), + // RFC3339 Section 4.3 + 'startTime' => (($ev['adjust']) ? datetime_convert('UTC','UTC',$ev['dtstart'], ATOM_TIME) : datetime_convert('UTC','UTC',$ev['dtstart'],'Y-m-d\\TH:i:s-00:00')), + 'content' => bbcode($ev['description']), + 'location' => [ 'type' => 'Place', 'content' => bbcode($ev['location']) ], + 'source' => [ 'content' => format_event_bbcode($ev), 'mediaType' => 'text/bbcode' ], + 'actor' => $actor, + ]; + if($actor) { + return $y; + } + } + } + + return $x; + + } + + static function encode_item_collection($items,$id,$type,$extra = null) { $ret = [ @@ -348,10 +411,17 @@ class Activity { $ret['type'] = 'Tombstone'; $ret['formerType'] = self::activity_obj_mapper($i['obj_type']); $ret['id'] = ((strpos($i['mid'],'http') === 0) ? $i['mid'] : z_root() . '/item/' . urlencode($i['mid'])); + $actor = self::encode_person($i['author'],false); + if($actor) + $ret['actor'] = $actor; + else + return []; return $ret; } $ret['type'] = self::activity_mapper($i['verb']); + + $ret['id'] = ((strpos($i['mid'],'http') === 0) ? $i['mid'] : z_root() . '/activity/' . urlencode($i['mid'])); if($i['title']) @@ -423,6 +493,10 @@ class Activity { if(! is_array($i['obj'])) { $i['obj'] = json_decode($i['obj'],true); } + if($i['obj']['type'] === ACTIVITY_OBJ_PHOTO) { + $i['obj']['id'] = $i['id']; + } + $obj = self::encode_object($i['obj']); if($obj) $ret['object'] = $obj; @@ -568,6 +642,9 @@ class Activity { 'http://activitystrea.ms/schema/1.0/tag' => 'Add', 'http://activitystrea.ms/schema/1.0/follow' => 'Follow', 'http://activitystrea.ms/schema/1.0/unfollow' => 'Unfollow', + 'http://purl.org/zot/activity/attendyes' => 'Accept', + 'http://purl.org/zot/activity/attendno' => 'Reject', + 'http://purl.org/zot/activity/attendmaybe' => 'TentativeAccept' ]; @@ -607,6 +684,9 @@ class Activity { 'http://activitystrea.ms/schema/1.0/tag' => 'Add', 'http://activitystrea.ms/schema/1.0/follow' => 'Follow', 'http://activitystrea.ms/schema/1.0/unfollow' => 'Unfollow', + 'http://purl.org/zot/activity/attendyes' => 'Accept', + 'http://purl.org/zot/activity/attendno' => 'Reject', + 'http://purl.org/zot/activity/attendmaybe' => 'TentativeAccept' ]; @@ -1412,7 +1492,33 @@ class Activity { $s['obj_type'] = ACTIVITY_OBJ_COMMENT; } - $s['obj'] = $act->obj; + + if($act->obj['type'] === 'Event') { + $s['obj'] = []; + $s['obj']['asld'] = $act->obj; + $s['obj']['type'] = ACTIVITY_OBJ_EVENT; + $s['obj']['id'] = $act->obj['id']; + $s['obj']['title'] = $act->obj['summary']; + + if(strpos($act->obj['startTime'],'Z')) + $s['obj']['adjust'] = true; + else + $s['obj']['adjust'] = false; + + $s['obj']['dtstart'] = datetime_convert('UTC','UTC',$act->obj['startTime']); + if($act->obj['endTime']) + $s['obj']['dtend'] = datetime_convert('UTC','UTC',$act->obj['endTime']); + else + $s['obj']['nofinish'] = true; + $s['obj']['description'] = $act->obj['content']; + + if(array_path_exists('location/content',$act->obj)) + $s['obj']['location'] = $act->obj['location']['content']; + + } + else { + $s['obj'] = $act->obj; + } $instrument = $act->get_property_obj('instrument'); if((! $instrument) && (! $response_activity)) { @@ -1540,7 +1646,9 @@ class Activity { } - if($act->obj['type'] === 'Image') { + // avoid double images from hubzilla to zap/osada + + if($act->obj['type'] === 'Image' && strpos($s['body'],'zrl=') === false) { $ptr = null; diff --git a/Zotlabs/Lib/Apps.php b/Zotlabs/Lib/Apps.php index 8cf62c01a..9edd00667 100644 --- a/Zotlabs/Lib/Apps.php +++ b/Zotlabs/Lib/Apps.php @@ -1,32 +1,34 @@ -<?php /** @file */ +<?php namespace Zotlabs\Lib; -/** - * Apps - * - */ - require_once('include/plugin.php'); require_once('include/channel.php'); - +/** + * @brief Apps class. + * + */ class Apps { static public $available_apps = null; static public $installed_apps = null; - static public $base_apps = null; - - + /** + * @brief + * + * @param boolean $translate (optional) default true + * @return array + */ static public function get_system_apps($translate = true) { + $ret = []; - $ret = array(); if(is_dir('apps')) $files = glob('apps/*.apd'); else $files = glob('app/*.apd'); + if($files) { foreach($files as $f) { $x = self::parse_app_description($f,$translate); @@ -50,14 +52,17 @@ class Apps { } } - call_hooks('get_system_apps',$ret); + /** + * @hooks get_system_apps + * Hook to manipulate the system apps array. + */ + call_hooks('get_system_apps', $ret); return $ret; - } static public function get_base_apps() { - return get_config('system','base_apps',[ + $x = get_config('system','base_apps',[ 'Connections', 'Network', 'Settings', @@ -72,6 +77,14 @@ class Apps { 'Mail', 'Profile Photo' ]); + + /** + * @hooks get_base_apps + * Hook to manipulate the base apps array. + */ + call_hooks('get_base_apps', $x); + + return $x; } static public function import_system_apps() { @@ -79,7 +92,7 @@ class Apps { return; self::$base_apps = self::get_base_apps(); - + $apps = self::get_system_apps(false); self::$available_apps = q("select * from app where app_channel = 0"); @@ -104,6 +117,7 @@ class Apps { // $id will be boolean true or false to install an app, or an integer id to update an existing app if($id === false) continue; + if($id !== true) { // if we already installed this app, but it changed, preserve any categories we created $s = EMPTY_STR; @@ -124,16 +138,17 @@ class Apps { $app['guid'] = hash('whirlpool',$app['name']); $app['system'] = 1; self::app_install(local_channel(),$app); - } - } + } } /** - * Install the system app if no system apps have been installed, or if a new system app + * Install the system app if no system apps have been installed, or if a new system app * is discovered, or if the version of a system app changes. + * + * @param array $app + * @return boolean|int */ - static public function check_install_system_app($app) { if((! is_array(self::$available_apps)) || (! count(self::$available_apps))) { return true; @@ -157,17 +172,16 @@ class Apps { return $notfound; } - /** - * Install the system app if no system apps have been installed, or if a new system app - * is discovered, or if the version of a system app changes. + * Install the personal app if no personal apps have been installed, or if a new personal app + * is discovered, or if the version of a personal app changes. + * + * @param array $app + * @return boolean|int */ - - - static public function check_install_personal_app($app) { $installed = false; - foreach(self::$installed_apps as $iapp) { + foreach(self::$installed_apps as $iapp) { if($iapp['app_id'] == hash('whirlpool',$app['name'])) { $installed = true; if(($iapp['app_version'] != $app['version']) @@ -187,19 +201,24 @@ class Apps { return strcasecmp($a['name'],$b['name']); } - - static public function parse_app_description($f,$translate = true) { - - $ret = array(); + /** + * @brief Parse app description. + * + * @param string $f filename + * @param boolean $translate (optional) default true + * @return boolean|array + */ + static public function parse_app_description($f, $translate = true) { + $ret = []; + $matches = []; $baseurl = z_root(); - $channel = \App::get_channel(); - $address = (($channel) ? $channel['channel_address'] : ''); - + //$channel = \App::get_channel(); + //$address = (($channel) ? $channel['channel_address'] : ''); + //future expansion $observer = \App::get_observer(); - $lines = @file($f); if($lines) { @@ -208,7 +227,7 @@ class Apps { $ret[$matches[1]] = trim($matches[2]); } } - } + } if(! $ret['photo']) $ret['photo'] = $baseurl . '/' . get_default_profile_photo(80); @@ -290,10 +309,12 @@ class Apps { if($ret) { if($translate) self::translate_system_apps($ret); + return $ret; } + return false; - } + } static public function translate_system_apps(&$arr) { @@ -309,17 +330,17 @@ class Apps { 'Remote Diagnostics' => t('Remote Diagnostics'), 'Suggest Channels' => t('Suggest Channels'), 'Login' => t('Login'), - 'Channel Manager' => t('Channel Manager'), + 'Channel Manager' => t('Channel Manager'), 'Network' => t('Stream'), 'Settings' => t('Settings'), 'Files' => t('Files'), 'Webpages' => t('Webpages'), 'Wiki' => t('Wiki'), - 'Channel Home' => t('Channel Home'), + 'Channel Home' => t('Channel Home'), 'View Profile' => t('View Profile'), - 'Photos' => t('Photos'), - 'Events' => t('Events'), - 'Directory' => t('Directory'), + 'Photos' => t('Photos'), + 'Events' => t('Events'), + 'Directory' => t('Directory'), 'Help' => t('Help'), 'Mail' => t('Mail'), 'Mood' => t('Mood'), @@ -364,30 +385,31 @@ class Apps { if(array_key_exists($arr[$x]['name'],$apps)) { $arr[$x]['name'] = $apps[$arr[$x]['name']]; } else { - // Try to guess by app name if not in list - $arr[$x]['name'] = t(trim($arr[$x]['name'])); + // Try to guess by app name if not in list + $arr[$x]['name'] = t(trim($arr[$x]['name'])); } } } - } - - // papp is a portable app - - static public function app_render($papp,$mode = 'view') { - - /** - * modes: - * view: normal mode for viewing an app via bbcode from a conversation or page - * provides install/update button if you're logged in locally - * install: like view but does not display app-bin options if they are present - * list: normal mode for viewing an app on the app page - * no buttons are shown - * edit: viewing the app page in editing mode provides a delete button - * nav: render apps for app-bin - */ - + /** + * @brief + * + * @param array $papp + * papp is a portable app + * @param string $mode (optional) default 'view' + * Render modes: + * * \b view: normal mode for viewing an app via bbcode from a conversation or page + * provides install/update button if you're logged in locally + * * \b install: like view but does not display app-bin options if they are present + * * \b list: normal mode for viewing an app on the app page + * no buttons are shown + * * \b edit: viewing the app page in editing mode provides a delete button + * * \b nav: render apps for app-bin + * + * @return void|string Parsed HTML + */ + static public function app_render($papp, $mode = 'view') { $installed = false; if(! $papp) @@ -412,7 +434,7 @@ class Apps { $sys = get_sys_channel(); $view_channel = $sys['channel_id']; } - self::app_macros($view_channel,$papp); + self::app_macros($view_channel,$papp); } if(strpos($papp['url'], ',')) { @@ -425,7 +447,6 @@ class Apps { $papp['url'] = z_root() . ((strpos($papp['url'],'/') === 0) ? '' : '/') . $papp['url']; - foreach($papp as $k => $v) { if(strpos($v,'http') === 0 && $k != 'papp') { if(! (local_channel() && strpos($v,z_root()) === 0)) { @@ -507,7 +528,7 @@ class Apps { if($x) { $hosturl = $x['scheme'] . '://' . $x['host'] . '/'; } - } + } } $install_action = (($installed) ? t('Update') : t('Install')); @@ -590,8 +611,14 @@ class Apps { return false; } - - static public function can_delete($uid,$app) { + /** + * @brief + * + * @param mixed $uid If not set return false, otherwise no influence + * @param array $app + * @return boolean + */ + static public function can_delete($uid, $app) { if(! $uid) { return false; } @@ -599,7 +626,7 @@ class Apps { $base_apps = self::get_base_apps(); if($base_apps) { foreach($base_apps as $b) { - if($app['guid'] === hash('whirlpool',$b)) { + if($app['guid'] === hash('whirlpool', $b)) { return false; } } @@ -611,7 +638,6 @@ class Apps { static public function app_destroy($uid,$app) { if($uid && $app['guid']) { - $x = q("select * from app where app_id = '%s' and app_channel = %d limit 1", dbesc($app['guid']), intval($uid) @@ -620,7 +646,7 @@ class Apps { if(! intval($x[0]['app_deleted'])) { $x[0]['app_deleted'] = 1; if(self::can_delete($uid,$app)) { - $r = q("delete from app where app_id = '%s' and app_channel = %d", + q("delete from app where app_id = '%s' and app_channel = %d", dbesc($app['guid']), intval($uid) ); @@ -628,10 +654,15 @@ class Apps { intval(TERM_OBJ_APP), intval($x[0]['id']) ); + /** + * @hooks app_destroy + * Called after app entry got removed from database + * and provide app array from database. + */ call_hooks('app_destroy', $x[0]); } else { - $r = q("update app set app_deleted = 1 where app_id = '%s' and app_channel = %d", + q("update app set app_deleted = 1 where app_id = '%s' and app_channel = %d", dbesc($app['guid']), intval($uid) ); @@ -645,22 +676,23 @@ class Apps { } } } - } - static public function app_undestroy($uid,$app) { - - // undelete a system app - + /** + * @brief Undelete a system app. + * + * @param int $uid + * @param array $app + */ + static public function app_undestroy($uid, $app) { if($uid && $app['guid']) { - $x = q("select * from app where app_id = '%s' and app_channel = %d limit 1", dbesc($app['guid']), intval($uid) ); if($x) { if($x[0]['app_system']) { - $r = q("update app set app_deleted = 0 where app_id = '%s' and app_channel = %d", + q("update app set app_deleted = 0 where app_id = '%s' and app_channel = %d", dbesc($app['guid']), intval($uid) ); @@ -669,7 +701,15 @@ class Apps { } } - static public function app_feature($uid,$app,$term) { + /** + * @brief + * + * @param int $uid + * @param array $app + * @param string $term + * @return void + */ + static public function app_feature($uid, $app, $term) { $r = q("select id from app where app_id = '%s' and app_channel = %d limit 1", dbesc($app['guid']), intval($uid) @@ -693,23 +733,37 @@ class Apps { } } - static public function app_installed($uid,$app,$bypass_filter=false) { + /** + * @brief + * + * @param int $uid + * @param array $app + * @param boolean $bypass_filter (optional) default false + * @return boolean + */ + static public function app_installed($uid, $app, $bypass_filter = false) { $r = q("select id from app where app_id = '%s' and app_channel = %d limit 1", - dbesc((array_key_exists('guid',$app)) ? $app['guid'] : ''), + dbesc((array_key_exists('guid', $app)) ? $app['guid'] : ''), intval($uid) ); - if (!$bypass_filter) { + if(!$bypass_filter) { $filter_arr = [ - 'uid'=>$uid, - 'app'=>$app, - 'installed'=>$r + 'uid' => $uid, + 'app' => $app, + 'installed' => $r ]; - call_hooks('app_installed_filter',$filter_arr); + /** + * @hooks app_installed_filter + * * \e int \b uid + * * \e array \b app + * * \e mixed \b installed - return value + */ + call_hooks('app_installed_filter', $filter_arr); $r = $filter_arr['installed']; } - return(($r) ? true : false); + return(($r) ? true : false); } @@ -725,11 +779,17 @@ class Apps { 'app'=>$app, 'installed'=>$r ]; - call_hooks('addon_app_installed_filter',$filter_arr); + /** + * @hooks addon_app_installed_filter + * * \e int \b uid + * * \e array \b app + * * \e mixed \b installed - return value + */ + call_hooks('addon_app_installed_filter', $filter_arr); $r = $filter_arr['installed']; } - return(($r) ? true : false); + return(($r) ? true : false); } static public function system_app_installed($uid,$app,$bypass_filter=false) { @@ -744,28 +804,39 @@ class Apps { 'app'=>$app, 'installed'=>$r ]; - call_hooks('system_app_installed_filter',$filter_arr); + /** + * @hooks system_app_installed_filter + * * \e int \b uid + * * \e array \b app + * * \e mixed \b installed - return value + */ + call_hooks('system_app_installed_filter', $filter_arr); $r = $filter_arr['installed']; } - return(($r) ? true : false); + return(($r) ? true : false); } - - + /** + * @brief + * + * @param int $uid + * @param boolean $deleted + * @param array $cats + * @return boolean|array + */ static public function app_list($uid, $deleted = false, $cats = []) { - if($deleted) - $sql_extra = ""; + if($deleted) + $sql_extra = ''; else - $sql_extra = " and app_deleted = 0 "; + $sql_extra = ' and app_deleted = 0 '; if($cats) { - - $cat_sql_extra = " and ( "; + $cat_sql_extra = ' and ( '; foreach($cats as $cat) { if(strpos($cat_sql_extra, 'term')) - $cat_sql_extra .= "or "; + $cat_sql_extra .= 'or '; $cat_sql_extra .= "term = '" . dbesc($cat) . "' "; } @@ -777,11 +848,13 @@ class Apps { ); if(! $r) return $r; - $sql_extra .= " and app.id in ( "; + + $sql_extra .= ' and app.id in ( '; $s = ''; foreach($r as $rr) { if($s) $s .= ','; + $s .= intval($rr['oid']); } $sql_extra .= $s . ') '; @@ -792,12 +865,26 @@ class Apps { ); if($r) { - $hookinfo = Array('uid'=>$uid,'deleted'=>$deleted,'cats'=>$cats,'apps'=>$r); - call_hooks('app_list',$hookinfo); + $hookinfo = [ + 'uid' => $uid, + 'deleted' => $deleted, + 'cats' => $cats, + 'apps' => $r, + ]; + /** + * @hooks app_list + * * \e int \b uid + * * \e boolean \b deleted + * * \e array \b cats + * * \e array \b apps - return value + */ + call_hooks('app_list', $hookinfo); $r = $hookinfo['apps']; - for($x = 0; $x < count($r); $x ++) { + + for($x = 0; $x < count($r); $x++) { if(! $r[$x]['app_system']) $r[$x]['type'] = 'personal'; + $r[$x]['term'] = q("select * from term where otype = %d and oid = %d", intval(TERM_OBJ_APP), intval($r[$x]['id']) @@ -805,7 +892,7 @@ class Apps { } } - return($r); + return $r; } static public function app_order($uid,$apps,$menu) { @@ -837,13 +924,14 @@ class Apps { $ret[] = $ap; } } - return $ret; + return $ret; } static function find_app_in_array($name,$arr) { if(! $arr) return false; + foreach($arr as $x) { if($x['name'] === $name) { return $x; @@ -852,8 +940,16 @@ class Apps { return false; } - static function moveup($uid,$guid,$menu) { - $syslist = array(); + /** + * @brief + * + * @param int $uid + * @param int $guid + * @param string $menu + * @return void + */ + static function moveup($uid, $guid, $menu) { + $syslist = []; $conf = (($menu === 'nav_featured_app') ? 'app_order' : 'app_pin_order'); @@ -863,6 +959,7 @@ class Apps { $papp = self::app_encode($li); if($menu !== 'nav_pinned_app' && strpos($papp['categories'],'nav_pinned_app') !== false) continue; + $syslist[] = $papp; } } @@ -875,8 +972,6 @@ class Apps { if(! $syslist) return; - $newlist = []; - foreach($syslist as $k => $li) { if($li['guid'] === $guid) { $position = $k; @@ -885,6 +980,7 @@ class Apps { } if(! $position) return; + $dest_position = $position - 1; $saved = $syslist[$dest_position]; $syslist[$dest_position] = $syslist[$position]; @@ -896,11 +992,18 @@ class Apps { } set_pconfig($uid,'system',$conf,implode(',',$narr)); - } - static function movedown($uid,$guid,$menu) { - $syslist = array(); + /** + * @brief + * + * @param int $uid + * @param int $guid + * @param string $menu + * @return void + */ + static function movedown($uid, $guid, $menu) { + $syslist = []; $conf = (($menu === 'nav_featured_app') ? 'app_order' : 'app_pin_order'); @@ -910,6 +1013,7 @@ class Apps { $papp = self::app_encode($li); if($menu !== 'nav_pinned_app' && strpos($papp['categories'],'nav_pinned_app') !== false) continue; + $syslist[] = $papp; } } @@ -922,8 +1026,6 @@ class Apps { if(! $syslist) return; - $newlist = []; - foreach($syslist as $k => $li) { if($li['guid'] === $guid) { $position = $k; @@ -932,6 +1034,7 @@ class Apps { } if($position >= count($syslist) - 1) return; + $dest_position = $position + 1; $saved = $syslist[$dest_position]; $syslist[$dest_position] = $syslist[$position]; @@ -943,7 +1046,6 @@ class Apps { } set_pconfig($uid,'system',$conf,implode(',',$narr)); - } static public function app_decode($s) { @@ -951,8 +1053,14 @@ class Apps { return json_decode($x,true); } - - static public function app_macros($uid,&$arr) { + /** + * @brief + * + * @param int $uid + * @param[in,out] array $arr + * @return void + */ + static public function app_macros($uid, &$arr) { if(! intval($uid)) return; @@ -960,21 +1068,17 @@ class Apps { $baseurl = z_root(); $channel = channelx_by_n($uid); $address = (($channel) ? $channel['channel_address'] : ''); - + //future expansion - $observer = \App::get_observer(); - + //$observer = \App::get_observer(); + $arr['url'] = str_replace(array('$baseurl','$nick'),array($baseurl,$address),$arr['url']); $arr['photo'] = str_replace(array('$baseurl','$nick'),array($baseurl,$address),$arr['photo']); - } - - - static public function app_store($arr) { //logger('app_store: ' . print_r($arr,true)); @@ -1158,16 +1262,20 @@ class Apps { } return $ret; - } - - static public function app_encode($app,$embed = false) { - - $ret = array(); + /** + * @brief + * + * @param array $app + * @param boolean $embed (optional) default false + * @return array|string + */ + static public function app_encode($app, $embed = false) { + $ret = []; $ret['type'] = 'personal'; - + if($app['app_id']) $ret['guid'] = $app['app_id']; @@ -1200,7 +1308,7 @@ class Apps { if($app['app_price']) $ret['price'] = $app['app_price']; - + if($app['app_page']) $ret['page'] = $app['app_page']; @@ -1224,12 +1332,12 @@ class Apps { foreach($app['term'] as $t) { if($s) $s .= ','; + $s .= $t['term']; } $ret['categories'] = $s; } - if(! $embed) return $ret; @@ -1237,18 +1345,15 @@ class Apps { if(array_key_exists('categories',$ret)) unset($ret['categories']); - + $j = json_encode($ret); - return '[app]' . chunk_split(base64_encode($j),72,"\n") . '[/app]'; + return '[app]' . chunk_split(base64_encode($j),72,"\n") . '[/app]'; } static public function papp_encode($papp) { return chunk_split(base64_encode(json_encode($papp)),72,"\n"); - } } - - diff --git a/Zotlabs/Lib/DReport.php b/Zotlabs/Lib/DReport.php index 21b320cac..18087e29f 100644 --- a/Zotlabs/Lib/DReport.php +++ b/Zotlabs/Lib/DReport.php @@ -87,17 +87,29 @@ class DReport { // Is the sender one of our channels? - $c = q("select channel_id from channel where channel_hash = '%s' limit 1", + $c = q("select channel_id from channel where channel_hash = '%s' or channel_portable_id = '%s' limit 1", + dbesc($dr['sender']), dbesc($dr['sender']) ); + if(! $c) return false; + // legacy zot recipients add a space and their name to the xchan. remove it if true. + + $legacy_recipient = strpos($dr['recipient'], ' '); + if($legacy_recipient !== false) { + $legacy_recipient_parts = explode(' ', $dr['recipient'], 2); + $rxchan = $legacy_recipient_parts[0]; + } + else { + $rxchan = $dr['recipient']; + } - // is the recipient one of our connections, or do we want to store every report? - $rxchan = $dr['recipient']; + // is the recipient one of our connections, or do we want to store every report? + $pcf = get_pconfig($c[0]['channel_id'],'system','dreport_store_all'); if($pcf) return true; diff --git a/Zotlabs/Lib/Libzot.php b/Zotlabs/Lib/Libzot.php index 87a5126f4..27502009c 100644 --- a/Zotlabs/Lib/Libzot.php +++ b/Zotlabs/Lib/Libzot.php @@ -2,11 +2,6 @@ namespace Zotlabs\Lib; -/** - * @brief lowlevel implementation of Zot6 protocol. - * - */ - use Zotlabs\Zot6\HTTPSig; use Zotlabs\Access\Permissions; use Zotlabs\Access\PermissionLimits; @@ -14,14 +9,17 @@ use Zotlabs\Daemon\Master; require_once('include/crypto.php'); - +/** + * @brief Lowlevel implementation of Zot6 protocol. + * + */ class Libzot { /** * @brief Generates a unique string for use as a zot guid. * - * Generates a unique string for use as a zot guid using our DNS-based url, the - * channel nickname and some entropy. + * Generates a unique string for use as a zot guid using our DNS-based url, + * the channel nickname and some entropy. * The entropy ensures uniqueness against re-installs where the same URL and * nickname are chosen. * @@ -32,9 +30,8 @@ class Libzot { * immediate universe. * * @param string $channel_nick a unique nickname of controlling entity - * @returns string + * @return string */ - static function new_uid($channel_nick) { $rawstr = z_root() . '/' . $channel_nick . '.' . mt_rand(); return(base64url_encode(hash('whirlpool', $rawstr, true), true)); @@ -52,8 +49,8 @@ class Libzot { * * @param string $guid * @param string $pubkey + * @return string */ - static function make_xchan_hash($guid, $pubkey) { return base64url_encode(hash('whirlpool', $guid . $pubkey, true)); } @@ -65,10 +62,8 @@ class Libzot { * should only be used by channels which are defined on this hub. * * @param string $hash - xchan_hash - * @returns array of hubloc (hub location structures) - * + * @return array of hubloc (hub location structures) */ - static function get_hublocs($hash) { /* Only search for active hublocs - e.g. those that haven't been marked deleted */ @@ -92,16 +87,17 @@ class Libzot { * packet type: one of 'ping', 'pickup', 'purge', 'refresh', 'keychange', 'force_refresh', 'notify', 'auth_check' * @param array $recipients * envelope recipients, array of portable_id's; empty for public posts - * @param string msg + * @param string $msg * optional message + * @param string $encoding + * optional encoding, default 'activitystreams' * @param string $remote_key * optional public site key of target hub used to encrypt entire packet * NOTE: remote_key and encrypted packets are required for 'auth_check' packets, optional for all others * @param string $methods - * optional comma separated list of encryption methods @ref self::best_algorithm() + * optional comma separated list of encryption methods @ref best_algorithm() * @returns string json encoded zot packet */ - static function build_packet($channel, $type = 'activity', $recipients = null, $msg = '', $encoding = 'activitystreams', $remote_key = null, $methods = '') { $sig_method = get_config('system','signature_algorithm','sha256'); @@ -146,11 +142,10 @@ class Libzot { * @brief Choose best encryption function from those available on both sites. * * @param string $methods - * comma separated list of encryption methods + * Comma separated list of encryption methods * @return string first match from our site method preferences crypto_methods() array - * of a method which is common to both sites; or 'aes256cbc' if no matches are found. + * of a method which is common to both sites; or 'aes256cbc' if no matches are found. */ - static function best_algorithm($methods) { $x = [ @@ -164,7 +159,6 @@ class Libzot { * * \e string \b methods - comma separated list of encryption methods * * \e string \b result - the algorithm to return */ - call_hooks('zot_best_algorithm', $x); if($x['result']) @@ -190,7 +184,7 @@ class Libzot { /** - * @brief send a zot message + * @brief Send a zot message. * * @see z_post_url() * @@ -200,18 +194,17 @@ class Libzot { * @param array $crypto (required if encrypted httpsig, requires hubloc_sitekey and site_crypto elements) * @return array see z_post_url() for returned data format */ - static function zot($url, $data, $channel = null,$crypto = null) { if($channel) { - $headers = [ - 'X-Zot-Token' => random_string(), - 'Digest' => HTTPSig::generate_digest_header($data), + $headers = [ + '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', + $h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel),false,'sha512', (($crypto) ? [ 'key' => $crypto['hubloc_sitekey'], 'algorithm' => self::best_algorithm($crypto['site_crypto']) ] : false)); } else { @@ -227,7 +220,6 @@ class Libzot { /** * @brief Refreshes after permission changed or friending, etc. * - * * refresh is typically invoked when somebody has changed permissions of a channel and they are notified * to fetch new permissions via a finger/discovery operation. This may result in a new connection * (abook entry) being added to a local channel and it may result in auto-permissions being granted. @@ -251,7 +243,6 @@ class Libzot { * * \b true if successful * * otherwise \b false */ - static function refresh($them, $channel = null, $force = false) { logger('them: ' . print_r($them,true), LOGGER_DATA, LOG_DEBUG); @@ -265,13 +256,13 @@ class Libzot { } else { $r = null; - + // if they re-installed the server we could end up with the wrong record - pointing to the old install. // We'll order by reverse id to try and pick off the newest one first and hopefully end up with the // correct hubloc. If this doesn't work we may have to re-write this section to try them all. if(array_key_exists('xchan_addr',$them) && $them['xchan_addr']) { - $r = q("select hubloc_id_url, hubloc_primary from hubloc where hubloc_addr = '%s' order by hubloc_id desc", + $r = q("select hubloc_id_url, hubloc_primary from hubloc where hubloc_addr = '%s' and hubloc_network = 'zot6' order by hubloc_id desc", dbesc($them['xchan_addr']) ); } @@ -317,7 +308,7 @@ class Libzot { if(! $hsig_valid) { logger('http signature not valid: ' . print_r($hsig,true)); - return $result; + return false; } @@ -356,7 +347,7 @@ class Libzot { ); if($r) { -logger('4'); + // connection exists // if the dob is the same as what we have stored (disregarding the year), keep the one @@ -416,7 +407,7 @@ logger('4'); if($y) { logger("New introduction received for {$channel['channel_name']}"); $new_perms = get_all_perms($channel['channel_id'],$x['hash'],false); - + // Send a clone sync packet and a permissions update if permissions have changed $new_connection = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_xchan = '%s' and abook_channel = %d and abook_self = 0 order by abook_created desc limit 1", @@ -524,10 +515,14 @@ logger('4'); return false; } - - - - static function valid_hub($sender,$site_id) { + /** + * @brief + * + * @param string $sender + * @param string $site_id + * @return null|array + */ + static function valid_hub($sender, $site_id) { $r = q("select hubloc.*, site.site_crypto from hubloc left join site on hubloc_url = site_url where hubloc_hash = '%s' and hubloc_site_id = '%s' limit 1", dbesc($sender), @@ -548,7 +543,6 @@ logger('4'); } return $r[0]; - } /** @@ -559,21 +553,14 @@ logger('4'); * origination address. This will fetch the discovery packet of the sender, * which contains the public key we need to verify our guid and url signatures. * - * @param array $arr an associative array which must contain: - * * \e string \b guid => guid of conversant - * * \e string \b guid_sig => guid signed with conversant's private key - * * \e string \b url => URL of the origination hub of this communication - * * \e string \b url_sig => URL signed with conversant's private key + * @param string $id * * @return array An associative array with - * * \b success boolean true or false - * * \b message (optional) error string only if success is false + * * \e boolean \b success + * * \e string \b message (optional, unused) error string only if success is false */ - static function register_hub($id) { - $id_hash = false; - $valid = false; $hsig_valid = false; $result = [ 'success' => false ]; @@ -807,7 +794,7 @@ logger('4'); // If setting for the default profile, unset the profile photo flag from any other photos I own if($is_default_profile) { - q("UPDATE photo SET photo_usage = %d WHERE photo_usage = %d AND resource_id != '%s' AND aid = %d AND uid = %d", + q("UPDATE photo SET photo_usage = %d WHERE photo_usage = %d AND resource_id != '%s' AND aid = %d AND uid = %d", intval(PHOTO_NORMAL), intval(PHOTO_PROFILE), dbesc($hash), @@ -954,8 +941,8 @@ logger('4'); * @param string $hub - url of site we just contacted * @param array $arr - output of z_post_url() * @param array $outq - The queue structure attached to this request + * @return void */ - static function process_response($hub, $arr, $outq) { logger('remote: ' . print_r($arr,true),LOGGER_DATA); @@ -986,7 +973,7 @@ logger('4'); if(! $x['success']) { // 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()), @@ -994,10 +981,20 @@ logger('4'); ); } - if(is_array($x) && array_key_exists('delivery_report',$x) && is_array($x['delivery_report'])) { + 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)) { + + // legacy recipients add a space and their name to the xchan. split those if true. + $legacy_recipient = strpos($xx['recipient'], ' '); + if($legacy_recipient !== false) { + $legacy_recipient_parts = explode(' ', $xx['recipient'], 2); + $xx['recipient'] = $legacy_recipient_parts[0]; + $xx['name'] = $legacy_recipient_parts[1]; + } + 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']), @@ -1073,11 +1070,6 @@ logger('4'); * * @param array $arr * 'pickup' structure returned from remote site - * @param string $sender_url - * the url specified by the sender in the initial communication. - * We will verify the sender and url in each returned message structure and - * also verify that all the messages returned match the site url that we are - * currently processing. * * @returns array * Suitable for logging remotely, enumerating the processing results of each message/recipient combination @@ -1085,7 +1077,6 @@ logger('4'); * * [1] => \e string $delivery_status * * [2] => \e string $address */ - static function import($arr) { $env = $arr; @@ -1107,7 +1098,7 @@ logger('4'); $has_data = array_key_exists('data',$env) && $env['data']; $data = (($has_data) ? $env['data'] : false); - $AS = null; + $AS = null; if($env['encoding'] === 'activitystreams') { @@ -1165,7 +1156,6 @@ logger('4'); $deliveries = self::public_recips($env,$AS); - } $deliveries = array_unique($deliveries); @@ -1184,31 +1174,31 @@ logger('4'); //logger($AS->debug()); - $r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1", + $r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1", dbesc($AS->actor['id']) - ); + ); if($r) { $arr['author_xchan'] = $r[0]['hubloc_hash']; } - $s = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1", + $s = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' 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']; + $arr['owner_xchan'] = $env['sender']; } if($private) { $arr['item_private'] = true; } - // @fixme - spoofable + /// @FIXME - spoofable if($AS->data['hubloc']) { $arr['item_verified'] = true; } @@ -1237,12 +1227,19 @@ logger('4'); } if ($result) { $return = array_merge($return, $result); - } + } return $return; } - static function is_top_level($env,$act) { + /** + * @brief + * + * @param array $env + * @param object $act + * @return boolean + */ + static function is_top_level($env, $act) { if($env['encoding'] === 'zot' && array_key_exists('flags',$env) && in_array('thread_parent', $env['flags'])) { return true; } @@ -1285,9 +1282,9 @@ logger('4'); * Some of these will be rejected, but this gives us a place to start. * * @param array $msg - * @return NULL|array + * @param object $act + * @return array */ - static function public_recips($msg, $act) { require_once('include/channel.php'); @@ -1432,7 +1429,7 @@ logger('4'); * will normally arrive first via sync delivery, but this isn't guaranteed. * There's a chance the current delivery could take place before the cloned copy arrives * hence the item could have the wrong ACL and *could* be used in subsequent deliveries or - * access checks. + * access checks. */ if($sender === $channel['channel_portable_id'] && $arr['author_xchan'] === $channel['channel_portable_id'] && $arr['mid'] === $arr['parent_mid']) { @@ -1487,14 +1484,14 @@ logger('4'); intval($channel['channel_id']) ); if ($parent) { - $allowed = can_comment_on_post($d,$parent[0]); + $allowed = can_comment_on_post($sender,$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'); @@ -1503,16 +1500,18 @@ logger('4'); } } -logger('item: ' . print_r($arr,true), LOGGER_DATA); + logger('item: ' . print_r($arr,true), LOGGER_DATA); if($arr['mid'] !== $arr['parent_mid']) { -logger('checking source: "' . $arr['mid'] . '" != "' . $arr['parent_mid'] . '"'); + + logger('checking source: "' . $arr['mid'] . '" != "' . $arr['parent_mid'] . '"'); + // check source route. // We are only going to accept comments from this sender if the comment has the same route as the top-level-post, // this is so that permissions mismatches between senders apply to the entire conversation // 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, owner_xchan, item_private from item where mid = '%s' and uid = %d limit 1", dbesc($arr['parent_mid']), intval($channel['channel_id']) @@ -1535,20 +1534,25 @@ logger('checking source: "' . $arr['mid'] . '" != "' . $arr['parent_mid'] . '"') // the top level post is unlikely to be imported and // this is just an exercise in futility. + + if((! get_pconfig($channel['channel_id'],'system','hyperdrive',false)) || (! $arr['verb'] === 'Announce')) { + continue; + } + if((! $relay) && (! $request) && (! $local_public) && perm_is_allowed($channel['channel_id'],$sender,'send_stream')) { self::fetch_conversation($channel,$arr['parent_mid']); } continue; } - + 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, + // 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']; @@ -1591,7 +1595,7 @@ logger('checking source: "' . $arr['mid'] . '" != "' . $arr['parent_mid'] . '"') $arr['route'] = $last_prior_route; } } -logger('hey'); + $ab = q("select * from abook where abook_channel = %d and abook_xchan = '%s'", intval($channel['channel_id']), dbesc($arr['owner_xchan']) @@ -1602,13 +1606,13 @@ logger('hey'); // remove_community_tag is a no-op if this isn't a community tag activity self::remove_community_tag($sender,$arr,$channel['channel_id']); - + // set these just in case we need to store a fresh copy of the deleted post. // This could happen if the delete got here before the original post did. $arr['aid'] = $channel['channel_account_id']; $arr['uid'] = $channel['channel_id']; - + $item_id = self::delete_imported_item($sender,$arr,$channel['channel_id'],$relay); $DR->update(($item_id) ? 'deleted' : 'delete_failed'); $result[] = $DR->get(); @@ -1704,7 +1708,7 @@ logger('hey'); * * \e array \b item * * \e array \b sender * * \e array \b channel - */ + */ call_hooks('activity_received', $parr); // don't add a source route if it's a relay or later recipients will get a route mismatch if(! $relay) @@ -1769,17 +1773,17 @@ logger('hey'); logger($AS->debug()); - $r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1", + $r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' 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", + $r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1", dbesc($AS->actor['id']) ); - } + } if(! $r) { logger('FOF Activity: no actor'); continue; @@ -1799,9 +1803,9 @@ logger('hey'); $arr['author_xchan'] = $r[0]['hubloc_hash']; } - $s = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1", + $s = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1", dbesc($a['signature']['signer']) - ); + ); if($s) { $arr['owner_xchan'] = $s[0]['hubloc_hash']; @@ -1810,7 +1814,7 @@ logger('hey'); $arr['owner_xchan'] = $a['signature']['signer']; } - // @fixme - spoofable + /// @FIXME - spoofable if($AS->data['hubloc']) { $arr['item_verified'] = true; } @@ -1824,7 +1828,7 @@ logger('hey'); $result = self::process_delivery($arr['owner_xchan'],$arr, [ $channel['channel_portable_id'] ],false,false,true); if ($result) { $ret = array_merge($ret, $result); - } + } } return $ret; @@ -1841,8 +1845,8 @@ logger('hey'); * * \e int \b obj_type * * \e int \b mid * @param int $uid + * @return void */ - static function remove_community_tag($sender, $arr, $uid) { if(! (activity_match($arr['verb'], ACTIVITY_TAG) && ($arr['obj_type'] == ACTIVITY_OBJ_TAGTERM))) @@ -1870,7 +1874,7 @@ logger('hey'); } $i = $r[0]; - + if($i['target']) $i['target'] = json_decode($i['target'],true); if($i['object']) @@ -1913,8 +1917,8 @@ logger('hey'); * @param array $orig * @param int $uid * @param boolean $tag_delivery + * @return void|array */ - static function update_imported_item($sender, $item, $orig, $uid, $tag_delivery) { // If this is a comment being updated, remove any privacy information @@ -2054,7 +2058,7 @@ logger('hey'); } foreach($deliveries as $d) { - + $DR = new DReport(z_root(),$sender,$d,$arr['mid']); $r = q("select * from channel where channel_portable_id = '%s' limit 1", @@ -2073,7 +2077,7 @@ logger('hey'); if(! perm_is_allowed($channel['channel_id'],$sender,'post_mail')) { - /* + /* * Always allow somebody to reply if you initiated the conversation. It's anti-social * and a bit rude to send a private message to somebody and block their ability to respond. * If you are being harrassed and want to put an end to it, delete the conversation. @@ -2133,12 +2137,13 @@ logger('hey'); * @brief Processes delivery of profile. * * @see import_directory_profile() + * * @param array $sender an associative array * * \e string \b hash a xchan_hash * @param array $arr * @param array $deliveries (unused) + * @return void */ - static function process_profile_delivery($sender, $arr, $deliveries) { logger('process_profile_delivery', LOGGER_DEBUG); @@ -2159,6 +2164,7 @@ logger('hey'); * * \e string \b hash a xchan_hash * @param array $arr * @param array $deliveries (unused) deliveries is irrelevant + * @return void */ static function process_location_delivery($sender, $arr, $deliveries) { @@ -2176,7 +2182,7 @@ logger('hey'); $x = Libsync::sync_locations($xchan,$arr,true); logger('results: ' . print_r($x,true), LOGGER_DEBUG); if($x['changed']) { - $guid = random_string() . '@' . App::get_hostname(); + //$guid = random_string() . '@' . App::get_hostname(); Libzotdir::update_modtime($sender,$r[0]['xchan_guid'],$arr['locations'][0]['address'],UPDATE_FLAGS_UPDATED); } } @@ -2200,8 +2206,8 @@ logger('hey'); * * @param string $sender_hash A channel hash * @param array $locations + * @return void */ - static function check_location_move($sender_hash, $locations) { if(! $locations) @@ -2243,7 +2249,6 @@ logger('hey'); } - /** * @brief Returns an array with all known distinct hubs for this channel. * @@ -2252,7 +2257,6 @@ logger('hey'); * * \e string \b channel_hash the hash of the channel * @return array an array with associative arrays */ - static function encode_locations($channel) { $ret = []; @@ -2293,7 +2297,7 @@ logger('hey'); if(! $z['site_id']) { $z['site_id'] = Libzot::make_xchan_hash($z['url'],$z['sitekey']); } - + $ret[] = $z; } } @@ -2306,10 +2310,8 @@ logger('hey'); * @brief * * @param array $arr - * @param string $pubkey * @return boolean true if updated or inserted */ - static function import_site($arr) { if( (! is_array($arr)) || (! $arr['url']) || (! $arr['site_sig'])) @@ -2584,20 +2586,20 @@ logger('hey'); $feed = ((x($arr,'feed')) ? intval($arr['feed']) : 0); if($ztarget) { - $t = q("select * from hubloc where hubloc_id_url = '%s' limit 1", + $t = q("select * from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1", dbesc($ztarget) ); if($t) { - + $ztarget_hash = $t[0]['hubloc_hash']; } else { - + // should probably perform discovery of the requestor (target) but if they actually had - // permissions we would know about them and we only want to know who they are to + // permissions we would know about them and we only want to know who they are to // enumerate their specific permissions - + $ztarget_hash = EMPTY_STR; } } @@ -2744,7 +2746,7 @@ logger('hey'); $ret['id'] = $e['xchan_guid']; $ret['id_sig'] = self::sign($e['xchan_guid'], $e['channel_prvkey']); - $ret['primary_location'] = [ + $ret['primary_location'] = [ 'address' => $e['xchan_addr'], 'url' => $e['xchan_url'], 'connections_url' => $e['xchan_connurl'], @@ -2766,7 +2768,7 @@ logger('hey'); $ret['searchable'] = $searchable; $ret['adult_content'] = $adult_channel; $ret['public_forum'] = $public_forum; - + $ret['comments'] = map_scope(PermissionLimits::Get($e['channel_id'],'post_comments')); $ret['mail'] = map_scope(PermissionLimits::Get($e['channel_id'],'post_mail')); @@ -2824,14 +2826,20 @@ logger('hey'); $ret['locations'] = $x; $ret['site'] = self::site_info(); + /** + * @hooks zotinfo + * Hook to manipulate the zotinfo array before it is returned. + */ + call_hooks('zotinfo', $ret); - call_hooks('zotinfo',$ret); - - return($ret); - + return $ret; } - + /** + * @brief Get siteinfo. + * + * @return array + */ static function site_info() { $signing_key = get_config('system','prvkey'); @@ -2868,7 +2876,7 @@ logger('hey'); if($dirmode != DIRECTORY_MODE_STANDALONE) { $register_policy = intval(get_config('system','register_policy')); - + if($register_policy == REGISTER_CLOSED) $ret['site']['register_policy'] = 'closed'; if($register_policy == REGISTER_APPROVE) @@ -2915,18 +2923,16 @@ logger('hey'); } return $ret['site']; - } /** * @brief * * @param array $hub - * @param string $sitekey (optional, default empty) + * @param string $site_id (optional, default empty) * * @return string hubloc_url */ - static function update_hub_connected($hub, $site_id = '') { if ($site_id) { @@ -2985,12 +2991,21 @@ logger('hey'); return $hub['hubloc_url']; } - + /** + * @brief + * + * @param string $data + * @param string $key + * @param string $alg (optional) default 'sha256' + * @return string + */ static function sign($data,$key,$alg = 'sha256') { if(! $key) return 'no key'; + $sig = ''; openssl_sign($data,$sig,$key,$alg); + return $alg . '.' . base64url_encode($sig); } @@ -3003,24 +3018,27 @@ logger('hey'); if ($key && count($x) === 2) { $alg = $x[0]; $signature = base64url_decode($x[1]); - + $verify = @openssl_verify($data,$signature,$key,$alg); if ($verify === (-1)) { while ($msg = openssl_error_string()) { logger('openssl_verify: ' . $msg,LOGGER_NORMAL,LOG_ERR); } - btlogger('openssl_verify: key: ' . $key, LOGGER_DEBUG, LOG_ERR); + btlogger('openssl_verify: key: ' . $key, LOGGER_DEBUG, LOG_ERR); } } return(($verify > 0) ? true : false); } - - + /** + * @brief + * + * @return boolean + */ static function is_zot_request() { - $x = getBestSupportedMimeType([ 'application/x-zot+json' ]); + return(($x) ? true : false); } diff --git a/Zotlabs/Lib/PConfig.php b/Zotlabs/Lib/PConfig.php index 5e5954c95..c08c11e75 100644 --- a/Zotlabs/Lib/PConfig.php +++ b/Zotlabs/Lib/PConfig.php @@ -112,9 +112,11 @@ class PConfig { * The configuration key to set * @param string $value * The value to store + * @param string $updated (optional) + * The datetime to store * @return mixed Stored $value or false */ - static public function Set($uid, $family, $key, $value, $updated=NULL) { + 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) @@ -131,14 +133,19 @@ class PConfig { $dbvalue = ((is_array($value)) ? serialize($value) : $value); $dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue); + $now = datetime_convert(); if (! $updated) { - $updated = datetime_convert(); + //Sometimes things happen fast... very fast. + //To make sure legitimate updates aren't rejected + //because not enough time has passed. We say our updates + //happened just a short time in the past rather than right now. + $updated = datetime_convert('UTC','UTC','-2 seconds'); } $hash = hash('sha256',$family.':'.$key); if (self::Get($uid, 'hz_delpconfig', $hash) !== false) { - if (self::Get($uid, 'hz_delpconfig', $hash) > $updated) { + if (self::Get($uid, 'hz_delpconfig', $hash) > $now) { logger('Refusing to update pconfig with outdated info (Item deleted more recently).', LOGGER_NORMAL, LOG_ERR); return self::Get($uid,$family,$key); } else { @@ -173,7 +180,7 @@ class PConfig { } else { - $new = (\App::$config[$uid][$family]['pcfgud:'.$key] < $updated); + $new = (\App::$config[$uid][$family]['pcfgud:'.$key] < $now); if ($new) { @@ -234,16 +241,18 @@ class PConfig { * The category of the configuration value * @param string $key * The configuration key to delete - * @return mixed + * @param string $updated (optional) + * The datetime to store + * @return boolean */ 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); + $updated = ($updated) ? $updated : datetime_convert('UTC','UTC','-2 seconds'); + $now = datetime_convert(); + $newer = (\App::$config[$uid][$family]['pcfgud:'.$key] < $now); if (! $newer) { logger('Refusing to delete pconfig with outdated delete request.', LOGGER_NORMAL, LOG_ERR); @@ -266,22 +275,13 @@ class PConfig { dbesc($key) ); + // Synchronize delete with clones. + 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/Lib/Share.php b/Zotlabs/Lib/Share.php index d3ecbf7fa..3a2ab1783 100644 --- a/Zotlabs/Lib/Share.php +++ b/Zotlabs/Lib/Share.php @@ -54,6 +54,7 @@ class Share { if(! $this->item) return $obj; + $obj['asld'] = $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 83d243177..7ce0d0f9e 100644 --- a/Zotlabs/Lib/ThreadItem.php +++ b/Zotlabs/Lib/ThreadItem.php @@ -101,6 +101,7 @@ class ThreadItem { || strlen($item['deny_cid']) || strlen($item['deny_gid'])))) ? t('Private Message') : false); + $shareable = ((($conv->get_profile_owner() == local_channel() && local_channel()) && ($item['item_private'] != 1)) ? true : false); // allow an exemption for sharing stuff from your private feeds @@ -115,6 +116,16 @@ class ThreadItem { $privacy_warning = true; } + if ($lock) { + if (count(get_terms_oftype($item['term'],TERM_FORUM))) { + $privacy_warning = true; + } + } + + if ($lock && $privacy_warning) { + $lock = t('Privacy conflict. Discretion advised.'); + } + $mode = $conv->get_mode(); switch($item['item_type']) { @@ -411,9 +422,9 @@ class ThreadItem { 'rawmid' => $item['mid'], 'plink' => get_plink($item), 'edpost' => $edpost, // ((feature_enabled($conv->get_profile_owner(),'edit_posts')) ? $edpost : ''), - 'star' => ((feature_enabled($conv->get_profile_owner(),'star_posts')) ? $star : ''), + 'star' => ((feature_enabled($conv->get_profile_owner(),'star_posts') && ($item['item_type'] == ITEM_TYPE_POST)) ? $star : ''), 'tagger' => ((feature_enabled($conv->get_profile_owner(),'commtag')) ? $tagger : ''), - 'filer' => ((feature_enabled($conv->get_profile_owner(),'filing')) ? $filer : ''), + 'filer' => ((feature_enabled($conv->get_profile_owner(),'filing') && ($item['item_type'] == ITEM_TYPE_POST)) ? $filer : ''), 'bookmark' => (($conv->get_profile_owner() == local_channel() && local_channel() && $has_bookmarks) ? t('Save Bookmarks') : ''), 'addtocal' => (($has_event) ? t('Add to Calendar') : ''), 'drop' => $drop, |