diff options
Diffstat (limited to 'Zotlabs')
221 files changed, 14097 insertions, 13122 deletions
diff --git a/Zotlabs/Access/AccessList.php b/Zotlabs/Access/AccessList.php index 7cf7b5587..af6c4b7a6 100644 --- a/Zotlabs/Access/AccessList.php +++ b/Zotlabs/Access/AccessList.php @@ -54,7 +54,7 @@ class AccessList { * * \e string \b channel_deny_gid => string of denied gids */ function __construct($channel) { - if($channel) { + if ($channel) { $this->allow_cid = $channel['channel_allow_cid']; $this->allow_gid = $channel['channel_allow_gid']; $this->deny_cid = $channel['channel_deny_cid']; @@ -99,7 +99,6 @@ class AccessList { $this->allow_gid = $arr['allow_gid']; $this->deny_cid = $arr['deny_cid']; $this->deny_gid = $arr['deny_gid']; - $this->explicit = $explicit; } diff --git a/Zotlabs/Access/PermissionLimits.php b/Zotlabs/Access/PermissionLimits.php index c11dc95e6..fb5fe6133 100644 --- a/Zotlabs/Access/PermissionLimits.php +++ b/Zotlabs/Access/PermissionLimits.php @@ -2,6 +2,7 @@ namespace Zotlabs\Access; +use App; use Zotlabs\Lib\PConfig; /** @@ -39,10 +40,10 @@ class PermissionLimits { */ static public function Std_Limits() { $limits = []; - $perms = Permissions::Perms(); + $perms = Permissions::Perms(); - foreach($perms as $k => $v) { - if(strstr($k, 'view')) + foreach ($perms as $k => $v) { + if (strstr($k, 'view')) $limits[$k] = PERMS_PUBLIC; else $limits[$k] = PERMS_SPECIFIC; @@ -77,14 +78,14 @@ class PermissionLimits { * * \b array with all permission limits, if $perm is not set */ static public function Get($channel_id, $perm = '') { - if($perm) { + if ($perm) { return intval(PConfig::Get($channel_id, 'perm_limits', $perm)); } PConfig::Load($channel_id); - if(array_key_exists($channel_id, \App::$config) - && array_key_exists('perm_limits', \App::$config[$channel_id])) - return \App::$config[$channel_id]['perm_limits']; + if (array_key_exists($channel_id, App::$config) + && array_key_exists('perm_limits', App::$config[$channel_id])) + return App::$config[$channel_id]['perm_limits']; return false; } diff --git a/Zotlabs/Access/PermissionRoles.php b/Zotlabs/Access/PermissionRoles.php index 82df0c34b..998b6d8d2 100644 --- a/Zotlabs/Access/PermissionRoles.php +++ b/Zotlabs/Access/PermissionRoles.php @@ -218,13 +218,13 @@ class PermissionRoles { // set permissionlimits for this permission here, for example: // if($perm === 'mynewperm') - // \Zotlabs\Access\PermissionLimits::Set($uid,$perm,1); + // PermissionLimits::Set($uid,$perm,1); if($perm === 'view_wiki') - \Zotlabs\Access\PermissionLimits::Set($uid, $perm, PERMS_PUBLIC); + PermissionLimits::Set($uid, $perm, PERMS_PUBLIC); if($perm === 'write_wiki') - \Zotlabs\Access\PermissionLimits::Set($uid, $perm, PERMS_SPECIFIC); + PermissionLimits::Set($uid, $perm, PERMS_SPECIFIC); // set autoperms here if applicable @@ -262,11 +262,11 @@ class PermissionRoles { case 'view_wiki': set_abconfig($uid,$ab['abook_xchan'],'my_perms',$perm, intval(get_abconfig($uid,$ab['abook_xchan'],'my_perms','view_pages'))); - + break; case 'write_wiki': set_abconfig($uid,$ab['abook_xchan'],'my_perms',$perm, intval(get_abconfig($uid,$ab['abook_xchan'],'my_perms','write_pages'))); - + break; default: break; } @@ -317,4 +317,4 @@ class PermissionRoles { return $roles; } -}
\ No newline at end of file +} diff --git a/Zotlabs/Access/Permissions.php b/Zotlabs/Access/Permissions.php index 35016ed57..45dd30d69 100644 --- a/Zotlabs/Access/Permissions.php +++ b/Zotlabs/Access/Permissions.php @@ -65,9 +65,9 @@ class Permissions { 'write_wiki' => t('Can write to my wiki pages'), 'post_wall' => t('Can post on my channel (wall) page'), 'post_comments' => t('Can comment on or like my posts'), - 'post_mail' => t('Can send me private mail messages'), + 'post_mail' => t('Can send me direct messages'), 'post_like' => t('Can like/dislike profiles and profile things'), - 'tag_deliver' => t('Can forward to all my channel connections via ! mentions in posts'), + 'tag_deliver' => t('Can forward direct messages to all my channel connections (forum)'), 'chat' => t('Can chat with me'), 'republish' => t('Can source my public posts in derived channels'), 'delegate' => t('Can administer my channel') @@ -75,7 +75,7 @@ class Permissions { $x = [ 'permissions' => $perms, - 'filter' => $filter + 'filter' => $filter ]; /** * @hooks permissions_list @@ -84,7 +84,7 @@ class Permissions { */ call_hooks('permissions_list', $x); - return($x['permissions']); + return ($x['permissions']); } /** @@ -96,10 +96,10 @@ class Permissions { */ static public function BlockedAnonPerms() { - $res = []; + $res = []; $perms = PermissionLimits::Std_limits(); - foreach($perms as $perm => $limit) { - if($limit != PERMS_PUBLIC) { + foreach ($perms as $perm => $limit) { + if ($limit != PERMS_PUBLIC) { $res[] = $perm; } } @@ -111,7 +111,7 @@ class Permissions { */ call_hooks('write_perms', $x); - return($x['permissions']); + return ($x['permissions']); } /** @@ -120,20 +120,20 @@ class Permissions { * Converts [ 0 => 'view_stream', ... ] * to [ 'view_stream' => 1 ] for any permissions in $arr; * Undeclared permissions which exist in Perms() are added and set to 0. - * + * * @param array $arr * @return array */ static public function FilledPerms($arr) { - if(is_null($arr)) { + if (is_null($arr)) { btlogger('FilledPerms: null'); $arr = []; } $everything = self::Perms(); - $ret = []; - foreach($everything as $k => $v) { - if(in_array($k, $arr)) + $ret = []; + foreach ($everything as $k => $v) { + if (in_array($k, $arr)) $ret[$k] = 1; else $ret[$k] = 0; @@ -155,9 +155,9 @@ class Permissions { */ static public function OPerms($arr) { $ret = []; - if($arr) { - foreach($arr as $k => $v) { - $ret[] = [ 'name' => $k, 'value' => $v ]; + if ($arr) { + foreach ($arr as $k => $v) { + $ret[] = ['name' => $k, 'value' => $v]; } } return $ret; @@ -170,15 +170,16 @@ class Permissions { * @return boolean|array */ static public function FilledAutoperms($channel_id) { - if(! intval(get_pconfig($channel_id,'system','autoperms'))) + if (!intval(get_pconfig($channel_id, 'system', 'autoperms'))) return false; $arr = []; + $r = q("select * from pconfig where uid = %d and cat = 'autoperms'", intval($channel_id) ); - if($r) { - foreach($r as $rr) { + if ($r) { + foreach ($r as $rr) { $arr[$rr['k']] = intval($rr['v']); } } @@ -193,11 +194,11 @@ class Permissions { * @return boolean true if all perms from $p1 exist also in $p2 */ static public function PermsCompare($p1, $p2) { - foreach($p1 as $k => $v) { - if(! array_key_exists($k, $p2)) + foreach ($p1 as $k => $v) { + if (!array_key_exists($k, $p2)) return false; - if($p1[$k] != $p2[$k]) + if ($p1[$k] != $p2[$k]) return false; } @@ -214,18 +215,18 @@ class Permissions { */ static public function connect_perms($channel_id) { - $my_perms = []; - $permcat = null; + $my_perms = []; + $permcat = null; $automatic = 0; // If a default permcat exists, use that - $pc = ((feature_enabled($channel_id,'permcats')) ? get_pconfig($channel_id,'system','default_permcat') : 'default'); - if(! in_array($pc, [ '','default' ])) { - $pcp = new Zlib\Permcat($channel_id); + $pc = ((feature_enabled($channel_id, 'permcats')) ? get_pconfig($channel_id, 'system', 'default_permcat') : 'default'); + if (!in_array($pc, ['', 'default'])) { + $pcp = new Zlib\Permcat($channel_id); $permcat = $pcp->fetch($pc); - if($permcat && $permcat['perms']) { - foreach($permcat['perms'] as $p) { + if ($permcat && $permcat['perms']) { + foreach ($permcat['perms'] as $p) { $my_perms[$p['name']] = $p['value']; } } @@ -235,15 +236,15 @@ class Permissions { // and if there was no permcat or a default permcat, set the perms // from the role - $role = get_pconfig($channel_id,'system','permissions_role'); - if($role) { + $role = get_pconfig($channel_id, 'system', 'permissions_role'); + if ($role) { $xx = PermissionRoles::role_perms($role); - if($xx['perms_auto']) + if ($xx['perms_auto']) $automatic = 1; - if((! $my_perms) && ($xx['perms_connect'])) { + if ((!$my_perms) && ($xx['perms_connect'])) { $default_perms = $xx['perms_connect']; - $my_perms = Permissions::FilledPerms($default_perms); + $my_perms = Permissions::FilledPerms($default_perms); } } @@ -251,11 +252,11 @@ class Permissions { // it is likely a custom permissions role. First see if there are any // automatic permissions. - if(! $my_perms) { + if (!$my_perms) { $m = Permissions::FilledAutoperms($channel_id); - if($m) { + if ($m) { $automatic = 1; - $my_perms = $m; + $my_perms = $m; } } @@ -263,35 +264,35 @@ class Permissions { // custom perms but they are not automatic. They will be stored in abconfig with // the channel's channel_hash (the 'self' connection). - if(! $my_perms) { + if (!$my_perms) { $r = q("select channel_hash from channel where channel_id = %d", intval($channel_id) ); - if($r) { + if ($r) { $x = q("select * from abconfig where chan = %d and xchan = '%s' and cat = 'my_perms'", intval($channel_id), dbesc($r[0]['channel_hash']) ); - if($x) { - foreach($x as $xv) { + if ($x) { + foreach ($x as $xv) { $my_perms[$xv['k']] = intval($xv['v']); } } } } - return ( [ 'perms' => $my_perms, 'automatic' => $automatic ] ); + return (['perms' => $my_perms, 'automatic' => $automatic]); } static public function serialise($p) { $n = []; - if($p) { - foreach($p as $k => $v) { - if(intval($v)) { + if ($p) { + foreach ($p as $k => $v) { + if (intval($v)) { $n[] = $k; } } } - return implode(',',$n); + return implode(',', $n); } } diff --git a/Zotlabs/Daemon/Addon.php b/Zotlabs/Daemon/Addon.php index c2889e596..c6778750d 100644 --- a/Zotlabs/Daemon/Addon.php +++ b/Zotlabs/Daemon/Addon.php @@ -2,13 +2,12 @@ namespace Zotlabs\Daemon; -require_once('include/zot.php'); - class Addon { - static public function run($argc,$argv) { + static public function run($argc, $argv) { - call_hooks('daemon_addon',$argv); + call_hooks('daemon_addon', $argv); } + } diff --git a/Zotlabs/Daemon/Cache_embeds.php b/Zotlabs/Daemon/Cache_embeds.php index 08088abd6..9e5b8d2bb 100644 --- a/Zotlabs/Daemon/Cache_embeds.php +++ b/Zotlabs/Daemon/Cache_embeds.php @@ -2,7 +2,6 @@ namespace Zotlabs\Daemon; - class Cache_embeds { static public function run($argc,$argv) { diff --git a/Zotlabs/Daemon/Cache_query.php b/Zotlabs/Daemon/Cache_query.php new file mode 100644 index 000000000..5f92ae6d0 --- /dev/null +++ b/Zotlabs/Daemon/Cache_query.php @@ -0,0 +1,36 @@ +<?php + +namespace Zotlabs\Daemon; + +use Zotlabs\Lib\Cache; + +class Cache_query { + + static public function run($argc, $argv) { + + if(! $argc == 3) + return; + + $key = $argv[1]; + + $pid = get_config('procid', $key, false); + if ($pid && (function_exists('posix_kill') ? posix_kill($pid, 0) : true)) { + logger($key . ': procedure already run with pid ' . $pid, LOGGER_DEBUG); + return; + } + + $pid = getmypid(); + set_config('procid', $key, $pid); + + array_shift($argv); + array_shift($argv); + + $arr = json_decode(base64_decode($argv[0]), true); + + $r = call_user_func_array('q', $arr); + if($r) + Cache::set($key, serialize($r)); + + del_config('procid', $key); + } +} diff --git a/Zotlabs/Daemon/Channel_purge.php b/Zotlabs/Daemon/Channel_purge.php new file mode 100644 index 000000000..9fceb0fb9 --- /dev/null +++ b/Zotlabs/Daemon/Channel_purge.php @@ -0,0 +1,34 @@ +<?php + +namespace Zotlabs\Daemon; + +class Channel_purge { + + static public function run($argc,$argv) { + + cli_startup(); + + $channel_id = intval($argv[1]); + + $channel = q("select * from channel where channel_id = %d and channel_removed = 1", + intval($channel_id) + ); + + if (! $channel) { + return; + } + + do { + $r = q("select id from item where uid = %d and item_deleted = 0 limit 1000", + intval($channel_id) + ); + if ($r) { + foreach ($r as $rv) { + drop_item($rv['id'], false); + } + } + } while ($r); + + return; + } +} diff --git a/Zotlabs/Daemon/Checksites.php b/Zotlabs/Daemon/Checksites.php index 3bcfdd7cf..7227e96e4 100644 --- a/Zotlabs/Daemon/Checksites.php +++ b/Zotlabs/Daemon/Checksites.php @@ -6,34 +6,35 @@ require_once('include/hubloc.php'); class Checksites { - static public function run($argc,$argv) { + static public function run($argc, $argv) { logger('checksites: start'); - - if(($argc > 1) && ($argv[1])) + + if (($argc > 1) && ($argv[1])) $site_id = $argv[1]; - if($site_id) + if ($site_id) $sql_options = " and site_url = '" . dbesc($argv[1]) . "' "; - $days = intval(get_config('system','sitecheckdays')); - if($days < 1) + $days = intval(get_config('system', 'sitecheckdays')); + if ($days < 1) $days = 30; $r = q("select * from site where site_dead = 0 and site_update < %s - INTERVAL %s and site_type = %d $sql_options ", - db_utcnow(), db_quoteinterval($days . ' DAY'), + db_utcnow(), + db_quoteinterval($days . ' DAY'), intval(SITE_TYPE_ZOT) ); - if(! $r) + if (!$r) return; - foreach($r as $rr) { - if(! strcasecmp($rr['site_url'],z_root())) + foreach ($r as $rr) { + if (!strcasecmp($rr['site_url'], z_root())) continue; $x = ping_site($rr['site_url']); - if($x['success']) { + if ($x['success']) { logger('checksites: ' . $rr['site_url']); q("update site set site_update = '%s' where site_url = '%s' ", dbesc(datetime_convert()), diff --git a/Zotlabs/Daemon/Content_importer.php b/Zotlabs/Daemon/Content_importer.php new file mode 100644 index 000000000..67f1c8e80 --- /dev/null +++ b/Zotlabs/Daemon/Content_importer.php @@ -0,0 +1,77 @@ +<?php + +namespace Zotlabs\Daemon; + +use Zotlabs\Web\HTTPSig; +use Zotlabs\Lib\PConfig; + + +require_once('include/cli_startup.php'); +require_once('include/attach.php'); +require_once('include/import.php'); + +class Content_importer { + + static public function run($argc,$argv) { + cli_startup(); + + $page = $argv[1]; + $since = $argv[2]; + $until = $argv[3]; + $channel_address = $argv[4]; + $hz_server = urldecode($argv[5]); + + $m = parse_url($hz_server); + + $channel = channelx_by_nick($channel_address); + if(! $channel) { + logger('channel not found'); + return; + } + + $headers = [ + 'X-API-Token' => random_string(), + 'X-API-Request' => $hz_server . '/api/z/1.0/item/export_page?f=&since=' . urlencode($since) . '&until=' . urlencode($until) . '&page=' . $page , + 'Host' => $m['host'], + '(request-target)' => 'get /api/z/1.0/item/export_page?f=&since=' . urlencode($since) . '&until=' . urlencode($until) . '&page=' . $page , + ]; + + $headers = HTTPSig::create_sig($headers,$channel['channel_prvkey'], channel_url($channel),true,'sha512'); + + $x = z_fetch_url($hz_server . '/api/z/1.0/item/export_page?f=&since=' . urlencode($since) . '&until=' . urlencode($until) . '&page=' . $page,false,$redirects,[ 'headers' => $headers ]); + + // logger('item fetch: ' . print_r($x,true)); + + if(! $x['success']) { + logger('no API response',LOGGER_DEBUG); + killme(); + } + + $j = json_decode($x['body'],true); + + if(! is_array($j['item']) || ! count($j['item'])) { + PConfig::Set($channel['channel_id'], 'import', 'content_completed', 1); + return; + } + + $saved_notification_flags = notifications_off($channel['channel_id']); + + import_items($channel,$j['item'],false,((array_key_exists('relocate',$j)) ? $j['relocate'] : null)); + + notifications_on($channel['channel_id'], $saved_notification_flags); + + PConfig::Set($channel['channel_id'], 'import', 'content_progress', [ + 'items_total' => $j['items_total'], + 'items_page' => $j['items_page'], + 'items_current_page' => count($j['item']), + 'last_page' => $page, + 'next_cmd' => ['Content_importer', sprintf('%d',$page + 1), $since, $until, $channel['channel_address'], urlencode($hz_server)] + ]); + + $page++; + + Master::Summon([ 'Content_importer', sprintf('%d',$page), $since, $until, $channel['channel_address'], urlencode($hz_server) ]); + + return; + } +} diff --git a/Zotlabs/Daemon/Convo.php b/Zotlabs/Daemon/Convo.php new file mode 100644 index 000000000..940216b2c --- /dev/null +++ b/Zotlabs/Daemon/Convo.php @@ -0,0 +1,58 @@ +<?php + +namespace Zotlabs\Daemon; + +use Zotlabs\Lib\Activity; +use Zotlabs\Lib\ActivityStreams; +use Zotlabs\Lib\ASCollection; + +class Convo { + + static public function run($argc, $argv) { + + logger('convo invoked: ' . print_r($argv, true)); + + if ($argc != 4) { + return; + } + + $id = $argv[1]; + $channel_id = intval($argv[2]); + $contact_hash = $argv[3]; + + $channel = channelx_by_n($channel_id); + if (!$channel) { + return; + } + + $r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash + WHERE abook_channel = %d and abook_xchan = '%s' LIMIT 1", + intval($channel_id), + dbesc($contact_hash) + ); + if (!$r) { + return; + } + + $contact = array_shift($r); + + $obj = new ASCollection($id, $channel); + + $messages = $obj->get(); + + if ($messages) { + foreach ($messages as $message) { + if (is_string($message)) { + $message = Activity::fetch($message, $channel); + } + // set client flag because comments will probably just be objects and not full blown activities + // and that lets us use implied_create + $AS = new ActivityStreams($message); + if ($AS->is_valid() && is_array($AS->obj)) { + $item = Activity::decode_note($AS); + Activity::store($channel, $contact['abook_xchan'], $AS, $item); + } + } + } + } +} diff --git a/Zotlabs/Daemon/Cron.php b/Zotlabs/Daemon/Cron.php index 703d6ce08..6629491de 100644 --- a/Zotlabs/Daemon/Cron.php +++ b/Zotlabs/Daemon/Cron.php @@ -6,14 +6,14 @@ use Zotlabs\Lib\Libsync; class Cron { - static public function run($argc,$argv) { + static public function run($argc, $argv) { - $maxsysload = intval(get_config('system','maxloadavg')); - if($maxsysload < 1) + $maxsysload = intval(get_config('system', 'maxloadavg')); + if ($maxsysload < 1) $maxsysload = 50; - if(function_exists('sys_getloadavg')) { + if (function_exists('sys_getloadavg')) { $load = sys_getloadavg(); - if(intval($load[0]) > $maxsysload) { + if (intval($load[0]) > $maxsysload) { logger('system: load ' . $load . ' too high. Cron deferred to next scheduled run.'); return; } @@ -21,21 +21,21 @@ class Cron { // Check for a lockfile. If it exists, but is over an hour old, it's stale. Ignore it. $lockfile = 'store/[data]/cron'; - if((file_exists($lockfile)) && (filemtime($lockfile) > (time() - 3600)) - && (! get_config('system','override_cron_lockfile'))) { + if ((file_exists($lockfile)) && (filemtime($lockfile) > (time() - 3600)) + && (!get_config('system', 'override_cron_lockfile'))) { logger("cron: Already running"); return; } - + // Create a lockfile. Needs two vars, but $x doesn't need to contain anything. + $x = ''; file_put_contents($lockfile, $x); logger('cron: start'); - + // run queue delivery process in the background Master::Summon(array('Queue')); - Master::Summon(array('Poller')); /** @@ -46,27 +46,27 @@ class Cron { db_utcnow(), db_quoteinterval('3 MINUTE') ); - - // expire any expired mail - q("delete from mail where expires > '%s' and expires < %s ", - dbesc(NULL_DATE), - db_utcnow() - ); + require_once('include/account.php'); + remove_expired_registrations(); + + $interval = get_config('system', 'delivery_interval', 3); // expire any expired items - $r = q("select id,item_wall from item where expires > '2001-01-01 00:00:00' and expires < %s + $r = q("select id,item_wall from item where expires > '2001-01-01 00:00:00' and expires < %s and item_deleted = 0 ", db_utcnow() ); - if($r) { + if ($r) { require_once('include/items.php'); - foreach($r as $rr) { - drop_item($rr['id'],false,(($rr['item_wall']) ? DROPITEM_PHASE1 : DROPITEM_NORMAL)); - if($rr['item_wall']) { + foreach ($r as $rr) { + drop_item($rr['id'], false, (($rr['item_wall']) ? DROPITEM_PHASE1 : DROPITEM_NORMAL)); + if ($rr['item_wall']) { // The notifier isn't normally invoked unless item_drop is interactive. - Master::Summon( [ 'Notifier', 'drop', $rr['id'] ] ); + Master::Summon(['Notifier', 'drop', $rr['id']]); + if ($interval) + @time_sleep_until(microtime(true) + (float)$interval); } } } @@ -78,9 +78,9 @@ class Cron { dbesc(NULL_DATE), db_utcnow() ); - if($r) { + if ($r) { require_once('include/security.php'); - foreach($r as $rr) { + foreach ($r as $rr) { atoken_delete($rr['atoken_id']); } } @@ -90,33 +90,33 @@ class Cron { // or dead entries. $r = q("select channel_id from channel where channel_dirdate < %s - INTERVAL %s and channel_removed = 0", - db_utcnow(), + db_utcnow(), db_quoteinterval('30 DAY') ); - if($r) { - foreach($r as $rr) { - Master::Summon(array('Directory',$rr['channel_id'],'force')); - if($interval) - @time_sleep_until(microtime(true) + (float) $interval); + if ($r) { + foreach ($r as $rr) { + Master::Summon(array('Directory', $rr['channel_id'], 'force')); + if ($interval) + @time_sleep_until(microtime(true) + (float)$interval); } } - + // Clean expired photos from cache - + $r = q("SELECT DISTINCT xchan, content FROM photo WHERE photo_usage = %d AND expires < %s - INTERVAL %s", intval(PHOTO_CACHE), db_utcnow(), - db_quoteinterval(get_config('system','active_expire_days', '30') . ' DAY') + db_quoteinterval(get_config('system', 'active_expire_days', '30') . ' DAY') ); - if($r) { + if ($r) { q("DELETE FROM photo WHERE photo_usage = %d AND expires < %s - INTERVAL %s", intval(PHOTO_CACHE), db_utcnow(), - db_quoteinterval(get_config('system','active_expire_days', '30') . ' DAY') + db_quoteinterval(get_config('system', 'active_expire_days', '30') . ' DAY') ); - foreach($r as $rr) { + foreach ($r as $rr) { $file = dbunescbin($rr['content']); - if(is_file($file)) { + if (is_file($file)) { @unlink($file); @rmdir(dirname($file)); logger('info: deleted cached photo file ' . $file, LOGGER_DEBUG); @@ -126,80 +126,72 @@ class Cron { // publish any applicable items that were set to be published in the future // (time travel posts). Restrict to items that have come of age in the last - // couple of days to limit the query to something reasonable. + // couple of days to limit the query to something reasonable. $r = q("select id from item where item_delayed = 1 and created <= %s and created > '%s' ", db_utcnow(), - dbesc(datetime_convert('UTC','UTC','now - 2 days')) + dbesc(datetime_convert('UTC', 'UTC', 'now - 2 days')) ); - if($r) { - foreach($r as $rr) { + if ($r) { + foreach ($r as $rr) { $x = q("update item set item_delayed = 0 where id = %d", intval($rr['id']) ); - if($x) { + if ($x) { $z = q("select * from item where id = %d", - intval($message_id) + intval($rr['id']) ); - if($z) { + if ($z) { xchan_query($z); $sync_item = fetch_post_tags($z); Libsync::build_sync_packet($sync_item[0]['uid'], - [ - 'item' => [ encode_item($sync_item[0],true) ] + [ + 'item' => [encode_item($sync_item[0], true)] ] ); } - Master::Summon(array('Notifier','wall-new',$rr['id'])); + Master::Summon(array('Notifier', 'wall-new', $rr['id'])); + if ($interval) + @time_sleep_until(microtime(true) + (float)$interval); } } } - - // check if any connections transitioned to zot6 and upgrade the connections to zot6 at this hub if so. - require_once('include/connections.php'); - z6trans_connections(); - require_once('include/attach.php'); attach_upgrade(); - $abandon_days = intval(get_config('system','account_abandon_days')); - if($abandon_days < 1) - $abandon_days = 0; - - // once daily run birthday_updates and then expire in background // FIXME: add birthday updates, both locally and for xprof for use // by directory servers - $d1 = intval(get_config('system','last_expire_day')); - $d2 = intval(datetime_convert('UTC','UTC','now','d')); + $d1 = intval(get_config('system', 'last_expire_day')); + $d2 = intval(datetime_convert('UTC', 'UTC', 'now', 'd')); // Allow somebody to staggger daily activities if they have more than one site on their server, // or if it happens at an inconvenient (busy) hour. - $h1 = intval(get_config('system','cron_hour')); - $h2 = intval(datetime_convert('UTC','UTC','now','G')); + $h1 = intval(get_config('system', 'cron_hour')); + $h2 = intval(datetime_convert('UTC', 'UTC', 'now', 'G')); - if(($d2 != $d1) && ($h1 == $h2)) { + if (($d2 != $d1) && ($h1 == $h2)) { Master::Summon(array('Cron_daily')); } // update any photos which didn't get imported properly // This should be rare - $r = q("select xchan_photo_l, xchan_hash from xchan where xchan_photo_l != '' and xchan_photo_m = '' + $r = q("select xchan_photo_l, xchan_hash from xchan where xchan_photo_l != '' and xchan_photo_m = '' and xchan_photo_date < %s - INTERVAL %s", - db_utcnow(), + db_utcnow(), db_quoteinterval('1 DAY') ); - if($r) { + if ($r) { require_once('include/photo/photo_driver.php'); - foreach($r as $rr) { + foreach ($r as $rr) { $photos = import_xchan_photo($rr['xchan_photo_l'], $rr['xchan_hash'], false, true); - $x = q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' + q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'", dbesc($photos[0]), dbesc($photos[1]), @@ -213,33 +205,29 @@ class Cron { // pull in some public posts - $disable_discover_tab = get_config('system','disable_discover_tab') || get_config('system','disable_discover_tab') === false; - if(! $disable_discover_tab) - Master::Summon(array('Externals')); + $disable_discover_tab = get_config('system', 'disable_discover_tab') || get_config('system', 'disable_discover_tab') === false; + if (!$disable_discover_tab) + Master::Summon(['Externals']); - $generation = 0; + $restart = false; - $restart = false; - - if(($argc > 1) && ($argv[1] == 'restart')) { - $restart = true; + if (($argc > 1) && ($argv[1] == 'restart')) { + $restart = true; $generation = intval($argv[2]); - if(! $generation) + if (!$generation) return; } reload_plugins(); - $d = datetime_convert(); - // TODO check to see if there are any cronhooks before wasting a process - if(! $restart) + if (!$restart) Master::Summon(array('Cronhooks')); - set_config('system','lastcron',datetime_convert()); + set_config('system', 'lastcron', datetime_convert()); - //All done - clear the lockfile + //All done - clear the lockfile @unlink($lockfile); return; diff --git a/Zotlabs/Daemon/Cron_daily.php b/Zotlabs/Daemon/Cron_daily.php index 07533cc6e..d1b74a032 100644 --- a/Zotlabs/Daemon/Cron_daily.php +++ b/Zotlabs/Daemon/Cron_daily.php @@ -2,9 +2,11 @@ namespace Zotlabs\Daemon; +use Zotlabs\Lib\Libzotdir; + class Cron_daily { - static public function run($argc,$argv) { + static public function run($argc, $argv) { logger('cron_daily: start'); @@ -13,15 +15,12 @@ class Cron_daily { * */ - - require_once('include/dir_fns.php'); - check_upstream_directory(); - + Libzotdir::check_upstream_directory(); // Fire off the Cron_weekly process if it's the correct day. - - $d3 = intval(datetime_convert('UTC','UTC','now','N')); - if($d3 == 7) { + + $d3 = intval(datetime_convert('UTC', 'UTC', 'now', 'N')); + if ($d3 == 7) { Master::Summon(array('Cron_weekly')); } @@ -52,8 +51,8 @@ class Cron_daily { // Clean up emdedded content cache q("DELETE FROM cache WHERE updated < %s - INTERVAL %s", - db_utcnow(), - db_quoteinterval(get_config('system','active_expire_days', '30') . ' DAY') + db_utcnow(), + db_quoteinterval(get_config('system', 'active_expire_days', '30') . ' DAY') ); //update statistics in config @@ -67,8 +66,8 @@ class Cron_daily { // expire old delivery reports - $keep_reports = intval(get_config('system','expire_delivery_reports')); - if($keep_reports === 0) + $keep_reports = intval(get_config('system', 'expire_delivery_reports')); + if ($keep_reports === 0) $keep_reports = 10; q("delete from dreport where dreport_time < %s - INTERVAL %s", @@ -80,28 +79,26 @@ class Cron_daily { downgrade_accounts(); // If this is a directory server, request a sync with an upstream - // directory at least once a day, up to once every poll interval. + // directory at least once a day, up to once every poll interval. // Pull remote changes and push local changes. - // potential issue: how do we keep from creating an endless update loop? + // potential issue: how do we keep from creating an endless update loop? - $dirmode = get_config('system','directory_mode'); + $dirmode = get_config('system', 'directory_mode'); - if($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) { - require_once('include/dir_fns.php'); - sync_directories($dirmode); + if ($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) { + Libzotdir::sync_directories($dirmode); } - Master::Summon(array('Expire')); Master::Summon(array('Cli_suggest')); remove_obsolete_hublocs(); + remove_duplicate_singleton_hublocs(); - z6_discover(); - - call_hooks('cron_daily',datetime_convert()); + $date = datetime_convert(); + call_hooks('cron_daily', $date); - set_config('system','last_expire_day',intval(datetime_convert('UTC','UTC','now','d'))); + set_config('system', 'last_expire_day', intval(datetime_convert('UTC', 'UTC', 'now', 'd'))); /** * End Cron Daily diff --git a/Zotlabs/Daemon/Cron_weekly.php b/Zotlabs/Daemon/Cron_weekly.php index d44400767..407aa40ef 100644 --- a/Zotlabs/Daemon/Cron_weekly.php +++ b/Zotlabs/Daemon/Cron_weekly.php @@ -4,21 +4,22 @@ namespace Zotlabs\Daemon; class Cron_weekly { - static public function run($argc,$argv) { + static public function run($argc, $argv) { /** * Cron Weekly - * + * * Actions in the following block are executed once per day only on Sunday (once per week). * */ - call_hooks('cron_weekly',datetime_convert()); + $date = datetime_convert(); + call_hooks('cron_weekly', $date); z_check_cert(); prune_hub_reinstalls(); - + mark_orphan_hubsxchans(); // Find channels that were removed in the last three weeks, but @@ -31,8 +32,8 @@ class Cron_weekly { db_utcnow(), db_quoteinterval('21 DAY'), db_utcnow(), db_quoteinterval('10 DAY') ); - if($r) { - foreach($r as $rv) { + if ($r) { + foreach ($r as $rv) { channel_remove_final($rv['channel_id']); } } @@ -43,14 +44,14 @@ class Cron_weekly { db_utcnow(), db_quoteinterval('14 DAY') ); - $dirmode = intval(get_config('system','directory_mode')); - if($dirmode === DIRECTORY_MODE_SECONDARY || $dirmode === DIRECTORY_MODE_PRIMARY) { - logger('regdir: ' . print_r(z_fetch_url(get_directory_primary() . '/regdir?f=&url=' . urlencode(z_root()) . '&realm=' . urlencode(get_directory_realm())),true)); + $dirmode = intval(get_config('system', 'directory_mode')); + if ($dirmode === DIRECTORY_MODE_SECONDARY || $dirmode === DIRECTORY_MODE_PRIMARY) { + logger('regdir: ' . print_r(z_fetch_url(get_directory_primary() . '/regdir?f=&url=' . urlencode(z_root()) . '&realm=' . urlencode(get_directory_realm())), true)); } // Check for dead sites Master::Summon(array('Checksites')); - + // update searchable doc indexes Master::Summon(array('Importdoc')); diff --git a/Zotlabs/Daemon/CurlAuth.php b/Zotlabs/Daemon/CurlAuth.php index de41382e3..2396da9aa 100644 --- a/Zotlabs/Daemon/CurlAuth.php +++ b/Zotlabs/Daemon/CurlAuth.php @@ -2,6 +2,8 @@ namespace Zotlabs\Daemon; +use App; + // generate a curl compatible cookie file with an authenticated session for the given channel_id. // If this file is then used with curl and the destination url is sent through zid() or manually // manipulated to add a zid, it should allow curl to provide zot magic-auth across domains. @@ -10,15 +12,15 @@ namespace Zotlabs\Daemon; class CurlAuth { - static public function run($argc,$argv) { + static public function run($argc, $argv) { - if($argc != 2) + if ($argc != 2) return; - \App::$session->start(); + App::$session->start(); $_SESSION['authenticated'] = 1; - $_SESSION['uid'] = $argv[1]; + $_SESSION['uid'] = $argv[1]; $x = session_id(); @@ -29,14 +31,14 @@ class CurlAuth { $output = ''; - if($e) { + if ($e) { $lines = file($f); - if($lines) { - foreach($lines as $line) { - if(strlen($line) > 0 && $line[0] != '#' && substr_count($line, "\t") == 6) { + if ($lines) { + foreach ($lines as $line) { + if (strlen($line) > 0 && $line[0] != '#' && substr_count($line, "\t") == 6) { $tokens = explode("\t", $line); $tokens = array_map('trim', $tokens); - if($tokens[4] > time()) { + if ($tokens[4] > time()) { $output .= $line . "\n"; } } @@ -46,9 +48,9 @@ class CurlAuth { } } $t = time() + (24 * 3600); - file_put_contents($f, $output . 'HttpOnly_' . \App::get_hostname() . "\tFALSE\t/\tTRUE\t$t\tPHPSESSID\t" . $x, (($e) ? FILE_APPEND : 0)); + file_put_contents($f, $output . 'HttpOnly_' . App::get_hostname() . "\tFALSE\t/\tTRUE\t$t\tPHPSESSID\t" . $x, (($e) ? FILE_APPEND : 0)); - file_put_contents($c,$x); + file_put_contents($c, $x); return; } diff --git a/Zotlabs/Daemon/Deliver.php b/Zotlabs/Daemon/Deliver.php index c853af6a8..400ef697b 100644 --- a/Zotlabs/Daemon/Deliver.php +++ b/Zotlabs/Daemon/Deliver.php @@ -2,28 +2,28 @@ namespace Zotlabs\Daemon; -require_once('include/queue_fn.php'); +use Zotlabs\Lib\Queue; class Deliver { - - static public function run($argc,$argv) { - if($argc < 2) + static public function run($argc, $argv) { + + if ($argc < 2) return; - logger('deliver: invoked: ' . print_r($argv,true), LOGGER_DATA); + logger('deliver: invoked: ' . print_r($argv, true), LOGGER_DATA); - for($x = 1; $x < $argc; $x ++) { + for ($x = 1; $x < $argc; $x++) { - if(! $argv[$x]) + if (!$argv[$x]) continue; $r = q("select * from outq where outq_hash = '%s'", dbesc($argv[$x]) ); - if($r) { - queue_deliver($r[0],true); + if ($r) { + Queue::deliver($r[0], true); } } diff --git a/Zotlabs/Daemon/Deliver_hooks.php b/Zotlabs/Daemon/Deliver_hooks.php index e8b5acef0..4d3ce4e1d 100644 --- a/Zotlabs/Daemon/Deliver_hooks.php +++ b/Zotlabs/Daemon/Deliver_hooks.php @@ -2,21 +2,18 @@ namespace Zotlabs\Daemon; -require_once('include/zot.php'); - class Deliver_hooks { - static public function run($argc,$argv) { + static public function run($argc, $argv) { - if($argc < 2) + if ($argc < 2) return; - $r = q("select * from item where id = '%d'", intval($argv[1]) ); - if($r) - call_hooks('notifier_normal',$r[0]); + if ($r) + call_hooks('notifier_normal', $r[0]); } } diff --git a/Zotlabs/Daemon/Directory.php b/Zotlabs/Daemon/Directory.php index ab58432de..3996b8079 100644 --- a/Zotlabs/Daemon/Directory.php +++ b/Zotlabs/Daemon/Directory.php @@ -8,40 +8,40 @@ use Zotlabs\Lib\Queue; class Directory { - static public function run($argc,$argv){ + static public function run($argc, $argv) { - if($argc < 2) + if ($argc < 2) return; - $force = false; + $force = false; $pushall = true; - if($argc > 2) { - if($argv[2] === 'force') + if ($argc > 2) { + if ($argv[2] === 'force') $force = true; - if($argv[2] === 'nopush') + if ($argv[2] === 'nopush') $pushall = false; - } + } logger('directory update', LOGGER_DEBUG); - $dirmode = get_config('system','directory_mode'); - if($dirmode === false) + $dirmode = get_config('system', 'directory_mode'); + if ($dirmode === false) $dirmode = DIRECTORY_MODE_NORMAL; $x = q("select * from channel where channel_id = %d limit 1", intval($argv[1]) ); - if(! $x) + if (!$x) return; $channel = $x[0]; - if($dirmode != DIRECTORY_MODE_NORMAL) { + if ($dirmode != DIRECTORY_MODE_NORMAL) { // this is an in-memory update and we don't need to send a network packet. - Libzotdir::local_dir_update($argv[1],$force); + Libzotdir::local_dir_update($argv[1], $force); q("update channel set channel_dirdate = '%s' where channel_id = %d", dbesc(datetime_convert()), @@ -49,8 +49,9 @@ class Directory { ); // Now update all the connections - if($pushall) - Master::Summon(array('Notifier','refresh_all',$channel['channel_id'])); + if ($pushall) { + Master::Summon(array('Notifier', 'refresh_all', $channel['channel_id'])); + } return; } @@ -63,20 +64,18 @@ class Directory { // ensure the upstream directory is updated - - $packet = Libzot::build_packet($channel,(($force) ? 'force_refresh' : 'refresh')); - $z = Libzot::zot($url,$packet,$channel); - + $packet = Libzot::build_packet($channel, (($force) ? 'force_refresh' : 'refresh')); + $z = Libzot::zot($url, $packet, $channel); // re-queue if unsuccessful - if(! $z['success']) { + if (!$z['success']) { /** @FIXME we aren't updating channel_dirdate if we have to queue * the directory packet. That means we'll try again on the next poll run. */ - $hash = random_string(); + $hash = new_uuid(); Queue::insert(array( 'hash' => $hash, @@ -95,8 +94,8 @@ class Directory { } // Now update all the connections - if($pushall) - Master::Summon(array('Notifier','refresh_all',$channel['channel_id'])); - + if ($pushall) { + Master::Summon(array('Notifier', 'refresh_all', $channel['channel_id'])); + } } } diff --git a/Zotlabs/Daemon/Expire.php b/Zotlabs/Daemon/Expire.php index a688d6f97..99fe68b6f 100644 --- a/Zotlabs/Daemon/Expire.php +++ b/Zotlabs/Daemon/Expire.php @@ -2,26 +2,28 @@ namespace Zotlabs\Daemon; +require_once('include/items.php'); class Expire { - static public function run($argc,$argv){ + static public function run($argc, $argv) { cli_startup(); - - $pid = get_config('expire', 'procid', false); + + $pid = get_config('procid', 'expire', false); if ($pid && (function_exists('posix_kill') ? posix_kill($pid, 0) : true)) { - logger('Expire: procedure already run with pid ' . $pid, LOGGER_DEBUG); - return; + logger('procedure already run with pid ' . $pid, LOGGER_DEBUG); + return; } - + $pid = getmypid(); - set_config('expire', 'procid', $pid); + set_config('procid', 'expire', $pid); // perform final cleanup on previously delete items $r = q("select id from item where item_deleted = 1 and item_pending_remove = 0 and changed < %s - INTERVAL %s", - db_utcnow(), db_quoteinterval('10 DAY') + db_utcnow(), + db_quoteinterval('10 DAY') ); if ($r) { foreach ($r as $rr) { @@ -32,23 +34,22 @@ class Expire { // physically remove anything that has been deleted for more than two months /** @FIXME - this is a wretchedly inefficient query */ - $r = q("delete from item where item_pending_remove = 1 and changed < %s - INTERVAL %s", - db_utcnow(), db_quoteinterval('36 DAY') + q("delete from item where item_pending_remove = 1 and changed < %s - INTERVAL %s", + db_utcnow(), + db_quoteinterval('36 DAY') ); - /** @FIXME make this optional as it could have a performance impact on large sites */ - if (intval(get_config('system', 'optimize_items'))) q("optimize table item"); logger('expire: start with pid ' . $pid, LOGGER_DEBUG); - $site_expire = intval(get_config('system', 'default_expire_days')); - $commented_days = intval(get_config('system','active_expire_days')); + $site_expire = intval(get_config('system', 'default_expire_days')); + $commented_days = intval(get_config('system', 'active_expire_days')); logger('site_expire: ' . $site_expire); - $r = q("SELECT channel_id, channel_system, channel_address, channel_expire_days from channel where true"); + $r = dbq("SELECT channel_id, channel_system, channel_address, channel_expire_days from channel where true"); if ($r) { foreach ($r as $rr) { @@ -64,11 +65,12 @@ class Expire { $channel_expire = $service_class_expire; else $channel_expire = $site_expire; - + if (intval($channel_expire) && (intval($channel_expire) < intval($rr['channel_expire_days'])) || intval($rr['channel_expire_days'] == 0)) { $expire_days = $channel_expire; - } else { + } + else { $expire_days = $rr['channel_expire_days']; } @@ -93,13 +95,13 @@ class Expire { } logger('Expire: sys interval: ' . $expire_days, LOGGER_DEBUG); - + if ($expire_days) item_expire($x['channel_id'], $expire_days, $commented_days); logger('Expire: sys: done', LOGGER_DEBUG); } - - del_config('expire', 'procid'); + + del_config('procid', 'expire'); } } diff --git a/Zotlabs/Daemon/Externals.php b/Zotlabs/Daemon/Externals.php index a9988a509..81414d02d 100644 --- a/Zotlabs/Daemon/Externals.php +++ b/Zotlabs/Daemon/Externals.php @@ -2,97 +2,153 @@ namespace Zotlabs\Daemon; -require_once('include/zot.php'); +use Zotlabs\Lib\Activity; +use Zotlabs\Lib\Libzot; +use Zotlabs\Lib\ActivityStreams; +use Zotlabs\Lib\ASCollection; + require_once('include/channel.php'); class Externals { - static public function run($argc,$argv){ + static public function run($argc, $argv) { + + logger('externals: start'); - $total = 0; + $importer = get_sys_channel(); + $total = 0; $attempts = 0; logger('externals: startup', LOGGER_DEBUG); // pull in some public posts + while ($total == 0 && $attempts < 3) { + $arr = ['url' => '']; + call_hooks('externals_url_select', $arr); - while($total == 0 && $attempts < 3) { - $arr = array('url' => ''); - call_hooks('externals_url_select',$arr); - - if($arr['url']) { + if ($arr['url']) { $url = $arr['url']; - } + } else { - $randfunc = db_getfunc('RAND'); + $networks = ['zot6']; + + if (plugin_is_installed('pubcrawl')) { + $networks[] = 'activitypub'; + } - // fixme this query does not deal with directory realms. + stringify_array_elms($networks); + $networks_str = implode(',', $networks); - $r = q("select site_url, site_pull from site where site_url != '%s' and site_flags != %d and site_type = %d and site_dead = 0 order by $randfunc limit 1", + $randfunc = db_getfunc('RAND'); + + // fixme this query does not deal with directory realms. + //$r = q("select site_url, site_pull from site where site_url != '%s' + //and site_flags != %d and site_type = %d + //and site_dead = 0 and site_project like '%s' and site_version > '5.3.1' order by $randfunc limit 1", + //dbesc(z_root()), + //intval(DIRECTORY_MODE_STANDALONE), + //intval(SITE_TYPE_ZOT), + //dbesc('hubzilla%') + //); + + $r = q("SELECT * FROM hubloc + LEFT JOIN abook ON abook_xchan = hubloc_hash + LEFT JOIN site ON site_url = hubloc_url WHERE + hubloc_network IN ( $networks_str ) AND + abook_xchan IS NULL AND + hubloc_url != '%s' AND + hubloc_updated > '%s' AND + hubloc_primary = 1 AND hubloc_deleted = 0 AND + site_dead = 0 + ORDER BY $randfunc LIMIT 1", dbesc(z_root()), - intval(DIRECTORY_MODE_STANDALONE), - intval(SITE_TYPE_ZOT) + datetime_convert('UTC', 'UTC', 'now - 30 days') ); - if($r) - $url = $r[0]['site_url']; + + $contact = $r[0]; + + if ($contact) { + $url = $contact['hubloc_id_url']; + } + } + + if (!$url) { + continue; } $blacklisted = false; - if(! check_siteallowed($url)) { + if (!check_siteallowed($contact['hubloc_url'])) { logger('blacklisted site: ' . $url); $blacklisted = true; } - $attempts ++; + $attempts++; // make sure we can eventually break out if somebody blacklists all known sites - if($blacklisted) { - if($attempts > 20) + if ($blacklisted) { + if ($attempts > 5) break; - $attempts --; + $attempts--; continue; } - if($url) { - if($r[0]['site_pull'] > NULL_DATE) - $mindate = urlencode(datetime_convert('','',$r[0]['site_pull'] . ' - 1 day')); - else { - $days = get_config('externals','since_days'); - if($days === false) - $days = 15; - $mindate = urlencode(datetime_convert('','','now - ' . intval($days) . ' days')); + $cl = Activity::get_actor_collections($contact['hubloc_hash']); + if(empty($cl)) { + $cl = get_xconfig($contact['hubloc_hash'], 'activitypub', 'collections'); + } + + if (is_array($cl) && array_key_exists('outbox', $cl)) { + $url = $cl['outbox']; + } + else { + $url = str_replace('/channel/', '/outbox/', $contact['hubloc_id_url']); + if ($url) { + $url .= '?top=1'; } + } + + if ($url) { + logger('fetching outbox: ' . $url); + + $obj = new ASCollection($url, $importer, 0, 10); + $messages = $obj->get(); - $feedurl = $url . '/zotfeed?f=&mindate=' . $mindate; + if ($messages) { + foreach ($messages as $message) { + if (is_string($message)) { + $message = Activity::fetch($message, $importer); + } - logger('externals: pulling public content from ' . $feedurl, LOGGER_DEBUG); + if ($message['type'] !== 'Create') { + continue; + } - $x = z_fetch_url($feedurl); - if(($x) && ($x['success'])) { + if ($contact['hubloc_network'] === 'zot6') { + // make sure we only fetch top level items + if (isset($message['object']['inReplyTo'])) { + continue; + } - q("update site set site_pull = '%s' where site_url = '%s'", - dbesc(datetime_convert()), - dbesc($url) - ); + Libzot::fetch_conversation($importer, $message['object']['id']); + $total++; + continue; + } - $j = json_decode($x['body'],true); - if($j['success'] && $j['messages']) { - $sys = get_sys_channel(); - foreach($j['messages'] as $message) { - // on these posts, clear any route info. - $message['route'] = ''; - $results = process_delivery(array('hash' => 'undefined'), get_item_elements($message), - array(array('hash' => $sys['xchan_hash'])), false, true); - $total ++; + $AS = new ActivityStreams($message); + if ($AS->is_valid() && is_array($AS->obj)) { + $item = Activity::decode_note($AS); + Activity::store($importer, $contact['abook_xchan'], $AS, $item); + $total++; } - logger('externals: import_public_posts: ' . $total . ' messages imported', LOGGER_DEBUG); } } + logger('fetched messages count: ' . $total); } } + return; } } diff --git a/Zotlabs/Daemon/File_importer.php b/Zotlabs/Daemon/File_importer.php new file mode 100644 index 000000000..7067e152d --- /dev/null +++ b/Zotlabs/Daemon/File_importer.php @@ -0,0 +1,71 @@ +<?php + +namespace Zotlabs\Daemon; + +use Zotlabs\Web\HTTPSig; +use Zotlabs\Lib\PConfig; + + +require_once('include/cli_startup.php'); +require_once('include/attach.php'); +require_once('include/import.php'); + +class File_importer { + + static public function run($argc,$argv) { + + cli_startup(); + + $page = $argv[1]; + $channel_address = $argv[2]; + $hz_server = urldecode($argv[3]); + + $m = parse_url($hz_server); + + $channel = channelx_by_nick($channel_address); + if(! $channel) { + logger('channel not found'); + return; + } + + $headers = [ + 'X-API-Token' => random_string(), + 'X-API-Request' => $hz_server . '/api/z/1.0/file/export_page?f=records=1&page=' . $page, + 'Host' => $m['host'], + '(request-target)' => 'get /api/z/1.0/file/export_page?f=records=1&page=' . $page, + ]; + + $headers = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel),true,'sha512'); + + // TODO: implement total count + $x = z_fetch_url($hz_server . '/api/z/1.0/file/export_page?f=records=1&page=' . $page, false, $redirects, [ 'headers' => $headers ]); + // logger('file fetch: ' . print_r($x,true)); + + if(! $x['success']) { + logger('no API response',LOGGER_DEBUG); + killme(); + } + + $j = json_decode($x['body'],true); + + if(! is_array($j['results'][0]['attach']) || ! count($j['results'][0]['attach'])) { + PConfig::Set($channel['channel_id'], 'import', 'files_completed', 1); + return; + } + + $r = sync_files($channel, $j['results']); + + PConfig::Set($channel['channel_id'], 'import', 'files_progress', [ + 'files_total' => $j['total'], + 'files_page' => 1, // export page atm returns just one file + 'last_page' => $page, + 'next_cmd' => ['File_importer',sprintf('%d',$page + 1), $channel['channel_address'], urlencode($hz_server)] + ]); + + $page++; + + Master::Summon([ 'File_importer',sprintf('%d',$page), $channel['channel_address'], urlencode($hz_server) ]); + + return; + } +} diff --git a/Zotlabs/Daemon/Gprobe.php b/Zotlabs/Daemon/Gprobe.php index 6951aa1d4..29efcf475 100644 --- a/Zotlabs/Daemon/Gprobe.php +++ b/Zotlabs/Daemon/Gprobe.php @@ -9,27 +9,27 @@ use Zotlabs\Lib\Zotfinger; // performs zot_finger on $argv[1], which is a hex_encoded webbie/reddress class Gprobe { - static public function run($argc,$argv) { + static public function run($argc, $argv) { - if($argc != 2) + if ($argc != 2) return; $url = hex2bin($argv[1]); - if(! strpos($url,'@')) + if (!strpos($url, '@')) return; - $r = q("select * from hubloc where hubloc_addr = '%s' limit 1", + $r = q("select * from hubloc where hubloc_addr = '%s' and hubloc_network = 'zot6' limit 1", dbesc($url) ); - if(! $r) { + if (!$r) { $href = Webfinger::zot_url(punify($url)); - if($href) { + if ($href) { $zf = Zotfinger::exec($href, null); } - if(is_array($zf) && array_path_exists('signature/signer',$zf) && $zf['signature']['signer'] === $href && intval($zf['signature']['header_valid'])) { - $xc = Libzot::import_xchan($zf['data']); + if (is_array($zf) && array_path_exists('signature/signer', $zf) && $zf['signature']['signer'] === $href && intval($zf['signature']['header_valid'])) { + Libzot::import_xchan($zf['data']); } } diff --git a/Zotlabs/Daemon/Importdoc.php b/Zotlabs/Daemon/Importdoc.php index 0ca589e4a..9e818e2b3 100755..100644 --- a/Zotlabs/Daemon/Importdoc.php +++ b/Zotlabs/Daemon/Importdoc.php @@ -5,7 +5,7 @@ namespace Zotlabs\Daemon; class Importdoc { - static public function run($argc,$argv) { + static public function run($argc, $argv) { require_once('include/help.php'); @@ -16,20 +16,20 @@ class Importdoc { static public function update_docs_dir($s) { $f = basename($s); $d = dirname($s); - if($s === 'doc/html') + if ($s === 'doc/html') return; $files = glob("$d/$f"); - if($files) { - foreach($files as $fi) { - if($fi === 'doc/html') { + if ($files) { + foreach ($files as $fi) { + if ($fi === 'doc/html') { continue; } - if(is_dir($fi)) { + if (is_dir($fi)) { self::update_docs_dir("$fi/*"); } else { // don't update media content - if(strpos(z_mime_content_type($fi),'text') === 0) { + if (strpos(z_mime_content_type($fi), 'text') === 0) { store_doc_file($fi); } } diff --git a/Zotlabs/Daemon/Importfile.php b/Zotlabs/Daemon/Importfile.php index 749949679..299fb1ee5 100644 --- a/Zotlabs/Daemon/Importfile.php +++ b/Zotlabs/Daemon/Importfile.php @@ -6,22 +6,21 @@ use Zotlabs\Lib\Libsync; class Importfile { - static public function run($argc,$argv){ + static public function run($argc, $argv) { - logger('Importfile: ' . print_r($argv,true)); + logger('Importfile: ' . print_r($argv, true)); - if($argc < 3) + if ($argc < 3) return; $channel = channelx_by_n($argv[1]); - if(! $channel) + if (!$channel) return; $srcfile = $argv[2]; $folder = (($argc > 3) ? $argv[3] : ''); $dstname = (($argc > 4) ? $argv[4] : ''); - - $hash = random_string(); + $hash = random_string(); $arr = [ 'src' => $srcfile, @@ -35,15 +34,15 @@ class Importfile { 'replace' => true ]; - if($folder) + if ($folder) $arr['folder'] = $folder; - attach_store($channel,$channel['channel_hash'],'import',$arr); + attach_store($channel, $channel['channel_hash'], 'import', $arr); + + $sync = attach_export_data($channel, $hash); + if ($sync) + Libsync::build_sync_packet($channel['channel_id'], ['file' => [$sync]]); - $sync = attach_export_data($channel,$hash); - if($sync) - Libsync::build_sync_packet($channel['channel_id'],array('file' => array($sync))); - return; } } diff --git a/Zotlabs/Daemon/Master.php b/Zotlabs/Daemon/Master.php index 8c3a7e570..6fa656be5 100644 --- a/Zotlabs/Daemon/Master.php +++ b/Zotlabs/Daemon/Master.php @@ -2,58 +2,57 @@ namespace Zotlabs\Daemon; -if(array_search( __file__ , get_included_files()) === 0) { +if (array_search(__file__, get_included_files()) === 0) { require_once('include/cli_startup.php'); array_shift($argv); $argc = count($argv); - if($argc) - Master::Release($argc,$argv); + if ($argc) + Master::Release($argc, $argv); return; } - class Master { static public function Summon($arr) { $hookinfo = [ - 'argv'=>$arr + 'argv' => $arr ]; - call_hooks ('daemon_master_summon',$hookinfo); + call_hooks('daemon_master_summon', $hookinfo); - $arr = $hookinfo['argv']; + $arr = $hookinfo['argv']; $argc = count($arr); - if ((!is_array($arr) || (count($arr) < 1))) { - logger("Summon handled by hook.",LOGGER_DEBUG); + if ((!is_array($arr) || ($argc < 1))) { + logger("Summon handled by hook.", LOGGER_DEBUG); return; } - $phpbin = get_config('system','phpbin','php'); - proc_run($phpbin,'Zotlabs/Daemon/Master.php',$arr); + $phpbin = get_config('system', 'phpbin', 'php'); + proc_run($phpbin, 'Zotlabs/Daemon/Master.php', $arr); } - static public function Release($argc,$argv) { + static public function Release($argc, $argv) { cli_startup(); $hookinfo = [ - 'argv'=>$argv + 'argv' => $argv ]; - call_hooks ('daemon_master_release',$hookinfo); + call_hooks('daemon_master_release', $hookinfo); $argv = $hookinfo['argv']; $argc = count($argv); - if ((!is_array($argv) || (count($argv) < 1))) { - logger("Release handled by hook.",LOGGER_DEBUG); + if ((!is_array($argv) || ($argc < 1))) { + logger("Release handled by hook.", LOGGER_DEBUG); return; } - logger('Master: release: ' . json_encode($argv), LOGGER_ALL,LOG_DEBUG); - $cls = '\\Zotlabs\\Daemon\\' . $argv[0]; - $cls::run($argc,$argv); + logger('Master: release: ' . json_encode($argv), LOGGER_ALL, LOG_DEBUG); + $cls = '\\Zotlabs\\Daemon\\' . $argv[0]; + $cls::run($argc, $argv); } } diff --git a/Zotlabs/Daemon/Notifier.php b/Zotlabs/Daemon/Notifier.php index 28c512d4a..368a9229d 100644 --- a/Zotlabs/Daemon/Notifier.php +++ b/Zotlabs/Daemon/Notifier.php @@ -6,37 +6,27 @@ use Zotlabs\Lib\Libzot; use Zotlabs\Lib\Activity; use Zotlabs\Lib\Queue; -require_once('include/queue_fn.php'); require_once('include/html2plain.php'); require_once('include/conversation.php'); -require_once('include/zot.php'); require_once('include/items.php'); require_once('include/bbcode.php'); - /* - * This file was at one time responsible for doing all deliveries, but this caused - * big problems on shared hosting systems, where the process might get killed by the - * hosting provider and nothing would get delivered. - * It now only delivers one message under certain cases, and invokes a queued - * delivery mechanism (include/deliver.php) to deliver individual contacts at - * controlled intervals. - * This has a much better chance of surviving random processes getting killed - * by the hosting provider. + * Notifier - message dispatch and preparation for delivery * * The basic flow is: * Identify the type of message * Collect any information that needs to be sent * Convert it into a suitable generic format for sending - * Figure out who the recipients are and if we need to relay + * Figure out who the recipients are and if we need to relay * through a conversation owner - * Once we know what recipients are involved, collect a list of + * Once we know what recipients are involved, collect a list of * destination sites * Build and store a queue item for each unique site and invoke * a delivery process for each site or a small number of sites (1-3) * and add a slight delay between each delivery invocation if desired (usually) - * + * */ /* @@ -54,17 +44,15 @@ require_once('include/bbcode.php'); * event (in events.php) * expire (in items.php) * like (in like.php, poke.php) - * mail (in message.php) * tag (in photos.php, poke.php, tagger.php) * tgroup (in items.php) * wall-new (in photos.php, item.php) * * and ITEM_ID is the id of the item in the database that needs to be sent to others. * - * ZOT + * ZOT * permission_create abook_id * permission_accept abook_id - * permission_reject abook_id * permission_update abook_id * refresh_all channel_id * purge channel_id xchan_hash @@ -72,7 +60,6 @@ require_once('include/bbcode.php'); * expire channel_id * relay item_id (item was relayed to owner, we will deliver it as owner) * single_activity item_id (deliver to a singleton network from the appropriate clone) - * single_mail mail_id (deliver to a singleton network from the appropriate clone) * location channel_id * request channel_id xchan_hash message_id * rating xlink_id @@ -81,201 +68,153 @@ require_once('include/bbcode.php'); */ - class Notifier { - static public function run($argc,$argv){ + static public $deliveries = []; + static public $recipients = []; + static public $env_recips = []; + static public $packet_type = 'activity'; + static public $encoding = 'activitystreams'; + static public $encoded_item = null; + static public $channel = null; + static public $private = false; - if($argc < 3) + static public function run($argc, $argv) { + + if ($argc < 3) { return; + } - logger('notifier: invoked: ' . print_r($argv,true), LOGGER_DEBUG); + logger('notifier: invoked: ' . print_r($argv, true), LOGGER_DEBUG); $cmd = $argv[1]; $item_id = $argv[2]; - if(! $item_id) + if (!$item_id) { return; + } - $sys = get_sys_channel(); - - $deliveries = array(); + self::$deliveries = []; + self::$recipients = []; + self::$env_recips = []; + self::$packet_type = 'activity'; + self::$encoding = 'activitystreams'; + self::$encoded_item = null; + self::$channel = null; + self::$private = false; - $request = false; - $mail = false; - $top_level = false; - $location = false; - $recipients = array(); - $url_recipients = array(); + $sys = get_sys_channel(); $normal_mode = true; - $packet_type = 'undefined'; - - if($cmd === 'mail' || $cmd === 'single_mail') { - $normal_mode = false; - $mail = true; - $private = true; - $message = q("SELECT * FROM mail WHERE id = %d LIMIT 1", - intval($item_id) - ); - if(! $message) { - return; - } - xchan_mail_query($message[0]); - $uid = $message[0]['channel_id']; - $recipients[] = $message[0]['from_xchan']; // include clones - $recipients[] = $message[0]['to_xchan']; - $item = $message[0]; - - $encoded_item = encode_mail($item); - - $s = q("select * from channel where channel_id = %d limit 1", - intval($item['channel_id']) - ); - if($s) - $channel = $s[0]; - - } - elseif($cmd === 'request') { - $channel_id = $item_id; - $xchan = $argv[3]; - $request_message_id = $argv[4]; - - $s = q("select * from channel where channel_id = %d limit 1", - intval($channel_id) - ); - if($s) - $channel = $s[0]; - $private = true; - $recipients[] = $xchan; - $packet_type = 'request'; - $normal_mode = false; - } - elseif($cmd === 'keychange') { - $channel = channelx_by_n($item_id); - $r = q("select abook_xchan from abook where abook_channel = %d", + if ($cmd === 'keychange') { + self::$channel = channelx_by_n($item_id); + $r = q("select abook_xchan from abook where abook_channel = %d", intval($item_id) ); - if($r) { - foreach($r as $rr) { - $recipients[] = $rr['abook_xchan']; + if ($r) { + foreach ($r as $rr) { + self::$recipients[] = $rr['abook_xchan']; } } - $private = false; - $packet_type = 'keychange'; - $normal_mode = false; + self::$private = false; + self::$packet_type = 'keychange'; + self::$encoded_item = get_pconfig(self::$channel['channel_id'], 'system', 'keychange'); + self::$encoding = 'zot'; + $normal_mode = false; } - elseif(in_array($cmd, [ 'permission_update', 'permission_reject', 'permission_accept', 'permission_create' ])) { - // Get the (single) recipient + elseif (in_array($cmd, ['permission_update', 'permission_accept', 'permission_create'])) { + // Get the (single) recipient $r = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_id = %d and abook_self = 0", intval($item_id) ); - if($r) { - $uid = $r[0]['abook_channel']; + if ($r) { + $recip = $r[0]; + // Get the sender - $channel = channelx_by_n($uid); - if($channel) { - $perm_update = array('sender' => $channel, 'recipient' => $r[0], 'success' => false, 'deliveries' => ''); - - if($cmd === 'permission_create') - call_hooks('permissions_create',$perm_update); - elseif($cmd === 'permission_accept') - call_hooks('permissions_accept',$perm_update); - elseif($cmd === 'permission_reject') - call_hooks('permissions_reject',$perm_update); - else - call_hooks('permissions_update',$perm_update); - - if($perm_update['success']) { - if($perm_update['deliveries']) { - $deliveries[] = $perm_update['deliveries']; - do_delivery($deliveries); + self::$channel = channelx_by_n($recip['abook_channel']); + if (self::$channel) { + $perm_update = ['sender' => self::$channel, 'recipient' => $recip, 'success' => false, 'deliveries' => '']; + + switch ($cmd) { + case 'permission_create': + call_hooks('permissions_create', $perm_update); + break; + case 'permission_accept': + call_hooks('permissions_accept', $perm_update); + break; + case 'permission_update': + call_hooks('permissions_update', $perm_update); + break; + default: + break; + } + + if ($perm_update['success']) { + if ($perm_update['deliveries']) { + self::$deliveries[] = $perm_update['deliveries']; + do_delivery(self::$deliveries); } - return; + return; } else { - $recipients[] = $r[0]['abook_xchan']; - $private = false; - $packet_type = 'refresh'; - $packet_recips = array(array('guid' => $r[0]['xchan_guid'],'guid_sig' => $r[0]['xchan_guid_sig'],'hash' => $r[0]['xchan_hash'])); + self::$recipients[] = $recip['abook_xchan']; + self::$private = false; + self::$packet_type = 'refresh'; + self::$env_recips = [$recip['xchan_hash']]; } } } } - elseif($cmd === 'refresh_all') { + elseif ($cmd === 'refresh_all') { logger('notifier: refresh_all: ' . $item_id); - $uid = $item_id; - $channel = channelx_by_n($item_id); - $r = q("select abook_xchan from abook where abook_channel = %d", - intval($item_id) - ); - if($r) { - foreach($r as $rr) { - $recipients[] = $rr['abook_xchan']; - } - } - $private = false; - $packet_type = 'refresh'; - } - elseif($cmd === 'location') { - logger('notifier: location: ' . $item_id); - $s = q("select * from channel where channel_id = %d limit 1", - intval($item_id) - ); - if($s) - $channel = $s[0]; - $uid = $item_id; - $recipients = array(); + + self::$channel = channelx_by_n($item_id, true); + $r = q("select abook_xchan from abook where abook_channel = %d", intval($item_id) ); - if($r) { - foreach($r as $rr) { - $recipients[] = $rr['abook_xchan']; + if ($r) { + foreach ($r as $rr) { + self::$recipients[] = $rr['abook_xchan']; } } - $encoded_item = array('locations' => zot_encode_locations($channel),'type' => 'location', 'encoding' => 'zot'); - $target_item = array('aid' => $channel['channel_account_id'],'uid' => $channel['channel_id']); - $private = false; - $packet_type = 'location'; - $location = true; + // In case we deleted the channel, our abook entry has already vanished. + // In order to be able to update our clones we need to add ourself here. + self::$recipients[] = self::$channel['channel_hash']; + + self::$private = false; + self::$packet_type = 'refresh'; } - elseif($cmd === 'purge') { + elseif ($cmd === 'purge') { $xchan = $argv[3]; logger('notifier: purge: ' . $item_id . ' => ' . $xchan); - if (! $xchan) { + if (!$xchan) { return; } - $channel = channelx_by_n($item_id); - $recipients[] = $xchan; - $private = true; - $packet_type = 'purge'; - $packet_recips[] = ['hash' => $xchan]; + self::$channel = channelx_by_n($item_id, true); + self::$recipients = [$xchan]; + self::$private = true; + self::$packet_type = 'purge'; } - elseif($cmd === 'purge_all') { - + elseif ($cmd === 'purge_all') { logger('notifier: purge_all: ' . $item_id); - $channel = channelx_by_n($item_id); + self::$channel = channelx_by_n($item_id, true); + self::$recipients = []; + self::$private = false; + self::$packet_type = 'purge'; - $recipients = []; $r = q("select abook_xchan from abook where abook_channel = %d and abook_self = 0", intval($item_id) ); - if (! $r) { + if (!$r) { return; } foreach ($r as $rr) { - $recipients[] = $rr['abook_xchan']; - $packet_recips[] = ['hash' => $rr['abook_xchan']]; + self::$recipients[] = $rr['abook_xchan']; } - - $private = false; - $packet_type = 'purge'; - - } else { @@ -283,38 +222,34 @@ class Notifier { // Fetch the target item - $r = q("SELECT * FROM item WHERE id = %d and parent != 0 LIMIT 1", + $r = q("SELECT * FROM item WHERE id = %d AND parent != 0", intval($item_id) ); - - if(! $r) + if (!$r) { return; + } xchan_query($r); - $r = fetch_post_tags($r); - + $target_item = $r[0]; - if(in_array($target_item['author']['xchan_network'], ['rss', 'anon'])) { + if (in_array($target_item['author']['xchan_network'], ['rss', 'anon'])) { logger('notifier: target item author is not a fetchable actor', LOGGER_DEBUG); return; } - $deleted_item = false; - - if(intval($target_item['item_deleted'])) { + if (intval($target_item['item_deleted'])) { logger('notifier: target item ITEM_DELETED', LOGGER_DEBUG); - $deleted_item = true; } - if(! in_array(intval($target_item['item_type']), [ ITEM_TYPE_POST ] )) { - $hookinfo=[ - 'targetitem'=>$target_item, - 'deliver'=>false + if (!in_array(intval($target_item['item_type']), [ITEM_TYPE_POST])) { + $hookinfo = [ + 'targetitem' => $target_item, + 'deliver' => false ]; if (intval($target_item['item_type'] == ITEM_TYPE_CUSTOM)) { - call_hooks('customitem_deliver',$hookinfo); + call_hooks('customitem_deliver', $hookinfo); } if (!$hookinfo['deliver']) { @@ -328,14 +263,20 @@ class Notifier { // Check for non published items, but allow an exclusion for transmitting hidden file activities - if(intval($target_item['item_unpublished']) || intval($target_item['item_delayed']) || - intval($target_item['item_blocked']) || - ( intval($target_item['item_hidden']) && ($target_item['obj_type'] !== ACTIVITY_OBJ_FILE))) { + if (intval($target_item['item_unpublished']) || intval($target_item['item_delayed']) || + intval($target_item['item_blocked']) || + (intval($target_item['item_hidden']) && ($target_item['obj_type'] !== ACTIVITY_OBJ_FILE))) { logger('notifier: target item not published, so not forwardable', LOGGER_DEBUG); return; } - if(strpos($target_item['postopts'],'nodeliver') !== false) { + // follow/unfollow is for internal use only + if (in_array($target_item['verb'], [ACTIVITY_FOLLOW, ACTIVITY_UNFOLLOW])) { + logger('not fowarding follow/unfollow note activity'); + return; + } + + if (strpos($target_item['postopts'], 'nodeliver') !== false) { logger('notifier: target item is undeliverable', LOGGER_DEBUG); return; } @@ -343,79 +284,75 @@ class Notifier { $s = q("select * from channel left join xchan on channel_hash = xchan_hash where channel_id = %d limit 1", intval($target_item['uid']) ); - if($s) - $channel = $s[0]; + if ($s) { + self::$channel = $s[0]; + } - if($channel['channel_hash'] !== $target_item['author_xchan'] && $channel['channel_hash'] !== $target_item['owner_xchan']) { - logger("notifier: Sending channel {$channel['channel_hash']} is not owner {$target_item['owner_xchan']} or author {$target_item['author_xchan']}", LOGGER_NORMAL, LOG_WARNING); + if (self::$channel['channel_hash'] !== $target_item['author_xchan'] && self::$channel['channel_hash'] !== $target_item['owner_xchan']) { + logger("notifier: Sending channel " . self::$channel['channel_hash'] . " is not owner {$target_item['owner_xchan']} or author {$target_item['author_xchan']}", LOGGER_NORMAL, LOG_WARNING); return; } - - if($target_item['mid'] === $target_item['parent_mid']) { - $parent_item = $target_item; + if ($target_item['mid'] === $target_item['parent_mid']) { + $parent_item = $target_item; $top_level_post = true; } else { // fetch the parent item - $r = q("SELECT * from item where id = %d order by id asc", + $r = q("SELECT * FROM item WHERE id = %d", intval($target_item['parent']) ); - if(! $r) + if (!$r) { return; + } - if(strpos($r[0]['postopts'],'nodeliver') !== false) { + if (strpos($r[0]['postopts'], 'nodeliver') !== false) { logger('notifier: target item is undeliverable', LOGGER_DEBUG, LOG_NOTICE); return; } xchan_query($r); $r = fetch_post_tags($r); - - $parent_item = $r[0]; + + $parent_item = $r[0]; $top_level_post = false; } // avoid looping of discover items 12/4/2014 - - if($sys && $parent_item['uid'] == $sys['channel_id']) + if ($sys && $parent_item['uid'] == $sys['channel_id']) { return; + } - $encoded_item = encode_item($target_item); - + $m = get_iconfig($target_item, 'activitypub', 'signed_data'); // Re-use existing signature unless the activity type changed to a Tombstone, which won't verify. - $m = ((intval($target_item['item_deleted'])) ? '' : get_iconfig($target_item,'activitystreams','signed_data')); - - if($m) { - $activity = json_decode($m,true); + if ($m && (!intval($target_item['item_deleted']))) { + self::$encoded_item = json_decode($m, true); } else { - $activity = array_merge(['@context' => [ + self::$encoded_item = array_merge(['@context' => [ ACTIVITYSTREAMS_JSONLD_REV, 'https://w3id.org/security/v1', z_root() . ZOT_APSCHEMA_REV - ]], Activity::encode_activity($target_item) + ]], Activity::encode_activity($target_item) ); - } + } - logger('target_item: ' . print_r($target_item,true), LOGGER_DEBUG); - logger('encoded: ' . print_r($activity,true), LOGGER_DEBUG); + logger('target_item: ' . print_r($target_item, true), LOGGER_DEBUG); + logger('encoded: ' . print_r(self::$encoded_item, true), LOGGER_DEBUG); // Send comments to the owner to re-deliver to everybody in the conversation // We only do this if the item in question originated on this site. This prevents looping. // To clarify, a site accepting a new comment is responsible for sending it to the owner for relay. - // Relaying should never be initiated on a post that arrived from elsewhere. + // Relaying should never be initiated on a post that arrived from elsewhere. // We should normally be able to rely on ITEM_ORIGIN, but start_delivery_chain() incorrectly set this // flag on comments for an extended period. So we'll also call comment_local_origin() which looks at - // the hostname in the message_id and provides a second (fallback) opinion. - - $relay_to_owner = (((! $top_level_post) && (intval($target_item['item_origin'])) && comment_local_origin($target_item)) ? true : false); - - + // the hostname in the message_id and provides a second (fallback) opinion. - $uplink = false; + $relay_to_owner = (!$top_level_post && intval($target_item['item_origin']) && comment_local_origin($target_item)); + $uplink = false; + $upstream = false; // $cmd === 'relay' indicates the owner is sending it to the original recipients // don't allow the item in the relay command to relay to owner under any circumstances, it will loop @@ -425,154 +362,141 @@ class Notifier { // tag_deliver'd post which needs to be sent back to the original author - if(($cmd === 'uplink') && intval($parent_item['item_uplink']) && (! $top_level_post)) { - logger('notifier: uplink'); - $uplink = true; - } + if (($cmd === 'uplink') && intval($parent_item['item_uplink']) && (!$top_level_post)) { + logger('notifier: uplink'); + $uplink = true; + self::$packet_type = 'response'; + } - if(($relay_to_owner || $uplink) && ($cmd !== 'relay')) { + if (($relay_to_owner || $uplink) && ($cmd !== 'relay')) { logger('notifier: followup relay', LOGGER_DEBUG); - $recipients = array(($uplink) ? $parent_item['source_xchan'] : $parent_item['owner_xchan']); - $private = true; - if(! $encoded_item['flags']) - $encoded_item['flags'] = array(); - $encoded_item['flags'][] = 'relay'; - $upstream = true; + $sendto = (($uplink) ? $parent_item['source_xchan'] : $parent_item['owner_xchan']); + self::$recipients = [$sendto]; + self::$private = true; + $upstream = true; + self::$packet_type = 'response'; } else { - logger('notifier: normal distribution', LOGGER_DEBUG); - if($cmd === 'relay') - logger('notifier: owner relay'); - $upstream = false; + if ($cmd === 'relay') { + logger('owner relay (downstream delivery)'); + } + else { + logger('normal (downstream) distribution', LOGGER_DEBUG); + } + + if ($parent_item && $parent_item['item_private'] !== $target_item['item_private']) { + logger('conversation privacy mismatch - downstream delivery prevented'); + return; + } + // if our parent is a tag_delivery recipient, uplink to the original author causing - // a delivery fork. - - if(($parent_item) && intval($parent_item['item_uplink']) && (! $top_level_post) && ($cmd !== 'uplink')) { + // a delivery fork. + if ($parent_item && intval($parent_item['item_uplink']) && !$top_level_post && $cmd !== 'uplink') { // don't uplink a relayed post to the relay owner - if($parent_item['source_xchan'] !== $parent_item['owner_xchan']) { + if ($parent_item['source_xchan'] !== $parent_item['owner_xchan']) { logger('notifier: uplinking this item'); - Master::Summon(array('Notifier','uplink',$item_id)); + Master::Summon(['Notifier', 'uplink', $item_id]); } } - $private = false; - $recipients = collect_recipients($parent_item,$private); + self::$private = false; + self::$recipients = collect_recipients($parent_item, self::$private); + // FIXME add any additional recipients such as mentions, etc. if ($top_level_post) { // remove clones who will receive the post via sync - $recipients = array_diff($recipients, [ $target_item['owner_xchan'] ]); - } - - // FIXME add any additional recipients such as mentions, etc. + self::$recipients = array_values(array_diff(self::$recipients, [$target_item['owner_xchan']])); + } // don't send deletions onward for other people's stuff - // TODO verify this is needed - copied logic from same place in old code - - if(intval($target_item['item_deleted']) && (! intval($target_item['item_wall']))) { + if (intval($target_item['item_deleted']) && (!intval($target_item['item_wall']))) { logger('notifier: ignoring delete notification for non-wall item', LOGGER_NORMAL, LOG_NOTICE); return; } } } - $walltowall = (($top_level_post && $channel['xchan_hash'] === $target_item['author_xchan']) ? true : false); - // Generic delivery section, we have an encoded item and recipients // Now start the delivery process - $x = $encoded_item; - $x['title'] = 'private'; - $x['body'] = 'private'; - logger('notifier: encoded item: ' . print_r($x,true), LOGGER_DATA, LOG_DEBUG); - - //logger('notifier: encoded activity: ' . print_r($activity,true), LOGGER_DATA, LOG_DEBUG); + logger('encoded item: ' . print_r(self::$encoded_item, true), LOGGER_DATA, LOG_DEBUG); - stringify_array_elms($recipients); - if(! $recipients) { + stringify_array_elms(self::$recipients); + if (!self::$recipients) { logger('no recipients'); return; } - // logger('notifier: recipients: ' . print_r($recipients,true), LOGGER_NORMAL, LOG_DEBUG); + // logger('recipients: ' . print_r(self::$recipients,true), LOGGER_NORMAL, LOG_DEBUG); - $env_recips = (($private) ? array() : null); - - $details = q("select xchan_hash, xchan_network, xchan_addr, xchan_guid, xchan_guid_sig from xchan where xchan_hash in (" . protect_sprintf(implode(',',$recipients)) . ")"); + if (!count(self::$env_recips)) { + self::$env_recips = ((self::$private) ? [] : null); + } - $recip_list = array(); + $recip_list = []; - if($details) { - foreach($details as $d) { + $details = dbq("select xchan_hash, xchan_network, xchan_addr, xchan_guid, xchan_guid_sig from xchan where xchan_hash in (" . protect_sprintf(implode(',', self::$recipients)) . ")"); - $recip_list[] = $d['xchan_addr'] . ' (' . $d['xchan_hash'] . ')'; - if($private) { - $env_recips[] = [ - 'guid' => $d['xchan_guid'], - 'guid_sig' => $d['xchan_guid_sig'], - 'hash' => $d['xchan_hash'] - ]; + if ($details) { + foreach ($details as $d) { + $recip_list[] = $d['xchan_addr'] . ' (' . $d['xchan_hash'] . ')'; + if (self::$private) { + self::$env_recips[] = $d['xchan_hash']; } } } - $narr = [ - 'channel' => $channel, + 'channel' => self::$channel, 'upstream' => $upstream, - 'env_recips' => $env_recips, - 'packet_recips' => $packet_recips, - 'recipients' => $recipients, - 'item' => $item, + 'env_recips' => self::$env_recips, + 'recipients' => self::$recipients, 'target_item' => $target_item, 'parent_item' => $parent_item, 'top_level_post' => $top_level_post, - 'private' => $private, + 'private' => self::$private, 'relay_to_owner' => $relay_to_owner, 'uplink' => $uplink, 'cmd' => $cmd, - 'mail' => $mail, - 'single' => (($cmd === 'single_mail' || $cmd === 'single_activity') ? true : false), - 'location' => $location, - 'request' => $request, + 'single' => ($cmd === 'single_activity'), 'normal_mode' => $normal_mode, - 'packet_type' => $packet_type, - 'walltowall' => $walltowall, + 'packet_type' => self::$packet_type, 'queued' => [] ]; call_hooks('notifier_process', $narr); - if($narr['queued']) { - foreach($narr['queued'] as $pq) - $deliveries[] = $pq; + if ($narr['queued']) { + foreach ($narr['queued'] as $pq) + self::$deliveries[] = $pq; } // notifier_process can alter the recipient list - $recipients = $narr['recipients']; - $env_recips = $narr['env_recips']; - $packet_recips = $narr['packet_recips']; + self::$recipients = $narr['recipients']; + self::$env_recips = $narr['env_recips']; - if(($private) && (! $env_recips)) { + if (self::$private && !self::$env_recips) { // shouldn't happen - logger('notifier: private message with no envelope recipients.' . print_r($argv,true), LOGGER_NORMAL, LOG_NOTICE); + logger('notifier: private message with no envelope recipients.' . print_r($argv, true), LOGGER_NORMAL, LOG_NOTICE); + return; } - - logger('notifier: recipients (may be delivered to more if public): ' . print_r($recip_list,true), LOGGER_DEBUG); - + + logger('notifier: recipients (may be delivered to more if public): ' . print_r($recip_list, true), LOGGER_DEBUG); + // Now we have collected recipients (except for external mentions, FIXME) // Let's reduce this to a set of hubs; checking that the site is not dead. - $hubs = q("select hubloc.*, site.site_crypto, site.site_flags, site.site_version, site.site_project, site.site_dead from hubloc left join site on site_url = hubloc_url - where hubloc_hash in (" . protect_sprintf(implode(',',$recipients)) . ") + $hubs = q("select hubloc.*, site.site_crypto, site.site_flags, site.site_dead from hubloc left join site on site_url = hubloc_url + where hubloc_hash in (" . protect_sprintf(implode(',', self::$recipients)) . ") and hubloc_error = 0 and hubloc_deleted = 0" ); - // public posts won't make it to the local public stream unless there's a recipient on this site. + // public posts won't make it to the local public stream unless there's a recipient on this site. // This code block sees if it's a public post and localhost is missing, and if so adds an entry for the local sys channel to the $hubs list - if (! $private) { + if (!self::$private) { $found_localhost = false; if ($hubs) { foreach ($hubs as $h) { @@ -582,115 +506,108 @@ class Notifier { } } } - if (! $found_localhost) { - $localhub = q("select hubloc.*, site.site_crypto, site.site_flags, site.site_version, site.site_project, site.site_dead from hubloc - left join site on site_url = hubloc_url where hubloc_id_url = '%s' and hubloc_error = 0 and hubloc_deleted = 0", + if (!$found_localhost) { + $localhub = q("select hubloc.*, site.site_crypto, site.site_flags, site.site_dead from hubloc left join site on site_url = hubloc_url + where hubloc_id_url = '%s' and hubloc_error = 0 and hubloc_deleted = 0", dbesc(z_root() . '/channel/sys') ); if ($localhub) { - $hubs = array_merge($hubs, $localhub); + $hubs = array_merge($localhub, $hubs); } } } - - if(! $hubs) { + + if (!$hubs) { logger('notifier: no hubs', LOGGER_NORMAL, LOG_NOTICE); return; } /** - * Reduce the hubs to those that are unique. For zot hubs, we need to verify uniqueness by the sitekey, + * Reduce the hubs to those that are unique. For zot hubs, we need to verify uniqueness by the sitekey, * since it may have been a re-install which has not yet been detected and pruned. * For other networks which don't have or require sitekeys, we'll have to use the URL */ - $hublist = []; // this provides an easily printable list for the logs - $dhubs = []; // delivery hubs where we store our resulting unique array - $keys = []; // array of keys to check uniquness for zot hubs - $urls = []; // array of urls to check uniqueness of hubs from other networks - $hub_env = []; // per-hub envelope so we don't broadcast the entire envelope to all - $dead = []; // known dead hubs - report them as undeliverable - - foreach($hubs as $hub) { + $hublist = []; // this provides an easily printable list for the logs + $dhubs = []; // delivery hubs where we store our resulting unique array + $keys = []; // array of keys to check uniquness for zot hubs + $urls = []; // array of urls to check uniqueness of hubs from other networks + $hub_env = []; // per-hub envelope so we don't broadcast the entire envelope to all + $dead_hosts = []; // known dead hubs - report them as undeliverable - if (intval($hub['site_dead'])) { - $dead[] = $hub; + foreach ($hubs as $hub) { + if (isset($hub['site_dead']) && intval($hub['site_dead'])) { + if(!in_array($hub['hubloc_host'], $dead_hosts)) { + $dead_hosts[] = $hub['hubloc_host']; + } continue; } - if($env_recips) { - foreach($env_recips as $er) { - if($hub['hubloc_hash'] === $er['hash']) { - if(! array_key_exists($hub['hubloc_host'] . $hub['hubloc_sitekey'], $hub_env)) { - $hub_env[$hub['hubloc_host'] . $hub['hubloc_sitekey']] = []; + if (self::$env_recips) { + foreach (self::$env_recips as $er) { + if ($hub['hubloc_hash'] === $er) { + if (!array_key_exists($hub['hubloc_site_id'], $hub_env)) { + $hub_env[$hub['hubloc_site_id']] = []; } - $hub_env[$hub['hubloc_host'] . $hub['hubloc_sitekey']][] = $er; + $hub_env[$hub['hubloc_site_id']][] = $er; } } } - - if($hub['hubloc_network'] == 'zot') { - if(! in_array($hub['hubloc_sitekey'],$keys)) { - $hublist[] = $hub['hubloc_host'] . ' ' . $hub['hubloc_network']; - $dhubs[] = $hub; - $keys[] = $hub['hubloc_sitekey']; - } - } - else { - if(! in_array($hub['hubloc_url'],$urls)) { - if($hub['hubloc_url'] === z_root()) { + if ($hub['hubloc_network'] === 'zot6') { + if (!in_array($hub['hubloc_sitekey'], $keys)) { + if ($hub['hubloc_url'] === z_root()) { //deliver to local hub first array_unshift($hublist, $hub['hubloc_host'] . ' ' . $hub['hubloc_network']); array_unshift($dhubs, $hub); } else { $hublist[] = $hub['hubloc_host'] . ' ' . $hub['hubloc_network']; - $dhubs[] = $hub; + $dhubs[] = $hub; } - $urls[] = $hub['hubloc_url']; + $keys[] = $hub['hubloc_sitekey']; + } + } + else { + if (!in_array($hub['hubloc_url'], $urls)) { + $hublist[] = $hub['hubloc_host'] . ' ' . $hub['hubloc_network']; + $dhubs[] = $hub; + $urls[] = $hub['hubloc_url']; } } } - logger('notifier: will notify/deliver to these hubs: ' . print_r($hublist,true), LOGGER_DEBUG, LOG_DEBUG); + logger('notifier: will notify/deliver to these hubs: ' . print_r($hublist, true), LOGGER_DEBUG, LOG_DEBUG); - foreach($dhubs as $hub) { + foreach ($dhubs as $hub) { - logger('notifier_hub: ' . $hub['hubloc_url'],LOGGER_DEBUG); + logger('notifier_hub: ' . $hub['hubloc_url'], LOGGER_DEBUG); - if(! in_array($hub['hubloc_network'], [ 'zot','zot6' ])) { + if ($hub['hubloc_network'] !== 'zot6') { $narr = [ - 'channel' => $channel, + 'channel' => self::$channel, 'upstream' => $upstream, - 'env_recips' => $env_recips, - 'packet_recips' => $packet_recips, - 'recipients' => $recipients, - 'item' => $item, + 'env_recips' => self::$env_recips, + 'recipients' => self::$recipients, 'target_item' => $target_item, 'parent_item' => $parent_item, 'hub' => $hub, 'top_level_post' => $top_level_post, - 'private' => $private, + 'private' => self::$private, 'relay_to_owner' => $relay_to_owner, 'uplink' => $uplink, 'cmd' => $cmd, - 'mail' => $mail, - 'single' => (($cmd === 'single_mail' || $cmd === 'single_activity') ? true : false), - 'location' => $location, - 'request' => $request, + 'single' => $cmd === 'single_activity', 'normal_mode' => $normal_mode, - 'packet_type' => $packet_type, - 'walltowall' => $walltowall, + 'packet_type' => self::$packet_type, 'queued' => [] ]; - - call_hooks('notifier_hub',$narr); - if($narr['queued']) { - foreach($narr['queued'] as $pq) - $deliveries[] = $pq; + call_hooks('notifier_hub', $narr); + if ($narr['queued']) { + foreach ($narr['queued'] as $pq) + self::$deliveries[] = $pq; } continue; @@ -705,176 +622,83 @@ class Notifier { // will invoke a delivery to those connections which are connected to just that // hub instance. - if($cmd === 'single_mail' || $cmd === 'single_activity') { + if ($cmd === 'single_activity') { continue; } - if(! in_array($hub['hubloc_network'], [ 'zot','zot6' ])) { - continue; - } - - // Do not change this to a uuid as long as we have traditional zot servers - // in the loop. The signature verification step can't handle dashes in the - // hashes. - - $hash = random_string(48); + // default: zot protocol - $packet = null; - $pmsg = ''; - - if($packet_type === 'refresh' || $packet_type === 'purge') { - if($hub['hubloc_network'] === 'zot6') { - $packet = Libzot::build_packet($channel, $packet_type, ids_to_array($packet_recips,'hash')); - } - else { - $packet = zot_build_packet($channel,$packet_type,(($packet_recips) ? $packet_recips : null)); - } - } - if($packet_type === 'keychange' && $hub['hubloc_network'] === 'zot') { - $pmsg = get_pconfig($channel['channel_id'],'system','keychange'); - $packet = zot_build_packet($channel,$packet_type,(($packet_recips) ? $packet_recips : null)); - } - elseif($packet_type === 'request' && $hub['hubloc_network'] === 'zot') { - $env = (($hub_env && $hub_env[$hub['hubloc_host'] . $hub['hubloc_sitekey']]) ? $hub_env[$hub['hubloc_host'] . $hub['hubloc_sitekey']] : ''); - $packet = zot_build_packet($channel,$packet_type,$env,$hub['hubloc_sitekey'],$hub['site_crypto'], - $hash, array('message_id' => $request_message_id) - ); - } + $hash = new_uuid(); - if($packet) { - Queue::insert( - [ - 'hash' => $hash, - 'account_id' => $channel['channel_account_id'], - 'channel_id' => $channel['channel_id'], - 'posturl' => $hub['hubloc_callback'], - 'driver' => $hub['hubloc_network'], - 'notify' => $packet, - 'msg' => (($pmsg) ? json_encode($pmsg) : '') - ] - ); + $env = (($hub_env && $hub_env[$hub['hubloc_site_id']]) ? $hub_env[$hub['hubloc_site_id']] : ''); + if ((self::$private) && (!$env)) { + continue; } - else { - $env = (($hub_env && $hub_env[$hub['hubloc_host'] . $hub['hubloc_sitekey']]) ? $hub_env[$hub['hubloc_host'] . $hub['hubloc_sitekey']] : ''); - - - if($hub['hubloc_network'] === 'zot6') { - $zenv = []; - if($env) { - foreach($env as $e) { - $zenv[] = $e['hash']; - } - } - - $packet_type = (($upstream || $uplink) ? 'response' : 'activity'); - - // block zot private reshares from zot6, as this could cause a number of privacy issues - // due to parenting differences between the reshare implementations. In zot a reshare is - // a standalone parent activity and in zot6 it is a followup/child of the original activity. - // For public reshares, some comments to the reshare on the zot fork will not make it to zot6 - // due to these different message models. This cannot be prevented at this time. - - if($packet_type === 'activity' && $activity['type'] === 'Announce' && intval($target_item['item_private'])) { - continue; - } - - $packet = Libzot::build_packet($channel,$packet_type,$zenv,$activity,'activitystreams',(($private) ? $hub['hubloc_sitekey'] : null),$hub['site_crypto']); - } - else { - // currently zot6 delivery is only performed on normal items and not sync items or mail or anything else - // Eventually we will do this for all deliveries, but for now ensure this is precisely what we are dealing - // with before switching to zot6 as the primary zot6 handler checks for the existence of a message delivery report - // to trigger dequeue'ing - - $z6 = (($encoded_item && $encoded_item['type'] === 'activity' && (! array_key_exists('allow_cid',$encoded_item))) ? true : false); - if($z6) { - $packet = zot6_build_packet($channel,'notify',$env, json_encode($encoded_item), (($private) ? $hub['hubloc_sitekey'] : null), $hub['site_crypto'],$hash); - } - else { - $packet = zot_build_packet($channel,'notify',$env, (($private) ? $hub['hubloc_sitekey'] : null), $hub['site_crypto'],$hash); - - } - } + $packet = Libzot::build_packet(self::$channel, self::$packet_type, $env, self::$encoded_item, self::$encoding, ((self::$private) ? $hub['hubloc_sitekey'] : null), $hub['site_crypto']); - // remove this after most hubs have updated to version 5.0 - if(stripos($hub['site_project'], 'hubzilla') !== false && version_compare($hub['site_version'], '4.7.3', '<=')) { - if($encoded_item['type'] === 'mail') { - $encoded_item['from']['network'] = 'zot'; - $encoded_item['from']['guid_sig'] = str_replace('sha256.', '', $encoded_item['from']['guid_sig']); - } - else { - $encoded_item['owner']['network'] = 'zot'; - $encoded_item['owner']['guid_sig'] = str_replace('sha256.', '', $encoded_item['owner']['guid_sig']); - if(strpos($encoded_item['author']['url'], z_root()) === 0) { - $encoded_item['author']['network'] = 'zot'; - $encoded_item['author']['guid_sig'] = str_replace('sha256.', '', $encoded_item['author']['guid_sig']); - } - } - } + Queue::insert( + [ + 'hash' => $hash, + 'account_id' => self::$channel['channel_account_id'], + 'channel_id' => self::$channel['channel_id'], + 'posturl' => $hub['hubloc_callback'], + 'notify' => $packet, + 'msg' => EMPTY_STR + ] + ); - Queue::insert( - [ - 'hash' => $hash, - 'account_id' => $target_item['aid'], - 'channel_id' => $target_item['uid'], - 'posturl' => $hub['hubloc_callback'], - 'driver' => $hub['hubloc_network'], - 'notify' => $packet, - 'msg' => json_encode($encoded_item) - ] + // only create delivery reports for normal undeleted items + if (is_array($target_item) && (!$target_item['item_deleted']) && (!get_config('system', 'disable_dreport'))) { + q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_name, dreport_result, dreport_time, dreport_xchan, dreport_queue ) + values ( '%s', '%s','%s','%s','%s','%s','%s','%s' ) ", + dbesc($target_item['mid']), + dbesc($hub['hubloc_host']), + dbesc($hub['hubloc_host']), + dbesc($hub['hubloc_host']), + dbesc('queued'), + dbesc(datetime_convert()), + dbesc(self::$channel['channel_hash']), + dbesc($hash) ); - - // only create delivery reports for normal undeleted items - if(is_array($target_item) && array_key_exists('postopts',$target_item) && (! $target_item['item_deleted']) && (! get_config('system','disable_dreport'))) { - q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_result, dreport_time, dreport_xchan, dreport_queue ) values ( '%s','%s','%s','%s','%s','%s','%s' ) ", - dbesc($target_item['mid']), - dbesc($hub['hubloc_host']), - dbesc($hub['hubloc_host']), - dbesc('queued'), - dbesc(datetime_convert()), - dbesc($channel['channel_hash']), - dbesc($hash) - ); - } } - $deliveries[] = $hash; + self::$deliveries[] = $hash; + } - - if($normal_mode) { + + if ($normal_mode) { + // This wastes a process if there are no delivery hooks configured, so check this before launching the new process $x = q("select * from hook where hook = 'notifier_normal'"); - if($x) { - Master::Summon( [ 'Deliver_hooks', $target_item['id'] ] ); + if ($x) { + Master::Summon(['Deliver_hooks', $target_item['id']]); } } - if($deliveries) - do_delivery($deliveries); - - logger('notifier: basic loop complete.', LOGGER_DEBUG); - - if ($dead) { - foreach ($dead as $deceased) { - if (is_array($target_item) && (! $target_item['item_deleted']) && (! get_config('system','disable_dreport'))) { - q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_name, dreport_result, dreport_time, dreport_xchan, dreport_queue ) - values ( '%s', '%s','%s','%s','%s','%s','%s','%s' ) ", - dbesc($target_item['mid']), - dbesc($deceased['hubloc_host']), - dbesc($deceased['hubloc_host']), - dbesc($deceased['hubloc_host']), - dbesc('undeliverable/unresponsive site'), - dbesc(datetime_convert()), - dbesc($channel['channel_hash']), - dbesc(random_string(48)) - ); - } + if (self::$deliveries) { + do_delivery(self::$deliveries); + } + + if ($dead_hosts && is_array($target_item) && (!$target_item['item_deleted']) && (!get_config('system', 'disable_dreport'))) { + foreach ($dead_hosts as $deceased_host) { + $r = q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_name, dreport_result, dreport_time, dreport_xchan, dreport_queue ) + values ( '%s', '%s','%s','%s','%s','%s','%s','%s' ) ", + dbesc($target_item['mid']), + dbesc($deceased_host), + dbesc($deceased_host), + dbesc($deceased_host), + dbesc('undeliverable/unresponsive site'), + dbesc(datetime_convert()), + dbesc(self::$channel['channel_hash']), + dbesc(new_uuid()) + ); } } - call_hooks('notifier_end',$target_item); + call_hooks('notifier_end', $target_item); logger('notifer: complete.'); + return; } diff --git a/Zotlabs/Daemon/Onedirsync.php b/Zotlabs/Daemon/Onedirsync.php index cc16c0b58..ea995be9e 100644 --- a/Zotlabs/Daemon/Onedirsync.php +++ b/Zotlabs/Daemon/Onedirsync.php @@ -2,31 +2,31 @@ namespace Zotlabs\Daemon; -require_once('include/zot.php'); -require_once('include/dir_fns.php'); - +use Zotlabs\Lib\Libzot; +use Zotlabs\Lib\Libzotdir; class Onedirsync { - static public function run($argc,$argv) { + static public function run($argc, $argv) { logger('onedirsync: start ' . intval($argv[1])); - - if(($argc > 1) && (intval($argv[1]))) + + if (($argc > 1) && (intval($argv[1]))) $update_id = intval($argv[1]); - if(! $update_id) { + if (!$update_id) { logger('onedirsync: no update'); return; } - + $r = q("select * from updates where ud_id = %d limit 1", intval($update_id) ); - if(! $r) + if (!$r) return; - if(($r[0]['ud_flags'] & UPDATE_FLAGS_UPDATED) || (! $r[0]['ud_addr'])) + + if (($r[0]['ud_flags'] & UPDATE_FLAGS_UPDATED) || (!$r[0]['ud_addr'])) return; // Have we probed this channel more recently than the other directory server @@ -38,8 +38,8 @@ class Onedirsync { dbesc($r[0]['ud_date']), intval(UPDATE_FLAGS_UPDATED) ); - if($x) { - $y = q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and ( ud_flags & %d ) = 0 and ud_date != '%s'", + if ($x) { + q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and ( ud_flags & %d ) = 0 and ud_date != '%s'", intval(UPDATE_FLAGS_UPDATED), dbesc($r[0]['ud_addr']), intval(UPDATE_FLAGS_UPDATED), @@ -50,26 +50,28 @@ class Onedirsync { // ignore doing an update if this ud_addr refers to a known dead hubloc - $h = q("select * from hubloc where hubloc_addr = '%s' limit 1", - dbesc($r[0]['ud_addr']) + $h = q("select * from hubloc where hubloc_addr = '%s'", + dbesc($r[0]['ud_addr']), ); - if(($h) && ($h[0]['hubloc_status'] & HUBLOC_OFFLINE)) { - $y = q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and ( ud_flags & %d ) = 0 ", - intval(UPDATE_FLAGS_UPDATED), + + $h = Libzot::zot_record_preferred($h); + + if (($h) && (($h['hubloc_status'] & HUBLOC_OFFLINE) || $h['hubloc_deleted'] || $h['hubloc_error'])) { + q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and ( ud_flags & %d ) = 0 ", + intval(UPDATE_FLAGS_DELETED), dbesc($r[0]['ud_addr']), intval(UPDATE_FLAGS_UPDATED) ); - return; } - // we might have to pull this out some day, but for now update_directory_entry() + // we might have to pull this out some day, but for now update_directory_entry() // runs zot_finger() and is kind of zot specific - if($h && $h[0]['hubloc_network'] !== 'zot') + if ($h && $h['hubloc_network'] !== 'zot6') return; - update_directory_entry($r[0]); + Libzotdir::update_directory_entry($r[0]); return; } diff --git a/Zotlabs/Daemon/Onepoll.php b/Zotlabs/Daemon/Onepoll.php index 93a5412b0..e2a02ede4 100644 --- a/Zotlabs/Daemon/Onepoll.php +++ b/Zotlabs/Daemon/Onepoll.php @@ -2,68 +2,71 @@ namespace Zotlabs\Daemon; +use Zotlabs\Lib\Activity; +use Zotlabs\Lib\ActivityStreams; +use Zotlabs\Lib\ASCollection; use Zotlabs\Lib\Libzot; -require_once('include/zot.php'); require_once('include/socgraph.php'); - +require_once('include/feedutils.php'); class Onepoll { - static public function run($argc,$argv) { + static public function run($argc, $argv) { logger('onepoll: start'); - - if(($argc > 1) && (intval($argv[1]))) + + if (($argc > 1) && (intval($argv[1]))) $contact_id = intval($argv[1]); - if(! $contact_id) { + if (!$contact_id) { logger('onepoll: no contact'); return; } - $d = datetime_convert(); + $sql_extra = ''; + $allow_feeds = get_config('system', 'feed_contacts'); + if(!$allow_feeds) { + $sql_extra = ' and abook_feed = 0 '; + } $contacts = q("SELECT abook.*, xchan.*, account.* - FROM abook LEFT JOIN account on abook_account = account_id left join xchan on xchan_hash = abook_xchan - where abook_id = %d + FROM abook LEFT JOIN account on abook_account = account_id left join xchan on xchan_hash = abook_xchan + where abook_id = %d $sql_extra and abook_pending = 0 and abook_archived = 0 and abook_blocked = 0 and abook_ignored = 0 AND (( account_flags = %d ) OR ( account_flags = %d )) limit 1", intval($contact_id), intval(ACCOUNT_OK), intval(ACCOUNT_UNVERIFIED) - ); + ); - if(! $contacts) { + if (!$contacts) { logger('onepoll: abook_id not found: ' . $contact_id); return; } - $contact = $contacts[0]; - - $t = $contact['abook_updated']; - + $contact = $contacts[0]; $importer_uid = $contact['abook_channel']; - + $r = q("SELECT * from channel left join xchan on channel_hash = xchan_hash where channel_id = %d limit 1", intval($importer_uid) ); - if(! $r) + if (!$r) return; $importer = $r[0]; logger("onepoll: poll: ({$contact['id']}) IMPORTER: {$importer['xchan_name']}, CONTACT: {$contact['xchan_name']}"); - $last_update = ((($contact['abook_updated'] === $contact['abook_created']) || ($contact['abook_updated'] <= NULL_DATE)) - ? datetime_convert('UTC','UTC','now - 7 days') - : datetime_convert('UTC','UTC',$contact['abook_updated'] . ' - 2 days') + $last_update = ((($contact['abook_updated'] === $contact['abook_created']) || ($contact['abook_updated'] <= NULL_DATE)) + ? datetime_convert('UTC', 'UTC', 'now - 7 days') + : datetime_convert('UTC', 'UTC', $contact['abook_updated'] . ' - 2 days') ); - if($contact['xchan_network'] === 'rss') { + if ($contact['xchan_network'] === 'rss') { logger('onepoll: processing feed ' . $contact['xchan_name'], LOGGER_DEBUG); - $alive = handle_feed($importer['channel_id'],$contact_id,$contact['xchan_hash']); + $alive = handle_feed($importer['channel_id'], $contact_id, $contact['xchan_hash']); if ($alive) { q("update abook set abook_connected = '%s' where abook_id = %d", dbesc(datetime_convert()), @@ -72,22 +75,18 @@ class Onepoll { } return; } - - if(! in_array($contact['xchan_network'],['zot','zot6'])) + + if ($contact['xchan_network'] !== 'zot6') return; // update permissions - if($contact['xchan_network'] === 'zot6') - $x = Libzot::refresh($contact,$importer); - - if($contact['xchan_network'] === 'zot') - $x = zot_refresh($contact,$importer); + $x = Libzot::refresh($contact, $importer); $responded = false; $updated = datetime_convert(); $connected = datetime_convert(); - if(! $x) { + if (!$x) { // mark for death by not updating abook_connected, this is caught in include/poller.php q("update abook set abook_updated = '%s' where abook_id = %d", dbesc($updated), @@ -103,83 +102,87 @@ class Onepoll { $responded = true; } - if(! $responded) + if (!$responded) return; - if($contact['xchan_connurl']) { - $fetch_feed = true; - $x = null; + $fetch_feed = true; - // They haven't given us permission to see their stream + // They haven't given us permission to see their stream + $can_view_stream = intval(get_abconfig($importer_uid, $contact['abook_xchan'], 'their_perms', 'view_stream')); - $can_view_stream = intval(get_abconfig($importer_uid,$contact['abook_xchan'],'their_perms','view_stream')); + if (!$can_view_stream) { + $fetch_feed = false; + } - if(! $can_view_stream) - $fetch_feed = false; + // we haven't given them permission to send us their stream + $can_send_stream = intval(get_abconfig($importer_uid, $contact['abook_xchan'], 'my_perms', 'send_stream')); - // we haven't given them permission to send us their stream + if (!$can_send_stream) { + $fetch_feed = false; + } - $can_send_stream = intval(get_abconfig($importer_uid,$contact['abook_xchan'],'my_perms','send_stream')); - - if(! $can_send_stream) - $fetch_feed = false; + if ($fetch_feed) { - if($fetch_feed) { + $max = intval(get_config('system', 'max_imported_posts', 30)); - if(strpos($contact['xchan_connurl'],z_root()) === 0) { - // local channel - save a network fetch - $c = channelx_by_hash($contact['xchan_hash']); - if($c) { - $x = [ - 'success' => true, - 'body' => json_encode( [ - 'success' => true, - 'messages' => zot_feed($c['channel_id'], $importer['xchan_hash'], [ 'mindate' => $last_update ]) - ]) - ]; - } + if (intval($max)) { + $cl = Activity::get_actor_collections($contact['abook_xchan']); + if(empty($cl)) { + $cl = get_xconfig($contact['abook_xchan'], 'activitypub', 'collections'); } - else { - // remote fetch - $feedurl = str_replace('/poco/','/zotfeed/',$contact['xchan_connurl']); - $feedurl .= '?f=&mindate=' . urlencode($last_update) . '&zid=' . $importer['channel_address'] . '@' . \App::get_hostname(); - $recurse = 0; - $x = z_fetch_url($feedurl, false, $recurse, [ 'session' => true ]); + if (is_array($cl) && array_key_exists('outbox', $cl)) { + $url = $cl['outbox']; + } + else { + $url = str_replace('/poco/', '/outbox/', $contact['xchan_connurl']); } - logger('feed_update: ' . print_r($x,true), LOGGER_DATA); - } + if ($url) { + logger('fetching outbox'); + $url = $url . '?date_begin=' . urlencode($last_update); - if(($x) && ($x['success'])) { - $total = 0; - logger('onepoll: feed update ' . $contact['xchan_name'] . ' ' . $feedurl); - - $j = json_decode($x['body'],true); - if($j['success'] && $j['messages']) { - foreach($j['messages'] as $message) { - $results = process_delivery(array('hash' => $contact['xchan_hash']), get_item_elements($message), - array(array('hash' => $importer['xchan_hash'])), false); - logger('onepoll: feed_update: process_delivery: ' . print_r($results,true), LOGGER_DATA); - $total ++; + if($contact['xchan_network'] === 'zot6') { + $url = $url . '&top=1'; + } + + $obj = new ASCollection($url, $importer, 0, $max); + $messages = $obj->get(); + + if ($messages) { + foreach ($messages as $message) { + if (is_string($message)) { + $message = Activity::fetch($message, $importer); + } + + if ($contact['xchan_network'] === 'zot6') { + // make sure we only fetch top level items + if ($message['type'] === 'Create' && !isset($message['object']['inReplyTo'])) { + Libzot::fetch_conversation($importer, $message['object']['id']); + } + continue; + } + + $AS = new ActivityStreams($message); + if ($AS->is_valid() && is_array($AS->obj)) { + $item = Activity::decode_note($AS); + Activity::store($importer, $contact['abook_xchan'], $AS, $item); + } + } } - logger("onepoll: $total messages processed"); } } } - // update the poco details for this connection + $r = q("SELECT xlink_id from xlink where xlink_xchan = '%s' and xlink_updated > %s - INTERVAL %s and xlink_static = 0 limit 1", + intval($contact['xchan_hash']), + db_utcnow(), + db_quoteinterval('1 DAY') + ); - if($contact['xchan_connurl']) { - $r = q("SELECT xlink_id from xlink - where xlink_xchan = '%s' and xlink_updated > %s - INTERVAL %s and xlink_static = 0 limit 1", - intval($contact['xchan_hash']), - db_utcnow(), db_quoteinterval('1 DAY') - ); - if(! $r) { - poco_load($contact['xchan_hash'],$contact['xchan_connurl']); - } + if (!$r) { + poco_load($contact['xchan_hash'], $contact['xchan_connurl']); } return; diff --git a/Zotlabs/Daemon/Poller.php b/Zotlabs/Daemon/Poller.php index ebc0584ba..88213a7c9 100644 --- a/Zotlabs/Daemon/Poller.php +++ b/Zotlabs/Daemon/Poller.php @@ -4,201 +4,193 @@ namespace Zotlabs\Daemon; class Poller { - static public function run($argc,$argv) { + static public function run($argc, $argv) { - $maxsysload = intval(get_config('system','maxloadavg')); - if($maxsysload < 1) + $maxsysload = intval(get_config('system', 'maxloadavg')); + if ($maxsysload < 1) $maxsysload = 50; - if(function_exists('sys_getloadavg')) { + if (function_exists('sys_getloadavg')) { $load = sys_getloadavg(); - if(intval($load[0]) > $maxsysload) { + if (intval($load[0]) > $maxsysload) { logger('system: load ' . $load . ' too high. Poller deferred to next scheduled run.'); return; } } - $interval = intval(get_config('system','poll_interval')); - if(! $interval) - $interval = ((get_config('system','delivery_interval') === false) ? 3 : intval(get_config('system','delivery_interval'))); + $interval = intval(get_config('system', 'poll_interval')); + if (!$interval) + $interval = ((get_config('system', 'delivery_interval') === false) ? 3 : intval(get_config('system', 'delivery_interval'))); // Check for a lockfile. If it exists, but is over an hour old, it's stale. Ignore it. $lockfile = 'store/[data]/poller'; - if((file_exists($lockfile)) && (filemtime($lockfile) > (time() - 3600)) - && (! get_config('system','override_poll_lockfile'))) { + if ((file_exists($lockfile)) && (filemtime($lockfile) > (time() - 3600)) + && (!get_config('system', 'override_poll_lockfile'))) { logger("poller: Already running"); return; } - + // Create a lockfile. Needs two vars, but $x doesn't need to contain anything. + $x = ''; file_put_contents($lockfile, $x); logger('poller: start'); - - $manual_id = 0; - $generation = 0; - $force = false; - $restart = false; + $manual_id = 0; + $force = false; - if(($argc > 1) && ($argv[1] == 'force')) + if (($argc > 1) && ($argv[1] == 'force')) $force = true; - if(($argc > 1) && ($argv[1] == 'restart')) { - $restart = true; + if (($argc > 1) && ($argv[1] == 'restart')) { $generation = intval($argv[2]); - if(! $generation) + if (!$generation) return; } - if(($argc > 1) && intval($argv[1])) { + if (($argc > 1) && intval($argv[1])) { $manual_id = intval($argv[1]); $force = true; } - $sql_extra = (($manual_id) ? " AND abook_id = " . intval($manual_id) . " " : ""); reload_plugins(); - $d = datetime_convert(); - // Only poll from those with suitable relationships - - $abandon_sql = (($abandon_days) - ? sprintf(" AND account_lastlog > %s - INTERVAL %s ", db_utcnow(), db_quoteinterval(intval($abandon_days).' DAY')) - : '' + $abandon_days = intval(get_config('system', 'account_abandon_days', 0)); + $abandon_sql = (($abandon_days) + ? sprintf(" AND account_lastlog > %s - INTERVAL %s ", db_utcnow(), db_quoteinterval(intval($abandon_days) . ' DAY')) + : '' ); $randfunc = db_getfunc('RAND'); - + $contacts = q("SELECT abook.abook_updated, abook.abook_connected, abook.abook_feed, abook.abook_channel, abook.abook_id, abook.abook_archived, abook.abook_pending, abook.abook_ignored, abook.abook_blocked, xchan.xchan_network, - account.account_lastlog, account.account_flags - FROM abook LEFT JOIN xchan on abook_xchan = xchan_hash + account.account_lastlog, account.account_flags + FROM abook LEFT JOIN xchan on abook_xchan = xchan_hash LEFT JOIN account on abook_account = account_id where abook_self = 0 - $sql_extra + $sql_extra AND (( account_flags = %d ) OR ( account_flags = %d )) $abandon_sql ORDER BY $randfunc", intval(ACCOUNT_OK), intval(ACCOUNT_UNVERIFIED) // FIXME ); - if($contacts) { + if ($contacts) { + foreach ($contacts as $contact) { - foreach($contacts as $contact) { - - $update = false; + $update = false; $t = $contact['abook_updated']; $c = $contact['abook_connected']; - if(intval($contact['abook_feed'])) { - $min = service_class_fetch($contact['abook_channel'],'minimum_feedcheck_minutes'); - if(! $min) - $min = intval(get_config('system','minimum_feedcheck_minutes')); - if(! $min) + if (intval($contact['abook_feed'])) { + $min = service_class_fetch($contact['abook_channel'], 'minimum_feedcheck_minutes'); + if (!$min) + $min = intval(get_config('system', 'minimum_feedcheck_minutes')); + if (!$min) $min = 60; - $x = datetime_convert('UTC','UTC',"now - $min minutes"); - if($c < $x) { - Master::Summon(array('Onepoll',$contact['abook_id'])); - if($interval) - @time_sleep_until(microtime(true) + (float) $interval); + $x = datetime_convert('UTC', 'UTC', "now - $min minutes"); + if ($c < $x) { + Master::Summon(['Onepoll', $contact['abook_id']]); + if ($interval) + @time_sleep_until(microtime(true) + (float)$interval); } continue; } - - if(! in_array($contact['xchan_network'],['zot','zot6'])) + if ($contact['xchan_network'] !== 'zot6') continue; - if($c == $t) { - if(datetime_convert('UTC','UTC', 'now') > datetime_convert('UTC','UTC', $t . " + 1 day")) + if ($c == $t) { + if (datetime_convert('UTC', 'UTC', 'now') > datetime_convert('UTC', 'UTC', $t . " + 1 day")) $update = true; } else { - + // if we've never connected with them, start the mark for death countdown from now - - if($c <= NULL_DATE) { - $r = q("update abook set abook_connected = '%s' where abook_id = %d", + + if ($c <= NULL_DATE) { + q("update abook set abook_connected = '%s' where abook_id = %d", dbesc(datetime_convert()), intval($contact['abook_id']) ); - $c = datetime_convert(); + $c = datetime_convert(); $update = true; } // He's dead, Jim - if(strcmp(datetime_convert('UTC','UTC', 'now'),datetime_convert('UTC','UTC', $c . " + 30 day")) > 0) { - $r = q("update abook set abook_archived = 1 where abook_id = %d", + if (strcmp(datetime_convert('UTC', 'UTC', 'now'), datetime_convert('UTC', 'UTC', $c . " + 30 day")) > 0) { + q("update abook set abook_archived = 1 where abook_id = %d", intval($contact['abook_id']) ); - $update = false; continue; } - if(intval($contact['abook_archived'])) { - $update = false; + if (intval($contact['abook_archived'])) { continue; } // might be dead, so maybe don't poll quite so often - + // recently deceased, so keep up the regular schedule for 3 days - - if((strcmp(datetime_convert('UTC','UTC', 'now'),datetime_convert('UTC','UTC', $c . " + 3 day")) > 0) - && (strcmp(datetime_convert('UTC','UTC', 'now'),datetime_convert('UTC','UTC', $t . " + 1 day")) > 0)) + + if ((strcmp(datetime_convert('UTC', 'UTC', 'now'), datetime_convert('UTC', 'UTC', $c . " + 3 day")) > 0) + && (strcmp(datetime_convert('UTC', 'UTC', 'now'), datetime_convert('UTC', 'UTC', $t . " + 1 day")) > 0)) $update = true; // After that back off and put them on a morphine drip - if(strcmp(datetime_convert('UTC','UTC', 'now'),datetime_convert('UTC','UTC', $t . " + 2 day")) > 0) { + if (strcmp(datetime_convert('UTC', 'UTC', 'now'), datetime_convert('UTC', 'UTC', $t . " + 2 day")) > 0) { $update = true; } } - if(intval($contact['abook_pending']) || intval($contact['abook_archived']) || intval($contact['abook_ignored']) || intval($contact['abook_blocked'])) + if (intval($contact['abook_pending']) || intval($contact['abook_archived']) || intval($contact['abook_ignored']) || intval($contact['abook_blocked'])) continue; - if((! $update) && (! $force)) - continue; + if ((!$update) && (!$force)) + continue; - Master::Summon(array('Onepoll',$contact['abook_id'])); - if($interval) - @time_sleep_until(microtime(true) + (float) $interval); + Master::Summon(['Onepoll', $contact['abook_id']]); + if ($interval) + @time_sleep_until(microtime(true) + (float)$interval); } } - if($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) { + $dirmode = intval(get_config('system', 'directory_mode')); + + if ($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) { $r = q("SELECT u.ud_addr, u.ud_id, u.ud_last FROM updates AS u INNER JOIN (SELECT ud_addr, max(ud_id) AS ud_id FROM updates WHERE ( ud_flags & %d ) = 0 AND ud_addr != '' AND ( ud_last <= '%s' OR ud_last > %s - INTERVAL %s ) GROUP BY ud_addr) AS s ON s.ud_id = u.ud_id ", intval(UPDATE_FLAGS_UPDATED), dbesc(NULL_DATE), db_utcnow(), db_quoteinterval('7 DAY') ); - if($r) { - foreach($r as $rr) { + if ($r) { + foreach ($r as $rr) { // If they didn't respond when we attempted before, back off to once a day // After 7 days we won't bother anymore - if($rr['ud_last'] > NULL_DATE) - if($rr['ud_last'] > datetime_convert('UTC','UTC', 'now - 1 day')) + if ($rr['ud_last'] > NULL_DATE) + if ($rr['ud_last'] > datetime_convert('UTC', 'UTC', 'now - 1 day')) continue; - Master::Summon(array('Onedirsync',$rr['ud_id'])); - if($interval) - @time_sleep_until(microtime(true) + (float) $interval); + Master::Summon(['Onedirsync', $rr['ud_id']]); + if ($interval) + @time_sleep_until(microtime(true) + (float)$interval); } } - } + } - set_config('system','lastpoll',datetime_convert()); + set_config('system', 'lastpoll', datetime_convert()); - //All done - clear the lockfile + //All done - clear the lockfile @unlink($lockfile); diff --git a/Zotlabs/Daemon/Queue.php b/Zotlabs/Daemon/Queue.php index 814148404..41aaf45ed 100644 --- a/Zotlabs/Daemon/Queue.php +++ b/Zotlabs/Daemon/Queue.php @@ -2,17 +2,16 @@ namespace Zotlabs\Daemon; -require_once('include/queue_fn.php'); -require_once('include/zot.php'); +use Zotlabs\Lib\Queue as LibQueue; class Queue { - static public function run($argc,$argv) { + static public function run($argc, $argv) { require_once('include/items.php'); require_once('include/bbcode.php'); - if($argc > 1) + if ($argc > 1) $queue_id = $argv[1]; else $queue_id = EMPTY_STR; @@ -25,11 +24,10 @@ class Queue { $r = q("select outq_posturl from outq where outq_created < %s - INTERVAL %s", db_utcnow(), db_quoteinterval('3 DAY') ); - if($r) { - foreach($r as $rr) { - $site_url = ''; - $h = parse_url($rr['outq_posturl']); - $desturl = $h['scheme'] . '://' . $h['host'] . (($h['port']) ? ':' . $h['port'] : ''); + if ($r) { + foreach ($r as $rr) { + $h = parse_url($rr['outq_posturl']); + $desturl = $h['scheme'] . '://' . $h['host'] . (isset($h['port']) ? ':' . $h['port'] : ''); q("update site set site_dead = 1 where site_dead = 0 and site_url = '%s' and site_update < %s - INTERVAL %s", dbesc($desturl), db_utcnow(), db_quoteinterval('1 MONTH') @@ -37,11 +35,11 @@ class Queue { } } - $r = q("DELETE FROM outq WHERE outq_created < %s - INTERVAL %s", + q("DELETE FROM outq WHERE outq_created < %s - INTERVAL %s", db_utcnow(), db_quoteinterval('3 DAY') ); - if($queue_id) { + if ($queue_id) { $r = q("SELECT * FROM outq WHERE outq_hash = '%s' LIMIT 1", dbesc($queue_id) ); @@ -49,17 +47,17 @@ class Queue { else { // For the first 12 hours we'll try to deliver every 15 minutes - // After that, we'll only attempt delivery once per hour. - // This currently only handles the default queue drivers ('zot' or '') which we will group by posturl + // After that, we'll only attempt delivery once per hour. + // This currently only handles the default queue drivers ('zot' or '') which we will group by posturl // so that we don't start off a thousand deliveries for a couple of dead hubs. // The zot driver will deliver everything destined for a single hub once contact is made (*if* contact is made). // Other drivers will have to do something different here and may need their own query. - - // Note: this requires some tweaking as new posts to long dead hubs once a day will keep them in the + + // Note: this requires some tweaking as new posts to long dead hubs once a day will keep them in the // "every 15 minutes" category. We probably need to prioritise them when inserted into the queue // or just prior to this query based on recent and long-term delivery history. If we have good reason to believe - // the site is permanently down, there's no reason to attempt delivery at all, or at most not more than once - // or twice a day. + // the site is permanently down, there's no reason to attempt delivery at all, or at most not more than once + // or twice a day. $sqlrandfunc = db_getfunc('rand'); @@ -67,19 +65,19 @@ class Queue { db_utcnow() ); while ($r) { - foreach($r as $rv) { - queue_deliver($rv); + foreach ($r as $rv) { + LibQueue::deliver($rv); } $r = q("SELECT *,$sqlrandfunc as rn FROM outq WHERE outq_delivered = 0 and outq_scheduled < %s order by rn limit 1", db_utcnow() ); } } - if(! $r) + if (!$r) return; - foreach($r as $rv) { - queue_deliver($rv); + foreach ($r as $rv) { + LibQueue::deliver($rv); } } } diff --git a/Zotlabs/Daemon/Ratenotif.php b/Zotlabs/Daemon/Ratenotif.php deleted file mode 100644 index 8afde2c4c..000000000 --- a/Zotlabs/Daemon/Ratenotif.php +++ /dev/null @@ -1,126 +0,0 @@ -<?php - -namespace Zotlabs\Daemon; - -require_once('include/zot.php'); -require_once('include/queue_fn.php'); - - -class Ratenotif { - - static public function run($argc,$argv) { - - - // Deprecated - return; - - - require_once("datetime.php"); - require_once('include/items.php'); - - if($argc < 3) - return; - - - logger('ratenotif: invoked: ' . print_r($argv,true), LOGGER_DEBUG); - - $cmd = $argv[1]; - - $item_id = $argv[2]; - - - if($cmd === 'rating') { - $r = q("select * from xlink where xlink_id = %d and xlink_static = 1 limit 1", - intval($item_id) - ); - if(! $r) { - logger('rating not found'); - return; - } - - $encoded_item = array( - 'type' => 'rating', - 'encoding' => 'zot', - 'target' => $r[0]['xlink_link'], - 'rating' => intval($r[0]['xlink_rating']), - 'rating_text' => $r[0]['xlink_rating_text'], - 'signature' => $r[0]['xlink_sig'], - 'edited' => $r[0]['xlink_updated'] - ); - } - - $channel = channelx_by_hash($r[0]['xlink_xchan']); - if(! $channel) { - logger('no channel'); - return; - } - - - $primary = get_directory_primary(); - - if(! $primary) - return; - - - $interval = ((get_config('system','delivery_interval') !== false) - ? intval(get_config('system','delivery_interval')) : 2 ); - - $deliveries_per_process = intval(get_config('system','delivery_batch_count')); - - if($deliveries_per_process <= 0) - $deliveries_per_process = 1; - - $deliver = array(); - - $x = z_fetch_url($primary . '/regdir'); - if($x['success']) { - $j = json_decode($x['body'],true); - if($j && $j['success'] && is_array($j['directories'])) { - - foreach($j['directories'] as $h) { - if($h == z_root()) - continue; - - $hash = random_string(); - $n = zot_build_packet($channel,'notify',null,null,'',$hash); - - queue_insert(array( - 'hash' => $hash, - 'account_id' => $channel['channel_account_id'], - 'channel_id' => $channel['channel_id'], - 'posturl' => $h . '/post', - 'notify' => $n, - 'msg' => json_encode($encoded_item) - )); - - - $x = q("select count(outq_hash) as total from outq where outq_delivered = 0"); - if(intval($x[0]['total']) > intval(get_config('system','force_queue_threshold',300))) { - logger('immediate delivery deferred.', LOGGER_DEBUG, LOG_INFO); - update_queue_item($hash); - continue; - } - - $deliver[] = $hash; - - if(count($deliver) >= $deliveries_per_process) { - Master::Summon(array('Deliver',$deliver)); - $deliver = array(); - if($interval) - @time_sleep_until(microtime(true) + (float) $interval); - } - } - - // catch any stragglers - - if(count($deliver)) { - Master::Summon(array('Deliver',$deliver)); - } - } - } - - logger('ratenotif: complete.'); - return; - - } -} diff --git a/Zotlabs/Daemon/Thumbnail.php b/Zotlabs/Daemon/Thumbnail.php index e1f17c304..3688e8ae5 100644 --- a/Zotlabs/Daemon/Thumbnail.php +++ b/Zotlabs/Daemon/Thumbnail.php @@ -5,30 +5,30 @@ namespace Zotlabs\Daemon; class Thumbnail { - static public function run($argc,$argv) { + static public function run($argc, $argv) { - if(! $argc == 2) + if (!$argc == 2) return; $c = q("select * from attach where hash = '%s' ", dbesc($argv[1]) ); - if(! $c) + if (!$c) return; $attach = $c[0]; - $preview_style = intval(get_config('system','thumbnail_security',0)); - $preview_width = intval(get_config('system','thumbnail_width',300)); - $preview_height = intval(get_config('system','thumbnail_height',300)); + $preview_style = intval(get_config('system', 'thumbnail_security', 0)); + $preview_width = intval(get_config('system', 'thumbnail_width', 300)); + $preview_height = intval(get_config('system', 'thumbnail_height', 300)); $p = [ 'attach' => $attach, 'preview_style' => $preview_style, 'preview_width' => $preview_width, 'preview_height' => $preview_height, - 'thumbnail' => null + 'thumbnail' => null ]; /** @@ -40,39 +40,38 @@ class Thumbnail { * * \e string \b thumbnail */ - call_hooks('thumbnail',$p); - if($p['thumbnail']) { + call_hooks('thumbnail', $p); + if ($p['thumbnail']) { return; } - $default_controller = null; - + $files = glob('Zotlabs/Thumbs/*.php'); - if($files) { - foreach($files as $f) { - $clsname = '\\Zotlabs\\Thumbs\\' . ucfirst(basename($f,'.php')); - if(class_exists($clsname)) { + if ($files) { + foreach ($files as $f) { + $clsname = '\\Zotlabs\\Thumbs\\' . ucfirst(basename($f, '.php')); + if (class_exists($clsname)) { $x = new $clsname(); - if(method_exists($x,'Match')) { + if (method_exists($x, 'Match')) { $matched = $x->Match($attach['filetype']); - if($matched) { - $x->Thumb($attach,$preview_style,$preview_width,$preview_height); + if ($matched) { + $x->Thumb($attach, $preview_style, $preview_width, $preview_height); } } - if(method_exists($x,'MatchDefault')) { - $default_matched = $x->MatchDefault(substr($attach['filetype'],0,strpos($attach['filetype'],'/'))); - if($default_matched) { + if (method_exists($x, 'MatchDefault')) { + $default_matched = $x->MatchDefault(substr($attach['filetype'], 0, strpos($attach['filetype'], '/'))); + if ($default_matched) { $default_controller = $x; } } } } } - if(($default_controller) - && ((! file_exists(dbunescbin($attach['content']) . '.thumb')) + if (($default_controller) + && ((!file_exists(dbunescbin($attach['content']) . '.thumb')) || (filectime(dbunescbin($attach['content']) . 'thumb') < (time() - 60)))) { - $default_controller->Thumb($attach,$preview_style,$preview_width,$preview_height); + $default_controller->Thumb($attach, $preview_style, $preview_width, $preview_height); } } } diff --git a/Zotlabs/Lib/ASCollection.php b/Zotlabs/Lib/ASCollection.php new file mode 100644 index 000000000..392dd5d4e --- /dev/null +++ b/Zotlabs/Lib/ASCollection.php @@ -0,0 +1,150 @@ +<?php + +namespace Zotlabs\Lib; + +/** + * Class for dealing with fetching ActivityStreams collections (ordered or unordered, normal or paged). + * Construct with either an existing object or url and an optional channel to sign requests. + * $direction is 0 (default) to fetch from the beginning, and 1 to fetch from the end and reverse order the resultant array. + * An optional limit to the number of records returned may also be specified. + * Use $class->get() to return an array of collection members. + */ +class ASCollection { + + private $channel = null; + private $nextpage = null; + private $limit = 0; + private $direction = 0; // 0 = forward, 1 = reverse + private $data = []; + private $history = []; + + function __construct($obj, $channel = null, $direction = 0, $limit = 0) { + + $this->channel = $channel; + $this->direction = $direction; + $this->limit = $limit; + + if (is_array($obj)) { + $data = $obj; + } + + if (is_string($obj)) { + $data = Activity::fetch($obj, $channel); + $this->history[] = $obj; + } + + if (!is_array($data)) { + return; + } + + if (!in_array($data['type'], ['Collection', 'OrderedCollection', 'OrderedCollectionPage'])) { + return false; + } + + if ($this->direction) { + if (array_key_exists('last', $data) && $data['last']) { + $this->nextpage = $data['last']; + } + } + else { + if (array_key_exists('first', $data) && $data['first']) { + $this->nextpage = $data['first']; + } + } + + if (isset($data['items']) && is_array($data['items'])) { + $this->data = (($this->direction) ? array_reverse($data['items']) : $data['items']); + } + elseif (isset($data['orderedItems']) && is_array($data['orderedItems'])) { + $this->data = (($this->direction) ? array_reverse($data['orderedItems']) : $data['orderedItems']); + } + + if ($this->limit) { + if (count($this->data) > $limit) { + $this->data = array_slice($this->data, 0, $limit); + return; + } + } + + do { + $x = $this->next(); + } while ($x); + } + + function get() { + return $this->data; + } + + function next() { + + if (!$this->nextpage) { + return false; + } + + if (is_array($this->nextpage)) { + $data = $this->nextpage; + } + + if (is_string($this->nextpage)) { + if (in_array($this->nextpage, $this->history)) { + // recursion detected + return false; + } + $data = Activity::fetch($this->nextpage, $this->channel); + $this->history[] = $this->nextpage; + } + + if (!is_array($data)) { + return false; + } + + if (!in_array($data['type'], ['CollectionPage', 'OrderedCollectionPage'])) { + return false; + } + + $this->setnext($data); + + if (isset($data['items']) && is_array($data['items'])) { + $this->data = array_merge($this->data, (($this->direction) ? array_reverse($data['items']) : $data['items'])); + } + elseif (isset($data['orderedItems']) && is_array($data['orderedItems'])) { + $this->data = array_merge($this->data, (($this->direction) ? array_reverse($data['orderedItems']) : $data['orderedItems'])); + } + + if ($this->limit) { + if (count($this->data) > $this->limit) { + $this->data = array_slice($this->data, 0, $this->limit); + $this->nextpage = false; + return true; + } + } + + return true; + } + + function setnext($data) { + if ($this->direction) { + if (array_key_exists('prev', $data) && $data['prev']) { + $this->nextpage = $data['prev']; + } + elseif (array_key_exists('first', $data) && $data['first']) { + $this->nextpage = $data['first']; + } + else { + $this->nextpage = false; + } + } + else { + if (array_key_exists('next', $data) && $data['next']) { + $this->nextpage = $data['next']; + } + elseif (array_key_exists('last', $data) && $data['last']) { + $this->nextpage = $data['last']; + } + else { + $this->nextpage = false; + } + } + logger('nextpage: ' . $this->nextpage, LOGGER_DEBUG); + } +}
\ No newline at end of file diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index 807216400..c355aa26e 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -2,82 +2,84 @@ namespace Zotlabs\Lib; +use App; use Zotlabs\Access\PermissionLimits; +use Zotlabs\Access\PermissionRoles; +use Zotlabs\Access\Permissions; use Zotlabs\Daemon\Master; use Zotlabs\Web\HTTPSig; +use Zotlabs\Lib\XConfig; +use Zotlabs\Lib\Libzot; require_once('include/event.php'); require_once('include/html2plain.php'); +require_once('include/items.php'); class Activity { static function encode_object($x) { - - if(($x) && (! is_array($x)) && (substr(trim($x),0,1)) === '{' ) { - $x = json_decode($x,true); + if (($x) && (!is_array($x)) && (substr(trim($x), 0, 1)) === '{') { + $x = json_decode($x, true); } - if(is_array($x)) { + if (is_array($x)) { - if(array_key_exists('asld',$x)) { + if (array_key_exists('asld', $x)) { return $x['asld']; } - if($x['type'] === ACTIVITY_OBJ_PERSON) { - return self::fetch_person($x); - } - if($x['type'] === ACTIVITY_OBJ_PROFILE) { - return self::fetch_profile($x); + if ($x['type'] === ACTIVITY_OBJ_PERSON) { + return self::fetch_person($x); } - if(in_array($x['type'], [ ACTIVITY_OBJ_NOTE, ACTIVITY_OBJ_ARTICLE ] )) { - return self::fetch_item($x); + if ($x['type'] === ACTIVITY_OBJ_PROFILE) { + return self::fetch_profile($x); } - if($x['type'] === ACTIVITY_OBJ_THING) { - return self::fetch_thing($x); + if (in_array($x['type'], [ACTIVITY_OBJ_NOTE, ACTIVITY_OBJ_ARTICLE])) { + return self::fetch_item($x); } - if($x['type'] === ACTIVITY_OBJ_EVENT) { - return self::fetch_event($x); + if ($x['type'] === ACTIVITY_OBJ_THING) { + return self::fetch_thing($x); } - if($x['type'] === ACTIVITY_OBJ_PHOTO) { - return self::fetch_image($x); + if ($x['type'] === ACTIVITY_OBJ_EVENT) { + return self::fetch_event($x); } - call_hooks('encode_object',$x); + call_hooks('encode_object', $x); } return $x; } - static function fetch($url,$channel = null) { + static function fetch($url, $channel = null) { $redirects = 0; - if(! check_siteallowed($url)) { + if (!check_siteallowed($url)) { logger('blacklisted: ' . $url); return null; } - if(! $channel) { + if (!$channel) { $channel = get_sys_channel(); } logger('fetch: ' . $url, LOGGER_DEBUG); - if(strpos($url,'x-zot:') === 0) { - $x = ZotURL::fetch($url,$channel); + if (strpos($url, 'x-zot:') === 0) { + $x = ZotURL::fetch($url, $channel); } else { $m = parse_url($url); // handle bearcaps if ($m['scheme'] === 'bear') { - $params = explode('&',$m['query']); + $params = explode('&', $m['query']); if ($params) { foreach ($params as $p) { - if (substr($p,0,2) === 'u=') { - $url = substr($p,2); + if (substr($p, 0, 2) === 'u=') { + $url = substr($p, 2); } - if (substr($p,0,2) === 't=') { - $token = substr($p,2); + if (substr($p, 0, 2) === 't=') { + $token = substr($p, 2); } } $m = parse_url($url); @@ -85,21 +87,37 @@ class Activity { } $headers = [ - 'Accept' => 'application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams"', + 'Accept' => ActivityStreams::get_accept_header_string($channel), 'Host' => $m['host'], - 'Date' => datetime_convert('UTC','UTC', 'now', 'D, d M Y H:i:s \\G\\M\\T'), + 'Date' => datetime_convert('UTC', 'UTC', 'now', 'D, d M Y H:i:s \\G\\M\\T'), '(request-target)' => 'get ' . get_request_string($url) ]; + if (isset($token)) { $headers['Authorization'] = 'Bearer ' . $token; } - $h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel),false); - $x = z_fetch_url($url, true, $redirects, [ 'headers' => $h ] ); + + $h = HTTPSig::create_sig($headers, $channel['channel_prvkey'], channel_url($channel), false); + $x = z_fetch_url($url, true, $redirects, ['headers' => $h]); } - if($x['success']) { - $y = json_decode($x['body'],true); - logger('returned: ' . json_encode($y,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES), LOGGER_DEBUG); + if ($x['success']) { + $m = parse_url($url); + if ($m) { + $y = [ 'scheme' => $m['scheme'], 'host' => $m['host'] ]; + if (array_key_exists('port', $m)) + $y['port'] = $m['port']; + $site_url = unparse_url($y); + q("UPDATE site SET site_update = '%s', site_dead = 0 WHERE site_url = '%s' AND site_update < %s - INTERVAL %s", + dbesc(datetime_convert()), + dbesc($site_url), + db_utcnow(), + db_quoteinterval('1 DAY') + ); + } + + $y = json_decode($x['body'], true); + logger('returned: ' . json_encode($y, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), LOGGER_DEBUG); return json_decode($x['body'], true); } else { @@ -109,24 +127,21 @@ class Activity { return null; } - - - static function fetch_person($x) { return self::fetch_profile($x); } static function fetch_profile($x) { - $r = q("select * from xchan where xchan_url like '%s' limit 1", - dbesc($x['id'] . '/%') + $r = q("select * from xchan where xchan_url = '%s' limit 1", + dbesc($x['id']) ); - if(! $r) { + if (!$r) { $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($x['id']) ); - } - if(! $r) + } + if (!$r) return []; return self::encode_person($r[0]); @@ -140,7 +155,7 @@ class Activity { dbesc($x['id']) ); - if(! $r) + if (!$r) return []; $x = [ @@ -149,7 +164,7 @@ class Activity { 'name' => $r[0]['obj_term'] ]; - if($r[0]['obj_image']) + if ($r[0]['obj_image']) $x['image'] = $r[0]['obj_image']; return $x; @@ -158,7 +173,7 @@ class Activity { static function fetch_item($x) { - if (array_key_exists('source',$x)) { + if (array_key_exists('source', $x)) { // This item is already processed and encoded return $x; } @@ -166,8 +181,8 @@ class Activity { $r = q("select * from item where mid = '%s' limit 1", dbesc($x['id']) ); - if($r) { - xchan_query($r,true); + if ($r) { + xchan_query($r, true); $r = fetch_post_tags($r); if (in_array($r[0]['verb'], ['Create', 'Invite']) && $r[0]['obj_type'] === ACTIVITY_OBJ_EVENT) { $r[0]['verb'] = 'Invite'; @@ -177,22 +192,22 @@ class Activity { } } - static function fetch_image($x) { + $ret = [ - 'type' => 'Image', - 'id' => $x['id'], - 'name' => $x['title'], - 'content' => bbcode($x['body'], [ 'cache' => true ]), - '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'] + 'type' => 'Image', + 'id' => $x['id'], + 'name' => $x['title'], + 'content' => bbcode($x['body'], ['cache' => true]), + '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; @@ -202,42 +217,42 @@ class Activity { // convert old Zot event objects to ActivityStreams Event objects - if (array_key_exists('content',$x) && array_key_exists('dtstart',$x)) { + if (array_key_exists('content', $x) && array_key_exists('dtstart', $x)) { $ev = bbtoevent($x['content']); - if($ev) { + if ($ev) { - if (! $ev['timezone']) { + if (!$ev['timezone']) { $ev['timezone'] = 'UTC'; } $actor = null; - if(array_key_exists('author',$x) && array_key_exists('link',$x['author'])) { + if (array_key_exists('author', $x) && array_key_exists('link', $x['author'])) { $actor = $x['author']['link'][0]['href']; } - $y = [ + $y = [ 'type' => 'Event', 'id' => z_root() . '/event/' . $ev['event_hash'], 'name' => $ev['summary'], -// 'summary' => bbcode($ev['summary'], [ 'cache' => true ]), + // 'summary' => bbcode($ev['summary'], [ 'cache' => true ]), // RFC3339 Section 4.3 - 'startTime' => (($ev['adjust']) ? datetime_convert($ev['timezone'],'UTC',$ev['dtstart'], ATOM_TIME) : datetime_convert('UTC','UTC',$ev['dtstart'],'Y-m-d\\TH:i:s-00:00')), - 'content' => bbcode($ev['description'], [ 'cache' => true ]), - 'location' => [ 'type' => 'Place', 'content' => bbcode($ev['location'], [ 'cache' => true ]) ], - 'source' => [ 'content' => format_event_bbcode($ev,true), 'mediaType' => 'text/bbcode' ], + 'startTime' => (($ev['adjust']) ? datetime_convert($ev['timezone'], 'UTC', $ev['dtstart'], ATOM_TIME) : datetime_convert('UTC', 'UTC', $ev['dtstart'], 'Y-m-d\\TH:i:s-00:00')), + 'content' => bbcode($ev['description'], ['cache' => true]), + 'location' => ['type' => 'Place', 'content' => bbcode($ev['location'], ['cache' => true])], + 'source' => ['content' => format_event_bbcode($ev, true), 'mediaType' => 'text/bbcode'], 'actor' => $actor, ]; - if(! $ev['nofinish']) { - $y['endTime'] = (($ev['adjust']) ? datetime_convert($ev['timezone'],'UTC',$ev['dtend'], ATOM_TIME) : datetime_convert('UTC','UTC',$ev['dtend'],'Y-m-d\\TH:i:s-00:00')); + if (!$ev['nofinish']) { + $y['endTime'] = (($ev['adjust']) ? datetime_convert($ev['timezone'], 'UTC', $ev['dtend'], ATOM_TIME) : datetime_convert('UTC', 'UTC', $ev['dtend'], 'Y-m-d\\TH:i:s-00:00')); } - + // copy attachments from the passed object - these are already formatted for ActivityStreams - if($x['attachment']) { + if ($x['attachment']) { $y['attachment'] = $x['attachment']; } - if($actor) { + if ($actor) { return $y; } } @@ -247,52 +262,112 @@ class Activity { } - - static function encode_item_collection($items,$id,$type,$extra = null) { + static function paged_collection_init($total, $id, $type = 'OrderedCollection') { $ret = [ - 'id' => z_root() . '/' . $id, - 'type' => $type, - 'totalItems' => count($items), + 'id' => z_root() . '/' . $id, + 'type' => $type, + 'totalItems' => $total, ]; - if($extra) - $ret = array_merge($ret,$extra); - if($items) { + $numpages = $total / App::$pager['itemspage']; + $lastpage = (($numpages > intval($numpages)) ? intval($numpages) + 1 : $numpages); + + $ret['first'] = z_root() . '/' . App::$query_string . '?page=1'; + $ret['last'] = z_root() . '/' . App::$query_string . '?page=' . $lastpage; + + return $ret; + + } + + static function encode_item_collection($items, $id, $type, $total = 0) { + + if ($total > 30) { + $ret = [ + 'id' => z_root() . '/' . $id, + 'type' => $type . 'Page', + ]; + + $numpages = $total / App::$pager['itemspage']; + $lastpage = (($numpages > intval($numpages)) ? intval($numpages) + 1 : $numpages); + $url_parts = parse_url($id); + + $ret['partOf'] = z_root() . '/' . $url_parts['path']; + + $extra_query_args = ''; + $query_args = null; + if(isset($url_parts['query'])) { + parse_str($url_parts['query'], $query_args); + } + + if(is_array($query_args)) { + unset($query_args['page']); + foreach($query_args as $k => $v) + $extra_query_args .= '&' . urlencode($k) . '=' . urlencode($v); + } + + if (App::$pager['page'] < $lastpage) { + $ret['next'] = z_root() . '/' . $url_parts['path'] . '?page=' . (intval(App::$pager['page']) + 1) . $extra_query_args; + } + if (App::$pager['page'] > 1) { + $ret['prev'] = z_root() . '/' . $url_parts['path'] . '?page=' . (intval(App::$pager['page']) - 1) . $extra_query_args; + } + } + else { + $ret = [ + 'id' => z_root() . '/' . $id, + 'type' => $type, + 'totalItems' => $total, + ]; + } + + if ($items) { $x = []; - foreach($items as $i) { - $t = self::encode_activity($i); - if($t) + foreach ($items as $i) { + $m = get_iconfig($i['id'], 'activitypub', 'rawmsg'); + if ($m) { + if (is_string($m)) + $t = json_decode($m, true); + else + $t = $m; + } + else { + $t = self::encode_activity($i); + } + if ($t) { $x[] = $t; + } } - if($type === 'OrderedCollection') + if ($type === 'OrderedCollection') { $ret['orderedItems'] = $x; - else + } + else { $ret['items'] = $x; + } } return $ret; } - static function encode_follow_collection($items,$id,$type,$extra = null) { + static function encode_follow_collection($items, $id, $type, $extra = null) { $ret = [ - 'id' => z_root() . '/' . $id, - 'type' => $type, + 'id' => z_root() . '/' . $id, + 'type' => $type, 'totalItems' => count($items), ]; - if($extra) - $ret = array_merge($ret,$extra); + if ($extra) + $ret = array_merge($ret, $extra); - if($items) { + if ($items) { $x = []; - foreach($items as $i) { - if($i['xchan_url']) { + foreach ($items as $i) { + if ($i['xchan_url']) { $x[] = $i['xchan_url']; } } - if($type === 'OrderedCollection') + if ($type === 'OrderedCollection') $ret['orderedItems'] = $x; else $ret['items'] = $x; @@ -301,18 +376,17 @@ class Activity { return $ret; } - - - static function encode_item($i) { $ret = []; - if($i['verb'] === ACTIVITY_FRIEND) { + + + if ($i['verb'] === ACTIVITY_FRIEND) { // Hubzilla 'make-friend' activity, no direct mapping from AS1 to AS2 - make it a note $objtype = 'Note'; } - else { + else { $objtype = self::activity_obj_mapper($i['obj_type']); } @@ -321,13 +395,13 @@ class Activity { } if (intval($i['item_deleted'])) { - $ret['type'] = 'Tombstone'; + $ret['type'] = 'Tombstone'; $ret['formerType'] = $objtype; - $ret['id'] = $i['mid']; - if($i['id'] != $i['parent']) + $ret['id'] = $i['mid']; + if ($i['id'] != $i['parent']) $ret['inReplyTo'] = $i['thr_parent']; - $ret['to'] = [ ACTIVITY_PUBLIC_INBOX ]; + $ret['to'] = [ACTIVITY_PUBLIC_INBOX]; return $ret; } @@ -336,7 +410,7 @@ class Activity { $ret = $i['obj']; } else { - $ret = json_decode($i['obj'],true); + $ret = json_decode($i['obj'], true); } } @@ -348,96 +422,95 @@ class Activity { $ret = $i['obj']; } else { - $ret = json_decode($i['obj'],true); + $ret = json_decode($i['obj'], true); } - - if(array_path_exists('actor/id',$ret)) { + + if (array_path_exists('actor/id', $ret)) { $ret['actor'] = $ret['actor']['id']; } } } - - $ret['id'] = ((strpos($i['mid'],'http') === 0) ? $i['mid'] : z_root() . '/item/' . urlencode($i['mid'])); + $ret['id'] = ((strpos($i['mid'], 'http') === 0) ? $i['mid'] : z_root() . '/item/' . urlencode($i['mid'])); $ret['diaspora:guid'] = $i['uuid']; - if($i['title']) + if ($i['title']) $ret['name'] = $i['title']; - $ret['published'] = datetime_convert('UTC','UTC',$i['created'],ATOM_TIME); - if($i['created'] !== $i['edited']) - $ret['updated'] = datetime_convert('UTC','UTC',$i['edited'],ATOM_TIME); - if ($i['expires'] <= NULL_DATE) { - $ret['expires'] = datetime_convert('UTC','UTC',$i['expires'],ATOM_TIME); + $ret['published'] = datetime_convert('UTC', 'UTC', $i['created'], ATOM_TIME); + if ($i['created'] !== $i['edited']) + $ret['updated'] = datetime_convert('UTC', 'UTC', $i['edited'], ATOM_TIME); + if ($i['expires'] > NULL_DATE) { + $ret['expires'] = datetime_convert('UTC', 'UTC', $i['expires'], ATOM_TIME); } - if($i['app']) { - $ret['generator'] = [ 'type' => 'Application', 'name' => $i['app'] ]; + if ($i['app']) { + $ret['generator'] = ['type' => 'Application', 'name' => $i['app']]; } - if($i['location'] || $i['coord']) { - $ret['location'] = [ 'type' => 'Place' ]; - if($i['location']) { + if ($i['location'] || $i['coord']) { + $ret['location'] = ['type' => 'Place']; + if ($i['location']) { $ret['location']['name'] = $i['location']; } - if($i['coord']) { - $l = explode(' ',$i['coord']); - $ret['location']['latitude'] = $l[0]; + if ($i['coord']) { + $l = explode(' ', $i['coord']); + $ret['location']['latitude'] = $l[0]; $ret['location']['longitude'] = $l[1]; } } if (intval($i['item_wall']) && $i['mid'] === $i['parent_mid']) { - $ret['commentPolicy'] = map_scope(PermissionLimits::Get($i['uid'],'post_comments')); + $ret['commentPolicy'] = map_scope(PermissionLimits::Get($i['uid'], 'post_comments')); } if (intval($i['item_private']) === 2) { $ret['directMessage'] = true; } - if (array_key_exists('comments_closed',$i) && $i['comments_closed'] !== EMPTY_STR && $i['comments_closed'] !== NULL_DATE) { - if($ret['commentPolicy']) { + if (array_key_exists('comments_closed', $i) && $i['comments_closed'] !== EMPTY_STR && $i['comments_closed'] > NULL_DATE) { + if ($ret['commentPolicy']) { $ret['commentPolicy'] .= ' '; } - $ret['commentPolicy'] .= 'until=' . datetime_convert('UTC','UTC',$i['comments_closed'],ATOM_TIME); + $ret['commentPolicy'] .= 'until=' . datetime_convert('UTC', 'UTC', $i['comments_closed'], ATOM_TIME); } $ret['attributedTo'] = $i['author']['xchan_url']; - if($i['id'] != $i['parent']) { - $ret['inReplyTo'] = ((strpos($i['thr_parent'],'http') === 0) ? $i['thr_parent'] : z_root() . '/item/' . urlencode($i['thr_parent'])); + if ($i['id'] != $i['parent']) { + $ret['inReplyTo'] = ((strpos($i['thr_parent'], 'http') === 0) ? $i['thr_parent'] : z_root() . '/item/' . urlencode($i['thr_parent'])); } - if($i['mimetype'] === 'text/bbcode') { - if($i['title']) - $ret['name'] = bbcode($i['title'], [ 'cache' => true ]); - if($i['summary']) - $ret['summary'] = bbcode($i['summary'], [ 'cache' => true ]); - $ret['content'] = bbcode($i['body'], [ 'cache' => true ]); - $ret['source'] = [ 'content' => $i['body'], 'mediaType' => 'text/bbcode' ]; + if ($i['mimetype'] === 'text/bbcode') { + if ($i['title']) + $ret['name'] = bbcode($i['title'], ['cache' => true]); + if ($i['summary']) + $ret['summary'] = bbcode($i['summary'], ['cache' => true]); + $ret['content'] = bbcode($i['body'], ['cache' => true]); + $ret['source'] = ['content' => $i['body'], 'mediaType' => 'text/bbcode']; } - $actor = self::encode_person($i['author'],false); - if($actor) + $actor = self::encode_person($i['author'], false); + if ($actor) $ret['actor'] = $actor; else return []; $t = self::encode_taxonomy($i); - if($t) { - $ret['tag'] = $t; + if ($t) { + $ret['tag'] = $t; } $a = self::encode_attachment($i); - if($a) { + if ($a) { $ret['attachment'] = $a; } - $public = (($i['item_private']) ? false : true); + $public = (($i['item_private']) ? false : true); $top_level = (($i['mid'] === $i['parent_mid']) ? true : false); if ($public) { - $ret['to'] = [ ACTIVITY_PUBLIC_INBOX ]; - $ret['cc'] = [ z_root() . '/followers/' . substr($i['author']['xchan_addr'],0,strpos($i['author']['xchan_addr'],'@')) ]; + $ret['to'] = [ACTIVITY_PUBLIC_INBOX]; + $ret['cc'] = [z_root() . '/followers/' . substr($i['author']['xchan_addr'], 0, strpos($i['author']['xchan_addr'], '@'))]; } else { @@ -450,7 +523,7 @@ class Activity { $ret['to'] = []; if ($ret['tag']) { foreach ($ret['tag'] as $mention) { - if (is_array($mention) && array_key_exists('href',$mention) && $mention['href']) { + if (is_array($mention) && array_key_exists('href', $mention) && $mention['href']) { $h = q("select * from hubloc where hubloc_id_url = '%s' limit 1", dbesc($mention['href']) ); @@ -461,7 +534,7 @@ class Activity { else { $addr = $h[0]['hubloc_id_url']; } - if (! in_array($addr,$ret['to'])) { + if (!in_array($addr, $ret['to'])) { $ret['to'][] = $addr; } } @@ -478,7 +551,7 @@ class Activity { else { $addr = $d[0]['hubloc_id_url']; } - if (! in_array($addr,$ret['to'])) { + if (!in_array($addr, $ret['to'])) { $ret['cc'][] = $addr; } } @@ -487,7 +560,7 @@ class Activity { $mentions = self::map_mentions($i); if (count($mentions) > 0) { - if (! $ret['to']) { + if (!$ret['to']) { $ret['to'] = $mentions; } else { @@ -503,32 +576,32 @@ class Activity { $ret = []; - if ($item['tag'] && is_array($item['tag'])) { + if (array_key_exists('tag', $item) && is_array($item['tag'])) { $ptr = $item['tag']; - if (! array_key_exists(0,$ptr)) { - $ptr = [ $ptr ]; + if (!array_key_exists(0, $ptr)) { + $ptr = [$ptr]; } foreach ($ptr as $t) { - if (! array_key_exists('type',$t)) + if (!array_key_exists('type', $t)) $t['type'] = 'Hashtag'; - switch($t['type']) { - case 'Hashtag': - $ret[] = [ 'ttype' => TERM_HASHTAG, 'url' => $t['href'], 'term' => escape_tags((substr($t['name'],0,1) === '#') ? substr($t['name'],1) : $t['name']) ]; - break; + if (array_key_exists('href', $t) && array_key_exists('name', $t)) { + switch ($t['type']) { + case 'Hashtag': + $ret[] = ['ttype' => TERM_HASHTAG, 'url' => $t['href'], 'term' => escape_tags((substr($t['name'], 0, 1) === '#') ? substr($t['name'], 1) : $t['name'])]; + break; - case 'Mention': - $mention_type = substr($t['name'],0,1); - if ($mention_type === '!') { - $ret[] = [ 'ttype' => TERM_FORUM, 'url' => $t['href'], 'term' => escape_tags(substr($t['name'],1)) ]; - } - else { - $ret[] = [ 'ttype' => TERM_MENTION, 'url' => $t['href'], 'term' => escape_tags((substr($t['name'],0,1) === '@') ? substr($t['name'],1) : $t['name']) ]; - } - break; + case 'Mention': + $ret[] = ['ttype' => TERM_MENTION, 'url' => $t['href'], 'term' => escape_tags((substr($t['name'], 0, 1) === '@') ? substr($t['name'], 1) : $t['name'])]; + break; - default: - break; + case 'Bookmark': + $ret[] = ['ttype' => TERM_BOOKMARK, 'url' => $t['href'], 'term' => escape_tags($t['name'])]; + break; + + default: + break; + } } } } @@ -536,30 +609,28 @@ class Activity { return $ret; } - - static function encode_taxonomy($item) { $ret = []; - if($item['term']) { - foreach($item['term'] as $t) { - switch($t['ttype']) { + if (array_key_exists('term', $item) && is_array($item['term'])) { + foreach ($item['term'] as $t) { + switch ($t['ttype']) { case TERM_HASHTAG: // href is required so if we don't have a url in the taxonomy, ignore it and keep going. - if($t['url']) { - $ret[] = [ 'type' => 'Hashtag', 'href' => $t['url'], 'name' => '#' . $t['term'] ]; + if ($t['url']) { + $ret[] = ['type' => 'Hashtag', 'href' => $t['url'], 'name' => '#' . $t['term']]; } break; - case TERM_FORUM: - $ret[] = [ 'type' => 'Mention', 'href' => $t['url'], 'name' => '!' . $t['term'] ]; + case TERM_MENTION: + $ret[] = ['type' => 'Mention', 'href' => $t['url'], 'name' => '@' . $t['term']]; break; - case TERM_MENTION: - $ret[] = [ 'type' => 'Mention', 'href' => $t['url'], 'name' => '@' . $t['term'] ]; + case TERM_BOOKMARK: + $ret[] = ['type' => 'Bookmark', 'href' => $t['url'], 'name' => $t['term']]; break; - + default: break; } @@ -573,28 +644,28 @@ class Activity { $ret = []; - if($item['attach']) { - $atts = ((is_array($item['attach'])) ? $item['attach'] : json_decode($item['attach'],true)); - if($atts) { - foreach($atts as $att) { - if(strpos($att['type'],'image')) { - $ret[] = [ 'type' => 'Image', 'url' => $att['href'] ]; + if (array_key_exists('attach', $item)) { + $atts = ((is_array($item['attach'])) ? $item['attach'] : json_decode($item['attach'], true)); + if ($atts) { + foreach ($atts as $att) { + if (isset($att['type']) && strpos($att['type'], 'image')) { + $ret[] = ['type' => 'Image', 'url' => $att['href']]; } else { - $ret[] = [ 'type' => 'Link', 'mediaType' => $att['type'], 'href' => $att['href'] ]; + $ret[] = ['type' => 'Link', 'mediaType' => $att['type'], 'href' => $att['href']]; } } } } - if ($item['iconfig']) { + if (array_key_exists('iconfig', $item) && is_array($item['iconfig'])) { foreach ($item['iconfig'] as $att) { if ($att['sharing']) { $value = ((is_string($att['v']) && preg_match('|^a:[0-9]+:{.*}$|s', $att['v'])) ? unserialize($att['v']) : $att['v']); - $ret[] = [ 'type' => 'PropertyValue', 'name' => 'zot.' . $att['cat'] . '.' . $att['k'], 'value' => $value ]; + $ret[] = ['type' => 'PropertyValue', 'name' => 'zot.' . $att['cat'] . '.' . $att['k'], 'value' => $value]; } } } - + return $ret; } @@ -604,20 +675,20 @@ class Activity { if (is_array($item['attachment']) && $item['attachment']) { $ptr = $item['attachment']; - if (! array_key_exists(0,$ptr)) { - $ptr = [ $ptr ]; + if (!array_key_exists(0, $ptr)) { + $ptr = [$ptr]; } foreach ($ptr as $att) { $entry = []; if ($att['type'] === 'PropertyValue') { - if (array_key_exists('name',$att) && $att['name']) { - $key = explode('.',$att['name']); + if (array_key_exists('name', $att) && $att['name']) { + $key = explode('.', $att['name']); if (count($key) === 3 && $key[0] === 'zot') { - $entry['cat'] = $key[1]; - $entry['k'] = $key[2]; - $entry['v'] = $att['value']; + $entry['cat'] = $key[1]; + $entry['k'] = $key[2]; + $entry['v'] = $att['value']; $entry['sharing'] = '1'; - $ret[] = $entry; + $ret[] = $entry; } } } @@ -626,24 +697,22 @@ class Activity { return $ret; } - - static function decode_attachment($item) { $ret = []; - if($item['attachment']) { - foreach($item['attachment'] as $att) { + if (array_key_exists('attachment', $item) && is_array($item['attachment'])) { + foreach ($item['attachment'] as $att) { $entry = []; - if($att['href']) + if (array_key_exists('href', $att)) $entry['href'] = $att['href']; - elseif($att['url']) + elseif (array_key_exists('url', $att)) $entry['href'] = $att['url']; - if($att['mediaType']) + if (array_key_exists('mediaType', $att)) $entry['type'] = $att['mediaType']; - elseif($att['type'] === 'Image') + elseif (array_key_exists('type', $att) && $att['type'] === 'Image') $entry['type'] = 'image/jpeg'; - if($entry) + if ($entry) $ret[] = $entry; } } @@ -651,211 +720,188 @@ class Activity { return $ret; } - - static function encode_activity($i, $recurse = false) { $ret = []; $reply = false; - - if($i['verb'] === ACTIVITY_FRIEND) { + if ($i['verb'] === ACTIVITY_FRIEND) { // Hubzilla 'make-friend' activity, no direct mapping from AS1 to AS2 - make it a note $ret['obj'] = []; } $ret['type'] = self::activity_mapper($i['verb']); - $fragment = ''; if (intval($i['item_deleted']) && !$recurse) { $is_response = false; if (ActivityStreams::is_response_activity($ret['type'])) { $ret['type'] = 'Undo'; - $fragment = 'undo'; + $fragment = 'undo'; $is_response = true; } else { $ret['type'] = 'Delete'; - $fragment = 'delete'; + $fragment = 'delete'; } - $ret['id'] = str_replace('/item/','/activity/',$i['mid']) . '#' . $fragment; - $actor = self::encode_person($i['author'],false); + $ret['id'] = str_replace('/item/', '/activity/', $i['mid']) . '#' . $fragment; + $actor = self::encode_person($i['author'], false); if ($actor) $ret['actor'] = $actor; else - return []; + return []; - $obj = (($is_response) ? self::encode_activity($i,true) : self::encode_item($i,true)); + $obj = (($is_response) ? self::encode_activity($i, true) : self::encode_item($i)); if ($obj) { - if (array_path_exists('object/id',$obj)) { + if (array_path_exists('object/id', $obj)) { $obj['object'] = $obj['object']['id']; } - unset($obj['cc']); - $obj['to'] = [ ACTIVITY_PUBLIC_INBOX ]; + if (isset($obj['cc'])) { + unset($obj['cc']); + } + $obj['to'] = [ACTIVITY_PUBLIC_INBOX]; $ret['object'] = $obj; } else return []; - $ret['to'] = [ ACTIVITY_PUBLIC_INBOX ]; + $ret['to'] = [ACTIVITY_PUBLIC_INBOX]; return $ret; } - if($ret['type'] === 'emojiReaction') { + if ($ret['type'] === 'emojiReaction') { // There may not be an object for these items for legacy reasons - it should be the conversation parent. $p = q("select * from item where mid = '%s' and uid = %d", dbesc($i['parent_mid']), intval($i['uid']) ); - if($p) { - xchan_query($p,true); - $p = fetch_post_tags($p); + if ($p) { + xchan_query($p, true); + $p = fetch_post_tags($p); $i['obj'] = self::encode_item($p[0]); // convert to zot6 emoji reaction encoding which uses the target object to indicate the // specific emoji instead of overloading the verb or type. - - $im = explode('#',$i['verb']); - if($im && count($im) > 1) + + $im = explode('#', $i['verb']); + if ($im && count($im) > 1) $emoji = $im[1]; - if(preg_match("/\[img(.*?)\](.*?)\[\/img\]/ism", $i['body'], $match)) { + if (preg_match("/\[img(.*?)\](.*?)\[\/img\]/ism", $i['body'], $match)) { $ln = $match[2]; } $i['tgt_type'] = 'Image'; - + $i['target'] = [ 'type' => 'Image', 'name' => $emoji, 'url' => (($ln) ? $ln : z_root() . '/images/emoji/' . $emoji . '.png') ]; - + } } - if (strpos($i['mid'],z_root() . '/item/') !== false) { - $ret['id'] = str_replace('/item/','/activity/',$i['mid']); + if (strpos($i['mid'], z_root() . '/item/') !== false) { + $ret['id'] = str_replace('/item/', '/activity/', $i['mid']); } - elseif (strpos($i['mid'],z_root() . '/event/') !== false) { - $ret['id'] = str_replace('/event/','/activity/',$i['mid']); + elseif (strpos($i['mid'], z_root() . '/event/') !== false) { + $ret['id'] = str_replace('/event/', '/activity/', $i['mid']); } else { - $ret['id'] = ((strpos($i['mid'],'http') === 0) ? $i['mid'] : z_root() . '/activity/' . urlencode($i['mid'])); + $ret['id'] = ((strpos($i['mid'], 'http') === 0) ? $i['mid'] : z_root() . '/activity/' . urlencode($i['mid'])); } $ret['diaspora:guid'] = $i['uuid']; - if($i['title']) - $ret['name'] = html2plain(bbcode($i['title'], [ 'cache' => true ])); + if ($i['title']) + $ret['name'] = html2plain(bbcode($i['title'], ['cache' => true])); - if($i['summary']) - $ret['summary'] = bbcode($i['summary'], [ 'cache' => true ]); + if ($i['summary']) + $ret['summary'] = bbcode($i['summary'], ['cache' => true]); - if($ret['type'] === 'Announce') { - $tmp = preg_replace('/\[share(.*?)\[\/share\]/ism',EMPTY_STR, $i['body']); - $ret['content'] = bbcode($tmp, [ 'cache' => true ]); - $ret['source'] = [ - 'content' => $i['body'], + if ($ret['type'] === 'Announce') { + $tmp = preg_replace('/\[share(.*?)\[\/share\]/ism', EMPTY_STR, $i['body']); + $ret['content'] = bbcode($tmp, ['cache' => true]); + $ret['source'] = [ + 'content' => $i['body'], 'mediaType' => 'text/bbcode' ]; } - $ret['published'] = datetime_convert('UTC','UTC',$i['created'],ATOM_TIME); - if($i['created'] !== $i['edited']) - $ret['updated'] = datetime_convert('UTC','UTC',$i['edited'],ATOM_TIME); - if($i['app']) { - $ret['generator'] = [ 'type' => 'Application', 'name' => $i['app'] ]; + $ret['published'] = datetime_convert('UTC', 'UTC', $i['created'], ATOM_TIME); + if ($i['created'] !== $i['edited']) + $ret['updated'] = datetime_convert('UTC', 'UTC', $i['edited'], ATOM_TIME); + if ($i['app']) { + $ret['generator'] = ['type' => 'Application', 'name' => $i['app']]; } - if($i['location'] || $i['coord']) { - $ret['location'] = [ 'type' => 'Place' ]; - if($i['location']) { + if ($i['location'] || $i['coord']) { + $ret['location'] = ['type' => 'Place']; + if ($i['location']) { $ret['location']['name'] = $i['location']; } - if($i['coord']) { - $l = explode(' ',$i['coord']); - $ret['location']['latitude'] = $l[0]; + if ($i['coord']) { + $l = explode(' ', $i['coord']); + $ret['location']['latitude'] = $l[0]; $ret['location']['longitude'] = $l[1]; } } - if($i['id'] != $i['parent']) { + if ($i['id'] != $i['parent']) { $reply = true; // inReplyTo needs to be set in the activity for followup actions (Like, Dislike, Announce, etc.), // but *not* for comments and RSVPs, where it should only be present in the object - - if (! in_array($ret['type'],[ 'Create','Update','Accept','Reject','TentativeAccept','TentativeReject' ])) { - $ret['inReplyTo'] = ((strpos($i['thr_parent'],'http') === 0) ? $i['thr_parent'] : z_root() . '/item/' . urlencode($i['thr_parent'])); - } - - if($i['item_private']) { - $d = q("select xchan_url, xchan_addr, xchan_name from item left join xchan on xchan_hash = author_xchan where id = %d limit 1", - intval($i['parent']) - ); - if($d) { - $is_directmessage = false; - $recips = get_iconfig($i['parent'], 'activitypub', 'recips'); - if(array_path_exists('to', $recips) && in_array($i['author']['xchan_url'], $recips['to'])) { - $reply_url = $d[0]['xchan_url']; - $is_directmessage = true; - } - else { - $reply_url = z_root() . '/followers/' . substr($i['author']['xchan_addr'],0,strpos($i['author']['xchan_addr'],'@')); - } - - $reply_addr = (($d[0]['xchan_addr']) ? $d[0]['xchan_addr'] : $d[0]['xchan_name']); - } + if (!in_array($ret['type'], ['Create', 'Update', 'Accept', 'Reject', 'TentativeAccept', 'TentativeReject'])) { + $ret['inReplyTo'] = ((strpos($i['thr_parent'], 'http') === 0) ? $i['thr_parent'] : z_root() . '/item/' . urlencode($i['thr_parent'])); } - } - $actor = self::encode_person($i['author'],false); - if($actor) + $actor = self::encode_person($i['author'], false); + if ($actor) $ret['actor'] = $actor; else - return []; + return []; - if(strpos($i['body'],'[/share]') !== false) { + if (strpos($i['body'], '[/share]') !== false) { $i['obj'] = null; } - if($i['obj']) { - if(! is_array($i['obj'])) { - $i['obj'] = json_decode($i['obj'],true); + if ($i['obj']) { + if (!is_array($i['obj'])) { + $i['obj'] = json_decode($i['obj'], true); } - if($i['obj']['type'] === ACTIVITY_OBJ_PHOTO) { + if ($i['obj']['type'] === ACTIVITY_OBJ_PHOTO) { $i['obj']['id'] = $i['mid']; } $obj = self::encode_object($i['obj']); - if($obj) + if ($obj) $ret['object'] = $obj; else return []; } else { $obj = self::encode_item($i); - if($obj) + if ($obj) $ret['object'] = $obj; else return []; } - if(array_path_exists('object/type',$ret) && $ret['object']['type'] === 'Event' && $ret['type'] === 'Create') { + if (array_path_exists('object/type', $ret) && $ret['object']['type'] === 'Event' && $ret['type'] === 'Create') { $ret['type'] = 'Invite'; } - if($i['target']) { - if(! is_array($i['target'])) { - $i['target'] = json_decode($i['target'],true); + if ($i['target']) { + if (!is_array($i['target'])) { + $i['target'] = json_decode($i['target'], true); } $tgt = self::encode_object($i['target']); - if($tgt) + if ($tgt) $ret['target'] = $tgt; else return []; @@ -868,12 +914,12 @@ class Activity { // addressing madness - $public = (($i['item_private']) ? false : true); + $public = (($i['item_private']) ? false : true); $top_level = (($reply) ? false : true); if ($public) { - $ret['to'] = [ ACTIVITY_PUBLIC_INBOX ]; - $ret['cc'] = [ z_root() . '/followers/' . substr($i['author']['xchan_addr'],0,strpos($i['author']['xchan_addr'],'@')) ]; + $ret['to'] = [ACTIVITY_PUBLIC_INBOX]; + $ret['cc'] = [z_root() . '/followers/' . substr($i['author']['xchan_addr'], 0, strpos($i['author']['xchan_addr'], '@'))]; } else { @@ -886,7 +932,7 @@ class Activity { $ret['to'] = []; if ($ret['tag']) { foreach ($ret['tag'] as $mention) { - if (is_array($mention) && array_key_exists('href',$mention) && $mention['href']) { + if (is_array($mention) && array_key_exists('href', $mention) && $mention['href']) { $h = q("select * from hubloc where hubloc_id_url = '%s' limit 1", dbesc($mention['href']) ); @@ -897,7 +943,7 @@ class Activity { else { $addr = $h[0]['hubloc_id_url']; } - if (! in_array($addr,$ret['to'])) { + if (!in_array($addr, $ret['to'])) { $ret['to'][] = $addr; } } @@ -915,7 +961,7 @@ class Activity { else { $addr = $d[0]['hubloc_id_url']; } - if (! in_array($addr,$ret['to'])) { + if (!in_array($addr, $ret['to'])) { $ret['cc'][] = $addr; } } @@ -924,7 +970,7 @@ class Activity { $mentions = self::map_mentions($i); if (count($mentions) > 0) { - if (! $ret['to']) { + if (!$ret['to']) { $ret['to'] = $mentions; } else { @@ -936,22 +982,19 @@ class Activity { } // Returns an array of URLS for any mention tags found in the item array $i. - static function map_mentions($i) { - if (! $i['term']) { - return []; - } - $list = []; - foreach ($i['term'] as $t) { - if (! $t['url']) { - continue; - } - if ($t['ttype'] == TERM_MENTION) { - $url = self::lookup_term_url($t['url']); - $list[] = (($url) ? $url : $t['url']); + if (array_key_exists('term', $i) && is_array($i['term'])) { + foreach ($i['term'] as $t) { + if (!$t['url']) { + continue; + } + if ($t['ttype'] == TERM_MENTION) { + $url = self::lookup_term_url($t['url']); + $list[] = (($url) ? $url : $t['url']); + } } } @@ -959,11 +1002,10 @@ class Activity { } // Returns an array of all recipients targeted by private item array $i. - static function map_acl($i) { $ret = []; - if (! $i['item_private']) { + if (!$i['item_private']) { return $ret; } @@ -977,8 +1019,8 @@ class Activity { } if ($i['allow_cid']) { - $tmp = expand_acl($i['allow_cid']); - $list = stringify_array($tmp,true); + $tmp = expand_acl($i['allow_cid']); + $list = stringify_array($tmp, true); if ($list) { $details = q("select hubloc_id_url from hubloc where hubloc_hash in (" . $list . ") and hubloc_id_url != ''"); if ($details) { @@ -1013,22 +1055,22 @@ class Activity { static function encode_person($p, $extended = true) { - if(! $p['xchan_url']) + if (!$p['xchan_url']) return []; - if(! $extended) { + if (!$extended) { return $p['xchan_url']; } $ret = []; - $c = ((array_key_exists('channel_id',$p)) ? $p : channelx_by_hash($p['xchan_hash'])); + $c = ((array_key_exists('channel_id', $p)) ? $p : channelx_by_hash($p['xchan_hash'])); - $ret['type'] = 'Person'; + $ret['type'] = 'Person'; if ($c) { - $role = get_pconfig($c['channel_id'],'system','permissions_role'); - if (strpos($role,'forum') !== false) { + $role = get_pconfig($c['channel_id'], 'system', 'permissions_role'); + if (strpos($role, 'forum') !== false) { $ret['type'] = 'Group'; } } @@ -1037,33 +1079,23 @@ class Activity { $ret['id'] = channel_url($c); } else { - $ret['id'] = ((strpos($p['xchan_hash'],'http') === 0) ? $p['xchan_hash'] : $p['xchan_url']); + $ret['id'] = ((strpos($p['xchan_hash'], 'http') === 0) ? $p['xchan_hash'] : $p['xchan_url']); } - if($p['xchan_addr'] && strpos($p['xchan_addr'],'@')) - $ret['preferredUsername'] = substr($p['xchan_addr'],0,strpos($p['xchan_addr'],'@')); - $ret['name'] = $p['xchan_name']; - $ret['updated'] = datetime_convert('UTC','UTC',$p['xchan_name_date'],ATOM_TIME); - $ret['icon'] = [ + if ($p['xchan_addr'] && strpos($p['xchan_addr'], '@')) + $ret['preferredUsername'] = substr($p['xchan_addr'], 0, strpos($p['xchan_addr'], '@')); + + $ret['name'] = $p['xchan_name']; + $ret['updated'] = datetime_convert('UTC', 'UTC', $p['xchan_name_date'], ATOM_TIME); + $ret['icon'] = [ 'type' => 'Image', - 'mediaType' => (($p['xchan_photo_mimetype']) ? $p['xchan_photo_mimetype'] : 'image/png' ), - 'updated' => datetime_convert('UTC','UTC',$p['xchan_photo_date'],ATOM_TIME), + 'mediaType' => (($p['xchan_photo_mimetype']) ? $p['xchan_photo_mimetype'] : 'image/png'), + 'updated' => datetime_convert('UTC', 'UTC', $p['xchan_photo_date'], ATOM_TIME), 'url' => $p['xchan_photo_l'], 'height' => 300, 'width' => 300, ]; - $ret['url'] = [ - [ - 'type' => 'Link', - 'mediaType' => 'text/html', - 'href' => $p['xchan_url'] - ], - [ - 'type' => 'Link', - 'mediaType' => 'text/x-zot+json', - 'href' => $p['xchan_url'] - ] - ]; + $ret['url'] = $p['xchan_url']; $ret['publicKey'] = [ 'id' => $p['xchan_url'], @@ -1071,98 +1103,126 @@ class Activity { 'publicKeyPem' => $p['xchan_pubkey'] ]; - $arr = [ 'xchan' => $p, 'encoded' => $ret ]; + if ($c) { + $ret['outbox'] = z_root() . '/outbox/' . $c['channel_address']; + } + + $arr = [ + 'xchan' => $p, + 'encoded' => $ret + ]; + call_hooks('encode_person', $arr); $ret = $arr['encoded']; - return $ret; } + static function encode_item_object($item, $elm = 'obj') { + $ret = []; + if ($item[$elm]) { + if (! is_array($item[$elm])) { + $item[$elm] = json_decode($item[$elm],true); + } + if ($item[$elm]['type'] === ACTIVITY_OBJ_PHOTO) { + $item[$elm]['id'] = $item['mid']; + } + $obj = self::encode_object($item[$elm]); + if ($obj) + return $obj; + else + return []; + } + else { + $obj = self::encode_item($item); + if ($obj) + return $obj; + else + return []; + } - - + } static function activity_mapper($verb) { - if(strpos($verb,'/') === false) { + if (strpos($verb, '/') === false) { return $verb; } $acts = [ - 'http://activitystrea.ms/schema/1.0/post' => 'Create', - 'http://activitystrea.ms/schema/1.0/share' => 'Announce', - 'http://activitystrea.ms/schema/1.0/update' => 'Update', - 'http://activitystrea.ms/schema/1.0/like' => 'Like', - 'http://activitystrea.ms/schema/1.0/favorite' => 'Like', - 'http://purl.org/zot/activity/dislike' => 'Dislike', - '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', - 'Invite' => 'Invite', - 'Delete' => 'Delete', - 'Undo' => 'Undo' + 'http://activitystrea.ms/schema/1.0/post' => 'Create', + 'http://activitystrea.ms/schema/1.0/share' => 'Announce', + 'http://activitystrea.ms/schema/1.0/update' => 'Update', + 'http://activitystrea.ms/schema/1.0/like' => 'Like', + 'http://activitystrea.ms/schema/1.0/favorite' => 'Like', + 'http://purl.org/zot/activity/dislike' => 'Dislike', + '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://activitystrea.ms/schema/1.0/stop-following' => 'Unfollow', + 'http://purl.org/zot/activity/attendyes' => 'Accept', + 'http://purl.org/zot/activity/attendno' => 'Reject', + 'http://purl.org/zot/activity/attendmaybe' => 'TentativeAccept', + 'Invite' => 'Invite', + 'Delete' => 'Delete', + 'Undo' => 'Undo' ]; - call_hooks('activity_mapper',$acts); + call_hooks('activity_mapper', $acts); - if(array_key_exists($verb,$acts) && $acts[$verb]) { + if (array_key_exists($verb, $acts) && $acts[$verb]) { return $acts[$verb]; } // Reactions will just map to normal activities - if(strpos($verb,ACTIVITY_REACT) !== false) + if (strpos($verb, ACTIVITY_REACT) !== false) return 'emojiReaction'; - if(strpos($verb,ACTIVITY_MOOD) !== false) + if (strpos($verb, ACTIVITY_MOOD) !== false) return 'Create'; - if(strpos($verb,ACTIVITY_FRIEND) !== false) + if (strpos($verb, ACTIVITY_FRIEND) !== false) return 'Create'; - if(strpos($verb,ACTIVITY_POKE) !== false) + if (strpos($verb, ACTIVITY_POKE) !== false) return 'Activity'; - // We should return false, however this will trigger an uncaught execption and crash + // We should return false, however this will trigger an uncaught execption and crash // the delivery system if encountered by the JSON-LDSignature library - + logger('Unmapped activity: ' . $verb); return 'Create'; - // return false; -} - - + // return false; + } static function activity_decode_mapper($verb) { $acts = [ - 'http://activitystrea.ms/schema/1.0/post' => 'Create', - 'http://activitystrea.ms/schema/1.0/share' => 'Announce', - 'http://activitystrea.ms/schema/1.0/update' => 'Update', - 'http://activitystrea.ms/schema/1.0/like' => 'Like', - 'http://activitystrea.ms/schema/1.0/favorite' => 'Like', - 'http://purl.org/zot/activity/dislike' => 'Dislike', - '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', - 'Invite' => 'Invite', - 'Delete' => 'Delete', - 'Undo' => 'Undo' + 'http://activitystrea.ms/schema/1.0/post' => 'Create', + 'http://activitystrea.ms/schema/1.0/share' => 'Announce', + 'http://activitystrea.ms/schema/1.0/update' => 'Update', + 'http://activitystrea.ms/schema/1.0/like' => 'Like', + 'http://activitystrea.ms/schema/1.0/favorite' => 'Like', + 'http://purl.org/zot/activity/dislike' => 'Dislike', + '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://activitystrea.ms/schema/1.0/stop-following' => 'Unfollow', + 'http://purl.org/zot/activity/attendyes' => 'Accept', + 'http://purl.org/zot/activity/attendno' => 'Reject', + 'http://purl.org/zot/activity/attendmaybe' => 'TentativeAccept', + 'Invite' => 'Invite', + 'Delete' => 'Delete', + 'Undo' => 'Undo' ]; - call_hooks('activity_decode_mapper',$acts); + call_hooks('activity_decode_mapper', $acts); - foreach($acts as $k => $v) { - if($verb === $v) { + foreach ($acts as $k => $v) { + if ($verb === $v) { return $k; } } @@ -1175,33 +1235,33 @@ class Activity { static function activity_obj_decode_mapper($obj) { $objs = [ - 'http://activitystrea.ms/schema/1.0/note' => 'Note', - 'http://activitystrea.ms/schema/1.0/note' => 'Article', - 'http://activitystrea.ms/schema/1.0/comment' => 'Note', - 'http://activitystrea.ms/schema/1.0/person' => 'Person', - 'http://purl.org/zot/activity/profile' => 'Profile', - 'http://activitystrea.ms/schema/1.0/photo' => 'Image', - 'http://activitystrea.ms/schema/1.0/profile-photo' => 'Icon', - 'http://activitystrea.ms/schema/1.0/event' => 'Event', - 'http://purl.org/zot/activity/location' => 'Place', - 'http://purl.org/zot/activity/chessgame' => 'Game', - 'http://purl.org/zot/activity/tagterm' => 'zot:Tag', - 'http://purl.org/zot/activity/thing' => 'Object', - 'http://purl.org/zot/activity/file' => 'zot:File', - 'http://purl.org/zot/activity/mood' => 'zot:Mood', - 'Invite' => 'Invite', - 'Question' => 'Question', - 'Document' => 'Document', - 'Audio' => 'Audio', - 'Video' => 'Video', - 'Delete' => 'Delete', - 'Undo' => 'Undo' + 'http://activitystrea.ms/schema/1.0/note' => 'Note', + 'http://activitystrea.ms/schema/1.0/note' => 'Article', + 'http://activitystrea.ms/schema/1.0/comment' => 'Note', + 'http://activitystrea.ms/schema/1.0/person' => 'Person', + 'http://purl.org/zot/activity/profile' => 'Profile', + 'http://activitystrea.ms/schema/1.0/photo' => 'Image', + 'http://activitystrea.ms/schema/1.0/profile-photo' => 'Icon', + 'http://activitystrea.ms/schema/1.0/event' => 'Event', + 'http://purl.org/zot/activity/location' => 'Place', + 'http://purl.org/zot/activity/chessgame' => 'Game', + 'http://purl.org/zot/activity/tagterm' => 'zot:Tag', + 'http://purl.org/zot/activity/thing' => 'Object', + 'http://purl.org/zot/activity/file' => 'zot:File', + 'http://purl.org/zot/activity/mood' => 'zot:Mood', + 'Invite' => 'Invite', + 'Question' => 'Question', + 'Document' => 'Document', + 'Audio' => 'Audio', + 'Video' => 'Video', + 'Delete' => 'Delete', + 'Undo' => 'Undo' ]; - call_hooks('activity_obj_decode_mapper',$objs); + call_hooks('activity_obj_decode_mapper', $objs); - foreach($objs as $k => $v) { - if($obj === $v) { + foreach ($objs as $k => $v) { + if ($obj === $v) { return $k; } } @@ -1210,45 +1270,42 @@ class Activity { return 'Note'; } - - - static function activity_obj_mapper($obj) { $objs = [ - 'http://activitystrea.ms/schema/1.0/note' => 'Note', - 'http://activitystrea.ms/schema/1.0/comment' => 'Note', - 'http://activitystrea.ms/schema/1.0/person' => 'Person', - 'http://purl.org/zot/activity/profile' => 'Profile', - 'http://activitystrea.ms/schema/1.0/photo' => 'Image', - 'http://activitystrea.ms/schema/1.0/profile-photo' => 'Icon', - 'http://activitystrea.ms/schema/1.0/event' => 'Event', - 'http://purl.org/zot/activity/location' => 'Place', - 'http://purl.org/zot/activity/chessgame' => 'Game', - 'http://purl.org/zot/activity/tagterm' => 'zot:Tag', - 'http://purl.org/zot/activity/thing' => 'Object', - 'http://purl.org/zot/activity/file' => 'zot:File', - 'http://purl.org/zot/activity/mood' => 'zot:Mood', - 'Invite' => 'Invite', - 'Question' => 'Question', - 'Audio' => 'Audio', - 'Video' => 'Video', - 'Delete' => 'Delete', - 'Undo' => 'Undo' + 'http://activitystrea.ms/schema/1.0/note' => 'Note', + 'http://activitystrea.ms/schema/1.0/comment' => 'Note', + 'http://activitystrea.ms/schema/1.0/person' => 'Person', + 'http://purl.org/zot/activity/profile' => 'Profile', + 'http://activitystrea.ms/schema/1.0/photo' => 'Image', + 'http://activitystrea.ms/schema/1.0/profile-photo' => 'Icon', + 'http://activitystrea.ms/schema/1.0/event' => 'Event', + 'http://purl.org/zot/activity/location' => 'Place', + 'http://purl.org/zot/activity/chessgame' => 'Game', + 'http://purl.org/zot/activity/tagterm' => 'zot:Tag', + 'http://purl.org/zot/activity/thing' => 'Object', + 'http://purl.org/zot/activity/file' => 'zot:File', + 'http://purl.org/zot/activity/mood' => 'zot:Mood', + 'Invite' => 'Invite', + 'Question' => 'Question', + 'Audio' => 'Audio', + 'Video' => 'Video', + 'Delete' => 'Delete', + 'Undo' => 'Undo' ]; - call_hooks('activity_obj_mapper',$objs); + call_hooks('activity_obj_mapper', $objs); if ($obj === 'Answer') { return 'Note'; } - if (strpos($obj,'/') === false) { + if (strpos($obj, '/') === false) { return $obj; } - if(array_key_exists($obj,$objs)) { + if (array_key_exists($obj, $objs)) { return $objs[$obj]; } @@ -1259,108 +1316,103 @@ class Activity { } + static function follow($channel, $act) { - static function follow($channel,$act) { - - $contact = null; + $contact = null; $their_follow_id = null; /* - * - * if $act->type === 'Follow', actor is now following $channel - * if $act->type === 'Accept', actor has approved a follow request from $channel - * + * + * if $act->type === 'Follow', actor is now following $channel + * if $act->type === 'Accept', actor has approved a follow request from $channel + * */ - $person_obj = $act->actor; - - if($act->type === 'Follow') { - $their_follow_id = $act->id; - } - elseif($act->type === 'Accept') { - $my_follow_id = z_root() . '/follow/' . $contact['id']; + if (in_array($act->type, [ 'Follow', 'Invite', 'Join'])) { + $their_follow_id = $act->id; } - - if(is_array($person_obj)) { + + $person_obj = (($act->type == 'Invite') ? $act->obj : $act->actor); + + if (is_array($person_obj)) { // store their xchan and hubloc - self::actor_store($person_obj['id'],$person_obj); + self::actor_store($person_obj['id'], $person_obj); - // Find any existing abook record + // Find any existing abook record $r = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_xchan = '%s' and abook_channel = %d limit 1", dbesc($person_obj['id']), intval($channel['channel_id']) ); - if($r) { + if ($r) { $contact = $r[0]; } } $x = \Zotlabs\Access\PermissionRoles::role_perms('social'); - $p = \Zotlabs\Access\Permissions::FilledPerms($x['perms_connect']); - $their_perms = \Zotlabs\Access\Permissions::serialise($p); + $their_perms = \Zotlabs\Access\Permissions::FilledPerms($x['perms_connect']); - if($contact && $contact['abook_id']) { + if ($contact && $contact['abook_id']) { - // A relationship of some form already exists on this site. + // A relationship of some form already exists on this site. - switch($act->type) { + switch ($act->type) { case 'Follow': + case 'Invite': + case 'Join': // A second Follow request, but we haven't approved the first one - - if($contact['abook_pending']) { + if ($contact['abook_pending']) { return; } // We've already approved them or followed them first // Send an Accept back to them - - set_abconfig($channel['channel_id'],$person_obj['id'],'pubcrawl','their_follow_id', $their_follow_id); - Master::Summon([ 'Notifier', 'permissions_accept', $contact['abook_id'] ]); + set_abconfig($channel['channel_id'], $person_obj['id'], 'pubcrawl', 'their_follow_id', $their_follow_id); + Master::Summon(['Notifier', 'permission_accept', $contact['abook_id']]); return; case 'Accept': // They accepted our Follow request - set default permissions - - set_abconfig($channel['channel_id'],$contact['abook_xchan'],'system','their_perms',$their_perms); + + set_abconfig($channel['channel_id'], $contact['abook_xchan'], 'system', 'their_perms', $their_perms); $abook_instance = $contact['abook_instance']; - - if(strpos($abook_instance,z_root()) === false) { - if($abook_instance) + + if (strpos($abook_instance, z_root()) === false) { + if ($abook_instance) $abook_instance .= ','; $abook_instance .= z_root(); - $r = q("update abook set abook_instance = '%s', abook_not_here = 0 + q("update abook set abook_instance = '%s', abook_not_here = 0 where abook_id = %d and abook_channel = %d", dbesc($abook_instance), intval($contact['abook_id']), intval($channel['channel_id']) ); } - + return; default: return; - + } } // No previous relationship exists. - if($act->type === 'Accept') { + if ($act->type === 'Accept') { // This should not happen unless we deleted the connection before it was accepted. return; } // From here on out we assume a Follow activity to somebody we have no existing relationship with - set_abconfig($channel['channel_id'],$person_obj['id'],'pubcrawl','their_follow_id', $their_follow_id); + set_abconfig($channel['channel_id'], $person_obj['id'], 'pubcrawl', 'their_follow_id', $their_follow_id); // The xchan should have been created by actor_store() above @@ -1368,17 +1420,17 @@ class Activity { dbesc($person_obj['id']) ); - if(! $r) { + if (!$r) { logger('xchan not found for ' . $person_obj['id']); return; } $ret = $r[0]; $p = \Zotlabs\Access\Permissions::connect_perms($channel['channel_id']); - $my_perms = \Zotlabs\Access\Permissions::serialise($p['perms']); + $my_perms = $p['perms']; $automatic = $p['automatic']; - $closeness = get_pconfig($channel['channel_id'],'system','new_abook_closeness',80); + $closeness = get_pconfig($channel['channel_id'], 'system', 'new_abook_closeness', 80); $r = abook_store_lowlevel( [ @@ -1394,64 +1446,65 @@ class Activity { 'abook_instance' => z_root() ] ); - + if($my_perms) - set_abconfig($channel['channel_id'],$ret['xchan_hash'],'system','my_perms',$my_perms); + foreach($my_perms as $k => $v) + set_abconfig($channel['channel_id'],$ret['xchan_hash'],'my_perms',$k,$v); if($their_perms) - set_abconfig($channel['channel_id'],$ret['xchan_hash'],'system','their_perms',$their_perms); + foreach($their_perms as $k => $v) + set_abconfig($channel['channel_id'],$ret['xchan_hash'],'their_perms',$k,$v); - - if($r) { + if ($r) { logger("New ActivityPub follower for {$channel['channel_name']}"); $new_connection = q("select * from abook left join xchan on abook_xchan = xchan_hash left join hubloc on hubloc_hash = xchan_hash where abook_channel = %d and abook_xchan = '%s' order by abook_created desc limit 1", intval($channel['channel_id']), dbesc($ret['xchan_hash']) ); - if($new_connection) { - \Zotlabs\Lib\Enotify::submit( + if ($new_connection) { + Enotify::submit( [ - 'type' => NOTIFY_INTRO, - 'from_xchan' => $ret['xchan_hash'], - 'to_xchan' => $channel['channel_hash'], - 'link' => z_root() . '/connedit/' . $new_connection[0]['abook_id'], + 'type' => NOTIFY_INTRO, + 'from_xchan' => $ret['xchan_hash'], + 'to_xchan' => $channel['channel_hash'], + 'link' => z_root() . '/connedit/' . $new_connection[0]['abook_id'], ] ); - if($my_perms && $automatic) { + if ($my_perms && $automatic) { // send an Accept for this Follow activity - Master::Summon([ 'Notifier', 'permissions_accept', $new_connection[0]['abook_id'] ]); + Master::Summon(['Notifier', 'permission_accept', $new_connection[0]['abook_id']]); // Send back a Follow notification to them - Master::Summon([ 'Notifier', 'permissions_create', $new_connection[0]['abook_id'] ]); + Master::Summon(['Notifier', 'permission_create', $new_connection[0]['abook_id']]); } - $clone = array(); - foreach($new_connection[0] as $k => $v) { - if(strpos($k,'abook_') === 0) { + $clone = []; + foreach ($new_connection[0] as $k => $v) { + if (strpos($k, 'abook_') === 0) { $clone[$k] = $v; } } unset($clone['abook_id']); unset($clone['abook_account']); unset($clone['abook_channel']); - - $abconfig = load_abconfig($channel['channel_id'],$clone['abook_xchan']); - if($abconfig) + $abconfig = load_abconfig($channel['channel_id'], $clone['abook_xchan']); + + if ($abconfig) $clone['abconfig'] = $abconfig; - Libsync::build_sync_packet($channel['channel_id'], [ 'abook' => array($clone) ] ); + Libsync::build_sync_packet($channel['channel_id'], ['abook' => [$clone]]); } } /* If there is a default group for this channel and permissions are automatic, add this member to it */ - if($channel['channel_default_group'] && $automatic) { - $g = Group::rec_byhash($channel['channel_id'],$channel['channel_default_group']); - if($g) - Group::member_add($channel['channel_id'],'',$ret['xchan_hash'],$g['id']); + if ($channel['channel_default_group'] && $automatic) { + $g = Group::rec_byhash($channel['channel_id'], $channel['channel_default_group']); + if ($g) + Group::member_add($channel['channel_id'], '', $ret['xchan_hash'], $g['id']); } @@ -1459,8 +1512,7 @@ class Activity { } - - static function unfollow($channel,$act) { + static function unfollow($channel, $act) { $contact = null; @@ -1470,60 +1522,115 @@ class Activity { $person_obj = $act->actor; - if(is_array($person_obj)) { + if (is_array($person_obj)) { $r = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_xchan = '%s' and abook_channel = %d limit 1", dbesc($person_obj['id']), intval($channel['channel_id']) ); - if($r) { + if ($r) { // remove all permissions they provided - del_abconfig($channel['channel_id'],$r[0]['xchan_hash'],'system','their_perms',EMPTY_STR); + del_abconfig($channel['channel_id'], $r[0]['xchan_hash'], 'system', 'their_perms'); } } return; } + static function actor_store($url, $person_obj, $force = false) { + + if (!is_array($person_obj)) { + return; + } + +/* not implemented + if (array_key_exists('movedTo',$person_obj) && $person_obj['movedTo'] && ! is_array($person_obj['movedTo'])) { + $tgt = self::fetch($person_obj['movedTo']); + if (is_array($tgt)) { + self::actor_store($person_obj['movedTo'],$tgt); + ActivityPub::move($person_obj['id'],$tgt); + } + return; + } +*/ + $ap_hubloc = null; + + $hublocs = self::get_actor_hublocs($url); + if ($hublocs) { + foreach ($hublocs as $hub) { + if ($hub['hubloc_network'] === 'activitypub') { + $ap_hubloc = $hub; + } + if ($hub['hubloc_network'] === 'zot6') { + Libzot::update_cached_hubloc($hub); + } + } + } + if ($ap_hubloc) { + // we already have a stored record. Determine if it needs updating. + if ($ap_hubloc['hubloc_updated'] < datetime_convert('UTC','UTC',' now - 3 days') || $force) { + $person_obj = self::fetch($url); + } + else { + return; + } + } - static function actor_store($url,$person_obj) { + if (isset($person_obj['id'])) { + $url = $person_obj['id']; + } - if(! is_array($person_obj)) + if (! $url) { return; + } $inbox = $person_obj['inbox']; // invalid identity - if (! $inbox || strpos($inbox,z_root()) !== false) { + if (!$inbox || strpos($inbox, z_root()) !== false) { return; } + // store the actor record in XConfig + XConfig::Set($url, 'system', 'actor_record', $person_obj); + $name = $person_obj['name']; - if(! $name) + if (!$name) { $name = $person_obj['preferredUsername']; - if(! $name) + } + if (!$name) { $name = t('Unknown'); + } - if($person_obj['icon']) { - if(is_array($person_obj['icon'])) { - if(array_key_exists('url',$person_obj['icon'])) + $icon = z_root() . '/' . get_default_profile_photo(300); + if ($person_obj['icon']) { + if (is_array($person_obj['icon'])) { + if (array_key_exists('url', $person_obj['icon'])) { $icon = $person_obj['icon']['url']; - else - $icon = $person_obj['icon'][0]['url']; + } + else { + if (is_string($person_obj['icon'][0])) { + $icon = $person_obj['icon'][0]; + } + elseif (array_key_exists('url', $person_obj['icon'][0])) { + $icon = $person_obj['icon'][0]['url']; + } + } } - else + else { $icon = $person_obj['icon']; + } } - $links = false; + $links = false; $profile = false; if (is_array($person_obj['url'])) { - if (! array_key_exists(0,$person_obj['url'])) { - $links = [ $person_obj['url'] ]; + if (!array_key_exists(0, $person_obj['url'])) { + $links = [$person_obj['url']]; } else { $links = $person_obj['url']; @@ -1532,11 +1639,11 @@ class Activity { if ($links) { foreach ($links as $link) { - if (array_key_exists('mediaType',$link) && $link['mediaType'] === 'text/html') { + if (array_key_exists('mediaType', $link) && $link['mediaType'] === 'text/html') { $profile = $link['href']; } } - if (! $profile) { + if (!$profile) { $profile = $links[0]['href']; } } @@ -1544,109 +1651,98 @@ class Activity { $profile = $person_obj['url']; } - if (! $profile) { + if (!$profile) { $profile = $url; } - $collections = []; - - if($inbox) { - $collections['inbox'] = $inbox; - if($person_obj['outbox']) - $collections['outbox'] = $person_obj['outbox']; - if($person_obj['followers']) - $collections['followers'] = $person_obj['followers']; - if($person_obj['following']) - $collections['following'] = $person_obj['following']; - if($person_obj['endpoints'] && $person_obj['endpoints']['sharedInbox']) - $collections['sharedInbox'] = $person_obj['endpoints']['sharedInbox']; - } - - if(array_key_exists('publicKey',$person_obj) && array_key_exists('publicKeyPem',$person_obj['publicKey'])) { - if($person_obj['id'] === $person_obj['publicKey']['owner']) { + if (array_key_exists('publicKey', $person_obj) && array_key_exists('publicKeyPem', $person_obj['publicKey'])) { + if ($person_obj['id'] === $person_obj['publicKey']['owner']) { $pubkey = $person_obj['publicKey']['publicKeyPem']; - if(strstr($pubkey,'RSA ')) { - $pubkey = rsatopem($pubkey); + if (strstr($pubkey, 'RSA ')) { + $pubkey = Keyutils::rsaToPem($pubkey); } } } - $r = q("select * from xchan where xchan_hash = '%s' limit 1", + $m = parse_url($url); + if($m) { + $hostname = $m['host']; + $baseurl = $m['scheme'] . '://' . $m['host'] . (($m['port']) ? ':' . $m['port'] : ''); + $site_url = $m['scheme'] . '://' . $m['host']; + } + + $r = q("select * from xchan join hubloc on xchan_hash = hubloc_hash where xchan_hash = '%s'", dbesc($url) ); - if(! $r) { - // create a new record - - $r = xchan_store_lowlevel( - [ - 'xchan_hash' => $url, - 'xchan_guid' => $url, - 'xchan_pubkey' => $pubkey, - 'xchan_addr' => '', - 'xchan_url' => $profile, - 'xchan_name' => $name, - 'xchan_name_date' => datetime_convert(), - 'xchan_network' => 'activitypub' - ] - ); - } - else { + if($r) { // Record exists. Cache existing records for one week at most - // then refetch to catch updated profile photos, names, etc. - - $d = datetime_convert('UTC','UTC','now - 1 week'); - if($r[0]['xchan_name_date'] > $d) + // then refetch to catch updated profile photos, names, etc. + $d = datetime_convert('UTC', 'UTC', 'now - 3 days'); + if($r[0]['hubloc_updated'] > $d && !$force) { return; + } - // update existing record - $r = q("update xchan set xchan_name = '%s', xchan_pubkey = '%s', xchan_network = '%s', xchan_name_date = '%s' where xchan_hash = '%s'", - dbesc($name), - dbesc($pubkey), - dbesc('activitypub'), + q("UPDATE site SET site_update = '%s', site_dead = 0 WHERE site_url = '%s'", dbesc(datetime_convert()), + dbesc($site_url) + ); + + // update existing xchan record + q("update xchan set xchan_name = '%s', xchan_guid = '%s', xchan_pubkey = '%s', xchan_network = 'activitypub', xchan_name_date = '%s' where xchan_hash = '%s'", + dbesc(escape_tags($name)), + dbesc(escape_tags($url)), + dbesc(escape_tags($pubkey)), + dbescdate(datetime_convert()), dbesc($url) ); - } - if($collections) { - set_xconfig($url,'activitypub','collections',$collections); + // update existing hubloc record + q("update hubloc set hubloc_guid = '%s', hubloc_network = 'activitypub', hubloc_url = '%s', hubloc_host = '%s', hubloc_callback = '%s', hubloc_updated = '%s', hubloc_id_url = '%s' where hubloc_hash = '%s'", + dbesc(escape_tags($url)), + dbesc(escape_tags($baseurl)), + dbesc(escape_tags($hostname)), + dbesc(escape_tags($inbox)), + dbescdate(datetime_convert()), + dbesc(escape_tags($profile)), + dbesc($url) + ); } + else { + // create a new record - $r = q("select * from hubloc where hubloc_hash = '%s' limit 1", - dbesc($url) - ); - - - $m = parse_url($url); - if($m) { - $hostname = $m['host']; - $baseurl = $m['scheme'] . '://' . $m['host'] . (($m['port']) ? ':' . $m['port'] : ''); - } + xchan_store_lowlevel( + [ + 'xchan_hash' => escape_tags($url), + 'xchan_guid' => escape_tags($url), + 'xchan_pubkey' => escape_tags($pubkey), + 'xchan_addr' => '', + 'xchan_url' => escape_tags($profile), + 'xchan_name' => escape_tags($name), + 'xchan_name_date' => datetime_convert(), + 'xchan_network' => 'activitypub' + ] + ); - if(! $r) { - $r = hubloc_store_lowlevel( + hubloc_store_lowlevel( [ - 'hubloc_guid' => $url, - 'hubloc_hash' => $url, + 'hubloc_guid' => escape_tags($url), + 'hubloc_hash' => escape_tags($url), 'hubloc_addr' => '', 'hubloc_network' => 'activitypub', - 'hubloc_url' => $baseurl, - 'hubloc_host' => $hostname, - 'hubloc_callback' => $inbox, + 'hubloc_url' => escape_tags($baseurl), + 'hubloc_host' => escape_tags($hostname), + 'hubloc_callback' => escape_tags($inbox), 'hubloc_updated' => datetime_convert(), 'hubloc_primary' => 1, - 'hubloc_id_url' => $profile + 'hubloc_id_url' => escape_tags($profile) ] ); } - if(! $icon) - $icon = z_root() . '/' . get_default_profile_photo(300); - - $photos = import_xchan_photo($icon,$url); - $r = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'", - dbescdate(datetime_convert('UTC','UTC',$photos[5])), + $photos = import_xchan_photo($icon, $url); + q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'", + dbescdate(datetime_convert('UTC', 'UTC', $photos[5])), dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), @@ -1656,54 +1752,46 @@ class Activity { } + static function create_action($channel, $observer_hash, $act) { - static function create_action($channel,$observer_hash,$act) { - - if(in_array($act->obj['type'], [ 'Note', 'Article', 'Video' ])) { - self::create_note($channel,$observer_hash,$act); + if (in_array($act->obj['type'], ['Note', 'Article', 'Video'])) { + self::create_note($channel, $observer_hash, $act); } } - static function announce_action($channel,$observer_hash,$act) { + static function announce_action($channel, $observer_hash, $act) { - if(in_array($act->type, [ 'Announce' ])) { - self::announce_note($channel,$observer_hash,$act); + if (in_array($act->type, ['Announce'])) { + self::announce_note($channel, $observer_hash, $act); } } + static function like_action($channel, $observer_hash, $act) { - static function like_action($channel,$observer_hash,$act) { - - if(in_array($act->obj['type'], [ 'Note', 'Article', 'Video' ])) { - self::like_note($channel,$observer_hash,$act); + if (in_array($act->obj['type'], ['Note', 'Article', 'Video'])) { + self::like_note($channel, $observer_hash, $act); } } // sort function width decreasing - - static function vid_sort($a,$b) { - if($a['width'] === $b['width']) + static function vid_sort($a, $b) { + if ($a['width'] === $b['width']) return 0; return (($a['width'] > $b['width']) ? -1 : 1); } - static function create_note($channel,$observer_hash,$act) { + 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']) : ''); - $parent = ((array_key_exists('inReplyTo',$act->obj)) ? urldecode($act->obj['inReplyTo']) : ''); - if($parent) { + if ($parent) { $r = q("select * from item where uid = %d and ( mid = '%s' or mid = '%s' ) limit 1", intval($channel['channel_id']), @@ -1711,39 +1799,54 @@ class Activity { dbesc(basename($parent)) ); - if(! $r) { + 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)) { + if ($r[0]['owner_xchan'] === $channel['channel_hash']) { + if (!perm_is_allowed($channel['channel_id'], $observer_hash, 'send_stream') && !$is_sys_channel) { logger('no comment permission.'); return; } } - $s['parent_mid'] = $r[0]['mid']; - $s['owner_xchan'] = $r[0]['owner_xchan']; + $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'); + if (!perm_is_allowed($channel['channel_id'], $observer_hash, 'send_stream') && !$is_sys_channel) { + logger('no send_stream permission'); return; } $s['owner_xchan'] = $s['author_xchan'] = $observer_hash; } - + + if ($act->recips && (!in_array(ACTIVITY_PUBLIC_INBOX, $act->recips))) + $s['item_private'] = 1; + + + if (array_key_exists('directMessage', $act->obj) && intval($act->obj['directMessage'])) { + $s['item_private'] = 2; + } + + if (intval($s['item_private']) === 2) { + if (!perm_is_allowed($channel['channel_id'], $observer_hash, 'post_mail')) { + logger('no post_mail permission'); + return; + } + } + $abook = q("select * from abook where abook_xchan = '%s' and abook_channel = %d limit 1", dbesc($observer_hash), intval($channel['channel_id']) ); - + $content = self::get_content($act->obj); - if(! $content) { + if (!$content) { logger('no content'); return; } @@ -1756,105 +1859,105 @@ class Activity { $s['author_xchan'] = self::find_best_identity($s['author_xchan']); $s['owner_xchan'] = self::find_best_identity($s['owner_xchan']); - if(!$s['author_xchan']) { + if (!$s['author_xchan']) { logger('No author: ' . print_r($act, true)); } - if(!$s['owner_xchan']) { + if (!$s['owner_xchan']) { logger('No owner: ' . print_r($act, true)); } - if(!$s['author_xchan'] || !$s['owner_xchan']) + if (!$s['author_xchan'] || !$s['owner_xchan']) return; - $s['mid'] = urldecode($act->obj['id']); - $s['uuid'] = $act->obj['diaspora:guid']; + $s['mid'] = urldecode($act->obj['id']); + $s['uuid'] = $act->obj['diaspora:guid']; $s['plink'] = urldecode($act->obj['id']); - if($act->data['published']) { - $s['created'] = datetime_convert('UTC','UTC',$act->data['published']); + 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']); + 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']); + 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']); + elseif ($act->obj['updated']) { + $s['edited'] = datetime_convert('UTC', 'UTC', $act->obj['updated']); } if ($act->data['expires']) { - $s['expires'] = datetime_convert('UTC','UTC',$act->data['expires']); + $s['expires'] = datetime_convert('UTC', 'UTC', $act->data['expires']); } elseif ($act->obj['expires']) { - $s['expires'] = datetime_convert('UTC','UTC',$act->obj['expires']); + $s['expires'] = datetime_convert('UTC', 'UTC', $act->obj['expires']); } - if(! $s['created']) + if (!$s['created']) $s['created'] = datetime_convert(); - if(! $s['edited']) + if (!$s['edited']) $s['edited'] = $s['created']; - if(! $s['parent_mid']) + 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['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) + $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)) { + 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(! \Zotlabs\Lib\MessageFilter::evaluate($s,get_config('system','pubstream_incl'),get_config('system','pubstream_excl'))) { + if ($channel['channel_system']) { + if (!MessageFilter::evaluate($s, get_config('system', 'pubstream_incl'), get_config('system', 'pubstream_excl'))) { logger('post is filtered'); return; } } - if($abook) { - if(! post_is_importable($s,$abook[0])) { + if ($abook) { + if (!post_is_importable($s, $abook[0])) { logger('post is filtered'); return; } } - if($act->obj['conversation']) { - set_iconfig($s,'ostatus','conversation',$act->obj['conversation'],1); + if ($act->obj['conversation']) { + set_iconfig($s, 'ostatus', 'conversation', $act->obj['conversation'], 1); } $a = self::decode_taxonomy($act->obj); - if($a) { + if ($a) { $s['term'] = $a; } $a = self::decode_attachment($act->obj); - if($a) { + if ($a) { $s['attach'] = $a; } - if($act->obj['type'] === 'Note' && $s['attach']) { - $s['body'] .= self::bb_attach($s['attach'],$s['body']); + 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') { + if ($act->obj['type'] === 'Video') { $vtypes = [ 'video/mp4', @@ -1863,20 +1966,20 @@ class Activity { ]; $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)) { + 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) { + 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; } @@ -1884,17 +1987,9 @@ class Activity { } } - if($act->recips && (! in_array(ACTIVITY_PUBLIC_INBOX,$act->recips))) - $s['item_private'] = 1; - - - if (array_key_exists('directMessage',$act->obj) && intval($act->obj['directMessage'])) { - $s['item_private'] = 2; - } - - set_iconfig($s,'activitypub','recips',$act->raw_recips); - if($parent) { - set_iconfig($s,'activitypub','rawmsg',$act->raw,1); + set_iconfig($s, 'activitypub', 'recips', $act->raw_recips); + if ($parent) { + set_iconfig($s, 'activitypub', 'rawmsg', $act->raw, 1); } $x = null; @@ -1903,8 +1998,8 @@ class Activity { dbesc($s['mid']), intval($s['uid']) ); - if($r) { - if($s['edited'] > $r[0]['edited']) { + if ($r) { + if ($s['edited'] > $r[0]['edited']) { $x = item_store_update($s); } else { @@ -1915,20 +2010,20 @@ class Activity { $x = item_store($s); } - if(is_array($x) && $x['item_id']) { - if($parent) { - if($s['owner_xchan'] === $channel['channel_hash']) { + 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'])); + Master::Summon(['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]); + if ($r) { + send_status_notifications($x['item_id'], $r[0]); } } - sync_an_item($channel['channel_id'],$x['item_id']); + sync_an_item($channel['channel_id'], $x['item_id']); } } @@ -1940,26 +2035,24 @@ class Activity { dbesc($id) ); - if($x) { - return sprintf('@[zrl=%s]%s[/zrl]',$x[0]['xchan_url'],$x[0]['xchan_name']); + if ($x) { + return sprintf('@[zrl=%s]%s[/zrl]', $x[0]['xchan_url'], $x[0]['xchan_name']); } return '@{' . $id . '}'; } - - - static function update_poll($item,$post) { - $multi = false; - $mid = $post['mid']; + static function update_poll($item, $post) { + $multi = false; + $mid = $post['mid']; $content = $post['title']; - - if (! $item) { + + if (!$item) { return false; } - $o = json_decode($item['obj'],true); - if ($o && array_key_exists('anyOf',$o)) { + $o = json_decode($item['obj'], true); + if ($o && array_key_exists('anyOf', $o)) { $multi = true; } @@ -1969,7 +2062,7 @@ class Activity { ); // prevent any duplicate votes by same author for oneOf and duplicate votes with same author and same answer for anyOf - + if ($r) { if ($multi) { foreach ($r as $rv) { @@ -1986,103 +2079,112 @@ class Activity { } } } - + $answer_found = false; - $found = false; + $found = false; if ($multi) { - for ($c = 0; $c < count($o['anyOf']); $c ++) { + for ($c = 0; $c < count($o['anyOf']); $c++) { if ($o['anyOf'][$c]['name'] === $content) { $answer_found = true; if (is_array($o['anyOf'][$c]['replies'])) { - foreach($o['anyOf'][$c]['replies'] as $reply) { - if(is_array($reply) && array_key_exists('id',$reply) && $reply['id'] === $mid) { + foreach ($o['anyOf'][$c]['replies'] as $reply) { + if (is_array($reply) && array_key_exists('id', $reply) && $reply['id'] === $mid) { $found = true; } } } - if (! $found) { - $o['anyOf'][$c]['replies']['totalItems'] ++; - $o['anyOf'][$c]['replies']['items'][] = [ 'id' => $mid, 'type' => 'Note' ]; + if (!$found) { + $o['anyOf'][$c]['replies']['totalItems']++; + $o['anyOf'][$c]['replies']['items'][] = ['id' => $mid, 'type' => 'Note']; } } } } else { - for ($c = 0; $c < count($o['oneOf']); $c ++) { + for ($c = 0; $c < count($o['oneOf']); $c++) { if ($o['oneOf'][$c]['name'] === $content) { $answer_found = true; if (is_array($o['oneOf'][$c]['replies'])) { - foreach($o['oneOf'][$c]['replies'] as $reply) { - if(is_array($reply) && array_key_exists('id',$reply) && $reply['id'] === $mid) { + foreach ($o['oneOf'][$c]['replies'] as $reply) { + if (is_array($reply) && array_key_exists('id', $reply) && $reply['id'] === $mid) { $found = true; } } } - if (! $found) { - $o['oneOf'][$c]['replies']['totalItems'] ++; - $o['oneOf'][$c]['replies']['items'][] = [ 'id' => $mid, 'type' => 'Note' ]; + if (!$found) { + $o['oneOf'][$c]['replies']['totalItems']++; + $o['oneOf'][$c]['replies']['items'][] = ['id' => $mid, 'type' => 'Note']; } } } } - logger('updated_poll: ' . print_r($o,true),LOGGER_DATA); - if ($answer_found && ! $found) { - $x = q("update item set obj = '%s', edited = '%s' where id = %d", + logger('updated_poll: ' . print_r($o, true), LOGGER_DATA); + if ($answer_found && !$found) { + q("update item set obj = '%s', edited = '%s' where id = %d", dbesc(json_encode($o)), dbesc(datetime_convert()), intval($item['id']) ); - Master::Summon( [ 'Notifier', 'wall-new', $item['id'] ] ); + Master::Summon(['Notifier', 'wall-new', $item['id']]); return true; } return false; } + static function decode_note($act) { + // Within our family of projects, Follow/Unfollow of a thread is an internal activity which should not be transmitted, + // hence if we receive it - ignore or reject it. + // Unfollow is not defined by ActivityStreams, which prefers Undo->Follow. + // This may have to be revisited if AP projects start using Follow for objects other than actors. - static function decode_note($act) { + if (in_array($act->type, [ 'Follow', 'Unfollow' ])) { + return false; + } $response_activity = false; $s = []; - if(is_array($act->obj)) { + if (is_array($act->obj)) { $content = self::get_content($act->obj); } - + $s['owner_xchan'] = $act->actor['id']; $s['author_xchan'] = $act->actor['id']; // ensure we store the original actor - self::actor_store($act->actor['id'],$act->actor); + self::actor_store($act->actor['id'], $act->actor); $s['mid'] = $act->obj['id']; - $s['uuid'] = $act->obj['diaspora:guid']; + $s['uuid'] = $act->obj['diaspora:guid']; $s['parent_mid'] = $act->parent_id; - if($act->data['published']) { - $s['created'] = datetime_convert('UTC','UTC',$act->data['published']); + if (array_key_exists('published', $act->data)) { + $s['created'] = datetime_convert('UTC', 'UTC', $act->data['published']); + $s['commented'] = $s['created']; } - elseif($act->obj['published']) { - $s['created'] = datetime_convert('UTC','UTC',$act->obj['published']); + elseif (array_key_exists('published', $act->obj)) { + $s['created'] = datetime_convert('UTC', 'UTC', $act->obj['published']); + $s['commented'] = $s['created']; } - if($act->data['updated']) { - $s['edited'] = datetime_convert('UTC','UTC',$act->data['updated']); + if (array_key_exists('updated', $act->data)) { + $s['edited'] = datetime_convert('UTC', 'UTC', $act->data['updated']); } - elseif($act->obj['updated']) { - $s['edited'] = datetime_convert('UTC','UTC',$act->obj['updated']); + elseif (array_key_exists('updated', $act->obj)) { + $s['edited'] = datetime_convert('UTC', 'UTC', $act->obj['updated']); } - if ($act->data['expires']) { - $s['expires'] = datetime_convert('UTC','UTC',$act->data['expires']); + if (array_key_exists('expires', $act->data)) { + $s['expires'] = datetime_convert('UTC', 'UTC', $act->data['expires']); } - elseif ($act->obj['expires']) { - $s['expires'] = datetime_convert('UTC','UTC',$act->obj['expires']); + elseif (array_key_exists('expires', $act->obj)) { + $s['expires'] = datetime_convert('UTC', 'UTC', $act->obj['expires']); } - if(ActivityStreams::is_response_activity($act->type)) { + if (ActivityStreams::is_response_activity($act->type)) { $response_activity = true; @@ -2092,85 +2194,86 @@ class Activity { // over-ride the object timestamp with the activity - if($act->data['published']) { - $s['created'] = datetime_convert('UTC','UTC',$act->data['published']); + if ($act->data['published']) { + $s['created'] = datetime_convert('UTC', 'UTC', $act->data['published']); } - if($act->data['updated']) { - $s['edited'] = datetime_convert('UTC','UTC',$act->data['updated']); + if ($act->data['updated']) { + $s['edited'] = datetime_convert('UTC', 'UTC', $act->data['updated']); } $obj_actor = ((isset($act->obj['actor'])) ? $act->obj['actor'] : $act->get_actor('attributedTo', $act->obj)); // ensure we store the original actor - self::actor_store($obj_actor['id'],$obj_actor); + + self::actor_store($obj_actor['id'], $obj_actor); $mention = self::get_actor_bbmention($obj_actor['id']); - if($act->type === 'Like') { - $content['content'] = sprintf( t('Likes %1$s\'s %2$s'),$mention,$act->obj['type']) . "\n\n" . $content['content']; + if ($act->type === 'Like') { + $content['content'] = sprintf(t('Likes %1$s\'s %2$s'), $mention, $act->obj['type']) . "\n\n" . $content['content']; } - if($act->type === 'Dislike') { - $content['content'] = sprintf( t('Doesn\'t like %1$s\'s %2$s'),$mention,$act->obj['type']) . "\n\n" . $content['content']; + if ($act->type === 'Dislike') { + $content['content'] = sprintf(t('Doesn\'t like %1$s\'s %2$s'), $mention, $act->obj['type']) . "\n\n" . $content['content']; } // handle event RSVPs - if (($act->obj['type'] === 'Event') || ($act->obj['type'] === 'Invite' && array_path_exists('object/type',$act->obj) && $act->obj['object']['type'] === 'Event')) { + if (($act->obj['type'] === 'Event') || ($act->obj['type'] === 'Invite' && array_path_exists('object/type', $act->obj) && $act->obj['object']['type'] === 'Event')) { if ($act->type === 'Accept') { - $content['content'] = sprintf( t('Will attend %s\'s event'),$mention) . EOL . EOL . $content['content']; + $content['content'] = sprintf(t('Will attend %s\'s event'), $mention) . EOL . EOL . $content['content']; } if ($act->type === 'Reject') { - $content['content'] = sprintf( t('Will not attend %s\'s event'),$mention) . EOL . EOL . $content['content']; + $content['content'] = sprintf(t('Will not attend %s\'s event'), $mention) . EOL . EOL . $content['content']; } if ($act->type === 'TentativeAccept') { - $content['content'] = sprintf( t('May attend %s\'s event'),$mention) . EOL . EOL . $content['content']; + $content['content'] = sprintf(t('May attend %s\'s event'), $mention) . EOL . EOL . $content['content']; } if ($act->type === 'TentativeReject') { - $content['content'] = sprintf( t('May not attend %s\'s event'),$mention) . EOL . EOL . $content['content']; + $content['content'] = sprintf(t('May not attend %s\'s event'), $mention) . EOL . EOL . $content['content']; } } - if($act->type === 'Announce') { - $content['content'] = sprintf( t('🔁 Repeated %1$s\'s %2$s'), $mention, $act->obj['type']); + if ($act->type === 'Announce') { + $content['content'] = sprintf(t('🔁 Repeated %1$s\'s %2$s'), $mention, $act->obj['type']); } if ($act->type === 'emojiReaction') { $content['content'] = (($act->tgt && $act->tgt['type'] === 'Image') ? '[img=32x32]' . $act->tgt['url'] . '[/img]' : '&#x' . $act->tgt['name'] . ';'); - } + } } - if(! $s['created']) + if (! array_key_exists('created', $s)) $s['created'] = datetime_convert(); - if(! $s['edited']) + if (! array_key_exists('edited', $s)) $s['edited'] = $s['created']; - $s['title'] = (($response_activity) ? EMPTY_STR : self::bb_content($content,'name')); - $s['summary'] = self::bb_content($content,'summary'); - $s['body'] = ((self::bb_content($content,'bbcode') && (! $response_activity)) ? self::bb_content($content,'bbcode') : self::bb_content($content,'content')); + $s['title'] = (($response_activity) ? EMPTY_STR : self::bb_content($content, 'name')); + $s['summary'] = self::bb_content($content, 'summary'); + $s['body'] = ((self::bb_content($content, 'bbcode') && (!$response_activity)) ? self::bb_content($content, 'bbcode') : self::bb_content($content, 'content')); - $s['verb'] = self::activity_decode_mapper($act->type); + $s['verb'] = self::activity_decode_mapper($act->type); // Mastodon does not provide update timestamps when updating poll tallies which means race conditions may occur here. if ($act->type === 'Update' && $act->obj['type'] === 'Question' && $s['edited'] === $s['created']) { $s['edited'] = datetime_convert(); } - if(in_array($act->type, [ 'Delete', 'Undo', 'Tombstone' ]) || ($act->type === 'Create' && $act->obj['type'] === 'Tombstone')) { + if (in_array($act->type, ['Delete', 'Undo', 'Tombstone']) || ($act->type === 'Create' && $act->obj['type'] === 'Tombstone')) { $s['item_deleted'] = 1; } $s['obj_type'] = self::activity_obj_decode_mapper($act->obj['type']); - if($s['obj_type'] === ACTIVITY_OBJ_NOTE && $s['mid'] !== $s['parent_mid']) { + if ($s['obj_type'] === ACTIVITY_OBJ_NOTE && $s['mid'] !== $s['parent_mid']) { $s['obj_type'] = ACTIVITY_OBJ_COMMENT; } $eventptr = null; - if ($act->obj['type'] === 'Invite' && array_path_exists('object/type',$act->obj) && $act->obj['object']['type'] === 'Event') { + if ($act->obj['type'] === 'Invite' && array_path_exists('object/type', $act->obj) && $act->obj['object']['type'] === 'Event') { $eventptr = $act->obj['object']; $s['mid'] = $s['parent_mid'] = $act->obj['id']; } - - if($act->obj['type'] === 'Event') { + + if ($act->obj['type'] === 'Event') { if ($act->type === 'Invite') { $s['mid'] = $s['parent_mid'] = $act->id; } @@ -2179,52 +2282,51 @@ class Activity { if ($eventptr) { - $s['obj'] = []; - $s['obj']['asld'] = $eventptr; - $s['obj']['type'] = ACTIVITY_OBJ_EVENT; - $s['obj']['id'] = $eventptr['id']; + $s['obj'] = []; + $s['obj']['asld'] = $eventptr; + $s['obj']['type'] = ACTIVITY_OBJ_EVENT; + $s['obj']['id'] = $eventptr['id']; $s['obj']['title'] = $eventptr['name']; - if(strpos($act->obj['startTime'],'Z')) + if (strpos($act->obj['startTime'], 'Z')) $s['obj']['adjust'] = true; else $s['obj']['adjust'] = false; - $s['obj']['dtstart'] = datetime_convert('UTC','UTC',$eventptr['startTime']); - if($act->obj['endTime']) - $s['obj']['dtend'] = datetime_convert('UTC','UTC',$eventptr['endTime']); + $s['obj']['dtstart'] = datetime_convert('UTC', 'UTC', $eventptr['startTime']); + if ($act->obj['endTime']) + $s['obj']['dtend'] = datetime_convert('UTC', 'UTC', $eventptr['endTime']); else $s['obj']['nofinish'] = true; $s['obj']['description'] = $eventptr['content']; - if(array_path_exists('location/content',$eventptr)) + if (array_path_exists('location/content', $eventptr)) $s['obj']['location'] = $eventptr['location']['content']; } else { - $s['obj'] = $act->obj; + $s['obj'] = $act->obj; } $generator = $act->get_property_obj('generator'); - if((! $generator) && (! $response_activity)) { - $generator = $act->get_property_obj('generator',$act->obj); + if ((!$generator) && (!$response_activity)) { + $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)) { + 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(! $response_activity) { + if (!$response_activity) { $a = self::decode_taxonomy($act->obj); - if($a) { + if ($a) { $s['term'] = $a; - foreach($a as $b) { - if($b['ttype'] === TERM_EMOJI) { - $s['title'] = str_replace($b['term'],'[img=16x16]' . $b['url'] . '[/img]',$s['title']); - $s['summary'] = str_replace($b['term'],'[img=16x16]' . $b['url'] . '[/img]',$s['summary']); - $s['body'] = str_replace($b['term'],'[img=16x16]' . $b['url'] . '[/img]',$s['body']); + foreach ($a as $b) { + if ($b['ttype'] === TERM_EMOJI) { + $s['title'] = str_replace($b['term'], '[img=16x16]' . $b['url'] . '[/img]', $s['title']); + $s['summary'] = str_replace($b['term'], '[img=16x16]' . $b['url'] . '[/img]', $s['summary']); + $s['body'] = str_replace($b['term'], '[img=16x16]' . $b['url'] . '[/img]', $s['body']); } } } @@ -2241,28 +2343,30 @@ class Activity { $s['iconfig'] = $a; } - if($act->obj['type'] === 'Note' && $s['attach']) { - $s['body'] .= self::bb_attach($s['attach'],$s['body']); - } + if (array_key_exists('type', $act->obj)) { - if ($act->obj['type'] === 'Question' && in_array($act->type,['Create','Update'])) { - if ($act->obj['endTime']) { - $s['comments_closed'] = datetime_convert('UTC','UTC', $act->obj['endTime']); + if ($act->obj['type'] === 'Note' && $s['attach']) { + $s['body'] .= self::bb_attach($s['attach'], $s['body']); } - } - if ($act->obj['closed']) { - $s['comments_closed'] = datetime_convert('UTC','UTC', $act->obj['closed']); - } + if ($act->obj['type'] === 'Question' && in_array($act->type, ['Create', 'Update'])) { + if (array_key_exists('endTime', $act->obj)) { + $s['comments_closed'] = datetime_convert('UTC', 'UTC', $act->obj['endTime']); + } + } + } + if (array_key_exists('closed', $act->obj)) { + $s['comments_closed'] = datetime_convert('UTC', 'UTC', $act->obj['closed']); + } // 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(! $response_activity) { - if($act->obj['type'] === 'Video') { + if (!$response_activity) { + if ($act->obj['type'] === 'Video') { $vtypes = [ 'video/mp4', @@ -2273,27 +2377,27 @@ class Activity { $mps = []; $ptr = null; - if(array_key_exists('url',$act->obj)) { - if(is_array($act->obj['url'])) { - if(array_key_exists(0,$act->obj['url'])) { + if (array_key_exists('url', $act->obj)) { + if (is_array($act->obj['url'])) { + if (array_key_exists(0, $act->obj['url'])) { $ptr = $act->obj['url']; } else { - $ptr = [ $act->obj['url'] ]; + $ptr = [$act->obj['url']]; } - foreach($ptr as $vurl) { + foreach ($ptr as $vurl) { // peertube uses the non-standard element name 'mimeType' here - if(array_key_exists('mimeType',$vurl)) { - if(in_array($vurl['mimeType'], $vtypes)) { - if(! array_key_exists('width',$vurl)) { + if (array_key_exists('mimeType', $vurl)) { + if (in_array($vurl['mimeType'], $vtypes)) { + if (!array_key_exists('width', $vurl)) { $vurl['width'] = 0; } $mps[] = $vurl; } } - elseif(array_key_exists('mediaType',$vurl)) { - if(in_array($vurl['mediaType'], $vtypes)) { - if(! array_key_exists('width',$vurl)) { + elseif (array_key_exists('mediaType', $vurl)) { + if (in_array($vurl['mediaType'], $vtypes)) { + if (!array_key_exists('width', $vurl)) { $vurl['width'] = 0; } $mps[] = $vurl; @@ -2301,22 +2405,22 @@ class Activity { } } } - if($mps) { - usort($mps,[ __CLASS__, 'vid_sort' ]); - foreach($mps as $m) { - if(intval($m['width']) < 500 && self::media_not_in_body($m['href'],$s['body'])) { + if ($mps) { + usort($mps, [__CLASS__, 'vid_sort']); + foreach ($mps as $m) { + if (intval($m['width']) < 500 && self::media_not_in_body($m['href'], $s['body'])) { $s['body'] .= "\n\n" . '[video]' . $m['href'] . '[/video]'; break; } } } - elseif(is_string($act->obj['url']) && self::media_not_in_body($act->obj['url'],$s['body'])) { + elseif (is_string($act->obj['url']) && self::media_not_in_body($act->obj['url'], $s['body'])) { $s['body'] .= "\n\n" . '[video]' . $act->obj['url'] . '[/video]'; } } } - if($act->obj['type'] === 'Audio') { + if ($act->obj['type'] === 'Audio') { $atypes = [ 'audio/mpeg', @@ -2326,50 +2430,50 @@ class Activity { $ptr = null; - if(array_key_exists('url',$act->obj)) { - if(is_array($act->obj['url'])) { - if(array_key_exists(0,$act->obj['url'])) { + if (array_key_exists('url', $act->obj)) { + if (is_array($act->obj['url'])) { + if (array_key_exists(0, $act->obj['url'])) { $ptr = $act->obj['url']; } else { - $ptr = [ $act->obj['url'] ]; + $ptr = [$act->obj['url']]; } - foreach($ptr as $vurl) { - if(in_array($vurl['mediaType'], $atypes) && self::media_not_in_body($vurl['href'],$s['body'])) { + foreach ($ptr as $vurl) { + if (in_array($vurl['mediaType'], $atypes) && self::media_not_in_body($vurl['href'], $s['body'])) { $s['body'] .= "\n\n" . '[audio]' . $vurl['href'] . '[/audio]'; break; } } } - elseif(is_string($act->obj['url']) && self::media_not_in_body($act->obj['url'],$s['body'])) { + elseif (is_string($act->obj['url']) && self::media_not_in_body($act->obj['url'], $s['body'])) { $s['body'] .= "\n\n" . '[audio]' . $act->obj['url'] . '[/audio]'; } } } - if($act->obj['type'] === 'Image') { + if ($act->obj['type'] === 'Image' && strpos($s['body'],'zrl=') === false) { $ptr = null; - if(array_key_exists('url',$act->obj)) { - if(is_array($act->obj['url'])) { - if(array_key_exists(0,$act->obj['url'])) { + if (array_key_exists('url', $act->obj)) { + if (is_array($act->obj['url'])) { + if (array_key_exists(0, $act->obj['url'])) { $ptr = $act->obj['url']; } else { - $ptr = [ $act->obj['url'] ]; + $ptr = [$act->obj['url']]; } - foreach($ptr as $vurl) { - if(strpos($s['body'],$vurl['href']) === false) { - $bb_imgs .= '[zmg]' . $vurl['href'] . '[/zmg]' . "\n\n"; + foreach ($ptr as $vurl) { + if (strpos($s['body'], $vurl['href']) === false) { + $bb_imgs = '[zmg]' . $vurl['href'] . '[/zmg]' . "\n\n"; break; } } $s['body'] = $bb_imgs . $s['body']; } - elseif(is_string($act->obj['url'])) { - if(strpos($s['body'],$act->obj['url']) === false) { + elseif (is_string($act->obj['url'])) { + if (strpos($s['body'], $act->obj['url']) === false) { $s['body'] .= '[zmg]' . $act->obj['url'] . '[/zmg]' . "\n\n" . $s['body']; } } @@ -2377,36 +2481,36 @@ class Activity { } - if($act->obj['type'] === 'Page' && ! $s['body']) { + if ($act->obj['type'] === 'Page' && !$s['body']) { $ptr = null; $purl = EMPTY_STR; - if(array_key_exists('url',$act->obj)) { - if(is_array($act->obj['url'])) { - if(array_key_exists(0,$act->obj['url'])) { + if (array_key_exists('url', $act->obj)) { + if (is_array($act->obj['url'])) { + if (array_key_exists(0, $act->obj['url'])) { $ptr = $act->obj['url']; } else { - $ptr = [ $act->obj['url'] ]; + $ptr = [$act->obj['url']]; } - foreach($ptr as $vurl) { - if(array_key_exists('mediaType',$vurl) && $vurl['mediaType'] === 'text/html') { + foreach ($ptr as $vurl) { + if (array_key_exists('mediaType', $vurl) && $vurl['mediaType'] === 'text/html') { $purl = $vurl['href']; break; } - elseif(array_key_exists('mimeType',$vurl) && $vurl['mimeType'] === 'text/html') { + elseif (array_key_exists('mimeType', $vurl) && $vurl['mimeType'] === 'text/html') { $purl = $vurl['href']; break; } } } - elseif(is_string($act->obj['url'])) { + elseif (is_string($act->obj['url'])) { $purl = $act->obj['url']; } - if($purl) { + if ($purl) { $li = z_fetch_url(z_root() . '/linkinfo?binurl=' . bin2hex($purl)); - if($li['success'] && $li['body']) { + if ($li['success'] && $li['body']) { $s['body'] .= "\n" . $li['body']; } else { @@ -2418,32 +2522,31 @@ class Activity { } - - if(in_array($act->obj['type'],[ 'Note','Article','Page' ])) { + if (in_array($act->obj['type'], ['Note', 'Article', 'Page'])) { $ptr = null; - if(array_key_exists('url',$act->obj)) { - if(is_array($act->obj['url'])) { - if(array_key_exists(0,$act->obj['url'])) { + if (array_key_exists('url', $act->obj)) { + if (is_array($act->obj['url'])) { + if (array_key_exists(0, $act->obj['url'])) { $ptr = $act->obj['url']; } else { - $ptr = [ $act->obj['url'] ]; + $ptr = [$act->obj['url']]; } - foreach($ptr as $vurl) { - if(array_key_exists('mediaType',$vurl) && $vurl['mediaType'] === 'text/html') { + foreach ($ptr as $vurl) { + if (array_key_exists('mediaType', $vurl) && $vurl['mediaType'] === 'text/html') { $s['plink'] = $vurl['href']; break; } } } - elseif(is_string($act->obj['url'])) { + elseif (is_string($act->obj['url'])) { $s['plink'] = $act->obj['url']; } } } - if(! $s['plink']) { + if (!$s['plink']) { $s['plink'] = $s['mid']; } @@ -2456,24 +2559,24 @@ class Activity { } if (is_array($act->obj)) { - if (array_key_exists('directMessage',$act->obj) && intval($act->obj['directMessage'])) { + if (array_key_exists('directMessage', $act->obj) && intval($act->obj['directMessage'])) { $s['item_private'] = 2; } } - set_iconfig($s,'activitypub','recips',$act->raw_recips); + set_iconfig($s, 'activitypub', 'recips', $act->raw_recips); $parent = (($s['parent_mid'] && $s['parent_mid'] === $s['mid']) ? true : false); - if($parent) { - set_iconfig($s,'activitypub','rawmsg',$act->raw,1); + if ($parent) { + set_iconfig($s, 'activitypub', 'rawmsg', $act->raw, 1); } $hookinfo = [ 'act' => $act, - 's' => $s + 's' => $s ]; - call_hooks('decode_note',$hookinfo); + call_hooks('decode_note', $hookinfo); $s = $hookinfo['s']; @@ -2481,51 +2584,176 @@ class Activity { } - static function store($channel,$observer_hash,$act,$item,$fetch_parents = true) { - + static function store($channel, $observer_hash, $act, $item, $fetch_parents = true, $force = false) { $is_sys_channel = is_sys_channel($channel['channel_id']); + $is_child_node = false; - // 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. + // TODO: not implemented + // Pleroma scrobbles can be really noisy and contain lots of duplicate activities. Disable them by default. + /*if (($act->type === 'Listen') && ($is_sys_channel || get_pconfig($channel['channel_id'], 'system', 'allow_scrobbles', false))) { + return; + }*/ - $pubstream = ((is_array($act->obj) && array_key_exists('to', $act->obj) && in_array(ACTIVITY_PUBLIC_INBOX, $act->obj['to'])) ? true : false); - $is_parent = (($item['parent_mid'] && $item['parent_mid'] === $item['mid']) ? true : false); + // TODO: this his handled in pubcrawl atm. + // very unpleasant and imperfect way of determining a Mastodon DM + /*if ($act->raw_recips && array_key_exists('to',$act->raw_recips) && is_array($act->raw_recips['to']) && count($act->raw_recips['to']) === 1 && $act->raw_recips['to'][0] === channel_url($channel) && ! $act->raw_recips['cc']) { + $item['item_private'] = 2; + }*/ - if($is_parent && (! perm_is_allowed($channel['channel_id'],$observer_hash,'send_stream') && ! ($is_sys_channel && $pubstream))) { - logger('no permission'); - return; + if ($item['parent_mid'] && $item['parent_mid'] !== $item['mid']) { + $is_child_node = true; } - if(is_array($act->obj)) { - $content = self::get_content($act->obj); + $allowed = false; + + // TODO: not implemented + // $permit_mentions = intval(PConfig::Get($channel['channel_id'], 'system','permit_all_mentions') && i_am_mentioned($channel,$item)); + + if ($is_child_node) { + + $p = q("select * from item where mid = '%s' and uid = %d and item_wall = 1", + dbesc($item['parent_mid']), + intval($channel['channel_id']) + ); + if ($p) { + // set the owner to the owner of the parent + $item['owner_xchan'] = $p[0]['owner_xchan']; + + // check permissions against the author, not the sender + $allowed = perm_is_allowed($channel['channel_id'], $item['author_xchan'], 'post_comments'); + if ((!$allowed)/* && $permit_mentions*/) { + if ($p[0]['owner_xchan'] === $channel['channel_hash']) { + $allowed = false; + } + else { + $allowed = true; + } + } + + // TODO: not implemented + /*if (absolutely_no_comments($p[0])) { + $allowed = false; + }*/ + + if (!$allowed) { + logger('rejected comment from ' . $item['author_xchan'] . ' for ' . $channel['channel_address']); + logger('rejected: ' . print_r($item, true), LOGGER_DATA); + + // TODO: not implemented + // let the sender know we received their comment but we don't permit spam here. + // self::send_rejection_activity($channel,$item['author_xchan'],$item); + return; + } + + // TODO: not implemented + /*if (perm_is_allowed($channel['channel_id'],$item['author_xchan'],'moderated')) { + $item['item_blocked'] = ITEM_MODERATED; + }*/ + } + else { + + $allowed = true; + // reject public stream comments that weren't sent by the conversation owner + if ($is_sys_channel && $item['owner_xchan'] !== $observer_hash && !$fetch_parents) { + $allowed = false; + } + } + + if ($p && $p[0]['obj_type'] === 'Question') { + if ($item['obj_type'] === 'Note' && $item['title'] && (!$item['content'])) { + $item['obj_type'] = 'Answer'; + } + } } - if(! $content) { - logger('no content'); + else { + + // The $item['item_fetched'] flag is set in fetch_and_store_parents(). + // In this case we should check against author permissions because sender is not owner. + if (perm_is_allowed($channel['channel_id'], (($item['item_fetched']) ? $item['author_xchan'] : $observer_hash), 'send_stream') || $is_sys_channel) { + $allowed = true; + } + // TODO: not implemented + /*if ($permit_mentions) { + $allowed = true; + }*/ + } + + if (tgroup_check($channel['channel_id'], $item) && (!$is_child_node)) { + // for forum deliveries, make sure we keep a copy of the signed original + set_iconfig($item, 'activitypub', 'rawmsg', $act->raw, 1); + $allowed = true; + } + + if (intval($item['item_private']) === 2) { + if (!perm_is_allowed($channel['channel_id'], $observer_hash, 'post_mail')) { + $allowed = false; + } + } + + if ($is_sys_channel) { + + /* TODO: not implemented + if (! check_pubstream_channelallowed($observer_hash)) { + $allowed = false; + } + + // don't allow pubstream posts if the sender even has a clone on a pubstream denied site + + $h = q("select hubloc_url from hubloc where hubloc_hash = '%s'", + dbesc($observer_hash) + ); + if ($h) { + foreach ($h as $hub) { + if (! check_pubstream_siteallowed($hub['hubloc_url'])) { + $allowed = false; + break; + } + } + } + */ + + if (intval($item['item_private'])) { + $allowed = false; + } + } + + // TODO: not implemented + /*$blocked = LibBlock::fetch($channel['channel_id'],BLOCKTYPE_SERVER); + if ($blocked) { + foreach($blocked as $b) { + if (strpos($observer_hash,$b['block_entity']) !== false) { + $allowed = false; + } + } + }*/ + + if (!$allowed && !$force) { + logger('no permission'); return; } $item['aid'] = $channel['channel_account_id']; $item['uid'] = $channel['channel_id']; - // Make sure we use the zot6 identity where applicable + // Some authors may be zot6 authors in which case we want to store their nomadic identity + // instead of their ActivityPub identity $item['author_xchan'] = self::find_best_identity($item['author_xchan']); $item['owner_xchan'] = self::find_best_identity($item['owner_xchan']); - if(!$item['author_xchan']) { + if (!$item['author_xchan']) { logger('No author: ' . print_r($act, true)); } - if(!$item['owner_xchan']) { + if (!$item['owner_xchan']) { logger('No owner: ' . print_r($act, true)); } - if(!$item['author_xchan'] || !$item['owner_xchan']) + if (!$item['author_xchan'] || !$item['owner_xchan']) return; - if($channel['channel_system']) { - if(! MessageFilter::evaluate($item,get_config('system','pubstream_incl'),get_config('system','pubstream_excl'))) { + if ($channel['channel_system']) { + if (!MessageFilter::evaluate($item, get_config('system', 'pubstream_incl'), get_config('system', 'pubstream_excl'))) { logger('post is filtered'); return; } @@ -2536,81 +2764,90 @@ class Activity { intval($channel['channel_id']) ); - if($abook) { - if(! post_is_importable($item,$abook[0])) { + if ($abook) { + if (!post_is_importable($item, $abook[0])) { logger('post is filtered'); return; } } - - if($act->obj['conversation']) { - set_iconfig($item,'ostatus','conversation',$act->obj['conversation'],1); + if (array_key_exists('conversation', $act->obj)) { + set_iconfig($item, 'ostatus', 'conversation', $act->obj['conversation'], 1); } // This isn't perfect but the best we can do for now. + $item['comment_policy'] = ((isset($act->data['commentPolicy'])) ? $act->data['commentPolicy'] : 'authenticated'); + + set_iconfig($item, 'activitypub', 'recips', $act->raw_recips); - $item['comment_policy'] = 'authenticated'; + // TODO: inheritPrivacy should probably be set in encode activity. Zap does not do so yet - check what this is about + if (!(isset($act->data['inheritPrivacy']) && $act->data['inheritPrivacy'])) { + if ($item['item_private']) { + $item['item_restrict'] = $item['item_restrict'] & 1; + if ($is_child_node) { + $item['allow_cid'] = '<' . $channel['channel_hash'] . '>'; + $item['allow_gid'] = $item['deny_cid'] = $item['deny_gid'] = ''; + } + logger('restricted'); + } + } - set_iconfig($item,'activitypub','recips',$act->raw_recips); + if (intval($act->sigok)) { + $item['item_verified'] = 1; + } - if(! $is_parent) { - $p = q("select parent_mid, id, obj_type from item where mid = '%s' and uid = %d limit 1", + $parent = null; + + if ($is_child_node) { + + $parent = q("select * from item where mid = '%s' and uid = %d limit 1", dbesc($item['parent_mid']), intval($item['uid']) ); - if(! $p) { - $a = (($fetch_parents) ? self::fetch_and_store_parents($channel,$act,$item) : false); - if($a) { - $p = q("select parent_mid from item where mid = '%s' and uid = %d limit 1", - dbesc($item['parent_mid']), - intval($item['uid']) - ); - } - else { - logger('could not fetch parents'); + if (!$parent) { + if (!plugin_is_installed('pubcrawl')) { return; - - // @TODO we maybe could accept these is we formatted the body correctly with share_bb() - // or at least provided a link to the object - // if(in_array($act->type,[ 'Like','Dislike' ])) { - // return; - // } - - // @TODO do we actually want that? - // if no parent was fetched, turn into a top-level post - - // turn into a top level post - // $s['parent_mid'] = $s['mid']; - // $s['thr_parent'] = $s['mid']; } - } - - - if ($p[0]['obj_type'] === 'Question') { - if ($item['obj_type'] === ACTIVITY_OBJ_NOTE && $item['title'] && (! $item['content'])) { - $item['obj_type'] = 'Answer'; + else { + $fetch = false; + // TODO: debug + // if (perm_is_allowed($channel['channel_id'],$observer_hash,'send_stream') && (PConfig::Get($channel['channel_id'],'system','hyperdrive',true) || $act->type === 'Announce')) { + if (perm_is_allowed($channel['channel_id'], $observer_hash, 'send_stream') || $is_sys_channel) { + $fetch = (($fetch_parents) ? self::fetch_and_store_parents($channel, $observer_hash, $item, $force) : false); + } + if ($fetch) { + $parent = q("select * from item where mid = '%s' and uid = %d limit 1", + dbesc($item['parent_mid']), + intval($item['uid']) + ); + } + else { + logger('no parent'); + return; + } } } - - if($p[0]['parent_mid'] !== $item['parent_mid']) { + if ($parent[0]['parent_mid'] !== $item['parent_mid']) { $item['thr_parent'] = $item['parent_mid']; } else { - $item['thr_parent'] = $p[0]['parent_mid']; + $item['thr_parent'] = $parent[0]['parent_mid']; } - $item['parent_mid'] = $p[0]['parent_mid']; + $item['parent_mid'] = $parent[0]['parent_mid']; } + // TODO: not implemented + // self::rewrite_mentions($item); + $r = q("select id, created, edited from item where mid = '%s' and uid = %d limit 1", dbesc($item['mid']), intval($item['uid']) ); - if($r) { - if($item['edited'] > $r[0]['edited']) { + if ($r) { + if ($item['edited'] > $r[0]['edited']) { $item['id'] = $r[0]['id']; - $x = item_store_update($item); + $x = item_store_update($item); } else { return; @@ -2620,99 +2857,122 @@ class Activity { $x = item_store($item); } - if(is_array($x) && $x['item_id']) { - if($is_parent) { - if($item['owner_xchan'] === $channel['channel_hash']) { + if ($fetch_parents && $parent && !intval($parent[0]['item_private'])) { + logger('topfetch', LOGGER_DEBUG); + // if the thread owner is a connnection, we will already receive any additional comments to their posts + // but if they are not we can try to fetch others in the background + $x = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash + WHERE abook_channel = %d and abook_xchan = '%s' LIMIT 1", + intval($channel['channel_id']), + dbesc($parent[0]['owner_xchan']) + ); + if (!$x) { + // determine if the top-level post provides a replies collection + if ($parent[0]['obj']) { + $parent[0]['obj'] = json_decode($parent[0]['obj'], true); + } + logger('topfetch: ' . print_r($parent[0], true), LOGGER_ALL); + $id = ((array_path_exists('obj/replies/id', $parent[0])) ? $parent[0]['obj']['replies']['id'] : false); + if (!$id) { + $id = ((array_path_exists('obj/replies', $parent[0]) && is_string($parent[0]['obj']['replies'])) ? $parent[0]['obj']['replies'] : false); + } + if ($id) { + Master::Summon(['Convo', $id, $channel['channel_id'], $observer_hash]); + } + } + } + + if (is_array($x) && $x['item_id']) { + if ($is_child_node) { + if ($item['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'])); + Master::Summon(['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]); + if ($r) { + send_status_notifications($x['item_id'], $r[0]); } } - sync_an_item($channel['channel_id'],$x['item_id']); + sync_an_item($channel['channel_id'], $x['item_id']); } } - static public function fetch_and_store_parents($channel,$act,$item) { - + static public function fetch_and_store_parents($channel, $observer_hash, $item, $force = false) { logger('fetching parents'); $p = []; - $current_act = $act; $current_item = $item; - while($current_item['parent_mid'] !== $current_item['mid']) { + while ($current_item['parent_mid'] !== $current_item['mid']) { $n = self::fetch($current_item['parent_mid'], $channel); - if(! $n) { - break; - } - $a = new ActivityStreams($n); - - //logger($a->debug()); - if(! $a->is_valid()) { + if (!$n) { break; } - if (is_array($a->actor) && array_key_exists('id',$a->actor)) { - self::actor_store($a->actor['id'],$a->actor); + $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); } - $replies = null; - if(isset($a->obj['replies']['first']['items'])) { - $replies = $a->obj['replies']['first']['items']; - // we already have this one - array_diff($replies, [$current_item['mid']]); + logger($a->debug(), LOGGER_DATA); + + if (!$a->is_valid()) { + logger('not a valid activity'); + break; } - $item = null; - - switch($a->type) { - case 'Create': - case 'Update': - //case 'Like': - //case 'Dislike': - case 'Announce': - $item = self::decode_note($a); - break; - default: - break; + $item = Activity::decode_note($a); + if (!$item) { + break; } $hookinfo = [ - 'a' => $a, + 'a' => $a, 'item' => $item ]; - call_hooks('fetch_and_store',$hookinfo); + call_hooks('fetch_and_store', $hookinfo); $item = $hookinfo['item']; - if($item) { + if ($item) { + $item['item_fetched'] = 1; - array_unshift($p,[ $a, $item, $replies]); - - if($item['parent_mid'] === $item['mid'] || count($p) > 20) { + if (intval($channel['channel_system']) && intval($item['item_private'])) { + $p = []; break; } + if (count($p) > 100) { + $p = []; + break; + } + + array_unshift($p, [$a, $item]); + + if ($item['parent_mid'] === $item['mid']) { + break; + } } - $current_act = $a; + $current_item = $item; } - if($p) { - foreach($p as $pv) { - self::store($channel,$pv[0]->actor['id'],$pv[0],$pv[1],false); - if($pv[2]) - self::fetch_and_store_replies($channel, $pv[2]); + if ($p) { + foreach ($p as $pv) { + if ($pv[0]->is_valid()) { + Activity::store($channel, $observer_hash, $pv[0], $pv[1], false, $force); + } } return true; } @@ -2720,29 +2980,110 @@ class Activity { return false; } + /* + static public function fetch_and_store_parents($channel, $item) { + + logger('fetching parents'); + + $p = []; + + $current_item = $item; + + while ($current_item['parent_mid'] !== $current_item['mid']) { + $n = self::fetch($current_item['parent_mid'], $channel); + if (!$n) { + break; + } + $a = new ActivityStreams($n); + + //logger($a->debug()); + + if (!$a->is_valid()) { + break; + } + + if (is_array($a->actor) && array_key_exists('id', $a->actor)) { + self::actor_store($a->actor['id'], $a->actor); + } + + $replies = null; + if (isset($a->obj['replies']['first']['items'])) { + $replies = $a->obj['replies']['first']['items']; + // we already have this one + array_diff($replies, [$current_item['mid']]); + } + + $item = null; + + switch ($a->type) { + case 'Create': + case 'Update': + //case 'Like': + //case 'Dislike': + case 'Announce': + $item = self::decode_note($a); + break; + default: + break; + + } + + $hookinfo = [ + 'a' => $a, + 'item' => $item + ]; + + call_hooks('fetch_and_store', $hookinfo); + + $item = $hookinfo['item']; + + if ($item) { + + array_unshift($p, [$a, $item, $replies]); + + if ($item['parent_mid'] === $item['mid'] || count($p) > 20) { + break; + } + + } + $current_item = $item; + } + + if ($p) { + foreach ($p as $pv) { + self::store($channel, $pv[0]->actor['id'], $pv[0], $pv[1], false); + if ($pv[2]) + self::fetch_and_store_replies($channel, $pv[2]); + } + return true; + } + + return false; + } + */ static public function fetch_and_store_replies($channel, $arr) { logger('fetching replies'); - logger(print_r($arr,true)); + logger(print_r($arr, true)); $p = []; - foreach($arr as $url) { + foreach ($arr as $url) { $n = self::fetch($url, $channel); - if(! $n) { + if (!$n) { break; } $a = new ActivityStreams($n); - if(! $a->is_valid()) { + if (!$a->is_valid()) { break; } $item = null; - switch($a->type) { + switch ($a->type) { case 'Create': case 'Update': case 'Like': @@ -2755,62 +3096,56 @@ class Activity { } $hookinfo = [ - 'a' => $a, + 'a' => $a, 'item' => $item ]; - call_hooks('fetch_and_store',$hookinfo); + call_hooks('fetch_and_store', $hookinfo); $item = $hookinfo['item']; - if($item) { - array_unshift($p,[ $a, $item ]); + if ($item) { + array_unshift($p, [$a, $item]); } } - if($p) { - foreach($p as $pv) { - self::store($channel,$pv[0]->actor['id'],$pv[0],$pv[1],false); + if ($p) { + foreach ($p as $pv) { + self::store($channel, $pv[0]->actor['id'], $pv[0], $pv[1], false); } } } - static function announce_note($channel,$observer_hash,$act) { + 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)) { + if (!perm_is_allowed($channel['channel_id'], $observer_hash, 'send_stream') && !$is_sys_channel) { logger('no permission'); return; } $content = self::get_content($act->obj); - if(! $content) { + 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['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']) + if (!$s['created']) $s['created'] = datetime_convert(); - if(! $s['edited']) + if (!$s['edited']) $s['edited'] = $s['created']; @@ -2820,8 +3155,8 @@ class Activity { $s['obj_type'] = ACTIVITY_OBJ_NOTE; $s['app'] = t('ActivityPub'); - if($channel['channel_system']) { - if(! \Zotlabs\Lib\MessageFilter::evaluate($s,get_config('system','pubstream_incl'),get_config('system','pubstream_excl'))) { + if ($channel['channel_system']) { + if (!MessageFilter::evaluate($s, get_config('system', 'pubstream_incl'), get_config('system', 'pubstream_excl'))) { logger('post is filtered'); return; } @@ -2832,61 +3167,61 @@ class Activity { intval($channel['channel_id']) ); - if($abook) { - if(! post_is_importable($s,$abook[0])) { + if ($abook) { + if (!post_is_importable($s, $abook[0])) { logger('post is filtered'); return; } } - if($act->obj['conversation']) { - set_iconfig($s,'ostatus','conversation',$act->obj['conversation'],1); + if ($act->obj['conversation']) { + set_iconfig($s, 'ostatus', 'conversation', $act->obj['conversation'], 1); } $a = self::decode_taxonomy($act->obj); - if($a) { + if ($a) { $s['term'] = $a; } $a = self::decode_attachment($act->obj); - if($a) { + 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'] . - "']"; + $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"; + if ($content['name']) + $body .= self::bb_content($content, 'name') . "\r\n"; - $body .= self::bb_content($content,'content'); + $body .= self::bb_content($content, 'content'); - if($act->obj['type'] === 'Note' && $s['attach']) { - $body .= self::bb_attach($s['attach'],$body); + 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; + $s['title'] = self::bb_content($content, 'name'); + $s['body'] = $body; - if($act->recips && (! in_array(ACTIVITY_PUBLIC_INBOX,$act->recips))) + if ($act->recips && (!in_array(ACTIVITY_PUBLIC_INBOX, $act->recips))) $s['item_private'] = 1; - set_iconfig($s,'activitypub','recips',$act->raw_recips); + 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']) { + if ($r) { + if ($s['edited'] > $r[0]['edited']) { $x = item_store_update($s); } else { @@ -2897,35 +3232,35 @@ class Activity { $x = item_store($s); } - if(is_array($x) && $x['item_id']) { - if($s['owner_xchan'] === $channel['channel_hash']) { + if (is_array($x) && $x['item_id']) { + 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'])); + Master::Summon(['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]); + if ($r) { + send_status_notifications($x['item_id'], $r[0]); } - sync_an_item($channel['channel_id'],$x['item_id']); + sync_an_item($channel['channel_id'], $x['item_id']); } } - static function like_note($channel,$observer_hash,$act) { + static function like_note($channel, $observer_hash, $act) { $s = []; $parent = $act->obj['id']; - - if($act->type === 'Like') + + if ($act->type === 'Like') $s['verb'] = ACTIVITY_LIKE; - if($act->type === 'Dislike') + if ($act->type === 'Dislike') $s['verb'] = ACTIVITY_DISLIKE; - if(! $parent) + if (!$parent) return; $r = q("select * from item where uid = %d and ( mid = '%s' or mid = '%s' ) limit 1", @@ -2934,7 +3269,7 @@ class Activity { dbesc(urldecode(basename($parent))) ); - if(! $r) { + if (!$r) { logger('parent not found.'); return; } @@ -2942,14 +3277,14 @@ class Activity { 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')) { + 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']) { + if ($parent_item['mid'] === $parent_item['parent_mid']) { $s['parent_mid'] = $parent_item['mid']; } else { @@ -2957,31 +3292,29 @@ class Activity { $s['parent_mid'] = $parent_item['parent_mid']; } - $s['owner_xchan'] = $parent_item['owner_xchan']; + $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']) + if (!$s['parent_mid']) $s['parent_mid'] = $s['mid']; - - $post_type = (($parent_item['resource_type'] === 'photo') ? t('photo') : t('post')); - $links = array(array('rel' => 'alternate','type' => 'text/html', 'href' => $parent_item['plink'])); - $objtype = (($parent_item['resource_type'] === 'photo') ? ACTIVITY_OBJ_PHOTO : ACTIVITY_OBJ_NOTE ); + $post_type = (($parent_item['resource_type'] === 'photo') ? t('photo') : t('post')); - $body = $parent_item['body']; + $links = [['rel' => 'alternate', 'type' => 'text/html', 'href' => $parent_item['plink']]]; + $objtype = (($parent_item['resource_type'] === 'photo') ? ACTIVITY_OBJ_PHOTO : ACTIVITY_OBJ_NOTE); $z = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($parent_item['author_xchan']) ); - if($z) - $item_author = $z[0]; + if ($z) + $item_author = $z[0]; - $object = json_encode(array( + $object = json_encode([ 'type' => $post_type, 'id' => $parent_item['mid'], 'parent' => (($parent_item['thr_parent']) ? $parent_item['thr_parent'] : $parent_item['parent_mid']), @@ -2990,77 +3323,75 @@ class Activity { 'content' => $parent_item['body'], 'created' => $parent_item['created'], 'edited' => $parent_item['edited'], - 'author' => array( + 'author' => [ '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 + 'link' => [ + ['rel' => 'alternate', 'type' => 'text/html', 'href' => $item_author['xchan_url']], + ['rel' => 'photo', 'type' => $item_author['xchan_photo_mimetype'], 'href' => $item_author['xchan_photo_m']]], + ], + ], JSON_UNESCAPED_SLASHES ); - if($act->type === 'Like') + if ($act->type === 'Like') $bodyverb = t('%1$s likes %2$s\'s %3$s'); - if($act->type === 'Dislike') + 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 ); + $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'); + $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['route'] = $parent_item['route']; $s['item_private'] = $parent_item['item_private']; - $s['obj_type'] = $objtype; - $s['obj'] = $object; + $s['obj_type'] = $objtype; + $s['obj'] = $object; - if($act->obj['conversation']) { - set_iconfig($s,'ostatus','conversation',$act->obj['conversation'],1); + if ($act->obj['conversation']) { + set_iconfig($s, 'ostatus', 'conversation', $act->obj['conversation'], 1); } - if($act->recips && (! in_array(ACTIVITY_PUBLIC_INBOX,$act->recips))) + if ($act->recips && (!in_array(ACTIVITY_PUBLIC_INBOX, $act->recips))) $s['item_private'] = 1; - set_iconfig($s,'activitypub','recips',$act->raw_recips); + set_iconfig($s, 'activitypub', 'recips', $act->raw_recips); $result = item_store($s); - if($result['success']) { + 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']); + if (intval($parent_item['item_origin'])) + Master::Summon(['Notifier', 'comment-import', $result['item_id']]); + sync_an_item($channel['channel_id'], $result['item_id']); } return; } - - - static function bb_attach($attach,$body) { + static function bb_attach($attach, $body) { $ret = false; - foreach($attach as $a) { - if(strpos($a['type'],'image') !== false) { - if(self::media_not_in_body($a['href'],$body)) { + foreach ($attach as $a) { + if (array_key_exists('type',$a) && stripos($a['type'], 'image') !== false) { + if (self::media_not_in_body($a['href'], $body)) { $ret .= "\n\n" . '[img]' . $a['href'] . '[/img]'; } } - if(array_key_exists('type',$a) && strpos($a['type'], 'video') === 0) { - if(self::media_not_in_body($a['href'],$body)) { + if (array_key_exists('type', $a) && stripos($a['type'], 'video') !== false) { + if (self::media_not_in_body($a['href'], $body)) { $ret .= "\n\n" . '[video]' . $a['href'] . '[/video]'; } } - if(array_key_exists('type',$a) && strpos($a['type'], 'audio') === 0) { - if(self::media_not_in_body($a['href'],$body)) { + if (array_key_exists('type', $a) && stripos($a['type'], 'audio') !== false) { + if (self::media_not_in_body($a['href'], $body)) { $ret .= "\n\n" . '[audio]' . $a['href'] . '[/audio]'; } } @@ -3069,116 +3400,112 @@ class Activity { return $ret; } - // check for the existence of existing media link in body + static function media_not_in_body($s, $body) { - static function media_not_in_body($s,$body) { - - if((strpos($body,']' . $s . '[/img]') === false) && - (strpos($body,']' . $s . '[/zmg]') === false) && - (strpos($body,']' . $s . '[/video]') === false) && - (strpos($body,']' . $s . '[/audio]') === false)) { + if ((strpos($body, ']' . $s . '[/img]') === false) && + (strpos($body, ']' . $s . '[/zmg]') === false) && + (strpos($body, ']' . $s . '[/video]') === false) && + (strpos($body, ']' . $s . '[/audio]') === false)) { return true; } return false; } - - static function bb_content($content,$field) { + static function bb_content($content, $field) { require_once('include/html2bbcode.php'); require_once('include/event.php'); $ret = false; - if(is_array($content[$field])) { - foreach($content[$field] as $k => $v) { - $ret .= html2bbcode($v); - // save this for auto-translate or dynamic filtering - // $ret .= '[language=' . $k . ']' . html2bbcode($v) . '[/language]'; - } - } - else { - if($field === 'bbcode' && array_key_exists('bbcode',$content)) { - $ret = $content[$field]; + if (array_key_exists($field, $content)) { + if (is_array($content[$field])) { + foreach ($content[$field] as $k => $v) { + $ret .= html2bbcode($v); + // save this for auto-translate or dynamic filtering + // $ret .= '[language=' . $k . ']' . html2bbcode($v) . '[/language]'; + } } else { - $ret = html2bbcode($content[$field]); + if ($field === 'bbcode' && array_key_exists('bbcode', $content)) { + $ret = $content[$field]; + } + else { + $ret = html2bbcode($content[$field]); + } } } - if($field === 'content' && $content['event'] && (! strpos($ret,'[event'))) { + + if ($field === 'content' && array_key_exists('event', $content) && (!strpos($ret, '[event'))) { $ret .= format_event_bbcode($content['event']); } return $ret; } - static function get_content($act) { $content = []; - $event = null; + $event = null; - if ((! $act) || (! is_array($act))) { + if ((!$act) || (!is_array($act))) { return $content; } - if($act['type'] === 'Event') { - $adjust = false; - $event = []; - $event['event_hash'] = $act['id']; - if(array_key_exists('startTime',$act) && strpos($act['startTime'],-1,1) === 'Z') { - $adjust = true; - $event['adjust'] = 1; - $event['dtstart'] = datetime_convert('UTC','UTC',$event['startTime'] . (($adjust) ? '' : 'Z')); - } - if(array_key_exists('endTime',$act)) { - $event['dtend'] = datetime_convert('UTC','UTC',$event['endTime'] . (($adjust) ? '' : 'Z')); - } - else { - $event['nofinish'] = true; - } - } - - foreach ([ 'name', 'summary', 'content' ] as $a) { - if (($x = self::get_textfield($act,$a)) !== false) { + if ($act['type'] === 'Event') { + $adjust = false; + $event = []; + $event['event_hash'] = $act['id']; + if (array_key_exists('startTime', $act) && strpos($act['startTime'], -1, 1) === 'Z') { + $adjust = true; + $event['adjust'] = 1; + $event['dtstart'] = datetime_convert('UTC', 'UTC', $event['startTime'] . (($adjust) ? '' : 'Z')); + } + if (array_key_exists('endTime', $act)) { + $event['dtend'] = datetime_convert('UTC', 'UTC', $event['endTime'] . (($adjust) ? '' : 'Z')); + } + else { + $event['nofinish'] = true; + } + } + + foreach (['name', 'summary', 'content'] as $a) { + if (($x = self::get_textfield($act, $a)) !== false) { $content[$a] = $x; } } - if($event) { + if ($event) { $event['summary'] = $content['name']; - if(! $event['summary']) { - if($content['summary']) { + if (!$event['summary']) { + if ($content['summary']) { $event['summary'] = html2plain($content['summary']); } } $event['description'] = html2bbcode($content['content']); - if($event['summary'] && $event['dtstart']) { + if ($event['summary'] && $event['dtstart']) { $content['event'] = $event; } } - if (array_path_exists('source/mediaType',$act) && array_path_exists('source/content',$act)) { + if (array_path_exists('source/mediaType', $act) && array_path_exists('source/content', $act)) { if ($act['source']['mediaType'] === 'text/bbcode') { $content['bbcode'] = purify_html($act['source']['content']); } } - - return $content; } + static function get_textfield($act, $field) { - static function get_textfield($act,$field) { - $content = false; - if(array_key_exists($field,$act) && $act[$field]) + if (array_key_exists($field, $act) && $act[$field]) $content = purify_html($act[$field]); - elseif(array_key_exists($field . 'Map',$act) && $act[$field . 'Map']) { - foreach($act[$field . 'Map'] as $k => $v) { + elseif (array_key_exists($field . 'Map', $act) && $act[$field . 'Map']) { + foreach ($act[$field . 'Map'] as $k => $v) { $content[escape_tags($k)] = purify_html($v); } } @@ -3187,11 +3514,10 @@ class Activity { // 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) + 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 ); @@ -3200,8 +3526,8 @@ class Activity { } } - if (! $auth) { - if (array_key_exists('token',$_REQUEST) && $_REQUEST['token']) { + if (!$auth) { + if (array_key_exists('token', $_REQUEST) && $_REQUEST['token']) { $auth = $_REQUEST['token']; } } @@ -3211,8 +3537,8 @@ class Activity { static function find_best_identity($xchan) { - if(filter_var($xchan, FILTER_VALIDATE_URL)) { - $r = q("select hubloc_hash, hubloc_network from hubloc where hubloc_id_url = '%s' and hubloc_network in ('zot6', 'zot') and hubloc_deleted = 0", + if (filter_var($xchan, FILTER_VALIDATE_URL)) { + $r = q("SELECT hubloc_hash, hubloc_network FROM hubloc WHERE hubloc_id_url = '%s' AND hubloc_network IN ('zot6', 'activitypub') AND hubloc_deleted = 0", dbesc($xchan) ); if ($r) { @@ -3226,4 +3552,67 @@ class Activity { } + static function get_cached_actor($id) { + $actor = XConfig::Get($id,'system', 'actor_record'); + + if ($actor) { + return $actor; + } + + // try other get_cached_actor providers (e.g. diaspora) + $hookdata = [ + 'id' => $id, + 'actor' => false + ]; + + call_hooks('get_cached_actor_provider', $hookdata); + + return $hookdata['actor']; + } + + static function get_actor_hublocs($url, $options = 'all') { + + $hublocs = false; + + switch ($options) { + case 'activitypub': + $hublocs = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash where hubloc_hash = '%s' and hubloc_deleted = 0 ", + dbesc($url) + ); + break; + case 'zot6': + $hublocs = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash where hubloc_id_url = '%s' and hubloc_deleted = 0 ", + dbesc($url) + ); + break; + case 'all': + default: + $hublocs = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash where ( hubloc_id_url = '%s' OR hubloc_hash = '%s' ) and hubloc_deleted = 0 ", + dbesc($url), + dbesc($url) + ); + break; + } + + return $hublocs; + } + + static function get_actor_collections($url) { + $ret = []; + $actor_record = XConfig::Get($url,'system','actor_record'); + if (! $actor_record) { + return $ret; + } + + foreach ( [ 'inbox','outbox','followers','following' ] as $collection) { + if (isset($actor_record[$collection]) && $actor_record[$collection]) { + $ret[$collection] = $actor_record[$collection]; + } + } + if (array_path_exists('endpoints/sharedInbox',$actor_record) && $actor_record['endpoints']['sharedInbox']) { + $ret['sharedInbox'] = $actor_record['endpoints']['sharedInbox']; + } + + return $ret; + } } diff --git a/Zotlabs/Lib/ActivityStreams.php b/Zotlabs/Lib/ActivityStreams.php index a0ba52aa6..fa38c569e 100644 --- a/Zotlabs/Lib/ActivityStreams.php +++ b/Zotlabs/Lib/ActivityStreams.php @@ -7,25 +7,24 @@ namespace Zotlabs\Lib; * * Parses an ActivityStream JSON string. */ - class ActivityStreams { - 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 = 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; /** @@ -37,29 +36,29 @@ class ActivityStreams { */ function __construct($string) { - $this->raw = $string; + $this->raw = $string; - 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 - - 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']) { - $saved = json_encode($this->data,JSON_UNESCAPED_SLASHES); - $this->data = $tmp; - $this->data['signer'] = $ret['signer']; + 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']) { + if ($ret['hubloc']) { $this->data['hubloc'] = $ret['hubloc']; } } @@ -68,57 +67,57 @@ class ActivityStreams { $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']) { + 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; + $this->valid = false; } } } - if($this->is_valid()) { + if ($this->is_valid()) { $this->id = $this->get_property_obj('id'); $this->type = $this->get_primary_type(); - $this->actor = $this->get_actor('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'); $this->recips = $this->collect_recips(); $this->ldsig = $this->get_compound_property('signature'); - 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']) { - $this->sigok = LDSignatures::verify($this->data,$this->signer['publicKey']['publicKeyPem']); + 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']) { + $this->sigok = LDSignatures::verify($this->data, $this->signer['publicKey']['publicKeyPem']); } } - if(! $this->obj) { - $this->obj = $this->data; + if (!$this->obj) { + $this->obj = $this->data; $this->type = 'Create'; - if(! $this->actor) { - $this->actor = $this->get_actor('attributedTo',$this->obj); + if (!$this->actor) { + $this->actor = $this->get_actor('attributedTo', $this->obj); } } // fetch recursive or embedded activities - - if ($this->obj && is_array($this->obj) && array_key_exists('object',$this->obj)) { + + if ($this->obj && is_array($this->obj) && array_key_exists('object', $this->obj)) { $this->obj['object'] = $this->get_compound_property($this->obj['object']); } - 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); + 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)) { + 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']; } } @@ -147,19 +146,19 @@ class ActivityStreams { function collect_recips($base = '', $namespace = '') { $x = []; - $fields = [ 'to', 'cc', 'bto', 'bcc', 'audience']; - foreach($fields as $f) { + $fields = ['to', 'cc', 'bto', 'bcc', 'audience']; + foreach ($fields as $f) { $y = $this->get_compound_property($f, $base, $namespace); - if($y) { - if (! is_array($this->raw_recips)) { + if ($y) { + if (!is_array($this->raw_recips)) { $this->raw_recips = []; } - if (! is_array($y)) { - $y = [ $y ]; + if (!is_array($y)) { + $y = [$y]; } $this->raw_recips[$f] = $y; - $x = array_merge($x, $y); + $x = array_merge($x, $y); } } // not yet ready for prime time @@ -167,21 +166,21 @@ class ActivityStreams { return $x; } - function expand($arr,$base = '',$namespace = '') { + function expand($arr, $base = '', $namespace = '') { $ret = []; // 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) { - $ret = array_merge($ret,$x); + $x = $this->get_compound_property($a, $base, $namespace); + if ($x) { + $ret = array_merge($ret, $x); } } } @@ -202,33 +201,33 @@ class ActivityStreams { */ function get_namespace($base, $namespace) { - if(! $namespace) + if (!$namespace) return ''; $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 = ''; } } @@ -248,14 +247,14 @@ class ActivityStreams { */ 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); + $base = (($base) ? $base : $this->data); $propname = (($prefix) ? $prefix . ':' : '') . $property; - if(! is_array($base)) { - btlogger('not an array: ' . print_r($base,true)); + if (!is_array($base)) { + btlogger('not an array: ' . print_r($base, true)); return null; } @@ -279,14 +278,14 @@ class ActivityStreams { } static function is_an_actor($s) { - return (in_array($s, [ 'Application','Group','Organization','Person','Service' ])); + return (in_array($s, ['Application', 'Group', 'Organization', 'Person', 'Service'])); } static function is_response_activity($s) { - if (! $s) { + if (!$s) { return false; } - return (in_array($s, [ 'Like', 'Dislike', 'Flag', 'Block', 'Announce', 'Accept', 'Reject', 'TentativeAccept', 'TentativeReject', 'emojiReaction', 'EmojiReaction', 'EmojiReact' ])); + return (in_array($s, ['Like', 'Dislike', 'Flag', 'Block', 'Accept', 'Reject', 'TentativeAccept', 'TentativeReject', 'emojiReaction', 'EmojiReaction', 'EmojiReact'])); } /** @@ -298,25 +297,17 @@ class ActivityStreams { * @return NULL|mixed */ - function get_actor($property,$base='',$namespace = '') { + 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; + if ($this->is_url($x)) { + $y = Activity::get_cached_actor($x); + if ($y) { 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->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; @@ -336,7 +327,7 @@ class ActivityStreams { */ 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; @@ -344,23 +335,23 @@ class ActivityStreams { } // verify and unpack JSalmon signature if present - - if(is_array($x) && array_key_exists('signed',$x)) { + + 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']; + 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']) { + 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]; } @@ -374,7 +365,7 @@ class ActivityStreams { * @return boolean */ function is_url($url) { - if(($url) && (! is_array($url)) && (strpos($url, 'http') === 0)) { + if (($url) && (!is_array($url)) && (strpos($url, 'http') === 0)) { return true; } @@ -389,13 +380,13 @@ class ActivityStreams { * @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; } } @@ -409,15 +400,32 @@ class ActivityStreams { return $x; } - static function is_as_request() { + static function is_as_request($channel = null) { + + $hookdata = []; + if ($channel) + $hookdata['channel'] = $channel; + + $hookdata['data'] = ['application/x-zot-activity+json']; + + call_hooks('is_as_request', $hookdata); + + $x = getBestSupportedMimeType($hookdata['data']); + return (($x) ? true : false); + + } + + static function get_accept_header_string($channel = null) { + + $hookdata = []; + if ($channel) + $hookdata['channel'] = $channel; + + $hookdata['data'] = 'application/x-zot-activity+json'; - $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"' - ]); + call_hooks('get_accept_header_string', $hookdata); - return(($x) ? true : false); + return $hookdata['data']; } diff --git a/Zotlabs/Lib/Apps.php b/Zotlabs/Lib/Apps.php index 7b980b8d3..2c5b8a546 100644 --- a/Zotlabs/Lib/Apps.php +++ b/Zotlabs/Lib/Apps.php @@ -2,7 +2,7 @@ namespace Zotlabs\Lib; -use Zotlabs\Lib\Libsync; +use App; require_once('include/plugin.php'); require_once('include/channel.php'); @@ -21,9 +21,10 @@ class Apps { * @brief * * @param boolean $translate (optional) default true + * @param boolean $sync (optional) default false used if called from sync_sysapps() * @return array */ - static public function get_system_apps($translate = true) { + static public function get_system_apps($translate = true, $sync = false) { $ret = []; if(is_dir('apps')) @@ -33,7 +34,7 @@ class Apps { if($files) { foreach($files as $f) { - $x = self::parse_app_description($f,$translate); + $x = self::parse_app_description($f, $translate, $sync); if($x) { $ret[] = $x; } @@ -45,7 +46,7 @@ class Apps { $path = explode('/',$f); $plugin = trim($path[1]); if(plugin_is_installed($plugin)) { - $x = self::parse_app_description($f,$translate); + $x = self::parse_app_description($f, $translate, $sync); if($x) { $x['plugin'] = $plugin; $ret[] = $x; @@ -76,7 +77,9 @@ class Apps { 'Directory', 'Search', 'Help', - 'Profile Photo' + 'Profile Photo', + 'HQ', + 'Post' ]); /** @@ -209,7 +212,7 @@ class Apps { * @param boolean $translate (optional) default true * @return boolean|array */ - static public function parse_app_description($f, $translate = true) { + static public function parse_app_description($f, $translate = true, $sync = false) { $ret = []; $matches = []; @@ -255,7 +258,7 @@ class Apps { if(array_key_exists('categories',$ret)) $ret['categories'] = str_replace(array('\'','"'),array(''','&dquot;'),$ret['categories']); - if(array_key_exists('requires',$ret)) { + if(array_key_exists('requires',$ret) && !$sync) { $requires = explode(',',$ret['requires']); foreach($requires as $require) { $require = trim(strtolower($require)); @@ -307,14 +310,16 @@ class Apps { } } } - if($ret) { - if($translate) - self::translate_system_apps($ret); - return $ret; + if(empty($ret)) { + return false; } - return false; + if($translate) { + self::translate_system_apps($ret); + } + + return $ret; } @@ -374,7 +379,7 @@ class Apps { 'Permission Categories' => t('Permission Categories'), 'Public Stream' => t('Public Stream'), 'My Chatrooms' => t('My Chatrooms'), - 'Channel Export' => t('Channel Export') + 'Channel Export' => t('Channel Export'), ); if(array_key_exists('name',$arr)) { @@ -524,7 +529,7 @@ class Apps { } elseif(remote_channel()) { $observer = \App::get_observer(); - if($observer && $observer['xchan_network'] === 'zot') { + if($observer && $observer['xchan_network'] === 'zot6') { // some folks might have xchan_url redirected offsite, use the connurl $x = parse_url($observer['xchan_connurl']); if($x) { @@ -536,13 +541,47 @@ class Apps { $install_action = (($installed) ? t('Update') : t('Install')); $icon = ((strpos($papp['photo'],'icon:') === 0) ? substr($papp['photo'],5) : ''); + if (!$installed && $mode === 'module') { + $_SESSION['return_url'] = App::$query_string; + return replace_macros(get_markup_template('app_install.tpl'), [ + '$papp' => $papp, + '$install' => $install_action + ]); + } + if($mode === 'navbar') { + return replace_macros(get_markup_template('app_nav_pinned.tpl'),array( + '$app' => $papp, + '$icon' => $icon, + )); + } + + if($mode === 'nav') { return replace_macros(get_markup_template('app_nav.tpl'),array( '$app' => $papp, '$icon' => $icon, )); } + if($mode === 'inline') { + return replace_macros(get_markup_template('app_inline.tpl'),array( + '$app' => $papp, + '$icon' => $icon, + '$installed' => $installed, + '$purchase' => ((isset($papp['page']) && (! $installed)) ? t('Purchase') : ''), + '$action_label' => $install_action + )); + } + + if(in_array($mode, ['nav-order', 'nav-order-pinned'])) { + return replace_macros(get_markup_template('app_order.tpl'),array( + '$app' => $papp, + '$icon' => $icon, + '$hosturl' => $hosturl, + '$mode' => $mode + )); + } + if($mode === 'install') { $papp['embed'] = true; } @@ -551,7 +590,7 @@ class Apps { '$app' => $papp, '$icon' => $icon, '$hosturl' => $hosturl, - '$purchase' => (($papp['page'] && (! $installed)) ? t('Purchase') : ''), + '$purchase' => ((isset($papp['page']) && (! $installed)) ? t('Purchase') : ''), '$installed' => $installed, '$action_label' => (($hosturl && in_array($mode, ['view','install'])) ? $install_action : ''), '$edit' => ((local_channel() && $installed && $mode == 'edit') ? t('Edit') : ''), @@ -559,12 +598,10 @@ class Apps { '$undelete' => ((local_channel() && $mode == 'edit') ? t('Undelete') : ''), '$settings_url' => ((local_channel() && $installed && $mode == 'list') ? $papp['settings_url'] : ''), '$deleted' => $papp['deleted'], - '$feature' => (($papp['embed'] || $mode == 'edit') ? false : true), - '$pin' => (($papp['embed'] || $mode == 'edit') ? false : true), + '$feature' => ((isset($papp['embed']) || $mode == 'edit') ? false : true), + '$pin' => ((isset($papp['embed']) || $mode == 'edit') ? false : true), '$featured' => ((strpos($papp['categories'], 'nav_featured_app') === false) ? false : true), '$pinned' => ((strpos($papp['categories'], 'nav_pinned_app') === false) ? false : true), - '$navapps' => (($mode == 'nav') ? true : false), - '$order' => (($mode === 'nav-order' || $mode === 'nav-order-pinned') ? true : false), '$mode' => $mode, '$add' => t('Add to app-tray'), '$remove' => t('Remove from app-tray'), @@ -574,6 +611,7 @@ class Apps { )); } + static public function app_install($uid,$app) { if(! is_array($app)) { @@ -588,10 +626,12 @@ class Apps { $app['uid'] = $uid; - if(self::app_installed($uid,$app,true)) + if(self::app_installed($uid,$app,true)) { $x = self::app_update($app); - else + } + else { $x = self::app_store($app); + } if($x['success']) { $r = q("select * from app where app_id = '%s' and app_channel = %d limit 1", @@ -599,13 +639,12 @@ class Apps { intval($uid) ); if($r) { - if(($app['uid']) && (! $r[0]['app_system'])) { + if($app['uid']) { if($app['categories'] && (! $app['term'])) { $r[0]['term'] = q("select * from term where otype = %d and oid = %d", intval(TERM_OBJ_APP), intval($r[0]['id']) ); - Libsync::build_sync_packet($uid,array('app' => $r[0])); } } } @@ -634,6 +673,7 @@ class Apps { } } } + return true; } @@ -645,38 +685,35 @@ class Apps { dbesc($app['guid']), intval($uid) ); - if($x) { - if(! intval($x[0]['app_deleted'])) { - $x[0]['app_deleted'] = 1; - if(self::can_delete($uid,$app)) { - q("delete from app where app_id = '%s' and app_channel = %d", - dbesc($app['guid']), - intval($uid) - ); - q("delete from term where otype = %d and oid = %d", - 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 { - q("update app set app_deleted = 1 where app_id = '%s' and app_channel = %d", - dbesc($app['guid']), - intval($uid) - ); - } - if(! intval($x[0]['app_system'])) { - Libsync::build_sync_packet($uid,array('app' => $x)); - } - } - else { - self::app_undestroy($uid,$app); - } + + if($x && intval($x[0]['app_deleted'])) { + self::app_undestroy($uid, $app); + return; + } + + if(self::can_delete($uid,$app)) { + q("delete from app where app_id = '%s' and app_channel = %d", + dbesc($app['guid']), + intval($uid) + ); + + q("delete from term where otype = %d and oid = %d", + 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 { + q("update app set app_deleted = 1 where app_id = '%s' and app_channel = %d", + dbesc($app['guid']), + intval($uid) + ); } } } @@ -693,13 +730,11 @@ class Apps { dbesc($app['guid']), intval($uid) ); - if($x) { - if($x[0]['app_system']) { - q("update app set app_deleted = 0 where app_id = '%s' and app_channel = %d", - dbesc($app['guid']), - intval($uid) - ); - } + if($x && intval($x[0]['app_deleted']) && $x[0]['app_system']) { + q("update app set app_deleted = 0 where app_id = '%s' and app_channel = %d", + dbesc($app['guid']), + intval($uid) + ); } } } @@ -1276,58 +1311,58 @@ class Apps { $ret['type'] = 'personal'; - if($app['app_id']) + if(!empty($app['app_id'])) $ret['guid'] = $app['app_id']; - if($app['app_sig']) + if(!empty($app['app_sig'])) $ret['sig'] = $app['app_sig']; - if($app['app_author']) + if(!empty($app['app_author'])) $ret['author'] = $app['app_author']; - if($app['app_name']) + if(!empty($app['app_name'])) $ret['name'] = $app['app_name']; - if($app['app_desc']) + if(!empty($app['app_desc'])) $ret['desc'] = $app['app_desc']; - if($app['app_url']) + if(!empty($app['app_url'])) $ret['url'] = $app['app_url']; - if($app['app_photo']) + if(!empty($app['app_photo'])) $ret['photo'] = $app['app_photo']; - if($app['app_icon']) + if(!empty($app['app_icon'])) $ret['icon'] = $app['app_icon']; - if($app['app_version']) + if(!empty($app['app_version'])) $ret['version'] = $app['app_version']; - if($app['app_addr']) + if(!empty($app['app_addr'])) $ret['addr'] = $app['app_addr']; - if($app['app_price']) + if(!empty($app['app_price'])) $ret['price'] = $app['app_price']; - if($app['app_page']) + if(!empty($app['app_page'])) $ret['page'] = $app['app_page']; - if($app['app_requires']) + if(!empty($app['app_requires'])) $ret['requires'] = $app['app_requires']; - if($app['app_system']) + if(!empty($app['app_system'])) $ret['system'] = $app['app_system']; - if($app['app_options']) + if(!empty($app['app_options'])) $ret['options'] = $app['app_options']; - if($app['app_plugin']) + if(!empty($app['app_plugin'])) $ret['plugin'] = trim($app['app_plugin']); - if($app['app_deleted']) + if(!empty($app['app_deleted'])) $ret['deleted'] = $app['app_deleted']; - if($app['term']) { + if(!empty($app['term']) && is_array($app['term'])) { $s = ''; foreach($app['term'] as $t) { if($s) @@ -1356,4 +1391,17 @@ class Apps { return chunk_split(base64_encode(json_encode($papp)),72,"\n"); } + static public function get_papp($app) { + + $r = q("select * from app where app_id = '%s' and app_channel = 0 limit 1", + dbesc(hash('whirlpool', $app)) + ); + + if ($r) { + $papp = self::app_encode($r[0]); + return $papp; + } + + return false; + } } diff --git a/Zotlabs/Lib/Connect.php b/Zotlabs/Lib/Connect.php index 481b02ce2..38fe69995 100644 --- a/Zotlabs/Lib/Connect.php +++ b/Zotlabs/Lib/Connect.php @@ -146,7 +146,7 @@ class Connect { } - $allowed = ((in_array($xchan['xchan_network'],['rss','zot','zot6'])) ? 1 : 0); + $allowed = ((in_array($xchan['xchan_network'],['rss', 'zot6'])) ? 1 : 0); $hookdata = ['channel_id' => $uid, 'follow_address' => $url, 'xchan' => $xchan, 'allowed' => $allowed, 'singleton' => 0]; call_hooks('follow_allow',$hookdata); @@ -207,13 +207,13 @@ class Connect { } $my_perms = $p['perms']; - + $profile_assign = get_pconfig($uid,'system','profile_assign',''); // See if we are already connected by virtue of having an abook record - $r = q("select abook_id, abook_xchan, abook_pending, abook_instance from abook + $r = q("select abook_id, abook_xchan, abook_pending, abook_instance from abook where abook_xchan = '%s' and abook_channel = %d limit 1", dbesc($xchan_hash), intval($uid) @@ -282,7 +282,7 @@ class Connect { // fetch the entire record - $r = q("select abook.*, xchan.* from abook left join xchan on abook_xchan = xchan_hash + $r = q("select abook.*, xchan.* from abook left join xchan on abook_xchan = xchan_hash where abook_xchan = '%s' and abook_channel = %d limit 1", dbesc($xchan_hash), intval($uid) diff --git a/Zotlabs/Lib/Crypto.php b/Zotlabs/Lib/Crypto.php new file mode 100644 index 000000000..f1794ae64 --- /dev/null +++ b/Zotlabs/Lib/Crypto.php @@ -0,0 +1,206 @@ +<?php + +namespace Zotlabs\Lib; + +use Exception; + +class Crypto { + + public static $openssl_algorithms = [ + + // zot6 nickname, opensslname, keylength, ivlength + + ['aes256ctr', 'aes-256-ctr', 32, 16], + ['camellia256cfb', 'camellia-256-cfb', 32, 16], + ['cast5cfb', 'cast5-cfb', 16, 8], + ['aes256cbc', 'aes-256-cbc', 32, 16] // remove after legacy zot has been sunset + + ]; + + public static function methods() { + $ret = []; + + foreach (self::$openssl_algorithms as $ossl) { + $ret[] = $ossl[0] . '.oaep'; + } + + call_hooks('crypto_methods', $ret); + return $ret; + } + + public static function signing_methods() { + + $ret = ['sha256']; + call_hooks('signing_methods', $ret); + return $ret; + + } + + public static function new_keypair($bits) { + + $openssl_options = [ + 'digest_alg' => 'sha1', + 'private_key_bits' => $bits, + 'encrypt_key' => false + ]; + + $conf = get_config('system', 'openssl_conf_file'); + + if ($conf) { + $openssl_options['config'] = $conf; + } + + $result = openssl_pkey_new($openssl_options); + + if (empty($result)) { + return false; + } + + // Get private key + + $response = ['prvkey' => '', 'pubkey' => '']; + + openssl_pkey_export($result, $response['prvkey']); + + // Get public key + $pkey = openssl_pkey_get_details($result); + $response['pubkey'] = $pkey["key"]; + + return $response; + + } + + public static function sign($data, $key, $alg = 'sha256') { + + if (!$key) { + return false; + } + + $sig = ''; + openssl_sign($data, $sig, $key, $alg); + return $sig; + } + + public static function verify($data, $sig, $key, $alg = 'sha256') { + + if (!$key) { + return false; + } + + try { + $verify = openssl_verify($data, $sig, $key, $alg); + } catch (Exception $e) { + $verify = (-1); + } + + 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); + } + + return (($verify > 0) ? true : false); + } + + public static function encapsulate($data, $pubkey, $alg) { + + if (!($alg && $pubkey)) { + return $data; + } + + $alg_base = $alg; + $padding = OPENSSL_PKCS1_PADDING; + + $exts = explode('.', $alg); + if (count($exts) > 1) { + switch ($exts[1]) { + case 'oaep': + $padding = OPENSSL_PKCS1_OAEP_PADDING; + break; + default: + break; + } + $alg_base = $exts[0]; + } + + $method = null; + + foreach (self::$openssl_algorithms as $ossl) { + if ($ossl[0] === $alg_base) { + $method = $ossl; + break; + } + } + + if ($method) { + $result = ['encrypted' => true]; + + $key = openssl_random_pseudo_bytes(256); + $iv = openssl_random_pseudo_bytes(256); + + $key1 = substr($key, 0, $method[2]); + $iv1 = substr($iv, 0, $method[3]); + + $result['data'] = base64url_encode(openssl_encrypt($data, $method[1], $key1, OPENSSL_RAW_DATA, $iv1), true); + + openssl_public_encrypt($key, $k, $pubkey, $padding); + openssl_public_encrypt($iv, $i, $pubkey, $padding); + + $result['alg'] = $alg; + $result['key'] = base64url_encode($k, true); + $result['iv'] = base64url_encode($i, true); + return $result; + + } + else { + $x = ['data' => $data, 'pubkey' => $pubkey, 'alg' => $alg, 'result' => $data]; + call_hooks('crypto_encapsulate', $x); + return $x['result']; + } + } + + public static function unencapsulate($data, $prvkey) { + + if (!(is_array($data) && array_key_exists('encrypted', $data) && array_key_exists('alg', $data) && $data['alg'])) { + logger('not encrypted'); + + return $data; + } + + $alg_base = $data['alg']; + $padding = OPENSSL_PKCS1_PADDING; + + $exts = explode('.', $data['alg']); + if (count($exts) > 1) { + switch ($exts[1]) { + case 'oaep': + $padding = OPENSSL_PKCS1_OAEP_PADDING; + break; + default: + break; + } + $alg_base = $exts[0]; + } + + $method = null; + + foreach (self::$openssl_algorithms as $ossl) { + if ($ossl[0] === $alg_base) { + $method = $ossl; + break; + } + } + + if ($method) { + openssl_private_decrypt(base64url_decode($data['key']), $k, $prvkey, $padding); + openssl_private_decrypt(base64url_decode($data['iv']), $i, $prvkey, $padding); + return openssl_decrypt(base64url_decode($data['data']), $method[1], substr($k, 0, $method[2]), OPENSSL_RAW_DATA, substr($i, 0, $method[3])); + } + else { + $x = ['data' => $data, 'prvkey' => $prvkey, 'alg' => $data['alg'], 'result' => $data]; + call_hooks('crypto_unencapsulate', $x); + return $x['result']; + } + } +} diff --git a/Zotlabs/Lib/DReport.php b/Zotlabs/Lib/DReport.php index 7515d3292..2263529b2 100644 --- a/Zotlabs/Lib/DReport.php +++ b/Zotlabs/Lib/DReport.php @@ -87,8 +87,7 @@ class DReport { // Is the sender one of our channels? - $c = q("select channel_id from channel where channel_hash = '%s' or channel_portable_id = '%s' limit 1", - dbesc($dr['sender']), + $c = q("select channel_id from channel where channel_hash = '%s' limit 1", dbesc($dr['sender']) ); diff --git a/Zotlabs/Lib/Enotify.php b/Zotlabs/Lib/Enotify.php index c78325ee3..d02dab739 100644 --- a/Zotlabs/Lib/Enotify.php +++ b/Zotlabs/Lib/Enotify.php @@ -43,7 +43,7 @@ class Enotify { dbesc($params['to_xchan']) ); } - if ($x & $y) { + if ($x && $y) { $sender = $x[0]; $recip = $y[0]; } else { @@ -64,29 +64,29 @@ class Enotify { $sitename = get_config('system','sitename'); $site_admin = sprintf( t('%s Administrator'), $sitename); $opt_out1 = sprintf( t('This email was sent by %1$s at %2$s.'), t('$Projectname'), \App::get_hostname()); - $opt_out2 = sprintf( t('To stop receiving these messages, please adjust your Notification Settings at %s'), z_root() . '/settings'); + $opt_out2 = sprintf( t('To stop receiving these messages, please adjust your Notification Settings at %s'), z_root() . '/settings'); $hopt_out2 = sprintf( t('To stop receiving these messages, please adjust your %s.'), '<a href="' . z_root() . '/settings' . '">' . t('Notification Settings') . '</a>'); $sender_name = $product; $hostname = \App::get_hostname(); if(strpos($hostname,':')) - $hostname = substr($hostname,0,strpos($hostname,':')); + $hostname = substr($hostname, 0, strpos($hostname,':')); // Do not translate 'noreply' as it must be a legal 7-bit email address - $reply_email = get_config('system','reply_address'); + $reply_email = get_config('system', 'reply_address'); if(! $reply_email) $reply_email = 'noreply' . '@' . $hostname; - $sender_email = get_config('system','from_email'); + $sender_email = get_config('system', 'from_email'); if(! $sender_email) $sender_email = 'Administrator' . '@' . $hostname; - - $sender_name = get_config('system','from_email_name'); + + $sender_name = get_config('system', 'from_email_name'); if(! $sender_name) $sender_name = \Zotlabs\Lib\System::get_site_name(); - $additional_mail_header = ""; + $additional_mail_header = ''; if(array_key_exists('item', $params)) { require_once('include/conversation.php'); @@ -108,33 +108,34 @@ class Enotify { logger('notification invoked for an old item which may have been refetched.',LOGGER_DEBUG,LOG_INFO); return; } - } + } else { $title = $body = ''; } - $always_show_in_notices = get_pconfig($recip['channel_id'],'system','always_show_in_notices'); - $vnotify = get_pconfig($recip['channel_id'],'system','vnotify'); + $always_show_in_notices = get_pconfig($recip['channel_id'], 'system', 'always_show_in_notices'); + $vnotify = get_pconfig($recip['channel_id'], 'system', 'vnotify'); $salutation = $recip['channel_name']; // e.g. "your post", "David's photo", etc. $possess_desc = t('%s <!item_type!>'); +// @@TODO: consider using switch instead of those elseif if ($params['type'] == NOTIFY_MAIL) { logger('notification: mail'); - $subject = sprintf( t('[$Projectname:Notify] New mail received at %s'),$sitename); - - $preamble = sprintf( t('%1$s sent you a new private message at %2$s.'), $sender['xchan_name'],$sitename); - $epreamble = sprintf( t('%1$s sent you %2$s.'),'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', '[zrl=$itemlink]' . t('a private message') . '[/zrl]'); - $sitelink = t('Please visit %s to view and/or reply to your private messages.'); - $tsitelink = sprintf( $sitelink, $siteurl . '/mail/' . $params['item']['id'] ); - $hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '/mail/' . $params['item']['id'] . '">' . $sitename . '</a>'); - $itemlink = $siteurl . '/mail/' . $params['item']['id']; + $subject = sprintf( t('[$Projectname:Notify] New direct message received at %s'), $sitename); + + $preamble = sprintf( t('%1$s sent you a new direct message at %2$s'), $sender['xchan_name'], $sitename); + $epreamble = sprintf( t('%1$s sent you %2$s.'), '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', '[zrl=$itemlink]' . t('a direct message') . '[/zrl]'); + $sitelink = t('Please visit %s to view and/or reply to your direct messages.'); + $tsitelink = sprintf( $sitelink, $siteurl . '/hq/' . gen_link_id($params['item']['mid'])); + $hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '/hq/' . gen_link_id($params['item']['mid']) . '">' . $sitename . '</a>'); + $itemlink = $siteurl . '/hq/' . gen_link_id($params['item']['mid']); } - if ($params['type'] == NOTIFY_COMMENT) { + elseif ($params['type'] === NOTIFY_COMMENT) { //logger("notification: params = " . print_r($params, true), LOGGER_DEBUG); $moderated = (($params['item']['item_blocked'] == ITEM_MODERATED) ? true : false); @@ -171,7 +172,6 @@ class Enotify { // Check to see if there was already a notify for this post. // If so don't create a second notification - $p = null; $p = q("select id from notify where link = '%s' and uid = %d limit 1", dbesc($params['link']), intval($recip['channel_id']) @@ -181,7 +181,7 @@ class Enotify { pop_lang(); return; } - + // if it's a post figure out who's post it is. @@ -196,6 +196,7 @@ class Enotify { xchan_query($p); +//@@FIXME $p can be null (line 188) $item_post_type = item_post_type($p[0]); // $private = $p[0]['item_private']; $parent_id = $p[0]['id']; @@ -219,7 +220,7 @@ class Enotify { $itemlink, $p[0]['author']['xchan_name'], $item_post_type); - + // "your post" if($p[0]['owner']['xchan_name'] == $p[0]['author']['xchan_name'] && intval($p[0]['item_wall'])) $dest_str = sprintf(t('%1$s %2$s [zrl=%3$s]your %4$s[/zrl]'), @@ -230,15 +231,15 @@ class Enotify { // Some mail softwares relies on subject field for threading. // So, we cannot have different subjects for notifications of the same thread. - // Before this we have the name of the replier on the subject rendering + // Before this we have the name of the replier on the subject rendering // differents subjects for messages on the same thread. if($moderated) $subject = sprintf( t('[$Projectname:Notify] Moderated Comment to conversation #%1$d by %2$s'), $parent_id, $sender['xchan_name']); else $subject = sprintf( t('[$Projectname:Notify] Comment to conversation #%1$d by %2$s'), $parent_id, $sender['xchan_name']); - $preamble = sprintf( t('%1$s commented on an item/conversation you have been following.'), $sender['xchan_name']); - $epreamble = $dest_str; + $preamble = sprintf( t('%1$s commented on an item/conversation you have been following'), $sender['xchan_name']); + $epreamble = $dest_str; $sitelink = t('Please visit %s to view and/or reply to the conversation.'); $tsitelink = sprintf( $sitelink, $siteurl ); @@ -247,10 +248,10 @@ class Enotify { $tsitelink .= "\n\n" . sprintf( t('Please visit %s to approve or reject this comment.'), z_root() . '/moderate' ); $hsitelink .= "<br><br>" . sprintf( t('Please visit %s to approve or reject this comment.'), '<a href="' . z_root() . '/moderate">' . z_root() . '/moderate</a>' ); } - + } - if ($params['type'] == NOTIFY_LIKE) { + elseif ($params['type'] === NOTIFY_LIKE) { // logger("notification: params = " . print_r($params, true), LOGGER_DEBUG); $itemlink = $params['link']; @@ -268,7 +269,6 @@ class Enotify { // Check to see if there was already a notify for this post. // If so don't create a second notification - $p = null; $p = q("select id from notify where link = '%s' and uid = %d limit 1", dbesc($params['link']), intval($recip['channel_id']) @@ -278,7 +278,7 @@ class Enotify { pop_lang(); return; } - + // if it's a post figure out who's post it is. @@ -293,7 +293,7 @@ class Enotify { xchan_query($p); - +//@@FIXME $p can be null (line 285) $item_post_type = item_post_type($p[0]); // $private = $p[0]['item_private']; $parent_id = $p[0]['id']; @@ -302,7 +302,7 @@ class Enotify { // "your post" - if($p[0]['owner']['xchan_name'] == $p[0]['author']['xchan_name'] && intval($p[0]['item_wall'])) + if($p[0]['owner']['xchan_name'] === $p[0]['author']['xchan_name'] && intval($p[0]['item_wall'])) $dest_str = sprintf(t('%1$s liked [zrl=%2$s]your %3$s[/zrl]'), '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', $itemlink, @@ -314,12 +314,12 @@ class Enotify { // Some mail softwares relies on subject field for threading. // So, we cannot have different subjects for notifications of the same thread. - // Before this we have the name of the replier on the subject rendering + // Before this we have the name of the replier on the subject rendering // differents subjects for messages on the same thread. $subject = sprintf( t('[$Projectname:Notify] Like received to conversation #%1$d by %2$s'), $parent_id, $sender['xchan_name']); - $preamble = sprintf( t('%1$s liked an item/conversation you created.'), $sender['xchan_name']); - $epreamble = $dest_str; + $preamble = sprintf( t('%1$s liked an item/conversation you created'), $sender['xchan_name']); + $epreamble = $dest_str; $sitelink = t('Please visit %s to view and/or reply to the conversation.'); $tsitelink = sprintf( $sitelink, $siteurl ); @@ -328,14 +328,14 @@ class Enotify { - if($params['type'] == NOTIFY_WALL) { + elseif($params['type'] === NOTIFY_WALL) { $subject = sprintf( t('[$Projectname:Notify] %s posted to your profile wall') , $sender['xchan_name']); $preamble = sprintf( t('%1$s posted to your profile wall at %2$s') , $sender['xchan_name'], $sitename); $epreamble = sprintf( t('%1$s posted to [zrl=%2$s]your wall[/zrl]') , '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', - $params['link']); + $params['link']); $sitelink = t('Please visit %s to view and/or reply to the conversation.'); $tsitelink = sprintf( $sitelink, $siteurl ); @@ -343,9 +343,8 @@ class Enotify { $itemlink = $params['link']; } - if ($params['type'] == NOTIFY_TAGSELF) { + elseif ($params['type'] === NOTIFY_TAGSELF) { - $p = null; $p = q("select id from notify where link = '%s' and uid = %d limit 1", dbesc($params['link']), intval($recip['channel_id']) @@ -355,12 +354,12 @@ class Enotify { pop_lang(); return; } - + $subject = sprintf( t('[$Projectname:Notify] %s tagged you') , $sender['xchan_name']); $preamble = sprintf( t('%1$s tagged you at %2$s') , $sender['xchan_name'], $sitename); $epreamble = sprintf( t('%1$s [zrl=%2$s]tagged you[/zrl].') , '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', - $params['link']); + $params['link']); $sitelink = t('Please visit %s to view and/or reply to the conversation.'); $tsitelink = sprintf( $sitelink, $siteurl ); @@ -368,12 +367,12 @@ class Enotify { $itemlink = $params['link']; } - if ($params['type'] == NOTIFY_POKE) { + elseif ($params['type'] === NOTIFY_POKE) { $subject = sprintf( t('[$Projectname:Notify] %1$s poked you') , $sender['xchan_name']); $preamble = sprintf( t('%1$s poked you at %2$s') , $sender['xchan_name'], $sitename); $epreamble = sprintf( t('%1$s [zrl=%2$s]poked you[/zrl].') , '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', - $params['link']); + $params['link']); $subject = str_replace('poked', t($params['activity']), $subject); $preamble = str_replace('poked', t($params['activity']), $preamble); @@ -385,12 +384,12 @@ class Enotify { $itemlink = $params['link']; } - if ($params['type'] == NOTIFY_TAGSHARE) { + elseif ($params['type'] === NOTIFY_TAGSHARE) { $subject = sprintf( t('[$Projectname:Notify] %s tagged your post') , $sender['xchan_name']); $preamble = sprintf( t('%1$s tagged your post at %2$s'),$sender['xchan_name'], $sitename); $epreamble = sprintf( t('%1$s tagged [zrl=%2$s]your post[/zrl]') , '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', - $itemlink); + $itemlink); $sitelink = t('Please visit %s to view and/or reply to the conversation.'); $tsitelink = sprintf( $sitelink, $siteurl ); @@ -398,12 +397,12 @@ class Enotify { $itemlink = $params['link']; } - if ($params['type'] == NOTIFY_INTRO) { + elseif ($params['type'] === NOTIFY_INTRO) { $subject = sprintf( t('[$Projectname:Notify] Introduction received')); - $preamble = sprintf( t('You\'ve received an new connection request from \'%1$s\' at %2$s'), $sender['xchan_name'], $sitename); + $preamble = sprintf( t('You\'ve received an new connection request from \'%1$s\' at %2$s'), $sender['xchan_name'], $sitename); $epreamble = sprintf( t('You\'ve received [zrl=%1$s]a new connection request[/zrl] from %2$s.'), $siteurl . '/connections/ifpending', - '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]'); + '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]'); $body = sprintf( t('You may visit their profile at %s'),$sender['xchan_url']); $sitelink = t('Please visit %s to approve or reject the connection request.'); @@ -412,13 +411,13 @@ class Enotify { $itemlink = $params['link']; } - if ($params['type'] == NOTIFY_SUGGEST) { + elseif ($params['type'] === NOTIFY_SUGGEST) { $subject = sprintf( t('[$Projectname:Notify] Friend suggestion received')); - $preamble = sprintf( t('You\'ve received a friend suggestion from \'%1$s\' at %2$s'), $sender['xchan_name'], $sitename); + $preamble = sprintf( t('You\'ve received a friend suggestion from \'%1$s\' at %2$s'), $sender['xchan_name'], $sitename); $epreamble = sprintf( t('You\'ve received [zrl=%1$s]a friend suggestion[/zrl] for %2$s from %3$s.'), $itemlink, '[zrl=' . $params['item']['url'] . ']' . $params['item']['name'] . '[/zrl]', - '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]'); + '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]'); $body = t('Name:') . ' ' . $params['item']['name'] . "\n"; $body .= t('Photo:') . ' ' . $params['item']['photo'] . "\n"; @@ -430,11 +429,11 @@ class Enotify { $itemlink = $params['link']; } - if ($params['type'] == NOTIFY_CONFIRM) { + elseif ($params['type'] === NOTIFY_CONFIRM) { // ? } - if ($params['type'] == NOTIFY_SYSTEM) { + elseif ($params['type'] === NOTIFY_SYSTEM) { // ? } @@ -462,7 +461,7 @@ class Enotify { $sitelink = $h['sitelink']; $tsitelink = $h['tsitelink']; $hsitelink = $h['hsitelink']; - $itemlink = $h['itemlink']; + $itemlink = $h['itemlink']; require_once('include/html2bbcode.php'); @@ -477,7 +476,7 @@ class Enotify { } while ($dups === true); - $datarray = array(); + $datarray = []; $datarray['hash'] = $hash; $datarray['sender_hash'] = $sender['xchan_hash']; $datarray['xname'] = $sender['xchan_name']; @@ -510,11 +509,11 @@ class Enotify { // Mark some notifications as seen right away // Note! The notification have to be created, because they are used to send emails // So easiest solution to hide them from Notices is to mark them as seen right away. - // Another option would be to not add them to the DB, and change how emails are handled + // Another option would be to not add them to the DB, and change how emails are handled // (probably would be better that way) if (!$always_show_in_notices) { - if (($params['type'] == NOTIFY_WALL) || ($params['type'] == NOTIFY_MAIL) || ($params['type'] == NOTIFY_INTRO)) { + if (($params['type'] === NOTIFY_WALL) || ($params['type'] === NOTIFY_MAIL) || ($params['type'] === NOTIFY_INTRO)) { $seen = 1; } } @@ -550,12 +549,12 @@ class Enotify { } $itemlink = z_root() . '/notify/view/' . $notify_id; - $msg = str_replace('$itemlink',$itemlink,$epreamble); + $msg = str_replace('$itemlink', $itemlink, $epreamble); // wretched hack, but we don't want to duplicate all the preamble variations and we also don't want to screw up a translation if ((\App::$language === 'en' || (! \App::$language)) && strpos($msg,', ')) - $msg = substr($msg,strpos($msg,', ')+1); + $msg = substr($msg, strpos($msg,', ')+1); $datarray['id'] = $notify_id; $datarray['msg'] = $msg; @@ -575,7 +574,7 @@ class Enotify { logger('notification: sending notification email'); - $hn = get_pconfig($recip['channel_id'],'system','email_notify_host'); + $hn = get_pconfig($recip['channel_id'], 'system', 'email_notify_host'); if($hn && (! stristr(\App::get_hostname(),$hn))) { // this isn't the email notification host pop_lang(); @@ -584,15 +583,15 @@ class Enotify { $textversion = strip_tags(html_entity_decode(bbcode(stripslashes(str_replace(array("\\r", "\\n"), array( "", "\n"), $body))),ENT_QUOTES,'UTF-8')); - $htmlversion = bbcode(stripslashes(str_replace(array("\\r","\\n"), array("","<br />\n"),$body))); + $htmlversion = bbcode(stripslashes(str_replace(array("\\r","\\n"), array('',"<br />\n"),$body))); - // use $_SESSION['zid_override'] to force zid() to use + // use $_SESSION['zid_override'] to force zid() to use // the recipient address instead of the current observer $_SESSION['zid_override'] = channel_reddress($recip); $_SESSION['zrl_override'] = z_root() . '/channel/' . $recip['channel_address']; - + $textversion = zidify_links($textversion); $htmlversion = zidify_links($htmlversion); @@ -601,7 +600,7 @@ class Enotify { unset($_SESSION['zid_override']); unset($_SESSION['zrl_override']); - $datarray = array(); + $datarray = []; $datarray['banner'] = $banner; $datarray['product'] = $product; $datarray['preamble'] = $preamble; @@ -754,21 +753,21 @@ class Enotify { return $params['result']; } - $fromName = email_header_encode(html_entity_decode($params['fromName'],ENT_QUOTES,'UTF-8'),'UTF-8'); + $fromName = email_header_encode(html_entity_decode($params['fromName'],ENT_QUOTES,'UTF-8'),'UTF-8'); $messageSubject = email_header_encode(html_entity_decode($params['messageSubject'],ENT_QUOTES,'UTF-8'),'UTF-8'); // generate a mime boundary - $mimeBoundary = rand(0, 9) . "-" - .rand(100000000, 999999999) . "-" - .rand(100000000, 999999999) . "=:" + $mimeBoundary = rand(0, 9) . '-' + .rand(100000000, 999999999) . '-' + .rand(100000000, 999999999) . '=:' .rand(10000, 99999); // generate a multipart/alternative message header $messageHeader = $params['additionalMailHeader'] . "From: $fromName <{$params['fromEmail']}>" . PHP_EOL . - "Reply-To: $fromName <{$params['replyTo']}>" . PHP_EOL . - "MIME-Version: 1.0" . PHP_EOL . + "Reply-To: $fromName <{$params['replyTo']}>" . PHP_EOL . + 'MIME-Version: 1.0' . PHP_EOL . "Content-Type: multipart/alternative; boundary=\"{$mimeBoundary}\""; // assemble the final multipart message body with the text and html types included @@ -776,15 +775,15 @@ class Enotify { $htmlBody = chunk_split(base64_encode($params['htmlVersion'])); $multipartMessageBody = - "--" . $mimeBoundary . PHP_EOL . // plain text section - "Content-Type: text/plain; charset=UTF-8" . PHP_EOL . - "Content-Transfer-Encoding: base64" . PHP_EOL . PHP_EOL . + '--' . $mimeBoundary . PHP_EOL . // plain text section + 'Content-Type: text/plain; charset=UTF-8' . PHP_EOL . + 'Content-Transfer-Encoding: base64' . PHP_EOL . PHP_EOL . $textBody . PHP_EOL . - "--" . $mimeBoundary . PHP_EOL . // text/html section - "Content-Type: text/html; charset=UTF-8" . PHP_EOL . - "Content-Transfer-Encoding: base64" . PHP_EOL . PHP_EOL . + '--' . $mimeBoundary . PHP_EOL . // text/html section + 'Content-Type: text/html; charset=UTF-8' . PHP_EOL . + 'Content-Transfer-Encoding: base64' . PHP_EOL . PHP_EOL . $htmlBody . PHP_EOL . - "--" . $mimeBoundary . "--" . PHP_EOL; // message ending + '--' . $mimeBoundary . '--' . PHP_EOL; // message ending // send the message $res = mail( @@ -793,7 +792,7 @@ class Enotify { $multipartMessageBody, // message body $messageHeader // message headers ); - logger("notification: enotify::send returns " . (($res) ? 'success' : 'failure'), LOGGER_DEBUG); + logger('notification: enotify::send returns ' . (($res) ? 'success' : 'failure'), LOGGER_DEBUG); return $res; } @@ -803,7 +802,7 @@ class Enotify { require_once('include/conversation.php'); - // Call localize_item to get a one line status for activities. + // Call localize_item to get a one line status for activities. // This should set $item['localized'] to indicate we have a brief summary. // and perhaps $item['shortlocalized'] for an even briefer summary @@ -833,13 +832,12 @@ class Enotify { $edit = false; if($item['edited'] > $item['created']) { + $edit = true; if($item['item_thread_top']) { $itemem_text = sprintf( t('edited a post dated %s'), relative_date($item['created'])); - $edit = true; } else { $itemem_text = sprintf( t('edited a comment dated %s'), relative_date($item['created'])); - $edit = true; } } @@ -856,18 +854,18 @@ class Enotify { 'photo' => $item[$who]['xchan_photo_s'], 'when' => (($edit) ? datetime_convert('UTC', date_default_timezone_get(), $item['edited']) : datetime_convert('UTC', date_default_timezone_get(), $item['created'])), 'class' => (intval($item['item_unseen']) ? 'notify-unseen' : 'notify-seen'), - 'b64mid' => (($item['mid']) ? 'b64.' . base64url_encode($item['mid']) : ''), - //'b64mid' => ((in_array($item['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) ? 'b64.' . base64url_encode($item['thr_parent']) : 'b64.' . base64url_encode($item['mid'])), + 'b64mid' => (($item['mid']) ? gen_link_id($item['mid']) : ''), + //'b64mid' => ((in_array($item['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) ? gen_link_id($item['thr_parent']) : gen_link_id($item['mid'])), 'thread_top' => (($item['item_thread_top']) ? true : false), 'message' => bbcode(escape_tags($itemem_text)), - 'body' => htmlentities(html2plain(bbcode($item['body']), 75, true), ENT_COMPAT, 'UTF-8', false), + 'body' => htmlentities(html2plain(bbcode($item['body'], ['drop_media', true]), 75, true), ENT_QUOTES, 'UTF-8', false), // these are for the superblock addon 'hash' => $item[$who]['xchan_hash'], 'uid' => $item['uid'], 'display' => true ); - call_hooks('enotify_format',$x); + call_hooks('enotify_format', $x); if(! $x['display']) { return []; } @@ -884,9 +882,9 @@ class Enotify { $mid = basename($tt['link']); - $b64mid = ((strpos($mid, 'b64.') === 0) ? $mid : 'b64.' . base64url_encode($mid)); + $b64mid = gen_link_id($mid); $x = [ - 'notify_link' => z_root() . '/notify/view/' . $tt['id'], + 'notify_link' => (($tt['ntype'] === NOTIFY_MAIL) ? $tt['link'] : z_root() . '/notify/view/' . $tt['id']), 'name' => $tt['xname'], 'url' => $tt['url'], 'photo' => $tt['photo'], @@ -903,7 +901,7 @@ class Enotify { static public function format_intros($rr) { - $x = [ + return [ 'notify_link' => z_root() . '/connections/ifpending', 'name' => $rr['xchan_name'], 'addr' => $rr['xchan_addr'], @@ -914,13 +912,11 @@ class Enotify { 'message' => t('added your channel') ]; - return $x; - } static public function format_files($rr) { - $x = [ + return [ 'notify_link' => z_root() . '/sharedwithme', 'name' => $rr['author']['xchan_name'], 'addr' => $rr['author']['xchan_addr'], @@ -931,13 +927,11 @@ class Enotify { 'message' => t('shared a file with you') ]; - return $x; - } static public function format_mail($rr) { - $x = [ + return [ 'notify_link' => z_root() . '/mail/' . $rr['id'], 'name' => $rr['xchan_name'], 'addr' => $rr['xchan_addr'], @@ -945,11 +939,9 @@ class Enotify { 'photo' => $rr['xchan_photo_s'], 'when' => datetime_convert('UTC', date_default_timezone_get(), $rr['created']), 'hclass' => (intval($rr['mail_seen']) ? 'notify-seen' : 'notify-unseen'), - 'message' => t('sent you a private message'), + 'message' => t('sent you a direct message'), ]; - return $x; - } static public function format_all_events($rr) { @@ -959,7 +951,7 @@ class Enotify { $today = ((substr($strt, 0, 10) === datetime_convert('UTC', date_default_timezone_get(), 'now', 'Y-m-d')) ? true : false); $when = day_translate(datetime_convert('UTC', (($rr['adjust']) ? date_default_timezone_get() : 'UTC'), $rr['dtstart'], $bd_format)) . (($today) ? ' ' . t('[today]') : ''); - $x = [ + return [ 'notify_link' => z_root() . '/cdav/calendar/' . $rr['event_hash'], 'name' => $rr['xchan_name'], 'addr' => $rr['xchan_addr'], @@ -970,23 +962,20 @@ class Enotify { 'message' => t('created an event') ]; - return $x; } static public function format_register($rr) { - $x = [ + return [ 'notify_link' => z_root() . '/admin/accounts', - 'name' => $rr['account_email'], - //'addr' => $rr['account_email'], + 'name' => $rr['reg_did2'], + //'addr' => '', 'photo' => z_root() . '/' . get_default_profile_photo(48), - 'when' => datetime_convert('UTC', date_default_timezone_get(),$rr['account_created']), + 'when' => datetime_convert('UTC', date_default_timezone_get(),$rr['reg_created']), 'hclass' => ('notify-unseen'), - 'message' => t('requires approval') + 'message' => t('status verified') ]; - return $x; - } } diff --git a/Zotlabs/Lib/Hashpath.php b/Zotlabs/Lib/Hashpath.php new file mode 100644 index 000000000..f3b25d2b6 --- /dev/null +++ b/Zotlabs/Lib/Hashpath.php @@ -0,0 +1,55 @@ +<?php +namespace Zotlabs\Lib; + +/* + * Zotlabs\Lib\Hashpath + * + * Creates hashed directory structures for fast access and resistance to overloading any single directory with files. + * + * Takes a $hash which could be any string + * a $prefix which is where to place the hash directory in the filesystem, default is current directory + * use an empty string for $prefix to place hash directories directly off the root directory + * an optional $depth and $slice (default is 2) to indicate the hash level + * $depth = 1, 256 directories, suitable for < 384K records/files + * $depth = 2, 65536 directories, suitable for < 98M records/files + * $depth = 3, 16777216 directories, suitable for < 2.5B records/files + * ... + * The total number of records anticipated divided by the number of hash directories should generally be kept to + * less than 1500 entries for optimum performance though this varies by operating system and filesystem type. + * ext4 uses 32 bit inode numbers (~4B record limit) so use caution or alternative filesystem types with $depth above 3. + * an optional $mkdir (boolean) to recursively create the directory (ignoring errors) before returning + * + * examples: for a $hash of 'abcdefg' and prefix of 'path' the following paths are returned for $depth = 1 and $depth = 3 + * path/7d/7d1a54127b222502f5b79b5fb0803061152a44f92b37e23c6527baf665d4da9a + * path/7d/1a/54/7d1a54127b222502f5b79b5fb0803061152a44f92b37e23c6527baf665d4da9a + * + * see also: boot.php:os_mkdir() - here we provide the equivalent of mkdir -p with permissions of 770. + * + */ + +class Hashpath { + + static function path($hash, $prefix = '.', $depth = 1, $slice = 2, $mkdir = true, $alg = false) { + + if ($alg) + $hash = hash($alg, $hash); + + $start = 0; + if ($depth < 1) + $depth = 1; + $sluglen = $depth * $slice; + + do { + $slug = substr($hash, $start, $slice); + $prefix .= '/' . $slug; + $start += $slice; + $sluglen -= $slice; + } + while ($sluglen); + + if ($mkdir) + os_mkdir($prefix, STORAGE_DEFAULT_PERMISSIONS, true); + + return $prefix . '/' . $hash; + } +} diff --git a/Zotlabs/Lib/JSalmon.php b/Zotlabs/Lib/JSalmon.php index 48a4e649b..f9fe99706 100644 --- a/Zotlabs/Lib/JSalmon.php +++ b/Zotlabs/Lib/JSalmon.php @@ -18,7 +18,7 @@ class JSalmon { $precomputed = '.' . base64url_encode($data_type,true) . '.YmFzZTY0dXJs.UlNBLVNIQTI1Ng'; - $signature = base64url_encode(rsa_sign($data . $precomputed, $key), true); + $signature = base64url_encode(Crypto::sign($data . $precomputed, $key), true); return ([ 'signed' => true, @@ -40,21 +40,21 @@ class JSalmon { $ret = [ 'results' => [] ]; if(! is_array($x)) { - return $false; + return false; } if(! ( array_key_exists('signed',$x) && $x['signed'])) { - return $false; + return false; } - $signed_data = preg_replace('/\s+/','',$x['data']) . '.' - . base64url_encode($x['data_type'],true) . '.' - . base64url_encode($x['encoding'],true) . '.' + $signed_data = preg_replace('/\s+/','',$x['data']) . '.' + . base64url_encode($x['data_type'],true) . '.' + . base64url_encode($x['encoding'],true) . '.' . base64url_encode($x['alg'],true); $key = HTTPSig::get_key(EMPTY_STR,'zot6',base64url_decode($x['sigs']['key_id'])); logger('key: ' . print_r($key,true)); if($key['portable_id'] && $key['public_key']) { - if(rsa_verify($signed_data,base64url_decode($x['sigs']['value']),$key['public_key'])) { + if(Crypto::verify($signed_data,base64url_decode($x['sigs']['value']),$key['public_key'])) { logger('verified'); $ret = [ 'success' => true, 'signer' => $key['portable_id'], 'hubloc' => $key['hubloc'] ]; } diff --git a/Zotlabs/Lib/Keyutils.php b/Zotlabs/Lib/Keyutils.php new file mode 100644 index 000000000..616ecfcf6 --- /dev/null +++ b/Zotlabs/Lib/Keyutils.php @@ -0,0 +1,99 @@ +<?php + +namespace Zotlabs\Lib; + +use phpseclib\Crypt\RSA; +use phpseclib\Math\BigInteger; + +/** + * Keyutils + * Convert RSA keys between various formats + */ +class Keyutils { + + /** + * @param string $m modulo + * @param string $e exponent + * @return string + */ + public static function meToPem($m, $e) { + + $rsa = new RSA(); + $rsa->loadKey([ + 'e' => new BigInteger($e, 256), + 'n' => new BigInteger($m, 256) + ]); + return $rsa->getPublicKey(); + + } + + /** + * @param string key + * @return string + */ + public static function rsaToPem($key) { + + $rsa = new RSA(); + $rsa->setPublicKey($key); + + return $rsa->getPublicKey(RSA::PUBLIC_FORMAT_PKCS8); + + } + + /** + * @param string key + * @return string + */ + public static function pemToRsa($key) { + + $rsa = new RSA(); + $rsa->setPublicKey($key); + + return $rsa->getPublicKey(RSA::PUBLIC_FORMAT_PKCS1); + + } + + /** + * @param string $key key + * @param string $m reference modulo + * @param string $e reference exponent + */ + public static function pemToMe($key, &$m, &$e) { + + $rsa = new RSA(); + $rsa->loadKey($key); + $rsa->setPublicKey(); + + $m = $rsa->modulus->toBytes(); + $e = $rsa->exponent->toBytes(); + + } + + /** + * @param string $pubkey + * @return string + */ + public static function salmonKey($pubkey) { + self::pemToMe($pubkey, $m, $e); + return 'RSA' . '.' . base64url_encode($m, true) . '.' . base64url_encode($e, true); + } + + /** + * @param string $key + * @return string + */ + public static function convertSalmonKey($key) { + if (strstr($key, ',')) + $rawkey = substr($key, strpos($key, ',') + 1); + else + $rawkey = substr($key, 5); + + $key_info = explode('.', $rawkey); + + $m = base64url_decode($key_info[1]); + $e = base64url_decode($key_info[2]); + + return self::meToPem($m, $e); + } + +}
\ No newline at end of file diff --git a/Zotlabs/Lib/LDSignatures.php b/Zotlabs/Lib/LDSignatures.php index 2eba66ccf..1c2095f10 100644 --- a/Zotlabs/Lib/LDSignatures.php +++ b/Zotlabs/Lib/LDSignatures.php @@ -12,7 +12,7 @@ class LDSignatures { $ohash = self::hash(self::signable_options($data['signature'])); $dhash = self::hash(self::signable_data($data)); - $x = rsa_verify($ohash . $dhash,base64_decode($data['signature']['signatureValue']), $pubkey); + $x = Crypto::verify($ohash . $dhash,base64_decode($data['signature']['signatureValue']), $pubkey); logger('LD-verify: ' . intval($x)); return $x; @@ -35,11 +35,11 @@ class LDSignatures { $ohash = self::hash(self::signable_options($options)); $dhash = self::hash(self::signable_data($data)); - $options['signatureValue'] = base64_encode(rsa_sign($ohash . $dhash,$channel['channel_prvkey'])); + $options['signatureValue'] = base64_encode(Crypto::sign($ohash . $dhash,$channel['channel_prvkey'])); $signed = array_merge([ - '@context' => [ - ACTIVITYSTREAMS_JSONLD_REV, + '@context' => [ + ACTIVITYSTREAMS_JSONLD_REV, 'https://w3id.org/security/v1' ], ],$options); @@ -88,7 +88,7 @@ class LDSignatures { return ''; jsonld_set_document_loader('jsonld_document_loader'); - + try { $d = jsonld_normalize($data,[ 'algorithm' => 'URDNA2015', 'format' => 'application/nquads' ]); } @@ -117,7 +117,7 @@ class LDSignatures { $precomputed = '.' . base64url_encode($data_type,false) . '.YmFzZTY0dXJs.UlNBLVNIQTI1Ng=='; - $signature = base64url_encode(rsa_sign($data . $precomputed,$channel['channel_prvkey'])); + $signature = base64url_encode(Crypto::sign($data . $precomputed,$channel['channel_prvkey'])); return ([ 'id' => $arr['id'], diff --git a/Zotlabs/Lib/Libsync.php b/Zotlabs/Lib/Libsync.php index cff320e11..c4f1b20ea 100644 --- a/Zotlabs/Lib/Libsync.php +++ b/Zotlabs/Lib/Libsync.php @@ -2,9 +2,9 @@ namespace Zotlabs\Lib; -use Zotlabs\Lib\Libzot; -use Zotlabs\Lib\Queue; +use App; +use Zotlabs\Daemon\Master; class Libsync { @@ -23,129 +23,128 @@ class Libsync { logger('build_sync_packet'); - $keychange = (($packet && array_key_exists('keychange',$packet)) ? true : false); - if($keychange) { + $keychange = (($packet && array_key_exists('keychange', $packet)) ? true : false); + if ($keychange) { logger('keychange sync'); } - if(! $uid) + if (!$uid) $uid = local_channel(); - if(! $uid) + if (!$uid) return; $r = q("select * from channel where channel_id = %d limit 1", intval($uid) ); - if(! $r) + if (!$r) return; $channel = $r[0]; - // don't provide these in the export + // don't provide these in the export unset($channel['channel_active']); unset($channel['channel_password']); unset($channel['channel_salt']); - if(intval($channel['channel_removed'])) + if (intval($channel['channel_removed'])) return; $h = q("select hubloc.*, site.site_crypto from hubloc left join site on site_url = hubloc_url where hubloc_hash = '%s' and hubloc_deleted = 0", dbesc(($keychange) ? $packet['keychange']['old_hash'] : $channel['channel_hash']) ); - if(! $h) + if (!$h) return; - $synchubs = array(); + $synchubs = []; - foreach($h as $x) { - if($x['hubloc_host'] == \App::get_hostname()) + foreach ($h as $x) { + if ($x['hubloc_host'] == App::get_hostname()) continue; $y = q("select site_dead from site where site_url = '%s' limit 1", dbesc($x['hubloc_url']) ); - if((! $y) || ($y[0]['site_dead'] == 0)) + if ((!$y) || ($y[0]['site_dead'] == 0)) $synchubs[] = $x; } - if(! $synchubs) + if (!$synchubs) return; - $env_recips = [ $channel['channel_hash'] ]; + $env_recips = [$channel['channel_hash']]; - if($packet) - logger('packet: ' . print_r($packet, true),LOGGER_DATA, LOG_DEBUG); + if ($packet) + logger('packet: ' . print_r($packet, true), LOGGER_DATA, LOG_DEBUG); - $info = (($packet) ? $packet : array()); - $info['type'] = 'sync'; + $info = (($packet) ? $packet : []); + $info['type'] = 'sync'; $info['encoding'] = 'hz'; // note: not zot, this packet is very platform specific - $info['relocate'] = ['channel_address' => $channel['channel_address'], 'url' => z_root() ]; + $info['relocate'] = ['channel_address' => $channel['channel_address'], 'url' => z_root()]; - if(array_key_exists($uid,\App::$config) && array_key_exists('transient',\App::$config[$uid])) { - $settings = \App::$config[$uid]['transient']; - if($settings) { + if (array_key_exists($uid, App::$config) && array_key_exists('transient', App::$config[$uid])) { + $settings = App::$config[$uid]['transient']; + if ($settings) { $info['config'] = $settings; } } - if($channel) { - $info['channel'] = array(); - foreach($channel as $k => $v) { + if ($channel) { + $info['channel'] = []; + foreach ($channel as $k => $v) { // filter out any joined tables like xchan - if(strpos($k,'channel_') !== 0) + if (strpos($k, 'channel_') !== 0) continue; // don't pass these elements, they should not be synchronised $disallowed = [ - 'channel_id','channel_account_id','channel_primary','channel_address', - 'channel_deleted','channel_removed','channel_system' + 'channel_id', 'channel_account_id', 'channel_primary', 'channel_address', + 'channel_deleted', 'channel_removed', 'channel_system' ]; - if(! $keychange) { + if (!$keychange) { $disallowed[] = 'channel_prvkey'; } - if(in_array($k,$disallowed)) + if (in_array($k, $disallowed)) continue; $info['channel'][$k] = $v; } } - if($groups_changed) { + if ($groups_changed) { $r = q("select hash as collection, visible, deleted, gname as name from pgrp where uid = %d", intval($uid) ); - if($r) + if ($r) $info['collections'] = $r; $r = q("select pgrp.hash as collection, pgrp_member.xchan as member from pgrp left join pgrp_member on pgrp.id = pgrp_member.gid where pgrp_member.uid = %d", intval($uid) ); - if($r) + if ($r) $info['collection_members'] = $r; } - $interval = ((get_config('system','delivery_interval') !== false) - ? intval(get_config('system','delivery_interval')) : 2 ); + $interval = ((get_config('system', 'delivery_interval') !== false) + ? intval(get_config('system', 'delivery_interval')) : 2); - logger('Packet: ' . print_r($info,true), LOGGER_DATA, LOG_DEBUG); + logger('Packet: ' . print_r($info, true), LOGGER_DATA, LOG_DEBUG); $total = count($synchubs); - - foreach($synchubs as $hub) { + foreach ($synchubs as $hub) { $hash = random_string(); - $n = Libzot::build_packet($channel,'sync',$env_recips,json_encode($info),'hz',$hub['hubloc_sitekey'],$hub['site_crypto']); - Queue::insert(array( + $n = Libzot::build_packet($channel, 'sync', $env_recips, json_encode($info), 'hz', $hub['hubloc_sitekey'], $hub['site_crypto']); + Queue::insert([ 'hash' => $hash, 'account_id' => $channel['channel_account_id'], 'channel_id' => $channel['channel_id'], @@ -153,29 +152,29 @@ class Libsync { 'driver' => $hub['hubloc_network'], 'notify' => $n, 'msg' => EMPTY_STR - )); + ]); $x = q("select count(outq_hash) as total from outq where outq_delivered = 0"); - if(intval($x[0]['total']) > intval(get_config('system','force_queue_threshold',3000))) { + if (intval($x[0]['total']) > intval(get_config('system', 'force_queue_threshold', 3000))) { logger('immediate delivery deferred.', LOGGER_DEBUG, LOG_INFO); Queue::update($hash); continue; } - \Zotlabs\Daemon\Master::Summon(array('Deliver', $hash)); + Master::Summon(['Deliver', $hash]); $total = $total - 1; - if($interval && $total) - @time_sleep_until(microtime(true) + (float) $interval); + if ($interval && $total) + @time_sleep_until(microtime(true) + (float)$interval); } } /** * @brief * - * @param array $sender + * @param string $sender * @param array $arr * @param array $deliveries * @return array @@ -186,17 +185,16 @@ class Libsync { require_once('include/import.php'); $result = []; - - $keychange = ((array_key_exists('keychange',$arr)) ? true : false); + $keychange = ((array_key_exists('keychange', $arr)) ? true : false); foreach ($deliveries as $d) { $r = q("select * from channel where channel_hash = '%s' limit 1", dbesc($sender) ); - $DR = new \Zotlabs\Lib\DReport(z_root(),$sender,$d,'sync'); + $DR = new DReport(z_root(), $sender, $d, 'sync'); - if (! $r) { + if (!$r) { $DR->update('recipient not found'); $result[] = $DR->get(); continue; @@ -206,153 +204,153 @@ class Libsync { $DR->set_name($channel['channel_name'] . ' <' . channel_reddress($channel) . '>'); - $max_friends = service_class_fetch($channel['channel_id'],'total_channels'); - $max_feeds = account_service_class_fetch($channel['channel_account_id'],'total_feeds'); + $max_friends = service_class_fetch($channel['channel_id'], 'total_channels'); + $max_feeds = account_service_class_fetch($channel['channel_account_id'], 'total_feeds'); - if($channel['channel_hash'] != $sender) { + if ($channel['channel_hash'] != $sender) { logger('Possible forgery. Sender ' . $sender . ' is not ' . $channel['channel_hash']); $DR->update('channel mismatch'); $result[] = $DR->get(); continue; } - if($keychange) { - self::keychange($channel,$arr); + if ($keychange) { + self::keychange($channel, $arr); continue; } // if the clone is active, so are we - if(substr($channel['channel_active'],0,10) !== substr(datetime_convert(),0,10)) { + if (substr($channel['channel_active'], 0, 10) !== substr(datetime_convert(), 0, 10)) { q("UPDATE channel set channel_active = '%s' where channel_id = %d", dbesc(datetime_convert()), intval($channel['channel_id']) ); } - if(array_key_exists('config',$arr) && is_array($arr['config']) && count($arr['config'])) { - foreach($arr['config'] as $cat => $k) { - foreach($arr['config'][$cat] as $k => $v) - set_pconfig($channel['channel_id'],$cat,$k,$v); + if (array_key_exists('config', $arr) && is_array($arr['config']) && count($arr['config'])) { + foreach ($arr['config'] as $cat => $k) { + foreach ($arr['config'][$cat] as $k => $v) + set_pconfig($channel['channel_id'], $cat, $k, $v); } } - if(array_key_exists('obj',$arr) && $arr['obj']) - sync_objs($channel,$arr['obj']); + if (array_key_exists('obj', $arr) && $arr['obj']) + sync_objs($channel, $arr['obj']); + + if (array_key_exists('likes', $arr) && $arr['likes']) + import_likes($channel, $arr['likes']); + + if (array_key_exists('app', $arr) && $arr['app']) + sync_apps($channel, $arr['app']); + + if (array_key_exists('sysapp',$arr) && $arr['sysapp']) { + sync_sysapps($channel, $arr['sysapp']); + } - if(array_key_exists('likes',$arr) && $arr['likes']) - import_likes($channel,$arr['likes']); + if (array_key_exists('addressbook', $arr) && $arr['addressbook']) + sync_addressbook($channel, $arr['addressbook']); - if(array_key_exists('app',$arr) && $arr['app']) - sync_apps($channel,$arr['app']); - - if(array_key_exists('addressbook',$arr) && $arr['addressbook']) - sync_addressbook($channel,$arr['addressbook']); + if (array_key_exists('calendar', $arr) && $arr['calendar']) + sync_calendar($channel, $arr['calendar']); - if(array_key_exists('calendar',$arr) && $arr['calendar']) - sync_calendar($channel,$arr['calendar']); + if (array_key_exists('chatroom', $arr) && $arr['chatroom']) + sync_chatrooms($channel, $arr['chatroom']); - if(array_key_exists('chatroom',$arr) && $arr['chatroom']) - sync_chatrooms($channel,$arr['chatroom']); + //if (array_key_exists('mail', $arr) && $arr['mail']) + // sync_mail($channel, $arr['mail']); - if(array_key_exists('conv',$arr) && $arr['conv']) - import_conv($channel,$arr['conv']); + if (array_key_exists('event', $arr) && $arr['event']) + sync_events($channel, $arr['event']); - if(array_key_exists('mail',$arr) && $arr['mail']) - sync_mail($channel,$arr['mail']); - - if(array_key_exists('event',$arr) && $arr['event']) - sync_events($channel,$arr['event']); + if (array_key_exists('event_item', $arr) && $arr['event_item']) + sync_items($channel, $arr['event_item'], ((array_key_exists('relocate', $arr)) ? $arr['relocate'] : null)); - if(array_key_exists('event_item',$arr) && $arr['event_item']) - sync_items($channel,$arr['event_item'],((array_key_exists('relocate',$arr)) ? $arr['relocate'] : null)); + if (array_key_exists('item', $arr) && $arr['item']) + sync_items($channel, $arr['item'], ((array_key_exists('relocate', $arr)) ? $arr['relocate'] : null)); - if(array_key_exists('item',$arr) && $arr['item']) - sync_items($channel,$arr['item'],((array_key_exists('relocate',$arr)) ? $arr['relocate'] : null)); - // deprecated, maintaining for a few months for upward compatibility // this should sync webpages, but the logic is a bit subtle - if(array_key_exists('item_id',$arr) && $arr['item_id']) - sync_items($channel,$arr['item_id']); + //if (array_key_exists('item_id', $arr) && $arr['item_id']) + // sync_items($channel, $arr['item_id']); - if(array_key_exists('menu',$arr) && $arr['menu']) - sync_menus($channel,$arr['menu']); - - if(array_key_exists('file',$arr) && $arr['file']) - sync_files($channel,$arr['file']); + if (array_key_exists('menu', $arr) && $arr['menu']) + sync_menus($channel, $arr['menu']); - if(array_key_exists('wiki',$arr) && $arr['wiki']) - sync_items($channel,$arr['wiki'],((array_key_exists('relocate',$arr)) ? $arr['relocate'] : null)); + if (array_key_exists('file', $arr) && $arr['file']) + sync_files($channel, $arr['file']); - if(array_key_exists('channel',$arr) && is_array($arr['channel']) && count($arr['channel'])) { + if (array_key_exists('wiki', $arr) && $arr['wiki']) + sync_items($channel, $arr['wiki'], ((array_key_exists('relocate', $arr)) ? $arr['relocate'] : null)); - $remote_channel = $arr['channel']; + if (array_key_exists('channel', $arr) && is_array($arr['channel']) && count($arr['channel'])) { + + $remote_channel = $arr['channel']; $remote_channel['channel_id'] = $channel['channel_id']; - if(array_key_exists('channel_pageflags',$arr['channel']) && intval($arr['channel']['channel_pageflags'])) { + if (array_key_exists('channel_pageflags', $arr['channel']) && intval($arr['channel']['channel_pageflags'])) { // Several pageflags are site-specific and cannot be sync'd. - // Only allow those bits which are shareable from the remote and then + // Only allow those bits which are shareable from the remote and then // logically OR with the local flags - $arr['channel']['channel_pageflags'] = $arr['channel']['channel_pageflags'] & (PAGE_HIDDEN|PAGE_AUTOCONNECT|PAGE_APPLICATION|PAGE_PREMIUM|PAGE_ADULT); + $arr['channel']['channel_pageflags'] = $arr['channel']['channel_pageflags'] & (PAGE_HIDDEN | PAGE_AUTOCONNECT | PAGE_APPLICATION | PAGE_PREMIUM | PAGE_ADULT); $arr['channel']['channel_pageflags'] = $arr['channel']['channel_pageflags'] | $channel['channel_pageflags']; } $disallowed = [ - 'channel_id', 'channel_account_id', 'channel_primary', 'channel_prvkey', - 'channel_address', 'channel_notifyflags', 'channel_removed', 'channel_deleted', - 'channel_system', 'channel_r_stream', 'channel_r_profile', 'channel_r_abook', - 'channel_r_storage', 'channel_r_pages', 'channel_w_stream', 'channel_w_wall', - 'channel_w_comment', 'channel_w_mail', 'channel_w_like', 'channel_w_tagwall', - 'channel_w_chat', 'channel_w_storage', 'channel_w_pages', 'channel_a_republish', + 'channel_id', 'channel_account_id', 'channel_primary', 'channel_prvkey', + 'channel_address', 'channel_notifyflags', 'channel_removed', 'channel_deleted', + 'channel_system', 'channel_r_stream', 'channel_r_profile', 'channel_r_abook', + 'channel_r_storage', 'channel_r_pages', 'channel_w_stream', 'channel_w_wall', + 'channel_w_comment', 'channel_w_mail', 'channel_w_like', 'channel_w_tagwall', + 'channel_w_chat', 'channel_w_storage', 'channel_w_pages', 'channel_a_republish', 'channel_a_delegate' ]; - $clean = array(); - foreach($arr['channel'] as $k => $v) { - if(in_array($k,$disallowed)) + $clean = []; + foreach ($arr['channel'] as $k => $v) { + if (in_array($k, $disallowed)) continue; $clean[$k] = $v; } - if(count($clean)) { - foreach($clean as $k => $v) { - $r = dbq("UPDATE channel set " . dbesc($k) . " = '" . dbesc($v) - . "' where channel_id = " . intval($channel['channel_id']) ); + if (count($clean)) { + foreach ($clean as $k => $v) { + dbq("UPDATE channel set " . dbesc($k) . " = '" . dbesc($v) . "' where channel_id = " . intval($channel['channel_id'])); } } } - if(array_key_exists('abook',$arr) && is_array($arr['abook']) && count($arr['abook'])) { + if (array_key_exists('abook', $arr) && is_array($arr['abook']) && count($arr['abook'])) { $total_friends = 0; - $total_feeds = 0; + $total_feeds = 0; $r = q("select abook_id, abook_feed from abook where abook_channel = %d", intval($channel['channel_id']) ); - if($r) { + if ($r) { // don't count yourself $total_friends = ((count($r) > 0) ? count($r) - 1 : 0); - foreach($r as $rr) - if(intval($rr['abook_feed'])) - $total_feeds ++; + foreach ($r as $rr) + if (intval($rr['abook_feed'])) + $total_feeds++; } - $disallowed = array('abook_id','abook_account','abook_channel','abook_rating','abook_rating_text','abook_not_here'); + $disallowed = ['abook_id', 'abook_account', 'abook_channel', 'abook_rating', 'abook_rating_text', 'abook_not_here']; $fields = db_columns('abook'); - foreach($arr['abook'] as $abook) { + foreach ($arr['abook'] as $abook) { $abconfig = null; - if(array_key_exists('abconfig',$abook) && is_array($abook['abconfig']) && count($abook['abconfig'])) + if (array_key_exists('abconfig', $abook) && is_array($abook['abconfig']) && count($abook['abconfig'])) $abconfig = $abook['abconfig']; - if(! array_key_exists('abook_blocked',$abook)) { + if (!array_key_exists('abook_blocked', $abook)) { // convert from redmatrix $abook['abook_blocked'] = (($abook['abook_flags'] & 0x0001) ? 1 : 0); $abook['abook_ignored'] = (($abook['abook_flags'] & 0x0002) ? 1 : 0); @@ -364,20 +362,20 @@ class Libsync { $abook['abook_feed'] = (($abook['abook_flags'] & 0x0100) ? 1 : 0); } - $clean = array(); - if($abook['abook_xchan'] && $abook['entry_deleted']) { + $clean = []; + if ($abook['abook_xchan'] && $abook['entry_deleted']) { logger('Removing abook entry for ' . $abook['abook_xchan']); $r = q("select abook_id, abook_feed from abook where abook_xchan = '%s' and abook_channel = %d and abook_self = 0 limit 1", dbesc($abook['abook_xchan']), intval($channel['channel_id']) ); - if($r) { - contact_remove($channel['channel_id'],$r[0]['abook_id']); - if($total_friends) - $total_friends --; - if(intval($r[0]['abook_feed'])) - $total_feeds --; + if ($r) { + contact_remove($channel['channel_id'], $r[0]['abook_id']); + if ($total_friends) + $total_friends--; + if (intval($r[0]['abook_feed'])) + $total_feeds--; } continue; } @@ -386,31 +384,31 @@ class Libsync { // This relies on the undocumented behaviour that red sites send xchan info with the abook // and import_author_xchan will look them up on all federated networks - if($abook['abook_xchan'] && $abook['xchan_addr']) { + if ($abook['abook_xchan'] && $abook['xchan_addr']) { $h = Libzot::get_hublocs($abook['abook_xchan']); - if(! $h) { + if (!$h) { $xhash = import_author_xchan(encode_item_xchan($abook)); - if(! $xhash) { + if (!$xhash) { logger('Import of ' . $abook['xchan_addr'] . ' failed.'); continue; } } } - foreach($abook as $k => $v) { - if(in_array($k,$disallowed) || (strpos($k,'abook') !== 0)) { + foreach ($abook as $k => $v) { + if (in_array($k, $disallowed) || (strpos($k, 'abook') !== 0)) { continue; } - if(! in_array($k,$fields)) { + if (!in_array($k, $fields)) { continue; } $clean[$k] = $v; } - if(! array_key_exists('abook_xchan',$clean)) + if (!array_key_exists('abook_xchan', $clean)) continue; - if(array_key_exists('abook_instance',$clean) && $clean['abook_instance'] && strpos($clean['abook_instance'],z_root()) === false) { + if (array_key_exists('abook_instance', $clean) && $clean['abook_instance'] && strpos($clean['abook_instance'], z_root()) === false) { $clean['abook_not_here'] = 1; } @@ -422,12 +420,12 @@ class Libsync { // make sure we have an abook entry for this xchan on this system - if(! $r) { - if($max_friends !== false && $total_friends > $max_friends) { + if (!$r) { + if ($max_friends !== false && $total_friends > $max_friends) { logger('total_channels service class limit exceeded'); continue; } - if($max_feeds !== false && intval($clean['abook_feed']) && $total_feeds > $max_feeds) { + if ($max_feeds !== false && intval($clean['abook_feed']) && $total_feeds > $max_feeds) { logger('total_feeds service class limit exceeded'); continue; } @@ -438,18 +436,16 @@ class Libsync { 'abook_channel' => $channel['channel_id'] ] ); - $total_friends ++; - if(intval($clean['abook_feed'])) - $total_feeds ++; + $total_friends++; + if (intval($clean['abook_feed'])) + $total_feeds++; } - if(count($clean)) { - foreach($clean as $k => $v) { - if($k == 'abook_dob') + if (count($clean)) { + foreach ($clean as $k => $v) { + if ($k == 'abook_dob') $v = dbescdate($v); - - $r = dbq("UPDATE abook set " . dbesc($k) . " = '" . dbesc($v) - . "' where abook_xchan = '" . dbesc($clean['abook_xchan']) . "' and abook_channel = " . intval($channel['channel_id'])); + dbq("UPDATE abook set " . dbesc($k) . " = '" . dbesc($v) . "' where abook_xchan = '" . dbesc($clean['abook_xchan']) . "' and abook_channel = " . intval($channel['channel_id'])); } } @@ -459,10 +455,10 @@ class Libsync { // translate_abook_perms_inbound($channel,$abook); - if($abconfig) { + if ($abconfig) { /// @fixme does not handle sync of del_abconfig - foreach($abconfig as $abc) { - set_abconfig($channel['channel_id'],$abc['xchan'],$abc['cat'],$abc['k'],$abc['v']); + foreach ($abconfig as $abc) { + set_abconfig($channel['channel_id'], $abc['xchan'], $abc['cat'], $abc['k'], $abc['v']); } } } @@ -470,21 +466,21 @@ class Libsync { // sync collections (privacy groups) oh joy... - if(array_key_exists('collections',$arr) && is_array($arr['collections']) && count($arr['collections'])) { + if (array_key_exists('collections', $arr) && is_array($arr['collections']) && count($arr['collections'])) { $x = q("select * from pgrp where uid = %d", intval($channel['channel_id']) ); - foreach($arr['collections'] as $cl) { + foreach ($arr['collections'] as $cl) { $found = false; - if($x) { - foreach($x as $y) { - if($cl['collection'] == $y['hash']) { + if ($x) { + foreach ($x as $y) { + if ($cl['collection'] == $y['hash']) { $found = true; break; } } - if($found) { - if(($y['gname'] != $cl['name']) + if ($found) { + if (($y['gname'] != $cl['name']) || ($y['visible'] != $cl['visible']) || ($y['deleted'] != $cl['deleted'])) { q("update pgrp set gname = '%s', visible = %d, deleted = %d where hash = '%s' and uid = %d", @@ -495,15 +491,15 @@ class Libsync { intval($channel['channel_id']) ); } - if(intval($cl['deleted']) && (! intval($y['deleted']))) { + if (intval($cl['deleted']) && (!intval($y['deleted']))) { q("delete from pgrp_member where gid = %d", intval($y['id']) ); } } } - if(! $found) { - $r = q("INSERT INTO pgrp ( hash, uid, visible, deleted, gname ) + if (!$found) { + q("INSERT INTO pgrp ( hash, uid, visible, deleted, gname ) VALUES( '%s', %d, %d, %d, '%s' ) ", dbesc($cl['collection']), intval($channel['channel_id']), @@ -517,16 +513,16 @@ class Libsync { // They need to be removed by marking deleted and removing the members. // This shouldn't happen except for clones created before this function was written. - if($x) { + if ($x) { $found_local = false; - foreach($x as $y) { - foreach($arr['collections'] as $cl) { - if($cl['collection'] == $y['hash']) { + foreach ($x as $y) { + foreach ($arr['collections'] as $cl) { + if ($cl['collection'] == $y['hash']) { $found_local = true; break; } } - if(! $found_local) { + if (!$found_local) { q("delete from pgrp_member where gid = %d", intval($y['id']) ); @@ -546,38 +542,38 @@ class Libsync { // now sync the members - if(array_key_exists('collection_members', $arr) + if (array_key_exists('collection_members', $arr) && is_array($arr['collection_members']) && count($arr['collection_members'])) { // first sort into groups keyed by the group hash - $members = array(); - foreach($arr['collection_members'] as $cm) { - if(! array_key_exists($cm['collection'],$members)) - $members[$cm['collection']] = array(); + $members = []; + foreach ($arr['collection_members'] as $cm) { + if (!array_key_exists($cm['collection'], $members)) + $members[$cm['collection']] = []; $members[$cm['collection']][] = $cm['member']; } // our group list is already synchronised - if($x) { - foreach($x as $y) { - + if ($x) { + foreach ($x as $y) { + // for each group, loop on members list we just received - if(isset($y['hash']) && isset($members[$y['hash']])) { - foreach($members[$y['hash']] as $member) { + if (isset($y['hash']) && isset($members[$y['hash']])) { + foreach ($members[$y['hash']] as $member) { $found = false; - $z = q("select xchan from pgrp_member where gid = %d and uid = %d and xchan = '%s' limit 1", + $z = q("select xchan from pgrp_member where gid = %d and uid = %d and xchan = '%s' limit 1", intval($y['id']), intval($channel['channel_id']), dbesc($member) ); - if($z) + if ($z) $found = true; - + // if somebody is in the group that wasn't before - add them - - if(! $found) { + + if (!$found) { q("INSERT INTO pgrp_member (uid, gid, xchan) VALUES( %d, %d, '%s' ) ", intval($channel['channel_id']), @@ -587,16 +583,16 @@ class Libsync { } } } - + // now retrieve a list of members we have on this site $m = q("select xchan from pgrp_member where gid = %d and uid = %d", intval($y['id']), intval($channel['channel_id']) ); - if($m) { - foreach($m as $mm) { + if ($m) { + foreach ($m as $mm) { // if the local existing member isn't in the list we just received - remove them - if(! in_array($mm['xchan'],$members[$y['hash']])) { + if (!in_array($mm['xchan'], $members[$y['hash']])) { q("delete from pgrp_member where xchan = '%s' and gid = %d and uid = %d", dbesc($mm['xchan']), intval($y['id']), @@ -610,17 +606,17 @@ class Libsync { } } - if(array_key_exists('profile',$arr) && is_array($arr['profile']) && count($arr['profile'])) { + if (array_key_exists('profile', $arr) && is_array($arr['profile']) && count($arr['profile'])) { - $disallowed = array('id','aid','uid','guid'); + $disallowed = ['id', 'aid', 'uid', 'guid']; + + foreach ($arr['profile'] as $profile) { - foreach($arr['profile'] as $profile) { - $x = q("select * from profile where profile_guid = '%s' and uid = %d limit 1", dbesc($profile['profile_guid']), intval($channel['channel_id']) ); - if(! $x) { + if (!$x) { profile_store_lowlevel( [ 'aid' => $channel['channel_account_id'], @@ -628,29 +624,29 @@ class Libsync { 'profile_guid' => $profile['profile_guid'], ] ); - + $x = q("select * from profile where profile_guid = '%s' and uid = %d limit 1", dbesc($profile['profile_guid']), intval($channel['channel_id']) ); - if(! $x) + if (!$x) continue; } - $clean = array(); - foreach($profile as $k => $v) { - if(in_array($k,$disallowed)) + $clean = []; + foreach ($profile as $k => $v) { + if (in_array($k, $disallowed)) continue; - if($profile['is_default'] && in_array($k,['photo','thumb'])) + if ($profile['is_default'] && in_array($k, ['photo', 'thumb'])) continue; - if($k === 'name') + if ($k === 'name') $clean['fullname'] = $v; - elseif($k === 'with') + elseif ($k === 'with') $clean['partner'] = $v; - elseif($k === 'work') + elseif ($k === 'work') $clean['employment'] = $v; - elseif(array_key_exists($k,$x[0])) + elseif (array_key_exists($k, $x[0])) $clean[$k] = $v; /** @@ -658,7 +654,7 @@ class Libsync { * We also need to import local photos if a custom photo is selected */ - if((strpos($profile['thumb'],'/photo/profile/l/') !== false) || intval($profile['is_default'])) { + if ((strpos($profile['thumb'], '/photo/profile/l/') !== false) || intval($profile['is_default'])) { $profile['photo'] = z_root() . '/photo/profile/l/' . $channel['channel_id']; $profile['thumb'] = z_root() . '/photo/profile/m/' . $channel['channel_id']; } @@ -668,11 +664,11 @@ class Libsync { } } - if(count($clean)) { - foreach($clean as $k => $v) { - $r = dbq("UPDATE profile set " . TQUOT . dbesc($k) . TQUOT . " = '" . dbesc($v) - . "' where profile_guid = '" . dbesc($profile['profile_guid']) - . "' and uid = " . intval($channel['channel_id'])); + if (count($clean)) { + foreach ($clean as $k => $v) { + dbq("UPDATE profile set " . TQUOT . dbesc($k) . TQUOT . " = '" . dbesc($v) + . "' where profile_guid = '" . dbesc($profile['profile_guid']) + . "' and uid = " . intval($channel['channel_id'])); } } } @@ -687,7 +683,7 @@ class Libsync { */ call_hooks('process_channel_sync_delivery', $addon); - $DR = new \Zotlabs\Lib\DReport(z_root(),$d,$d,'sync','channel sync delivered'); + $DR = new DReport(z_root(), $d, $d, 'sync', 'channel sync delivered'); $DR->set_name($channel['channel_name'] . ' <' . channel_reddress($channel) . '>'); @@ -708,22 +704,34 @@ class Libsync { static function sync_locations($sender, $arr, $absolute = false) { - $ret = array(); + $ret = []; + + // If a sender reports that the channel has been deleted, delete its hubloc + if (isset($arr['deleted_locally']) && intval($arr['deleted_locally'])) { + q("UPDATE hubloc SET hubloc_deleted = 1, hubloc_updated = '%s' WHERE hubloc_hash = '%s' AND hubloc_url = '%s'", + dbesc(datetime_convert()), + dbesc($sender['hash']), + dbesc($sender['site']['url']) + ); + } - if($arr['locations']) { + if ($arr['locations']) { - if($absolute) - self::check_location_move($sender['hash'],$arr['locations']); + if ($absolute) + Libzot::check_location_move($sender['hash'], $arr['locations']); $xisting = q("select * from hubloc where hubloc_hash = '%s'", dbesc($sender['hash']) ); + if(!$xisting) + $xisting = []; + // See if a primary is specified $has_primary = false; - foreach($arr['locations'] as $location) { - if($location['primary']) { + foreach ($arr['locations'] as $location) { + if ($location['primary']) { $has_primary = true; break; } @@ -731,32 +739,32 @@ class Libsync { // Ensure that they have one primary hub - if(! $has_primary) + if (!$has_primary) $arr['locations'][0]['primary'] = true; - foreach($arr['locations'] as $location) { - if(! Libzot::verify($location['url'],$location['url_sig'],$sender['public_key'])) { + foreach ($arr['locations'] as $location) { + if (!Libzot::verify($location['url'], $location['url_sig'], $sender['public_key'])) { logger('Unable to verify site signature for ' . $location['url']); - $ret['message'] .= sprintf( t('Unable to verify site signature for %s'), $location['url']) . EOL; + $ret['message'] .= sprintf(t('Unable to verify site signature for %s'), $location['url']) . EOL; continue; } - for($x = 0; $x < count($xisting); $x ++) { - if(($xisting[$x]['hubloc_url'] === $location['url']) + for ($x = 0; $x < count($xisting); $x++) { + if (($xisting[$x]['hubloc_url'] === $location['url']) && ($xisting[$x]['hubloc_sitekey'] === $location['sitekey'])) { $xisting[$x]['updated'] = true; } } - if(! $location['sitekey']) { - logger('Empty hubloc sitekey. ' . print_r($location,true)); + if (!$location['sitekey']) { + logger('Empty hubloc sitekey. ' . print_r($location, true)); continue; } // Catch some malformed entries from the past which still exist - if(strpos($location['address'],'/') !== false) - $location['address'] = substr($location['address'],0,strpos($location['address'],'/')); + if (strpos($location['address'], '/') !== false) + $location['address'] = substr($location['address'], 0, strpos($location['address'], '/')); // match as many fields as possible in case anything at all changed. @@ -773,18 +781,18 @@ class Libsync { dbesc($location['callback']), dbesc($location['sitekey']) ); - if($r) { + if ($r) { logger('Hub exists: ' . $location['url'], LOGGER_DEBUG); - + // update connection timestamp if this is the site we're talking to // This only happens when called from import_xchan $current_site = false; - $t = datetime_convert('UTC','UTC','now - 15 minutes'); - - if(array_key_exists('site',$arr) && $location['url'] == $arr['site']['url']) { - q("update hubloc set hubloc_connected = '%s', hubloc_updated = '%s' where hubloc_id = %d and hubloc_connected < '%s'", + $t = datetime_convert('UTC', 'UTC', 'now - 15 minutes'); + + if (array_key_exists('site', $arr) && $location['url'] == $arr['site']['url']) { + q("update hubloc set hubloc_connected = '%s', hubloc_updated = '%s' where hubloc_id = %d and hubloc_updated < '%s'", dbesc(datetime_convert()), dbesc(datetime_convert()), intval($r[0]['hubloc_id']), @@ -793,11 +801,11 @@ class Libsync { $current_site = true; } - if($current_site && intval($r[0]['hubloc_error'])) { + if ($current_site && intval($r[0]['hubloc_error'])) { q("update hubloc set hubloc_error = 0 where hubloc_id = %d", intval($r[0]['hubloc_id']) ); - if(intval($r[0]['hubloc_orphancheck'])) { + if (intval($r[0]['hubloc_orphancheck'])) { q("update hubloc set hubloc_orphancheck = 0 where hubloc_id = %d", intval($r[0]['hubloc_id']) ); @@ -808,70 +816,71 @@ class Libsync { } // Remove pure duplicates - if(count($r) > 1) { - for($h = 1; $h < count($r); $h ++) { + if (count($r) > 1) { + for ($h = 1; $h < count($r); $h++) { q("delete from hubloc where hubloc_id = %d", intval($r[$h]['hubloc_id']) ); - $what .= 'duplicate_hubloc_removed '; + $what .= 'duplicate_hubloc_removed '; $changed = true; } } - if(intval($r[0]['hubloc_primary']) && (! $location['primary'])) { - $m = q("update hubloc set hubloc_primary = 0, hubloc_updated = '%s' where hubloc_id_url = '%s'", + if (intval($r[0]['hubloc_primary']) && (!$location['primary'])) { + q("update hubloc set hubloc_primary = 0, hubloc_updated = '%s' where hubloc_id_url = '%s'", dbesc(datetime_convert()), dbesc($r[0]['hubloc_id_url']) ); $r[0]['hubloc_primary'] = intval($location['primary']); hubloc_change_primary($r[0]); - $what .= 'primary_hub '; + $what .= 'primary_hub '; $changed = true; } - elseif((! intval($r[0]['hubloc_primary'])) && ($location['primary'])) { - $m = q("update hubloc set hubloc_primary = 1, hubloc_updated = '%s' where hubloc_id = %d", + elseif ((!intval($r[0]['hubloc_primary'])) && ($location['primary'])) { + q("update hubloc set hubloc_primary = 1, hubloc_updated = '%s' where hubloc_id = %d", dbesc(datetime_convert()), intval($r[0]['hubloc_id']) ); // make sure hubloc_change_primary() has current data $r[0]['hubloc_primary'] = intval($location['primary']); hubloc_change_primary($r[0]); - $what .= 'primary_hub '; + $what .= 'primary_hub '; $changed = true; } - elseif($absolute) { + elseif ($absolute) { // Absolute sync - make sure the current primary is correctly reflected in the xchan $pr = hubloc_change_primary($r[0]); - if($pr) { - $what .= 'xchan_primary '; + if ($pr) { + $what .= 'xchan_primary '; $changed = true; } } - if(intval($r[0]['hubloc_deleted']) && (! intval($location['deleted']))) { - $n = q("update hubloc set hubloc_deleted = 0, hubloc_updated = '%s' where hubloc_id_url = '%s'", + if (intval($r[0]['hubloc_deleted']) && (!intval($location['deleted']))) { + q("update hubloc set hubloc_deleted = 0, hubloc_updated = '%s' where hubloc_id_url = '%s'", dbesc(datetime_convert()), dbesc($r[0]['hubloc_id_url']) ); - $what .= 'undelete_hub '; + $what .= 'undelete_hub '; $changed = true; } - elseif((! intval($r[0]['hubloc_deleted'])) && (intval($location['deleted']))) { + elseif ((!intval($r[0]['hubloc_deleted'])) && (intval($location['deleted']))) { logger('deleting hubloc: ' . $r[0]['hubloc_addr']); - $n = q("update hubloc set hubloc_deleted = 1, hubloc_updated = '%s' where hubloc_id_url = '%s'", + q("update hubloc set hubloc_deleted = 1, hubloc_updated = '%s' where hubloc_id_url = '%s'", dbesc(datetime_convert()), dbesc($r[0]['hubloc_id_url']) ); - $what .= 'delete_hub '; + $what .= 'delete_hub '; $changed = true; } + continue; } // Existing hubs are dealt with. Now let's process any new ones. // New hub claiming to be primary. Make it so by removing any existing primaries. - if(intval($location['primary'])) { - $r = q("update hubloc set hubloc_primary = 0, hubloc_updated = '%s' where hubloc_hash = '%s' and hubloc_primary = 1", + if (intval($location['primary'])) { + q("update hubloc set hubloc_primary = 0, hubloc_updated = '%s' where hubloc_hash = '%s' and hubloc_primary = 1", dbesc(datetime_convert()), dbesc($sender['hash']) ); @@ -879,7 +888,7 @@ class Libsync { logger('New hub: ' . $location['url']); - $r = hubloc_store_lowlevel( + hubloc_store_lowlevel( [ 'hubloc_guid' => $sender['id'], 'hubloc_guid_sig' => $sender['id_sig'], @@ -890,7 +899,7 @@ class Libsync { 'hubloc_primary' => intval($location['primary']), 'hubloc_url' => $location['url'], 'hubloc_url_sig' => $location['url_sig'], - 'hubloc_site_id' => Libzot::make_xchan_hash($location['url'],$location['sitekey']), + 'hubloc_site_id' => Libzot::make_xchan_hash($location['url'], $location['sitekey']), 'hubloc_host' => $location['host'], 'hubloc_callback' => $location['callback'], 'hubloc_sitekey' => $location['sitekey'], @@ -899,30 +908,32 @@ class Libsync { ] ); - $what .= 'newhub '; + $what .= 'newhub '; $changed = true; - if($location['primary']) { - $r = q("select * from hubloc where hubloc_addr = '%s' and hubloc_sitekey = '%s' limit 1", + if ($location['primary']) { + $r = q("select * from hubloc where hubloc_addr = '%s' and hubloc_sitekey = '%s'", dbesc($location['address']), dbesc($location['sitekey']) ); - if($r) - hubloc_change_primary($r[0]); + if ($r) { + $r = Libzot::zot_record_preferred($r); + hubloc_change_primary($r); + } } } // get rid of any hubs we have for this channel which weren't reported. - if($absolute && $xisting) { - foreach($xisting as $x) { - if(! array_key_exists('updated',$x)) { + if ($absolute && $xisting) { + foreach ($xisting as $x) { + if (!array_key_exists('updated', $x)) { logger('Deleting unreferenced hub location ' . $x['hubloc_addr']); - $r = q("update hubloc set hubloc_deleted = 1, hubloc_updated = '%s' where hubloc_id_url = '%s'", + q("update hubloc set hubloc_deleted = 1, hubloc_updated = '%s' where hubloc_id_url = '%s'", dbesc(datetime_convert()), dbesc($x['hubloc_id_url']) ); - $what .= 'removed_hub '; + $what .= 'removed_hub '; $changed = true; } } @@ -933,22 +944,22 @@ class Libsync { } $ret['change_message'] = $what; - $ret['changed'] = $changed; + $ret['changed'] = $changed; return $ret; } - static function keychange($channel,$arr) { + static function keychange($channel, $arr) { // verify the keychange operation - if(! Libzot::verify($arr['channel']['channel_pubkey'],$arr['keychange']['new_sig'],$channel['channel_prvkey'])) { + if (!Libzot::verify($arr['channel']['channel_pubkey'], $arr['keychange']['new_sig'], $channel['channel_prvkey'])) { logger('sync keychange: verification failed'); return; } - $sig = Libzot::sign($channel['channel_guid'],$arr['channel']['channel_prvkey']); - $hash = Libzot::make_xchan_hash($channel['channel_guid'],$arr['channel']['channel_pubkey']); + $sig = Libzot::sign($channel['channel_guid'], $arr['channel']['channel_prvkey']); + $hash = Libzot::make_xchan_hash($channel['channel_guid'], $arr['channel']['channel_pubkey']); $r = q("update channel set channel_prvkey = '%s', channel_pubkey = '%s', channel_guid_sig = '%s', @@ -959,16 +970,16 @@ class Libsync { dbesc($hash), intval($channel['channel_id']) ); - if(! $r) { + if (!$r) { logger('keychange sync: channel update failed'); return; - } + } $r = q("select * from channel where channel_id = %d", intval($channel['channel_id']) ); - if(! $r) { + if (!$r) { logger('keychange sync: channel retrieve failed'); return; } @@ -980,11 +991,11 @@ class Libsync { dbesc(z_root()) ); - if($h) { - foreach($h as $hv) { + if ($h) { + foreach ($h as $hv) { $hv['hubloc_guid_sig'] = $sig; $hv['hubloc_hash'] = $hash; - $hv['hubloc_url_sig'] = Libzot::sign(z_root(),$channel['channel_prvkey']); + $hv['hubloc_url_sig'] = Libzot::sign(z_root(), $channel['channel_prvkey']); hubloc_store_lowlevel($hv); } } @@ -997,12 +1008,12 @@ class Libsync { dbesc($hash) ); - if(($x) && (! $check)) { + if (($x) && (!$check)) { $oldxchan = $x[0]; - foreach($x as $xv) { - $xv['xchan_guid_sig'] = $sig; - $xv['xchan_hash'] = $hash; - $xv['xchan_pubkey'] = $channel['channel_pubkey']; + foreach ($x as $xv) { + $xv['xchan_guid_sig'] = $sig; + $xv['xchan_hash'] = $hash; + $xv['xchan_pubkey'] = $channel['channel_pubkey']; xchan_store_lowlevel($xv); $newxchan = $xv; } @@ -1012,14 +1023,14 @@ class Libsync { dbesc($arr['keychange']['old_hash']) ); - if($a) { + if ($a) { q("update abook set abook_xchan = '%s' where abook_id = %d", dbesc($hash), intval($a[0]['abook_id']) ); } - xchan_change_key($oldxchan,$newxchan,$arr['keychange']); + xchan_change_key($oldxchan, $newxchan, $arr['keychange']); } diff --git a/Zotlabs/Lib/Libzot.php b/Zotlabs/Lib/Libzot.php index f0fe3ab24..200a2c486 100644 --- a/Zotlabs/Lib/Libzot.php +++ b/Zotlabs/Lib/Libzot.php @@ -34,7 +34,7 @@ class Libzot { */ static function new_uid($channel_nick) { $rawstr = z_root() . '/' . $channel_nick . '.' . mt_rand(); - return(base64url_encode(hash('whirlpool', $rawstr, true), true)); + return (base64url_encode(hash('whirlpool', $rawstr, true), true)); } @@ -87,7 +87,7 @@ 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 array $msg * optional message * @param string $encoding * optional encoding, default 'activitystreams' @@ -98,15 +98,15 @@ class Libzot { * 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 = '') { + static function build_packet($channel, $type = 'activity', $recipients = null, $msg = [], $encoding = 'activitystreams', $remote_key = null, $methods = '') { - $sig_method = get_config('system','signature_algorithm','sha256'); + $sig_method = get_config('system', 'signature_algorithm', 'sha256'); $data = [ 'type' => $type, 'encoding' => $encoding, 'sender' => $channel['channel_hash'], - 'site_id' => self::make_xchan_hash(z_root(), get_config('system','pubkey')), + 'site_id' => self::make_xchan_hash(z_root(), get_config('system', 'pubkey')), 'version' => System::get_zot_revision(), ]; @@ -116,8 +116,8 @@ class Libzot { if ($msg) { $actor = channel_url($channel); - if ($encoding === 'activitystreams' && array_key_exists('actor',$msg) && is_string($msg['actor']) && $actor === $msg['actor']) { - $msg = JSalmon::sign($msg,$actor,$channel['channel_prvkey']); + if ($encoding === 'activitystreams' && array_key_exists('actor', $msg) && is_string($msg['actor']) && $actor === $msg['actor']) { + $msg = JSalmon::sign($msg, $actor, $channel['channel_prvkey']); } $data['data'] = $msg; } @@ -125,12 +125,12 @@ class Libzot { unset($data['encoding']); } - logger('packet: ' . print_r($data,true), LOGGER_DATA, LOG_DEBUG); + logger('packet: ' . print_r($data, true), LOGGER_DATA, LOG_DEBUG); if ($remote_key) { $algorithm = self::best_algorithm($methods); if ($algorithm) { - $data = crypto_encapsulate(json_encode($data),$remote_key, $algorithm); + $data = Crypto::encapsulate(json_encode($data), $remote_key, $algorithm); } } @@ -143,14 +143,14 @@ class Libzot { * * @param string $methods * Comma separated list of encryption methods - * @return string first match from our site method preferences crypto_methods() array + * @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. */ static function best_algorithm($methods) { $x = [ 'methods' => $methods, - 'result' => '' + 'result' => '' ]; /** @@ -161,18 +161,18 @@ class Libzot { */ call_hooks('zot_best_algorithm', $x); - if($x['result']) + if ($x['result']) return $x['result']; - if($methods) { + if ($methods) { $x = explode(',', $methods); - if($x) { - $y = crypto_methods(); - if($y) { - foreach($y as $yv) { + if ($x) { + $y = Crypto::methods(); + if ($y) { + foreach ($y as $yv) { $yv = trim($yv); - if(in_array($yv, $x)) { - return($yv); + if (in_array($yv, $x)) { + return ($yv); } } } @@ -186,17 +186,17 @@ class Libzot { /** * @brief Send a zot message. * - * @see z_post_url() - * * @param string $url - * @param array $data + * @param string $data * @param array $channel (required if using zot6 delivery) * @param array $crypto (required if encrypted httpsig, requires hubloc_sitekey and site_crypto elements) * @return array see z_post_url() for returned data format + * @see z_post_url() + * */ - static function zot($url, $data, $channel = null,$crypto = null) { + static function zot($url, $data, $channel = null, $crypto = null) { - if($channel) { + if ($channel) { $headers = [ 'X-Zot-Token' => random_string(), 'Digest' => HTTPSig::generate_digest_header($data), @@ -204,8 +204,8 @@ class Libzot { '(request-target)' => 'post ' . get_request_string($url) ]; - $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)); + $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 { $h = []; @@ -213,7 +213,7 @@ class Libzot { $redirects = 0; - return z_post_url($url,$data,$redirects,((empty($h)) ? [] : [ 'headers' => $h ])); + return z_post_url($url, $data, $redirects, ((empty($h)) ? [] : ['headers' => $h])); } @@ -237,7 +237,7 @@ class Libzot { * * @param array $them => xchan structure of sender * @param array $channel => local channel structure of target recipient, required for "friending" operations - * @param array $force (optional) default false + * @param boolean $force (optional) default false * * @return boolean * * \b true if successful @@ -245,9 +245,9 @@ class Libzot { */ static function refresh($them, $channel = null, $force = false) { - logger('them: ' . print_r($them,true), LOGGER_DATA, LOG_DEBUG); + logger('them: ' . print_r($them, true), LOGGER_DATA, LOG_DEBUG); if ($channel) - logger('channel: ' . print_r($channel,true), LOGGER_DATA, LOG_DEBUG); + logger('channel: ' . print_r($channel, true), LOGGER_DATA, LOG_DEBUG); $url = null; @@ -261,12 +261,12 @@ class Libzot { // 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']) { + if (array_key_exists('xchan_addr', $them) && $them['xchan_addr']) { $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']) ); } - if(! $r) { + if (!$r && array_key_exists('xchan_hash', $them) && $them['xchan_hash']) { $r = q("select hubloc_id_url, hubloc_primary from hubloc where hubloc_hash = '%s' order by hubloc_id desc", dbesc($them['xchan_hash']) ); @@ -276,81 +276,84 @@ class Libzot { foreach ($r as $rr) { if (intval($rr['hubloc_primary'])) { $url = $rr['hubloc_id_url']; - $record = $rr; + break; } } - if (! $url) { + if (!$url) { $url = $r[0]['hubloc_id_url']; } } } - if (! $url) { + + if (!$url) { logger('zot_refresh: no url'); return false; } + $m = parse_url($url); + $site_url = unparse_url([ 'scheme' => $m['scheme'], 'host' => $m['host'] ]); + $s = q("select site_dead from site where site_url = '%s' limit 1", - dbesc($url) + dbesc($site_url) ); - if($s && intval($s[0]['site_dead']) && (! $force)) { + if ($s && intval($s[0]['site_dead']) && (!$force)) { logger('zot_refresh: site ' . $url . ' is marked dead and force flag is not set. Cancelling operation.'); return false; } - $record = Zotfinger::exec($url,$channel); + $record = Zotfinger::exec($url, $channel); // Check the HTTP signature - $hsig = $record['signature']; - if($hsig && $hsig['signer'] === $url && $hsig['header_valid'] === true && $hsig['content_valid'] === true) + if ($hsig && $hsig['signer'] === $url && $hsig['header_valid'] === true && $hsig['content_valid'] === true) { $hsig_valid = true; + } - if(! $hsig_valid) { - logger('http signature not valid: ' . print_r($hsig,true)); + if (!$hsig_valid) { + logger('http signature not valid: ' . print_r($hsig, true)); return false; } - - logger('zot-info: ' . print_r($record,true), LOGGER_DATA, LOG_DEBUG); + logger('zot-info: ' . print_r($record, true), LOGGER_DATA, LOG_DEBUG); $x = self::import_xchan($record['data'], (($force) ? UPDATE_FLAGS_FORCED : UPDATE_FLAGS_UPDATED)); - - if(! $x['success']) + if (!$x['success']) { return false; + } - if($channel && $record['data']['permissions']) { - $permissions = explode(',',$record['data']['permissions']); + if ($channel && $record['data']['permissions']) { + $permissions = explode(',', $record['data']['permissions']); - if($permissions && is_array($permissions)) { - $old_read_stream_perm = get_abconfig($channel['channel_id'],$x['hash'],'their_perms','view_stream'); + if ($permissions && is_array($permissions)) { + $old_read_stream_perm = get_abconfig($channel['channel_id'], $x['hash'], 'their_perms', 'view_stream'); $permissions = Permissions::FilledPerms($permissions); - foreach($permissions as $k => $v) { - set_abconfig($channel['channel_id'],$x['hash'],'their_perms',$k,$v); + foreach ($permissions as $k => $v) { + set_abconfig($channel['channel_id'], $x['hash'], 'their_perms', $k, $v); } } - if(array_key_exists('profile',$record['data']) && array_key_exists('next_birthday',$record['data']['profile'])) { - $next_birthday = datetime_convert('UTC','UTC',$record['data']['profile']['next_birthday']); + if (array_key_exists('profile', $record['data']) && array_key_exists('next_birthday', $record['data']['profile'])) { + $next_birthday = datetime_convert('UTC', 'UTC', $record['data']['profile']['next_birthday']); } else { $next_birthday = NULL_DATE; } - $profile_assign = get_pconfig($channel['channel_id'],'system','profile_assign',''); + $profile_assign = get_pconfig($channel['channel_id'], 'system', 'profile_assign', ''); // Keep original perms to check if we need to notify them - $previous_perms = get_all_perms($channel['channel_id'],$x['hash']); + $previous_perms = get_all_perms($channel['channel_id'], $x['hash']); $r = q("select * from abook where abook_xchan = '%s' and abook_channel = %d and abook_self = 0 limit 1", dbesc($x['hash']), intval($channel['channel_id']) ); - if($r) { + if ($r) { // connection exists @@ -358,8 +361,9 @@ class Libzot { // we have as we may have updated the year after sending a notification; and resetting // to the one we just received would cause us to create duplicated events. - if(substr($r[0]['abook_dob'],5) == substr($next_birthday,5)) + if (substr($r[0]['abook_dob'], 5) == substr($next_birthday, 5)) { $next_birthday = $r[0]['abook_dob']; + } $y = q("update abook set abook_dob = '%s' where abook_xchan = '%s' and abook_channel = %d @@ -369,12 +373,14 @@ class Libzot { intval($channel['channel_id']) ); - if(! $y) + if (!$y) { logger('abook update failed'); + } else { // if we were just granted read stream permission and didn't have it before, try to pull in some posts - if((! $old_read_stream_perm) && (intval($permissions['view_stream']))) - Master::Summon([ 'Onepoll', $r[0]['abook_id'] ]); + if (!$old_read_stream_perm && intval($permissions['view_stream'])) { + Master::Summon(['Onepoll', $r[0]['abook_id']]); + } } } else { @@ -386,13 +392,13 @@ class Libzot { // new connection - if($my_perms) { - foreach($my_perms as $k => $v) { - set_abconfig($channel['channel_id'],$x['hash'],'my_perms',$k,$v); + if ($my_perms) { + foreach ($my_perms as $k => $v) { + set_abconfig($channel['channel_id'], $x['hash'], 'my_perms', $k, $v); } } - $closeness = get_pconfig($channel['channel_id'],'system','new_abook_closeness',80); + $closeness = get_pconfig($channel['channel_id'], 'system', 'new_abook_closeness', 80); $y = abook_store_lowlevel( [ @@ -408,9 +414,9 @@ class Libzot { ] ); - if($y) { + if ($y) { logger("New introduction received for {$channel['channel_name']}"); - $new_perms = get_all_perms($channel['channel_id'],$x['hash'],false); + $new_perms = get_all_perms($channel['channel_id'], $x['hash'], false); // Send a clone sync packet and a permissions update if permissions have changed @@ -419,54 +425,63 @@ class Libzot { intval($channel['channel_id']) ); - if($new_connection) { - if(! Permissions::PermsCompare($new_perms,$previous_perms)) - Master::Summon([ 'Notifier', 'permissions_create', $new_connection[0]['abook_id'] ]); + if ($new_connection) { + if (!Permissions::PermsCompare($new_perms, $previous_perms)) { + Master::Summon(['Notifier', 'permission_create', $new_connection[0]['abook_id']]); + } + Enotify::submit( [ - 'type' => NOTIFY_INTRO, - 'from_xchan' => $x['hash'], - 'to_xchan' => $channel['channel_hash'], - 'link' => z_root() . '/connedit/' . $new_connection[0]['abook_id'] + 'type' => NOTIFY_INTRO, + 'from_xchan' => $x['hash'], + 'to_xchan' => $channel['channel_hash'], + 'link' => z_root() . '/connedit/' . $new_connection[0]['abook_id'] ] ); - if(intval($permissions['view_stream'])) { - if(intval(get_pconfig($channel['channel_id'],'perm_limits','send_stream') & PERMS_PENDING) - || (! intval($new_connection[0]['abook_pending']))) - Master::Summon([ 'Onepoll', $new_connection[0]['abook_id'] ]); + if (intval($permissions['view_stream'])) { + if (intval(get_pconfig($channel['channel_id'], 'perm_limits', 'send_stream') & PERMS_PENDING) + || (!intval($new_connection[0]['abook_pending']))) { + Master::Summon(['Onepoll', $new_connection[0]['abook_id']]); + } } - // If there is a default group for this channel, add this connection to it - // for pending connections this will happens at acceptance time. + // for pending connections this will happen at acceptance time. - if(! intval($new_connection[0]['abook_pending'])) { + if (!intval($new_connection[0]['abook_pending'])) { $default_group = $channel['channel_default_group']; - if($default_group) { - $g = Group::rec_byhash($channel['channel_id'],$default_group); - if($g) - Group::member_add($channel['channel_id'],'',$x['hash'],$g['id']); + + if ($default_group) { + $g = Group::rec_byhash($channel['channel_id'], $default_group); + + if ($g) { + Group::member_add($channel['channel_id'], '', $x['hash'], $g['id']); + } } } unset($new_connection[0]['abook_id']); unset($new_connection[0]['abook_account']); unset($new_connection[0]['abook_channel']); - $abconfig = load_abconfig($channel['channel_id'],$new_connection['abook_xchan']); - if($abconfig) + + $abconfig = load_abconfig($channel['channel_id'], $new_connection['abook_xchan']); + + if ($abconfig) { $new_connection['abconfig'] = $abconfig; + } + + Libsync::build_sync_packet($channel['channel_id'], ['abook' => $new_connection]); - Libsync::build_sync_packet($channel['channel_id'], array('abook' => $new_connection)); } } - } return true; } return false; } + /** * @brief Look up if channel is known and previously verified. * @@ -480,6 +495,7 @@ class Libzot { * * \e string \b id_sig => id signed with conversant's private key * * \e string \b location => URL of the origination hub of this communication * * \e string \b location_sig => URL signed with conversant's private key + * * \e string \b site_id => URL signed with conversant's private key * @param boolean $multiple (optional) default false * * @return array|null @@ -489,9 +505,9 @@ class Libzot { static function gethub($arr, $multiple = false) { - if($arr['id'] && $arr['id_sig'] && $arr['location'] && $arr['location_sig']) { + if ($arr['id'] && $arr['id_sig'] && $arr['location'] && $arr['location_sig'] && $arr['site_id']) { - if(! check_siteallowed($arr['location'])) { + if (!check_siteallowed($arr['location'])) { logger('blacklisted site: ' . $arr['location']); return null; } @@ -509,13 +525,13 @@ class Libzot { dbesc($arr['location_sig']), dbesc($arr['site_id']) ); - if($r) { + if ($r) { logger('Found', LOGGER_DEBUG); return (($multiple) ? $r : $r[0]); } + logger('Not found: ' . print_r($arr, true), LOGGER_DEBUG); } - logger('Not found: ' . print_r($arr,true), LOGGER_DEBUG); - + logger('Incomplete array: ' . print_r($arr, true), LOGGER_DEBUG); return false; } @@ -532,16 +548,16 @@ class Libzot { dbesc($sender), dbesc($site_id) ); - if(! $r) { + if (!$r) { return null; } - if(! check_siteallowed($r[0]['hubloc_url'])) { + if (!check_siteallowed($r[0]['hubloc_url'])) { logger('blacklisted site: ' . $r[0]['hubloc_url']); return null; } - if(! check_channelallowed($r[0]['hubloc_hash'])) { + if (!check_channelallowed($r[0]['hubloc_hash'])) { logger('blacklisted channel: ' . $r[0]['hubloc_hash']); return null; } @@ -567,9 +583,9 @@ class Libzot { $hsig_valid = false; - $result = [ 'success' => false ]; + $result = ['success' => false]; - if(! $id) { + if (!$id) { return $result; } @@ -578,16 +594,16 @@ class Libzot { // Check the HTTP signature $hsig = $record['signature']; - if($hsig['signer'] === $id && $hsig['header_valid'] === true && $hsig['content_valid'] === true) { + if ($hsig['signer'] === $id && $hsig['header_valid'] === true && $hsig['content_valid'] === true) { $hsig_valid = true; } - if(! $hsig_valid) { - logger('http signature not valid: ' . print_r($hsig,true)); + if (!$hsig_valid) { + logger('http signature not valid: ' . print_r($hsig, true)); return $result; } $c = self::import_xchan($record['data']); - if($c['success']) { + if ($c['success']) { $result['success'] = true; } else { @@ -617,7 +633,6 @@ class Libzot { */ static function import_xchan($arr, $ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) { - /** * @hooks import_xchan * Called when processing the result of zot_finger() to store the result @@ -625,26 +640,26 @@ class Libzot { */ call_hooks('import_xchan', $arr); - $ret = array('success' => false); - $dirmode = intval(get_config('system','directory_mode')); + $ret = ['success' => false]; + $dirmode = intval(get_config('system', 'directory_mode')); $changed = false; - $what = ''; + $what = ''; - if(! ($arr['id'] && $arr['id_sig'])) { - logger('No identity information provided. ' . print_r($arr,true)); + if (!($arr['id'] && $arr['id_sig'])) { + logger('No identity information provided. ' . print_r($arr, true)); return $ret; } - $xchan_hash = self::make_xchan_hash($arr['id'],$arr['public_key']); + $xchan_hash = self::make_xchan_hash($arr['id'], $arr['public_key']); $arr['hash'] = $xchan_hash; $import_photos = false; - $sig_methods = ((array_key_exists('signing',$arr) && is_array($arr['signing'])) ? $arr['signing'] : [ 'sha256' ]); - $verified = false; + $sig_methods = ((array_key_exists('signing', $arr) && is_array($arr['signing'])) ? $arr['signing'] : ['sha256']); + $verified = false; - if(! self::verify($arr['id'],$arr['id_sig'],$arr['public_key'])) { + if (!self::verify($arr['id'], $arr['id_sig'], $arr['public_key'])) { logger('Unable to verify channel signature for ' . $arr['address']); return $ret; } @@ -652,7 +667,7 @@ class Libzot { $verified = true; } - if(! $verified) { + if (!$verified) { $ret['message'] = t('Unable to verify channel signature'); return $ret; } @@ -663,40 +678,40 @@ class Libzot { dbesc($xchan_hash) ); - if(! array_key_exists('connect_url', $arr)) + if (!array_key_exists('connect_url', $arr)) $arr['connect_url'] = ''; - if($r) { - if($arr['photo'] && array_key_exists('updated',$arr['photo']) && $r[0]['xchan_photo_date'] != $arr['photo']['updated']) { + if ($r) { + + if ($arr['photo'] && array_key_exists('updated', $arr['photo']) && $arr['photo']['updated'] > $r[0]['xchan_photo_date']) $import_photos = true; - } // if we import an entry from a site that's not ours and either or both of us is off the grid - hide the entry. /** @TODO: check if we're the same directory realm, which would mean we are allowed to see it */ - $dirmode = get_config('system','directory_mode'); + $dirmode = get_config('system', 'directory_mode'); - if((($arr['site']['directory_mode'] === 'standalone') || ($dirmode & DIRECTORY_MODE_STANDALONE)) && ($arr['site']['url'] != z_root())) + if ((($arr['site']['directory_mode'] === 'standalone') || ($dirmode & DIRECTORY_MODE_STANDALONE)) && ($arr['site']['url'] != z_root())) $arr['searchable'] = false; $hidden = (1 - intval($arr['searchable'])); $hidden_changed = $adult_changed = $deleted_changed = $pubforum_changed = 0; - if(intval($r[0]['xchan_hidden']) != (1 - intval($arr['searchable']))) + if (intval($r[0]['xchan_hidden']) != (1 - intval($arr['searchable']))) $hidden_changed = 1; - if(intval($r[0]['xchan_selfcensored']) != intval($arr['adult_content'])) + if (intval($r[0]['xchan_selfcensored']) != intval($arr['adult_content'])) $adult_changed = 1; - if(intval($r[0]['xchan_deleted']) != intval($arr['deleted'])) + if (intval($r[0]['xchan_deleted']) != intval($arr['deleted'])) $deleted_changed = 1; // new style 6-MAR-2019 - if(array_key_exists('channel_type',$arr)) { - if($arr['channel_type'] === 'collection') { + if (array_key_exists('channel_type', $arr)) { + if ($arr['channel_type'] === 'collection') { // do nothing at this time. } - elseif($arr['channel_type'] === 'group') { + elseif ($arr['channel_type'] === 'group') { $arr['public_forum'] = 1; } else { @@ -706,27 +721,27 @@ class Libzot { // old style - if(intval($r[0]['xchan_pubforum']) != intval($arr['public_forum'])) + if (intval($r[0]['xchan_pubforum']) != intval($arr['public_forum'])) $pubforum_changed = 1; - if($arr['protocols']) { - $protocols = implode(',',$arr['protocols']); - if($protocols !== 'zot6') { - set_xconfig($xchan_hash,'system','protocols',$protocols); + if ($arr['protocols']) { + $protocols = implode(',', $arr['protocols']); + if ($protocols !== 'zot6') { + set_xconfig($xchan_hash, 'system', 'protocols', $protocols); } else { - del_xconfig($xchan_hash,'system','protocols'); + del_xconfig($xchan_hash, 'system', 'protocols'); } } - if(($r[0]['xchan_name_date'] != $arr['name_updated']) + if (($r[0]['xchan_name_date'] != $arr['name_updated']) || ($r[0]['xchan_connurl'] != $arr['primary_location']['connections_url']) || ($r[0]['xchan_addr'] != $arr['primary_location']['address']) || ($r[0]['xchan_follow'] != $arr['primary_location']['follow_url']) || ($r[0]['xchan_connpage'] != $arr['connect_url']) || ($r[0]['xchan_url'] != $arr['primary_location']['url']) - || $hidden_changed || $adult_changed || $deleted_changed || $pubforum_changed ) { + || $hidden_changed || $adult_changed || $deleted_changed || $pubforum_changed) { $rup = q("update xchan set xchan_name = '%s', xchan_name_date = '%s', xchan_connurl = '%s', xchan_follow = '%s', xchan_connpage = '%s', xchan_hidden = %d, xchan_selfcensored = %d, xchan_deleted = %d, xchan_pubforum = %d, xchan_addr = '%s', xchan_url = '%s' where xchan_hash = '%s'", @@ -744,18 +759,18 @@ class Libzot { dbesc($xchan_hash) ); - logger('Update: existing: ' . print_r($r[0],true), LOGGER_DATA, LOG_DEBUG); - logger('Update: new: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG); - $what .= 'xchan '; + logger('Update: existing: ' . print_r($r[0], true), LOGGER_DATA, LOG_DEBUG); + logger('Update: new: ' . print_r($arr, true), LOGGER_DATA, LOG_DEBUG); + $what .= 'xchan '; $changed = true; } } else { $import_photos = true; - if((($arr['site']['directory_mode'] === 'standalone') + if ((($arr['site']['directory_mode'] === 'standalone') || ($dirmode & DIRECTORY_MODE_STANDALONE)) - && ($arr['site']['url'] != z_root())) + && ($arr['site']['url'] != z_root())) $arr['searchable'] = false; $x = xchan_store_lowlevel( @@ -764,8 +779,8 @@ class Libzot { 'xchan_guid' => $arr['id'], 'xchan_guid_sig' => $arr['id_sig'], 'xchan_pubkey' => $arr['public_key'], - 'xchan_photo_mimetype' => $arr['photo_mimetype'], - 'xchan_photo_l' => $arr['photo'], + 'xchan_photo_mimetype' => $arr['photo']['type'], + 'xchan_photo_l' => $arr['photo']['url'], 'xchan_addr' => escape_tags($arr['primary_location']['address']), 'xchan_url' => escape_tags($arr['primary_location']['url']), 'xchan_connurl' => $arr['primary_location']['connections_url'], @@ -773,7 +788,7 @@ class Libzot { 'xchan_connpage' => $arr['connect_url'], 'xchan_name' => (($arr['name']) ? escape_tags($arr['name']) : '-'), 'xchan_network' => 'zot6', - 'xchan_photo_date' => $arr['photo_updated'], + 'xchan_photo_date' => $arr['photo']['updated'], 'xchan_name_date' => $arr['name_updated'], 'xchan_hidden' => intval(1 - intval($arr['searchable'])), 'xchan_selfcensored' => $arr['adult_content'], @@ -782,11 +797,11 @@ class Libzot { ] ); - $what .= 'new_xchan'; + $what .= 'new_xchan'; $changed = true; } - if($import_photos) { + if ($import_photos) { require_once('include/photo/photo_driver.php'); @@ -795,13 +810,16 @@ class Libzot { $local = q("select channel_account_id, channel_id from channel where channel_hash = '%s' limit 1", dbesc($xchan_hash) ); - if($local) { - $ph = z_fetch_url($arr['photo']['url'], true); - if($ph['success']) { + if ($local) { + + $ph = false; + if (strpos($arr['photo']['url'], z_root()) === false) + $ph = z_fetch_url($arr['photo']['url'], true); + if ($ph['success']) { $hash = import_channel_photo($ph['body'], $arr['photo']['type'], $local[0]['channel_account_id'], $local[0]['channel_id']); - if($hash) { + if ($hash) { // unless proven otherwise $is_default_profile = 1; @@ -809,13 +827,13 @@ class Libzot { intval($local[0]['channel_account_id']), intval($local[0]['channel_id']) ); - if($profile) { - if(! intval($profile[0]['is_default'])) + if ($profile) { + if (!intval($profile[0]['is_default'])) $is_default_profile = 0; } // If setting for the default profile, unset the profile photo flag from any other photos I own - if($is_default_profile) { + 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", intval(PHOTO_NORMAL), intval(PHOTO_PROFILE), @@ -827,20 +845,20 @@ class Libzot { } // reset the names in case they got messed up when we had a bug in this function - $photos = array( + $photos = [ z_root() . '/photo/profile/l/' . $local[0]['channel_id'], z_root() . '/photo/profile/m/' . $local[0]['channel_id'], z_root() . '/photo/profile/s/' . $local[0]['channel_id'], $arr['photo_mimetype'], false - ); + ]; } } else { $photos = import_xchan_photo($arr['photo']['url'], $xchan_hash); } - if($photos) { - if($photos[4]) { + if ($photos) { + if ($photos[4]) { // importing the photo failed somehow. Leave the photo_date alone so we can try again at a later date. // This often happens when somebody joins the matrix with a bad cert. $r = q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' @@ -855,7 +873,7 @@ class Libzot { else { $r = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'", - dbescdate(datetime_convert('UTC','UTC',$arr['photo_updated'])), + dbescdate(datetime_convert('UTC', 'UTC', $arr['photo_updated'])), dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), @@ -863,7 +881,7 @@ class Libzot { dbesc($xchan_hash) ); } - $what .= 'photo '; + $what .= 'photo '; $changed = true; } } @@ -873,12 +891,12 @@ class Libzot { $s = Libsync::sync_locations($arr, $arr); - if($s) { - if($s['change_message']) + if ($s) { + if ($s['change_message']) $what .= $s['change_message']; - if($s['changed']) + if ($s['changed']) $changed = $s['changed']; - if($s['message']) + if ($s['message']) $ret['message'] .= $s['message']; } @@ -890,24 +908,24 @@ class Libzot { // Are we a directory server of some kind? $other_realm = false; - $realm = get_directory_realm(); - if(array_key_exists('site',$arr) - && array_key_exists('realm',$arr['site']) - && (strpos($arr['site']['realm'],$realm) === false)) + $realm = get_directory_realm(); + if (array_key_exists('site', $arr) + && array_key_exists('realm', $arr['site']) + && (strpos($arr['site']['realm'], $realm) === false)) $other_realm = true; - if($dirmode != DIRECTORY_MODE_NORMAL) { + if ($dirmode != DIRECTORY_MODE_NORMAL) { // We're some kind of directory server. However we can only add directory information // if the entry is in the same realm (or is a sub-realm). Sub-realms are denoted by // including the parent realm in the name. e.g. 'RED_GLOBAL:foo' would allow an entry to // be in directories for the local realm (foo) and also the RED_GLOBAL realm. - if(array_key_exists('profile',$arr) && is_array($arr['profile']) && (! $other_realm)) { - $profile_changed = Libzotdir::import_directory_profile($xchan_hash,$arr['profile'],$address,$ud_flags, 1); - if($profile_changed) { - $what .= 'profile '; + if (array_key_exists('profile', $arr) && is_array($arr['profile']) && (!$other_realm)) { + $profile_changed = Libzotdir::import_directory_profile($xchan_hash, $arr['profile'], $address, $ud_flags, 1); + if ($profile_changed) { + $what .= 'profile '; $changed = true; } } @@ -923,20 +941,20 @@ class Libzot { } } - if(array_key_exists('site',$arr) && is_array($arr['site'])) { + if (array_key_exists('site', $arr) && is_array($arr['site'])) { $profile_changed = self::import_site($arr['site']); - if($profile_changed) { - $what .= 'site '; + if ($profile_changed) { + $what .= 'site '; $changed = true; } } - if(($changed) || ($ud_flags == UPDATE_FLAGS_FORCED)) { + if (($changed) || ($ud_flags == UPDATE_FLAGS_FORCED)) { $guid = random_string() . '@' . \App::get_hostname(); - Libzotdir::update_modtime($xchan_hash,$guid,$address,$ud_flags); - logger('Changed: ' . $what,LOGGER_DEBUG); + Libzotdir::update_modtime($xchan_hash, $guid, $address, $ud_flags); + logger('Changed: ' . $what, LOGGER_DEBUG); } - elseif(! $ud_flags) { + elseif (!$ud_flags) { // nothing changed but we still need to update the updates record q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and not (ud_flags & %d) > 0 ", intval(UPDATE_FLAGS_UPDATED), @@ -945,12 +963,12 @@ class Libzot { ); } - if(! x($ret,'message')) { + if (!x($ret, 'message')) { $ret['success'] = true; - $ret['hash'] = $xchan_hash; + $ret['hash'] = $xchan_hash; } - logger('Result: ' . print_r($ret,true), LOGGER_DATA, LOG_DEBUG); + logger('Result: ' . print_r($ret, true), LOGGER_DATA, LOG_DEBUG); return $ret; } @@ -967,32 +985,32 @@ class Libzot { */ static function process_response($hub, $arr, $outq) { - logger('remote: ' . print_r($arr,true),LOGGER_DATA); + logger('remote: ' . print_r($arr, true), LOGGER_DATA); - if(! $arr['success']) { + if (!$arr['success']) { logger('Failed: ' . $hub); return; } $x = json_decode($arr['body'], true); - if(! $x) { + if (!$x) { logger('No json from ' . $hub); logger('Headers: ' . print_r($arr['header'], true), LOGGER_DATA, LOG_DEBUG); } - $x = crypto_unencapsulate($x, get_config('system','prvkey')); + $x = Crypto::unencapsulate($x, get_config('system', 'prvkey')); - if(! is_array($x)) { - $x = json_decode($x,true); + if (!is_array($x)) { + $x = json_decode($x, true); } - if(! is_array($x)) { + if (!is_array($x)) { btlogger('failed communication - no response'); } - if($x) { - if(! $x['success']) { + if ($x) { + if (!$x['success']) { // handle remote validation issues @@ -1003,18 +1021,18 @@ class Libzot { ); } - 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)) { + 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) { + if ($legacy_recipient !== false) { $legacy_recipient_parts = explode(' ', $xx['recipient'], 2); - $xx['recipient'] = $legacy_recipient_parts[0]; - $xx['name'] = $legacy_recipient_parts[1]; + $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' ) ", @@ -1023,7 +1041,7 @@ class Libzot { dbesc($xx['recipient']), dbesc($xx['name']), dbesc($xx['status']), - dbesc(datetime_convert('UTC','UTC',$xx['date'])), + dbesc(datetime_convert('UTC', 'UTC', $xx['date'])), dbesc($xx['sender']) ); } @@ -1046,10 +1064,10 @@ class Libzot { // synchronous message types are handled immediately // async messages remain in the queue until processed. - if(intval($outq['outq_async'])) - Queue::remove($outq['outq_hash'],$outq['outq_channel']); + if (intval($outq['outq_async'])) + Queue::remove($outq['outq_hash'], $outq['outq_channel']); - logger('zot_process_response: ' . print_r($x,true), LOGGER_DEBUG); + logger('zot_process_response: ' . print_r($x, true), LOGGER_DEBUG); } /** @@ -1067,16 +1085,16 @@ class Libzot { * If everything checks out on the remote end, we will receive back a packet containing one or more messages, * which will be processed and delivered before this function ultimately returns. * - * @see zot_import() - * * @param array $arr * decrypted and json decoded notify packet from remote site * @return array from zot_import() + * @see zot_import() + * */ static function fetch($arr) { - logger('zot_fetch: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG); + logger('zot_fetch: ' . print_r($arr, true), LOGGER_DATA, LOG_DEBUG); return self::import($arr); @@ -1101,15 +1119,15 @@ class Libzot { */ static function import($arr) { - $env = $arr; + $env = $arr; $private = false; - $return = []; + $return = []; $result = null; - logger('Notify: ' . print_r($env,true), LOGGER_DATA, LOG_DEBUG); + logger('Notify: ' . print_r($env, true), LOGGER_DATA, LOG_DEBUG); - if(! is_array($env)) { + if (!is_array($env)) { logger('decode error'); return; } @@ -1117,59 +1135,60 @@ class Libzot { $message_request = false; - $has_data = array_key_exists('data',$env) && $env['data']; - $data = (($has_data) ? $env['data'] : false); + $has_data = array_key_exists('data', $env) && $env['data']; + $data = (($has_data) ? $env['data'] : false); $AS = null; - if($env['encoding'] === 'activitystreams') { + if ($env['encoding'] === 'activitystreams') { - $AS = new ActivityStreams($data); - if(! $AS->is_valid()) { - logger('Activity rejected: ' . print_r($data,true)); - return; - } - if (is_array($AS->obj)) { - $arr = Activity::decode_note($AS); - } - else { - $arr = []; - } + $AS = new ActivityStreams($data); + if (!$AS->is_valid()) { + logger('Activity rejected: ' . print_r($data, true)); + return; + } + if (is_array($AS->obj)) { + $arr = Activity::decode_note($AS); + } + else { + $arr = []; + } - logger($AS->debug(),LOGGER_DATA); + logger($AS->debug(), LOGGER_DATA); } + $deliveries = null; - if(array_key_exists('recipients',$env) && count($env['recipients'])) { + if (array_key_exists('recipients', $env) && count($env['recipients'])) { logger('specific recipients'); - logger('recipients: ' . print_r($env['recipients'],true),LOGGER_DEBUG); + logger('recipients: ' . print_r($env['recipients'], true), LOGGER_DEBUG); $recip_arr = []; - foreach($env['recipients'] as $recip) { - $recip_arr[] = $recip; + foreach ($env['recipients'] as $recip) { + $recip_arr[] = $recip; } $r = false; - if($recip_arr) { - stringify_array_elms($recip_arr,true); - $recips = implode(',',$recip_arr); - $r = q("select channel_hash as hash from channel where channel_hash in ( " . $recips . " ) and channel_removed = 0 "); + if ($recip_arr) { + stringify_array_elms($recip_arr, true); + $recips = implode(',', $recip_arr); + $r = q("select channel_hash as hash from channel where channel_hash in ( " . $recips . " ) and channel_removed = 0 "); } - if(! $r) { + if (!$r) { logger('recips: no recipients on this site'); return; } // Response messages will inherit the privacy of the parent - if($env['type'] !== 'response') + if ($env['type'] !== 'response') $private = true; - $deliveries = ids_to_array($r,'hash'); + $deliveries = ids_to_array($r, 'hash'); // We found somebody on this site that's in the recipient list. } @@ -1182,28 +1201,32 @@ class Libzot { // and who are allowed to see them based on the sender's permissions // @fixme; - $deliveries = self::public_recips($env,$AS); + $deliveries = self::public_recips($env, $AS); } $deliveries = array_unique($deliveries); - if(! $deliveries) { + if (!$deliveries) { logger('No deliveries on this site'); return; } - if($has_data) { + if ($has_data) { - if(in_array($env['type'],['activity','response'])) { + if (in_array($env['type'], ['activity', 'response'])) { + + if(!isset($AS->actor['id'])) { + logger('No actor id!'); + return; + } - $r = q("select hubloc_hash, hubloc_network from hubloc where hubloc_id_url = '%s' ", + $r = q("select hubloc_hash, hubloc_network, hubloc_url from hubloc where hubloc_id_url = '%s'", dbesc($AS->actor['id']) ); - if($r) { - // selects a zot6 hash if available, otherwise use whatever we have + if ($r) { $r = self::zot_record_preferred($r); $arr['author_xchan'] = $r['hubloc_hash']; } @@ -1213,48 +1236,58 @@ class Libzot { return; } - $s = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1", - dbesc($env['sender']) - ); + $arr['owner_xchan'] = $env['sender']; + + if(filter_var($env['sender'], FILTER_VALIDATE_URL)) { + // in individual delivery, change owner if needed + $s = q("select hubloc_hash, hubloc_url 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']; + if ($s) { + $arr['owner_xchan'] = $s[0]['hubloc_hash']; + } } - else { - $arr['owner_xchan'] = $env['sender']; + + if (! $arr['owner_xchan']) { + logger('No owner!'); + return; } - if ($private && (! intval($arr['item_private']))) { + if ($private && (!intval($arr['item_private']))) { $arr['item_private'] = 1; } if ($arr['mid'] === $arr['parent_mid']) { - if (is_array($AS->obj) && array_key_exists('commentPolicy',$AS->obj)) { - $p = strstr($AS->obj['commentPolicy'],'until='); - if($p !== false) { - $arr['comments_closed'] = datetime_convert('UTC','UTC', substr($p,6)); - $arr['comment_policy'] = trim(str_replace($p,'',$AS->obj['commentPolicy'])); + if (is_array($AS->obj) && array_key_exists('commentPolicy', $AS->obj)) { + $p = strstr($AS->obj['commentPolicy'], 'until='); + if ($p !== false) { + $comments_closed_at = datetime_convert('UTC', 'UTC', substr($p, 6)); + if ($comments_closed_at === $arr['created']) { + $arr['item_nocomment'] = 1; + } + else { + $arr['comments_closed'] = $comments_closed_at; + $arr['comment_policy'] = trim(str_replace($p, '', $AS->obj['commentPolicy'])); + } } else { - $arr['comment_policy'] = $AS->obj['commentPolicy']; + $arr['comment_policy'] = $AS->obj['commentPolicy']; } } } - - /// @FIXME - spoofable - if($AS->data['hubloc']) { + if ($AS->data['hubloc']) { $arr['item_verified'] = true; - if (! array_key_exists('comment_policy',$arr)) { + if (!array_key_exists('comment_policy', $arr)) { // set comment policy depending on source hub. Unknown or osada is ActivityPub. // Anything else we'll say is zot - which could have a range of project names $s = q("select site_project from site where site_url = '%s' limit 1", dbesc($r[0]['hubloc_url']) ); - if ((! $s) || (in_array($s[0]['site_project'],[ '', 'osada' ]))) { + if ((!$s) || (in_array($s[0]['site_project'], ['', 'osada']))) { $arr['comment_policy'] = 'authenticated'; } else { @@ -1262,28 +1295,28 @@ class Libzot { } } } - if($AS->data['signed_data']) { - IConfig::Set($arr,'activitystreams','signed_data',$AS->data['signed_data'],false); - } + if ($AS->data['signed_data']) { + IConfig::Set($arr, 'activitypub', 'signed_data', $AS->data['signed_data'], false); + } - logger('Activity received: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG); - logger('Activity recipients: ' . print_r($deliveries,true), LOGGER_DATA, LOG_DEBUG); + logger('Activity received: ' . print_r($arr, true), LOGGER_DATA, LOG_DEBUG); + logger('Activity recipients: ' . print_r($deliveries, true), LOGGER_DATA, LOG_DEBUG); - $relay = (($env['type'] === 'response') ? true : false ); + $relay = (($env['type'] === 'response') ? true : false); - $result = self::process_delivery($env['sender'],$AS,$arr,$deliveries,$relay,false,$message_request); + $result = self::process_delivery($env['sender'], $AS, $arr, $deliveries, $relay, false, $message_request); } - elseif($env['type'] === 'sync') { + elseif ($env['type'] === 'sync') { // $arr = get_channelsync_elements($data); - $arr = json_decode($data,true); + $arr = json_decode($data, true); - logger('Channel sync received: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG); - logger('Channel sync recipients: ' . print_r($deliveries,true), LOGGER_DATA, LOG_DEBUG); + logger('Channel sync received: ' . print_r($arr, true), LOGGER_DATA, LOG_DEBUG); + logger('Channel sync recipients: ' . print_r($deliveries, true), LOGGER_DATA, LOG_DEBUG); if ($env['encoding'] === 'hz') { - $result = Libsync::process_channel_sync_delivery($env['sender'],$arr,$deliveries); + $result = Libsync::process_channel_sync_delivery($env['sender'], $arr, $deliveries); } else { logger('sync packet type not supported.'); @@ -1305,15 +1338,15 @@ class Libzot { * @return boolean */ static function is_top_level($env, $act) { - if($env['encoding'] === 'zot' && array_key_exists('flags',$env) && in_array('thread_parent', $env['flags'])) { + if ($env['encoding'] === 'zot' && array_key_exists('flags', $env) && in_array('thread_parent', $env['flags'])) { return true; } - if($act) { - if(in_array($act->type, ['Like','Dislike'])) { + if ($act) { + if (in_array($act->type, ['Like', 'Dislike'])) { return false; } - $x = self::find_parent($env,$act); - if($x === $act->id || $x === $act->obj['id']) { + $x = self::find_parent($env, $act); + if ($x === $act->id || $x === $act->obj['id']) { return true; } } @@ -1321,12 +1354,12 @@ class Libzot { } - static function find_parent($env,$act) { - if($act) { - if(in_array($act->type, ['Like','Dislike'])) { + static function find_parent($env, $act) { + if ($act) { + if (in_array($act->type, ['Like', 'Dislike']) && is_array($act->obj)) { return $act->obj['id']; } - if($act->parent_id) { + if ($act->parent_id) { return $act->parent_id; } } @@ -1355,58 +1388,55 @@ class Libzot { require_once('include/channel.php'); $check_mentions = false; - $include_sys = false; + $include_sys = false; - if($msg['type'] === 'activity') { - $disable_discover_tab = get_config('system','disable_discover_tab') || get_config('system','disable_discover_tab') === false; - if(! $disable_discover_tab) + if ($msg['type'] === 'activity') { + $disable_discover_tab = get_config('system', 'disable_discover_tab') || get_config('system', 'disable_discover_tab') === false; + if (!$disable_discover_tab) $include_sys = true; $perm = 'send_stream'; - if(self::is_top_level($msg,$act)) { + if (self::is_top_level($msg, $act)) { $check_mentions = true; } } - elseif($msg['type'] === 'mail') - $perm = 'post_mail'; $r = []; $c = q("select channel_id, channel_hash from channel where channel_removed = 0"); - if($c) { - foreach($c as $cc) { - if(perm_is_allowed($cc['channel_id'],$msg['sender'],$perm)) { + if ($c) { + foreach ($c as $cc) { + if (perm_is_allowed($cc['channel_id'], $msg['sender'], $perm)) { $r[] = $cc['channel_hash']; } } } - if($include_sys) { + if ($include_sys) { $sys = get_sys_channel(); - if($sys) + if ($sys) $r[] = $sys['channel_hash']; } - // look for any public mentions on this site // They will get filtered by tgroup_check() so we don't need to check permissions now - if($check_mentions) { + if ($check_mentions) { // It's a top level post. Look at the tags. See if any of them are mentions and are on this hub. - if($act && $act->obj) { - if(is_array($act->obj['tag']) && $act->obj['tag']) { - foreach($act->obj['tag'] as $tag) { - if($tag['type'] === 'Mention' && (strpos($tag['href'],z_root()) !== false)) { + if ($act && $act->obj) { + if (is_array($act->obj['tag']) && $act->obj['tag']) { + foreach ($act->obj['tag'] as $tag) { + if ($tag['type'] === 'Mention' && (strpos($tag['href'], z_root()) !== false)) { $address = basename($tag['href']); - if($address) { + if ($address) { $z = q("select channel_hash as hash from channel where channel_address = '%s' and channel_removed = 0 limit 1", dbesc($address) ); - if($z) { + if ($z) { $r[] = $z[0]['hash']; } } @@ -1420,15 +1450,15 @@ class Libzot { // everybody that stored a copy of the parent. This way we know we're covered. We'll check the // comment permissions when we deliver them. - $thread_parent = self::find_parent($msg,$act); + $thread_parent = self::find_parent($msg, $act); - if($thread_parent) { + if ($thread_parent) { $z = q("select channel_hash as hash from channel left join item on channel.channel_id = item.uid where ( item.thr_parent = '%s' OR item.parent_mid = '%s' ) ", dbesc($thread_parent), dbesc($thread_parent) ); - if($z) { - foreach($z as $zv) { + if ($z) { + foreach ($z as $zv) { $r[] = $zv['hash']; } } @@ -1438,11 +1468,11 @@ class Libzot { // There are probably a lot of duplicates in $r at this point. We need to filter those out. // It's a bit of work since it's a multi-dimensional array - if($r) { + if ($r) { $r = array_values(array_unique($r)); } - logger('public_recips: ' . print_r($r,true), LOGGER_DATA, LOG_DEBUG); + logger('public_recips: ' . print_r($r, true), LOGGER_DATA, LOG_DEBUG); return $r; } @@ -1465,22 +1495,22 @@ class Libzot { // We've validated the sender. Now make sure that the sender is the owner or author - if(! $public) { - if($sender != $arr['owner_xchan'] && $sender != $arr['author_xchan']) { + if (!$public) { + if ($sender != $arr['owner_xchan'] && $sender != $arr['author_xchan']) { logger("Sender $sender is not owner {$arr['owner_xchan']} or author {$arr['author_xchan']} - mid {$arr['mid']}"); return; } } - foreach($deliveries as $d) { + foreach ($deliveries as $d) { $local_public = $public; - $DR = new DReport(z_root(),$sender,$d,$arr['mid']); + $DR = new DReport(z_root(), $sender, $d, $arr['mid']); $channel = channelx_by_hash($d); - if (! $channel) { + if (!$channel) { $DR->update('recipient not found'); $result[] = $DR->get(); continue; @@ -1488,16 +1518,16 @@ class Libzot { $DR->set_name($channel['channel_name'] . ' <' . channel_reddress($channel) . '>'); - if(($act) && ($act->obj) && (! is_array($act->obj))) { - // The initial object fetch failed using the sys channel credentials. + if (($act) && ($act->obj) && (!is_array($act->obj))) { + // The initial object fetch failed using the sys channel credentials. // Try again using the delivery channel credentials. - // We will also need to re-parse the $item array, + // We will also need to re-parse the $item array, // but preserve any values that were set during anonymous parsing. - $o = Activity::fetch($act->obj,$channel); - if($o) { + $o = Activity::fetch($act->obj, $channel); + if ($o) { $act->obj = $o; - $arr = array_merge(Activity::decode_note($act),$arr); + $arr = array_merge(Activity::decode_note($act), $arr); } else { @@ -1505,7 +1535,7 @@ class Libzot { $result[] = $DR->get(); continue; } - } + } /** * We need to block normal top-level message delivery from our clones, as the delivered @@ -1516,7 +1546,7 @@ class Libzot { * access checks. */ - if($sender === $channel['channel_hash'] && $arr['author_xchan'] === $channel['channel_hash'] && $arr['mid'] === $arr['parent_mid']) { + if ($sender === $channel['channel_hash'] && $arr['author_xchan'] === $channel['channel_hash'] && $arr['mid'] === $arr['parent_mid']) { $DR->update('self delivery ignored'); $result[] = $DR->get(); continue; @@ -1526,32 +1556,31 @@ class Libzot { // for comments travelling upstream. Wait and catch them on the way down. // They may have been blocked by the owner. - if(intval($channel['channel_system']) && (! $arr['item_private']) && (! $relay)) { + if (intval($channel['channel_system']) && (!$arr['item_private']) && (!$relay)) { $local_public = true; $r = q("select xchan_selfcensored from xchan where xchan_hash = '%s' limit 1", dbesc($sender) ); // don't import sys channel posts from selfcensored authors - if($r && (intval($r[0]['xchan_selfcensored']))) { + if ($r && (intval($r[0]['xchan_selfcensored']))) { $local_public = false; continue; } - if(! MessageFilter::evaluate($arr,get_config('system','pubstream_incl'),get_config('system','pubstream_excl'))) { + if (!MessageFilter::evaluate($arr, get_config('system', 'pubstream_incl'), get_config('system', 'pubstream_excl'))) { $local_public = false; continue; } } - $tag_delivery = tgroup_check($channel['channel_id'],$arr); - - $perm = 'send_stream'; - if(($arr['mid'] !== $arr['parent_mid']) && ($relay)) + $tag_delivery = tgroup_check($channel['channel_id'], $arr); + $perm = 'send_stream'; + if (($arr['mid'] !== $arr['parent_mid']) && ($relay)) $perm = 'post_comments'; // This is our own post, possibly coming from a channel clone - if($arr['owner_xchan'] == $d) { + if ($arr['owner_xchan'] == $d) { $arr['item_wall'] = 1; } else { @@ -1560,15 +1589,15 @@ class Libzot { $friendofriend = false; - if ((! $tag_delivery) && (! $local_public)) { - $allowed = (perm_is_allowed($channel['channel_id'],$sender,$perm)); - if((! $allowed) && $perm === 'post_comments') { + if ((!$tag_delivery) && (!$local_public)) { + $allowed = (perm_is_allowed($channel['channel_id'], $sender, $perm)); + if (!$allowed) { $parent = q("select * from item where mid = '%s' and uid = %d limit 1", dbesc($arr['parent_mid']), intval($channel['channel_id']) ); if ($parent) { - $allowed = can_comment_on_post($sender,$parent[0]); + $allowed = can_comment_on_post($sender, $parent[0]); } } @@ -1588,7 +1617,7 @@ class Libzot { // doesn't exist. if ($perm === 'send_stream') { - if (get_pconfig($channel['channel_id'],'system','hyperdrive',false) || $arr['verb'] === ACTIVITY_SHARE) { + if (get_pconfig($channel['channel_id'], 'system', 'hyperdrive', false) || $arr['verb'] === ACTIVITY_SHARE) { $allowed = true; } } @@ -1599,7 +1628,13 @@ class Libzot { $friendofriend = true; } - if (! $allowed) { + if (intval($arr['item_private']) === 2) { + if (!perm_is_allowed($channel['channel_id'], $sender, 'post_mail')) { + $allowed = false; + } + } + + if (!$allowed) { logger("permission denied for delivery to channel {$channel['channel_id']} {$channel['channel_address']}"); $DR->update('permission denied'); $result[] = $DR->get(); @@ -1609,7 +1644,7 @@ class Libzot { // logger('item: ' . print_r($arr,true), LOGGER_DATA); - if($arr['mid'] !== $arr['parent_mid']) { + if ($arr['mid'] !== $arr['parent_mid']) { logger('checking source: "' . $arr['mid'] . '" != "' . $arr['parent_mid'] . '"'); @@ -1624,7 +1659,7 @@ class Libzot { intval($channel['channel_id']) ); - if(! $r) { + if (!$r) { $DR->update('comment parent not found'); $result[] = $DR->get(); @@ -1634,14 +1669,14 @@ class Libzot { // have the copy and we don't want the request to loop. // Also don't do this if this comment came from a conversation request packet. // It's possible that comments are allowed but posting isn't and that could - // cause a conversation fetch loop. + // cause a conversation fetch loop. // We'll also check the send_stream permission - because if it isn't allowed, // the top level post is unlikely to be imported and // this is just an exercise in futility. - if((! $relay) && (! $request) && (! $local_public) - && perm_is_allowed($channel['channel_id'],$sender,'send_stream')) { - self::fetch_conversation($channel,$arr['parent_mid']); + if ((!$relay) && (!$request) && (!$local_public) + && perm_is_allowed($channel['channel_id'], $sender, 'send_stream')) { + self::fetch_conversation($channel, $arr['parent_mid']); } continue; } @@ -1650,13 +1685,13 @@ class Libzot { // route checking doesn't work correctly here because we've changed the privacy $r[0]['route'] = EMPTY_STR; // If this is a poll response, convert the obj_type to our (internal-only) "Answer" type - if ($arr['obj_type'] === ACTIVITY_OBJ_COMMENT && $arr['title'] && (! $arr['body'])) { + if ($arr['obj_type'] === ACTIVITY_OBJ_COMMENT && $arr['title'] && (!$arr['body'])) { $arr['obj_type'] = 'Answer'; } } - if($relay || $friendofriend || (intval($r[0]['item_private']) === 0 && intval($arr['item_private']) === 0)) { + 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. @@ -1664,7 +1699,7 @@ class Libzot { // but we are now getting comments via listener delivery // and if there is no privacy on this or the parent, we don't care about the route, // so just set the owner and route accordingly. - $arr['route'] = $r[0]['route']; + $arr['route'] = $r[0]['route']; $arr['owner_xchan'] = $r[0]['owner_xchan']; } else { @@ -1676,24 +1711,24 @@ class Libzot { // Always accept empty routes and firehose items (route contains 'undefined') . $existing_route = explode(',', $r[0]['route']); - $routes = count($existing_route); - if($routes) { - $last_hop = array_pop($existing_route); - $last_prior_route = implode(',',$existing_route); + $routes = count($existing_route); + if ($routes) { + $last_hop = array_pop($existing_route); + $last_prior_route = implode(',', $existing_route); } else { - $last_hop = ''; + $last_hop = ''; $last_prior_route = ''; } - if(in_array('undefined',$existing_route) || $last_hop == 'undefined' || $sender == 'undefined') + if (in_array('undefined', $existing_route) || $last_hop == 'undefined' || $sender == 'undefined') $last_hop = ''; $current_route = (($arr['route']) ? $arr['route'] . ',' : '') . $sender; - if($last_hop && $last_hop != $sender) { + if ($last_hop && $last_hop != $sender) { logger('comment route mismatch: parent route = ' . $r[0]['route'] . ' expected = ' . $current_route, LOGGER_DEBUG); - logger('comment route mismatch: parent msg = ' . $r[0]['id'],LOGGER_DEBUG); + logger('comment route mismatch: parent msg = ' . $r[0]['id'], LOGGER_DEBUG); $DR->update('comment route mismatch'); $result[] = $DR->get(); continue; @@ -1712,10 +1747,10 @@ class Libzot { ); $abook = (($ab) ? $ab[0] : null); - if(intval($arr['item_deleted'])) { + if (intval($arr['item_deleted'])) { // remove_community_tag is a no-op if this isn't a community tag activity - self::remove_community_tag($sender,$arr,$channel['channel_id']); + 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. @@ -1723,13 +1758,13 @@ class Libzot { $arr['aid'] = $channel['channel_account_id']; $arr['uid'] = $channel['channel_id']; - $item_id = self::delete_imported_item($sender,$act,$arr,$channel['channel_id'],$relay); + $item_id = self::delete_imported_item($sender, $act, $arr, $channel['channel_id'], $relay); $DR->update(($item_id) ? 'deleted' : 'delete_failed'); $result[] = $DR->get(); - if($relay && $item_id) { + if ($relay && $item_id) { logger('process_delivery: invoking relay'); - Master::Summon([ 'Notifier', 'relay', intval($item_id) ]); + Master::Summon(['Notifier', 'relay', intval($item_id)]); $DR->update('relayed'); $result[] = $DR->get(); } @@ -1746,11 +1781,11 @@ class Libzot { intval($channel['channel_id']) ); - if($r) { + if ($r) { // We already have this post. $item_id = $r[0]['id']; - if(intval($r[0]['item_deleted'])) { + if (intval($r[0]['item_deleted'])) { // It was deleted locally. $DR->update('update ignored'); $result[] = $DR->get(); @@ -1758,19 +1793,19 @@ class Libzot { continue; } // Maybe it has been edited? - elseif($arr['edited'] > $r[0]['edited']) { - $arr['id'] = $r[0]['id']; + elseif ($arr['edited'] > $r[0]['edited']) { + $arr['id'] = $r[0]['id']; $arr['uid'] = $channel['channel_id']; - if(($arr['mid'] == $arr['parent_mid']) && (! post_is_importable($arr,$abook))) { + if (($arr['mid'] == $arr['parent_mid']) && (!post_is_importable($arr, $abook))) { $DR->update('update ignored'); $result[] = $DR->get(); } else { - $item_result = self::update_imported_item($sender,$arr,$r[0],$channel['channel_id'],$tag_delivery); + $item_result = self::update_imported_item($sender, $arr, $r[0], $channel['channel_id'], $tag_delivery); $DR->update('updated'); $result[] = $DR->get(); - if(! $relay) - add_source_route($item_id,$sender); + if (!$relay) + add_source_route($item_id, $sender); } } else { @@ -1779,7 +1814,7 @@ class Libzot { // We need this line to ensure wall-to-wall comments are relayed (by falling through to the relay bit), // and at the same time not relay any other relayable posts more than once, because to do so is very wasteful. - if(! intval($r[0]['item_origin'])) + if (!intval($r[0]['item_origin'])) continue; } } @@ -1790,7 +1825,7 @@ class Libzot { // if it's a sourced post, call the post_local hooks as if it were // posted locally so that crosspost connectors will be triggered. - if(check_item_source($arr['uid'], $arr) || ($channel['xchan_pubforum'] == 1)) { + if (check_item_source($arr['uid'], $arr) || ($channel['xchan_pubforum'] == 1)) { /** * @hooks post_local * Called when an item has been posted on this machine via mod/item.php (also via API). @@ -1801,19 +1836,19 @@ class Libzot { $item_id = 0; - if(($arr['mid'] == $arr['parent_mid']) && (! post_is_importable($arr,$abook))) { + if (($arr['mid'] == $arr['parent_mid']) && (!post_is_importable($arr, $abook))) { $DR->update('post ignored'); $result[] = $DR->get(); } else { $item_result = item_store($arr); - if($item_result['success']) { + if ($item_result['success']) { $item_id = $item_result['item_id']; - $parr = [ - 'item_id' => $item_id, - 'item' => $arr, - 'sender' => $sender, - 'channel' => $channel + $parr = [ + 'item_id' => $item_id, + 'item' => $arr, + 'sender' => $sender, + 'channel' => $channel ]; /** * @hooks activity_received @@ -1825,8 +1860,8 @@ class Libzot { */ 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) - add_source_route($item_id,$sender); + if (!$relay) + add_source_route($item_id, $sender); } $DR->update(($item_id) ? 'posted' : 'storage failed: ' . $item_result['message']); $result[] = $DR->get(); @@ -1836,132 +1871,132 @@ class Libzot { // preserve conversations with which you are involved from expiration $stored = (($item_result && $item_result['item']) ? $item_result['item'] : false); - if((is_array($stored)) && ($stored['id'] != $stored['parent']) + if ((is_array($stored)) && ($stored['id'] != $stored['parent']) && ($stored['author_xchan'] === $channel['channel_hash'] || $stored['author_xchan'] === $channel['channel_hash'])) { retain_item($stored['item']['parent']); } - if($relay && $item_id) { + if ($relay && $item_id) { logger('Invoking relay'); - Master::Summon([ 'Notifier', 'relay', intval($item_id) ]); + Master::Summon(['Notifier', 'relay', intval($item_id)]); $DR->addto_update('relayed'); $result[] = $DR->get(); } } - if(! $deliveries) - $result[] = array('', 'no recipients', '', $arr['mid']); + if (!$deliveries) + $result[] = ['', 'no recipients', '', $arr['mid']]; logger('Local results: ' . print_r($result, true), LOGGER_DEBUG); return $result; } - static public function fetch_conversation($channel,$mid) { + static public function fetch_conversation($channel, $mid) { // Use Zotfinger to create a signed request - $a = Zotfinger::exec($mid,$channel); + logger('fetching conversation: ' . $mid, LOGGER_DEBUG); - logger('received conversation: ' . print_r($a,true), LOGGER_DATA); + $a = Zotfinger::exec($mid, $channel); - if($a['data']['type'] !== 'OrderedCollection') { - return; + logger('received conversation: ' . print_r($a, true), LOGGER_DATA); + + if (!$a) { + return false; } - if(! intval($a['data']['totalItems'])) { - return; + if ($a['data']['type'] !== 'OrderedCollection') { + return false; + } + + $obj = new ASCollection($a['data'], $channel); + $items = $obj->get(); + + if (!$items) { + return false; } $ret = []; + $signer = q("select hubloc_hash, hubloc_url from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1", dbesc($a['signature']['signer']) ); - foreach($a['data']['orderedItems'] as $activity) { + foreach ($items as $activity) { $AS = new ActivityStreams($activity); - if(! $AS->is_valid()) { - logger('FOF Activity rejected: ' . print_r($activity,true)); + 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; } $arr = Activity::decode_note($AS); - logger($AS->debug()); + // logger($AS->debug()); - - $r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1", + $r = q("select hubloc_hash, hubloc_network from hubloc where hubloc_id_url = '%s'", dbesc($AS->actor['id']) ); + $r = self::zot_record_preferred($r); - if(! $r) { - $y = import_author_xchan([ 'url' => $AS->actor['id'] ]); - if($y) { - $r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1", + if (!$r) { + $y = import_author_xchan(['url' => $AS->actor['id']]); + if ($y) { + $r = q("select hubloc_hash, hubloc_network from hubloc where hubloc_id_url = '%s'", dbesc($AS->actor['id']) ); + $r = self::zot_record_preferred($r); } - if(! $r) { + if (!$r) { logger('FOF Activity: no actor'); continue; } } - if($AS->obj['actor'] && $AS->obj['actor']['id'] && $AS->obj['actor']['id'] !== $AS->actor['id']) { - $y = import_author_xchan([ 'url' => $AS->obj['actor']['id'] ]); - if(! $y) { + if ($AS->obj['actor'] && $AS->obj['actor']['id'] && $AS->obj['actor']['id'] !== $AS->actor['id']) { + $y = import_author_xchan(['url' => $AS->obj['actor']['id']]); + if (!$y) { logger('FOF Activity: no object actor'); continue; } } - - if($r) { - $arr['author_xchan'] = $r[0]['hubloc_hash']; + if ($r) { + $arr['author_xchan'] = $r['hubloc_hash']; } - $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']; + if ($signer) { + $arr['owner_xchan'] = $signer[0]['hubloc_hash']; } else { $arr['owner_xchan'] = $a['signature']['signer']; } - - /// @FIXME - spoofable - if($AS->data['hubloc']) { + if ($AS->data['hubloc'] || $arr['author_xchan'] === $arr['owner_xchan']) { $arr['item_verified'] = true; } - // set comment policy depending on source hub. Unknown or osada is ActivityPub. - // Anything else we'll say is zot - which could have a range of project names - - if ($signer) { - $s = q("select site_project from site where site_url = '%s' limit 1", - dbesc($signer[0]['hubloc_url']) - ); - if ((! $s) || (in_array($s[0]['site_project'],[ '', 'osada' ]))) { - $arr['comment_policy'] = 'authenticated'; - } - else { - $arr['comment_policy'] = 'contacts'; + if ($AS->data['signed_data']) { + IConfig::Set($arr, 'activitypub', 'signed_data', $AS->data['signed_data'], false); + $j = json_decode($AS->data['signed_data'], true); + if ($j) { + IConfig::Set($arr, 'activitypub', 'rawmsg', json_encode(JSalmon::unpack($j['data'])), true); } } - - if($AS->data['signed_data']) { - IConfig::Set($arr,'activitystreams','signed_data',$AS->data['signed_data'],false); - } - - logger('FOF Activity received: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG); + logger('FOF Activity received: ' . print_r($arr, true), LOGGER_DATA, LOG_DEBUG); logger('FOF Activity recipient: ' . $channel['channel_hash'], LOGGER_DATA, LOG_DEBUG); - $result = self::process_delivery($arr['owner_xchan'],$AS, $arr, [ $channel['channel_hash'] ],false,false,true); + $result = self::process_delivery($arr['owner_xchan'], $AS, $arr, [$channel['channel_hash']], false, false, true); if ($result) { $ret = array_merge($ret, $result); } @@ -1970,7 +2005,6 @@ class Libzot { return $ret; } - /** * @brief Remove community tag. * @@ -1984,12 +2018,12 @@ class Libzot { */ static function remove_community_tag($sender, $arr, $uid) { - if(! (activity_match($arr['verb'], ACTIVITY_TAG) && ($arr['obj_type'] == ACTIVITY_OBJ_TAGTERM))) + if (!(activity_match($arr['verb'], ACTIVITY_TAG) && ($arr['obj_type'] == ACTIVITY_OBJ_TAGTERM))) return; logger('remove_community_tag: invoked'); - if(! get_pconfig($uid,'system','blocktags')) { + if (!get_pconfig($uid, 'system', 'blocktags')) { logger('Permission denied.'); return; } @@ -1998,24 +2032,24 @@ class Libzot { dbesc($arr['mid']), intval($uid) ); - if(! $r) { + if (!$r) { logger('No item'); return; } - if(($sender != $r[0]['owner_xchan']) && ($sender != $r[0]['author_xchan'])) { + if (($sender != $r[0]['owner_xchan']) && ($sender != $r[0]['author_xchan'])) { logger('Sender not authorised.'); return; } $i = $r[0]; - if($i['target']) - $i['target'] = json_decode($i['target'],true); - if($i['object']) - $i['object'] = json_decode($i['object'],true); + if ($i['target']) + $i['target'] = json_decode($i['target'], true); + if ($i['object']) + $i['object'] = json_decode($i['object'], true); - if(! ($i['target'] && $i['object'])) { + if (!($i['target'] && $i['object'])) { logger('No target/object'); return; } @@ -2026,7 +2060,7 @@ class Libzot { dbesc($message_id), intval($uid) ); - if(! $r) { + if (!$r) { logger('No parent message'); return; } @@ -2038,28 +2072,28 @@ class Libzot { intval(TERM_HASHTAG), intval(TERM_COMMUNITYTAG), dbesc($i['object']['title']), - dbesc(get_rel_link($i['object']['link'],'alternate')) + dbesc(get_rel_link($i['object']['link'], 'alternate')) ); } /** * @brief Updates an imported item. * - * @see item_store_update() - * * @param string $sender * @param array $item * @param array $orig * @param int $uid * @param boolean $tag_delivery * @return void|array + * @see item_store_update() + * */ static function update_imported_item($sender, $item, $orig, $uid, $tag_delivery) { // If this is a comment being updated, remove any privacy information // so that item_store_update will set it from the original. - if($item['mid'] !== $item['parent_mid']) { + if ($item['mid'] !== $item['parent_mid']) { unset($item['allow_cid']); unset($item['allow_gid']); unset($item['deny_cid']); @@ -2070,7 +2104,7 @@ class Libzot { // we need the tag_delivery check for downstream flowing posts as the stored post // may have a different owner than the one being transmitted. - if(($sender != $orig['owner_xchan'] && $sender != $orig['author_xchan']) && (! $tag_delivery)) { + if (($sender != $orig['owner_xchan'] && $sender != $orig['author_xchan']) && (!$tag_delivery)) { logger('sender is not owner or author'); return; } @@ -2081,13 +2115,13 @@ class Libzot { // If we're updating an event that we've saved locally, we store the item info first // because event_addtocal will parse the body to get the 'new' event details - if($orig['resource_type'] === 'event') { + if ($orig['resource_type'] === 'event') { $res = event_addtocal($orig['id'], $uid); - if(! $res) + if (!$res) logger('update event: failed'); } - if(! $x['item_id']) + if (!$x['item_id']) logger('update_imported_item: failed: ' . $x['message']); else logger('update_imported_item'); @@ -2111,8 +2145,8 @@ class Libzot { logger('invoked', LOGGER_DEBUG); $ownership_valid = false; - $item_found = false; - $post_id = 0; + $item_found = false; + $post_id = 0; if ($item['verb'] === 'Tombstone') { // The id of the deleted thing is the item mid (activity id) @@ -2131,17 +2165,17 @@ class Libzot { dbesc($sender), dbesc($sender), dbesc($mid), - dbesc(str_replace('/activity/','/item/',$mid)), + dbesc(str_replace('/activity/', '/item/', $mid)), intval($uid) ); - if($r) { + if ($r) { $stored = $r[0]; // we proved ownership in the sql query $ownership_valid = true; - $post_id = $stored['id']; + $post_id = $stored['id']; $item_found = true; } else { @@ -2149,7 +2183,7 @@ class Libzot { logger('delete received for non-existent item or not owned by sender - ignoring.'); } - if($ownership_valid === false) { + if ($ownership_valid === false) { logger('delete_imported_item: failed: ownership issue'); return false; } @@ -2173,10 +2207,10 @@ class Libzot { } } - if($item_found) { - if(intval($stored['item_deleted'])) { + if ($item_found) { + if (intval($stored['item_deleted'])) { logger('delete_imported_item: item was already deleted'); - if(! $relay) + if (!$relay) return false; // This is a bit hackish, but may have to suffice until the notification/delivery loop is optimised @@ -2205,100 +2239,16 @@ class Libzot { return $post_id; } - static function process_mail_delivery($sender, $arr, $deliveries) { - - $result = array(); - - if($sender != $arr['from_xchan']) { - logger('process_mail_delivery: sender is not mail author'); - return; - } - - foreach($deliveries as $d) { - - $DR = new DReport(z_root(),$sender,$d,$arr['mid']); - - $r = q("select * from channel where channel_hash = '%s' limit 1", - dbesc($d['hash']) - ); - - if(! $r) { - $DR->update('recipient not found'); - $result[] = $DR->get(); - continue; - } - - $channel = $r[0]; - $DR->set_name($channel['channel_name'] . ' <' . channel_reddress($channel) . '>'); - - - 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. - */ - - $return = false; - if($arr['parent_mid']) { - $return = q("select * from mail where mid = '%s' and channel_id = %d limit 1", - dbesc($arr['parent_mid']), - intval($channel['channel_id']) - ); - } - if(! $return) { - logger("permission denied for mail delivery {$channel['channel_id']}"); - $DR->update('permission denied'); - $result[] = $DR->get(); - continue; - } - } - - - $r = q("select id from mail where mid = '%s' and channel_id = %d limit 1", - dbesc($arr['mid']), - intval($channel['channel_id']) - ); - if($r) { - if(intval($arr['mail_recalled'])) { - $x = q("delete from mail where id = %d and channel_id = %d", - intval($r[0]['id']), - intval($channel['channel_id']) - ); - $DR->update('mail recalled'); - $result[] = $DR->get(); - logger('mail_recalled'); - } - else { - $DR->update('duplicate mail received'); - $result[] = $DR->get(); - logger('duplicate mail received'); - } - continue; - } - else { - $arr['account_id'] = $channel['channel_account_id']; - $arr['channel_id'] = $channel['channel_id']; - $item_id = mail_store($arr); - $DR->update('mail delivered'); - $result[] = $DR->get(); - } - } - - return $result; - } - /** * @brief Processes delivery of profile. * - * @see import_directory_profile() - * - * @param string $sender + * @param string $sender * @param array $arr * @param array $deliveries (unused) * @return void + * @see import_directory_profile() + * */ static function process_profile_delivery($sender, $arr, $deliveries) { @@ -2307,7 +2257,7 @@ class Libzot { $r = q("select xchan_addr from xchan where xchan_hash = '%s' limit 1", dbesc($sender) ); - if($r) { + if ($r) { Libzotdir::import_directory_profile($sender, $arr, $r[0]['xchan_addr'], UPDATE_FLAGS_UPDATED, 0); } } @@ -2316,7 +2266,7 @@ class Libzot { /** * @brief * - * @param string $sender + * @param string $sender * @param array $arr * @param array $deliveries (unused) deliveries is irrelevant * @return void @@ -2329,16 +2279,16 @@ class Libzot { $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($sender) ); - if($r) { - $xchan = [ 'id' => $r[0]['xchan_guid'], 'id_sig' => $r[0]['xchan_guid_sig'], - 'hash' => $r[0]['xchan_hash'], 'public_key' => $r[0]['xchan_pubkey'] ]; - } - if(array_key_exists('locations',$arr) && $arr['locations']) { - $x = Libsync::sync_locations($xchan,$arr,true); - logger('results: ' . print_r($x,true), LOGGER_DEBUG); - if($x['changed']) { + if ($r) { + $xchan = ['id' => $r[0]['xchan_guid'], 'id_sig' => $r[0]['xchan_guid_sig'], + 'hash' => $r[0]['xchan_hash'], 'public_key' => $r[0]['xchan_pubkey']]; + } + if (array_key_exists('locations', $arr) && $arr['locations']) { + $x = Libsync::sync_locations($xchan, $arr, true); + logger('results: ' . print_r($x, true), LOGGER_DEBUG); + if ($x['changed']) { //$guid = random_string() . '@' . App::get_hostname(); - Libzotdir::update_modtime($sender,$r[0]['xchan_guid'],$arr['locations'][0]['address'],UPDATE_FLAGS_UPDATED); + Libzotdir::update_modtime($sender, $r[0]['xchan_guid'], $arr['locations'][0]['address'], UPDATE_FLAGS_UPDATED); } } } @@ -2365,10 +2315,10 @@ class Libzot { */ static function check_location_move($sender_hash, $locations) { - if(! $locations) + if (!$locations) return; - if(count($locations) != 1) + if (count($locations) != 1) return; $loc = $locations[0]; @@ -2377,10 +2327,10 @@ class Libzot { dbesc($sender_hash) ); - if(! $r) + if (!$r) return; - if($loc['url'] !== z_root()) { + if ($loc['url'] !== z_root()) { $x = q("update channel set channel_moved = '%s' where channel_hash = '%s' limit 1", dbesc($loc['url']), dbesc($sender_hash) @@ -2390,7 +2340,7 @@ class Libzot { // of the move on singleton networks $arr = [ - 'channel' => $r[0], + 'channel' => $r[0], 'locations' => $locations ]; /** @@ -2407,23 +2357,23 @@ class Libzot { /** * @brief Returns an array with all known distinct hubs for this channel. * - * @see self::get_hublocs() * @param array $channel an associative array which must contain * * \e string \b channel_hash the hash of the channel * @return array an array with associative arrays + * @see self::get_hublocs() */ static function encode_locations($channel) { $ret = []; $x = self::get_hublocs($channel['channel_hash']); - if($x && count($x)) { - foreach($x as $hub) { + if ($x && count($x)) { + foreach ($x as $hub) { // if this is a local channel that has been deleted, the hubloc is no good - make sure it is marked deleted // so that nobody tries to use it. - if(intval($channel['channel_removed']) && $hub['hubloc_url'] === z_root()) + if (intval($channel['channel_removed']) && $hub['hubloc_url'] === z_root()) $hub['hubloc_deleted'] = 1; @@ -2442,15 +2392,15 @@ class Libzot { // version compatibility tweaks - if(! strpos($z['url_sig'],'.')) { + if (!strpos($z['url_sig'], '.')) { $z['url_sig'] = 'sha256.' . $z['url_sig']; } - if(! $z['id_url']) { - $z['id_url'] = $z['url'] . '/channel/' . substr($z['address'],0,strpos($z['address'],'@')); + if (!$z['id_url']) { + $z['id_url'] = $z['url'] . '/channel/' . substr($z['address'], 0, strpos($z['address'], '@')); } - if(! $z['site_id']) { - $z['site_id'] = Libzot::make_xchan_hash($z['url'],$z['sitekey']); + if (!$z['site_id']) { + $z['site_id'] = Libzot::make_xchan_hash($z['url'], $z['sitekey']); } $ret[] = $z; @@ -2469,10 +2419,10 @@ class Libzot { */ static function import_site($arr) { - if( (! is_array($arr)) || (! $arr['url']) || (! $arr['site_sig'])) + if ((!is_array($arr)) || (!$arr['url']) || (!$arr['site_sig'])) return false; - if(! self::verify($arr['url'], $arr['site_sig'], $arr['sitekey'])) { + if (!self::verify($arr['url'], $arr['site_sig'], $arr['sitekey'])) { logger('Bad url_sig'); return false; } @@ -2483,66 +2433,66 @@ class Libzot { $r = q("select * from site where site_url = '%s' limit 1", dbesc($arr['url']) ); - if($r) { - $exists = true; + if ($r) { + $exists = true; $siterecord = $r[0]; } $site_directory = 0; - if($arr['directory_mode'] == 'normal') + if ($arr['directory_mode'] == 'normal') $site_directory = DIRECTORY_MODE_NORMAL; - if($arr['directory_mode'] == 'primary') + if ($arr['directory_mode'] == 'primary') $site_directory = DIRECTORY_MODE_PRIMARY; - if($arr['directory_mode'] == 'secondary') + if ($arr['directory_mode'] == 'secondary') $site_directory = DIRECTORY_MODE_SECONDARY; - if($arr['directory_mode'] == 'standalone') + if ($arr['directory_mode'] == 'standalone') $site_directory = DIRECTORY_MODE_STANDALONE; $register_policy = 0; - if($arr['register_policy'] == 'closed') + if ($arr['register_policy'] == 'closed') $register_policy = REGISTER_CLOSED; - if($arr['register_policy'] == 'open') + if ($arr['register_policy'] == 'open') $register_policy = REGISTER_OPEN; - if($arr['register_policy'] == 'approve') + if ($arr['register_policy'] == 'approve') $register_policy = REGISTER_APPROVE; $access_policy = 0; - if(array_key_exists('access_policy',$arr)) { - if($arr['access_policy'] === 'private') + if (array_key_exists('access_policy', $arr)) { + if ($arr['access_policy'] === 'private') $access_policy = ACCESS_PRIVATE; - if($arr['access_policy'] === 'paid') + if ($arr['access_policy'] === 'paid') $access_policy = ACCESS_PAID; - if($arr['access_policy'] === 'free') + if ($arr['access_policy'] === 'free') $access_policy = ACCESS_FREE; - if($arr['access_policy'] === 'tiered') + if ($arr['access_policy'] === 'tiered') $access_policy = ACCESS_TIERED; } // don't let insecure sites register as public hubs - if(strpos($arr['url'],'https://') === false) + if (strpos($arr['url'], 'https://') === false) $access_policy = ACCESS_PRIVATE; - if($access_policy != ACCESS_PRIVATE) { + if ($access_policy != ACCESS_PRIVATE) { $x = z_fetch_url($arr['url'] . '/siteinfo.json'); - if(! $x['success']) + if (!$x['success']) $access_policy = ACCESS_PRIVATE; } - $directory_url = htmlspecialchars($arr['directory_url'],ENT_COMPAT,'UTF-8',false); - $url = htmlspecialchars(strtolower($arr['url']),ENT_COMPAT,'UTF-8',false); - $sellpage = htmlspecialchars($arr['sellpage'],ENT_COMPAT,'UTF-8',false); - $site_location = htmlspecialchars($arr['location'],ENT_COMPAT,'UTF-8',false); - $site_realm = htmlspecialchars($arr['realm'],ENT_COMPAT,'UTF-8',false); - $site_project = htmlspecialchars($arr['project'],ENT_COMPAT,'UTF-8',false); - $site_crypto = ((array_key_exists('encryption',$arr) && is_array($arr['encryption'])) ? htmlspecialchars(implode(',',$arr['encryption']),ENT_COMPAT,'UTF-8',false) : ''); - $site_version = ((array_key_exists('version',$arr)) ? htmlspecialchars($arr['version'],ENT_COMPAT,'UTF-8',false) : ''); + $directory_url = htmlspecialchars($arr['directory_url'], ENT_COMPAT, 'UTF-8', false); + $url = htmlspecialchars(strtolower($arr['url']), ENT_COMPAT, 'UTF-8', false); + $sellpage = htmlspecialchars($arr['sellpage'], ENT_COMPAT, 'UTF-8', false); + $site_location = htmlspecialchars($arr['location'], ENT_COMPAT, 'UTF-8', false); + $site_realm = htmlspecialchars($arr['realm'], ENT_COMPAT, 'UTF-8', false); + $site_project = htmlspecialchars($arr['project'], ENT_COMPAT, 'UTF-8', false); + $site_crypto = ((array_key_exists('encryption', $arr) && is_array($arr['encryption'])) ? htmlspecialchars(implode(',', $arr['encryption']), ENT_COMPAT, 'UTF-8', false) : ''); + $site_version = ((array_key_exists('version', $arr)) ? htmlspecialchars($arr['version'], ENT_COMPAT, 'UTF-8', false) : ''); // You can have one and only one primary directory per realm. // Downgrade any others claiming to be primary. As they have // flubbed up this badly already, don't let them be directory servers at all. - if(($site_directory === DIRECTORY_MODE_PRIMARY) + if (($site_directory === DIRECTORY_MODE_PRIMARY) && ($site_realm === get_directory_realm()) && ($arr['url'] != get_directory_primary())) { $site_directory = DIRECTORY_MODE_NORMAL; @@ -2550,12 +2500,12 @@ class Libzot { $site_flags = $site_directory; - if(array_key_exists('zot',$arr)) { - set_sconfig($arr['url'],'system','zot_version',$arr['zot']); + if (array_key_exists('zot', $arr)) { + set_sconfig($arr['url'], 'system', 'zot_version', $arr['zot']); } - if($exists) { - if(($siterecord['site_flags'] != $site_flags) + if ($exists) { + if (($siterecord['site_flags'] != $site_flags) || ($siterecord['site_access'] != $access_policy) || ($siterecord['site_directory'] != $directory_url) || ($siterecord['site_sellpage'] != $sellpage) @@ -2564,12 +2514,12 @@ class Libzot { || ($siterecord['site_project'] != $site_project) || ($siterecord['site_realm'] != $site_realm) || ($siterecord['site_crypto'] != $site_crypto) - || ($siterecord['site_version'] != $site_version) ) { + || ($siterecord['site_version'] != $site_version)) { $update = true; - // logger('import_site: input: ' . print_r($arr,true)); - // logger('import_site: stored: ' . print_r($siterecord,true)); + // logger('import_site: input: ' . print_r($arr,true)); + // logger('import_site: stored: ' . print_r($siterecord,true)); $r = q("update site set site_dead = 0, site_location = '%s', site_flags = %d, site_access = %d, site_directory = '%s', site_register = %d, site_update = '%s', site_sellpage = '%s', site_realm = '%s', site_type = %d, site_project = '%s', site_version = '%s', site_crypto = '%s' where site_url = '%s'", @@ -2587,8 +2537,8 @@ class Libzot { dbesc($site_crypto), dbesc($url) ); - if(! $r) { - logger('Update failed. ' . print_r($arr,true)); + if (!$r) { + logger('Update failed. ' . print_r($arr, true)); } } else { @@ -2620,8 +2570,8 @@ class Libzot { ] ); - if(! $r) { - logger('Record create failed. ' . print_r($arr,true)); + if (!$r) { + logger('Record create failed. ' . print_r($arr, true)); } } @@ -2631,14 +2581,14 @@ class Libzot { /** * @brief Returns path to /rpost * - * @todo We probably should make rpost discoverable. - * * @param array $observer * * \e string \b xchan_url * @return string + * @todo We probably should make rpost discoverable. + * */ static function get_rpost_path($observer) { - if(! $observer) + if (!$observer) return ''; $parsed = parse_url($observer['xchan_url']); @@ -2659,39 +2609,44 @@ class Libzot { // we may only end up with one; which results in posts with no author name or photo and are a bit // of a hassle to repair. If either or both are missing, do a full discovery probe. - if(! array_key_exists('id',$x)) { - return import_author_activitypub($x); + if(!isset($x['id']) && !isset($x['key']) && !isset($x['id_sig'])) { + return false; } - $hash = self::make_xchan_hash($x['id'],$x['key']); + $hash = self::make_xchan_hash($x['id'], $x['key']); $desturl = $x['url']; + $found_primary = false; + $r1 = q("select hubloc_url, hubloc_updated, site_dead from hubloc left join site on hubloc_url = site_url where hubloc_guid = '%s' and hubloc_guid_sig = '%s' and hubloc_primary = 1 limit 1", dbesc($x['id']), dbesc($x['id_sig']) ); + if ($r1) { + $found_primary = true; + } $r2 = q("select xchan_hash from xchan where xchan_guid = '%s' and xchan_guid_sig = '%s' limit 1", dbesc($x['id']), dbesc($x['id_sig']) ); - $site_dead = false; + $primary_dead = false; - if($r1 && intval($r1[0]['site_dead'])) { - $site_dead = true; + if ($r1 && intval($r1[0]['site_dead'])) { + $primary_dead = true; } // We have valid and somewhat fresh information. Always true if it is our own site. - if($r1 && $r2 && ( $r1[0]['hubloc_updated'] > datetime_convert('UTC','UTC','now - 1 week') || $r1[0]['hubloc_url'] === z_root() ) ) { + if ($r1 && $r2 && ($r1[0]['hubloc_updated'] > datetime_convert('UTC', 'UTC', 'now - 1 week') || $r1[0]['hubloc_url'] === z_root())) { logger('in cache', LOGGER_DEBUG); return $hash; } - logger('not in cache or cache stale - probing: ' . print_r($x,true), LOGGER_DEBUG,LOG_INFO); + logger('not in cache or cache stale - probing: ' . print_r($x, true), LOGGER_DEBUG, LOG_INFO); // The primary hub may be dead. Try to find another one associated with this identity that is // still alive. If we find one, use that url for the discovery/refresh probe. Otherwise, the dead site @@ -2699,24 +2654,25 @@ class Libzot { // cached entry and the identity is valid. It's just unreachable until they bring back their // server from the grave or create another clone elsewhere. - if($site_dead) { - logger('dead site - ignoring', LOGGER_DEBUG,LOG_INFO); + if ($primary_dead || ! $found_primary) { + logger('dead or unknown primary site - ignoring', LOGGER_DEBUG, LOG_INFO); $r = q("select hubloc_id_url from hubloc left join site on hubloc_url = site_url where hubloc_hash = '%s' and site_dead = 0", dbesc($hash) ); - if($r) { - logger('found another site that is not dead: ' . $r[0]['hubloc_url'], LOGGER_DEBUG,LOG_INFO); - $desturl = $r[0]['hubloc_url']; + + if ($r) { + logger('found another site that is not dead: ' . $r[0]['hubloc_id_url'], LOGGER_DEBUG, LOG_INFO); + $desturl = $r[0]['hubloc_id_url']; } else { return $hash; } } - $them = [ 'hubloc_id_url' => $desturl ]; - if(self::refresh($them)) + $them = ['hubloc_id_url' => $desturl]; + if (self::refresh($them)) return $hash; return false; @@ -2724,27 +2680,27 @@ class Libzot { static function zotinfo($arr) { - logger('arr: ' . print_r($arr,true)); + logger('arr: ' . print_r($arr, true)); $ret = []; - $zhash = ((x($arr,'guid_hash')) ? $arr['guid_hash'] : ''); - $zguid = ((x($arr,'guid')) ? $arr['guid'] : ''); - $zguid_sig = ((x($arr,'guid_sig')) ? $arr['guid_sig'] : ''); - $zaddr = ((x($arr,'address')) ? $arr['address'] : ''); - $ztarget = ((x($arr,'target_url')) ? $arr['target_url'] : ''); - $zsig = ((x($arr,'target_sig')) ? $arr['target_sig'] : ''); - $zkey = ((x($arr,'key')) ? $arr['key'] : ''); - $mindate = ((x($arr,'mindate')) ? $arr['mindate'] : ''); - $token = ((x($arr,'token')) ? $arr['token'] : ''); - $feed = ((x($arr,'feed')) ? intval($arr['feed']) : 0); - - if($ztarget) { + $zhash = ((x($arr, 'guid_hash')) ? $arr['guid_hash'] : ''); + $zguid = ((x($arr, 'guid')) ? $arr['guid'] : ''); + $zguid_sig = ((x($arr, 'guid_sig')) ? $arr['guid_sig'] : ''); + $zaddr = ((x($arr, 'address')) ? $arr['address'] : ''); + $ztarget = ((x($arr, 'target_url')) ? $arr['target_url'] : ''); + $zsig = ((x($arr, 'target_sig')) ? $arr['target_sig'] : ''); + $zkey = ((x($arr, 'key')) ? $arr['key'] : ''); + $mindate = ((x($arr, 'mindate')) ? $arr['mindate'] : ''); + $token = ((x($arr, 'token')) ? $arr['token'] : ''); + $feed = ((x($arr, 'feed')) ? intval($arr['feed']) : 0); + + if ($ztarget) { $t = q("select * from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1", dbesc($ztarget) ); - if($t) { + if ($t) { $ztarget_hash = $t[0]['hubloc_hash']; @@ -2762,21 +2718,21 @@ class Libzot { $r = null; - if(strlen($zhash)) { + if (strlen($zhash)) { $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash where channel_hash = '%s' limit 1", dbesc($zhash) ); } - elseif(strlen($zguid) && strlen($zguid_sig)) { + elseif (strlen($zguid) && strlen($zguid_sig)) { $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash where channel_guid = '%s' and channel_guid_sig = '%s' limit 1", dbesc($zguid), dbesc($zguid_sig) ); } - elseif(strlen($zaddr)) { - if(strpos($zaddr,'[system]') === false) { /* normal address lookup */ + elseif (strlen($zaddr)) { + if (strpos($zaddr, '[system]') === false) { /* normal address lookup */ $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash where ( channel_address = '%s' or xchan_addr = '%s' ) limit 1", dbesc($zaddr), @@ -2799,7 +2755,7 @@ class Libzot { $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash where channel_system = 1 order by channel_id limit 1"); - if(! $r) { + if (!$r) { $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash where channel_removed = 0 order by channel_id limit 1"); } @@ -2807,45 +2763,44 @@ class Libzot { } else { $ret['message'] = 'Invalid request'; - return($ret); + return ($ret); } - if(! $r) { + if (!$r) { $ret['message'] = 'Item not found.'; - return($ret); + return ($ret); } $e = $r[0]; - $id = $e['channel_id']; - $sys_channel = (intval($e['channel_system']) ? true : false); - $special_channel = (($e['channel_pageflags'] & PAGE_PREMIUM) ? true : false); - $adult_channel = (($e['channel_pageflags'] & PAGE_ADULT) ? true : false); + $sys_channel = (intval($e['channel_system']) ? true : false); + $special_channel = (($e['channel_pageflags'] & PAGE_PREMIUM) ? true : false); + $adult_channel = (($e['channel_pageflags'] & PAGE_ADULT) ? true : false); $censored = (($e['channel_pageflags'] & PAGE_CENSORED) ? true : false); - $searchable = (($e['channel_pageflags'] & PAGE_HIDDEN) ? false : true); + $searchable = (($e['channel_pageflags'] & PAGE_HIDDEN) ? false : true); $deleted = (intval($e['xchan_deleted']) ? true : false); - if($deleted || $censored || $sys_channel) + if ($deleted || $censored || $sys_channel) $searchable = false; $public_forum = false; - $role = get_pconfig($e['channel_id'],'system','permissions_role'); - if($role === 'forum' || $role === 'repository') { + $role = get_pconfig($e['channel_id'], 'system', 'permissions_role'); + if ($role === 'forum' || $role === 'repository') { $public_forum = true; } else { // check if it has characteristics of a public forum based on custom permissions. $m = Permissions::FilledAutoperms($e['channel_id']); - if($m) { - foreach($m as $k => $v) { - if($k == 'tag_deliver' && intval($v) == 1) - $ch ++; - if($k == 'send_stream' && intval($v) == 0) - $ch ++; - } - if($ch == 2) + if ($m) { + foreach ($m as $k => $v) { + if ($k == 'tag_deliver' && intval($v) == 1) + $ch++; + if ($k == 'send_stream' && intval($v) == 0) + $ch++; + } + if ($ch == 2) $public_forum = true; } } @@ -2856,128 +2811,141 @@ class Libzot { intval($e['channel_id']) ); - $profile = array(); + $profile = []; - if($p) { + if ($p) { - if(! intval($p[0]['publish'])) + if (!intval($p[0]['publish'])) $searchable = false; - $profile['description'] = $p[0]['pdesc']; - $profile['birthday'] = $p[0]['dob']; - if(($profile['birthday'] != '0000-00-00') && (($bd = z_birthday($p[0]['dob'],'UTC')) !== '')) + $profile['description'] = $p[0]['pdesc']; + $profile['birthday'] = $p[0]['dob']; + if (($profile['birthday'] != '0000-00-00') && (($bd = z_birthday($p[0]['dob'], 'UTC')) !== '')) $profile['next_birthday'] = $bd; - if($age = age($p[0]['dob'],$e['channel_timezone'],'')) + if ($age = age($p[0]['dob'], $e['channel_timezone'], '')) $profile['age'] = $age; - $profile['gender'] = $p[0]['gender']; - $profile['marital'] = $p[0]['marital']; - $profile['sexual'] = $p[0]['sexual']; - $profile['locale'] = $p[0]['locality']; - $profile['region'] = $p[0]['region']; - $profile['postcode'] = $p[0]['postal_code']; - $profile['country'] = $p[0]['country_name']; - $profile['about'] = $p[0]['about']; - $profile['homepage'] = $p[0]['homepage']; - $profile['hometown'] = $p[0]['hometown']; - - if($p[0]['keywords']) { - $tags = array(); - $k = explode(' ',$p[0]['keywords']); - if($k) { - foreach($k as $kk) { - if(trim($kk," \t\n\r\0\x0B,")) { - $tags[] = trim($kk," \t\n\r\0\x0B,"); + $profile['gender'] = $p[0]['gender']; + $profile['marital'] = $p[0]['marital']; + $profile['sexual'] = $p[0]['sexual']; + $profile['locale'] = $p[0]['locality']; + $profile['region'] = $p[0]['region']; + $profile['postcode'] = $p[0]['postal_code']; + $profile['country'] = $p[0]['country_name']; + $profile['about'] = $p[0]['about']; + $profile['homepage'] = $p[0]['homepage']; + $profile['hometown'] = $p[0]['hometown']; + + if ($p[0]['keywords']) { + $tags = []; + $k = explode(' ', $p[0]['keywords']); + if ($k) { + foreach ($k as $kk) { + if (trim($kk, " \t\n\r\0\x0B,")) { + $tags[] = trim($kk, " \t\n\r\0\x0B,"); } } } - if($tags) + if ($tags) $profile['keywords'] = $tags; } } // Communication details - $ret['id'] = $e['xchan_guid']; - $ret['id_sig'] = self::sign($e['xchan_guid'], $e['channel_prvkey']); + $ret['id'] = $e['xchan_guid']; + $ret['id_sig'] = self::sign($e['xchan_guid'], $e['channel_prvkey']); $ret['primary_location'] = [ - 'address' => $e['xchan_addr'], - 'url' => $e['xchan_url'], - 'connections_url' => $e['xchan_connurl'], - 'follow_url' => $e['xchan_follow'], + 'address' => $e['xchan_addr'], + 'url' => $e['xchan_url'], + 'connections_url' => $e['xchan_connurl'], + 'follow_url' => $e['xchan_follow'], ]; - $ret['public_key'] = $e['xchan_pubkey']; - $ret['username'] = $e['channel_address']; - $ret['name'] = $e['xchan_name']; - $ret['name_updated'] = $e['xchan_name_date']; - $ret['photo'] = [ + $ret['public_key'] = $e['xchan_pubkey']; + $ret['username'] = $e['channel_address']; + $ret['name'] = $e['xchan_name']; + $ret['name_updated'] = $e['xchan_name_date']; + $ret['photo'] = [ 'url' => $e['xchan_photo_l'], 'type' => $e['xchan_photo_mimetype'], 'updated' => $e['xchan_photo_date'] ]; - $ret['channel_role'] = get_pconfig($e['channel_id'],'system','permissions_role','custom'); - $ret['protocols'] = [ 'zot6', 'zot' ]; - $ret['searchable'] = $searchable; - $ret['adult_content'] = $adult_channel; - $ret['public_forum'] = $public_forum; + $ret['channel_role'] = get_pconfig($e['channel_id'], 'system', 'permissions_role', 'custom'); + + $hookinfo = [ + 'channel_id' => $id, + 'protocols' => ['zot6'] + ]; + /** + * @hooks channel_protocols + * * \e int \b channel_id + * * \e array \b protocols + */ + call_hooks('channel_protocols', $hookinfo); + + $ret['protocols'] = $hookinfo['protocols']; + $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')); + $ret['comments'] = map_scope(PermissionLimits::Get($e['channel_id'], 'post_comments')); + $ret['mail'] = map_scope(PermissionLimits::Get($e['channel_id'], 'post_mail')); - if($deleted) - $ret['deleted'] = $deleted; + if ($deleted) + $ret['deleted'] = $deleted; - if(intval($e['channel_removed'])) + if (intval($e['channel_removed'])) { $ret['deleted_locally'] = true; + } // premium or other channel desiring some contact with potential followers before connecting. // This is a template - %s will be replaced with the follow_url we discover for the return channel. - if($special_channel) { + if ($special_channel) { $ret['connect_url'] = (($e['xchan_connpage']) ? $e['xchan_connpage'] : z_root() . '/connect/' . $e['channel_address']); } // This is a template for our follow url, %s will be replaced with a webbie - if(! $ret['follow_url']) + if (!$ret['follow_url']) $ret['follow_url'] = z_root() . '/follow?f=&url=%s'; - $permissions = get_all_perms($e['channel_id'],$ztarget_hash,false,false); + $permissions = get_all_perms($e['channel_id'], $ztarget_hash, false, false); - if($ztarget_hash) { + if ($ztarget_hash) { $permissions['connected'] = false; - $b = q("select * from abook where abook_xchan = '%s' and abook_channel = %d limit 1", + $b = q("select * from abook where abook_xchan = '%s' and abook_channel = %d limit 1", dbesc($ztarget_hash), intval($e['channel_id']) ); - if($b) + if ($b) $permissions['connected'] = true; } - if($permissions['view_profile']) - $ret['profile'] = $profile; + if ($permissions['view_profile']) + $ret['profile'] = $profile; $concise_perms = []; - if($permissions) { - foreach($permissions as $k => $v) { - if($v) { + if ($permissions) { + foreach ($permissions as $k => $v) { + if ($v) { $concise_perms[] = $k; } } - $permissions = implode(',',$concise_perms); + $permissions = implode(',', $concise_perms); } - $ret['permissions'] = $permissions; - $ret['permissions_for'] = $ztarget; + $ret['permissions'] = $permissions; + $ret['permissions_for'] = $ztarget; // array of (verified) hubs this channel uses $x = self::encode_locations($e); - if($x) + if ($x) $ret['locations'] = $x; $ret['site'] = self::site_info(); @@ -2997,58 +2965,58 @@ class Libzot { */ static function site_info() { - $signing_key = get_config('system','prvkey'); - $sig_method = get_config('system','signature_algorithm','sha256'); + $signing_key = get_config('system', 'prvkey'); + $sig_method = get_config('system', 'signature_algorithm', 'sha256'); - $ret = []; - $ret['site'] = []; - $ret['site']['url'] = z_root(); - $ret['site']['site_sig'] = self::sign(z_root(), $signing_key); - $ret['site']['post'] = z_root() . '/zot'; + $ret = []; + $ret['site'] = []; + $ret['site']['url'] = z_root(); + $ret['site']['site_sig'] = self::sign(z_root(), $signing_key); + $ret['site']['post'] = z_root() . '/zot'; $ret['site']['openWebAuth'] = z_root() . '/owa'; $ret['site']['authRedirect'] = z_root() . '/magic'; - $ret['site']['sitekey'] = get_config('system','pubkey'); + $ret['site']['sitekey'] = get_config('system', 'pubkey'); - $dirmode = get_config('system','directory_mode'); - if(($dirmode === false) || ($dirmode == DIRECTORY_MODE_NORMAL)) + $dirmode = get_config('system', 'directory_mode'); + if (($dirmode === false) || ($dirmode == DIRECTORY_MODE_NORMAL)) $ret['site']['directory_mode'] = 'normal'; - if($dirmode == DIRECTORY_MODE_PRIMARY) + if ($dirmode == DIRECTORY_MODE_PRIMARY) $ret['site']['directory_mode'] = 'primary'; - elseif($dirmode == DIRECTORY_MODE_SECONDARY) + elseif ($dirmode == DIRECTORY_MODE_SECONDARY) $ret['site']['directory_mode'] = 'secondary'; - elseif($dirmode == DIRECTORY_MODE_STANDALONE) + elseif ($dirmode == DIRECTORY_MODE_STANDALONE) $ret['site']['directory_mode'] = 'standalone'; - if($dirmode != DIRECTORY_MODE_NORMAL) + if ($dirmode != DIRECTORY_MODE_NORMAL) $ret['site']['directory_url'] = z_root() . '/dirsearch'; - $ret['site']['encryption'] = crypto_methods(); - $ret['site']['zot'] = System::get_zot_revision(); + $ret['site']['encryption'] = Crypto::methods(); + $ret['site']['zot'] = System::get_zot_revision(); // hide detailed site information if you're off the grid - if($dirmode != DIRECTORY_MODE_STANDALONE) { + if ($dirmode != DIRECTORY_MODE_STANDALONE) { - $register_policy = intval(get_config('system','register_policy')); + $register_policy = intval(get_config('system', 'register_policy')); - if($register_policy == REGISTER_CLOSED) + if ($register_policy == REGISTER_CLOSED) $ret['site']['register_policy'] = 'closed'; - if($register_policy == REGISTER_APPROVE) + if ($register_policy == REGISTER_APPROVE) $ret['site']['register_policy'] = 'approve'; - if($register_policy == REGISTER_OPEN) + if ($register_policy == REGISTER_OPEN) $ret['site']['register_policy'] = 'open'; - $access_policy = intval(get_config('system','access_policy')); + $access_policy = intval(get_config('system', 'access_policy')); - if($access_policy == ACCESS_PRIVATE) + if ($access_policy == ACCESS_PRIVATE) $ret['site']['access_policy'] = 'private'; - if($access_policy == ACCESS_PAID) + if ($access_policy == ACCESS_PAID) $ret['site']['access_policy'] = 'paid'; - if($access_policy == ACCESS_FREE) + if ($access_policy == ACCESS_FREE) $ret['site']['access_policy'] = 'free'; - if($access_policy == ACCESS_TIERED) + if ($access_policy == ACCESS_TIERED) $ret['site']['access_policy'] = 'tiered'; $ret['site']['accounts'] = account_total(); @@ -3056,24 +3024,24 @@ class Libzot { require_once('include/channel.php'); $ret['site']['channels'] = channel_total(); - $ret['site']['admin'] = get_config('system','admin_email'); + $ret['site']['admin'] = get_config('system', 'admin_email'); - $visible_plugins = array(); - if(is_array(\App::$plugins) && count(\App::$plugins)) { + $visible_plugins = []; + if (is_array(\App::$plugins) && count(\App::$plugins)) { $r = q("select * from addon where hidden = 0"); - if($r) - foreach($r as $rr) + if ($r) + foreach ($r as $rr) $visible_plugins[] = $rr['aname']; } - $ret['site']['plugins'] = $visible_plugins; - $ret['site']['sitehash'] = get_config('system','location_hash'); - $ret['site']['sitename'] = get_config('system','sitename'); - $ret['site']['sellpage'] = get_config('system','sellpage'); - $ret['site']['location'] = get_config('system','site_location'); - $ret['site']['realm'] = get_directory_realm(); - $ret['site']['project'] = System::get_platform_name(); - $ret['site']['version'] = System::get_project_version(); + $ret['site']['plugins'] = $visible_plugins; + $ret['site']['sitehash'] = get_config('system', 'location_hash'); + $ret['site']['sitename'] = get_config('system', 'sitename'); + $ret['site']['sellpage'] = get_config('system', 'sellpage'); + $ret['site']['location'] = get_config('system', 'site_location'); + $ret['site']['realm'] = get_directory_realm(); + $ret['site']['project'] = System::get_platform_name(); + $ret['site']['version'] = System::get_project_version(); } @@ -3143,6 +3111,11 @@ class Libzot { ); } + // this site obviously isn't dead because they are trying to communicate with us. + q("update site set site_dead = 0 where site_dead = 1 and site_url = '%s' ", + dbesc($hub['hubloc_url']) + ); + return $hub['hubloc_url']; } @@ -3154,36 +3127,36 @@ class Libzot { * @param string $alg (optional) default 'sha256' * @return string */ - static function sign($data,$key,$alg = 'sha256') { - if(! $key) + static function sign($data, $key, $alg = 'sha256') { + if (!$key) return 'no key'; $sig = ''; - openssl_sign($data,$sig,$key,$alg); + openssl_sign($data, $sig, $key, $alg); return $alg . '.' . base64url_encode($sig); } - static function verify($data,$sig,$key) { + static function verify($data, $sig, $key) { $verify = 0; - $x = explode('.',$sig,2); + $x = explode('.', $sig, 2); if ($key && count($x) === 2) { - $alg = $x[0]; + $alg = $x[0]; $signature = base64url_decode($x[1]); - $verify = @openssl_verify($data,$signature,$key,$alg); + $verify = @openssl_verify($data, $signature, $key, $alg); if ($verify === (-1)) { while ($msg = openssl_error_string()) { - logger('openssl_verify: ' . $msg,LOGGER_NORMAL,LOG_ERR); + logger('openssl_verify: ' . $msg, LOGGER_NORMAL, LOG_ERR); } btlogger('openssl_verify: key: ' . $key, LOGGER_DEBUG, LOG_ERR); } } - return(($verify > 0) ? true : false); + return (($verify > 0) ? true : false); } /** @@ -3192,25 +3165,20 @@ class Libzot { * @return boolean */ static function is_zot_request() { - $x = getBestSupportedMimeType([ 'application/x-zot+json' ]); + $x = getBestSupportedMimeType(['application/x-zot+json']); - return(($x) ? true : false); + return (($x) ? true : false); } static public function zot_record_preferred($arr, $check = 'hubloc_network') { - if(! $arr) { + if (!$arr) { return $arr; } - foreach($arr as $v) { - if($v[$check] === 'zot6') { - return $v; - } - } - foreach($arr as $v) { - if($v[$check] === 'zot') { + foreach ($arr as $v) { + if ($v[$check] === 'zot6') { return $v; } } @@ -3219,4 +3187,10 @@ class Libzot { } + static function update_cached_hubloc($hubloc) { + if ($hubloc['hubloc_updated'] > datetime_convert('UTC','UTC','now - 1 week') || $hubloc['hubloc_url'] === z_root()) { + return; + } + self::refresh( [ 'hubloc_id_url' => $hubloc['hubloc_id_url'] ] ); + } } diff --git a/Zotlabs/Lib/Libzotdir.php b/Zotlabs/Lib/Libzotdir.php index b02516a98..4f35a1b80 100644 --- a/Zotlabs/Lib/Libzotdir.php +++ b/Zotlabs/Lib/Libzotdir.php @@ -19,7 +19,6 @@ class Libzotdir { */ static function find_upstream_directory($dirmode) { - global $DIRECTORY_FALLBACK_SERVERS; $preferred = get_config('system','directory_server'); @@ -31,7 +30,7 @@ class Libzotdir { ); if(($r) && ($r[0]['site_flags'] & DIRECTORY_MODE_STANDALONE)) { $preferred = ''; - } + } } @@ -42,19 +41,21 @@ class Libzotdir { * from our list of directory servers. However, if we're a directory * server ourself, point at the local instance * We will then set this value so this should only ever happen once. - * Ideally there will be an admin setting to change to a different + * Ideally there will be an admin setting to change to a different * directory server if you don't like our choice or if circumstances change. */ + $directory_fallback_servers = get_directory_fallback_servers(); + $dirmode = intval(get_config('system','directory_mode')); if ($dirmode == DIRECTORY_MODE_NORMAL) { - $toss = mt_rand(0,count($DIRECTORY_FALLBACK_SERVERS)); - $preferred = $DIRECTORY_FALLBACK_SERVERS[$toss]; + $toss = mt_rand(0,count($directory_fallback_servers)); + $preferred = $directory_fallback_servers[$toss]; if(! $preferred) { $preferred = DIRECTORY_FALLBACK_MASTER; } set_config('system','directory_server',$preferred); - } + } else { set_config('system','directory_server',z_root()); } @@ -108,7 +109,7 @@ class Libzotdir { $ret = get_config('directory', $setting); - // 'safemode' is the default if there is no observer or no established preference. + // 'safemode' is the default if there is no observer or no established preference. if($setting === 'safemode' && $ret === false) $ret = 1; @@ -175,8 +176,8 @@ class Libzotdir { * * Checks the directory mode of this hub to see if it is some form of directory server. If it is, * get the directory realm of this hub. Fetch a list of all other directory servers in this realm and request - * a directory sync packet. This will contain both directory updates and new ratings. Store these all in the DB. - * In the case of updates, we will query each of them asynchronously from a poller task. Ratings are stored + * a directory sync packet. This will contain both directory updates and new ratings. Store these all in the DB. + * In the case of updates, we will query each of them asynchronously from a poller task. Ratings are stored * directly if the rater's signature matches. * * @param int $dirmode; @@ -188,16 +189,17 @@ class Libzotdir { return; $realm = get_directory_realm(); + if ($realm == DIRECTORY_REALM) { - $r = q("select * from site where (site_flags & %d) > 0 and site_url != '%s' and site_type = %d and ( site_realm = '%s' or site_realm = '') ", + $r = q("select * from site where (site_flags & %d) > 0 and site_url != '%s' and site_type = %d and ( site_realm = '%s' or site_realm = '') and site_dead = 0", intval(DIRECTORY_MODE_PRIMARY|DIRECTORY_MODE_SECONDARY), dbesc(z_root()), intval(SITE_TYPE_ZOT), dbesc($realm) ); - } + } else { - $r = q("select * from site where (site_flags & %d) > 0 and site_url != '%s' and site_realm like '%s' and site_type = %d ", + $r = q("select * from site where (site_flags & %d) > 0 and site_url != '%s' and site_realm like '%s' and site_type = %d and site_dead = 0", intval(DIRECTORY_MODE_PRIMARY|DIRECTORY_MODE_SECONDARY), dbesc(z_root()), dbesc(protect_sprintf('%' . $realm . '%')), @@ -214,14 +216,14 @@ class Libzotdir { [ 'site_url' => DIRECTORY_FALLBACK_MASTER, 'site_flags' => DIRECTORY_MODE_PRIMARY, - 'site_update' => NULL_DATE, + 'site_update' => NULL_DATE, 'site_directory' => DIRECTORY_FALLBACK_MASTER . '/dirsearch', 'site_realm' => DIRECTORY_REALM, 'site_valid' => 1, ] ); - $r = q("select * from site where site_flags in (%d, %d) and site_url != '%s' and site_type = %d ", + $r = q("select * from site where site_flags in (%d, %d) and site_url != '%s' and site_type = %d and site_dead = 0", intval(DIRECTORY_MODE_PRIMARY), intval(DIRECTORY_MODE_SECONDARY), dbesc(z_root()), @@ -250,7 +252,7 @@ class Libzotdir { continue; $j = json_decode($x['body'],true); - if (!($j['transactions']) || ($j['ratings'])) + if (!$j['transactions']) continue; q("update site set site_sync = '%s' where site_url = '%s'", @@ -262,6 +264,11 @@ class Libzotdir { if (is_array($j['transactions']) && count($j['transactions'])) { foreach ($j['transactions'] as $t) { + + if (empty($t['hash']) || empty($t['transaction_id']) || empty($t['address'])) { + continue; + } + $r = q("select * from updates where ud_guid = '%s' limit 1", dbesc($t['transaction_id']) ); @@ -273,7 +280,7 @@ class Libzotdir { $ud_flags |= UPDATE_FLAGS_DELETED; if (is_array($t['flags']) && in_array('forced',$t['flags'])) $ud_flags |= UPDATE_FLAGS_FORCED; - + $z = q("insert into updates ( ud_hash, ud_guid, ud_date, ud_flags, ud_addr ) values ( '%s', '%s', '%s', %d, '%s' ) ", dbesc($t['hash']), @@ -308,13 +315,22 @@ class Libzotdir { if ($ud['ud_addr'] && (! ($ud['ud_flags'] & UPDATE_FLAGS_DELETED))) { $success = false; + $zf = []; $href = Webfinger::zot_url(punify($ud['ud_addr'])); if($href) { $zf = Zotfinger::exec($href); } - if(is_array($zf) && array_path_exists('signature/signer',$zf) && $zf['signature']['signer'] === $href && intval($zf['signature']['header_valid'])) { + if(array_path_exists('signature/signer',$zf) && $zf['signature']['signer'] === $href && intval($zf['signature']['header_valid'])) { $xc = Libzot::import_xchan($zf['data'], 0, $ud); + // This is a workaround for a missing xchan_updated column + // TODO: implement xchan_updated in the xchan table and update this column instead + if($zf['data']['primary_location']['address'] && $zf['data']['primary_location']['url']) { + q("UPDATE hubloc SET hubloc_updated = '%s' WHERE hubloc_id_url = '%s' AND hubloc_primary = 1", + dbesc(datetime_convert()), + dbesc($zf['data']['primary_location']['url']) + ); + } } else { q("update updates set ud_last = '%s' where ud_addr = '%s'", @@ -338,10 +354,10 @@ class Libzotdir { static function local_dir_update($uid, $force) { - + logger('local_dir_update: uid: ' . $uid, LOGGER_DEBUG); - $p = q("select channel.channel_hash, channel_address, channel_timezone, channel_portable_id, profile.* from profile left join channel on channel_id = uid where uid = %d and is_default = 1", + $p = q("select channel.channel_hash, channel_address, channel_timezone, profile.* from profile left join channel on channel_id = uid where uid = %d and is_default = 1", intval($uid) ); @@ -350,11 +366,10 @@ class Libzotdir { if ($p) { $hash = $p[0]['channel_hash']; - $legacy_hash = $p[0]['channel_portable_id']; $profile['description'] = $p[0]['pdesc']; $profile['birthday'] = $p[0]['dob']; - if ($age = age($p[0]['dob'],$p[0]['channel_timezone'],'')) + if ($age = age($p[0]['dob'],$p[0]['channel_timezone'],'')) $profile['age'] = $age; $profile['gender'] = $p[0]['gender']; @@ -389,10 +404,9 @@ class Libzotdir { ); if(intval($r[0]['xchan_hidden']) != $hidden) { - $r = q("update xchan set xchan_hidden = %d where xchan_hash in ('%s', '%s')", + $r = q("update xchan set xchan_hidden = %d where xchan_hash = '%s'", intval($hidden), - dbesc($hash), - dbesc($legacy_hash) + dbesc($hash) ); } @@ -406,16 +420,14 @@ class Libzotdir { } else { // they may have made it private - q("delete from xprof where xprof_hash in ('%s', '%s')", - dbesc($hash), - dbesc($legacy_hash) + q("delete from xprof where xprof_hash = '%s'", + dbesc($hash) ); - q("delete from xtag where xtag_hash in ('%s', '%s')", - dbesc($hash), - dbesc($legacy_hash) + q("delete from xtag where xtag_hash = '%s'", + dbesc($hash) ); } - + } $ud_hash = random_string() . '@' . \App::get_hostname(); @@ -446,7 +458,7 @@ class Libzotdir { $arr['xprof_hash'] = $hash; $arr['xprof_dob'] = (($profile['birthday'] === '0000-00-00') ? $profile['birthday'] : datetime_convert('','',$profile['birthday'],'Y-m-d')); // !!!! check this for 0000 year $arr['xprof_age'] = (($profile['age']) ? intval($profile['age']) : 0); - $arr['xprof_desc'] = (($profile['description']) ? htmlspecialchars($profile['description'], ENT_COMPAT,'UTF-8',false) : ''); + $arr['xprof_desc'] = (($profile['description']) ? htmlspecialchars($profile['description'], ENT_COMPAT,'UTF-8',false) : ''); $arr['xprof_gender'] = (($profile['gender']) ? htmlspecialchars($profile['gender'], ENT_COMPAT,'UTF-8',false) : ''); $arr['xprof_marital'] = (($profile['marital']) ? htmlspecialchars($profile['marital'], ENT_COMPAT,'UTF-8',false) : ''); $arr['xprof_sexual'] = (($profile['sexual']) ? htmlspecialchars($profile['sexual'], ENT_COMPAT,'UTF-8',false) : ''); @@ -631,8 +643,13 @@ class Libzotdir { $dirmode = intval(get_config('system', 'directory_mode')); - if($dirmode == DIRECTORY_MODE_NORMAL) + if($dirmode == DIRECTORY_MODE_NORMAL) { return; + } + + if (empty($hash) || empty($guid) || empty($addr)) { + return; + } if($flags) { q("insert into updates (ud_hash, ud_guid, ud_date, ud_flags, ud_addr ) values ( '%s', '%s', '%s', %d, '%s' )", @@ -641,7 +658,7 @@ class Libzotdir { dbesc(datetime_convert()), intval($flags), dbesc($addr) - ); + ); } else { q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and not (ud_flags & %d)>0 ", @@ -652,9 +669,4 @@ class Libzotdir { } } - - - - - } diff --git a/Zotlabs/Lib/MessageFilter.php b/Zotlabs/Lib/MessageFilter.php index 750d6d424..21e6ca26a 100644 --- a/Zotlabs/Lib/MessageFilter.php +++ b/Zotlabs/Lib/MessageFilter.php @@ -11,8 +11,6 @@ class MessageFilter { require_once('include/html2plain.php'); - unobscure($item); - $text = prepare_text($item['body'],$item['mimetype']); $text = html2plain(($item['title']) ? $item['title'] . ' ' . $text : $text); diff --git a/Zotlabs/Lib/NativeWiki.php b/Zotlabs/Lib/NativeWiki.php index 3ec032075..9e6a3ac85 100644 --- a/Zotlabs/Lib/NativeWiki.php +++ b/Zotlabs/Lib/NativeWiki.php @@ -9,7 +9,7 @@ define ( 'NWIKI_ITEM_RESOURCE_TYPE', 'nwiki' ); class NativeWiki { - static public function listwikis($channel, $observer_hash) { + public static function listwikis($channel, $observer_hash) { $sql_extra = item_permissions_sql($channel['channel_id'], $observer_hash); $wikis = q("SELECT * FROM item @@ -40,7 +40,7 @@ class NativeWiki { } - function create_wiki($channel, $observer_hash, $wiki, $acl) { + public static function create_wiki($channel, $observer_hash, $wiki, $acl) { $resource_id = new_uuid(); $uuid = new_uuid(); @@ -101,7 +101,8 @@ class NativeWiki { } } - function update_wiki($channel_id, $observer_hash, $arr, $acl) { + + public static function update_wiki($channel_id, $observer_hash, $arr, $acl) { $w = self::get_wiki($channel_id, $observer_hash, $arr['resource_id']); $item = $w['wiki']; @@ -156,8 +157,8 @@ class NativeWiki { } } - static public function sync_a_wiki_item($uid,$id,$resource_id) { + public static function sync_a_wiki_item($uid,$id,$resource_id) { $r = q("SELECT * from item WHERE uid = %d AND ( id = %d OR ( resource_type = '%s' and resource_id = '%s' )) ", intval($uid), @@ -165,8 +166,8 @@ class NativeWiki { dbesc(NWIKI_ITEM_RESOURCE_TYPE), dbesc($resource_id) ); - if($r) { + $q = q("select * from item where resource_type = 'nwikipage' and resource_id = '%s'", dbesc($r[0]['resource_id']) ); @@ -185,35 +186,42 @@ class NativeWiki { } } - function delete_wiki($channel_id,$observer_hash,$resource_id) { + + public static function delete_wiki($channel_id,$observer_hash,$resource_id) { $w = self::get_wiki($channel_id,$observer_hash,$resource_id); - $item = $w['wiki']; - if(! $item) { - return array('item' => null, 'success' => false); - } - else { - $drop = drop_item($item['id'], false, DROPITEM_NORMAL); + if(! $w['wiki']) { + return [ 'success' => false ]; } + else { + + $r = q("SELECT id FROM item WHERE uid = %s AND resource_id = '%s'", + intval($channel_id), + dbesc($resource_id) + ); + + $ids = array_column($r, 'id'); + drop_items($ids, true, DROPITEM_PHASE1); - info( t('Wiki files deleted successfully')); + info(t('Wiki files deleted successfully')); - return array('item' => $item, 'item_id' => $item['id'], 'success' => (($drop === 1) ? true : false)); + return [ 'success' => true ]; + } } - static public function get_wiki($channel_id, $observer_hash, $resource_id) { + public static function get_wiki($channel_id, $observer_hash, $resource_id) { $sql_extra = item_permissions_sql($channel_id,$observer_hash); $item = q("SELECT * FROM item WHERE uid = %d AND resource_type = '%s' AND resource_id = '%s' AND item_deleted = 0 - $sql_extra limit 1", + $sql_extra ORDER BY id LIMIT 1", intval($channel_id), dbesc(NWIKI_ITEM_RESOURCE_TYPE), dbesc($resource_id) ); if(! $item) { - return array('wiki' => null); + return [ 'wiki' => null ]; } else { @@ -236,7 +244,7 @@ class NativeWiki { } - static public function exists_by_name($uid, $urlName) { + public static function exists_by_name($uid, $urlName) { $sql_extra = item_permissions_sql($uid); @@ -258,7 +266,8 @@ class NativeWiki { } - static public function get_permissions($resource_id, $owner_id, $observer_hash) { + public static function get_permissions($resource_id, $owner_id, $observer_hash) { + // TODO: For now, only the owner can edit $sql_extra = item_permissions_sql($owner_id, $observer_hash); @@ -283,6 +292,7 @@ class NativeWiki { } } + public static function name_encode ($string) { $string = html_entity_decode($string); @@ -298,6 +308,7 @@ class NativeWiki { return $ret; } + public static function name_decode ($string) { $encoding = mb_internal_encoding(); diff --git a/Zotlabs/Lib/NativeWikiPage.php b/Zotlabs/Lib/NativeWikiPage.php index d84cc50a8..3c61ea800 100644 --- a/Zotlabs/Lib/NativeWikiPage.php +++ b/Zotlabs/Lib/NativeWikiPage.php @@ -109,6 +109,7 @@ class NativeWikiPage { return [ 'success' => false, 'message' => t('Wiki page create failed.') ]; } + static public function rename_page($arr) { $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); @@ -163,11 +164,13 @@ class NativeWikiPage { return [ 'success' => true, 'page' => $page ]; } - return [ 'success' => false, 'item_id' => $c['item_id'], 'message' => t('Page not found') ]; + return [ 'success' => false, 'message' => t('Page not found') ]; } + static public function get_page_content($arr) { + $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); $observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : ''); @@ -198,7 +201,9 @@ class NativeWikiPage { } + static public function page_history($arr) { + $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); $observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : ''); @@ -290,6 +295,7 @@ class NativeWikiPage { return null; } + static public function load_page_history($arr) { $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); @@ -338,8 +344,8 @@ class NativeWikiPage { return null; } - static public function save_page($arr) { + static public function save_page($arr) { $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); $content = ((array_key_exists('content',$arr)) ? $arr['content'] : ''); $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); @@ -385,19 +391,20 @@ class NativeWikiPage { $ret = item_store($item, false, false); if($ret['item_id']) - return array('message' => '', 'item_id' => $ret['item_id'], 'filename' => $filename, 'success' => true); + return array('message' => '', 'item_id' => $ret['item_id'], 'filename' => $pageUrlName, 'success' => true); else return array('message' => t('Page update failed.'), 'success' => false); } + static public function delete_page($arr) { - $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); - $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); - $observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : ''); - $channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0); - $w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); + $pageUrlName = (array_key_exists('pageUrlName',$arr) ? $arr['pageUrlName'] : ''); + $resource_id = (array_key_exists('resource_id',$arr) ? $arr['resource_id'] : ''); + $observer_hash = (array_key_exists('observer_hash',$arr) ? $arr['observer_hash'] : ''); + $channel_id = (array_key_exists('channel_id',$arr) ? $arr['channel_id'] : 0); + $w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); if(! $w['wiki']) { return [ 'success' => false, 'message' => t('Error reading wiki') ]; } @@ -417,14 +424,16 @@ class NativeWikiPage { } if($ids) { - drop_items($ids); + drop_items($ids, true, DROPITEM_PHASE1); return [ 'success' => true ]; } return [ 'success' => false, 'message' => t('Nothing deleted') ]; } + static public function revert_page($arr) { + $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); $commitHash = ((array_key_exists('commitHash',$arr)) ? $arr['commitHash'] : null); @@ -432,12 +441,12 @@ class NativeWikiPage { $channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0); if (! $commitHash) { - return array('content' => $content, 'message' => 'No commit was provided', 'success' => false); + return array('message' => 'No commit was provided', 'success' => false); } $w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); if (!$w['wiki']) { - return array('content' => $content, 'message' => 'Error reading wiki', 'success' => false); + return array('message' => 'Error reading wiki', 'success' => false); } $x = $arr; @@ -451,11 +460,13 @@ class NativeWikiPage { $content = $loaded['body']; return [ 'content' => $content, 'success' => true ]; } - return [ 'content' => $content, 'success' => false ]; + return [ 'success' => false ]; } } + static public function compare_page($arr) { + $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); $currentCommit = ((array_key_exists('currentCommit',$arr)) ? $arr['currentCommit'] : (-1)); @@ -491,6 +502,7 @@ class NativeWikiPage { } + static public function commit($arr) { $commit_msg = ((array_key_exists('commit_msg', $arr)) ? $arr['commit_msg'] : t('Page updated')); @@ -571,7 +583,6 @@ class NativeWikiPage { } - /** * Replace the instances of the string [toc] with a list element that will be populated by * a table of contents by the JavaScript library @@ -587,6 +598,7 @@ class NativeWikiPage { return $s; } + /** * Converts a select set of bbcode tags. Much of the code is copied from include/bbcode.php * @param string $s @@ -626,7 +638,9 @@ class NativeWikiPage { return $s; } + static public function get_file_ext($arr) { + if($arr['mimetype'] === 'text/bbcode') return '.bb'; elseif($arr['mimetype'] === 'text/markdown') diff --git a/Zotlabs/Lib/PConfig.php b/Zotlabs/Lib/PConfig.php index c08c11e75..765131f0d 100644 --- a/Zotlabs/Lib/PConfig.php +++ b/Zotlabs/Lib/PConfig.php @@ -132,6 +132,7 @@ class PConfig { // manage array value $dbvalue = ((is_array($value)) ? serialize($value) : $value); $dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue); + $new = false; $now = datetime_convert(); if (! $updated) { diff --git a/Zotlabs/Lib/Queue.php b/Zotlabs/Lib/Queue.php index 6acc58bc5..e03816f05 100644 --- a/Zotlabs/Lib/Queue.php +++ b/Zotlabs/Lib/Queue.php @@ -2,8 +2,8 @@ namespace Zotlabs\Lib; -use Zotlabs\Lib\Libzot; - +use Zotlabs\Zot6\Receiver; +use Zotlabs\Zot6\Zot6Handler; class Queue { @@ -31,19 +31,19 @@ class Queue { $might_be_down = ((datetime_convert('UTC','UTC',$y[0]['earliest']) < datetime_convert('UTC','UTC','now - 2 days')) ? true : false); - // Set all other records for this destination way into the future. + // Set all other records for this destination way into the future. // The queue delivers by destination. We'll keep one queue item for // this destination (this one) with a shorter delivery. If we succeed // once, we'll try to deliver everything for that destination. - // The delivery will be set to at most once per hour, and if the + // The delivery will be set to at most once per hour, and if the // queue item is less than 12 hours old, we'll schedule for fifteen - // minutes. + // minutes. - $r = q("UPDATE outq SET outq_scheduled = '%s' WHERE outq_posturl = '%s'", + q("UPDATE outq SET outq_scheduled = '%s' WHERE outq_posturl = '%s'", dbesc(datetime_convert('UTC','UTC','now + 5 days')), dbesc($x[0]['outq_posturl']) ); - + $since = datetime_convert('UTC','UTC',$x[0]['outq_created']); if(($might_be_down) || ($since < datetime_convert('UTC','UTC','now - 12 hour'))) { @@ -53,9 +53,9 @@ class Queue { $next = datetime_convert('UTC','UTC','now + ' . intval($add_priority) . ' minutes'); } - q("UPDATE outq SET outq_updated = '%s', - outq_priority = outq_priority + %d, - outq_scheduled = '%s' + q("UPDATE outq SET outq_updated = '%s', + outq_priority = outq_priority + %d, + outq_scheduled = '%s' WHERE outq_hash = '%s'", dbesc(datetime_convert()), @@ -69,7 +69,7 @@ class Queue { static function remove($id,$channel_id = 0) { logger('queue: remove queue item ' . $id,LOGGER_DEBUG); $sql_extra = (($channel_id) ? " and outq_channel = " . intval($channel_id) . " " : ''); - + q("DELETE FROM outq WHERE outq_hash = '%s' $sql_extra", dbesc($id) ); @@ -78,7 +78,7 @@ class Queue { static function remove_by_posturl($posturl) { logger('queue: remove queue posturl ' . $posturl,LOGGER_DEBUG); - + q("DELETE FROM outq WHERE outq_posturl = '%s' ", dbesc($posturl) ); @@ -88,10 +88,10 @@ class Queue { static function set_delivered($id,$channel = 0) { logger('queue: set delivered ' . $id,LOGGER_DEBUG); - $sql_extra = (($channel_id) ? " and outq_channel = " . intval($channel_id) . " " : ''); + $sql_extra = (($channel['channel_id']) ? " and outq_channel = " . intval($channel['channel_id']) . " " : ''); // Set the next scheduled run date so far in the future that it will be expired - // long before it ever makes it back into the delivery chain. + // long before it ever makes it back into the delivery chain. q("update outq set outq_delivered = 1, outq_updated = '%s', outq_scheduled = '%s' where outq_hash = '%s' $sql_extra ", dbesc(datetime_convert()), @@ -111,7 +111,7 @@ class Queue { } $x = q("insert into outq ( outq_hash, outq_account, outq_channel, outq_driver, outq_posturl, outq_async, outq_priority, - outq_created, outq_updated, outq_scheduled, outq_notify, outq_msg ) + outq_created, outq_updated, outq_scheduled, outq_notify, outq_msg ) values ( '%s', %d, %d, '%s', '%s', %d, %d, '%s', '%s', '%s', '%s', '%s' )", dbesc($arr['hash']), intval($arr['account_id']), @@ -119,7 +119,7 @@ class Queue { dbesc(($arr['driver']) ? $arr['driver'] : 'zot6'), dbesc($arr['posturl']), intval(1), - intval(($arr['priority']) ? $arr['priority'] : 0), + intval(isset($arr['priority']) ? $arr['priority'] : 0), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc(datetime_convert()), @@ -136,8 +136,8 @@ class Queue { $base = null; $h = parse_url($outq['outq_posturl']); - if($h !== false) - $base = $h['scheme'] . '://' . $h['host'] . (($h['port']) ? ':' . $h['port'] : ''); + if($h !== false) + $base = $h['scheme'] . '://' . $h['host'] . (isset($h['port']) ? ':' . $h['port'] : ''); if(($base) && ($base !== z_root()) && ($immediate)) { $y = q("select site_update, site_dead from site where site_url = '%s' ", @@ -150,7 +150,7 @@ class Queue { return; } if($y[0]['site_update'] < datetime_convert('UTC','UTC','now - 1 month')) { - self::update($outq['outq_hash'],10); + self::update($outq['outq_hash'], 10); logger('immediate delivery deferred for site ' . $base); return; } @@ -161,12 +161,12 @@ class Queue { // your site has existed. Since we don't know for sure what these sites are, // call them unknown - site_store_lowlevel( + site_store_lowlevel( [ 'site_url' => $base, 'site_update' => datetime_convert(), 'site_dead' => 0, - 'site_type' => intval(($outq['outq_driver'] === 'post') ? SITE_TYPE_NOTZOT : SITE_TYPE_UNKNOWN), + 'site_type' => SITE_TYPE_UNKNOWN, 'site_crypto' => '' ] ); @@ -174,67 +174,18 @@ class Queue { } $arr = array('outq' => $outq, 'base' => $base, 'handled' => false, 'immediate' => $immediate); - call_hooks('queue_deliver',$arr); + call_hooks('queue_deliver', $arr); if($arr['handled']) return; - // "post" queue driver - used for diaspora and friendica-over-diaspora communications. - - if($outq['outq_driver'] === 'post') { - $result = z_post_url($outq['outq_posturl'],$outq['outq_msg']); - if($result['success'] && $result['return_code'] < 300) { - logger('deliver: queue post success to ' . $outq['outq_posturl'], LOGGER_DEBUG); - if($base) { - q("update site set site_update = '%s', site_dead = 0 where site_url = '%s' ", - dbesc(datetime_convert()), - dbesc($base) - ); - } - q("update dreport set dreport_result = '%s', dreport_time = '%s' where dreport_queue = '%s'", - dbesc('accepted for delivery'), - dbesc(datetime_convert()), - dbesc($outq['outq_hash']) - ); - self::remove($outq['outq_hash']); - - // server is responding - see if anything else is going to this destination and is piled up - // and try to send some more. We're relying on the fact that do_delivery() results in an - // immediate delivery otherwise we could get into a queue loop. - - if(! $immediate) { - $x = q("select outq_hash from outq where outq_posturl = '%s' and outq_delivered = 0", - dbesc($outq['outq_posturl']) - ); - - $piled_up = array(); - if($x) { - foreach($x as $xx) { - $piled_up[] = $xx['outq_hash']; - } - } - if($piled_up) { - // call do_delivery() with the force flag - do_delivery($piled_up, true); - } - } - } - else { - logger('deliver: queue post returned ' . $result['return_code'] - . ' from ' . $outq['outq_posturl'],LOGGER_DEBUG); - self::update($outq['outq_hash'],10); - } - return; - } - // normal zot delivery logger('deliver: dest: ' . $outq['outq_posturl'], LOGGER_DEBUG); - if($outq['outq_posturl'] === z_root() . '/zot') { // local delivery - $zot = new \Zotlabs\Zot6\Receiver(new \Zotlabs\Zot6\Zot6Handler(),$outq['outq_notify']); - $result = $zot->run(true); + $zot = new Receiver(new Zot6Handler(), $outq['outq_notify']); + $result = $zot->run(); logger('returned_json: ' . json_encode($result,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES), LOGGER_DATA); logger('deliver: local zot delivery succeeded to ' . $outq['outq_posturl']); Libzot::process_response($outq['outq_posturl'],[ 'success' => true, 'body' => json_encode($result) ], $outq); @@ -244,13 +195,14 @@ class Queue { $channel = null; if($outq['outq_channel']) { - $channel = channelx_by_n($outq['outq_channel']); + $channel = channelx_by_n($outq['outq_channel'], true); } $host_crypto = null; if($channel && $base) { - $h = q("select hubloc_sitekey, site_crypto from hubloc left join site on hubloc_url = site_url where site_url = '%s' and hubloc_sitekey != '' order by hubloc_id desc limit 1", + $h = q("SELECT hubloc_sitekey, site_crypto FROM hubloc LEFT JOIN site ON hubloc_url = site_url + WHERE site_url = '%s' AND hubloc_network = 'zot6' ORDER BY hubloc_id DESC LIMIT 1", dbesc($base) ); if($h) { @@ -260,7 +212,7 @@ class Queue { $msg = $outq['outq_notify']; - $result = Libzot::zot($outq['outq_posturl'],$msg,$channel,$host_crypto); + $result = Libzot::zot($outq['outq_posturl'], $msg, $channel, $host_crypto); if($result['success']) { logger('deliver: remote zot delivery succeeded to ' . $outq['outq_posturl']); @@ -269,7 +221,7 @@ class Queue { else { logger('deliver: remote zot delivery failed to ' . $outq['outq_posturl']); logger('deliver: remote zot delivery fail data: ' . print_r($result,true), LOGGER_DATA); - self::update($outq['outq_hash'],10); + self::update($outq['outq_hash'], 10); } } return; diff --git a/Zotlabs/Lib/Share.php b/Zotlabs/Lib/Share.php index d34c0eaba..81f378d0d 100644 --- a/Zotlabs/Lib/Share.php +++ b/Zotlabs/Lib/Share.php @@ -2,21 +2,19 @@ namespace Zotlabs\Lib; -use Zotlabs\Lib\Activity; - class Share { private $item = null; public function __construct($post_id) { - + if(! $post_id) return; - + if(! (local_channel() || remote_channel())) return; - + $r = q("SELECT * from item left join xchan on author_xchan = xchan_hash WHERE id = %d LIMIT 1", intval($post_id) ); @@ -25,26 +23,26 @@ class Share { if(($r[0]['item_private']) && ($r[0]['xchan_network'] !== 'rss')) return; - + $sql_extra = item_permissions_sql($r[0]['uid']); - + $r = q("select * from item where id = %d $sql_extra", intval($post_id) ); if(! $r) return; - + if($r[0]['mimetype'] !== 'text/bbcode') return; - + /** @FIXME eventually we want to post remotely via rpost on your home site */ // When that works remove this next bit: - + if(! local_channel()) return; xchan_query($r); - + $this->item = $r[0]; return; } @@ -68,14 +66,14 @@ class Share { 'address' => $this->item['author']['xchan_addr'], 'network' => $this->item['author']['xchan_network'], 'link' => [ - [ - 'rel' => 'alternate', - 'type' => 'text/html', + [ + 'rel' => 'alternate', + 'type' => 'text/html', 'href' => $this->item['author']['xchan_url'] ], [ - 'rel' => 'photo', - 'type' => $this->item['author']['xchan_photo_mimetype'], + 'rel' => 'photo', + 'type' => $this->item['author']['xchan_photo_mimetype'], 'href' => $this->item['author']['xchan_photo_m'] ] ] @@ -86,14 +84,14 @@ class Share { 'address' => $this->item['owner']['xchan_addr'], 'network' => $this->item['owner']['xchan_network'], 'link' => [ - [ - 'rel' => 'alternate', - 'type' => 'text/html', + [ + 'rel' => 'alternate', + 'type' => 'text/html', 'href' => $this->item['owner']['xchan_url'] ], [ - 'rel' => 'photo', - 'type' => $this->item['owner']['xchan_photo_mimetype'], + 'rel' => 'photo', + 'type' => $this->item['owner']['xchan_photo_mimetype'], 'href' => $this->item['owner']['xchan_photo_m'] ] ] @@ -119,7 +117,7 @@ class Share { $object = json_decode($this->item['obj'],true); $photo_bb = $object['body']; } - + if (strpos($this->item['body'], "[/share]") !== false) { $pos = strpos($this->item['body'], "[share"); $bb = substr($this->item['body'], $pos); @@ -128,7 +126,7 @@ class Share { "' profile='" . $this->item['author']['xchan_url'] . "' avatar='" . $this->item['author']['xchan_photo_s'] . "' link='" . $this->item['plink'] . - "' auth='" . ((in_array($this->item['author']['xchan_network'], ['zot6', 'zot'])) ? 'true' : 'false') . + "' auth='" . (($this->item['author']['xchan_network'] === 'zot6') ? 'true' : 'false') . "' posted='" . $this->item['created'] . "' message_id='" . $this->item['mid'] . "']"; diff --git a/Zotlabs/Lib/ThreadItem.php b/Zotlabs/Lib/ThreadItem.php index 024502d2a..cd54fea17 100644 --- a/Zotlabs/Lib/ThreadItem.php +++ b/Zotlabs/Lib/ThreadItem.php @@ -35,7 +35,7 @@ class ThreadItem { public function __construct($data) { - + $this->data = $data; $this->toplevel = ($this->get_id() == $this->get_data_value('parent')); $this->threaded = get_config('system','thread_allow'); @@ -43,7 +43,7 @@ class ThreadItem { $observer = \App::get_observer(); // Prepare the children - if($data['children']) { + if(isset($data['children'])) { foreach($data['children'] as $item) { /* @@ -98,10 +98,11 @@ class ThreadItem { $conv = $this->get_conversation(); $observer = $conv->get_observer(); - $lock = (((intval($item['item_private'])) || (($item['uid'] == local_channel()) && (strlen($item['allow_cid']) || strlen($item['allow_gid']) + $lock = (((intval($item['item_private'])) || (($item['uid'] == local_channel()) && (strlen($item['allow_cid']) || strlen($item['allow_gid']) || strlen($item['deny_cid']) || strlen($item['deny_gid'])))) ? t('Private Message') : false); + $locktype = $item['item_private']; $shareable = ((($conv->get_profile_owner() == local_channel() && local_channel()) && ($item['item_private'] != 1)) ? true : false); @@ -151,9 +152,9 @@ class ThreadItem { if($observer && $observer['xchan_hash'] - && ($observer['xchan_hash'] == $this->get_data_value('author_xchan') - || $observer['xchan_hash'] == $this->get_data_value('owner_xchan') - || $observer['xchan_hash'] == $this->get_data_value('source_xchan') + && ($observer['xchan_hash'] == $this->get_data_value('author_xchan') + || $observer['xchan_hash'] == $this->get_data_value('owner_xchan') + || $observer['xchan_hash'] == $this->get_data_value('source_xchan') || $this->get_data_value('uid') == local_channel())) $dropping = true; @@ -169,15 +170,15 @@ class ThreadItem { 'dropping' => $dropping, 'delete' => t('Delete'), ); - } + } elseif(is_site_admin()) { $drop = [ 'dropping' => true, 'delete' => t('Admin Delete') ]; } // FIXME - if($observer_is_pageowner) { + if($observer_is_pageowner) { $multidrop = array( - 'select' => t('Select'), + 'select' => t('Select'), ); } @@ -223,7 +224,7 @@ class ThreadItem { if(! feature_enabled($conv->get_profile_owner(),'dislike')) unset($conv_responses['dislike']); - + $responses = get_responses($conv_responses,$response_verbs,$this,$item); $my_responses = []; @@ -254,7 +255,7 @@ class ThreadItem { } $showlike = ((x($conv_responses['like'],$item['mid'])) ? format_like($conv_responses['like'][$item['mid']],$conv_responses['like'][$item['mid'] . '-l'],'like',$item['mid']) : ''); - $showdislike = ((x($conv_responses['dislike'],$item['mid']) && feature_enabled($conv->get_profile_owner(),'dislike')) + $showdislike = ((x($conv_responses['dislike'],$item['mid']) && feature_enabled($conv->get_profile_owner(),'dislike')) ? format_like($conv_responses['dislike'][$item['mid']],$conv_responses['dislike'][$item['mid'] . '-l'],'dislike',$item['mid']) : ''); /* @@ -264,7 +265,7 @@ class ThreadItem { */ $this->check_wall_to_wall(); - + if($this->is_toplevel()) { // FIXME check this permission if(($conv->get_profile_owner() == local_channel()) && (! array_key_exists('real_uid',$item))) { @@ -275,7 +276,7 @@ class ThreadItem { ); } - } + } else { $is_comment = true; } @@ -298,7 +299,7 @@ class ThreadItem { ); */ - $settings = t('Conversation Tools'); + $settings = t('Conversation Features'); } $has_bookmarks = false; @@ -349,7 +350,7 @@ class ThreadItem { // $viewthread (below) is only valid in list mode. If this is a channel page, build the thread viewing link // since we can't depend on llink or plink pointing to the right local location. - + $owner_address = substr($item['owner']['xchan_addr'],0,strpos($item['owner']['xchan_addr'],'@')); $viewthread = $item['llink']; if($conv->get_mode() === 'channel') @@ -357,7 +358,7 @@ class ThreadItem { $comment_count_txt = sprintf( tt('%d comment','%d comments',$total_children),$total_children ); $list_unseen_txt = (($unseen_comments) ? sprintf( t('%d unseen'),$unseen_comments) : ''); - + $children = $this->get_children(); $has_tags = (($body['tags'] || $body['categories'] || $body['mentions'] || $body['attachments'] || $body['folders']) ? true : false); @@ -366,7 +367,7 @@ class ThreadItem { call_hooks('dropdown_extras',$dropdown_extras_arr); $dropdown_extras = $dropdown_extras_arr['dropdown_extras']; - $midb64 = 'b64.' . base64url_encode($item['mid']); + $midb64 = gen_link_id($item['mid']); $mids = [ $midb64 ]; $response_mids = []; foreach($response_verbs as $v) { @@ -386,7 +387,7 @@ class ThreadItem { $tmp_item = array( 'template' => $this->get_template(), 'mode' => $mode, - 'item_type' => intval($item['item_type']), + 'item_type' => intval($item['item_type']), //'type' => implode("",array_slice(explode("/",$item['verb']),-1)), 'body' => $body['html'], 'tags' => $body['tags'], @@ -432,6 +433,7 @@ class ThreadItem { 'editedtime' => (($item['edited'] != $item['created']) ? sprintf( t('last edited: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['edited'], 'r')) : ''), 'expiretime' => (($item['expires'] > NULL_DATE) ? sprintf( t('Expires: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['expires'], 'r')):''), 'lock' => $lock, + 'locktype' => $locktype, 'delayed' => $item['item_delayed'], 'privacy_warning' => $privacy_warning, 'verified' => $verified, @@ -501,7 +503,7 @@ class ThreadItem { 'wait' => t('Please wait'), 'thread_level' => $thread_level, 'settings' => $settings, - 'thr_parent' => (($item['parent_mid'] != $item['thr_parent']) ? 'b64.' . base64url_encode($item['thr_parent']) : '') + 'thr_parent' => (($item['parent_mid'] != $item['thr_parent']) ? gen_link_id($item['thr_parent']) : '') ); $arr = array('item' => $item, 'output' => $tmp_item); @@ -518,8 +520,8 @@ class ThreadItem { // needed for scroll to comment from notification but needs more work // as we do not want to open all comments unless there is actually an #item_xx anchor -// and the url fragment is not sent to the server. -// if(in_array(\App::$module,['display','update_display'])) +// and the url fragment is not sent to the server. +// if(in_array(\App::$module,['display','update_display'])) // $visible_comments = 99999; if(($this->get_display_mode() === 'normal') && ($nb_children > 0)) { @@ -539,7 +541,7 @@ class ThreadItem { } } } - + $result['private'] = $item['item_private']; $result['toplevel'] = ($this->is_toplevel() ? 'toplevel_item' : ''); @@ -554,7 +556,7 @@ class ThreadItem { return $result; } - + public function get_id() { return $this->get_data_value('id'); } @@ -609,7 +611,7 @@ class ThreadItem { if(activity_match($item->get_data_value('verb'),ACTIVITY_LIKE) || activity_match($item->get_data_value('verb'),ACTIVITY_DISLIKE)) { return false; } - + $item->set_parent($this); $this->children[] = $item; return end($this->children); @@ -683,7 +685,7 @@ class ThreadItem { */ public function set_conversation($conv) { $previous_mode = ($this->conversation ? $this->conversation->get_mode() : ''); - + $this->conversation = $conv; // Set it on our children too @@ -792,7 +794,7 @@ class ThreadItem { if(!$this->is_toplevel() && !get_config('system','thread_allow')) { return ''; } - + $comment_box = ''; $conv = $this->get_conversation(); @@ -808,7 +810,7 @@ class ThreadItem { $arr = array('comment_buttons' => '','id' => $this->get_id()); call_hooks('comment_buttons',$arr); $comment_buttons = $arr['comment_buttons']; - + $comment_box = replace_macros($template,array( '$return_path' => '', '$threaded' => $this->is_threaded(), @@ -840,7 +842,7 @@ class ThreadItem { '$cipher' => $conv->get_cipher(), '$sourceapp' => \App::$sourcename, '$observer' => get_observer_hash(), - '$anoncomments' => ((($conv->get_mode() === 'channel' || $conv->get_mode() === 'display') && perm_is_allowed($conv->get_profile_owner(),'','post_comments')) ? true : false), + '$anoncomments' => ((in_array($conv->get_mode(), ['channel', 'display', 'cards', 'articles']) && perm_is_allowed($conv->get_profile_owner(),'','post_comments')) ? true : false), '$anonname' => [ 'anonname', t('Your full name (required)') ], '$anonmail' => [ 'anonmail', t('Your email address (required)') ], '$anonurl' => [ 'anonurl', t('Your website URL (optional)') ] @@ -865,7 +867,7 @@ class ThreadItem { if($conv->get_mode() === 'channel') return; - + if($this->is_toplevel() && ($this->get_data_value('author_xchan') != $this->get_data_value('owner_xchan'))) { $this->owner_url = chanlink_hash($this->data['owner']['xchan_hash']); $this->owner_photo = $this->data['owner']['xchan_photo_m']; diff --git a/Zotlabs/Lib/ThreadStream.php b/Zotlabs/Lib/ThreadStream.php index 68b2c70dd..7fe8fcc2e 100644 --- a/Zotlabs/Lib/ThreadStream.php +++ b/Zotlabs/Lib/ThreadStream.php @@ -77,7 +77,7 @@ class ThreadStream { $this->reload = $_SESSION['return_url']; break; case 'display': - // in this mode we set profile_owner after initialisation (from conversation()) and then + // in this mode we set profile_owner after initialisation (from conversation()) and then // pull some trickery which allows us to re-invoke this function afterward // it's an ugly hack so @FIXME $this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments'); @@ -170,14 +170,14 @@ class ThreadStream { * Only add things that will be displayed */ - + if(($item->get_data_value('id') != $item->get_data_value('parent')) && (activity_match($item->get_data_value('verb'),ACTIVITY_LIKE) || activity_match($item->get_data_value('verb'),ACTIVITY_DISLIKE))) { return false; } $item->set_commentable(false); $ob_hash = (($this->observer) ? $this->observer['xchan_hash'] : ''); - + if(! comments_are_now_closed($item->get_data())) { if(($item->get_data_value('author_xchan') === $ob_hash) || ($item->get_data_value('owner_xchan') === $ob_hash)) $item->set_commentable(true); @@ -194,7 +194,7 @@ class ThreadStream { } if($this->mode === 'pubstream' && (! local_channel())) { $item->set_commentable(false); - } + } $item->set_conversation($this); diff --git a/Zotlabs/Lib/Verify.php b/Zotlabs/Lib/Verify.php index 8703e29e6..f8dc8f8d4 100644 --- a/Zotlabs/Lib/Verify.php +++ b/Zotlabs/Lib/Verify.php @@ -5,7 +5,7 @@ namespace Zotlabs\Lib; class Verify { - function create($type,$channel_id,$token,$meta) { + public static function create($type,$channel_id,$token,$meta) { return q("insert into verify ( vtype, channel, token, meta, created ) values ( '%s', %d, '%s', '%s', '%s' )", dbesc($type), intval($channel_id), @@ -15,7 +15,7 @@ class Verify { ); } - function match($type,$channel_id,$token,$meta) { + public static function match($type,$channel_id,$token,$meta) { $r = q("select id from verify where vtype = '%s' and channel = %d and token = '%s' and meta = '%s' limit 1", dbesc($type), intval($channel_id), @@ -31,7 +31,7 @@ class Verify { return false; } - function get_meta($type,$channel_id,$token) { + public static function get_meta($type,$channel_id,$token) { $r = q("select id, meta from verify where vtype = '%s' and channel = %d and token = '%s' limit 1", dbesc($type), intval($channel_id), @@ -52,7 +52,7 @@ class Verify { * @param string $type Verify type * @param string $interval SQL compatible time interval */ - function purge($type, $interval) { + public static function purge($type, $interval) { q("delete from verify where vtype = '%s' and created < %s - INTERVAL %s", dbesc($type), db_utcnow(), diff --git a/Zotlabs/Lib/ZotURL.php b/Zotlabs/Lib/ZotURL.php index 98d1febe5..6bb01fd7a 100644 --- a/Zotlabs/Lib/ZotURL.php +++ b/Zotlabs/Lib/ZotURL.php @@ -21,9 +21,8 @@ class ZotURL { } $portable_url = substr($url,6); - $u = explode('/',$portable_url); + $u = explode('/',$portable_url); $portable_id = $u[0]; - $hosts = self::lookup($portable_id); if(! $hosts) { @@ -39,8 +38,8 @@ class ZotURL { if($channel && $m) { - $headers = [ - 'Accept' => 'application/x-zot+json', + $headers = [ + 'Accept' => 'application/x-zot+json', 'Content-Type' => 'application/x-zot+json', 'X-Zot-Token' => random_string(), 'Digest' => HTTPSig::generate_digest_header($data), @@ -50,9 +49,9 @@ class ZotURL { $h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel),false); } else { - $h = [ 'Accept: application/x-zot+json' ]; + $h = [ 'Accept: application/x-zot+json' ]; } - + $result = []; $redirects = 0; diff --git a/Zotlabs/Lib/Zotfinger.php b/Zotlabs/Lib/Zotfinger.php index e853d6ebc..840d91403 100644 --- a/Zotlabs/Lib/Zotfinger.php +++ b/Zotlabs/Lib/Zotfinger.php @@ -18,8 +18,8 @@ class Zotfinger { if($channel && $m) { - $headers = [ - 'Accept' => 'application/x-zot+json', + $headers = [ + 'Accept' => 'application/x-zot+json', 'Content-Type' => 'application/x-zot+json', 'X-Zot-Token' => random_string(), 'Digest' => HTTPSig::generate_digest_header($data), @@ -29,11 +29,10 @@ class Zotfinger { $h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel),false); } else { - $h = [ 'Accept: application/x-zot+json' ]; + $h = [ 'Accept: application/x-zot+json' ]; } - - $result = []; + $result = []; $redirects = 0; $x = z_post_url($resource,$data,$redirects, [ 'headers' => $h ] ); @@ -44,11 +43,11 @@ class Zotfinger { if ($verify) { $result['signature'] = HTTPSig::verify($x, EMPTY_STR, 'zot6'); } - + $result['data'] = json_decode($x['body'],true); if($result['data'] && is_array($result['data']) && array_key_exists('encrypted',$result['data']) && $result['data']['encrypted']) { - $result['data'] = json_decode(crypto_unencapsulate($result['data'],get_config('system','prvkey')),true); + $result['data'] = json_decode(Crypto::unencapsulate($result['data'],get_config('system','prvkey')),true); } logger('decrypted: ' . print_r($result,true)); diff --git a/Zotlabs/Module/Acl.php b/Zotlabs/Module/Acl.php index e0206bd43..aeb02eeaa 100644 --- a/Zotlabs/Module/Acl.php +++ b/Zotlabs/Module/Acl.php @@ -2,6 +2,8 @@ namespace Zotlabs\Module; +use Zotlabs\Lib\Libzotdir; + require_once 'include/acl_selectors.php'; require_once 'include/group.php'; @@ -46,20 +48,20 @@ class Acl extends \Zotlabs\Web\Controller { // 'a' => autocomplete connections (mod_connections, mod_poke, mod_sources, mod_photos) // 'x' => nav search bar autocomplete (match any xchan) // $_REQUEST['query'] contains autocomplete search text. - - // List of channels whose connections to also suggest, + + // List of channels whose connections to also suggest, // e.g. currently viewed channel or channels mentioned in a post $extra_channels = (x($_REQUEST,'extra_channels') ? $_REQUEST['extra_channels'] : array()); - + // The different autocomplete libraries use different names for the search text // parameter. Internally we'll use $search to represent the search text no matter - // what request variable it was attached to. - + // what request variable it was attached to. + if(array_key_exists('query',$_REQUEST)) { $search = $_REQUEST['query']; } - + if( (! local_channel()) && (! in_array($type, [ 'x', 'c', 'f' ]))) killme(); @@ -68,7 +70,7 @@ class Acl extends \Zotlabs\Web\Controller { if(in_array($type, [ 'm', 'a', 'c', 'f' ])) { // These queries require permission checking. We'll create a simple array of xchan_hash for those with - // the requisite permissions which we can check against. + // the requisite permissions which we can check against. $x = q("select xchan from abconfig where chan = %d and cat = 'their_perms' and k = '%s' and v = '1'", intval(local_channel()), @@ -85,34 +87,34 @@ class Acl extends \Zotlabs\Web\Controller { $sql_extra2 = "AND ( xchan_name LIKE " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . " OR xchan_addr LIKE " . protect_sprintf( "'%" . dbesc(punify($search)) . ((strpos($search,'@') === false) ? "%@%'" : "%'")) . ") "; $sql_extra2_xchan = "AND ( xchan_name LIKE " . protect_sprintf( "'" . dbesc($search) . "%'" ) . " OR xchan_addr LIKE " . protect_sprintf( "'" . dbesc(punify($search)) . ((strpos($search,'@') === false) ? "%@%'" : "%'")) . ") "; - // This horrible mess is needed because position also returns 0 if nothing is found. + // This horrible mess is needed because position also returns 0 if nothing is found. // Would be MUCH easier if it instead returned a very large value - // Otherwise we could just + // Otherwise we could just // order by LEAST(POSITION($search IN xchan_name),POSITION($search IN xchan_addr)). - $order_extra2 = "CASE WHEN xchan_name LIKE " - . protect_sprintf( "'%" . dbesc($search) . "%'" ) - . " then POSITION('" . protect_sprintf(dbesc($search)) + $order_extra2 = "CASE WHEN xchan_name LIKE " + . protect_sprintf( "'%" . dbesc($search) . "%'" ) + . " then POSITION('" . protect_sprintf(dbesc($search)) . "' IN xchan_name) else position('" . protect_sprintf(dbesc(punify($search))) . "' IN xchan_addr) end, "; $sql_extra3 = "AND ( xchan_addr like " . protect_sprintf( "'%" . dbesc(punify($search)) . "%'" ) . " OR xchan_name like " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . " ) "; - + } else { $sql_extra = $sql_extra2 = $sql_extra3 = ""; } - - + + $groups = array(); $contacts = array(); - + if($type == '' || $type == 'g') { // virtual groups based on private profile viewing ability $r = q("select id, profile_guid, profile_name from profile where is_default = 0 and uid = %d", intval(local_channel()) - ); + ); if($r) { foreach($r as $rv) { $groups[] = array( @@ -130,19 +132,19 @@ class Acl extends \Zotlabs\Web\Controller { // Normal privacy groups $r = q("SELECT pgrp.id, pgrp.hash, pgrp.gname - FROM pgrp, pgrp_member - WHERE pgrp.deleted = 0 AND pgrp.uid = %d + FROM pgrp, pgrp_member + WHERE pgrp.deleted = 0 AND pgrp.uid = %d AND pgrp_member.gid = pgrp.id $sql_extra GROUP BY pgrp.id - ORDER BY pgrp.gname + ORDER BY pgrp.gname LIMIT %d OFFSET %d", intval(local_channel()), intval($count), intval($start) ); - if($r) { + if($r) { foreach($r as $g){ // logger('acl: group: ' . $g['gname'] . ' members: ' . group_get_members_xchan($g['id'])); $groups[] = array( @@ -157,10 +159,10 @@ class Acl extends \Zotlabs\Web\Controller { } } } - + if($type == '' || $type == 'c' || $type === 'f') { - $extra_channels_sql = ''; + $extra_channels_sql = ''; // Only include channels who allow the observer to view their connections if($extra_channels) { @@ -172,7 +174,7 @@ class Acl extends \Zotlabs\Web\Controller { } } } - + // Getting info from the abook is better for local users because it contains info about permissions if(local_channel()) { if($extra_channels_sql != '') @@ -199,7 +201,7 @@ class Acl extends \Zotlabs\Web\Controller { $r2 = array(); foreach($r1 as $rr) { $x = atoken_xchan($rr); - $r2[] = [ + $r2[] = [ 'id' => 'a' . $rr['atoken_id'] , 'hash' => $x['xchan_hash'], 'name' => $x['xchan_name'], @@ -211,42 +213,43 @@ class Acl extends \Zotlabs\Web\Controller { 'abook_self' => 0 ]; } - } + } // add connections - - $r = q("SELECT abook_id as id, xchan_hash as hash, xchan_name as name, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, abook_their_perms, xchan_pubforum, abook_flags, abook_self - FROM abook left join xchan on abook_xchan = xchan_hash + + $r = q("SELECT abook_id as id, xchan_hash as hash, xchan_name as name, xchan_network as net, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, abook_their_perms, xchan_pubforum, abook_flags, abook_self + FROM abook left join xchan on abook_xchan = xchan_hash WHERE (abook_channel = %d $extra_channels_sql) AND abook_blocked = 0 and abook_pending = 0 and xchan_deleted = 0 $sql_extra2 order by $order_extra2 xchan_name asc" , intval(local_channel()) ); + if($r2) $r = array_merge($r2,$r); } else { // Visitors - $r = q("SELECT xchan_hash as id, xchan_hash as hash, xchan_name as name, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, 0 as abook_their_perms, 0 as abook_flags, 0 as abook_self + $r = q("SELECT xchan_hash as id, xchan_hash as hash, xchan_name as name, xchan_network as net, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, 0 as abook_their_perms, 0 as abook_flags, 0 as abook_self FROM xchan left join xlink on xlink_link = xchan_hash WHERE xlink_xchan = '%s' AND xchan_deleted = 0 $sql_extra2_xchan order by $order_extra2 xchan_name asc" , dbesc(get_observer_hash()) ); - + // Find contacts of extra channels // This is probably more complicated than it needs to be if($extra_channels_sql) { // Build a list of hashes that we got previously so we don't get them again $known_hashes = array("'".get_observer_hash()."'"); if($r) - foreach($r as $rr) + foreach($r as $rr) $known_hashes[] = "'".$rr['hash']."'"; $known_hashes_sql = 'AND xchan_hash not in ('.join(',',$known_hashes).')'; - - $r2 = q("SELECT abook_id as id, xchan_hash as hash, xchan_name as name, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, abook_their_perms, abook_flags, abook_self - FROM abook left join xchan on abook_xchan = xchan_hash + + $r2 = q("SELECT abook_id as id, xchan_hash as hash, xchan_name as name, xchan_network as net, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, abook_their_perms, abook_flags, abook_self + FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel IN ($extra_channels_sql) $known_hashes_sql AND abook_blocked = 0 and abook_pending = 0 and abook_hidden = 0 and xchan_deleted = 0 $sql_extra2 order by $order_extra2 xchan_name asc"); if($r2) $r = array_merge($r,$r2); - + // Sort accoring to match position, then alphabetically. This could be avoided if the above two SQL queries could be combined into one, and the sorting could be done on the SQl server (like in the case of a local user) $matchpos = function($x) use($search) { $namepos = strpos($x['name'],$search); @@ -269,24 +272,23 @@ class Acl extends \Zotlabs\Web\Controller { } } if((count($r) < 100) && $type == 'c') { - $r2 = q("SELECT substr(xchan_hash,1,18) as id, xchan_hash as hash, xchan_name as name, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, 0 as abook_their_perms, 0 as abook_flags, 0 as abook_self - FROM xchan + $r2 = q("SELECT substr(xchan_hash,1,18) as id, xchan_hash as hash, xchan_name as name, xchan_network as net, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, 0 as abook_their_perms, 0 as abook_flags, 0 as abook_self + FROM xchan WHERE xchan_deleted = 0 and not xchan_network in ('rss','anon','unknown') $sql_extra2_xchan order by $order_extra2 xchan_name asc" ); if($r2) { $r = array_merge($r,$r2); $r = unique_multidim_array($r,'hash'); - } + } } } elseif($type == 'm') { - $r = array(); - $z = q("SELECT xchan_hash as hash, xchan_name as name, xchan_addr as nick, xchan_photo_s as micro, xchan_url as url + $z = q("SELECT xchan_hash as hash, xchan_name as name, xchan_network as net, xchan_addr as nick, xchan_photo_s as micro, xchan_url as url FROM abook left join xchan on abook_xchan = xchan_hash - WHERE abook_channel = %d + WHERE abook_channel = %d and xchan_deleted = 0 - and xchan_network IN ('zot', 'diaspora', 'friendica-over-diaspora') + and not xchan_network IN ('rss', 'anon', 'unknown') $sql_extra3 ORDER BY xchan_name ASC ", intval(local_channel()) @@ -298,18 +300,18 @@ class Acl extends \Zotlabs\Web\Controller { } } } - + } elseif($type == 'a') { - - $r = q("SELECT abook_id as id, xchan_name as name, xchan_hash as hash, xchan_addr as nick, xchan_photo_s as micro, xchan_network as network, xchan_url as url, xchan_addr as attag , abook_their_perms FROM abook left join xchan on abook_xchan = xchan_hash + + $r = q("SELECT abook_id as id, xchan_name as name, xchan_network as net, xchan_hash as hash, xchan_addr as nick, xchan_photo_s as micro, xchan_url as url, xchan_addr as attag , abook_their_perms FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d and xchan_deleted = 0 $sql_extra3 ORDER BY xchan_name ASC ", intval(local_channel()) ); - + } elseif($type == 'x') { $r = $this->navbar_complete($a); @@ -323,7 +325,7 @@ class Acl extends \Zotlabs\Web\Controller { ); } } - + $o = array( 'start' => $start, 'count' => $count, @@ -334,27 +336,34 @@ class Acl extends \Zotlabs\Web\Controller { } else $r = array(); - + if($r) { + $i = count($contacts); + $x = []; foreach($r as $g) { - - if(in_array($g['network'],['rss','anon','unknown']) && ($type != 'a')) + + if(in_array($g['net'],['rss','anon','unknown']) && ($type != 'a')) continue; $g['hash'] = urlencode($g['hash']); - + if(! $g['nick']) { $g['nick'] = $g['url']; } + $clink = ($g['nick']) ? $g['nick'] : $g['url']; + $lkey = md5($clink); + if (! array_key_exists($lkey, $x)) + $x[$lkey] = $i; + if(in_array($g['hash'],$permitted) && $type === 'f' && (! $noforums)) { - $contacts[] = array( + $contacts[$i] = array( "type" => "c", "photo" => "images/twopeople.png", "name" => $g['name'], "id" => urlencode($g['id']), "xid" => $g['hash'], - "link" => (($g['nick']) ? $g['nick'] : $g['url']), + "link" => $clink, "nick" => substr($g['nick'],0,strpos($g['nick'],'@')), "self" => (intval($g['abook_self']) ? 'abook-self' : ''), "taggable" => 'taggable', @@ -362,24 +371,28 @@ class Acl extends \Zotlabs\Web\Controller { ); } if($type !== 'f') { - $contacts[] = array( - "type" => "c", - "photo" => $g['micro'], - "name" => $g['name'], - "id" => urlencode($g['id']), - "xid" => $g['hash'], - "link" => (($g['nick']) ? $g['nick'] : $g['url']), - "nick" => ((strpos($g['nick'],'@')) ? substr($g['nick'],0,strpos($g['nick'],'@')) : $g['nick']), - "self" => (intval($g['abook_self']) ? 'abook-self' : ''), - "taggable" => '', - "label" => '', - ); + if (! array_key_exists($x[$lkey], $contacts) || ($contacts[$x[$lkey]]['net'] !== 'zot6' && $g['net'] == 'zot6')) { + $contacts[$x[$lkey]] = array( + "type" => "c", + "photo" => $g['micro'], + "name" => $g['name'], + "id" => urlencode($g['id']), + "xid" => $g['hash'], + "link" => $clink, + "nick" => ((strpos($g['nick'],'@')) ? substr($g['nick'],0,strpos($g['nick'],'@')) : $g['nick']), + "self" => (intval($g['abook_self']) ? 'abook-self' : ''), + "taggable" => '', + "label" => '', + "net" => $g['net'] + ); + } } - } + $i++; + } } - + $items = array_merge($groups, $contacts); - + $o = array( 'start' => $start, 'count' => $count, @@ -393,50 +406,49 @@ class Acl extends \Zotlabs\Web\Controller { function navbar_complete(&$a) { - + // logger('navbar_complete'); - + if(observer_prohibited()) { return; } - + $dirmode = intval(get_config('system','directory_mode')); $search = ((x($_REQUEST,'search')) ? htmlentities($_REQUEST['search'],ENT_COMPAT,'UTF-8',false) : ''); if(! $search || mb_strlen($search) < 2) return array(); - + $star = false; $address = false; - + if(substr($search,0,1) === '@') $search = substr($search,1); - + if(substr($search,0,1) === '*') { $star = true; $search = substr($search,1); } - + if(strpos($search,'@') !== false) { $address = true; } - + if(($dirmode == DIRECTORY_MODE_PRIMARY) || ($dirmode == DIRECTORY_MODE_STANDALONE)) { $url = z_root() . '/dirsearch'; } - + if(! $url) { - require_once("include/dir_fns.php"); - $directory = find_upstream_directory($dirmode); + $directory = Libzotdir::find_upstream_directory($dirmode); $url = $directory['url'] . '/dirsearch'; } $token = get_config('system','realm_token'); - + $count = (x($_REQUEST,'count') ? $_REQUEST['count'] : 100); if($url) { $query = $url . '?f=' . (($token) ? '&t=' . urlencode($token) : ''); $query .= '&name=' . urlencode($search) . "&limit=$count" . (($address) ? '&address=' . urlencode(punify($search)) : ''); - + $x = z_fetch_url($query); if($x['success']) { $t = 0; diff --git a/Zotlabs/Module/Activity.php b/Zotlabs/Module/Activity.php index b75f0b245..2fbc35274 100644 --- a/Zotlabs/Module/Activity.php +++ b/Zotlabs/Module/Activity.php @@ -26,7 +26,12 @@ class Activity extends Controller { $portable_id = EMPTY_STR; - $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 "; + $item_normal_extra = sprintf(" and not verb in ('%s', '%s') ", + dbesc(ACTIVITY_FOLLOW), + dbesc(ACTIVITY_UNFOLLOW) + ); + + $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 $item_normal_extra "; $i = null; @@ -86,7 +91,7 @@ class Activity extends Controller { } $parents_str = ids_to_querystr($i,'item_id'); - + $items = q("SELECT item.*, item.id AS item_id FROM item WHERE item.parent IN ( %s ) $item_normal ", dbesc($parents_str) ); @@ -143,8 +148,8 @@ class Activity extends Controller { http_status_exit(403, 'Forbidden'); $i = ZlibActivity::encode_item_collection($nitems,'conversation/' . $item_id,'OrderedCollection'); - if($portable_id) { - ThreadListener::store(z_root() . '/activity/' . $item_id,$portable_id); + if($portable_id && (! intval($items[0]['item_private']))) { + ThreadListener::store(z_root() . '/activity/' . $item_id, $portable_id); } if(! $i) @@ -197,8 +202,12 @@ class Activity extends Controller { } } - $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 "; + $item_normal_extra = sprintf(" and not verb in ('%s', '%s') ", + dbesc(ACTIVITY_FOLLOW), + dbesc(ACTIVITY_UNFOLLOW) + ); + + $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 $item_normal_extra "; $sigdata = HTTPSig::verify(EMPTY_STR); if ($sigdata['portable_id'] && $sigdata['header_valid']) { @@ -239,6 +248,16 @@ class Activity extends Controller { xchan_query($r,true); $items = fetch_post_tags($r,false); + if ($portable_id && (! intval($items[0]['item_private']))) { + $c = q("select abook_id from abook where abook_channel = %d and abook_xchan = '%s'", + intval($items[0]['uid']), + dbesc($portable_id) + ); + if (! $c) { + ThreadListener::store(z_root() . '/activity/' . $item_id, $portable_id); + } + } + $channel = channelx_by_n($items[0]['uid']); $x = array_merge( ['@context' => [ diff --git a/Zotlabs/Module/Admin.php b/Zotlabs/Module/Admin.php index 88b84b9d2..59a9e22b2 100644 --- a/Zotlabs/Module/Admin.php +++ b/Zotlabs/Module/Admin.php @@ -8,7 +8,6 @@ namespace Zotlabs\Module; -require_once('include/queue_fn.php'); require_once('include/account.php'); /** @@ -101,11 +100,14 @@ class Admin extends \Zotlabs\Web\Controller { // pending registrations - $pdg = q("SELECT account.*, register.hash from account left join register on account_id = register.uid where (account_flags & %d ) > 0 ", - intval(ACCOUNT_PENDING) + // $pdg = q("SELECT account.*, register.reg_hash from account left join register on account_id = register.reg_uid // where (account_flags & %d ) > 0 ", + // intval(ACCOUNT_PENDING) + // ); + $pdg = q("SELECT COUNT(*) AS pdg FROM register WHERE reg_vital = 1 AND reg_expires > '%s' ", + dbesc(date('Y-m-d H:i:s')) ); - $pending = (($pdg) ? count($pdg) : 0); + $pending = ($pdg ? $pdg[0]['pdg'] : 0); // available channels, primary and clones $channels = array(); diff --git a/Zotlabs/Module/Admin/Accounts.php b/Zotlabs/Module/Admin/Accounts.php index 0c7e089be..1c1911b3a 100644 --- a/Zotlabs/Module/Admin/Accounts.php +++ b/Zotlabs/Module/Admin/Accounts.php @@ -5,7 +5,7 @@ namespace Zotlabs\Module\Admin; class Accounts { - + /** * @brief Handle POST actions on accounts admin page. * @@ -15,14 +15,105 @@ class Accounts { * */ + const MYP = 'ZAR'; // ZAR2x + const VERSION = '2.0.0'; + function post() { $pending = ( x($_POST, 'pending') ? $_POST['pending'] : array() ); $users = ( x($_POST, 'user') ? $_POST['user'] : array() ); $blocked = ( x($_POST, 'blocked') ? $_POST['blocked'] : array() ); - + check_form_security_token_redirectOnErr('/admin/accounts', 'admin_accounts'); - + + $isajax = is_ajax(); + $rc = 0; + + If (!is_site_admin()) { + if ($isajax) { + killme(); + exit; + } + goaway(z_root() . '/'); + } + + if ($isajax) { + //$debug = print_r($_SESSION[self::MYP],true); + $zarop = (x($_POST['zardo']) && preg_match('/^[ad]{1,1}$/', $_POST['zardo']) ) + ? $_POST['zardo'] : ''; + // zarat arrives with leading underscore _n + $zarat = (x($_POST['zarat']) && preg_match('/^_{1,1}[0-9]{1,6}$/', $_POST['zarat']) ) + ? substr($_POST['zarat'],1) : ''; + $zarse = (x($_POST['zarse']) && preg_match('/^[0-9a-f]{8,8}$/', $_POST['zarse']) ) + ? hex2bin($_POST['zarse']) : ''; + + if ($zarop && $zarat >= 0 && $zarse && $zarse == $_SESSION[self::MYP]['h'][$zarat]) { + + // + if ($zarop == 'd') { + $rd = q("UPDATE register SET reg_vital = 0 WHERE reg_id = %d AND SUBSTR(reg_hash,1,4) = '%s' ", + intval($_SESSION[self::MYP]['i'][$zarat]), + dbesc($_SESSION[self::MYP]['h'][$zarat]) + ); + $rc = '×'; + } + elseif ($zarop == 'a') { + // approval, REGISTER_DENIED by user 0x0040, REGISTER_AGREED by user 0x0020 @Regate + $rd = q("UPDATE register SET reg_flags = (reg_flags & ~ 16), " + . " reg_vital = (CASE (reg_flags & ~ 48) WHEN 0 THEN 0 ELSE 1 END) " + . " WHERE reg_vital = 1 AND reg_id = %d AND SUBSTR(reg_hash,1,4) = '%s' ", + intval($_SESSION[self::MYP]['i'][$zarat]), + dbesc($_SESSION[self::MYP]['h'][$zarat]) + ); + $rc = 0; + $rs = q("SELECT * from register WHERE reg_id = %d ", + intval($_SESSION[self::MYP]['i'][$zarat]) + ); + if ($rs && ($rs[0]['reg_flags'] & ~ 48) == 0) { + // create account + $rc = 'ok'.$rs[0]['reg_id']; + $ac = create_account_from_register($rs[0]); + if ( $ac['success'] ) { + $rc .= '✔'; + + $auto_create = get_config('system','auto_channel_create',1); + + if($auto_create) { + $reonar = json_decode($rs[0]['reg_stuff'], true); + // prepare channel creation + if($reonar['chan.name']) + set_aconfig($ac['account']['account_id'], 'register', 'channel_name', $reonar['chan.name']); + + if($reonar['chan.did1']) + set_aconfig($ac['account']['account_id'], 'register', 'channel_address', $reonar['chan.did1']); + + $permissions_role = get_config('system','default_permissions_role'); + if($permissions_role) + set_aconfig($ac['account']['account_id'], 'register', 'permissions_role', $permissions_role); + + // create channel + $new_channel = auto_channel_create($ac['account']['account_id']); + + if($new_channel['success']) { + $rc .= ' c,ok' . $new_channel['channel']['channel_id'] . '✔'; + } + else { + $rc .= ' c ×'; + } + } + + + } + } else { + $rc='oh ×'; + } + } + echo json_encode(array('re' => $zarop, 'at' => '_' . $zarat, 'rc' => $rc)); + } + killme(); + exit; + } + // change to switch structure? // account block/unblock button was submitted if (x($_POST, 'page_accounts_block')) { @@ -55,7 +146,7 @@ class Accounts { account_deny($hash); } } - + goaway(z_root() . '/admin/accounts' ); } @@ -75,19 +166,21 @@ class Accounts { $account = q("SELECT * FROM account WHERE account_id = %d", intval($uid) ); - + if (! $account) { notice( t('Account not found') . EOL); goaway(z_root() . '/admin/accounts' ); } - + check_form_security_token_redirectOnErr('/admin/accounts', 'admin_accounts', 't'); - + + $debug = ''; + switch (argv(2)){ case 'delete': // delete user account_remove($uid,true,false); - + notice( sprintf(t("Account '%s' deleted"), $account[0]['account_email']) . EOL); break; case 'block': @@ -95,7 +188,7 @@ class Accounts { intval(ACCOUNT_BLOCKED), intval($uid) ); - + notice( sprintf( t("Account '%s' blocked") , $account[0]['account_email']) . EOL); break; case 'unblock': @@ -103,27 +196,74 @@ class Accounts { intval(ACCOUNT_BLOCKED), intval($uid) ); - + notice( sprintf( t("Account '%s' unblocked"), $account[0]['account_email']) . EOL); break; } - + goaway(z_root() . '/admin/accounts' ); } - - /* get pending */ - $pending = q("SELECT account.*, register.hash from account left join register on account_id = register.uid where (account_flags & %d )>0 ", - intval(ACCOUNT_PENDING) - ); - + + $tao = 'tao.zar.zarax = ' . "'" . '<img src="' . z_root() . '/images/zapax16.gif">' . "';\n"; + + + // by default we will only return verified results. if reg_all is set we will return everything'' + $get_all = isset($_REQUEST['get_all']); + $pending = get_pending_accounts($get_all); + + unset($_SESSION[self::MYP]); + + if ($pending) { + // collect and group all ip + $atips = dbq("SELECT reg_atip AS atip, COUNT(reg_atip) AS atips FROM register + WHERE reg_vital = 1 GROUP BY reg_atip" + ); + + (($atips) ? $atipn = array_column($atips, 'atips', 'atip') : $atipn = ['' => 0]); + + $tao .= 'tao.zar.zarar = {'; + foreach ($pending as $n => $v) { + + $stuff = json_decode($v['reg_stuff'], true); + + if(isset($stuff['msg'])) { + $pending[$n]['msg'] = $stuff['msg']; + } + + if (array_key_exists($v['reg_atip'], $atipn)) { + $pending[$n]['reg_atip'] = $v['reg_atip']; + $pending[$n]['reg_atip_n'] = $atipn[$v['reg_atip']]; + } + + $pending[$n]['status'] = ''; + if($pending[$n]['reg_flags'] & ACCOUNT_UNVERIFIED > 0) + $pending[$n]['status'] = [t('Unverified'), 'bg-warning']; + + if($pending[$n]['status'] && $pending[$n]['reg_expires'] < datetime_convert()) + $pending[$n]['status'] = [t('Expired'), 'bg-danger text-white']; + + // timezone adjust date_time for display + $pending[$n]['reg_created'] = datetime_convert('UTC', date_default_timezone_get(), $pending[$n]['reg_created']); + $pending[$n]['reg_startup'] = datetime_convert('UTC', date_default_timezone_get(), $pending[$n]['reg_startup']); + $pending[$n]['reg_expires'] = datetime_convert('UTC', date_default_timezone_get(), $pending[$n]['reg_expires']); + + // better secure + $tao .= $n . ": '" . substr(bin2hex($v['reg_hash']),0,8) . "',"; + $_SESSION[self::MYP]['h'][] = substr($v['reg_hash'],0,4); + $_SESSION[self::MYP]['i'][] = $v['reg_id']; + } + $tao = rtrim($tao,',') . '};' . "\n"; + } + // <- hilmar] + /* get accounts */ - + $total = q("SELECT count(*) as total FROM account"); if (count($total)) { \App::set_pager_total($total[0]['total']); \App::set_pager_itemspage(100); } - + $serviceclass = (($_REQUEST['class']) ? " and account_service_class = '" . dbesc($_REQUEST['class']) . "' " : ''); $key = (($_REQUEST['key']) ? dbesc($_REQUEST['key']) : 'account_id'); @@ -134,8 +274,8 @@ class Accounts { $base = z_root() . '/admin/accounts?f='; $odir = (($dir === 'asc') ? '0' : '1'); - $users = q("SELECT account_id , account_email, account_lastlog, account_created, account_expires, account_service_class, ( account_flags & %d ) > 0 as blocked, - (SELECT %s FROM channel as ch WHERE ch.channel_account_id = ac.account_id and ch.channel_removed = 0 ) as channels FROM account as ac + $users = q("SELECT account_id , account_email, account_lastlog, account_created, account_expires, account_service_class, ( account_flags & %d ) > 0 as blocked, + (SELECT %s FROM channel as ch WHERE ch.channel_account_id = ac.account_id and ch.channel_removed = 0 ) as channels FROM account as ac where true $serviceclass and account_flags != %d order by $key $dir limit %d offset %d ", intval(ACCOUNT_BLOCKED), db_concat('ch.channel_address', ' '), @@ -143,15 +283,15 @@ class Accounts { intval(\App::$pager['itemspage']), intval(\App::$pager['start']) ); - + // function _setup_users($e){ // $accounts = Array( - // t('Normal Account'), + // t('Normal Account'), // t('Soapbox Account'), // t('Community/Celebrity Account'), // t('Automatic Friend Account') // ); - + // $e['page_flags'] = $accounts[$e['page-flags']]; // $e['register_date'] = relative_date($e['register_date']); // $e['login_date'] = relative_date($e['login_date']); @@ -159,49 +299,57 @@ class Accounts { // return $e; // } // $users = array_map("_setup_users", $users); - + $t = get_markup_template('admin_accounts.tpl'); $o = replace_macros($t, array( // strings // + '$debug' => $debug, '$title' => t('Administration'), '$page' => t('Accounts'), '$submit' => t('Submit'), - '$select_all' => t('select all'), - '$h_pending' => t('Registrations waiting for confirm'), - '$th_pending' => array( t('Request date'), t('Email') ), - '$no_pending' => t('No registrations.'), + '$get_all' => (($get_all) ? t('Show verified registrations') : t('Show all registrations')), + '$get_all_link' => (($get_all) ? z_root() .'/admin/accounts' : z_root() .'/admin/accounts?get_all'), + '$sel_tall' => t('Select toggle'), + '$sel_deny' => t('Deny selected'), + '$sel_aprv' => t('Approve selected'), + '$h_pending' => (($get_all) ? t('All registrations') : t('Verified registrations waiting for approval')), + '$th_pending' => array(t('Request date'), 'dId2', t('Email'), 'IP', t('Requests')), + '$no_pending' => (($get_all) ? t('No registrations available') : t('No verified registrations available')), '$approve' => t('Approve'), '$deny' => t('Deny'), '$delete' => t('Delete'), '$block' => t('Block'), '$unblock' => t('Unblock'), + '$verified' => t('Verified'), + '$not_verified' => t('Not yet verified'), '$odir' => $odir, '$base' => $base, '$h_users' => t('Accounts'), - '$th_users' => array( + '$th_users' => array( [ t('ID'), 'account_id' ], [ t('Email'), 'account_email' ], - [ t('All Channels'), 'channels' ], + [ t('All channels'), 'channels' ], [ t('Register date'), 'account_created' ], [ t('Last login'), 'account_lastlog' ], [ t('Expires'), 'account_expires' ], - [ t('Service Class'), 'account_service_class'] ), - - '$confirm_delete_multi' => t('Selected accounts will be deleted!\n\nEverything these accounts had posted on this site will be permanently deleted!\n\nAre you sure?'), - '$confirm_delete' => t('The account {0} will be deleted!\n\nEverything this account has posted on this site will be permanently deleted!\n\nAre you sure?'), - + [ t('Service class'), 'account_service_class'] ), + + '$confirm_delete_multi' => p2j(t('Selected accounts will be deleted!\n\nEverything these accounts had posted on this site will be permanently deleted!\n\nAre you sure?')), + '$confirm_delete' => p2j(t('The account {0} will be deleted!\n\nEverything this account has posted on this site will be permanently deleted!\n\nAre you sure?')), + '$form_security_token' => get_form_security_token("admin_accounts"), - + // values // - '$baseurl' => z_root(), - - '$pending' => $pending, - '$users' => $users, + '$baseurl' => z_root(), + '$tao' => $tao, + '$pending' => $pending, + '$users' => $users, + '$msg' => t('Message') )); $o .= paginate($a); - + return $o; } - } + diff --git a/Zotlabs/Module/Admin/Channels.php b/Zotlabs/Module/Admin/Channels.php index e0f26112d..09769a166 100644 --- a/Zotlabs/Module/Admin/Channels.php +++ b/Zotlabs/Module/Admin/Channels.php @@ -173,4 +173,4 @@ class Channels { return $o; } -}
\ No newline at end of file +} diff --git a/Zotlabs/Module/Admin/Queue.php b/Zotlabs/Module/Admin/Queue.php index 5a47413ee..baa50591f 100644 --- a/Zotlabs/Module/Admin/Queue.php +++ b/Zotlabs/Module/Admin/Queue.php @@ -2,35 +2,35 @@ namespace Zotlabs\Module\Admin; - +use Zotlabs\Lib\Queue as LibQueue; class Queue { - + function get() { $o = ''; - + $expert = ((array_key_exists('expert',$_REQUEST)) ? intval($_REQUEST['expert']) : 0); - + if($_REQUEST['drophub']) { hubloc_mark_as_down($_REQUEST['drophub']); - remove_queue_by_posturl($_REQUEST['drophub']); + LibQueue::remove_by_posturl($_REQUEST['drophub']); } - + if($_REQUEST['emptyhub']) { - remove_queue_by_posturl($_REQUEST['emptyhub']); + LibQueue::remove_by_posturl($_REQUEST['emptyhub']); } - - $r = q("select count(outq_posturl) as total, max(outq_priority) as priority, outq_posturl from outq + + $r = q("select count(outq_posturl) as total, max(outq_priority) as priority, outq_posturl from outq where outq_delivered = 0 group by outq_posturl order by total desc"); - + for($x = 0; $x < count($r); $x ++) { $r[$x]['eurl'] = urlencode($r[$x]['outq_posturl']); $r[$x]['connected'] = datetime_convert('UTC',date_default_timezone_get(),$r[$x]['connected'],'Y-m-d'); } - + $o = replace_macros(get_markup_template('admin_queue.tpl'), array( '$banner' => t('Queue Statistics'), '$numentries' => t('Total Entries'), @@ -43,11 +43,11 @@ class Queue { '$entries' => $r, '$expert' => $expert )); - + return $o; } - -}
\ No newline at end of file + +} diff --git a/Zotlabs/Module/Admin/Site.php b/Zotlabs/Module/Admin/Site.php index 011bf3ce4..76e117a84 100644 --- a/Zotlabs/Module/Admin/Site.php +++ b/Zotlabs/Module/Admin/Site.php @@ -5,14 +5,25 @@ namespace Zotlabs\Module\Admin; class Site { + /** * @brief POST handler for Admin Site Page. * */ function post(){ + // [hilmar-> + $this->isajax = is_ajax(); + $this->eol = $this->isajax ? "\n" : EOL; + // ] if (!x($_POST, 'page_site')) { - return; + // [ + if (!$this->isajax) + // ] + return; } + // [ + $this->msgbg = ''; + // <-hilmar] check_form_security_token_redirectOnErr('/admin/site', 'admin_site'); @@ -24,14 +35,17 @@ class Site { $siteinfo = ((x($_POST,'siteinfo')) ? trim($_POST['siteinfo']) : ''); $language = ((x($_POST,'language')) ? notags(trim($_POST['language'])) : ''); $theme = ((x($_POST,'theme')) ? notags(trim($_POST['theme'])) : ''); -// $theme_mobile = ((x($_POST,'theme_mobile')) ? notags(trim($_POST['theme_mobile'])) : ''); -// $site_channel = ((x($_POST,'site_channel')) ? notags(trim($_POST['site_channel'])) : ''); + // $theme_mobile = ((x($_POST,'theme_mobile')) ? notags(trim($_POST['theme_mobile'])) : ''); + // $site_channel = ((x($_POST,'site_channel')) ? notags(trim($_POST['site_channel'])) : ''); $maximagesize = ((x($_POST,'maximagesize')) ? intval(trim($_POST['maximagesize'])) : 0); $register_policy = ((x($_POST,'register_policy')) ? intval(trim($_POST['register_policy'])) : 0); + $register_wo_email = ((x($_POST,'register_wo_email')) ? intval(trim($_POST['register_wo_email'])) : 0); $minimum_age = ((x($_POST,'minimum_age')) ? intval(trim($_POST['minimum_age'])) : 13); $access_policy = ((x($_POST,'access_policy')) ? intval(trim($_POST['access_policy'])) : 0); - $invite_only = ((x($_POST,'invite_only')) ? True : False); + $reg_autochannel = ((x($_POST,'auto_channel_create')) ? True : False); + $invitation_only = ((x($_POST,'invitation_only')) ? True : False); + $invitation_also = ((x($_POST,'invitation_also')) ? True : False); $abandon_days = ((x($_POST,'abandon_days')) ? intval(trim($_POST['abandon_days'])) : 0); $register_text = ((x($_POST,'register_text')) ? notags(trim($_POST['register_text'])) : ''); @@ -75,6 +89,16 @@ class Site { $maxloadavg = ((x($_POST,'maxloadavg')) ? intval(trim($_POST['maxloadavg'])) : 50); $feed_contacts = ((x($_POST,'feed_contacts')) ? intval($_POST['feed_contacts']) : 0); $verify_email = ((x($_POST,'verify_email')) ? 1 : 0); + $register_perday = ((x($_POST,'register_perday')) ? intval(trim($_POST['register_perday'])) : 50); + $register_sameip = ((x($_POST,'register_sameip')) ? intval(trim($_POST['register_sameip'])) : 3); + + $regdelayn = ((x($_POST,'zardelayn')) ? intval(trim($_POST['zardelayn'])) : 0); + $regdelayu = ((x($_POST,'zardelay')) ? notags(trim($_POST['zardelay'])) : ''); + $reg_delay = (preg_match('/^[a-z]{1,1}$/', $regdelayu) ? $regdelayn . $regdelayu : ''); + $regexpiren = ((x($_POST,'zarexpiren')) ? intval(trim($_POST['zarexpiren'])) : 0); + $regexpireu = ((x($_POST,'zarexpire')) ? notags(trim($_POST['zarexpire'])) : ''); + $reg_expire = (preg_match('/^[a-z]{1,1}$/', $regexpireu) ? $regexpiren . $regexpireu : ''); + $imagick_path = ((x($_POST,'imagick_path')) ? trim($_POST['imagick_path']) : ''); $force_queue = ((intval($_POST['force_queue']) > 0) ? intval($_POST['force_queue']) : 3000); $pub_incl = escape_tags(trim($_POST['pub_incl'])); @@ -82,6 +106,35 @@ class Site { $permissions_role = escape_tags(trim($_POST['permissions_role'])); + // [hilmar-> + $this->register_duty = ((x($_POST,'register_duty')) ? notags(trim($_POST['register_duty'])) : ''); + if (! preg_match('/^[0-9 .,:\-]{0,191}$/', $this->register_duty)) { + $this->msgbg .= 'ZAR0131E,' . t('Invalid input') . $this->eol; + $this->error++; + } else { + + $this->duty(); + + if ($this->isajax) { + echo json_encode(array('msgbg' => $this->msgbg, 'me' => 'zar')); + // that mission is complete + killme(); + exit; + + } else { + + //logger( print_r( $this->msgbg, true) ); + //logger( print_r( $this->joo, true) ); + if ($this->error === 0) { + set_config('system', 'register_duty', $this->register_duty); + set_config('system', 'register_duty_jso', $this->joo); + } else { + notice('ZAR0130E,'.t('Errors') . ': ' . $this->error) . EOL . $this->msgfg; + } + } + } + // <-hilmar] + set_config('system', 'feed_contacts', $feed_contacts); set_config('system', 'delivery_interval', $delivery_interval); set_config('system', 'delivery_batch_count', $delivery_batch_count); @@ -96,6 +149,10 @@ class Site { set_config('system', 'login_on_homepage', $login_on_homepage); set_config('system', 'enable_context_help', $enable_context_help); set_config('system', 'verify_email', $verify_email); + set_config('system', 'max_daily_registrations', $register_perday); + set_config('system', 'register_sameip', $register_sameip); + set_config('system', 'register_delay', $reg_delay); + set_config('system', 'register_expire', $reg_expire); set_config('system', 'default_expire_days', $default_expire_days); set_config('system', 'active_expire_days', $active_expire_days); set_config('system', 'reply_address', $reply_address); @@ -126,17 +183,20 @@ class Site { set_config('system','siteinfo',$siteinfo); set_config('system', 'language', $language); set_config('system', 'theme', $theme); -// if ( $theme_mobile === '---' ) { -// del_config('system', 'mobile_theme'); -// } else { -// set_config('system', 'mobile_theme', $theme_mobile); -// } - // set_config('system','site_channel', $site_channel); + // if ( $theme_mobile === '---' ) { + // del_config('system', 'mobile_theme'); + // } else { + // set_config('system', 'mobile_theme', $theme_mobile); + // } + // set_config('system','site_channel', $site_channel); set_config('system','maximagesize', $maximagesize); set_config('system','register_policy', $register_policy); + set_config('system','register_wo_email', $register_wo_email); set_config('system','minimum_age', $minimum_age); - set_config('system','invitation_only', $invite_only); + set_config('system','auto_channel_create', $reg_autochannel); + set_config('system', 'invitation_only', $invitation_only); + set_config('system', 'invitation_also', $invitation_also); set_config('system','access_policy', $access_policy); set_config('system','account_abandon_days', $abandon_days); set_config('system','register_text', $register_text); @@ -260,6 +320,8 @@ class Site { REGISTER_APPROVE => t("Yes - with approval"), REGISTER_OPEN => t("Yes") ); + $this->register_duty = get_config('system', 'register_duty', '-:-'); + $register_perday = get_config('system','max_daily_registrations', 50); /* Acess policy */ $access_choices = Array( @@ -286,9 +348,66 @@ class Site { $homelogin = get_config('system','login_on_homepage'); $enable_context_help = get_config('system','enable_context_help'); + // for reuse reg_delay and reg_expire + $reg_rabots = array( + 'i' => t('Minute(s)'), + 'h' => t('Hour(s)') , + 'd' => t('Day(s)') , + 'w' => t('Week(s)') , + 'm' => t('Month(s)') , + 'y' => t('Year(s)') + ); + $regdelay_n = $regdelay_u = false; + $regdelay = get_config('system','register_delay'); + if ($regdelay) + list($regdelay_n, $regdelay_u) = array(substr($regdelay,0,-1),substr($regdelay,-1)); + $reg_delay = replace_macros(get_markup_template('field_duration.qmc.tpl'), + array( + 'label' => t('Register verification delay'), + 'qmc' => 'zar', + 'qmcid' => '', + 'help' => t('Time to wait before a registration can be verified'), + 'field' => array( + 'name' => 'delay', + 'title' => t('duration up from now'), + 'value' => ($regdelay_n === false ? 0 : $regdelay_n), + 'min' => '0', + 'max' => '99', + 'size' => '2', + 'default' => ($regdelay_u === false ? 'i' : $regdelay_u) + ), + 'rabot' => $reg_rabots + ) + ); + $regexpire_n = $regexpire_u = false; + $regexpire = get_config('system','register_expire'); + if ($regexpire) + list($regexpire_n, $regexpire_u) = array(substr($regexpire,0,-1),substr($regexpire,-1)); + $reg_expire = replace_macros(get_markup_template('field_duration.qmc.tpl'), + array( + 'label' => t('Register verification expiration time'), + 'qmc' => 'zar', + 'qmcid' => '', + 'help' => t('Time before an unverified registration will expire'), + 'field' => array( + 'name' => 'expire', + 'title' => t('duration up from now'), + 'value' => ($regexpire_n === false ? 3 : $regexpire_n), + 'min' => '0', + 'max' => '99', + 'size' => '2', + 'default' => ($regexpire_u === false ? 'd' : $regexpire_u) + ), + 'rabot' => $reg_rabots + ) + ); + + $tao = ''; $t = get_markup_template("admin_site.tpl"); return replace_macros($t, array( '$title' => t('Administration'), + // interfacing js vars + '$tao' => $tao, '$page' => t('Site'), '$submit' => t('Submit'), '$registration' => t('Registration'), @@ -305,21 +424,87 @@ class Site { '$siteinfo' => array('siteinfo', t('Site Information'), get_config('system','siteinfo'), t("Publicly visible description of this site. Displayed on siteinfo page. BBCode can be used here")), '$language' => array('language', t("System language"), get_config('system','language'), "", $lang_choices), '$theme' => array('theme', t("System theme"), get_config('system','theme'), t("Default system theme - may be over-ridden by user profiles - <a href='#' id='cnftheme'>change theme settings</a>"), $theme_choices), -// '$theme_mobile' => array('theme_mobile', t("Mobile system theme"), get_config('system','mobile_theme'), t("Theme for mobile devices"), $theme_choices_mobile), -// '$site_channel' => array('site_channel', t("Channel to use for this website's static pages"), get_config('system','site_channel'), t("Site Channel")), + // '$theme_mobile' => array('theme_mobile', t("Mobile system theme"), get_config('system','mobile_theme'), t("Theme for mobile devices"), $theme_choices_mobile), + // '$site_channel' => array('site_channel', t("Channel to use for this website's static pages"), get_config('system','site_channel'), t("Site Channel")), '$feed_contacts' => array('feed_contacts', t('Allow Feeds as Connections'),get_config('system','feed_contacts'),t('(Heavy system resource usage)')), '$maximagesize' => array('maximagesize', t("Maximum image size"), intval(get_config('system','maximagesize')), t("Maximum size in bytes of uploaded images. Default is 0, which means no limits.")), - '$register_policy' => array('register_policy', t("Does this site allow new member registration?"), get_config('system','register_policy'), "", $register_choices), - '$invite_only' => array('invite_only', t("Invitation only"), get_config('system','invitation_only'), t("Only allow new member registrations with an invitation code. Above register policy must be set to Yes.")), '$minimum_age' => array('minimum_age', t("Minimum age"), (x(get_config('system','minimum_age'))?get_config('system','minimum_age'):13), t("Minimum age (in years) for who may register on this site.")), '$access_policy' => array('access_policy', t("Which best describes the types of account offered by this hub?"), get_config('system','access_policy'), t("This is displayed on the public server site list."), $access_choices), - '$register_text' => array('register_text', t("Register text"), htmlspecialchars(get_config('system','register_text'), ENT_QUOTES, 'UTF-8'), t("Will be displayed prominently on the registration page.")), + + // Register + // [hilmar-> + '$register_text' => [ + 'register_text', + t("Register text"), + htmlspecialchars(get_config('system','register_text'), ENT_QUOTES, 'UTF-8'), + t("This text will be displayed prominently at the registration page") + ], + '$register_policy' => [ + 'register_policy', + t("Does this site allow new member registration?"), + get_config('system','register_policy'), + "", + $register_choices, + ], + '$register_duty' => [ + 'register_duty', + t('Configure the registration open days/hours'), + get_config('system', 'register_duty', '-:-'), + t('Empty or \'-:-\' value will keep registration open 24/7 (default)') . EOL . + t('Weekdays and hours must be separated by colon \':\', From-To ranges with a dash `-` example: 1:800-1200') . EOL . + t('Weekday:Hour pairs must be separated by space \' \' example: 1:900-1700 2:900-1700') . EOL . + t('From-To ranges must be separated by comma \',\' example: 1:800-1200,1300-1700 or 1-2,4-5:900-1700') . EOL . + t('Advanced examples:') . ' 1-5:0900-1200,1300-1700 6:900-1230 ' . t('or') . ' 1-2,4-5:800-1800<br>' . EOL . + '<a id="zar083a" class="btn btn-sm btn-outline-secondary zuia">' . t('Check your configuration') . '</a>'. EOL + ], + '$register_perday' => [ + 'register_perday', + t('Max account registrations per day'), + get_config('system', 'max_daily_registrations', 50), + t('Unlimited if zero or no value - default 50') + ], + '$register_sameip' => [ + 'register_sameip', + t('Max account registrations from same IP'), + get_config('system', 'register_sameip', 3), + t('Unlimited if zero or no value - default 3') + ], + '$reg_delay' => $reg_delay, + '$reg_expire' => $reg_expire, + '$reg_autochannel' => [ + 'auto_channel_create', + t("Auto channel create"), + get_config('system','auto_channel_create', 1), + t("If disabled the channel will be created in a separate step during the registration process") + ], + '$invitation_only' => [ + 'invitation_only', + t("Require invite code"), + get_config('system', 'invitation_only', 0) + ], + '$invitation_also' => [ + 'invitation_also', + t("Allow invite code"), + get_config('system', 'invitation_also', 0) + ], + '$verify_email' => [ + 'verify_email', + t("Require email address"), + get_config('system','verify_email'), + t("The provided email address will be verified (recommended)") + ], + '$abandon_days' => [ + 'abandon_days', + t('Abandon account after x days'), + get_config('system','account_abandon_days'), + t('Will not waste system resources polling external sites for abandonded accounts. Enter 0 for no time limit.') + ], + // <-hilmar] + '$role' => $role, '$frontpage' => array('frontpage', t("Site homepage to show visitors (default: login box)"), get_config('system','frontpage'), t("example: 'pubstream' to show public stream, 'page/sys/home' to show a system webpage called 'home' or 'include:home.html' to include a file.")), '$mirror_frontpage' => array('mirror_frontpage', t("Preserve site homepage URL"), get_config('system','mirror_frontpage'), t('Present the site homepage in a frame at the original location instead of redirecting')), - '$abandon_days' => array('abandon_days', t('Accounts abandoned after x days'), get_config('system','account_abandon_days'), t('Will not waste system resources polling external sites for abandonded accounts. Enter 0 for no time limit.')), '$allowed_sites' => array('allowed_sites', t("Allowed friend domains"), get_config('system','allowed_sites'), t("Comma separated list of domains which are allowed to establish friendships with this site. Wildcards are accepted. Empty to allow any domains")), - '$verify_email' => array('verify_email', t("Verify Email Addresses"), get_config('system','verify_email'), t("Check to verify email addresses used in account registration (recommended).")), '$force_publish' => array('publish_all', t("Force publish"), get_config('system','publish_all'), t("Check to force all profiles on this site to be listed in the site directory.")), '$disable_discover_tab' => array('disable_discover_tab', t('Import Public Streams'), $discover_tab, t('Import and allow access to public content pulled from other sites. Warning: this content is unmoderated.')), '$site_firehose' => array('site_firehose', t('Site only Public Streams'), get_config('system','site_firehose'), t('Allow access to public content originating only from this site if Imported Public Streams are disabled.')), @@ -350,15 +535,184 @@ class Site { '$maxloadavg' => array('maxloadavg', t("Maximum Load Average"), ((intval(get_config('system','maxloadavg')) > 0)?get_config('system','maxloadavg'):50), t("Maximum system load before delivery and poll processes are deferred - default 50.")), '$default_expire_days' => array('default_expire_days', t('Expiration period in days for imported (grid/network) content'), intval(get_config('system','default_expire_days')), t('0 for no expiration of imported content')), '$active_expire_days' => array('active_expire_days', t('Do not expire any posts which have comments less than this many days ago'), intval(get_config('system','active_expire_days',7)), ''), - '$sellpage' => array('site_sellpage', t('Public servers: Optional landing (marketing) webpage for new registrants'), get_config('system','sellpage',''), sprintf( t('Create this page first. Default is %s/register'),z_root())), '$first_page' => array('first_page', t('Page to display after creating a new channel'), get_config('system','workflow_channel_next','profiles'), t('Default: profiles')), - '$location' => array('site_location', t('Optional: site location'), get_config('system','site_location',''), t('Region or country')), - - '$form_security_token' => get_form_security_token("admin_site"), )); } + /** + * @brief Admin page site common post submit and ajax interaction + * @author hilmar runge + * @since 2020-02-04 + * Configure register office duty weekdays and hours + * Syntax: weekdays:hours [weekdays:hours] + * [.d[,d-d.]]]:hhmm-hhmm[,hhmm-hhmm...] + * ranges are between blanks, days are 1-7, where 1 = Monday + * hours are [h]hmm 3-4digit 24 clock values + * ie 0900-1200,1300-1800 for hours + * ie 1-2,4,5 for weekdays + * ie 1-2:900-1800 monday and tuesday open from 9 to 18h + * + * @var $register_duty is the input field from the admin -> site page + * @return the results are in the class vars $error, $msgbg and $jsoo + * $jsoo is + */ + + // 3-4 digit 24h clock regex + const regxTime34 = '/^(?:2[0-3]|[01][0-9]|[0-9])[0-5][0-9]$/'; + var $wdconst = array('','mo','tu','we','th','fr','sa','so'); + + // in + var $register_duty; + // intermediate + var $isajax; + // return + var $jsoo; + var $msgbg; + var $error = 0; + var $msgfg = ''; + + private function duty() { + + $aro=array_fill(1, 7, 0); + + if ($this->isajax) { + $op = (preg_match('/[a-z]{2,4}/', $_REQUEST['zarop'])) ? $_REQUEST['zarop'] : ''; + if ($op == 'zar083') { + $this->msgbg = 'Testmode:' . $this->eol . $this->msgbg; + } else { + killme(); + exit; + } + } + + $ranges = preg_split('/\s+/', $this->register_duty); + $this->msgbg .= '..ranges: ' . print_r(count($ranges),true) . $this->eol; + + foreach ($ranges as $rn => $range) { + list($ws,$hs,) = explode(':', $range); + + $ws ? $arw = explode( ',', $ws) : $arw=array(); + $this->msgbg .= ($rn+1).'.weekday ranges: ' . count($arw) . $this->eol; + // $this->msgbg .= print_r($arw,true); + $hs ? $arh = explode( ',', $hs) : $arh=array(); + $this->msgbg .= ($rn+1).'.hour ranges: ' . count($arh) . $this->eol; + + $this->msgbg .= ($rn+1).'.wdays: ' . ( $ws ? print_r($ws,true) : 'none') . ' : ' + . ' hours: ' . print_r($hs,true) . $this->eol; + + // several hs may belog to one wd + // aro[0] is tmp store + foreach ($arh as $hs) { + list($ho,$hc,) = explode( '-', $hs ); + + // no value forces open very early, and be sure having valid hhmm values + !$ho ? $ho = "0000" : ''; + !$hc ? $hc = "0000" : ''; // pseudo + if (preg_match(self::regxTime34, $ho) + && preg_match(self::regxTime34, $hc)) { + + // fix pseudo, allow no reverse range + $hc == "0000" || $ho > $hc ? $hc = "2400" : ''; + + $aro[0][$ho] = 0; + $aro[0][$hc] = 1; + + $this->msgbg .= ($ho ? ' .open:' . $ho : '') . ($hc ? ' close:' . $hc : '') .$this->eol; + } else { + $this->msgbg .= ' .' . t('Invalid 24h time value (hhmm/hmm)') . $this->eol; + $this->msgfg .= ' .ZAR0132E,' . t('Invalid 24h time value (hhmm/hmm)') . $this->eol; + $this->error++; + } + } + + // the weekday(s) values or ranges + foreach ($arw as $ds) { + $wd=explode('-', $ds); + array_key_exists("1", $wd) && $wd[1]=="" ? $wd[1] = "7" : ''; // a case 3- + array_key_exists("1", $wd) && $wd[0]=="" ? $wd[0] = "1" : ''; // a case -3 + !array_key_exists("1", $wd) ? $wd[1] = $wd[0] : ''; // a case 3 + if ($wd[0] > $wd[1]) continue; // reverse order will be ignored // a case 5-3 + if (preg_match('/^[1-7]{1}$/', $wd[0])) { + if (preg_match('/^[1-7]{1}$/', $wd[1])) { + // $this->msgbg .= print_r($wd,true); + for ($i=$wd[0]; $i<=$wd[1]; $i++) { + // take the tmp store for the selected day(s) + $aro[$i]=$aro[0]; + } + } + } + } + //$this->msgbg .= 'aro0: ' . print_r($aro,true) . $this->eol; // 4devels + // clear the tmp store + $aro[0]=array(); + } + // discart the tmp store + unset($aro[0]); + // not configured days close at the beginning 0000h + for ($i=1;$i<=7;$i++) { is_array($aro[$i]) ? '' : $aro[$i] = array("0000" => 1); } + // $this->msgbg .= 'aro: ' . print_r($aro,true) . $this->eol; // 4devels + + if ($this->isajax) { + // tell what we have + // $this->msgbg .= 'aro0: ' . print_r($aro,true) . $this->eol; // 4devels + $this->msgbg .= 'Duty time table:' . $this->eol; + foreach ($aro as $dow => $hrs) { + $this->msgbg .= ' ' . $this->wdconst[$dow] . ' '; + // $this->msgbg .= '**' . print_r($hrs,true); + foreach ($hrs as $h => $o) { + $this->msgbg .= ((!$o) ? $h . ':open' : $h . ':close') . ', '; + } + $this->msgbg = rtrim($this->msgbg, ', ') . $this->eol; + } + + $this->msgbg .= 'Generating 6 random times to check duty hours: ' . $this->eol; + // we only need some random dates from anyway in past or future + // because only the weekday and the clock is to test + for ($i=0; $i<6; $i++) { + $adow = rand(1, 7); // 1 to 7 (days) + $cdow = $this->wdconst[$adow]; + // below is the essential algo to verify a date (of format Hi) meets an open or closed condition + $t = date('Hi', ( rand(time(), 60*60*24+time()) ) ); + $how='close'; + foreach ($aro[$adow] as $o => $v) { + // $this->msgbg .= 'debug: ' . $o . ' gt ' . $t . ' / ' . $v . $this->eol; // 4devels + if ($o > $t) { + $how = ($v ? 'open' : 'close'); + break; + } + } + // now we know + $this->msgbg .= ' ' . $cdow . '.' . $t . '=' . $how . ', '; + } + $this->msgbg = rtrim($this->msgbg, ', ') . $this->eol; + } + + /* + //$jov1 = array( 'view1' => $aro, 'view2' => ''); + $jov2=array(); + foreach ($aro as $d => $ts) { + foreach ($ts as $t => $ft) { + $jov2['view2'][$ft][] = $d.$t; + //$ft=="1" && $t=="0000" ? $jov2['view2']["0"][] = $d."2400" : ''; + } + } + $this->msgbg .= print_r($jov2, true) . $this->eol; // 4devels + */ + + $this->joo = json_encode($aro); + // $this->msgbg .= $this->joo . $this->eol; // 4devels + // $this->msgbg .= print_r($aro, true) . $this->eol; // 4devels + $okko = (json_decode($this->joo, true) ? true : false); + if (!$okko) { + $this->msgbg .= 'ZAR0139D,json 4 duty KO crash' . $this->eol; + $this->msgfg .= 'ZAR0139D,json 4 duty KO crash' . $this->eol; + $this->error++; + } + return ; + } + + } diff --git a/Zotlabs/Module/Affinity.php b/Zotlabs/Module/Affinity.php index f0d99f1e7..0e163b89a 100644 --- a/Zotlabs/Module/Affinity.php +++ b/Zotlabs/Module/Affinity.php @@ -44,17 +44,14 @@ class Affinity extends \Zotlabs\Web\Controller { if(! local_channel()) return; - $desc = t('This app presents a slider control in your connection editor and also on your network page. The slider represents your degree of friendship (affinity) with each connection. It allows you to zoom in or out and display conversations from only your closest friends or everybody in your stream.'); - if(! Apps::system_app_installed(local_channel(),'Affinity Tool')) { + if(! Apps::system_app_installed(local_channel(), 'Affinity Tool')) { //Do not display any associated widgets at this point App::$pdl = ''; - - $o = '<b>' . t('Affinity Tool App') . ' (' . t('Not Installed') . '):</b><br>'; - $o .= $desc; - return $o; + $papp = Apps::get_papp('Affinity Tool'); + return Apps::app_render($papp, 'module'); } - $text = t('The numbers below represent the minimum and maximum slider default positions for your network/stream page as a percentage.'); + $text = t('The numbers below represent the minimum and maximum slider default positions for your network/stream page as a percentage.'); $content = '<div class="section-content-info-wrapper">' . $text . '</div>'; diff --git a/Zotlabs/Module/Appman.php b/Zotlabs/Module/Appman.php index 39689665e..26e564aa5 100644 --- a/Zotlabs/Module/Appman.php +++ b/Zotlabs/Module/Appman.php @@ -1,18 +1,18 @@ <?php /** @file */ -namespace Zotlabs\Module; +namespace Zotlabs\Module; -//require_once('include/apps.php'); - -use \Zotlabs\Lib as Zlib; +use App; +use Zotlabs\Lib\Apps; +use Zotlabs\Lib\Libsync; class Appman extends \Zotlabs\Web\Controller { function post() { - + if(! local_channel()) return; - + if($_POST['url']) { $arr = array( 'uid' => intval($_REQUEST['uid']), @@ -32,32 +32,72 @@ class Appman extends \Zotlabs\Web\Controller { 'sig' => escape_tags($_REQUEST['sig']), 'categories' => escape_tags($_REQUEST['categories']) ); - - $_REQUEST['appid'] = Zlib\Apps::app_install(local_channel(),$arr); - - if(Zlib\Apps::app_installed(local_channel(),$arr)) + + $_REQUEST['appid'] = Apps::app_install(local_channel(),$arr); + + if(Apps::app_installed(local_channel(),$arr)) info( t('App installed.') . EOL); goaway(z_root() . '/apps'); return; //not reached } - - - $papp = Zlib\Apps::app_decode($_POST['papp']); - + + + $papp = Apps::app_decode($_POST['papp']); + if(! is_array($papp)) { notice( t('Malformed app.') . EOL); return; } - + if($_POST['install']) { - Zlib\Apps::app_install(local_channel(),$papp); - if(Zlib\Apps::app_installed(local_channel(),$papp)) + Apps::app_install(local_channel(),$papp); + if(Apps::app_installed(local_channel(),$papp)) info( t('App installed.') . EOL); + +hz_syslog('install: ' . print_r($papp,true)); + + $sync = q("SELECT * FROM app WHERE app_channel = %d AND app_id = '%s' LIMIT 1", + intval(local_channel()), + dbesc($papp['guid']) + ); + + if (!$sync) { + return; + } + + if (intval($sync[0]['app_system'])) { + Libsync::build_sync_packet($uid, ['sysapp' => $sync]); + } + else { + Libsync::build_sync_packet($uid, ['app' => $sync]); + } + } - + if($_POST['delete']) { - Zlib\Apps::app_destroy(local_channel(),$papp); + + // Fetch the app for sync before it is deleted (if it is deletable)) + $sync = q("SELECT * FROM app WHERE app_channel = %d AND app_id = '%s' LIMIT 1", + intval(local_channel()), + dbesc($papp['guid']) + ); + + if (!$sync) { + return; + } + + Apps::app_destroy(local_channel(), $papp); + + // Now flag it deleted + $sync[0]['app_deleted'] = 1; + + if (intval($sync[0]['app_system'])) { + Libsync::build_sync_packet($uid, ['sysapp' => $sync]); + } + else { + Libsync::build_sync_packet($uid, ['app' => $sync]); + } } if($_POST['edit']) { @@ -65,37 +105,65 @@ class Appman extends \Zotlabs\Web\Controller { } if($_POST['feature']) { - Zlib\Apps::app_feature(local_channel(), $papp, $_POST['feature']); + Apps::app_feature(local_channel(), $papp, $_POST['feature']); + + $sync = q("SELECT * FROM app WHERE app_channel = %d AND app_id = '%s' LIMIT 1", + intval(local_channel()), + dbesc($papp['guid']) + ); + + if (intval($sync[0]['app_system'])) { + Libsync::build_sync_packet($uid, ['sysapp' => $sync]); + } + else { + Libsync::build_sync_packet($uid, ['app' => $sync]); + } } if($_POST['pin']) { - Zlib\Apps::app_feature(local_channel(), $papp, $_POST['pin']); + Apps::app_feature(local_channel(), $papp, $_POST['pin']); + + $sync = q("SELECT * FROM app WHERE app_channel = %d AND app_id = '%s' LIMIT 1", + intval(local_channel()), + dbesc($papp['guid']) + ); + + if (intval($sync[0]['app_system'])) { + Libsync::build_sync_packet($uid, ['sysapp' => $sync]); + } + else { + Libsync::build_sync_packet($uid, ['app' => $sync]); + } } - if($_SESSION['return_url']) + if($_POST['aj']) { + killme(); + } + + if($_SESSION['return_url']) goaway(z_root() . '/' . $_SESSION['return_url']); goaway(z_root() . '/apps'); - - + + } - - + + function get() { - + if(! local_channel()) { notice( t('Permission denied.') . EOL); return; } - $channel = \App::get_channel(); + $channel = App::get_channel(); if(argc() > 3) { if(argv(2) === 'moveup') { - Zlib\Apps::moveup(local_channel(),argv(1),argv(3)); + Apps::moveup(local_channel(),argv(1),argv(3)); } if(argv(2) === 'movedown') { - Zlib\Apps::movedown(local_channel(),argv(1),argv(3)); + Apps::movedown(local_channel(),argv(1),argv(3)); } goaway(z_root() . '/apporder'); } @@ -129,12 +197,12 @@ class Appman extends \Zotlabs\Web\Controller { } } - $embed = array('embed', t('Embed code'), Zlib\Apps::app_encode($app,true),'', 'onclick="this.select();"'); - + $embed = array('embed', t('Embed code'), Apps::app_encode($app,true),'', 'onclick="this.select();"'); + } - + return replace_macros(get_markup_template('app_create.tpl'), array( - + '$banner' => (($app) ? t('Edit App') : t('Create App')), '$app' => $app, '$guid' => (($app) ? $app['app_id'] : ''), @@ -154,7 +222,7 @@ class Appman extends \Zotlabs\Web\Controller { '$embed' => $embed, '$submit' => t('Submit') )); - + } - + } diff --git a/Zotlabs/Module/Apps.php b/Zotlabs/Module/Apps.php index 05b4495fc..77d1f2aec 100644 --- a/Zotlabs/Module/Apps.php +++ b/Zotlabs/Module/Apps.php @@ -9,7 +9,7 @@ class Apps extends \Zotlabs\Web\Controller { function get() { nav_set_selected('Apps'); - + if(argc() == 2 && argv(1) == 'edit') $mode = 'edit'; else @@ -18,9 +18,9 @@ class Apps extends \Zotlabs\Web\Controller { $available = ((argc() == 2 && argv(1) === 'available') ? true : false); $_SESSION['return_url'] = \App::$query_string; - + $apps = array(); - + if(local_channel()) { Zlib\Apps::import_system_apps(); $syslist = array(); @@ -37,9 +37,9 @@ class Apps extends \Zotlabs\Web\Controller { $syslist = Zlib\Apps::get_system_apps(true); usort($syslist,'Zotlabs\\Lib\\Apps::app_name_compare'); - + // logger('apps: ' . print_r($syslist,true)); - + foreach($syslist as $app) { $apps[] = Zlib\Apps::app_render($app,(($available) ? 'install' : $mode)); } @@ -53,7 +53,7 @@ class Apps extends \Zotlabs\Web\Controller { '$manage' => (($available) ? '' : t('Manage Apps')), '$create' => (($mode == 'edit') ? t('Create Custom App') : '') )); - + } - + } diff --git a/Zotlabs/Module/Article_edit.php b/Zotlabs/Module/Article_edit.php index 635b3ce2a..97c87f2ba 100644 --- a/Zotlabs/Module/Article_edit.php +++ b/Zotlabs/Module/Article_edit.php @@ -63,9 +63,9 @@ class Article_edit extends \Zotlabs\Web\Controller { if ($catsenabled){ $itm = fetch_post_tags($itm); - + $cats = get_terms_oftype($itm[0]['term'], TERM_CATEGORY); - + foreach ($cats as $cat) { if (strlen($category)) $category .= ', '; @@ -85,7 +85,6 @@ class Article_edit extends \Zotlabs\Web\Controller { $mimetype = $itm[0]['mimetype']; - $summary = (($itm[0]['summary']) ? '[summary]' . $itm[0]['summary'] . '[/summary]' . "\r\n" : ''); $content = $itm[0]['body']; $rp = 'articles/' . $channel['channel_address']; @@ -109,10 +108,11 @@ class Article_edit extends \Zotlabs\Web\Controller { 'ptyp' => $itm[0]['type'], 'mimeselect' => false, 'mimetype' => $itm[0]['mimetype'], - 'body' => $summary . undo_post_tagging($content), + 'body' => undo_post_tagging($content), 'post_id' => $post_id, 'visitor' => true, 'title' => htmlspecialchars($itm[0]['title'],ENT_COMPAT,'UTF-8'), + 'summary' => htmlspecialchars($itm[0]['summary'],ENT_COMPAT,'UTF-8'), 'placeholdertitle' => t('Title (optional)'), 'pagetitle' => $card_title, 'profile_uid' => (intval($channel['channel_id'])), diff --git a/Zotlabs/Module/Articles.php b/Zotlabs/Module/Articles.php index 3f726ebb9..0db098a31 100644 --- a/Zotlabs/Module/Articles.php +++ b/Zotlabs/Module/Articles.php @@ -15,7 +15,7 @@ require_once('include/opengraph.php'); class Articles extends Controller { function init() { - + if(argc() > 1) $which = argv(1); @@ -28,13 +28,13 @@ class Articles extends Controller { return; } } - + profile_load($which); - + } - + function get($update = 0, $load = false) { - + if(observer_prohibited(true)) { return login(); } @@ -48,15 +48,13 @@ class Articles extends Controller { if(! Apps::system_app_installed(App::$profile_uid, 'Articles')) { //Do not display any associated widgets at this point App::$pdl = ''; - - $o = '<b>' . t('Articles App') . ' (' . t('Not Installed') . '):</b><br>'; - $o .= t('Create interactive articles'); - return $o; + $papp = Apps::get_papp('Articles'); + return Apps::app_render($papp, 'module'); } nav_set_selected('Articles'); - head_add_link([ + head_add_link([ 'rel' => 'alternate', 'type' => 'application/json+oembed', 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . App::$query_string), @@ -65,7 +63,7 @@ class Articles extends Controller { $category = (($_REQUEST['cat']) ? escape_tags(trim($_REQUEST['cat'])) : ''); - + if($category) { $sql_extra2 .= protect_sprintf(term_item_parent_query(App::$profile['profile_uid'],'item', $category, TERM_CATEGORY)); } @@ -74,24 +72,24 @@ class Articles extends Controller { $datequery2 = ((x($_GET,'dbegin') && is_a_date_arg($_GET['dbegin'])) ? notags($_GET['dbegin']) : ''); $which = argv(1); - + $selected_card = ((argc() > 2) ? argv(2) : ''); $_SESSION['return_url'] = App::$query_string; - + $uid = local_channel(); $owner = App::$profile_uid; $observer = App::get_observer(); - + $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); - + if(! perm_is_allowed($owner,$ob_hash,'view_pages')) { notice( t('Permission denied.') . EOL); return; } - + $is_owner = ($uid && $uid == $owner); - + $channel = channelx_by_n($owner); if($channel) { @@ -105,7 +103,7 @@ class Articles extends Controller { else { $channel_acl = [ 'allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '' ]; } - + if(perm_is_allowed($owner,$ob_hash,'write_pages')) { @@ -114,16 +112,15 @@ class Articles extends Controller { 'webpage' => ITEM_TYPE_ARTICLE, 'is_owner' => true, 'content_label' => t('Add Article'), - 'button' => t('Create'), + 'button' => t('Save'), 'nickname' => $channel['channel_address'], - 'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] + 'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'), - 'acl' => (($is_owner) ? populate_acl($channel_acl, false, + 'acl' => (($is_owner) ? populate_acl($channel_acl, false, PermissionDescription::fromGlobalPermission('view_pages')) : ''), 'permissions' => $channel_acl, 'showacl' => (($is_owner) ? true : false), 'visitor' => true, - 'body' => '[summary][/summary]', 'hide_location' => false, 'hide_voting' => false, 'profile_uid' => intval($owner), @@ -147,12 +144,12 @@ class Articles extends Controller { else { $editor = ''; } - + $itemspage = get_pconfig(local_channel(),'system','itemspage'); App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 10)); $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(App::$pager['itemspage']), intval(App::$pager['start'])); - + $sql_extra = item_permissions_sql($owner); $sql_item = ''; @@ -176,8 +173,8 @@ class Articles extends Controller { $sql_extra2 .= " and item.item_thread_top != 0 "; } - $r = q("select * from item - where item.uid = %d and item_type = %d + $r = q("select * from item + where item.uid = %d and item_type = %d $sql_extra $sql_extra2 $sql_item order by item.created desc $pager_sql", intval($owner), intval(ITEM_TYPE_ARTICLE) @@ -214,7 +211,7 @@ class Articles extends Controller { opengraph_add_meta((! empty($items) ? $r[0] : []), $channel); $mode = 'articles'; - + if(get_pconfig(local_channel(),'system','articles_list_mode') && (! $selected_card)) $page_mode = 'pager_list'; else diff --git a/Zotlabs/Module/Attach.php b/Zotlabs/Module/Attach.php index 490d5edd0..5f5779b51 100644 --- a/Zotlabs/Module/Attach.php +++ b/Zotlabs/Module/Attach.php @@ -1,61 +1,188 @@ <?php + namespace Zotlabs\Module; +use ZipArchive; +use Zotlabs\Web\Controller; +use Zotlabs\Lib\Verify; + require_once('include/security.php'); require_once('include/attach.php'); +class Attach extends Controller { -class Attach extends \Zotlabs\Web\Controller { + function post() { - function init() { - - if(argc() < 2) { - notice( t('Item not available.') . EOL); + $attach_ids = ((x($_REQUEST, 'attach_ids')) ? $_REQUEST['attach_ids'] : []); + $attach_path = ((x($_REQUEST, 'attach_path')) ? $_REQUEST['attach_path'] : ''); + $channel_id = ((x($_REQUEST, 'channel_id')) ? intval($_REQUEST['channel_id']) : 0); + $channel = channelx_by_n($channel_id); + + if (!$channel) { + notice(t('Channel not found.') . EOL); return; } - - $r = attach_by_hash(argv(1),get_observer_hash(),((argc() > 2) ? intval(argv(2)) : 0)); - - if(! $r['success']) { - notice( $r['message'] . EOL); + + $strip_str = '/cloud/' . $channel['channel_address'] . '/'; + $count = strlen($strip_str); + $attach_path = substr($attach_path, $count); + + if ($attach_ids) { + + $zip_dir = 'store/[data]/' . $channel['channel_address'] . '/tmp'; + if (!is_dir($zip_dir)) + mkdir($zip_dir, STORAGE_DEFAULT_PERMISSIONS, true); + + $token = random_string(32); + + $zip_file = 'download_' . $token . '.zip'; + $zip_path = $zip_dir . '/' . $zip_file; + + $zip = new ZipArchive(); + + if ($zip->open($zip_path, ZipArchive::CREATE) === true) { + + $zip_filename = self::zip_archive_handler($zip, $attach_ids, $attach_path); + + $zip->close(); + + $meta = [ + 'zip_filename' => $zip_filename, + 'zip_path' => $zip_path + ]; + + Verify::create('zip_token', 0, $token, json_encode($meta)); + + json_return_and_die([ + 'success' => true, + 'token' => $token + ]); + + } + } + } + + function get() { + + if (argc() < 2) { + notice(t('Item not available.') . EOL); return; } - + + $token = ((x($_REQUEST, 'token')) ? $_REQUEST['token'] : ''); + + if (argv(1) === 'download') { + $meta = Verify::get_meta('zip_token', 0, $token); + + if (!$meta) + killme(); + + $meta = json_decode($meta, true); + + header('Content-Type: application/zip'); + header('Content-Disposition: attachment; filename="' . $meta['zip_filename'] . '"'); + header('Content-Length: ' . filesize($meta['zip_path'])); + + $istream = fopen($meta['zip_path'], 'rb'); + $ostream = fopen('php://output', 'wb'); + if ($istream && $ostream) { + pipe_streams($istream, $ostream); + fclose($istream); + fclose($ostream); + } + + unlink($meta['zip_path']); + killme(); + } + + $r = attach_by_hash(argv(1), get_observer_hash(), ((argc() > 2) ? intval(argv(2)) : 0)); + + if (!$r['success']) { + notice($r['message'] . EOL); + return; + } + $c = q("select channel_address from channel where channel_id = %d limit 1", intval($r['data']['uid']) ); - - if(! $c) + + if (!$c) return; - - - $unsafe_types = array('text/html','text/css','application/javascript'); - - if(in_array($r['data']['filetype'],$unsafe_types) && (! channel_codeallowed($r['data']['uid']))) { - header('Content-type: text/plain'); + + $unsafe_types = array('text/html', 'text/css', 'application/javascript'); + + if (in_array($r['data']['filetype'], $unsafe_types) && (!channel_codeallowed($r['data']['uid']))) { + header('Content-Type: text/plain'); } else { - header('Content-type: ' . $r['data']['filetype']); - } - - header('Content-disposition: attachment; filename="' . $r['data']['filename'] . '"'); - if(intval($r['data']['os_storage'])) { - $fname = dbunescbin($r['data']['content']); - if(strpos($fname,'store') !== false) - $istream = fopen($fname,'rb'); + header('Content-Type: ' . $r['data']['filetype']); + } + + header('Content-Disposition: attachment; filename="' . $r['data']['filename'] . '"'); + if (intval($r['data']['os_storage'])) { + $fname = $r['data']['content']; + if (strpos($fname, 'store') !== false) + $istream = fopen($fname, 'rb'); else - $istream = fopen('store/' . $c[0]['channel_address'] . '/' . $fname,'rb'); - $ostream = fopen('php://output','wb'); - if($istream && $ostream) { - pipe_streams($istream,$ostream); + $istream = fopen('store/' . $c[0]['channel_address'] . '/' . $fname, 'rb'); + $ostream = fopen('php://output', 'wb'); + if ($istream && $ostream) { + pipe_streams($istream, $ostream); fclose($istream); fclose($ostream); } } else - echo dbunescbin($r['data']['content']); + echo $r['data']['content']; killme(); - + } - + + public function zip_archive_handler($zip, $attach_ids, $attach_path, $pass = 1) { + + $observer_hash = get_observer_hash(); + $single = ((count($attach_ids) == 1) ? true : false); + $download_name = 'download.zip'; + + foreach ($attach_ids as $attach_id) { + + $r = attach_by_id($attach_id, $observer_hash); + + if (!$r['success']) { + continue; + } + + if ($r['data']['is_dir'] && $single && $pass === 1) + $download_name = $r['data']['filename'] . '.zip'; + + $zip_path = $r['data']['display_path']; + + if ($attach_path) { + $strip_str = $attach_path . '/'; + $count = strlen($strip_str); + $zip_path = substr($r['data']['display_path'], $count); + } + + if ($r['data']['is_dir']) { + $zip->addEmptyDir($zip_path); + + $d = q("SELECT id FROM attach WHERE folder = '%s'", + dbesc($r['data']['hash']) + ); + + $attach_ids = ids_to_array($d); + self::zip_archive_handler($zip, $attach_ids, $attach_path, $pass++); + } + else { + $file_path = $r['data']['content']; + $zip->addFile($file_path, $zip_path); + // compressing can be ressource intensive - just store the data + $zip->setCompressionName($zip_path, ZipArchive::CM_STORE); + } + + } + + return $download_name; + } + } diff --git a/Zotlabs/Module/Attach_edit.php b/Zotlabs/Module/Attach_edit.php new file mode 100644 index 000000000..5880d8f13 --- /dev/null +++ b/Zotlabs/Module/Attach_edit.php @@ -0,0 +1,203 @@ +<?php +namespace Zotlabs\Module; +/** + * @file Zotlabs/Module/Attach_edit.php + * + */ + +use App; +use Zotlabs\Web\Controller; +use Zotlabs\Lib\Libsync; +use Zotlabs\Access\AccessList; + +class Attach_edit extends Controller { + + function post() { + + if (!local_channel() && !remote_channel()) { + return; + } + + $attach_ids = ((x($_POST, 'attach_ids')) ? $_POST['attach_ids'] : []); + $attach_id = ((x($_POST, 'attach_id')) ? intval($_POST['attach_id']) : 0); + $channel_id = ((x($_POST, 'channel_id')) ? intval($_POST['channel_id']) : 0); + $dnd = ((x($_POST, 'dnd')) ? intval($_POST['dnd']) : 0); + $permissions = ((x($_POST, 'permissions')) ? intval($_POST['permissions']) : 0); + $return_path = ((x($_POST, 'return_path')) ? notags($_POST['return_path']) : 'cloud'); + $delete = ((x($_POST, 'delete')) ? intval($_POST['delete']) : 0); + $newfolder = ((x($_POST, 'newfolder_' . $attach_id)) ? notags($_POST['newfolder_' . $attach_id]) : ''); + if(! $newfolder) + $newfolder = ((x($_POST, 'newfolder')) ? notags($_POST['newfolder']) : ''); + $newfilename = ((x($_POST, 'newfilename_' . $attach_id)) ? notags($_POST['newfilename_' . $attach_id]) : ''); + $recurse = ((x($_POST, 'recurse_' . $attach_id)) ? intval($_POST['recurse_' . $attach_id]) : 0); + if(! $recurse) + $recurse = ((x($_POST, 'recurse')) ? intval($_POST['recurse']) : 0); + $notify = ((x($_POST, 'notify_edit_' . $attach_id)) ? intval($_POST['notify_edit_' . $attach_id]) : 0); + $copy = ((x($_POST, 'copy_' . $attach_id)) ? intval($_POST['copy_' . $attach_id]) : 0); + if(! $copy) + $copy = ((x($_POST, 'copy')) ? intval($_POST['copy']) : 0); + + $categories = ((x($_POST, 'categories_' . $attach_id)) ? notags($_POST['categories_' . $attach_id]) : ''); + if(! $categories) + $categories = ((x($_POST, 'categories')) ? notags($_POST['categories']) : ''); + + if($attach_id) + $attach_ids[] = $attach_id; + + $single = ((count($attach_ids) === 1) ? true : false); + + $channel = channelx_by_n($channel_id); + + if (! $channel) { + notice(t('Channel not found.') . EOL); + return; + } + + $nick = $channel['channel_address']; + $observer = App::get_observer(); + $observer_hash = (($observer) ? $observer['xchan_hash'] : ''); + $is_owner = ((local_channel() == $channel_id) ? true : false); + + $ids_str = implode(',', $attach_ids); + + $r = q("SELECT id, uid, hash, creator, folder, filename, is_photo, is_dir FROM attach WHERE id IN ( %s ) AND uid = %d", + dbesc($ids_str), + intval($channel_id) + ); + + if (! $r) { + notice(t('File not found.') . EOL); + return; + } + + foreach ($r as $rr) { + $actions_done = ''; + $attach_id = $rr['id']; + $resource = $rr['hash']; + $creator = $rr['creator']; + $folder = $rr['folder']; + $filename = $rr['filename']; + $is_photo = intval($rr['is_photo']); + $is_dir = intval($rr['is_dir']); + $admin_delete = false; + + $is_creator = (($creator == $observer_hash) ? true : false); + $move = ((! $copy && ($folder !== $newfolder || (($single) ? $filename !== $newfilename : false))) ? true : false); + + $perms = get_all_perms($channel_id, $observer_hash); + + if (! ($perms['view_storage'] || is_site_admin())) { + notice( t('Permission denied.') . EOL); + continue; + } + + if (! $perms['write_storage']) { + if (is_site_admin()) { + $admin_delete = true; + } + else { + notice( t('Permission denied.') . EOL); + continue; + } + } + + if (!$is_owner && !$admin_delete) { + if(! $is_creator) { + notice( t('Permission denied.') . EOL); + continue; + } + } + + if ($delete) { + attach_delete($channel_id, $resource, $is_photo); + $actions_done .= 'delete,'; + } + + if ($copy) { + if($is_dir && $resource == $newfolder) { + notice( t('Can not copy folder into itself.') . EOL); + continue; + } + $x = attach_copy($channel_id, $resource, $newfolder, (($single) ? $newfilename : '')); + if ($x['success']) + $resource = $x['resource_id']; + + $actions_done .= 'copy,'; + + } + + if ($move) { + if($is_dir && $resource == $newfolder) { + notice( sprintf(t('Can not move folder "%s" into itself.'), $filename) . EOL); + continue; + } + $x = attach_move($channel_id, $resource, $newfolder, (($single) ? $newfilename : '')); + + $actions_done .= 'move,'; + + } + + if(! $delete && ! $dnd) { + if ($single || (! $single && $categories)) { + q("DELETE FROM term WHERE uid = %d AND oid = %d AND otype = %d", + intval($channel_id), + intval($attach_id), + intval(TERM_OBJ_FILE) + ); + $cat = explode(',', $categories); + if ($cat) { + foreach($cat as $term) { + $term = trim(escape_tags($term)); + if ($term) { + $term_link = z_root() . '/cloud/' . $nick . '/?cat=' . $term; + store_item_tag($channel_id, $attach_id, TERM_OBJ_FILE, TERM_CATEGORY, $term, $term_link); + } + } + $actions_done .= 'cat_add,'; + } + } + else { + q("DELETE FROM term WHERE uid = %d AND oid = %d AND otype = %d", + intval($channel_id), + intval($attach_id), + intval(TERM_OBJ_FILE) + ); + $actions_done .= 'cat_remove,'; + } + + if ($is_owner && ($single || (! $single && $permissions))) { + $acl = new AccessList($channel); + $acl->set_from_array($_REQUEST); + $x = $acl->get(); + + attach_change_permissions($channel_id, $resource, $x['allow_cid'], $x['allow_gid'], $x['deny_cid'], $x['deny_gid'], $recurse, true); + $actions_done .= 'permissions,'; + + if ($notify) { + attach_store_item($channel, $observer, $resource); + $actions_done .= 'notify,'; + } + } + } + + if (! $admin_delete && $actions_done) { + $sync = attach_export_data($channel, $resource, (($delete) ? true : false)); + + if ($sync) { + Libsync::build_sync_packet($channel_id, ['file' => [$sync]]); + } + } + + logger('attach_edit: ' . $actions_done); + + } + + if($dnd || $delete) { + json_return_and_die([ 'success' => true ]); + } + + goaway($return_path); + + } + +} diff --git a/Zotlabs/Module/Authtest.php b/Zotlabs/Module/Authtest.php index 239ae3bdb..d85af09dc 100644 --- a/Zotlabs/Module/Authtest.php +++ b/Zotlabs/Module/Authtest.php @@ -1,41 +1,38 @@ <?php namespace Zotlabs\Module; -require_once('include/zot.php'); - - class Authtest extends \Zotlabs\Web\Controller { function get() { - - + + $auth_success = false; $o .= '<h3>Magic-Auth Diagnostic</h3>'; - + if(! local_channel()) { notice( t('Permission denied.') . EOL); return $o; } - + $o .= '<form action="authtest" method="get">'; $o .= 'Target URL: <input type="text" style="width: 250px;" name="dest" value="' . $_GET['dest'] .'" />'; - $o .= '<input type="submit" name="submit" value="Submit" /></form>'; - + $o .= '<input type="submit" name="submit" value="Submit" /></form>'; + $o .= '<br /><br />'; - + if(x($_GET,'dest')) { if(strpos($_GET['dest'],'@')) { $_GET['dest'] = $_REQUEST['dest'] = 'https://' . substr($_GET['dest'],strpos($_GET['dest'],'@')+1) . '/channel/' . substr($_GET['dest'],0,strpos($_GET['dest'],'@')); } - + $_REQUEST['test'] = 1; $mod = new Magic(); $x = $mod->init($a); $o .= 'Local Setup returns: ' . print_r($x,true); - - - + + + if($x['url']) { $z = z_fetch_url($x['url'] . '&test=1'); if($z['success']) { @@ -50,12 +47,12 @@ class Authtest extends \Zotlabs\Web\Controller { $o .= 'fetch url failure.' . print_r($z,true); } } - + if(! $auth_success) $o .= 'Authentication Failed!' . EOL; } - + return str_replace("\n",'<br />',$o); } - + } diff --git a/Zotlabs/Module/Bookmarks.php b/Zotlabs/Module/Bookmarks.php index 4b4929c65..659884fed 100644 --- a/Zotlabs/Module/Bookmarks.php +++ b/Zotlabs/Module/Bookmarks.php @@ -16,33 +16,33 @@ class Bookmarks extends \Zotlabs\Web\Controller { nav_set_selected('Bookmarks'); - $item_id = intval($_REQUEST['item']); - $burl = trim($_REQUEST['burl']); - + $item_id = (isset($_REQUEST['item']) ? $_REQUEST['item'] : false); + $burl = (isset($_REQUEST['burl']) ? trim($_REQUEST['burl']) : ''); + if(! $item_id) return; - + $u = \App::get_channel(); - + $item_normal = item_normal(); - + $i = q("select * from item where id = %d and uid = %d $item_normal limit 1", intval($item_id), intval(local_channel()) ); - + if(! $i) return; - + $i = fetch_post_tags($i); - + $item = $i[0]; - - $terms = get_terms_oftype($item['term'],TERM_BOOKMARK); - + + $terms = (x($item, 'term') ? get_terms_oftype($item['term'],TERM_BOOKMARK) : false); + if($terms) { require_once('include/bookmarks.php'); - + $s = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($item['author_xchan']) ); @@ -58,13 +58,13 @@ class Bookmarks extends \Zotlabs\Web\Controller { } else bookmark_add($u,$s[0],$t,$item['item_private']); - + info( t('Bookmark added') . EOL); } } killme(); } - + function get() { if(! local_channel()) { notice( t('Permission denied.') . EOL); @@ -74,49 +74,47 @@ class Bookmarks extends \Zotlabs\Web\Controller { if(! Apps::system_app_installed(local_channel(), 'Bookmarks')) { //Do not display any associated widgets at this point App::$pdl = ''; - - $o = '<b>' . t('Bookmarks App') . ' (' . t('Not Installed') . '):</b><br>'; - $o .= t('Bookmark links from posts and manage them'); - return $o; + $papp = Apps::get_papp('Bookmarks'); + return Apps::app_render($papp, 'module'); } - + require_once('include/menu.php'); require_once('include/conversation.php'); - + $channel = \App::get_channel(); - + $o = ''; - + $o .= '<div class="generic-content-wrapper-styled">'; - - $o .= '<h3>' . t('My Bookmarks') . '</h3>'; - + + $o .= '<h3>' . t('Bookmarks') . '</h3>'; + $x = menu_list(local_channel(),'',MENU_BOOKMARK); - + if($x) { foreach($x as $xx) { $y = menu_fetch($xx['menu_name'],local_channel(),get_observer_hash()); $o .= menu_render($y,'',true); } } - + $o .= '<h3>' . t('My Connections Bookmarks') . '</h3>'; - - + + $x = menu_list(local_channel(),'',MENU_SYSTEM|MENU_BOOKMARK); - + if($x) { foreach($x as $xx) { $y = menu_fetch($xx['menu_name'],local_channel(),get_observer_hash()); $o .= menu_render($y,'',true); } } - + $o .= '</div>'; - + return $o; - + } - - + + } diff --git a/Zotlabs/Module/Cal.php b/Zotlabs/Module/Cal.php index 07bee38bd..329150424 100644 --- a/Zotlabs/Module/Cal.php +++ b/Zotlabs/Module/Cal.php @@ -19,47 +19,45 @@ class Cal extends Controller { if(observer_prohibited()) { return; } - + if(argc() > 1) { $nick = argv(1); - + profile_load($nick); - + $channelx = channelx_by_nick($nick); - + if(! $channelx) { notice( t('Channel not found.') . EOL); return; } - + App::$data['channel'] = $channelx; - + $observer = App::get_observer(); App::$data['observer'] = $observer; - - $observer_xchan = (($observer) ? $observer['xchan_hash'] : ''); - + head_set_icon(App::$data['channel']['xchan_photo_s']); - + App::$page['htmlhead'] .= "<script> var profile_uid = " . ((App::$data['channel']) ? App::$data['channel']['channel_id'] : 0) . "; </script>" ; - + } - + return; } - - - + + + function get() { - + if(observer_prohibited()) { return; } - + $channel = App::$data['channel']; // since we don't currently have an event permission - use the stream permission - + if(! perm_is_allowed($channel['channel_id'], get_observer_hash(), 'view_stream')) { notice( t('Permissions denied.') . EOL); return; @@ -78,10 +76,10 @@ class Cal extends Controller { if(! perm_is_allowed($channel['channel_id'], get_observer_hash(), 'view_contacts') || App::$profile['hide_friends']) $sql_extra .= " and etype != 'birthday' "; - + $first_day = feature_enabled($channel['channel_id'], 'cal_first_day'); $first_day = (($first_day) ? $first_day : 0); - + $start = ''; $finish = ''; @@ -89,7 +87,7 @@ class Cal extends Controller { if (x($_GET,'start')) $start = $_GET['start']; if (x($_GET,'end')) $finish = $_GET['end']; } - + $start = datetime_convert('UTC','UTC',$start); $finish = datetime_convert('UTC','UTC',$finish); $adjust_start = datetime_convert('UTC', date_default_timezone_get(), $start); @@ -109,10 +107,10 @@ class Cal extends Controller { // Noting this for now - it will need to be fixed here and in Friendica. // Ultimately the finish date shouldn't be involved in the query. $r = q("SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan, item.id as item_id - from event left join item on event.event_hash = item.resource_id - where item.resource_type = 'event' and event.uid = %d and event.uid = item.uid - AND (( event.adjust = 0 AND ( event.dtend >= '%s' or event.nofinish = 1 ) AND event.dtstart <= '%s' ) - OR ( event.adjust = 1 AND ( event.dtend >= '%s' or event.nofinish = 1 ) AND event.dtstart <= '%s' )) + from event left join item on event.event_hash = item.resource_id + where item.resource_type = 'event' and event.uid = %d and event.uid = item.uid + AND (( event.adjust = 0 AND ( event.dtend >= '%s' or event.nofinish = 1 ) AND event.dtstart <= '%s' ) + OR ( event.adjust = 1 AND ( event.dtend >= '%s' or event.nofinish = 1 ) AND event.dtstart <= '%s' )) $sql_extra", intval($channel['channel_id']), dbesc($start), @@ -121,7 +119,7 @@ class Cal extends Controller { dbesc($adjust_finish) ); } - + if($r) { xchan_query($r); $r = fetch_post_tags($r,true); @@ -129,20 +127,16 @@ class Cal extends Controller { } $events = []; - + if($r) { foreach($r as $rr) { - $tz = get_iconfig($rr, 'event', 'timezone'); - if(! $tz) - $tz = 'UTC'; - - $start = (($rr['adjust']) ? datetime_convert($tz, date_default_timezone_get(), $rr['dtstart'], 'c') : datetime_convert('UTC', 'UTC', $rr['dtstart'], 'c')); + $start = (($rr['adjust']) ? datetime_convert('UTC', date_default_timezone_get(), $rr['dtstart'], 'c') : datetime_convert('UTC', 'UTC', $rr['dtstart'], 'c')); if ($rr['nofinish']){ $end = null; } else { - $end = (($rr['adjust']) ? datetime_convert($tz, date_default_timezone_get(), $rr['dtend'], 'c') : datetime_convert('UTC', 'UTC', $rr['dtend'], 'c')); + $end = (($rr['adjust']) ? datetime_convert('UTC', date_default_timezone_get(), $rr['dtend'], 'c') : datetime_convert('UTC', 'UTC', $rr['dtend'], 'c')); } $html = ''; @@ -151,6 +145,10 @@ class Cal extends Controller { $html = format_event_html($rr); } + $tz = get_iconfig($rr, 'event', 'timezone'); + if(! $tz) + $tz = 'UTC'; + $events[] = array( 'calendar_id' => 'channel_calendar', 'rw' => true, @@ -159,10 +157,10 @@ class Cal extends Controller { 'timezone' => $tz, 'start'=> $start, 'end' => $end, - 'drop' => $drop, + 'drop' => false, 'allDay' => (($rr['adjust']) ? 0 : 1), 'title' => html_entity_decode($rr['summary'], ENT_COMPAT, 'UTF-8'), - 'editable' => $edit ? true : false, + 'editable' => false, 'item' => $rr, 'plink' => [$rr['plink'], t('Link to source')], 'description' => html_entity_decode($rr['description'], ENT_COMPAT, 'UTF-8'), @@ -180,7 +178,7 @@ class Cal extends Controller { echo json_encode($events); killme(); } - + if (x($_GET,'id')) { $o = replace_macros(get_markup_template("cal_event.tpl"), [ '$events' => $events @@ -205,14 +203,14 @@ class Cal extends Controller { '$prev' => t('Previous'), '$next' => t('Next'), '$today' => t('Today'), - '$title' => $title, - '$dtstart' => $dtstart, - '$dtend' => $dtend, + '$title' => '', + '$dtstart' => '', + '$dtend' => '', '$nick' => $nick ]); return $o; - + } - + } diff --git a/Zotlabs/Module/Card_edit.php b/Zotlabs/Module/Card_edit.php index e01e70fdb..c57a0f043 100644 --- a/Zotlabs/Module/Card_edit.php +++ b/Zotlabs/Module/Card_edit.php @@ -63,9 +63,9 @@ class Card_edit extends \Zotlabs\Web\Controller { if ($catsenabled){ $itm = fetch_post_tags($itm); - + $cats = get_terms_oftype($itm[0]['term'], TERM_CATEGORY); - + foreach ($cats as $cat) { if (strlen($category)) $category .= ', '; @@ -114,6 +114,7 @@ class Card_edit extends \Zotlabs\Web\Controller { 'post_id' => $post_id, 'visitor' => true, 'title' => htmlspecialchars($itm[0]['title'],ENT_COMPAT,'UTF-8'), + 'summary' => htmlspecialchars($itm[0]['summary'],ENT_COMPAT,'UTF-8'), 'placeholdertitle' => t('Title (optional)'), 'pagetitle' => $card_title, 'profile_uid' => (intval($channel['channel_id'])), diff --git a/Zotlabs/Module/Cards.php b/Zotlabs/Module/Cards.php index c44f7942b..b71af6044 100644 --- a/Zotlabs/Module/Cards.php +++ b/Zotlabs/Module/Cards.php @@ -47,10 +47,8 @@ class Cards extends Controller { if(! Apps::system_app_installed(App::$profile_uid, 'Cards')) { //Do not display any associated widgets at this point App::$pdl = ''; - - $o = '<b>' . t('Cards App') . ' (' . t('Not Installed') . '):</b><br>'; - $o .= t('Create personal planning cards'); - return $o; + $papp = Apps::get_papp('Cards'); + return Apps::app_render($papp, 'module'); } nav_set_selected('Cards'); @@ -110,7 +108,7 @@ class Cards extends Controller { 'webpage' => ITEM_TYPE_CARD, 'is_owner' => true, 'content_label' => t('Add Card'), - 'button' => t('Create'), + 'button' => t('Save'), 'nickname' => $channel['channel_address'], 'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'), diff --git a/Zotlabs/Module/Cdav.php b/Zotlabs/Module/Cdav.php index d7d57664c..599552545 100644 --- a/Zotlabs/Module/Cdav.php +++ b/Zotlabs/Module/Cdav.php @@ -5,6 +5,9 @@ use App; use Zotlabs\Lib\Apps; use Zotlabs\Web\Controller; use Zotlabs\Web\HTTPSig; +use Zotlabs\Lib\Libzot; +use Zotlabs\Lib\Libsync; + require_once('include/event.php'); @@ -47,11 +50,12 @@ class Cdav extends Controller { if($sigblock) { $keyId = str_replace('acct:','',$sigblock['keyId']); if($keyId) { - $r = q("select * from hubloc where hubloc_addr = '%s' limit 1", + $r = q("select * from hubloc where hubloc_id_url = '%s'", dbesc($keyId) ); if($r) { - $c = channelx_by_hash($r[0]['hubloc_hash']); + $r = Libzot::zot_record_preferred($r); + $c = channelx_by_hash($r['hubloc_hash']); if($c) { $a = q("select * from account where account_id = %d limit 1", intval($c['channel_account_id']) @@ -131,7 +135,7 @@ class Cdav extends Controller { $auth = new \Zotlabs\Storage\BasicAuth(); $auth->setRealm(ucfirst(\Zotlabs\Lib\System::get_platform_name()) . 'CalDAV/CardDAV'); - if (local_channel()) { + if(local_channel()) { logger('loggedin'); @@ -149,36 +153,39 @@ class Cdav extends Controller { $auth->observer = $channel['channel_hash']; $principalUri = 'principals/' . $channel['channel_address']; - if(!cdav_principal($principalUri)) { + if(! cdav_principal($principalUri)) { $this->activate($pdo, $channel); - if(!cdav_principal($principalUri)) { + if(! cdav_principal($principalUri)) { return; } } } - + // Track CDAV updates from remote clients - $httpmethod = $_SERVER['REQUEST_METHOD']; + $httpmethod = $_SERVER['REQUEST_METHOD']; if($httpmethod === 'PUT' || $httpmethod === 'DELETE') { + $channel = channelx_by_nick(argv(2)); + $principalUri = 'principals/' . $channel['channel_address']; $httpuri = $_SERVER['REQUEST_URI']; logger("debug: method: " . $httpmethod, LOGGER_DEBUG); logger("debug: uri: " . $httpuri, LOGGER_DEBUG); - if(strpos($httpuri, 'cdav/addressbooks')) { + if(strpos($httpuri, 'cdav/addressbooks') !== false) { $sync = 'addressbook'; $cdavtable = 'addressbooks'; } - elseif(strpos($httpuri, 'cdav/calendars')) { + elseif(strpos($httpuri, 'cdav/calendars') !== false) { $sync = 'calendar'; $cdavtable = 'calendarinstances'; } - else + else { $sync = false; + } if($sync) { @@ -187,37 +194,36 @@ class Cdav extends Controller { logger("debug: body: " . $httpbody, LOGGER_DEBUG); - if($x = get_cdav_id($principalUri, explode("/", $httpuri)[4], $cdavtable)) { + if($x = get_cdav_id($principalUri, argv(3), $cdavtable)) { $cdavdata = $this->get_cdav_data($x['id'], $cdavtable); - $etag = (isset($_SERVER['HTTP_IF_MATCH']) ? $_SERVER['HTTP_IF_MATCH'] : false); - + // delete - if($httpmethod === 'DELETE' && $cdavdata['etag'] == $etag) - build_sync_packet($channel['channel_id'], [ + if($httpmethod === 'DELETE' && $cdavdata['etag'] == $etag) { + Libsync::build_sync_packet($channel['channel_id'], [ $sync => [ 'action' => 'delete_card', 'uri' => $cdavdata['uri'], 'carduri' => $uri ] ]); + } else { - if($etag) { + if($etag && $cdavdata['etag'] !== $etag) { // update - if($cdavdata['etag'] !== $etag) - build_sync_packet($channel['channel_id'], [ - $sync => [ - 'action' => 'update_card', - 'uri' => $cdavdata['uri'], - 'carduri' => $uri, - 'card' => $httpbody - ] - ]); + Libsync::build_sync_packet($channel['channel_id'], [ + $sync => [ + 'action' => 'update_card', + 'uri' => $cdavdata['uri'], + 'carduri' => $uri, + 'card' => $httpbody + ] + ]); } else { // new - build_sync_packet($channel['channel_id'], [ + Libsync::build_sync_packet($channel['channel_id'], [ $sync => [ 'action' => 'import', 'uri' => $cdavdata['uri'], @@ -231,7 +237,6 @@ class Cdav extends Controller { } } - $principalBackend = new \Sabre\DAVACL\PrincipalBackend\PDO($pdo); $carddavBackend = new \Sabre\CardDAV\Backend\PDO($pdo); $caldavBackend = new \Sabre\CalDAV\Backend\PDO($pdo); @@ -264,7 +269,7 @@ class Cdav extends Controller { // Plugins $server->addPlugin(new \Sabre\DAV\Auth\Plugin($auth)); - //$server->addPlugin(new \Sabre\DAV\Browser\Plugin()); + // $server->addPlugin(new \Sabre\DAV\Browser\Plugin()); $server->addPlugin(new \Sabre\DAV\Sync\Plugin()); $server->addPlugin(new \Sabre\DAV\Sharing\Plugin()); $server->addPlugin(new \Sabre\DAVACL\Plugin()); @@ -272,7 +277,7 @@ class Cdav extends Controller { // CalDAV plugins $server->addPlugin(new \Sabre\CalDAV\Plugin()); $server->addPlugin(new \Sabre\CalDAV\SharingPlugin()); - //$server->addPlugin(new \Sabre\CalDAV\Schedule\Plugin()); + // $server->addPlugin(new \Sabre\CalDAV\Schedule\Plugin()); $server->addPlugin(new \Sabre\CalDAV\ICSExportPlugin()); // CardDAV plugins @@ -280,7 +285,7 @@ class Cdav extends Controller { $server->addPlugin(new \Sabre\CardDAV\VCFExportPlugin()); // And off we go! - $server->exec(); + $server->start(); killme(); @@ -337,7 +342,7 @@ class Cdav extends Controller { // set new calendar to be visible set_pconfig(local_channel(), 'cdav_calendar' , $id[0], 1); - build_sync_packet($channel['channel_id'], [ + Libsync::build_sync_packet($channel['channel_id'], [ 'calendar' => [ 'action' => 'create', 'uri' => $calendarUri, @@ -413,7 +418,7 @@ class Cdav extends Controller { $calendarData = $vcalendar->serialize(); $caldavBackend->createCalendarObject($id, $objectUri, $calendarData); - build_sync_packet($channel['channel_id'], [ + Libsync::build_sync_packet($channel['channel_id'], [ 'calendar' => [ 'action' => 'import', 'uri' => $cdavdata['uri'], @@ -444,7 +449,7 @@ class Cdav extends Controller { $caldavBackend->updateCalendar($id, $patch); $patch->commit(); - build_sync_packet($channel['channel_id'], [ + Libsync::build_sync_packet($channel['channel_id'], [ 'calendar' => [ 'action' => 'edit', 'uri' => $cdavdata['uri'], @@ -510,7 +515,7 @@ class Cdav extends Controller { $calendarData = $vcalendar->serialize(); $caldavBackend->updateCalendarObject($id, $uri, $calendarData); - build_sync_packet($channel['channel_id'], [ + Libsync::build_sync_packet($channel['channel_id'], [ 'calendar' => [ 'action' => 'update_card', 'uri' => $cdavdata['uri'], @@ -536,7 +541,7 @@ class Cdav extends Controller { $caldavBackend->deleteCalendarObject($id, $uri); - build_sync_packet($channel['channel_id'], [ + Libsync::build_sync_packet($channel['channel_id'], [ 'calendar' => [ 'action' => 'delete_card', 'uri' => $cdavdata['uri'], @@ -594,7 +599,7 @@ class Cdav extends Controller { $calendarData = $vcalendar->serialize(); $caldavBackend->updateCalendarObject($id, $uri, $calendarData); - build_sync_packet($channel['channel_id'], [ + Libsync::build_sync_packet($channel['channel_id'], [ 'calendar' => [ 'action' => 'update_card', 'uri' => $cdavdata['uri'], @@ -653,7 +658,7 @@ class Cdav extends Controller { $carddavBackend->createAddressBook($principalUri, $addressbookUri, $properties); - build_sync_packet($channel['channel_id'], [ + Libsync::build_sync_packet($channel['channel_id'], [ 'addressbook' => [ 'action' => 'create', 'uri' => $addressbookUri, @@ -680,7 +685,7 @@ class Cdav extends Controller { $carddavBackend->updateAddressBook($id, $patch); $patch->commit(); - build_sync_packet($channel['channel_id'], [ + Libsync::build_sync_packet($channel['channel_id'], [ 'addressbook' => [ 'action' => 'edit', 'uri' => $cdavdata['uri'], @@ -724,7 +729,7 @@ class Cdav extends Controller { $cardData = $vcard->serialize(); $carddavBackend->createCard($id, $uri, $cardData); - build_sync_packet($channel['channel_id'], [ + Libsync::build_sync_packet($channel['channel_id'], [ 'addressbook' => [ 'action' => 'import', 'uri' => $cdavdata['uri'], @@ -762,8 +767,8 @@ class Cdav extends Controller { $cardData = $vcard->serialize(); $carddavBackend->updateCard($id, $uri, $cardData); - - build_sync_packet($channel['channel_id'], [ + + Libsync::build_sync_packet($channel['channel_id'], [ 'addressbook' => [ 'action' => 'update_card', 'uri' => $cdavdata['uri'], @@ -788,7 +793,7 @@ class Cdav extends Controller { $carddavBackend->deleteCard($id, $uri); - build_sync_packet($channel['channel_id'], [ + Libsync::build_sync_packet($channel['channel_id'], [ 'addressbook' => [ 'action' => 'delete_card', 'uri' => $cdavdata['uri'], @@ -804,7 +809,7 @@ class Cdav extends Controller { $src = $_FILES['userfile']['tmp_name']; if($src) { - + $carddata = @file_get_contents($src); if($_REQUEST['c_upload']) { @@ -840,14 +845,14 @@ class Cdav extends Controller { $objects = new \Sabre\VObject\Splitter\VCard($carddata); $profile = \Sabre\VObject\Node::PROFILE_CARDDAV; $backend = new \Sabre\CardDAV\Backend\PDO($pdo); - + $cdavdata = $this->get_cdav_data($id, 'addressbooks'); } - + $ids = []; import_cdav_card($id, $ext, $table, $column, $objects, $profile, $backend, $ids, true); - - build_sync_packet($channel['channel_id'], [ + + Libsync::build_sync_packet($channel['channel_id'], [ $sync => [ 'action' => 'import', 'uri' => $cdavdata['uri'], @@ -868,10 +873,8 @@ class Cdav extends Controller { if((argv(1) === 'addressbook') && (! Apps::system_app_installed(local_channel(), 'CardDAV'))) { //Do not display any associated widgets at this point App::$pdl = ''; - - $o = '<b>' . t('CardDAV App') . ' (' . t('Not Installed') . '):</b><br>'; - $o .= t('CalDAV capable addressbook'); - return $o; + $papp = Apps::get_papp('CardDAV'); + return Apps::app_render($papp, 'module'); } App::$profile_uid = local_channel(); @@ -1013,7 +1016,7 @@ class Cdav extends Controller { $catsenabled = feature_enabled(local_channel(), 'categories'); require_once('include/acl_selectors.php'); - + $accesslist = new \Zotlabs\Access\AccessList($channel); $perm_defaults = $accesslist->get(); @@ -1054,6 +1057,7 @@ class Cdav extends Controller { '$cancel' => t('Cancel'), '$create' => t('Create'), '$recurrence_warning' => t('Sorry! Editing of recurrent events is not yet implemented.'), + '$disabled_warning' => t('Could not fetch calendar resource. The selected calendar might be disabled.'), '$channel_hash' => $channel['channel_hash'], '$acl' => $acl, @@ -1167,7 +1171,7 @@ class Cdav extends Controller { set_pconfig(local_channel(), 'cdav_calendar', $id, argv(4)); - build_sync_packet(local_channel(), [ + Libsync::build_sync_packet(local_channel(), [ 'calendar' => [ 'action' => 'switch', 'uri' => $cdavdata['uri'], @@ -1190,7 +1194,7 @@ class Cdav extends Controller { $caldavBackend->deleteCalendar($id); - build_sync_packet($channel['channel_id'], [ + Libsync::build_sync_packet($channel['channel_id'], [ 'calendar' => [ 'action' => 'drop', 'uri' => $cdavdata['uri'] @@ -1409,7 +1413,7 @@ class Cdav extends Controller { $carddavBackend->deleteAddressBook($id); if($cdavdata) - build_sync_packet($channel['channel_id'], [ + Libsync::build_sync_packet($channel['channel_id'], [ 'addressbook' => [ 'action' => 'drop', 'uri' => $cdavdata['uri'] @@ -1427,7 +1431,7 @@ class Cdav extends Controller { return; $uri = 'principals/' . $channel['channel_address']; - + $r = q("select * from principals where uri = '%s' limit 1", dbesc($uri) diff --git a/Zotlabs/Module/Channel.php b/Zotlabs/Module/Channel.php index 7ff394750..6261a2f06 100644 --- a/Zotlabs/Module/Channel.php +++ b/Zotlabs/Module/Channel.php @@ -4,10 +4,13 @@ namespace Zotlabs\Module; use App; -use Zotlabs\Web\Controller; +use Zotlabs\Lib\Activity; +use Zotlabs\Lib\ActivityStreams; +use Zotlabs\Lib\Crypto; +use Zotlabs\Lib\Libzot; use Zotlabs\Lib\PermissionDescription; +use Zotlabs\Web\Controller; use Zotlabs\Web\HTTPSig; -use Zotlabs\Lib\Libzot; require_once('include/items.php'); require_once('include/security.php'); @@ -20,88 +23,122 @@ require_once('include/opengraph.php'); * @brief Channel Controller * */ - class Channel extends Controller { function init() { - if(in_array(substr($_GET['search'],0,1),[ '@', '!', '?'])) - goaway('search' . '?f=&search=' . $_GET['search']); + if (array_key_exists('search', $_GET) && (in_array(substr($_GET['search'], 0, 1), ['@', '!', '?']) || strpos($_GET['search'], 'https://') === 0)) + goaway(z_root() . '/search?f=&search=' . $_GET['search']); $which = null; - if(argc() > 1) + if (argc() > 1) $which = argv(1); - if(! $which) { - if(local_channel()) { + if (!$which) { + if (local_channel()) { $channel = App::get_channel(); - if($channel && $channel['channel_address']) - $which = $channel['channel_address']; + if ($channel && $channel['channel_address']) + $which = $channel['channel_address']; } } - if(! $which) { - notice( t('You must be logged in to see this page.') . EOL ); + if (!$which) { + notice(t('You must be logged in to see this page.') . EOL); return; } $profile = 0; - $channel = App::get_channel(); - if((local_channel()) && (argc() > 2) && (argv(2) === 'view')) { - $which = $channel['channel_address']; + if ((local_channel()) && (argc() > 2) && (argv(2) === 'view')) { + $channel = App::get_channel(); + $which = $channel['channel_address']; $profile = argv(1); } - $channel = channelx_by_nick($which); - if(! $channel) { + $channel = channelx_by_nick($which, true); + + if (!$channel) { http_status_exit(404, 'Not found'); } - // handle zot6 channel discovery + // handle zot6 channel discovery + + if (Libzot::is_zot_request()) { - if(Libzot::is_zot_request()) { - $sigdata = HTTPSig::verify(file_get_contents('php://input'), EMPTY_STR, 'zot6'); - if($sigdata && $sigdata['signer'] && $sigdata['header_valid']) { - $data = json_encode(Libzot::zotinfo([ 'address' => $channel['channel_address'], 'target_url' => $sigdata['signer'] ])); + if ($sigdata && $sigdata['signer'] && $sigdata['header_valid']) { + $data = json_encode(Libzot::zotinfo(['address' => $channel['channel_address'], 'target_url' => $sigdata['signer']])); + $s = q("select site_crypto, hubloc_sitekey from site left join hubloc on hubloc_url = site_url where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1", dbesc($sigdata['signer']) ); - if($s) { - $data = json_encode(crypto_encapsulate($data,$s[0]['hubloc_sitekey'],Libzot::best_algorithm($s[0]['site_crypto']))); + if ($s) { + $data = json_encode(Crypto::encapsulate($data, $s[0]['hubloc_sitekey'], Libzot::best_algorithm($s[0]['site_crypto']))); } } else { - $data = json_encode(Libzot::zotinfo([ 'address' => $channel['channel_address'] ])); + $data = json_encode(Libzot::zotinfo(['guid_hash' => $channel['channel_hash']])); } - $headers = [ - 'Content-Type' => 'application/x-zot+json', + $headers = [ + 'Content-Type' => 'application/x-zot+json', 'Digest' => HTTPSig::generate_digest_header($data), '(request-target)' => strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI'] - ]; - $h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel)); + ]; + + $h = HTTPSig::create_sig($headers, $channel['channel_prvkey'], channel_url($channel)); HTTPSig::set_headers($h); echo $data; killme(); } + if ($channel['channel_removed']) { + http_status_exit(410, 'Gone'); + } + + if (ActivityStreams::is_as_request($channel)) { + + // Somebody may attempt an ActivityStreams fetch on one of our message permalinks + // Make it do the right thing. + + $mid = ((x($_REQUEST, 'mid')) ? unpack_link_id($_REQUEST['mid']) : ''); + if ($mid === false) { + http_status_exit(404, 'Not found'); + } + + if ($mid) { + $obj = null; + if (strpos($mid, z_root() . '/item/') === 0) { + App::$argc = 2; + App::$argv = ['item', basename($mid)]; + $obj = new Item(); + } + if (strpos($mid, z_root() . '/activity/') === 0) { + App::$argc = 2; + App::$argv = ['activity', basename($mid)]; + $obj = new Activity(); + } + if ($obj) { + $obj->init(); + } + } + as_return_and_die(Activity::encode_person($channel, true), $channel); + } - if((local_channel()) && (argc() > 2) && (argv(2) === 'view')) { - $which = $channel['channel_address']; + if ((local_channel()) && (argc() > 2) && (argv(2) === 'view')) { + $which = $channel['channel_address']; $profile = argv(1); } - head_add_link( [ - 'rel' => 'alternate', + head_add_link([ + 'rel' => 'alternate', 'type' => 'application/atom+xml', 'title' => t('Posts and comments'), 'href' => z_root() . '/feed/' . $which ]); - head_add_link( [ - 'rel' => 'alternate', + head_add_link([ + 'rel' => 'alternate', 'type' => 'application/atom+xml', 'title' => t('Only posts'), 'href' => z_root() . '/feed/' . $which . '?f=&top=1' @@ -110,20 +147,24 @@ class Channel extends Controller { // Run profile_load() here to make sure the theme is set before // we start loading content - profile_load($which,$profile); - + profile_load($which, $profile); + // Add Opengraph markup - $mid = ((x($_REQUEST,'mid')) ? $_REQUEST['mid'] : ''); - if(strpos($mid,'b64.') === 0) - $mid = @base64url_decode(substr($mid,4)); - - if($mid) - $r = q("SELECT * FROM item WHERE mid = '%s' AND uid = %d AND item_private = 0 LIMIT 1", - dbesc($mid), - intval($channel['channel_id']) - ); - - opengraph_add_meta($r ? $r[0] : [], $channel); + $mid = ((x($_REQUEST, 'mid')) ? unpack_link_id($_REQUEST['mid']) : ''); + + if ($mid === false) { + notice(t('Malformed message id.') . EOL); + return; + } + + if ($mid) { + $r = q("SELECT * FROM item WHERE mid = '%s' AND uid = %d AND item_private = 0 LIMIT 1", + dbesc($mid), + intval($channel['channel_id']) + ); + } + + opengraph_add_meta((isset($r) && count($r) ? $r[0] : []), $channel); } function get($update = 0, $load = false) { @@ -132,99 +173,98 @@ class Channel extends Controller { $category = $datequery = $datequery2 = ''; - $mid = ((x($_REQUEST,'mid')) ? $_REQUEST['mid'] : ''); - - if(strpos($mid,'b64.') === 0) - $decoded = @base64url_decode(substr($mid,4)); - if($decoded) - $mid = $decoded; + $mid = ((x($_REQUEST, 'mid')) ? unpack_link_id($_REQUEST['mid']) : ''); + if ($mid === false) { + notice(t('Malformed message id.') . EOL); + return; + } - $datequery = ((x($_GET,'dend') && is_a_date_arg($_GET['dend'])) ? notags($_GET['dend']) : ''); - $datequery2 = ((x($_GET,'dbegin') && is_a_date_arg($_GET['dbegin'])) ? notags($_GET['dbegin']) : ''); + $datequery = ((x($_GET, 'dend') && is_a_date_arg($_GET['dend'])) ? notags($_GET['dend']) : ''); + $datequery2 = ((x($_GET, 'dbegin') && is_a_date_arg($_GET['dbegin'])) ? notags($_GET['dbegin']) : ''); - if(observer_prohibited(true)) { + if (observer_prohibited(true)) { return login(); } - $category = ((x($_REQUEST,'cat')) ? $_REQUEST['cat'] : ''); - $hashtags = ((x($_REQUEST,'tag')) ? $_REQUEST['tag'] : ''); - $order = ((x($_GET,'order')) ? notags($_GET['order']) : 'post'); - $search = ((x($_GET,'search')) ? $_GET['search'] : EMPTY_STR); + $category = ((x($_REQUEST, 'cat')) ? $_REQUEST['cat'] : ''); + $hashtags = ((x($_REQUEST, 'tag')) ? $_REQUEST['tag'] : ''); + $order = ((x($_GET, 'order')) ? notags($_GET['order']) : 'post'); + $search = ((x($_GET, 'search')) ? $_GET['search'] : EMPTY_STR); - $groups = array(); + $groups = []; $o = ''; - if($update) { + if ($update) { // Ensure we've got a profile owner if updating. App::$profile['profile_uid'] = App::$profile_uid = $update; } $is_owner = (((local_channel()) && (App::$profile['profile_uid'] == local_channel())) ? true : false); - $channel = App::get_channel(); + $channel = App::get_channel(); $observer = App::get_observer(); - $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); + $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); - $perms = get_all_perms(App::$profile['profile_uid'],$ob_hash); + $perms = get_all_perms(App::$profile['profile_uid'], $ob_hash); - if(! $perms['view_stream']) { + if (!$perms['view_stream']) { // We may want to make the target of this redirect configurable - if($perms['view_profile']) { - notice( t('Insufficient permissions. Request redirected to profile page.') . EOL); - goaway (z_root() . "/profile/" . App::$profile['channel_address']); + if ($perms['view_profile']) { + notice(t('Insufficient permissions. Request redirected to profile page.') . EOL); + goaway(z_root() . "/profile/" . App::$profile['channel_address']); } - notice( t('Permission denied.') . EOL); + notice(t('Permission denied.') . EOL); return; } - if(! $update) { + if (!$update) { - nav_set_selected('Channel Home'); + nav_set_selected('Channel'); // search terms header - if($search) { - $o .= replace_macros(get_markup_template("section_title.tpl"),array( - '$title' => t('Search Results For:') . ' ' . htmlspecialchars($search, ENT_COMPAT,'UTF-8') - )); + if ($search) { + $o .= replace_macros(get_markup_template("section_title.tpl"), [ + '$title' => t('Search Results For:') . ' ' . htmlspecialchars($search, ENT_COMPAT, 'UTF-8') + ]); } - if($channel && $is_owner) { - $channel_acl = array( + if ($channel && $is_owner) { + $channel_acl = [ 'allow_cid' => $channel['channel_allow_cid'], 'allow_gid' => $channel['channel_allow_gid'], - 'deny_cid' => $channel['channel_deny_cid'], - 'deny_gid' => $channel['channel_deny_gid'] - ); + 'deny_cid' => $channel['channel_deny_cid'], + 'deny_gid' => $channel['channel_deny_gid'] + ]; } else { - $channel_acl = [ 'allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '' ]; + $channel_acl = ['allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '']; } - if($perms['post_wall']) { - - $x = array( - 'is_owner' => $is_owner, - 'allow_location' => ((($is_owner || $observer) && (intval(get_pconfig(App::$profile['profile_uid'],'system','use_browser_location')))) ? true : false), - 'default_location' => (($is_owner) ? App::$profile['channel_location'] : ''), - 'nickname' => App::$profile['channel_address'], - 'lockstate' => (((strlen(App::$profile['channel_allow_cid'])) || (strlen(App::$profile['channel_allow_gid'])) || (strlen(App::$profile['channel_deny_cid'])) || (strlen(App::$profile['channel_deny_gid']))) ? 'lock' : 'unlock'), - 'acl' => (($is_owner) ? populate_acl($channel_acl,true, PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post') : ''), - 'permissions' => $channel_acl, - 'showacl' => (($is_owner) ? 'yes' : ''), - 'bang' => '', - 'visitor' => (($is_owner || $observer) ? true : false), - 'profile_uid' => App::$profile['profile_uid'], + if ($perms['post_wall']) { + + $x = [ + 'is_owner' => $is_owner, + 'allow_location' => ((($is_owner || $observer) && (intval(get_pconfig(App::$profile['profile_uid'], 'system', 'use_browser_location')))) ? true : false), + 'default_location' => (($is_owner) ? App::$profile['channel_location'] : ''), + 'nickname' => App::$profile['channel_address'], + 'lockstate' => (((strlen(App::$profile['channel_allow_cid'])) || (strlen(App::$profile['channel_allow_gid'])) || (strlen(App::$profile['channel_deny_cid'])) || (strlen(App::$profile['channel_deny_gid']))) ? 'lock' : 'unlock'), + 'acl' => (($is_owner) ? populate_acl($channel_acl, true, PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post') : ''), + 'permissions' => $channel_acl, + 'showacl' => (($is_owner) ? 'yes' : ''), + 'bang' => '', + 'visitor' => (($is_owner || $observer) ? true : false), + 'profile_uid' => App::$profile['profile_uid'], 'editor_autocomplete' => true, - 'bbco_autocomplete' => 'bbcode', - 'bbcode' => true, - 'jotnets' => true, - 'reset' => t('Reset form') - ); + 'bbco_autocomplete' => 'bbcode', + 'bbcode' => true, + 'jotnets' => true, + 'reset' => t('Reset form') + ]; - $o .= status_editor($a,$x,false,'Channel'); + $o .= status_editor($a, $x, false, 'Channel'); } } @@ -233,16 +273,16 @@ class Channel extends Controller { /** * Get permissions SQL - if $remote_contact is true, our remote user has been pre-verified and we already have fetched his/her groups */ - - $item_normal = " and item.item_hidden = 0 and item.item_type = 0 and item.item_deleted = 0 + + $item_normal = " and item.item_hidden = 0 and item.item_type = 0 and item.item_deleted = 0 and item.item_unpublished = 0 and item.item_pending_remove = 0 and item.item_blocked = 0 "; - if (! $is_owner) - $item_normal .= "and item.item_delayed = 0 "; + if (!$is_owner) + $item_normal .= "and item.item_delayed = 0 "; $item_normal_update = item_normal_update(); - $sql_extra = item_permissions_sql(App::$profile['profile_uid']); + $sql_extra = item_permissions_sql(App::$profile['profile_uid']); - if(feature_enabled(App::$profile['profile_uid'], 'channel_list_mode') && (! $mid)) + if (feature_enabled(App::$profile['profile_uid'], 'channel_list_mode') && (!$mid)) $page_mode = 'list'; else $page_mode = 'client'; @@ -250,13 +290,13 @@ class Channel extends Controller { $abook_uids = " and abook.abook_channel = " . intval(App::$profile['profile_uid']) . " "; $simple_update = ''; - if($update && $_SESSION['loadtime']) - $simple_update = " AND (( item_unseen = 1 AND item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) OR item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) "; + if ($update && $_SESSION['loadtime']) + $simple_update = " AND (( item_unseen = 1 AND item.changed > '" . datetime_convert('UTC', 'UTC', $_SESSION['loadtime']) . "' ) OR item.changed > '" . datetime_convert('UTC', 'UTC', $_SESSION['loadtime']) . "' ) "; - if($search) { + if ($search) { $search = escape_tags($search); - if(strpos($search,'#') === 0) { - $sql_extra .= term_query('item',substr($search,1),TERM_HASHTAG,TERM_COMMUNITYTAG); + if (strpos($search, '#') === 0) { + $sql_extra .= term_query('item', substr($search, 1), TERM_HASHTAG, TERM_COMMUNITYTAG); } else { $sql_extra .= sprintf(" AND (item.body like '%s' OR item.title like '%s') ", @@ -266,19 +306,19 @@ class Channel extends Controller { } } - head_add_link([ + head_add_link([ 'rel' => 'alternate', 'type' => 'application/json+oembed', 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . App::$query_string), 'title' => 'oembed' ]); - if(($update) && (! $load)) { + if (($update) && (!$load)) { - if($mid) { - $r = q("SELECT parent AS item_id from item where mid like '%s' and uid = %d $item_normal_update + if ($mid) { + $r = q("SELECT parent AS item_id from item where mid = '%s' and uid = %d $item_normal_update AND item_wall = 1 $simple_update $sql_extra limit 1", - dbesc($mid . '%'), + dbesc($mid), intval(App::$profile['profile_uid']) ); } @@ -296,61 +336,62 @@ class Channel extends Controller { } else { - if(x($category)) { - $sql_extra2 .= protect_sprintf(term_item_parent_query(App::$profile['profile_uid'],'item', $category, TERM_CATEGORY)); + $sql_extra2 = ''; + if (x($category)) { + $sql_extra2 .= protect_sprintf(term_item_parent_query(App::$profile['profile_uid'], 'item', $category, TERM_CATEGORY)); } - if(x($hashtags)) { - $sql_extra2 .= protect_sprintf(term_item_parent_query(App::$profile['profile_uid'],'item', $hashtags, TERM_HASHTAG, TERM_COMMUNITYTAG)); + if (x($hashtags)) { + $sql_extra2 .= protect_sprintf(term_item_parent_query(App::$profile['profile_uid'], 'item', $hashtags, TERM_HASHTAG, TERM_COMMUNITYTAG)); } - if($datequery) { - $sql_extra2 .= protect_sprintf(sprintf(" AND item.created <= '%s' ", dbesc(datetime_convert(date_default_timezone_get(),'',$datequery)))); - $order = 'post'; + if ($datequery) { + $sql_extra2 .= protect_sprintf(sprintf(" AND item.created <= '%s' ", dbesc(datetime_convert(date_default_timezone_get(), '', $datequery)))); + $order = 'post'; } - if($datequery2) { - $sql_extra2 .= protect_sprintf(sprintf(" AND item.created >= '%s' ", dbesc(datetime_convert(date_default_timezone_get(),'',$datequery2)))); + if ($datequery2) { + $sql_extra2 .= protect_sprintf(sprintf(" AND item.created >= '%s' ", dbesc(datetime_convert(date_default_timezone_get(), '', $datequery2)))); } - if($order === 'post') + if ($order === 'post') $ordering = "created"; else $ordering = "commented"; - $itemspage = get_pconfig(local_channel(),'system','itemspage'); + $itemspage = get_pconfig(local_channel(), 'system', 'itemspage'); App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 10)); $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(App::$pager['itemspage']), intval(App::$pager['start'])); - if($noscript_content || $load) { - if($mid) { - $r = q("SELECT parent AS item_id from item where mid like '%s' and uid = %d $item_normal + if ($noscript_content || $load) { + if ($mid) { + $r = q("SELECT parent AS item_id from item where mid = '%s' and uid = %d $item_normal AND item_wall = 1 $sql_extra limit 1", - dbesc($mid . '%'), + dbesc($mid), intval(App::$profile['profile_uid']) ); - if (! $r) { - notice( t('Permission denied.') . EOL); + if (!$r) { + notice(t('Permission denied.') . EOL); } } else { - $r = q("SELECT DISTINCT item.parent AS item_id, $ordering FROM item + $r = q("SELECT DISTINCT item.parent AS item_id, $ordering FROM item left join abook on ( item.author_xchan = abook.abook_xchan $abook_uids ) WHERE true and item.uid = %d $item_normal AND (abook.abook_blocked = 0 or abook.abook_flags is null) AND item.item_wall = 1 AND item.item_thread_top = 1 - $sql_extra $sql_extra2 + $sql_extra $sql_extra2 ORDER BY $ordering DESC, item_id $pager_sql ", intval(App::$profile['profile_uid']) ); } } else { - $r = array(); + $r = []; } } - if($r) { + if ($r) { - $parents_str = ids_to_querystr($r,'item_id'); + $parents_str = ids_to_querystr($r, 'item_id'); $r = q("SELECT item.*, item.id AS item_id FROM item @@ -363,28 +404,38 @@ class Channel extends Controller { xchan_query($r); $items = fetch_post_tags($r, true); - $items = conv_sort($items,$ordering); + $items = conv_sort($items, $ordering); - if($load && $mid && (! count($items))) { + if ($load && $mid && (!count($items))) { // This will happen if we don't have sufficient permissions // to view the parent item (or the item itself if it is toplevel) - notice( t('Permission denied.') . EOL); + notice(t('Permission denied.') . EOL); } - } else { - $items = array(); + } + else { + $items = []; } - if((! $update) && (! $load)) { + // Add pinned content + if (!x($_REQUEST, 'mid') && !$search) { + $pinned = new \Zotlabs\Widget\Pinned; + $r = $pinned->widget(intval(App::$profile['profile_uid']), [ITEM_TYPE_POST]); + $o .= $r['html']; + } + + $mode = (($search) ? 'search' : 'channel'); + + if ((!$update) && (!$load)) { - if($decoded) - $mid = 'b64.' . base64url_encode($mid); + //if we got a decoded hash we must encode it again before handing to javascript + $mid = gen_link_id($mid); // This is ugly, but we can't pass the profile_uid through the session to the ajax updater, // because browser prefetching might change it on us. We have to deliver it with the page. - $maxheight = get_pconfig(App::$profile['profile_uid'],'system','channel_divmore_height'); - if(! $maxheight) + $maxheight = get_pconfig(App::$profile['profile_uid'], 'system', 'channel_divmore_height'); + if (!$maxheight) $maxheight = 400; $o .= '<div id="live-channel"></div>' . "\r\n"; @@ -392,57 +443,48 @@ class Channel extends Controller { . "; var netargs = '?f='; var profile_page = " . App::$pager['page'] . "; divmore_height = " . intval($maxheight) . ";</script>\r\n"; - App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"),array( - '$baseurl' => z_root(), - '$pgtype' => 'channel', - '$uid' => ((App::$profile['profile_uid']) ? App::$profile['profile_uid'] : '0'), - '$gid' => '0', - '$cid' => '0', - '$cmin' => '(-1)', - '$cmax' => '(-1)', - '$star' => '0', - '$liked' => '0', - '$conv' => '0', - '$spam' => '0', - '$nouveau' => '0', - '$wall' => '1', - '$fh' => '0', - '$dm' => '0', - '$page' => ((App::$pager['page'] != 1) ? App::$pager['page'] : 1), - '$search' => $search, - '$xchan' => '', - '$order' => (($order) ? urlencode($order) : ''), - '$list' => ((x($_REQUEST,'list')) ? intval($_REQUEST['list']) : 0), - '$file' => '', - '$cats' => (($category) ? urlencode($category) : ''), - '$tags' => (($hashtags) ? urlencode($hashtags) : ''), - '$mid' => (($mid) ? urlencode($mid) : ''), - '$verb' => '', - '$net' => '', - '$dend' => $datequery, - '$dbegin' => $datequery2, - '$conv_mode' => 'channel' - )); - + App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"), [ + '$baseurl' => z_root(), + '$pgtype' => 'channel', + '$uid' => ((App::$profile['profile_uid']) ? App::$profile['profile_uid'] : '0'), + '$gid' => '0', + '$cid' => '0', + '$cmin' => '(-1)', + '$cmax' => '(-1)', + '$star' => '0', + '$liked' => '0', + '$conv' => '0', + '$spam' => '0', + '$nouveau' => '0', + '$wall' => '1', + '$fh' => '0', + '$dm' => '0', + '$page' => ((App::$pager['page'] != 1) ? App::$pager['page'] : 1), + '$search' => $search, + '$xchan' => '', + '$order' => (($order) ? urlencode($order) : ''), + '$list' => ((x($_REQUEST, 'list')) ? intval($_REQUEST['list']) : 0), + '$file' => '', + '$cats' => (($category) ? urlencode($category) : ''), + '$tags' => (($hashtags) ? urlencode($hashtags) : ''), + '$mid' => (($mid) ? urlencode($mid) : ''), + '$verb' => '', + '$net' => '', + '$dend' => $datequery, + '$dbegin' => $datequery2, + '$conv_mode' => 'channel', + '$page_mode' => $page_mode + ]); } - // Add pinned content - if(! x($_REQUEST,'mid') && ! $search) { - $pinned = new \Zotlabs\Widget\Pinned; - $r = $pinned->widget(intval(App::$profile['profile_uid']), [ITEM_TYPE_POST]); - $o .= $r['html']; - } - - $mode = (($search) ? 'search' : 'channel'); - - if($update) { - $o .= conversation($items,$mode,$update,$page_mode); + if ($update) { + $o .= conversation($items, $mode, $update, $page_mode); } else { $o .= '<noscript>'; - if($noscript_content) { - $o .= conversation($items,$mode,$update,'traditional'); + if ($noscript_content) { + $o .= conversation($items, $mode, $update, 'traditional'); $o .= alt_pager(count($items)); } else { @@ -450,14 +492,14 @@ class Channel extends Controller { } $o .= '</noscript>'; - $o .= conversation($items,$mode,$update,$page_mode); + $o .= conversation($items, $mode, $update, $page_mode); - if ($mid && $items[0]['title']) + if ($mid && count($items) > 0 && isset($items[0]['title'])) App::$page['title'] = $items[0]['title'] . " - " . App::$page['title']; } - if($mid) + if ($mid) $o .= '<div id="content-complete"></div>'; $_SESSION['loadtime'] = datetime_convert(); diff --git a/Zotlabs/Module/Channel_calendar.php b/Zotlabs/Module/Channel_calendar.php index ae4afb2f3..26c6aaf40 100644 --- a/Zotlabs/Module/Channel_calendar.php +++ b/Zotlabs/Module/Channel_calendar.php @@ -1,7 +1,12 @@ <?php + namespace Zotlabs\Module; +use App; +use Zotlabs\Web\Controller; use Zotlabs\Lib\Libsync; +use Zotlabs\Access\AccessList; +use Zotlabs\Daemon\Master; require_once('include/conversation.php'); require_once('include/bbcode.php'); @@ -10,37 +15,37 @@ require_once('include/event.php'); require_once('include/items.php'); require_once('include/html2plain.php'); -class Channel_calendar extends \Zotlabs\Web\Controller { +class Channel_calendar extends Controller { function post() { - - logger('post: ' . print_r($_REQUEST,true), LOGGER_DATA); - - if(! local_channel()) - return; - $event_id = ((x($_POST,'event_id')) ? intval($_POST['event_id']) : 0); - $event_hash = ((x($_POST,'event_hash')) ? $_POST['event_hash'] : ''); - - $xchan = ((x($_POST,'xchan')) ? dbesc($_POST['xchan']) : ''); + logger('post: ' . print_r($_REQUEST, true), LOGGER_DATA); + $uid = local_channel(); + if (!$uid) + return; + + $event_id = ((x($_POST, 'event_id')) ? intval($_POST['event_id']) : 0); + + $xchan = ((x($_POST, 'xchan')) ? dbesc($_POST['xchan']) : ''); + // only allow editing your own events. - if(($xchan) && ($xchan !== get_observer_hash())) + if (($xchan) && ($xchan !== get_observer_hash())) return; $categories = escape_tags(trim($_POST['categories'])); - + // allday events have adjust = 0, normal events have adjust = 1 $adjust = intval($_POST['adjust']); - $start = datetime_convert((($adjust) ? $tz : 'UTC'), 'UTC', escape_tags($_REQUEST['dtstart'])); - $finish = datetime_convert((($adjust) ? $tz : 'UTC'), 'UTC', escape_tags($_REQUEST['dtend'])); + $timezone = ((x($_POST, 'timezone_select')) ? escape_tags(trim($_POST['timezone_select'])) : ''); + $tz = (($timezone) ? $timezone : date_default_timezone_get()); - $timezone = ((x($_POST,'timezone_select')) ? escape_tags(trim($_POST['timezone_select'])) : ''); - $tz = (($timezone) ? $timezone : date_default_timezone_get()); + $start = datetime_convert((($adjust) ? $tz : 'UTC'), 'UTC', escape_tags($_REQUEST['dtstart'])); + $finish = datetime_convert((($adjust) ? $tz : 'UTC'), 'UTC', escape_tags($_REQUEST['dtend'])); - if(! $adjust) + if (!$adjust) $tz = 'UTC'; $summary = escape_tags(trim($_POST['summary'])); @@ -52,88 +57,86 @@ class Channel_calendar extends \Zotlabs\Web\Controller { // It won't hurt anything, but somebody will file a bug report // and we'll waste a bunch of time responding to it. Time that // could've been spent doing something else. - - if(strcmp($finish,$start) < 0 && !$nofinish) { - notice( t('Event can not end before it has started.') . EOL); - if(intval($_REQUEST['preview'])) { - echo( t('Unable to generate preview.')); + + if (strcmp($finish, $start) < 0) { + notice(t('Event can not end before it has started.') . EOL); + if (intval($_REQUEST['preview'])) { + echo(t('Unable to generate preview.')); } killme(); } - - if((! $summary) || (! $start)) { - notice( t('Event title and start time are required.') . EOL); - if(intval($_REQUEST['preview'])) { - echo( t('Unable to generate preview.')); + + if ((!$summary) || (!$start)) { + notice(t('Event title and start time are required.') . EOL); + if (intval($_REQUEST['preview'])) { + echo(t('Unable to generate preview.')); } killme(); } - $channel = \App::get_channel(); - - $acl = new \Zotlabs\Access\AccessList(false); - - if($event_id) { + $acl = new AccessList([]); + + if ($event_id) { $x = q("select * from event where id = %d and uid = %d limit 1", intval($event_id), - intval(local_channel()) + intval($uid) ); - if(! $x) { - notice( t('Event not found.') . EOL); - if(intval($_REQUEST['preview'])) { - echo( t('Unable to generate preview.')); + if (!$x) { + notice(t('Event not found.') . EOL); + if (intval($_REQUEST['preview'])) { + echo(t('Unable to generate preview.')); killme(); } return; } - + $acl->set($x[0]); - + $created = $x[0]['created']; - $edited = datetime_convert(); + $edited = datetime_convert(); } else { $created = $edited = datetime_convert(); $acl->set_from_array($_POST); } - + $post_tags = array(); - $channel = \App::get_channel(); - $ac = $acl->get(); + $channel = App::get_channel(); + $ac = $acl->get(); $str_contact_allow = $ac['allow_cid']; $str_group_allow = $ac['allow_gid']; - $str_contact_deny = $ac['deny_cid']; - $str_group_deny = $ac['deny_gid']; + $str_contact_deny = $ac['deny_cid']; + $str_group_deny = $ac['deny_gid']; $private = $acl->is_private(); require_once('include/text.php'); - $results = linkify_tags($desc, local_channel()); + $results = linkify_tags($desc, $uid); - if($results) { + if ($results) { // Set permissions based on tag replacements - set_linkified_perms($results, $str_contact_allow, $str_group_allow, local_channel(), false, $private); + set_linkified_perms($results, $str_contact_allow, $str_group_allow, $uid, $private); - foreach($results as $result) { + foreach ($results as $result) { $success = $result['success']; - if($success['replaced']) { + if ($success['replaced']) { $post_tags[] = array( - 'uid' => local_channel(), + 'uid' => $uid, 'ttype' => $success['termtype'], 'otype' => TERM_OBJ_POST, 'term' => $success['term'], 'url' => $success['url'] - ); + ); } } } - if(strlen($categories)) { - $cats = explode(',',$categories); - foreach($cats as $cat) { + if (strlen($categories)) { + $cats = explode(',', $categories); + foreach ($cats as $cat) { $post_tags[] = array( - 'uid' => local_channel(), + 'uid' => $uid, 'ttype' => TERM_CATEGORY, 'otype' => TERM_OBJ_POST, 'term' => trim($cat), @@ -141,175 +144,170 @@ class Channel_calendar extends \Zotlabs\Web\Controller { ); } } - - $datarray = array(); - $datarray['dtstart'] = $start; - $datarray['dtend'] = $finish; - $datarray['summary'] = $summary; + + $datarray = array(); + $datarray['dtstart'] = $start; + $datarray['dtend'] = $finish; + $datarray['summary'] = $summary; $datarray['description'] = $desc; - $datarray['location'] = $location; - $datarray['etype'] = $type; - $datarray['adjust'] = $adjust; - $datarray['nofinish'] = 0; - $datarray['uid'] = local_channel(); - $datarray['account'] = get_account_id(); + $datarray['location'] = $location; + $datarray['etype'] = $type; + $datarray['adjust'] = $adjust; + $datarray['nofinish'] = 0; + $datarray['uid'] = $uid; + $datarray['account'] = get_account_id(); $datarray['event_xchan'] = $channel['channel_hash']; - $datarray['allow_cid'] = $str_contact_allow; - $datarray['allow_gid'] = $str_group_allow; - $datarray['deny_cid'] = $str_contact_deny; - $datarray['deny_gid'] = $str_group_deny; - $datarray['private'] = intval($private); - $datarray['id'] = $event_id; - $datarray['created'] = $created; - $datarray['edited'] = $edited; - $datarray['timezone'] = $tz; - - - if(intval($_REQUEST['preview'])) { + $datarray['allow_cid'] = $str_contact_allow; + $datarray['allow_gid'] = $str_group_allow; + $datarray['deny_cid'] = $str_contact_deny; + $datarray['deny_gid'] = $str_group_deny; + $datarray['private'] = intval($private); + $datarray['id'] = $event_id; + $datarray['created'] = $created; + $datarray['edited'] = $edited; + $datarray['timezone'] = $tz; + + + if (intval($_REQUEST['preview'])) { $html = format_event_html($datarray); echo $html; killme(); } - + $event = event_store_event($datarray); - - if($post_tags) + + if ($post_tags) $datarray['term'] = $post_tags; - - $item_id = event_store_item($datarray,$event); - - if($item_id) { + + $item_id = event_store_item($datarray, $event); + + if ($item_id) { $r = q("select * from item where id = %d", intval($item_id) ); - if($r) { + if ($r) { xchan_query($r); $sync_item = fetch_post_tags($r); - $z = q("select * from event where event_hash = '%s' and uid = %d limit 1", + $z = q("select * from event where event_hash = '%s' and uid = %d limit 1", dbesc($r[0]['resource_id']), intval($channel['channel_id']) ); - if($z) { - Libsync::build_sync_packet($channel['channel_id'],array('event_item' => array(encode_item($sync_item[0],true)),'event' => $z)); + if ($z) { + Libsync::build_sync_packet($channel['channel_id'], array('event_item' => array(encode_item($sync_item[0], true)), 'event' => $z)); } } } - - \Zotlabs\Daemon\Master::Summon(array('Notifier','event',$item_id)); + + Master::Summon(array('Notifier', 'event', $item_id)); killme(); - + } - - - + + function get() { - - if(argc() > 2 && argv(1) == 'ical') { + + if (argc() > 2 && argv(1) == 'ical') { $event_id = argv(2); - + require_once('include/security.php'); $sql_extra = permissions_sql(local_channel()); - + $r = q("select * from event where event_hash = '%s' $sql_extra limit 1", dbesc($event_id) ); - if($r) { + if ($r) { header('Content-type: text/calendar'); - header('content-disposition: attachment; filename="' . t('event') . '-' . $event_id . '.ics"' ); + header('content-disposition: attachment; filename="' . t('event') . '-' . $event_id . '.ics"'); echo ical_wrapper($r); killme(); } else { - notice( t('Event not found.') . EOL ); + notice(t('Event not found.') . EOL); return; } } - - if(! local_channel()) { - notice( t('Permission denied.') . EOL); + + if (!local_channel()) { + notice(t('Permission denied.') . EOL); return; } - if((argc() > 2) && (argv(1) === 'ignore') && intval(argv(2))) { - $r = q("update event set dismissed = 1 where id = %d and uid = %d", + if ((argc() > 2) && (argv(1) === 'ignore') && intval(argv(2))) { + q("update event set dismissed = 1 where id = %d and uid = %d", intval(argv(2)), intval(local_channel()) ); } - - if((argc() > 2) && (argv(1) === 'unignore') && intval(argv(2))) { - $r = q("update event set dismissed = 0 where id = %d and uid = %d", + + if ((argc() > 2) && (argv(1) === 'unignore') && intval(argv(2))) { + q("update event set dismissed = 0 where id = %d and uid = %d", intval(argv(2)), intval(local_channel()) ); } - $channel = \App::get_channel(); - - $mode = 'view'; - $export = false; - $ignored = ((x($_REQUEST,'ignored')) ? " and dismissed = " . intval($_REQUEST['ignored']) . " " : ''); + $mode = 'view'; + $export = false; + $ignored = ((x($_REQUEST, 'ignored')) ? " and dismissed = " . intval($_REQUEST['ignored']) . " " : ''); - if(argc() > 1) { - if(argc() > 2 && argv(1) === 'add') { - $mode = 'add'; + if (argc() > 1) { + if (argc() > 2 && argv(1) === 'add') { + $mode = 'add'; $item_id = intval(argv(2)); } - if(argc() > 2 && argv(1) === 'drop') { - $mode = 'drop'; + if (argc() > 2 && argv(1) === 'drop') { + $mode = 'drop'; $event_id = argv(2); } - if(argc() <= 2 && argv(1) === 'export') { + if (argc() <= 2 && argv(1) === 'export') { $export = true; } - if(argc() > 2 && intval(argv(1)) && intval(argv(2))) { + if (argc() > 2 && intval(argv(1)) && intval(argv(2))) { $mode = 'view'; } - if(argc() <= 2) { - $mode = 'view'; + if (argc() <= 2) { + $mode = 'view'; $event_id = argv(1); } } - - if($mode === 'add') { - event_addtocal($item_id,local_channel()); + + if ($mode === 'add') { + event_addtocal($item_id, local_channel()); killme(); } - - if($mode == 'view') { - + + if ($mode == 'view') { + /* edit/create form */ - if($event_id) { - $r = q("SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1", + if ($event_id) { + q("SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1", dbesc($event_id), intval(local_channel()) ); - if(count($r)) - $orig_event = $r[0]; } - - $channel = \App::get_channel(); - if (argv(1) === 'json'){ - if (x($_GET,'start')) $start = $_GET['start']; - if (x($_GET,'end')) $finish = $_GET['end']; + $channel = App::get_channel(); + + if (argv(1) === 'json') { + if (x($_GET, 'start')) $start = $_GET['start']; + if (x($_GET, 'end')) $finish = $_GET['end']; } - - $start = datetime_convert('UTC','UTC',$start); - $finish = datetime_convert('UTC','UTC',$finish); - $adjust_start = datetime_convert('UTC', date_default_timezone_get(), $start); + + $start = datetime_convert('UTC', 'UTC', $start); + $finish = datetime_convert('UTC', 'UTC', $finish); + $adjust_start = datetime_convert('UTC', date_default_timezone_get(), $start); $adjust_finish = datetime_convert('UTC', date_default_timezone_get(), $finish); - if (x($_GET,'id')){ - $r = q("SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan, item.id as item_id + if (x($_GET, 'id')) { + $r = q("SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan, item.id as item_id from event left join item on item.resource_id = event.event_hash where item.resource_type = 'event' and event.uid = %d and event.id = %d limit 1", intval(local_channel()), intval($_GET['id']) ); } - elseif($export) { + elseif ($export) { $r = q("SELECT event.*, item.id as item_id from event left join item on item.resource_id = event.event_hash where event.uid = %d and event.dtstart > '%s' and event.dtend > event.dtstart", @@ -335,104 +333,105 @@ class Channel_calendar extends \Zotlabs\Web\Controller { dbesc($adjust_finish) ); } - - if($r && ! $export) { + + if ($r && !$export) { xchan_query($r); - $r = fetch_post_tags($r,true); + $r = fetch_post_tags($r, true); $r = sort_by_date($r); } $events = []; - if($r) { - - foreach($r as $rr) { + if ($r) { + + foreach ($r as $rr) { $start = (($rr['adjust']) ? datetime_convert('UTC', date_default_timezone_get(), $rr['dtstart'], 'c') : datetime_convert('UTC', 'UTC', $rr['dtstart'], 'c')); - if ($rr['nofinish']){ + if ($rr['nofinish']) { $end = null; - } else { + } + else { $end = (($rr['adjust']) ? datetime_convert('UTC', date_default_timezone_get(), $rr['dtend'], 'c') : datetime_convert('UTC', 'UTC', $rr['dtend'], 'c')); } - $catsenabled = feature_enabled(local_channel(),'categories'); - $categories = ''; - if($catsenabled){ - if($rr['term']) { + $catsenabled = feature_enabled(local_channel(), 'categories'); + $categories = ''; + if ($catsenabled) { + if ($rr['term']) { $cats = get_terms_oftype($rr['term'], TERM_CATEGORY); foreach ($cats as $cat) { - if(strlen($categories)) + if (strlen($categories)) $categories .= ', '; $categories .= $cat['term']; } } } - $edit = ((local_channel() && $rr['author_xchan'] == get_observer_hash()) ? array(z_root().'/events/'.$rr['event_hash'].'?expandform=1',t('Edit event'),'','') : false); - - $drop = array(z_root().'/events/drop/'.$rr['event_hash'],t('Delete event'),'',''); - + $edit = ((local_channel() && $rr['author_xchan'] == get_observer_hash()) ? array(z_root() . '/events/' . $rr['event_hash'] . '?expandform=1', t('Edit event'), '', '') : false); + + $drop = array(z_root() . '/events/drop/' . $rr['event_hash'], t('Delete event'), '', ''); + $tz = get_iconfig($rr, 'event', 'timezone'); - if(! $tz) + if (!$tz) $tz = 'UTC'; $events[] = array( 'calendar_id' => 'channel_calendar', - 'rw' => true, - 'id'=>$rr['id'], - 'uri' => $rr['event_hash'], - 'timezone' => $tz, - 'start'=> $start, - 'end' => $end, - 'drop' => $drop, - 'allDay' => (($rr['adjust']) ? 0 : 1), - 'title' => html_entity_decode($rr['summary'], ENT_COMPAT, 'UTF-8'), - 'editable' => $edit ? true : false, - 'item' => $rr, - 'plink' => [$rr['plink'], t('Link to source')], + 'rw' => true, + 'id' => $rr['id'], + 'uri' => $rr['event_hash'], + 'timezone' => $tz, + 'start' => $start, + 'end' => $end, + 'drop' => $drop, + 'allDay' => (($rr['adjust']) ? 0 : 1), + 'title' => html_entity_decode($rr['summary'], ENT_COMPAT, 'UTF-8'), + 'editable' => $edit ? true : false, + 'item' => $rr, + 'plink' => [$rr['plink'], t('Link to source')], 'description' => html_entity_decode($rr['description'], ENT_COMPAT, 'UTF-8'), - 'location' => html_entity_decode($rr['location'], ENT_COMPAT, 'UTF-8'), - 'allow_cid' => expand_acl($rr['allow_cid']), - 'allow_gid' => expand_acl($rr['allow_gid']), - 'deny_cid' => expand_acl($rr['deny_cid']), - 'deny_gid' => expand_acl($rr['deny_gid']), - 'categories' => $categories + 'location' => html_entity_decode($rr['location'], ENT_COMPAT, 'UTF-8'), + 'allow_cid' => expand_acl($rr['allow_cid']), + 'allow_gid' => expand_acl($rr['allow_gid']), + 'deny_cid' => expand_acl($rr['deny_cid']), + 'deny_gid' => expand_acl($rr['deny_gid']), + 'categories' => $categories ); } } - - if($export) { + + if ($export) { header('Content-type: text/calendar'); - header('content-disposition: attachment; filename="' . t('calendar') . '-' . $channel['channel_address'] . '.ics"' ); + header('content-disposition: attachment; filename="' . t('calendar') . '-' . $channel['channel_address'] . '.ics"'); echo ical_wrapper($r); killme(); } - if (\App::$argv[1] === 'json'){ + if (App::$argv[1] === 'json') { json_return_and_die($events); } } - - if($mode === 'drop' && $event_id) { + + if ($mode === 'drop' && $event_id) { $r = q("SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1", dbesc($event_id), intval(local_channel()) ); - + $sync_event = $r[0]; - - if($r) { + + if ($r) { $r = q("delete from event where event_hash = '%s' and uid = %d", dbesc($event_id), intval(local_channel()) ); - if($r) { + if ($r) { $sync_event['event_deleted'] = 1; - Libsync::build_sync_packet(0,array('event' => array($sync_event))); + Libsync::build_sync_packet(0, array('event' => array($sync_event))); $i = q("select * from item where resource_type = 'event' and resource_id = '%s' and uid = %d", dbesc($event_id), @@ -441,11 +440,11 @@ class Channel_calendar extends \Zotlabs\Web\Controller { if ($i) { - $can_delete = false; + $can_delete = false; $local_delete = true; $ob_hash = get_observer_hash(); - if($ob_hash && ($ob_hash === $i[0]['author_xchan'] || $ob_hash === $i[0]['owner_xchan'] || $ob_hash === $i[0]['source_xchan'])) { + if ($ob_hash && ($ob_hash === $i[0]['author_xchan'] || $ob_hash === $i[0]['owner_xchan'] || $ob_hash === $i[0]['source_xchan'])) { $can_delete = true; } @@ -453,49 +452,49 @@ class Channel_calendar extends \Zotlabs\Web\Controller { // If the item originated on this site+channel the deletion will propagate downstream. // Otherwise just the local copy is removed. - if(is_site_admin()) { + if (is_site_admin()) { $local_delete = true; - if(intval($i[0]['item_origin'])) + if (intval($i[0]['item_origin'])) $can_delete = true; } - if($can_delete || $local_delete) { + if ($can_delete || $local_delete) { // if this is a different page type or it's just a local delete // but not by the item author or owner, do a simple deletion - $complex = false; + $complex = false; - if(intval($i[0]['item_type']) || ($local_delete && (! $can_delete))) { + if (intval($i[0]['item_type']) || ($local_delete && (!$can_delete))) { drop_item($i[0]['id']); } else { // complex deletion that needs to propagate and be performed in phases - drop_item($i[0]['id'],true,DROPITEM_PHASE1); + drop_item($i[0]['id'], true, DROPITEM_PHASE1); $complex = true; } $ii = q("select * from item where id = %d", intval($i[0]['id']) ); - if($ii) { + if ($ii) { xchan_query($ii); $sync_item = fetch_post_tags($ii); - Libsync::build_sync_packet($i[0]['uid'],array('item' => array(encode_item($sync_item[0],true)))); + Libsync::build_sync_packet($i[0]['uid'], array('item' => array(encode_item($sync_item[0], true)))); } - if($complex) { - tag_deliver($i[0]['uid'],$i[0]['id']); + if ($complex) { + tag_deliver($i[0]['uid'], $i[0]['id']); } } } killme(); } - notice( t('Failed to remove event' ) . EOL); + notice(t('Failed to remove event') . EOL); killme(); } } - + } - + } diff --git a/Zotlabs/Module/Chanview.php b/Zotlabs/Module/Chanview.php index 12e1891d4..fc1146023 100644 --- a/Zotlabs/Module/Chanview.php +++ b/Zotlabs/Module/Chanview.php @@ -10,49 +10,49 @@ use Zotlabs\Lib\Zotfinger; class Chanview extends \Zotlabs\Web\Controller { function get() { - + $observer = App::get_observer(); $xchan = null; - + $r = null; - + if($_REQUEST['hash']) { - $r = q("select * from xchan where xchan_hash = '%s'", + $r = q("select * from xchan where xchan_hash = '%s' and xchan_deleted = 0", dbesc($_REQUEST['hash']) ); } if($_REQUEST['address']) { - $r = q("select * from xchan where xchan_addr = '%s'", + $r = q("select * from xchan where xchan_addr = '%s' and xchan_deleted = 0", dbesc(punify($_REQUEST['address'])) ); } elseif(local_channel() && intval($_REQUEST['cid'])) { - $r = q("SELECT abook.*, xchan.* + $r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash - WHERE abook_channel = %d and abook_id = %d", + WHERE abook_channel = %d and abook_id = %d and xchan_deleted = 0", intval(local_channel()), intval($_REQUEST['cid']) ); - } + } elseif($_REQUEST['url']) { - + // if somebody re-installed they will have more than one xchan, use the most recent name date as this is - // the most useful consistently ascending table item we have. - - $r = q("select * from xchan where xchan_url = '%s' order by xchan_name_date desc", + // the most useful consistently ascending table item we have. + + $r = q("select * from xchan where xchan_url = '%s' and xchan_deleted = 0 order by xchan_name_date desc", dbesc($_REQUEST['url']) ); } if($r) { App::$poi = Libzot::zot_record_preferred($r, 'xchan_network'); } - - + + // Here, let's see if we have an xchan. If we don't, how we proceed is determined by what - // info we do have. If it's a URL, we can offer to visit it directly. If it's a webbie or - // address, we can and should try to import it. If it's just a hash, we can't continue, but we + // info we do have. If it's a URL, we can offer to visit it directly. If it's a webbie or + // address, we can and should try to import it. If it's just a hash, we can't continue, but we // probably wouldn't have a hash if we don't already have an xchan for this channel. - + if(! App::$poi) { logger('mod_chanview: fallback'); @@ -70,8 +70,8 @@ class Chanview extends \Zotlabs\Web\Controller { $zf = Zotfinger::exec($_REQUEST['url'], null); if(array_path_exists('signature/signer',$zf) && $zf['signature']['signer'] === $_REQUEST['url'] && intval($zf['signature']['header_valid'])) { - Libzot::import_xchan($j); - $r = q("select * from xchan where xchan_url = '%s'", + Libzot::import_xchan($zf['data']); + $r = q("select * from xchan where xchan_url = '%s' and xchan_deleted = 0", dbesc($_REQUEST['url']) ); if($r) { @@ -80,7 +80,7 @@ class Chanview extends \Zotlabs\Web\Controller { } if(! $r) { if(discover_by_webbie($_REQUEST['url'])) { - $r = q("select * from xchan where xchan_url = '%s'", + $r = q("select * from xchan where xchan_url = '%s' and xchan_deleted = 0", dbesc($_REQUEST['url']) ); if($r) { @@ -90,7 +90,7 @@ class Chanview extends \Zotlabs\Web\Controller { } } } - + if(! App::$poi) { notice( t('Channel not found.') . EOL); return; @@ -98,9 +98,9 @@ class Chanview extends \Zotlabs\Web\Controller { $is_zot = false; $connected = false; - + $url = App::$poi['xchan_url']; - if(in_array(App::$poi['xchan_network'], ['zot', 'zot6'])) { + if(App::$poi['xchan_network'] === 'zot6') { $is_zot = true; } if(local_channel()) { @@ -111,29 +111,29 @@ class Chanview extends \Zotlabs\Web\Controller { if($c) $connected = true; } - - // We will load the chanview template if it's a foreign network, + + // We will load the chanview template if it's a foreign network, // just so that we can provide a connect button along with a profile // photo. Chances are we can't load the remote profile into an iframe // because of cross-domain security headers. So provide a link to - // the remote profile. + // the remote profile. // If we are already connected, just go to the profile. // Zot channels will usually have a connect link. - + if($is_zot || $connected) { if($is_zot && $observer) { $url = zid($url); } goaway($url); } - else { + else { $o = replace_macros(get_markup_template('chanview.tpl'),array( '$url' => $url, '$full' => t('toggle full screen mode') )); - + return $o; } } - + } diff --git a/Zotlabs/Module/Chat.php b/Zotlabs/Module/Chat.php index 28e775f9d..323471161 100644 --- a/Zotlabs/Module/Chat.php +++ b/Zotlabs/Module/Chat.php @@ -14,7 +14,7 @@ require_once('include/bookmarks.php'); class Chat extends Controller { function init() { - + $which = null; if(argc() > 1) $which = argv(1); @@ -29,79 +29,77 @@ class Chat extends Controller { notice( t('You must be logged in to see this page.') . EOL ); return; } - + $profile = 0; $channel = App::get_channel(); - + if((local_channel()) && (argc() > 2) && (argv(2) === 'view')) { $which = $channel['channel_address']; - $profile = argv(1); + $profile = argv(1); } - + // Run profile_load() here to make sure the theme is set before // we start loading content - + profile_load($which,$profile); - + } - + function post() { - + if($_POST['room_name']) - $room = strip_tags(trim($_POST['room_name'])); - + $room = strip_tags(trim($_POST['room_name'])); + if((! $room) || (! local_channel())) return; - + $channel = App::get_channel(); - - + + if($_POST['action'] === 'drop') { logger('delete chatroom'); Chatroom::destroy($channel,array('cr_name' => $room)); goaway(z_root() . '/chat/' . $channel['channel_address']); } - + $acl = new AccessList($channel); $acl->set_from_array($_REQUEST); - + $arr = $acl->get(); $arr['name'] = $room; $arr['expire'] = intval($_POST['chat_expire']); if(intval($arr['expire']) < 0) $arr['expire'] = 0; - + Chatroom::create($channel,$arr); - + $x = q("select * from chatroom where cr_name = '%s' and cr_uid = %d limit 1", dbesc($room), intval(local_channel()) ); - + Libsync::build_sync_packet(0, array('chatroom' => $x)); - + if($x) goaway(z_root() . '/chat/' . $channel['channel_address'] . '/' . $x[0]['cr_id']); - + // that failed. Try again perhaps? - + goaway(z_root() . '/chat/' . $channel['channel_address'] . '/new'); - - + + } - - + + function get() { if(! Apps::system_app_installed(App::$profile_uid, 'Chatrooms')) { //Do not display any associated widgets at this point App::$pdl = ''; - - $o = '<b>' . t('Chatrooms App') . ' (' . t('Not Installed') . '):</b><br>'; - $o .= t('Access Controlled Chatrooms'); - return $o; + $papp = Apps::get_papp('Chatrooms'); + return Apps::app_render($papp, 'module'); } - + if(local_channel()) { $channel = App::get_channel(); nav_set_selected('Chatrooms'); @@ -113,24 +111,24 @@ class Chat extends Controller { notice( t('Permission denied.') . EOL); return; } - + if(! perm_is_allowed(App::$profile['profile_uid'],$observer,'chat')) { notice( t('Permission denied.') . EOL); return; } - + if((argc() > 3) && intval(argv(2)) && (argv(3) === 'leave')) { Chatroom::leave($observer,argv(2),$_SERVER['REMOTE_ADDR']); goaway(z_root() . '/channel/' . argv(1)); } - - + + if((argc() > 3) && intval(argv(2)) && (argv(3) === 'status')) { $ret = array('success' => false); $room_id = intval(argv(2)); if(! $room_id || ! $observer) return; - + $r = q("select * from chatroom where cr_id = %d limit 1", intval($room_id) ); @@ -139,7 +137,7 @@ class Chat extends Controller { } require_once('include/security.php'); $sql_extra = permissions_sql($r[0]['cr_uid']); - + $x = q("select * from chatroom where cr_id = %d and cr_uid = %d $sql_extra limit 1", intval($room_id), intval($r[0]['cr_uid']) @@ -155,9 +153,9 @@ class Chat extends Controller { $ret['chatroom'] = $r[0]['cr_name']; $ret['inroom'] = $y[0]['total']; } - + // figure out how to present a timestamp of the last activity, since we don't know the observer's timezone. - + $z = q("select created from chat where chat_room = %d order by created desc limit 1", intval($room_id) ); @@ -166,13 +164,13 @@ class Chat extends Controller { } json_return_and_die($ret); } - - + + if(argc() > 2 && intval(argv(2))) { - + $room_id = intval(argv(2)); $bookmark_link = get_bookmark_link($ob); - + $x = Chatroom::enter($observer,$room_id,'online',$_SERVER['REMOTE_ADDR']); if(! $x) return; @@ -180,26 +178,26 @@ class Chat extends Controller { intval($room_id), intval(App::$profile['profile_uid']) ); - + if($x) { $acl = new AccessList(false); $acl->set($x[0]); - + $private = $acl->is_private(); $room_name = $x[0]['cr_name']; if($bookmark_link) - $bookmark_link .= '&url=' . z_root() . '/chat/' . argv(1) . '/' . argv(2) . '&title=' . urlencode($x[0]['cr_name']) . (($private) ? '&private=1' : '') . '&ischat=1'; + $bookmark_link .= '&url=' . z_root() . '/chat/' . argv(1) . '/' . argv(2) . '&title=' . urlencode($x[0]['cr_name']) . (($private) ? '&private=1' : '') . '&ischat=1'; } else { notice( t('Room not found') . EOL); return; } - + $cipher = get_pconfig(local_channel(),'system','default_cipher'); if(! $cipher) $cipher = 'AES-128-CCM'; - - + + $o = replace_macros(get_markup_template('chat.tpl'),array( '$is_owner' => ((local_channel() && local_channel() == $x[0]['cr_uid']) ? true : false), '$room_name' => $room_name, @@ -223,7 +221,7 @@ class Chat extends Controller { } require_once('include/conversation.php'); - + $o = ''; $acl = new AccessList($channel); @@ -246,12 +244,12 @@ class Chat extends Controller { '$deny_gid' => acl2json($channel_acl['deny_gid']), '$lockstate' => $lockstate, '$submit' => t('Submit') - + )); } $rooms = Chatroom::roomlist(App::$profile['profile_uid']); - + $o .= replace_macros(get_markup_template('chatrooms.tpl'), array( '$header' => sprintf( t('%1$s\'s Chatrooms'), App::$profile['fullname']), '$name' => t('Name'), @@ -259,15 +257,15 @@ class Chat extends Controller { '$nickname' => App::$profile['channel_address'], '$rooms' => $rooms, '$norooms' => t('No chatrooms available'), - '$newroom' => t('Create New'), + '$newroom' => t('Add Room'), '$is_owner' => ((local_channel() && local_channel() == App::$profile['profile_uid']) ? 1 : 0), '$chatroom_new' => $chatroom_new, '$expire' => t('Expiration'), '$expire_unit' => t('min') //minutes )); - + return $o; - + } - + } diff --git a/Zotlabs/Module/Cloud.php b/Zotlabs/Module/Cloud.php index f595e0fac..6ff95b5cf 100644 --- a/Zotlabs/Module/Cloud.php +++ b/Zotlabs/Module/Cloud.php @@ -8,7 +8,11 @@ namespace Zotlabs\Module; */ use Sabre\DAV as SDAV; -use \Zotlabs\Storage; +use \Zotlabs\Web\Controller; +use \Zotlabs\Storage\BasicAuth; +use \Zotlabs\Storage\Directory; +use \Zotlabs\Storage\Browser; + // composer autoloader for SabreDAV require_once('vendor/autoload.php'); @@ -20,7 +24,7 @@ require_once('include/attach.php'); * @brief Cloud Module. * */ -class Cloud extends \Zotlabs\Web\Controller { +class Cloud extends Controller { /** * @brief Fires up the SabreDAV server. @@ -42,7 +46,7 @@ class Cloud extends \Zotlabs\Web\Controller { - $auth = new \Zotlabs\Storage\BasicAuth(); + $auth = new BasicAuth(); $ob_hash = get_observer_hash(); @@ -72,7 +76,7 @@ class Cloud extends \Zotlabs\Web\Controller { if($x !== \App::$query_string) goaway(z_root() . '/' . $x); - $rootDirectory = new \Zotlabs\Storage\Directory('/', $auth); + $rootDirectory = new Directory('/', [], $auth); // A SabreDAV server-object $server = new SDAV\Server($rootDirectory); @@ -85,7 +89,7 @@ class Cloud extends \Zotlabs\Web\Controller { $is_readable = false; // provide a directory view for the cloud in Hubzilla - $browser = new \Zotlabs\Storage\Browser($auth); + $browser = new Browser($auth); $auth->setBrowserPlugin($browser); $server->addPlugin($browser); @@ -101,17 +105,17 @@ class Cloud extends \Zotlabs\Web\Controller { // All we need to do now, is to fire up the server - $server->exec(); + $server->start(); if($browser->build_page) construct_page(); - + killme(); } function DAVException($err) { - + if($err instanceof \Sabre\DAV\Exception\NotFound) { notice( t('Not found') . EOL); } @@ -119,14 +123,15 @@ class Cloud extends \Zotlabs\Web\Controller { notice( t('Permission denied') . EOL); } elseif($err instanceof \Sabre\DAV\Exception\NotImplemented) { - notice( t('Please refresh page') . EOL); + // notice( t('Please refresh page') . EOL); + goaway(z_root() . '/' . \App::$query_string); } else { notice( t('Unknown error') . EOL); } construct_page(); - + killme(); } diff --git a/Zotlabs/Module/Connect.php b/Zotlabs/Module/Connect.php index 31da42035..b934cb963 100644 --- a/Zotlabs/Module/Connect.php +++ b/Zotlabs/Module/Connect.php @@ -18,11 +18,11 @@ class Connect extends Controller { App::$error = 404; return; } - + $r = q("select * from channel where channel_address = '%s' limit 1", dbesc($which) ); - + if($r) App::$data['channel'] = $r[0]; @@ -30,36 +30,36 @@ class Connect extends Controller { profile_load($which,''); } - + function post() { - + if(! array_key_exists('channel', App::$data)) return; $channel_id = App::$data['channel']['channel_id']; $edit = ((local_channel() && (local_channel() == $channel_id)) ? true : false); - + if($edit) { $has_premium = ((App::$data['channel']['channel_pageflags'] & PAGE_PREMIUM) ? 1 : 0); $premium = (($_POST['premium']) ? intval($_POST['premium']) : 0); $text = escape_tags($_POST['text']); - + if($has_premium != $premium) { $r = q("update channel set channel_pageflags = ( channel_pageflags %s %d ) where channel_id = %d", db_getfunc('^'), intval(PAGE_PREMIUM), - intval(local_channel()) + intval(local_channel()) ); - + \Zotlabs\Daemon\Master::Summon(array('Notifier','refresh_all',$channel_id)); } set_pconfig($channel_id,'system','selltext',$text); // reload the page completely to get fresh data goaway(z_root() . '/' . App::$query_string); - + } - + $url = ''; $observer = App::get_observer(); if(($observer) && ($_POST['submit'] === t('Continue'))) { @@ -70,18 +70,18 @@ class Connect extends Controller { dbesc($observer['xchan_hash']) ); if($r) - $url = $r[0]['hubloc_url'] . '/follow?f=&url=' . urlencode(channel_reddress(App::$data['channel'])); + $url = $r[0]['hubloc_url'] . '/follow?f=&interactive=1&url=' . urlencode(channel_reddress(App::$data['channel'])); } } if($url) goaway($url . '&confirm=1'); else notice('Unable to connect to your home hub location.'); - + } - - - + + + function get() { if(! array_key_exists('channel', App::$data)) @@ -90,11 +90,11 @@ class Connect extends Controller { $channel_id = App::$data['channel']['channel_id']; $edit = ((local_channel() && (local_channel() == $channel_id)) ? true : false); - + $text = get_pconfig($channel_id,'system','selltext'); - + if($edit) { - + $o = replace_macros(get_markup_template('sellpage_edit.tpl'),array( '$header' => t('Premium Channel Setup'), '$address' => App::$data['channel']['channel_address'], @@ -105,36 +105,36 @@ class Connect extends Controller { '$lbl2' => t('Potential connections will then see the following text before proceeding:'), '$desc2' => t('By continuing, I certify that I have complied with any instructions provided on this page.'), '$submit' => t('Submit'), - - + + )); return $o; } else { if(! $text) $text = t('(No specific instructions have been provided by the channel owner.)'); - + $submit = replace_macros(get_markup_template('sellpage_submit.tpl'), array( - '$continue' => t('Continue'), + '$continue' => t('Continue'), '$address' => App::$data['channel']['channel_address'] )); - + $o = replace_macros(get_markup_template('sellpage_view.tpl'),array( '$header' => t('Restricted or Premium Channel'), '$desc' => t('This channel may require additional steps or acknowledgement of the following conditions prior to connecting:'), - '$text' => prepare_text($text), - + '$text' => prepare_text($text), + '$desc2' => t('By continuing, I certify that I have complied with any instructions provided on this page.'), '$submit' => $submit, - + )); - + $arr = array('channel' => App::$data['channel'],'observer' => App::get_observer(), 'sellpage' => $o, 'submit' => $submit); call_hooks('connect_premium', $arr); $o = $arr['sellpage']; - + } - + return $o; } } diff --git a/Zotlabs/Module/Connections.php b/Zotlabs/Module/Connections.php index 7dc301623..5025f4e22 100644 --- a/Zotlabs/Module/Connections.php +++ b/Zotlabs/Module/Connections.php @@ -109,6 +109,7 @@ class Connections extends \Zotlabs\Web\Controller { case 'all': $head = t('All'); + break; default: $search_flags = " and abook_blocked = 0 and abook_ignored = 0 and abook_hidden = 0 and abook_archived = 0 and abook_not_here = 0 "; $active = true; @@ -238,7 +239,7 @@ class Connections extends \Zotlabs\Web\Controller { } $r = q("SELECT COUNT(abook.abook_id) AS total FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash - where abook_channel = %d and abook_self = 0 and xchan_deleted = 0 and xchan_orphan = 0 $sql_extra $sql_extra2 ", + where abook_channel = %d and abook_self = 0 and xchan_deleted = 0 and xchan_orphan = 0 $sql_extra ", intval(local_channel()) ); if($r) { @@ -247,7 +248,7 @@ class Connections extends \Zotlabs\Web\Controller { } $r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash - WHERE abook_channel = %d and abook_self = 0 and xchan_deleted = 0 and xchan_orphan = 0 $sql_extra $sql_extra2 ORDER BY $sql_order LIMIT %d OFFSET %d ", + WHERE abook_channel = %d and abook_self = 0 and xchan_deleted = 0 and xchan_orphan = 0 $sql_extra ORDER BY $sql_order LIMIT %d OFFSET %d ", intval(local_channel()), intval(App::$pager['itemspage']), intval(App::$pager['start']) diff --git a/Zotlabs/Module/Connedit.php b/Zotlabs/Module/Connedit.php index becf8460d..7fabf1224 100644 --- a/Zotlabs/Module/Connedit.php +++ b/Zotlabs/Module/Connedit.php @@ -9,6 +9,7 @@ namespace Zotlabs\Module; use App; use Zotlabs\Lib\Apps; +use Zotlabs\Lib\Crypto; use Zotlabs\Lib\Libzot; use Zotlabs\Lib\Libsync; use Zotlabs\Daemon\Master; @@ -32,69 +33,69 @@ class Connedit extends Controller { */ function init() { - + if(! local_channel()) return; - + if((argc() >= 2) && intval(argv(1))) { $r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash - WHERE abook_channel = %d and abook_id = %d LIMIT 1", + WHERE abook_channel = %d and abook_id = %d and xchan_deleted = 0 LIMIT 1", intval(local_channel()), intval(argv(1)) ); if($r) { - App::$poi = array_shift($r); + App::$poi = $r[0]; } } - + $channel = App::get_channel(); if($channel) head_set_icon($channel['xchan_photo_s']); - + } - + /* @brief Evaluate posted values and set changes * */ - + function post() { - + if(! local_channel()) return; - + $contact_id = intval(argv(1)); if(! $contact_id) return; - + $channel = App::get_channel(); - + // TODO if configured for hassle-free permissions, we'll post the form with ajax as soon as the // connection enable is toggled to a special autopost url and set permissions immediately, leaving // the other form elements alone pending a manual submit of the form. The downside is that there // will be a window of opportunity when the permissions have been set but before you've had a chance // to review and possibly restrict them. The upside is we won't have to warn you that your connection // can't do anything until you save the bloody form. - + $autopost = (((argc() > 2) && (argv(2) === 'auto')) ? true : false); - + $orig_record = q("SELECT * FROM abook WHERE abook_id = %d AND abook_channel = %d LIMIT 1", intval($contact_id), intval(local_channel()) ); - + if(! $orig_record) { notice( t('Could not access contact record.') . EOL); goaway(z_root() . '/connections'); return; // NOTREACHED } - + call_hooks('contact_edit_post', $_POST); - + $vc = get_abconfig(local_channel(),$orig_record['abook_xchan'],'system','vcard'); - $vcard = (($vc) ? \Sabre\VObject\Reader::read($vc) : null); + $vcard = (($vc) ? \Sabre\VObject\Reader::read($vc) : null); $serialised_vcard = update_vcard($_REQUEST,$vcard); if($serialised_vcard) set_abconfig(local_channel(),$orig_record[0]['abook_xchan'],'system','vcard',$serialised_vcard); @@ -107,8 +108,8 @@ class Connedit extends Controller { $autoperms = null; $is_self = false; } - - + + $profile_id = ((array_key_exists('profile_assign',$_POST)) ? $_POST['profile_assign'] : $orig_record[0]['abook_profile']); if($profile_id) { @@ -121,17 +122,17 @@ class Connedit extends Controller { return; } } - + $abook_incl = ((array_key_exists('abook_incl',$_POST)) ? escape_tags($_POST['abook_incl']) : $orig_record[0]['abook_incl']); $abook_excl = ((array_key_exists('abook_excl',$_POST)) ? escape_tags($_POST['abook_excl']) : $orig_record[0]['abook_excl']); $hidden = intval($_POST['hidden']); - + $priority = intval($_POST['poll']); if($priority > 5 || $priority < 0) $priority = 0; - + if(! array_key_exists('closeness',$_POST)) { $_POST['closeness'] = 80; } @@ -139,15 +140,15 @@ class Connedit extends Controller { if($closeness < 0 || $closeness > 99) { $closeness = 80; } - + $rating = intval($_POST['rating']); if($rating < (-10)) $rating = (-10); if($rating > 10) $rating = 10; - + $rating_text = trim(escape_tags($_REQUEST['rating_text'])); - + $all_perms = Permissions::Perms(); if($all_perms) { @@ -168,27 +169,27 @@ class Connedit extends Controller { } } - if(! is_null($autoperms)) + if(! is_null($autoperms)) set_pconfig($channel['channel_id'],'system','autoperms',$autoperms); - + $new_friend = false; - + // only store a record and notify the directory if the rating changed if(! $is_self) { - + $signed = $orig_record[0]['abook_xchan'] . '.' . $rating . '.' . $rating_text; - $sig = base64url_encode(rsa_sign($signed,$channel['channel_prvkey'])); + $sig = base64url_encode(Crypto::sign($signed,$channel['channel_prvkey'])); $rated = ((intval($rating) || strlen($rating_text)) ? true : false); - + $record = 0; - + $z = q("select * from xlink where xlink_xchan = '%s' and xlink_link = '%s' and xlink_static = 1 limit 1", dbesc($channel['channel_hash']), dbesc($orig_record[0]['abook_xchan']) ); - + if($z) { if(($z[0]['xlink_rating'] != $rating) || ($z[0]['xlink_rating_text'] != $rating_text)) { $record = $z[0]['xlink_id']; @@ -219,22 +220,19 @@ class Connedit extends Controller { if($z) $record = $z[0]['xlink_id']; } - if($record) { - Master::Summon(array('Ratenotif','rating',$record)); - } } - + if(($_REQUEST['pending']) && intval($orig_record[0]['abook_pending'])) { $new_friend = true; - + // @fixme it won't be common, but when you accept a new connection request // the permissions will now be that of your permissions role and ignore // any you may have set manually on the form. We'll probably see a bug if somebody // tries to set the permissions *and* approve the connection in the same // request. The workaround is to approve the connection, then go back and // adjust permissions as desired. - + $p = Permissions::connect_perms(local_channel()); $my_perms = $p['perms']; if($my_perms) { @@ -247,7 +245,7 @@ class Connedit extends Controller { $abook_pending = (($new_friend) ? 0 : $orig_record[0]['abook_pending']); - + $r = q("UPDATE abook SET abook_profile = '%s', abook_closeness = %d, abook_pending = %d, abook_incl = '%s', abook_excl = '%s' where abook_id = %d AND abook_channel = %d", @@ -259,7 +257,7 @@ class Connedit extends Controller { intval($contact_id), intval(local_channel()) ); - + if($r) info( t('Connection updated.') . EOL); else @@ -267,16 +265,16 @@ class Connedit extends Controller { if(! intval(App::$poi['abook_self'])) { if($new_friend) { - Master::Summon( [ 'Notifier', 'permission_accept', $contact_id ] ); + Master::Summon( [ 'Notifier', 'permission_accept', $contact_id ] ); } - Master::Summon( [ - 'Notifier', - (($new_friend) ? 'permission_create' : 'permission_update'), - $contact_id + Master::Summon( [ + 'Notifier', + (($new_friend) ? 'permission_create' : 'permission_update'), + $contact_id ]); } - + if($new_friend) { $default_group = $channel['channel_default_group']; if($default_group) { @@ -285,11 +283,11 @@ class Connedit extends Controller { if($g) group_add_member(local_channel(),'',App::$poi['abook_xchan'],$g['id']); } - + // Check if settings permit ("post new friend activity" is allowed, and // friends in general or this friend in particular aren't hidden) // and send out a new friend activity - + $pr = q("select * from profile where uid = %d and is_default = 1 and hide_friends = 0", intval($channel['channel_id']) ); @@ -305,23 +303,23 @@ class Connedit extends Controller { $xarr['deny_cid'] = $channel['channel_deny_cid']; $xarr['deny_gid'] = $channel['channel_deny_gid']; $xarr['item_private'] = (($xarr['allow_cid']||$xarr['allow_gid']||$xarr['deny_cid']||$xarr['deny_gid']) ? 1 : 0); - + $xarr['body'] = '[zrl=' . $channel['xchan_url'] . ']' . $channel['xchan_name'] . '[/zrl]' . ' ' . t('is now connected to') . ' ' . '[zrl=' . App::$poi['xchan_url'] . ']' . App::$poi['xchan_name'] . '[/zrl]'; - + $xarr['body'] .= "\n\n\n" . '[zrl=' . App::$poi['xchan_url'] . '][zmg=80x80]' . App::$poi['xchan_photo_m'] . '[/zmg][/zrl]'; - + post_activity_item($xarr); - + } - - + + // pull in a bit of content if there is any to pull in Master::Summon(array('Onepoll',$contact_id)); - + } - + // Refresh the structure in memory with the new data - + $r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d and abook_id = %d LIMIT 1", @@ -331,34 +329,34 @@ class Connedit extends Controller { if($r) { App::$poi = $r[0]; } - + if($new_friend) { $arr = array('channel_id' => local_channel(), 'abook' => App::$poi); call_hooks('accept_follow', $arr); } - + $this->connedit_clone($a); - + if(($_REQUEST['pending']) && (!$_REQUEST['done'])) goaway(z_root() . '/connections/ifpending'); - + return; - + } - + /* @brief Clone connection * * */ - + function connedit_clone(&$a) { - + if(! App::$poi) return; - - + + $channel = App::get_channel(); - + $r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d and abook_id = %d LIMIT 1", @@ -368,40 +366,40 @@ class Connedit extends Controller { if($r) { App::$poi = array_shift($r); } - + $clone = App::$poi; - + unset($clone['abook_id']); unset($clone['abook_account']); unset($clone['abook_channel']); - + $abconfig = load_abconfig($channel['channel_id'],$clone['abook_xchan']); if($abconfig) $clone['abconfig'] = $abconfig; - + Libsync::build_sync_packet(0 /* use the current local_channel */, array('abook' => array($clone))); } - + /* @brief Generate content of connection edit page * * */ - + function get() { - + $sort_type = 0; $o = ''; - + if(! local_channel()) { notice( t('Permission denied.') . EOL); return login(); } - + $section = ((array_key_exists('section',$_REQUEST)) ? $_REQUEST['section'] : ''); $channel = App::get_channel(); - + $yes_no = array(t('No'),t('Yes')); - + $connect_perms = Permissions::connect_perms(local_channel()); $o .= "<script>function connectDefaultShare() { @@ -415,31 +413,31 @@ class Connedit extends Controller { } } $o .= " }\n</script>\n"; - + if(argc() == 3) { - + $contact_id = intval(argv(1)); if(! $contact_id) return; - + $cmd = argv(2); $orig_record = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash - WHERE abook_id = %d AND abook_channel = %d AND abook_self = 0 LIMIT 1", + WHERE abook_id = %d AND abook_channel = %d AND abook_self = 0 and xchan_deleted = 0 LIMIT 1", intval($contact_id), intval(local_channel()) ); - + if(! count($orig_record)) { notice( t('Could not access address book record.') . EOL); goaway(z_root() . '/connections'); } - + if($cmd === 'update') { // pull feed and consume it, which should subscribe to the hub. Master::Summon(array('Poller',$contact_id)); goaway(z_root() . '/connedit/' . $contact_id); - + } if($cmd === 'fetchvc') { @@ -474,25 +472,20 @@ class Connedit extends Controller { dbesc($orig_record[0]['xchan_hash']) ); $cmd = 'refresh'; - } + } if($cmd === 'refresh') { - if($orig_record[0]['xchan_network'] === 'zot') { - if(! zot_refresh($orig_record[0],App::get_channel())) - notice( t('Refresh failed - channel is currently unavailable.') ); - } - elseif($orig_record[0]['xchan_network'] === 'zot6') { + if($orig_record[0]['xchan_network'] === 'zot6') { if(! Libzot::refresh($orig_record[0],App::get_channel())) notice( t('Refresh failed - channel is currently unavailable.') ); } else { - // if you are on a different network we'll force a refresh of the connection basic info Master::Summon(array('Notifier','permission_update',$contact_id)); } goaway(z_root() . '/connedit/' . $contact_id); } - + if($cmd === 'block') { if(abook_toggle_flag($orig_record[0],ABOOK_FLAG_BLOCKED)) { $this->connedit_clone($a); @@ -501,7 +494,7 @@ class Connedit extends Controller { notice(t('Unable to set address book parameters.') . EOL); goaway(z_root() . '/connedit/' . $contact_id); } - + if($cmd === 'ignore') { if(abook_toggle_flag($orig_record[0],ABOOK_FLAG_IGNORED)) { $this->connedit_clone($a); @@ -510,7 +503,7 @@ class Connedit extends Controller { notice(t('Unable to set address book parameters.') . EOL); goaway(z_root() . '/connedit/' . $contact_id); } - + if($cmd === 'archive') { if(abook_toggle_flag($orig_record[0],ABOOK_FLAG_ARCHIVED)) { $this->connedit_clone($a); @@ -519,7 +512,7 @@ class Connedit extends Controller { notice(t('Unable to set address book parameters.') . EOL); goaway(z_root() . '/connedit/' . $contact_id); } - + if($cmd === 'hide') { if(abook_toggle_flag($orig_record[0],ABOOK_FLAG_HIDDEN)) { $this->connedit_clone($a); @@ -528,10 +521,10 @@ class Connedit extends Controller { notice(t('Unable to set address book parameters.') . EOL); goaway(z_root() . '/connedit/' . $contact_id); } - + // We'll prevent somebody from unapproving an already approved contact. // Though maybe somebody will want this eventually (??) - + if($cmd === 'approve') { if(intval($orig_record[0]['abook_pending'])) { if(abook_toggle_flag($orig_record[0],ABOOK_FLAG_PENDING)) { @@ -542,10 +535,10 @@ class Connedit extends Controller { } goaway(z_root() . '/connedit/' . $contact_id); } - - + + if($cmd === 'drop') { - + contact_remove(local_channel(), $orig_record[0]['abook_id']); Master::Summon( [ 'Notifier', 'purge', local_channel(), $orig_record[0]['xchan_hash'] ] ); @@ -556,17 +549,17 @@ class Connedit extends Controller { 'entry_deleted' => true)) ) ); - + info( t('Connection has been removed.') . EOL ); if(x($_SESSION,'return_url')) goaway(z_root() . '/' . $_SESSION['return_url']); goaway(z_root() . '/contacts'); - + } } - + if(App::$poi) { - + $abook_prev = 0; $abook_next = 0; @@ -595,14 +588,14 @@ class Connedit extends Controller { } $tools = array( - + 'view' => array( 'label' => t('View Profile'), 'url' => chanlink_cid($contact['abook_id']), 'sel' => '', 'title' => sprintf( t('View %s\'s profile'), $contact['xchan_name']), ), - + 'refresh' => array( 'label' => t('Refresh Permissions'), 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/refresh', @@ -616,14 +609,14 @@ class Connedit extends Controller { 'sel' => '', 'title' => t('Fetch updated photo'), ), - + 'recent' => array( 'label' => t('Recent Activity'), 'url' => z_root() . '/network/?f=&cid=' . $contact['abook_id'], 'sel' => '', 'title' => t('View recent posts and comments'), ), - + 'block' => array( 'label' => (intval($contact['abook_blocked']) ? t('Unblock') : t('Block')), 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/block', @@ -631,7 +624,7 @@ class Connedit extends Controller { 'title' => t('Block (or Unblock) all communications with this connection'), 'info' => (intval($contact['abook_blocked']) ? t('This connection is blocked!') : ''), ), - + 'ignore' => array( 'label' => (intval($contact['abook_ignored']) ? t('Unignore') : t('Ignore')), 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/ignore', @@ -639,7 +632,7 @@ class Connedit extends Controller { 'title' => t('Ignore (or Unignore) all inbound communications from this connection'), 'info' => (intval($contact['abook_ignored']) ? t('This connection is ignored!') : ''), ), - + 'archive' => array( 'label' => (intval($contact['abook_archived']) ? t('Unarchive') : t('Archive')), 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/archive', @@ -647,7 +640,7 @@ class Connedit extends Controller { 'title' => t('Archive (or Unarchive) this connection - mark channel dead but keep content'), 'info' => (intval($contact['abook_archived']) ? t('This connection is archived!') : ''), ), - + 'hide' => array( 'label' => (intval($contact['abook_hidden']) ? t('Unhide') : t('Hide')), 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/hide', @@ -655,18 +648,18 @@ class Connedit extends Controller { 'title' => t('Hide or Unhide this connection from your other connections'), 'info' => (intval($contact['abook_hidden']) ? t('This connection is hidden!') : ''), ), - + 'delete' => array( 'label' => t('Delete'), 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/drop', 'sel' => '', 'title' => t('Delete this connection'), ), - + ); - if($contact['xchan_network'] === 'zot') { + if($contact['xchan_network'] === 'zot6') { $tools['fetchvc'] = [ 'label' => t('Fetch Vcard'), 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/fetchvc', @@ -684,24 +677,24 @@ class Connedit extends Controller { 'sel' => '', 'title' => t('Open Individual Permissions section by default'), ]; - + $self = false; - + if(intval($contact['abook_self'])) { $self = true; $abook_prev = $abook_next = 0; } - + $vc = get_abconfig(local_channel(),$contact['abook_xchan'],'system','vcard'); - $vctmp = (($vc) ? \Sabre\VObject\Reader::read($vc) : null); + $vctmp = (($vc) ? \Sabre\VObject\Reader::read($vc) : null); $vcard = (($vctmp) ? get_vcard_array($vctmp,$contact['abook_id']) : [] ); if(! $vcard) $vcard['fn'] = $contact['xchan_name']; $tpl = get_markup_template("abook_edit.tpl"); - + if(Apps::system_app_installed(local_channel(),'Affinity Tool')) { $sections['affinity'] = [ @@ -710,7 +703,7 @@ class Connedit extends Controller { 'sel' => '', 'title' => t('Open Set Affinity section by default'), ]; - + $labels = [ t('Me'), t('Family'), @@ -720,7 +713,7 @@ class Connedit extends Controller { ]; call_hooks('affinity_labels',$labels); $label_str = ''; - + if($labels) { foreach($labels as $l) { if($label_str) { @@ -731,11 +724,11 @@ class Connedit extends Controller { $label_str .= "'" . $l . "'"; } } - + $slider_tpl = get_markup_template('contact_slider.tpl'); - + $slideval = intval($contact['abook_closeness']); - + $slide = replace_macros($slider_tpl,array( '$min' => 1, '$val' => $slideval, @@ -751,22 +744,22 @@ class Connedit extends Controller { 'title' => t('Open Custom Filter section by default'), ]; } - + $rating_val = 0; $rating_text = ''; - + $xl = q("select * from xlink where xlink_xchan = '%s' and xlink_link = '%s' and xlink_static = 1", dbesc($channel['channel_hash']), dbesc($contact['xchan_hash']) ); - + if($xl) { $rating_val = intval($xl[0]['xlink_rating']); $rating_text = $xl[0]['xlink_rating_text']; } - + $rating_enabled = get_config('system','rating_enabled'); - + if($rating_enabled) { $rating = replace_macros(get_markup_template('rating_slider.tpl'),array( '$min' => -10, @@ -776,28 +769,28 @@ class Connedit extends Controller { else { $rating = false; } - - + + $perms = array(); $channel = App::get_channel(); - + $global_perms = Permissions::Perms(); $existing = get_all_perms(local_channel(),$contact['abook_xchan'],false); - + $unapproved = array('pending', t('Approve this connection'), '', t('Accept connection to allow communication'), array(t('No'),('Yes'))); - + $multiprofs = ((feature_enabled(local_channel(),'multi_profiles')) ? true : false); - + if($slide && !$multiprofs) $affinity = t('Set Affinity'); - + if(!$slide && $multiprofs) $affinity = t('Set Profile'); - + if($slide && $multiprofs) $affinity = t('Set Affinity & Profile'); - + $theirs = q("select * from abconfig where chan = %d and xchan = '%s' and cat = 'their_perms'", intval(local_channel()), dbesc($contact['abook_xchan']) @@ -812,20 +805,20 @@ class Connedit extends Controller { foreach($global_perms as $k => $v) { $thisperm = get_abconfig(local_channel(),$contact['abook_xchan'],'my_perms',$k); //fixme - + $checkinherited = PermissionLimits::Get(local_channel(),$k); - + // For auto permissions (when $self is true) we don't want to look at existing // permissions because they are enabled for the channel owner if((! $self) && ($existing[$k])) $thisperm = "1"; - - + + $perms[] = array('perms_' . $k, $v, ((array_key_exists($k,$their_perms)) ? intval($their_perms[$k]) : ''),$thisperm, 1, (($checkinherited & PERMS_SPECIFIC) ? '' : '1'), '', $checkinherited); } - + $pcat = new Permcat(local_channel()); $pcatlist = $pcat->listing(); $permcats = []; @@ -838,23 +831,23 @@ class Connedit extends Controller { $locstr = locations_by_netid($contact['xchan_hash']); if(! $locstr) $locstr = unpunify($contact['xchan_url']); - + $clone_warn = ''; - $clonable = (in_array($contact['xchan_network'],['zot', 'zot6', 'rss']) ? true : false); + $clonable = in_array($contact['xchan_network'], ['zot6', 'rss']); if(! $clonable) { $clone_warn = '<strong>'; - $clone_warn .= ((intval($contact['abook_not_here'])) + $clone_warn .= ((intval($contact['abook_not_here'])) ? t('This connection is unreachable from this location.') : t('This connection may be unreachable from other channel locations.') ); $clone_warn .= '</strong><br>' . t('Location independence is not supported by their network.'); } - + if(intval($contact['abook_not_here']) && $unclonable) $not_here = t('This connection is unreachable from this location. Location independence is not supported by their network.'); - + $o .= replace_macros($tpl, [ '$header' => (($self) ? t('Connection Default Permissions') : sprintf( t('Connection: %s'),$contact['xchan_name'])), '$autoperms' => array('autoperms',t('Apply these permissions automatically'), ((get_pconfig(local_channel(),'system','autoperms')) ? 1 : 0), t('Connection requests will be approved without your interaction'), $yes_no), @@ -910,7 +903,7 @@ class Connedit extends Controller { '$name' => $contact['xchan_name'], '$abook_prev' => $abook_prev, '$abook_next' => $abook_next, - '$vcard_label' => t('Details'), + '$vcard_label' => t('Details'), '$displayname' => $displayname, '$name_label' => t('Name'), '$org_label' => t('Organisation'), @@ -939,13 +932,13 @@ class Connedit extends Controller { '$zip_code' => t('ZIP Code'), '$country' => t('Country') ]); - + $arr = array('contact' => $contact,'output' => $o); - + call_hooks('contact_edit', $arr); - + return $arr['output']; - - } + + } } } diff --git a/Zotlabs/Module/Dav.php b/Zotlabs/Module/Dav.php index adab25e45..96fe2c898 100644 --- a/Zotlabs/Module/Dav.php +++ b/Zotlabs/Module/Dav.php @@ -51,11 +51,12 @@ class Dav extends \Zotlabs\Web\Controller { if($sigblock) { $keyId = str_replace('acct:','',$sigblock['keyId']); if($keyId) { - $r = q("select * from hubloc where hubloc_addr = '%s' limit 1", + $r = q("select * from hubloc where hubloc_id_url = '%s'", dbesc($keyId) ); if($r) { - $c = channelx_by_hash($r[0]['hubloc_hash']); + $r = Libzot::zot_record_preferred($r); + $c = channelx_by_hash($r['hubloc_hash']); if($c) { $a = q("select * from account where account_id = %d limit 1", intval($c['channel_account_id']) @@ -99,7 +100,7 @@ class Dav extends \Zotlabs\Web\Controller { $auth->setRealm(ucfirst(\Zotlabs\Lib\System::get_platform_name()) . ' ' . 'WebDAV'); - $rootDirectory = new \Zotlabs\Storage\Directory('/', $auth); + $rootDirectory = new \Zotlabs\Storage\Directory('/', [], $auth); // A SabreDAV server-object $server = new SDAV\Server($rootDirectory); @@ -123,7 +124,7 @@ class Dav extends \Zotlabs\Web\Controller { // $server->addPlugin(new \Zotlabs\Storage\QuotaPlugin($auth)); // All we need to do now, is to fire up the server - $server->exec(); + $server->start(); killme(); } diff --git a/Zotlabs/Module/Defperms.php b/Zotlabs/Module/Defperms.php index f2f7c10e5..309a5a65a 100644 --- a/Zotlabs/Module/Defperms.php +++ b/Zotlabs/Module/Defperms.php @@ -19,13 +19,13 @@ class Defperms extends Controller { */ function init() { - + if(! local_channel()) return; if(! Apps::system_app_installed(local_channel(), 'Default Permissions')) return; - + $r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_self = 1 and abook_channel = %d LIMIT 1", @@ -37,39 +37,39 @@ class Defperms extends Controller { $channel = App::get_channel(); if($channel) - head_set_icon($channel['xchan_photo_s']); + head_set_icon($channel['xchan_photo_s']); } - + /* @brief Evaluate posted values and set changes * */ - + function post() { - + if(! local_channel()) return; if(! Apps::system_app_installed(local_channel(), 'Default Permissions')) return; - + $contact_id = intval(argv(1)); if(! $contact_id) return; - + $channel = App::get_channel(); - + $orig_record = q("SELECT * FROM abook WHERE abook_id = %d AND abook_channel = %d LIMIT 1", intval($contact_id), intval(local_channel()) ); - + if(! $orig_record) { notice( t('Could not access contact record.') . EOL); goaway(z_root() . '/connections'); return; // NOTREACHED } - + if(intval($orig_record[0]['abook_self'])) { $autoperms = intval($_POST['autoperms']); @@ -79,8 +79,8 @@ class Defperms extends Controller { $autoperms = null; $is_self = false; } - - + + $all_perms = \Zotlabs\Access\Permissions::Perms(); if($all_perms) { @@ -105,15 +105,15 @@ class Defperms extends Controller { } } - if(! is_null($autoperms)) + if(! is_null($autoperms)) set_pconfig($channel['channel_id'],'system','autoperms',$autoperms); - - + + notice( t('Settings updated.') . EOL); - + // Refresh the structure in memory with the new data - + $r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d and abook_id = %d LIMIT 1", @@ -123,28 +123,28 @@ class Defperms extends Controller { if($r) { App::$poi = $r[0]; } - - + + $this->defperms_clone($a); - + goaway(z_root() . '/defperms'); - + return; - + } - + /* @brief Clone connection * * */ - + function defperms_clone(&$a) { - + if(! App::$poi) return; - + $channel = App::get_channel(); - + $r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d and abook_id = %d LIMIT 1", @@ -154,30 +154,30 @@ class Defperms extends Controller { if($r) { App::$poi = array_shift($r); } - + $clone = App::$poi; - + unset($clone['abook_id']); unset($clone['abook_account']); unset($clone['abook_channel']); - + $abconfig = load_abconfig($channel['channel_id'],$clone['abook_xchan']); if($abconfig) $clone['abconfig'] = $abconfig; - + Libsync::build_sync_packet(0 /* use the current local_channel */, array('abook' => array($clone))); } - + /* @brief Generate content of connection default permissions page * * */ - + function get() { - + $sort_type = 0; $o = ''; - + if(! local_channel()) { notice( t('Permission denied.') . EOL); return login(); @@ -186,17 +186,15 @@ class Defperms extends Controller { if(! Apps::system_app_installed(local_channel(), 'Default Permissions')) { //Do not display any associated widgets at this point App::$pdl = ''; - - $o = '<b>' . t('Default Permissions App') . ' (' . t('Not Installed') . '):</b><br>'; - $o .= t('Set custom default permissions for new connections'); - return $o; + $papp = Apps::get_papp('Default Permissions'); + return Apps::app_render($papp, 'module'); } - + $section = ((array_key_exists('section',$_REQUEST)) ? $_REQUEST['section'] : ''); $channel = App::get_channel(); - + $yes_no = array(t('No'),t('Yes')); - + $connect_perms = \Zotlabs\Access\Permissions::connect_perms(local_channel()); $o .= "<script>function connectDefaultShare() { @@ -210,28 +208,28 @@ class Defperms extends Controller { } } $o .= " }\n</script>\n"; - + if(App::$poi) { - + $sections = []; $self = false; - + $tpl = get_markup_template('defperms.tpl'); - - + + $perms = array(); $channel = App::get_channel(); $contact = App::$poi; - + $global_perms = \Zotlabs\Access\Permissions::Perms(); $hidden_perms = []; - + foreach($global_perms as $k => $v) { $thisperm = get_abconfig(local_channel(),$contact['abook_xchan'],'my_perms',$k); - + $checkinherited = \Zotlabs\Access\PermissionLimits::Get(local_channel(),$k); $inherited = (($checkinherited & PERMS_SPECIFIC) ? false : true); @@ -241,7 +239,7 @@ class Defperms extends Controller { $hidden_perms[] = [ 'perms_' . $k, intval($thisperm) ]; } } - + $pcat = new \Zotlabs\Lib\Permcat(local_channel()); $pcatlist = $pcat->listing(); $permcats = []; @@ -272,13 +270,13 @@ class Defperms extends Controller { '$contact_id' => $contact['abook_id'], '$name' => $contact['xchan_name'], ]); - + $arr = array('contact' => $contact,'output' => $o); - + call_hooks('contact_edit', $arr); - + return $arr['output']; - - } + + } } } diff --git a/Zotlabs/Module/Directory.php b/Zotlabs/Module/Directory.php index e1bf0f6cf..b39887c9e 100644 --- a/Zotlabs/Module/Directory.php +++ b/Zotlabs/Module/Directory.php @@ -4,9 +4,10 @@ namespace Zotlabs\Module; use App; use Zotlabs\Web\Controller; +use Zotlabs\Lib\Libzotdir; + require_once('include/socgraph.php'); -require_once('include/dir_fns.php'); require_once('include/bbcode.php'); require_once('include/html2plain.php'); @@ -15,7 +16,7 @@ class Directory extends Controller { function init() { App::set_pager_itemspage(30); - + if(local_channel() && x($_GET,'ignore')) { q("insert into xign ( uid, xchan ) values ( %d, '%s' ) ", intval(local_channel()), @@ -26,12 +27,12 @@ class Directory extends Controller { if(local_channel()) App::$profile_uid = local_channel(); - + $observer = get_observer_hash(); $global_changed = false; $safe_changed = false; $pubforums_changed = false; - + if(array_key_exists('global',$_REQUEST)) { $globaldir = intval($_REQUEST['global']); $global_changed = true; @@ -41,7 +42,7 @@ class Directory extends Controller { if($observer) set_xconfig($observer,'directory','globaldir',$globaldir); } - + if(array_key_exists('safe',$_REQUEST)) { $safemode = intval($_REQUEST['safe']); $safe_changed = true; @@ -51,8 +52,8 @@ class Directory extends Controller { if($observer) set_xconfig($observer,'directory','safemode',$safemode); } - - + + if(array_key_exists('pubforums',$_REQUEST)) { $pubforums = intval($_REQUEST['pubforums']); $pubforums_changed = true; @@ -64,52 +65,52 @@ class Directory extends Controller { } } - + function get() { - + if(observer_prohibited()) { notice( t('Public access denied.') . EOL); return; } - + if(get_config('system','block_public_directory',false) && (! get_observer_hash())) { notice( t('Public access denied.') . EOL); return; } - + $observer = get_observer_hash(); - - $globaldir = get_directory_setting($observer, 'globaldir'); + + $globaldir = Libzotdir::get_directory_setting($observer, 'globaldir'); // override your personal global search pref if we're doing a navbar search of the directory if(intval($_REQUEST['navsearch'])) $globaldir = 1; - - $safe_mode = get_directory_setting($observer, 'safemode'); - - $pubforums = get_directory_setting($observer, 'pubforums'); - + + $safe_mode = Libzotdir::get_directory_setting($observer, 'safemode'); + + $pubforums = Libzotdir::get_directory_setting($observer, 'pubforums'); + $o = ''; nav_set_selected('Directory'); - + if(x($_POST,'search')) $search = notags(trim($_POST['search'])); else $search = ((x($_GET,'search')) ? notags(trim(rawurldecode($_GET['search']))) : ''); - - + + if(strpos($search,'=') && local_channel() && feature_enabled(local_channel(), 'advanced_dirsearch')) $advanced = $search; - + $keywords = (($_GET['keywords']) ? $_GET['keywords'] : ''); - + // Suggest channels if no search terms or keywords are given $suggest = (local_channel() && x($_REQUEST,'suggest')) ? $_REQUEST['suggest'] : ''; - + if($suggest) { // the directory options have no effect in suggestion mode - + $globaldir = 1; $safe_mode = 1; $type = 0; @@ -120,7 +121,7 @@ class Directory extends Controller { notice( t('No default suggestions were found.') . EOL); return; } - + // Remember in which order the suggestions were $addresses = array(); $common = array(); @@ -129,7 +130,7 @@ class Directory extends Controller { $common[$rr['xchan_addr']] = ((intval($rr['total']) > 0) ? intval($rr['total']) - 1 : 0); $addresses[$rr['xchan_addr']] = $index++; } - + // Build query to get info about suggested people $advanced = ''; foreach(array_keys($addresses) as $address) { @@ -137,13 +138,13 @@ class Directory extends Controller { } // Remove last space in the advanced query $advanced = rtrim($advanced); - + } - + $tpl = get_markup_template('directory_header.tpl'); - + $dirmode = intval(get_config('system','directory_mode')); - + $directory_admin = false; if(($dirmode == DIRECTORY_MODE_PRIMARY) || ($dirmode == DIRECTORY_MODE_STANDALONE)) { @@ -154,19 +155,19 @@ class Directory extends Controller { } if(! $url) { - $directory = find_upstream_directory($dirmode); + $directory = Libzotdir::find_upstream_directory($dirmode); if((! $directory) || (! array_key_exists('url',$directory)) || (! $directory['url'])) logger('CRITICAL: No directory server URL'); $url = $directory['url'] . '/dirsearch'; } - + $token = get_config('system','realm_token'); - - + + logger('mod_directory: URL = ' . $url, LOGGER_DEBUG); - + $contacts = array(); - + if(local_channel()) { $x = q("select abook_xchan from abook where abook_channel = %d", intval(local_channel()) @@ -176,24 +177,24 @@ class Directory extends Controller { $contacts[] = $xx['abook_xchan']; } } - + if($url) { - + $numtags = get_config('system','directorytags'); - + $kw = ((intval($numtags) > 0) ? intval($numtags) : 50); - + if(get_config('system','disable_directory_keywords')) $kw = 0; - + $query = $url . '?f=&kw=' . $kw . (($safe_mode != 1) ? '&safe=' . $safe_mode : ''); - + if($token) $query .= '&t=' . $token; - + if(! $globaldir) $query .= '&hub=' . App::get_hostname(); - + if($search) $query .= '&name=' . urlencode($search) . '&keywords=' . urlencode($search); if(strpos($search,'@')) @@ -204,29 +205,29 @@ class Directory extends Controller { $query .= '&query=' . urlencode($advanced); if(! is_null($pubforums)) $query .= '&pubforums=' . intval($pubforums); - + $directory_sort_order = get_config('system','directory_sort_order'); if(! $directory_sort_order) $directory_sort_order = 'date'; - + $sort_order = ((x($_REQUEST,'order')) ? $_REQUEST['order'] : $directory_sort_order); - + if($sort_order) $query .= '&order=' . urlencode($sort_order); - + if(App::$pager['page'] != 1) $query .= '&p=' . App::$pager['page']; - + logger('mod_directory: query: ' . $query); - + $x = z_fetch_url($query); logger('directory: return from upstream: ' . print_r($x,true), LOGGER_DATA); - + if($x['success']) { $t = 0; $j = json_decode($x['body'],true); if($j) { - + if($j['results']) { $results = $j['results']; @@ -235,23 +236,23 @@ class Directory extends Controller { } $entries = array(); - + $photo = 'thumb'; - + foreach($results as $rr) { - + $profile_link = chanlink_url($rr['url']); - + $pdesc = (($rr['description']) ? $rr['description'] . '<br />' : ''); - $connect_link = ((local_channel()) ? z_root() . '/follow?f=&url=' . urlencode($rr['address']) : ''); - + $connect_link = ((local_channel()) ? z_root() . '/follow?f=&interactive=1&url=' . urlencode($rr['address']) : ''); + // Checking status is disabled ATM until someone checks the performance impact more carefully //$online = remote_online_status($rr['address']); $online = ''; - + if(in_array($rr['hash'],$contacts)) $connect_link = ''; - + $location = ''; if(strlen($rr['locale'])) $location .= $rr['locale']; @@ -265,53 +266,53 @@ class Directory extends Controller { $location .= ', '; $location .= $rr['country']; } - + $age = ''; if(strlen($rr['birthday'])) { if(($years = age($rr['birthday'],'UTC','')) > 0) $age = $years; } - + $page_type = ''; - + $rating_enabled = get_config('system','rating_enabled'); if($rr['total_ratings'] && $rating_enabled) $total_ratings = sprintf( tt("%d rating", "%d ratings", $rr['total_ratings']), $rr['total_ratings']); else $total_ratings = ''; - + $profile = $rr; - + if ((x($profile,'locale') == 1) || (x($profile,'region') == 1) || (x($profile,'postcode') == 1) || (x($profile,'country') == 1)) - + $gender = ((x($profile,'gender') == 1) ? t('Gender: ') . $profile['gender']: False); - + $marital = ((x($profile,'marital') == 1) ? t('Status: ') . $profile['marital']: False); - + $homepage = ((x($profile,'homepage') == 1) ? t('Homepage: ') : False); - $homepageurl = ((x($profile,'homepage') == 1) ? html2plain($profile['homepage']) : ''); - + $homepageurl = ((x($profile,'homepage') == 1) ? html2plain($profile['homepage']) : ''); + $hometown = ((x($profile,'hometown') == 1) ? html2plain($profile['hometown']) : False); - + $about = ((x($profile,'about') == 1) ? zidify_links(bbcode($profile['about'], ['tryoembed' => false])) : False); if ($about && $safe_mode) { $about = html2plain($about); } - + $keywords = ((x($profile,'keywords')) ? $profile['keywords'] : ''); - + $out = ''; - + if($keywords) { $keywords = str_replace(',',' ', $keywords); $keywords = str_replace(' ',' ', $keywords); $karr = explode(' ', $keywords); - + if($karr) { if(local_channel()) { $r = q("select keywords from profile where uid = %d and is_default = 1 limit 1", @@ -332,9 +333,9 @@ class Directory extends Controller { $out .= '<a href="' . z_root() . '/directory/f=&keywords=' . urlencode($k) .'">' . $k . '</a>'; } } - + } - + $entry = array( 'id' => ++$t, 'profile_link' => $profile_link, @@ -366,7 +367,7 @@ class Directory extends Controller { 'about' => $about, 'about_label' => t('About:'), 'conn_label' => t('Connect'), - 'forum_label' => t('Public Forum:'), + 'forum_label' => t('Public Forum:'), 'connect' => $connect_link, 'online' => $online, 'kw' => (($out) ? t('Keywords: ') : ''), @@ -378,36 +379,36 @@ class Directory extends Controller { 'common_count' => intval($common[$rr['address']]), 'safe' => $safe_mode ); - + $arr = array('contact' => $rr, 'entry' => $entry); - + call_hooks('directory_item', $arr); - + unset($profile); unset($location); - + if(! $arr['entry']) { continue; - } - + } + if($sort_order == '' && $suggest) { $entries[$addresses[$rr['address']]] = $arr['entry']; // Use the same indexes as originally to get the best suggestion first } - + else { $entries[] = $arr['entry']; } } - + ksort($entries); // Sort array by key so that foreach-constructs work as expected - + if($j['keywords']) { App::$data['directory_keywords'] = $j['keywords']; } - + logger('mod_directory: entries: ' . print_r($entries,true), LOGGER_DATA); - - + + if($_REQUEST['aj']) { if($entries) { $o = replace_macros(get_markup_template('directajax.tpl'),array( @@ -422,9 +423,9 @@ class Directory extends Controller { } else { $maxheight = 94; - + $dirtitle = (($globaldir) ? t('Global Directory') : t('Local Directory')); - + $o .= "<script> var page_query = '" . escape_tags(urlencode($_GET['q'])) . "'; var extra_args = '" . extra_query_args() . "' ; divmore_height = " . intval($maxheight) . "; </script>"; $o .= replace_macros($tpl, array( '$search' => $search, @@ -442,10 +443,10 @@ class Directory extends Controller { '$reversedate' => t('Oldest to Newest'), '$suggest' => $suggest ? '&suggest=1' : '' )); - - + + } - + } else { if($_REQUEST['aj']) { @@ -463,7 +464,7 @@ class Directory extends Controller { } return $o; } - + static public function reorder_results($results,$suggests) { if(! $suggests) diff --git a/Zotlabs/Module/Dirsearch.php b/Zotlabs/Module/Dirsearch.php index c15b13a90..78205a9fc 100644 --- a/Zotlabs/Module/Dirsearch.php +++ b/Zotlabs/Module/Dirsearch.php @@ -4,26 +4,22 @@ namespace Zotlabs\Module; use App; use Zotlabs\Web\Controller; -require_once('include/dir_fns.php'); - - - class Dirsearch extends Controller { function init() { App::set_pager_itemspage(30); - + } - + function get() { - + $ret = array('success' => false); - + // logger('request: ' . print_r($_REQUEST,true)); - - + + $dirmode = intval(get_config('system','directory_mode')); - + if($dirmode == DIRECTORY_MODE_NORMAL) { $ret['message'] = t('This site is not a directory server'); json_return_and_die($ret); @@ -31,24 +27,24 @@ class Dirsearch extends Controller { $access_token = $_REQUEST['t']; - + $token = get_config('system','realm_token'); if($token && $access_token != $token) { $ret['message'] = t('This directory server requires an access token'); json_return_and_die($ret); } - - + + if(argc() > 1 && argv(1) === 'sites') { $ret = $this->list_public_sites(); json_return_and_die($ret); } - + $sql_extra = ''; - - + + $tables = array('name','address','locale','region','postcode','country','gender','marital','sexual','keywords'); - + if($_REQUEST['query']) { $advanced = $this->dir_parse_query($_REQUEST['query']); if($advanced) { @@ -64,9 +60,9 @@ class Dirsearch extends Controller { } } } - + $hash = ((x($_REQUEST['hash'])) ? $_REQUEST['hash'] : ''); - + $name = ((x($_REQUEST,'name')) ? $_REQUEST['name'] : ''); $hub = ((x($_REQUEST,'hub')) ? $_REQUEST['hub'] : ''); $address = ((x($_REQUEST,'address')) ? $_REQUEST['address'] : ''); @@ -82,16 +78,16 @@ class Dirsearch extends Controller { $agele = ((x($_REQUEST,'agele')) ? intval($_REQUEST['agele']) : 0 ); $kw = ((x($_REQUEST,'kw')) ? intval($_REQUEST['kw']) : 0 ); $forums = ((array_key_exists('pubforums',$_REQUEST)) ? intval($_REQUEST['pubforums']) : 0); - + if(get_config('system','disable_directory_keywords')) $kw = 0; - - + + // by default use a safe search $safe = ((x($_REQUEST,'safe'))); // ? intval($_REQUEST['safe']) : 1 ); if ($safe === false) $safe = 1; - + if(array_key_exists('sync',$_REQUEST)) { if($_REQUEST['sync']) $sync = datetime_convert('UTC','UTC',$_REQUEST['sync']); @@ -100,7 +96,7 @@ class Dirsearch extends Controller { } else $sync = false; - + if(($dirmode == DIRECTORY_MODE_STANDALONE) && (! $hub)) { $hub = \App::get_hostname(); } @@ -109,13 +105,13 @@ class Dirsearch extends Controller { $hub_query = " and xchan_hash in (select hubloc_hash from hubloc where hubloc_host = '" . protect_sprintf(dbesc($hub)) . "') "; else $hub_query = ''; - + $sort_order = ((x($_REQUEST,'order')) ? $_REQUEST['order'] : ''); - + $joiner = ' OR '; if($_REQUEST['and']) $joiner = ' AND '; - + if($name) $sql_extra .= $this->dir_query_build($joiner,'xchan_name',$name); if($address) @@ -136,89 +132,89 @@ class Dirsearch extends Controller { $sql_extra .= $this->dir_query_build($joiner,'xprof_sexual',$sexual); if($keywords) $sql_extra .= $this->dir_query_build($joiner,'xprof_keywords',$keywords); - - - // we only support an age range currently. You must set both agege - // (greater than or equal) and agele (less than or equal) - + + + // we only support an age range currently. You must set both agege + // (greater than or equal) and agele (less than or equal) + if($agele && $agege) { $sql_extra .= " $joiner ( xprof_age <= " . intval($agele) . " "; $sql_extra .= " AND xprof_age >= " . intval($agege) . ") "; } - - + + if($hash) { $sql_extra = " AND xchan_hash like '" . dbesc($hash) . protect_sprintf('%') . "' "; } - - + + $perpage = (($_REQUEST['n']) ? $_REQUEST['n'] : 60); $page = (($_REQUEST['p']) ? intval($_REQUEST['p'] - 1) : 0); $startrec = (($page+1) * $perpage) - $perpage; $limit = (($_REQUEST['limit']) ? intval($_REQUEST['limit']) : 0); $return_total = ((x($_REQUEST,'return_total')) ? intval($_REQUEST['return_total']) : 0); - + // mtime is not currently working - + $mtime = ((x($_REQUEST,'mtime')) ? datetime_convert('UTC','UTC',$_REQUEST['mtime']) : ''); - - // ok a separate tag table won't work. + + // ok a separate tag table won't work. // merge them into xprof - + $ret['success'] = true; - + // If &limit=n, return at most n entries // If &return_total=1, we count matching entries and return that as 'total_items' for use in pagination. // By default we return one page (default 80 items maximum) and do not count total entries - + $logic = ((strlen($sql_extra)) ? 'false' : 'true'); - + if($hash) $logic = 'true'; - + if($dirmode == DIRECTORY_MODE_STANDALONE) { $sql_extra .= " and xchan_addr like '%%" . \App::get_hostname() . "' "; } - + $safesql = (($safe > 0) ? " and xchan_censored = 0 and xchan_selfcensored = 0 " : ''); if($safe < 0) $safesql = " and ( xchan_censored = 1 OR xchan_selfcensored = 1 ) "; - + if($forums) $safesql .= " and xchan_pubforum = " . ((intval($forums)) ? '1 ' : '0 '); - - if($limit) + + if($limit) $qlimit = " LIMIT $limit "; else { $qlimit = " LIMIT " . intval($perpage) . " OFFSET " . intval($startrec); if($return_total) { - $r = q("SELECT COUNT(xchan_hash) AS total FROM xchan left join xprof on xchan_hash = xprof_hash where $logic $sql_extra and xchan_network = 'zot' and xchan_hidden = 0 and xchan_orphan = 0 and xchan_deleted = 0 $safesql "); + $r = q("SELECT COUNT(xchan_hash) AS total FROM xchan left join xprof on xchan_hash = xprof_hash where $logic $sql_extra and xchan_network = 'zot6' and xchan_hidden = 0 and xchan_orphan = 0 and xchan_deleted = 0 $safesql "); if($r) { $ret['total_items'] = $r[0]['total']; } } } - + if($sort_order == 'normal') { $order = " order by xchan_name asc "; - - // Start the alphabetic search at 'A' + + // Start the alphabetic search at 'A' // This will make a handful of channels whose names begin with // punctuation un-searchable in this mode - + $safesql .= " and ascii(substring(xchan_name FROM 1 FOR 1)) > 64 "; } elseif($sort_order == 'reverse') $order = " order by xchan_name desc "; elseif($sort_order == 'reversedate') $order = " order by xchan_name_date asc "; - else + else $order = " order by xchan_name_date desc "; - - + + if($sync) { $spkt = array('transactions' => array()); - $r = q("select * from updates where ud_date >= '%s' and ud_guid != '' order by ud_date desc", + $r = q("select * from updates where ud_date >= '%s' and ud_guid != '' and ud_addr != '' order by ud_date desc", dbesc($sync) ); if($r) { @@ -228,7 +224,7 @@ class Dirsearch extends Controller { $flags[] = 'deleted'; if($rr['ud_flags'] & UPDATE_FLAGS_FORCED) $flags[] = 'forced'; - + $spkt['transactions'][] = array( 'hash' => $rr['ud_hash'], 'address' => $rr['ud_addr'], @@ -238,87 +234,48 @@ class Dirsearch extends Controller { ); } } - $r = q("select * from xlink where xlink_static = 1 and xlink_updated >= '%s' ", - dbesc($sync) - ); - if($r) { - $spkt['ratings'] = array(); - foreach($r as $rr) { - $spkt['ratings'][] = array( - 'type' => 'rating', - 'encoding' => 'zot', - 'channel' => $rr['xlink_xchan'], - 'target' => $rr['xlink_link'], - 'rating' => intval($rr['xlink_rating']), - 'rating_text' => $rr['xlink_rating_text'], - 'signature' => $rr['xlink_sig'], - 'edited' => $rr['xlink_updated'] - ); - } - } json_return_and_die($spkt); } else { - - $r = q("SELECT xchan.*, xprof.* from xchan left join xprof on xchan_hash = xprof_hash - where ( $logic $sql_extra ) $hub_query and xchan_network = 'zot' and xchan_system = 0 and xchan_hidden = 0 and xchan_orphan = 0 and xchan_deleted = 0 - $safesql $order $qlimit " + + $r = q("SELECT + xchan.xchan_name as name, + xchan.xchan_hash as hash, + xchan.xchan_censored as censored, + xchan.xchan_selfcensored as selfcensored, + xchan.xchan_pubforum as public_forum, + xchan.xchan_url as url, + xchan.xchan_photo_l as photo_l, + xchan.xchan_photo_m as photo, + xchan.xchan_addr as address, + xprof.xprof_desc as description, + xprof.xprof_locale as locale, + xprof.xprof_region as region, + xprof.xprof_postcode as postcode, + xprof.xprof_country as country, + xprof.xprof_dob as birthday, + xprof.xprof_age as age, + xprof.xprof_gender as gender, + xprof.xprof_marital as marital, + xprof.xprof_sexual as sexual, + xprof.xprof_about as about, + xprof.xprof_homepage as homepage, + xprof.xprof_hometown as hometown, + xprof.xprof_keywords as keywords + from xchan left join xprof on xchan_hash = xprof_hash left join hubloc on hubloc_hash = xchan_hash + where hubloc_primary = 1 and hubloc_updated > %s - INTERVAL %s and ( $logic $sql_extra ) $hub_query and xchan_network = 'zot6' and xchan_system = 0 and xchan_hidden = 0 and xchan_orphan = 0 and xchan_deleted = 0 + $safesql $order $qlimit", + db_utcnow(), + db_quoteinterval('30 DAY') ); - - - - $ret['page'] = $page + 1; - $ret['records'] = count($r); + } - - - + if($r) { - - $entries = array(); - - foreach($r as $rr) { - - $entry = array(); - - $pc = q("select count(xlink_rating) as total_ratings from xlink where xlink_link = '%s' and xlink_rating != 0 and xlink_static = 1 group by xlink_rating", - dbesc($rr['xchan_hash']) - ); - - if($pc) - $entry['total_ratings'] = intval($pc[0]['total_ratings']); - else - $entry['total_ratings'] = 0; - - $entry['name'] = $rr['xchan_name']; - $entry['hash'] = $rr['xchan_hash']; - $entry['censored'] = $rr['xchan_censored']; - $entry['selfcensored'] = $rr['xchan_selfcensored']; - $entry['public_forum'] = (intval($rr['xchan_pubforum']) ? true : false); - $entry['url'] = $rr['xchan_url']; - $entry['photo_l'] = $rr['xchan_photo_l']; - $entry['photo'] = $rr['xchan_photo_m']; - $entry['address'] = $rr['xchan_addr']; - $entry['description'] = $rr['xprof_desc']; - $entry['locale'] = $rr['xprof_locale']; - $entry['region'] = $rr['xprof_region']; - $entry['postcode'] = $rr['xprof_postcode']; - $entry['country'] = $rr['xprof_country']; - $entry['birthday'] = $rr['xprof_dob']; - $entry['age'] = $rr['xprof_age']; - $entry['gender'] = $rr['xprof_gender']; - $entry['marital'] = $rr['xprof_marital']; - $entry['sexual'] = $rr['xprof_sexual']; - $entry['about'] = $rr['xprof_about']; - $entry['homepage'] = $rr['xprof_homepage']; - $entry['hometown'] = $rr['xprof_hometown']; - $entry['keywords'] = $rr['xprof_keywords']; - - $entries[] = $entry; - - } - - $ret['results'] = $entries; + $ret['results'] = $r; + $ret['page'] = $page + 1; + $ret['records'] = count($r); + if($kw) { $k = dir_tagadelic($kw, $hub); if($k) { @@ -328,30 +285,30 @@ class Dirsearch extends Controller { } } } - } - + } + json_return_and_die($ret); } - + function dir_query_build($joiner,$field,$s) { $ret = ''; if(trim($s)) $ret .= dbesc($joiner) . " " . dbesc($field) . " like '" . protect_sprintf( '%' . dbesc($s) . '%' ) . "' "; return $ret; } - + function dir_flag_build($joiner,$field,$bit,$s) { return dbesc($joiner) . " ( " . dbesc($field) . " & " . intval($bit) . " ) " . ((intval($s)) ? '>' : '=' ) . " 0 "; } - - + + function dir_parse_query($s) { - + $ret = array(); $curr = array(); $all = explode(' ',$s); $quoted_string = false; - + if($all) { foreach($all as $q) { if($quoted_string === false) { @@ -382,7 +339,7 @@ class Dirsearch extends Controller { $ret[] = $curr; $curr = array(); continue; - } + } else { $ret[] = $curr; $curr = array(); @@ -405,15 +362,15 @@ class Dirsearch extends Controller { logger('dir_parse_query:' . print_r($ret,true),LOGGER_DATA); return $ret; } - - - - - - - + + + + + + + function list_public_sites() { - + $rand = db_getfunc('rand'); $realm = get_directory_realm(); if($realm == DIRECTORY_REALM) { @@ -428,16 +385,16 @@ class Dirsearch extends Controller { intval(SITE_TYPE_ZOT) ); } - + $ret = array('success' => false); - + if($r) { $ret['success'] = true; $ret['sites'] = array(); $insecure = array(); - + foreach($r as $rr) { - + if($rr['site_access'] == ACCESS_FREE) $access = 'free'; elseif($rr['site_access'] == ACCESS_PAID) @@ -446,14 +403,14 @@ class Dirsearch extends Controller { $access = 'tiered'; else $access = 'private'; - + if($rr['site_register'] == REGISTER_OPEN) $register = 'open'; elseif($rr['site_register'] == REGISTER_APPROVE) $register = 'approve'; else $register = 'closed'; - + if(strpos($rr['site_url'],'https://') !== false) $ret['sites'][] = array('url' => $rr['site_url'], 'access' => $access, 'register' => $register, 'sellpage' => $rr['site_sellpage'], 'location' => $rr['site_location'], 'project' => $rr['site_project'], 'version' => $rr['site_version']); else diff --git a/Zotlabs/Module/Display.php b/Zotlabs/Module/Display.php index 81ac0f7b8..02a79f854 100644 --- a/Zotlabs/Module/Display.php +++ b/Zotlabs/Module/Display.php @@ -1,6 +1,8 @@ <?php namespace Zotlabs\Module; +use App; + require_once("include/bbcode.php"); require_once('include/security.php'); require_once('include/conversation.php'); @@ -19,42 +21,51 @@ class Display extends \Zotlabs\Web\Controller { if(argc() > 1) { $module_format = substr(argv(1),strrpos(argv(1),'.') + 1); if(! in_array($module_format,['atom','zot','json'])) - $module_format = 'html'; + $module_format = 'html'; } if(observer_prohibited()) { notice( t('Public access denied.') . EOL); return; } - + if(argc() > 1) { $item_hash = argv(1); if($module_format !== 'html') { $item_hash = substr($item_hash,0,strrpos($item_hash,'.')); } } - - if($_REQUEST['mid']) + + if($_REQUEST['mid']) { $item_hash = $_REQUEST['mid']; + } + + $item_hash = unpack_link_id($item_hash); + + if ($item_hash === false) { + App::$error = 400; + notice(t('Malformed message id.') . EOL); + return; + } - if(! $item_hash) { - \App::$error = 404; + if(!$item_hash) { + App::$error = 404; notice( t('Item not found.') . EOL); return; } - + $observer_is_owner = false; if(local_channel() && (! $update)) { - - $channel = \App::get_channel(); + + $channel = App::get_channel(); $channel_acl = array( - 'allow_cid' => $channel['channel_allow_cid'], - 'allow_gid' => $channel['channel_allow_gid'], - 'deny_cid' => $channel['channel_deny_cid'], + 'allow_cid' => $channel['channel_allow_cid'], + 'allow_gid' => $channel['channel_allow_gid'], + 'deny_cid' => $channel['channel_deny_cid'], 'deny_gid' => $channel['channel_deny_gid'] - ); + ); $x = array( 'is_owner' => true, @@ -62,7 +73,7 @@ class Display extends \Zotlabs\Web\Controller { 'default_location' => $channel['channel_location'], 'nickname' => $channel['channel_address'], 'lockstate' => (($group || $cid || $channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'), - 'acl' => populate_acl($channel_acl,true, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post'), + 'acl' => populate_acl($channel_acl,true, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post'), 'permissions' => $channel_acl, 'bang' => '', 'visitor' => true, @@ -75,32 +86,27 @@ class Display extends \Zotlabs\Web\Controller { 'jotnets' => true, 'reset' => t('Reset form') ); - + $o = '<div id="jot-popup">'; $o .= status_editor($a,$x,false,'Display'); $o .= '</div>'; } - + // This page can be viewed by anybody so the query could be complicated // First we'll see if there is a copy of the item which is owned by us - if we're logged in locally. - // If that fails (or we aren't logged in locally), + // If that fails (or we aren't logged in locally), // query an item in which the observer (if logged in remotely) has cid or gid rights - // and if that fails, look for a copy of the post that has no privacy restrictions. + // and if that fails, look for a copy of the post that has no privacy restrictions. // If we find the post, but we don't find a copy that we're allowed to look at, this fact needs to be reported. - + // find a copy of the item somewhere - - $target_item = null; - if(strpos($item_hash,'b64.') === 0) - $decoded = @base64url_decode(substr($item_hash,4)); - if($decoded) - $item_hash = $decoded; + $target_item = null; - $r = q("select id, uid, mid, parent, parent_mid, thr_parent, verb, item_type, item_deleted, author_xchan, item_blocked from item where mid like '%s' limit 1", - dbesc($item_hash . '%') + $r = q("select id, uid, mid, parent, parent_mid, thr_parent, verb, item_type, item_deleted, author_xchan, item_blocked from item where mid = '%s' limit 1", + dbesc($item_hash) ); - + if($r) { $target_item = $r[0]; } @@ -110,21 +116,21 @@ class Display extends \Zotlabs\Web\Controller { ); if($x) { // not yet ready for prime time -// \App::$poi = $x[0]; +// App::$poi = $x[0]; } //if the item is to be moderated redirect to /moderate if($target_item['item_blocked'] == ITEM_MODERATED) { goaway(z_root() . '/moderate/' . $target_item['id']); } - + $r = null; - + if($target_item['item_type'] == ITEM_TYPE_WEBPAGE) { $x = q("select * from channel where channel_id = %d limit 1", intval($target_item['uid']) ); - $y = q("select * from iconfig left join item on iconfig.iid = item.id + $y = q("select * from iconfig left join item on iconfig.iid = item.id where item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'WEBPAGE' and item.id = %d limit 1", intval($target_item['uid']), intval($target_item['parent']) @@ -141,7 +147,7 @@ class Display extends \Zotlabs\Web\Controller { $x = q("select * from channel where channel_id = %d limit 1", intval($target_item['uid']) ); - $y = q("select * from iconfig left join item on iconfig.iid = item.id + $y = q("select * from iconfig left join item on iconfig.iid = item.id where item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'ARTICLE' and item.id = %d limit 1", intval($target_item['uid']), intval($target_item['parent']) @@ -160,7 +166,7 @@ class Display extends \Zotlabs\Web\Controller { intval($target_item['uid']) ); - $y = q("select * from iconfig left join item on iconfig.iid = item.id + $y = q("select * from iconfig left join item on iconfig.iid = item.id where item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'CARD' and item.id = %d limit 1", intval($target_item['uid']), intval($target_item['parent']) @@ -179,7 +185,7 @@ class Display extends \Zotlabs\Web\Controller { notice( t('Page not found.') . EOL); return ''; } - + $simple_update = ''; if($update && $_SESSION['loadtime']) $simple_update = " AND (( item_unseen = 1 AND item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) OR item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) "; @@ -189,17 +195,15 @@ class Display extends \Zotlabs\Web\Controller { // if the target item is not a post (eg a like) we want to address its thread parent //$mid = ((($target_item['verb'] == ACTIVITY_LIKE) || ($target_item['verb'] == ACTIVITY_DISLIKE)) ? $target_item['thr_parent'] : $target_item['mid']); - $mid = $target_item['mid']; - // if we got a decoded hash we must encode it again before handing to javascript - if($decoded) - $mid = 'b64.' . base64url_encode($mid); + // if we got a decoded hash we must encode it again before handing to javascript + $mid = gen_link_id($target_item['mid']); $o .= '<div id="live-display"></div>' . "\r\n"; $o .= "<script> var profile_uid = " . ((intval(local_channel())) ? local_channel() : (-1)) - . "; var netargs = '?f='; var profile_page = " . \App::$pager['page'] . "; </script>\r\n"; - - \App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"),array( + . "; var netargs = '?f='; var profile_page = " . App::$pager['page'] . "; </script>\r\n"; + + App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"),array( '$baseurl' => z_root(), '$pgtype' => 'display', '$uid' => '0', @@ -215,7 +219,7 @@ class Display extends \Zotlabs\Web\Controller { '$dm' => '0', '$nouveau' => '0', '$wall' => '0', - '$page' => ((\App::$pager['page'] != 1) ? \App::$pager['page'] : 1), + '$page' => ((App::$pager['page'] != 1) ? App::$pager['page'] : 1), '$list' => ((x($_REQUEST,'list')) ? intval($_REQUEST['list']) : 0), '$search' => '', '$xchan' => '', @@ -230,10 +234,10 @@ class Display extends \Zotlabs\Web\Controller { '$mid' => (($mid) ? urlencode($mid) : '') )); - head_add_link([ + head_add_link([ 'rel' => 'alternate', 'type' => 'application/json+oembed', - 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . \App::$query_string), + 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . App::$query_string), 'title' => 'oembed' ]); @@ -243,94 +247,89 @@ class Display extends \Zotlabs\Web\Controller { $item_normal = item_normal(); $item_normal_update = item_normal_update(); - $sql_extra = public_permissions_sql($observer_hash); + $sql_extra = ((local_channel()) ? EMPTY_STR : item_permissions_sql(0, $observer_hash)); if($noscript_content || $load) { - $r = null; - require_once('include/channel.php'); $sys = get_sys_channel(); - $sysid = $sys['channel_id']; + // in case somebody turned off public access to sys channel content using permissions + // make that content unsearchable by ensuring the owner uid can't match + $sys_id = perm_is_allowed($sys['channel_id'], $observer_hash, 'view_stream') ? $sys['channel_id'] : 0; + + $r = null; if(local_channel()) { - $r = q("SELECT item.id as item_id from item WHERE uid = %d and mid = '%s' $item_normal limit 1", + $r = q("SELECT item.id AS item_id FROM item WHERE uid = %d AND mid = '%s' $item_normal LIMIT 1", intval(local_channel()), dbesc($target_item['parent_mid']) ); } - if(! $r) { - - // in case somebody turned off public access to sys channel content using permissions - // make that content unsearchable by ensuring the owner uid can't match - - if(! perm_is_allowed($sysid,$observer_hash,'view_stream')) - $sysid = 0; - - $r = q("SELECT item.id as item_id from item - WHERE mid = '%s' - AND (((( item.allow_cid = '' AND item.allow_gid = '' AND item.deny_cid = '' - AND item.deny_gid = '' AND item_private = 0 ) - and uid in ( " . stream_perms_api_uids(($observer_hash) ? (PERMS_NETWORK|PERMS_PUBLIC) : PERMS_PUBLIC) . " )) - OR uid = %d ) - $sql_extra ) + if(!$r) { + $r = q("SELECT item.id AS item_id FROM item + WHERE ((mid = '%s' + AND (((( item.allow_cid = '' AND item.allow_gid = '' AND item.deny_cid = '' + AND item.deny_gid = '' AND item_private = 0 ) + AND uid IN ( " . stream_perms_api_uids(($observer_hash) ? (PERMS_NETWORK|PERMS_PUBLIC) : PERMS_PUBLIC) . " )) + OR uid = %d ))) OR + (mid = '%s' $sql_extra )) $item_normal limit 1", dbesc($target_item['parent_mid']), - intval($sysid) + intval($sys_id), + dbesc($target_item['parent_mid']) ); } } - - elseif($update && !$load) { - $r = null; + elseif($update && !$load) { require_once('include/channel.php'); $sys = get_sys_channel(); - $sysid = $sys['channel_id']; + // in case somebody turned off public access to sys channel content using permissions + // make that content unsearchable by ensuring the owner uid can't match + $sys_id = perm_is_allowed($sys['channel_id'], $observer_hash, 'view_stream') ? $sys['channel_id'] : 0; + + $r = null; if(local_channel()) { $r = q("SELECT item.parent AS item_id from item WHERE uid = %d - and parent_mid = '%s' + AND parent_mid = '%s' $item_normal_update $simple_update - limit 1", + LIMIT 1", intval(local_channel()), dbesc($target_item['parent_mid']) ); } - if($r === null) { - // in case somebody turned off public access to sys channel content using permissions - // make that content unsearchable by ensuring the owner_xchan can't match - if(! perm_is_allowed($sysid,$observer_hash,'view_stream')) - $sysid = 0; - $r = q("SELECT item.parent AS item_id from item - WHERE parent_mid = '%s' - AND (((( item.allow_cid = '' AND item.allow_gid = '' AND item.deny_cid = '' - AND item.deny_gid = '' AND item_private = 0 ) + + if(! $r) { + $r = q("SELECT item.id as item_id from item + WHERE ((parent_mid = '%s' + AND (((( item.allow_cid = '' AND item.allow_gid = '' AND item.deny_cid = '' + AND item.deny_gid = '' AND item_private = 0 ) and uid in ( " . stream_perms_api_uids(($observer_hash) ? (PERMS_NETWORK|PERMS_PUBLIC) : PERMS_PUBLIC) . " )) - OR uid = %d ) - $sql_extra ) - $item_normal_update - $simple_update + OR uid = %d ))) OR + (parent_mid = '%s' $sql_extra )) + $item_normal limit 1", dbesc($target_item['parent_mid']), - intval($sysid) + intval($sys_id), + dbesc($target_item['parent_mid']) ); } } - + else { - $r = array(); + $r = []; } if($r) { $parents_str = ids_to_querystr($r,'item_id'); if($parents_str) { - $items = q("SELECT item.*, item.id AS item_id + $items = q("SELECT item.*, item.id AS item_id FROM item - WHERE parent in ( %s ) $item_normal ", + WHERE parent in ( %s ) $sql_extra $item_normal ", dbesc($parents_str) ); xchan_query($items); @@ -341,10 +340,10 @@ class Display extends \Zotlabs\Web\Controller { else { $items = array(); } - + switch($module_format) { - + case 'html': if ($update) { @@ -360,10 +359,10 @@ class Display extends \Zotlabs\Web\Controller { } $o .= '</noscript>'; - \App::$page['title'] = (($items[0]['title']) ? $items[0]['title'] . " - " . \App::$page['title'] : \App::$page['title']); + App::$page['title'] = (($items[0]['title']) ? $items[0]['title'] . " - " . App::$page['title'] : App::$page['title']); $o .= conversation($items, 'display', $update, 'client'); - } + } break; @@ -373,14 +372,14 @@ class Display extends \Zotlabs\Web\Controller { '$version' => xmlify(\Zotlabs\Lib\System::get_project_version()), '$generator' => xmlify(\Zotlabs\Lib\System::get_platform_name()), '$generator_uri' => 'https://hubzilla.org', - '$feed_id' => xmlify(\App::$cmd), + '$feed_id' => xmlify(App::$cmd), '$feed_title' => xmlify(t('Article')), '$feed_updated' => xmlify(datetime_convert('UTC', 'UTC', 'now', ATOM_TIME)), '$author' => '', '$owner' => '', - '$profile_page' => xmlify(z_root() . '/display/' . $target_item['mid']), + '$profile_page' => xmlify(z_root() . '/display/' . gen_link_id($target_item['mid'])), )); - + $x = [ 'xml' => $atom, 'channel' => $channel, 'observer_hash' => $observer_hash, 'params' => $params ]; call_hooks('atom_feed_top',$x); @@ -406,13 +405,13 @@ class Display extends \Zotlabs\Web\Controller { header('Content-type: application/atom+xml'); echo $atom; killme(); - + } $o .= '<div id="content-complete"></div>'; if((($update && $load) || $noscript_content) && (! $items)) { - + $r = q("SELECT id, item_deleted FROM item WHERE mid = '%s' LIMIT 1", dbesc($item_hash) ); @@ -421,14 +420,14 @@ class Display extends \Zotlabs\Web\Controller { if(intval($r[0]['item_deleted'])) { notice( t('Item has been removed.') . EOL ); } - else { - notice( t('Permission denied.') . EOL ); + else { + notice( t('Permission denied.') . EOL ); } } else { notice( t('Item not found.') . EOL ); } - + } $_SESSION['loadtime'] = datetime_convert(); diff --git a/Zotlabs/Module/Dreport.php b/Zotlabs/Module/Dreport.php index 0fc36dc29..42f337b76 100644 --- a/Zotlabs/Module/Dreport.php +++ b/Zotlabs/Module/Dreport.php @@ -5,33 +5,21 @@ namespace Zotlabs\Module; class Dreport extends \Zotlabs\Web\Controller { function get() { - + if(! local_channel()) { notice( t('Permission denied') . EOL); return; } - + $table = 'item'; - $channel = \App::get_channel(); - - $mid = ((argc() > 1) ? argv(1) : ''); - $encoded_mid = ''; + $mid = ((argc() > 1) ? unpack_link_id(argv(1)) : ''); - if(strpos($mid,'b64.') === 0) { - $encoded_mid = $mid; - $mid = @base64url_decode(substr($mid,4)); - } if($mid === 'push') { $table = 'push'; - $mid = ((argc() > 2) ? argv(2) : ''); - - if(strpos($mid,'b64.') === 0) { - $encoded_mid = $mid; - $mid = @base64url_decode(substr($mid,4)); - } + $mid = ((argc() > 2) ? unpack_link_id(argv(2)) : ''); - if($mid) { + if($mid) { $i = q("select id from item where mid = '%s' and uid = %d and ( author_xchan = '%s' or ( owner_xchan = '%s' and item_wall = 1 )) ", dbesc($mid), intval($channel['channel_id']), @@ -43,23 +31,14 @@ class Dreport extends \Zotlabs\Web\Controller { } } sleep(3); - goaway(z_root() . '/dreport/' . (($encoded_mid) ? $encoded_mid : $mid)); + goaway(z_root() . '/dreport/' . gen_link_id($mid)); } - if($mid === 'mail') { - $table = 'mail'; - $mid = ((argc() > 2) ? argv(2) : ''); - if(strpos($mid,'b64.') === 0) - $mid = @base64url_decode(substr($mid,4)); - - } - - if(! $mid) { notice( t('Invalid message') . EOL); return; } - + switch($table) { case 'item': $i = q("select id from item where mid = '%s' and ( author_xchan = '%s' or ( owner_xchan = '%s' and item_wall = 1 )) ", @@ -68,39 +47,32 @@ class Dreport extends \Zotlabs\Web\Controller { dbesc($channel['channel_hash']) ); break; - case 'mail': - $i = q("select id from mail where mid = '%s' and from_xchan = '%s'", - dbesc($mid), - dbesc($channel['channel_hash']) - ); - break; default: break; } - + if(! $i) { notice( t('Permission denied') . EOL); return; } - - $r = q("select * from dreport where (dreport_xchan = '%s' or dreport_xchan = '%s') and dreport_mid = '%s'", + + $r = q("select * from dreport where dreport_xchan = '%s' and dreport_mid = '%s'", dbesc($channel['channel_hash']), - dbesc($channel['channel_portable_id']), dbesc($mid) ); - + if(! $r) { notice( t('no results') . EOL); // return; } - + for($x = 0; $x < count($r); $x++ ) { - + // This has two purposes: 1. make the delivery report strings translateable, and // 2. assign an ordering to item delivery results so we can group them and provide // a readable report with more interesting events listed toward the top and lesser // interesting items towards the bottom - + switch($r[$x]['dreport_result']) { case 'channel sync processed': $r[$x]['gravity'] = 0; @@ -132,27 +104,18 @@ class Dreport extends \Zotlabs\Web\Controller { case 'recipient not found': $r[$x]['dreport_result'] = t('recipient not found'); break; - case 'mail recalled': - $r[$x]['dreport_result'] = t('mail recalled'); - break; - case 'duplicate mail received': - $r[$x]['dreport_result'] = t('duplicate mail received'); - break; - case 'mail delivered': - $r[$x]['dreport_result'] = t('mail delivered'); - break; default: $r[$x]['gravity'] = 1; break; } } - + usort($r,'self::dreport_gravity_sort'); $entries = array(); foreach($r as $rr) { - $entries[] = [ - 'name' => escape_tags($rr['dreport_name'] ?: $rr['dreport_recip']), + $entries[] = [ + 'name' => escape_tags($rr['dreport_name'] ?: $rr['dreport_recip']), 'result' => escape_tags($rr['dreport_result']), 'time' => escape_tags(datetime_convert('UTC',date_default_timezone_get(),$rr['dreport_time'])) ]; @@ -167,14 +130,14 @@ class Dreport extends \Zotlabs\Web\Controller { '$push' => t('Redeliver'), '$entries' => $entries )); - - + + return $o; - - - + + + } - + private static function dreport_gravity_sort($a,$b) { if($a['gravity'] == $b['gravity']) { if($a['dreport_name'] === $b['dreport_name']) @@ -183,5 +146,5 @@ class Dreport extends \Zotlabs\Web\Controller { } return (($a['gravity'] > $b['gravity']) ? 1 : (-1)); } - + } diff --git a/Zotlabs/Module/Editpost.php b/Zotlabs/Module/Editpost.php index 49b2892e8..c6cfc6dc4 100644 --- a/Zotlabs/Module/Editpost.php +++ b/Zotlabs/Module/Editpost.php @@ -58,9 +58,9 @@ class Editpost extends \Zotlabs\Web\Controller { if ($catsenabled){ $itm = fetch_post_tags($itm); - + $cats = get_terms_oftype($itm[0]['term'], TERM_CATEGORY); - + foreach ($cats as $cat) { if (strlen($category)) $category .= ', '; @@ -95,6 +95,7 @@ class Editpost extends \Zotlabs\Web\Controller { 'defloc' => $channel['channel_location'], 'visitor' => true, 'title' => htmlspecialchars_decode($itm[0]['title'],ENT_COMPAT), + 'summary' => htmlspecialchars_decode($itm[0]['summary'],ENT_COMPAT), 'category' => $category, 'showacl' => false, 'profile_uid' => $owner_uid, diff --git a/Zotlabs/Module/Embedphotos.php b/Zotlabs/Module/Embedphotos.php index 9b0884197..ed5b24724 100644 --- a/Zotlabs/Module/Embedphotos.php +++ b/Zotlabs/Module/Embedphotos.php @@ -40,7 +40,8 @@ class Embedphotos extends \Zotlabs\Web\Controller { if (!$href) { json_return_and_die(array('errormsg' => 'Error retrieving link ' . $href, 'status' => false)); } - $resource_id = array_pop(explode('/', $href)); + $arr = explode('/', $href); + $resource_id = array_pop($arr); $x = self::photolink($resource_id); if($x) json_return_and_die(array('status' => true, 'photolink' => $x, 'resource_id' => $resource_id)); diff --git a/Zotlabs/Module/Events.php b/Zotlabs/Module/Events.php deleted file mode 100644 index 681d6887d..000000000 --- a/Zotlabs/Module/Events.php +++ /dev/null @@ -1,750 +0,0 @@ -<?php -namespace Zotlabs\Module; - -require_once('include/conversation.php'); -require_once('include/bbcode.php'); -require_once('include/datetime.php'); -require_once('include/event.php'); -require_once('include/items.php'); -require_once('include/html2plain.php'); - -class Events extends \Zotlabs\Web\Controller { - - function post() { - - // this module is deprecated - return; - - logger('post: ' . print_r($_REQUEST,true), LOGGER_DATA); - - if(! local_channel()) - return; - - if(($_FILES) && array_key_exists('userfile',$_FILES) && intval($_FILES['userfile']['size'])) { - $src = $_FILES['userfile']['tmp_name']; - if($src) { - $result = parse_ical_file($src,local_channel()); - if($result) - info( t('Calendar entries imported.') . EOL); - else - notice( t('No calendar entries found.') . EOL); - @unlink($src); - } - goaway(z_root() . '/events'); - } - - - $event_id = ((x($_POST,'event_id')) ? intval($_POST['event_id']) : 0); - $event_hash = ((x($_POST,'event_hash')) ? $_POST['event_hash'] : ''); - - $xchan = ((x($_POST,'xchan')) ? dbesc($_POST['xchan']) : ''); - $uid = local_channel(); - - $start_text = escape_tags($_REQUEST['start_text']); - $finish_text = escape_tags($_REQUEST['finish_text']); - - $adjust = intval($_POST['adjust']); - $nofinish = intval($_POST['nofinish']); - - $timezone = ((x($_POST,'timezone_select')) ? notags(trim($_POST['timezone_select'])) : ''); - - $tz = (($timezone) ? $timezone : date_default_timezone_get()); - - $categories = escape_tags(trim($_POST['category'])); - - // only allow editing your own events. - - if(($xchan) && ($xchan !== get_observer_hash())) - return; - - if($start_text) { - $start = $start_text; - } - else { - $start = sprintf('%d-%d-%d %d:%d:0',$startyear,$startmonth,$startday,$starthour,$startminute); - } - - - if($finish_text) { - $finish = $finish_text; - } - else { - $finish = sprintf('%d-%d-%d %d:%d:0',$finishyear,$finishmonth,$finishday,$finishhour,$finishminute); - } - - if($nofinish) { - $finish = NULL_DATE; - } - - - if($adjust) { - $start = datetime_convert($tz,'UTC',$start); - if(! $nofinish) - $finish = datetime_convert($tz,'UTC',$finish); - } - else { - $start = datetime_convert('UTC','UTC',$start); - if(! $nofinish) - $finish = datetime_convert('UTC','UTC',$finish); - } - - // Don't allow the event to finish before it begins. - // It won't hurt anything, but somebody will file a bug report - // and we'll waste a bunch of time responding to it. Time that - // could've been spent doing something else. - - - $summary = escape_tags(trim($_POST['summary'])); - $desc = escape_tags(trim($_POST['desc'])); - $location = escape_tags(trim($_POST['location'])); - $type = escape_tags(trim($_POST['type'])); - - require_once('include/text.php'); - linkify_tags($desc, local_channel()); - linkify_tags($location, local_channel()); - - //$action = ($event_hash == '') ? 'new' : "event/" . $event_hash; - - //fixme: this url gives a wsod if there is a linebreak detected in one of the variables ($desc or $location) - //$onerror_url = z_root() . "/events/" . $action . "?summary=$summary&description=$desc&location=$location&start=$start_text&finish=$finish_text&adjust=$adjust&nofinish=$nofinish&type=$type"; - $onerror_url = z_root() . "/events"; - - if(strcmp($finish,$start) < 0 && !$nofinish) { - notice( t('Event can not end before it has started.') . EOL); - if(intval($_REQUEST['preview'])) { - echo( t('Unable to generate preview.')); - killme(); - } - goaway($onerror_url); - } - - if((! $summary) || (! $start)) { - notice( t('Event title and start time are required.') . EOL); - if(intval($_REQUEST['preview'])) { - echo( t('Unable to generate preview.')); - killme(); - } - goaway($onerror_url); - } - - // $share = ((intval($_POST['distr'])) ? intval($_POST['distr']) : 0); - - $share = 1; - - $channel = \App::get_channel(); - - $acl = new \Zotlabs\Access\AccessList(false); - - if($event_id) { - $x = q("select * from event where id = %d and uid = %d limit 1", - intval($event_id), - intval(local_channel()) - ); - if(! $x) { - notice( t('Event not found.') . EOL); - if(intval($_REQUEST['preview'])) { - echo( t('Unable to generate preview.')); - killme(); - } - return; - } - - $acl->set($x[0]); - - $created = $x[0]['created']; - $edited = datetime_convert(); - - if($x[0]['allow_cid'] === '<' . $channel['channel_hash'] . '>' - && $x[0]['allow_gid'] === '' && $x[0]['deny_cid'] === '' && $x[0]['deny_gid'] === '') { - $share = false; - } - else { - $share = true; - } - } - else { - $created = $edited = datetime_convert(); - if($share) { - $acl->set_from_array($_POST); - } - else { - $acl->set(array('allow_cid' => '<' . $channel['channel_hash'] . '>', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '')); - } - } - - $post_tags = array(); - $channel = \App::get_channel(); - $ac = $acl->get(); - - if(strlen($categories)) { - $cats = explode(',',$categories); - foreach($cats as $cat) { - $post_tags[] = array( - 'uid' => $profile_uid, - 'ttype' => TERM_CATEGORY, - 'otype' => TERM_OBJ_POST, - 'term' => trim($cat), - 'url' => $channel['xchan_url'] . '?f=&cat=' . urlencode(trim($cat)) - ); - } - } - - $datarray = array(); - $datarray['dtstart'] = $start; - $datarray['dtend'] = $finish; - $datarray['summary'] = $summary; - $datarray['description'] = $desc; - $datarray['location'] = $location; - $datarray['etype'] = $type; - $datarray['adjust'] = $adjust; - $datarray['nofinish'] = $nofinish; - $datarray['uid'] = local_channel(); - $datarray['account'] = get_account_id(); - $datarray['event_xchan'] = $channel['channel_hash']; - $datarray['allow_cid'] = $ac['allow_cid']; - $datarray['allow_gid'] = $ac['allow_gid']; - $datarray['deny_cid'] = $ac['deny_cid']; - $datarray['deny_gid'] = $ac['deny_gid']; - $datarray['private'] = (($acl->is_private()) ? 1 : 0); - $datarray['id'] = $event_id; - $datarray['created'] = $created; - $datarray['edited'] = $edited; - - if(intval($_REQUEST['preview'])) { - $html = format_event_html($datarray); - echo $html; - killme(); - } - - $event = event_store_event($datarray); - - if($post_tags) - $datarray['term'] = $post_tags; - - $item_id = event_store_item($datarray,$event); - - if($item_id) { - $r = q("select * from item where id = %d", - intval($item_id) - ); - if($r) { - xchan_query($r); - $sync_item = fetch_post_tags($r); - $z = q("select * from event where event_hash = '%s' and uid = %d limit 1", - dbesc($r[0]['resource_id']), - intval($channel['channel_id']) - ); - if($z) { - build_sync_packet($channel['channel_id'],array('event_item' => array(encode_item($sync_item[0],true)),'event' => $z)); - } - } - } - - if($share) - \Zotlabs\Daemon\Master::Summon(array('Notifier','event',$item_id)); - - } - - - - function get() { - - // this module is deprecated - return; - - if(argc() > 2 && argv(1) == 'ical') { - $event_id = argv(2); - - require_once('include/security.php'); - $sql_extra = permissions_sql(local_channel()); - - $r = q("select * from event where event_hash = '%s' $sql_extra limit 1", - dbesc($event_id) - ); - if($r) { - header('Content-type: text/calendar'); - header('content-disposition: attachment; filename="' . t('event') . '-' . $event_id . '.ics"' ); - echo ical_wrapper($r); - killme(); - } - else { - notice( t('Event not found.') . EOL ); - return; - } - } - - if(! local_channel()) { - notice( t('Permission denied.') . EOL); - return; - } - - \App::$profile_uid = local_channel(); - nav_set_selected('Events'); - - - if((argc() > 2) && (argv(1) === 'ignore') && intval(argv(2))) { - $r = q("update event set dismissed = 1 where id = %d and uid = %d", - intval(argv(2)), - intval(local_channel()) - ); - } - - if((argc() > 2) && (argv(1) === 'unignore') && intval(argv(2))) { - $r = q("update event set dismissed = 0 where id = %d and uid = %d", - intval(argv(2)), - intval(local_channel()) - ); - } - - $first_day = feature_enabled(local_channel(), 'events_cal_first_day'); - $first_day = (($first_day) ? $first_day : 0); - - $htpl = get_markup_template('event_head.tpl'); - \App::$page['htmlhead'] .= replace_macros($htpl,array( - '$baseurl' => z_root(), - '$module_url' => '/events', - '$modparams' => 1, - '$lang' => \App::$language, - '$first_day' => $first_day - )); - - $o = ''; - - $channel = \App::get_channel(); - - $mode = 'view'; - $y = 0; - $m = 0; - $ignored = ((x($_REQUEST,'ignored')) ? " and dismissed = " . intval($_REQUEST['ignored']) . " " : ''); - - - // logger('args: ' . print_r(\App::$argv,true)); - - - - if(argc() > 1) { - if(argc() > 2 && argv(1) === 'add') { - $mode = 'add'; - $item_id = intval(argv(2)); - } - if(argc() > 2 && argv(1) === 'drop') { - $mode = 'drop'; - $event_id = argv(2); - } - if(argc() > 2 && intval(argv(1)) && intval(argv(2))) { - $mode = 'view'; - $y = intval(argv(1)); - $m = intval(argv(2)); - } - if(argc() <= 2) { - $mode = 'view'; - $event_id = argv(1); - } - } - - if($mode === 'add') { - event_addtocal($item_id,local_channel()); - killme(); - } - - if($mode == 'view') { - - /* edit/create form */ - if($event_id) { - $r = q("SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1", - dbesc($event_id), - intval(local_channel()) - ); - if(count($r)) - $orig_event = $r[0]; - } - - $channel = \App::get_channel(); - - // Passed parameters overrides anything found in the DB - if(!x($orig_event)) - $orig_event = array(); - - // In case of an error the browser is redirected back here, with these parameters filled in with the previous values - /* - if(x($_REQUEST,'nofinish')) $orig_event['nofinish'] = $_REQUEST['nofinish']; - if(x($_REQUEST,'adjust')) $orig_event['adjust'] = $_REQUEST['adjust']; - if(x($_REQUEST,'summary')) $orig_event['summary'] = $_REQUEST['summary']; - if(x($_REQUEST,'description')) $orig_event['description'] = $_REQUEST['description']; - if(x($_REQUEST,'location')) $orig_event['location'] = $_REQUEST['location']; - if(x($_REQUEST,'start')) $orig_event['dtstart'] = $_REQUEST['start']; - if(x($_REQUEST,'finish')) $orig_event['dtend'] = $_REQUEST['finish']; - if(x($_REQUEST,'type')) $orig_event['etype'] = $_REQUEST['type']; - */ - - $n_checked = ((x($orig_event) && $orig_event['nofinish']) ? ' checked="checked" ' : ''); - $a_checked = ((x($orig_event) && $orig_event['adjust']) ? ' checked="checked" ' : ''); - $t_orig = ((x($orig_event)) ? $orig_event['summary'] : ''); - $d_orig = ((x($orig_event)) ? $orig_event['description'] : ''); - $l_orig = ((x($orig_event)) ? $orig_event['location'] : ''); - $eid = ((x($orig_event)) ? $orig_event['id'] : 0); - $event_xchan = ((x($orig_event)) ? $orig_event['event_xchan'] : $channel['channel_hash']); - $mid = ((x($orig_event)) ? $orig_event['mid'] : ''); - - if(! x($orig_event)) { - $sh_checked = ''; - $a_checked = ' checked="checked" '; - } - else { - $sh_checked = ((($orig_event['allow_cid'] === '<' . $channel['channel_hash'] . '>' || (! $orig_event['allow_cid'])) && (! $orig_event['allow_gid']) && (! $orig_event['deny_cid']) && (! $orig_event['deny_gid'])) ? '' : ' checked="checked" ' ); - } - - if($orig_event['event_xchan']) - $sh_checked .= ' disabled="disabled" '; - - $sdt = ((x($orig_event)) ? $orig_event['dtstart'] : 'now'); - - $fdt = ((x($orig_event)) ? $orig_event['dtend'] : '+1 hour'); - - $tz = date_default_timezone_get(); - if(x($orig_event)) - $tz = (($orig_event['adjust']) ? date_default_timezone_get() : 'UTC'); - - $syear = datetime_convert('UTC', $tz, $sdt, 'Y'); - $smonth = datetime_convert('UTC', $tz, $sdt, 'm'); - $sday = datetime_convert('UTC', $tz, $sdt, 'd'); - $shour = datetime_convert('UTC', $tz, $sdt, 'H'); - $sminute = datetime_convert('UTC', $tz, $sdt, 'i'); - - $stext = datetime_convert('UTC',$tz,$sdt); - $stext = substr($stext,0,14) . "00:00"; - - $fyear = datetime_convert('UTC', $tz, $fdt, 'Y'); - $fmonth = datetime_convert('UTC', $tz, $fdt, 'm'); - $fday = datetime_convert('UTC', $tz, $fdt, 'd'); - $fhour = datetime_convert('UTC', $tz, $fdt, 'H'); - $fminute = datetime_convert('UTC', $tz, $fdt, 'i'); - - $ftext = datetime_convert('UTC',$tz,$fdt); - $ftext = substr($ftext,0,14) . "00:00"; - - $type = ((x($orig_event)) ? $orig_event['etype'] : 'event'); - - $f = get_config('system','event_input_format'); - if(! $f) - $f = 'ymd'; - - $catsenabled = feature_enabled(local_channel(),'categories'); - - $category = ''; - - if($catsenabled && x($orig_event)){ - $itm = q("select * from item where resource_type = 'event' and resource_id = '%s' and uid = %d limit 1", - dbesc($orig_event['event_hash']), - intval(local_channel()) - ); - $itm = fetch_post_tags($itm); - if($itm) { - $cats = get_terms_oftype($itm[0]['term'], TERM_CATEGORY); - foreach ($cats as $cat) { - if(strlen($category)) - $category .= ', '; - $category .= $cat['term']; - } - } - } - - require_once('include/acl_selectors.php'); - - $acl = new \Zotlabs\Access\AccessList($channel); - $perm_defaults = $acl->get(); - - $permissions = ((x($orig_event)) ? $orig_event : $perm_defaults); - - $tpl = get_markup_template('event_form.tpl'); - - $form = replace_macros($tpl,array( - '$post' => z_root() . '/events', - '$eid' => $eid, - '$type' => $type, - '$xchan' => $event_xchan, - '$mid' => $mid, - '$event_hash' => $event_id, - '$summary' => array('summary', (($event_id) ? t('Edit event title') : t('Event title')), $t_orig, t('Required'), '*'), - '$catsenabled' => $catsenabled, - '$placeholdercategory' => t('Categories (comma-separated list)'), - '$c_text' => (($event_id) ? t('Edit Category') : t('Category')), - '$category' => $category, - '$required' => '<span class="required" title="' . t('Required') . '">*</span>', - '$s_dsel' => datetimesel($f,new \DateTime(),\DateTime::createFromFormat('Y',$syear+5),\DateTime::createFromFormat('Y-m-d H:i',"$syear-$smonth-$sday $shour:$sminute"), (($event_id) ? t('Edit start date and time') : t('Start date and time')), 'start_text',true,true,'','',true,$first_day), - '$n_text' => t('Finish date and time are not known or not relevant'), - '$n_checked' => $n_checked, - '$f_dsel' => datetimesel($f,new \DateTime(),\DateTime::createFromFormat('Y',$fyear+5),\DateTime::createFromFormat('Y-m-d H:i',"$fyear-$fmonth-$fday $fhour:$fminute"), (($event_id) ? t('Edit finish date and time') : t('Finish date and time')),'finish_text',true,true,'start_text','',false,$first_day), - '$nofinish' => array('nofinish', t('Finish date and time are not known or not relevant'), $n_checked, '', array(t('No'),t('Yes')), 'onclick="enableDisableFinishDate();"'), - '$adjust' => array('adjust', t('Adjust for viewer timezone'), $a_checked, t('Important for events that happen in a particular place. Not practical for global holidays.'), array(t('No'),t('Yes'))), - '$a_text' => t('Adjust for viewer timezone'), - '$d_text' => (($event_id) ? t('Edit Description') : t('Description')), - '$d_orig' => $d_orig, - '$l_text' => (($event_id) ? t('Edit Location') : t('Location')), - '$l_orig' => $l_orig, - '$t_orig' => $t_orig, - '$preview' => t('Preview'), - '$perms_label' => t('Permission settings'), - // populating the acl dialog was a permission description from view_stream because Cal.php, which - // displays events, says "since we don't currently have an event permission - use the stream permission" - '$acl' => (($orig_event['event_xchan']) ? '' : populate_acl(((x($orig_event)) ? $orig_event : $perm_defaults), false, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'))), - - '$allow_cid' => acl2json($permissions['allow_cid']), - '$allow_gid' => acl2json($permissions['allow_gid']), - '$deny_cid' => acl2json($permissions['deny_cid']), - '$deny_gid' => acl2json($permissions['deny_gid']), - '$tz_choose' => feature_enabled(local_channel(),'event_tz_select'), - '$timezone' => array('timezone_select' , t('Timezone:'), date_default_timezone_get(), '', get_timezones()), - - '$lockstate' => (($acl->is_private()) ? 'lock' : 'unlock'), - - '$submit' => t('Submit'), - '$advanced' => t('Advanced Options') - - )); - /* end edit/create form */ - - $thisyear = datetime_convert('UTC',date_default_timezone_get(),'now','Y'); - $thismonth = datetime_convert('UTC',date_default_timezone_get(),'now','m'); - if(! $y) - $y = intval($thisyear); - if(! $m) - $m = intval($thismonth); - - $export = false; - if(argc() === 4 && argv(3) === 'export') - $export = true; - - // Put some limits on dates. The PHP date functions don't seem to do so well before 1900. - // An upper limit was chosen to keep search engines from exploring links millions of years in the future. - - if($y < 1901) - $y = 1900; - if($y > 2099) - $y = 2100; - - $nextyear = $y; - $nextmonth = $m + 1; - if($nextmonth > 12) { - $nextmonth = 1; - $nextyear ++; - } - - $prevyear = $y; - if($m > 1) - $prevmonth = $m - 1; - else { - $prevmonth = 12; - $prevyear --; - } - - $dim = get_dim($y,$m); - $start = sprintf('%d-%d-%d %d:%d:%d',$y,$m,1,0,0,0); - $finish = sprintf('%d-%d-%d %d:%d:%d',$y,$m,$dim,23,59,59); - - - if (argv(1) === 'json'){ - if (x($_GET,'start')) $start = $_GET['start']; - if (x($_GET,'end')) $finish = $_GET['end']; - } - - $start = datetime_convert('UTC','UTC',$start); - $finish = datetime_convert('UTC','UTC',$finish); - - $adjust_start = datetime_convert('UTC', date_default_timezone_get(), $start); - $adjust_finish = datetime_convert('UTC', date_default_timezone_get(), $finish); - - if (x($_GET,'id')){ - $r = q("SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan - from event left join item on resource_id = event_hash where resource_type = 'event' and event.uid = %d and event.id = %d limit 1", - intval(local_channel()), - intval($_GET['id']) - ); - } elseif($export) { - $r = q("SELECT * from event where uid = %d - AND (( adjust = 0 AND ( dtend >= '%s' or nofinish = 1 ) AND dtstart <= '%s' ) - OR ( adjust = 1 AND ( dtend >= '%s' or nofinish = 1 ) AND dtstart <= '%s' )) ", - intval(local_channel()), - dbesc($start), - dbesc($finish), - dbesc($adjust_start), - dbesc($adjust_finish) - ); - } - else { - // fixed an issue with "nofinish" events not showing up in the calendar. - // There's still an issue if the finish date crosses the end of month. - // Noting this for now - it will need to be fixed here and in Friendica. - // Ultimately the finish date shouldn't be involved in the query. - - $r = q("SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan - from event left join item on event_hash = resource_id - where resource_type = 'event' and event.uid = %d and event.uid = item.uid $ignored - AND (( adjust = 0 AND ( dtend >= '%s' or nofinish = 1 ) AND dtstart <= '%s' ) - OR ( adjust = 1 AND ( dtend >= '%s' or nofinish = 1 ) AND dtstart <= '%s' )) ", - intval(local_channel()), - dbesc($start), - dbesc($finish), - dbesc($adjust_start), - dbesc($adjust_finish) - ); - } - - $links = array(); - - if($r && ! $export) { - xchan_query($r); - $r = fetch_post_tags($r,true); - - $r = sort_by_date($r); - } - - if($r) { - foreach($r as $rr) { - $j = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtstart'], 'j') : datetime_convert('UTC','UTC',$rr['dtstart'],'j')); - if(! x($links,$j)) - $links[$j] = z_root() . '/' . \App::$cmd . '#link-' . $j; - } - } - - $events=array(); - - $last_date = ''; - $fmt = t('l, F j'); - - if($r) { - - foreach($r as $rr) { - - $j = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtstart'], 'j') : datetime_convert('UTC','UTC',$rr['dtstart'],'j')); - $d = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtstart'], $fmt) : datetime_convert('UTC','UTC',$rr['dtstart'],$fmt)); - $d = day_translate($d); - - $start = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtstart'], 'c') : datetime_convert('UTC','UTC',$rr['dtstart'],'c')); - if ($rr['nofinish']){ - $end = null; - } else { - $end = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtend'], 'c') : datetime_convert('UTC','UTC',$rr['dtend'],'c')); - - // give a fake end to birthdays so they get crammed into a - // single day on the calendar - - if($rr['etype'] === 'birthday') - $end = null; - } - - - $is_first = ($d !== $last_date); - - $last_date = $d; - - $edit = ((local_channel() && $rr['author_xchan'] == get_observer_hash()) ? array(z_root().'/events/'.$rr['event_hash'].'?expandform=1',t('Edit event'),'','') : false); - - $drop = array(z_root().'/events/drop/'.$rr['event_hash'],t('Delete event'),'',''); - - $title = strip_tags(html_entity_decode(zidify_links(bbcode($rr['summary'])),ENT_QUOTES,'UTF-8')); - if(! $title) { - list($title, $_trash) = explode("<br",bbcode($rr['desc']),2); - $title = strip_tags(html_entity_decode($title,ENT_QUOTES,'UTF-8')); - } - $html = format_event_html($rr); - $rr['desc'] = zidify_links(smilies(bbcode($rr['desc']))); - $rr['description'] = htmlentities(html2plain(bbcode($rr['description'])),ENT_COMPAT,'UTF-8',false); - $rr['location'] = zidify_links(smilies(bbcode($rr['location']))); - $events[] = array( - 'id'=>$rr['id'], - 'hash' => $rr['event_hash'], - 'start'=> $start, - 'end' => $end, - 'drop' => $drop, - 'allDay' => false, - 'title' => $title, - - 'j' => $j, - 'd' => $d, - 'edit' => $edit, - 'is_first'=>$is_first, - 'item'=>$rr, - 'html'=>$html, - 'plink' => array($rr['plink'],t('Link to Source'),'',''), - ); - - } - } - - if($export) { - header('Content-type: text/calendar'); - header('content-disposition: attachment; filename="' . t('calendar') . '-' . $channel['channel_address'] . '.ics"' ); - echo ical_wrapper($r); - killme(); - } - - if (\App::$argv[1] === 'json'){ - echo json_encode($events); killme(); - } - - // links: array('href', 'text', 'extra css classes', 'title') - if (x($_GET,'id')){ - $tpl = get_markup_template("event.tpl"); - } - else { - $tpl = get_markup_template("events-js.tpl"); - } - - $o = replace_macros($tpl, array( - '$baseurl' => z_root(), - '$new_event' => array(z_root().'/events',(($event_id) ? t('Edit Event') : t('Create Event')),'',''), - '$previus' => array(z_root()."/events/$prevyear/$prevmonth",t('Previous'),'',''), - '$next' => array(z_root()."/events/$nextyear/$nextmonth",t('Next'),'',''), - '$export' => array(z_root()."/events/$y/$m/export",t('Export'),'',''), - '$calendar' => cal($y,$m,$links, ' eventcal'), - '$events' => $events, - '$view_label' => t('View'), - '$month' => t('Month'), - '$week' => t('Week'), - '$day' => t('Day'), - '$prev' => t('Previous'), - '$next' => t('Next'), - '$today' => t('Today'), - '$form' => $form, - '$expandform' => ((x($_GET,'expandform')) ? true : false), - )); - - if (x($_GET,'id')){ echo $o; killme(); } - - return $o; - } - - if($mode === 'drop' && $event_id) { - $r = q("SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1", - dbesc($event_id), - intval(local_channel()) - ); - - $sync_event = $r[0]; - - if($r) { - $r = q("delete from event where event_hash = '%s' and uid = %d", - dbesc($event_id), - intval(local_channel()) - ); - if($r) { - $r = q("update item set resource_type = '', resource_id = '' where resource_type = 'event' and resource_id = '%s' and uid = %d", - dbesc($event_id), - intval(local_channel()) - ); - $sync_event['event_deleted'] = 1; - build_sync_packet(0,array('event' => array($sync_event))); - - info( t('Event removed') . EOL); - } - else { - notice( t('Failed to remove event' ) . EOL); - } - goaway(z_root() . '/events'); - } - } - - } - -} diff --git a/Zotlabs/Module/Fhublocs.php b/Zotlabs/Module/Fhublocs.php index dcd399a1f..9dcece715 100644 --- a/Zotlabs/Module/Fhublocs.php +++ b/Zotlabs/Module/Fhublocs.php @@ -3,7 +3,6 @@ namespace Zotlabs\Module; use Zotlabs\Lib\Libzot; -require_once('include/zot.php'); require_once('include/crypto.php'); /* fix missing or damaged hublocs */ @@ -15,12 +14,12 @@ class Fhublocs extends \Zotlabs\Web\Controller { if(! is_site_admin()) return; - + $o = ''; - + $r = q("select * from channel where channel_removed = 0"); $sitekey = get_config('system','pubkey'); - + if($r) { foreach($r as $rr) { @@ -38,14 +37,14 @@ class Fhublocs extends \Zotlabs\Web\Controller { if($found) { $o .= 'Hubloc exists for ' . $rr['channel_name'] . EOL; continue; - } + } } $y = q("select xchan_addr from xchan where xchan_hash = '%s' limit 1", dbesc($rr['channel_hash']) ); if($y) $primary_address = $y[0]['xchan_addr']; - + $hub_address = channel_reddress($rr); $primary = (($hub_address === $primary_address) ? 1 : 0); @@ -56,26 +55,9 @@ class Fhublocs extends \Zotlabs\Web\Controller { dbesc($rr['channel_hash']), dbesc(z_root()) ); - + // Create a verified hub location pointing to this site. - -/* - $h = hubloc_store_lowlevel( - [ - 'hubloc_guid' => $rr['channel_guid'], - 'hubloc_guid_sig' => $rr['channel_guid_sig'], - 'hubloc_hash' => $rr['channel_hash'], - 'hubloc_addr' => channel_reddress($rr), - 'hubloc_network' => 'zot', - 'hubloc_primary' => $primary, - 'hubloc_url' => z_root(), - 'hubloc_url_sig' => base64url_encode(rsa_sign(z_root(),$rr['channel_prvkey'])), - 'hubloc_host' => \App::get_hostname(), - 'hubloc_callback' => z_root() . '/post', - 'hubloc_sitekey' => $sitekey - ] - ); -*/ + $h = hubloc_store_lowlevel( [ 'hubloc_guid' => $rr['channel_guid'], @@ -99,11 +81,11 @@ class Fhublocs extends \Zotlabs\Web\Controller { $o . 'local hubloc created for ' . $rr['channel_name'] . EOL; else $o .= 'DB update failed for ' . $rr['channel_name'] . EOL; - + } - + return $o; - + } } } diff --git a/Zotlabs/Module/File_upload.php b/Zotlabs/Module/File_upload.php index 1735e9487..d4c9ad59a 100644 --- a/Zotlabs/Module/File_upload.php +++ b/Zotlabs/Module/File_upload.php @@ -11,17 +11,16 @@ require_once('include/photos.php'); class File_upload extends \Zotlabs\Web\Controller { function post() { - logger('file upload: ' . print_r($_REQUEST,true)); logger('file upload: ' . print_r($_FILES,true)); - + $channel = (($_REQUEST['channick']) ? channelx_by_nick($_REQUEST['channick']) : null); - + if(! $channel) { logger('channel not found'); killme(); } - + $_REQUEST['source'] = 'file_upload'; if($channel['channel_id'] != local_channel()) { @@ -40,13 +39,11 @@ class File_upload extends \Zotlabs\Web\Controller { $r = attach_mkdir($channel, get_observer_hash(), $_REQUEST); if($r['success']) { $hash = $r['data']['hash']; - $sync = attach_export_data($channel,$hash); if($sync) { Libsync::build_sync_packet($channel['channel_id'],array('file' => array($sync))); } - goaway(z_root() . '/cloud/' . $channel['channel_address'] . '/' . $r['data']['display_path']); - + goaway(z_root() . '/' . $_REQUEST['return_url']); } } else { @@ -54,8 +51,6 @@ class File_upload extends \Zotlabs\Web\Controller { $matches = []; $partial = false; - - if(array_key_exists('HTTP_CONTENT_RANGE',$_SERVER)) { $pm = preg_match('/bytes (\d*)\-(\d*)\/(\d*)/',$_SERVER['HTTP_CONTENT_RANGE'],$matches); if($pm) { @@ -69,7 +64,7 @@ class File_upload extends \Zotlabs\Web\Controller { if($x['partial']) { header('Range: bytes=0-' . (($x['length']) ? $x['length'] - 1 : 0)); - json_return_and_die($result); + json_return_and_die($x); } else { header('Range: bytes=0-' . (($x['size']) ? $x['size'] - 1 : 0)); @@ -83,7 +78,7 @@ class File_upload extends \Zotlabs\Web\Controller { ]; } } - else { + else { if(! array_key_exists('userfile',$_FILES)) { $_FILES['userfile'] = [ 'name' => $_FILES['files']['name'], @@ -103,8 +98,12 @@ class File_upload extends \Zotlabs\Web\Controller { } } + + if(is_ajax()) + killme(); + goaway(z_root() . '/' . $_REQUEST['return_url']); - + } - + } diff --git a/Zotlabs/Module/Filestorage.php b/Zotlabs/Module/Filestorage.php index 0c6233493..0d132e998 100644 --- a/Zotlabs/Module/Filestorage.php +++ b/Zotlabs/Module/Filestorage.php @@ -11,6 +11,9 @@ class Filestorage extends \Zotlabs\Web\Controller { function post() { + notice( t('Deprecated!') . EOL); + return; + $channel_id = ((x($_POST, 'uid')) ? intval($_POST['uid']) : 0); if((! $channel_id) || (! local_channel()) || ($channel_id != local_channel())) { @@ -47,6 +50,9 @@ class Filestorage extends \Zotlabs\Web\Controller { function get() { + notice( t('Deprecated!') . EOL); + return; + if(argc() > 1) $which = argv(1); else { @@ -88,7 +94,7 @@ class Filestorage extends \Zotlabs\Web\Controller { } else { notice( t('Permission denied.') . EOL); - if($json_return) + if($json_return) json_return_and_die([ 'success' => false ]); return; } @@ -102,24 +108,23 @@ class Filestorage extends \Zotlabs\Web\Controller { if(! $r) { notice( t('File not found.') . EOL); - if($json_return) + if($json_return) json_return_and_die([ 'success' => false ]); goaway(z_root() . '/cloud/' . $which); } - if(local_channel() !== $owner) { + if((local_channel() !== $owner) && !$admin_delete) { if($r[0]['creator'] && $r[0]['creator'] !== $ob_hash) { notice( t('Permission denied.') . EOL); - if($json_return) + if($json_return) json_return_and_die([ 'success' => false ]); goaway(z_root() . '/cloud/' . $which); } } - $f = $r[0]; $channel = channelx_by_n($owner); @@ -138,7 +143,7 @@ class Filestorage extends \Zotlabs\Web\Controller { if($json_return) json_return_and_die([ 'success' => true ]); - goaway(dirname($url)); + //goaway(dirname($url)); } diff --git a/Zotlabs/Module/Follow.php b/Zotlabs/Module/Follow.php index 11febd8fc..4fe20f56b 100644 --- a/Zotlabs/Module/Follow.php +++ b/Zotlabs/Module/Follow.php @@ -14,7 +14,7 @@ use Zotlabs\Daemon\Master; class Follow extends Controller { function init() { - + if (ActivityStreams::is_as_request() && argc() == 2) { $abook_id = intval(argv(1)); @@ -73,11 +73,11 @@ class Follow extends Controller { $url = notags(trim(punify($_REQUEST['url']))); $return_url = $_SESSION['return_url']; $confirm = intval($_REQUEST['confirm']); - $interactive = (($_REQUEST['interactive']) ? intval($_REQUEST['interactive']) : 1); + $interactive = (($_REQUEST['interactive']) ? intval($_REQUEST['interactive']) : 1); $channel = App::get_channel(); $result = Connect::connect($channel,$url); - + if ($result['success'] == false) { if ($result['message']) { notice($result['message']); @@ -89,9 +89,9 @@ class Follow extends Controller { json_return_and_die($result); } } - + info( t('Connection added.') . EOL); - + $clone = array(); foreach ($result['abook'] as $k => $v) { if (strpos($k,'abook_') === 0) { @@ -101,30 +101,30 @@ class Follow extends Controller { unset($clone['abook_id']); unset($clone['abook_account']); unset($clone['abook_channel']); - + $abconfig = load_abconfig($channel['channel_id'],$clone['abook_xchan']); if ($abconfig) { $clone['abconfig'] = $abconfig; } Libsync::build_sync_packet(0, [ 'abook' => [ $clone ] ], true); - + $can_view_stream = their_perms_contains($channel['channel_id'],$clone['abook_xchan'],'view_stream'); - + // If we can view their stream, pull in some posts - + if (($can_view_stream) || ($result['abook']['xchan_network'] === 'rss')) { Master::Summon([ 'Onepoll', $result['abook']['abook_id'] ]); } - + if ($interactive) { goaway(z_root() . '/connedit/' . $result['abook']['abook_id'] . '?follow=1'); } else { json_return_and_die([ 'success' => true ]); } - + } - + function get() { if (! local_channel()) { return login(); diff --git a/Zotlabs/Module/Getfile.php b/Zotlabs/Module/Getfile.php index 6d31d23fd..28d7eabb5 100644 --- a/Zotlabs/Module/Getfile.php +++ b/Zotlabs/Module/Getfile.php @@ -1,24 +1,26 @@ <?php namespace Zotlabs\Module; +use Zotlabs\Lib\Crypto; use Zotlabs\Web\HTTPSig; +use Zotlabs\Lib\Libzot; /** * module: getfile - * + * * used for synchronising files and photos across clones - * + * * The site initiating the file operation will send a sync packet to known clones. * They will respond by building the DB structures they require, then will provide a * post request to this site to grab the file data. This is sent as a stream direct to * disk at the other end, avoiding memory issues. * * Since magic-auth cannot easily be used by the CURL process at the other end, - * we will require a signed request which includes a timestamp. This should not be - * used without SSL and is potentially vulnerable to replay if an attacker decrypts + * we will require a signed request which includes a timestamp. This should not be + * used without SSL and is potentially vulnerable to replay if an attacker decrypts * the SSL traffic fast enough. The amount of time slop is configurable but defaults * to 3 minutes. - * + * */ @@ -53,13 +55,13 @@ class Getfile extends \Zotlabs\Web\Controller { $keyId = $sigblock['keyId']; if($keyId) { - $r = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash - where hubloc_addr = '%s' limit 1", + $r = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash + where hubloc_id_url = '%s'", dbesc(str_replace('acct:','',$keyId)) ); if($r) { - $hubloc = $r[0]; - $verified = HTTPSig::verify('',$hubloc['xchan_pubkey']); + $hubloc = Libzot::zot_record_preferred($r); + $verified = HTTPSig::verify('',$hubloc['xchan_pubkey']); if($verified && $verified['header_signed'] && $verified['header_valid'] && $hash == $hubloc['hubloc_hash']) { $header_verified = true; } @@ -73,15 +75,15 @@ class Getfile extends \Zotlabs\Web\Controller { logger('post: ' . print_r($_POST,true),LOGGER_DEBUG,LOG_INFO); if($header_verified) { logger('HTTPSig verified'); - } - + } + $channel = channelx_by_hash($hash); if((! $channel) || (! $time) || (! $sig)) { logger('error: missing info'); killme(); } - + if(isset($_POST['resolution'])) $resolution = intval($_POST['resolution']); elseif(substr($resource,-2,1) == '-') { @@ -90,22 +92,22 @@ class Getfile extends \Zotlabs\Web\Controller { } else { $resolution = (-1); - } + } $slop = intval(get_pconfig($channel['channel_id'],'system','getfile_time_slop')); if($slop < 1) $slop = 3; - + $d1 = datetime_convert('UTC','UTC',"now + $slop minutes"); - $d2 = datetime_convert('UTC','UTC',"now - $slop minutes"); - + $d2 = datetime_convert('UTC','UTC',"now - $slop minutes"); + if(! $header_verified) { if(($time > $d1) || ($time < $d2)) { logger('time outside allowable range'); killme(); } - - if(! rsa_verify($hash . '.' . $time,base64url_decode($sig),$channel['channel_pubkey'])) { + + if(! Crypto::verify($hash . '.' . $time,base64url_decode($sig),$channel['channel_pubkey'])) { logger('verify failed.'); killme(); } @@ -136,20 +138,20 @@ class Getfile extends \Zotlabs\Web\Controller { else { echo dbunescbin($r[0]['content']); } - } + } killme(); } $r = attach_by_hash($resource,$channel['channel_hash'],$revision); - + if(! $r['success']) { logger('attach_by_hash failed: ' . $r['message']); notice( $r['message'] . EOL); return; } - + $unsafe_types = array('text/html','text/css','application/javascript'); - + if(in_array($r['data']['filetype'],$unsafe_types) && (! channel_codeallowed($channel['channel_id']))) { header('Content-type: text/plain'); } diff --git a/Zotlabs/Module/Group.php b/Zotlabs/Module/Group.php index 993d428f5..a2d55a325 100644 --- a/Zotlabs/Module/Group.php +++ b/Zotlabs/Module/Group.php @@ -26,7 +26,7 @@ class Group extends Controller { } function post() { - + if(! local_channel()) { notice( t('Permission denied.') . EOL); return; @@ -35,10 +35,10 @@ class Group extends Controller { if(! Apps::system_app_installed(local_channel(), 'Privacy Groups')) { return; } - + if((argc() == 2) && (argv(1) === 'new')) { check_form_security_token_redirectOnErr('/group/new', 'group_edit'); - + $name = notags(trim($_POST['groupname'])); $public = intval($_POST['public']); $r = group_add(local_channel(),$name,$public); @@ -49,11 +49,11 @@ class Group extends Controller { notice( t('Could not create privacy group.') . EOL ); } goaway(z_root() . '/group'); - + } if((argc() == 2) && (intval(argv(1)))) { check_form_security_token_redirectOnErr('/group', 'group_edit'); - + $r = q("SELECT * FROM pgrp WHERE id = %d AND uid = %d LIMIT 1", intval(argv(1)), intval(local_channel()) @@ -61,12 +61,12 @@ class Group extends Controller { if(! $r) { notice( t('Privacy group not found.') . EOL ); goaway(z_root() . '/connections'); - + } $group = $r[0]; $groupname = notags(trim($_POST['groupname'])); $public = intval($_POST['public']); - + $hookinfo = [ 'pgrp_extras' => '', 'group'=>$group['id'] ]; call_hooks ('privacygroup_extras_post',$hookinfo); @@ -83,18 +83,14 @@ class Group extends Controller { Libsync::build_sync_packet(local_channel(),null,true); } - + goaway(z_root() . '/group/' . argv(1) . '/' . argv(2)); } - return; + return; } - + function get() { - $change = false; - - logger('mod_group: ' . App::$cmd,LOGGER_DEBUG); - if(! local_channel()) { notice( t('Permission denied') . EOL); return; @@ -103,12 +99,14 @@ class Group extends Controller { if(! Apps::system_app_installed(local_channel(), 'Privacy Groups')) { //Do not display any associated widgets at this point App::$pdl = ''; - - $o = '<b>' . t('Privacy Groups App') . ' (' . t('Not Installed') . '):</b><br>'; - $o .= t('Management of privacy groups'); - return $o; + $papp = Apps::get_papp('Privacy Groups'); + return Apps::app_render($papp, 'module'); } + logger('mod_group: ' . App::$cmd,LOGGER_DEBUG); + + $change = false; + // Switch to text mode interface if we have more than 'n' contacts or group members $switchtotext = get_pconfig(local_channel(),'system','groupedit_image_limit'); if($switchtotext === false) @@ -166,16 +164,16 @@ class Group extends Controller { $context = array('$submit' => t('Submit')); $tpl = get_markup_template('group_edit.tpl'); - + if((argc() == 3) && (argv(1) === 'drop')) { check_form_security_token_redirectOnErr('/group', 'group_drop', 't'); - + if(intval(argv(2))) { $r = q("SELECT gname FROM pgrp WHERE id = %d AND uid = %d LIMIT 1", intval(argv(2)), intval(local_channel()) ); - if($r) + if($r) $result = group_rmv(local_channel(),$r[0]['gname']); if($result) { $hookinfo = [ 'pgrp_extras' => '', 'group' => argv(2) ]; @@ -188,23 +186,23 @@ class Group extends Controller { goaway(z_root() . '/group'); // NOTREACHED } - - + + if((argc() > 2) && intval(argv(1)) && argv(2)) { - + check_form_security_token_ForbiddenOnErr('group_member_change', 't'); - + $r = q("SELECT abook_xchan from abook left join xchan on abook_xchan = xchan_hash where abook_xchan = '%s' and abook_channel = %d and xchan_deleted = 0 and abook_self = 0 and abook_blocked = 0 and abook_pending = 0 limit 1", dbesc(base64url_decode(argv(2))), intval(local_channel()) ); if(count($r)) $change = base64url_decode(argv(2)); - + } - + if((argc() > 1) && (intval(argv(1)))) { - + require_once('include/acl_selectors.php'); $r = q("SELECT * FROM pgrp WHERE id = %d AND uid = %d AND deleted = 0 LIMIT 1", intval(argv(1)), @@ -215,28 +213,28 @@ class Group extends Controller { goaway(z_root() . '/connections'); } $group = $r[0]; - - + + $members = group_get_members($group['id']); - + $preselected = array(); if(count($members)) { foreach($members as $member) if(! in_array($member['xchan_hash'],$preselected)) $preselected[] = $member['xchan_hash']; } - + if($change) { - + if(in_array($change,$preselected)) { group_rmv_member(local_channel(),$group['gname'],$change); } else { group_add_member(local_channel(),$group['gname'],$change); } - + $members = group_get_members($group['id']); - + $preselected = array(); if(count($members)) { foreach($members as $member) @@ -260,19 +258,19 @@ class Group extends Controller { '$form_security_token_drop' => get_form_security_token("group_drop"), '$pgrp_extras' => $pgrp_extras, ); - + } - + if(! isset($group)) return; - + $groupeditor = array( 'label_members' => t('Group members'), 'members' => array(), 'label_contacts' => t('Not in this group'), 'contacts' => array(), ); - + $sec_token = addslashes(get_form_security_token('group_member_change')); $textmode = (($switchtotext && (count($members) > $switchtotext)) ? true : 'card'); foreach($members as $member) { @@ -284,11 +282,11 @@ class Group extends Controller { else group_rmv_member(local_channel(),$group['gname'],$member['xchan_hash']); } - + $r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d AND abook_self = 0 and abook_blocked = 0 and abook_pending = 0 and xchan_deleted = 0 order by xchan_name asc", intval(local_channel()) ); - + if(count($r)) { $textmode = (($switchtotext && (count($r) > $switchtotext)) ? true : 'card'); foreach($r as $member) { @@ -299,20 +297,20 @@ class Group extends Controller { } } } - + $context['$groupeditor'] = $groupeditor; $context['$desc'] = t('Click a channel to toggle membership'); $context['$pgrp_extras'] = $pgrp_extras; - + if($change) { $tpl = get_markup_template('groupeditor.tpl'); echo replace_macros($tpl, $context); killme(); } - + return replace_macros($tpl, $context); - + } - - + + } diff --git a/Zotlabs/Module/Home.php b/Zotlabs/Module/Home.php index 7f2d6424d..315d05af6 100644 --- a/Zotlabs/Module/Home.php +++ b/Zotlabs/Module/Home.php @@ -1,100 +1,114 @@ <?php + namespace Zotlabs\Module; +use App; +use Zotlabs\Lib\Libzot; +use Zotlabs\Web\Controller; +use Zotlabs\Web\HTTPSig; + require_once('include/items.php'); require_once('include/conversation.php'); - -class Home extends \Zotlabs\Web\Controller { +class Home extends Controller { function init() { - $ret = array(); - - call_hooks('home_init',$ret); + $ret = []; + + call_hooks('home_init', $ret); + + if (Libzot::is_zot_request()) { + $key = get_config('system', 'prvkey'); + $ret = json_encode(Libzot::site_info()); + + $headers = ['Content-Type' => 'application/x-zot+json', 'Digest' => HTTPSig::generate_digest_header($ret)]; + $headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI']; + $h = HTTPSig::create_sig($headers, $key, z_root()); + HTTPSig::set_headers($h); + + echo $ret; + killme(); + } $splash = ((argc() > 1 && argv(1) === 'splash') ? true : false); - - $channel = \App::get_channel(); - if(local_channel() && $channel && $channel['xchan_url'] && ! $splash) { + + $channel = App::get_channel(); + if (local_channel() && $channel && $channel['xchan_url'] && !$splash) { $dest = (($ret['startpage']) ? $ret['startpage'] : ''); - if(! $dest) - $dest = get_config('system','startpage'); - if(! $dest) - $dest = z_root() . '/network'; - + if (!$dest) + $dest = get_config('system', 'startpage'); + if (!$dest) + $dest = z_root() . '/hq'; + goaway($dest); } - if(remote_channel() && (! $splash) && $_SESSION['atoken']) { + if (remote_channel() && (!$splash) && $_SESSION['atoken']) { $r = q("select * from atoken where atoken_id = %d", intval($_SESSION['atoken']) ); - if($r) { + if ($r) { $x = channelx_by_n($r[0]['atoken_uid']); - if($x) { + if ($x) { goaway(z_root() . '/channel/' . $x['channel_address']); } } - } + } - - if(get_account_id() && ! $splash) { + if (get_account_id() && !$splash) { goaway(z_root() . '/new_channel'); } - + } - - + function get($update = 0, $load = false) { - + $o = ''; - - - if(x($_SESSION,'theme')) + + if (x($_SESSION, 'theme')) unset($_SESSION['theme']); - if(x($_SESSION,'mobile_theme')) + if (x($_SESSION, 'mobile_theme')) unset($_SESSION['mobile_theme']); - + $splash = ((argc() > 1 && argv(1) === 'splash') ? true : false); - - call_hooks('home_content',$o); - if($o) + + call_hooks('home_content', $o); + if ($o) return $o; - - $frontpage = get_config('system','frontpage'); - if($frontpage) { - if(strpos($frontpage,'include:') !== false) { - $file = trim(str_replace('include:' , '', $frontpage)); - if(file_exists($file)) { - \App::$page['template'] = 'full'; - \App::$page['title'] = t('$Projectname'); - $o .= file_get_contents($file); + + $frontpage = get_config('system', 'frontpage'); + if ($frontpage) { + if (strpos($frontpage, 'include:') !== false) { + $file = trim(str_replace('include:', '', $frontpage)); + if (file_exists($file)) { + App::$page['template'] = 'full'; + App::$page['title'] = t('$Projectname'); + $o .= file_get_contents($file); return $o; } } - if(strpos($frontpage,'http') !== 0) + if (strpos($frontpage, 'http') !== 0) $frontpage = z_root() . '/' . $frontpage; - if(intval(get_config('system','mirror_frontpage'))) { + if (intval(get_config('system', 'mirror_frontpage'))) { $o = '<html><head><title>' . t('$Projectname') . '</title></head><body style="margin: 0; padding: 0; border: none;" ><iframe src="' . $frontpage . '" width="100%" height="100%" style="margin: 0; padding: 0; border: none;" ></iframe></body></html>'; echo $o; killme(); } goaway($frontpage); } - - - $sitename = get_config('system','sitename'); - if($sitename) - $o .= '<h1 class="home-welcome">' . sprintf( t('Welcome to %s') ,$sitename) . '</h1>'; - - $loginbox = get_config('system','login_on_homepage'); - if(intval($loginbox) || $loginbox === false) + + $sitename = get_config('system', 'sitename'); + if ($sitename) + $o .= '<h1 class="home-welcome">' . sprintf(t('Welcome to %s'), $sitename) . '</h1>'; + + $loginbox = get_config('system', 'login_on_homepage'); + if (intval($loginbox) || $loginbox === false) $o .= login(true); - + return $o; - + } - + } diff --git a/Zotlabs/Module/Hq.php b/Zotlabs/Module/Hq.php index 4c2067299..3b8e88488 100644 --- a/Zotlabs/Module/Hq.php +++ b/Zotlabs/Module/Hq.php @@ -1,6 +1,10 @@ <?php namespace Zotlabs\Module; +use App; +use Zotlabs\Widget\Messages; + + require_once("include/bbcode.php"); require_once('include/security.php'); require_once('include/conversation.php'); @@ -14,67 +18,52 @@ class Hq extends \Zotlabs\Web\Controller { if(! local_channel()) return; - \App::$profile_uid = local_channel(); + App::$profile_uid = local_channel(); } - function post() { + function get($update = 0, $load = false) { - if(!local_channel()) + if(!local_channel()) { return; - - if($_REQUEST['notify_id']) { - q("update notify set seen = 1 where id = %d and uid = %d", - intval($_REQUEST['notify_id']), - intval(local_channel()) - ); } - killme(); - - } + if(argc() > 1 && argv(1) !== 'load') { + $item_hash = unpack_link_id(argv(1)); + } - function get($update = 0, $load = false) { + if(isset($_REQUEST['mid'])) { + $item_hash = unpack_link_id($_REQUEST['mid']); + } - if(!local_channel()) + if($item_hash === false) { + notice(t('Malformed message id.') . EOL); return; - - if(argc() > 1 && argv(1) !== 'load') { - $item_hash = argv(1); } - - if($_REQUEST['mid']) - $item_hash = $_REQUEST['mid']; $item_normal = item_normal(); $item_normal_update = item_normal_update(); if(! $item_hash) { - $r = q("SELECT mid FROM item + $r = q("SELECT mid FROM item WHERE uid = %d $item_normal - AND mid = parent_mid + AND mid = parent_mid + AND item_private IN (0, 1) ORDER BY created DESC LIMIT 1", intval(local_channel()) ); - if($r[0]['mid']) { - $item_hash = 'b64.' . base64url_encode($r[0]['mid']); + $item_hash = $r[0]['mid']; } } if($item_hash) { - if(strpos($item_hash,'b64.') === 0) - $decoded = @base64url_decode(substr($item_hash,4)); - - if($decoded) - $item_hash = $decoded; - $target_item = null; - $r = q("select id, uid, mid, parent_mid, thr_parent, verb, item_type, item_deleted, item_blocked from item where mid like '%s' limit 1", - dbesc($item_hash . '%') + $r = q("select id, uid, mid, parent_mid, thr_parent, verb, item_type, item_deleted, item_blocked from item where mid = '%s' limit 1", + dbesc($item_hash) ); - + if($r) { $target_item = $r[0]; } @@ -83,7 +72,7 @@ class Hq extends \Zotlabs\Web\Controller { if($target_item['item_blocked'] == ITEM_MODERATED) { goaway(z_root() . '/moderate/' . $target_item['id']); } - + $simple_update = ''; if($update && $_SESSION['loadtime']) $simple_update = " AND (( item_unseen = 1 AND item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) OR item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) "; @@ -94,16 +83,16 @@ class Hq extends \Zotlabs\Web\Controller { $sys_item = false; } - + if(! $update) { - $channel = \App::get_channel(); + $channel = App::get_channel(); $channel_acl = [ - 'allow_cid' => $channel['channel_allow_cid'], - 'allow_gid' => $channel['channel_allow_gid'], - 'deny_cid' => $channel['channel_deny_cid'], + 'allow_cid' => $channel['channel_allow_cid'], + 'allow_gid' => $channel['channel_allow_gid'], + 'deny_cid' => $channel['channel_deny_cid'], 'deny_gid' => $channel['channel_deny_gid'] - ]; + ]; $x = [ 'is_owner' => true, @@ -125,13 +114,7 @@ class Hq extends \Zotlabs\Web\Controller { 'reset' => t('Reset form') ]; - $o = replace_macros(get_markup_template("hq.tpl"), - [ - '$no_messages' => (($target_item) ? false : true), - '$no_messages_label' => [ t('Welcome to Hubzilla!'), t('You have got no unseen posts...') ], - '$editor' => status_editor($a,$x,false,'Hq') - ] - ); + $o = status_editor($a, $x, true); } @@ -142,10 +125,9 @@ class Hq extends \Zotlabs\Web\Controller { if($target_item) { // if the target item is not a post (eg a like) we want to address its thread parent //$mid = ((($target_item['verb'] == ACTIVITY_LIKE) || ($target_item['verb'] == ACTIVITY_DISLIKE)) ? $target_item['thr_parent'] : $target_item['mid']); - $mid = $target_item['mid']; - // if we got a decoded hash we must encode it again before handing to javascript - if($decoded) - $mid = 'b64.' . base64url_encode($mid); + + // if we got a decoded hash we must encode it again before handing to javascript + $mid = gen_link_id($target_item['mid']); } else { $mid = ''; @@ -153,9 +135,9 @@ class Hq extends \Zotlabs\Web\Controller { $o .= '<div id="live-hq"></div>' . "\r\n"; $o .= "<script> var profile_uid = " . local_channel() - . "; var netargs = '?f='; var profile_page = " . \App::$pager['page'] . ";</script>\r\n"; - - \App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"),[ + . "; var netargs = '?f='; var profile_page = " . App::$pager['page'] . ";</script>\r\n"; + + App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"),[ '$baseurl' => z_root(), '$pgtype' => 'hq', '$uid' => local_channel(), @@ -241,14 +223,14 @@ class Hq extends \Zotlabs\Web\Controller { else { $r = []; } - + if($r) { - $items = q("SELECT item.*, item.id AS item_id + $items = q("SELECT item.*, item.id AS item_id FROM item WHERE parent = '%s' $item_normal ", dbesc($r[0]['item_id']) ); - + xchan_query($items,true,(($sys_item) ? local_channel() : 0)); $items = fetch_post_tags($items,true); $items = conv_sort($items,'created'); @@ -267,4 +249,16 @@ class Hq extends \Zotlabs\Web\Controller { } + function post() { + if (!local_channel()) + return; + + $options['offset'] = $_REQUEST['offset']; + $options['type'] = $_REQUEST['type']; + + $ret = Messages::get_messages_page($options); + + json_return_and_die($ret); + } + } diff --git a/Zotlabs/Module/Import.php b/Zotlabs/Module/Import.php index 2c6e09fa7..eee72b945 100644 --- a/Zotlabs/Module/Import.php +++ b/Zotlabs/Module/Import.php @@ -2,13 +2,17 @@ namespace Zotlabs\Module; -require_once('include/zot.php'); require_once('include/channel.php'); require_once('include/import.php'); require_once('include/perm_upgrade.php'); require_once('library/urlify/URLify.php'); +use App; +use Zotlabs\Daemon\Master; use Zotlabs\Lib\Libzot; +use Zotlabs\Web\Controller; +use Zotlabs\Web\HTTPSig; +use Zotlabs\Lib\PConfig; /** @@ -17,7 +21,7 @@ use Zotlabs\Lib\Libzot; * Import a channel, either by direct file upload or via * connection to another server. */ -class Import extends \Zotlabs\Web\Controller { +class Import extends Controller { /** * @brief Import channel into account. @@ -26,95 +30,94 @@ class Import extends \Zotlabs\Web\Controller { */ function import_account($account_id) { - if(! $account_id){ + if (!$account_id) { logger('No account ID supplied'); return; } - $max_friends = account_service_class_fetch($account_id,'total_channels'); - $max_feeds = account_service_class_fetch($account_id,'total_feeds'); - $data = null; - $seize = ((x($_REQUEST,'make_primary')) ? intval($_REQUEST['make_primary']) : 0); - $import_posts = ((x($_REQUEST,'import_posts')) ? intval($_REQUEST['import_posts']) : 0); - $moving = intval($_REQUEST['moving']); - $src = $_FILES['filename']['tmp_name']; - $filename = basename($_FILES['filename']['name']); - $filesize = intval($_FILES['filename']['size']); - $filetype = $_FILES['filename']['type']; - $newname = trim(strtolower($_REQUEST['newname'])); + $max_friends = account_service_class_fetch($account_id, 'total_channels'); + $max_feeds = account_service_class_fetch($account_id, 'total_feeds'); + $data = null; + $seize = ((x($_REQUEST, 'make_primary')) ? intval($_REQUEST['make_primary']) : 0); + $import_posts = ((x($_REQUEST, 'import_posts')) ? intval($_REQUEST['import_posts']) : 0); + $moving = false; //intval($_REQUEST['moving']); + $src = $_FILES['filename']['tmp_name']; + $filename = basename($_FILES['filename']['name']); + $filesize = intval($_FILES['filename']['size']); + $filetype = $_FILES['filename']['type']; + $newname = trim(strtolower($_REQUEST['newname'])); // import channel from file - if($src) { + if ($src) { // This is OS specific and could also fail if your tmpdir isn't very // large mostly used for Diaspora which exports gzipped files. - if(strpos($filename,'.gz')){ - @rename($src,$src . '.gz'); + if (strpos($filename, '.gz')) { + @rename($src, $src . '.gz'); @system('gunzip ' . escapeshellarg($src . '.gz')); } - if($filesize) { + if ($filesize) { $data = @file_get_contents($src); } unlink($src); } // import channel from another server - if(! $src) { - $old_address = ((x($_REQUEST,'old_address')) ? $_REQUEST['old_address'] : ''); - if(! $old_address) { + if (!$src) { + $old_address = ((x($_REQUEST, 'old_address')) ? $_REQUEST['old_address'] : ''); + if (!$old_address) { logger('Nothing to import.'); - notice( t('Nothing to import.') . EOL); + notice(t('Nothing to import.') . EOL); return; - } else if(strpos($old_address, 'ï¼ ')) { - // if you copy the identity address from your profile page, make it work for convenience - WARNING: this is a utf-8 variant and NOT an ASCII ampersand. Please do not edit. + } else if (strpos($old_address, 'ï¼ ')) { + // if you copy the identity address from your profile page, make it work for convenience - WARNING: this is a utf-8 variant and NOT an ASCII ampersand. Please do not edit. $old_address = str_replace('ï¼ ', '@', $old_address); } - $email = ((x($_REQUEST,'email')) ? $_REQUEST['email'] : ''); - $password = ((x($_REQUEST,'password')) ? $_REQUEST['password'] : ''); + $email = ((x($_REQUEST, 'email')) ? $_REQUEST['email'] : ''); + $password = ((x($_REQUEST, 'password')) ? $_REQUEST['password'] : ''); - $channelname = substr($old_address,0,strpos($old_address,'@')); - $servername = substr($old_address,strpos($old_address,'@')+1); + $channelname = substr($old_address, 0, strpos($old_address, '@')); + $servername = substr($old_address, strpos($old_address, '@') + 1); $api_path = probe_api_path($servername); - if(! $api_path) { - notice( t('Unable to download data from old server') . EOL); + if (!$api_path) { + notice(t('Unable to download data from old server') . EOL); return; } $api_path .= 'channel/export/basic?f=&channel=' . $channelname; - if($import_posts) + if ($import_posts) $api_path .= '&posts=1'; $binary = false; $redirects = 0; $opts = array('http_auth' => $email . ':' . $password); $ret = z_fetch_url($api_path, $binary, $redirects, $opts); - if($ret['success']) { + if ($ret['success']) { $data = $ret['body']; - } - else { - notice( t('Unable to download data from old server') . EOL); + } else { + notice(t('Unable to download data from old server') . EOL); return; } } - if(! $data) { + if (!$data) { logger('Empty import file.'); - notice( t('Imported file is empty.') . EOL); + notice(t('Imported file is empty.') . EOL); return; } - $data = json_decode($data,true); + $data = json_decode($data, true); //logger('import: data: ' . print_r($data,true)); //print_r($data); - if(! array_key_exists('compatibility',$data)) { - call_hooks('import_foreign_channel_data',$data); - if($data['handled']) + if (!array_key_exists('compatibility', $data)) { + call_hooks('import_foreign_channel_data', $data); + if ($data['handled']) return; } @@ -132,141 +135,108 @@ class Import extends \Zotlabs\Web\Controller { // prevent incompatible osada or zap data from horking your database - if(array_path_exists('compatibility/codebase',$data)) { + if (array_path_exists('compatibility/codebase', $data)) { notice('Data export format is not compatible with this software'); return; } - if(version_compare($data['compatibility']['version'], '4.7.3', '<=')) { + if (version_compare($data['compatibility']['version'], '4.7.3', '<=')) { // zot6 transition: cloning is not compatible with older versions notice('Data export format is not compatible with this software (not a zot6 channel)'); return; } - if($moving) + if ($moving) $seize = 1; // import channel - $relocate = ((array_key_exists('relocate',$data)) ? $data['relocate'] : null); + $relocate = ((array_key_exists('relocate', $data)) ? $data['relocate'] : null); - if(array_key_exists('channel',$data)) { + if (array_key_exists('channel', $data)) { - $max_identities = account_service_class_fetch($account_id,'total_identities'); + $max_identities = account_service_class_fetch($account_id, 'total_identities'); - if($max_identities !== false) { - $r = q("select channel_id from channel where channel_account_id = %d", + if ($max_identities !== false) { + $r = q("select channel_id from channel where channel_account_id = %d and channel_removed = 0", intval($account_id) ); - if($r && count($r) > $max_identities) { - notice( sprintf( t('Your service plan only allows %d channels.'), $max_identities) . EOL); + if ($r && count($r) > $max_identities) { + notice(sprintf(t('Your service plan only allows %d channels.'), $max_identities) . EOL); return; } } - if($newname) { - $x = false; + if ($newname) { + $x = false; - if(get_config('system','unicode_usernames')) { - $x = punify(mb_strtolower($newname)); - } + if (get_config('system', 'unicode_usernames')) { + $x = punify(mb_strtolower($newname)); + } - if((! $x) || strlen($x) > 64) { - $x = strtolower(\URLify::transliterate($newname)); + if ((!$x) || strlen($x) > 64) { + $x = strtolower(\URLify::transliterate($newname)); } $newname = $x; } $channel = import_channel($data['channel'], $account_id, $seize, $newname); - } - else { - $moving = false; - $channel = \App::get_channel(); + } else { + $moving = false; + $channel = App::get_channel(); } - if(! $channel) { - logger('Channel not found. ', print_r($channel,true)); - notice( t('No channel. Import failed.') . EOL); + if (!$channel) { + logger('Channel not found. ', print_r($channel, true)); + notice(t('No channel. Import failed.') . EOL); return; } - if(is_array($data['config'])) { - import_config($channel,$data['config']); + if (is_array($data['config'])) { + import_config($channel, $data['config']); } logger('import step 2'); - if(array_key_exists('channel',$data)) { - if($data['photo']) { + if (array_key_exists('channel', $data)) { + if ($data['photo']) { require_once('include/photo/photo_driver.php'); - import_channel_photo(base64url_decode($data['photo']['data']),$data['photo']['type'],$account_id,$channel['channel_id']); + import_channel_photo(base64url_decode($data['photo']['data']), $data['photo']['type'], $account_id, $channel['channel_id']); } - if(is_array($data['profile'])) - import_profiles($channel,$data['profile']); + if (is_array($data['profile'])) + import_profiles($channel, $data['profile']); } logger('import step 3'); - if(is_array($data['hubloc'])) { - import_hublocs($channel,$data['hubloc'],$seize,$moving); - } - - logger('import step 4'); - // create new hubloc for the new channel at this site - if(array_key_exists('channel',$data)) { - if($channel['channel_portable_id']) { - $r = hubloc_store_lowlevel( - [ - 'hubloc_guid' => $channel['channel_guid'], - 'hubloc_guid_sig' => $channel['channel_guid_sig'], - 'hubloc_hash' => $channel['channel_portable_id'], - 'hubloc_addr' => channel_reddress($channel), - 'hubloc_network' => 'zot', - 'hubloc_primary' => (($seize) ? 1 : 0), - 'hubloc_url' => z_root(), - 'hubloc_url_sig' => base64url_encode(rsa_sign(z_root(),$channel['channel_prvkey'])), - 'hubloc_host' => \App::get_hostname(), - 'hubloc_callback' => z_root() . '/post', - 'hubloc_sitekey' => get_config('system','pubkey'), - 'hubloc_updated' => datetime_convert() - ] - ); + if (array_key_exists('channel', $data)) { - // reset the original primary hubloc if it is being seized - if($seize) { - $r = q("update hubloc set hubloc_primary = 0 where hubloc_primary = 1 and hubloc_hash = '%s' and hubloc_url != '%s' ", - dbesc($channel['channel_portable_id']), - dbesc(z_root()) - ); - } - } - - // create a new zot6 hubloc if we have got a channel_portable_id + // create a new zot6 hubloc $r = hubloc_store_lowlevel( [ - 'hubloc_guid' => $channel['channel_guid'], + 'hubloc_guid' => $channel['channel_guid'], 'hubloc_guid_sig' => $channel['channel_guid_sig'], - 'hubloc_hash' => $channel['channel_hash'], - 'hubloc_addr' => channel_reddress($channel), - 'hubloc_network' => 'zot6', - 'hubloc_primary' => (($seize) ? 1 : 0), - 'hubloc_url' => z_root(), - 'hubloc_url_sig' => 'sha256.' . base64url_encode(rsa_sign(z_root(),$channel['channel_prvkey'])), - 'hubloc_host' => \App::get_hostname(), + 'hubloc_hash' => $channel['channel_hash'], + 'hubloc_addr' => channel_reddress($channel), + 'hubloc_network' => 'zot6', + 'hubloc_primary' => (($seize) ? 1 : 0), + 'hubloc_url' => z_root(), + 'hubloc_url_sig' => Libzot::sign(z_root(),$channel['channel_prvkey']), + 'hubloc_host' => App::get_hostname(), 'hubloc_callback' => z_root() . '/zot', - 'hubloc_sitekey' => get_config('system','pubkey'), - 'hubloc_updated' => datetime_convert(), - 'hubloc_id_url' => channel_url($channel), - 'hubloc_site_id' => Libzot::make_xchan_hash(z_root(),get_config('system','pubkey')) + 'hubloc_sitekey' => get_config('system', 'pubkey'), + 'hubloc_updated' => datetime_convert(), + 'hubloc_id_url' => channel_url($channel), + 'hubloc_site_id' => Libzot::make_xchan_hash(z_root(), get_config('system', 'pubkey')) ] ); // reset the original primary hubloc if it is being seized - if($seize) { + if ($seize) { $r = q("update hubloc set hubloc_primary = 0 where hubloc_primary = 1 and hubloc_hash = '%s' and hubloc_url != '%s' ", dbesc($channel['channel_hash']), dbesc(z_root()) @@ -275,123 +245,90 @@ class Import extends \Zotlabs\Web\Controller { } - logger('import step 5'); - + logger('import step 4'); // import xchans and contact photos - if(array_key_exists('channel',$data) && $seize) { + if (array_key_exists('channel', $data) && $seize) { // replace any existing xchan we may have on this site if we're seizing control - $r = q("delete from xchan where ( xchan_hash = '%s' or xchan_hash = '%s' ) ", - dbesc($channel['channel_hash']), - dbesc($channel['channel_portable_id']) + $r = q("delete from xchan where xchan_hash = '%s'", + dbesc($channel['channel_hash']) ); - if($channel['channel_portable_id']) { - $r = xchan_store_lowlevel( - [ - 'xchan_hash' => $channel['channel_portable_id'], - 'xchan_guid' => $channel['channel_guid'], - 'xchan_guid_sig' => $channel['channel_guid_sig'], - 'xchan_pubkey' => $channel['channel_pubkey'], - 'xchan_photo_l' => z_root() . "/photo/profile/l/" . $channel['channel_id'], - 'xchan_photo_m' => z_root() . "/photo/profile/m/" . $channel['channel_id'], - 'xchan_photo_s' => z_root() . "/photo/profile/s/" . $channel['channel_id'], - 'xchan_addr' => channel_reddress($channel), - 'xchan_url' => z_root() . '/channel/' . $channel['channel_address'], - 'xchan_connurl' => z_root() . '/poco/' . $channel['channel_address'], - 'xchan_follow' => z_root() . '/follow?f=&url=%s', - 'xchan_name' => $channel['channel_name'], - 'xchan_network' => 'zot', - 'xchan_photo_date' => datetime_convert(), - 'xchan_name_date' => datetime_convert() - ] - ); - } - $r = xchan_store_lowlevel( [ - 'xchan_hash' => $channel['channel_hash'], - 'xchan_guid' => $channel['channel_guid'], - 'xchan_guid_sig' => $channel['channel_guid_sig'], - 'xchan_pubkey' => $channel['channel_pubkey'], - 'xchan_photo_l' => z_root() . "/photo/profile/l/" . $channel['channel_id'], - 'xchan_photo_m' => z_root() . "/photo/profile/m/" . $channel['channel_id'], - 'xchan_photo_s' => z_root() . "/photo/profile/s/" . $channel['channel_id'], - 'xchan_addr' => channel_reddress($channel), - 'xchan_url' => z_root() . '/channel/' . $channel['channel_address'], - 'xchan_connurl' => z_root() . '/poco/' . $channel['channel_address'], - 'xchan_follow' => z_root() . '/follow?f=&url=%s', - 'xchan_name' => $channel['channel_name'], - 'xchan_network' => 'zot6', - 'xchan_photo_date' => datetime_convert(), - 'xchan_name_date' => datetime_convert() + 'xchan_hash' => $channel['channel_hash'], + 'xchan_guid' => $channel['channel_guid'], + 'xchan_guid_sig' => $channel['channel_guid_sig'], + 'xchan_pubkey' => $channel['channel_pubkey'], + 'xchan_photo_l' => z_root() . "/photo/profile/l/" . $channel['channel_id'], + 'xchan_photo_m' => z_root() . "/photo/profile/m/" . $channel['channel_id'], + 'xchan_photo_s' => z_root() . "/photo/profile/s/" . $channel['channel_id'], + 'xchan_addr' => channel_reddress($channel), + 'xchan_url' => z_root() . '/channel/' . $channel['channel_address'], + 'xchan_connurl' => z_root() . '/poco/' . $channel['channel_address'], + 'xchan_follow' => z_root() . '/follow?f=&url=%s', + 'xchan_name' => $channel['channel_name'], + 'xchan_network' => 'zot6', + 'xchan_photo_date' => datetime_convert(), + 'xchan_name_date' => datetime_convert() ] ); } - logger('import step 6'); + logger('import step 5'); // import xchans $xchans = $data['xchan']; - if($xchans) { - foreach($xchans as $xchan) { + if ($xchans) { + foreach ($xchans as $xchan) { - if($xchan['xchan_network'] === 'zot') { - $hash = make_xchan_hash($xchan['xchan_guid'],$xchan['xchan_guid_sig']); - if($hash !== $xchan['xchan_hash']) { - logger('forged xchan: ' . print_r($xchan,true)); + if ($xchan['xchan_network'] === 'zot6') { + $zhash = Libzot::make_xchan_hash($xchan['xchan_guid'], $xchan['xchan_pubkey']); + if ($zhash !== $xchan['xchan_hash']) { + logger('forged xchan: ' . print_r($xchan, true)); continue; } } - if($xchan['xchan_network'] === 'zot6') { - $zhash = Libzot::make_xchan_hash($xchan['xchan_guid'],$xchan['xchan_pubkey']); - if($zhash !== $xchan['xchan_hash']) { - logger('forged xchan: ' . print_r($xchan,true)); - continue; - } - } - - if(! array_key_exists('xchan_hidden',$xchan)) { - $xchan['xchan_hidden'] = (($xchan['xchan_flags'] & 0x0001) ? 1 : 0); - $xchan['xchan_orphan'] = (($xchan['xchan_flags'] & 0x0002) ? 1 : 0); - $xchan['xchan_censored'] = (($xchan['xchan_flags'] & 0x0004) ? 1 : 0); + if (!array_key_exists('xchan_hidden', $xchan)) { + $xchan['xchan_hidden'] = (($xchan['xchan_flags'] & 0x0001) ? 1 : 0); + $xchan['xchan_orphan'] = (($xchan['xchan_flags'] & 0x0002) ? 1 : 0); + $xchan['xchan_censored'] = (($xchan['xchan_flags'] & 0x0004) ? 1 : 0); $xchan['xchan_selfcensored'] = (($xchan['xchan_flags'] & 0x0008) ? 1 : 0); - $xchan['xchan_system'] = (($xchan['xchan_flags'] & 0x0010) ? 1 : 0); - $xchan['xchan_pubforum'] = (($xchan['xchan_flags'] & 0x0020) ? 1 : 0); - $xchan['xchan_deleted'] = (($xchan['xchan_flags'] & 0x1000) ? 1 : 0); + $xchan['xchan_system'] = (($xchan['xchan_flags'] & 0x0010) ? 1 : 0); + $xchan['xchan_pubforum'] = (($xchan['xchan_flags'] & 0x0020) ? 1 : 0); + $xchan['xchan_deleted'] = (($xchan['xchan_flags'] & 0x1000) ? 1 : 0); } $r = q("select xchan_hash from xchan where xchan_hash = '%s' limit 1", dbesc($xchan['xchan_hash']) ); - if($r) + if ($r) continue; - create_table_from_array('xchan',$xchan); + create_table_from_array('xchan', $xchan); require_once('include/photo/photo_driver.php'); - if($xchan['xchan_hash'] === $channel['channel_hash']) { + if ($xchan['xchan_hash'] === $channel['channel_hash']) { $r = q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s' where xchan_hash = '%s'", dbesc(z_root() . '/photo/profile/l/' . $channel['channel_id']), dbesc(z_root() . '/photo/profile/m/' . $channel['channel_id']), dbesc(z_root() . '/photo/profile/s/' . $channel['channel_id']), dbesc($xchan['xchan_hash']) ); - } - else { - $photos = import_xchan_photo($xchan['xchan_photo_l'],$xchan['xchan_hash']); - if($photos[4]) + } else { + $photos = import_xchan_photo($xchan['xchan_photo_l'], $xchan['xchan_hash']); + if ($photos[4]) $photodate = NULL_DATE; else $photodate = $xchan['xchan_photo_date']; - $r = q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s', xchan_photo_date = '%s' where xchan_hash = '%s'", + q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s', xchan_photo_date = '%s' where xchan_hash = '%s'", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), @@ -402,7 +339,14 @@ class Import extends \Zotlabs\Web\Controller { } } - logger('import step 7'); + logger('import step 6'); + } + + logger('import step 7'); + + // this must happen after xchans got imported! + if (is_array($data['hubloc'])) { + import_hublocs($channel, $data['hubloc'], $seize, $moving); } $friends = 0; @@ -410,13 +354,13 @@ class Import extends \Zotlabs\Web\Controller { // import contacts $abooks = $data['abook']; - if($abooks) { - foreach($abooks as $abook) { + if ($abooks) { + foreach ($abooks as $abook) { $abook_copy = $abook; $abconfig = null; - if(array_key_exists('abconfig',$abook) && is_array($abook['abconfig']) && count($abook['abconfig'])) + if (array_key_exists('abconfig', $abook) && is_array($abook['abconfig']) && count($abook['abconfig'])) $abconfig = $abook['abconfig']; unset($abook['abook_id']); @@ -429,33 +373,32 @@ class Import extends \Zotlabs\Web\Controller { $abook['abook_account'] = $account_id; $abook['abook_channel'] = $channel['channel_id']; - if(! array_key_exists('abook_blocked',$abook)) { - $abook['abook_blocked'] = (($abook['abook_flags'] & 0x0001 ) ? 1 : 0); - $abook['abook_ignored'] = (($abook['abook_flags'] & 0x0002 ) ? 1 : 0); - $abook['abook_hidden'] = (($abook['abook_flags'] & 0x0004 ) ? 1 : 0); - $abook['abook_archived'] = (($abook['abook_flags'] & 0x0008 ) ? 1 : 0); - $abook['abook_pending'] = (($abook['abook_flags'] & 0x0010 ) ? 1 : 0); - $abook['abook_unconnected'] = (($abook['abook_flags'] & 0x0020 ) ? 1 : 0); - $abook['abook_self'] = (($abook['abook_flags'] & 0x0080 ) ? 1 : 0); - $abook['abook_feed'] = (($abook['abook_flags'] & 0x0100 ) ? 1 : 0); + if (!array_key_exists('abook_blocked', $abook)) { + $abook['abook_blocked'] = (($abook['abook_flags'] & 0x0001) ? 1 : 0); + $abook['abook_ignored'] = (($abook['abook_flags'] & 0x0002) ? 1 : 0); + $abook['abook_hidden'] = (($abook['abook_flags'] & 0x0004) ? 1 : 0); + $abook['abook_archived'] = (($abook['abook_flags'] & 0x0008) ? 1 : 0); + $abook['abook_pending'] = (($abook['abook_flags'] & 0x0010) ? 1 : 0); + $abook['abook_unconnected'] = (($abook['abook_flags'] & 0x0020) ? 1 : 0); + $abook['abook_self'] = (($abook['abook_flags'] & 0x0080) ? 1 : 0); + $abook['abook_feed'] = (($abook['abook_flags'] & 0x0100) ? 1 : 0); } - if(array_key_exists('abook_instance',$abook) && $abook['abook_instance'] && strpos($abook['abook_instance'],z_root()) === false) { + if (array_key_exists('abook_instance', $abook) && $abook['abook_instance'] && strpos($abook['abook_instance'], z_root()) === false) { $abook['abook_not_here'] = 1; - } + } - if($abook['abook_self']) { - $role = get_pconfig($channel['channel_id'],'system','permissions_role'); - if(($role === 'forum') || ($abook['abook_my_perms'] & PERMS_W_TAGWALL)) { + if ($abook['abook_self']) { + $role = get_pconfig($channel['channel_id'], 'system', 'permissions_role'); + if (($role === 'forum') || ($abook['abook_my_perms'] & PERMS_W_TAGWALL)) { q("update xchan set xchan_pubforum = 1 where xchan_hash = '%s' ", dbesc($abook['abook_xchan']) ); } - } - else { - if($max_friends !== false && $friends > $max_friends) + } else { + if ($max_friends !== false && $friends > $max_friends) continue; - if($max_feeds !== false && intval($abook['abook_feed']) && ($feeds > $max_feeds)) + if ($max_feeds !== false && intval($abook['abook_feed']) && ($feeds > $max_feeds)) continue; } @@ -463,30 +406,29 @@ class Import extends \Zotlabs\Web\Controller { dbesc($abook['abook_xchan']), intval($channel['channel_id']) ); - if($r) { - foreach($abook as $k => $v) { - $r = q("UPDATE abook SET " . TQUOT . "%s" . TQUOT . " = '%s' WHERE abook_xchan = '%s' AND abook_channel = %d", + if ($r) { + foreach ($abook as $k => $v) { + q("UPDATE abook SET " . TQUOT . "%s" . TQUOT . " = '%s' WHERE abook_xchan = '%s' AND abook_channel = %d", dbesc($k), dbesc($v), dbesc($abook['abook_xchan']), intval($channel['channel_id']) ); } - } - else { + } else { abook_store_lowlevel($abook); - $friends ++; - if(intval($abook['abook_feed'])) - $feeds ++; + $friends++; + if (intval($abook['abook_feed'])) + $feeds++; } - translate_abook_perms_inbound($channel,$abook_copy); + translate_abook_perms_inbound($channel, $abook_copy); - if($abconfig) { + if ($abconfig) { /// @FIXME does not handle sync of del_abconfig - foreach($abconfig as $abc) { - set_abconfig($channel['channel_id'],$abc['xchan'],$abc['cat'],$abc['k'],$abc['v']); + foreach ($abconfig as $abc) { + set_abconfig($channel['channel_id'], $abc['xchan'], $abc['cat'], $abc['k'], $abc['v']); } } } @@ -494,13 +436,14 @@ class Import extends \Zotlabs\Web\Controller { logger('import step 8'); } + // import groups $groups = $data['group']; - if($groups) { + if ($groups) { $saved = array(); - foreach($groups as $group) { + foreach ($groups as $group) { $saved[$group['hash']] = array('old' => $group['id']); - if(array_key_exists('name', $group)) { + if (array_key_exists('name', $group)) { $group['gname'] = $group['name']; unset($group['name']); } @@ -512,8 +455,8 @@ class Import extends \Zotlabs\Web\Controller { $r = q("select * from pgrp where uid = %d", intval($channel['channel_id']) ); - if($r) { - foreach($r as $rr) { + if ($r) { + foreach ($r as $rr) { $saved[$rr['hash']]['new'] = $rr['id']; } } @@ -521,12 +464,12 @@ class Import extends \Zotlabs\Web\Controller { // import group members $group_members = $data['group_member']; - if($group_members) { - foreach($group_members as $group_member) { + if ($group_members) { + foreach ($group_members as $group_member) { unset($group_member['id']); $group_member['uid'] = $channel['channel_id']; - foreach($saved as $x) { - if($x['old'] == $group_member['gid']) + foreach ($saved as $x) { + if ($x['old'] == $group_member['gid']) $group_member['gid'] = $x['new']; } create_table_from_array('pgrp_member', $group_member); @@ -535,70 +478,78 @@ class Import extends \Zotlabs\Web\Controller { logger('import step 9'); - if(is_array($data['obj'])) - import_objs($channel,$data['obj']); - if(is_array($data['likes'])) - import_likes($channel,$data['likes']); + if (is_array($data['obj'])) + import_objs($channel, $data['obj']); - if(is_array($data['app'])) - import_apps($channel,$data['app']); + if (is_array($data['likes'])) + import_likes($channel, $data['likes']); - if(is_array($data['sysapp'])) - import_sysapps($channel,$data['sysapp']); + if (is_array($data['app'])) + import_apps($channel, $data['app']); - if(is_array($data['chatroom'])) - import_chatrooms($channel,$data['chatroom']); + if (is_array($data['sysapp'])) + import_sysapps($channel, $data['sysapp']); - if(is_array($data['conv'])) - import_conv($channel,$data['conv']); + if (is_array($data['chatroom'])) + import_chatrooms($channel, $data['chatroom']); - if(is_array($data['mail'])) - import_mail($channel,$data['mail']); + if (is_array($data['event'])) + import_events($channel, $data['event']); - if(is_array($data['event'])) - import_events($channel,$data['event']); + if (is_array($data['event_item'])) + import_items($channel, $data['event_item'], false, $relocate); - if(is_array($data['event_item'])) - import_items($channel,$data['event_item'],false,$relocate); + if (is_array($data['menu'])) + import_menus($channel, $data['menu']); - if(is_array($data['menu'])) - import_menus($channel,$data['menu']); + if (is_array($data['wiki'])) + import_items($channel, $data['wiki'], false, $relocate); - if(is_array($data['wiki'])) - import_items($channel,$data['wiki'],false,$relocate); + if (is_array($data['webpages'])) + import_items($channel, $data['webpages'], false, $relocate); - if(is_array($data['webpages'])) - import_items($channel,$data['webpages'],false,$relocate); + $addon = array('channel' => $channel, 'data' => $data); + call_hooks('import_channel', $addon); - $addon = array('channel' => $channel,'data' => $data); - call_hooks('import_channel',$addon); + if ($import_posts && array_key_exists('item', $data) && $data['item']) { + import_items($channel, $data['item'], false, $relocate); + } - $saved_notification_flags = notifications_off($channel['channel_id']); + // Immediately notify old server about the new clone + Master::Summon( [ 'Notifier', 'refresh_all', $channel['channel_id'] ] ); - if($import_posts && array_key_exists('item',$data) && $data['item']) - import_items($channel,$data['item'],false,$relocate); + // This will indirectly perform a refresh_all *and* update the directory + Master::Summon(array('Directory', $channel['channel_id'])); - notifications_on($channel['channel_id'],$saved_notification_flags); + if ($api_path && $import_posts) { // we are importing from a server and not a file - if(array_key_exists('item_id',$data) && $data['item_id']) - import_item_ids($channel,$data['item_id']); + $m = parse_url($api_path); - // send out refresh requests - // notify old server that it may no longer be primary. + $hz_server = $m['scheme'] . '://' . $m['host']; - \Zotlabs\Daemon\Master::Summon(array('Notifier','location',$channel['channel_id'])); + $since = datetime_convert(date_default_timezone_get(),date_default_timezone_get(),'0001-01-01 00:00'); + $until = datetime_convert(date_default_timezone_get(),date_default_timezone_get(),'now + 1 day'); - // This will indirectly perform a refresh_all *and* update the directory + $poll_interval = get_config('system','poll_interval',3); + $page = 0; - \Zotlabs\Daemon\Master::Summon(array('Directory', $channel['channel_id'])); + Master::Summon([ 'Content_importer', sprintf('%d',$page), $since, $until, $channel['channel_address'], urlencode($hz_server) ]); + Master::Summon([ 'File_importer',sprintf('%d',$page), $channel['channel_address'], urlencode($hz_server) ]); + } - notice( t('Import completed.') . EOL); + // i do not think this is still used + //if (array_key_exists('item_id', $data) && $data['item_id']) + // import_item_ids($channel, $data['item_id']); change_channel($channel['channel_id']); - goaway(z_root() . '/network' ); + if ($api_path && $import_posts) + goaway(z_root() . '/import_progress'); + + goaway(z_root()); + } /** @@ -606,7 +557,7 @@ class Import extends \Zotlabs\Web\Controller { */ function post() { $account_id = get_account_id(); - if(! $account_id) + if (!$account_id) return; check_form_security_token_redirectOnErr('/import', 'channel_import'); @@ -621,27 +572,29 @@ class Import extends \Zotlabs\Web\Controller { */ function get() { - if(! get_account_id()) { - notice( t('You must be logged in to use this feature.') . EOL); + if (!get_account_id()) { + notice(t('You must be logged in to use this feature.') . EOL); return ''; } - $o = replace_macros(get_markup_template('channel_import.tpl'),array( - '$title' => t('Import Channel'), + nav_set_selected('Channel Import'); + + $o = replace_macros(get_markup_template('channel_import.tpl'), array( + '$title' => t('Channel Import'), '$desc' => t('Use this form to import an existing channel from a different server/hub. You may retrieve the channel identity from the old server/hub via the network or provide an export file.'), '$label_filename' => t('File to Upload'), '$choice' => t('Or provide the old server/hub details'), - '$old_address' => [ 'old_address', t('Your old identity address (xyz@example.com)'), '', ''], - '$email' => [ 'email', t('Your old login email address'), '', '' ], - '$password' => [ 'password', t('Your old login password'), '', '' ], - '$import_posts' => [ 'import_posts', t('Import a few months of posts if possible (limited by available memory'), false, '', [ t('No'), t('Yes') ]], + '$old_address' => ['old_address', t('Your old identity address (xyz@example.com)'), '', ''], + '$email' => ['email', t('Your old login email address'), '', ''], + '$password' => ['password', t('Your old login password'), '', ''], + '$import_posts' => ['import_posts', t('Import your items and files (limited by available memory)'), false, '', [t('No'), t('Yes')]], '$common' => t('For either option, please choose whether to make this hub your new primary address, or whether your old location should continue this role. You will be able to post from either location, but only one can be marked as the primary location for files, photos, and media.'), - '$make_primary' => [ 'make_primary', t('Make this hub my primary location'), false, '', [ t('No'), t('Yes') ] ], - '$moving' => [ 'moving', t('Move this channel (disable all previous locations)'), false, '', [ t('No'), t('Yes') ] ], - '$newname' => [ 'newname', t('Use this channel nickname instead of the one provided'), '', t('Leave blank to keep your existing channel nickname. You will be randomly assigned a similar nickname if either name is already allocated on this site.')], + '$make_primary' => ['make_primary', t('Make this hub my primary location'), false, '', [t('No'), t('Yes')]], + '$moving' => ['moving', t('Move this channel (disable all previous locations)'), false, '', [t('No'), t('Yes')]], + '$newname' => ['newname', t('Use this channel nickname instead of the one provided'), '', t('Leave blank to keep your existing channel nickname. You will be randomly assigned a similar nickname if either name is already allocated on this site.')], '$pleasewait' => t('This process may take several minutes to complete. Please submit the form only once and leave this page open until finished.'), diff --git a/Zotlabs/Module/Import_progress.php b/Zotlabs/Module/Import_progress.php new file mode 100644 index 000000000..761d2f215 --- /dev/null +++ b/Zotlabs/Module/Import_progress.php @@ -0,0 +1,122 @@ +<?php +namespace Zotlabs\Module; + +use Zotlabs\Lib\PConfig; +use Zotlabs\Daemon\Master; + +class Import_progress extends \Zotlabs\Web\Controller { + + function post() { + + if(! local_channel()) + return; + + } + + function get() { + + if(! local_channel()) { + return; + } + + nav_set_selected('Channel Import'); + + // items + $c = PConfig::Get(local_channel(), 'import', 'content_progress'); + + if ($c) { + $total_cpages = floor(intval($c['items_total']) / intval($c['items_page'])); + if(!$total_cpages) { + $total_cpages = 1; // because of floor + } + + $cpage = $c['last_page'] + 1; // because page count start at 0 + + $cprogress = intval(floor((intval($cpage) * 100) / $total_cpages)); + $ccompleted_str = t('Item sync completed!'); + + if(argv(1) === 'resume_itemsync' && $cprogress < 100) { + Master::Summon($c['next_cmd']); + goaway('/import_progress'); + } + } + else { + $cprogress = 'waiting to start...'; + + if (PConfig::Get(local_channel(), 'import', 'content_completed')) { + // There was nothing todo. Fake 100% and mention that there were no files found + $cprogress = 100; + } + + $ccompleted_str = t('Item sync completed but no items were found!'); + + if(argv(1) === 'resume_itemsync') { + Master::Summon(["Content_importer","0","0001-01-01 00:00:00","2021-10-02 19:49:14","ct5","https%3A%2F%2Fhub.somaton.com"]); + goaway('/import_progress'); + } + } + + $cprogress_str = ((intval($cprogress)) ? $cprogress . '%' : $cprogress); + + // files + $f = PConfig::Get(local_channel(), 'import', 'files_progress'); + + if ($f) { + $total_fpages = floor(intval($f['files_total']) / intval($f['files_page'])); + if(!$total_fpages) { + $total_fpages = 1; + } + + $fpage = $f['last_page'] + 1; + + $fprogress = intval(floor((intval($fpage) * 100) / $total_fpages)); + $fcompleted_str = t('File sync completed!'); + + if(argv(1) === 'resume_filesync' && $fprogress < 100) { + Master::Summon($f['next_cmd']); + goaway('/import_progress'); + } + + + } + else { + $fprogress = 'waiting to start...'; + + if (PConfig::Get(local_channel(), 'import', 'files_completed')) { + // There was nothing todo. Fake 100% and mention that there were no files found + $fprogress = 100; + } + + $fcompleted_str = t('File sync completed but no files were found!'); + } + + $fprogress_str = ((intval($fprogress)) ? $fprogress . '%' : $fprogress); + + if(is_ajax()) { + $ret = [ + 'cprogress' => $cprogress, + 'fprogress' => $fprogress + ]; + + json_return_and_die($ret); + } + + $o = replace_macros(get_markup_template("import_progress.tpl"), [ + '$chtitle_str' => t('Channel clone status'), + '$ctitle_str' => t('Item sync status'), + '$ftitle_str' => t('File sync status'), + '$cprogress_str' => $cprogress_str, + '$cprogress' => intval($cprogress), + '$fprogress_str' => $fprogress_str, + '$fprogress' => intval($fprogress), + '$fcompleted_str' => $fcompleted_str, + '$ccompleted_str' => $ccompleted_str, + '$chcompleted_str' => t('Channel cloning completed!'), + '$resume_str' => t('Resume'), + '$resume_helper_str' => t('Only resume if sync stalled!') + ]); + + return $o; + } + +} diff --git a/Zotlabs/Module/Invite.php b/Zotlabs/Module/Invite.php index 6359da54c..40f972385 100644 --- a/Zotlabs/Module/Invite.php +++ b/Zotlabs/Module/Invite.php @@ -6,7 +6,7 @@ use Zotlabs\Lib\Apps; use Zotlabs\Web\Controller; /** - * module: invite.php + * module: invitexv2.php * * send email invitations to join social network * @@ -15,160 +15,558 @@ use Zotlabs\Web\Controller; class Invite extends Controller { + /** + * While coding this, I want to introduce a system of qualified messages and notifications. + * Each message consists of a 3 letter prefix, a 4 digit number and a one letter suffix (PREnnnnS). + * The spirit about is not from me, but many decades used by IBM inc. in devel with best success. + * + * The system prefix, used uppercase as system message id, lowercase as css and js prefix (classes, ids etc). + * Usually not used as self::MYP, but placed in the code dominant enough for easy to find. + * + * Concrete here: + * The prefix indicates Z for the Zlabs(core), A for Account stuff, I for Invite. + * The numbers scope will be 00xx within/for templates, 01xx for get, 02xx for post functions. + * Message qualification ends with a uppercase suffix, where + * I=Info(only), + * W=Warning(more then info and less then error), + * E=Error, + * F=Fatal(for unexpected errors). + * Btw, in case of using fail2ban, a scan of messages going to log is very much more with ease, + * esspecially in multi language driven systems where messages vary. + * + * @author Hilmar Runge + * @version 2.0.0 + * @since 2020-01-20 + * + */ + + const MYP = 'ZAI'; + const VERSION = '2.0.0'; + function post() { - - if(! local_channel()) { - notice( t('Permission denied.') . EOL); + + // zai02 + + if (! local_channel()) { + notice( 'ZAI0201E,' .t('Permission denied.') . EOL); return; } - if(! Apps::system_app_installed(local_channel(), 'Invite')) { + if (! Apps::system_app_installed(local_channel(), 'Invite')) { + notice( 'ZAI0202E,' . t('Invite App') . ' (' . t('Not Installed') . ')' . EOL); return; } - + check_form_security_token_redirectOnErr('/', 'send_invite'); - - $max_invites = intval(get_config('system','max_invites')); - if(! $max_invites) - $max_invites = 50; - - $current_invites = intval(get_pconfig(local_channel(),'system','sent_invites')); - if($current_invites > $max_invites) { - notice( t('Total invitation limit exceeded.') . EOL); + + $ok = $ko = 0; + $feedbk = ''; + $isajax = is_ajax(); + $eol = $isajax ? "\n" : EOL; + $policy = intval(get_config('system','register_policy')); + if ($policy == REGISTER_CLOSED) { + notice( 'ZAI0212E,' . t('Register is closed') . ')' . EOL); return; - }; - - - $recips = ((x($_POST,'recipients')) ? explode("\n",$_POST['recipients']) : array()); - $message = ((x($_POST,'message')) ? notags(trim($_POST['message'])) : ''); - - $total = 0; - - if(get_config('system','invitation_only')) { - $invonly = true; - $x = get_pconfig(local_channel(),'system','invites_remaining'); - if((! $x) && (! is_site_admin())) - return; - } - - foreach($recips as $recip) { - - $recip = trim($recip); - if(! $recip) - continue; - - if(! validate_email($recip)) { - notice( sprintf( t('%s : Not a valid email address.'), $recip) . EOL); - continue; + } + if ($policy == REGISTER_OPEN) + $flags = 0; + elseif ($policy == REGISTER_APPROVE) + $flags = ACCOUNT_PENDING; + $flags = ($flags | intval(get_config('system','verify_email'))); + + // how many max recipients in one mail submit + $maxto = get_config('system','invitation_max_recipients', 'na'); + If (is_site_admin()) { + // set, if admin is operator, default to 12 + if ($maxto === 'na') set_config('system','invitation_max_recipients', 12); + } + $maxto = ($maxto === 'na') ? 12 : $maxto; + + // language code current for the invitation + $lcc = x($_POST['zailcc']) && preg_match('/[a-z\-]{2,5}/', $_POST['zailcc']) + ? $_POST['zailcc'] + : ''; + + // expiration duration amount quantity, in case of doubts defaults 2 + $durn = x($_POST['zaiexpiren']) && preg_match('/[0-9]{1,2}/', $_POST['zaiexpiren']) + ? trim(intval($_POST['zaiexpiren'])) + : '2'; + !$durn ? $durn = 2 : ''; + + // expiration duration unit 1st letter (day, weeks, months, years), defaults days + $durq = x($_POST['zaiexpire']) && preg_match('/[ihd]{1,1}/', $_POST['zaiexpire']) + ? $_POST['zaiexpire'] + : 'd'; + + $dur = self::calcdue($durn.$durq); + $due = t('Note, the invitation code is valid up to') . ' ' . $dur['due']; + + if ($isajax) { + $feedbk .= 'ZAI0207I ' . $due . $eol; + } + + // take the received email addresses and discart duplicates + $recips = array_filter( array_unique( preg_replace('/^\s*$/', '', + ((x($_POST,'zaito')) ? explode( "\n",$_POST['zaito']) : array() ) ))); + + $havto = count($recips); + + if ( $havto > $maxto) { + $feedbk .= 'ZAI0210E ' . sprintf( t('Too many recipients for one invitation (max %d)'), $maxto) . $eol; + $ko++; + + } elseif ( $havto == 0 ) { + $feedbk .= 'ZAI0211E ' . t('No recipients for this invitation') . $eol; + $ko++; + + } else { + + // each email address + foreach($recips as $n => $recip) { + + // if empty ignore + $recip = $recips[$n] = trim($recip); + if(! $recip) continue; + + // see if we have an email address who@domain.tld + if (!preg_match('/^.{2,64}\@[a-z0-9.-]{4,32}\.[a-z]{2,12}$/', $recip)) { + $feedbk .= 'ZAI0203E ' . ($n+1) . ': ' . sprintf( t('(%s) : Not a valid email address'), $recip) . $eol; + $ko++; + continue; + } + if(! validate_email($recip)) { + $feedbk .= 'ZAI0204E ' . ($n+1) . ': ' . sprintf( t('(%s) : Not a real email address'), $recip) . $eol; + $ko++; + continue; + } + + // do we accept the email (not black listed) + if(! allowed_email($recip)) { + $feedbk .= 'ZAI0205E ' . ($n+1) . ': ' . sprintf( t('(%s) : Not allowed email address'), $recip) . $eol; + $ko++; + continue; + } + + // is the email address just in use for account or registered before + $r = q("SELECT account_email AS em FROM account WHERE account_email = '%s'" + . " UNION " + ."SELECT reg_email AS em FROM register WHERE reg_vital = 1 AND reg_email = '%s' LIMIT 1;", + dbesc($recip), + dbesc($recip) + ); + if($r && $r[0]['em'] == $recip) { + $feedbk .= 'ZAI0206E ' . ($n+1) . ': ' . sprintf( t('(%s) : email address already in use'), $recip) . $eol; + $ko++; + continue; + } + + if ($isajax) { + // seems we have an email address acceptable + $feedbk .= 'ZAI0209I ' . ($n+1) . ': ' . sprintf( t('(%s) : Accepted email address'), $recip) . $eol; + } } - - else - $nmessage = $message; - - $account = App::get_account(); - - $res = z_mail( - [ + } + + if ($isajax) { + // we are not silent on the ajax road + echo json_encode(array('feedbk' => $feedbk, 'due' => $due)); + + // that mission is complete + killme(); + exit; + } + + // Total ?todo notice( t('Invitation limit exceeded. Please contact your site administrator.') . EOL); + + // any errors up to now in fg? + + + // down from here, only on the main road (no more ajax) + + // tell if sth is to tell + $feedbk ? notice($feedbk) . $eol : ''; + + if ($ko > 0) return; + + // the personal mailtext + $mailtext = ((x($_POST,'zaitxt')) ? notags(trim($_POST['zaitxt'])) : ''); + + // to log in db + $reonar = json_decode( ((x($_POST,'zaireon')) ? notags(trim($_POST['zaireon'])) : ''), TRUE, 8) ; + + // me, the invitor + $account = App::get_account(); + $reonar['from'] = $account['account_email']; + $reonar['date'] = datetime_convert(); + $reonar['fromip'] = $_SERVER['REMOTE_ADDR']; + + // who is the invitor on + $inby = local_channel(); + + $ok = $ko = 0; + + // send the mail(s) + foreach($recips as $n => $recip) { + + $reonar['due'] = $due; + $reonar['to'] = $recip; + $reonar['txtpersonal'] = $mailtext; + + // generate an invide code to store and pm + $invite_code = autoname(8) . rand(1000,9999); + + // again the final localized templates $reonar['subject'] $reonar['lang'] $reonar['tpl'] + + // save current operators lc and take the desired to mail + push_lang($reonar['lang']); + // resolve + $tx = replace_macros(get_intltext_template('invite.'.$reonar['tpl'].'.tpl'), + array( + '$projectname' => t('$Projectname'), + '$invite_code' => $invite_code, + '$invite_where' => z_root() . '/register', + '$invite_whereami' => str_replace('@', '@+', $reonar['whereami']), + '$invite_whoami' => z_root() . '/channel/' . $reonar['whoami'], + '$invite_anywhere' => z_root() . '/pubsites' + ) + ); + // restore lc to operator + pop_lang(); + + $reonar['txttemplate'] = $tx; + + // pm + $zem = z_mail( + [ 'toEmail' => $recip, 'fromName' => ' ', - 'fromEmail' => $account['account_email'], - 'messageSubject' => t('Please join us on $Projectname'), - 'textVersion' => $nmessage, + 'fromEmail' => $reonar['from'], + 'messageSubject' => $reonar['subject'], + 'textVersion' => ($mailtext ? $mailtext . "\n\n" : '') . $tx . "\n" . $due, ] ); - - if($res) { - $total ++; - $current_invites ++; - set_pconfig(local_channel(),'system','sent_invites',$current_invites); - if($current_invites > $max_invites) { - notice( t('Invitation limit exceeded. Please contact your site administrator.') . EOL); - return; - } - } - else { - notice( sprintf( t('%s : Message delivery failed.'), $recip) . EOL); + + if(!$zem) { + + $ko++; + $msg = 'ZAI0208E,' . sprintf( t('%s : Message delivery failed.'), $recip); + + } else { + + $ok++; + $msg = 'ZAI0208I ' . sprintf( t('To %s : Message delivery success.'), $recip); + + // if verify_email is the rule, email becomes a dId2 - NO + // $did2 = ($flags & ACCOUNT_UNVERIFIED) == ACCOUNT_UNVERIFIED ? $recip : ''; + + // always enforce verify email with invitations, thus email becomes a dId2 + $did2 = $recip; + $flags |= ACCOUNT_UNVERIFIED; + + // defaults vital, reg_pass + $r = q("INSERT INTO register (" + . "reg_flags,reg_didx,reg_did2,reg_hash,reg_created,reg_startup,reg_expires,reg_email,reg_byc,reg_uid,reg_atip,reg_lang,reg_stuff)" + . " VALUES ( %d, 'i', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', '%s', '%s') ", + intval($flags), + dbesc($did2), + dbesc($invite_code), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + dbesc($dur['due']), + dbesc($recip), + intval($inby), + intval($account['account_id']), + dbesc($reonar['fromip']), + dbesc($reonar['lang']), + dbesc(json_encode( array('reon' => $reonar) )) + ); } - + $msg .= ' (a' . $account['account_id'] . ', c' . $inby . ', from:' . $reonar['from'] . ')'; + zar_log( $msg); } - notice( sprintf( tt("%d message sent.", "%d messages sent.", $total) , $total) . EOL); + + $ok + $ko > 0 + ? notice( 'ZAI0212I ' . sprintf( t('%1$d mail(s) sent, %2$d mail error(s)'), $ok, $ko) . EOL) + : ''; + //logger( print_r( $reonar, true) ); + return; } - - + + function get() { - + + // zai1 + if(! local_channel()) { - notice( t('Permission denied.') . EOL); + notice( 'ZAI0101E,' . t('Permission denied.') . EOL); return; } if(! Apps::system_app_installed(local_channel(), 'Invite')) { //Do not display any associated widgets at this point App::$pdl = ''; + $papp = Apps::get_papp('Invite'); + return Apps::app_render($papp, 'module'); + } - $o = '<b>' . t('Invite App') . ' (' . t('Not Installed') . '):</b><br>'; - $o .= t('Send email invitations to join this network'); + if (! (get_config('system','invitation_also') || get_config('system','invitation_only')) ) { + $o = 'ZAI0103E,' . t('Invites not proposed by configuration') . '. '; + $o .= t('Contact the site admin'); return $o; } + // invitation_by_user may still not configured, the default 'na' will tell this + // if configured, 0 disables invitations by users, other numbers are how many invites a user may propagate + $invuser = get_config('system','invitation_by_user', 'na'); + + // if the mortal user drives the invitation + If (! is_site_admin()) { + + // when not configured, 4 is the default + $invuser = ($invuser === 'na') ? 4 : $invuser; + + // a config value 0 disables invitation by users + if (!$invuser) { + $o = 'ZAI0104E, ' . t('Invites by users not enabled') . '. '; + return $o; + } + + if ($ihave >= $invuser) { + notice( 'ZAI0105W,' . t('You have no more invitations available') . EOL); + return ''; + } + + } else { + // general deity admin invite limit infinite (theoretical) + if ($invuser === 'na') set_config('system','invitation_by_user', 4); + // for display only + $invuser = '∞'; + } + + // xchan record of the page observer + // while quoting matters the user, the sending is associated with a channel (of the user) + // also the admin may and should decide, which channel will told to the public + $ob = App::get_observer(); + if(! $ob) + return 'ZAI0109F,' . t('Not on xchan') . EOL; + $whereami = $ob['xchan_addr']; + $channel = App::get_channel(); + $whoami = $channel['channel_address']; + + // to pass also to post() + $tao = 'tao.zai.whereami = ' . "'" . $whereami . "';\n" + . 'tao.zai.whoami = ' . "'" . $whoami . "';\n"; + + // expirations, duration interval + $dur = self::calcdue(); + $tao .= 'tao.zai.expire = { durn: ' . $dur['durn'] + . ', durq: ' . "'" . $dur['durq'] . "'" + . ', due: ' . "'" . $dur['due'] . "' };\n"; + + // to easy redisplay the empty form nav_set_selected('Invite'); - + + // inform about the count of invitations we have at all + $r = q("SELECT count(reg_id) as ct FROM register WHERE reg_vital = 1"); // where not admin TODO + $wehave = ($r ? $r[0]['ct'] : 0); + + // invites max for all users except admins + $invmaxau = intval(get_config('system','invitations_max_users')); + if(! $invmaxau) { + $invmaxau = 50; + if (is_site_admin()) { + set_config('system','invitations_max_users',intval($invmaxau)); + } + } + + if ($wehave > $invmaxau) { + if (! is_site_admin()) { + $feedbk .= 'ZAI0200E,' . t('All users invitation limit exceeded.') . $eol; + } + } + + // let see how many invites currently used by the user + $r = q("SELECT count(reg_id) AS n FROM register WHERE reg_vital = 1 AND reg_byc = %d", + intval(local_channel())); + $ihave = $r ? $r[0]['n'] : 0; + $tpl = get_markup_template('invite.tpl'); - $invonly = false; - - if(get_config('system','invitation_only')) { - $invonly = true; - $x = get_pconfig(local_channel(),'system','invites_remaining'); - if((! $x) && (! is_site_admin())) { - notice( t('You have no more invitations available') . EOL); - return ''; + + $inv_rabots = array( + 'i' => t('Minute(s)'), + 'h' => t('Hour(s)') , + 'd' => t('Day(s)') + ); + $inv_expire = replace_macros(get_markup_template('field_duration.qmc.tpl'), + array( + 'label' => t('Invitation expires after'), + 'qmc' => 'zai', + 'qmcid' => 'ZAI0014I', + 'field' => array( + 'name' => 'expire', + 'title' => t('duration up from now'), + 'value' => ($invexpire_n ? $invexpire_n : 2), + 'min' => '1', + 'max' => '99', + 'size' => '2', + 'default' => ($invexpire_u ? $invexpire_u : 'd') + ), + 'rabot' => $inv_rabots + ) + ); + + // let generate an invite code that here and never will be applied (only to fill displayed template) + // real invite codes become generated for each recipient when we store the new invitation(s) + // $invite_code = substr(str_shuffle('abcdefghijklmnopqrstuvwxyz'), 0, 8) . rand(1000,9999); + // let take one descriptive for template (as said is never used) + $invite_code = 'INVITATE2020'; + + // what languages we use now + $lccmy = ((isset(App::$config['system']['language'])) ? App::$config['system']['language'] : 'en'); + // and all the localized templates belonging to invite + $tpls = glob('view/*/invite.*.tpl'); + + $tpla=$tplx=$tplxs=array(); + foreach ($tpls as $tpli) { + list( $nop, $l, $t ) = explode( '/', $tpli); + if ( preg_match('/\.subject/', $t) =='1' ) { + // indicate a subject tpl exists + $t=str_replace(array('invite.', '.subject', '.tpl'), '', $t); + $tplxs[$l][$t]=true; + continue; } + // collect unique template names cross all languages and + // tpla[language][]=template those available in each language + $tplx[] = $tpla[$l][] = str_replace( array('invite.', '.tpl'), '', $t); } - - if($invonly && ($x || is_site_admin())) { - $invite_code = autoname(8) . rand(1000,9999); - $nmessage = str_replace('$invite_code',$invite_code,$message); - - $r = q("INSERT INTO register (hash,created,uid,password,lang) VALUES ('%s', '%s',0,'','') ", - dbesc($invite_code), - dbesc(datetime_convert()) + + $langs = array_keys($tpla); + asort($langs); + + $tplx = array_unique($tplx); + asort($tplx); + + // prepare current language and the default standard template (causual) for js + // With and in js, I use a var 'tao' as a shortcut for top array object + // and also qualify the object with the prefix zai = tao.zai as my var used outsite functions + // can be unique within the overall included spaghette whirls + // one can say Im too lazy to write prototypes and just I can agree. + // tao simply applies the fact of using the same var as object and/or array in ja. + $tao.='tao.zai.lccmy = ' . "'" . $lccmy . "';\n" . 'tao.zai.itpl = ' . "'" . 'casual' . "';\n"; + + $lcclane=$tx=$tplin=''; + //$lccsym='<span class="fa zai_fa zai_lccsym"></span>'; // alt  + $tplsym='<span class="fa zai_fa">ïƒ </span>'; + + // I will uncomment for js console debug + // $tao.='tao.zai.debug = ' . "'" . json_encode($tplxs) . "';\n"; + + // running thru the localized templates (subjects and textmsgs) and bring them to tao + // lcc LanguageCountryCode, + // lcc2 is a 2 character and lcc5 a 5 character LanguageCountryCode + foreach($tpla as $l => $tn) { + + // restyle lc to iso getttext format to avoid errors in js, hilite the current + $lcc = str_replace('-', '_', $l); + $hi = ($l == $lccmy) ? ' zai_hi' : ''; + $lcc2 = strlen($l) == 2 ? ' zai_lcc2' : ''; + $lcc5 = strlen($l) == 5 ? ' zai_lcc5' : ''; + $lccg = ' zai_lccg' . substr( $l, 0, 2 ); + $lcclane + .= '<span class="fa zai_fa zai_lccsym' . $lcc2 . $lcc5 . $lccg . '"></span>' + . '<a href="javascript:;" class="zai_lcc' . $lcc2 . $lcc5 . $lccg . $hi . '">' . $lcc . '</a>'; + // textmsg + $tao .= 'tao.zai.t.' . $lcc . ' = {};' . "\n"; + // subject + $tao .= 'tao.zai.s.' . $lcc . ' = {};' . "\n"; + + // resolve localized templates and take intented lc for + foreach($tn as $t1) { + + // save current lc and take the desired + push_lang($l); + + // resolve + $tx = replace_macros(get_intltext_template('invite.'.$t1.'.tpl'), + array( + '$projectname' => t('$Projectname'), + '$invite_code' => $invite_code, + '$invite_where' => z_root() . '/register', + '$invite_whereami' => $whereami, + '$invite_whoami' => z_root() . '/channel/' . $whoami, + '$invite_anywhere' => z_root() . '/pubsites' + ) ); - - if(! is_site_admin()) { - $x --; - if($x >= 0) - set_pconfig(local_channel(),'system','invites_remaining',$x); - else - return; - } + + // a default subject if no associated exists + $ts=t('Invitation'); + if ( $tplxs[$l][$t1] ) + $ts = replace_macros(get_intltext_template('invite.'.$t1.'.subject.tpl'), + array( + '$projectname' => t('$Projectname'), + '$invite_loc' => get_config('system','sitename') + ) + ); + + // restore lc to current foreground + pop_lang(); + + // bring to tao as js like it + $tao .= 'tao.zai.t.' . $lcc . '.' . $t1 . " = '" . rawurlencode($tx) . "';\n"; + $tao .= 'tao.zai.s.' . $lcc . '.' . $t1 . " = '" . rawurlencode($ts) . "';\n"; } - - $ob = App::get_observer(); - if(! $ob) - return $o; - - $channel = App::get_channel(); - + } + + // hilite the current defauls just from the beginning + foreach ($tplx as $t1) { + $hi = ($t1 == 'casual') ? ' zai_hi' : ''; + $tplin .= $tplsym.'<a href="javascript:;" id="zai-' . $t1 + . '" class="invites'.$hi.'">' . $t1 . '</a>'; + } + + // fill the form for foreground $o = replace_macros($tpl, array( '$form_security_token' => get_form_security_token("send_invite"), + '$zai' => strtolower(self::MYP), + '$tao' => $tao, '$invite' => t('Send invitations'), - '$addr_text' => t('Enter email addresses, one per line:'), - '$msg_text' => t('Your message:'), - '$default_message' => t('Please join my community on $Projectname.') . "\r\n" . "\r\n" - . $linktxt - . (($invonly) ? "\r\n" . "\r\n" . t('You will need to supply this invitation code:') . " " . $invite_code . "\r\n" . "\r\n" : '') - . t('1. Register at any $Projectname location (they are all inter-connected)') - . "\r\n" . "\r\n" . z_root() . '/register' - . "\r\n" . "\r\n" . t('2. Enter my $Projectname network address into the site searchbar.') - . "\r\n" . "\r\n" . $ob['xchan_addr'] . ' (' . t('or visit') . " " . z_root() . '/channel/' . $channel['channel_address'] . ')' - . "\r\n" . "\r\n" - . t('3. Click [Connect]') - . "\r\n" . "\r\n" , + '$ihave' => 'ZAI0106I, ' . t('Invitations I am using') . ': ' . $ihave . ' / ' . $invuser, + '$wehave' => 'ZAI0107I, ' . t('Invitations we are using') . ': ' . $wehave . ' / ' . $invmaxau, + '$n10' => 'ZAI0010I', '$m10' => t('§ Note, the email(s) sent will be recorded in the system logs'), + '$n11' => 'ZAI0011I', '$m11' => t('Enter email addresses, one per line:'), + '$n12' => 'ZAI0012I', '$m12' => t('Your message:'), + '$n13' => 'ZAI0013I', '$m13' => t('Invite template'), + '$inv_expire' => $inv_expire, + '$subject_label' => t('Subject:'), + '$subject' => t('Invitation'), + '$lcclane' => $lcclane, + '$tplin' => $tplin, + '$standard_message' => '', + '$personal_message' => '', + '$personal_pointer' => t('Here you may enter personal notes to the recipient(s)'), + '$due' => t('Note, the invitation code is valid up to') . ' ' . $dur['due'], '$submit' => t('Submit') )); - + return $o; } - + + function calcdue($duri=false) { + // expirations, duration interval + if ($duri===false) + $duri = get_config('system','register_expire', '2d'); + if ( preg_match( '/^[0-9]{1,2}[ihdwmy]{1}$/', $duri ) ) { + $durq = substr($duri, -1); + $durn = substr($duri, 0, -1); + $due = date('Y-m-d H:i:s', strtotime('+' . $durn . ' ' + . str_replace( array(':i',':h',':d',':w',':m',':y'), + array('minutes', 'hours', 'days', 'weeks', 'months', 'years'), + (':'.$durq)) + )); + return array( 'durn' => $durn, 'durq' => $durq, 'due' => $due); + } + return false; + } } + diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php index 7c438c309..7099a54e5 100644 --- a/Zotlabs/Module/Item.php +++ b/Zotlabs/Module/Item.php @@ -26,17 +26,17 @@ require_once('include/conversation.php'); /** * * This is the POST destination for most all locally posted - * text stuff. This function handles status, wall-to-wall status, - * local comments, and remote coments that are posted on this site + * text stuff. This function handles status, wall-to-wall status, + * local comments, and remote coments that are posted on this site * (as opposed to being delivered in a feed). - * Also processed here are posts and comments coming through the - * statusnet/twitter API. - * All of these become an "item" which is our basic unit of + * Also processed here are posts and comments coming through the + * statusnet/twitter API. + * All of these become an "item" which is our basic unit of * information. - * Posts that originate externally or do not fall into the above - * posting categories go through item_store() instead of this function. + * Posts that originate externally or do not fall into the above + * posting categories go through item_store() instead of this function. * - */ + */ class Item extends Controller { @@ -55,7 +55,12 @@ class Item extends Controller { $portable_id = EMPTY_STR; - $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 "; + $item_normal_extra = sprintf(" and not verb in ('%s', '%s') ", + dbesc(ACTIVITY_FOLLOW), + dbesc(ACTIVITY_UNFOLLOW) + ); + + $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 $item_normal_extra "; $i = null; @@ -107,7 +112,7 @@ class Item extends Controller { } $parents_str = ids_to_querystr($i,'item_id'); - + $items = q("SELECT item.*, item.id AS item_id FROM item WHERE item.parent IN ( %s ) $item_normal order by item.id asc", dbesc($parents_str) ); @@ -132,13 +137,14 @@ class Item extends Controller { $i = Activity::encode_item_collection($items, 'conversation/' . $item_id, 'OrderedCollection'); - if($portable_id) { - ThreadListener::store(z_root() . '/item/' . $item_id,$portable_id); - } if(! $i) http_status_exit(404, 'Not found'); + if($portable_id && (! intval($items[0]['item_private']))) { + ThreadListener::store(z_root() . '/item/' . $item_id, $portable_id); + } + $x = array_merge(['@context' => [ ACTIVITYSTREAMS_JSONLD_REV, 'https://w3id.org/security/v1', @@ -166,7 +172,12 @@ class Item extends Controller { $portable_id = EMPTY_STR; - $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 "; + $item_normal_extra = sprintf(" and not verb in ('%s', '%s') ", + dbesc(ACTIVITY_FOLLOW), + dbesc(ACTIVITY_UNFOLLOW) + ); + + $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 $item_normal_extra "; $i = null; @@ -237,6 +248,16 @@ class Item extends Controller { if(! $i) http_status_exit(404, 'Not found'); + if ($portable_id && (! intval($items[0]['item_private']))) { + $c = q("select abook_id from abook where abook_channel = %d and abook_xchan = '%s'", + intval($items[0]['uid']), + dbesc($portable_id) + ); + if (! $c) { + ThreadListener::store(z_root() . '/item/' . $item_id, $portable_id); + } + } + $x = array_merge(['@context' => [ ACTIVITYSTREAMS_JSONLD_REV, 'https://w3id.org/security/v1', @@ -259,16 +280,17 @@ class Item extends Controller { if(argc() > 1 && argv(1) !== 'drop') { - $x = q("select uid, item_wall, llink, mid from item where mid = '%s' or mid = '%s' ", + $x = q("select uid, item_wall, llink, mid from item where mid = '%s' or mid = '%s' or uuid = '%s'", dbesc(z_root() . '/item/' . argv(1)), - dbesc(z_root() . '/activity/' . argv(1)) + dbesc(z_root() . '/activity/' . argv(1)), + dbesc(argv(1)) ); if($x) { foreach($x as $xv) { if (intval($xv['item_wall'])) { $c = channelx_by_n($xv['uid']); if ($c) { - goaway($c['xchan_url'] . '?mid=' . gen_link_id($xv['mid'])); + goaway(z_root() . '/channel/' . $c['channel_address'] . '?mid=' . gen_link_id($xv['mid'])); } } } @@ -285,7 +307,7 @@ class Item extends Controller { // This will change. Figure out who the observer is and whether or not // they have permission to post here. Else ignore the post. - + if((! local_channel()) && (! remote_channel()) && (! x($_REQUEST,'anonname'))) return; @@ -293,25 +315,25 @@ class Item extends Controller { $channel = null; $observer = null; $datarray = []; - - + + /** * Is this a reply to something? */ - + $parent = ((x($_REQUEST,'parent')) ? intval($_REQUEST['parent']) : 0); $parent_mid = ((x($_REQUEST,'parent_mid')) ? trim($_REQUEST['parent_mid']) : ''); $mode = (($_REQUEST['conv_mode'] === 'channel') ? 'channel' : 'network'); - + $remote_xchan = ((x($_REQUEST,'remote_xchan')) ? trim($_REQUEST['remote_xchan']) : false); $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($remote_xchan) ); if($r) $remote_observer = $r[0]; - else + else $remote_xchan = $remote_observer = false; - + $profile_uid = ((x($_REQUEST,'profile_uid')) ? intval($_REQUEST['profile_uid']) : 0); require_once('include/channel.php'); @@ -321,7 +343,7 @@ class Item extends Controller { $channel = $sys; $observer = $sys; } - + if(x($_REQUEST,'dropitems')) { require_once('include/items.php'); $arr_drop = explode(',',$_REQUEST['dropitems']); @@ -330,36 +352,36 @@ class Item extends Controller { echo json_encode($json); killme(); } - + call_hooks('post_local_start', $_REQUEST); - + // logger('postvars ' . print_r($_REQUEST,true), LOGGER_DATA); - + $api_source = ((x($_REQUEST,'api_source') && $_REQUEST['api_source']) ? true : false); - + $consensus = intval($_REQUEST['consensus']); $nocomment = intval($_REQUEST['nocomment']); $is_poll = ((trim($_REQUEST['poll_answers'][0]) != '' && trim($_REQUEST['poll_answers'][1]) != '') ? true : false); // 'origin' (if non-zero) indicates that this network is where the message originated, - // for the purpose of relaying comments to other conversation members. + // for the purpose of relaying comments to other conversation members. // If using the API from a device (leaf node) you must set origin to 1 (default) or leave unset. // If the API is used from another network with its own distribution - // and deliveries, you may wish to set origin to 0 or false and allow the other + // and deliveries, you may wish to set origin to 0 or false and allow the other // network to relay comments. - - // If you are unsure, it is prudent (and important) to leave it unset. - + + // If you are unsure, it is prudent (and important) to leave it unset. + $origin = (($api_source && array_key_exists('origin',$_REQUEST)) ? intval($_REQUEST['origin']) : 1); - + // To represent message-ids on other networks - this will create an iconfig record - + $namespace = (($api_source && array_key_exists('namespace',$_REQUEST)) ? strip_tags($_REQUEST['namespace']) : ''); $remote_id = (($api_source && array_key_exists('remote_id',$_REQUEST)) ? strip_tags($_REQUEST['remote_id']) : ''); - + $owner_hash = null; - + $message_id = ((x($_REQUEST,'message_id') && $api_source) ? strip_tags($_REQUEST['message_id']) : ''); $created = ((x($_REQUEST,'created')) ? datetime_convert(date_default_timezone_get(),'UTC',$_REQUEST['created']) : datetime_convert()); $post_id = ((x($_REQUEST,'post_id')) ? intval($_REQUEST['post_id']) : 0); @@ -373,49 +395,49 @@ class Item extends Controller { $layout_mid = ((x($_REQUEST,'layout_mid')) ? escape_tags($_REQUEST['layout_mid']): ''); $plink = ((x($_REQUEST,'permalink')) ? escape_tags($_REQUEST['permalink']) : ''); $obj_type = ((x($_REQUEST,'obj_type')) ? escape_tags($_REQUEST['obj_type']) : ACTIVITY_OBJ_NOTE); - - // allow API to bulk load a bunch of imported items with sending out a bunch of posts. + + // allow API to bulk load a bunch of imported items with sending out a bunch of posts. $nopush = ((x($_REQUEST,'nopush')) ? intval($_REQUEST['nopush']) : 0); - + /* * Check service class limits */ if ($uid && !(x($_REQUEST,'parent')) && !(x($_REQUEST,'post_id'))) { $ret = $this->item_check_service_class($uid,(($_REQUEST['webpage'] == ITEM_TYPE_WEBPAGE) ? true : false)); - if (!$ret['success']) { + if (!$ret['success']) { notice( t($ret['message']) . EOL) ; if($api_source) - return ( [ 'success' => false, 'message' => 'service class exception' ] ); - if(x($_REQUEST,'return')) + return ( [ 'success' => false, 'message' => 'service class exception' ] ); + if(x($_REQUEST,'return')) goaway(z_root() . "/" . $return_path ); killme(); } } - + if($pagetitle) { require_once('library/urlify/URLify.php'); $pagetitle = strtolower(\URLify::transliterate($pagetitle)); } - - + + $item_flags = $item_restrict = 0; $expires = NULL_DATE; - + $route = ''; $parent_item = null; $parent_contact = null; $thr_parent = ''; $parid = 0; $r = false; - + if($parent || $parent_mid) { - + if(! x($_REQUEST,'type')) $_REQUEST['type'] = 'net-comment'; - + if($obj_type == ACTIVITY_OBJ_NOTE) $obj_type = ACTIVITY_OBJ_COMMENT; - + if($parent) { $r = q("SELECT * FROM item WHERE id = %d LIMIT 1", intval($parent) @@ -438,7 +460,7 @@ class Item extends Controller { ); } - // if interacting with a pubstream item, + // if interacting with a pubstream item, // create a copy of the parent in your stream if($r[0]['uid'] === $sys['channel_id'] && local_channel()) { @@ -449,8 +471,8 @@ class Item extends Controller { if(! $r) { notice( t('Unable to locate original post.') . EOL); if($api_source) - return ( [ 'success' => false, 'message' => 'invalid post id' ] ); - if(x($_REQUEST,'return')) + return ( [ 'success' => false, 'message' => 'invalid post id' ] ); + if(x($_REQUEST,'return')) goaway(z_root() . "/" . $return_path ); killme(); } @@ -461,15 +483,15 @@ class Item extends Controller { $parent = $r[0]['id']; // multi-level threading - preserve the info but re-parent to our single level threading - + $thr_parent = $parent_mid; - + $route = $parent_item['route']; - + } $moderated = false; - + if(! $observer) { $observer = \App::get_observer(); if(! $observer) { @@ -479,13 +501,13 @@ class Item extends Controller { $remote_xchan = $remote_observer = $observer; } } - } - + } + if(! $observer) { notice( t('Permission denied.') . EOL) ; if($api_source) - return ( [ 'success' => false, 'message' => 'permission denied' ] ); - if(x($_REQUEST,'return')) + return ( [ 'success' => false, 'message' => 'permission denied' ] ); + if(x($_REQUEST,'return')) goaway(z_root() . "/" . $return_path ); killme(); } @@ -499,12 +521,12 @@ class Item extends Controller { if((array_key_exists('owner',$parent_item)) && intval($parent_item['owner']['abook_self'])==1 ) $can_comment = perm_is_allowed($profile_uid,$observer['xchan_hash'],'post_comments'); } - + if(! $can_comment) { notice( t('Permission denied.') . EOL) ; if($api_source) - return ( [ 'success' => false, 'message' => 'permission denied' ] ); - if(x($_REQUEST,'return')) + return ( [ 'success' => false, 'message' => 'permission denied' ] ); + if(x($_REQUEST,'return')) goaway(z_root() . "/" . $return_path ); killme(); } @@ -513,30 +535,30 @@ class Item extends Controller { if(! perm_is_allowed($profile_uid,$observer['xchan_hash'],($webpage) ? 'write_pages' : 'post_wall')) { notice( t('Permission denied.') . EOL) ; if($api_source) - return ( [ 'success' => false, 'message' => 'permission denied' ] ); - if(x($_REQUEST,'return')) + return ( [ 'success' => false, 'message' => 'permission denied' ] ); + if(x($_REQUEST,'return')) goaway(z_root() . "/" . $return_path ); killme(); } } - - + + // is this an edited post? - + $orig_post = null; - + if($namespace && $remote_id) { // It wasn't an internally generated post - see if we've got an item matching this remote service id $i = q("select iid from iconfig where cat = 'system' and k = '%s' and v = '%s' limit 1", dbesc($namespace), - dbesc($remote_id) + dbesc($remote_id) ); if($i) - $post_id = $i[0]['iid']; + $post_id = $i[0]['iid']; } - + $iconfig = null; - + if($post_id) { $i = q("SELECT * FROM item WHERE uid = %d AND id = %d LIMIT 1", intval($profile_uid), @@ -549,8 +571,8 @@ class Item extends Controller { intval($post_id) ); } - - + + if(! $channel) { if($uid && $uid == $profile_uid) { $channel = \App::get_channel(); @@ -564,19 +586,19 @@ class Item extends Controller { $channel = $r[0]; } } - - + + if(! $channel) { logger("mod_item: no channel."); if($api_source) - return ( [ 'success' => false, 'message' => 'no channel' ] ); - if(x($_REQUEST,'return')) + return ( [ 'success' => false, 'message' => 'no channel' ] ); + if(x($_REQUEST,'return')) goaway(z_root() . "/" . $return_path ); killme(); } - + $owner_xchan = null; - + $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($channel['channel_hash']) ); @@ -586,50 +608,50 @@ class Item extends Controller { else { logger("mod_item: no owner."); if($api_source) - return ( [ 'success' => false, 'message' => 'no owner' ] ); - if(x($_REQUEST,'return')) + return ( [ 'success' => false, 'message' => 'no owner' ] ); + if(x($_REQUEST,'return')) goaway(z_root() . "/" . $return_path ); killme(); } - + $walltowall = false; $walltowall_comment = false; - + if($remote_xchan && ! $moderated) $observer = $remote_observer; - + if($observer) { logger('mod_item: post accepted from ' . $observer['xchan_name'] . ' for ' . $owner_xchan['xchan_name'], LOGGER_DEBUG); - + // wall-to-wall detection. // For top-level posts, if the author and owner are different it's a wall-to-wall // For comments, We need to additionally look at the parent and see if it's a wall post that originated locally. - + if($observer['xchan_name'] != $owner_xchan['xchan_name']) { if(($parent_item) && ($parent_item['item_wall'] && $parent_item['item_origin'])) { $walltowall_comment = true; $walltowall = true; } if(! $parent) { - $walltowall = true; + $walltowall = true; } } } - + $acl = new \Zotlabs\Access\AccessList($channel); - $view_policy = \Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_stream'); + $view_policy = \Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_stream'); $comment_policy = \Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'post_comments'); - + $public_policy = ((x($_REQUEST,'public_policy')) ? escape_tags($_REQUEST['public_policy']) : map_scope($view_policy,true)); if($webpage) $public_policy = ''; if($public_policy) $private = 1; - + if($orig_post) { $private = 0; - // webpages are allowed to change ACLs after the fact. Normal conversation items aren't. + // webpages are allowed to change ACLs after the fact. Normal conversation items aren't. if($webpage) { $acl->set_from_array($_REQUEST); } @@ -641,8 +663,8 @@ class Item extends Controller { if($public_policy || $acl->is_private()) { $private = (($private) ? $private : 1); - } - + } + $location = $orig_post['location']; $coord = $orig_post['coord']; $verb = $orig_post['verb']; @@ -651,7 +673,7 @@ class Item extends Controller { $summary = trim($_REQUEST['summary']); $body = trim($_REQUEST['body']); $item_flags = $orig_post['item_flags']; - + $item_origin = $orig_post['item_origin']; $item_unseen = $orig_post['item_unseen']; $item_starred = $orig_post['item_starred']; @@ -675,16 +697,16 @@ class Item extends Controller { $item_delayed = $orig_post['item_delayed']; $item_pending_remove = $orig_post['item_pending_remove']; $item_blocked = $orig_post['item_blocked']; - - - + + + $postopts = $orig_post['postopts']; $created = $orig_post['created']; $expires = $orig_post['expires']; $mid = $orig_post['mid']; $parent_mid = $orig_post['parent_mid']; $plink = $orig_post['plink']; - + } else { if(! $walltowall) { @@ -695,18 +717,18 @@ class Item extends Controller { $acl->set_from_array($_REQUEST); } elseif(! $api_source) { - + // if no ACL has been defined and we aren't using the API, the form // didn't send us any parameters. This means there's no ACL or it has // been reset to the default audience. // If $api_source is set and there are no ACL parameters, we default // to the channel permissions which were set in the ACL contructor. - + $acl->set(array('allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '')); } } - - + + $location = notags(trim($_REQUEST['location'])); $coord = notags(trim($_REQUEST['coord'])); $verb = notags(trim($_REQUEST['verb'])); @@ -716,34 +738,34 @@ class Item extends Controller { $body .= trim($_REQUEST['attachment']); $postopts = ''; - $allow_empty = ((array_key_exists('allow_empty',$_REQUEST)) ? intval($_REQUEST['allow_empty']) : 0); + $allow_empty = ((array_key_exists('allow_empty',$_REQUEST)) ? intval($_REQUEST['allow_empty']) : 0); $private = (($private) ? $private : intval($acl->is_private() || ($public_policy))); - + // If this is a comment, set the permissions from the parent. - + if($parent_item) { $acl->set($parent_item); - $private = intval($acl->is_private() || $parent_item['item_private']); - $public_policy = $parent_item['public_policy']; - $owner_hash = $parent_item['owner_xchan']; - $webpage = $parent_item['item_type']; + $private = intval($parent_item['item_private']); + $public_policy = $parent_item['public_policy']; + $owner_hash = $parent_item['owner_xchan']; + $webpage = $parent_item['item_type']; } - + if((! $allow_empty) && (! strlen($body))) { if($preview) killme(); info( t('Empty post discarded.') . EOL ); if($api_source) - return ( [ 'success' => false, 'message' => 'no content' ] ); - if(x($_REQUEST,'return')) + return ( [ 'success' => false, 'message' => 'no content' ] ); + if(x($_REQUEST,'return')) goaway(z_root() . "/" . $return_path ); killme(); } } - - - + + + if(feature_enabled($profile_uid,'content_expire')) { if(x($_REQUEST,'expire')) { $expires = datetime_convert(date_default_timezone_get(),'UTC', $_REQUEST['expire']); @@ -756,16 +778,16 @@ class Item extends Controller { $mimetype = notags(trim($_REQUEST['mimetype'])); if(! $mimetype) $mimetype = 'text/bbcode'; - - $execflag = ((intval($uid) == intval($profile_uid) + + $execflag = ((intval($uid) == intval($profile_uid) && ($channel['channel_pageflags'] & PAGE_ALLOWCODE)) ? true : false); if($preview) { $summary = z_input_filter($summary,$mimetype,$execflag); $body = z_input_filter($body,$mimetype,$execflag); } - + $arr = [ 'profile_uid' => $profile_uid, 'summary' => $summary, 'content' => $body, 'mimetype' => $mimetype ]; call_hooks('post_content',$arr); @@ -773,7 +795,7 @@ class Item extends Controller { $body = $arr['content']; $mimetype = $arr['mimetype']; - + $gacl = $acl->get(); $str_contact_allow = $gacl['allow_cid']; $str_group_allow = $gacl['allow_gid']; @@ -784,7 +806,7 @@ class Item extends Controller { $groupww = false; // if this is a wall-to-wall post to a group, turn it into a direct message - + $role = get_pconfig($profile_uid,'system','permissions_role'); $rolesettings = PermissionRoles::role_perms($role); @@ -793,57 +815,46 @@ class Item extends Controller { $is_group = (($channel_type === 'group') ? true : false); - if (($is_group) && ($walltowall) && (! $walltowall_comment)) { + if (($is_group) && ($walltowall) && (! $walltowall_comment)) { $groupww = true; $str_contact_allow = $owner_xchan['xchan_hash']; $str_group_allow = ''; } $post_tags = []; - + + + if($mimetype === 'text/bbcode') { - - require_once('include/text.php'); - - + + require_once('include/text.php'); + + // BBCODE alert: the following functions assume bbcode input // and will require alternatives for alternative content-types (text/html, text/markdown, text/plain, etc.) // we may need virtual or template classes to implement the possible alternatives - if(strpos($body,'[/summary]') !== false) { - $match = ''; - $cnt = preg_match("/\[summary\](.*?)\[\/summary\]/ism",$body,$match); - if($cnt) { - $summary .= $match[1]; - } - $body_content = preg_replace("/\[summary\](.*?)\[\/summary\]/ism", '',$body); - $body = trim($body_content); - } - - $summary = cleanup_bbcode($summary); - $body = cleanup_bbcode($body); - + // Look for tags and linkify them - $results = linkify_tags($summary, ($uid) ? $uid : $profile_uid); $results = linkify_tags($body, ($uid) ? $uid : $profile_uid); if($results) { - + // Set permissions based on tag replacements - set_linkified_perms($results, $str_contact_allow, $str_group_allow, $profile_uid, $parent_item, $private); - + set_linkified_perms($results, $str_contact_allow, $str_group_allow, $profile_uid, $private, $parent_item); + foreach($results as $result) { $success = $result['success']; if($success['replaced']) { $post_tags[] = array( - 'uid' => $profile_uid, + 'uid' => $profile_uid, 'ttype' => $success['termtype'], 'otype' => TERM_OBJ_POST, 'term' => $success['term'], 'url' => $success['url'] - ); + ); } } @@ -854,10 +865,10 @@ class Item extends Controller { $private = 2; } - + /** * - * When a photo was uploaded into the message using the (profile wall) ajax + * When a photo was uploaded into the message using the (profile wall) ajax * uploader, The permissions are initially set to disallow anybody but the * owner from seeing it. This is because the permissions may not yet have been * set for the post. If it's private, the photo permissions should be set @@ -867,27 +878,22 @@ class Item extends Controller { * * If the post was end-to-end encrypted we can't find images and attachments in the body, * use our media_str input instead which only contains these elements - but only do this - * when encrypted content exists because the photo/attachment may have been removed from + * when encrypted content exists because the photo/attachment may have been removed from * the post and we should keep it private. If it's encrypted we have no way of knowing - * so we'll set the permissions regardless and realise that the media may not be - * referenced in the post. + * so we'll set the permissions regardless and realise that the media may not be + * referenced in the post. * */ - + if(! $preview) { fix_attached_photo_permissions($profile_uid,$owner_xchan['xchan_hash'],((strpos($body,'[/crypt]')) ? $_POST['media_str'] : $body),$str_contact_allow,$str_group_allow,$str_contact_deny,$str_group_deny); - fix_attached_photo_permissions($profile_uid,$owner_xchan['xchan_hash'],((strpos($summary,'[/crypt]')) ? $_POST['media_str'] : $summary),$str_contact_allow,$str_group_allow,$str_contact_deny,$str_group_deny); - - fix_attached_file_permissions($channel,$observer['xchan_hash'],((strpos($body,'[/crypt]')) ? $_POST['media_str'] : $body),$str_contact_allow,$str_group_allow,$str_contact_deny,$str_group_deny); - } - - + $attachments = ''; $match = false; - + if(preg_match_all('/(\[attachment\](.*?)\[\/attachment\])/',$body,$match)) { $attachments = array(); $i = 0; @@ -910,10 +916,9 @@ class Item extends Controller { } } - if(preg_match_all('/(\[share=(.*?)\](.*?)\[\/share\])/',$body,$match)) { - // process share by id + // process share by id $i = 0; foreach($match[2] as $mtch) { @@ -922,11 +927,10 @@ class Item extends Controller { $i++; } } - + + // BBCODE end alert } - - // BBCODE end alert - + if(strlen($categories)) { $cats = explode(',',$categories); @@ -943,15 +947,15 @@ class Item extends Controller { } $post_tags[] = array( - 'uid' => $profile_uid, + 'uid' => $profile_uid, 'ttype' => TERM_CATEGORY, 'otype' => TERM_OBJ_POST, 'term' => trim($cat), 'url' => $catlink - ); + ); } } - + if($orig_post) { // preserve original tags $t = q("select * from term where oid = %d and otype = %d and uid = %d and ttype in ( %d, %d, %d )", @@ -965,26 +969,26 @@ class Item extends Controller { if($t) { foreach($t as $t1) { $post_tags[] = array( - 'uid' => $profile_uid, + 'uid' => $profile_uid, 'ttype' => $t1['ttype'], 'otype' => TERM_OBJ_POST, 'term' => $t1['term'], 'url' => $t1['url'], - ); + ); } } - } - - + } + + $item_unseen = ((local_channel() != $profile_uid) ? 1 : 0); - $item_wall = (($post_type === 'wall' || $post_type === 'wall-comment') ? 1 : 0); + $item_wall = (($_REQUEST['type'] === 'wall' || $_REQUEST['type'] === 'wall-comment') ? 1 : 0); $item_origin = (($origin) ? 1 : 0); $item_consensus = (($consensus) ? 1 : 0); $item_nocomment = (($nocomment) ? 1 : 0); - - + + // determine if this is a wall post - + if($parent) { $item_wall = $parent_item['item_wall']; } @@ -993,20 +997,20 @@ class Item extends Controller { $item_wall = 1; } } - - + + if($moderated) $item_blocked = ITEM_MODERATED; - - + + if(! strlen($verb)) $verb = ACTIVITY_POST ; - + $notify_type = (($parent) ? 'comment-new' : 'wall-new' ); - + if(! $mid) { $uuid = (($message_id) ? $message_id : item_message_id()); - $mid = z_root() . '/item/' . $uuid; + $mid = z_root() . '/item/' . $uuid; } @@ -1034,23 +1038,23 @@ class Item extends Controller { if(! $parent_mid) { $parent_mid = $mid; } - + if($parent_item) $parent_mid = $parent_item['mid']; // Fallback so that we alway have a thr_parent - + if(!$thr_parent) $thr_parent = $mid; - + $item_thread_top = ((! $parent) ? 1 : 0); - + // fix permalinks for cards - + if($webpage == ITEM_TYPE_CARD) { $plink = z_root() . '/cards/' . $channel['channel_address'] . '/' . (($pagetitle) ? $pagetitle : $uuid); } @@ -1138,27 +1142,27 @@ class Item extends Controller { $datarray['item_unpublished'] = intval($item_unpublished); $datarray['item_delayed'] = intval($item_delayed); $datarray['item_pending_remove'] = intval($item_pending_remove); - $datarray['item_blocked'] = intval($item_blocked); + $datarray['item_blocked'] = intval($item_blocked); $datarray['layout_mid'] = $layout_mid; $datarray['public_policy'] = $public_policy; - $datarray['comment_policy'] = map_scope($comment_policy); + $datarray['comment_policy'] = map_scope($comment_policy); $datarray['term'] = array_unique($post_tags, SORT_REGULAR); $datarray['plink'] = $plink; $datarray['route'] = $route; // A specific ACL over-rides public_policy completely - + if(! empty_acl($datarray)) $datarray['public_policy'] = ''; if($iconfig) $datarray['iconfig'] = $iconfig; - + // preview mode - prepare the body for display and send it via json - + if($preview) { require_once('include/conversation.php'); - + $datarray['owner'] = $owner_xchan; $datarray['author'] = $observer; $datarray['attach'] = json_encode($datarray['attach']); @@ -1169,45 +1173,45 @@ class Item extends Controller { } if($orig_post) $datarray['edit'] = true; - + // suppress duplicates, *unless* you're editing an existing post. This could get picked up // as a duplicate if you're editing it very soon after posting it initially and you edited - // some attribute besides the content, such as title or categories. + // some attribute besides the content, such as title or categories. if(feature_enabled($profile_uid,'suppress_duplicates') && (! $orig_post)) { - + $z = q("select created from item where uid = %d and created > %s - INTERVAL %s and body = '%s' limit 1", intval($profile_uid), db_utcnow(), db_quoteinterval('2 MINUTE'), dbesc($body) ); - + if($z) { $datarray['cancel'] = 1; notice( t('Duplicate post suppressed.') . EOL); logger('Duplicate post. Faking plugin cancel.'); } } - + call_hooks('post_local',$datarray); - + if(x($datarray,'cancel')) { logger('mod_item: post cancelled by plugin or duplicate suppressed.'); if($return_path) goaway(z_root() . "/" . $return_path); if($api_source) - return ( [ 'success' => false, 'message' => 'operation cancelled' ] ); + return ( [ 'success' => false, 'message' => 'operation cancelled' ] ); $json = array('cancel' => 1); $json['reload'] = z_root() . '/' . $_REQUEST['jsreload']; echo json_encode($json); killme(); } - - + + if(mb_strlen($datarray['title']) > 191) $datarray['title'] = mb_substr($datarray['title'],0,191); - + if($webpage) { IConfig::Set($datarray,'system', webpage_to_namespace($webpage), (($pagetitle) ? $pagetitle : basename($datarray['mid'])), true); @@ -1220,20 +1224,20 @@ class Item extends Controller { if($orig_post) { $datarray['id'] = $post_id; - + $x = item_store_update($datarray,$execflag); - + // We only need edit activities for other federated protocols - // which do not support edits natively. While this does federate + // which do not support edits natively. While this does federate // edits, it presents a number of issues locally - such as #757 and #758. // The SQL check for an edit activity would not perform that well so to fix these issues - // requires an additional item flag (perhaps 'item_edit_activity') that we can add to the + // requires an additional item flag (perhaps 'item_edit_activity') that we can add to the // query for searches and notifications. - // For now we'll just forget about trying to make edits work on network protocols that - // don't support them. + // For now we'll just forget about trying to make edits work on network protocols that + // don't support them. - // item_create_edit_activity($x); + // item_create_edit_activity($x); if(! $parent) { $r = q("select * from item where id = %d", @@ -1247,7 +1251,7 @@ class Item extends Controller { } if(! $nopush) Master::Summon([ 'Notifier', 'edit_post', $post_id ]); - + if($api_source) return($x); @@ -1260,18 +1264,18 @@ class Item extends Controller { } else $post_id = 0; - + $post = item_store($datarray,$execflag); - + $post_id = $post['item_id']; $datarray = $post['item']; if($post_id) { logger('mod_item: saved item ' . $post_id); - + if($parent) { - + // prevent conversations which you are involved from being expired if(local_channel()) @@ -1279,7 +1283,7 @@ class Item extends Controller { // only send comment notification if this is a wall-to-wall comment, // otherwise it will happen during delivery - + if(($datarray['owner_xchan'] != $datarray['author_xchan']) && (intval($parent_item['item_wall']))) { Enotify::submit(array( 'type' => NOTIFY_COMMENT, @@ -1292,12 +1296,12 @@ class Item extends Controller { 'parent' => $parent, 'parent_mid' => $parent_item['mid'] )); - + } } else { $parent = $post_id; - + if(($datarray['owner_xchan'] != $datarray['author_xchan']) && ($datarray['item_type'] == ITEM_TYPE_POST)) { Enotify::submit(array( 'type' => NOTIFY_WALL, @@ -1309,7 +1313,7 @@ class Item extends Controller { 'otype' => 'item' )); } - + if($uid && $uid == $profile_uid && (is_item_normal($datarray))) { q("update channel set channel_lastpost = '%s' where channel_id = %d", dbesc(datetime_convert()), @@ -1317,11 +1321,11 @@ class Item extends Controller { ); } } - + // photo comments turn the corresponding item visible to the profile wall // This way we don't see every picture in your new photo album posted to your wall at once. // They will show up as people comment on them. - + if(intval($parent_item['item_hidden'])) { $r = q("UPDATE item SET item_hidden = 0 WHERE id = %d", intval($parent_item['id']) @@ -1337,8 +1341,8 @@ class Item extends Controller { return ( [ 'success' => false, 'message' => 'system error' ] ); killme(); } - - if(($parent == $post_id) || ($datarray['item_private'] == 1)) { + + if($parent || $datarray['item_private'] == 1) { $r = q("select * from item where id = %d", intval($post_id) ); @@ -1348,10 +1352,10 @@ class Item extends Controller { Libsync::build_sync_packet($profile_uid,array('item' => array(encode_item($sync_item[0],true)))); } } - + $datarray['id'] = $post_id; $datarray['llink'] = z_root() . '/display/' . gen_link_id($datarray['mid']); - + call_hooks('post_local_end', $datarray); if ($groupww) { @@ -1360,19 +1364,23 @@ class Item extends Controller { if(! $nopush) Master::Summon([ 'Notifier', $notify_type, $post_id ]); - + logger('post_complete'); if($moderated) { info(t('Your comment is awaiting approval.') . EOL); } - + // figure out how to return, depending on from whence we came - + if($api_source) return $post; - + if($return_path) { + if($return_path === 'hq') { + goaway(z_root() . '/hq/' . gen_link_id($datarray['mid'])); + } + goaway(z_root() . "/" . $return_path); } @@ -1382,7 +1390,7 @@ class Item extends Controller { $item[] = $datarray; $item[0]['owner'] = $owner_xchan; $item[0]['author'] = $observer; - $item[0]['attach'] = json_encode($datarray['attach']); + $item[0]['attach'] = $datarray['attach']; $json = [ 'success' => 1, @@ -1392,29 +1400,29 @@ class Item extends Controller { if(x($_REQUEST,'jsreload') && strlen($_REQUEST['jsreload'])) $json['reload'] = z_root() . '/' . $_REQUEST['jsreload']; - + logger('post_json: ' . print_r($json,true), LOGGER_DEBUG); - + echo json_encode($json); killme(); // NOTREACHED } - - + + function get() { - + if((! local_channel()) && (! remote_channel())) return; - + if((argc() == 3) && (argv(1) === 'drop') && intval(argv(2))) { - + require_once('include/items.php'); $i = q("select id, uid, item_origin, author_xchan, owner_xchan, source_xchan, item_type from item where id = %d limit 1", intval(argv(2)) ); - + if($i) { $can_delete = false; $local_delete = false; @@ -1422,14 +1430,14 @@ class Item extends Controller { if(local_channel() && local_channel() == $i[0]['uid']) { $local_delete = true; } - + $ob_hash = get_observer_hash(); if($ob_hash && ($ob_hash === $i[0]['author_xchan'] || $ob_hash === $i[0]['owner_xchan'] || $ob_hash === $i[0]['source_xchan'])) { $can_delete = true; } // The site admin can delete any post/item on the site. - // If the item originated on this site+channel the deletion will propagate downstream. + // If the item originated on this site+channel the deletion will propagate downstream. // Otherwise just the local copy is removed. if(is_site_admin()) { @@ -1443,11 +1451,11 @@ class Item extends Controller { notice( t('Permission denied.') . EOL); return; } - + // if this is a different page type or it's just a local delete // but not by the item author or owner, do a simple deletion - $complex = false; + $complex = false; if(intval($i[0]['item_type']) || ($local_delete && (! $can_delete))) { drop_item($i[0]['id']); @@ -1473,15 +1481,15 @@ class Item extends Controller { } } } - - - + + + function item_check_service_class($channel_id,$iswebpage) { $ret = array('success' => false, 'message' => ''); - + if ($iswebpage) { - $r = q("select count(i.id) as total from item i - right join channel c on (i.author_xchan=c.channel_hash and i.uid=c.channel_id ) + $r = q("select count(i.id) as total from item i + right join channel c on (i.author_xchan=c.channel_hash and i.uid=c.channel_id ) and i.parent=i.id and i.item_type = %d and i.item_deleted = 0 and i.uid= %d ", intval(ITEM_TYPE_WEBPAGE), intval($channel_id) @@ -1492,11 +1500,11 @@ class Item extends Controller { intval($channel_id) ); } - + if(! $r) { $ret['message'] = t('Unable to obtain post information from database.'); return $ret; - } + } if (!$iswebpage) { $max = engr_units_to_bytes(service_class_fetch($channel_id,'total_items')); @@ -1510,13 +1518,13 @@ class Item extends Controller { if(! service_class_allows($channel_id,'total_pages',$r[0]['total'])) { $result['message'] .= upgrade_message() . sprintf( t('You have reached your limit of %1$.0f webpages.'),$max); return $result; - } + } } - + $ret['success'] = true; return $ret; } - + function extract_bb_poll_data(&$body,$item) { $multiple = false; @@ -1550,7 +1558,7 @@ class Item extends Controller { } $matches = null; - + if (preg_match('/\[question=multiple\](.*?)\[\/question\]/ism',$body,$matches)) { $obj['content'] = bbcode($matches[1]); $body = str_replace('[question=multiple]' . $matches[1] . '[/question]', $matches[1], $body); @@ -1558,7 +1566,7 @@ class Item extends Controller { } $matches = null; - + if (preg_match('/\[ends\](.*?)\[\/ends\]/ism',$body,$matches)) { $obj['endTime'] = datetime_convert(date_default_timezone_get(),'UTC', $matches[1],ATOM_TIME); $body = str_replace('[ends]' . $matches[1] . '[/ends]', EMPTY_STR, $body); diff --git a/Zotlabs/Module/Lang.php b/Zotlabs/Module/Lang.php index a32f933a6..fe185ebea 100644 --- a/Zotlabs/Module/Lang.php +++ b/Zotlabs/Module/Lang.php @@ -7,16 +7,60 @@ use Zotlabs\Web\Controller; class Lang extends Controller { + const MYP = 'ZIN'; + const VERSION = '2.0.0'; + + function post() { + + $re = []; + $isajax = is_ajax(); + $eol = $isajax ? "\n" : EOL; + + if (! Apps::system_app_installed(local_channel(), 'Language')) { + $re['msg'] = 'ZIN0202E, ' . t('Language App') . ' (' . t('Not Installed') . ')' ; + notice( $re['msg'] . EOL); + if ($isajax) { + echo json_encode( $re ); + killme(); + exit; + } else { + return; + } + } + + $lc = x($_POST['zinlc']) && preg_match('/^\?\?|[a-z]{2,2}[x_\-]{0,1}[a-zA-Z]{0,2}$/', $_POST['zinlc']) + ? $_POST['zinlc'] : ''; + $lcs= x($_POST['zinlcs']) && preg_match('/^[a-z,_\-]{0,191}$/', $_POST['zinlcs']) + ? $_POST['zinlcs'] : ''; + + if ($isajax) { + + if ($lc == '??') { + $re['lc'] = get_best_language(); + $re['lcs'] = language_list(); + } else { + $re['lc'] = $lc; + $re['alc'] = App::$language; + $re['slc'] = $_SESSION['language']; + $_SESSION['language'] = $lc; + App::$language = $lc; + load_translation_table($lc, true); + } + + echo json_encode( $re ); + killme(); + exit; + } + } + function get() { if(local_channel()) { if(! Apps::system_app_installed(local_channel(), 'Language')) { - //Do not display any associated widgets at this point - App::$pdl = ''; - - $o = '<b>' . t('Language App') . ' (' . t('Not Installed') . '):</b><br>'; - $o .= t('Change UI language'); - return $o; + //Do not display any associated widgets at this point + App::$pdl = ''; + $papp = Apps::get_papp('Language'); + return Apps::app_render($papp, 'module'); } } @@ -24,5 +68,5 @@ class Lang extends Controller { return lang_selector(); } - + } diff --git a/Zotlabs/Module/Like.php b/Zotlabs/Module/Like.php index bb5c6db7a..e3fe4a954 100644 --- a/Zotlabs/Module/Like.php +++ b/Zotlabs/Module/Like.php @@ -1,37 +1,42 @@ <?php + namespace Zotlabs\Module; +use App; use Zotlabs\Lib\Activity; use Zotlabs\Lib\Libsync; +use Zotlabs\Web\Controller; +use Zotlabs\Daemon\Master; + require_once('include/security.php'); require_once('include/bbcode.php'); require_once('include/items.php'); require_once('include/conversation.php'); -class Like extends \Zotlabs\Web\Controller { +class Like extends Controller { private function reaction_to_activity($reaction) { $acts = [ - 'like' => ACTIVITY_LIKE , - 'dislike' => ACTIVITY_DISLIKE , - 'agree' => ACTIVITY_AGREE , - 'disagree' => ACTIVITY_DISAGREE , - 'abstain' => ACTIVITY_ABSTAIN , - 'attendyes' => ACTIVITY_ATTEND , - 'attendno' => ACTIVITY_ATTENDNO , - 'attendmaybe' => ACTIVITY_ATTENDMAYBE + 'like' => ACTIVITY_LIKE, + 'dislike' => ACTIVITY_DISLIKE, + 'agree' => ACTIVITY_AGREE, + 'disagree' => ACTIVITY_DISAGREE, + 'abstain' => ACTIVITY_ABSTAIN, + 'attendyes' => ACTIVITY_ATTEND, + 'attendno' => ACTIVITY_ATTENDNO, + 'attendmaybe' => ACTIVITY_ATTENDMAYBE ]; // unlike (etc.) reactions are an undo of positive reactions, rather than a negative action. // The activity is the same in undo actions and will have the same activity mapping - if(substr($reaction,0,2) === 'un') { - $reaction = substr($reaction,2); + if (substr($reaction, 0, 2) === 'un') { + $reaction = substr($reaction, 2); } - if(array_key_exists($reaction,$acts)) { + if (array_key_exists($reaction, $acts)) { return $acts[$reaction]; } @@ -41,117 +46,124 @@ class Like extends \Zotlabs\Web\Controller { private function like_response($arr) { - if($arr['conv_mode'] === 'channel') { + $page_mode = (($arr['item']['item_thread_top'] && $_REQUEST['page_mode']) ? $_REQUEST['page_mode'] : 'r_preview'); + $conv_mode = (($_REQUEST['conv_mode']) ? $_REQUEST['conv_mode'] : 'network'); + + if ($conv_mode === 'channel') { $parts = explode('@', $arr['owner_xchan']['xchan_addr']); profile_load($parts[0]); } - $item_normal = item_normal(); - $activities = q("SELECT item.*, item.id AS item_id FROM item - WHERE uid = %d $item_normal - AND thr_parent = '%s' - AND verb IN ('%s', '%s', '%s', '%s', '%s')", - intval($arr['item']['uid']), - dbesc($arr['item']['mid']), - dbesc(ACTIVITY_LIKE), - dbesc(ACTIVITY_DISLIKE), - dbesc(ACTIVITY_ATTEND), - dbesc(ACTIVITY_ATTENDNO), - dbesc(ACTIVITY_ATTENDMAYBE) - ); - - xchan_query($activities,true); - - $convitems[] = $arr['item']; - $convitems = array_merge($convitems, $activities); - - $convitems = fetch_post_tags($convitems,true); + if ($page_mode === 'list') { + $items = q("SELECT item.*, item.id AS item_id FROM item + WHERE uid = %d $item_normal + AND parent = %d", + intval($arr['item']['uid']), + intval($arr['item']['parent']) + ); + xchan_query($items, true); + $items = fetch_post_tags($items, true); + $items = conv_sort($items, 'commented'); + } + else { + $activities = q("SELECT item.*, item.id AS item_id FROM item + WHERE uid = %d $item_normal + AND thr_parent = '%s' + AND verb IN ('%s', '%s', '%s', '%s', '%s')", + intval($arr['item']['uid']), + dbesc($arr['item']['mid']), + dbesc(ACTIVITY_LIKE), + dbesc(ACTIVITY_DISLIKE), + dbesc(ACTIVITY_ATTEND), + dbesc(ACTIVITY_ATTENDNO), + dbesc(ACTIVITY_ATTENDMAYBE) + ); + xchan_query($activities, true); + $items = array_merge([$arr['item']], $activities); + $items = fetch_post_tags($items, true); + } $ret = [ 'success' => 1, 'orig_id' => $arr['orig_item_id'], //this is required for pubstream items where $item_id != $item['id'] - 'id' => $arr['item']['id'], - 'html' => conversation($convitems, $arr['conv_mode'], true, 'r_preview'), + 'id' => $arr['item']['id'], + 'html' => conversation($items, $conv_mode, true, $page_mode), ]; - return $ret; } public function get() { - - $o = EMPTY_STR; + $o = EMPTY_STR; $sys_channel = get_sys_channel(); - $sys_channel_id = (($sys_channel) ? $sys_channel['channel_id'] : 0); - - $observer = \App::get_observer(); + $observer = App::get_observer(); $interactive = $_REQUEST['interactive']; - if((! $observer) || ($interactive)) { + + if ((!$observer) || ($interactive)) { $o .= '<h1>' . t('Like/Dislike') . '</h1>'; $o .= EOL . EOL; - - if(! $observer) { - $_SESSION['return_url'] = \App::$query_string; + + if (!$observer) { + $_SESSION['return_url'] = App::$query_string; + $o .= t('This action is restricted to members.') . EOL; $o .= t('Please <a href="rmagic">login with your $Projectname ID</a> or <a href="register">register as a new $Projectname member</a> to continue.') . EOL; return $o; } } - + $verb = notags(trim($_GET['verb'])); - $mode = (($_GET['conv_mode'] === 'channel') ? 'channel' : 'network'); - if(! $verb) + if (!$verb) $verb = 'like'; - + $activity = $this->reaction_to_activity($verb); - if(! $activity) { - return EMPTY_STR; + if (!$activity) { + return EMPTY_STR; } $is_rsvp = false; - if (in_array($activity, [ ACTIVITY_ATTEND, ACTIVITY_ATTENDNO, ACTIVITY_ATTENDMAYBE ])) { + if (in_array($activity, [ACTIVITY_ATTEND, ACTIVITY_ATTENDNO, ACTIVITY_ATTENDMAYBE])) { $is_rsvp = true; } - $extended_like = false; - $object = $target = null; - $post_type = EMPTY_STR; - $objtype = EMPTY_STR; - - if(argc() == 3) { - - if(! $observer) + $object = $target = null; + $post_type = EMPTY_STR; + $objtype = EMPTY_STR; + + if (argc() == 3) { + + if (!$observer) killme(); - + $extended_like = true; - $obj_type = argv(1); - $obj_id = argv(2); - $public = true; - - if($obj_type == 'profile') { + $obj_type = argv(1); + $obj_id = argv(2); + $public = true; + + if ($obj_type == 'profile') { $r = q("select * from profile where profile_guid = '%s' limit 1", dbesc(argv(2)) ); - if(! $r) - killme(); + if (!$r) + killme(); $owner_uid = $r[0]['uid']; - if($r[0]['is_default']) + if ($r[0]['is_default']) $public = true; - if(! $public) { + if (!$public) { $d = q("select abook_xchan from abook where abook_profile = '%s' and abook_channel = %d", dbesc($r[0]['profile_guid']), intval($owner_uid) ); - if(! $d) { + if (!$d) { // forgery - illegal - if($interactive) { - notice( t('Invalid request.') . EOL); + if ($interactive) { + notice(t('Invalid request.') . EOL); return $o; } killme(); @@ -159,122 +171,122 @@ class Like extends \Zotlabs\Web\Controller { // $d now contains a list of those who can see this profile - only send the status notification // to them. $allow_cid = $allow_gid = $deny_cid = $deny_gid = ''; - foreach($d as $dd) { + foreach ($d as $dd) { $allow_cid .= '<' . $dd['abook_xchan'] . '>'; } } $post_type = t('channel'); - $objtype = ACTIVITY_OBJ_PROFILE; - + $objtype = ACTIVITY_OBJ_PROFILE; + $profile = $r[0]; } - elseif($obj_type == 'thing') { - + elseif ($obj_type == 'thing') { + $r = q("select * from obj where obj_type = %d and obj_obj = '%s' limit 1", - intval(TERM_OBJ_THING), - dbesc(argv(2)) - ); - - if(! $r) { - if($interactive) { - notice( t('Invalid request.') . EOL); + intval(TERM_OBJ_THING), + dbesc(argv(2)) + ); + + if (!$r) { + if ($interactive) { + notice(t('Invalid request.') . EOL); return $o; } - killme(); + killme(); } - + $owner_uid = $r[0]['obj_channel']; - + $allow_cid = $r[0]['allow_cid']; $allow_gid = $r[0]['allow_gid']; - $deny_cid = $r[0]['deny_cid']; - $deny_gid = $r[0]['deny_gid']; - if($allow_cid || $allow_gid || $deny_cid || $deny_gid) + $deny_cid = $r[0]['deny_cid']; + $deny_gid = $r[0]['deny_gid']; + if ($allow_cid || $allow_gid || $deny_cid || $deny_gid) $public = false; - + $post_type = t('thing'); - $objtype = ACTIVITY_OBJ_PROFILE; - $tgttype = ACTIVITY_OBJ_THING; - + $objtype = ACTIVITY_OBJ_PROFILE; + $tgttype = ACTIVITY_OBJ_THING; + $links = array(); - $links[] = array('rel' => 'alternate', 'type' => 'text/html', - 'href' => z_root() . '/thing/' . $r[0]['obj_obj']); - if($r[0]['imgurl']) + $links[] = array('rel' => 'alternate', 'type' => 'text/html', + 'href' => z_root() . '/thing/' . $r[0]['obj_obj']); + if ($r[0]['imgurl']) $links[] = array('rel' => 'photo', 'href' => $r[0]['obj_imgurl']); - + $target = json_encode(array( 'type' => $tgttype, 'title' => $r[0]['obj_term'], 'id' => z_root() . '/thing/' . $r[0]['obj_obj'], 'link' => $links )); - + $plink = '[zrl=' . z_root() . '/thing/' . $r[0]['obj_obj'] . ']' . $r[0]['obj_term'] . '[/zrl]'; - + } - - if(! ($owner_uid && $r)) { - if($interactive) { - notice( t('Invalid request.') . EOL); + + if (!($owner_uid && $r)) { + if ($interactive) { + notice(t('Invalid request.') . EOL); return $o; } killme(); } - + // The resultant activity is going to be a wall-to-wall post, so make sure this is allowed - - $perms = get_all_perms($owner_uid,$observer['xchan_hash']); - - if(! ($perms['post_like'] && $perms['view_profile'])) { - if($interactive) { - notice( t('Permission denied.') . EOL); + + $perms = get_all_perms($owner_uid, $observer['xchan_hash']); + + if (!($perms['post_like'] && $perms['view_profile'])) { + if ($interactive) { + notice(t('Permission denied.') . EOL); return $o; } killme(); } - + $ch = q("select * from channel left join xchan on channel_hash = xchan_hash where channel_id = %d limit 1", intval($owner_uid) ); - if(! $ch) { - if($interactive) { - notice( t('Channel unavailable.') . EOL); + if (!$ch) { + if ($interactive) { + notice(t('Channel unavailable.') . EOL); return $o; } killme(); } - - if(! $plink) + + if (!$plink) $plink = '[zrl=' . z_root() . '/profile/' . $ch[0]['channel_address'] . ']' . $post_type . '[/zrl]'; - - $object = json_encode(Activity::fetch_profile([ 'id' => channel_url($ch[0]) ])); + + $object = json_encode(Activity::fetch_profile(['id' => channel_url($ch[0])])); // second like of the same thing is "undo" for the first like - + $z = q("select * from likes where channel_id = %d and liker = '%s' and verb = '%s' and target_type = '%s' and target_id = '%s' limit 1", intval($ch[0]['channel_id']), dbesc($observer['xchan_hash']), dbesc($activity), - dbesc(($tgttype)?$tgttype:$objtype), + dbesc(($tgttype) ? $tgttype : $objtype), dbesc($obj_id) ); - - if($z) { + + if ($z) { $z[0]['deleted'] = 1; - Libsync::build_sync_packet($ch[0]['channel_id'],array('likes' => $z)); - + Libsync::build_sync_packet($ch[0]['channel_id'], array('likes' => $z)); + q("delete from likes where id = %d", intval($z[0]['id']) ); - if($z[0]['i_mid']) { + if ($z[0]['i_mid']) { $r = q("select id from item where mid = '%s' and uid = %d limit 1", dbesc($z[0]['i_mid']), intval($ch[0]['channel_id']) ); - if($r) - drop_item($r[0]['id'],false); - if($interactive) { - notice( t('Previous action reversed.') . EOL); + if ($r) + drop_item($r[0]['id'], false); + if ($interactive) { + notice(t('Previous action reversed.') . EOL); return $o; } } @@ -283,19 +295,19 @@ class Like extends \Zotlabs\Web\Controller { } else { - if(! $observer) + if (!$observer) killme(); - + // this is used to like an item or comment - + $item_id = ((argc() == 2) ? notags(trim(argv(1))) : 0); - + logger('like: verb ' . $verb . ' item ' . $item_id, LOGGER_DEBUG); - + // get the item. Allow linked photos (which are normally hidden) to be liked - $r = q("SELECT * FROM item WHERE id = %d - and item_type in (0,6,7) and item_deleted = 0 and item_unpublished = 0 + $r = q("SELECT * FROM item WHERE id = %d + and item_type in (0,6,7) and item_deleted = 0 and item_unpublished = 0 and item_delayed = 0 and item_pending_remove = 0 and item_blocked = 0 LIMIT 1", intval($item_id) ); @@ -304,32 +316,30 @@ class Like extends \Zotlabs\Web\Controller { // create a copy of the parent in your stream. If not the conversation // parent, copy that as well. - if($r) { - if($r[0]['uid'] === $sys_channel['channel_id'] && local_channel()) { - $r = [ copy_of_pubitem(\App::get_channel(), $r[0]['mid']) ]; + if ($r) { + if ($r[0]['uid'] === $sys_channel['channel_id'] && local_channel()) { + $r = [copy_of_pubitem(App::get_channel(), $r[0]['mid'])]; } } - if(! $item_id || (! $r)) { + if (!$item_id || (!$r)) { logger('like: no item ' . $item_id); killme(); } - xchan_query($r,true); - - $item = $r[0]; + xchan_query($r, true); + $item = $r[0]; $owner_uid = $r[0]['uid']; $owner_aid = $r[0]['aid']; - $can_comment = false; - if((array_key_exists('owner',$item)) && intval($item['owner']['abook_self'])) - $can_comment = perm_is_allowed($item['uid'],$observer['xchan_hash'],'post_comments'); - else - $can_comment = can_comment_on_post($observer['xchan_hash'],$item); + if ((array_key_exists('owner', $item)) && intval($item['owner']['abook_self'])) + $can_comment = perm_is_allowed($item['uid'], $observer['xchan_hash'], 'post_comments'); + else + $can_comment = can_comment_on_post($observer['xchan_hash'], $item); - if(! $can_comment) { - notice( t('Permission denied') . EOL); + if (!$can_comment) { + notice(t('Permission denied') . EOL); killme(); } @@ -337,7 +347,7 @@ class Like extends \Zotlabs\Web\Controller { dbesc($item['owner_xchan']) ); - if($r) + if ($r) $thread_owner = $r[0]; else killme(); @@ -345,223 +355,207 @@ class Like extends \Zotlabs\Web\Controller { $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($item['author_xchan']) ); - if($r) + if ($r) $item_author = $r[0]; else killme(); - $verbs = " '".dbesc($activity)."' "; - - $multi_undo = false; - + $verbs = " '" . dbesc($activity) . "' "; + + $multi_undo = false; + // event participation and consensus items are essentially radio toggles. If you make a subsequent choice, - // we need to eradicate your first choice. - - if($activity === ACTIVITY_ATTEND || $activity === ACTIVITY_ATTENDNO || $activity === ACTIVITY_ATTENDMAYBE) { - $verbs = " '" . dbesc(ACTIVITY_ATTEND) . "','" . dbesc(ACTIVITY_ATTENDNO) . "','" . dbesc(ACTIVITY_ATTENDMAYBE) . "' "; + // we need to eradicate your first choice. + + if ($activity === ACTIVITY_ATTEND || $activity === ACTIVITY_ATTENDNO || $activity === ACTIVITY_ATTENDMAYBE) { + $verbs = " '" . dbesc(ACTIVITY_ATTEND) . "','" . dbesc(ACTIVITY_ATTENDNO) . "','" . dbesc(ACTIVITY_ATTENDMAYBE) . "' "; $multi_undo = 1; } - if($activity === ACTIVITY_AGREE || $activity === ACTIVITY_DISAGREE || $activity === ACTIVITY_ABSTAIN) { - $verbs = " '" . dbesc(ACTIVITY_AGREE) . "','" . dbesc(ACTIVITY_DISAGREE) . "','" . dbesc(ACTIVITY_ABSTAIN) . "' "; + if ($activity === ACTIVITY_AGREE || $activity === ACTIVITY_DISAGREE || $activity === ACTIVITY_ABSTAIN) { + $verbs = " '" . dbesc(ACTIVITY_AGREE) . "','" . dbesc(ACTIVITY_DISAGREE) . "','" . dbesc(ACTIVITY_ABSTAIN) . "' "; $multi_undo = true; } - + $item_normal = item_normal(); - + $r = q("SELECT id, parent, uid, verb FROM item WHERE verb in ( $verbs ) $item_normal AND author_xchan = '%s' AND thr_parent = '%s' and uid = %d ", dbesc($observer['xchan_hash']), dbesc($item['mid']), intval($owner_uid) ); - - if($r) { + + if ($r) { // already liked it. Drop that item. require_once('include/items.php'); - foreach($r as $rr) { - drop_item($rr['id'],false,DROPITEM_PHASE1); + foreach ($r as $rr) { + drop_item($rr['id'], false, DROPITEM_PHASE1); // set the changed timestamp on the parent so we'll see the update without a page reload - $z = q("update item set changed = '%s' where id = %d and uid = %d", + q("update item set changed = '%s' where id = %d and uid = %d", dbesc(datetime_convert()), intval($rr['parent']), intval($rr['uid']) ); - // Prior activity was a duplicate of the one we're submitting, just undo it; + // Prior activity was a duplicate of the one we're submitting, just undo it; // don't fall through and create another - if(activity_match($rr['verb'],$activity)) + if (activity_match($rr['verb'], $activity)) $multi_undo = false; - + // drop_item was not done interactively, so we need to invoke the notifier // in order to push the changes to connections - \Zotlabs\Daemon\Master::Summon(array('Notifier','drop',$rr['id'])); + Master::Summon(array('Notifier', 'drop', $rr['id'])); - } - - if($interactive) + + if ($interactive) return; - - if(! $multi_undo) { + + if (!$multi_undo) { $ret = self::like_response([ - 'item' => $item, - 'orig_item_id' => $item_id, - 'owner_xchan' => $thread_owner, - 'conv_mode' => $mode + 'item' => $item, + 'orig_item_id' => $item_id, + 'owner_xchan' => $thread_owner ]); json_return_and_die($ret); } - - } } - + $uuid = item_message_id(); - + $arr = array(); - - $arr['uuid'] = $uuid; - $arr['mid'] = z_root() . (($is_rsvp) ? '/activity/' : '/item/') . $uuid; - if($extended_like) { + $arr['uuid'] = $uuid; + $arr['mid'] = z_root() . (($is_rsvp) ? '/activity/' : '/item/') . $uuid; + + if ($extended_like) { $arr['item_thread_top'] = 1; - $arr['item_origin'] = 1; - $arr['item_wall'] = 1; + $arr['item_origin'] = 1; + $arr['item_wall'] = 1; } else { $post_type = (($item['resource_type'] === 'photo') ? t('photo') : t('status')); - if($item['obj_type'] === ACTIVITY_OBJ_EVENT) + if ($item['obj_type'] === ACTIVITY_OBJ_EVENT) $post_type = t('event'); - - $links = array(array('rel' => 'alternate','type' => 'text/html', 'href' => $item['plink'])); - $objtype = (($item['resource_type'] === 'photo') ? ACTIVITY_OBJ_PHOTO : ACTIVITY_OBJ_NOTE ); - if($objtype === ACTIVITY_OBJ_NOTE && (! intval($item['item_thread_top']))) + $objtype = (($item['resource_type'] === 'photo') ? ACTIVITY_OBJ_PHOTO : ACTIVITY_OBJ_NOTE); + + if ($objtype === ACTIVITY_OBJ_NOTE && (!intval($item['item_thread_top']))) $objtype = ACTIVITY_OBJ_COMMENT; - - $body = $item['body']; - - $object = json_encode(Activity::fetch_item( [ 'id' => $item['mid'] ])); + $object = json_encode(Activity::fetch_item(['id' => $item['mid']])); + + if (!intval($item['item_thread_top'])) + $post_type = 'comment'; - if(! intval($item['item_thread_top'])) - $post_type = 'comment'; - - $arr['item_origin'] = 1; + $arr['item_origin'] = 1; $arr['item_notshown'] = 1; - $arr['item_type'] = $item['item_type']; - - if(intval($item['item_wall'])) + $arr['item_type'] = $item['item_type']; + + if (intval($item['item_wall'])) $arr['item_wall'] = 1; - + // if this was a linked photo and was hidden, unhide it. - - if(intval($item['item_hidden'])) { + + if (intval($item['item_hidden'])) { $r = q("update item set item_hidden = 0 where id = %d", intval($item['id']) ); - } - + } + } - - if($verb === 'like') + + if ($verb === 'like') $bodyverb = t('%1$s likes %2$s\'s %3$s'); - if($verb === 'dislike') + if ($verb === 'dislike') $bodyverb = t('%1$s doesn\'t like %2$s\'s %3$s'); - if($verb === 'agree') + if ($verb === 'agree') $bodyverb = t('%1$s agrees with %2$s\'s %3$s'); - if($verb === 'disagree') + if ($verb === 'disagree') $bodyverb = t('%1$s doesn\'t agree with %2$s\'s %3$s'); - if($verb === 'abstain') + if ($verb === 'abstain') $bodyverb = t('%1$s abstains from a decision on %2$s\'s %3$s'); - if($verb === 'attendyes') + if ($verb === 'attendyes') $bodyverb = t('%1$s is attending %2$s\'s %3$s'); - if($verb === 'attendno') + if ($verb === 'attendno') $bodyverb = t('%1$s is not attending %2$s\'s %3$s'); - if($verb === 'attendmaybe') + if ($verb === 'attendmaybe') $bodyverb = t('%1$s may attend %2$s\'s %3$s'); - - if(! isset($bodyverb)) - killme(); - - - - if($extended_like) { - $ulink = '[zrl=' . $ch[0]['xchan_url'] . '][bdi]' . $ch[0]['xchan_name'] . '[/bdi][/zrl]'; - $alink = '[zrl=' . $observer['xchan_url'] . '][bdi]' . $observer['xchan_name'] . '[/bdi][/zrl]'; + + if (!isset($bodyverb)) + killme(); + + if ($extended_like) { + $ulink = '[zrl=' . $ch[0]['xchan_url'] . '][bdi]' . $ch[0]['xchan_name'] . '[/bdi][/zrl]'; + $alink = '[zrl=' . $observer['xchan_url'] . '][bdi]' . $observer['xchan_name'] . '[/bdi][/zrl]'; $private = (($public) ? 0 : 1); } else { - $arr['parent'] = $item['id']; - $arr['thr_parent'] = $item['mid']; - $ulink = '[zrl=' . $item_author['xchan_url'] . '][bdi]' . $item_author['xchan_name'] . '[/bdi][/zrl]'; - $alink = '[zrl=' . $observer['xchan_url'] . '][bdi]' . $observer['xchan_name'] . '[/bdi][/zrl]'; - $plink = '[zrl=' . z_root() . '/display/' . gen_link_id($item['mid']) . ']' . $post_type . '[/zrl]'; - $allow_cid = $item['allow_cid']; - $allow_gid = $item['allow_gid']; - $deny_cid = $item['deny_cid']; - $deny_gid = $item['deny_gid']; - $private = $item['private']; - + $arr['parent'] = $item['id']; + $arr['thr_parent'] = $item['mid']; + $ulink = '[zrl=' . $item_author['xchan_url'] . '][bdi]' . $item_author['xchan_name'] . '[/bdi][/zrl]'; + $alink = '[zrl=' . $observer['xchan_url'] . '][bdi]' . $observer['xchan_name'] . '[/bdi][/zrl]'; + $plink = '[zrl=' . z_root() . '/display/' . gen_link_id($item['mid']) . ']' . $post_type . '[/zrl]'; + $allow_cid = $item['allow_cid']; + $allow_gid = $item['allow_gid']; + $deny_cid = $item['deny_cid']; + $deny_gid = $item['deny_gid']; + $private = $item['private']; + } - - + $arr['aid'] = (($extended_like) ? $ch[0]['channel_account_id'] : $owner_aid); $arr['uid'] = $owner_uid; - - - $arr['item_flags'] = $item_flags; - $arr['item_wall'] = $item_wall; + $arr['item_flags'] = $item['item_flags']; + $arr['item_wall'] = $item['item_wall']; $arr['parent_mid'] = (($extended_like) ? $arr['mid'] : $item['mid']); $arr['owner_xchan'] = (($extended_like) ? $ch[0]['xchan_hash'] : $thread_owner['xchan_hash']); $arr['author_xchan'] = $observer['xchan_hash']; - - - $arr['body'] = sprintf( $bodyverb, $alink, $ulink, $plink ); - if($obj_type === 'thing' && $r[0]['imgurl']) { + $arr['body'] = sprintf($bodyverb, $alink, $ulink, $plink); + + if ($obj_type === 'thing' && $r[0]['imgurl']) { $arr['body'] .= "\n\n[zmg=80x80]" . $r[0]['imgurl'] . '[/zmg]'; - } - if($obj_type === 'profile') { - if($public) { - $arr['body'] .= "\n\n" . '[embed]' . z_root() . '/profile/' . $ch[0]['channel_address'] . '[/embed]'; + } + if ($obj_type === 'profile') { + if ($public) { + $arr['body'] .= "\n\n" . '[embed]' . z_root() . '/profile/' . $ch[0]['channel_address'] . '[/embed]'; } else $arr['body'] .= "\n\n[zmg=80x80]" . $profile['thumb'] . '[/zmg]'; - } - - - $arr['verb'] = $activity; - $arr['obj_type'] = $objtype; - $arr['obj'] = $object; - - if($target) { - $arr['tgt_type'] = $tgttype; - $arr['target'] = $target; } - - $arr['allow_cid'] = $allow_cid; - $arr['allow_gid'] = $allow_gid; - $arr['deny_cid'] = $deny_cid; - $arr['deny_gid'] = $deny_gid; - $arr['item_private'] = $private; - - call_hooks('post_local',$arr); - - $post = item_store($arr); + + $arr['verb'] = $activity; + $arr['obj_type'] = $objtype; + $arr['obj'] = $object; + + if ($target) { + $arr['tgt_type'] = $tgttype; + $arr['target'] = $target; + } + + $arr['allow_cid'] = $allow_cid; + $arr['allow_gid'] = $allow_gid; + $arr['deny_cid'] = $deny_cid; + $arr['deny_gid'] = $deny_gid; + $arr['item_private'] = $private; + + call_hooks('post_local', $arr); + + $post = item_store($arr); $post_id = $post['item_id']; // save the conversation from expiration - if(local_channel() && array_key_exists('item',$post) && (intval($post['item']['id']) != intval($post['item']['parent']))) - retain_item($post['item']['parent']); - + if (local_channel() && array_key_exists('item', $post) && (intval($post['item']['id']) != intval($post['item']['parent']))) + retain_item($post['item']['parent']); + $arr['id'] = $post_id; - + call_hooks('post_local_end', $arr); - - - if($extended_like) { + + if ($extended_like) { $r = q("insert into likes (channel_id,liker,likee,iid,i_mid,verb,target_type,target_id,target) values (%d,'%s','%s',%d,'%s','%s','%s','%s','%s')", intval($ch[0]['channel_id']), dbesc($observer['xchan_hash']), @@ -569,39 +563,38 @@ class Like extends \Zotlabs\Web\Controller { intval($post_id), dbesc($arr['mid']), dbesc($activity), - dbesc(($tgttype)? $tgttype : $objtype), + dbesc(($tgttype) ? $tgttype : $objtype), dbesc($obj_id), - dbesc(($target) ? $target : $object) + dbesc(($target) ? $target : $object) ); $r = q("select * from likes where liker = '%s' and likee = '%s' and i_mid = '%s' and verb = '%s' and target_type = '%s' and target_id = '%s' ", dbesc($observer['xchan_hash']), dbesc($ch[0]['channel_hash']), dbesc($arr['mid']), dbesc($activity), - dbesc(($tgttype)? $tgttype : $objtype), + dbesc(($tgttype) ? $tgttype : $objtype), dbesc($obj_id) ); - if($r) - Libsync::build_sync_packet($ch[0]['channel_id'],array('likes' => $r)); - + if ($r) + Libsync::build_sync_packet($ch[0]['channel_id'], array('likes' => $r)); + } - - \Zotlabs\Daemon\Master::Summon(array('Notifier','like',$post_id)); - - if($interactive) { - notice( t('Action completed.') . EOL); + + Master::Summon(array('Notifier', 'like', $post_id)); + + if ($interactive) { + notice(t('Action completed.') . EOL); $o .= t('Thank you.'); return $o; } $ret = self::like_response([ - 'item' => $item, - 'orig_item_id' => $item_id, - 'owner_xchan' => $thread_owner, - 'conv_mode' => $mode + 'item' => $item, + 'orig_item_id' => $item_id, + 'owner_xchan' => $thread_owner ]); json_return_and_die($ret); } - + } diff --git a/Zotlabs/Module/Linkinfo.php b/Zotlabs/Module/Linkinfo.php index 76c679cc5..a05575cb6 100644 --- a/Zotlabs/Module/Linkinfo.php +++ b/Zotlabs/Module/Linkinfo.php @@ -5,37 +5,37 @@ namespace Zotlabs\Module; class Linkinfo extends \Zotlabs\Web\Controller { function get() { - + logger('linkinfo: ' . print_r($_REQUEST,true)); - + $text = null; $str_tags = ''; - $process_oembed = true; - + $process_oembed = true; + $br = "\n"; - + if(x($_GET,'binurl')) $url = trim(hex2bin($_GET['binurl'])); else $url = trim($_GET['url']); - + if(substr($url,0,1) === '!') { $process_oembed = false; $url = substr($url,1); } $url = strip_zids($url); - + if((substr($url,0,1) != '/') && (substr($url,0,4) != 'http')) $url = 'http://' . $url; - - + + if($_GET['title']) $title = strip_tags(trim($_GET['title'])); - + if($_GET['description']) $text = strip_tags(trim($_GET['description'])); - + if($_GET['tags']) { $arr_tags = str_getcsv($_GET['tags']); if(count($arr_tags)) { @@ -43,23 +43,25 @@ class Linkinfo extends \Zotlabs\Web\Controller { $str_tags = $br . implode(' ',$arr_tags) . $br; } } - + logger('linkinfo: ' . $url); - - // Replace plink URL with 'share' tag if possible - preg_match("/(mid=b64\.|display\/|posts\/)([\w\-]+)(&.+)?$/", $url, $mid); - - if (!empty($mid) && $mid[1] == 'mid=b64.') - $mid[2] = base64_decode($mid[2]); - - $r = q("SELECT id FROM item WHERE mid = '%s' AND uid = %d AND item_private = 0 LIMIT 1", - dbesc((empty($mid) ? $url : $mid[2])), - intval(local_channel()) - ); - if ($r) { - echo "[share=" . $r[0]['id'] . "][/share]"; - killme(); - } + + // Replace plink URL with 'share' tag if possible + preg_match("/(mid=b64\.|display\/|posts\/)([\w\-]+)(&.+)?$/", $url, $mid); + + if (!empty($mid)) { + $mid[2] = unpack_link_id($mid[2]); + } + + $r = q("SELECT id FROM item WHERE mid = '%s' AND uid = %d AND item_private = 0 LIMIT 1", + dbesc((empty($mid) ? $url : $mid[2])), + intval(local_channel()) + ); + + if ($r) { + echo "[share=" . $r[0]['id'] . "][/share]"; + killme(); + } $result = z_fetch_url($url,false,0,array('novalidate' => true, 'nobody' => true)); if($result['success']) { @@ -108,13 +110,13 @@ class Linkinfo extends \Zotlabs\Web\Controller { } } } - + $template = $br . '#^[url=%s]%s[/url]%s' . $br; - + $arr = array('url' => $url, 'text' => ''); - + call_hooks('parse_link', $arr); - + if(strlen($arr['text'])) { echo $arr['text']; killme(); @@ -127,28 +129,28 @@ class Linkinfo extends \Zotlabs\Web\Controller { killme(); } } - + if($url && $title && $text) { - + $text = $br . '[quote]' . trim($text) . '[/quote]' . $br; - + $title = str_replace(array("\r","\n"),array('',''),$title); - + $result = sprintf($template,$url,($title) ? $title : $url,$text) . $str_tags; - + logger('linkinfo (unparsed): returns: ' . $result); - + echo $result; killme(); } - + $siteinfo = self::parseurl_getsiteinfo($url); - + // If the site uses this platform, use zrl rather than url so they get zids sent to them by default - + if(is_matrix_url($url)) $template = str_replace('url','zrl',$template); - + if($siteinfo["title"] == "") { echo sprintf($template,$url,$url,'') . $str_tags; killme(); @@ -156,19 +158,19 @@ class Linkinfo extends \Zotlabs\Web\Controller { $text = $siteinfo["text"]; $title = $siteinfo["title"]; } - + $image = ""; if(is_array($siteinfo["images"]) && count($siteinfo["images"])){ /* Execute below code only if image is present in siteinfo */ - + $total_images = 0; $max_images = get_config('system','max_bookmark_images'); if($max_images === false) $max_images = 2; else $max_images = intval($max_images); - + foreach ($siteinfo["images"] as $imagedata) { if ($url) { $image .= sprintf('[url=%s]', $url); @@ -183,57 +185,57 @@ class Linkinfo extends \Zotlabs\Web\Controller { break; } } - + if(strlen($text)) { $text = $br.'[quote]'.trim($text).'[/quote]'.$br ; } - + if($image) { $text = $br.$br.$image.$text; } $title = str_replace(array("\r","\n"),array('',''),$title); - + $result = sprintf($template,$url,($title) ? $title : $url,$text) . $str_tags; - + logger('linkinfo: returns: ' . $result, LOGGER_DEBUG); - + echo trim($result); killme(); - + } - - + + public static function deletexnode(&$doc, $node) { $xpath = new \DomXPath($doc); $list = $xpath->query("//".$node); foreach ($list as $child) $child->parentNode->removeChild($child); } - + public static function completeurl($url, $scheme) { $urlarr = parse_url($url); - + if (isset($urlarr["scheme"])) return($url); - + $schemearr = parse_url($scheme); - + $complete = $schemearr["scheme"]."://".$schemearr["host"]; - + if ($schemearr["port"] != "") $complete .= ":".$schemearr["port"]; - + if(strpos($urlarr['path'],'/') !== 0) $complete .= '/'; - + $complete .= $urlarr["path"]; - + if ($urlarr["query"] != "") $complete .= "?".$urlarr["query"]; - + if ($urlarr["fragment"] != "") $complete .= "#".$urlarr["fragment"]; - + return($complete); } @@ -251,7 +253,7 @@ class Linkinfo extends \Zotlabs\Web\Controller { $p = substr($m,strpos($m,'/')+1); // get the channel to check permissions - + $u = channelx_by_nick($nick); if($u && $p) { @@ -272,18 +274,18 @@ class Linkinfo extends \Zotlabs\Web\Controller { return EMPTY_STR; } - + public static function parseurl_getsiteinfo($url) { $siteinfo = array(); - - + + $result = z_fetch_url($url,false,0,array('novalidate' => true)); if(! $result['success']) return $siteinfo; - + $header = $result['header']; $body = $result['body']; - + // Check codepage in HTTP headers or HTML if not exist $cp = (preg_match('/Content-Type: text\/html; charset=(.+)\r\n/i', $header, $o) ? $o[1] : ''); if(empty($cp)) @@ -291,10 +293,10 @@ class Linkinfo extends \Zotlabs\Web\Controller { $body = mb_convert_encoding($body, 'UTF-8', $cp); $body = mb_convert_encoding($body, 'HTML-ENTITIES', "UTF-8"); - + $doc = new \DOMDocument(); @$doc->loadHTML($body); - + self::deletexnode($doc, 'style'); self::deletexnode($doc, 'script'); self::deletexnode($doc, 'option'); @@ -306,14 +308,14 @@ class Linkinfo extends \Zotlabs\Web\Controller { self::deletexnode($doc, 'h6'); self::deletexnode($doc, 'ol'); self::deletexnode($doc, 'ul'); - + $xpath = new \DomXPath($doc); - + //$list = $xpath->query("head/title"); $list = $xpath->query("//title"); foreach ($list as $node) $siteinfo["title"] = html_entity_decode($node->nodeValue, ENT_QUOTES, "UTF-8"); - + //$list = $xpath->query("head/meta[@name]"); $list = $xpath->query("//meta[@name]"); foreach ($list as $node) { @@ -321,9 +323,9 @@ class Linkinfo extends \Zotlabs\Web\Controller { if ($node->attributes->length) foreach ($node->attributes as $attribute) $attr[$attribute->name] = $attribute->value; - + $attr["content"] = html_entity_decode($attr["content"], ENT_QUOTES, "UTF-8"); - + switch (strtolower($attr["name"])) { case "fulltitle": $siteinfo["title"] = trim($attr["content"]); @@ -365,7 +367,7 @@ class Linkinfo extends \Zotlabs\Web\Controller { break; } } - + //$list = $xpath->query("head/meta[@property]"); $list = $xpath->query("//meta[@property]"); foreach ($list as $node) { @@ -373,9 +375,9 @@ class Linkinfo extends \Zotlabs\Web\Controller { if ($node->attributes->length) foreach ($node->attributes as $attribute) $attr[$attribute->name] = $attribute->value; - + $attr["content"] = html_entity_decode($attr["content"], ENT_QUOTES, "UTF-8"); - + switch (strtolower($attr["property"])) { case "og:image": $siteinfo["image"] = $attr["content"]; @@ -388,7 +390,7 @@ class Linkinfo extends \Zotlabs\Web\Controller { break; } } - + if ($siteinfo["image"] == "") { $list = $xpath->query("//img[@src]"); foreach ($list as $node) { @@ -396,10 +398,10 @@ class Linkinfo extends \Zotlabs\Web\Controller { if ($node->attributes->length) foreach ($node->attributes as $attribute) $attr[$attribute->name] = $attribute->value; - + $src = self::completeurl($attr["src"], $url); $photodata = @getimagesize($src); - + if (($photodata) && ($photodata[0] > 150) and ($photodata[1] > 150)) { if ($photodata[0] > 300) { $photodata[1] = round($photodata[1] * (300 / $photodata[0])); @@ -413,36 +415,36 @@ class Linkinfo extends \Zotlabs\Web\Controller { "width"=>$photodata[0], "height"=>$photodata[1]); } - + } } else { $src = self::completeurl($siteinfo["image"], $url); - + unset($siteinfo["image"]); - + $photodata = @getimagesize($src); - + if (($photodata) && ($photodata[0] > 10) and ($photodata[1] > 10)) $siteinfo["images"][] = array("src"=>$src, "width"=>$photodata[0], "height"=>$photodata[1]); } - + if ($siteinfo["text"] == "") { $text = ""; - + $list = $xpath->query("//div[@class='article']"); foreach ($list as $node) if (strlen($node->nodeValue) > 40) $text .= " ".trim($node->nodeValue); - + if ($text == "") { $list = $xpath->query("//div[@class='content']"); foreach ($list as $node) if (strlen($node->nodeValue) > 40) $text .= " ".trim($node->nodeValue); } - + // If none text was found then take the paragraph content if ($text == "") { $list = $xpath->query("//p"); @@ -450,21 +452,21 @@ class Linkinfo extends \Zotlabs\Web\Controller { if (strlen($node->nodeValue) > 40) $text .= " ".trim($node->nodeValue); } - + if ($text != "") { $text = trim(str_replace(array("\n", "\r"), array(" ", " "), $text)); - + while (strpos($text, " ")) $text = trim(str_replace(" ", " ", $text)); - + $text = substr(html_entity_decode($text, ENT_QUOTES, "UTF-8"), 0, 350); $siteinfo["text"] = rtrim(substr($text, 0, strrpos($text, " ")), "?.,:;!-") . '...'; } } - + return($siteinfo); } - + private static function arr_add_hashes(&$item,$k) { $item = '#' . $item; diff --git a/Zotlabs/Module/Lockview.php b/Zotlabs/Module/Lockview.php index 8c8519c57..11c781df0 100644 --- a/Zotlabs/Module/Lockview.php +++ b/Zotlabs/Module/Lockview.php @@ -19,22 +19,22 @@ class Lockview extends \Zotlabs\Web\Controller { } } } - + $type = ((argc() > 1) ? argv(1) : 0); if (is_numeric($type)) { $item_id = intval($type); $type='item'; - } + } else { $item_id = ((argc() > 2) ? intval(argv(2)) : 0); } - + if(! $item_id) killme(); - + if (! in_array($type, array('item', 'photo', 'attach', 'event', 'menu_item', 'chatroom'))) killme(); - + // we have different naming in in menu_item table and chatroom table switch($type) { case 'menu_item': @@ -47,17 +47,17 @@ class Lockview extends \Zotlabs\Web\Controller { $id = 'id'; break; } - + $r = q("SELECT * FROM %s WHERE $id = %d LIMIT 1", dbesc($type), intval($item_id) ); - + if(! $r) killme(); - + $item = $r[0]; - + //we have different naming in in menu_item table and chatroom table switch($type) { case 'menu_item': @@ -70,37 +70,37 @@ class Lockview extends \Zotlabs\Web\Controller { $uid = $item['uid']; break; } - + if($uid != local_channel()) { echo '<div class="dropdown-item">' . t('Remote privacy information not available.') . '</div>'; killme(); } - - if(intval($item['item_private']) && (! strlen($item['allow_cid'])) && (! strlen($item['allow_gid'])) + + if(intval($item['item_private']) && (! strlen($item['allow_cid'])) && (! strlen($item['allow_gid'])) && (! strlen($item['deny_cid'])) && (! strlen($item['deny_gid']))) { - + // if the post is private, but public_policy is blank ("visible to the internet"), and there aren't any // specific recipients, we're the recipient of a post with "bcc" or targeted recipients; so we'll just show it // as unknown specific recipients. The sender will have the visibility list and will fall through to the // next section. - + echo '<div class="dropdown-item">' . translate_scope((! $item['public_policy']) ? 'specific' : $item['public_policy']) . '</div>'; killme(); } - + $allowed_users = expand_acl($item['allow_cid']); $allowed_groups = expand_acl($item['allow_gid']); $deny_users = expand_acl($item['deny_cid']); $deny_groups = expand_acl($item['deny_gid']); - + $o = '<div class="dropdown-item">' . t('Visible to:') . '</div>'; $l = array(); - + stringify_array_elms($allowed_groups,true); stringify_array_elms($allowed_users,true); stringify_array_elms($deny_groups,true); stringify_array_elms($deny_users,true); - + $profile_groups = []; if($allowed_groups) { @@ -113,24 +113,24 @@ class Lockview extends \Zotlabs\Web\Controller { if(count($profile_groups)) { $r = q("SELECT profile_name FROM profile WHERE profile_guid IN ( " . implode(', ', $profile_groups) . " )"); if($r) - foreach($r as $rr) + foreach($r as $rr) $l[] = '<div class="dropdown-item"><b>' . t('Profile','acl') . ' ' . $rr['profile_name'] . '</b></div>'; } if(count($allowed_groups)) { $r = q("SELECT gname FROM pgrp WHERE hash IN ( " . implode(', ', $allowed_groups) . " )"); if($r) - foreach($r as $rr) + foreach($r as $rr) $l[] = '<div class="dropdown-item"><b>' . $rr['gname'] . '</b></div>'; } if(count($allowed_users)) { $r = q("SELECT xchan_name FROM xchan WHERE xchan_hash IN ( " . implode(', ',$allowed_users) . " )"); if($r) - foreach($r as $rr) + foreach($r as $rr) $l[] = '<div class="dropdown-item">' . $rr['xchan_name'] . '</div>'; if($atokens) { foreach($atokens as $at) { - if(in_array("'" . $at['xchan_hash'] . "'",$allowed_users)) { + if(in_array("'" . $at['xchan_hash'] . "'",$allowed_users)) { $l[] = '<div class="dropdown-item">' . $at['xchan_name'] . '</div>'; } } @@ -149,7 +149,7 @@ class Lockview extends \Zotlabs\Web\Controller { if(count($profile_groups)) { $r = q("SELECT profile_name FROM profile WHERE profile_guid IN ( " . implode(', ', $profile_groups) . " )"); if($r) - foreach($r as $rr) + foreach($r as $rr) $l[] = '<div class="dropdown-item"><b><strike>' . t('Profile','acl') . ' ' . $rr['profile_name'] . '</strike></b></div>'; } @@ -158,18 +158,18 @@ class Lockview extends \Zotlabs\Web\Controller { if(count($deny_groups)) { $r = q("SELECT gname FROM pgrp WHERE hash IN ( " . implode(', ', $deny_groups) . " )"); if($r) - foreach($r as $rr) + foreach($r as $rr) $l[] = '<div class="dropdown-item"><b><strike>' . $rr['gname'] . '</strike></b></div>'; } if(count($deny_users)) { $r = q("SELECT xchan_name FROM xchan WHERE xchan_hash IN ( " . implode(', ', $deny_users) . " )"); if($r) - foreach($r as $rr) + foreach($r as $rr) $l[] = '<div class="dropdown-item"><strike>' . $rr['xchan_name'] . '</strike></div>'; if($atokens) { foreach($atokens as $at) { - if(in_array("'" . $at['xchan_hash'] . "'",$deny_users)) { + if(in_array("'" . $at['xchan_hash'] . "'",$deny_users)) { $l[] = '<div class="dropdown-item"><strike>' . $at['xchan_name'] . '</strike></div>'; } } @@ -177,11 +177,11 @@ class Lockview extends \Zotlabs\Web\Controller { } - + echo $o . implode($l); killme(); - - + + } - + } diff --git a/Zotlabs/Module/Locs.php b/Zotlabs/Module/Locs.php index 2dd359c95..1ece47231 100644 --- a/Zotlabs/Module/Locs.php +++ b/Zotlabs/Module/Locs.php @@ -28,9 +28,8 @@ class Locs extends Controller { return; } - q("UPDATE hubloc SET hubloc_primary = 0 WHERE hubloc_primary = 1 AND (hubloc_hash = '%s' OR hubloc_hash = '%s')", - dbesc($channel['channel_hash']), - dbesc($channel['channel_portable_id']) + q("UPDATE hubloc SET hubloc_primary = 0 WHERE hubloc_primary = 1 AND hubloc_hash = '%s'", + dbesc($channel['channel_hash']) ); q("UPDATE hubloc SET hubloc_primary = 1 WHERE hubloc_id = %d AND hubloc_hash = '%s'", @@ -81,10 +80,9 @@ class Locs extends Controller { } } - q("UPDATE hubloc SET hubloc_deleted = 1 WHERE hubloc_id_url = '%s' AND (hubloc_hash = '%s' OR hubloc_hash = '%s')", + q("UPDATE hubloc SET hubloc_deleted = 1 WHERE hubloc_id_url = '%s' AND hubloc_hash = '%s'", dbesc($r[0]['hubloc_id_url']), - dbesc($channel['channel_hash']), - dbesc($channel['channel_portable_id']) + dbesc($channel['channel_hash']) ); Master::Summon( [ 'Notifier', 'refresh_all', $channel['channel_id'] ] ); return; @@ -118,11 +116,6 @@ class Locs extends Controller { return; } - for($x = 0; $x < count($r); $x ++) { - $r[$x]['primary'] = (intval($r[$x]['hubloc_primary']) ? true : false); - $r[$x]['deleted'] = (intval($r[$x]['hubloc_deleted']) ? true : false); - } - $o = replace_macros(get_markup_template('locmanage.tpl'), array( '$header' => t('Manage Channel Locations'), '$loc' => t('Location'), @@ -134,7 +127,8 @@ class Locs extends Controller { '$sync_text' => t('Please wait several minutes between consecutive operations.'), '$drop_text' => t('When possible, drop a location by logging into that website/hub and removing your channel.'), '$last_resort' => t('Use this form to drop the location if the hub is no longer operating.'), - '$hubs' => $r + '$hubs' => $r, + '$base_url' => z_root() )); return $o; diff --git a/Zotlabs/Module/Magic.php b/Zotlabs/Module/Magic.php index b4372e26d..bfd38d2fa 100644 --- a/Zotlabs/Module/Magic.php +++ b/Zotlabs/Module/Magic.php @@ -40,7 +40,7 @@ class Magic extends Controller { goaway($dest); } - $basepath = $parsed['scheme'] . '://' . $parsed['host'] . (($parsed['port']) ? ':' . $parsed['port'] : ''); + $basepath = $parsed['scheme'] . '://' . $parsed['host'] . (isset($parsed['port']) ? ':' . $parsed['port'] : ''); $owapath = SConfig::get($basepath,'system','openwebauth', $basepath . '/owa'); // This is ready-made for a plugin that provides a blacklist or "ask me" before blindly authenticating. @@ -110,6 +110,7 @@ class Magic extends Controller { $headers['(request-target)'] = 'post ' . '/owa'; $headers = HTTPSig::create_sig($headers,$channel['channel_prvkey'], channel_url($channel),true,'sha512'); + $redirects = 0; $x = z_post_url($owapath,$data,$redirects,[ 'headers' => $headers ]); logger('owa fetch returned: ' . print_r($x,true),LOGGER_DATA); if ($x['success']) { diff --git a/Zotlabs/Module/Manage.php b/Zotlabs/Module/Manage.php index 20d5b0449..3f168c15d 100644 --- a/Zotlabs/Module/Manage.php +++ b/Zotlabs/Module/Manage.php @@ -5,18 +5,18 @@ namespace Zotlabs\Module; class Manage extends \Zotlabs\Web\Controller { function get() { - + if((! get_account_id()) || ($_SESSION['delegate'])) { notice( t('Permission denied.') . EOL); return; } nav_set_selected('Channel Manager', 'settings/manage'); - + require_once('include/security.php'); - + $change_channel = ((argc() > 1) ? intval(argv(1)) : 0); - + if((argc() > 2) && (argv(2) === 'default')) { $r = q("select channel_id from channel where channel_id = %d and channel_account_id = %d limit 1", intval($change_channel), @@ -31,7 +31,7 @@ class Manage extends \Zotlabs\Web\Controller { goaway(z_root() . '/manage'); } - + if($change_channel) { $r = change_channel($change_channel); @@ -45,29 +45,29 @@ class Manage extends \Zotlabs\Web\Controller { } goaway(z_root()); } - + $channels = null; - + $r = q("select channel.*, xchan.* from channel left join xchan on channel.channel_hash = xchan.xchan_hash where channel.channel_account_id = %d and channel_removed = 0 order by channel_name ", intval(get_account_id()) ); - + $account = \App::get_account(); - + if($r && count($r)) { $channels = $r; for($x = 0; $x < count($channels); $x ++) { $channels[$x]['link'] = 'manage/' . intval($channels[$x]['channel_id']); - $channels[$x]['default'] = (($channels[$x]['channel_id'] == $account['account_default_channel']) ? "1" : ''); + $channels[$x]['default'] = (($channels[$x]['channel_id'] == $account['account_default_channel']) ? "1" : ''); $channels[$x]['default_links'] = '1'; - - + + /* this is not currently implemented in the UI and probably should not (performance) $c = q("SELECT id, item_wall FROM item WHERE item_unseen = 1 and uid = %d " . item_normal(), intval($channels[$x]['channel_id']) ); - - if($c) { + + if($c) { foreach ($c as $it) { if(intval($it['item_wall'])) $channels[$x]['home'] ++; @@ -75,25 +75,16 @@ class Manage extends \Zotlabs\Web\Controller { $channels[$x]['network'] ++; } } - - + */ + $intr = q("SELECT COUNT(abook.abook_id) AS total FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash where abook_channel = %d and abook_pending = 1 and abook_self = 0 and abook_ignored = 0 and xchan_deleted = 0 and xchan_orphan = 0 ", intval($channels[$x]['channel_id']) ); - + if($intr) $channels[$x]['intros'] = intval($intr[0]['total']); - - - $mails = q("SELECT count(id) as total from mail WHERE channel_id = %d AND mail_seen = 0 and from_xchan != '%s' ", - intval($channels[$x]['channel_id']), - dbesc($channels[$x]['channel_hash']) - ); - - if($mails) - $channels[$x]['mail'] = intval($mails[0]['total']); - - + + /* this is not currently implemented in the UI and probably should not (performance) $events = q("SELECT etype, dtstart, adjust FROM event WHERE event.uid = %d AND dtstart < '%s' AND dtstart > '%s' and dismissed = 0 ORDER BY dtstart ASC ", @@ -101,7 +92,7 @@ class Manage extends \Zotlabs\Web\Controller { dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now + 7 days')), dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now - 1 days')) ); - + if($events) { $channels[$x]['all_events'] = count($events); @@ -126,9 +117,10 @@ class Manage extends \Zotlabs\Web\Controller { } } } + */ } - } + } $r = q("select count(channel_id) as total from channel where channel_account_id = %d and channel_removed = 0", intval(get_account_id()) @@ -140,23 +132,23 @@ class Manage extends \Zotlabs\Web\Controller { else { $channel_usage_message = ''; } - - + + $create = array( 'new_channel', t('Create a new channel'), t('Create New')); - + $delegates = null; if(local_channel()) { - $delegates = q("select * from abook left join xchan on abook_xchan = xchan_hash where + $delegates = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d and abook_xchan in ( select xchan from abconfig where chan = %d and cat = 'their_perms' and k = 'delegate' and v = '1' )", intval(local_channel()), intval(local_channel()) ); } - + if($delegates) { for($x = 0; $x < count($delegates); $x ++) { - $delegates[$x]['link'] = 'magic?f=&bdest=' . bin2hex($delegates[$x]['xchan_url']) + $delegates[$x]['link'] = 'magic?f=&bdest=' . bin2hex($delegates[$x]['xchan_url'] . '?zid=' . get_my_address() . '&delegate=' . urlencode($delegates[$x]['xchan_addr'])) . '&delegate=' . urlencode($delegates[$x]['xchan_addr']); $delegates[$x]['channel_name'] = $delegates[$x]['xchan_name']; $delegates[$x]['delegate'] = 1; @@ -165,9 +157,9 @@ class Manage extends \Zotlabs\Web\Controller { else { $delegates = null; } - + $o = replace_macros(get_markup_template('channels.tpl'), array( - '$header' => t('Channel Manager'), + '$header' => t('Channels'), '$msg_selected' => t('Current Channel'), '$selected' => local_channel(), '$desc' => ((count($channels) > 1 || $delegates) ? t('Switch to one of your channels by selecting it.') : ''), @@ -175,15 +167,14 @@ class Manage extends \Zotlabs\Web\Controller { '$msg_make_default' => t('Make Default'), '$create' => $create, '$all_channels' => $channels, - '$mail_format' => t('%d new messages'), '$intros_format' => t('%d new introductions'), '$channel_usage_message' => $channel_usage_message, '$delegated_desc' => t('Delegated Channel'), '$delegates' => $delegates )); - + return $o; - + } - + } diff --git a/Zotlabs/Module/Manifest.php b/Zotlabs/Module/Manifest.php new file mode 100644 index 000000000..6fe468a14 --- /dev/null +++ b/Zotlabs/Module/Manifest.php @@ -0,0 +1,50 @@ +<?php +namespace Zotlabs\Module; + +use App; +use Zotlabs\Web\Controller; +use Zotlabs\Lib\System; + +class Manifest extends Controller { + + function init() { + + $ret = [ + 'name' => ucfirst(System::get_platform_name()), + 'short_name' => ucfirst(System::get_platform_name()), + 'icons' => [ + [ 'src' => '/images/app/hz-72.png', 'sizes' => '72x72', 'type' => 'image/png' ], + [ 'src' => '/images/app/hz-96.png', 'sizes' => '96x96', 'type' => 'image/png' ], + [ 'src' => '/images/app/hz-128.png', 'sizes' => '128x128', 'type' => 'image/png' ], + [ 'src' => '/images/app/hz-144.png', 'sizes' => '144x144', 'type' => 'image/png' ], + [ 'src' => '/images/app/hz-152.png', 'sizes' => '152x152', 'type' => 'image/png' ], + [ 'src' => '/images/app/hz-192.png', 'sizes' => '192x192', 'type' => 'image/png' ], + [ 'src' => '/images/app/hz-348.png', 'sizes' => '384x384', 'type' => 'image/png' ], + [ 'src' => '/images/app/hz-512.png', 'sizes' => '512x512', 'type' => 'image/png' ], + [ 'src' => '/images/app/hz.svg', 'sizes' => '64x64', 'type' => 'image/xml+svg' ] + ], + 'scope' => '/', + 'start_url' => z_root(), + 'display' => 'standalone', + 'orientation' => 'any', + 'share_target' => [ + 'action' => '/rpost', + 'method' => 'POST', + 'enctype' => 'multipart/form-data', + 'params' => [ + 'title' => 'title', + 'text' => 'body', + 'url' => 'url', + 'files' => [ + [ 'name' => 'userfile', + 'accept' => [ 'image/*', 'audio/*', 'video/*', 'text/*', 'application/*' ] + ] + ] + ] + ] + ]; + + json_return_and_die($ret,'application/manifest+json'); + } + +} diff --git a/Zotlabs/Module/Message.php b/Zotlabs/Module/Message.php deleted file mode 100644 index 5856bfbdf..000000000 --- a/Zotlabs/Module/Message.php +++ /dev/null @@ -1,108 +0,0 @@ -<?php -namespace Zotlabs\Module; - -require_once('include/acl_selectors.php'); -require_once('include/message.php'); -require_once('include/zot.php'); -require_once("include/bbcode.php"); - - -class Message extends \Zotlabs\Web\Controller { - - function get() { - - $o = ''; - nav_set_selected('messages'); - - if(! local_channel()) { - notice( t('Permission denied.') . EOL); - return login(); - } - - $channel = \App::get_channel(); - head_set_icon($channel['xchan_photo_s']); - - $cipher = get_pconfig(local_channel(),'system','default_cipher'); - if(! $cipher) - $cipher = 'aes256'; - - /* - if((argc() == 3) && (argv(1) === 'dropconv')) { - if(! intval(argv(2))) - return; - $cmd = argv(1); - $r = private_messages_drop(local_channel(), argv(2), true); - if($r) - info( t('Conversation removed.') . EOL ); - goaway(z_root() . '/mail/combined' ); - } - - if(argc() == 2) { - - switch(argv(1)) { - case 'combined': - $mailbox = 'combined'; - $header = t('Conversations'); - break; - case 'inbox': - $mailbox = 'inbox'; - $header = t('Received Messages'); - break; - case 'outbox': - $mailbox = 'outbox'; - $header = t('Sent Messages'); - break; - default: - break; - } - - // private_messages_list() can do other more complicated stuff, for now keep it simple - - $r = private_messages_list(local_channel(), $mailbox, \App::$pager['start'], \App::$pager['itemspage']); - - if(! $r) { - info( t('No messages.') . EOL); - return $o; - } - - $messages = array(); - - foreach($r as $rr) { - - $messages[] = array( - 'id' => $rr['id'], - 'from_name' => $rr['from']['xchan_name'], - 'from_url' => chanlink_hash($rr['from_xchan']), - 'from_photo' => $rr['from']['xchan_photo_s'], - 'to_name' => $rr['to']['xchan_name'], - 'to_url' => chanlink_hash($rr['to_xchan']), - 'to_photo' => $rr['to']['xchan_photo_s'], - 'subject' => (($rr['seen']) ? $rr['title'] : '<strong>' . $rr['title'] . '</strong>'), - 'delete' => t('Delete conversation'), - 'body' => zidify_links(smilies(bbcode($rr['body']))), - 'date' => datetime_convert('UTC',date_default_timezone_get(),$rr['created'], t('D, d M Y - g:i A')), - 'seen' => $rr['seen'] - ); - } - - - $tpl = get_markup_template('mail_head.tpl'); - $o = replace_macros($tpl, array( - '$header' => $header, - '$messages' => $messages - )); - - - $o .= alt_pager(count($r)); - - return $o; - - return; - - } - */ - - return; - } - -} diff --git a/Zotlabs/Module/Mood.php b/Zotlabs/Module/Mood.php index 453f08f9f..cb2ca566b 100644 --- a/Zotlabs/Module/Mood.php +++ b/Zotlabs/Module/Mood.php @@ -14,36 +14,36 @@ require_once('include/items.php'); class Mood extends Controller { function init() { - + if(! local_channel()) return; if(! Apps::system_app_installed(local_channel(), 'Mood')) { return; } - + $uid = local_channel(); $channel = App::get_channel(); $verb = notags(trim($_GET['verb'])); - - if(! $verb) + + if(! $verb) return; - + $verbs = get_mood_verbs(); - + if(! array_key_exists($verb,$verbs)) return; - + $activity = ACTIVITY_MOOD . '#' . urlencode($verb); - + $parent = ((x($_GET,'parent')) ? intval($_GET['parent']) : 0); - - + + logger('mood: verb ' . $verb, LOGGER_DEBUG); - - + + if($parent) { - $r = q("select mid, owner_xchan, private, allow_cid, allow_gid, deny_cid, deny_gid + $r = q("select mid, owner_xchan, private, allow_cid, allow_gid, deny_cid, deny_gid from item where id = %d and parent = %d and uid = %d limit 1", intval($parent), intval($parent), @@ -59,24 +59,24 @@ class Mood extends Controller { } } else { - + $private = 0; - + $allow_cid = $channel['channel_allow_cid']; $allow_gid = $channel['channel_allow_gid']; $deny_cid = $channel['channel_deny_cid']; $deny_gid = $channel['channel_deny_gid']; } - + $poster = App::get_observer(); - + $uuid = item_message_id(); $mid = z_root() . '/item/' . $uuid; - - $action = sprintf( t('%1$s is %2$s','mood'), '[zrl=' . $poster['xchan_url'] . ']' . $poster['xchan_name'] . '[/zrl]' , $verbs[$verb]); - + + $action = sprintf( t('%1$s is %2$s','mood'), '[zrl=' . $poster['xchan_url'] . ']' . $poster['xchan_name'] . '[/zrl]' , $verbs[$verb]); + $arr = array(); - + $arr['aid'] = get_account_id(); $arr['uid'] = $uid; $arr['uuid'] = $uuid; @@ -97,31 +97,31 @@ class Mood extends Controller { $arr['item_unseen'] = 1; if(! $parent_mid) $item['item_thread_top'] = 1; - + if ((! $arr['plink']) && intval($arr['item_thread_top'])) { $arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . urlencode($arr['mid']); } - - + + $post = item_store($arr); $item_id = $post['item_id']; - + if($item_id) { \Zotlabs\Daemon\Master::Summon(array('Notifier','activity', $item_id)); } - + call_hooks('post_local_end', $arr); - + if($_SESSION['return_url']) goaway(z_root() . '/' . $_SESSION['return_url']); - + return; } - - - + + + function get() { - + if(! local_channel()) { notice( t('Permission denied.') . EOL); return; @@ -130,26 +130,24 @@ class Mood extends Controller { if(! Apps::system_app_installed(local_channel(), 'Mood')) { //Do not display any associated widgets at this point App::$pdl = ''; - - $o = '<b>' . t('Mood App') . ' (' . t('Not Installed') . '):</b><br>'; - $o .= t('Set your current mood and tell your friends'); - return $o; + $papp = Apps::get_papp('Mood'); + return Apps::app_render($papp, 'module'); } nav_set_selected('Mood'); $parent = ((x($_GET,'parent')) ? intval($_GET['parent']) : '0'); - + $verbs = get_mood_verbs(); - + $shortlist = array(); foreach($verbs as $k => $v) if($v !== 'NOTRANSLATION') $shortlist[] = array($k,$v); - - + + $tpl = get_markup_template('mood_content.tpl'); - + $o = replace_macros($tpl,array( '$title' => t('Mood'), '$desc' => t('Set your current mood and tell your friends'), @@ -157,9 +155,9 @@ class Mood extends Controller { '$parent' => $parent, '$submit' => t('Submit'), )); - + return $o; - + } - + } diff --git a/Zotlabs/Module/Network.php b/Zotlabs/Module/Network.php index 4a1692d64..03c56b9a2 100644 --- a/Zotlabs/Module/Network.php +++ b/Zotlabs/Module/Network.php @@ -20,44 +20,46 @@ class Network extends \Zotlabs\Web\Controller { return; } - if(in_array(substr($_GET['search'],0,1),[ '@', '!', '?'])) - goaway('search' . '?f=&search=' . $_GET['search']); - + $search = $_GET['search'] ?? ''; + + if(in_array(substr($search, 0, 1), [ '@', '!', '?']) || strpos($search, 'https://') === 0) + goaway(z_root() . '/search?f=&search=' . $search); + if(count($_GET) < 2) { - $network_options = get_pconfig(local_channel(),'system','network_page_default'); + $network_options = get_pconfig(local_channel(), 'system', 'network_page_default'); if($network_options) - goaway('network' . '?f=&' . $network_options); + goaway(z_root() . '/network?f=&' . $network_options); } - + $channel = App::get_channel(); App::$profile_uid = local_channel(); head_set_icon($channel['xchan_photo_s']); - + } - + function get($update = 0, $load = false) { - + if(! local_channel()) { $_SESSION['return_url'] = App::$query_string; return login(false); } - + $o = ''; $arr = array('query' => App::$query_string); - + call_hooks('network_content_init', $arr); - + $channel = App::get_channel(); $item_normal = item_normal(); $item_normal_update = item_normal_update(); - + $datequery = $datequery2 = ''; - + $group = 0; - + $nouveau = false; - + $datequery = ((x($_GET,'dend') && is_a_date_arg($_GET['dend'])) ? notags($_GET['dend']) : ''); $datequery2 = ((x($_GET,'dbegin') && is_a_date_arg($_GET['dbegin'])) ? notags($_GET['dbegin']) : ''); $gid = ((x($_GET,'gid')) ? intval($_GET['gid']) : 0); @@ -80,20 +82,20 @@ class Network extends \Zotlabs\Web\Controller { break; } - $search = (($_GET['search']) ? $_GET['search'] : ''); + $search = $_GET['search'] ?? ''; if($search) { - if(strpos($search,'#') === 0) { + if(strpos($search, '#') === 0) { $hashtags = substr($search,1); $search = ''; } } - + if($datequery) $order = 'post'; - - + + // filter by collection (e.g. group) - + if($gid) { $r = q("SELECT * FROM pgrp WHERE id = %d AND uid = %d LIMIT 1", intval($gid), @@ -106,39 +108,41 @@ class Network extends \Zotlabs\Web\Controller { goaway(z_root() . '/network'); // NOTREACHED } - + $group = $gid; $group_hash = $r[0]['hash']; $def_acl = array('allow_gid' => '<' . $r[0]['hash'] . '>'); } - - $default_cmin = ((Apps::system_app_installed(local_channel(),'Affinity Tool')) ? get_pconfig(local_channel(),'affinity','cmin',0) : (-1)); - $default_cmax = ((Apps::system_app_installed(local_channel(),'Affinity Tool')) ? get_pconfig(local_channel(),'affinity','cmax',99) : (-1)); - - $cid = ((x($_GET,'cid')) ? intval($_GET['cid']) : 0); - $star = ((x($_GET,'star')) ? intval($_GET['star']) : 0); - $liked = ((x($_GET,'liked')) ? intval($_GET['liked']) : 0); - $conv = ((x($_GET,'conv')) ? intval($_GET['conv']) : 0); - $spam = ((x($_GET,'spam')) ? intval($_GET['spam']) : 0); - $cmin = ((array_key_exists('cmin',$_GET)) ? intval($_GET['cmin']) : $default_cmin); - $cmax = ((array_key_exists('cmax',$_GET)) ? intval($_GET['cmax']) : $default_cmax); - $file = ((x($_GET,'file')) ? $_GET['file'] : ''); - $xchan = ((x($_GET,'xchan')) ? $_GET['xchan'] : ''); - $net = ((x($_GET,'net')) ? $_GET['net'] : ''); - $pf = ((x($_GET,'pf')) ? $_GET['pf'] : ''); - $unseen = ((x($_GET,'unseen')) ? $_GET['unseen'] : ''); - - if (Apps::system_app_installed(local_channel(),'Affinity Tool')) { - $affinity_locked = intval(get_pconfig(local_channel(),'affinity','lock',1)); + + $default_cmin = ((Apps::system_app_installed(local_channel(), 'Affinity Tool')) ? get_pconfig(local_channel(), 'affinity', 'cmin', 0) : (-1)); + $default_cmax = ((Apps::system_app_installed(local_channel(), 'Affinity Tool')) ? get_pconfig(local_channel(), 'affinity', 'cmax', 99) : (-1)); + + $cid = ((x($_GET, 'cid')) ? intval($_GET['cid']) : 0); + $star = ((x($_GET, 'star')) ? intval($_GET['star']) : 0); + $liked = ((x($_GET, 'liked')) ? intval($_GET['liked']) : 0); + $conv = ((x($_GET, 'conv')) ? intval($_GET['conv']) : 0); + $spam = ((x($_GET, 'spam')) ? intval($_GET['spam']) : 0); + $cmin = ((array_key_exists('cmin', $_GET)) ? intval($_GET['cmin']) : $default_cmin); + $cmax = ((array_key_exists('cmax', $_GET)) ? intval($_GET['cmax']) : $default_cmax); + $file = ((x($_GET, 'file')) ? $_GET['file'] : ''); + $xchan = ((x($_GET, 'xchan')) ? $_GET['xchan'] : ''); + $net = ((x($_GET, 'net')) ? $_GET['net'] : ''); + $pf = ((x($_GET, 'pf')) ? $_GET['pf'] : ''); + $unseen = ((x($_GET, 'unseen')) ? $_GET['unseen'] : ''); + + if (Apps::system_app_installed(local_channel(), 'Affinity Tool')) { + $affinity_locked = intval(get_pconfig(local_channel(), 'affinity', 'lock', 1)); if ($affinity_locked) { - set_pconfig(local_channel(),'affinity','cmin',$cmin); - set_pconfig(local_channel(),'affinity','cmax',$cmax); + set_pconfig(local_channel(), 'affinity', 'cmin', $cmin); + set_pconfig(local_channel(), 'affinity', 'cmax', $cmax); } - } + } - if(x($_GET,'search') || $file || (!$pf && $cid) || $hashtags || $verb || $category || $conv || $unseen) + if(x($_GET, 'search') || $file || (!$pf && $cid) || $hashtags || $verb || $category || $conv || $unseen) $nouveau = true; + $cid_r = []; + if($cid) { $cid_r = q("SELECT abook.abook_xchan, xchan.xchan_addr, xchan.xchan_name, xchan.xchan_url, xchan.xchan_photo_s, xchan.xchan_pubforum from abook left join xchan on abook_xchan = xchan_hash where abook_id = %d and abook_channel = %d and abook_blocked = 0 limit 1", intval($cid), @@ -155,16 +159,16 @@ class Network extends \Zotlabs\Web\Controller { } $def_acl = [ 'allow_cid' => '<' . $cid_r[0]['abook_xchan'] . '>', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '' ]; } - + if(! $update) { - + // search terms header if($search || $hashtags) { - $o .= replace_macros(get_markup_template("section_title.tpl"),array( - '$title' => t('Search Results For:') . ' ' . (($search) ? htmlspecialchars($search, ENT_COMPAT,'UTF-8') : '#' . htmlspecialchars($hashtags, ENT_COMPAT,'UTF-8')) + $o .= replace_macros(get_markup_template('section_title.tpl'), array( + '$title' => t('Search Results For:') . ' ' . (($search) ? htmlspecialchars($search, ENT_COMPAT, 'UTF-8') : '#' . htmlspecialchars($hashtags, ENT_COMPAT,'UTF-8')) )); } - + nav_set_selected('Network'); $bang = '!'; @@ -179,17 +183,17 @@ class Network extends \Zotlabs\Web\Controller { } $channel_acl = array( - 'allow_cid' => $channel['channel_allow_cid'], - 'allow_gid' => $channel['channel_allow_gid'], - 'deny_cid' => $channel['channel_deny_cid'], + 'allow_cid' => $channel['channel_allow_cid'], + 'allow_gid' => $channel['channel_allow_gid'], + 'deny_cid' => $channel['channel_deny_cid'], 'deny_gid' => $channel['channel_deny_gid'] ); $private_editing = (($group || $cid) ? true : false); - + $x = array( 'is_owner' => true, - 'allow_location' => ((intval(get_pconfig($channel['channel_id'],'system','use_browser_location'))) ? '1' : ''), + 'allow_location' => ((intval(get_pconfig($channel['channel_id'], 'system', 'use_browser_location'))) ? '1' : ''), 'default_location' => $channel['channel_location'], 'nickname' => $channel['channel_address'], 'lockstate' => (($private_editing || $channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'), @@ -204,34 +208,34 @@ class Network extends \Zotlabs\Web\Controller { 'jotnets' => true, 'reset' => t('Reset form') ); - - $status_editor = status_editor($a,$x,false,'Network'); + + $status_editor = status_editor($a, $x, false, 'Network'); $o .= $status_editor; } - - + + // We don't have to deal with ACL's on this page. You're looking at everything // that belongs to you, hence you can see all of it. We will filter by group if // desired. - - + + $sql_options = (($star) - ? " and item_starred = 1 " + ? ' and item_starred = 1 ' : ''); - + $sql_nets = ''; $item_thread_top = ' AND item_thread_top = 1 '; - + $sql_extra = ''; - + if($group) { $contact_str = ''; $contacts = group_get_members($group); if($contacts) { - $contact_str = ids_to_querystr($contacts,'xchan',true); + $contact_str = ids_to_querystr($contacts, 'xchan', true); } else { $contact_str = " '0' "; @@ -241,18 +245,18 @@ class Network extends \Zotlabs\Web\Controller { } $item_thread_top = ''; $sql_extra = " AND item.parent IN ( SELECT DISTINCT parent FROM item WHERE true $sql_options AND (( author_xchan IN ( $contact_str ) OR owner_xchan in ( $contact_str )) or allow_gid like '" . protect_sprintf('%<' . dbesc($group_hash) . '>%') . "' ) and id = parent $item_normal ) "; - + $x = group_rec_byhash(local_channel(), $group_hash); - + if($x) { - $title = replace_macros(get_markup_template("section_title.tpl"),array( + $title = replace_macros(get_markup_template('section_title.tpl'), array( '$title' => t('Privacy group: ') . $x['gname'] )); } - + $o = $title; $o .= $status_editor; - + } elseif($cid_r) { $item_thread_top = ''; @@ -285,10 +289,10 @@ class Network extends \Zotlabs\Web\Controller { // This is for threaded view cid queries (e.g. if a forum is selected from the forum filter) $ttype = (($pf) ? TERM_FORUM : TERM_MENTION); - $p1 = q("SELECT DISTINCT parent FROM item WHERE uid = " . intval(local_channel()) . " AND ( author_xchan = '" . dbesc($cid_r[0]['abook_xchan']) . "' OR owner_xchan = '" . dbesc($cid_r[0]['abook_xchan']) . "' ) $item_normal "); - $p2 = q("SELECT oid AS parent FROM term WHERE uid = " . intval(local_channel()) . " AND ttype = $ttype AND term = '" . dbesc($cid_r[0]['xchan_name']) . "'"); + $p1 = dbq("SELECT DISTINCT parent FROM item WHERE uid = " . intval(local_channel()) . " AND ( author_xchan = '" . dbesc($cid_r[0]['abook_xchan']) . "' OR owner_xchan = '" . dbesc($cid_r[0]['abook_xchan']) . "' ) $item_normal "); + $p2 = dbq("SELECT oid AS parent FROM term WHERE uid = " . intval(local_channel()) . " AND ttype = $ttype AND term = '" . dbesc($cid_r[0]['xchan_name']) . "'"); - $p_str = ids_to_querystr(array_merge($p1,$p2),'parent'); + $p_str = ids_to_querystr(array_merge($p1, $p2), 'parent'); if(! $p_str) killme(); @@ -296,7 +300,7 @@ class Network extends \Zotlabs\Web\Controller { } } - $title = replace_macros(get_markup_template("section_title.tpl"),array( + $title = replace_macros(get_markup_template('section_title.tpl'), array( '$title' => '<a href="' . zid($cid_r[0]['xchan_url']) . '" ><img src="' . zid($cid_r[0]['xchan_photo_s']) . '" alt="' . urlencode($cid_r[0]['xchan_name']) . '" /></a> <a href="' . zid($cid_r[0]['xchan_url']) . '" >' . $cid_r[0]['xchan_name'] . '</a>' )); @@ -310,7 +314,7 @@ class Network extends \Zotlabs\Web\Controller { if($r) { $item_thread_top = ''; $sql_extra = " AND item.parent IN ( SELECT DISTINCT parent FROM item WHERE true $sql_options AND uid = " . intval(local_channel()) . " AND ( author_xchan = '" . dbesc($xchan) . "' or owner_xchan = '" . dbesc($xchan) . "' ) $item_normal ) "; - $title = replace_macros(get_markup_template("section_title.tpl"),array( + $title = replace_macros(get_markup_template("section_title.tpl"), array( '$title' => '<a href="' . zid($r[0]['xchan_url']) . '" ><img src="' . zid($r[0]['xchan_photo_s']) . '" alt="' . urlencode($r[0]['xchan_name']) . '" /></a> <a href="' . zid($r[0]['xchan_url']) . '" >' . $r[0]['xchan_name'] . '</a>' )); @@ -324,79 +328,30 @@ class Network extends \Zotlabs\Web\Controller { } } - + if(x($category)) { $sql_extra .= protect_sprintf(term_query('item', $category, TERM_CATEGORY)); } if(x($hashtags)) { $sql_extra .= protect_sprintf(term_query('item', $hashtags, TERM_HASHTAG, TERM_COMMUNITYTAG)); } - - if(! $update) { - // The special div is needed for liveUpdate to kick in for this page. - // We only launch liveUpdate if you aren't filtering in some incompatible - // way and also you aren't writing a comment (discovered in javascript). - $maxheight = get_pconfig(local_channel(),'system','network_divmore_height'); - if(! $maxheight) - $maxheight = 400; - - - $o .= '<div id="live-network"></div>' . "\r\n"; - $o .= "<script> var profile_uid = " . local_channel() - . "; var profile_page = " . App::$pager['page'] - . "; divmore_height = " . intval($maxheight) . "; </script>\r\n"; - - App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"),array( - '$baseurl' => z_root(), - '$pgtype' => 'network', - '$uid' => ((local_channel()) ? local_channel() : '0'), - '$gid' => (($gid) ? $gid : '0'), - '$cid' => (($cid) ? $cid : '0'), - '$cmin' => (($cmin) ? $cmin : '(-1)'), - '$cmax' => (($cmax) ? $cmax : '(-1)'), - '$star' => (($star) ? $star : '0'), - '$liked' => (($liked) ? $liked : '0'), - '$conv' => (($conv) ? $conv : '0'), - '$spam' => (($spam) ? $spam : '0'), - '$fh' => '0', - '$dm' => (($dm) ? $dm : '0'), - '$nouveau' => (($nouveau) ? $nouveau : '0'), - '$wall' => '0', - '$list' => ((x($_REQUEST,'list')) ? intval($_REQUEST['list']) : 0), - '$page' => ((App::$pager['page'] != 1) ? App::$pager['page'] : 1), - '$search' => (($search) ? urlencode($search) : ''), - '$xchan' => (($xchan) ? urlencode($xchan) : ''), - '$order' => $order, - '$file' => (($file) ? urlencode($file) : ''), - '$cats' => (($category) ? urlencode($category) : ''), - '$tags' => (($hashtags) ? urlencode($hashtags) : ''), - '$dend' => $datequery, - '$mid' => '', - '$verb' => (($verb) ? urlencode($verb) : ''), - '$net' => (($net) ? urlencode($net) : ''), - '$dbegin' => $datequery2, - '$pf' => (($pf) ? intval($pf) : 0), - '$unseen' => (($unseen) ? urlencode($unseen) : '') - )); - } - $sql_extra3 = ''; - + if($datequery) { $sql_extra3 .= protect_sprintf(sprintf(" AND item.created <= '%s' ", dbesc(datetime_convert(date_default_timezone_get(),'',$datequery)))); } if($datequery2) { $sql_extra3 .= protect_sprintf(sprintf(" AND item.created >= '%s' ", dbesc(datetime_convert(date_default_timezone_get(),'',$datequery2)))); } - - $sql_extra2 = (($nouveau) ? '' : " AND item.parent = item.id "); + + $sql_extra2 = (($nouveau) ? '' : ' AND item.parent = item.id '); $sql_extra3 = (($nouveau) ? '' : $sql_extra3); - - if(x($_GET,'search')) { + + if(x($_GET, 'search')) { $search = escape_tags($_GET['search']); - if(strpos($search,'#') === 0) { - $sql_extra .= term_query('item',substr($search,1),TERM_HASHTAG,TERM_COMMUNITYTAG); + if(strpos($search, '#') === 0) { + $sql_extra .= term_query('item', substr($search, 1), TERM_HASHTAG, TERM_COMMUNITYTAG); } else { $sql_extra .= sprintf(" AND (item.body like '%s' OR item.title like '%s') ", @@ -405,7 +360,7 @@ class Network extends \Zotlabs\Web\Controller { ); } } - + if ($verb) { // the presence of a leading dot in the verb determines @@ -413,8 +368,8 @@ class Network extends \Zotlabs\Web\Controller { // The name 'verb' is a holdover from the earlier XML // ActivityStreams specification. - if (substr($verb,0,1) === '.') { - $verb = substr($verb,1); + if (substr($verb, 0, 1) === '.') { + $verb = substr($verb, 1); $sql_extra .= sprintf(" AND item.obj_type like '%s' ", dbesc(protect_sprintf('%' . $verb . '%')) ); @@ -425,60 +380,64 @@ class Network extends \Zotlabs\Web\Controller { ); } } - + if(strlen($file)) { - $sql_extra .= term_query('item',$file,TERM_FILE); + $sql_extra .= term_query('item', $file, TERM_FILE); } if ($dm) { - $sql_extra .= " AND item_private = 2 "; + $sql_extra .= ' AND item_private = 2 '; } - + else { + $sql_extra .= ' AND item_private IN (0, 1) '; + } + + if($conv) { $item_thread_top = ''; $sql_extra .= " AND ( author_xchan = '" . dbesc($channel['channel_hash']) . "' OR item_mentionsme = 1 ) "; } - + if($update && ! $load) { - + // only setup pagination on initial page view $pager_sql = ''; - + } else { - $itemspage = get_pconfig(local_channel(),'system','itemspage'); + $itemspage = get_pconfig(local_channel(), 'system', 'itemspage'); App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 10)); $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(App::$pager['itemspage']), intval(App::$pager['start'])); } - + // cmin and cmax are both -1 when the affinity tool is disabled - if(($cmin != (-1)) || ($cmax != (-1))) { - + if(($cmin !== (-1)) || ($cmax !== (-1))) { + // Not everybody who shows up in the network stream will be in your address book. // By default those that aren't are assumed to have closeness = 99; but this isn't // recorded anywhere. So if cmax is 99, we'll open the search up to anybody in // the stream with a NULL address book entry. - - $sql_nets .= " AND "; - - if($cmax == 99) - $sql_nets .= " ( "; - - $sql_nets .= "( abook.abook_closeness >= " . intval($cmin) . " "; - $sql_nets .= " AND abook.abook_closeness <= " . intval($cmax) . " ) "; - - if($cmax == 99) - $sql_nets .= " OR abook.abook_closeness IS NULL ) "; - + + $sql_nets .= ' AND '; + + if($cmax === 99) + $sql_nets .= ' ( '; + + $sql_nets .= '( abook.abook_closeness >= ' . intval($cmin) . ' '; + $sql_nets .= ' AND abook.abook_closeness <= ' . intval($cmax) . ' ) '; + + if($cmax === 99) + $sql_nets .= ' OR abook.abook_closeness IS NULL ) '; + } - $net_query = (($net) ? " left join xchan on xchan_hash = author_xchan " : ''); + $net_query = (($net) ? ' left join xchan on xchan_hash = author_xchan ' : ''); $net_query2 = (($net) ? " and xchan_network = '" . protect_sprintf(dbesc($net)) . "' " : ''); - $abook_uids = " and abook.abook_channel = " . local_channel() . " "; - $uids = " and item.uid = " . local_channel() . " "; - + $abook_uids = ' and abook.abook_channel = ' . local_channel() . ' '; + $uids = ' and item.uid = ' . local_channel() . ' '; + if(feature_enabled(local_channel(), 'network_list_mode')) $page_mode = 'list'; else @@ -502,9 +461,11 @@ class Network extends \Zotlabs\Web\Controller { if($update && $_SESSION['loadtime']) $simple_update = " AND (( item_unseen = 1 AND item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) OR item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) "; + $items = []; + if($nouveau && $load) { // "New Item View" - show all items unthreaded in reverse created date order - $items = q("SELECT item.*, item.id AS item_id, created FROM item + $items = dbq("SELECT item.*, item.id AS item_id, created FROM item left join abook on ( item.owner_xchan = abook.abook_xchan $abook_uids ) $net_query WHERE true $uids $item_normal @@ -514,26 +475,26 @@ class Network extends \Zotlabs\Web\Controller { ORDER BY item.created DESC $pager_sql " ); - $parents_str = ids_to_querystr($items,'item_id'); + $parents_str = ids_to_querystr($items, 'item_id'); require_once('include/items.php'); - + xchan_query($items); - - $items = fetch_post_tags($items,true); + + $items = fetch_post_tags($items, true); } elseif($update) { - + // Normal conversation view - + if($order === 'post') - $ordering = "created"; + $ordering = 'created'; else - $ordering = "commented"; - + $ordering = 'commented'; + if($load) { // Fetch a page full of parent items for this page - $r = q("SELECT item.parent AS item_id FROM item + $r = dbq("SELECT item.parent AS item_id FROM item left join abook on ( item.owner_xchan = abook.abook_xchan $abook_uids ) $net_query WHERE true $uids $item_thread_top $item_normal @@ -547,51 +508,98 @@ class Network extends \Zotlabs\Web\Controller { else { // this is an update - $r = q("SELECT item.parent AS item_id FROM item + $r = dbq("SELECT item.parent AS item_id FROM item left join abook on ( item.owner_xchan = abook.abook_xchan $abook_uids ) $net_query WHERE true $uids $item_normal_update $simple_update and (abook.abook_blocked = 0 or abook.abook_flags is null) - $sql_extra3 $sql_extra $sql_options $sql_nets $net_query2" + $sql_extra3 $sql_extra $sql_options $sql_nets $net_query2 " ); } // Then fetch all the children of the parents that are on this page - + if($r) { - - $parents_str = ids_to_querystr($r,'item_id'); - - $items = q("SELECT item.*, item.id AS item_id FROM item + $parents_str = ids_to_querystr($r, 'item_id'); + $items = dbq("SELECT item.*, item.id AS item_id FROM item WHERE true $uids $item_normal - AND item.parent IN ( %s ) - $sql_extra ", - dbesc($parents_str) + AND item.parent IN ( $parents_str ) + $sql_extra " ); - - xchan_query($items,true); - $items = fetch_post_tags($items,true); - $items = conv_sort($items,$ordering); + + xchan_query($items, true); + $items = fetch_post_tags($items, true); + $items = conv_sort($items, $ordering); } else { $items = array(); } } - + $mode = (($nouveau) ? 'network-new' : 'network'); if($search) $mode = 'search'; - - $o .= conversation($items,$mode,$update,$page_mode); - + + if(! $update) { + // The special div is needed for liveUpdate to kick in for this page. + // We only launch liveUpdate if you aren't filtering in some incompatible + // way and also you aren't writing a comment (discovered in javascript). + + $maxheight = get_pconfig(local_channel(), 'system', 'network_divmore_height'); + if(! $maxheight) + $maxheight = 400; + + + $o .= '<div id="live-network"></div>' . "\r\n"; + $o .= "<script> var profile_uid = " . local_channel() + . "; var profile_page = " . App::$pager['page'] + . "; divmore_height = " . intval($maxheight) . "; </script>\r\n"; + + App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"),array( + '$baseurl' => z_root(), + '$pgtype' => 'network', + '$uid' => ((local_channel()) ? local_channel() : '0'), + '$gid' => (($gid) ? $gid : '0'), + '$cid' => (($cid) ? $cid : '0'), + '$cmin' => (($cmin) ? $cmin : '(-1)'), + '$cmax' => (($cmax) ? $cmax : '(-1)'), + '$star' => (($star) ? $star : '0'), + '$liked' => (($liked) ? $liked : '0'), + '$conv' => (($conv) ? $conv : '0'), + '$spam' => (($spam) ? $spam : '0'), + '$fh' => '0', + '$dm' => (($dm) ? $dm : '0'), + '$nouveau' => (($nouveau) ? $nouveau : '0'), + '$wall' => '0', + '$list' => ((x($_REQUEST,'list')) ? intval($_REQUEST['list']) : 0), + '$page' => ((App::$pager['page'] != 1) ? App::$pager['page'] : 1), + '$search' => (($search) ? urlencode($search) : ''), + '$xchan' => (($xchan) ? urlencode($xchan) : ''), + '$order' => $order, + '$file' => (($file) ? urlencode($file) : ''), + '$cats' => (($category) ? urlencode($category) : ''), + '$tags' => (($hashtags) ? urlencode($hashtags) : ''), + '$dend' => $datequery, + '$mid' => '', + '$verb' => (($verb) ? urlencode($verb) : ''), + '$net' => (($net) ? urlencode($net) : ''), + '$dbegin' => $datequery2, + '$pf' => (($pf) ? intval($pf) : 0), + '$unseen' => (($unseen) ? urlencode($unseen) : ''), + '$page_mode' => $page_mode + )); + } + + $o .= conversation($items, $mode, $update, $page_mode); + if(($items) && (! $update)) $o .= alt_pager(count($items)); $_SESSION['loadtime'] = datetime_convert(); - + return $o; } - + } diff --git a/Zotlabs/Module/New_channel.php b/Zotlabs/Module/New_channel.php index 98aa480fe..84d492f8f 100644 --- a/Zotlabs/Module/New_channel.php +++ b/Zotlabs/Module/New_channel.php @@ -11,7 +11,7 @@ class New_channel extends \Zotlabs\Web\Controller { function init() { $cmd = ((argc() > 1) ? argv(1) : ''); - + if($cmd === 'autofill.json') { require_once('library/urlify/URLify.php'); $result = array('error' => false, 'message' => ''); @@ -20,14 +20,14 @@ class New_channel extends \Zotlabs\Web\Controller { $x = false; if(get_config('system','unicode_usernames')) { - $x = punify(mb_strtolower($n)); + $x = punify(mb_strtolower($n)); } if((! $x) || strlen($x) > 64) $x = strtolower(\URLify::transliterate($n)); - + $test = array(); - + // first name if(strpos($x,' ')) $test[] = legal_webbie(substr($x,0,strpos($x,' '))); @@ -44,19 +44,19 @@ class New_channel extends \Zotlabs\Web\Controller { json_return_and_die(check_webbie($test)); } - + if($cmd === 'checkaddr.json') { require_once('library/urlify/URLify.php'); $result = array('error' => false, 'message' => ''); $n = trim($_REQUEST['nick']); if(! $n) { - $n = trim($_REQUEST['name']); + $n = trim($_REQUEST['name']); } $x = false; if(get_config('system','unicode_usernames')) { - $x = punify(mb_strtolower($n)); + $x = punify(mb_strtolower($n)); } if((! $x) || strlen($x) > 64) @@ -64,7 +64,7 @@ class New_channel extends \Zotlabs\Web\Controller { $test = array(); - + // first name if(strpos($x,' ')) $test[] = legal_webbie(substr($x,0,strpos($x,' '))); @@ -80,57 +80,57 @@ class New_channel extends \Zotlabs\Web\Controller { $test[] = $n; $test[] = $n . mt_rand(1000,9999); } - + for($y = 0; $y < 100; $y ++) $test[] = 'id' . mt_rand(1000,9999); - + json_return_and_die(check_webbie($test)); } - - + + } - + function post() { - + $arr = $_POST; - + $acc = \App::get_account(); $arr['account_id'] = get_account_id(); - - // prevent execution by delegated channels as well as those not logged in. + + // prevent execution by delegated channels as well as those not logged in. // get_account_id() returns the account_id from the session. But \App::$account - // may point to the original authenticated account. - + // may point to the original authenticated account. + if((! $acc) || ($acc['account_id'] != $arr['account_id'])) { notice( t('Permission denied.') . EOL ); return; } - + $result = create_identity($arr); - + if(! $result['success']) { notice($result['message']); return; } - + $newuid = $result['channel']['channel_id']; - + change_channel($result['channel']['channel_id']); - - $next_page = get_config('system', 'workflow_channel_next', 'profiles'); + + $next_page = get_config('system', 'workflow_channel_next', 'profiles'); goaway(z_root() . '/' . $next_page); - + } - + function get() { - + $acc = \App::get_account(); - + if((! $acc) || $acc['account_id'] != get_account_id()) { notice( t('Permission denied.') . EOL); return; } - + $default_role = ''; $aid = get_account_id(); if($aid) { @@ -140,7 +140,7 @@ class New_channel extends \Zotlabs\Web\Controller { if($r && (! intval($r[0]['total']))) { $default_role = get_config('system','default_permissions_role','social'); } - + $limit = account_service_class_fetch(get_account_id(),'total_identities'); $canadd = true; if($r && ($limit !== false)) { @@ -155,7 +155,7 @@ class New_channel extends \Zotlabs\Web\Controller { } $name_help = '<span id="name_help_loading" style="display:none">' . t('Loading') . '</span><span id="name_help_text">'; - $name_help .= (($default_role) + $name_help .= (($default_role) ? t('Your real name is recommended.') : t('Examples: "Bob Jameson", "Lisa and her Horses", "Soccer", "Aviation Group"') ); @@ -176,10 +176,10 @@ class New_channel extends \Zotlabs\Web\Controller { $nickhub = '@' . \App::get_hostname(); $nickname = array('nickname', t('Choose a short nickname'), ((x($_REQUEST,'nickname')) ? $_REQUEST['nickname'] : ''), $nick_help, "*"); $role = array('permissions_role' , t('Channel role and privacy'), ($privacy_role) ? $privacy_role : 'social', t('Select a channel permission role compatible with your usage needs and privacy requirements.') . '<br>' . '<a href="help/member/member_guide#Channel_Permission_Roles" target="_blank">' . t('Read more about channel permission roles') . '</a>',$perm_roles); - + $o = replace_macros(get_markup_template('new_channel.tpl'), array( '$title' => t('Create a Channel'), - '$desc' => t('A channel is a unique network identity. It can represent a person (social network profile), a forum (group), a business or celebrity page, a newsfeed, and many other things.') , + '$desc' => t('A channel is a unique network identity. It can represent a person (social network profile), a forum (group), a business or celebrity page, a newsfeed, and many other things.') , '$label_import' => t('or <a href="import">import an existing channel</a> from another location.'), '$name' => $name, '$role' => $role, @@ -190,10 +190,10 @@ class New_channel extends \Zotlabs\Web\Controller { '$channel_usage_message' => $channel_usage_message, '$canadd' => $canadd )); - + return $o; - + } - - + + } diff --git a/Zotlabs/Module/Notes.php b/Zotlabs/Module/Notes.php index b448cff83..6e8e03f20 100644 --- a/Zotlabs/Module/Notes.php +++ b/Zotlabs/Module/Notes.php @@ -38,7 +38,6 @@ class Notes extends Controller { // push updates to channel clones if((argc() > 1) && (argv(1) === 'sync')) { - require_once('include/zot.php'); Libsync::build_sync_packet(); } @@ -52,11 +51,9 @@ class Notes extends Controller { if(! Apps::system_app_installed(local_channel(), 'Notes')) { //Do not display any associated widgets at this point - App::$pdl = EMPTY_STR; - - $o = '<b>' . t('Notes App') . ' (' . t('Not Installed') . '):</b><br>'; - $o .= t('A simple notes app with a widget (note: notes are not encrypted)'); - return $o; + App::$pdl = ''; + $papp = Apps::get_papp('Notes'); + return Apps::app_render($papp, 'module'); } $w = new \Zotlabs\Widget\Notes; diff --git a/Zotlabs/Module/Notifications.php b/Zotlabs/Module/Notifications.php index 1762ad5f6..c08628b47 100644 --- a/Zotlabs/Module/Notifications.php +++ b/Zotlabs/Module/Notifications.php @@ -6,25 +6,76 @@ require_once('include/bbcode.php'); class Notifications extends \Zotlabs\Web\Controller { function get() { - + if(! local_channel()) { - notice( t('Permission denied.') . EOL); return; } - + + // ajax mark all unseen items read + if(x($_REQUEST, 'markRead')) { + switch($_REQUEST['markRead']) { + case 'dm': + $r = q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND item_unseen = 1 AND item_private = 2", + intval(local_channel()) + ); + break; + case 'network': + $r = q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND item_unseen = 1 AND item_private IN (0, 1)", + intval(local_channel()) + ); + break; + case 'home': + $r = q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND item_unseen = 1 AND item_wall = 1 AND item_private IN (0, 1)", + intval(local_channel()) + ); + break; + case 'all_events': + $evdays = intval(get_pconfig(local_channel(), 'system', 'evdays', 3)); + $r = q("UPDATE event SET dismissed = 1 WHERE uid = %d AND dismissed = 0 AND dtstart < '%s' AND dtstart > '%s' ", + intval(local_channel()), + dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now + ' . intval($evdays) . ' days')), + dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now - 1 days')) + ); + break; + case 'notify': + $r = q("UPDATE notify SET seen = 1 WHERE seen = 0 AND uid = %d", + intval(local_channel()) + ); + break; + case 'pubs': + unset($_SESSION['static_loadtime']); + break; + default: + break; + } + killme(); + } + + // ajax mark all comments of a parent item read + if(x($_REQUEST, 'markItemRead') && local_channel()) { + $r = q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND parent = %d", + intval(local_channel()), + intval($_REQUEST['markItemRead']) + ); + killme(); + } + nav_set_selected('Notifications'); - + $o = ''; + $notif_content = ''; + $notifications_available = false; $r = q("select count(*) as total from notify where uid = %d and seen = 0", intval(local_channel()) ); - if($r && intval($t[0]['total']) > 49) { + if($r && intval($r[0]['total']) > 49) { $r = q("select * from notify where uid = %d and seen = 0 order by created desc limit 50", intval(local_channel()) ); - } else { + } + else { $r1 = q("select * from notify where uid = %d and seen = 0 order by created desc limit 50", intval(local_channel()) @@ -32,13 +83,13 @@ class Notifications extends \Zotlabs\Web\Controller { $r2 = q("select * from notify where uid = %d and seen = 1 order by created desc limit %d", intval(local_channel()), - intval(50 - intval($t[0]['total'])) + intval(50 - intval($r[0]['total'])) ); $r = array_merge($r1,$r2); } - + if($r) { - $notifications_available = 1; + $notifications_available = true; foreach ($r as $rr) { $x = strip_tags(bbcode($rr['msg'])); $notif_content .= replace_macros(get_markup_template('notify.tpl'),array( @@ -52,17 +103,17 @@ class Notifications extends \Zotlabs\Web\Controller { } } else { - $notif_content .= t('No more system notifications.'); + $notif_content = t('No more system notifications.'); } - + $o .= replace_macros(get_markup_template('notifications.tpl'),array( '$notif_header' => t('System Notifications'), '$notif_link_mark_seen' => t('Mark all seen'), '$notif_content' => $notif_content, '$notifications_available' => $notifications_available, )); - + return $o; } - + } diff --git a/Zotlabs/Module/Notify.php b/Zotlabs/Module/Notify.php index cffcc8099..4cbcfee05 100644 --- a/Zotlabs/Module/Notify.php +++ b/Zotlabs/Module/Notify.php @@ -1,14 +1,38 @@ <?php namespace Zotlabs\Module; +use \Zotlabs\Lib\PConfig; +use \Zotlabs\Web\Controller; - -class Notify extends \Zotlabs\Web\Controller { +class Notify extends Controller { function init() { if(! local_channel()) return; - + + if($_REQUEST['notify_id']) { + $update_notices_per_parent = PConfig::Get(local_channel(), 'system', 'update_notices_per_parent', 1); + + if($update_notices_per_parent) { + $r = q("SELECT parent FROM notify WHERE id = %d AND uid = %d", + intval($_REQUEST['notify_id']), + intval(local_channel()) + ); + q("update notify set seen = 1 where parent = '%s' and uid = %d", + dbesc($r[0]['parent']), + intval(local_channel()) + ); + } + else { + q("update notify set seen = 1 where id = %d and uid = %d", + intval($_REQUEST['notify_id']), + intval(local_channel()) + ); + } + + killme(); + } + if(argc() > 2 && argv(1) === 'view' && intval(argv(2))) { $r = q("select * from notify where id = %d and uid = %d limit 1", intval(argv(2)), @@ -29,24 +53,24 @@ class Notify extends \Zotlabs\Web\Controller { } goaway(z_root()); } - - + + } - - + + function get() { if(! local_channel()) return login(); - + $notif_tpl = get_markup_template('notifications.tpl'); - + $not_tpl = get_markup_template('notify.tpl'); require_once('include/bbcode.php'); - + $r = q("SELECT * from notify where uid = %d and seen = 0 order by created desc", intval(local_channel()) ); - + if($r) { foreach ($r as $it) { $notif_content .= replace_macros($not_tpl,array( @@ -56,18 +80,18 @@ class Notify extends \Zotlabs\Web\Controller { '$item_when' => relative_date($it['created']) )); } - } + } else { $notif_content .= t('No more system notifications.'); } - + $o .= replace_macros($notif_tpl,array( '$notif_header' => t('System Notifications'), '$tabs' => '', // $tabs, '$notif_content' => $notif_content, )); - + return $o; - + } } diff --git a/Zotlabs/Module/Oauth.php b/Zotlabs/Module/Oauth.php index 27c062df2..061296257 100644 --- a/Zotlabs/Module/Oauth.php +++ b/Zotlabs/Module/Oauth.php @@ -17,22 +17,22 @@ class Oauth extends Controller { if(! Apps::system_app_installed(local_channel(), 'OAuth Apps Manager')) return; - + if(x($_POST,'remove')){ check_form_security_token_redirectOnErr('/oauth', 'oauth'); - + $key = $_POST['remove']; q("DELETE FROM tokens WHERE id='%s' AND uid=%d", dbesc($key), local_channel()); goaway(z_root()."/oauth"); - return; + return; } - + if((argc() > 1) && (argv(1) === 'edit' || argv(1) === 'add') && x($_POST,'submit')) { - + check_form_security_token_redirectOnErr('oauth', 'oauth'); - + $name = ((x($_POST,'name')) ? escape_tags($_POST['name']) : ''); $key = ((x($_POST,'key')) ? escape_tags($_POST['key']) : ''); $secret = ((x($_POST,'secret')) ? escape_tags($_POST['secret']) : ''); @@ -48,7 +48,7 @@ class Oauth extends Controller { $ok = false; notice( t('Key and Secret are required') . EOL); } - + if($ok) { if ($_POST['submit']==t("Update")){ $r = q("UPDATE clients SET @@ -96,13 +96,11 @@ class Oauth extends Controller { if(! Apps::system_app_installed(local_channel(), 'OAuth Apps Manager')) { //Do not display any associated widgets at this point App::$pdl = ''; - - $o = '<b>' . t('OAuth Apps Manager App') . ' (' . t('Not Installed') . '):</b><br>'; - $o .= t('OAuth authentication tokens for mobile and remote apps'); - return $o; + $papp = Apps::get_papp('OAuth Apps Manager'); + return Apps::app_render($papp, 'module'); } - + if((argc() > 1) && (argv(1) === 'add')) { $tpl = get_markup_template("oauth_edit.tpl"); $o .= replace_macros($tpl, array( @@ -118,18 +116,18 @@ class Oauth extends Controller { )); return $o; } - + if((argc() > 2) && (argv(1) === 'edit')) { $r = q("SELECT * FROM clients WHERE client_id='%s' AND uid=%d", dbesc(argv(2)), local_channel()); - + if (!count($r)){ notice(t('Application not found.')); return; } $app = $r[0]; - + $tpl = get_markup_template("oauth_edit.tpl"); $o .= replace_macros($tpl, array( '$form_security_token' => get_form_security_token("oauth"), @@ -144,26 +142,26 @@ class Oauth extends Controller { )); return $o; } - + if((argc() > 2) && (argv(1) === 'delete')) { check_form_security_token_redirectOnErr('/oauth', 'oauth', 't'); - + $r = q("DELETE FROM clients WHERE client_id='%s' AND uid=%d", dbesc(argv(2)), local_channel()); goaway(z_root()."/oauth"); - return; + return; } - - - $r = q("SELECT clients.*, tokens.id as oauth_token, (clients.uid=%d) AS my + + + $r = q("SELECT clients.*, tokens.id as oauth_token, (clients.uid=%d) AS my FROM clients LEFT JOIN tokens ON clients.client_id=tokens.client_id WHERE clients.uid IN (%d,0)", local_channel(), local_channel()); - - + + $tpl = get_markup_template("oauth.tpl"); $o .= replace_macros($tpl, array( '$form_security_token' => get_form_security_token("oauth"), @@ -178,7 +176,7 @@ class Oauth extends Controller { '$apps' => $r, )); return $o; - + } } diff --git a/Zotlabs/Module/Oauth2.php b/Zotlabs/Module/Oauth2.php index db2687b4c..4b0b1991e 100644 --- a/Zotlabs/Module/Oauth2.php +++ b/Zotlabs/Module/Oauth2.php @@ -16,11 +16,11 @@ class Oauth2 extends Controller { if(! Apps::system_app_installed(local_channel(), 'OAuth2 Apps Manager')) return; - + if(x($_POST,'remove')){ check_form_security_token_redirectOnErr('oauth2', 'oauth2'); $name = ((x($_POST,'name')) ? escape_tags(trim($_POST['name'])) : ''); - logger("REMOVE! ".$name." uid: ".local_channel()); + logger("REMOVE! ".$name." uid: ".local_channel()); $key = $_POST['remove']; q("DELETE FROM oauth_authorization_codes WHERE client_id='%s' AND user_id=%d", dbesc($name), @@ -35,13 +35,13 @@ class Oauth2 extends Controller { intval(local_channel()) ); goaway(z_root()."/oauth2"); - return; + return; } - + if((argc() > 1) && (argv(1) === 'edit' || argv(1) === 'add') && x($_POST,'submit')) { - + check_form_security_token_redirectOnErr('oauth2', 'oauth2'); - + $name = ((x($_POST,'name')) ? escape_tags(trim($_POST['name'])) : ''); $secret = ((x($_POST,'secret')) ? escape_tags(trim($_POST['secret'])) : ''); $redirect = ((x($_POST,'redirect')) ? escape_tags(trim($_POST['redirect'])) : ''); @@ -53,7 +53,7 @@ class Oauth2 extends Controller { $ok = false; notice( t('Name and Secret are required') . EOL); } - + if($ok) { if ($_POST['submit']==t("Update")){ $r = q("UPDATE oauth_clients SET @@ -61,7 +61,7 @@ class Oauth2 extends Controller { client_secret = '%s', redirect_uri = '%s', grant_types = '%s', - scope = '%s', + scope = '%s', user_id = %d WHERE client_id='%s' and user_id = %s", dbesc($name), @@ -102,12 +102,10 @@ class Oauth2 extends Controller { if(! Apps::system_app_installed(local_channel(), 'OAuth2 Apps Manager')) { //Do not display any associated widgets at this point App::$pdl = ''; - - $o = '<b>' . t('OAuth2 Apps Manager App') . ' (' . t('Not Installed') . '):</b><br>'; - $o .= t('OAuth2 authenticatication tokens for mobile and remote apps'); - return $o; + $papp = Apps::get_papp('OAuth2 Apps Manager'); + return Apps::app_render($papp, 'module'); } - + if((argc() > 1) && (argv(1) === 'add')) { $tpl = get_markup_template("oauth2_edit.tpl"); $o .= replace_macros($tpl, array( @@ -123,20 +121,20 @@ class Oauth2 extends Controller { )); return $o; } - + if((argc() > 2) && (argv(1) === 'edit')) { $r = q("SELECT * FROM oauth_clients WHERE client_id='%s' AND user_id= %d", dbesc(argv(2)), intval(local_channel()) ); - + if (! $r){ notice(t('OAuth2 Application not found.')); return; } $app = $r[0]; - + $tpl = get_markup_template("oauth2_edit.tpl"); $o .= replace_macros($tpl, array( '$form_security_token' => get_form_security_token("oauth2"), @@ -151,10 +149,10 @@ class Oauth2 extends Controller { )); return $o; } - + if((argc() > 2) && (argv(1) === 'delete')) { check_form_security_token_redirectOnErr('oauth2', 'oauth2', 't'); - + $r = q("DELETE FROM oauth_clients WHERE client_id = '%s' AND user_id = %d", dbesc(argv(2)), intval(local_channel()) @@ -172,11 +170,11 @@ class Oauth2 extends Controller { intval(local_channel()) ); goaway(z_root()."/oauth2"); - return; + return; } - - $r = q("SELECT oauth_clients.*, oauth_access_tokens.access_token as oauth_token, (oauth_clients.user_id = %d) AS my + + $r = q("SELECT oauth_clients.*, oauth_access_tokens.access_token as oauth_token, (oauth_clients.user_id = %d) AS my FROM oauth_clients LEFT JOIN oauth_access_tokens ON oauth_clients.client_id=oauth_access_tokens.client_id AND oauth_clients.user_id=oauth_access_tokens.user_id @@ -184,7 +182,7 @@ class Oauth2 extends Controller { intval(local_channel()), intval(local_channel()) ); - + $tpl = get_markup_template("oauth2.tpl"); $o .= replace_macros($tpl, array( '$form_security_token' => get_form_security_token("oauth2"), @@ -199,7 +197,7 @@ class Oauth2 extends Controller { '$apps' => $r, )); return $o; - + } } diff --git a/Zotlabs/Module/Oep.php b/Zotlabs/Module/Oep.php index 75304161b..8e048a487 100644 --- a/Zotlabs/Module/Oep.php +++ b/Zotlabs/Module/Oep.php @@ -11,24 +11,24 @@ require_once('include/security.php'); class Oep extends \Zotlabs\Web\Controller { function init() { - + logger('oep: ' . print_r($_REQUEST,true), LOGGER_DEBUG, LOG_INFO); - + $html = ((argc() > 1 && argv(1) === 'html') ? true : false); if($_REQUEST['url']) { $_REQUEST['url'] = strip_zids($_REQUEST['url']); $url = $_REQUEST['url']; } - + if(! $url) http_status_exit(404, 'Not found'); - + $maxwidth = $_REQUEST['maxwidth']; $maxheight = $_REQUEST['maxheight']; $format = $_REQUEST['format']; if($format && $format !== 'json') http_status_exit(501, 'Not implemented'); - + if(fnmatch('*/photos/*/album/*',$url)) $arr = $this->oep_album_reply($_REQUEST); elseif(fnmatch('*/photos/*/image/*',$url)) @@ -47,7 +47,7 @@ class Oep extends \Zotlabs\Web\Controller { $arr = $this->oep_cards_reply($_REQUEST); elseif(fnmatch('*/articles/*',$url)) $arr = $this->oep_articles_reply($_REQUEST); - + if($arr) { if($html) { if($arr['type'] === 'rich') { @@ -61,13 +61,13 @@ class Oep extends \Zotlabs\Web\Controller { } killme(); } - + http_status_exit(404,'Not found'); - + } - + function oep_display_reply($args) { - + $ret = array(); $url = $args['url']; $maxwidth = intval($args['maxwidth']); @@ -77,14 +77,17 @@ class Oep extends \Zotlabs\Web\Controller { $res = $matches[2]; } - if(strpos($res,'b64.') === 0) { - $res = base64url_decode(substr($res,4)); + $res = unpack_link_id($res); + + if ($res === false) { + notice(t('Malformed message id.') . EOL); + return; } $item_normal = item_normal(); - $p = q("select * from item where mid like '%s' limit 1", - dbesc($res . '%') + $p = q("select * from item where mid = '%s' limit 1", + dbesc($res) ); if(! $p) @@ -92,7 +95,7 @@ class Oep extends \Zotlabs\Web\Controller { $c = channelx_by_n($p[0]['uid']); - + if(! ($c && $res)) return; @@ -100,60 +103,60 @@ class Oep extends \Zotlabs\Web\Controller { return; $sql_extra = item_permissions_sql($c['channel_id']); - - $p = q("select * from item where mid like '%s' and uid = %d $sql_extra $item_normal limit 1", - dbesc($res . '%'), + + $p = q("select * from item where mid = '%s' and uid = %d $sql_extra $item_normal limit 1", + dbesc($res), intval($c['channel_id']) ); if(! $p) return; - + xchan_query($p,true); $p = fetch_post_tags($p,true); // This function can get tripped up if the item is already a reshare - // (the multiple share declarations do not parse cleanly if nested) + // (the multiple share declarations do not parse cleanly if nested) // So build a template with a known nonsense string as the content, and then // replace that known string with the actual rendered content, sending // each content layer through bbcode() separately. $x = '2eGriplW^*Jmf4'; - + $o = "[share author='".urlencode($p[0]['author']['xchan_name']). - "' profile='".$p[0]['author']['xchan_url'] . - "' avatar='".$p[0]['author']['xchan_photo_s']. - "' link='".$p[0]['plink']. - "' auth='".((in_array($p[0]['author']['xchan_network'], ['zot6','zot'])) ? 'true' : 'false') . - "' posted='".$p[0]['created']. - "' message_id='".$p[0]['mid']."']"; + "' profile='".$p[0]['author']['xchan_url'] . + "' avatar='".$p[0]['author']['xchan_photo_s']. + "' link='".$p[0]['plink']. + "' auth='".(($p[0]['author']['xchan_network'] === 'zot6') ? 'true' : 'false') . + "' posted='".$p[0]['created']. + "' message_id='".$p[0]['mid']."']"; if($p[0]['title']) $o .= '[b]'.$p[0]['title'].'[/b]'."\r\n"; - $o .= $x; + $o .= $x; $o .= "[/share]"; $o = bbcode($o); - + $o = str_replace($x,bbcode($p[0]['body']),$o); - + $ret['type'] = 'rich'; - + $w = (($maxwidth) ? $maxwidth : 640); $h = (($maxheight) ? $maxheight : intval($w * 2 / 3)); - + $ret['html'] = '<div style="width: ' . $w . '; height: ' . $h . '; font-family: sans-serif,arial,freesans;" >' . $o . '</div>'; - + $ret['width'] = $w; $ret['height'] = $h; - + return $ret; - + } function oep_cards_reply($args) { - + $ret = []; $url = $args['url']; $maxwidth = intval($args['maxwidth']); @@ -164,7 +167,7 @@ class Oep extends \Zotlabs\Web\Controller { $res = $matches[3]; } if(! ($nick && $res)) - return $ret; + return $ret; $channel = channelx_by_nick($nick); @@ -187,8 +190,8 @@ class Oep extends \Zotlabs\Web\Controller { return $ret; } - $r = q("select * from item - where item.uid = %d and item_type = %d + $r = q("select * from item + where item.uid = %d and item_type = %d $sql_extra order by item.created desc", intval($channel['channel_id']), intval(ITEM_TYPE_CARD) @@ -208,39 +211,39 @@ class Oep extends \Zotlabs\Web\Controller { $x = '2eGriplW^*Jmf4'; - + $o = "[share author='".urlencode($p[0]['author']['xchan_name']). - "' profile='".$p[0]['author']['xchan_url'] . - "' avatar='".$p[0]['author']['xchan_photo_s']. - "' link='".$p[0]['plink']. - "' auth='".((in_array($p[0]['author']['xchan_network'], ['zot6','zot'])) ? 'true' : 'false') . - "' posted='".$p[0]['created']. - "' message_id='".$p[0]['mid']."']"; - if($p[0]['title']) + "' profile='".$p[0]['author']['xchan_url'] . + "' avatar='".$p[0]['author']['xchan_photo_s']. + "' link='".$p[0]['plink']. + "' auth='".(($p[0]['author']['xchan_network'] === 'zot6') ? 'true' : 'false') . + "' posted='".$p[0]['created']. + "' message_id='".$p[0]['mid']."']"; + if($p[0]['title']) $o .= '[b]'.$p[0]['title'].'[/b]'."\r\n"; - $o .= $x; + $o .= $x; $o .= "[/share]"; $o = bbcode($o); - + $o = str_replace($x,bbcode($p[0]['body']),$o); - + $ret['type'] = 'rich'; - + $w = (($maxwidth) ? $maxwidth : 640); $h = (($maxheight) ? $maxheight : intval($w * 2 / 3)); - + $ret['html'] = '<div style="width: ' . $w . '; height: ' . $h . '; font-family: sans-serif,arial,freesans;" >' . $o . '</div>'; - + $ret['width'] = $w; $ret['height'] = $h; - + return $ret; - + } function oep_articles_reply($args) { - + $ret = []; $url = $args['url']; $maxwidth = intval($args['maxwidth']); @@ -251,7 +254,7 @@ class Oep extends \Zotlabs\Web\Controller { $res = $matches[3]; } if(! ($nick && $res)) - return $ret; + return $ret; $channel = channelx_by_nick($nick); @@ -273,8 +276,8 @@ class Oep extends \Zotlabs\Web\Controller { return $ret; } - $r = q("select * from item - where item.uid = %d and item_type = %d + $r = q("select * from item + where item.uid = %d and item_type = %d $sql_extra order by item.created desc", intval($channel['channel_id']), intval(ITEM_TYPE_ARTICLE) @@ -294,137 +297,137 @@ class Oep extends \Zotlabs\Web\Controller { $x = '2eGriplW^*Jmf4'; - + $o = "[share author='".urlencode($p[0]['author']['xchan_name']). - "' profile='".$p[0]['author']['xchan_url'] . - "' avatar='".$p[0]['author']['xchan_photo_s']. - "' link='".$p[0]['plink']. - "' auth='".((in_array($p[0]['author']['xchan_network'], ['zot6','zot'])) ? 'true' : 'false') . - "' posted='".$p[0]['created']. - "' message_id='".$p[0]['mid']."']"; - if($p[0]['title']) - $o .= '[b]'.$p[0]['title'].'[/b]'."\r\n"; + "' profile='".$p[0]['author']['xchan_url'] . + "' avatar='".$p[0]['author']['xchan_photo_s']. + "' link='".$p[0]['plink']. + "' auth='".(($p[0]['author']['xchan_network'] === 'zot6') ? 'true' : 'false') . + "' posted='".$p[0]['created']. + "' message_id='".$p[0]['mid']."']"; + if($p[0]['title']) + $o .= '[b]'.$p[0]['title'].'[/b]'."\r\n"; - $o .= $x; + $o .= $x; $o .= "[/share]"; $o = bbcode($o); - + $o = str_replace($x,bbcode($p[0]['body']),$o); - + $ret['type'] = 'rich'; - + $w = (($maxwidth) ? $maxwidth : 640); $h = (($maxheight) ? $maxheight : intval($w * 2 / 3)); - + $ret['html'] = '<div style="width: ' . $w . '; height: ' . $h . '; font-family: sans-serif,arial,freesans;" >' . $o . '</div>'; - + $ret['width'] = $w; $ret['height'] = $h; - + return $ret; - + } - + function oep_mid_reply($args) { - + $ret = array(); $url = $args['url']; $maxwidth = intval($args['maxwidth']); $maxheight = intval($args['maxheight']); - + if(preg_match('#//(.*?)/(.*?)/(.*?)/(.*?)mid\=(.*?)(&|$)#',$url,$matches)) { $chn = $matches[3]; $res = $matches[5]; } - + if(! ($chn && $res)) return; $c = q("select * from channel where channel_address = '%s' limit 1", dbesc($chn) ); - + if(! $c) return; if(! perm_is_allowed($c[0]['channel_id'],get_observer_hash(),'view_stream')) return; - + $sql_extra = item_permissions_sql($c[0]['channel_id']); - + $p = q("select * from item where mid = '%s' and uid = %d $sql_extra limit 1", dbesc($res), intval($c[0]['channel_id']) ); if(! $p) return; - + xchan_query($p,true); $p = fetch_post_tags($p,true); // This function can get tripped up if the item is already a reshare - // (the multiple share declarations do not parse cleanly if nested) + // (the multiple share declarations do not parse cleanly if nested) // So build a template with a known nonsense string as the content, and then // replace that known string with the actual rendered content, sending // each content layer through bbcode() separately. $x = '2eGriplW^*Jmf4'; - + $o = "[share author='".urlencode($p[0]['author']['xchan_name']). "' profile='".$p[0]['author']['xchan_url'] . "' avatar='".$p[0]['author']['xchan_photo_s']. "' link='".$p[0]['plink']. - "' auth='".((in_array($p[0]['author']['xchan_network'], ['zot6','zot'])) ? 'true' : 'false') . + "' auth='".(($p[0]['author']['xchan_network'] === 'zot6') ? 'true' : 'false') . "' posted='".$p[0]['created']. "' message_id='".$p[0]['mid']."']"; if($p[0]['title']) $o .= '[b]'.$p[0]['title'].'[/b]'."\r\n"; - $o .= $x; + $o .= $x; $o .= "[/share]"; $o = bbcode($o); - + $o = str_replace($x,bbcode($p[0]['body']),$o); $ret['type'] = 'rich'; - + $w = (($maxwidth) ? $maxwidth : 640); $h = (($maxheight) ? $maxheight : intval($w * 2 / 3)); - + $ret['html'] = '<div style="width: ' . $w . '; height: ' . $h . '; font-family: sans-serif,arial,freesans;" >' . $o . '</div>'; - + $ret['width'] = $w; $ret['height'] = $h; - + return $ret; - + } - + function oep_profile_reply($args) { - - + + require_once('include/channel.php'); $url = $args['url']; - + if(preg_match('#//(.*?)/(.*?)/(.*?)(/|\?|&|$)#',$url,$matches)) { $chn = $matches[3]; } - + if(! $chn) return; - + $c = channelx_by_nick($chn); - + if(! $c) return; - - + + $maxwidth = intval($args['maxwidth']); $maxheight = intval($args['maxheight']); - + $width = 800; $height = 375; - + if($maxwidth) { $width = $maxwidth; $height = (375 / 800) * $width; @@ -434,59 +437,59 @@ class Oep extends \Zotlabs\Web\Controller { $width = (800 / 375) * $maxheight; $height = $maxheight; } - } + } $ret = array(); - + $ret['type'] = 'rich'; $ret['width'] = intval($width); $ret['height'] = intval($height); - + $ret['html'] = get_zcard_embed($c,get_observer_hash(),array('width' => $width, 'height' => $height)); - + return $ret; - + } - + function oep_album_reply($args) { - + $ret = array(); $url = $args['url']; $maxwidth = intval($args['maxwidth']); $maxheight = intval($args['maxheight']); - + if(preg_match('|//(.*?)/(.*?)/(.*?)/album/|',$url,$matches)) { $chn = $matches[3]; $res = basename($url); } - + if(! ($chn && $res)) return; $c = q("select * from channel where channel_address = '%s' limit 1", dbesc($chn) ); - + if(! $c) return; - + if(! perm_is_allowed($c[0]['channel_id'],get_observer_hash(),'view_files')) return; $sql_extra = permissions_sql($c[0]['channel_id']); - + $p = q("select resource_id from photo where album = '%s' and uid = %d and imgscale = 0 $sql_extra order by created desc limit 1", dbesc($res), intval($c[0]['channel_id']) ); if(! $p) return; - + $res = $p[0]['resource_id']; - + $r = q("select height, width, imgscale, resource_id from photo where uid = %d and resource_id = '%s' $sql_extra order by imgscale asc", intval($c[0]['channel_id']), dbesc($res) ); - + if($r) { foreach($r as $rr) { $foundres = false; @@ -494,62 +497,62 @@ class Oep extends \Zotlabs\Web\Controller { continue; if($maxwidth && $rr['width'] > $maxwidth) continue; - $foundres = true; + $foundres = true; break; } - + if($foundres) { $ret['type'] = 'link'; $ret['thumbnail_url'] = z_root() . '/photo/' . '/' . $rr['resource_id'] . '-' . $rr['imgscale']; $ret['thumbnail_width'] = $rr['width']; $ret['thumbnail_height'] = $rr['height']; } - - + + } return $ret; - + } - - + + function oep_phototop_reply($args) { - + $ret = array(); $url = $args['url']; $maxwidth = intval($args['maxwidth']); $maxheight = intval($args['maxheight']); - + if(preg_match('|//(.*?)/(.*?)/(.*?)$|',$url,$matches)) { $chn = $matches[3]; } - + if(! $chn) return; $c = q("select * from channel where channel_address = '%s' limit 1", dbesc($chn) ); - + if(! $c) return; - + if(! perm_is_allowed($c[0]['channel_id'],get_observer_hash(),'view_files')) return; $sql_extra = permissions_sql($c[0]['channel_id']); - + $p = q("select resource_id from photo where uid = %d and imgscale = 0 $sql_extra order by created desc limit 1", intval($c[0]['channel_id']) ); if(! $p) return; - + $res = $p[0]['resource_id']; - + $r = q("select height, width, imgscale, resource_id from photo where uid = %d and resource_id = '%s' $sql_extra order by imgscale asc", intval($c[0]['channel_id']), dbesc($res) ); - + if($r) { foreach($r as $rr) { $foundres = false; @@ -557,42 +560,42 @@ class Oep extends \Zotlabs\Web\Controller { continue; if($maxwidth && $rr['width'] > $maxwidth) continue; - $foundres = true; + $foundres = true; break; } - + if($foundres) { $ret['type'] = 'link'; $ret['thumbnail_url'] = z_root() . '/photo/' . '/' . $rr['resource_id'] . '-' . $rr['imgscale']; $ret['thumbnail_width'] = $rr['width']; $ret['thumbnail_height'] = $rr['height']; } - - + + } return $ret; - + } - - + + function oep_photo_reply($args) { - + $ret = array(); $url = $args['url']; $maxwidth = intval($args['maxwidth']); $maxheight = intval($args['maxheight']); - + if(preg_match('|//(.*?)/(.*?)/(.*?)/image/|',$url,$matches)) { $chn = $matches[3]; $res = basename($url); } - + if(! ($chn && $res)) return; $c = q("select * from channel where channel_address = '%s' limit 1", dbesc($chn) ); - + if(! $c) return; @@ -600,13 +603,13 @@ class Oep extends \Zotlabs\Web\Controller { return; $sql_extra = permissions_sql($c[0]['channel_id']); - - + + $r = q("select height, width, imgscale, resource_id from photo where uid = %d and resource_id = '%s' $sql_extra order by imgscale asc", intval($c[0]['channel_id']), dbesc($res) ); - + if($r) { foreach($r as $rr) { $foundres = false; @@ -614,20 +617,20 @@ class Oep extends \Zotlabs\Web\Controller { continue; if($maxwidth && $rr['width'] > $maxwidth) continue; - $foundres = true; + $foundres = true; break; } - + if($foundres) { $ret['type'] = 'link'; $ret['thumbnail_url'] = z_root() . '/photo/' . '/' . $rr['resource_id'] . '-' . $rr['imgscale']; $ret['thumbnail_width'] = $rr['width']; $ret['thumbnail_height'] = $rr['height']; } - - + + } return $ret; - + } } diff --git a/Zotlabs/Module/Outbox.php b/Zotlabs/Module/Outbox.php new file mode 100644 index 000000000..503b464d1 --- /dev/null +++ b/Zotlabs/Module/Outbox.php @@ -0,0 +1,124 @@ +<?php + +namespace Zotlabs\Module; + +use App; +use Zotlabs\Lib\Activity; +use Zotlabs\Lib\ActivityStreams; +use Zotlabs\Lib\Config; +use Zotlabs\Lib\ThreadListener; +use Zotlabs\Web\Controller; +use Zotlabs\Web\HTTPSig; + +class Outbox extends Controller { + + function init() { + if (ActivityStreams::is_as_request()) { + + if (observer_prohibited(true)) { + killme(); + } + + $channel = channelx_by_nick(argv(1)); + if (!$channel) { + killme(); + } + + if (intval($channel['channel_system'])) { + killme(); + } + + $sigdata = HTTPSig::verify(($_SERVER['REQUEST_METHOD'] === 'POST') ? file_get_contents('php://input') : EMPTY_STR); + if ($sigdata['portable_id'] && $sigdata['header_valid']) { + $portable_id = $sigdata['portable_id']; + if (!check_channelallowed($portable_id)) { + http_status_exit(403, 'Permission denied'); + } + if (!check_siteallowed($sigdata['signer'])) { + http_status_exit(403, 'Permission denied'); + } + observer_auth($portable_id); + } + elseif (Config::get('system', 'require_authenticated_fetch', false)) { + http_status_exit(403, 'Permission denied'); + } + + $observer_hash = get_observer_hash(); + + $params = []; + + $params['begin'] = ((x($_REQUEST, 'date_begin')) ? $_REQUEST['date_begin'] : NULL_DATE); + $params['end'] = ((x($_REQUEST, 'date_end')) ? $_REQUEST['date_end'] : ''); + $params['type'] = 'json'; + $params['pages'] = ((x($_REQUEST, 'pages')) ? intval($_REQUEST['pages']) : 0); + $params['top'] = ((x($_REQUEST, 'top')) ? intval($_REQUEST['top']) : 0); + $params['direction'] = ((x($_REQUEST, 'direction')) ? dbesc($_REQUEST['direction']) : 'desc'); // unimplemented + $params['cat'] = ((x($_REQUEST, 'cat')) ? escape_tags($_REQUEST['cat']) : ''); + $params['compat'] = 1; + + $total = items_fetch( + [ + 'total' => true, + 'wall' => 1, + 'datequery' => $params['end'], + 'datequery2' => $params['begin'], + 'direction' => dbesc($params['direction']), + 'pages' => $params['pages'], + 'order' => dbesc('post'), + 'top' => $params['top'], + 'cat' => $params['cat'], + 'compat' => $params['compat'] + ], $channel, $observer_hash, CLIENT_MODE_NORMAL, App::$module + ); + + if ($total) { + App::set_pager_total($total); + App::set_pager_itemspage(30); + } + + if (App::$pager['unset'] && $total > 30) { + $ret = Activity::paged_collection_init($total, App::$query_string); + } + else { + + $items = items_fetch( + [ + 'wall' => 1, + 'datequery' => $params['end'], + 'datequery2' => $params['begin'], + 'records' => intval(App::$pager['itemspage']), + 'start' => intval(App::$pager['start']), + 'direction' => dbesc($params['direction']), + 'pages' => $params['pages'], + 'order' => dbesc('post'), + 'top' => $params['top'], + 'cat' => $params['cat'], + 'compat' => $params['compat'] + ], $channel, $observer_hash, CLIENT_MODE_NORMAL, App::$module + ); + + if ($items && $observer_hash) { + + // check to see if this observer is a connection. If not, register any items + // belonging to this channel for notification of deletion/expiration + + $x = q("select abook_id from abook where abook_channel = %d and abook_xchan = '%s'", + intval($channel['channel_id']), + dbesc($observer_hash) + ); + if (!$x) { + foreach ($items as $item) { + if (strpos($item['mid'], z_root()) === 0) { + ThreadListener::store($item['mid'], $observer_hash); + } + } + } + } + + $ret = Activity::encode_item_collection($items, App::$query_string, 'OrderedCollection', $total); + } + + as_return_and_die($ret, $channel); + } + } +} diff --git a/Zotlabs/Module/Owa.php b/Zotlabs/Module/Owa.php index 561e35754..e30aa5fb4 100644 --- a/Zotlabs/Module/Owa.php +++ b/Zotlabs/Module/Owa.php @@ -11,9 +11,9 @@ use Zotlabs\Web\Controller; * See spec/OpenWebAuth/Home.md * Requests to this endpoint should be signed using HTTP Signatures * using the 'Authorization: Signature' authentication method - * If the signature verifies a token is returned. + * If the signature verifies a token is returned. * - * This token may be exchanged for an authenticated cookie. + * This token may be exchanged for an authenticated cookie. */ class Owa extends Controller { @@ -31,36 +31,34 @@ class Owa extends Controller { if ($sigblock) { $keyId = $sigblock['keyId']; if ($keyId) { - $r = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash - where ( hubloc_addr = '%s' or hubloc_id_url = '%s' ) ", - dbesc(str_replace('acct:','',$keyId)), + $r = q("SELECT * FROM hubloc LEFT JOIN xchan ON hubloc_hash = xchan_hash + WHERE hubloc_id_url = '%s' AND xchan_pubkey != '' ", dbesc($keyId) ); if (! $r) { $found = discover_by_webbie(str_replace('acct:','',$keyId)); if ($found) { - $r = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash - where ( hubloc_addr = '%s' or hubloc_id_url = '%s' ) ", - dbesc(str_replace('acct:','',$keyId)), + $r = q("SELECT * FROM hubloc LEFT JOIN xchan ON hubloc_hash = xchan_hash + WHERE hubloc_id_url = '%s' AND xchan_pubkey != '' ", dbesc($keyId) ); } } if ($r) { foreach ($r as $hubloc) { - $verified = HTTPSig::verify(file_get_contents('php://input')); + $verified = HTTPSig::verify(file_get_contents('php://input'), $hubloc['xchan_pubkey']); if ($verified && $verified['header_signed'] && $verified['header_valid'] && ($verified['content_valid'] || (! $verified['content_signed']))) { logger('OWA header: ' . print_r($verified,true),LOGGER_DATA); - logger('OWA success: ' . $hubloc['hubloc_addr'],LOGGER_DATA); + logger('OWA success: ' . $hubloc['hubloc_id_url'],LOGGER_DATA); $ret['success'] = true; $token = random_string(32); - Verify::create('owt',0,$token,$hubloc['hubloc_addr']); + Verify::create('owt',0,$token,$hubloc['hubloc_id_url']); $result = ''; openssl_public_encrypt($token,$result,$hubloc['xchan_pubkey']); $ret['encrypted_token'] = base64url_encode($result); break; } else { - logger('OWA fail: ' . $hubloc['hubloc_id'] . ' ' . $hubloc['hubloc_addr']); + logger('OWA fail: ' . $hubloc['hubloc_id'] . ' ' . $hubloc['hubloc_id_url']); } } } diff --git a/Zotlabs/Module/Pconfig.php b/Zotlabs/Module/Pconfig.php index b2b5d4386..8a71ab974 100644 --- a/Zotlabs/Module/Pconfig.php +++ b/Zotlabs/Module/Pconfig.php @@ -8,16 +8,16 @@ use Zotlabs\Lib\Libsync; class Pconfig extends \Zotlabs\Web\Controller { function post() { - + if(! local_channel()) return; - - + + if($_SESSION['delegate']) return; - + check_form_security_token_redirectOnErr('/pconfig', 'pconfig'); - + $cat = trim(escape_tags($_POST['cat'])); $k = trim(escape_tags($_POST['k'])); $v = trim($_POST['v']); @@ -27,16 +27,16 @@ class Pconfig extends \Zotlabs\Web\Controller { if (preg_match('|^a:[0-9]+:{.*}$|s',$v) || preg_match('|O:8:"stdClass":[0-9]+:{.*}$|s',$v)) { return; } - + if(in_array(argv(2),$this->disallowed_pconfig())) { notice( t('This setting requires special processing and editing has been blocked.') . EOL); return; } - + if(strpos($k,'password') !== false) { - $v = z_obscure($v); + $v = obscurify($v); } - + set_pconfig(local_channel(),$cat,$k,$v); Libsync::build_sync_packet(); @@ -46,24 +46,24 @@ class Pconfig extends \Zotlabs\Web\Controller { goaway(z_root() . '/pconfig/' . $cat . '/' . $k); } - - + + function get() { - + if(! local_channel()) { return login(); } - + $content = '<h3>' . t('Configuration Editor') . '</h3>'; $content .= '<div class="descriptive-paragraph">' . t('Warning: Changing some settings could render your channel inoperable. Please leave this page unless you are comfortable with and knowledgeable about how to correctly use this feature.') . '</div>' . EOL . EOL; - - - + + + if(argc() == 3) { $content .= '<a href="pconfig">pconfig[' . local_channel() . ']</a>' . EOL; $content .= '<a href="pconfig/' . escape_tags(argv(1)) . '">pconfig[' . local_channel() . '][' . escape_tags(argv(1)) . ']</a>' . EOL . EOL; $content .= '<a href="pconfig/' . escape_tags(argv(1)) . '/' . escape_tags(argv(2)) . '" >pconfig[' . local_channel() . '][' . escape_tags(argv(1)) . '][' . escape_tags(argv(2)) . ']</a> = ' . get_pconfig(local_channel(),escape_tags(argv(1)),escape_tags(argv(2))) . EOL; - + if(in_array(argv(2),$this->disallowed_pconfig())) { notice( t('This setting requires special processing and editing has been blocked.') . EOL); return $content; @@ -71,8 +71,8 @@ class Pconfig extends \Zotlabs\Web\Controller { else $content .= $this->pconfig_form(escape_tags(argv(1)),escape_tags(argv(2))); } - - + + if(argc() == 2) { $content .= '<a href="pconfig">pconfig[' . local_channel() . ']</a>' . EOL; load_pconfig(local_channel(),escape_tags(argv(1))); @@ -80,9 +80,9 @@ class Pconfig extends \Zotlabs\Web\Controller { $content .= '<a href="pconfig/' . escape_tags(argv(1)) . '/' . $k . '" >pconfig[' . local_channel() . '][' . escape_tags(argv(1)) . '][' . $k . ']</a> = ' . escape_tags($x) . EOL; } } - + if(argc() == 1) { - + $r = q("select * from pconfig where uid = " . local_channel()); if($r) { foreach($r as $rr) { @@ -91,33 +91,33 @@ class Pconfig extends \Zotlabs\Web\Controller { } } return $content; - + } - - + + function pconfig_form($cat,$k) { - + $o = '<form action="pconfig" method="post" >'; $o .= '<input type="hidden" name="form_security_token" value="' . get_form_security_token('pconfig') . '" />'; - + $v = get_pconfig(local_channel(),$cat,$k); - if(strpos($k,'password') !== false) - $v = z_unobscure($v); - + if(strpos($k,'password') !== false) + $v = unobscurify($v); + $o .= '<input type="hidden" name="cat" value="' . $cat . '" />'; $o .= '<input type="hidden" name="k" value="' . $k . '" />'; - + if(strpos($v,"\n")) $o .= '<textarea name="v" >' . escape_tags($v) . '</textarea>'; else $o .= '<input type="text" name="v" value="' . escape_tags($v) . '" />'; - - $o .= EOL . EOL; + + $o .= EOL . EOL; $o .= '<input type="submit" name="submit" value="' . t('Submit') . '" />'; $o .= '</form>'; - + return $o; - + } @@ -127,5 +127,5 @@ class Pconfig extends \Zotlabs\Web\Controller { 'permissions_role' ); } - + } diff --git a/Zotlabs/Module/Pdledit.php b/Zotlabs/Module/Pdledit.php index 36201544f..3b94c9611 100644 --- a/Zotlabs/Module/Pdledit.php +++ b/Zotlabs/Module/Pdledit.php @@ -27,10 +27,10 @@ class Pdledit extends Controller { info( t('Layout updated.') . EOL); goaway(z_root() . '/pdledit/' . $_REQUEST['module']); } - - + + function get() { - + if(! local_channel()) { notice( t('Permission denied.') . EOL); return; @@ -39,10 +39,8 @@ class Pdledit extends Controller { if(! Apps::system_app_installed(local_channel(), 'PDL Editor')) { //Do not display any associated widgets at this point App::$pdl = ''; - - $o = '<b>' . t('PDL Editor App') . ' (' . t('Not Installed') . '):</b><br>'; - $o .= t('Provides the ability to edit system page layouts'); - return $o; + $papp = Apps::get_papp('PDL Editor'); + return Apps::app_render($papp, 'module'); } if(argc() > 2 && argv(2) === 'reset') { @@ -68,7 +66,7 @@ class Pdledit extends Controller { $edited[] = substr(str_replace('.pdl','',$rv['k']),4); } } - + $files = glob('Zotlabs/Module/*.php'); if($files) { foreach($files as $f) { @@ -81,21 +79,21 @@ class Pdledit extends Controller { } $o .= '</div>'; - + // list module pdl files return $o; } - + $t = get_pconfig(local_channel(),'system',$module); $s = file_get_contents(theme_include($module)); if(! $t) { $t = $s; - } + } if(! $t) { notice( t('Layout not found.') . EOL); return ''; } - + $o = replace_macros(get_markup_template('pdledit.tpl'),array( '$header' => t('Edit System Page Description'), '$mname' => t('Module Name:'), @@ -107,8 +105,8 @@ class Pdledit extends Controller { '$content' => htmlspecialchars($t,ENT_COMPAT,'UTF-8'), '$submit' => t('Submit') )); - + return $o; } - + } diff --git a/Zotlabs/Module/Permcats.php b/Zotlabs/Module/Permcats.php index 6a599282c..58566373a 100644 --- a/Zotlabs/Module/Permcats.php +++ b/Zotlabs/Module/Permcats.php @@ -40,16 +40,16 @@ class Permcats extends Controller { } } } - + \Zotlabs\Lib\Permcat::update(local_channel(),$name,$pcarr); Libsync::build_sync_packet(); info( t('Permission category saved.') . EOL); - + return; } - + function get() { @@ -59,16 +59,14 @@ class Permcats extends Controller { if(! Apps::system_app_installed(local_channel(), 'Permission Categories')) { //Do not display any associated widgets at this point App::$pdl = ''; - - $o = '<b>' . t('Permission Categories App') . ' (' . t('Not Installed') . '):</b><br>'; - $o .= t('Create custom connection permission limits'); - return $o; + $papp = Apps::get_papp('Permission Categories'); + return Apps::app_render($papp, 'module'); } $channel = App::get_channel(); - if(argc() > 1) - $name = hex2bin(argv(1)); + if(argc() > 1) + $name = hex2bin(argv(1)); if(argc() > 2 && argv(2) === 'drop') { \Zotlabs\Lib\Permcat::delete(local_channel(),$name); @@ -130,5 +128,5 @@ class Permcats extends Controller { )); return $o; } - + } diff --git a/Zotlabs/Module/Photo.php b/Zotlabs/Module/Photo.php index c88696578..87697f5a7 100644 --- a/Zotlabs/Module/Photo.php +++ b/Zotlabs/Module/Photo.php @@ -35,7 +35,7 @@ class Photo extends \Zotlabs\Web\Controller { call_hooks('cache_mode_hook', $cache_mode); $observer_xchan = get_observer_hash(); - $cachecontrol = ''; + $cachecontrol = ', no-cache'; if(isset($type)) { @@ -81,18 +81,18 @@ class Photo extends \Zotlabs\Web\Controller { else $data = dbunescbin($r[0]['content']); } - } - if(! $data) { - $d = [ 'imgscale' => $resolution, 'channel_id' => $uid, 'default' => $default, 'data' => '', 'mimetype' => '' ]; - call_hooks('get_profile_photo',$d); - - $resolution = $d['imgscale']; - $uid = $d['channel_id']; - $default = $d['default']; - $data = $d['data']; - $mimetype = $d['mimetype']; - $modified = 0; + if(! $data) { + $d = [ 'imgscale' => $resolution, 'channel_id' => $uid, 'default' => $default, 'data' => '', 'mimetype' => '' ]; + call_hooks('get_profile_photo',$d); + + $resolution = $d['imgscale']; + $uid = $d['channel_id']; + $default = $d['default']; + $data = $d['data']; + $mimetype = $d['mimetype']; + $modified = 0; + } } if(! $data) { @@ -102,7 +102,7 @@ class Photo extends \Zotlabs\Web\Controller { $modified = filemtime($default); } - $cachecontrol = ', must-revalidate'; + $cachecontrol .= ', must-revalidate'; } else { @@ -147,7 +147,7 @@ class Photo extends \Zotlabs\Web\Controller { ); if($r) { $allowed = (-1); - + $filename = $r[0]['filename']; $u = intval($r[0]['photo_usage']); if($u) { $allowed = 1; @@ -169,6 +169,7 @@ class Photo extends \Zotlabs\Web\Controller { $url = z_root() . '/sslify/' . $filename . '?f=&url=' . urlencode($url); goaway($url); } + $cachecontrol = ''; } } } @@ -179,7 +180,7 @@ class Photo extends \Zotlabs\Web\Controller { $channel = channelx_by_n($r[0]['uid']); // Now we'll see if we can access the photo - $e = q("SELECT * FROM photo WHERE resource_id = '%s' AND imgscale = %d $sql_extra LIMIT 1", + $e = q("SELECT * FROM photo WHERE resource_id = '%s' AND imgscale = %d LIMIT 1", dbesc($photo), intval($resolution) ); @@ -193,9 +194,9 @@ class Photo extends \Zotlabs\Web\Controller { $mimetype = $e[0]['mimetype']; $modified = strtotime($e[0]['edited'] . 'Z'); - if(intval($e[0]['os_storage'])) { + if(intval($e[0]['os_storage'])) $streaming = $data; - } + if($e[0]['allow_cid'] != '' || $e[0]['allow_gid'] != '' || $e[0]['deny_gid'] != '' || $e[0]['deny_gid'] != '') $prvcachecontrol = 'no-store, no-cache, must-revalidate'; } @@ -271,7 +272,7 @@ class Photo extends \Zotlabs\Web\Controller { // in the event that infrastructure caching is present. $smaxage = intval($maxage/12); - header("Cache-Control: no-cache, s-maxage=" . $smaxage . ", max-age=" . $maxage . $cachecontrol); + header("Cache-Control: s-maxage=" . $smaxage . ", max-age=" . $maxage . $cachecontrol); } @@ -281,7 +282,7 @@ class Photo extends \Zotlabs\Web\Controller { header("Content-Length: " . (isset($filesize) ? $filesize : strlen($data))); // If it's a file resource, stream it. - if($streaming && $channel) { + if($streaming) { if(strpos($streaming,'store') !== false) $istream = fopen($streaming,'rb'); else diff --git a/Zotlabs/Module/Photos.php b/Zotlabs/Module/Photos.php index fa9216c97..57126df5f 100644 --- a/Zotlabs/Module/Photos.php +++ b/Zotlabs/Module/Photos.php @@ -16,66 +16,66 @@ require_once('include/text.php'); class Photos extends \Zotlabs\Web\Controller { function init() { - + if(observer_prohibited()) { return; } - + if(argc() > 1) { $nick = argv(1); - + profile_load($nick); - + $channelx = channelx_by_nick($nick); - + if(! $channelx) return; - + \App::$data['channel'] = $channelx; - + $observer = \App::get_observer(); \App::$data['observer'] = $observer; - + $observer_xchan = (($observer) ? $observer['xchan_hash'] : ''); - + head_set_icon(\App::$data['channel']['xchan_photo_s']); - + \App::$page['htmlhead'] .= "<script> var profile_uid = " . ((\App::$data['channel']) ? \App::$data['channel']['channel_id'] : 0) . "; </script>" ; - + } - + return; } - - - + + + function post() { - + logger('mod-photos: photos_post: begin' , LOGGER_DEBUG); - + logger('mod_photos: REQUEST ' . print_r($_REQUEST,true), LOGGER_DATA); logger('mod_photos: FILES ' . print_r($_FILES,true), LOGGER_DATA); - + $ph = photo_factory(''); - + $phototypes = $ph->supportedTypes(); - + $can_post = false; - + $page_owner_uid = \App::$data['channel']['channel_id']; - + if(perm_is_allowed($page_owner_uid,get_observer_hash(),'write_storage')) $can_post = true; - + if(! $can_post) { notice( t('Permission denied.') . EOL ); if(is_ajax()) killme(); return; } - + $s = abook_self($page_owner_uid); - + if(! $s) { notice( t('Page owner information could not be retrieved.') . EOL); logger('mod_photos: post: unable to locate contact record for page owner. uid=' . $page_owner_uid); @@ -83,30 +83,30 @@ class Photos extends \Zotlabs\Web\Controller { killme(); return; } - - $owner_record = $s[0]; - + + $owner_record = $s[0]; + $acl = new \Zotlabs\Access\AccessList(\App::$data['channel']); - + if((argc() > 3) && (argv(2) === 'album')) { - + $album = argv(3); if(! photos_album_exists($page_owner_uid, get_observer_hash(), $album)) { notice( t('Album not found.') . EOL); goaway(z_root() . '/' . $_SESSION['photo_return']); } - - + + /* * DELETE photo album and all its photos */ - + if($_REQUEST['dropalbum'] == t('Delete Album')) { - - + + $folder_hash = ''; - + $r = q("select * from attach where is_dir = 1 and uid = %d and hash = '%s'", intval($page_owner_uid), dbesc($album) @@ -116,13 +116,13 @@ class Photos extends \Zotlabs\Web\Controller { return; } $folder_hash = $r[0]['hash']; - - + + $res = array(); $admin_delete = false; // get the list of photos we are about to delete - + if(remote_channel() && (! local_channel())) { $str = photos_album_get_db_idstr($page_owner_uid,$album,remote_channel()); } @@ -139,7 +139,7 @@ class Photos extends \Zotlabs\Web\Controller { if(! $str) { goaway(z_root() . '/' . $_SESSION['photo_return']); } - + $r = q("select id from item where resource_id in ( $str ) and resource_type = 'photo' and uid = %d " . item_normal(), intval($page_owner_uid) ); @@ -148,34 +148,35 @@ class Photos extends \Zotlabs\Web\Controller { attach_delete($page_owner_uid, $i['resource_id'], true ); } } - + // remove the associated photos in case they weren't attached to an item - + q("delete from photo where resource_id in ( $str ) and uid = %d", intval($page_owner_uid) ); - + // @FIXME do the same for the linked attach - + if($folder_hash) { attach_delete($page_owner_uid, $folder_hash, true ); - if(! $admin_delete) { + if(! $admin_delete) { $sync = attach_export_data(\App::$data['channel'],$folder_hash, true); - - if($sync) + + if($sync) Libsync::build_sync_packet($page_owner_uid,array('file' => array($sync))); } } - + } - + goaway(z_root() . '/photos/' . \App::$data['channel']['channel_address']); + } - + if((argc() > 2) && (x($_REQUEST,'delete')) && ($_REQUEST['delete'] === t('Delete Photo'))) { // same as above but remove single photo - + $ob_hash = get_observer_hash(); if(! $ob_hash) goaway(z_root() . '/' . $_SESSION['photo_return']); @@ -185,18 +186,18 @@ class Photos extends \Zotlabs\Web\Controller { intval(local_channel()), dbesc(argv(2)) ); - + if($r) { attach_delete($page_owner_uid, $r[0]['resource_id'], true ); $sync = attach_export_data(\App::$data['channel'],$r[0]['resource_id'], true); - - if($sync) + + if($sync) Libsync::build_sync_packet($page_owner_uid,array('file' => array($sync))); } elseif(is_site_admin()) { // If the admin deletes a photo, don't sync attach_delete($page_owner_uid, argv(2), true); - } + } goaway(z_root() . '/photos/' . \App::$data['channel']['channel_address'] . '/album/' . $_SESSION['album_return']); @@ -208,10 +209,10 @@ class Photos extends \Zotlabs\Web\Controller { intval($page_owner_uid) ); if(($m) && ($m[0]['folder'] != $_POST['move_to_album'])) { - attach_move($page_owner_uid,argv(2),$_POST['move_to_album']); + attach_move($page_owner_uid,argv(2),$_POST['move_to_album']); $sync = attach_export_data(\App::$data['channel'], argv(2), false); - if($sync) + if($sync) Libsync::build_sync_packet($page_owner_uid,array('file' => array($sync))); if(! ($_POST['desc'] && $_POST['newtag'])) @@ -220,28 +221,28 @@ class Photos extends \Zotlabs\Web\Controller { } if((argc() > 2) && ((x($_POST,'desc') !== false) || (x($_POST,'newtag') !== false))) { - + $desc = ((x($_POST,'desc')) ? notags(trim($_POST['desc'])) : ''); $rawtags = ((x($_POST,'newtag')) ? notags(trim($_POST['newtag'])) : ''); $item_id = ((x($_POST,'item_id')) ? intval($_POST['item_id']) : 0); $is_nsfw = ((x($_POST,'adult')) ? intval($_POST['adult']) : 0); - + $acl->set_from_array($_POST); $perm = $acl->get(); - + $resource_id = argv(2); - - if((x($_POST,'rotate') !== false) && + + if((x($_POST,'rotate') !== false) && ( (intval($_POST['rotate']) == 1) || (intval($_POST['rotate']) == 2) )) { logger('rotate'); - + $r = q("select * from photo where resource_id = '%s' and uid = %d and imgscale = 0 limit 1", dbesc($resource_id), intval($page_owner_uid) ); if(count($r)) { - + $ph = photo_factory(@file_get_contents(dbunescbin($r[0]['content'])), $r[0]['mimetype']); if($ph->is_valid()) { $rotate_deg = ( (intval($_POST['rotate']) == 1) ? 270 : 90 ); @@ -255,12 +256,12 @@ class Photos extends \Zotlabs\Web\Controller { dbesc($resource_id), intval($page_owner_uid) ); - + $ph->saveImage(dbunescbin($r[0]['content'])); - - $arr = [ + + $arr = [ 'aid' => get_account_id(), - 'uid' => intval($page_owner_uid), + 'uid' => intval($page_owner_uid), 'resource_id' => dbesc($resource_id), 'filename' => $r[0]['filename'], 'imgscale' => 0, @@ -277,28 +278,31 @@ class Photos extends \Zotlabs\Web\Controller { unset($arr['os_syspath']); - if($width > 1024 || $height > 1024) + $width = $r[0]['width']; + $height = $r[0]['height']; + + if($width > 1024 || $height > 1024) $ph->scaleImage(1024); $ph->storeThumbnail($arr, PHOTO_RES_1024); - if($width > 640 || $height > 640) + if($width > 640 || $height > 640) $ph->scaleImage(640); $ph->storeThumbnail($arr, PHOTO_RES_640); - if($width > 320 || $height > 320) + if($width > 320 || $height > 320) $ph->scaleImage(320); $ph->storeThumbnail($arr, PHOTO_RES_320); } } } - + $p = q("SELECT mimetype, is_nsfw, description, resource_id, imgscale, allow_cid, allow_gid, deny_cid, deny_gid FROM photo WHERE resource_id = '%s' AND uid = %d ORDER BY imgscale DESC", dbesc($resource_id), intval($page_owner_uid) ); if($p) { $ext = $phototypes[$p[0]['mimetype']]; - + $r = q("UPDATE photo SET description = '%s', allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' WHERE resource_id = '%s' AND uid = %d", dbesc($desc), dbesc($perm['allow_cid']), @@ -309,9 +313,7 @@ class Photos extends \Zotlabs\Web\Controller { intval($page_owner_uid) ); } - - $item_private = (($str_contact_allow || $str_group_allow || $str_contact_deny || $str_group_deny) ? true : false); - + $old_is_nsfw = $p[0]['is_nsfw']; if($old_is_nsfw != $is_nsfw) { $r = q("update photo set is_nsfw = %d where resource_id = '%s' and uid = %d", @@ -320,31 +322,31 @@ class Photos extends \Zotlabs\Web\Controller { intval($page_owner_uid) ); } - + /* Don't make the item visible if the only change was the album name */ - + $visibility = 0; if($p[0]['description'] !== $desc || strlen($rawtags)) $visibility = 1; - + if(! $item_id) { $item_id = photos_create_item(\App::$data['channel'],get_observer_hash(),$p[0],$visibility); - + } - + if($item_id) { $r = q("SELECT * FROM item WHERE id = %d AND uid = %d LIMIT 1", intval($item_id), intval($page_owner_uid) ); - + if($r) { $old_tag = $r[0]['tag']; $old_inform = $r[0]['inform']; } } - - + + // make sure the linked item has the same permissions as the photo regardless of any other changes $x = q("update item set allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s', item_private = %d where id = %d", @@ -355,7 +357,7 @@ class Photos extends \Zotlabs\Web\Controller { intval($acl->is_private()), intval($item_id) ); - + // make sure the attach has the same permissions as the photo regardless of any other changes $x = q("update attach set allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' where hash = '%s' and uid = %d and is_photo = 1", dbesc($perm['allow_cid']), @@ -365,46 +367,46 @@ class Photos extends \Zotlabs\Web\Controller { dbesc($resource_id), intval($page_owner_uid) ); - - - + + + if(strlen($rawtags)) { - + $str_tags = ''; $inform = ''; - + // if the new tag doesn't have a namespace specifier (@foo or #foo) give it a mention - + $x = substr($rawtags,0,1); if($x !== '@' && $x !== '#') $rawtags = '@' . $rawtags; - + require_once('include/text.php'); $profile_uid = \App::$profile['profile_uid']; - + $results = linkify_tags($rawtags, (local_channel()) ? local_channel() : $profile_uid); - + $success = $results['success']; $post_tags = array(); - + foreach($results as $result) { $success = $result['success']; if($success['replaced']) { $post_tags[] = array( - 'uid' => $profile_uid, + 'uid' => $profile_uid, 'ttype' => $success['termtype'], 'otype' => TERM_OBJ_POST, 'term' => $success['term'], 'url' => $success['url'] - ); + ); } } - + $r = q("select * from item where id = %d and uid = %d limit 1", intval($item_id), intval($page_owner_uid) ); - + if($r) { $r = fetch_post_tags($r,true); $datarray = $r[0]; @@ -412,42 +414,42 @@ class Photos extends \Zotlabs\Web\Controller { if((! array_key_exists('term',$datarray)) || (! is_array($datarray['term']))) $datarray['term'] = $post_tags; else - $datarray['term'] = array_merge($datarray['term'],$post_tags); + $datarray['term'] = array_merge($datarray['term'],$post_tags); } item_store_update($datarray,$execflag); } - + } $sync = attach_export_data(\App::$data['channel'],$resource_id); - - if($sync) + + if($sync) Libsync::build_sync_packet($page_owner_uid,array('file' => array($sync))); - + goaway(z_root() . '/' . $_SESSION['photo_return']); return; // NOTREACHED - - + + } - - + + /** * default post action - upload a photo */ - + $channel = \App::$data['channel']; $observer = \App::$data['observer']; - + $_REQUEST['source'] = 'photos'; require_once('include/attach.php'); - + if(! local_channel()) { $_REQUEST['contact_allow'] = expand_acl($channel['channel_allow_cid']); $_REQUEST['group_allow'] = expand_acl($channel['channel_allow_gid']); $_REQUEST['contact_deny'] = expand_acl($channel['channel_deny_cid']); $_REQUEST['group_deny'] = expand_acl($channel['channel_deny_gid']); } - + $matches = []; $partial = false; @@ -467,7 +469,7 @@ class Photos extends \Zotlabs\Web\Controller { if($x['partial']) { header('Range: bytes=0-' . (($x['length']) ? $x['length'] - 1 : 0)); - json_return_and_die($result); + json_return_and_die($x); } else { header('Range: bytes=0-' . (($x['size']) ? $x['size'] - 1 : 0)); @@ -481,7 +483,7 @@ class Photos extends \Zotlabs\Web\Controller { ]; } } - else { + else { if(! array_key_exists('userfile',$_FILES)) { $_FILES['userfile'] = [ 'name' => $_FILES['files']['name'], @@ -494,53 +496,56 @@ class Photos extends \Zotlabs\Web\Controller { } $r = attach_store($channel,get_observer_hash(), '', $_REQUEST); - + if(! $r['success']) { notice($r['message'] . EOL); goaway(z_root() . '/photos/' . \App::$data['channel']['channel_address']); - } + } + + if(is_ajax()) + killme(); goaway(z_root() . '/photos/' . \App::$data['channel']['channel_address'] . '/album/' . $r['data']['folder']); - + } - - - + + + function get() { - + // URLs: // photos/name // photos/name/album/xxxxx (xxxxx is album name) // photos/name/image/xxxxx - - + + if(observer_prohibited()) { notice( t('Public access denied.') . EOL); return; } - + $unsafe = ((array_key_exists('unsafe',$_REQUEST) && $_REQUEST['unsafe']) ? 1 : 0); - + require_once('include/bbcode.php'); require_once('include/security.php'); require_once('include/conversation.php'); - + if(! x(\App::$data,'channel')) { notice( t('No photos selected') . EOL ); return; } - + $ph = photo_factory(''); $phototypes = $ph->supportedTypes(); - + $_SESSION['photo_return'] = \App::$cmd; - + // - // Parse arguments + // Parse arguments // - + $can_comment = perm_is_allowed(\App::$profile['profile_uid'],get_observer_hash(),'post_comments'); - + if(argc() > 3) { $datatype = argv(2); $datum = argv(3); @@ -552,70 +557,70 @@ class Photos extends \Zotlabs\Web\Controller { else $datatype = 'summary'; } - + if(argc() > 4) $cmd = argv(4); else $cmd = 'view'; - + // // Setup permissions structures // - + $can_post = false; $visitor = 0; - - + + $owner_uid = \App::$data['channel']['channel_id']; $owner_aid = \App::$data['channel']['channel_account_id']; - + $observer = \App::get_observer(); - + $can_post = perm_is_allowed($owner_uid,$observer['xchan_hash'],'write_storage'); $can_view = perm_is_allowed($owner_uid,$observer['xchan_hash'],'view_storage'); - + if(! $can_view) { notice( t('Access to this item is restricted.') . EOL); return; } - + $sql_item = item_permissions_sql($owner_uid,get_observer_hash()); $sql_extra = permissions_sql($owner_uid,get_observer_hash(),'photo'); $sql_attach = permissions_sql($owner_uid,get_observer_hash(),'attach'); nav_set_selected('Photos'); - + $o = '<script src="vendor/blueimp/jquery-file-upload/js/vendor/jquery.ui.widget.js"></script> <script src="vendor/blueimp/jquery-file-upload/js/jquery.iframe-transport.js"></script> <script src="vendor/blueimp/jquery-file-upload/js/jquery.fileupload.js"></script>'; - $o .= "<script> var profile_uid = " . \App::$profile['profile_uid'] + $o .= "<script> var profile_uid = " . \App::$profile['profile_uid'] . "; var netargs = '?f='; var profile_page = " . \App::$pager['page'] . "; </script>\r\n"; - + $_is_owner = (local_channel() && (local_channel() == $owner_uid)); - + /** * Display upload form */ - + if( $can_post) { - + $uploader = ''; - + $ret = array('post_url' => z_root() . '/photos/' . \App::$data['channel']['channel_address'], 'addon_text' => $uploader, 'default_upload' => true); - + call_hooks('photo_upload_form',$ret); - + /* Show space usage */ - + $r = q("select sum(filesize) as total from photo where aid = %d and imgscale = 0 ", intval(\App::$data['channel']['channel_account_id']) ); - - + + $limit = engr_units_to_bytes(service_class_fetch(\App::$data['channel']['channel_id'],'photo_upload_limit')); if($limit !== false) { $usage_message = sprintf( t("%1$.2f MB of %2$.2f MB photo storage used."), $r[0]['total'] / 1024000, $limit / 1024000 ); @@ -623,22 +628,22 @@ class Photos extends \Zotlabs\Web\Controller { else { $usage_message = sprintf( t('%1$.2f MB photo storage used.'), $r[0]['total'] / 1024000 ); } - + if($_is_owner) { $channel = \App::get_channel(); - + $acl = new \Zotlabs\Access\AccessList($channel); $channel_acl = $acl->get(); - + $lockstate = (($acl->is_private()) ? 'lock' : 'unlock'); } - + $aclselect = (($_is_owner) ? populate_acl($channel_acl,false, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_storage')) : ''); - + // this is wrong but is to work around an issue with js_upload wherein it chokes if these variables - // don't exist. They really should be set to a parseable representation of the channel's default permissions - // which can be processed by getSelected() - + // don't exist. They really should be set to a parseable representation of the channel's default permissions + // which can be processed by getSelected() + if(! $aclselect) { $aclselect = '<input id="group_allow" type="hidden" name="allow_gid[]" value="" /><input id="contact_allow" type="hidden" name="allow_cid[]" value="" /><input id="group_deny" type="hidden" name="deny_gid[]" value="" /><input id="contact_deny" type="hidden" name="deny_cid[]" value="" />'; } @@ -648,11 +653,11 @@ class Photos extends \Zotlabs\Web\Controller { if($datum) { $h = attach_by_hash_nodata($datum,get_observer_hash()); $selname = $h['data']['display_path']; - } + } + - $albums = ((array_key_exists('albums', \App::$data)) ? \App::$data['albums'] : photos_albums_list(\App::$data['channel'],\App::$data['observer'])); - + if(! $selname) { $def_album = get_pconfig(\App::$data['channel']['channel_id'],'system','photo_path'); if($def_album) { @@ -660,7 +665,7 @@ class Photos extends \Zotlabs\Web\Controller { $albums['album'][] = array('text' => $selname); } } - + $tpl = get_markup_template('photos_upload.tpl'); $upload_form = replace_macros($tpl,array( '$pagename' => t('Upload Photos'), @@ -685,22 +690,22 @@ class Photos extends \Zotlabs\Web\Controller { '$default' => (($ret['default_upload']) ? true : false), '$uploadurl' => $ret['post_url'], '$submit' => t('Upload') - + )); - + } - + // // dispatch request // - + /* * Display a single photo album */ - + if($datatype === 'album') { - head_add_link([ + head_add_link([ 'rel' => 'alternate', 'type' => 'application/json+oembed', 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . \App::$query_string), @@ -710,7 +715,7 @@ class Photos extends \Zotlabs\Web\Controller { if($x = photos_album_exists($owner_uid, get_observer_hash(), $datum)) { \App::set_pager_itemspage(30); $album = $x['display_path']; - } + } else { goaway(z_root() . '/photos/' . \App::$data['channel']['channel_address']); } @@ -721,7 +726,7 @@ class Photos extends \Zotlabs\Web\Controller { $order = 'DESC'; $r = q("SELECT p.resource_id, p.id, p.filename, p.mimetype, p.imgscale, p.description, p.created FROM photo p INNER JOIN - (SELECT resource_id, max(imgscale) imgscale FROM photo left join attach on folder = '%s' and photo.resource_id = attach.hash WHERE attach.uid = %d AND imgscale <= 4 AND photo_usage IN ( %d, %d ) and is_nsfw = %d $sql_extra GROUP BY resource_id) ph + (SELECT resource_id, max(imgscale) imgscale FROM photo left join attach on folder = '%s' and photo.resource_id = attach.hash WHERE attach.uid = %d AND imgscale <= 4 AND photo_usage IN ( %d, %d ) and is_nsfw = %d $sql_extra GROUP BY resource_id) ph ON (p.resource_id = ph.resource_id AND p.imgscale = ph.imgscale) ORDER BY created $order LIMIT %d OFFSET %d", dbesc($x['hash']), @@ -739,9 +744,9 @@ class Photos extends \Zotlabs\Web\Controller { if($can_post) { $album_e = $album; $albums = ((array_key_exists('albums', \App::$data)) ? \App::$data['albums'] : photos_albums_list(\App::$data['channel'],\App::$data['observer'])); - + // @fixme - syncronise actions with DAV - + // $edit_tpl = get_markup_template('album_edit.tpl'); // $album_edit = replace_macros($edit_tpl,array( // '$nametext' => t('Enter a new album name'), @@ -753,32 +758,32 @@ class Photos extends \Zotlabs\Web\Controller { // '$submit' => t('Submit'), // '$dropsubmit' => t('Delete Album') // )); - + } - + if($_GET['order'] === 'posted') $order = array(t('Show Newest First'), z_root() . '/photos/' . \App::$data['channel']['channel_address'] . '/album/' . $datum); else $order = array(t('Show Oldest First'), z_root() . '/photos/' . \App::$data['channel']['channel_address'] . '/album/' . $datum . '?f=&order=posted'); - + $photos = array(); if(count($r)) { $twist = 'rotright'; foreach($r as $rr) { - + if($twist == 'rotright') $twist = 'rotleft'; else $twist = 'rotright'; - + $ext = $phototypes[$rr['mimetype']]; - + $imgalt_e = $rr['filename']; $desc_e = $rr['description']; - + $imagelink = (z_root() . '/photos/' . \App::$data['channel']['channel_address'] . '/image/' . $rr['resource_id'] . (($_GET['order'] === 'posted') ? '?f=&order=posted' : '')); - + $photos[] = array( 'id' => $rr['id'], 'twist' => ' ' . $twist . rand(2,4), @@ -793,7 +798,7 @@ class Photos extends \Zotlabs\Web\Controller { ); } } - + if($_REQUEST['aj']) { if($photos) { $o = replace_macros(get_markup_template('photosajax.tpl'),array( @@ -821,71 +826,71 @@ class Photos extends \Zotlabs\Web\Controller { '$upload_form' => $upload_form, '$usage' => $usage_message )); - + } - + if((! $photos) && ($_REQUEST['aj'])) { $o .= '<div id="content-complete"></div>'; echo $o; killme(); } - + return $o; - - } - - /** + + } + + /** * Display one photo */ - + if($datatype === 'image') { - + \App::$page['htmlhead'] .= "\r\n" . '<link rel="alternate" type="application/json+oembed" href="' . z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . \App::$cmd) . '" title="oembed" />' . "\r\n"; - + $x = q("select folder from attach where hash = '%s' and uid = %d $sql_attach limit 1", dbesc($datum), intval($owner_uid) ); // fetch image, item containing image, then comments - - $ph = q("SELECT id,aid,uid,xchan,resource_id,created,edited,title,description,album,filename,mimetype,height,width,filesize,imgscale,photo_usage,is_nsfw,allow_cid,allow_gid,deny_cid,deny_gid FROM photo WHERE uid = %d AND resource_id = '%s' + + $ph = q("SELECT id,aid,uid,xchan,resource_id,created,edited,title,description,album,filename,mimetype,height,width,filesize,imgscale,photo_usage,is_nsfw,allow_cid,allow_gid,deny_cid,deny_gid FROM photo WHERE uid = %d AND resource_id = '%s' $sql_extra ORDER BY imgscale ASC ", intval($owner_uid), dbesc($datum) ); - + if(! ($ph && $x)) { - + /* Check again - this time without specifying permissions */ - + $ph = q("SELECT id FROM photo WHERE uid = %d AND resource_id = '%s' LIMIT 1", intval($owner_uid), dbesc($datum) ); - if($ph) + if($ph) notice( t('Permission denied. Access to this item may be restricted.') . EOL); else notice( t('Photo not available') . EOL ); return; } - - - + + + $prevlink = ''; $nextlink = ''; - - if($_GET['order'] === 'posted') + + if(isset($_GET['order']) && $_GET['order'] === 'posted') $order = 'ASC'; else $order = 'DESC'; - + $prvnxt = q("SELECT hash FROM attach WHERE folder = '%s' AND uid = %d AND is_photo = 1 $sql_attach ORDER BY created $order ", dbesc($x[0]['folder']), intval($owner_uid) - ); + ); if(count($prvnxt)) { for($z = 0; $z < count($prvnxt); $z++) { @@ -899,12 +904,12 @@ class Photos extends \Zotlabs\Web\Controller { break; } } - - $prevlink = z_root() . '/photos/' . \App::$data['channel']['channel_address'] . '/image/' . $prvnxt[$prv]['hash'] . (($_GET['order'] === 'posted') ? '?f=&order=posted' : ''); - $nextlink = z_root() . '/photos/' . \App::$data['channel']['channel_address'] . '/image/' . $prvnxt[$nxt]['hash'] . (($_GET['order'] === 'posted') ? '?f=&order=posted' : ''); + + $prevlink = z_root() . '/photos/' . \App::$data['channel']['channel_address'] . '/image/' . $prvnxt[$prv]['hash'] . (($order == 'ASC') ? '?f=&order=posted' : ''); + $nextlink = z_root() . '/photos/' . \App::$data['channel']['channel_address'] . '/image/' . $prvnxt[$nxt]['hash'] . (($order == 'ASC') ? '?f=&order=posted' : ''); } - - + + if(count($ph) == 1) $hires = $lores = $ph[0]; if(count($ph) > 1) { @@ -917,76 +922,76 @@ class Photos extends \Zotlabs\Web\Controller { $lores = $ph[1]; } } - + $album_link = z_root() . '/photos/' . \App::$data['channel']['channel_address'] . '/album/' . $x[0]['folder']; $tools = Null; $lock = Null; - + if($can_post && ($ph[0]['uid'] == $owner_uid)) { $tools = array( 'profile'=>array(z_root() . '/profile_photo/use/'.$ph[0]['resource_id'], t('Use as profile photo')), 'cover'=>array(z_root() . '/cover_photo/use/'.$ph[0]['resource_id'], t('Use as cover photo')), ); } - + // lockstate $lockstate = ( ( (strlen($ph[0]['allow_cid']) || strlen($ph[0]['allow_gid']) || strlen($ph[0]['deny_cid']) || strlen($ph[0]['deny_gid'])) ) ? array('lock', t('Private Photo')) : array('unlock', Null)); - + \App::$page['htmlhead'] .= '<script>$(document).keydown(function(event) {' . "\n"; if($prevlink) \App::$page['htmlhead'] .= 'if(event.ctrlKey && event.keyCode == 37) { event.preventDefault(); window.location.href = \'' . $prevlink . '\'; }' . "\n"; if($nextlink) \App::$page['htmlhead'] .= 'if(event.ctrlKey && event.keyCode == 39) { event.preventDefault(); window.location.href = \'' . $nextlink . '\'; }' . "\n"; \App::$page['htmlhead'] .= '});</script>'; - + if($prevlink) $prevlink = array($prevlink, t('Previous')); - + $photo = array( 'href' => z_root() . '/photo/' . $hires['resource_id'] . '-' . $hires['imgscale'] . '.' . $phototypes[$hires['mimetype']], 'title'=> t('View Full Size'), 'src' => z_root() . '/photo/' . $lores['resource_id'] . '-' . $lores['imgscale'] . '.' . $phototypes[$lores['mimetype']] ); - + if($nextlink) $nextlink = array($nextlink, t('Next')); - - + + // Do we have an item for this photo? - - $linked_items = q("SELECT * FROM item WHERE resource_id = '%s' and resource_type = 'photo' + + $linked_items = q("SELECT * FROM item WHERE resource_id = '%s' and resource_type = 'photo' $sql_item LIMIT 1", dbesc($datum) ); - + $map = null; - + if($linked_items) { - + xchan_query($linked_items); $linked_items = fetch_post_tags($linked_items,true); - + $link_item = $linked_items[0]; $item_normal = item_normal(); - - $r = q("select * from item where parent_mid = '%s' + + $r = q("select * from item where parent_mid = '%s' $item_normal and uid = %d $sql_item ", dbesc($link_item['mid']), intval($link_item['uid']) - + ); - + if($r) { xchan_query($r); $r = fetch_post_tags($r,true); $r = conv_sort($r,'commented'); } - + $tags = array(); - if($link_item['term']) { + if(x($link_item, 'term')) { $cnt = 0; foreach($link_item['term'] as $t) { $tags[$cnt] = array(0 => format_term_for_display($t)); @@ -997,23 +1002,23 @@ class Photos extends \Zotlabs\Web\Controller { $cnt ++; } } - + if((local_channel()) && (local_channel() == $link_item['uid'])) { q("UPDATE item SET item_unseen = 0 WHERE parent = %d and uid = %d and item_unseen = 1", intval($link_item['parent']), intval(local_channel()) ); } - + if($link_item['coord']) { $map = generate_map($link_item['coord']); } } - + // logger('mod_photo: link_item' . print_r($link_item,true)); - - // FIXME - remove this when we move to conversation module - + + // FIXME - remove this when we move to conversation module + $r = $r[0]['children']; $edit = null; @@ -1023,11 +1028,11 @@ class Photos extends \Zotlabs\Web\Controller { $caption_e = $ph[0]['description']; $aclselect_e = (($_is_owner) ? populate_acl($ph[0], true, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_storage')) : ''); $albums = ((array_key_exists('albums', \App::$data)) ? \App::$data['albums'] : photos_albums_list(\App::$data['channel'],\App::$data['observer'])); - + $_SESSION['album_return'] = bin2hex($ph[0]['album']); $folder_list = attach_folder_select_list($ph[0]['uid']); - + $edit = [ 'edit' => t('Edit photo'), 'id' => $link_item['id'], @@ -1058,17 +1063,17 @@ class Photos extends \Zotlabs\Web\Controller { 'delete' => t('Delete Photo') ]; } - + if(count($linked_items)) { - + $cmnt_tpl = get_markup_template('comment_item.tpl'); $tpl = get_markup_template('photo_item.tpl'); $return_url = \App::$cmd; - + $like_tpl = get_markup_template('like_noshare.tpl'); - + $likebuttons = ''; - + if($observer && ($can_post || $can_comment)) { $likebuttons = [ 'id' => $link_item['id'], @@ -1078,12 +1083,12 @@ class Photos extends \Zotlabs\Web\Controller { 'wait' => t('Please wait') ]; } - + $comments = ''; if(! $r) { if($observer && ($can_post || $can_comment)) { $commentbox = replace_macros($cmnt_tpl,array( - '$return_path' => '', + '$return_path' => '', '$mode' => 'photos', '$jsreload' => $return_url, '$type' => 'wall-comment', @@ -1101,28 +1106,28 @@ class Photos extends \Zotlabs\Web\Controller { )); } } - + $alike = array(); $dlike = array(); - + $like = ''; $dislike = ''; - + $conv_responses = array( 'like' => array('title' => t('Likes','title')),'dislike' => array('title' => t('Dislikes','title')), - 'agree' => array('title' => t('Agree','title')),'disagree' => array('title' => t('Disagree','title')), 'abstain' => array('title' => t('Abstain','title')), + 'agree' => array('title' => t('Agree','title')),'disagree' => array('title' => t('Disagree','title')), 'abstain' => array('title' => t('Abstain','title')), 'attendyes' => array('title' => t('Attending','title')), 'attendno' => array('title' => t('Not attending','title')), 'attendmaybe' => array('title' => t('Might attend','title')) ); - - - - + + + + if($r) { - + foreach($r as $item) { builtin_activity_puller($item, $conv_responses); } - + $like_count = ((x($alike,$link_item['mid'])) ? $alike[$link_item['mid']] : ''); $like_list = ((x($alike,$link_item['mid'])) ? $alike[$link_item['mid'] . '-l'] : ''); @@ -1133,7 +1138,7 @@ class Photos extends \Zotlabs\Web\Controller { $like_list_part = ''; } $like_button_label = tt('Like','Likes',$like_count,'noun'); - + //if (feature_enabled($conv->get_profile_owner(),'dislike')) { $dislike_count = ((x($dlike,$link_item['mid'])) ? $dlike[$link_item['mid']] : ''); $dislike_list = ((x($dlike,$link_item['mid'])) ? $dlike[$link_item['mid'] . '-l'] : ''); @@ -1145,44 +1150,42 @@ class Photos extends \Zotlabs\Web\Controller { $dislike_list_part = ''; } //} - - + + $like = ((isset($alike[$link_item['mid']])) ? format_like($alike[$link_item['mid']],$alike[$link_item['mid'] . '-l'],'like',$link_item['mid']) : ''); $dislike = ((isset($dlike[$link_item['mid']])) ? format_like($dlike[$link_item['mid']],$dlike[$link_item['mid'] . '-l'],'dislike',$link_item['mid']) : ''); - + // display comments - + foreach($r as $item) { $comment = ''; $template = $tpl; $sparkle = ''; - + if(((activity_match($item['verb'],ACTIVITY_LIKE)) || (activity_match($item['verb'],ACTIVITY_DISLIKE))) && ($item['id'] != $item['parent'])) continue; - + $redirect_url = z_root() . '/redir/' . $item['cid'] ; - - + + $profile_url = zid($item['author']['xchan_url']); $sparkle = ''; - - + + $profile_name = $item['author']['xchan_name']; $profile_avatar = $item['author']['xchan_photo_m']; - + $profile_link = $profile_url; - + $drop = ''; - + if($observer['xchan_hash'] === $item['author_xchan'] || $observer['xchan_hash'] === $item['owner_xchan']) $drop = replace_macros(get_markup_template('photo_drop.tpl'), array('$id' => $item['id'], '$delete' => t('Delete'))); - - + $name_e = $profile_name; $title_e = $item['title']; - unobscure($item); $body_e = prepare_text($item['body'],$item['mimetype']); - + $comments .= replace_macros($template,array( '$id' => $item['id'], '$mode' => 'photos', @@ -1197,9 +1200,9 @@ class Photos extends \Zotlabs\Web\Controller { '$drop' => $drop, '$comment' => $comment )); - + } - + if($observer && ($can_post || $can_comment)) { $commentbox = replace_macros($cmnt_tpl,array( '$return_path' => '', @@ -1216,20 +1219,20 @@ class Photos extends \Zotlabs\Web\Controller { '$ww' => '' )); } - + } $paginate = paginate($a); } - + $album_e = array($album_link,$ph[0]['album']); $like_e = $like; $dislike_e = $dislike; - - + + $response_verbs = array('like'); if(feature_enabled($owner_uid,'dislike')) $response_verbs[] = 'dislike'; - + $responses = get_responses($conv_responses,$response_verbs,'',$link_item); $hookdata = [ @@ -1238,7 +1241,7 @@ class Photos extends \Zotlabs\Web\Controller { 'nickname' => \App::$data['channel']['channel_address'] ]; call_hooks('photo_view_filter', $hookdata); - + $photo_tpl = get_markup_template('photo_view.tpl'); $o .= replace_macros($photo_tpl, array( '$id' => $ph[0]['id'], @@ -1255,7 +1258,7 @@ class Photos extends \Zotlabs\Web\Controller { '$tag_hdr' => t('In This Photo:'), '$tags' => $tags, 'responses' => $responses, - '$edit' => $edit, + '$edit' => $edit, '$map' => $map, '$map_text' => t('Map'), '$likebuttons' => $likebuttons, @@ -1277,26 +1280,26 @@ class Photos extends \Zotlabs\Web\Controller { '$paginate' => $paginate, '$onclick' => $hookdata['onclick'] )); - + \App::$data['photo_html'] = $o; - + return $o; } - + // Default - show recent photos with upload link (if applicable) //$o = ''; - + \App::$page['htmlhead'] .= "\r\n" . '<link rel="alternate" type="application/json+oembed" href="' . z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . \App::$cmd) . '" title="oembed" />' . "\r\n"; - + \App::set_pager_itemspage(30); - - $r = q("SELECT p.resource_id, p.id, p.filename, p.mimetype, p.album, p.imgscale, p.created, p.display_path - FROM photo p - INNER JOIN ( SELECT resource_id, max(imgscale) imgscale FROM photo - WHERE photo.uid = %d AND photo_usage IN ( %d, %d ) - AND is_nsfw = %d $sql_extra group by resource_id ) ph - ON (p.resource_id = ph.resource_id and p.imgscale = ph.imgscale) + + $r = q("SELECT p.resource_id, p.id, p.filename, p.mimetype, p.album, p.imgscale, p.created, p.display_path + FROM photo p + INNER JOIN ( SELECT resource_id, max(imgscale) imgscale FROM photo + WHERE photo.uid = %d AND photo_usage IN ( %d, %d ) + AND is_nsfw = %d $sql_extra group by resource_id ) ph + ON (p.resource_id = ph.resource_id and p.imgscale = ph.imgscale) ORDER by p.created DESC LIMIT %d OFFSET %d", intval(\App::$data['channel']['channel_id']), intval(PHOTO_NORMAL), @@ -1305,9 +1308,9 @@ class Photos extends \Zotlabs\Web\Controller { intval(\App::$pager['itemspage']), intval(\App::$pager['start']) ); - - - + + + $photos = array(); if($r) { $twist = 'rotright'; @@ -1321,7 +1324,7 @@ class Photos extends \Zotlabs\Web\Controller { else $twist = 'rotright'; $ext = $phototypes[$rr['mimetype']]; - + $alt_e = $rr['filename']; $name_e = dirname($rr['display_path']); @@ -1335,11 +1338,11 @@ class Photos extends \Zotlabs\Web\Controller { 'album' => array( 'name' => $name_e, ), - + ); } } - + if($_REQUEST['aj']) { if($photos) { $o = replace_macros(get_markup_template('photosajax.tpl'),array( @@ -1355,7 +1358,7 @@ class Photos extends \Zotlabs\Web\Controller { } else { $o .= "<script> var page_query = '" . escape_tags(urlencode($_GET['q'])) . "'; var extra_args = '" . extra_query_args() . "' ; </script>"; - $tpl = get_markup_template('photos_recent.tpl'); + $tpl = get_markup_template('photos_recent.tpl'); $o .= replace_macros($tpl, array( '$title' => t('Recent Photos'), '$album_id' => bin2hex(t('Recent Photos')), @@ -1365,18 +1368,18 @@ class Photos extends \Zotlabs\Web\Controller { '$upload_form' => $upload_form, '$usage' => $usage_message )); - + } - + if((! $photos) && ($_REQUEST['aj'])) { $o .= '<div id="content-complete"></div>'; echo $o; killme(); } - + // paginate($a); return $o; } - - + + } diff --git a/Zotlabs/Module/Pin.php b/Zotlabs/Module/Pin.php index 63b28754b..f82327ce6 100644 --- a/Zotlabs/Module/Pin.php +++ b/Zotlabs/Module/Pin.php @@ -6,6 +6,7 @@ namespace Zotlabs\Module; */ use App; +use Zotlabs\Lib\Libsync; class Pin extends \Zotlabs\Web\Controller { @@ -36,7 +37,7 @@ class Pin extends \Zotlabs\Web\Controller { http_status_exit(404, 'Not found'); } - $midb64 = 'b64.' . base64url_encode($r[0]['mid']); + $midb64 = gen_link_id($r[0]['mid']); $pinned = (in_array($midb64, get_pconfig($r[0]['uid'], 'pinned', $r[0]['item_type'], [])) ? true : false); switch(argv(1)) { @@ -64,6 +65,6 @@ class Pin extends \Zotlabs\Web\Controller { http_status_exit(404, 'Not found'); } - build_sync_packet($r[0]['uid'], [ 'config' ]); + Libsync::build_sync_packet($r[0]['uid'], [ 'config' ]); } } diff --git a/Zotlabs/Module/Ping.php b/Zotlabs/Module/Ping.php deleted file mode 100644 index 6e8042eaf..000000000 --- a/Zotlabs/Module/Ping.php +++ /dev/null @@ -1,707 +0,0 @@ -<?php - -namespace Zotlabs\Module; - -use Zotlabs\Lib\Apps; - -require_once('include/bbcode.php'); - -/** - * @brief Ping Controller. - * - */ -class Ping extends \Zotlabs\Web\Controller { - - /** - * @brief do several updates when pinged. - * - * This function does several tasks. Whenever called it checks for new messages, - * introductions, notifications, etc. and returns a json with the results. - * - * @result JSON - */ - function init() { - - $result = array(); - $notifs = array(); - - $result['notify'] = 0; - $result['home'] = 0; - $result['network'] = 0; - $result['intros'] = 0; - $result['mail'] = 0; - $result['register'] = 0; - $result['events'] = 0; - $result['events_today'] = 0; - $result['birthdays'] = 0; - $result['birthdays_today'] = 0; - $result['all_events'] = 0; - $result['all_events_today'] = 0; - $result['notice'] = []; - $result['info'] = []; - $result['pubs'] = 0; - $result['files'] = 0; - $result['forums'] = 0; - $result['forums_sub'] = []; - - if(! $_SESSION['static_loadtime']) - $_SESSION['static_loadtime'] = datetime_convert(); - - $t0 = dba_timer(); - - header("content-type: application/json"); - - $vnotify = false; - - $item_normal = item_normal(); - - if(local_channel()) { - $vnotify = get_pconfig(local_channel(),'system','vnotify'); - $evdays = intval(get_pconfig(local_channel(),'system','evdays')); - $ob_hash = get_observer_hash(); - } - - // if unset show all visual notification types - if($vnotify === false) - $vnotify = (-1); - if($evdays < 1) - $evdays = 3; - - /** - * If you have several windows open to this site and switch to a different channel - * in one of them, the others may get into a confused state showing you a page or options - * on that page which were only valid under the old identity. You session has changed. - * Therefore we send a notification of this fact back to the browser where it is picked up - * in javascript and which reloads the page it is on so that it is valid under the context - * of the now current channel. - */ - - $result['invalid'] = ((intval($_GET['uid'])) && (intval($_GET['uid']) != local_channel()) ? 1 : 0); - - /** - * Send all system messages (alerts) to the browser. - * Some are marked as informational and some represent - * errors or serious notifications. These typically - * will popup on the current page (no matter what page it is) - */ - - if(x($_SESSION, 'sysmsg')){ - foreach ($_SESSION['sysmsg'] as $m){ - $result['notice'][] = array('message' => $m); - } - unset($_SESSION['sysmsg']); - } - if(x($_SESSION, 'sysmsg_info')){ - foreach ($_SESSION['sysmsg_info'] as $m){ - $result['info'][] = array('message' => $m); - } - unset($_SESSION['sysmsg_info']); - } - if(! ($vnotify & VNOTIFY_INFO)) - $result['info'] = array(); - if(! ($vnotify & VNOTIFY_ALERT)) - $result['notice'] = array(); - - if(\App::$install) { - echo json_encode($result); - killme(); - } - - /** - * Update chat presence indication (if applicable) - */ - - if(get_observer_hash() && (! $result['invalid'])) { - $r = q("select cp_id, cp_room from chatpresence where cp_xchan = '%s' and cp_client = '%s' and cp_room = 0 limit 1", - dbesc(get_observer_hash()), - dbesc($_SERVER['REMOTE_ADDR']) - ); - $basic_presence = false; - if($r) { - $basic_presence = true; - q("update chatpresence set cp_last = '%s' where cp_id = %d", - dbesc(datetime_convert()), - intval($r[0]['cp_id']) - ); - } - if(! $basic_presence) { - q("insert into chatpresence ( cp_xchan, cp_last, cp_status, cp_client) - values( '%s', '%s', '%s', '%s' ) ", - dbesc(get_observer_hash()), - dbesc(datetime_convert()), - dbesc('online'), - dbesc($_SERVER['REMOTE_ADDR']) - ); - } - } - - /** - * Chatpresence continued... if somebody hasn't pinged recently, they've most likely left the page - * and shouldn't count as online anymore. We allow an expection for bots. - */ - - q("delete from chatpresence where cp_last < %s - INTERVAL %s and cp_client != 'auto' ", - db_utcnow(), db_quoteinterval('3 MINUTE') - ); - - - $sql_extra = ''; - if(! ($vnotify & VNOTIFY_LIKE)) - $sql_extra = " AND verb NOT IN ('" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') "; - - if(local_channel()) { - $notify_pubs = ($vnotify & VNOTIFY_PUBS) && can_view_public_stream() && Apps::system_app_installed(local_channel(), 'Public Stream'); - } - else { - $notify_pubs = can_view_public_stream(); - } - - if($notify_pubs) { - $sys = get_sys_channel(); - - $pubs = q("SELECT count(id) as total from item - WHERE uid = %d - AND item_unseen = 1 - AND author_xchan != '%s' - AND created > '" . datetime_convert('UTC','UTC',$_SESSION['static_loadtime']) . "' - $item_normal - $sql_extra", - intval($sys['channel_id']), - dbesc(get_observer_hash()) - ); - - if($pubs) - $result['pubs'] = intval($pubs[0]['total']); - } - - - - if((argc() > 1) && (argv(1) === 'pubs') && ($notify_pubs)) { - $sys = get_sys_channel(); - $result = array(); - - $r = q("SELECT * FROM item - WHERE uid = %d - AND item_unseen = 1 - AND author_xchan != '%s' - AND created > '" . datetime_convert('UTC','UTC',$_SESSION['static_loadtime']) . "' - $item_normal - $sql_extra - ORDER BY created DESC - LIMIT 300", - intval($sys['channel_id']), - dbesc(get_observer_hash()) - ); - - if($r) { - xchan_query($r); - foreach($r as $rr) { - $rr['llink'] = str_replace('display/', 'pubstream/?f=&mid=', $rr['llink']); - $result[] = \Zotlabs\Lib\Enotify::format($rr); - } - } - -// logger('ping (network||home): ' . print_r($result, true), LOGGER_DATA); - echo json_encode(array('notify' => $result)); - killme(); - } - - $t1 = dba_timer(); - - if((! local_channel()) || ($result['invalid'])) { - echo json_encode($result); - killme(); - } - - /** - * Everything following is only permitted under the context of a locally authenticated site member. - */ - - /** - * Handle "mark all xyz notifications read" requests. - */ - - // mark all items read - if(x($_REQUEST, 'markRead') && local_channel()) { - switch($_REQUEST['markRead']) { - case 'network': - $r = q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND item_unseen = 1", - intval(local_channel()) - ); - break; - case 'home': - $r = q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND item_unseen = 1 AND item_wall = 1", - intval(local_channel()) - ); - break; - case 'mail': - $r = q("UPDATE mail SET mail_seen = 1 WHERE channel_id = %d AND mail_seen = 0", - intval(local_channel()) - ); - break; - case 'all_events': - $r = q("UPDATE event SET dismissed = 1 WHERE uid = %d AND dismissed = 0 AND dtstart < '%s' AND dtstart > '%s' ", - intval(local_channel()), - dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now + ' . intval($evdays) . ' days')), - dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now - 1 days')) - ); - break; - case 'notify': - $r = q("update notify set seen = 1 where uid = %d", - intval(local_channel()) - ); - break; - case 'pubs': - unset($_SESSION['static_loadtime']); - break; - default: - break; - } - } - - if(x($_REQUEST, 'markItemRead') && local_channel()) { - $r = q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND parent = %d", - intval(local_channel()), - intval($_REQUEST['markItemRead']) - ); - } - - /** - * URL ping/something will return detail for "something", e.g. a json list with which to populate a notification - * dropdown menu. - */ - if(argc() > 1 && argv(1) === 'notify') { - $t = q("SELECT * FROM notify WHERE uid = %d AND seen = 0 ORDER BY CREATED DESC", - intval(local_channel()) - ); - - if($t) { - foreach($t as $tt) { - $message = trim(strip_tags(bbcode($tt['msg']))); - - if(strpos($message, $tt['xname']) === 0) - $message = substr($message, strlen($tt['xname']) + 1); - - $mid = basename($tt['link']); - $mid = ((strpos($mid, 'b64.') === 0) ? @base64url_decode(substr($mid, 4)) : $mid); - - if(in_array($tt['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) { - // we need the thread parent - $r = q("select thr_parent from item where mid = '%s' and uid = %d limit 1", - dbesc($mid), - intval(local_channel()) - ); - $b64mid = ((strpos($r[0]['thr_parent'], 'b64.') === 0) ? $r[0]['thr_parent'] : 'b64.' . base64url_encode($r[0]['thr_parent'])); - } - else { - $b64mid = ((strpos($mid, 'b64.') === 0) ? $mid : 'b64.' . base64url_encode($mid)); - } - - $notifs[] = array( - 'notify_link' => z_root() . '/notify/view/' . $tt['id'], - 'name' => $tt['xname'], - 'url' => $tt['url'], - 'photo' => $tt['photo'], - 'when' => relative_date($tt['created']), - 'hclass' => (($tt['seen']) ? 'notify-seen' : 'notify-unseen'), - 'b64mid' => (($tt['otype'] == 'item') ? $b64mid : 'undefined'), - 'notify_id' => (($tt['otype'] == 'item') ? $tt['id'] : 'undefined'), - 'message' => $message - ); - } - } - - echo json_encode(array('notify' => $notifs)); - killme(); - } - - if(argc() > 1 && argv(1) === 'mail') { - $channel = \App::get_channel(); - $t = q("select mail.*, xchan.* from mail left join xchan on xchan_hash = from_xchan - where channel_id = %d and mail_seen = 0 and mail_deleted = 0 - and from_xchan != '%s' order by created desc limit 50", - intval(local_channel()), - dbesc($channel['channel_hash']) - ); - - if($t) { - foreach($t as $zz) { - $notifs[] = array( - 'notify_link' => z_root() . '/mail/' . $zz['id'], - 'name' => $zz['xchan_name'], - 'addr' => $zz['xchan_addr'], - 'url' => $zz['xchan_url'], - 'photo' => $zz['xchan_photo_s'], - 'when' => relative_date($zz['created']), - 'hclass' => (intval($zz['mail_seen']) ? 'notify-seen' : 'notify-unseen'), - 'message' => t('sent you a private message'), - ); - } - } - - echo json_encode(array('notify' => $notifs)); - killme(); - } - - if(argc() > 1 && (argv(1) === 'network' || argv(1) === 'home')) { - $result = array(); - - if(argv(1) === 'home') { - $sql_extra .= ' and item_wall = 1 '; - } - - $r = q("SELECT * FROM item - WHERE uid = %d - AND item_unseen = 1 - AND author_xchan != '%s' - $item_normal - $sql_extra - ORDER BY created DESC - LIMIT 300", - intval(local_channel()), - dbesc($ob_hash) - ); - - if($r) { - xchan_query($r); - foreach($r as $item) { - $result[] = \Zotlabs\Lib\Enotify::format($item); - } - } -// logger('ping (network||home): ' . print_r($result, true), LOGGER_DATA); - echo json_encode(array('notify' => $result)); - killme(); - } - - if(argc() > 1 && (argv(1) === 'intros')) { - $result = array(); - - $r = q("SELECT * FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash where abook_channel = %d and abook_pending = 1 and abook_self = 0 and abook_ignored = 0 and xchan_deleted = 0 and xchan_orphan = 0 ORDER BY abook_created DESC LIMIT 50", - intval(local_channel()) - ); - - if($r) { - foreach($r as $rr) { - $result[] = array( - 'notify_link' => z_root() . '/connections/ifpending', - 'name' => $rr['xchan_name'], - 'addr' => $rr['xchan_addr'], - 'url' => $rr['xchan_url'], - 'photo' => $rr['xchan_photo_s'], - 'when' => relative_date($rr['abook_created']), - 'hclass' => ('notify-unseen'), - 'message' => t('added your channel') - ); - } - } - logger('ping (intros): ' . print_r($result, true), LOGGER_DATA); - echo json_encode(array('notify' => $result)); - killme(); - } - - if((argc() > 1 && (argv(1) === 'register')) && is_site_admin()) { - $result = array(); - - $r = q("SELECT account_email, account_created from account where (account_flags & %d) > 0", - intval(ACCOUNT_PENDING) - ); - if($r) { - foreach($r as $rr) { - $result[] = array( - 'notify_link' => z_root() . '/admin/accounts', - 'name' => $rr['account_email'], - 'addr' => $rr['account_email'], - 'url' => '', - 'photo' => z_root() . '/' . get_default_profile_photo(48), - 'when' => relative_date($rr['account_created']), - 'hclass' => ('notify-unseen'), - 'message' => t('requires approval') - ); - } - } - logger('ping (register): ' . print_r($result, true), LOGGER_DATA); - echo json_encode(array('notify' => $result)); - killme(); - } - - if(argc() > 1 && (argv(1) === 'all_events')) { - $bd_format = t('g A l F d') ; // 8 AM Friday January 18 - - $result = array(); - - $r = q("SELECT * FROM event left join xchan on event_xchan = xchan_hash - WHERE event.uid = %d AND dtstart < '%s' AND dtstart > '%s' and dismissed = 0 - and etype in ( 'event', 'birthday' ) - ORDER BY dtstart DESC LIMIT 1000", - intval(local_channel()), - dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now + ' . intval($evdays) . ' days')), - dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now - 1 days')) - ); - - if($r) { - foreach($r as $rr) { - - $strt = datetime_convert('UTC', (($rr['adjust']) ? date_default_timezone_get() : 'UTC'), $rr['dtstart']); - $today = ((substr($strt, 0, 10) === datetime_convert('UTC', date_default_timezone_get(), 'now', 'Y-m-d')) ? true : false); - $when = day_translate(datetime_convert('UTC', (($rr['adjust']) ? date_default_timezone_get() : 'UTC'), $rr['dtstart'], $bd_format)) . (($today) ? ' ' . t('[today]') : ''); - - $result[] = array( - 'notify_link' => z_root() . '/cdav/calendar/' . $rr['event_hash'], - 'name' => $rr['xchan_name'], - 'addr' => $rr['xchan_addr'], - 'url' => $rr['xchan_url'], - 'photo' => $rr['xchan_photo_s'], - 'when' => $when, - 'hclass' => ('notify-unseen'), - 'message' => t('posted an event') - ); - } - } - logger('ping (all_events): ' . print_r($result, true), LOGGER_DATA); - echo json_encode(array('notify' => $result)); - killme(); - } - - if(argc() > 1 && (argv(1) === 'files')) { - $result = array(); - - $r = q("SELECT item.created, xchan.xchan_name, xchan.xchan_addr, xchan.xchan_url, xchan.xchan_photo_s FROM item - LEFT JOIN xchan on author_xchan = xchan_hash - WHERE item.verb = '%s' - AND item.obj_type = '%s' - AND item.uid = %d - AND item.owner_xchan != '%s' - AND item.item_unseen = 1", - dbesc(ACTIVITY_POST), - dbesc(ACTIVITY_OBJ_FILE), - intval(local_channel()), - dbesc($ob_hash) - ); - if($r) { - foreach($r as $rr) { - $result[] = array( - 'notify_link' => z_root() . '/sharedwithme', - 'name' => $rr['xchan_name'], - 'addr' => $rr['xchan_addr'], - 'url' => $rr['xchan_url'], - 'photo' => $rr['xchan_photo_s'], - 'when' => relative_date($rr['created']), - 'hclass' => ('notify-unseen'), - 'message' => t('shared a file with you') - ); - } - } - logger('ping (files): ' . print_r($result, true), LOGGER_DATA); - echo json_encode(array('notify' => $result)); - killme(); - } - - /** - * Normal ping - just the counts, no detail - */ - if($vnotify & VNOTIFY_SYSTEM) { - $t = q("select count(*) as total from notify where uid = %d and seen = 0", - intval(local_channel()) - ); - if($t) - $result['notify'] = intval($t[0]['total']); - } - - $t2 = dba_timer(); - - if($vnotify & VNOTIFY_FILES) { - $files = q("SELECT count(id) as total FROM item - WHERE verb = '%s' - AND obj_type = '%s' - AND uid = %d - AND owner_xchan != '%s' - AND item_unseen = 1", - dbesc(ACTIVITY_POST), - dbesc(ACTIVITY_OBJ_FILE), - intval(local_channel()), - dbesc($ob_hash) - ); - if($files) - $result['files'] = intval($files[0]['total']); - } - - $t3 = dba_timer(); - - if($vnotify & (VNOTIFY_NETWORK|VNOTIFY_CHANNEL)) { - - $r = q("SELECT id, item_wall FROM item - WHERE uid = %d and item_unseen = 1 - $item_normal - $sql_extra - AND author_xchan != '%s'", - intval(local_channel()), - dbesc($ob_hash) - ); - - if($r) { - $arr = array('items' => $r); - call_hooks('network_ping', $arr); - - foreach ($r as $it) { - if(intval($it['item_wall'])) - $result['home'] ++; - else - $result['network'] ++; - } - } - } - if(! ($vnotify & VNOTIFY_NETWORK)) - $result['network'] = 0; - if(! ($vnotify & VNOTIFY_CHANNEL)) - $result['home'] = 0; - - $t4 = dba_timer(); - - if($vnotify & VNOTIFY_INTRO) { - $intr = q("SELECT COUNT(abook.abook_id) AS total FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash where abook_channel = %d and abook_pending = 1 and abook_self = 0 and abook_ignored = 0 and xchan_deleted = 0 and xchan_orphan = 0 ", - intval(local_channel()) - ); - - $t5 = dba_timer(); - - if($intr) - $result['intros'] = intval($intr[0]['total']); - } - - $t6 = dba_timer(); - $channel = \App::get_channel(); - - if($vnotify & VNOTIFY_MAIL) { - $mails = q("SELECT count(id) as total from mail - WHERE channel_id = %d AND mail_seen = 0 and from_xchan != '%s' ", - intval(local_channel()), - dbesc($channel['channel_hash']) - ); - if($mails) - $result['mail'] = intval($mails[0]['total']); - } - - if($vnotify & VNOTIFY_REGISTER) { - if (\App::$config['system']['register_policy'] == REGISTER_APPROVE && is_site_admin()) { - $regs = q("SELECT count(account_id) as total from account where (account_flags & %d) > 0", - intval(ACCOUNT_PENDING) - ); - if($regs) - $result['register'] = intval($regs[0]['total']); - } - } - - $t7 = dba_timer(); - - if($vnotify & (VNOTIFY_EVENT|VNOTIFY_EVENTTODAY|VNOTIFY_BIRTHDAY)) { - $events = q("SELECT etype, dtstart, adjust FROM event - WHERE event.uid = %d AND dtstart < '%s' AND dtstart > '%s' and dismissed = 0 - and etype in ( 'event', 'birthday' ) - ORDER BY dtstart ASC ", - intval(local_channel()), - dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now + ' . intval($evdays) . ' days')), - dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now - 1 days')) - ); - - if($events) { - $result['all_events'] = count($events); - - if($result['all_events']) { - $str_now = datetime_convert('UTC', date_default_timezone_get(), 'now', 'Y-m-d'); - foreach($events as $x) { - $bd = false; - if($x['etype'] === 'birthday') { - $result['birthdays'] ++; - $bd = true; - } - else { - $result['events'] ++; - } - if(datetime_convert('UTC', ((intval($x['adjust'])) ? date_default_timezone_get() : 'UTC'), $x['dtstart'], 'Y-m-d') === $str_now) { - $result['all_events_today'] ++; - if($bd) - $result['birthdays_today'] ++; - else - $result['events_today'] ++; - } - } - } - } - } - if(! ($vnotify & VNOTIFY_EVENT)) - $result['all_events'] = $result['events'] = 0; - if(! ($vnotify & VNOTIFY_EVENTTODAY)) - $result['all_events_today'] = $result['events_today'] = 0; - if(! ($vnotify & VNOTIFY_BIRTHDAY)) - $result['birthdays'] = 0; - - - - if($vnotify & VNOTIFY_FORUMS) { - $forums = get_forum_channels(local_channel()); - - if($forums) { - $item_normal = item_normal(); - $fcount = count($forums); - $forums['total'] = 0; - - for($x = 0; $x < $fcount; $x ++) { - $p = q("SELECT oid AS parent FROM term WHERE uid = %d AND ttype = %d AND term = '%s'", - intval(local_channel()), - intval(TERM_FORUM), - dbesc($forums[$x]['xchan_name']) - ); - - $p_str = ids_to_querystr($p, 'parent'); - $p_sql = (($p_str) ? "OR parent IN ( $p_str )" : ''); - - $r = q("select count(id) as unseen from item - where uid = %d and ( owner_xchan = '%s' OR author_xchan = '%s' $p_sql ) and item_unseen = 1 $item_normal $sql_extra", - intval(local_channel()), - dbesc($forums[$x]['xchan_hash']), - dbesc($forums[$x]['xchan_hash']) - ); - if($r[0]['unseen']) { - $forums[$x]['notify_link'] = (($forums[$x]['private_forum']) ? $forums[$x]['xchan_url'] : z_root() . '/network/?f=&pf=1&unseen=1&cid=' . $forums[$x]['abook_id']); - $forums[$x]['name'] = $forums[$x]['xchan_name']; - $forums[$x]['addr'] = $forums[$x]['xchan_addr']; - $forums[$x]['url'] = $forums[$x]['xchan_url']; - $forums[$x]['photo'] = $forums[$x]['xchan_photo_s']; - $forums[$x]['unseen'] = $r[0]['unseen']; - $forums[$x]['private_forum'] = (($forums[$x]['private_forum']) ? 'lock' : ''); - $forums[$x]['message'] = (($forums[$x]['private_forum']) ? t('Private forum') : t('Public forum')); - - $forums['total'] = $forums['total'] + $r[0]['unseen']; - - unset($forums[$x]['abook_id']); - unset($forums[$x]['xchan_hash']); - unset($forums[$x]['xchan_name']); - unset($forums[$x]['xchan_url']); - unset($forums[$x]['xchan_photo_s']); - - //if($forums[$x]['private_forum']) - // unset($forums[$x]['private_forum']); - - } - else { - unset($forums[$x]); - } - } - $result['forums'] = $forums['total']; - unset($forums['total']); - - $result['forums_sub'] = $forums; - } - } - - $x = json_encode($result); - - $t8 = dba_timer(); - -// logger('ping timer: ' . sprintf('%01.4f %01.4f %01.4f %01.4f %01.4f %01.4f %01.4f %01.4f',$t8 - $t7, $t7 - $t6, $t6 - $t5, $t5 - $t4, $t4 - $t3, $t3 - $t2, $t2 - $t1, $t1 - $t0)); - - echo $x; - killme(); - } - -} diff --git a/Zotlabs/Module/Poke.php b/Zotlabs/Module/Poke.php index 1f1edfa18..d60a7f426 100644 --- a/Zotlabs/Module/Poke.php +++ b/Zotlabs/Module/Poke.php @@ -9,11 +9,11 @@ use Zotlabs\Web\Controller; * * Poke, prod, finger, or otherwise do unspeakable things to somebody - who must be a connection in your address book * This function can be invoked with the required arguments (verb and cid and private and possibly parent) silently via ajax or - * other web request. You must be logged in and connected to a channel. + * other web request. You must be logged in and connected to a channel. * If the required arguments aren't present, we'll display a simple form to choose a recipient and a verb. * parent is a special argument which let's you attach this activity as a comment to an existing conversation, which * may have started with somebody else poking (etc.) somebody, but this isn't necessary. This can be used in the adult - * plugin version to have entire conversations where Alice poked Bob, Bob fingered Alice, Alice hugged Bob, etc. + * plugin version to have entire conversations where Alice poked Bob, Bob fingered Alice, Alice hugged Bob, etc. * * private creates a private conversation with the recipient. Otherwise your channel's default post privacy is used. * @@ -25,41 +25,41 @@ require_once('include/items.php'); class Poke extends Controller { function init() { - + if(! local_channel()) return; if(! Apps::system_app_installed(local_channel(), 'Poke')) { return; } - + $uid = local_channel(); $channel = App::get_channel(); - + $verb = notags(trim($_REQUEST['verb'])); - - if(! $verb) + + if(! $verb) return; - + $verbs = get_poke_verbs(); - + if(! array_key_exists($verb,$verbs)) return; - + $activity = ACTIVITY_POKE . '#' . urlencode($verbs[$verb][0]); - + $contact_id = intval($_REQUEST['cid']); $xchan = trim($_REQUEST['xchan']); if(! ($contact_id || $xchan)) return; - + $parent = ((x($_REQUEST,'parent')) ? intval($_REQUEST['parent']) : 0); - + logger('poke: verb ' . $verb . ' contact ' . $contact_id, LOGGER_DEBUG); - - + + if($contact_id) { $r = q("SELECT * FROM abook left join xchan on xchan_hash = abook_xchan where abook_id = %d and abook_channel = %d LIMIT 1", intval($contact_id), @@ -71,17 +71,17 @@ class Poke extends Controller { dbesc($xchan . '%') ); } - + if(! $r) { logger('poke: no target.'); return; } - + $target = $r[0]; $parent_item = null; - + if($parent) { - $r = q("select mid, item_private, owner_xchan, allow_cid, allow_gid, deny_cid, deny_gid + $r = q("select mid, item_private, owner_xchan, allow_cid, allow_gid, deny_cid, deny_gid from item where id = %d and parent = %d and uid = %d limit 1", intval($parent), intval($parent), @@ -98,18 +98,18 @@ class Poke extends Controller { } } elseif($contact_id) { - + $item_private = ((x($_GET,'private')) ? intval($_GET['private']) : 0); - + $allow_cid = (($item_private) ? '<' . $target['abook_xchan']. '>' : $channel['channel_allow_cid']); $allow_gid = (($item_private) ? '' : $channel['channel_allow_gid']); $deny_cid = (($item_private) ? '' : $channel['channel_deny_cid']); $deny_gid = (($item_private) ? '' : $channel['channel_deny_gid']); } - - + + $arr = array(); - + $arr['item_wall'] = 1; @@ -124,7 +124,7 @@ class Poke extends Controller { $arr['item_private'] = $item_private; $arr['obj_type'] = ACTIVITY_OBJ_PERSON; $arr['body'] = '[zrl=' . $channel['xchan_url'] . ']' . $channel['xchan_name'] . '[/zrl]' . ' ' . t($verbs[$verb][0]) . ' ' . '[zrl=' . $target['xchan_url'] . ']' . $target['xchan_name'] . '[/zrl]'; - + $obj = array( 'type' => ACTIVITY_OBJ_PERSON, 'title' => $target['xchan_name'], @@ -134,25 +134,25 @@ class Poke extends Controller { array('rel' => 'photo', 'type' => $target['xchan_photo_mimetype'], 'href' => $target['xchan_photo_l']) ), ); - + $arr['obj'] = json_encode($obj); - + $arr['item_origin'] = 1; $arr['item_wall'] = 1; $arr['item_unseen'] = 1; if(! $parent_item) $item['item_thread_top'] = 1; - - + + post_activity_item($arr); - + return; } - - - + + + function get() { - + if(! local_channel()) { notice( t('Permission denied.') . EOL); return; @@ -161,19 +161,17 @@ class Poke extends Controller { if(! Apps::system_app_installed(local_channel(), 'Poke')) { //Do not display any associated widgets at this point App::$pdl = ''; - - $o = '<b>' . t('Poke App') . ' (' . t('Not Installed') . '):</b><br>'; - $o .= t('Poke somebody in your addressbook'); - return $o; + $papp = Apps::get_papp('Poke'); + return Apps::app_render($papp, 'module'); } nav_set_selected('Poke'); - + $name = ''; $id = ''; - + if(intval($_REQUEST['c'])) { - $r = q("select abook_id, xchan_name from abook left join xchan on abook_xchan = xchan_hash + $r = q("select abook_id, xchan_name from abook left join xchan on abook_xchan = xchan_hash where abook_id = %d and abook_channel = %d limit 1", intval($_REQUEST['c']), intval(local_channel()) @@ -183,17 +181,17 @@ class Poke extends Controller { $id = $r[0]['abook_id']; } } - + $parent = ((x($_REQUEST,'parent')) ? intval($_REQUEST['parent']) : '0'); - + $verbs = get_poke_verbs(); - + $shortlist = array(); foreach($verbs as $k => $v) if($v[1] !== 'NOTRANSLATION') $shortlist[] = array($k,$v[1]); - - + + $poke_basic = get_config('system','poke_basic'); if($poke_basic) { $title = t('Poke'); @@ -203,7 +201,7 @@ class Poke extends Controller { $title = t('Poke/Prod'); $desc = t('Poke, prod or do other things to somebody'); } - + $o = replace_macros(get_markup_template('poke_content.tpl'),array( '$title' => $title, '$poke_basic' => $poke_basic, @@ -218,8 +216,8 @@ class Poke extends Controller { '$name' => $name, '$id' => $id )); - + return $o; - + } } diff --git a/Zotlabs/Module/Post.php b/Zotlabs/Module/Post.php deleted file mode 100644 index f67cbf020..000000000 --- a/Zotlabs/Module/Post.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php -/** - * @file Zotlabs/Module/Post.php - * - * @brief Zot endpoint. - * - */ - -namespace Zotlabs\Module; - -require_once('include/zot.php'); - -/** - * @brief Post module. - * - */ -class Post extends \Zotlabs\Web\Controller { - - function init() { - if(array_key_exists('auth', $_REQUEST)) { - $x = new \Zotlabs\Zot\Auth($_REQUEST); - exit; - } - } - - function post() { - if(array_key_exists('data',$_REQUEST)) { - $z = new \Zotlabs\Zot\Receiver($_REQUEST['data'], get_config('system', 'prvkey'), new \Zotlabs\Zot\ZotHandler()); - exit; - } - - } - -} diff --git a/Zotlabs/Module/Prate.php b/Zotlabs/Module/Prate.php deleted file mode 100644 index 2a8539ed0..000000000 --- a/Zotlabs/Module/Prate.php +++ /dev/null @@ -1,105 +0,0 @@ -<?php -namespace Zotlabs\Module; - - -class Prate extends \Zotlabs\Web\Controller { - - function init() { - if($_SERVER['REQUEST_METHOD'] === 'post') - return; - - if(! local_channel()) - return; - - $channel = \App::get_channel(); - - $target = argv(1); - if(! $target) - return; - - $r = q("select * from xlink where xlink_xchan = '%s' and xlink_link = '%s' and xlink_static = 1", - dbesc($channel['channel_hash']), - dbesc($target) - ); - if($r) - json_return_and_die(array('rating' => $r[0]['xlink_rating'],'rating_text' => $r[0]['xlink_rating_text'])); - killme(); - } - - function post() { - - if(! local_channel()) - return; - - $channel = \App::get_channel(); - - $target = trim($_REQUEST['target']); - if(! $target) - return; - - if($target === $channel['channel_hash']) - return; - - $rating = intval($_POST['rating']); - if($rating < (-10)) - $rating = (-10); - if($rating > 10) - $rating = 10; - - $rating_text = trim(escape_tags($_REQUEST['rating_text'])); - - $signed = $target . '.' . $rating . '.' . $rating_text; - - $sig = base64url_encode(rsa_sign($signed,$channel['channel_prvkey'])); - - - $z = q("select * from xlink where xlink_xchan = '%s' and xlink_link = '%s' and xlink_static = 1 limit 1", - dbesc($channel['channel_hash']), - dbesc($target) - ); - if($z) { - $record = $z[0]['xlink_id']; - $w = q("update xlink set xlink_rating = '%d', xlink_rating_text = '%s', xlink_sig = '%s', xlink_updated = '%s' - where xlink_id = %d", - intval($rating), - dbesc($rating_text), - dbesc($sig), - dbesc(datetime_convert()), - intval($record) - ); - } - else { - $w = q("insert into xlink ( xlink_xchan, xlink_link, xlink_rating, xlink_rating_text, xlink_sig, xlink_updated, xlink_static ) values ( '%s', '%s', %d, '%s', '%s', '%s', 1 ) ", - dbesc($channel['channel_hash']), - dbesc($target), - intval($rating), - dbesc($rating_text), - dbesc($sig), - dbesc(datetime_convert()) - ); - $z = q("select * from xlink where xlink_xchan = '%s' and xlink_link = '%s' and xlink_static = 1 limit 1", - dbesc($channel['channel_hash']), - dbesc($orig_record[0]['abook_xchan']) - ); - if($z) - $record = $z[0]['xlink_id']; - } - if($record) { - \Zotlabs\Daemon\Master::Summon(array('Ratenotif','rating',$record)); - } - - json_return_and_die(array('result' => true));; - } - - - - - - - - - - - - -} diff --git a/Zotlabs/Module/Probe.php b/Zotlabs/Module/Probe.php deleted file mode 100644 index d338b08ea..000000000 --- a/Zotlabs/Module/Probe.php +++ /dev/null @@ -1,59 +0,0 @@ -<?php -namespace Zotlabs\Module; - -use App; -use Zotlabs\Lib\Apps; - -require_once('include/zot.php'); - -class Probe extends \Zotlabs\Web\Controller { - - function get() { - - if(local_channel()) { - if(! Apps::system_app_installed(local_channel(), 'Remote Diagnostics')) { - //Do not display any associated widgets at this point - App::$pdl = ''; - - $o = '<b>' . t('Remote Diagnostics App') . ' (' . t('Not Installed') . '):</b><br>'; - $o .= t('Perform diagnostics on remote channels'); - return $o; - } - } - - nav_set_selected('Remote Diagnostics'); - - $o .= '<h3>Remote Diagnostics</h3>'; - - $o .= '<form action="probe" method="get">'; - $o .= 'Lookup address: <input type="text" style="width: 250px;" name="addr" value="' . $_GET['addr'] .'" />'; - $o .= '<input type="submit" name="submit" value="Submit" /></form>'; - - $o .= '<br /><br />'; - - if(x($_GET,'addr')) { - $channel = App::get_channel(); - $addr = trim($_GET['addr']); - $do_import = ((intval($_GET['import']) && is_site_admin()) ? true : false); - - $j = \Zotlabs\Zot\Finger::run($addr,$channel,false); - - $o .= '<pre>'; - if(! $j['success']) { - $o .= "<strong>https connection failed. Trying again with auto failover to http.</strong>\r\n\r\n"; - $j = \Zotlabs\Zot\Finger::run($addr,$channel,true); - if(! $j['success']) { - return $o; - } - } - if($do_import && $j) - $x = import_xchan($j); - if($j && $j['permissions'] && $j['permissions']['iv']) - $j['permissions'] = json_decode(crypto_unencapsulate($j['permissions'],$channel['channel_prvkey']),true); - $o .= str_replace("\n",'<br />',print_r($j,true)); - $o .= '</pre>'; - } - return $o; - } - -} diff --git a/Zotlabs/Module/Profile.php b/Zotlabs/Module/Profile.php index 4235f0b97..bcc7ad930 100644 --- a/Zotlabs/Module/Profile.php +++ b/Zotlabs/Module/Profile.php @@ -1,5 +1,13 @@ <?php -namespace Zotlabs\Module; /** @file */ + +namespace Zotlabs\Module; + +use App; +use Zotlabs\Lib\ActivityStreams; +use Zotlabs\Lib\Activity; +use Zotlabs\Web\Controller; + +/** @file */ require_once('include/contact_widgets.php'); require_once('include/items.php'); @@ -9,110 +17,115 @@ require_once('include/conversation.php'); require_once('include/acl_selectors.php'); - -class Profile extends \Zotlabs\Web\Controller { +class Profile extends Controller { function init() { - - if(argc() > 1) + + if (argc() > 1) $which = argv(1); else { - notice( t('Requested profile is not available.') . EOL ); - \App::$error = 404; + notice(t('Requested profile is not available.') . EOL); + App::$error = 404; return; } - nav_set_selected('Profile'); - + if (ActivityStreams::is_as_request()) { + $channel = channelx_by_nick($which); + if (!$channel) { + http_status_exit(404, 'Not found'); + } + + $p = Activity::encode_person($channel, true); + as_return_and_die(['type' => 'Profile', 'describes' => $p], $channel); + } + $profile = ''; - $channel = \App::get_channel(); - - if((local_channel()) && (argc() > 2) && (argv(2) === 'view')) { - $which = $channel['channel_address']; - $profile = argv(1); + + if ((local_channel()) && (argc() > 2) && (argv(2) === 'view')) { + $channel = App::get_channel(); + $which = $channel['channel_address']; + $profile = argv(1); + $r = q("select profile_guid from profile where id = %d and uid = %d limit 1", intval($profile), intval(local_channel()) ); - if(! $r) + + if (!$r) $profile = ''; $profile = $r[0]['profile_guid']; } - - head_add_link( [ - 'rel' => 'alternate', + + head_add_link([ + 'rel' => 'alternate', 'type' => 'application/atom+xml', 'title' => t('Posts and comments'), 'href' => z_root() . '/feed/' . $which ]); - head_add_link( [ - 'rel' => 'alternate', + head_add_link([ + 'rel' => 'alternate', 'type' => 'application/atom+xml', 'title' => t('Only posts'), 'href' => z_root() . '/feed/' . $which . '?f=&top=1' ]); - if(! $profile) { + if (!$profile) { $x = q("select channel_id as profile_uid from channel where channel_address = '%s' limit 1", dbesc(argv(1)) ); - if($x) { - \App::$profile = $x[0]; + if ($x) { + App::$profile = $x[0]; } } - - profile_load($which,$profile); - - + + profile_load($which, $profile); + } - + function get() { - - if(observer_prohibited(true)) { + + if (observer_prohibited(true)) { return login(); } - - $groups = array(); + nav_set_selected('Profile'); + $groups = []; + $o = ''; - $tab = 'profile'; - $o = ''; - - if(! (perm_is_allowed(\App::$profile['profile_uid'],get_observer_hash(), 'view_profile'))) { - notice( t('Permission denied.') . EOL); + if (!(perm_is_allowed(App::$profile['profile_uid'], get_observer_hash(), 'view_profile'))) { + notice(t('Permission denied.') . EOL); return; } - - if(argc() > 2 && argv(2) === 'vcard') { + if (argc() > 2 && argv(2) === 'vcard') { header('Content-type: text/vcard'); - header('content-disposition: attachment; filename="' . t('vcard') . '-' . $profile['channel_address'] . '.vcf"' ); - echo \App::$profile['profile_vcard']; + header('content-disposition: attachment; filename="' . t('vcard') . '-' . App::$profile['channel_address'] . '.vcf"'); + echo App::$profile['profile_vcard']; killme(); } - - $is_owner = ((local_channel()) && (local_channel() == \App::$profile['profile_uid']) ? true : false); - - if(\App::$profile['hidewall'] && (! $is_owner) && (! remote_channel())) { - notice( t('Permission denied.') . EOL); + + $is_owner = ((local_channel()) && (local_channel() == App::$profile['profile_uid']) ? true : false); + + if (App::$profile['hidewall'] && (!$is_owner) && (!remote_channel())) { + notice(t('Permission denied.') . EOL); return; } - - head_add_link([ + + head_add_link([ 'rel' => 'alternate', 'type' => 'application/json+oembed', - 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . \App::$query_string), + 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . App::$query_string), 'title' => 'oembed' ]); $o .= advanced_profile(); - call_hooks('profile_advanced',$o); + call_hooks('profile_advanced', $o); return $o; - + } - + } diff --git a/Zotlabs/Module/Profiles.php b/Zotlabs/Module/Profiles.php index 9ac0e725e..73bae45e8 100644 --- a/Zotlabs/Module/Profiles.php +++ b/Zotlabs/Module/Profiles.php @@ -12,11 +12,11 @@ class Profiles extends \Zotlabs\Web\Controller { function init() { nav_set_selected('Profiles', 'settings/profiles'); - + if(! local_channel()) { return; } - + if((argc() > 2) && (argv(1) === "drop") && intval(argv(2))) { $r = q("SELECT * FROM profile WHERE id = %d AND uid = %d AND is_default = 0 LIMIT 1", intval(argv(2)), @@ -28,11 +28,11 @@ class Profiles extends \Zotlabs\Web\Controller { return; // NOTREACHED } $profile_guid = $r['profile_guid']; - + check_form_security_token_redirectOnErr('/profiles', 'profile_drop', 't'); - + // move every contact using this profile as their default to the user default - + $r = q("UPDATE abook SET abook_profile = (SELECT profile_guid FROM profile WHERE is_default = 1 AND uid = %d LIMIT 1) WHERE abook_profile = '%s' AND abook_channel = %d ", intval(local_channel()), dbesc($profile_guid), @@ -44,34 +44,34 @@ class Profiles extends \Zotlabs\Web\Controller { ); if($r) info( t('Profile deleted.') . EOL); - - // @fixme this is a much more complicated sync - add any changed abook entries and + + // @fixme this is a much more complicated sync - add any changed abook entries and // also add deleted flag to profile structure // profiles_build_sync is just here as a placeholder - it doesn't work at all here - + // profiles_build_sync(local_channel()); - + goaway(z_root() . '/profiles'); return; // NOTREACHED } - - - - - + + + + + if((argc() > 1) && (argv(1) === 'new')) { - + // check_form_security_token_redirectOnErr('/profiles', 'profile_new', 't'); - + $r0 = q("SELECT id FROM profile WHERE uid = %d", intval(local_channel())); $num_profiles = count($r0); - + $name = t('Profile-') . ($num_profiles + 1); - + $r1 = q("SELECT fullname, photo, thumb FROM profile WHERE uid = %d AND is_default = 1 LIMIT 1", intval(local_channel())); - + $r2 = profile_store_lowlevel( [ 'aid' => intval(get_account_id()), @@ -83,27 +83,27 @@ class Profiles extends \Zotlabs\Web\Controller { 'thumb' => $r1[0]['thumb'] ] ); - + $r3 = q("SELECT id FROM profile WHERE uid = %d AND profile_name = '%s' LIMIT 1", intval(local_channel()), dbesc($name) ); - + info( t('New profile created.') . EOL); if(count($r3) == 1) goaway(z_root() . '/profiles/' . $r3[0]['id']); - + goaway(z_root() . '/profiles'); - } - + } + if((argc() > 2) && (argv(1) === 'clone')) { - + check_form_security_token_redirectOnErr('/profiles', 'profile_clone', 't'); - + $r0 = q("SELECT id FROM profile WHERE uid = %d", intval(local_channel())); $num_profiles = count($r0); - + $name = t('Profile-') . ($num_profiles + 1); $r1 = q("SELECT * FROM profile WHERE uid = %d AND id = %d LIMIT 1", intval(local_channel()), @@ -116,30 +116,30 @@ class Profiles extends \Zotlabs\Web\Controller { } unset($r1[0]['id']); $r1[0]['is_default'] = 0; - $r1[0]['publish'] = 0; + $r1[0]['publish'] = 0; $r1[0]['profile_name'] = dbesc($name); $r1[0]['profile_guid'] = dbesc(random_string()); - + create_table_from_array('profile', $r1[0]); - + $r3 = q("SELECT id FROM profile WHERE uid = %d AND profile_name = '%s' LIMIT 1", intval(local_channel()), dbesc($name) ); info( t('New profile created.') . EOL); - + profiles_build_sync(local_channel()); - + if(($r3) && (count($r3) == 1)) goaway(z_root() . '/profiles/' . $r3[0]['id']); - + goaway(z_root() . '/profiles'); - + return; // NOTREACHED } - + if((argc() > 2) && (argv(1) === 'export')) { - + $r1 = q("SELECT * FROM profile WHERE uid = %d AND id = %d LIMIT 1", intval(local_channel()), intval(argv(2)) @@ -151,7 +151,7 @@ class Profiles extends \Zotlabs\Web\Controller { } header('content-type: application/octet_stream'); header('content-disposition: attachment; filename="' . $r1[0]['profile_name'] . '.json"' ); - + unset($r1[0]['id']); unset($r1[0]['aid']); unset($r1[0]['uid']); @@ -162,10 +162,10 @@ class Profiles extends \Zotlabs\Web\Controller { echo json_encode($r1[0]); killme(); } - - - - + + + + // Run profile_load() here to make sure the theme is set before // we start loading content if(((argc() > 1) && (intval(argv(1)))) || !feature_enabled(local_channel(),'multi_profiles')) { @@ -187,28 +187,28 @@ class Profiles extends \Zotlabs\Web\Controller { \App::$error = 404; return; } - + $chan = \App::get_channel(); - + profile_load($chan['channel_address'],$r[0]['id']); } } - + function post() { - + if(! local_channel()) { notice( t('Permission denied.') . EOL); return; } - + require_once('include/activities.php'); - + $namechanged = false; - - + + // import from json export file. // Only import fields that are allowed on this hub - + if(x($_FILES,'userfile')) { $src = $_FILES['userfile']['tmp_name']; $filesize = intval($_FILES['userfile']['size']); @@ -230,10 +230,10 @@ class Profiles extends \Zotlabs\Web\Controller { } } } - + call_hooks('profile_post', $_POST); - - + + if((argc() > 1) && (argv(1) !== "new") && intval(argv(1))) { $orig = q("SELECT * FROM profile WHERE id = %d AND uid = %d LIMIT 1", intval(\App::$argv[1]), @@ -243,26 +243,26 @@ class Profiles extends \Zotlabs\Web\Controller { notice( t('Profile not found.') . EOL); return; } - + check_form_security_token_redirectOnErr('/profiles', 'profile_edit'); - + $is_default = (($orig[0]['is_default']) ? 1 : 0); - + $profile_name = notags(trim($_POST['profile_name'])); if(! strlen($profile_name)) { notice( t('Profile Name is required.') . EOL); return; } - + $dob = $_POST['dob'] ? escape_tags(trim($_POST['dob'])) : '0000-00-00'; // FIXME: Needs to be validated? - + $y = substr($dob,0,4); if((! ctype_digit($y)) || ($y < 1900)) $ignore_year = true; else $ignore_year = false; - + if($dob != '0000-00-00') { if(strpos($dob,'0000-') === 0) { $ignore_year = true; @@ -272,12 +272,12 @@ class Profiles extends \Zotlabs\Web\Controller { if($ignore_year) $dob = '0000-' . $dob; } - + $name = escape_tags(trim($_POST['name'])); - + if($orig[0]['fullname'] != $name) { $namechanged = true; - + $v = validate_channelname($name); if($v) { notice($v); @@ -285,7 +285,7 @@ class Profiles extends \Zotlabs\Web\Controller { $name = $orig[0]['fullname']; } } - + $pdesc = escape_tags(trim($_POST['pdesc'])); $gender = escape_tags(trim($_POST['gender'])); $address = escape_tags(trim($_POST['address'])); @@ -301,10 +301,10 @@ class Profiles extends \Zotlabs\Web\Controller { $hometown = escape_tags(trim($_POST['hometown'])); $politic = escape_tags(trim($_POST['politic'])); $religion = escape_tags(trim($_POST['religion'])); - + $likes = fix_mce_lf(escape_tags(trim($_POST['likes']))); $dislikes = fix_mce_lf(escape_tags(trim($_POST['dislikes']))); - + $about = fix_mce_lf(escape_tags(trim($_POST['about']))); $interest = fix_mce_lf(escape_tags(trim($_POST['interest']))); $contact = fix_mce_lf(escape_tags(trim($_POST['contact']))); @@ -316,11 +316,11 @@ class Profiles extends \Zotlabs\Web\Controller { $romance = fix_mce_lf(escape_tags(trim($_POST['romance']))); $work = fix_mce_lf(escape_tags(trim($_POST['work']))); $education = fix_mce_lf(escape_tags(trim($_POST['education']))); - + $hide_friends = ((intval($_POST['hide_friends'])) ? 1: 0); - + // start fresh and create a new vcard. TODO: preserve the original guid or whatever else needs saving -// $orig_vcard = (($orig[0]['profile_vcard']) ? \Sabre\VObject\Reader::read($orig[0]['profile_vcard']) : null); +// $orig_vcard = (($orig[0]['profile_vcard']) ? \Sabre\VObject\Reader::read($orig[0]['profile_vcard']) : null); $orig_vcard = null; @@ -347,7 +347,7 @@ class Profiles extends \Zotlabs\Web\Controller { 5 => $postal_code, 6 => $country_name ]; - + $profile_vcard = update_vcard($defcard,$orig_vcard); $orig_vcard = \Sabre\VObject\Reader::read($profile_vcard); @@ -370,19 +370,19 @@ class Profiles extends \Zotlabs\Web\Controller { linkify_tags($romance, local_channel()); linkify_tags($work, local_channel()); linkify_tags($education, local_channel()); - - + + $with = ((x($_POST,'with')) ? escape_tags(trim($_POST['with'])) : ''); - + if(! strlen($howlong)) $howlong = NULL_DATE; else $howlong = datetime_convert(date_default_timezone_get(),'UTC',$howlong); - + // linkify the relationship target if applicable - + $withchanged = false; - + if(strlen($with)) { if($with != strip_tags($orig[0]['partner'])) { $withchanged = true; @@ -392,7 +392,7 @@ class Profiles extends \Zotlabs\Web\Controller { $lookup = substr($lookup,1); $lookup = str_replace('_',' ', $lookup); $newname = $lookup; - + $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash WHERE xchan_name = '%s' AND abook_channel = %d LIMIT 1", dbesc($newname), intval(local_channel()) @@ -407,8 +407,8 @@ class Profiles extends \Zotlabs\Web\Controller { $prf = $r[0]['xchan_url']; $newname = $r[0]['xchan_name']; } - - + + if($prf) { $with = str_replace($lookup,'<a href="' . $prf . '">' . $newname . '</a>', $with); if(strpos($with,'@') === 0) @@ -418,7 +418,7 @@ class Profiles extends \Zotlabs\Web\Controller { else $with = $orig[0]['partner']; } - + $profile_fields_basic = get_profile_fields_basic(); $profile_fields_advanced = get_profile_fields_advanced(); $advanced = ((feature_enabled(local_channel(),'advanced_profiles')) ? true : false); @@ -426,7 +426,7 @@ class Profiles extends \Zotlabs\Web\Controller { $fields = $profile_fields_advanced; else $fields = $profile_fields_basic; - + $z = q("select * from profdef where true"); if($z) { foreach($z as $zz) { @@ -453,7 +453,7 @@ class Profiles extends \Zotlabs\Web\Controller { } } } - + $changes = array(); $value = ''; if($is_default) { @@ -513,12 +513,12 @@ class Profiles extends \Zotlabs\Web\Controller { $comma2 = (($region && $country_name) ? ', ' : ''); $value = $locality . $comma1 . $region . $comma2 . $country_name; } - + profile_activity($changes,$value); - - } - - $r = q("UPDATE profile + + } + + $r = q("UPDATE profile SET profile_name = '%s', fullname = '%s', pdesc = '%s', @@ -591,58 +591,58 @@ class Profiles extends \Zotlabs\Web\Controller { intval(argv(1)), intval(local_channel()) ); - + if($r) info( t('Profile updated.') . EOL); - - $r = q("select * from profile where id = %d and uid = %d limit 1", - intval(argv(1)), - intval(local_channel()) - ); - if($r) { - require_once('include/zot.php'); - Libsync::build_sync_packet(local_channel(),array('profile' => $r)); - } - + $channel = \App::get_channel(); - + if($namechanged && $is_default) { - $r = q("UPDATE xchan SET xchan_name = '%s', xchan_name_date = '%s' WHERE xchan_url = '%s'", + q("UPDATE xchan SET xchan_name = '%s', xchan_name_date = '%s' WHERE xchan_hash = '%s'", dbesc($name), dbesc(datetime_convert()), - dbesc(z_root() . '/channel/' . $channel['channel_address']) + dbesc($channel['xchan_hash']) ); - $r = q("UPDATE channel SET channel_name = '%s' WHERE channel_hash = '%s'", + q("UPDATE channel SET channel_name = '%s' WHERE channel_hash = '%s'", dbesc($name), dbesc($channel['xchan_hash']) ); } - + + $r = q("select * from profile where id = %d and uid = %d limit 1", + intval(argv(1)), + intval(local_channel()) + ); + + if($r) { + Libsync::build_sync_packet(local_channel(), ['profile' => $r]); + } + if($is_default) { - // reload the info for the sidebar widget - why does this not work? + // reload the info for the sidebar widget profile_load($channel['channel_address']); \Zotlabs\Daemon\Master::Summon(array('Directory',local_channel())); } } } - - + + function get() { - + $o = ''; - + $channel = \App::get_channel(); - + if(! local_channel()) { notice( t('Permission denied.') . EOL); return; } - + require_once('include/channel.php'); - + $profile_fields_basic = get_profile_fields_basic(); $profile_fields_advanced = get_profile_fields_advanced(); - + if(((argc() > 1) && (intval(argv(1)))) || !feature_enabled(local_channel(),'multi_profiles')) { if(feature_enabled(local_channel(),'multi_profiles')) $id = \App::$argv[1]; @@ -652,7 +652,7 @@ class Profiles extends \Zotlabs\Web\Controller { ); if($x) $id = $x[0]['id']; - } + } $r = q("SELECT * FROM profile WHERE id = %d AND uid = %d LIMIT 1", intval($id), intval(local_channel()) @@ -661,20 +661,20 @@ class Profiles extends \Zotlabs\Web\Controller { notice( t('Profile not found.') . EOL); return; } - + $editselect = 'none'; - + \App::$page['htmlhead'] .= replace_macros(get_markup_template('profed_head.tpl'), array( '$baseurl' => z_root(), '$editselect' => $editselect, )); - + $advanced = ((feature_enabled(local_channel(),'advanced_profiles')) ? true : false); if($advanced) $fields = $profile_fields_advanced; else $fields = $profile_fields_basic; - + $hide_friends = array( 'hide_friends', t('Hide your connections list from viewers of this profile'), @@ -682,36 +682,36 @@ class Profiles extends \Zotlabs\Web\Controller { '', array(t('No'),t('Yes')) ); - + $q = q("select * from profdef where true"); if($q) { $extra_fields = array(); - + foreach($q as $qq) { $mine = q("select v from profext where k = '%s' and hash = '%s' and channel_id = %d limit 1", - dbesc($qq['field_name']), + dbesc($qq['field_name']), dbesc($r[0]['profile_guid']), intval(local_channel()) ); - + if(array_key_exists($qq['field_name'],$fields)) { $extra_fields[] = array($qq['field_name'],$qq['field_desc'],(($mine) ? $mine[0]['v'] : ''), $qq['field_help']); } } } - + //logger('extra_fields: ' . print_r($extra_fields,true)); $vc = $r[0]['profile_vcard']; - $vctmp = (($vc) ? \Sabre\VObject\Reader::read($vc) : null); + $vctmp = (($vc) ? \Sabre\VObject\Reader::read($vc) : null); $vcard = (($vctmp) ? get_vcard_array($vctmp,$r[0]['id']) : [] ); - + $f = get_config('system','birthday_input_format'); if(! $f) $f = 'ymd'; - + $is_default = (($r[0]['is_default']) ? 1 : 0); - + $tpl = get_markup_template("profile_edit.tpl"); $o .= replace_macros($tpl,array( '$multi_profiles' => ((feature_enabled(local_channel(),'multi_profiles')) ? true : false), @@ -749,7 +749,7 @@ class Profiles extends \Zotlabs\Web\Controller { '$default' => t('This is your default profile.') . EOL . translate_scope(map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_profile'))), '$advanced' => $advanced, '$name' => array('name', t('Your full name'), $r[0]['fullname'], t('Required'), '*'), - '$pdesc' => array('pdesc', t('Title/Description'), $r[0]['pdesc']), + '$pdesc' => array('pdesc', t('Short title/description'), $r[0]['pdesc'], t('Maximal 190 characters'), '', 'maxlength="190"'), '$dob' => dob($r[0]['dob']), '$hide_friends' => $hide_friends, '$address' => array('address', t('Street address'), $r[0]['address']), @@ -802,18 +802,18 @@ class Profiles extends \Zotlabs\Web\Controller { '$delete' => t('Delete'), '$cancel' => t('Cancel'), )); - + $arr = array('profile' => $r[0], 'entry' => $o); call_hooks('profile_edit', $arr); - + return $o; } else { - + $r = q("SELECT * FROM profile WHERE uid = %d", local_channel()); if($r) { - + $tpl = get_markup_template('profile_entry.tpl'); foreach($r as $rr) { $profiles .= replace_macros($tpl, array( @@ -821,24 +821,24 @@ class Profiles extends \Zotlabs\Web\Controller { '$id' => $rr['id'], '$alt' => t('Profile Image'), '$profile_name' => $rr['profile_name'], - '$visible' => (($rr['is_default']) - ? '<strong>' . translate_scope(map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_profile'))) . '</strong>' + '$visible' => (($rr['is_default']) + ? '<strong>' . translate_scope(map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_profile'))) . '</strong>' : '<a href="' . z_root() . '/profperm/' . $rr['id'] . '" />' . t('Edit visibility') . '</a>') )); } - + $tpl_header = get_markup_template('profile_listing_header.tpl'); $o .= replace_macros($tpl_header,array( '$header' => t('Edit Profiles'), '$cr_new' => t('Create New'), '$cr_new_link' => 'profiles/new?t=' . get_form_security_token("profile_new"), '$profiles' => $profiles - )); - + )); + } return $o; } - + } - + } diff --git a/Zotlabs/Module/Pubsites.php b/Zotlabs/Module/Pubsites.php index daec5dde3..fd5aeaa72 100644 --- a/Zotlabs/Module/Pubsites.php +++ b/Zotlabs/Module/Pubsites.php @@ -1,18 +1,18 @@ <?php namespace Zotlabs\Module; +use Zotlabs\Lib\Libzotdir; class Pubsites extends \Zotlabs\Web\Controller { function get() { - require_once('include/dir_fns.php'); $dirmode = intval(get_config('system','directory_mode')); - + if(($dirmode == DIRECTORY_MODE_PRIMARY) || ($dirmode == DIRECTORY_MODE_STANDALONE)) { $url = z_root() . '/dirsearch'; } if(! $url) { - $directory = find_upstream_directory($dirmode); + $directory = Libzotdir::find_upstream_directory($dirmode); $url = $directory['url'] . '/dirsearch'; } $url .= '/sites'; @@ -20,12 +20,12 @@ class Pubsites extends \Zotlabs\Web\Controller { $rating_enabled = get_config('system','rating_enabled'); $o .= '<div class="generic-content-wrapper">'; - + $o .= '<div class="section-title-wrapper"><h2>' . t('Public Hubs') . '</h2></div>'; - - $o .= '<div class="section-content-tools-wrapper"><div class="descriptive-text">' . + + $o .= '<div class="section-content-tools-wrapper"><div class="descriptive-text">' . t('The listed hubs allow public registration for the $Projectname network. All hubs in the network are interlinked so membership on any of them conveys membership in the network as a whole. Some hubs may require subscription or provide tiered service plans. The hub itself <strong>may</strong> provide additional details.') . '</div>' . EOL; - + $ret = z_fetch_url($url); if($ret['success']) { $j = json_decode($ret['body'],true); @@ -48,8 +48,8 @@ class Pubsites extends \Zotlabs\Web\Controller { $host = strtolower(substr($jj['url'],strpos($jj['url'],'://')+3)); $rate_links = ((local_channel()) ? '<td><a href="rate?f=&target=' . $host . '" class="btn-btn-default"><i class="fa fa-check-square-o"></i> ' . t('Rate') . '</a></td>' : ''); $location = ''; - if(!empty($jj['location'])) { - $location = '<p title="' . t('Location') . '" style="margin: 5px 5px 0 0; text-align: right"><i class="fa fa-globe"></i> ' . $jj['location'] . '</p>'; + if(!empty($jj['location'])) { + $location = '<p title="' . t('Location') . '" style="margin: 5px 5px 0 0; text-align: right"><i class="fa fa-globe"></i> ' . $jj['location'] . '</p>'; } else { $location = '<br /> '; @@ -61,14 +61,14 @@ class Pubsites extends \Zotlabs\Web\Controller { $o .= '</tr>'; } } - + $o .= '</table>'; - + $o .= '</div></div>'; - + } } return $o; } - + } diff --git a/Zotlabs/Module/Pubstream.php b/Zotlabs/Module/Pubstream.php index 113f0a196..583974e22 100644 --- a/Zotlabs/Module/Pubstream.php +++ b/Zotlabs/Module/Pubstream.php @@ -16,10 +16,8 @@ class Pubstream extends \Zotlabs\Web\Controller { if(! Apps::system_app_installed(local_channel(), 'Public Stream')) { //Do not display any associated widgets at this point App::$pdl = ''; - - $o = '<b>' . t('Public Stream App') . ' (' . t('Not Installed') . '):</b><br>'; - $o .= t('The unmoderated public stream of this hub'); - return $o; + $papp = Apps::get_papp('Public Stream'); + return Apps::app_render($papp, 'module'); } } @@ -44,19 +42,16 @@ class Pubstream extends \Zotlabs\Web\Controller { $site_firehose = false; } - $mid = ((x($_REQUEST,'mid')) ? $_REQUEST['mid'] : ''); - $hashtags = ((x($_REQUEST,'tag')) ? $_REQUEST['tag'] : ''); - - - if(strpos($mid,'b64.') === 0) - $decoded = @base64url_decode(substr($mid,4)); - if($decoded) - $mid = $decoded; + $mid = ((x($_REQUEST, 'mid')) ? unpack_link_id($_REQUEST['mid']) : ''); + if ($mid === false) { + notice(t('Malformed message id.') . EOL); + return; + } + $hashtags = ((x($_REQUEST,'tag')) ? $_REQUEST['tag'] : ''); $item_normal = item_normal(); $item_normal_update = item_normal_update(); - - $net = ((array_key_exists('net',$_REQUEST)) ? escape_tags($_REQUEST['net']) : ''); + $net = ((array_key_exists('net',$_REQUEST)) ? escape_tags($_REQUEST['net']) : ''); $title = replace_macros(get_markup_template("section_title.tpl"),array( '$title' => (($hashtags) ? '#' . htmlspecialchars($hashtags, ENT_COMPAT,'UTF-8') : '') @@ -65,15 +60,15 @@ class Pubstream extends \Zotlabs\Web\Controller { $o = (($hashtags) ? $title : ''); if(local_channel() && (! $update)) { - + $channel = \App::get_channel(); $channel_acl = array( - 'allow_cid' => $channel['channel_allow_cid'], - 'allow_gid' => $channel['channel_allow_gid'], - 'deny_cid' => $channel['channel_deny_cid'], + 'allow_cid' => $channel['channel_allow_cid'], + 'allow_gid' => $channel['channel_allow_gid'], + 'deny_cid' => $channel['channel_deny_cid'], 'deny_gid' => $channel['channel_deny_gid'] - ); + ); $x = array( 'is_owner' => true, @@ -94,12 +89,12 @@ class Pubstream extends \Zotlabs\Web\Controller { 'jotnets' => true, 'reset' => t('Reset form') ); - + $o .= '<div id="jot-popup">'; $o .= status_editor($a,$x,false,'Pubstream'); $o .= '</div>'; } - + if(! $update && !$load) { nav_set_selected(t('Public Stream')); @@ -110,15 +105,14 @@ class Pubstream extends \Zotlabs\Web\Controller { $maxheight = get_config('system','home_divmore_height'); if(! $maxheight) $maxheight = 400; - + $o .= '<div id="live-pubstream"></div>' . "\r\n"; - $o .= "<script> var profile_uid = " . ((intval(local_channel())) ? local_channel() : (-1)) - . "; var profile_page = " . \App::$pager['page'] + $o .= "<script> var profile_uid = " . ((intval(local_channel())) ? local_channel() : (-1)) + . "; var profile_page = " . \App::$pager['page'] . "; divmore_height = " . intval($maxheight) . "; </script>\r\n"; - - //if we got a decoded hash we must encode it again before handing to javascript - if($decoded) - $mid = 'b64.' . base64url_encode($mid); + + //if we got a decoded hash we must encode it again before handing to javascript + $mid = gen_link_id($mid); \App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"),array( '$baseurl' => z_root(), @@ -151,7 +145,7 @@ class Pubstream extends \Zotlabs\Web\Controller { '$dbegin' => '' )); } - + if($update && ! $load) { // only setup pagination on initial page view $pager_sql = ''; @@ -160,10 +154,10 @@ class Pubstream extends \Zotlabs\Web\Controller { \App::set_pager_itemspage(10); $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(\App::$pager['itemspage']), intval(\App::$pager['start'])); } - + require_once('include/channel.php'); require_once('include/security.php'); - + if($site_firehose) { $uids = " and item.uid in ( " . stream_perms_api_uids(PERMS_PUBLIC) . " ) and item_private = 0 and item_wall = 1 "; } @@ -173,7 +167,7 @@ class Pubstream extends \Zotlabs\Web\Controller { $sql_extra = item_permissions_sql($sys['channel_id']); \App::$data['firehose'] = intval($sys['channel_id']); } - + if(get_config('system','public_list_mode')) $page_mode = 'list'; else @@ -184,7 +178,7 @@ class Pubstream extends \Zotlabs\Web\Controller { $sql_extra .= protect_sprintf(term_query('item', $hashtags, TERM_HASHTAG, TERM_COMMUNITYTAG)); } - $net_query = (($net) ? " left join xchan on xchan_hash = author_xchan " : ''); + $net_query = (($net) ? " left join xchan on xchan_hash = author_xchan " : ''); $net_query2 = (($net) ? " and xchan_network = '" . protect_sprintf(dbesc($net)) . "' " : ''); $abook_uids = " and abook.abook_channel = " . intval(\App::$profile['profile_uid']) . " "; @@ -196,13 +190,13 @@ class Pubstream extends \Zotlabs\Web\Controller { //logger('update: ' . $update . ' load: ' . $load); if($update) { - - $ordering = "commented"; - + + $ordering = get_config('system', 'pubstream_ordering', 'commented'); + if($load) { if($mid) { $r = q("SELECT parent AS item_id FROM item - left join abook on item.author_xchan = abook.abook_xchan + left join abook on item.author_xchan = abook.abook_xchan $net_query WHERE mid = '%s' $uids $item_normal and (abook.abook_blocked = 0 or abook.abook_flags is null) @@ -212,7 +206,7 @@ class Pubstream extends \Zotlabs\Web\Controller { } else { // Fetch a page full of parent items for this page - $r = q("SELECT item.id AS item_id FROM item + $r = dbq("SELECT item.id AS item_id FROM item left join abook on ( item.author_xchan = abook.abook_xchan $abook_uids ) $net_query WHERE true $uids and item.item_thread_top = 1 $item_normal @@ -234,7 +228,7 @@ class Pubstream extends \Zotlabs\Web\Controller { ); } else { - $r = q("SELECT parent AS item_id FROM item + $r = dbq("SELECT parent AS item_id FROM item left join abook on item.author_xchan = abook.abook_xchan $net_query WHERE true $uids $item_normal_update @@ -247,20 +241,19 @@ class Pubstream extends \Zotlabs\Web\Controller { // Then fetch all the children of the parents that are on this page $parents_str = ''; - + if($r) { - + $parents_str = ids_to_querystr($r,'item_id'); - - $items = q("SELECT item.*, item.id AS item_id FROM item + + $items = dbq("SELECT item.*, item.id AS item_id FROM item WHERE true $uids $item_normal - AND item.parent IN ( %s ) - $sql_extra ", - dbesc($parents_str) + AND item.parent IN ( $parents_str ) + $sql_extra" ); - + // use effective_uid param of xchan_query to help sort out comment permission - // for sys_channel owned items. + // for sys_channel owned items. xchan_query($items,true,(($sys) ? local_channel() : 0)); $items = fetch_post_tags($items,true); @@ -269,9 +262,9 @@ class Pubstream extends \Zotlabs\Web\Controller { else { $items = array(); } - + } - + // fake it $mode = (($hashtags) ? 'search' : 'pubstream'); @@ -279,13 +272,13 @@ class Pubstream extends \Zotlabs\Web\Controller { if($mid) $o .= '<div id="content-complete"></div>'; - + if(($items) && (! $update)) $o .= alt_pager(count($items)); $_SESSION['loadtime'] = datetime_convert(); return $o; - + } } diff --git a/Zotlabs/Module/Randprof.php b/Zotlabs/Module/Randprof.php index c38b07ead..731d3aece 100644 --- a/Zotlabs/Module/Randprof.php +++ b/Zotlabs/Module/Randprof.php @@ -15,7 +15,7 @@ class Randprof extends \Zotlabs\Web\Controller { $x = random_profile(); if($x) goaway(chanlink_hash($x)); - + /** FIXME this doesn't work at the moment as a fallback */ goaway(z_root() . '/profile'); } @@ -25,13 +25,11 @@ class Randprof extends \Zotlabs\Web\Controller { if(! Apps::system_app_installed(local_channel(), 'Random Channel')) { //Do not display any associated widgets at this point App::$pdl = ''; - - $o = '<b>' . t('Random Channel App') . ' (' . t('Not Installed') . '):</b><br>'; - $o .= t('Visit a random channel in the $Projectname network'); - return $o; + $papp = Apps::get_papp('Random Channel'); + return Apps::app_render($papp, 'module'); } } } - + } diff --git a/Zotlabs/Module/Rate.php b/Zotlabs/Module/Rate.php deleted file mode 100644 index c03aaa54f..000000000 --- a/Zotlabs/Module/Rate.php +++ /dev/null @@ -1,172 +0,0 @@ -<?php -namespace Zotlabs\Module; - - - -class Rate extends \Zotlabs\Web\Controller { - - function init() { - - if(! local_channel()) - return; - - $channel = \App::get_channel(); - - $target = $_REQUEST['target']; - if(! $target) - return; - - \App::$data['target'] = $target; - - if($target) { - $r = q("SELECT * FROM xchan where xchan_hash like '%s' LIMIT 1", - dbesc($target) - ); - if($r) { - \App::$poi = $r[0]; - } - else { - $r = q("select * from site where site_url like '%s' and site_type = %d", - dbesc('%' . $target), - intval(SITE_TYPE_ZOT) - ); - if($r) { - \App::$data['site'] = $r[0]; - \App::$data['site']['site_url'] = strtolower($r[0]['site_url']); - } - } - } - - - return; - - } - - - function post() { - - if(! local_channel()) - return; - - if(! \App::$data['target']) - return; - - if(! $_REQUEST['execute']) - return; - - $channel = \App::get_channel(); - - $rating = intval($_POST['rating']); - if($rating < (-10)) - $rating = (-10); - if($rating > 10) - $rating = 10; - - $rating_text = trim(escape_tags($_REQUEST['rating_text'])); - - $signed = \App::$data['target'] . '.' . $rating . '.' . $rating_text; - - $sig = base64url_encode(rsa_sign($signed,$channel['channel_prvkey'])); - - $z = q("select * from xlink where xlink_xchan = '%s' and xlink_link = '%s' and xlink_static = 1 limit 1", - dbesc($channel['channel_hash']), - dbesc(\App::$data['target']) - ); - - if($z) { - $record = $z[0]['xlink_id']; - $w = q("update xlink set xlink_rating = '%d', xlink_rating_text = '%s', xlink_sig = '%s', xlink_updated = '%s' - where xlink_id = %d", - intval($rating), - dbesc($rating_text), - dbesc($sig), - dbesc(datetime_convert()), - intval($record) - ); - } - else { - $w = q("insert into xlink ( xlink_xchan, xlink_link, xlink_rating, xlink_rating_text, xlink_sig, xlink_updated, xlink_static ) values ( '%s', '%s', %d, '%s', '%s', '%s', 1 ) ", - dbesc($channel['channel_hash']), - dbesc(\App::$data['target']), - intval($rating), - dbesc($rating_text), - dbesc($sig), - dbesc(datetime_convert()) - ); - $z = q("select * from xlink where xlink_xchan = '%s' and xlink_link = '%s' and xlink_static = 1 limit 1", - dbesc($channel['channel_hash']), - dbesc(\App::$data['target']) - ); - if($z) - $record = $z[0]['xlink_id']; - } - - if($record) { - \Zotlabs\Daemon\Master::Summon(array('Ratenotif','rating',$record)); - } - - } - - function get() { - - if(! local_channel()) { - notice( t('Permission denied.') . EOL); - return; - } - - // if(! \App::$data['target']) { - // notice( t('No recipients.') . EOL); - // return; - // } - - $rating_enabled = get_config('system','rating_enabled'); - if(! $rating_enabled) { - notice('Ratings are disabled on this site.'); - return; - } - - $channel = \App::get_channel(); - - $r = q("select * from xlink where xlink_xchan = '%s' and xlink_link = '%s' and xlink_static = 1", - dbesc($channel['channel_hash']), - dbesc(\App::$data['target']) - ); - if($r) { - \App::$data['xlink'] = $r[0]; - $rating_val = $r[0]['xlink_rating']; - $rating_text = $r[0]['xlink_rating_text']; - } - else { - $rating_val = 0; - $rating_text = ''; - } - - if($rating_enabled) { - $rating = replace_macros(get_markup_template('rating_slider.tpl'),array( - '$min' => -10, - '$val' => $rating_val - )); - } - else { - $rating = false; - } - - $o = replace_macros(get_markup_template('rating_form.tpl'),array( - '$header' => t('Rating'), - '$website' => t('Website:'), - '$site' => ((\App::$data['site']) ? '<a href="' . \App::$data['site']['site_url'] . '" >' . \App::$data['site']['site_url'] . '</a>' : ''), - 'target' => \App::$data['target'], - '$tgt_name' => ((\App::$poi && \App::$poi['xchan_name']) ? \App::$poi['xchan_name'] : sprintf( t('Remote Channel [%s] (not yet known on this site)'), substr(\App::$data['target'],0,16))), - '$lbl_rating' => t('Rating (this information is public)'), - '$lbl_rating_txt' => t('Optionally explain your rating (this information is public)'), - '$rating_txt' => $rating_text, - '$rating' => $rating, - '$rating_val' => $rating_val, - '$slide' => $slide, - '$submit' => t('Submit') - )); - - return $o; - - } -} diff --git a/Zotlabs/Module/Ratings.php b/Zotlabs/Module/Ratings.php deleted file mode 100644 index 055b16ca3..000000000 --- a/Zotlabs/Module/Ratings.php +++ /dev/null @@ -1,109 +0,0 @@ -<?php -namespace Zotlabs\Module; - -require_once('include/dir_fns.php'); - - -class Ratings extends \Zotlabs\Web\Controller { - - function init() { - - if(observer_prohibited()) { - return; - } - - if(local_channel()) - load_contact_links(local_channel()); - - $dirmode = intval(get_config('system','directory_mode')); - - $x = find_upstream_directory($dirmode); - if($x) - $url = $x['url']; - - $rating_enabled = get_config('system','rating_enabled'); - - if(! $rating_enabled) - return; - - if(argc() > 1) - $hash = argv(1); - - if(! $hash) { - notice('Must supply a channel identififier.'); - return; - } - - $results = false; - - $x = z_fetch_url($url . '/ratingsearch/' . urlencode($hash)); - - - if($x['success']) - $results = json_decode($x['body'],true); - - - if((! $results) || (! $results['success'])) { - - notice('No results.'); - return; - } - - if(array_key_exists('xchan_hash',$results['target'])) - \App::$poi = $results['target']; - - $friends = array(); - $others = array(); - - if($results['ratings']) { - foreach($results['ratings'] as $n) { - if(is_array(\App::$contacts) && array_key_exists($n['xchan_hash'],\App::$contacts)) - $friends[] = $n; - else - $others[] = $n; - } - } - - \App::$data = array('target' => $results['target'], 'results' => array_merge($friends,$others)); - - if(! \App::$data['results']) { - notice( t('No ratings') . EOL); - } - - return; - } - - - - - - function get() { - - if(observer_prohibited()) { - notice( t('Public access denied.') . EOL); - return; - } - - $rating_enabled = get_config('system','rating_enabled'); - - if(! $rating_enabled) - return; - - $site_target = ((array_key_exists('target',\App::$data) && array_key_exists('site_url',\App::$data['target'])) ? - '<a href="' . \App::$data['target']['site_url'] . '" >' . \App::$data['target']['site_url'] . '</a>' : ''); - - - $o = replace_macros(get_markup_template('prep.tpl'),array( - '$header' => t('Ratings'), - '$rating_lbl' => t('Rating: ' ), - '$website' => t('Website: '), - '$site' => $site_target, - '$rating_text_lbl' => t('Description: '), - '$raters' => \App::$data['results'] - )); - - return $o; - } - - -} diff --git a/Zotlabs/Module/Ratingsearch.php b/Zotlabs/Module/Ratingsearch.php deleted file mode 100644 index dcbfd6a9b..000000000 --- a/Zotlabs/Module/Ratingsearch.php +++ /dev/null @@ -1,78 +0,0 @@ -<?php -namespace Zotlabs\Module; - - - -class Ratingsearch extends \Zotlabs\Web\Controller { - - function init() { - - $ret = array('success' => false); - - $dirmode = intval(get_config('system','directory_mode')); - - if($dirmode == DIRECTORY_MODE_NORMAL) { - $ret['message'] = 'This site is not a directory server.'; - json_return_and_die($ret); - } - - if(argc() > 1) - $hash = argv(1); - - if(! $hash) { - $ret['message'] = 'No channel identifier'; - json_return_and_die($ret); - } - - if(strpos($hash,'@')) { - $r = q("select * from hubloc where hubloc_addr = '%s' limit 1", - dbesc($hash) - ); - if($r) - $hash = $r[0]['hubloc_hash']; - } - - $p = q("select * from xchan where xchan_hash like '%s'", - dbesc($hash . '%') - ); - - if($p) - $target = $p[0]['xchan_hash']; - else { - $p = q("select * from site where site_url like '%s' and site_type = %d ", - dbesc('%' . $hash), - intval(SITE_TYPE_ZOT) - ); - if($p) { - $target = strtolower($hash); - } - else { - $ret['message'] = 'Rating target not found'; - json_return_and_die($ret); - } - } - - if($p) - $ret['target'] = $p[0]; - - $ret['success'] = true; - - $r = q("select * from xlink left join xchan on xlink_xchan = xchan_hash - where xlink_link = '%s' and xlink_rating != 0 and xlink_static = 1 - and xchan_hidden = 0 and xchan_orphan = 0 and xchan_deleted = 0 - order by xchan_name asc", - dbesc($target) - ); - - if($r) { - $ret['ratings'] = $r; - } - else - $ret['ratings'] = array(); - - json_return_and_die($ret); - - } - - -} diff --git a/Zotlabs/Module/Rbmark.php b/Zotlabs/Module/Rbmark.php index 226cef69e..87b774495 100644 --- a/Zotlabs/Module/Rbmark.php +++ b/Zotlabs/Module/Rbmark.php @@ -6,12 +6,11 @@ require_once('include/crypto.php'); require_once('include/items.php'); require_once('include/taxonomy.php'); require_once('include/conversation.php'); -require_once('include/zot.php'); require_once('include/bookmarks.php'); /** * remote bookmark - * + * * https://yoursite/rbmark?f=&title=&url=&private=&remote_return= * * This can be called via either GET or POST, use POST for long body content as suhosin often limits GET parameter length @@ -31,45 +30,45 @@ class Rbmark extends \Zotlabs\Web\Controller { function post() { if($_POST['submit'] !== t('Save')) return; - + logger('rbmark_post: ' . print_r($_REQUEST,true)); - + $channel = \App::get_channel(); - + $t = array('url' => escape_tags($_REQUEST['url']),'term' => escape_tags($_REQUEST['title'])); bookmark_add($channel,$channel,$t,((x($_REQUEST,'private')) ? intval($_REQUEST['private']) : 0), array('menu_id' => ((x($_REQUEST,'menu_id')) ? intval($_REQUEST['menu_id']) : 0), 'menu_name' => ((x($_REQUEST,'menu_name')) ? escape_tags($_REQUEST['menu_name']) : ''), 'ischat' => ((x($_REQUEST['ischat'])) ? intval($_REQUEST['ischat']) : 0) )); - + goaway(z_root() . '/bookmarks'); - + } - - + + function get() { - + $o = ''; - + if(! local_channel()) { - + // The login procedure is going to bugger our $_REQUEST variables // so save them in the session. - + if(array_key_exists('url',$_REQUEST)) { $_SESSION['bookmark'] = $_REQUEST; } return login(); } - + // If we have saved rbmark session variables, but nothing in the current $_REQUEST, recover the saved variables - + if((! array_key_exists('url',$_REQUEST)) && (array_key_exists('bookmark',$_SESSION))) { $_REQUEST = $_SESSION['bookmark']; unset($_SESSION['bookmark']); } - + if($_REQUEST['remote_return']) { $_SESSION['remote_return'] = $_REQUEST['remote_return']; } @@ -78,12 +77,12 @@ class Rbmark extends \Zotlabs\Web\Controller { goaway($_SESSION['remote_return']); goaway(z_root() . '/bookmarks'); } - + $channel = \App::get_channel(); - - + + $m = menu_list($channel['channel_id'],'',MENU_BOOKMARK); - + $menus = array(); if($m) { $menus = array(0 => ''); @@ -92,10 +91,10 @@ class Rbmark extends \Zotlabs\Web\Controller { } } $menu_select = array('menu_id',t('Select a bookmark folder'),false,'',$menus); - - + + $o .= replace_macros(get_markup_template('rbmark.tpl'), array( - + '$header' => t('Save Bookmark'), '$url' => array('url',t('URL of bookmark'),escape_tags($_REQUEST['url'])), '$title' => array('title',t('Description'),escape_tags($_REQUEST['title'])), @@ -104,18 +103,18 @@ class Rbmark extends \Zotlabs\Web\Controller { '$submit' => t('Save'), '$menu_name' => array('menu_name',t('Or enter new bookmark folder name'),'',''), '$menus' => $menu_select - + )); - - - - - - + + + + + + return $o; - + } - - - + + + } diff --git a/Zotlabs/Module/Regate.php b/Zotlabs/Module/Regate.php new file mode 100644 index 000000000..379195461 --- /dev/null +++ b/Zotlabs/Module/Regate.php @@ -0,0 +1,447 @@ +<?php + +namespace Zotlabs\Module; + +require_once('include/security.php'); + +/** + * + * @version 2.0.0 + * @author hilmar runge + * @since 2020-03-03 + * Check verification pin + * input field email address + * input field pin (told during register) + * check duty + * check startup and expire + * compare email address + * check pin + * limited tries to enter the correct pin/pass 2 handle via f2b + * on success create account and update register + * + */ + + define ( 'REGISTER_AGREED', 0x0020 ); + define ( 'REGISTER_DENIED', 0x0040 ); + +class Regate extends \Zotlabs\Web\Controller { + + const MYP = 'ZAR'; //ZAR1x + const VERSION = '2.0.0'; + + + function post() { + + check_form_security_token_redirectOnErr('/', 'regate'); + + if ( argc() > 1 ) { + $did2 = hex2bin( substr( argv(1), 0, -1) ); + $didx = substr( argv(1), -1 ); + } + + $msg = ''; + $nextpage = ''; + + if ($did2) { + + $nowhhmm = date('Hi'); + $day = date('N'); + $now = datetime_convert(); + $ip = $_SERVER['REMOTE_ADDR']; + + $isduty = zar_register_dutystate(); + + if (!$_SESSION['zar']['invite_in_progress'] && ($isduty['isduty'] !== false && $isduty['isduty'] != 1)) { + // normally, that should never happen here + // log suitable for fail2ban also + $logmsg = 'ZAR1230S Unexpected registration verification request for ' + . get_config('system','sitename') . ' arrived from § ' . $ip . ' §'; + zar_log($logmsg); + goaway(z_root()); + } + + // do we have a valid dId2 ? + if (($didx == 'a' && substr( $did2 , -2) == substr( base_convert( md5( substr( $did2, 1, -2) ),16 ,10), -2)) || ($didx == 'e') || ($didx == 'i')) { + // check startup and expiration via [=[register + $r = q("SELECT * FROM register WHERE reg_vital = 1 AND reg_did2 = '%s' ORDER BY reg_created DESC ", + dbesc($did2) + ); + if ($r && count($r)) { + $r = $r[0]; + // check timeframe + if ($r['reg_startup'] <= $now && $r['reg_expires'] >= $now) { + if (isset($_POST['resend']) && $didx == 'e') { + $re = q("SELECT * FROM register WHERE reg_vital = 1 AND reg_didx = 'e' AND reg_did2 = '%s' ORDER BY reg_created DESC ", dbesc($r['reg_did2']) ); + if ($re) { + $re = $re[0]; + $reonar = json_decode($re['reg_stuff'], true); + if ($reonar) { + $reonar['subject'] = 'Re,Fwd,' . $reonar['subject']; + $zm = zar_reg_mail($reonar); + $msg = (($zm) ? t('Email resent') : t('Email resend failed')); + zar_log((($zm) ? 'ZAR1238I' : 'ZAR1238E') . ' ' . $msg . ' ' . $r['reg_did2']); + info($msg); + return; + } + } + } + + // check hash + if ( $didx == 'a' ) + $acpin = (preg_match('/^[0-9]{6,6}$/', $_POST['acpin']) ? $_POST['acpin'] : false); + elseif ( $didx == 'e' ) + $acpin = (preg_match('/^[0-9a-f]{24,24}$/', $_POST['acpin']) ? $_POST['acpin'] : false); + elseif ( $didx == 'i' ) + $acpin = $r['reg_hash']; + else + $acpin = false; + + if ( $acpin && ($r['reg_hash'] == $acpin )) { + + $flags = $r['reg_flags']; + if (($flags & ACCOUNT_UNVERIFIED) == ACCOUNT_UNVERIFIED) { + + // verification success + $msg_code = 'ZAR1237I'; + $msg = t('Verification successful'); + $reonar = json_decode( $r['reg_stuff'], true); + $reonar['valid'] = $now . ',' . $ip . ' ' . $did2 . ' ' . $msg_code . ' ' . $msg; + + // clear flag + $flags &= $flags ^ ACCOUNT_UNVERIFIED; + + // are we invited by the admin? + $isa = get_account_by_id($r['reg_uid']); + $isa = ($isa && ($isa['account_roles'] && ACCOUNT_ROLE_ADMIN)); + + // approve contra invite by admin + if ($isa && get_config('system','register_policy') == REGISTER_APPROVE) { + $flags &= $flags ^ ACCOUNT_PENDING; + } + + // sth todo? + $vital = $flags == 0 ? 0 : 1; + + // set flag + $flags |= REGISTER_AGREED; + zar_log($msg . ' ' . $did2 . ':flags' . $flags . ',rid' . $r['reg_id']); + + q("START TRANSACTION"); + + $qu = q("UPDATE register SET reg_stuff = '%s', reg_vital = %d, reg_flags = %d " + ." WHERE reg_id = %d ", + dbesc(json_encode($reonar)), + intval($vital), + intval($flags), + intval($r['reg_id']) + ); + + if (($flags & ACCOUNT_PENDING ) == ACCOUNT_PENDING) { + $nextpage = 'regate/' . bin2hex($did2) . $didx; + q("COMMIT"); + } + elseif (($flags ^ REGISTER_AGREED) == 0) { + + $cra = create_account_from_register([ 'reg_id' => $r['reg_id'] ]); + + if ($cra['success']) { + + q("COMMIT"); + $msg = t('Account successfull created'); + // zar_log($msg . ':' . print_r($cra, true)); + zar_log('ZAR1238I ' . $msg . ' ' . $cra['account']['account_email'] + . ' ' . $cra['account']['account_language']); + + authenticate_success($cra['account'],null,true,false,true); + + $nextpage = 'new_channel'; + + $auto_create = get_config('system', 'auto_channel_create', 1); + + if($auto_create) { + + $new_channel = ['success' => false]; + + // We do not reserve a channel_address before the registration is verified + // and possibly approved by the admin. + // If the provided channel_address has been claimed meanwhile, + // we will proceed to /new_channel. + + if(isset($reonar['chan.did1']) && check_webbie([$reonar['chan.did1']])) { + + // prepare channel creation + if($reonar['chan.name']) + set_aconfig($cra['account']['account_id'], 'register', 'channel_name', $reonar['chan.name']); + + if($reonar['chan.did1']) + set_aconfig($cra['account']['account_id'], 'register', 'channel_address', $reonar['chan.did1']); + + $permissions_role = get_config('system','default_permissions_role'); + if($permissions_role) + set_aconfig($cra['account']['account_id'], 'register', 'permissions_role', $permissions_role); + + // create channel + $new_channel = auto_channel_create($cra['account']['account_id']); + + if($new_channel['success']) { + $channel_id = $new_channel['channel']['channel_id']; + change_channel($channel_id); + $nextpage = 'profiles/' . $channel_id; + $msg_code = 'ZAR1239I'; + $msg = t('Channel successfull created') . ' ' . $did2; + } + } + + if(!$new_channel['success']) { + $msg_code = 'ZAR1239E'; + $msg = t('Automatic channel creation failed. Please create a channel.') . ' ' . $did2; + $nextpage = 'new_channel?name=' . $reonar['chan.name']; + } + + zar_log($msg_code . ' ' . $msg . ' ' . $reonar['chan.did1'] . ' (' . $reonar['chan.name'] . ')'); + + } + unset($_SESSION['login_return_url']); + } + else { + q("ROLLBACK"); + $msg_code = 'ZAR1238E'; + $msg = t('Account creation error'); + zar_log($msg_code . ' ' . $msg . ': ' . print_r($cra, true)); + } + } + else { + // new flags implemented and not recognized or sth like + zar_log('ZAR1237D unexpected,' . $flags); + } + } + else { + // nothing to confirm + $msg_code = 'ZAR1236E'; + $msg = t('Verify failed'); + } + } + else { + $msg_code = 'ZAR1235E'; + $msg = t('Token verification failed'); + } + } + else { + $msg_code = 'ZAR1234W'; + $msg = t('Request not inside time frame'); + //info($r[0]['reg_startup'] . EOL . $r[0]['reg_expire'] ); + } + } + else { + $msg_code = 'ZAR1232E'; + $msg = t('Identity unknown'); + zar_log($msg_code . ' ' . $msg . ':' . $did2 . $didx); + } + } + else { + $msg_code = 'ZAR1231E'; + $msg = t('dId2 mistaken'); + zar_log($msg_code . ' ' . $msg); + + } + + } + + if ($msg > '') info($msg); + goaway( z_root() . '/' . $nextpage ); + } + + + function get() { + + if (argc() == 1) { + if(isset($_GET['reg_id'])) { + if ( preg_match('/^.{2,64}\@[a-z0-9.-]{4,32}\.[a-z]{2,12}$/', $_GET['reg_id'] ) ) { + // dId2 E email + goaway(z_root() . '/regate/' . bin2hex($_GET['reg_id']) . 'e' ); + } + if ( preg_match('/^d{1,1}[0-9]{5,10}$/', $_GET['reg_id'] ) ) { + // dId2 A artifical & anonymous + goaway(z_root() . '/regate/' . bin2hex($_GET['reg_id']) . 'a' ); + } + notice(t('Identity unknown') . EOL); + } + + $o = replace_macros(get_markup_template('plain.tpl'), [ + '$title' => t('Your Registration ID'), + '$now' => '<form action="regate" method="get"><input type="text" name="reg_id" class="form-control form-group"><button class="btn btn-primary float-right">Submit</button></form>' + ]); + + return $o; + } + + $isduty = zar_register_dutystate(); + $nowfmt = $isduty['nowfmt']; + $atform = $isduty['atform']; + + if ($_SESSION['zar']['delayed']) { + $o = replace_macros(get_markup_template('regate_pre.tpl'), [ + '$title' => t('Registration verification'), + '$now' => $nowfmt, + '$id' => $_SESSION['zar']['id'], + '$pin' => $_SESSION['zar']['pin'], + '$regdelay' => $_SESSION['zar']['regdelay'], + '$regexpire' => $_SESSION['zar']['regexpire'], + '$strings' => [ + t('Hold on, you can start verification in'), + t('Please remember your verification token for ID'), + '', + t('Token validity') + ] + ]); + unset($_SESSION['zar']['delayed']); + return $o; + } + + if (argc() < 2) + return; + + $did2 = hex2bin( substr( argv(1), 0, -1) ); + $didx = substr( argv(1), -1 ); + $deny = argc() > 2 ? argv(2) : ''; + $deny = preg_match('/^[0-9a-f]{8,8}$/', $deny) ? hex2bin($deny) : false; + $now = datetime_convert(); + $ip = $_SERVER['REMOTE_ADDR']; + + $pin = ''; + + if(isset($_SESSION['zar']['pin'])) { + $pin = $_SESSION['zar']['pin']; + unset($_SESSION['zar']['pin']); + } + + // do we have a valid dId2 ? + if (($didx == 'a' && substr( $did2 , -2) == substr( base_convert( md5( substr( $did2, 1, -2) ),16 ,10), -2)) || ($didx == 'e') || ($didx == 'i')) { + + $r = q("SELECT * FROM register WHERE reg_vital = 1 AND reg_didx = '%s' AND reg_did2 = '%s' ORDER BY reg_created DESC", + dbesc($didx), + dbesc($did2) + ); + + if ($r && count($r) && $r[0]['reg_flags'] &= (ACCOUNT_UNVERIFIED | ACCOUNT_PENDING)) { + $r = $r[0]; + + // provide a button in case + $resend = (($r['reg_didx'] == 'e') ? t('Resend email') : ''); + + // is still only instance admins intervention required? + if ($r['reg_flags'] == ACCOUNT_PENDING) { + $o = replace_macros(get_markup_template('regate_post.tpl'), [ + '$title' => t('Registration status'), + '$id' => $did2, + '$strings' => [ + t('Verification successful!'), + t('Your login ID is'), + t('After your account has been approved by our administrator you will be able to login with your login ID and your provided password.') + ] + ]); + } + else { + + if ($deny) { + + if (substr($r['reg_hash'],0,4) == $deny) { + zar_log('ZAR1134S email verfication denied ' . $did2); + + $o = replace_macros(get_markup_template('plain.tpl'), [ + '$title' => t('Registration request revoked'), + '$infos' => t('Sorry for any inconvience. Thank you for your response.') + ]); + + $reonar = json_decode( $r['reg_stuff'], true); + $reonar['deny'] = $now . ',' . $ip . ' ' . $did2 . ' ' . $msg; + $flags = ( $r['reg_flags'] &= ( $r['reg_flags'] ^ ACCOUNT_UNVERIFIED) ) + | ( $r['reg_flags'] |= REGISTER_DENIED); + $rd = q("UPDATE register SET reg_stuff='%s', reg_vital=0, reg_flags=%d WHERE reg_id = %d ", + dbesc(json_encode($reonar)), + intval($flags), + intval($r['reg_id']) + ); + } + else { + zar_log('ZAR1135E not awaited url parameter received'); + goaway(z_root); + } + } + else { + + if ( $r['reg_startup'] <= $now && $r['reg_expires'] >= $now) { + $o = replace_macros(get_markup_template('regate.tpl'), [ + '$form_security_token' => get_form_security_token("regate"), + '$title' => t('Registration verification'), + '$desc' => t('Please enter your verification token for ID'), + '$email_extra' => (($didx === 'e') ? t('Please check your email!') : ''), + '$id' => $did2, + // we might consider to not provide $pin if a registration delay is configured + // and the pin turns out to be readable by bots + '$pin' => $pin, + '$did2' => bin2hex($did2) . $didx, + '$now' => $nowfmt, + '$atform' => $atform, + '$resend' => $resend, + '$submit' => t('Submit'), + '$acpin' => [ 'acpin', t('Verification token'),'','' ] + ]); + } + else { + // expired ? + if ( $now > $r['reg_expires'] ) { + $rd = q("UPDATE register SET reg_vital = 0 WHERE reg_id = %d ", + intval($r['reg_id']) + ); + + $o = replace_macros(get_markup_template('plain.tpl'), [ + '$infos' => t('ID expired'), + ]); + + return $o; + } + + $email_extra = (($didx === 'e') ? t('Please check your email!') : ''); + + $o = replace_macros(get_markup_template('regate_pre.tpl'), [ + '$title' => t('Registration verification'), + '$now' => $nowfmt, + '$id' => $did2, + '$countdown' => datetime_convert('UTC', 'UTC', $r['reg_startup'], 'c'), + '$strings' => [ + t('Hold on, you can start verification in'), + t('You will require the verification token for ID'), + $email_extra + ] + ]); + } + } + } + } + else { + $msg = t('Unknown or expired ID'); + zar_log('ZAR1132E ' . $msg . ':' . $did2 . ',' . $didx); + $o = replace_macros(get_markup_template('plain.tpl'), [ + '$title' => $title, + '$now' => $nowfmt, + '$infos' => $msg + ]); + } + + } + else { + $msg = 'ZAR1131E ' . t('dId2 malformed'); + // $log = ' from § ' . $ip . ' §' . ' (' . dbesc($did2) . ')'; + zar_log($msg); + $o = replace_macros(get_markup_template('plain.tpl'), [ + '$title' => $title, + '$now' => $nowfmt, + '$infos' => $msg + ]); + } + + return $o; + } +} + diff --git a/Zotlabs/Module/Register.php b/Zotlabs/Module/Register.php index 278cf15ca..683fcdc36 100644 --- a/Zotlabs/Module/Register.php +++ b/Zotlabs/Module/Register.php @@ -1,25 +1,34 @@ <?php + namespace Zotlabs\Module; +use App; use Zotlabs\Web\Controller; require_once('include/security.php'); +require_once('include/channel.php'); + class Register extends Controller { + const MYP = 'ZAR'; // ZAR0x + const VERSION = '2.0.0'; + function init() { - + + // ZAR0 + $result = null; $cmd = ((argc() > 1) ? argv(1) : ''); - + // Provide a stored request for somebody desiring a connection // when they first need to register someplace. Once they've - // created a channel, we'll try to revive the connection request + // created a channel, we'll try to revive the connection request // and process it. - + if($_REQUEST['connect']) $_SESSION['connect'] = $_REQUEST['connect']; - + switch($cmd) { case 'invite_check.json': $result = check_account_invite($_REQUEST['invite_code']); @@ -30,50 +39,161 @@ class Register extends Controller { case 'password_check.json': $result = check_account_password($_REQUEST['password1']); break; - default: + default: break; } if($result) { json_return_and_die($result); } } - - + function post() { check_form_security_token_redirectOnErr('/register', 'register'); - $max_dailies = intval(get_config('system','max_daily_registrations')); - if($max_dailies) { - $r = q("select count(account_id) as total from account where account_created > %s - INTERVAL %s", - db_utcnow(), db_quoteinterval('1 day') + /** + * [hilmar:] + * It may happen, the posted form arrives in a strange fashion. With the control of the duty hours + * for registration, the input form was disabled at html. While receiving posted data, checks are + * required if all is on the right road (most posts are not accepted during off duty). + * + */ + + + $act = q("SELECT COUNT(*) AS act FROM account")[0]['act']; + $is247 = false; + $ip = $_SERVER['REMOTE_ADDR']; + $sameip = intval(get_config('system','register_sameip', 3)); + $arr = $_POST; + $invite_code = ((x($arr,'invite_code')) ? notags(trim($arr['invite_code'])) : ''); + $name = ''; + $nick = ''; + $email = ((x($arr,'email')) ? notags(punify(trim($arr['email']))) : ''); + $password = ((x($arr,'password')) ? trim($arr['password']) : ''); + $password2 = ((x($arr,'password2')) ? trim($arr['password2']) : ''); + $register_msg = ((x($arr,'register_msg')) ? notags(trim($arr['register_msg'])) : ''); + $reonar = []; + $auto_create = get_config('system','auto_channel_create', 1); + $duty = zar_register_dutystate(); + + if (!get_config('system', 'register_duty_jso')) { + // if not yet configured default to true + $duty = array( 'isduty' => true, 'atfrm' => '', 'nowfmt' => ''); + } + + if($auto_create) { + $name = escape_tags(trim($arr['name'])); + + $name_error = validate_channelname($name); + if($name_error) { + notice($name_error . EOL); + return $ret; + } + + $nick = mb_strtolower(escape_tags(trim($arr['nickname']))); + if(!$nick) { + notice(t('Nickname is required.')); + return; + } + + if($nick === 'sys') { + notice(t('Reserved nickname. Please choose another.') . EOL); + return; + } + + if(check_webbie([$nick]) !== $nick) { + notice(t('Nickname has unsupported characters or is already being used on this site.') . EOL); + return; + } + } + + $email_verify = get_config('system', 'verify_email'); + if ($email_verify && !$email) { + notice(t('Email address required') . EOL); + return; + } + + if ($email) { + $email_result = check_account_email($email); + if ($email_result['error']) { + if ($email_result['email_unverified']) { + goaway(z_root() . '/regate/' . bin2hex($email) . 'e'); + } + return; + } + + } + + // case when an invited prepares the own account by supply own pw, accept tos, prepage channel (if auto) + if ($email && $invite_code) { + if ( preg_match('/^[a-z0-9]{12,12}$/', $invite_code ) ) { + $is247 = true; + } + } + + if ($act > 0 && !$is247 && !$duty['isduty']) { + // normally (except very 1st timr after install), that should never arrive here (ie js hack or sth like) + // log suitable for f2b also + $logmsg = 'Unexpected registration request off duty'; + notice($logmsg); + zar_log('ZAR0230S ' . $logmsg); + return; + } + + if ($sameip) { + $f = q("SELECT COUNT(reg_atip) AS atip FROM register WHERE reg_vital = 1 AND reg_atip = '%s' ", + dbesc($ip) ); - if($r && $r[0]['total'] >= $max_dailies) { - notice( t('Maximum daily site registrations exceeded. Please try again tomorrow.') . EOL); + if ($f && $f[0]['atip'] >= $sameip) { + $logmsg = 'ZAR0239S Exceeding same ip register request of ' . $sameip; + notice('Registrations from same IP exceeded.'); + zar_log($logmsg); return; } } - + + if (!$password) { + notice(t('No password provided') . EOL); + return; + } + + if ($password !== $password2) { + notice(t('Passwords do not match') . EOL); + return; + } + + $password_result = check_account_password($password); + if(!empty($password_result['error'])) { + $msg = $password_result['message']; + notice($msg); + zar_log($msg . ' ' . $did2); + return; + } + + $salt = random_string(32); + $password = $salt . ',' . hash('whirlpool', $salt . $password); + + // accept tos if(! x($_POST,'tos')) { - notice( t('Please indicate acceptance of the Terms of Service. Registration failed.') . EOL); + // msg! + notice(t('Terms of Service not accepted') . EOL); return; } - - $policy = get_config('system','register_policy'); - - $email_verify = get_config('system','verify_email'); - - + + $policy = get_config('system','register_policy'); + $invonly = get_config('system','invitation_only'); + $invalso = get_config('system','invitation_also'); + switch($policy) { - + case REGISTER_OPEN: $flags = ACCOUNT_OK; break; - + case REGISTER_APPROVE: - $flags = ACCOUNT_BLOCKED | ACCOUNT_PENDING; + $flags = ACCOUNT_PENDING; break; - + default: case REGISTER_CLOSED: if(! is_site_admin()) { @@ -83,164 +203,278 @@ class Register extends Controller { $flags = ACCOUNT_BLOCKED; break; } - - if($email_verify && $policy == REGISTER_OPEN) - $flags = $flags | ACCOUNT_UNVERIFIED; - - - if((! $_POST['password']) || ($_POST['password'] !== $_POST['password2'])) { - notice( t('Passwords do not match.') . EOL); - return; - } - - $arr = $_POST; + + if($email_verify && ($policy == REGISTER_OPEN || $policy == REGISTER_APPROVE)) + $flags = ($flags | ACCOUNT_UNVERIFIED); + + // $arr has $_POST; $arr['account_flags'] = $flags; - - $result = create_account($arr); - - if(! $result['success']) { - notice($result['message']); - return; - } - require_once('include/security.php'); - - - if($_REQUEST['name']) - set_aconfig($result['account']['account_id'],'register','channel_name',$_REQUEST['name']); - if($_REQUEST['nickname']) - set_aconfig($result['account']['account_id'],'register','channel_address',$_REQUEST['nickname']); - if($_REQUEST['permissions_role']) - set_aconfig($result['account']['account_id'],'register','permissions_role',$_REQUEST['permissions_role']); - - - $using_invites = intval(get_config('system','invitation_only')); - $num_invites = intval(get_config('system','number_invites')); - $invite_code = ((x($_POST,'invite_code')) ? notags(trim($_POST['invite_code'])) : ''); - - if($using_invites && $invite_code) { - q("delete from register where hash = '%s'", dbesc($invite_code)); - // @FIXME - this also needs to be considered when using 'invites_remaining' in mod/invite.php - set_aconfig($result['account']['account_id'],'system','invites_remaining',$num_invites); - } - - if($policy == REGISTER_OPEN ) { - if($email_verify) { - $res = verify_email_address($result); - } - else { - $res = send_register_success_email($result['email'],$result['password']); - } - if($res) { - if($invite_code) { - info( t('Registration successful. Continue to create your first channel...') . EOL ) ; - } - else { - info( t('Registration successful. Please check your email for validation instructions.') . EOL ) ; + $now = datetime_convert(); + $well = false; + + // s3 + if ($invite_code) { + + if ($invonly || $invalso) { + + $reg = q("SELECT * from register WHERE reg_vital = 1 AND reg_didx = 'i' AND reg_hash = '%s'", + dbesc($invite_code) + ); + + if ($reg && count($reg) == 1) { + $reg = $reg[0]; + if ($reg['reg_email'] == ($email)) { + + if ($reg['reg_startup'] <= $now && $reg['reg_expires'] >= $now) { + + if ($auto_create) { + $reonar['chan.name'] = $name; + $reonar['chan.did1'] = $nick; + } + + q("UPDATE register set reg_pass = '%s', reg_stuff = '%s' WHERE reg_id = '%s'", + dbesc($password), + dbesc(json_encode($reonar)), + intval($reg['reg_id']) + ); + + $msg = t('Invitation code succesfully applied'); + zar_log('ZAR0237I ' . $msg) . ', ' . $email; + // msg! + info($msg . EOL); + + + // the invitecode has verified us and we have all the info we need + // take the shortcut. + + $_SESSION['zar']['invite_in_progress'] = true; + + $mod = new Regate(); + $_REQUEST['form_security_token'] = get_form_security_token("regate"); + App::$argc = 2; + App::$argv[0] = 'regate'; + App::$argv[1] = bin2hex($reg['reg_did2']) . 'i'; + return $mod->post(); + + } else { + // msg! + notice(t('Invitation not in time or too late') . EOL); + return; + } + + } else { + // no match email adr + $msg = t('Invitation email failed'); + zar_log('ZAR0235S ' . $msg); + notice($msg . EOL); + return; + } + + } else { + // no match invitecode + $msg = t('Invitation code failed') ; + zar_log('ZAR0234S ' . $msg); + notice( $msg . EOL); + return; } + + } else { + notice(t('Invitations are not available') . EOL); + return; } + } - elseif($policy == REGISTER_APPROVE) { - $res = send_reg_approval_email($result); - if($res) { - info( t('Your registration is pending approval by the site owner.') . EOL ) ; + else { + if (!$invonly) { + $well = true; } else { - notice( t('Your registration can not be processed.') . EOL); + $msg = t('Registration on this hub is by invitation only') . EOL; + notice($msg); + zar_log('ZAR0233E ' . $msg); + return; } - goaway(z_root()); } - - if($email_verify) { - goaway(z_root() . '/email_validation/' . bin2hex($result['email'])); + + // check max daily registrations after we have dealt with the invitecode + if (self::check_reg_limits()['is']) { + notice('Max registrations per day exceeded.'); + return; } - // fall through and authenticate if no approvals or verifications were required. - - authenticate_success($result['account'],null,true,false,true); - - $new_channel = false; - $next_page = 'new_channel'; - - if(get_config('system','auto_channel_create')) { - $new_channel = auto_channel_create($result['account']['account_id']); - if($new_channel['success']) { - $channel_id = $new_channel['channel']['channel_id']; - change_channel($channel_id); - $next_page = '~'; + if ($well) { + + if($policy == REGISTER_OPEN || $policy == REGISTER_APPROVE ) { + + $cfgdelay = get_config('system', 'register_delay', '0i'); + $reg_delayed = calculate_adue( $cfgdelay ); + $regdelay = (($reg_delayed) ? datetime_convert(date_default_timezone_get(), 'UTC', $reg_delayed['due']) : $now); + + $cfgexpire = get_config('system', 'register_expire', '3d'); + $reg_expires = calculate_adue( $cfgexpire ); + $regexpire = (($reg_expires) ? datetime_convert(date_default_timezone_get(), 'UTC', $reg_expires['due']) : datetime_convert('UTC', 'UTC', 'now + 99 years')); + + // handle an email request that will be verified or an ivitation associated with an email address + if ($email > '' && $email_verify) { + // enforce in case of icdone + $flags |= ACCOUNT_UNVERIFIED; + $empin = $pass2 = random_string(24); + $did2 = $email; + $didx = 'e'; + + push_lang(($reg['lang']) ? $reg['lang'] : App::$language); + $reonar['from'] = get_config('system', 'from_email'); + $reonar['to'] = $email; + $reonar['subject'] = sprintf( t('Registration confirmation for %s'), get_config('system','sitename')); + $reonar['txttemplate']= replace_macros(get_intltext_template('register_verify_member.tpl'), + [ + '$sitename' => get_config('system','sitename'), + '$siteurl' => z_root(), + '$email' => $email, + '$timeframe' => [$regdelay, $regexpire], + '$mail' => bin2hex($email) . 'e', + '$ko' => bin2hex(substr($empin,0,4)), + '$hash' => $empin + ] + ); + pop_lang(); + zar_reg_mail($reonar); + + } else { + // that is an anonymous request without email or with email not to verify + $acpin = $pass2 = rand(100000,999999); + $did2 = rand(10,99); + $didx = 'a'; + // enforce delayed verify + $flags = ($flags | ACCOUNT_UNVERIFIED); + if ($email) { + $reonar['email.untrust'] = $email; + $reonar['email.comment'] = 'received, but no need for'; + } + } + + if ($auto_create) { + $reonar['chan.name'] = $name; + $reonar['chan.did1'] = $nick; + } + + if ($policy == REGISTER_APPROVE) { + $reonar['msg'] = $register_msg; + } + + $reg = q("INSERT INTO register (" + . "reg_flags,reg_didx,reg_did2,reg_hash,reg_created,reg_startup,reg_expires," + . "reg_email,reg_pass,reg_lang,reg_atip,reg_stuff)" + . " VALUES (%d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s') ", + intval($flags), + dbesc($didx), + dbesc($did2), + dbesc($pass2), + dbesc($now), + dbesc($regdelay), + dbesc($regexpire), + dbesc($email), + dbesc($password), + dbesc(App::$language), + dbesc($ip), + dbesc(json_encode($reonar)) + ); + + if ($didx == 'a') { + + $lid = q("SELECT reg_id FROM register WHERE reg_vital = 1 AND reg_did2 = '%s' AND reg_pass = '%s' ", + dbesc($did2), + dbesc($password) + ); + + if ($lid && count($lid) == 1 ) { + + $didnew = ( $lid[0]['reg_id'] . $did2 ) + . ( substr( base_convert( md5( $lid[0]['reg_id'] . $did2 ), 16, 10 ),-2 ) ); + + $reg = q("UPDATE register SET reg_did2 = CONCAT('d','%s') WHERE reg_id = %d ", + dbesc($didnew), intval($lid[0]['reg_id']) + ); + + zar_log( 'ZAR0239A ' . t('New register request') . ' d' . $didnew . ', ' + . $regdelay . ' - ' . $regexpire); + + if($reg_delayed) { + // this could be removed to make registration harder + $_SESSION['zar']['id'] = 'd' . $didnew; + $_SESSION['zar']['pin'] = $pass2; + $_SESSION['zar']['delayed'] = true; + $_SESSION['zar']['regdelay'] = datetime_convert('UTC', 'UTC', $regdelay, 'c'); + $_SESSION['zar']['regexpire'] = datetime_convert('UTC', 'UTC', $regexpire, 'c'); + } + else { + $_SESSION['zar']['pin'] = $pass2; + } + + goaway(z_root() . '/regate/' . bin2hex('d' . $didnew) . 'a' ); + } + else { + $msg = t('Error creating dId A'); + notice( $msg ); + zar_log( 'ZAR0239D,' . $msg . ' ' . $did2); + } + } + goaway(z_root() . '/regate/' . bin2hex($email) . $didx ); } - else - $new_channel = false; - } - - $x = get_config('system','workflow_register_next'); - if($x) { - $next_page = $x; - $_SESSION['workflow'] = true; } - - unset($_SESSION['login_return_url']); - goaway(z_root() . '/' . $next_page); - } - - - + + function get() { - + $registration_is = ''; $other_sites = ''; - + if(intval(get_config('system','register_policy')) === REGISTER_CLOSED) { if(intval(get_config('system','directory_mode')) === DIRECTORY_MODE_STANDALONE) { - notice( t('Registration on this hub is disabled.') . EOL); + notice(t('Registration on this hub is disabled.') . EOL); return; } - $mod = new Pubsites(); + $mod = new Pubsites(); return $mod->get(); } - + if(intval(get_config('system','register_policy')) == REGISTER_APPROVE) { $registration_is = t('Registration on this hub is by approval only.'); - $other_sites = t('<a href="pubsites">Register at another affiliated hub.</a>'); + $other_sites = '<a href="pubsites">' . t('Register at another affiliated hub in case when prefered') . '</a>'; } + $duty = zar_register_dutystate(); - $invitations = false; + if (!get_config('system', 'register_duty_jso')) { + // if not yet configured default to true + $duty = array( 'isduty' => true, 'atfrm' => '', 'nowfmt' => ''); + } + $invitations = false; if(intval(get_config('system','invitation_only'))) { $invitations = true; $registration_is = t('Registration on this hub is by invitation only.'); - $other_sites = t('<a href="pubsites">Register at another affiliated hub.</a>'); - } - - $max_dailies = intval(get_config('system','max_daily_registrations')); - if($max_dailies) { - $r = q("select count(account_id) as total from account where account_created > %s - INTERVAL %s", - db_utcnow(), db_quoteinterval('1 day') - ); - if($r && $r[0]['total'] >= $max_dailies) { - logger('max daily registrations exceeded.'); - notice( t('This site has exceeded the number of allowed daily account registrations. Please try again tomorrow.') . EOL); - return; - } + $other_sites = '<a href="pubsites">' . t('Register at another affiliated hub') . '</a>'; + } elseif (intval(get_config('system','invitation_also'))) { + $invitations = true; } - $privacy_role = ((x($_REQUEST,'permissions_role')) ? $_REQUEST['permissions_role'] : ""); - - $perm_roles = \Zotlabs\Access\PermissionRoles::roles(); + $opal = self::check_reg_limits(); + if ( $opal['is']) + $duty['atform'] = 'disabled'; // Configurable terms of service link - $tosurl = get_config('system','tos_url'); if(! $tosurl) $tosurl = z_root() . '/help/TermsOfService'; - + $toslink = '<a href="' . $tosurl . '" target="_blank">' . t('Terms of Service') . '</a>'; - + // Configurable whether to restrict age or not - default is based on international legal requirements // This can be relaxed if you are on a restricted server that does not share with public servers - + if(get_config('system','no_age_restriction')) { $label_tos = sprintf( t('I accept the %s for this website'), $toslink); } @@ -253,50 +487,96 @@ class Register extends Controller { } $enable_tos = 1 - intval(get_config('system','no_termsofservice')); - - $email = array('email', t('Your email address'), ((x($_REQUEST,'email')) ? strip_tags(trim($_REQUEST['email'])) : "")); - $password = array('password', t('Choose a password'), ''); - $password2 = array('password2', t('Please re-enter your password'), ''); + + $auto_create = get_config('system', 'auto_channel_create', 1); + $email_verify = get_config('system','verify_email'); + + $emailval = ((x($_REQUEST,'email')) ? strip_tags(trim($_REQUEST['email'])) : ""); + $email = ['email', + t('Your email address'), + $emailval, + (($email_verify) ? t('Required') : t('Optional')), + (($email_verify) ? '*' : ''), + $duty['atform'] + ]; + + $password = array('password', t('Choose a password'), '', '', '', $duty['atform']); + $password2 = array('password2', t('Please re-enter your password'), '', '', '', $duty['atform']); + $invite_code = array('invite_code', t('Please enter your invitation code'), ((x($_REQUEST,'invite_code')) ? strip_tags(trim($_REQUEST['invite_code'])) : "")); - $name = array('name', t('Your Name'), ((x($_REQUEST,'name')) ? $_REQUEST['name'] : ''), t('Real names are preferred.')); + + $name = array('name', t('Your name'), ((x($_REQUEST,'name')) ? $_REQUEST['name'] : ''), t('Real name is preferred'), '', '', $duty['atform']); $nickhub = '@' . str_replace(array('http://','https://','/'), '', get_config('system','baseurl')); - $nickname = array('nickname', t('Choose a short nickname'), ((x($_REQUEST,'nickname')) ? $_REQUEST['nickname'] : ''), sprintf( t('Your nickname will be used to create an easy to remember channel address e.g. nickname%s'), $nickhub)); - $role = array('permissions_role' , t('Channel role and privacy'), ($privacy_role) ? $privacy_role : 'social', t('Select a channel permission role for your usage needs and privacy requirements.') . ' <a href="help/member/member_guide#Channel_Permission_Roles" target="_blank">' . t('Read more about channel permission roles') . '</a>',$perm_roles); - $tos = array('tos', $label_tos, '', '', array(t('no'),t('yes'))); + $nickname = array('nickname', t('Choose a short nickname'), ((x($_REQUEST,'nickname')) ? $_REQUEST['nickname'] : ''), t('Your nickname will be used to create an easy to remember channel address'), '', '', $duty['atform']); + $tos = array('tos', $label_tos, ((x($_REQUEST,'tos')) ? $_REQUEST['tos'] : ''), '', [t('No'),t('Yes')], $duty['atform']); + + $register_msg = ['register_msg', t('Why do you want to join this hub?'), ((x($_REQUEST,'register_msg')) ? $_REQUEST['register_msg'] : ''), t('This will help to review your registration')]; - $auto_create = (get_config('system','auto_channel_create') ? true : false); - $default_role = get_config('system','default_permissions_role'); - $email_verify = get_config('system','verify_email'); - require_once('include/bbcode.php'); - - $o = replace_macros(get_markup_template('register.tpl'), array( + $o = replace_macros(get_markup_template('register.tpl'), array( '$form_security_token' => get_form_security_token("register"), '$title' => t('Registration'), '$reg_is' => $registration_is, + '$register_msg' => $register_msg, '$registertext' => bbcode(get_config('system','register_text')), '$other_sites' => $other_sites, + '$msg' => $opal['msg'], '$invitations' => $invitations, '$invite_code' => $invite_code, + '$haveivc' => t('I have an invite code'), + '$now' => $duty['nowfmt'], + '$atform' => $duty['atform'], '$auto_create' => $auto_create, '$name' => $name, - '$role' => $role, - '$default_role' => $default_role, '$nickname' => $nickname, '$enable_tos' => $enable_tos, '$tos' => $tos, '$email' => $email, + '$validate' => $validate, + '$validate_link'=> $validate_link, + '$validate_here'=> $validate_here, '$pass1' => $password, '$pass2' => $password2, '$submit' => t('Register'), - '$verify_note' => (($email_verify) ? t('This site requires email verification. After completing this form, please check your email for further instructions.') : ''), + '$nickhub' => $nickhub + )); - + return $o; - } - - + + function check_reg_limits() { + // check against register, account + $rear = array( 'is' => false, 'rn' => 0, 'an' => 0, 'msg' => '' ); + + $max_dailies = intval(get_config('system', 'max_daily_registrations', 50)); + + if ($max_dailies) { + + $r = q("SELECT COUNT(reg_id) AS nr FROM register WHERE reg_vital = 1 AND reg_created > %s - INTERVAL %s", + db_utcnow(), db_quoteinterval('1 day') + ); + + $rear['is'] = ( $r && $r[0]['nr'] >= $max_dailies ) ? true : false; + $rear['rn'] = $r[0]['nr']; + + if (!$rear['is']) { + $r = q("SELECT COUNT(account_id) AS nr FROM account WHERE account_created > %s - INTERVAL %s", + db_utcnow(), db_quoteinterval('1 day') + ); + + $rear['is'] = ( $r && ($r[0]['nr'] + $rear['rn']) >= $max_dailies ) ? true : false; + $rear['ra'] = $r[0]['nr']; + } + + if ( $rear['is']) { + $rear['msg'] = t('This site has exceeded the number of allowed daily account registrations.'); + zar_log('ZAR0333W ' . $rear['msg']); + $rear['is'] = true; + } + } + return $rear; + } } diff --git a/Zotlabs/Module/Regver.php b/Zotlabs/Module/Regver.php index 82b162f56..c45723063 100644 --- a/Zotlabs/Module/Regver.php +++ b/Zotlabs/Module/Regver.php @@ -6,8 +6,6 @@ class Regver extends \Zotlabs\Web\Controller { function get() { - global $lang; - $_SESSION['return_url'] = \App::$cmd; if(argc() != 3) diff --git a/Zotlabs/Module/Removeme.php b/Zotlabs/Module/Removeme.php index 876d61ca6..a0697675b 100644 --- a/Zotlabs/Module/Removeme.php +++ b/Zotlabs/Module/Removeme.php @@ -5,54 +5,54 @@ namespace Zotlabs\Module; class Removeme extends \Zotlabs\Web\Controller { function post() { - + if(! local_channel()) return; - + if($_SESSION['delegate']) return; - + if((! x($_POST,'qxz_password')) || (! strlen(trim($_POST['qxz_password'])))) return; - + if((! x($_POST,'verify')) || (! strlen(trim($_POST['verify'])))) return; - + if($_POST['verify'] !== $_SESSION['remove_account_verify']) return; - - + + $account = \App::get_account(); - - + + $x = account_verify_password($account['account_email'],$_POST['qxz_password']); if(! ($x && $x['account'])) return; - + if($account['account_password_changed'] > NULL_DATE) { $d1 = datetime_convert('UTC','UTC','now - 48 hours'); - if($account['account_password_changed'] > d1) { + if($account['account_password_changed'] > $d1) { notice( t('Channel removals are not allowed within 48 hours of changing the account password.') . EOL); return; } } - + $global_remove = 0; //intval($_POST['global']); channel_remove(local_channel(),1 - $global_remove,true); - + } - - + + function get() { - + if(! local_channel()) goaway(z_root()); - + $hash = random_string(); - + $_SESSION['remove_account_verify'] = $hash; - + $tpl = get_markup_template('removeme.tpl'); $o .= replace_macros($tpl, array( '$basedir' => z_root(), @@ -63,9 +63,9 @@ class Removeme extends \Zotlabs\Web\Controller { // '$global' => [ 'global', t('Remove this channel and all its clones from the network'), false, t('By default only the instance of the channel located on this hub will be removed from the network'), [ t('No'),t('Yes') ] ], '$submit' => t('Remove Channel') )); - - return $o; - + + return $o; + } - + } diff --git a/Zotlabs/Module/Rpost.php b/Zotlabs/Module/Rpost.php index f03dae2bf..013817597 100644 --- a/Zotlabs/Module/Rpost.php +++ b/Zotlabs/Module/Rpost.php @@ -1,16 +1,17 @@ <?php namespace Zotlabs\Module; /** @file */ +use Zotlabs\Lib\Libzot; + require_once('include/acl_selectors.php'); require_once('include/crypto.php'); require_once('include/items.php'); require_once('include/taxonomy.php'); require_once('include/conversation.php'); -require_once('include/zot.php'); /** * remote post - * + * * https://yoursite/rpost?f=&title=&body=&remote_return= * * This can be called via either GET or POST, use POST for long body content as suhosin often limits GET parameter length @@ -20,7 +21,7 @@ require_once('include/zot.php'); * body= Body of post * url= URL which will be parsed and the results appended to the body * source= Source application - * post_id= post_id of post to 'share' (local use only) + * post_id= post_id of post to 'share' (local use only) * remote_return= absolute URL to return after posting is finished * type= choices are 'html' or 'bbcode', default is 'bbcode' * @@ -32,17 +33,17 @@ require_once('include/zot.php'); class Rpost extends \Zotlabs\Web\Controller { function get() { - + $o = ''; - + if(! local_channel()) { if(remote_channel()) { // redirect to your own site. // We can only do this with a GET request so you'll need to keep the text short or risk getting truncated // by the wretched beast called 'suhosin'. All the browsers now allow long GET requests, but suhosin // blocks them. - - $url = get_rpost_path(\App::get_observer()); + + $url = Libzot::get_rpost_path(\App::get_observer()); // make sure we're not looping to our own hub if(($url) && (! stristr($url, \App::get_hostname()))) { foreach($_GET as $key => $arg) { @@ -53,10 +54,10 @@ class Rpost extends \Zotlabs\Web\Controller { goaway($url); } } - + // The login procedure is going to bugger our $_REQUEST variables // so save them in the session. - + if(array_key_exists('body',$_REQUEST)) { $_SESSION['rpost'] = $_REQUEST; } @@ -64,14 +65,81 @@ class Rpost extends \Zotlabs\Web\Controller { } nav_set_selected('Post'); - + + if (local_channel() && array_key_exists('userfile',$_FILES)) { + + $channel = App::get_channel(); + $observer = App::get_observer(); + + $def_album = get_pconfig($channel['channel_id'],'system','photo_path'); + $def_attach = get_pconfig($channel['channel_id'],'system','attach_path'); + + $r = attach_store($channel, (($observer) ? $observer['xchan_hash'] : ''), '', [ + 'source' => 'editor', + 'visible' => 0, + 'album' => $def_album, + 'directory' => $def_attach, + 'flags' => 1, // indicates temporary permissions are created + 'allow_cid' => '<' . $channel['channel_hash'] . '>' + ]); + + if (! $r['success']) { + notice( $r['message'] . EOL); + } + + $s = EMPTY_STR; + + if (intval($r['data']['is_photo'])) { + $s .= "\n\n" . $r['body'] . "\n\n"; + } + + $url = z_root() . '/cloud/' . $channel['channel_address'] . '/' . $r['data']['display_path']; + + if (strpos($r['data']['filetype'],'video') === 0) { + $s .= "\n\n" . '[zvideo]' . $url . '[/zvideo]' . "\n\n"; + } + + if (strpos($r['data']['filetype'],'audio') === 0) { + $s .= "\n\n" . '[zaudio]' . $url . '[/zaudio]' . "\n\n"; + } + + if ($r['data']['filetype'] === 'image/svg+xml') { + $x = @file_get_contents('store/' . $channel['channel_address'] . '/' . $r['data']['os_path']); + if ($x) { + $bb = svg2bb($x); + if ($bb) { + $s .= "\n\n" . $bb; + } + else { + logger('empty return from svgbb'); + } + } + else { + logger('unable to read svg data file: ' . 'store/' . $channel['channel_address'] . '/' . $r['data']['os_path']); + } + } + + if ($r['data']['filetype'] === 'text/calendar') { + $content = @file_get_contents('store/' . $channel['channel_address'] . '/' . $r['data']['os_path']); + if ($content) { + $ev = ical_to_ev($content); + if ($ev) { + $s .= "\n\n" . format_event_bbcode($ev[0]) . "\n\n"; + } + } + } + + $s .= "\n\n" . '[attachment]' . $r['data']['hash'] . ',' . $r['data']['revision'] . '[/attachment]' . "\n"; + $_REQUEST['body'] = ((array_key_exists('body',$_REQUEST)) ? $_REQUEST['body'] . $s : $s); + } + // If we have saved rpost session variables, but nothing in the current $_REQUEST, recover the saved variables - + if((! array_key_exists('body',$_REQUEST)) && (array_key_exists('rpost',$_SESSION))) { $_REQUEST = $_SESSION['rpost']; unset($_SESSION['rpost']); } - + if(array_key_exists('channel',$_REQUEST)) { $r = q("select channel_id from channel where channel_account_id = %d and channel_address = '%s' limit 1", intval(get_account_id()), @@ -82,7 +150,7 @@ class Rpost extends \Zotlabs\Web\Controller { $change = change_channel($r[0]['channel_id']); } } - + if($_REQUEST['remote_return']) { $_SESSION['remote_return'] = $_REQUEST['remote_return']; } @@ -91,21 +159,27 @@ class Rpost extends \Zotlabs\Web\Controller { goaway($_SESSION['remote_return']); goaway(z_root() . '/network'); } - + $plaintext = true; - + if(array_key_exists('type', $_REQUEST) && $_REQUEST['type'] === 'html') { require_once('include/html2bbcode.php'); - $_REQUEST['body'] = html2bbcode($_REQUEST['body']); + $_REQUEST['body'] = html2bbcode($_REQUEST['body']); } - + $channel = \App::get_channel(); - - - $acl = new \Zotlabs\Access\AccessList($channel); - - $channel_acl = $acl->get(); - + + if($_REQUEST['acl']) { + $acl = new \Zotlabs\Access\AccessList([]); + $acl->set($_REQUEST['acl']); + $channel_acl = $acl->get(); + } + else { + $acl = new \Zotlabs\Access\AccessList($channel); + $channel_acl = $acl->get(); + } + + if($_REQUEST['url']) { $x = z_fetch_url(z_root() . '/linkinfo?f=&url=' . urlencode($_REQUEST['url'])); if($x['success']) @@ -115,7 +189,7 @@ class Rpost extends \Zotlabs\Web\Controller { if($_REQUEST['post_id']) { $_REQUEST['body'] .= '[share=' . intval($_REQUEST['post_id']) . '][/share]'; } - + $x = array( 'is_owner' => true, 'allow_location' => ((intval(get_pconfig($channel['channel_id'],'system','use_browser_location'))) ? '1' : ''), @@ -137,19 +211,19 @@ class Rpost extends \Zotlabs\Web\Controller { 'bbcode' => true, 'jotnets' => true ); - + $editor = status_editor($a,$x,false,'Rpost'); - + $o .= replace_macros(get_markup_template('edpost_head.tpl'), array( '$title' => t('Edit post'), '$cancel' => '', '$editor' => $editor )); - + return $o; - + } - - - + + + } diff --git a/Zotlabs/Module/Search.php b/Zotlabs/Module/Search.php index c22bf2836..06a761998 100644 --- a/Zotlabs/Module/Search.php +++ b/Zotlabs/Module/Search.php @@ -1,85 +1,114 @@ <?php + namespace Zotlabs\Module; +use App; +use Zotlabs\Lib\Activity; +use Zotlabs\Lib\ActivityStreams; +use Zotlabs\Web\Controller; -class Search extends \Zotlabs\Web\Controller { +class Search extends Controller { function init() { - if(x($_REQUEST,'search')) - \App::$data['search'] = escape_tags($_REQUEST['search']); + if (x($_REQUEST, 'search')) + App::$data['search'] = escape_tags($_REQUEST['search']); } - - + function get($update = 0, $load = false) { - - if((get_config('system','block_public')) || (get_config('system','block_public_search'))) { - if ((! local_channel()) && (! remote_channel())) { - notice( t('Public access denied.') . EOL); + + if ((get_config('system', 'block_public')) || (get_config('system', 'block_public_search'))) { + if ((!local_channel()) && (!remote_channel())) { + notice(t('Public access denied.') . EOL); return; } } - + nav_set_selected('Search'); - - require_once("include/bbcode.php"); - require_once('include/security.php'); + + require_once('include/bbcode.php'); require_once('include/conversation.php'); require_once('include/items.php'); - + require_once('include/security.php'); + + $format = (($_REQUEST['format']) ? $_REQUEST['format'] : ''); - if($format !== '') { + if ($format !== '') { $update = $load = 1; } - - $observer = \App::get_observer(); + + $observer = App::get_observer(); $observer_hash = (($observer) ? $observer['xchan_hash'] : ''); - - $o = '<div id="live-search"></div>' . "\r\n"; - - $o .= '<div class="generic-content-wrapper-styled">' . "\r\n"; - - $o .= '<h3>' . t('Search') . '</h3>'; - - if(x(\App::$data,'search')) - $search = trim(\App::$data['search']); + + $o = '<div class="generic-content-wrapper-styled">' . "\r\n"; + + $o .= '<h2>' . t('Search') . '</h2>'; + + if (x(App::$data, 'search')) + $search = trim(App::$data['search']); else - $search = ((x($_GET,'search')) ? trim(escape_tags(rawurldecode($_GET['search']))) : ''); - + $search = ((x($_GET, 'search')) ? trim(escape_tags(rawurldecode($_GET['search']))) : ''); + $tag = false; - if(x($_GET,'tag')) { - $tag = true; - $search = ((x($_GET,'tag')) ? trim(escape_tags(rawurldecode($_GET['tag']))) : ''); + if (x($_GET, 'tag')) { + $tag = true; + $search = ((x($_GET, 'tag')) ? trim(escape_tags(rawurldecode($_GET['tag']))) : ''); } - $o .= search($search,'search-box','/search',((local_channel()) ? true : false)); - - if(strpos($search,'#') === 0) { - $tag = true; - $search = substr($search,1); + $o .= search($search, 'search-box', '/search', ((local_channel()) ? true : false)); + + if (local_channel() && strpos($search, 'https://') === 0 && !$update && !$load) { + $j = Activity::fetch(punify($search), App::get_channel()); + if ($j) { + $AS = new ActivityStreams($j); + if ($AS->is_valid()) { + // check if is_an_actor, otherwise import activity + if (is_array($AS->obj) && !ActivityStreams::is_an_actor($AS->obj)) { + $item = Activity::decode_note($AS); + if ($item) { + logger('parsed_item: ' . print_r($item, true), LOGGER_DATA); + Activity::store(App::get_channel(), $observer_hash, $AS, $item, true, true); + goaway(z_root() . '/display/' . gen_link_id($item['mid'])); + } + } + } + } + else { + // try other fetch providers (e.g. diaspora) + $hookdata = [ + 'channel' => App::get_channel(), + 'data' => $search + ]; + call_hooks('fetch_provider', $hookdata); + } + } + + if (strpos($search, '#') === 0) { + $tag = true; + $search = substr($search, 1); } - if(strpos($search,'@') === 0) { - $search = substr($search,1); + elseif(strpos($search, '@') === 0) { + $search = substr($search, 1); goaway(z_root() . '/directory' . '?f=1&navsearch=1&search=' . $search); } - if(strpos($search,'!') === 0) { - $search = substr($search,1); + elseif(strpos($search, '!') === 0) { + $search = substr($search, 1); goaway(z_root() . '/directory' . '?f=1&navsearch=1&search=' . $search); } - if(strpos($search,'?') === 0) { - $search = substr($search,1); + elseif(strpos($search, '?') === 0) { + $search = substr($search, 1); goaway(z_root() . '/help' . '?f=1&navsearch=1&search=' . $search); } - + // look for a naked webbie - if(strpos($search,'@') !== false) { + if (strpos($search, '@') !== false && strpos($search, 'http') !== 0) { goaway(z_root() . '/directory' . '?f=1&navsearch=1&search=' . $search); } - - if(! $search) + + if (!$search) return $o; - - if($tag) { - $wildtag = str_replace('*','%',$search); + + if ($tag) { + $wildtag = str_replace('*', '%', $search); $sql_extra = sprintf(" AND item.id IN (select oid from term where otype = %d and ttype in ( %d , %d) and term like '%s') ", intval(TERM_OBJ_POST), intval(TERM_HASHTAG), @@ -88,82 +117,82 @@ class Search extends \Zotlabs\Web\Controller { ); } else { - $regstr = db_getfunc('REGEXP'); + $regstr = db_getfunc('REGEXP'); $sql_extra = sprintf(" AND (item.title $regstr '%s' OR item.body $regstr '%s') ", dbesc(protect_sprintf(preg_quote($search))), dbesc(protect_sprintf(preg_quote($search)))); } - + // Here is the way permissions work in the search module... // Only public posts can be shown // OR your own posts if you are a logged in member - // No items will be shown if the member has a blocked profile wall. - + // No items will be shown if the member has a blocked profile wall. + + + if ((!$update) && (!$load)) { - if((! $update) && (! $load)) { - // This is ugly, but we can't pass the profile_uid through the session to the ajax updater, // because browser prefetching might change it on us. We have to deliver it with the page. - + $o .= '<div id="live-search"></div>' . "\r\n"; $o .= "<script> var profile_uid = " . ((intval(local_channel())) ? local_channel() : (-1)) - . "; var netargs = '?f='; var profile_page = " . \App::$pager['page'] . "; </script>\r\n"; - - \App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"),array( + . "; var netargs = '?f='; var profile_page = " . App::$pager['page'] . "; </script>\r\n"; + + App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"), [ '$baseurl' => z_root(), - '$pgtype' => 'search', - '$uid' => ((\App::$profile['profile_uid']) ? \App::$profile['profile_uid'] : '0'), - '$gid' => '0', - '$cid' => '0', - '$cmin' => '(-1)', - '$cmax' => '(-1)', - '$star' => '0', - '$liked' => '0', - '$conv' => '0', - '$spam' => '0', - '$fh' => '0', - '$dm' => '0', + '$pgtype' => 'search', + '$uid' => ((App::$profile['profile_uid']) ? App::$profile['profile_uid'] : '0'), + '$gid' => '0', + '$cid' => '0', + '$cmin' => '(-1)', + '$cmax' => '(-1)', + '$star' => '0', + '$liked' => '0', + '$conv' => '0', + '$spam' => '0', + '$fh' => '0', + '$dm' => '0', '$nouveau' => '0', - '$wall' => '0', - '$list' => ((x($_REQUEST,'list')) ? intval($_REQUEST['list']) : 0), - '$page' => ((\App::$pager['page'] != 1) ? \App::$pager['page'] : 1), - '$search' => (($tag) ? urlencode('#') : '') . $search, - '$xchan' => '', - '$order' => '', - '$file' => '', - '$cats' => '', - '$tags' => '', - '$mid' => '', - '$verb' => '', - '$net' => '', - '$dend' => '', - '$dbegin' => '' - )); - - - } - + '$wall' => '0', + '$list' => ((x($_REQUEST, 'list')) ? intval($_REQUEST['list']) : 0), + '$page' => ((App::$pager['page'] != 1) ? App::$pager['page'] : 1), + '$search' => (($tag) ? urlencode('#') : '') . $search, + '$xchan' => '', + '$order' => '', + '$file' => '', + '$cats' => '', + '$tags' => '', + '$mid' => '', + '$verb' => '', + '$net' => '', + '$dend' => '', + '$dbegin' => '' + ]); + + + } + $item_normal = item_normal_search(); - $pub_sql = public_permissions_sql($observer_hash); - + $pub_sql = public_permissions_sql($observer_hash); + require_once('include/channel.php'); - + $sys = get_sys_channel(); - - if(($update) && ($load)) { - $itemspage = get_pconfig(local_channel(),'system','itemspage'); - \App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 10)); - $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(\App::$pager['itemspage']), intval(\App::$pager['start'])); - + + if (($update) && ($load)) { + $itemspage = get_pconfig(local_channel(), 'system', 'itemspage'); + App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 10)); + $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(App::$pager['itemspage']), intval(App::$pager['start'])); + // in case somebody turned off public access to sys channel content with permissions - - if(! perm_is_allowed($sys['channel_id'],$observer_hash,'view_stream')) + + if (!perm_is_allowed($sys['channel_id'], $observer_hash, 'view_stream')) $sys['xchan_hash'] .= 'disabled'; - - if($load) { + + if ($load) { $r = null; - - if(local_channel()) { + + if (local_channel()) { $r = q("SELECT mid, MAX(id) as item_id from item - WHERE ((( item.allow_cid = '' AND item.allow_gid = '' AND item.deny_cid = '' AND item.deny_gid = '' AND item_private = 0 ) + WHERE ((( item.allow_cid = '' AND item.allow_gid = '' AND item.deny_cid = '' AND item.deny_gid = '' AND item_private = 0 ) OR ( item.uid = %d )) OR item.owner_xchan = '%s' ) $item_normal $sql_extra @@ -172,63 +201,61 @@ class Search extends \Zotlabs\Web\Controller { dbesc($sys['xchan_hash']) ); } - if($r === null) { + if ($r === null) { $r = q("SELECT mid, MAX(id) as item_id from item WHERE (((( item.allow_cid = '' AND item.allow_gid = '' AND item.deny_cid = '' AND item.deny_gid = '' AND item_private = 0 ) - and owner_xchan in ( " . stream_perms_xchans(($observer) ? (PERMS_NETWORK|PERMS_PUBLIC) : PERMS_PUBLIC) . " )) + and owner_xchan in ( " . stream_perms_xchans(($observer) ? (PERMS_NETWORK | PERMS_PUBLIC) : PERMS_PUBLIC) . " )) $pub_sql ) OR owner_xchan = '%s') $item_normal - $sql_extra + $sql_extra group by mid, created order by created desc $pager_sql", dbesc($sys['xchan_hash']) ); } - if($r) { - $str = ids_to_querystr($r,'item_id'); - $r = q("select *, id as item_id from item where id in ( " . $str . ") order by created desc "); + if ($r) { + $str = ids_to_querystr($r, 'item_id'); + $r = dbq("select *, id as item_id from item where id in ( " . $str . ") order by created desc"); } } else { - $r = array(); + $r = []; } - - } - - if($r) { + + if ($r) { xchan_query($r); - $items = fetch_post_tags($r,true); - } else { - $items = array(); - } - - - if($format == 'json') { - $result = array(); + $items = fetch_post_tags($r, true); + } + else { + $items = []; + } + + if ($format === 'json') { + $result = []; require_once('include/conversation.php'); - foreach($items as $item) { + foreach ($items as $item) { $item['html'] = zidify_links(bbcode($item['body'])); - $x = encode_item($item); - $x['html'] = prepare_text($item['body'],$item['mimetype']); - $result[] = $x; + $x = encode_item($item); + $x['html'] = prepare_text($item['body'], $item['mimetype']); + $result[] = $x; } - json_return_and_die(array('success' => true,'messages' => $result)); + json_return_and_die(['success' => true, 'messages' => $result]); } - - if($tag) - $o .= '<h2>' . sprintf( t('Items tagged with: %s'),$search) . '</h2>'; + + if ($tag) + $o .= '<h2>' . sprintf(t('Items tagged with: %s'), $search) . '</h2>'; else - $o .= '<h2>' . sprintf( t('Search results for: %s'),$search) . '</h2>'; - - $o .= conversation($items,'search',$update,'client'); - + $o .= '<h2>' . sprintf(t('Search results for: %s'), $search) . '</h2>'; + + $o .= conversation($items, 'search', $update, 'client'); + $o .= '</div>'; - + return $o; } - - + + } diff --git a/Zotlabs/Module/Settings.php b/Zotlabs/Module/Settings.php index 79031c98f..624cbb0c1 100644 --- a/Zotlabs/Module/Settings.php +++ b/Zotlabs/Module/Settings.php @@ -1,7 +1,6 @@ <?php namespace Zotlabs\Module; /** @file */ -require_once('include/zot.php'); require_once('include/security.php'); class Settings extends \Zotlabs\Web\Controller { @@ -11,68 +10,68 @@ class Settings extends \Zotlabs\Web\Controller { function init() { if(! local_channel()) return; - + if($_SESSION['delegate']) return; - + \App::$profile_uid = local_channel(); - + // default is channel settings in the absence of other arguments - + if(argc() == 1) { // We are setting these values - don't use the argc(), argv() functions here \App::$argc = 2; \App::$argv[] = 'channel'; - } + } $this->sm = new \Zotlabs\Web\SubModule(); } - - + + function post() { - + if(! local_channel()) return; - + if($_SESSION['delegate']) return; - + // logger('mod_settings: ' . print_r($_REQUEST,true)); - + if(argc() > 1) { if($this->sm->call('post') !== false) { return; } } - + goaway(z_root() . '/settings' ); return; // NOTREACHED } - - - + + + function get() { - + nav_set_selected('Settings'); - + if((! local_channel()) || ($_SESSION['delegate'])) { notice( t('Permission denied.') . EOL ); return login(); } - - + + $channel = \App::get_channel(); if($channel) head_set_icon($channel['xchan_photo_s']); - + $o = $this->sm->call('get'); if($o !== false) return $o; $o = ''; - - } + + } } diff --git a/Zotlabs/Module/Settings/Account.php b/Zotlabs/Module/Settings/Account.php index b40f516ca..97cc9389a 100644 --- a/Zotlabs/Module/Settings/Account.php +++ b/Zotlabs/Module/Settings/Account.php @@ -15,20 +15,23 @@ class Account { $account = \App::get_account(); if($email != $account['account_email']) { - if(! validate_email($email)) - $errs[] = t('Not valid email.'); - $adm = trim(get_config('system','admin_email')); - if(($adm) && (strcasecmp($email,$adm) == 0)) { - $errs[] = t('Protected email address. Cannot change to that email.'); - $email = \App::$account['account_email']; - } - if(! $errs) { - $r = q("update account set account_email = '%s' where account_id = %d", - dbesc($email), - intval($account['account_id']) - ); - if(! $r) - $errs[] = t('System failure storing new email. Please try again.'); + // a DId2 not an email addr does not allow to change to email addr + if (strpos($email, '@') > 0) { + if(! validate_email($email)) + $errs[] = t('Not valid email.'); + $adm = trim(get_config('system','admin_email')); + if(($adm) && (strcasecmp($email,$adm) == 0)) { + $errs[] = t('Protected email address. Cannot change to that email.'); + $email = \App::$account['account_email']; + } + if(! $errs) { + $r = q("update account set account_email = '%s' where account_id = %d", + dbesc($email), + intval($account['account_id']) + ); + if(! $r) + $errs[] = t('System failure storing new email. Please try again.'); + } } } @@ -92,6 +95,7 @@ class Account { call_hooks('account_settings', $account_settings); $email = \App::$account['account_email']; + $attremail = (!strpos($email, '@')) ? 'disabled="disabled"' : ''; $tpl = get_markup_template("settings_account.tpl"); $o .= replace_macros($tpl, array( @@ -101,7 +105,7 @@ class Account { '$password1'=> array('npassword', t('Enter New Password'), '', ''), '$password2'=> array('confirm', t('Confirm New Password'), '', t('Leave password fields blank unless changing')), '$submit' => t('Submit'), - '$email' => array('email', t('Email Address:'), $email, ''), + '$email' => array('email', t('DId2 or Email Address:'), $email, '', '', $attremail), '$removeme' => t('Remove Account'), '$removeaccount' => t('Remove this account including all its channels'), '$account_settings' => $account_settings diff --git a/Zotlabs/Module/Settings/Channel.php b/Zotlabs/Module/Settings/Channel.php index 2eed1efc9..e95752338 100644 --- a/Zotlabs/Module/Settings/Channel.php +++ b/Zotlabs/Module/Settings/Channel.php @@ -16,11 +16,11 @@ class Channel { $channel = \App::get_channel(); check_form_security_token_redirectOnErr('/settings', 'settings'); - + call_hooks('settings_post', $_POST); - + $set_perms = ''; - + $role = ((x($_POST,'permissions_role')) ? notags(trim($_POST['permissions_role'])) : ''); $oldrole = get_pconfig(local_channel(),'system','permissions_role'); @@ -28,9 +28,9 @@ class Channel { if($oldrole === 'social_party') { $oldrole = 'social_federation'; } - + if(($role != $oldrole) || ($role === 'custom')) { - + if($role === 'custom') { $hide_presence = (((x($_POST,'hide_presence')) && (intval($_POST['hide_presence']) == 1)) ? 1: 0); $publish = (((x($_POST,'profile_in_directory')) && (intval($_POST['profile_in_directory']) == 1)) ? 1: 0); @@ -38,18 +38,18 @@ class Channel { $r = q("update channel set channel_default_group = '%s' where channel_id = %d", dbesc($def_group), intval(local_channel()) - ); - + ); + $global_perms = \Zotlabs\Access\Permissions::Perms(); - + foreach($global_perms as $k => $v) { \Zotlabs\Access\PermissionLimits::Set(local_channel(),$k,intval($_POST[$k])); } $acl = new \Zotlabs\Access\AccessList($channel); $acl->set_from_array($_POST); $x = $acl->get(); - - $r = q("update channel set channel_allow_cid = '%s', channel_allow_gid = '%s', + + $r = q("update channel set channel_allow_cid = '%s', channel_allow_gid = '%s', channel_deny_cid = '%s', channel_deny_gid = '%s' where channel_id = %d", dbesc($x['allow_cid']), dbesc($x['allow_gid']), @@ -93,13 +93,13 @@ class Channel { } // no default collection else { - q("update channel set channel_default_group = '', channel_allow_gid = '', channel_allow_cid = '', channel_deny_gid = '', + q("update channel set channel_default_group = '', channel_allow_gid = '', channel_allow_cid = '', channel_deny_gid = '', channel_deny_cid = '' where channel_id = %d", intval(local_channel()) ); } - if($role_permissions['perms_connect']) { + if($role_permissions['perms_connect']) { $x = \Zotlabs\Access\Permissions::FilledPerms($role_permissions['perms_connect']); foreach($x as $k => $v) { set_abconfig(local_channel(),$channel['channel_hash'],'my_perms',$k, $v); @@ -110,7 +110,7 @@ class Channel { del_pconfig(local_channel(),'autoperms',$k); } } - } + } if($role_permissions['limits']) { foreach($role_permissions['limits'] as $k => $v) { @@ -121,11 +121,11 @@ class Channel { $publish = intval($role_permissions['directory_publish']); } } - + set_pconfig(local_channel(),'system','hide_online_status',$hide_presence); set_pconfig(local_channel(),'system','permissions_role',$role); } - + $username = ((x($_POST,'username')) ? notags(trim($_POST['username'])) : ''); $timezone = ((x($_POST,'timezone_select')) ? notags(trim($_POST['timezone_select'])) : ''); $defloc = ((x($_POST,'defloc')) ? notags(trim($_POST['defloc'])) : ''); @@ -135,36 +135,36 @@ class Channel { $evdays = ((x($_POST,'evdays')) ? intval($_POST['evdays']) : 3); $photo_path = ((x($_POST,'photo_path')) ? escape_tags(trim($_POST['photo_path'])) : ''); $attach_path = ((x($_POST,'attach_path')) ? escape_tags(trim($_POST['attach_path'])) : ''); - + $expire_items = ((x($_POST,'expire_items')) ? intval($_POST['expire_items']) : 0); $expire_starred = ((x($_POST,'expire_starred')) ? intval($_POST['expire_starred']) : 0); $expire_photos = ((x($_POST,'expire_photos'))? intval($_POST['expire_photos']) : 0); $expire_network_only = ((x($_POST,'expire_network_only'))? intval($_POST['expire_network_only']) : 0); - + $allow_location = (((x($_POST,'allow_location')) && (intval($_POST['allow_location']) == 1)) ? 1: 0); - + $blocktags = (((x($_POST,'blocktags')) && (intval($_POST['blocktags']) == 1)) ? 0: 1); // this setting is inverted! $unkmail = (((x($_POST,'unkmail')) && (intval($_POST['unkmail']) == 1)) ? 1: 0); $cntunkmail = ((x($_POST,'cntunkmail')) ? intval($_POST['cntunkmail']) : 0); - $suggestme = ((x($_POST,'suggestme')) ? intval($_POST['suggestme']) : 0); - $autoperms = ((x($_POST,'autoperms')) ? intval($_POST['autoperms']) : 0); - + $suggestme = ((x($_POST,'suggestme')) ? intval($_POST['suggestme']) : 0); + $autoperms = ((x($_POST,'autoperms')) ? intval($_POST['autoperms']) : 0); + $post_newfriend = (($_POST['post_newfriend'] == 1) ? 1: 0); $post_joingroup = (($_POST['post_joingroup'] == 1) ? 1: 0); $post_profilechange = (($_POST['post_profilechange'] == 1) ? 1: 0); $adult = (($_POST['adult'] == 1) ? 1 : 0); $defpermcat = ((x($_POST,'defpermcat')) ? notags(trim($_POST['defpermcat'])) : 'default'); - + $mailhost = ((array_key_exists('mailhost',$_POST)) ? notags(trim($_POST['mailhost'])) : ''); - + $pageflags = $channel['channel_pageflags']; $existing_adult = (($pageflags & PAGE_ADULT) ? 1 : 0); if($adult != $existing_adult) $pageflags = ($pageflags ^ PAGE_ADULT); - - + + $notify = 0; - + if(x($_POST,'notify1')) $notify += intval($_POST['notify1']); if(x($_POST,'notify2')) @@ -181,10 +181,10 @@ class Channel { $notify += intval($_POST['notify7']); if(x($_POST,'notify8')) $notify += intval($_POST['notify8']); - - + + $vnotify = 0; - + if(x($_POST,'vnotify1')) $vnotify += intval($_POST['vnotify1']); if(x($_POST,'vnotify2')) @@ -215,13 +215,14 @@ class Channel { $vnotify += intval($_POST['vnotify14']); if(x($_POST,'vnotify15')) $vnotify += intval($_POST['vnotify15']); - - $always_show_in_notices = x($_POST,'always_show_in_notices') ? 1 : 0; - + + $always_show_in_notices = x($_POST, 'always_show_in_notices') ? 1 : 0; + $update_notices_per_parent = x($_POST, 'update_notices_per_parent') ? 1 : 0; + $err = ''; - + $name_change = false; - + if($username != $channel['channel_name']) { $name_change = true; require_once('include/channel.php'); @@ -231,12 +232,12 @@ class Channel { return; } } - + if($timezone != $channel['channel_timezone']) { if(strlen($timezone)) date_default_timezone_set($timezone); } - + set_pconfig(local_channel(),'system','use_browser_location',$allow_location); set_pconfig(local_channel(),'system','suggestme', $suggestme); set_pconfig(local_channel(),'system','post_newfriend', $post_newfriend); @@ -245,13 +246,14 @@ class Channel { set_pconfig(local_channel(),'system','blocktags',$blocktags); set_pconfig(local_channel(),'system','vnotify',$vnotify); set_pconfig(local_channel(),'system','always_show_in_notices',$always_show_in_notices); + set_pconfig(local_channel(),'system','update_notices_per_parent',$update_notices_per_parent); set_pconfig(local_channel(),'system','evdays',$evdays); set_pconfig(local_channel(),'system','photo_path',$photo_path); set_pconfig(local_channel(),'system','attach_path',$attach_path); set_pconfig(local_channel(),'system','default_permcat',$defpermcat); set_pconfig(local_channel(),'system','email_notify_host',$mailhost); set_pconfig(local_channel(),'system','autoperms',$autoperms); - + $r = q("update channel set channel_name = '%s', channel_pageflags = %d, channel_timezone = '%s', channel_location = '%s', channel_notifyflags = %d, channel_max_anon_mail = %d, channel_max_friend_req = %d, channel_expire_days = %d $set_perms where channel_id = %d", dbesc($username), intval($pageflags), @@ -262,17 +264,17 @@ class Channel { intval($maxreq), intval($expire), intval(local_channel()) - ); + ); if($r) info( t('Settings updated.') . EOL); - + if(! is_null($publish)) { $r = q("UPDATE profile SET publish = %d WHERE is_default = 1 AND uid = %d", intval($publish), intval(local_channel()) ); } - + if($name_change) { // change name on all associated xchans by matching the url $r = q("update xchan set xchan_name = '%s', xchan_name_date = '%s' where xchan_url = '%s'", @@ -285,49 +287,49 @@ class Channel { intval($channel['channel_id']) ); } - + \Zotlabs\Daemon\Master::Summon(array('Directory',local_channel())); - + Libsync::build_sync_packet(); - - + + if($email_changed && \App::$config['system']['register_policy'] == REGISTER_VERIFY) { - + // FIXME - set to un-verified, blocked and redirect to logout // Q: Why? Are we verifying people or email addresses? // A: the policy is to verify email addresses } - + goaway(z_root() . '/settings' ); return; // NOTREACHED } - + function get() { - + require_once('include/acl_selectors.php'); require_once('include/permissions.php'); $yes_no = array(t('No'),t('Yes')); - - + + $p = q("SELECT * FROM profile WHERE is_default = 1 AND uid = %d LIMIT 1", intval(local_channel()) ); if(count($p)) $profile = $p[0]; - + load_pconfig(local_channel(),'expire'); - + $channel = \App::get_channel(); - + $global_perms = \Zotlabs\Access\Permissions::Perms(); $permiss = array(); - + $perm_opts = array( array( t('Nobody except yourself'), 0), - array( t('Only those you specifically allow'), PERMS_SPECIFIC), + array( t('Only those you specifically allow'), PERMS_SPECIFIC), array( t('Approved connections'), PERMS_CONTACTS), array( t('Any connections'), PERMS_PENDING), array( t('Anybody on this website'), PERMS_SITE), @@ -335,10 +337,10 @@ class Channel { array( t('Anybody authenticated'), PERMS_AUTHED), array( t('Anybody on the internet'), PERMS_PUBLIC) ); - + $limits = \Zotlabs\Access\PermissionLimits::Get(local_channel()); $anon_comments = get_config('system','anonymous_comments',true); - + foreach($global_perms as $k => $perm) { $options = array(); $can_be_public = ((strstr($k,'view') || ($k === 'post_comments' && $anon_comments)) ? true : false); @@ -347,61 +349,61 @@ class Channel { continue; $options[$opt[1]] = $opt[0]; } - $permiss[] = array($k,$perm,$limits[$k],'',$options); + $permiss[] = array($k,$perm,$limits[$k],'',$options); } - + // logger('permiss: ' . print_r($permiss,true)); - + $username = $channel['channel_name']; $nickname = $channel['channel_address']; $timezone = $channel['channel_timezone']; $notify = $channel['channel_notifyflags']; $defloc = $channel['channel_location']; - + $maxreq = $channel['channel_max_friend_req']; $expire = $channel['channel_expire_days']; $adult_flag = intval($channel['channel_pageflags'] & PAGE_ADULT); $sys_expire = get_config('system','default_expire_days'); - + // $unkmail = \App::$user['unkmail']; // $cntunkmail = \App::$user['cntunkmail']; - + $hide_presence = intval(get_pconfig(local_channel(), 'system','hide_online_status')); - - + + $expire_items = get_pconfig(local_channel(), 'expire','items'); $expire_items = (($expire_items===false)? '1' : $expire_items); // default if not set: 1 - + $expire_notes = get_pconfig(local_channel(), 'expire','notes'); $expire_notes = (($expire_notes===false)? '1' : $expire_notes); // default if not set: 1 - + $expire_starred = get_pconfig(local_channel(), 'expire','starred'); $expire_starred = (($expire_starred===false)? '1' : $expire_starred); // default if not set: 1 - + $expire_photos = get_pconfig(local_channel(), 'expire','photos'); $expire_photos = (($expire_photos===false)? '0' : $expire_photos); // default if not set: 0 - + $expire_network_only = get_pconfig(local_channel(), 'expire','network_only'); $expire_network_only = (($expire_network_only===false)? '0' : $expire_network_only); // default if not set: 0 - - + + $suggestme = get_pconfig(local_channel(), 'system','suggestme'); $suggestme = (($suggestme===false)? '0': $suggestme); // default if not set: 0 - + $post_newfriend = get_pconfig(local_channel(), 'system','post_newfriend'); $post_newfriend = (($post_newfriend===false)? '0': $post_newfriend); // default if not set: 0 - + $post_joingroup = get_pconfig(local_channel(), 'system','post_joingroup'); $post_joingroup = (($post_joingroup===false)? '0': $post_joingroup); // default if not set: 0 - + $post_profilechange = get_pconfig(local_channel(), 'system','post_profilechange'); $post_profilechange = (($post_profilechange===false)? '0': $post_profilechange); // default if not set: 0 - + $blocktags = get_pconfig(local_channel(),'system','blocktags'); $blocktags = (($blocktags===false) ? '0' : $blocktags); - + $timezone = date_default_timezone_get(); - + $opt_tpl = get_markup_template("field_checkbox.tpl"); if(get_config('system','publish_all')) { $profile_in_dir = '<input type="hidden" name="profile_in_directory" value="1" />'; @@ -411,20 +413,20 @@ class Channel { '$field' => array('profile_in_directory', t('Publish your default profile in the network directory'), $profile['publish'], '', $yes_no), )); } - + $suggestme = replace_macros($opt_tpl,array( '$field' => array('suggestme', t('Allow us to suggest you as a potential friend to new members?'), $suggestme, '', $yes_no), - + )); - + $subdir = ((strlen(\App::get_path())) ? '<br />' . t('or') . ' ' . z_root() . '/channel/' . $nickname : ''); $webbie = $nickname . '@' . \App::get_hostname(); $intl_nickname = unpunify($nickname) . '@' . unpunify(\App::get_hostname()); - + $tpl_addr = get_markup_template("settings_nick_set.tpl"); - + $prof_addr = replace_macros($tpl_addr,array( '$desc' => t('Your channel address is'), '$nickname' => (($intl_nickname === $webbie) ? $webbie : $intl_nickname . ' (' . $webbie . ')'), @@ -447,27 +449,27 @@ class Channel { $default_permcat = get_pconfig(local_channel(),'system','default_permcat','default'); - + $stpl = get_markup_template('settings.tpl'); - + $acl = new \Zotlabs\Access\AccessList($channel); $perm_defaults = $acl->get(); - + require_once('include/group.php'); $group_select = mini_group_select(local_channel(),$channel['channel_default_group']); - + $evdays = get_pconfig(local_channel(),'system','evdays'); if(! $evdays) $evdays = 3; - + $permissions_role = get_pconfig(local_channel(),'system','permissions_role'); if(! $permissions_role) $permissions_role = 'custom'; // compatibility mapping - can be removed after 3.4 release - if($permissions_role === 'social_party') + if($permissions_role === 'social_party') $permissions_role = 'social_federation'; - if(in_array($permissions_role,['forum','repository'])) + if(in_array($permissions_role,['forum','repository'])) $autoperms = replace_macros(get_markup_template('field_checkbox.tpl'), [ '$field' => [ 'autoperms',t('Automatic membership approval'), ((get_pconfig(local_channel(),'system','autoperms')) ? 1 : 0), t('If enabled, connection requests will be approved without your interaction'), $yes_no ]]); else @@ -477,8 +479,10 @@ class Channel { $perm_roles = \Zotlabs\Access\PermissionRoles::roles(); - $vnotify = get_pconfig(local_channel(),'system','vnotify'); $always_show_in_notices = get_pconfig(local_channel(),'system','always_show_in_notices'); + $update_notices_per_parent = get_pconfig(local_channel(), 'system', 'update_notices_per_parent', 1); + $vnotify = get_pconfig(local_channel(),'system','vnotify'); + if($vnotify === false) $vnotify = (-1); @@ -491,7 +495,7 @@ class Channel { $o .= replace_macros($stpl,array( '$ptitle' => t('Channel Settings'), - + '$submit' => t('Submit'), '$baseurl' => z_root(), '$uid' => local_channel(), @@ -503,15 +507,15 @@ class Channel { '$timezone' => array('timezone_select' , t('Your Timezone:'), $timezone, '', get_timezones()), '$defloc' => array('defloc', t('Default Post Location:'), $defloc, t('Geographical location to display on your posts')), '$allowloc' => array('allow_location', t('Use Browser Location:'), ((get_pconfig(local_channel(),'system','use_browser_location')) ? 1 : ''), '', $yes_no), - + '$adult' => array('adult', t('Adult Content'), $adult_flag, t('This channel frequently or regularly publishes adult content. (Please tag any adult material and/or nudity with #NSFW)'), $yes_no), - + '$h_prv' => t('Security and Privacy Settings'), '$permissions_set' => $permissions_set, '$perms_set_msg' => t('Your permissions are already configured. Click to view/adjust'), - + '$hide_presence' => array('hide_presence', t('Hide my online presence'),$hide_presence, t('Prevents displaying in your profile that you are online'), $yes_no), - + '$lbl_pmacro' => t('Simple Privacy Settings:'), '$pmacro3' => t('Very Public - <em>extremely permissive (should be used with caution)</em>'), '$pmacro2' => t('Typical - <em>default public, privacy when desired (similar to social network permissions but with improved privacy)</em>'), @@ -519,9 +523,9 @@ class Channel { '$pmacro0' => t('Blocked - <em>default blocked to/from everybody</em>'), '$permiss_arr' => $permiss, '$blocktags' => array('blocktags',t('Allow others to tag your posts'), 1-$blocktags, t('Often used by the community to retro-actively flag inappropriate content'), $yes_no), - + '$lbl_p2macro' => t('Channel Permission Limits'), - + '$expire' => array('expire',t('Expire other channel content after this many days'),$expire, t('0 or blank to use the website limit.') . ' ' . ((intval($sys_expire)) ? sprintf( t('This website expires after %d days.'),intval($sys_expire)) : t('This website does not expire imported content.')) . ' ' . t('The website limit takes precedence if lower than your limit.')), '$maxreq' => array('maxreq', t('Maximum Friend Requests/Day:'), intval($channel['channel_max_friend_req']) , t('May reduce spam activity')), '$permissions' => t('Default Privacy Group'), @@ -540,10 +544,10 @@ class Channel { '$profile_in_dir' => $profile_in_dir, '$hide_friends' => $hide_friends, '$hide_wall' => $hide_wall, - '$unkmail' => $unkmail, + '$unkmail' => $unkmail, '$cntunkmail' => array('cntunkmail', t('Maximum private messages per day from unknown people:'), intval($channel['channel_max_anon_mail']) ,t("Useful to reduce spamming")), - - '$autoperms' => $autoperms, + + '$autoperms' => $autoperms, '$h_not' => t('Notification Settings'), '$activity_options' => t('By default post a status message when:'), '$post_newfriend' => array('post_newfriend', t('accepting a friend request'), $post_newfriend, '', $yes_no), @@ -558,12 +562,12 @@ class Channel { '$notify6' => array('notify6', t('You receive a friend suggestion'), ($notify & NOTIFY_SUGGEST), NOTIFY_SUGGEST, '', $yes_no), '$notify7' => array('notify7', t('You are tagged in a post'), ($notify & NOTIFY_TAGSELF), NOTIFY_TAGSELF, '', $yes_no), '$notify8' => array('notify8', t('You are poked/prodded/etc. in a post'), ($notify & NOTIFY_POKE), NOTIFY_POKE, '', $yes_no), - + '$notify9' => array('notify9', t('Someone likes your post/comment'), ($notify & NOTIFY_LIKE), NOTIFY_LIKE, '', $yes_no), - - + + '$lbl_vnot' => t('Show visual notifications including:'), - + '$vnotify1' => array('vnotify1', t('Unseen stream activity'), ($vnotify & VNOTIFY_NETWORK), VNOTIFY_NETWORK, '', $yes_no), '$vnotify2' => array('vnotify2', t('Unseen channel activity'), ($vnotify & VNOTIFY_CHANNEL), VNOTIFY_CHANNEL, '', $yes_no), '$vnotify3' => array('vnotify3', t('Unseen private messages'), ($vnotify & VNOTIFY_MAIL), VNOTIFY_MAIL, t('Recommended'), $yes_no), @@ -581,12 +585,14 @@ class Channel { '$vnotify15' => array('vnotify15', t('Unseen forum posts'), ($vnotify & VNOTIFY_FORUMS), VNOTIFY_FORUMS, '', $yes_no), '$mailhost' => [ 'mailhost', t('Email notification hub (hostname)'), get_pconfig(local_channel(),'system','email_notify_host',\App::get_hostname()), sprintf( t('If your channel is mirrored to multiple hubs, set this to your preferred location. This will prevent duplicate email notifications. Example: %s'),\App::get_hostname()) ], '$always_show_in_notices' => array('always_show_in_notices', t('Show new wall posts, private messages and connections under Notices'), $always_show_in_notices, 1, '', $yes_no), - - '$evdays' => array('evdays', t('Notify me of events this many days in advance'), $evdays, t('Must be greater than 0')), + '$update_notices_per_parent' => array('update_notices_per_parent', t('Mark all notices of the thread read if a notice is clicked'), $update_notices_per_parent, 1, t('If no, only the clicked notice will be marked read'), $yes_no), + '$desktop_notifications_info' => t('Desktop notifications are unavailable because the required browser permission has not been granted'), + '$desktop_notifications_request' => t('Grant permission'), + '$evdays' => array('evdays', t('Notify me of events this many days in advance'), $evdays, t('Must be greater than 0')), '$basic_addon' => $plugin['basic'], '$sec_addon' => $plugin['security'], '$notify_addon' => $plugin['notify'], - + '$h_advn' => t('Advanced Account/Page Type Settings'), '$h_descadvn' => t('Change the behaviour of this account for special situations'), '$pagetype' => $pagetype, @@ -596,11 +602,11 @@ class Channel { '$removeme' => t('Remove Channel'), '$removechannel' => t('Remove this channel.'), )); - + call_hooks('settings_form',$o); - + //$o .= '</form>' . "\r\n"; - + return $o; } } diff --git a/Zotlabs/Module/Settings/Display.php b/Zotlabs/Module/Settings/Display.php index cade0a529..11181907b 100644 --- a/Zotlabs/Module/Settings/Display.php +++ b/Zotlabs/Module/Settings/Display.php @@ -24,7 +24,6 @@ class Display { $preload_images = ((x($_POST,'preload_images')) ? intval($_POST['preload_images']) : 0); - $channel_menu = ((x($_POST,'channel_menu')) ? intval($_POST['channel_menu']) : 0); $user_scalable = ((x($_POST,'user_scalable')) ? intval($_POST['user_scalable']) : 0); $nosmile = ((x($_POST,'nosmile')) ? intval($_POST['nosmile']) : 0); $title_tosource = ((x($_POST,'title_tosource')) ? intval($_POST['title_tosource']) : 0); @@ -46,7 +45,6 @@ class Display { set_pconfig(local_channel(),'system','itemspage', $itemspage); set_pconfig(local_channel(),'system','no_smilies',1-intval($nosmile)); set_pconfig(local_channel(),'system','title_tosource',$title_tosource); - set_pconfig(local_channel(),'system','channel_menu', $channel_menu); set_pconfig(local_channel(),'system','start_menu', $start_menu); $newschema = ''; @@ -197,7 +195,6 @@ class Display { '$ajaxint' => array('browser_update', t("Update browser every xx seconds"), $browser_update, t('Minimum of 10 seconds, no maximum')), '$itemspage' => array('itemspage', t("Maximum number of conversations to load at any time:"), $itemspage, t('Maximum of 30 items')), '$nosmile' => array('nosmile', t("Show emoticons (smilies) as images"), 1-intval($nosmile), '', $yes_no), - '$channel_menu' => [ 'channel_menu', t('Provide channel menu in navigation bar'), get_pconfig(local_channel(),'system','channel_menu',get_config('system','channel_menu',0)), t('Default: channel menu located in app menu'),$yes_no ], '$title_tosource' => array('title_tosource', t("Link post titles to source"), $title_tosource, '', $yes_no), '$theme_config' => $theme_config, '$start_menu' => ['start_menu', t('New Member Links'), $start_menu, t('Display new member quick links menu'), $yes_no] diff --git a/Zotlabs/Module/Setup.php b/Zotlabs/Module/Setup.php index 541e4fa21..ca8c19600 100644 --- a/Zotlabs/Module/Setup.php +++ b/Zotlabs/Module/Setup.php @@ -63,15 +63,15 @@ class Setup extends \Zotlabs\Web\Controller { return; // implied break; case 3: - $dbhost = trim($_POST['dbhost']); - $dbport = intval(trim($_POST['dbport'])); - $dbuser = trim($_POST['dbuser']); - $dbpass = trim($_POST['dbpass']); - $dbdata = trim($_POST['dbdata']); - $dbtype = intval(trim($_POST['dbtype'])); - $phpath = trim($_POST['phpath']); - $adminmail = trim($_POST['adminmail']); - $siteurl = trim($_POST['siteurl']); + $dbhost = ((isset($_POST['dbhost'])) ? trim($_POST['dbhost']) : ''); + $dbuser = ((isset($_POST['dbuser'])) ? trim($_POST['dbuser']) : ''); + $dbport = ((isset($_POST['dbport'])) ? intval(trim($_POST['dbport'])) : 0); + $dbpass = ((isset($_POST['dbpass'])) ? trim($_POST['dbpass']) : ''); + $dbdata = ((isset($_POST['dbdata'])) ? trim($_POST['dbdata']) : ''); + $dbtype = ((isset($_POST['dbtype'])) ? intval(trim($_POST['dbtype'])) : 0); + $phpath = ((isset($_POST['phpath'])) ? trim($_POST['phpath']) : ''); + $adminmail = ((isset($_POST['adminmail'])) ? trim($_POST['adminmail']) : ''); + $siteurl = ((isset($_POST['siteurl'])) ? trim($_POST['siteurl']) : ''); // $siteurl should not have a trailing slash @@ -88,16 +88,16 @@ class Setup extends \Zotlabs\Web\Controller { return; // implied break; case 4: - $dbhost = trim($_POST['dbhost']); - $dbport = intval(trim($_POST['dbport'])); - $dbuser = trim($_POST['dbuser']); - $dbpass = trim($_POST['dbpass']); - $dbdata = trim($_POST['dbdata']); - $dbtype = intval(trim($_POST['dbtype'])); - $phpath = trim($_POST['phpath']); - $timezone = trim($_POST['timezone']); - $adminmail = trim($_POST['adminmail']); - $siteurl = trim($_POST['siteurl']); + $dbhost = ((isset($_POST['dbhost'])) ? trim($_POST['dbhost']) : ''); + $dbuser = ((isset($_POST['dbuser'])) ? trim($_POST['dbuser']) : ''); + $dbport = ((isset($_POST['dbport'])) ? intval(trim($_POST['dbport'])) : 0); + $dbpass = ((isset($_POST['dbpass'])) ? trim($_POST['dbpass']) : ''); + $dbdata = ((isset($_POST['dbdata'])) ? trim($_POST['dbdata']) : ''); + $dbtype = ((isset($_POST['dbtype'])) ? intval(trim($_POST['dbtype'])) : 0); + $phpath = ((isset($_POST['phpath'])) ? trim($_POST['phpath']) : ''); + $timezone = ((isset($_POST['timezone'])) ? trim($_POST['timezone']) : ''); + $adminmail = ((isset($_POST['adminmail'])) ? trim($_POST['adminmail']) : ''); + $siteurl = ((isset($_POST['siteurl'])) ? trim($_POST['siteurl']) : ''); if($siteurl != z_root()) { $test = z_fetch_url($siteurl."/setup/testrewrite"); @@ -108,12 +108,14 @@ class Setup extends \Zotlabs\Web\Controller { } } - if(! \DBA::$dba->connected) { + $db = null; + + if(! isset(\DBA::$dba->connected)) { // connect to db $db = \DBA::dba_factory($dbhost, $dbport, $dbuser, $dbpass, $dbdata, $dbtype, true); } - if(! \DBA::$dba->connected) { + if(! isset(\DBA::$dba->connected)) { echo 'CRITICAL: DB not connected.'; killme(); } @@ -126,7 +128,7 @@ class Setup extends \Zotlabs\Web\Controller { '$dbpass' => $dbpass, '$dbdata' => $dbdata, '$dbtype' => $dbtype, - '$server_role' => 'pro', + '$server_role' => '', '$timezone' => $timezone, '$siteurl' => $siteurl, '$site_id' => random_string(), @@ -267,14 +269,14 @@ class Setup extends \Zotlabs\Web\Controller { case 2: { // Database config - $dbhost = ((x($_POST,'dbhost')) ? trim($_POST['dbhost']) : '127.0.0.1'); - $dbuser = trim($_POST['dbuser']); - $dbport = intval(trim($_POST['dbport'])); - $dbpass = trim($_POST['dbpass']); - $dbdata = trim($_POST['dbdata']); - $dbtype = intval(trim($_POST['dbtype'])); - $phpath = trim($_POST['phpath']); - $adminmail = trim($_POST['adminmail']); + $dbhost = ((isset($_POST['dbhost'])) ? trim($_POST['dbhost']) : '127.0.0.1'); + $dbuser = ((isset($_POST['dbuser'])) ? trim($_POST['dbuser']) : ''); + $dbport = ((isset($_POST['dbport'])) ? intval(trim($_POST['dbport'])) : 0); + $dbpass = ((isset($_POST['dbpass'])) ? trim($_POST['dbpass']) : ''); + $dbdata = ((isset($_POST['dbdata'])) ? trim($_POST['dbdata']) : ''); + $dbtype = ((isset($_POST['dbtype'])) ? intval(trim($_POST['dbtype'])) : 0); + $phpath = ((isset($_POST['phpath'])) ? trim($_POST['phpath']) : ''); + $adminmail = ((isset($_POST['adminmail'])) ? trim($_POST['adminmail']) : ''); $tpl = get_markup_template('install_db.tpl'); $o .= replace_macros($tpl, array( @@ -307,17 +309,17 @@ class Setup extends \Zotlabs\Web\Controller { }; break; case 3: { // Site settings require_once('include/datetime.php'); - $dbhost = ((x($_POST,'dbhost')) ? trim($_POST['dbhost']) : '127.0.0.1'); - $dbport = intval(trim($_POST['dbuser'])); - $dbuser = trim($_POST['dbuser']); - $dbpass = trim($_POST['dbpass']); - $dbdata = trim($_POST['dbdata']); - $dbtype = intval(trim($_POST['dbtype'])); - $phpath = trim($_POST['phpath']); - - $adminmail = trim($_POST['adminmail']); - $timezone = ((x($_POST,'timezone')) ? ($_POST['timezone']) : 'America/Los_Angeles'); + $dbhost = ((isset($_POST['dbhost'])) ? trim($_POST['dbhost']) : '127.0.0.1'); + $dbuser = ((isset($_POST['dbuser'])) ? trim($_POST['dbuser']) : ''); + $dbport = ((isset($_POST['dbport'])) ? intval(trim($_POST['dbport'])) : 0); + $dbpass = ((isset($_POST['dbpass'])) ? trim($_POST['dbpass']) : ''); + $dbdata = ((isset($_POST['dbdata'])) ? trim($_POST['dbdata']) : ''); + $dbtype = ((isset($_POST['dbtype'])) ? intval(trim($_POST['dbtype'])) : 0); + $phpath = ((isset($_POST['phpath'])) ? trim($_POST['phpath']) : ''); + $timezone = ((isset($_POST['timezone'])) ? trim($_POST['timezone']) : 'America/Los_Angeles'); + $adminmail = ((isset($_POST['adminmail'])) ? trim($_POST['adminmail']) : ''); + $siteurl = ((isset($_POST['siteurl'])) ? trim($_POST['siteurl']) : ''); $tpl = get_markup_template('install_settings.tpl'); $o .= replace_macros($tpl, array( diff --git a/Zotlabs/Module/Sources.php b/Zotlabs/Module/Sources.php index e535f6ebf..ef665e727 100644 --- a/Zotlabs/Module/Sources.php +++ b/Zotlabs/Module/Sources.php @@ -13,7 +13,7 @@ class Sources extends Controller { if(! Apps::system_app_installed(local_channel(), 'Channel Sources')) return; - + $source = intval($_REQUEST['source']); $xchan = escape_tags($_REQUEST['xchan']); $abook = intval($_REQUEST['abook']); @@ -22,21 +22,21 @@ class Sources extends Controller { $frequency = $_REQUEST['frequency']; $name = escape_tags($_REQUEST['name']); $tags = escape_tags($_REQUEST['tags']); - + $channel = \App::get_channel(); - + if($name == '*') $xchan = '*'; - + if($abook) { $r = q("select abook_xchan from abook where abook_id = %d and abook_channel = %d limit 1", intval($abook), intval(local_channel()) ); - if($r) + if($r) $xchan = $r[0]['abook_xchan']; } - + if(! $xchan) { notice ( t('Failed to create source. No channel selected.') . EOL); return; @@ -69,27 +69,25 @@ class Sources extends Controller { if($r) { info( t('Source updated.') . EOL); } - + } } - - + + function get() { if(! local_channel()) { notice( t('Permission denied.') . EOL); return; } - + if(! Apps::system_app_installed(local_channel(), 'Channel Sources')) { //Do not display any associated widgets at this point App::$pdl = ''; - - $o = '<b>' . t('Sources App') . ' (' . t('Not Installed') . '):</b><br>'; - $o .= t('Automatically import channel content from other channels or feeds'); - return $o; + $papp = Apps::get_papp('Channel Sources'); + return Apps::app_render($papp, 'module'); } - + // list sources if(argc() == 1) { $r = q("select source.*, xchan.* from source left join xchan on src_xchan = xchan_hash where src_channel_id = %d", @@ -111,23 +109,23 @@ class Sources extends Controller { )); return $o; } - + if(argc() == 2 && argv(1) === 'new') { // TODO add the words 'or RSS feed' and corresponding code to manage feeds and frequency - + $o = replace_macros(get_markup_template('sources_new.tpl'), array( '$title' => t('New Source'), '$desc' => t('Import all or selected content from the following channel into this channel and distribute it according to your channel settings.'), '$words' => array( 'words', t('Only import content with these words (one per line)'),'',t('Leave blank to import all public content')), '$name' => array( 'name', t('Channel Name'), '', '', '', 'autocomplete="off"'), '$tags' => array('tags', t('Add the following categories to posts imported from this source (comma separated)'),'',t('Optional')), - '$resend' => [ 'resend', t('Resend posts with this channel as author'), 0, t('Copyrights may apply'), [ t('No'), t('Yes') ]], + '$resend' => [ 'resend', t('Resend posts with this channel as author'), 0, t('Copyrights may apply'), [ t('No'), t('Yes') ]], '$submit' => t('Submit') )); return $o; - + } - + if(argc() == 2 && intval(argv(1))) { // edit source $r = q("select source.*, xchan.* from source left join xchan on src_xchan = xchan_hash where src_id = %d and src_channel_id = %d limit 1", @@ -144,9 +142,9 @@ class Sources extends Controller { notice( t('Source not found.') . EOL); return ''; } - + $r[0]['src_patt'] = htmlspecialchars($r[0]['src_patt'], ENT_QUOTES,'UTF-8'); - + $o = replace_macros(get_markup_template('sources_edit.tpl'), array( '$title' => t('Edit Source'), '$drop' => t('Delete Source'), @@ -156,15 +154,15 @@ class Sources extends Controller { '$xchan' => $r[0]['src_xchan'], '$abook' => $x[0]['abook_id'], '$tags' => array('tags', t('Add the following categories to posts imported from this source (comma separated)'),$r[0]['src_tag'],t('Optional')), - '$resend' => [ 'resend', t('Resend posts with this channel as author'), get_abconfig(local_channel(), $r[0]['xchan_hash'],'system','rself'), t('Copyrights may apply'), [ t('No'), t('Yes') ]], + '$resend' => [ 'resend', t('Resend posts with this channel as author'), get_abconfig(local_channel(), $r[0]['xchan_hash'],'system','rself'), t('Copyrights may apply'), [ t('No'), t('Yes') ]], '$name' => array( 'name', t('Channel Name'), $r[0]['xchan_name'], ''), '$submit' => t('Submit') )); return $o; - + } - + if(argc() == 3 && intval(argv(1)) && argv(2) === 'drop') { $r = q("select * from source where src_id = %d and src_channel_id = %d limit 1", intval(argv(1)), @@ -182,12 +180,12 @@ class Sources extends Controller { info( t('Source removed') . EOL); else notice( t('Unable to remove source.') . EOL); - + goaway(z_root() . '/sources'); - + } - + // shouldn't get here. - + } } diff --git a/Zotlabs/Module/Sse.php b/Zotlabs/Module/Sse.php index 46b4a8d87..3dab3d465 100644 --- a/Zotlabs/Module/Sse.php +++ b/Zotlabs/Module/Sse.php @@ -34,6 +34,7 @@ class Sse extends Controller { self::$uid = local_channel(); self::$ob_hash = get_observer_hash(); self::$sse_id = false; + self::$vnotify = -1; if(! self::$ob_hash) { if(session_id()) { @@ -45,9 +46,10 @@ class Sse extends Controller { } } - self::$vnotify = get_pconfig(self::$uid, 'system', 'vnotify'); + if (self::$uid) { + self::$vnotify = get_pconfig(self::$uid, 'system', 'vnotify'); + } - $sys = get_sys_channel(); $sleep_seconds = 3; self::$sse_enabled = get_config('system', 'sse_enabled', 0); @@ -95,6 +97,14 @@ class Sse extends Controller { $result = XConfig::Get(self::$ob_hash, 'sse', 'notifications', []); $lock = XConfig::Get(self::$ob_hash, 'sse', 'lock'); + // We do not have the local_channel in the addon. + // Reset pubs here if the app is not installed. + if (self::$uid && (!(self::$vnotify & VNOTIFY_PUBS) || !Apps::system_app_installed(self::$uid, 'Public Stream'))) { + $result['pubs']['count'] = 0; + $result['pubs']['notifications'] = []; + $result['pubs']['offset'] = -1; + } + if($result && !$lock) { echo "event: notifications\n"; echo 'data: ' . json_encode($result); @@ -109,7 +119,8 @@ class Sse extends Controller { echo 'data: {}'; echo "\n\n"; - ob_end_flush(); + if(ob_get_length() > 0) + ob_end_flush(); flush(); if(connection_status() != CONNECTION_NORMAL || connection_aborted()) { diff --git a/Zotlabs/Module/Sse_bs.php b/Zotlabs/Module/Sse_bs.php index 287c24829..eaaeae7b7 100644 --- a/Zotlabs/Module/Sse_bs.php +++ b/Zotlabs/Module/Sse_bs.php @@ -6,6 +6,7 @@ use App; use Zotlabs\Lib\Apps; use Zotlabs\Web\Controller; use Zotlabs\Lib\Enotify; +use Zotlabs\Lib\XConfig; class Sse_bs extends Controller { @@ -36,7 +37,7 @@ class Sse_bs extends Controller { self::$vnotify = get_pconfig(self::$uid, 'system', 'vnotify', -1); self::$evdays = intval(get_pconfig(self::$uid, 'system', 'evdays')); - self::$limit = 50; + self::$limit = 30; self::$offset = 0; self::$xchans = ''; @@ -54,10 +55,13 @@ class Sse_bs extends Controller { self::$xchans = ids_to_querystr($x, 'xchan_hash', true); } - if(intval(argv(2)) > 0) + if(intval(argv(2)) > 0) { self::$offset = argv(2); - else + } + else { $_SESSION['sse_loadtime'] = datetime_convert(); + } + $network = false; $dm = false; @@ -99,14 +103,14 @@ class Sse_bs extends Controller { self::bs_forums(), self::bs_pubs($pubs), self::bs_files(), - self::bs_mail(), self::bs_all_events(), - self::bs_register() + self::bs_register(), + self::bs_info_notice() ); - set_xconfig(self::$ob_hash, 'sse', 'timestamp', datetime_convert()); - set_xconfig(self::$ob_hash, 'sse', 'notifications', []); // reset the cache - set_xconfig(self::$ob_hash, 'sse', 'language', App::$language); + XConfig::Set(self::$ob_hash, 'sse', 'notifications', []); + XConfig::Set(self::$ob_hash, 'sse', 'timestamp', datetime_convert()); + XConfig::Set(self::$ob_hash, 'sse', 'language', App::$language); json_return_and_die($result); } @@ -123,7 +127,7 @@ class Sse_bs extends Controller { $mids[] = '\'' . dbesc(@base64url_decode(substr($a,4))) . '\''; } - $str = implode($mids, ','); + $str = implode(',', $mids); $x = [ 'channel_id' => self::$uid, 'update' => 'unset' ]; call_hooks('update_unseen',$x); @@ -142,11 +146,15 @@ class Sse_bs extends Controller { $result['network']['notifications'] = []; $result['network']['count'] = 0; - if(! self::$uid) + if(! self::$uid) { + $result['network']['offset'] = -1; return $result; + } - if(! (self::$vnotify & VNOTIFY_NETWORK)) + if(! (self::$vnotify & VNOTIFY_NETWORK)) { + $result['network']['offset'] = -1; return $result; + } $limit = intval(self::$limit); $offset = self::$offset; @@ -162,7 +170,7 @@ class Sse_bs extends Controller { $item_normal = item_normal(); if ($notifications) { - $items = q("SELECT * FROM item + $items = q("SELECT * FROM item WHERE uid = %d AND created <= '%s' AND item_unseen = 1 AND item_wall = 0 AND item_private IN (0, 1) @@ -181,7 +189,10 @@ class Sse_bs extends Controller { $result['network']['offset'] = ((count($items) == $limit) ? intval($offset + $limit) : -1); xchan_query($items); foreach($items as $item) { - $result['network']['notifications'][] = Enotify::format($item); + $parsed = Enotify::format($item); + if($parsed) { + $result['network']['notifications'][] = $parsed; + } } } else { @@ -190,7 +201,7 @@ class Sse_bs extends Controller { } - $r = q("SELECT count(id) as total FROM item + $r = q("SELECT count(id) as total FROM item WHERE uid = %d and item_unseen = 1 AND item_wall = 0 AND item_private IN (0, 1) AND obj_type NOT IN ('Document', 'Video', 'Audio', 'Image') AND author_xchan != '%s' @@ -211,11 +222,15 @@ class Sse_bs extends Controller { $result['dm']['notifications'] = []; $result['dm']['count'] = 0; - if(! self::$uid) + if(! self::$uid) { + $result['dm']['offset'] = -1; return $result; + } - if(! (self::$vnotify & VNOTIFY_MAIL)) + if(! (self::$vnotify & VNOTIFY_MAIL)) { + $result['dm']['offset'] = -1; return $result; + } $limit = intval(self::$limit); $offset = self::$offset; @@ -231,10 +246,10 @@ class Sse_bs extends Controller { $item_normal = item_normal(); if ($notifications) { - $items = q("SELECT * FROM item + $items = q("SELECT * FROM item WHERE uid = %d AND created <= '%s' - AND item_unseen = 1 AND item_wall = 0 AND item_private = 2 + AND item_unseen = 1 AND item_private = 2 AND obj_type NOT IN ('Document', 'Video', 'Audio', 'Image') AND author_xchan != '%s' $item_normal @@ -250,7 +265,10 @@ class Sse_bs extends Controller { $result['dm']['offset'] = ((count($items) == $limit) ? intval($offset + $limit) : -1); xchan_query($items); foreach($items as $item) { - $result['dm']['notifications'][] = Enotify::format($item); + $parsed = Enotify::format($item); + if($parsed) { + $result['dm']['notifications'][] = $parsed; + } } } else { @@ -259,8 +277,8 @@ class Sse_bs extends Controller { } - $r = q("SELECT count(id) as total FROM item - WHERE uid = %d and item_unseen = 1 AND item_wall = 0 AND item_private = 2 + $r = q("SELECT count(id) as total FROM item + WHERE uid = %d and item_unseen = 1 AND item_private = 2 $item_normal $sql_extra AND author_xchan != '%s'", @@ -279,11 +297,15 @@ class Sse_bs extends Controller { $result['home']['notifications'] = []; $result['home']['count'] = 0; - if(! self::$uid) + if(! self::$uid) { + $result['home']['offset'] = -1; return $result; + } - if(! (self::$vnotify & VNOTIFY_CHANNEL)) + if(! (self::$vnotify & VNOTIFY_CHANNEL)) { + $result['home']['offset'] = -1; return $result; + } $limit = intval(self::$limit); $offset = self::$offset; @@ -300,10 +322,10 @@ class Sse_bs extends Controller { $item_normal = item_normal(); if ($notifications) { - $items = q("SELECT * FROM item + $items = q("SELECT * FROM item WHERE uid = %d AND created <= '%s' - AND item_unseen = 1 AND item_wall = 1 + AND item_unseen = 1 AND item_wall = 1 AND item_private IN (0, 1) AND obj_type NOT IN ('Document', 'Video', 'Audio', 'Image') AND author_xchan != '%s' $item_normal @@ -319,7 +341,10 @@ class Sse_bs extends Controller { $result['home']['offset'] = ((count($items) == $limit) ? intval($offset + $limit) : -1); xchan_query($items); foreach($items as $item) { - $result['home']['notifications'][] = Enotify::format($item); + $parsed = Enotify::format($item); + if($parsed) { + $result['home']['notifications'][] = $parsed; + } } } else { @@ -328,8 +353,8 @@ class Sse_bs extends Controller { } - $r = q("SELECT count(id) as total FROM item - WHERE uid = %d and item_unseen = 1 AND item_wall = 1 + $r = q("SELECT count(id) as total FROM item + WHERE uid = %d and item_unseen = 1 AND item_wall = 1 AND item_private IN (0, 1) $item_normal $sql_extra AND author_xchan != '%s'", @@ -348,15 +373,19 @@ class Sse_bs extends Controller { $result['pubs']['notifications'] = []; $result['pubs']['count'] = 0; - if(! (self::$vnotify & VNOTIFY_PUBS)) + if(! (self::$vnotify & VNOTIFY_PUBS) || !Apps::system_app_installed(self::$uid, 'Public Stream')) { + $result['pubs']['offset'] = -1; return $result; + } if((observer_prohibited(true))) { + $result['pubs']['offset'] = -1; return $result; } if(! intval(get_config('system','open_pubstream',1))) { if(! get_observer_hash()) { + $result['pubs']['offset'] = -1; return $result; } } @@ -379,7 +408,7 @@ class Sse_bs extends Controller { $item_normal = item_normal(); if ($notifications) { - $items = q("SELECT * FROM item + $items = q("SELECT * FROM item WHERE uid = %d AND created <= '%s' AND item_unseen = 1 @@ -400,7 +429,10 @@ class Sse_bs extends Controller { $result['pubs']['offset'] = ((count($items) == $limit) ? intval($offset + $limit) : -1); xchan_query($items); foreach($items as $item) { - $result['pubs']['notifications'][] = Enotify::format($item); + $parsed = Enotify::format($item); + if($parsed) { + $result['pubs']['notifications'][] = $parsed; + } } } else { @@ -410,7 +442,7 @@ class Sse_bs extends Controller { } - $r = q("SELECT count(id) as total FROM item + $r = q("SELECT count(id) as total FROM item WHERE uid = %d AND item_unseen = 1 AND created > '%s' $item_normal @@ -516,7 +548,7 @@ class Sse_bs extends Controller { $p_str = ids_to_querystr($p, 'parent'); $p_sql = (($p_str) ? "OR parent IN ( $p_str )" : ''); - $r = q("select mid from item + $r = q("select mid from item where uid = %d and ( owner_xchan = '%s' OR author_xchan = '%s' $p_sql ) and item_unseen = 1 $sql_extra $item_normal", intval(self::$uid), dbesc($forums[$x]['xchan_hash']), @@ -528,7 +560,7 @@ class Sse_bs extends Controller { $b64mids = []; foreach($mids as $mid) - $b64mids[] = 'b64.' . base64url_encode($mid); + $b64mids[] = gen_link_id($mid); $forums[$x]['notify_link'] = z_root() . '/network/?f=&pf=1&unseen=1&cid=' . $forums[$x]['abook_id']; $forums[$x]['name'] = $forums[$x]['xchan_name']; @@ -577,7 +609,7 @@ class Sse_bs extends Controller { $item_normal = item_normal(); - $r = q("SELECT * FROM item + $r = q("SELECT * FROM item WHERE verb = '%s' AND obj_type IN ('Document', 'Video', 'Audio', 'Image') AND uid = %d @@ -592,7 +624,10 @@ class Sse_bs extends Controller { if($r) { xchan_query($r); foreach($r as $rr) { - $result['files']['notifications'][] = Enotify::format($rr); + $parsed = Enotify::format($rr); + if($parsed) { + $result['files']['notifications'][] = $parsed; + } } $result['files']['count'] = count($r); } @@ -601,36 +636,6 @@ class Sse_bs extends Controller { } - function bs_mail() { - - $result['mail']['notifications'] = []; - $result['mail']['count'] = 0; - $result['mail']['offset'] = -1; - - if(! self::$uid) - return $result; - - if(! (self::$vnotify & VNOTIFY_MAIL)) - return $result; - - $r = q("select mail.*, xchan.* from mail left join xchan on xchan_hash = from_xchan - where channel_id = %d and mail_seen = 0 and mail_deleted = 0 - and from_xchan != '%s' order by created desc", - intval(self::$uid), - dbesc(self::$ob_hash) - ); - - if($r) { - foreach($r as $rr) { - $result['mail']['notifications'][] = Enotify::format_mail($rr); - } - $result['mail']['count'] = count($r); - } - - return $result; - - } - function bs_all_events() { $result['all_events']['notifications'] = []; @@ -671,12 +676,15 @@ class Sse_bs extends Controller { if(! self::$uid && ! is_site_admin()) return $result; + $policy = intval(get_config('system','register_policy')); + if(($policy & REGISTER_APPROVE) != REGISTER_APPROVE) + return $result; + if(! (self::$vnotify & VNOTIFY_REGISTER)) return $result; - $r = q("SELECT account_email, account_created from account where (account_flags & %d) > 0", - intval(ACCOUNT_PENDING) - ); + $r = get_pending_accounts(); + if($r) { foreach($r as $rr) { $result['register']['notifications'][] = Enotify::format_register($rr); @@ -688,4 +696,22 @@ class Sse_bs extends Controller { } + function bs_info_notice() { + + $result['notice']['notifications'] = []; + $result['info']['notifications'] = []; + + $r = XConfig::Get(self::$ob_hash, 'sse', 'notifications', []); + + if(isset($r['notice'])) + $result['notice']['notifications'] = $r['notice']['notifications']; + + if(isset($r['info'])) + $result['info']['notifications'] = $r['info']['notifications']; + + return $result; + + } + + } diff --git a/Zotlabs/Module/Subthread.php b/Zotlabs/Module/Subthread.php index 30e57197d..a796d85cb 100644 --- a/Zotlabs/Module/Subthread.php +++ b/Zotlabs/Module/Subthread.php @@ -1,6 +1,9 @@ <?php namespace Zotlabs\Module; +use Zotlabs\Lib\Activity; + + require_once('include/security.php'); require_once('include/bbcode.php'); require_once('include/items.php'); @@ -10,22 +13,22 @@ require_once('include/items.php'); class Subthread extends \Zotlabs\Web\Controller { function get() { - + if(! local_channel()) { return; } - + $sys = get_sys_channel(); $channel = \App::get_channel(); $item_id = ((argc() > 2) ? notags(trim(argv(2))) : 0); - + if(argv(1) === 'sub') $activity = ACTIVITY_FOLLOW; elseif(argv(1) === 'unsub') $activity = ACTIVITY_UNFOLLOW; - - + + $i = q("select * from item where id = %d and uid = %d", intval($item_id), intval(local_channel()) @@ -42,7 +45,7 @@ class Subthread extends \Zotlabs\Web\Controller { $item_id = (($i) ? $i[0]['id'] : 0); } } - + if(! $i) { return; } @@ -56,37 +59,37 @@ class Subthread extends \Zotlabs\Web\Controller { dbesc($r[0]['parent']) ); } - + if((! $item_id) || (! $r)) { logger('subthread: no item ' . $item_id); return; } - + $item = $r[0]; - + $owner_uid = $item['uid']; $observer = \App::get_observer(); $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); - + if(! perm_is_allowed($owner_uid,$ob_hash,'post_comments')) return; - + $sys = get_sys_channel(); - + $owner_uid = $item['uid']; $owner_aid = $item['aid']; - + // if this is a "discover" item, (item['uid'] is the sys channel), // fallback to the item comment policy, which should've been // respected when generating the conversation thread. // Even if the activity is rejected by the item owner, it should still get attached - // to the local discover conversation on this site. - + // to the local discover conversation on this site. + if(($owner_uid != $sys['channel_id']) && (! perm_is_allowed($owner_uid,$observer['xchan_hash'],'post_comments'))) { notice( t('Permission denied') . EOL); killme(); } - + $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($item['owner_xchan']) ); @@ -94,7 +97,7 @@ class Subthread extends \Zotlabs\Web\Controller { $thread_owner = $r[0]; else killme(); - + $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($item['author_xchan']) ); @@ -102,50 +105,32 @@ class Subthread extends \Zotlabs\Web\Controller { $item_author = $r[0]; else killme(); - - - - + + + + $uuid = item_message_id(); $mid = z_root() . '/item/' . $uuid; $post_type = (($item['resource_type'] === 'photo') ? t('photo') : t('status')); - + $links = array(array('rel' => 'alternate','type' => 'text/html', 'href' => $item['plink'])); - $objtype = (($item['resource_type'] === 'photo') ? ACTIVITY_OBJ_PHOTO : ACTIVITY_OBJ_NOTE ); - + $objtype = (($item['resource_type'] === 'photo') ? ACTIVITY_OBJ_PHOTO : ACTIVITY_OBJ_NOTE ); + $body = $item['body']; - - $obj = json_encode(array( - 'type' => $objtype, - 'id' => $item['mid'], - 'parent' => (($item['thr_parent']) ? $item['thr_parent'] : $item['parent_mid']), - 'link' => $links, - 'title' => $item['title'], - 'content' => $item['body'], - 'created' => $item['created'], - 'edited' => $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'])), - ), - )); - + + $obj = Activity::fetch_item( [ 'id' => $item['mid'] ] ); + if(! intval($item['item_thread_top'])) - $post_type = 'comment'; - + $post_type = 'comment'; + if($activity === ACTIVITY_FOLLOW) $bodyverb = t('%1$s is following %2$s\'s %3$s'); if($activity === ACTIVITY_UNFOLLOW) $bodyverb = t('%1$s stopped following %2$s\'s %3$s'); - + $arr = array(); - + $arr['uuid'] = $uuid; $arr['mid'] = $mid; $arr['aid'] = $owner_aid; @@ -161,35 +146,35 @@ class Subthread extends \Zotlabs\Web\Controller { $arr['item_wall'] = 1; else $arr['item_wall'] = 0; - + $ulink = '[zrl=' . $item_author['xchan_url'] . ']' . $item_author['xchan_name'] . '[/zrl]'; $alink = '[zrl=' . $observer['xchan_url'] . ']' . $observer['xchan_name'] . '[/zrl]'; $plink = '[zrl=' . z_root() . '/display/' . gen_link_id($item['mid']) . ']' . $post_type . '[/zrl]'; - + $arr['body'] = sprintf( $bodyverb, $alink, $ulink, $plink ); - + $arr['verb'] = $activity; $arr['obj_type'] = $objtype; - $arr['obj'] = $obj; - + $arr['obj'] = json_encode($obj); + $arr['allow_cid'] = $item['allow_cid']; $arr['allow_gid'] = $item['allow_gid']; $arr['deny_cid'] = $item['deny_cid']; $arr['deny_gid'] = $item['deny_gid']; - - $post = item_store($arr); + + $post = item_store($arr); $post_id = $post['item_id']; - + $arr['id'] = $post_id; - + call_hooks('post_local_end', $arr); - + killme(); - - + + } - - - - + + + + } diff --git a/Zotlabs/Module/Suggest.php b/Zotlabs/Module/Suggest.php index 18961214e..22822bb87 100644 --- a/Zotlabs/Module/Suggest.php +++ b/Zotlabs/Module/Suggest.php @@ -15,17 +15,17 @@ class Suggest extends \Zotlabs\Web\Controller { if(! Apps::system_app_installed(local_channel(), 'Suggest Channels')) return; - + if(x($_GET,'ignore')) { q("insert into xign ( uid, xchan ) values ( %d, '%s' ) ", intval(local_channel()), dbesc($_GET['ignore']) ); } - + } - - + + function get() { if(! local_channel()) { @@ -36,31 +36,29 @@ class Suggest extends \Zotlabs\Web\Controller { if(! Apps::system_app_installed(local_channel(), 'Suggest Channels')) { //Do not display any associated widgets at this point App::$pdl = ''; - - $o = '<b>' . t('Suggest Channels App') . ' (' . t('Not Installed') . '):</b><br>'; - $o .= t('Suggestions for channels in the $Projectname network you might be interested in'); - return $o; + $papp = Apps::get_papp('Suggest Channels'); + return Apps::app_render($papp, 'module'); } $o = ''; nav_set_selected('Suggest Channels'); - + $_SESSION['return_url'] = z_root() . '/' . \App::$cmd; - + $r = suggestion_query(local_channel(),get_observer_hash()); - + if(! $r) { info( t('No suggestions available. If this is a new site, please try again in 24 hours.')); return; } - + $arr = array(); - + foreach($r as $rr) { - - $connlnk = z_root() . '/follow/?url=' . $rr['xchan_addr']; - + + $connlnk = z_root() . '/follow?f=&url=' . $rr['xchan_addr']; + $arr[] = array( 'url' => chanlink_url($rr['xchan_url']), 'common' => $rr['total'], @@ -73,15 +71,15 @@ class Suggest extends \Zotlabs\Web\Controller { 'ignore' => t('Ignore/Hide') ); } - - + + $o = replace_macros(get_markup_template('suggest_page.tpl'),array( '$title' => t('Channel Suggestions'), '$entries' => $arr )); - + return $o; - + } - + } diff --git a/Zotlabs/Module/Tokens.php b/Zotlabs/Module/Tokens.php index 1ba41dcc5..31b219019 100644 --- a/Zotlabs/Module/Tokens.php +++ b/Zotlabs/Module/Tokens.php @@ -46,7 +46,7 @@ class Tokens extends Controller { return; } if($atoken_id) { - $r = q("update atoken set atoken_name = '%s', atoken_token = '%s', atoken_expires = '%s' + $r = q("update atoken set atoken_name = '%s', atoken_token = '%s', atoken_expires = '%s' where atoken_id = %d and atoken_uid = %d", dbesc($name), dbesc($token), @@ -80,12 +80,12 @@ class Tokens extends Controller { } } } - + info( t('Token saved.') . EOL); return; } - + function get() { @@ -95,10 +95,8 @@ class Tokens extends Controller { if(! Apps::system_app_installed(local_channel(), 'Guest Access')) { //Do not display any associated widgets at this point App::$pdl = ''; - - $o = '<b>' . t('Guest Access App') . ' (' . t('Not Installed') . '):</b><br>'; - $o .= t('Create access tokens so that non-members can access private content'); - return $o; + $papp = Apps::get_papp('Guest Access'); + return Apps::app_render($papp, 'module'); } $channel = App::get_channel(); @@ -128,7 +126,7 @@ class Tokens extends Controller { $t = q("select * from atoken where atoken_uid = %d", intval(local_channel()) - ); + ); $desc = t('Use this form to create temporary access identifiers to share things with non-members. These identities may be used in Access Control Lists and visitors may login using these credentials to access private content.'); @@ -189,5 +187,5 @@ class Tokens extends Controller { )); return $o; } - + } diff --git a/Zotlabs/Module/Uexport.php b/Zotlabs/Module/Uexport.php index 3d1587b87..d73bc40d4 100644 --- a/Zotlabs/Module/Uexport.php +++ b/Zotlabs/Module/Uexport.php @@ -17,54 +17,53 @@ class Uexport extends Controller { if(argc() > 1) { $sections = (($_REQUEST['sections']) ? explode(',',$_REQUEST['sections']) : ''); + $zap_compat = (($_REQUEST['zap_compat']) ? intval($_REQUEST['zap_compat']) : false); $channel = App::get_channel(); if(argc() > 1 && intval(argv(1)) > 1900) { $year = intval(argv(1)); } - + if(argc() > 2 && intval(argv(2)) > 0 && intval(argv(2)) <= 12) { $month = intval(argv(2)); } - + header('content-type: application/json'); header('content-disposition: attachment; filename="' . $channel['channel_address'] . (($year) ? '-' . $year : '') . (($month) ? '-' . $month : '') . (($_REQUEST['sections']) ? '-' . $_REQUEST['sections'] : '') . '.json"' ); - + if($year) { - echo json_encode(identity_export_year(local_channel(),$year,$month)); + echo json_encode(identity_export_year(local_channel(),$year,$month, $zap_compat)); killme(); } - + if(argc() > 1 && argv(1) === 'basic') { - echo json_encode(identity_basic_export(local_channel(),$sections)); + echo json_encode(identity_basic_export(local_channel(),$sections, $zap_compat)); killme(); } - + // Warning: this option may consume a lot of memory - + if(argc() > 1 && argv(1) === 'complete') { $sections = get_default_export_sections(); $sections[] = 'items'; - echo json_encode(identity_basic_export(local_channel(),$sections)); + echo json_encode(identity_basic_export(local_channel(),$sections, $zap_compat)); killme(); } } } - + function get() { if(! Apps::system_app_installed(local_channel(), 'Channel Export')) { //Do not display any associated widgets at this point App::$pdl = ''; - - $o = '<b>' . t('Channel Export App') . ' (' . t('Not Installed') . '):</b><br>'; - $o .= t('Export your channel'); - return $o; + $papp = Apps::get_papp('Channel Export'); + return Apps::app_render($papp, 'module'); } - + $y = datetime_convert('UTC',date_default_timezone_get(),'now','Y'); - + $yearurl = z_root() . '/uexport/' . $y; $janurl = z_root() . '/uexport/' . $y . '/1'; $impurl = '/import_items'; @@ -76,14 +75,14 @@ class Uexport extends Controller { '$full' => t('Export your channel information and recent content to a JSON backup that can be restored or imported to another server hub. This backs up all of your connections, permissions, profile data and several months of posts. This file may be VERY large. Please be patient - it may take several minutes for this download to begin.'), '$by_year' => t('Export your posts from a given year.'), - + '$extra' => t('You may also export your posts and conversations for a particular year or month. Adjust the date in your browser location bar to select other dates. If the export fails (possibly due to memory exhaustion on your server hub), please try again selecting a more limited date range.'), '$extra2' => sprintf( t('To select all posts for a given year, such as this year, visit <a href="%1$s">%2$s</a>'),$yearurl,$yearurl), '$extra3' => sprintf( t('To select all posts for a given month, such as January of this year, visit <a href="%1$s">%2$s</a>'),$janurl,$janurl), '$extra4' => sprintf( t('These content files may be imported or restored by visiting <a href="%1$s">%2$s</a> on any site containing your channel. For best results please import or restore these in date order (oldest first).'),$impurl,$impurl) - + )); return $o; } - + } diff --git a/Zotlabs/Module/Webpages.php b/Zotlabs/Module/Webpages.php index 787ed5850..bc47484be 100644 --- a/Zotlabs/Module/Webpages.php +++ b/Zotlabs/Module/Webpages.php @@ -15,26 +15,26 @@ require_once('include/acl_selectors.php'); class Webpages extends Controller { function init() { - + if(argc() > 1 && argv(1) === 'sys' && is_site_admin()) { $sys = get_sys_channel(); if($sys && intval($sys['channel_id'])) { App::$is_sys = true; } } - + if(argc() > 1) $which = argv(1); else return; - + profile_load($which); - + } - - + + function get() { - + if(! App::$profile) { notice( t('Requested profile is not available.') . EOL ); App::$error = 404; @@ -44,22 +44,20 @@ class Webpages extends Controller { if(! Apps::system_app_installed(App::$profile_uid, 'Webpages')) { //Do not display any associated widgets at this point App::$pdl = ''; - - $o = '<b>' . t('Webpages App') . ' (' . t('Not Installed') . '):</b><br>'; - $o .= t('Provide managed web pages on your channel'); - return $o; + $papp = Apps::get_papp('Webpages'); + return Apps::app_render($papp, 'module'); } nav_set_selected('Webpages'); $which = argv(1); - + $_SESSION['return_url'] = App::$query_string; - + $uid = local_channel(); $owner = 0; $observer = App::get_observer(); - + $channel = App::get_channel(); switch ($_SESSION['action']) { @@ -74,7 +72,7 @@ class Webpages extends Controller { '$blocks' => $_SESSION['blocks'], )); return $o; - + case 'importselected': $_SESSION['action'] = null; break; @@ -85,7 +83,7 @@ class Webpages extends Controller { break; } require_once('include/import.php'); - + $pages = get_webpage_elements($channel, 'pages'); $layouts = get_webpage_elements($channel, 'layouts'); $blocks = get_webpage_elements($channel, 'blocks'); @@ -99,13 +97,13 @@ class Webpages extends Controller { )); $_SESSION['export'] = null; return $o; - + default : $_SESSION['action'] = null; break; } - - + + if(App::$is_sys && is_site_admin()) { $sys = get_sys_channel(); if($sys && intval($sys['channel_id'])) { @@ -114,7 +112,7 @@ class Webpages extends Controller { $observer = $sys; } } - + if(! $owner) { // Figure out who the page owner is. $r = q("select channel_id from channel where channel_address = '%s'", @@ -124,24 +122,24 @@ class Webpages extends Controller { $owner = intval($r[0]['channel_id']); } } - + $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); - + $perms = get_all_perms($owner,$ob_hash); - + if(! $perms['write_pages']) { notice( t('Permission denied.') . EOL); return; } - + $mimetype = (($_REQUEST['mimetype']) ? $_REQUEST['mimetype'] : get_pconfig($owner,'system','page_mimetype')); - + $layout = (($_REQUEST['layout']) ? $_REQUEST['layout'] : get_pconfig($owner,'system','page_layout')); - + // Create a status editor (for now - we'll need a WYSIWYG eventually) to create pages - // Nickname is set to the observers xchan, and profile_uid to the owner's. + // Nickname is set to the observers xchan, and profile_uid to the owner's. // This lets you post pages at other people's channels. - + if((! $channel) && ($uid) && ($uid == App::$profile_uid)) { $channel = App::get_channel(); } @@ -156,12 +154,12 @@ class Webpages extends Controller { else { $channel_acl = [ 'allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '' ]; } - + $is_owner = ($uid && $uid == $owner); $o = ''; - + $x = array( 'webpage' => ITEM_TYPE_WEBPAGE, 'is_owner' => true, @@ -183,23 +181,23 @@ class Webpages extends Controller { 'bbco_autocomplete' => 'bbcode', 'bbcode' => true ); - + if($_REQUEST['title']) $x['title'] = $_REQUEST['title']; if($_REQUEST['body']) $x['body'] = $_REQUEST['body']; if($_REQUEST['pagetitle']) $x['pagetitle'] = $_REQUEST['pagetitle']; - - - // Get a list of webpages. We can't display all them because endless scroll makes that unusable, + + + // Get a list of webpages. We can't display all them because endless scroll makes that unusable, // so just list titles and an edit link. - - + + $sql_extra = item_permissions_sql($owner); - - $r = q("select * from iconfig left join item on iconfig.iid = item.id - where item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'WEBPAGE' and item_type = %d + + $r = q("select * from iconfig left join item on iconfig.iid = item.id + where item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'WEBPAGE' and item_type = %d $sql_extra order by item.created desc", intval($owner), intval(ITEM_TYPE_WEBPAGE) @@ -211,14 +209,13 @@ class Webpages extends Controller { $editor = status_editor($a,$x,false,'Webpages'); $pages = null; - + if($r) { $pages = array(); foreach($r as $rr) { - unobscure($rr); - + $lockstate = (($rr['allow_cid'] || $rr['allow_gid'] || $rr['deny_cid'] || $rr['deny_gid']) ? 'lock' : 'unlock'); - + $element_arr = array( 'type' => 'webpage', 'title' => $rr['title'], @@ -243,11 +240,11 @@ class Webpages extends Controller { ); } } - - + + //Build the base URL for edit links $url = z_root() . '/editwebpage/' . $which; - + $o .= replace_macros(get_markup_template('webpagelist.tpl'), array( '$listtitle' => t('Webpages'), '$baseurl' => $url, @@ -266,19 +263,19 @@ class Webpages extends Controller { '$created_txt' => t('Created'), '$edited_txt' => t('Edited') )); - + return $o; } - + function post() { $action = $_REQUEST['action']; if( $action ){ switch ($action) { case 'scan': - + // the state of this variable tracks whether website files have been scanned (null, true, false) - $cloud = null; - + $cloud = null; + // Website files are to be imported from an uploaded zip file if(($_FILES) && array_key_exists('zip_file',$_FILES) && isset($_POST['w_upload'])) { $source = $_FILES["zip_file"]["tmp_name"]; @@ -306,8 +303,8 @@ class Webpages extends Controller { } else { notice( t('Error opening zip file') . EOL); return null; - } - } + } + } // Website files are to be imported from the channel cloud files if (($_POST) && array_key_exists('path',$_POST) && isset($_POST['cloudsubmit'])) { @@ -321,7 +318,7 @@ class Webpages extends Controller { $cloud = true; } - + // If the website files were uploaded or specified in the cloud files, then $cloud // should be either true or false if ($cloud !== null) { @@ -345,24 +342,24 @@ class Webpages extends Controller { notice( t('No webpage elements detected.') . EOL); $_SESSION['action'] = null; } - + } - + // If the website elements were imported from a zip file, delete the temporary decompressed files if ($cloud === false && $website && $elements) { $_SESSION['tempimportpath'] = $website; //rrmdir($website); // Delete the temporary decompressed files } - + break; - + case 'importselected': require_once('include/import.php'); $channel = App::get_channel(); - + // Import layout first so that pages that reference new layouts will find - // the mid of layout items in the database - + // the mid of layout items in the database + // Obtain the user-selected layouts to import and import them $checkedlayouts = $_POST['layout']; $layouts = []; @@ -380,7 +377,7 @@ class Webpages extends Controller { } } $_SESSION['import_layouts'] = $layouts; - + // Obtain the user-selected blocks to import and import them $checkedblocks = $_POST['block']; $blocks = []; @@ -398,7 +395,7 @@ class Webpages extends Controller { } } $_SESSION['import_blocks'] = $blocks; - + // Obtain the user-selected pages to import and import them $checkedpages = $_POST['page']; $pages = []; @@ -424,9 +421,9 @@ class Webpages extends Controller { unset($_SESSION['tempimportpath']); } break; - + case 'exportzipfile': - + if(isset($_POST['w_download'])) { $_SESSION['action'] = 'export_select_list'; $_SESSION['export'] = 'zipfile'; @@ -436,45 +433,45 @@ class Webpages extends Controller { $filename = 'website.zip'; } $_SESSION['zipfilename'] = $filename; - + } - + break; - + case 'exportcloud': if(isset($_POST['exportcloudpath']) && $_POST['exportcloudpath'] !== '') { $_SESSION['action'] = 'export_select_list'; $_SESSION['export'] = 'cloud'; $_SESSION['exportcloudpath'] = filter_var($_POST['exportcloudpath'], FILTER_SANITIZE_ENCODED); } - + break; - + case 'cloud': case 'zipfile': - + $channel = App::get_channel(); - + $tmp_folder_name = random_string(10); $zip_folder_name = random_string(10); $zip_filename = $_SESSION['zipfilename']; $tmp_folderpath = '/tmp/' . $tmp_folder_name; $zip_folderpath = '/tmp/' . $zip_folder_name; - if (!mkdir($zip_folderpath, 0770, false)) { + if (!mkdir($zip_folderpath, 0770, false)) { logger('Error creating zip file export folder: ' . $zip_folderpath, LOGGER_NORMAL); json_return_and_die(array('message' => 'Error creating zip file export folder')); } $zip_filepath = '/tmp/' . $zip_folder_name . '/' . $zip_filename; - + $checkedblocks = $_POST['block']; $blocks = []; if (!empty($checkedblocks)) { foreach ($checkedblocks as $mid) { - $b = q("select iconfig.v, iconfig.k, mimetype, title, body from iconfig - left join item on item.id = iconfig.iid + $b = q("select iconfig.v, iconfig.k, mimetype, title, body from iconfig + left join item on item.id = iconfig.iid where mid = '%s' and item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'BUILDBLOCK' order by iconfig.v asc limit 1", dbesc($mid), - intval($channel['channel_id']) + intval($channel['channel_id']) ); if($b) { $b = $b[0]; @@ -514,25 +511,25 @@ class Webpages extends Controller { $block_filepath = $tmp_blockfolder . '/' . $block_filename; $blockinfo['json']['contentfile'] = $block_filename; $block_jsonpath = $tmp_blockfolder . '/block.json'; - if (!is_dir($tmp_blockfolder) && !mkdir($tmp_blockfolder, 0770, true)) { + if (!is_dir($tmp_blockfolder) && !mkdir($tmp_blockfolder, 0770, true)) { logger('Error creating temp export folder: ' . $tmp_blockfolder, LOGGER_NORMAL); json_return_and_die(array('message' => 'Error creating temp export folder')); } file_put_contents($block_filepath, $blockinfo['body']); - file_put_contents($block_jsonpath, json_encode($blockinfo['json'], JSON_UNESCAPED_SLASHES)); + file_put_contents($block_jsonpath, json_encode($blockinfo['json'], JSON_UNESCAPED_SLASHES)); } } } - + $checkedlayouts = $_POST['layout']; $layouts = []; if (!empty($checkedlayouts)) { foreach ($checkedlayouts as $mid) { - $l = q("select iconfig.v, iconfig.k, mimetype, title, body from iconfig - left join item on item.id = iconfig.iid + $l = q("select iconfig.v, iconfig.k, mimetype, title, body from iconfig + left join item on item.id = iconfig.iid where mid = '%s' and item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'PDL' order by iconfig.v asc limit 1", dbesc($mid), - intval($channel['channel_id']) + intval($channel['channel_id']) ); if($l) { $l = $l[0]; @@ -558,38 +555,38 @@ class Webpages extends Controller { $layout_filepath = $tmp_layoutfolder . '/' . $layout_filename; $layoutinfo['json']['contentfile'] = $layout_filename; $layout_jsonpath = $tmp_layoutfolder . '/layout.json'; - if (!is_dir($tmp_layoutfolder) && !mkdir($tmp_layoutfolder, 0770, true)) { + if (!is_dir($tmp_layoutfolder) && !mkdir($tmp_layoutfolder, 0770, true)) { logger('Error creating temp export folder: ' . $tmp_layoutfolder, LOGGER_NORMAL); json_return_and_die(array('message' => 'Error creating temp export folder')); } file_put_contents($layout_filepath, $layoutinfo['body']); - file_put_contents($layout_jsonpath, json_encode($layoutinfo['json'], JSON_UNESCAPED_SLASHES)); + file_put_contents($layout_jsonpath, json_encode($layoutinfo['json'], JSON_UNESCAPED_SLASHES)); } } } - + $checkedpages = $_POST['page']; $pages = []; if (!empty($checkedpages)) { foreach ($checkedpages as $mid) { - - $p = q("select * from iconfig left join item on iconfig.iid = item.id + + $p = q("select * from iconfig left join item on iconfig.iid = item.id where item.uid = %d and item.mid = '%s' and iconfig.cat = 'system' and iconfig.k = 'WEBPAGE' and item_type = %d", intval($channel['channel_id']), dbesc($mid), intval(ITEM_TYPE_WEBPAGE) ); - + if($p) { foreach ($p as $pp) { // Get the associated layout $layoutinfo = array(); if($pp['layout_mid']) { - $l = q("select iconfig.v, iconfig.k, mimetype, title, body from iconfig - left join item on item.id = iconfig.iid + $l = q("select iconfig.v, iconfig.k, mimetype, title, body from iconfig + left join item on item.id = iconfig.iid where mid = '%s' and item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'PDL' order by iconfig.v asc limit 1", dbesc($pp['layout_mid']), - intval($channel['channel_id']) + intval($channel['channel_id']) ); if($l) { $l = $l[0]; @@ -614,12 +611,12 @@ class Webpages extends Controller { $layout_filepath = $tmp_layoutfolder . '/' . $layout_filename; $layoutinfo['json']['contentfile'] = $layout_filename; $layout_jsonpath = $tmp_layoutfolder . '/layout.json'; - if (!is_dir($tmp_layoutfolder) && !mkdir($tmp_layoutfolder, 0770, true)) { + if (!is_dir($tmp_layoutfolder) && !mkdir($tmp_layoutfolder, 0770, true)) { logger('Error creating temp export folder: ' . $tmp_layoutfolder, LOGGER_NORMAL); json_return_and_die(array('message' => 'Error creating temp export folder')); } file_put_contents($layout_filepath, $layoutinfo['body']); - file_put_contents($layout_jsonpath, json_encode($layoutinfo['json'], JSON_UNESCAPED_SLASHES)); + file_put_contents($layout_jsonpath, json_encode($layoutinfo['json'], JSON_UNESCAPED_SLASHES)); } } switch ($pp['mimetype']) { @@ -660,14 +657,14 @@ class Webpages extends Controller { $page_filepath = $tmp_pagefolder . '/' . $page_filename; $page_jsonpath = $tmp_pagefolder . '/page.json'; $pageinfo['json']['contentfile'] = $page_filename; - if (!is_dir($tmp_pagefolder) && !mkdir($tmp_pagefolder, 0770, true)) { + if (!is_dir($tmp_pagefolder) && !mkdir($tmp_pagefolder, 0770, true)) { logger('Error creating temp export folder: ' . $tmp_pagefolder, LOGGER_NORMAL); json_return_and_die(array('message' => 'Error creating temp export folder')); } file_put_contents($page_filepath, $pageinfo['body']); file_put_contents($page_jsonpath, json_encode($pageinfo['json'], JSON_UNESCAPED_SLASHES)); } - } + } } } if($action === 'zipfile') { @@ -686,23 +683,23 @@ class Webpages extends Controller { if(!$dirpath) { $x = attach_mkdirp($channel, $channel['channel_hash'], array('pathname' => $cloudpath)); $folder_hash = (($x['success']) ? $x['data']['hash'] : ''); - + if (!$x['success']) { logger('Failed to create cloud file folder', LOGGER_NORMAL); } $dirpath = get_dirpath_by_cloudpath($channel, $cloudpath); if (!is_dir($dirpath)) { logger('Failed to create cloud file folder', LOGGER_NORMAL); - } + } } - + $success = copy_folder_to_cloudfiles($channel, $channel['channel_hash'], $tmp_folderpath, $cloudpath); } } if(!$success) { logger('Error exporting webpage elements', LOGGER_NORMAL); } - + rrmdir($zip_folderpath); rrmdir($tmp_folderpath); // delete temporary files killme(); @@ -710,9 +707,9 @@ class Webpages extends Controller { default : break; } - + } - + } - + } diff --git a/Zotlabs/Module/Well_known.php b/Zotlabs/Module/Well_known.php index 0d7b222b8..af59b76e0 100644 --- a/Zotlabs/Module/Well_known.php +++ b/Zotlabs/Module/Well_known.php @@ -5,36 +5,28 @@ namespace Zotlabs\Module; class Well_known extends \Zotlabs\Web\Controller { function init(){ - + if(argc() > 1) { - + $arr = array('server' => $_SERVER, 'request' => $_REQUEST); call_hooks('well_known', $arr); - - + + if(! check_siteallowed($_SERVER['REMOTE_ADDR'])) { logger('well_known: site not allowed. ' . $_SERVER['REMOTE_ADDR']); killme(); } - + // from php.net re: REMOTE_HOST: - // Note: Your web server must be configured to create this variable. For example in Apache - // you'll need HostnameLookups On inside httpd.conf for it to exist. See also gethostbyaddr(). - + // Note: Your web server must be configured to create this variable. For example in Apache + // you'll need HostnameLookups On inside httpd.conf for it to exist. See also gethostbyaddr(). + if(get_config('system','siteallowed_remote_host') && (! check_siteallowed($_SERVER['REMOTE_HOST']))) { logger('well_known: site not allowed. ' . $_SERVER['REMOTE_HOST']); killme(); } - + switch(argv(1)) { - case 'zot-info': - \App::$argc -= 1; - array_shift(\App::$argv); - \App::$argv[0] = 'zfinger'; - $module = new \Zotlabs\Module\Zfinger(); - $module->init(); - break; - case 'webfinger': \App::$argc -= 1; array_shift(\App::$argv); @@ -42,7 +34,7 @@ class Well_known extends \Zotlabs\Web\Controller { $module = new \Zotlabs\Module\Wfinger(); $module->init(); break; - + case 'host-meta': \App::$argc -= 1; array_shift(\App::$argv); @@ -63,7 +55,7 @@ class Well_known extends \Zotlabs\Web\Controller { case 'dnt-policy.txt': echo file_get_contents('doc/dnt-policy.txt'); killme(); - + case 'caldav': case 'carddav': if ($_SERVER['REQUEST_METHOD'] == 'PROPFIND') { @@ -73,16 +65,16 @@ class Well_known extends \Zotlabs\Web\Controller { default: if(file_exists(\App::$cmd)) { - echo file_get_contents(\App::$cmd); + echo file_get_contents(\App::$cmd); killme(); } elseif(file_exists(\App::$cmd . '.php')) require_once(\App::$cmd . '.php'); break; - + } } - + http_status_exit(404); } } diff --git a/Zotlabs/Module/Wfinger.php b/Zotlabs/Module/Wfinger.php index d24a31a15..6dedc1ef1 100644 --- a/Zotlabs/Module/Wfinger.php +++ b/Zotlabs/Module/Wfinger.php @@ -1,36 +1,37 @@ <?php namespace Zotlabs\Module; -require_once('include/zot.php'); - +use Zotlabs\Lib\Keyutils; use Zotlabs\Lib\Libzot; class Wfinger extends \Zotlabs\Web\Controller { function init() { - + session_write_close(); $result = array(); - + $scheme = ''; - + if(x($_SERVER,'HTTPS') && $_SERVER['HTTPS']) $scheme = 'https'; elseif(x($_SERVER,'SERVER_PORT') && (intval($_SERVER['SERVER_PORT']) == 443)) $scheme = 'https'; - + elseif(x($_SERVER,'HTTP_X_FORWARDED_PROTO') && ($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')) + $scheme = 'https'; + $zot = intval($_REQUEST['zot']); - + if(($scheme !== 'https') && (! $zot)) { header($_SERVER["SERVER_PROTOCOL"] . ' ' . 500 . ' ' . 'Webfinger requires HTTPS'); killme(); } - - + + $resource = $_REQUEST['resource']; logger('webfinger: ' . $resource,LOGGER_DEBUG); - + $root_resource = false; $pchan = false; @@ -39,9 +40,9 @@ class Wfinger extends \Zotlabs\Web\Controller { $root_resource = true; $r = null; - + if(($resource) && (! $root_resource)) { - + if(strpos($resource,'acct:') === 0) { $channel = str_replace('acct:','',$resource); if(substr($channel,0,1) === '@' && strpos(substr($channel,1),'@')) { @@ -56,17 +57,17 @@ class Wfinger extends \Zotlabs\Web\Controller { goaway('https://' . $host . '/.well-known/webfinger?f=&resource=' . $resource . (($zot) ? '&zot=' . $zot : '')); } $channel = substr($channel,0,strpos($channel,'@')); - } + } } if(strpos($resource,'http') === 0) { $channel = str_replace('~','',basename($resource)); } - + if(substr($channel,0,1) === '[' ) { $channel = substr($channel,1); $channel = substr($channel,0,-1); $pchan = true; - $r = q("select * from pchan left join xchan on pchan_hash = xchan_hash + $r = q("select * from pchan left join xchan on pchan_hash = xchan_hash where pchan_guid = '%s' limit 1", dbesc($channel) ); @@ -74,16 +75,16 @@ class Wfinger extends \Zotlabs\Web\Controller { $r[0] = pchan_to_chan($r[0]); } } - else { - $r = q("select * from channel left join xchan on channel_hash = xchan_hash + else { + $r = q("select * from channel left join xchan on channel_hash = xchan_hash where channel_address = '%s' limit 1", dbesc($channel) ); } } - + header('Access-Control-Allow-Origin: *'); - + if($root_resource) { $result['subject'] = $resource; @@ -100,52 +101,52 @@ class Wfinger extends \Zotlabs\Web\Controller { - + } if($resource && $r) { - + $h = q("select hubloc_addr from hubloc where hubloc_hash = '%s' and hubloc_deleted = 0", dbesc($r[0]['channel_hash']) ); - + $result['subject'] = $resource; - + $aliases = array( z_root() . (($pchan) ? '/pchan/' : '/channel/') . $r[0]['channel_address'], z_root() . '/~' . $r[0]['channel_address'], z_root() . '/@' . $r[0]['channel_address'] ); - + if($h) { foreach($h as $hh) { $aliases[] = 'acct:' . $hh['hubloc_addr']; } } - + $result['aliases'] = []; - + $result['properties'] = [ 'http://webfinger.net/ns/name' => $r[0]['channel_name'], 'http://xmlns.com/foaf/0.1/name' => $r[0]['channel_name'], 'https://w3id.org/security/v1#publicKeyPem' => $r[0]['xchan_pubkey'], 'http://purl.org/zot/federation' => 'zot6,zot' ]; - - foreach($aliases as $alias) + + foreach($aliases as $alias) if($alias != $resource) $result['aliases'][] = $alias; - + if($pchan) { $result['links'] = [ - + [ 'rel' => 'http://webfinger.net/rel/avatar', 'type' => $r[0]['xchan_photo_mimetype'], 'href' => $r[0]['xchan_photo_l'] ], - + [ 'rel' => 'http://webfinger.net/rel/profile-page', 'href' => $r[0]['xchan_url'], @@ -153,7 +154,7 @@ class Wfinger extends \Zotlabs\Web\Controller { [ 'rel' => 'magic-public-key', - 'href' => 'data:application/magic-public-key,' . salmon_key($r[0]['channel_pubkey']), + 'href' => 'data:application/magic-public-key,' . Keyutils::salmonKey($r[0]['channel_pubkey']), ] ]; @@ -167,13 +168,13 @@ class Wfinger extends \Zotlabs\Web\Controller { [ 'rel' => 'http://webfinger.net/rel/avatar', 'type' => $r[0]['xchan_photo_mimetype'], - 'href' => $r[0]['xchan_photo_l'] + 'href' => $r[0]['xchan_photo_l'] ], [ 'rel' => 'http://microformats.org/profile/hcard', 'type' => 'text/html', - 'href' => z_root() . '/hcard/' . $r[0]['channel_address'] + 'href' => z_root() . '/hcard/' . $r[0]['channel_address'] ], [ @@ -187,8 +188,8 @@ class Wfinger extends \Zotlabs\Web\Controller { ], [ - 'rel' => 'http://schemas.google.com/g/2010#updates-from', - 'type' => 'application/atom+xml', + 'rel' => 'http://schemas.google.com/g/2010#updates-from', + 'type' => 'application/atom+xml', 'href' => z_root() . '/ofeed/' . $r[0]['channel_address'] ], @@ -221,7 +222,7 @@ class Wfinger extends \Zotlabs\Web\Controller { [ 'rel' => 'magic-public-key', - 'href' => 'data:application/magic-public-key,' . salmon_key($r[0]['channel_pubkey']), + 'href' => 'data:application/magic-public-key,' . Keyutils::salmonKey($r[0]['channel_pubkey']), ] ]; } @@ -236,12 +237,12 @@ class Wfinger extends \Zotlabs\Web\Controller { header($_SERVER["SERVER_PROTOCOL"] . ' ' . 400 . ' ' . 'Bad Request'); killme(); } - + $arr = [ 'channel' => $r[0], 'pchan' => $pchan, 'request' => $_REQUEST, 'result' => $result ]; call_hooks('webfinger',$arr); json_return_and_die($arr['result'],'application/jrd+json'); - + } - + } diff --git a/Zotlabs/Module/Wiki.php b/Zotlabs/Module/Wiki.php index 169dc6de1..a06119506 100644 --- a/Zotlabs/Module/Wiki.php +++ b/Zotlabs/Module/Wiki.php @@ -48,10 +48,8 @@ class Wiki extends Controller { if(! Apps::system_app_installed(App::$profile_uid, 'Wiki')) { //Do not display any associated widgets at this point App::$pdl = ''; - - $o = '<b>' . t('Wiki App') . ' (' . t('Not Installed') . '):</b><br>'; - $o .= t('Provide a wiki for your channel'); - return $o; + $papp = Apps::get_papp('Wiki'); + return Apps::app_render($papp, 'module'); } @@ -68,7 +66,7 @@ class Wiki extends Controller { $pageHistory = array(); $local_observer = null; $resource_id = ''; - + // init() should have forced the URL to redirect to /wiki/channel so assume argc() > 1 $nick = argv(1); @@ -98,9 +96,9 @@ class Wiki extends Controller { // Initialize the ACL to the channel default permissions $x = array( - 'lockstate' => (( $owner['channel_allow_cid'] || - $owner['channel_allow_gid'] || - $owner['channel_deny_cid'] || + 'lockstate' => (( $owner['channel_allow_cid'] || + $owner['channel_allow_gid'] || + $owner['channel_deny_cid'] || $owner['channel_deny_gid']) ? 'lock' : 'unlock' ), @@ -113,7 +111,7 @@ class Wiki extends Controller { ); } else { - // Not the channel owner + // Not the channel owner $owner_acl = $x = array(); } @@ -127,7 +125,6 @@ class Wiki extends Controller { $resource_id = argv(4); $w = NativeWiki::get_wiki($owner['channel_id'],$observer_hash,$resource_id); - // $w = NativeWiki::get_wiki($owner,$observer_hash,$resource_id); if(! $w['htmlName']) { notice(t('Error retrieving wiki') . EOL); @@ -218,12 +215,12 @@ class Wiki extends Controller { '$name' => t('Name'), '$type' => t('Type'), '$unlocked' => t('Any type'), - '$lockstate' => $x['lockstate'], - '$acl' => $x['acl'], - '$allow_cid' => $x['allow_cid'], - '$allow_gid' => $x['allow_gid'], - '$deny_cid' => $x['deny_cid'], - '$deny_gid' => $x['deny_gid'], + '$lockstate' => (x($x,'lockstate') ? $x['lockstate'] : ''), + '$acl' => (x($x,'acl') ? $x['acl'] : ''), + '$allow_cid' => (x($x,'allow_cid') ? $x['allow_cid'] : ''), + '$allow_gid' => (x($x,'allow_gid') ? $x['allow_gid'] : ''), + '$deny_cid' => (x($x,'deny_cid') ? $x['deny_cid'] : ''), + '$deny_gid' => (x($x,'deny_gid') ? $x['deny_gid'] : ''), '$typelock' => array('typelock', t('Lock content type'), '', '', array(t('No'), t('Yes'))), '$notify' => array('postVisible', t('Create a status post for this wiki'), '', '', array(t('No'), t('Yes'))), '$edit_wiki_name' => t('Edit Wiki Name') @@ -273,10 +270,10 @@ class Wiki extends Controller { if(! $w['resource_id']) { notice(t('Wiki not found') . EOL); goaway(z_root() . '/' . argv(0) . '/' . argv(1)); - } + } $resource_id = $w['resource_id']; - + if(! $wiki_owner) { // Check for observer permissions $observer_hash = get_observer_hash(); @@ -317,7 +314,7 @@ class Wiki extends Controller { 'channel_address' => $owner['channel_address'], 'refresh' => true ]); - //json_return_and_die(array('pages' => $page_list_html, 'message' => '', 'success' => true)); + //json_return_and_die(array('pages' => $page_list_html, 'message' => '', 'success' => true)); notice( t('Error retrieving page content') . EOL); //goaway(z_root() . '/' . argv(0) . '/' . argv(1) ); $renderedContent = NativeWikiPage::convert_links($html, argv(0) . '/' . argv(1) . '/' . NativeWiki::name_encode($wikiUrlName)); @@ -335,7 +332,7 @@ class Wiki extends Controller { $hookinfo = ['content' => $content, 'mimetype' => $mimeType]; call_hooks('wiki_preprocess',$hookinfo); $content = $hookinfo['content']; - + // Render the Markdown-formatted page content in HTML if($mimeType == 'text/bbcode') { $renderedContent = zidify_links(smilies(bbcode($content))); @@ -357,7 +354,7 @@ class Wiki extends Controller { // default: // Strip the extraneous URL components // goaway('/' . argv(0) . '/' . argv(1) . '/' . NativeWiki::name_encode($wikiUrlName) . '/' . $pageUrlName); } - + $wikiModalID = random_string(3); @@ -459,18 +456,18 @@ class Wiki extends Controller { } json_return_and_die(array('html' => $html, 'success' => true)); } - + // Create a new wiki // /wiki/channel/create/wiki if ((argc() > 3) && (argv(2) === 'create') && (argv(3) === 'wiki')) { - // Only the channel owner can create a wiki, at least until we create a + // Only the channel owner can create a wiki, at least until we create a // more detail permissions framework if (local_channel() !== intval($owner['channel_id'])) { goaway('/' . argv(0) . '/' . $nick . '/'); - } - $wiki = array(); + } + $wiki = array(); // backslashes won't work well in the javascript functions $name = str_replace('\\','',$_POST['wikiName']); @@ -479,12 +476,12 @@ class Wiki extends Controller { $wiki['postVisible'] = ((intval($_POST['postVisible'])) ? 1 : 0); $wiki['rawName'] = $name; $wiki['htmlName'] = escape_tags($name); - //$wiki['urlName'] = urlencode(urlencode($name)); + //$wiki['urlName'] = urlencode(urlencode($name)); $wiki['urlName'] = NativeWiki::name_encode($name); $wiki['mimeType'] = $_POST['mimeType']; $wiki['typelock'] = $_POST['typelock']; - if($wiki['urlName'] === '') { + if($wiki['urlName'] === '') { notice( t('Error creating wiki. Invalid name.') . EOL); goaway('/wiki'); return; //not reached @@ -508,7 +505,7 @@ class Wiki extends Controller { notice( t('Wiki created, but error creating Home page.')); goaway(z_root() . '/wiki/' . $nick . '/' . NativeWiki::name_encode($wiki['urlName'])); } - NativeWiki::sync_a_wiki_item($owner['channel_id'],$homePage['item_id'],$r['item']['resource_id']); + NativeWiki::sync_a_wiki_item($owner['channel_id'], $homePage['item_id'], $r['item']['resource_id']); goaway(z_root() . '/wiki/' . $nick . '/' . NativeWiki::name_encode($wiki['urlName']) . '/' . NativeWiki::name_encode($homePage['page']['urlName'])); } else { @@ -520,7 +517,7 @@ class Wiki extends Controller { // Update a wiki // /wiki/channel/update/wiki if ((argc() > 3) && (argv(2) === 'update') && (argv(3) === 'wiki')) { - // Only the channel owner can update a wiki, at least until we create a + // Only the channel owner can update a wiki, at least until we create a // more detail permissions framework if (local_channel() !== intval($owner['channel_id'])) { @@ -542,17 +539,16 @@ class Wiki extends Controller { } $wiki = NativeWiki::exists_by_name($owner['channel_id'], $arr['urlName']); - if($wiki['resource_id']) { $arr['resource_id'] = $wiki['resource_id']; - + $acl = new \Zotlabs\Access\AccessList($owner); $acl->set_from_array($_POST); $r = NativeWiki::update_wiki($owner['channel_id'], $observer_hash, $arr, $acl); if($r['success']) { - NativeWiki::sync_a_wiki_item($owner['channel_id'],$r['item_id'],$r['item']['resource_id']); + NativeWiki::sync_a_wiki_item($owner['channel_id'], $r['item_id'], $r['item']['resource_id']); goaway(z_root() . '/wiki/' . $nick); } else { @@ -567,18 +563,18 @@ class Wiki extends Controller { // Delete a wiki if ((argc() > 3) && (argv(2) === 'delete') && (argv(3) === 'wiki')) { - // Only the channel owner can delete a wiki, at least until we create a + // Only the channel owner can delete a wiki, at least until we create a // more detail permissions framework if (local_channel() !== intval($owner['channel_id'])) { logger('Wiki delete permission denied.'); json_return_and_die(array('message' => t('Wiki delete permission denied.'), 'success' => false)); - } - $resource_id = $_POST['resource_id']; + } + $resource_id = $_POST['resource_id']; $deleted = NativeWiki::delete_wiki($owner['channel_id'],$observer_hash,$resource_id); if ($deleted['success']) { - NativeWiki::sync_a_wiki_item($owner['channel_id'],$deleted['item_id'],$resource_id); + NativeWiki::sync_a_wiki_item($owner['channel_id'], 0, $resource_id); json_return_and_die(array('message' => '', 'success' => true)); - } + } else { logger('Error deleting wiki: ' . $resource_id . ' ' . $deleted['message']); json_return_and_die(array('message' => t('Error deleting wiki'), 'success' => false)); @@ -591,14 +587,14 @@ class Wiki extends Controller { $mimetype = $_POST['mimetype']; - $resource_id = $_POST['resource_id']; + $resource_id = $_POST['resource_id']; // Determine if observer has permission to create a page - - $perms = NativeWiki::get_permissions($resource_id, intval($owner['channel_id']), $observer_hash, $mimetype); + + $perms = NativeWiki::get_permissions($resource_id, intval($owner['channel_id']), $observer_hash); if(! $perms['write']) { logger('Wiki write permission denied. ' . EOL); - json_return_and_die(array('success' => false)); + json_return_and_die(array('success' => false)); } $name = isset($_POST['pageName']) ? $_POST['pageName'] : $_POST['missingPageName']; //Get new page name @@ -611,24 +607,23 @@ class Wiki extends Controller { } $page = NativeWikiPage::create_page($owner['channel_id'],$observer_hash, $name, $resource_id, $mimetype); - if($page['item_id']) { - $commit = NativeWikiPage::commit(array( - 'commit_msg' => t('New page created'), - 'resource_id' => $resource_id, + + $commit = NativeWikiPage::commit([ + 'commit_msg' => t('New page created'), + 'resource_id' => $resource_id, 'channel_id' => $owner['channel_id'], 'observer_hash' => $observer_hash, 'pageUrlName' => $name - )); - + ]); if($commit['success']) { - NativeWiki::sync_a_wiki_item($owner['channel_id'],$commit['item_id'],$resource_id); + NativeWiki::sync_a_wiki_item($owner['channel_id'], $commit['item_id'], $resource_id); //json_return_and_die(array('url' => '/' . argv(0) . '/' . argv(1) . '/' . urlencode($page['wiki']['urlName']) . '/' . urlencode($page['page']['urlName']), 'success' => true)); json_return_and_die(array('url' => '/' . argv(0) . '/' . argv(1) . '/' . $page['wiki']['urlName'] . '/' . $page['page']['urlName'], 'success' => true)); - } + } else { json_return_and_die(array('message' => 'Error making git commit','url' => '/' . argv(0) . '/' . argv(1) . '/' . NativeWiki::name_encode($page['wiki']['urlName']) . '/' . NativeWiki::name_encode($page['page']['urlName']),'success' => false)); - } + } } @@ -636,8 +631,8 @@ class Wiki extends Controller { logger('Error creating page'); json_return_and_die(array('message' => 'Error creating page.', 'success' => false)); } - } - + } + // Fetch page list for a wiki if((argc() === 5) && (argv(2) === 'get') && (argv(3) === 'page') && (argv(4) === 'list')) { $resource_id = $_POST['resource_id']; // resource_id for wiki in db @@ -645,7 +640,7 @@ class Wiki extends Controller { $perms = NativeWiki::get_permissions($resource_id, intval($owner['channel_id']), $observer_hash); if(!$perms['read']) { logger('Wiki read permission denied.' . EOL); - json_return_and_die(array('pages' => null, 'message' => 'Permission denied.', 'success' => false)); + json_return_and_die(array('pages' => null, 'message' => 'Permission denied.', 'success' => false)); } // @FIXME - we shouldn't invoke this if it isn't in the PDL or has been over-ridden @@ -658,17 +653,17 @@ class Wiki extends Controller { 'channel_address' => $owner['channel_address'], 'refresh' => true ]); - json_return_and_die(array('pages' => $page_list_html, 'message' => '', 'success' => true)); + json_return_and_die(array('pages' => $page_list_html, 'message' => '', 'success' => true)); } - + // Save a page if ((argc() === 4) && (argv(2) === 'save') && (argv(3) === 'page')) { - - $resource_id = $_POST['resource_id']; + + $resource_id = $_POST['resource_id']; $pageUrlName = $_POST['name']; $pageHtmlName = escape_tags($_POST['name']); $content = $_POST['content']; //Get new content - $commitMsg = $_POST['commitMsg']; + $commitMsg = $_POST['commitMsg']; if ($commitMsg === '') { $commitMsg = 'Updated ' . $pageHtmlName; } @@ -677,41 +672,46 @@ class Wiki extends Controller { $perms = NativeWiki::get_permissions($resource_id, intval($owner['channel_id']), $observer_hash); if(! $perms['write']) { logger('Wiki write permission denied. ' . EOL); - json_return_and_die(array('success' => false)); + json_return_and_die(array('success' => false)); } - $saved = NativeWikiPage::save_page(array('channel_id' => $owner['channel_id'], 'observer_hash' => $observer_hash, 'resource_id' => $resource_id, 'pageUrlName' => $pageUrlName, 'content' => $content)); - + $saved = NativeWikiPage::save_page([ + 'channel_id' => $owner['channel_id'], + 'observer_hash' => $observer_hash, + 'resource_id' => $resource_id, + 'pageUrlName' => $pageUrlName, + 'content' => $content + ]); if($saved['success']) { - $commit = NativeWikiPage::commit(array( - 'commit_msg' => $commitMsg, + + $commit = NativeWikiPage::commit([ + 'commit_msg' => $commitMsg, 'pageUrlName' => $pageUrlName, - 'resource_id' => $resource_id, + 'resource_id' => $resource_id, 'channel_id' => $owner['channel_id'], 'observer_hash' => $observer_hash, 'revision' => (-1) - )); - + ]); if($commit['success']) { - NativeWiki::sync_a_wiki_item($owner['channel_id'],$commit['item_id'],$resource_id); + NativeWiki::sync_a_wiki_item($owner['channel_id'], $commit['item_id'], $resource_id); json_return_and_die(array('message' => 'Wiki git repo commit made', 'success' => true , 'content' => $content)); } else { - json_return_and_die(array('message' => 'Error making git commit','success' => false)); + json_return_and_die(array('message' => 'Error making git commit','success' => false)); } } else { - json_return_and_die(array('message' => 'Error saving page', 'success' => false)); + json_return_and_die(array('message' => 'Error saving page', 'success' => false)); } } - + // Update page history // /wiki/channel/history/page if ((argc() === 4) && (argv(2) === 'history') && (argv(3) === 'page')) { - + $resource_id = $_POST['resource_id']; $pageUrlName = $_POST['name']; - + // Determine if observer has permission to read content $perms = NativeWiki::get_permissions($resource_id, intval($owner['channel_id']), $observer_hash); @@ -732,61 +732,73 @@ class Wiki extends Controller { // Delete a page if ((argc() === 4) && (argv(2) === 'delete') && (argv(3) === 'page')) { - $resource_id = $_POST['resource_id']; + $resource_id = $_POST['resource_id']; $pageUrlName = $_POST['name']; if ($pageUrlName === 'Home') { json_return_and_die(array('message' => t('Cannot delete Home'),'success' => false)); } + // Determine if observer has permission to delete pages // currently just allow page owner - if((! local_channel()) || (local_channel() != $owner['channel_id'])) { logger('Wiki write permission denied. ' . EOL); - json_return_and_die(array('success' => false)); + json_return_and_die(array('success' => false)); } $perms = NativeWiki::get_permissions($resource_id, intval($owner['channel_id']), $observer_hash); if(! $perms['write']) { logger('Wiki write permission denied. ' . EOL); - json_return_and_die(array('success' => false)); + json_return_and_die(array('success' => false)); } - $deleted = NativeWikiPage::delete_page(array('channel_id' => $owner['channel_id'], 'observer_hash' => $observer_hash, 'resource_id' => $resource_id, 'pageUrlName' => $pageUrlName)); + $deleted = NativeWikiPage::delete_page([ + 'channel_id' => $owner['channel_id'], + 'observer_hash' => $observer_hash, + 'resource_id' => $resource_id, + 'pageUrlName' => $pageUrlName + ]); if($deleted['success']) { - NativeWiki::sync_a_wiki_item($owner['channel_id'],$commit['item_id'],$resource_id); + NativeWiki::sync_a_wiki_item($owner['channel_id'], 0, $resource_id); json_return_and_die(array('message' => 'Wiki git repo commit made', 'success' => true)); } else { - json_return_and_die(array('message' => 'Error deleting page', 'success' => false)); + json_return_and_die(array('message' => 'Error deleting page', 'success' => false)); } } - + // Revert a page if ((argc() === 4) && (argv(2) === 'revert') && (argv(3) === 'page')) { - $resource_id = $_POST['resource_id']; + $resource_id = $_POST['resource_id']; $pageUrlName = $_POST['name']; $commitHash = $_POST['commitHash']; - // Determine if observer has permission to revert pages + // Determine if observer has permission to revert pages $perms = NativeWiki::get_permissions($resource_id, intval($owner['channel_id']), $observer_hash); if(! $perms['write']) { logger('Wiki write permission denied.' . EOL); - json_return_and_die(array('success' => false)); + json_return_and_die(array('success' => false)); } - $reverted = NativeWikiPage::revert_page(array('channel_id' => $owner['channel_id'], 'observer_hash' => $observer_hash, 'commitHash' => $commitHash, 'resource_id' => $resource_id, 'pageUrlName' => $pageUrlName)); + $reverted = NativeWikiPage::revert_page([ + 'channel_id' => $owner['channel_id'], + 'observer_hash' => $observer_hash, + 'commitHash' => $commitHash, + 'resource_id' => $resource_id, + 'pageUrlName' => $pageUrlName + ]); if($reverted['success']) { - json_return_and_die(array('content' => $reverted['content'], 'message' => '', 'success' => true)); - } else { - json_return_and_die(array('content' => '', 'message' => 'Error reverting page', 'success' => false)); + json_return_and_die(array('content' => $reverted['content'], 'message' => '', 'success' => true)); + } + else { + json_return_and_die(array('content' => '', 'message' => 'Error reverting page', 'success' => false)); } } - + // Compare page revisions if ((argc() === 4) && (argv(2) === 'compare') && (argv(3) === 'page')) { - $resource_id = $_POST['resource_id']; + $resource_id = $_POST['resource_id']; $pageUrlName = $_POST['name']; $compareCommit = $_POST['compareCommit']; $currentCommit = $_POST['currentCommit']; @@ -795,21 +807,21 @@ class Wiki extends Controller { $perms = NativeWiki::get_permissions($resource_id, intval($owner['channel_id']), $observer_hash); if(!$perms['read']) { logger('Wiki read permission denied.' . EOL); - json_return_and_die(array('success' => false)); + json_return_and_die(array('success' => false)); } $compare = NativeWikiPage::compare_page(array('channel_id' => $owner['channel_id'], 'observer_hash' => $observer_hash, 'currentCommit' => $currentCommit, 'compareCommit' => $compareCommit, 'resource_id' => $resource_id, 'pageUrlName' => $pageUrlName)); if($compare['success']) { $diffHTML = '<table class="text-center" width="100%"><tr><td class="lead" width="50%">' . t('Current Revision') . '</td><td class="lead" width="50%">' . t('Selected Revision') . '</td></tr></table>' . $compare['diff']; - json_return_and_die(array('diff' => $diffHTML, 'message' => '', 'success' => true)); + json_return_and_die(array('diff' => $diffHTML, 'message' => '', 'success' => true)); } else { - json_return_and_die(array('diff' => '', 'message' => 'Error comparing page', 'success' => false)); + json_return_and_die(array('diff' => '', 'message' => 'Error comparing page', 'success' => false)); } } - + // Rename a page if ((argc() === 4) && (argv(2) === 'rename') && (argv(3) === 'page')) { - $resource_id = $_POST['resource_id']; + $resource_id = $_POST['resource_id']; $pageUrlName = $_POST['oldName']; $pageNewName = str_replace('\\','',$_POST['newName']); if ($pageUrlName === 'Home') { @@ -823,34 +835,39 @@ class Wiki extends Controller { $perms = NativeWiki::get_permissions($resource_id, intval($owner['channel_id']), $observer_hash); if(! $perms['write']) { logger('Wiki write permission denied. ' . EOL); - json_return_and_die(array('success' => false)); + json_return_and_die(array('success' => false)); } - $renamed = NativeWikiPage::rename_page(array('channel_id' => $owner['channel_id'], 'observer_hash' => $observer_hash, 'resource_id' => $resource_id, 'pageUrlName' => $pageUrlName, 'pageNewName' => $pageNewName)); - + $renamed = NativeWikiPage::rename_page([ + 'channel_id' => $owner['channel_id'], + 'observer_hash' => $observer_hash, + 'resource_id' => $resource_id, + 'pageUrlName' => $pageUrlName, + 'pageNewName' => $pageNewName + ]); if($renamed['success']) { - $commit = NativeWikiPage::commit(array( + $commit = NativeWikiPage::commit([ 'channel_id' => $owner['channel_id'], - 'commit_msg' => 'Renamed ' . NativeWiki::name_decode($pageUrlName) . ' to ' . $renamed['page']['htmlName'], - 'resource_id' => $resource_id, + 'commit_msg' => 'Renamed ' . NativeWiki::name_decode($pageUrlName) . ' to ' . $renamed['page']['htmlName'], + 'resource_id' => $resource_id, 'observer_hash' => $observer_hash, 'pageUrlName' => $pageNewName - )); + ]); if($commit['success']) { - NativeWiki::sync_a_wiki_item($owner['channel_id'],$commit['item_id'],$resource_id); + NativeWiki::sync_a_wiki_item($owner['channel_id'], $commit['item_id'], $resource_id); json_return_and_die(array('name' => $renamed['page'], 'message' => 'Wiki git repo commit made', 'success' => true)); } else { - json_return_and_die(array('message' => 'Error making git commit','success' => false)); + json_return_and_die(array('message' => 'Error making git commit','success' => false)); } } else { - json_return_and_die(array('message' => 'Error renaming page', 'success' => false)); + json_return_and_die(array('message' => 'Error renaming page', 'success' => false)); } } //notice( t('You must be authenticated.')); json_return_and_die(array('message' => t('You must be authenticated.'), 'success' => false)); - + } } diff --git a/Zotlabs/Module/Xrd.php b/Zotlabs/Module/Xrd.php index 959e31cbe..21574eb8d 100644 --- a/Zotlabs/Module/Xrd.php +++ b/Zotlabs/Module/Xrd.php @@ -1,19 +1,21 @@ <?php namespace Zotlabs\Module; +use Zotlabs\Lib\Keyutils; + require_once('include/crypto.php'); class Xrd extends \Zotlabs\Web\Controller { function init() { - + $uri = urldecode(notags(trim($_GET['uri']))); $subject = $uri; logger('xrd: ' . $uri,LOGGER_DEBUG); - + $resource = $uri; - + if(substr($uri,0,4) === 'http') { $uri = str_replace('~','',$uri); $name = basename($uri); @@ -22,29 +24,29 @@ class Xrd extends \Zotlabs\Web\Controller { $local = str_replace('acct:', '', $uri); if(substr($local,0,2) == '//') $local = substr($local,2); - + $name = substr($local,0,strpos($local,'@')); } - + $r = q("SELECT * FROM channel WHERE channel_address = '%s' LIMIT 1", dbesc($name) ); - if(! $r) + if(! $r) killme(); - - $salmon_key = salmon_key($r[0]['channel_pubkey']); - + + $salmon_key = Keyutils::salmonKey($r[0]['channel_pubkey']); + header('Access-Control-Allow-Origin: *'); header("Content-type: application/xrd+xml"); - - + + $aliases = array('acct:' . channel_reddress($r[0]), z_root() . '/channel/' . $r[0]['channel_address'], z_root() . '/~' . $r[0]['channel_address']); - + for($x = 0; $x < count($aliases); $x ++) { if($aliases[$x] === $resource) unset($aliases[$x]); } - + $o = replace_macros(get_markup_template('xrd_person.tpl'), array( '$nick' => $r[0]['channel_address'], '$accturi' => $resource, @@ -60,14 +62,14 @@ class Xrd extends \Zotlabs\Web\Controller { '$modexp' => 'data:application/magic-public-key,' . $salmon_key, '$subscribe' => z_root() . '/follow?f=&url={uri}', )); - - + + $arr = array('user' => $r[0], 'xml' => $o); call_hooks('personal_xrd', $arr); - + echo $arr['xml']; killme(); - + } - + } diff --git a/Zotlabs/Module/Zfinger.php b/Zotlabs/Module/Zfinger.php deleted file mode 100644 index 533f0a5db..000000000 --- a/Zotlabs/Module/Zfinger.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php -namespace Zotlabs\Module; - -use Zotlabs\Web\HTTPSig; -use Zotlabs\Lib\Libzot; - -class Zfinger extends \Zotlabs\Web\Controller { - - function init() { - - require_once('include/zot.php'); - require_once('include/crypto.php'); - - $x = zotinfo($_REQUEST); - - if($x && $x['guid'] && $x['guid_sig']) { - $chan_hash = make_xchan_hash($x['guid'],$x['guid_sig']); - if($chan_hash) { - $chan = channelx_by_hash($chan_hash); - } - } - - $headers = []; - $headers['Content-Type'] = 'application/json' ; - $ret = json_encode($x); - - if($chan) { - $headers['Digest'] = HTTPSig::generate_digest_header($ret); - $h = HTTPSig::create_sig($headers,$chan['channel_prvkey'],'acct:' . channel_reddress($chan)); - HTTPSig::set_headers($h); - } - else { - foreach($headers as $k => $v) { - header($k . ': ' . $v); - } - } - - echo $ret; - killme(); - - } - -} diff --git a/Zotlabs/Module/Zotfeed.php b/Zotlabs/Module/Zotfeed.php index 8c13682b4..0b4c3c007 100644 --- a/Zotlabs/Module/Zotfeed.php +++ b/Zotlabs/Module/Zotfeed.php @@ -1,51 +1,22 @@ <?php namespace Zotlabs\Module; -require_once('include/items.php'); -require_once('include/zot.php'); - - -class Zotfeed extends \Zotlabs\Web\Controller { - - function init() { - - $result = array('success' => false); - - $mindate = (($_REQUEST['mindate']) ? datetime_convert('UTC','UTC',$_REQUEST['mindate']) : ''); - if(! $mindate) - $mindate = datetime_convert('UTC','UTC', 'now - 14 days'); - - if(observer_prohibited()) { - $result['message'] = 'Public access denied'; - json_return_and_die($result); - } - - $observer = \App::get_observer(); - - logger('observer: ' . get_observer_hash(), LOGGER_DEBUG); - - $channel_address = ((argc() > 1) ? argv(1) : ''); - if($channel_address) { - $r = q("select channel_id, channel_name from channel where channel_address = '%s' and channel_removed = 0 limit 1", - dbesc(argv(1)) - ); - } - else { - $x = get_sys_channel(); - if($x) - $r = array($x); - $mindate = datetime_convert('UTC','UTC', 'now - 14 days'); - } - if(! $r) { - $result['message'] = 'Channel not found.'; - json_return_and_die($result); - } - - logger('zotfeed request: ' . $r[0]['channel_name'], LOGGER_DEBUG); - $result['project'] = 'Hubzilla'; - $result['messages'] = zot_feed($r[0]['channel_id'],$observer['xchan_hash'],array('mindate' => $mindate)); - $result['success'] = true; - json_return_and_die($result); +use Zotlabs\Web\Controller; + +class Zotfeed extends Controller { + + function post() { + } - + + function get() { + + $outbox = new Outbox(); + return $outbox->init(); + + } + } + + + diff --git a/Zotlabs/Module/Zping.php b/Zotlabs/Module/Zping.php deleted file mode 100644 index d6128fa66..000000000 --- a/Zotlabs/Module/Zping.php +++ /dev/null @@ -1,33 +0,0 @@ -<?php -namespace Zotlabs\Module; /** @file */ - -require_once('include/zot.php'); - - -class Zping extends \Zotlabs\Web\Controller { - - function get() { - - // This is just a test utility function and may go away once we build these tools into - // the address book and directory to do dead site discovery. - - // The response packet include the current URL and key so we can discover if the server - // has been re-installed and clean up (e.g. get rid of) any old hublocs and xchans. - - // Remember to add '/post' to the url - - if(! local_channel()) - return; - - $url = $_REQUEST['url']; - - if(! $url) - return; - - - $m = zot_build_packet(\App::get_channel(),'ping'); - $r = zot_zot($url,$m); - return print_r($r,true); - - } -} diff --git a/Zotlabs/Photo/PhotoDriver.php b/Zotlabs/Photo/PhotoDriver.php index 94d2c3436..4c4f26e32 100644 --- a/Zotlabs/Photo/PhotoDriver.php +++ b/Zotlabs/Photo/PhotoDriver.php @@ -2,6 +2,8 @@ namespace Zotlabs\Photo; +use Zotlabs\Lib\Hashpath; + /** * @brief Abstract photo driver class. * @@ -505,18 +507,25 @@ abstract class PhotoDriver { * @return boolean */ public function storeThumbnail($arr, $scale = 0) { - - // We only process thumbnails here - if($scale == 0) - return false; - - $arr['imgscale'] = $scale; - - if(boolval(get_config('system','filesystem_storage_thumbnails', 0))) { - $channel = channelx_by_n($arr['uid']); + + // We only process thumbnails here + if($scale == 0) + return false; + + $arr['imgscale'] = $scale; + + if(boolval(get_config('system','photo_storage_type', 1))) { + $arr['os_storage'] = 1; - $arr['os_syspath'] = 'store/' . $channel['channel_address'] . '/' . $arr['os_path'] . '-' . $scale; - if(! $this->saveImage($arr['os_syspath'])) + + if (array_key_exists('uid', $arr) && ! in_array($scale, [ PHOTO_RES_PROFILE_300, PHOTO_RES_PROFILE_80, PHOTO_RES_PROFILE_48 ])) { + $channel = channelx_by_n($arr['uid']); + $arr['os_syspath'] = 'store/' . $channel['channel_address'] . '/' . $arr['os_path'] . '-' . $scale; + } + else + $arr['os_syspath'] = Hashpath::path($arr['resource_id'], 'store/[data]/[xchan]', 2, 1) . '-' . $scale; + + if (! $this->saveImage($arr['os_syspath'])) return false; } else diff --git a/Zotlabs/Render/Comanche.php b/Zotlabs/Render/Comanche.php index cf87cc7d7..5ce05243b 100644 --- a/Zotlabs/Render/Comanche.php +++ b/Zotlabs/Render/Comanche.php @@ -330,6 +330,8 @@ class Comanche { $name = str_replace($mtch[0], '', $name); } } + else + $var = []; if($channel_id) { $m = menu_fetch($name, $channel_id, get_observer_hash()); @@ -408,7 +410,8 @@ class Comanche { } //emit the block - $o .= (($var['wrap'] == 'none') ? '' : '<div class="' . $class . '">'); + $wrap = (! x($var, 'wrap') || $var['wrap'] == 'none' ? false : true); + $o .= ($wrap ? '' : '<div class="' . $class . '">'); if($r[0]['title'] && trim($r[0]['body']) != '$content') { $o .= '<h3>' . $r[0]['title'] . '</h3>'; @@ -421,7 +424,7 @@ class Comanche { $o .= prepare_text($r[0]['body'], $r[0]['mimetype']); } - $o .= (($var['wrap'] == 'none') ? '' : '</div>'); + $o .= ($wrap ? '' : '</div>'); } } diff --git a/Zotlabs/Render/SimpleTemplate.php b/Zotlabs/Render/SimpleTemplate.php index ff1bb5c3c..ff1bb5c3c 100755..100644 --- a/Zotlabs/Render/SimpleTemplate.php +++ b/Zotlabs/Render/SimpleTemplate.php diff --git a/Zotlabs/Render/SmartyInterface.php b/Zotlabs/Render/SmartyInterface.php index a40effecf..d80ea3f3a 100755..100644 --- a/Zotlabs/Render/SmartyInterface.php +++ b/Zotlabs/Render/SmartyInterface.php @@ -35,7 +35,7 @@ class SmartyInterface extends Smarty { $this->right_delimiter = App::get_template_rdelim('smarty3'); // Don't report errors so verbosely - $this->error_reporting = E_ALL & (~E_NOTICE); + $this->error_reporting = E_ALL & ~E_WARNING & ~E_NOTICE; } function parsed($template = '') { diff --git a/Zotlabs/Render/SmartyTemplate.php b/Zotlabs/Render/SmartyTemplate.php index 61fb72f8a..2cb96521b 100755..100644 --- a/Zotlabs/Render/SmartyTemplate.php +++ b/Zotlabs/Render/SmartyTemplate.php @@ -8,16 +8,16 @@ use App; class SmartyTemplate implements TemplateEngine { static $name ="smarty3"; - + public function __construct() { // Cannot use get_config() here because it is called during installation when there is no DB. // FIXME: this may leak private information such as system pathnames. - $basecompiledir = ((array_key_exists('smarty3_folder', App::$config['system'])) + $basecompiledir = ((array_key_exists('smarty3_folder', App::$config['system'])) ? App::$config['system']['smarty3_folder'] : ''); if (! $basecompiledir) { - $basecompiledir = str_replace('Zotlabs','',dirname(__dir__)) . "/" . TEMPLATE_BUILD_PATH; + $basecompiledir = str_replace('Zotlabs','',dirname(__dir__)) . TEMPLATE_BUILD_PATH; } if (! is_dir($basecompiledir)) { @os_mkdir(TEMPLATE_BUILD_PATH, STORAGE_DEFAULT_PERMISSIONS, true); @@ -30,7 +30,7 @@ class SmartyTemplate implements TemplateEngine { } App::$config['system']['smarty3_folder'] = $basecompiledir; } - + // TemplateEngine interface public function replace_macros($s, $r) { @@ -52,9 +52,9 @@ class SmartyTemplate implements TemplateEngine { } $s->assign($key, $value); } - return $s->parsed($template); + return $s->parsed($template); } - + public function get_markup_template($file, $root = '') { $template_file = theme_include($file, $root); if ($template_file) { @@ -62,7 +62,7 @@ class SmartyTemplate implements TemplateEngine { $template->filename = $template_file; return $template; - } + } return EMPTY_STR; } @@ -84,7 +84,7 @@ class SmartyTemplate implements TemplateEngine { $template = new SmartyInterface(); $template->filename = $template_file; return $template; - } + } return ""; } diff --git a/Zotlabs/Render/TemplateEngine.php b/Zotlabs/Render/TemplateEngine.php index 600ff913e..600ff913e 100755..100644 --- a/Zotlabs/Render/TemplateEngine.php +++ b/Zotlabs/Render/TemplateEngine.php diff --git a/Zotlabs/Storage/BasicAuth.php b/Zotlabs/Storage/BasicAuth.php index 3a48f5004..d23f3d848 100644 --- a/Zotlabs/Storage/BasicAuth.php +++ b/Zotlabs/Storage/BasicAuth.php @@ -125,10 +125,10 @@ class BasicAuth extends DAV\Auth\Backend\AbstractBasic { * Array with the values for the authenticated channel. * @return bool */ - protected function setAuthenticated($r) { - $this->channel_name = $r['channel_address']; - $this->channel_id = $r['channel_id']; - $this->channel_hash = $this->observer = $r['channel_hash']; + protected function setAuthenticated($channel) { + $this->channel_name = $channel['channel_address']; + $this->channel_id = $channel['channel_id']; + $this->channel_hash = $this->observer = $channel['channel_hash']; if ($this->observer) { $r = q("select * from xchan where xchan_hash = '%s' limit 1", @@ -139,8 +139,8 @@ class BasicAuth extends DAV\Auth\Backend\AbstractBasic { } } - $_SESSION['uid'] = $r['channel_id']; - $_SESSION['account_id'] = $r['channel_account_id']; + $_SESSION['uid'] = $channel['channel_id']; + $_SESSION['account_id'] = $channel['channel_account_id']; $_SESSION['authenticated'] = true; return true; } diff --git a/Zotlabs/Storage/Browser.php b/Zotlabs/Storage/Browser.php index fde66efcd..fdef35210 100644 --- a/Zotlabs/Storage/Browser.php +++ b/Zotlabs/Storage/Browser.php @@ -3,6 +3,7 @@ namespace Zotlabs\Storage; use Sabre\DAV; +use App; /** * @brief Provides a DAV frontend for the webbrowser. @@ -76,49 +77,82 @@ class Browser extends DAV\Browser\Plugin { * @param string $path which should be displayed */ public function generateDirectoryIndex($path) { - // (owner_id = channel_id) is visitor owner of this directory? - $is_owner = ((local_channel() && $this->auth->owner_id == local_channel()) ? true : false); - - if ($this->auth->getTimezone()) - date_default_timezone_set($this->auth->getTimezone()); require_once('include/conversation.php'); require_once('include/text.php'); - if ($this->auth->owner_nick) { - $html = ''; - } - $files = $this->server->getPropertiesForPath($path, array( - '{DAV:}displayname', - '{DAV:}resourcetype', - '{DAV:}getcontenttype', - '{DAV:}getcontentlength', - '{DAV:}getlastmodified', - ), 1); + $nick = $this->auth->owner_nick; + $channel_id = $this->auth->owner_id; + + // Is visitor owner of this directory? + $is_owner = ((local_channel() && $channel_id == local_channel()) ? true : false); + $cat = ((x($_REQUEST,'cat')) ? $_REQUEST['cat'] : ''); + + if ($this->auth->getTimezone()) { + date_default_timezone_set($this->auth->getTimezone()); + } + $files = $this->server->getPropertiesForPath($path, [], 1); $parent = $this->server->tree->getNodeForPath($path); - $parentpath = array(); - // only show parent if not leaving /cloud/; TODO how to improve this? - if ($path && $path != "cloud") { - list($parentUri) = \Sabre\Uri\split($path); - $fullPath = \Sabre\HTTP\encodePath($this->server->getBaseUri() . $parentUri); + $arr = explode('/', $parent->os_path); + end($arr); + $folder_parent = ((isset($arr[1])) ? prev($arr) : ''); + + $folder_list = attach_folder_select_list($channel_id); + + $siteroot_disabled = get_config('system', 'cloud_disable_siteroot'); + $is_root_folder = (($path === 'cloud/' . $nick) ? true : false); - $parentpath['icon'] = $this->enableAssets ? '<a href="' . $fullPath . '"><img src="' . $this->getAssetUrl('icons/parent' . $this->iconExtension) . '" width="24" alt="' . t('parent') . '"></a>' : ''; - $parentpath['path'] = $fullPath; + $parent_path = ''; + + if ($channel_id && ! $cat && !($siteroot_disabled && $is_root_folder)) { + list($parent_uri) = \Sabre\Uri\split($path); + $parent_path = \Sabre\HTTP\encodePath($this->server->getBaseUri() . $parent_uri); } - $f = array(); + $embedable_video_types = [ + 'video/mp4', + 'video/ogg', + 'video/webm' + ]; + + $embedable_audio_types = [ + 'audio/mpeg', + 'audio/wav', + 'audio/ogg', + 'audio/webm' + ]; + + $f = []; + foreach ($files as $file) { - $ft = array(); + + $ft = []; $type = null; - // This is the current directory, we can skip it - if (rtrim($file['href'], '/') == $path) continue; + $href = rtrim($file['href'], '/'); + + // This is the current directory - skip it + if ($href === $path) + continue; - list(, $name) = \Sabre\Uri\split($file['href']); + $node = $this->server->tree->getNodeForPath($href); + $data = $node->data; + $attach_hash = $data['hash']; + $folder_hash = $node->folder_hash; + + list(, $filename) = \Sabre\Uri\split($href); + + $name = isset($file[200]['{DAV:}displayname']) ? $file[200]['{DAV:}displayname'] : $filename; + $name = $this->escapeHTML($name); + + $size = isset($file[200]['{DAV:}getcontentlength']) ? (int)$file[200]['{DAV:}getcontentlength'] : ''; + + $lastmodified = ((isset($file[200]['{DAV:}getlastmodified'])) ? $file[200]['{DAV:}getlastmodified']->getTime()->format('Y-m-d H:i:s') : ''); if (isset($file[200]['{DAV:}resourcetype'])) { + $type = $file[200]['{DAV:}resourcetype']->getValue(); // resourcetype can have multiple values @@ -128,22 +162,22 @@ class Browser extends DAV\Browser\Plugin { // Some name mapping is preferred switch ($v) { case '{DAV:}collection' : - $type[$k] = t('Collection'); + $type[$k] = 'Collection'; break; case '{DAV:}principal' : - $type[$k] = t('Principal'); + $type[$k] = 'Principal'; break; case '{urn:ietf:params:xml:ns:carddav}addressbook' : - $type[$k] = t('Addressbook'); + $type[$k] = 'Addressbook'; break; case '{urn:ietf:params:xml:ns:caldav}calendar' : - $type[$k] = t('Calendar'); + $type[$k] = 'Calendar'; break; case '{urn:ietf:params:xml:ns:caldav}schedule-inbox' : - $type[$k] = t('Schedule Inbox'); + $type[$k] = 'Schedule Inbox'; break; case '{urn:ietf:params:xml:ns:caldav}schedule-outbox' : - $type[$k] = t('Schedule Outbox'); + $type[$k] = 'Schedule Outbox'; break; case '{http://calendarserver.org/ns/}calendar-proxy-read' : $type[$k] = 'Proxy-Read'; @@ -158,124 +192,173 @@ class Browser extends DAV\Browser\Plugin { // If no resourcetype was found, we attempt to use // the contenttype property - if (!$type && isset($file[200]['{DAV:}getcontenttype'])) { + if (! $type && isset($file[200]['{DAV:}getcontenttype'])) { $type = $file[200]['{DAV:}getcontenttype']; } - if (!$type) $type = t('Unknown'); - $size = isset($file[200]['{DAV:}getcontentlength']) ? (int)$file[200]['{DAV:}getcontentlength'] : ''; - $lastmodified = ((isset($file[200]['{DAV:}getlastmodified'])) ? $file[200]['{DAV:}getlastmodified']->getTime()->format('Y-m-d H:i:s') : ''); - - $fullPath = \Sabre\HTTP\encodePath('/' . trim($this->server->getBaseUri() . ($path ? $path . '/' : '') . $name, '/')); - - $displayName = isset($file[200]['{DAV:}displayname']) ? $file[200]['{DAV:}displayname'] : $name; - - $displayName = $this->escapeHTML($displayName); - $type = $this->escapeHTML($type); - - - $icon = ''; - - if ($this->enableAssets) { - $node = $this->server->tree->getNodeForPath(($path ? $path . '/' : '') . $name); - foreach (array_reverse($this->iconMap) as $class=>$iconName) { - if ($node instanceof $class) { - $icon = '<a href="' . $fullPath . '"><img src="' . $this->getAssetUrl($iconName . $this->iconExtension) . '" alt="" width="24"></a>'; - break; - } - } - } - - $parentHash = ''; - $owner = $this->auth->owner_id; - $splitPath = explode('/', $fullPath); - if (count($splitPath) > 3) { - for ($i = 3; $i < count($splitPath); $i++) { - $attachName = urldecode($splitPath[$i]); - $attachHash = $this->findAttachHash($owner, $parentHash, $attachName); - $parentHash = $attachHash; - } + if (! $type) { + $type = $data['filetype']; } + $type = $this->escapeHTML($type); - // generate preview icons for tile view. + // generate preview icons for tile view. // Currently we only handle images, but this could potentially be extended with plugins - // to provide document and video thumbnails. SVG, PDF and office documents have some + // to provide document and video thumbnails. SVG, PDF and office documents have some // security concerns and should only be allowed on single-user sites with tightly controlled - // upload access. system.thumbnail_security should be set to 1 if you want to include these - // types + // upload access. system.thumbnail_security should be set to 1 if you want to include these + // types $is_creator = false; $photo_icon = ''; $preview_style = intval(get_config('system','thumbnail_security',0)); - $r = q("select content, creator from attach where hash = '%s' and uid = %d limit 1", - dbesc($attachHash), - intval($owner) - ); - - if($r) { - $is_creator = (($r[0]['creator'] === get_observer_hash()) ? true : false); - if(file_exists(dbunescbin($r[0]['content']) . '.thumb')) { - $photo_icon = 'data:image/jpeg;base64,' . base64_encode(file_get_contents(dbunescbin($r[0]['content']) . '.thumb')); -// logger('found thumb: ' . $photo_icon); - } - } + $is_creator = (($data['creator'] === get_observer_hash()) ? true : false); - if(strpos($type,'image/') === 0 && $attachHash) { - $r = q("select resource_id, imgscale from photo where resource_id = '%s' and imgscale in ( %d, %d ) order by imgscale asc limit 1", - dbesc($attachHash), + if(strpos($type,'image/') === 0 && $attach_hash) { + $p = q("select resource_id, imgscale from photo where resource_id = '%s' and imgscale in ( %d, %d ) order by imgscale asc limit 1", + dbesc($attach_hash), intval(PHOTO_RES_320), intval(PHOTO_RES_PROFILE_80) ); - if($r) { - $photo_icon = 'photo/' . $r[0]['resource_id'] . '-' . $r[0]['imgscale']; + if($p) { + $photo_icon = 'photo/' . $p[0]['resource_id'] . '-' . $p[0]['imgscale']; } if($type === 'image/svg+xml' && $preview_style > 0) { - $photo_icon = $fullPath; + $photo_icon = $href; } } - $g = [ 'resource_id' => $attachHash, 'thumbnail' => $photo_icon, 'security' => $preview_style ]; + $g = [ 'resource_id' => $attach_hash, 'thumbnail' => $photo_icon, 'security' => $preview_style ]; call_hooks('file_thumbnail', $g); $photo_icon = $g['thumbnail']; + $lockstate = (($data['allow_cid'] || $data['allow_gid'] || $data['deny_cid'] || $data['deny_gid']) ? 'lock' : 'unlock'); + $id = $data['id']; - $attachIcon = ""; // "<a href=\"attach/".$attachHash."\" title=\"".$displayName."\"><i class=\"fa fa-arrow-circle-o-down\"></i></a>"; + if($id) { + $terms = q("select * from term where oid = %d AND otype = %d", + intval($id), + intval(TERM_OBJ_FILE) + ); + + $categories = []; + $terms_str = ''; + if($terms) { + foreach($terms as $t) { + $term = htmlspecialchars($t['term'],ENT_COMPAT,'UTF-8',false) ; + if(! trim($term)) + continue; + $categories[] = array('term' => $term, 'url' => $t['url']); + if ($terms_str) + $terms_str .= ','; + $terms_str .= $term; + } + $ft['terms'] = replace_macros(get_markup_template('item_categories.tpl'),array( + '$categories' => $categories + )); + } + } // put the array for this file together - $ft['attachId'] = $this->findAttachIdByHash($attachHash); - $ft['fileStorageUrl'] = substr($fullPath, 0, strpos($fullPath, "cloud/")) . "filestorage/" . $this->auth->owner_nick; + $ft['attach_id'] = $id; $ft['icon'] = $icon; $ft['photo_icon'] = $photo_icon; - $ft['attachIcon'] = (($size) ? $attachIcon : ''); - // @todo Should this be an item value, not a global one? - $ft['is_owner'] = $is_owner; $ft['is_creator'] = $is_creator; - $ft['fullPath'] = $fullPath; - $ft['displayName'] = $displayName; + $ft['rel_path'] = (($data) ? '/cloud/' . $nick .'/' . $data['display_path'] : $href); + $ft['full_path'] = z_root() . (($data) ? '/cloud/' . $nick .'/' . $data['display_path'] : $href); + $ft['name'] = $name; $ft['type'] = $type; $ft['size'] = $size; - $ft['sizeFormatted'] = userReadableSize($size); - $ft['lastmodified'] = (($lastmodified) ? datetime_convert('UTC', date_default_timezone_get(), $lastmodified) : ''); - $ft['iconFromType'] = getIconFromType($type); + $ft['collection'] = (($type === 'Collection') ? true : false); + $ft['size_formatted'] = userReadableSize($size); + $ft['last_modified'] = (($lastmodified) ? datetime_convert('UTC', date_default_timezone_get(), $lastmodified) : ''); + $ft['icon_from_type'] = getIconFromType($type); + + $ft['allow_cid'] = acl2json($data['allow_cid']); + $ft['allow_gid'] = acl2json($data['allow_gid']); + $ft['deny_cid'] = acl2json($data['deny_cid']); + $ft['deny_gid'] = acl2json($data['deny_gid']); + + $ft['raw_allow_cid'] = $data['allow_cid']; + $ft['raw_allow_gid'] = $data['allow_gid']; + $ft['raw_deny_cid'] = $data['deny_cid']; + $ft['raw_deny_gid'] = $data['deny_gid']; + + $ft['lockstate'] = $lockstate; + $ft['resource'] = $data['hash']; + $ft['folder'] = $data['folder']; + $ft['revision'] = $data['revision']; + $ft['newfilename'] = ['newfilename_' . $id, t('Change filename to'), $name]; + $ft['categories'] = ['categories_' . $id, t('Categories'), $terms_str]; + + // create a copy of the list which we can alter for the current resource + $folders = $folder_list; + + if($data['is_dir']) { + + $rm_path = $folders[$folder_hash]; + // can not copy a folder into itself or own child folders + foreach($folders as $k => $v) { + if(strpos($v, $rm_path) === 0) + unset($folders[$k]); + } + + } + + $ft['newfolder'] = ['newfolder_' . $id, t('Select a target location'), $data['folder'], '', $folders]; + $ft['copy'] = ['copy_' . $id, t('Copy to target location'), 0, '', [t('No'), t('Yes')]]; + $ft['recurse'] = ['recurse_' . $id, t('Set permissions for all files and sub folders'), 0, '', [t('No'), t('Yes')]]; + $ft['notify'] = ['notify_edit_' . $id, t('Notify your contacts about this file'), 0, '', [t('No'), t('Yes')]]; + + $embed_bbcode = ''; + $link_bbcode = ''; + $attach_bbcode = ''; + + if($data['is_photo']) { + $embed_bbcode = '[zmg]' . $ft['full_path'] . '[/zmg]'; + } + elseif(strpos($type, 'video') === 0 && in_array($type, $embedable_video_types)) { + $embed_bbcode = '[zvideo]' . $ft['full_path'] . '[/zvideo]'; + } + elseif(strpos($type, 'audio') === 0 && in_array($type, $embedable_audio_types)) { + $embed_bbcode = '[zaudio]' . $ft['full_path'] . '[/zaudio]'; + } + $ft['embed_bbcode'] = $embed_bbcode; + + if(! $data['is_dir']) { + $attach_bbcode = '[attachment]' . $data['hash'] . ',' . $data['revision'] . '[/attachment]'; + } + $ft['attach_bbcode'] = $attach_bbcode; + + $link_bbcode = '[zrl=' . $ft['full_path'] . ']' . $ft['name'] . '[/zrl]'; + $ft['link_bbcode'] = $link_bbcode; $f[] = $ft; } - $output = ''; if ($this->enablePost) { - $this->server->emit('onHTMLActionsPanel', array($parent, &$output, $path)); + $this->server->emit('onHTMLActionsPanel', [$parent, &$output, $path]); } $deftiles = (($is_owner) ? 0 : 1); + $tiles = ((array_key_exists('cloud_tiles',$_SESSION)) ? intval($_SESSION['cloud_tiles']) : $deftiles); $_SESSION['cloud_tiles'] = $tiles; - - $html .= replace_macros(get_markup_template('cloud.tpl'), array( - '$header' => t('Files') . ": " . $this->escapeHTML($path) . "/", + + $header = (($cat) ? t('File category') . ": " . $this->escapeHTML($cat) : t('Files')); + + $channel = channelx_by_n($channel_id); + if($channel) { + $acl = new \Zotlabs\Access\AccessList($channel); + $channel_acl = $acl->get(); + $lockstate = (($acl->is_private()) ? 'lock' : 'unlock'); + } + + $html = replace_macros(get_markup_template('cloud.tpl'), array( + '$header' => $header, '$total' => t('Total'), '$actionspanel' => $output, '$shared' => t('Shared'), @@ -283,9 +366,12 @@ class Browser extends DAV\Browser\Plugin { '$upload' => t('Add Files'), '$is_owner' => $is_owner, '$is_admin' => is_site_admin(), - '$admin_delete' => t('Admin Delete'), - '$parentpath' => $parentpath, - '$cpath' => bin2hex(\App::$query_string), + '$admin_delete_label' => t('Admin Delete'), + '$parentpath' => $parent_path, + '$folder_parent' => $folder_parent, + '$folder' => $parent->folder_hash, + '$is_root_folder' => $is_root_folder, + '$cpath' => bin2hex(App::$query_string), '$tiles' => intval($_SESSION['cloud_tiles']), '$entries' => $f, '$name' => t('Name'), @@ -293,17 +379,43 @@ class Browser extends DAV\Browser\Plugin { '$size' => t('Size'), '$lastmod' => t('Last Modified'), '$parent' => t('parent'), - '$edit' => t('Edit'), - '$delete' => t('Delete'), - '$nick' => $this->auth->getCurrentUser() + '$submit_label' => t('Submit'), + '$cancel_label' => t('Cancel'), + '$delete_label' => t('Delete'), + '$channel_id' => $channel_id, + '$cpdesc' => t('Copy/paste this code to attach file to a post'), + '$cpldesc' => t('Copy/paste this URL to link file from a web page'), + '$categories' => ['categories', t('Categories')], + '$recurse' => ['recurse', t('Set permissions for all files and sub folders'), 0, '', [t('No'), t('Yes')]], + '$newfolder' => ['newfolder', t('Select a target location'), $parent->folder_hash, '', $folder_list], + '$copy' => ['copy', t('Copy to target location'), 0, '', [t('No'), t('Yes')]], + '$return_path' => $path, + '$lockstate' => $lockstate, + '$allow_cid' => acl2json($channel_acl['allow_cid']), + '$allow_gid' => acl2json($channel_acl['allow_gid']), + '$deny_cid' => acl2json($channel_acl['deny_cid']), + '$deny_gid' => acl2json($channel_acl['deny_gid']), + '$is_owner' => $is_owner, + '$select_all_label' => t('Select All'), + '$bulk_actions_label' => t('Bulk Actions'), + '$adjust_permissions_label' => t('Adjust Permissions'), + '$move_copy_label' => t('Move or Copy'), + '$categories_label' => t('Categories'), + '$download_label' => t('Download'), + '$info_label' => t('Info'), + '$rename_label' => t('Rename'), + '$post_label' => t('Post'), + '$attach_bbcode_label' => t('Attachment BBcode'), + '$embed_bbcode_label' => t('Embed BBcode'), + '$link_bbcode_label' => t('Link BBcode'), + '$close_label' => t('Close') )); - $a = false; nav_set_selected('Files'); - \App::$page['content'] = $html; + App::$page['content'] = $html; load_pdl(); $current_theme = \Zotlabs\Render\Theme::current(); @@ -335,6 +447,7 @@ class Browser extends DAV\Browser\Plugin { // SimpleCollection, we won't need to show the panel either. if (get_class($node) === 'Sabre\\DAV\\SimpleCollection') return; + require_once('include/acl_selectors.php'); $aclselect = null; @@ -387,9 +500,38 @@ class Browser extends DAV\Browser\Plugin { $special = 'cloud/' . $this->auth->owner_nick; $count = strlen($special); + + if(strpos($path,$special) === 0) - $path = trim(substr($path,$count),'/'); + $display_path = trim(substr($path,$count),'/'); + + $breadcrumbs_html = ''; + + if($display_path && ! $_REQUEST['cat'] && ! $_SESSION['cloud_tiles']){ + $breadcrumbs = []; + $folders = explode('/', $display_path); + $folder_hashes = explode('/', $node->os_path); + $breadcrumb_path = z_root() . '/cloud/' . $this->auth->owner_nick; + + $breadcrumbs[] = [ + 'name' => $this->auth->owner_nick, + 'hash' => '', + 'path' => $breadcrumb_path + ]; + + foreach($folders as $i => $name) { + $breadcrumb_path .= '/' . $name; + $breadcrumbs[] = [ + 'name' => $name, + 'hash' => $folder_hashes[$i], + 'path' => $breadcrumb_path + ]; + } + $breadcrumbs_html = replace_macros(get_markup_template('breadcrumb.tpl'), array( + '$breadcrumbs' => $breadcrumbs + )); + } $output .= replace_macros(get_markup_template('cloud_actionspanel.tpl'), array( '$folder_header' => t('Create new folder'), @@ -404,11 +546,12 @@ class Browser extends DAV\Browser\Plugin { '$deny_cid' => acl2json($channel_acl['deny_cid']), '$deny_gid' => acl2json($channel_acl['deny_gid']), '$lockstate' => $lockstate, - '$return_url' => \App::$cmd, - '$path' => $path, - '$folder' => find_folder_hash_by_path($this->auth->owner_id, $path), + '$return_url' => $path, + '$folder' => $node->folder_hash, '$dragdroptext' => t('Drop files here to immediately upload'), - '$notify' => ['notify', t('Show in your contacts shared folder'), 0, '', [t('No'), t('Yes')]] + '$notify' => ['notify', t('Show in your contacts shared folder'), 0, '', [t('No'), t('Yes')]], + '$breadcrumbs_html' => $breadcrumbs_html, + '$drop_area_label' => t('You can select files via the upload button or drop them right here or into an existing folder.') )); } @@ -453,6 +596,21 @@ class Browser extends DAV\Browser\Plugin { return $hash; } + protected function findAttachHashFlat($owner, $attachName) { + $r = q("SELECT hash FROM attach WHERE uid = %d AND filename = '%s' ORDER BY edited DESC LIMIT 1", + intval($owner), + dbesc($attachName) + ); + $hash = ''; + if ($r) { + foreach ($r as $rr) { + $hash = $rr['hash']; + } + } + + return $hash; + } + /** * @brief Returns an attachment's id for a given hash. * diff --git a/Zotlabs/Storage/Directory.php b/Zotlabs/Storage/Directory.php index 1231dfa25..c56ffcbbb 100644 --- a/Zotlabs/Storage/Directory.php +++ b/Zotlabs/Storage/Directory.php @@ -25,7 +25,10 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo * @var string $red_path */ private $red_path; - private $folder_hash; + public $folder_hash; + public $data; + + /** * @brief The full path as seen in the browser. * /cloud + $red_path @@ -41,7 +44,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo * * @var string $os_path */ - private $os_path = ''; + public $os_path = ''; /** * @brief Sets up the directory node, expects a full path. @@ -49,7 +52,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo * @param string $ext_path a full path * @param BasicAuth &$auth_plugin */ - public function __construct($ext_path, &$auth_plugin) { + public function __construct($ext_path, $data, &$auth_plugin) { // $ext_path = urldecode($ext_path); logger('directory ' . $ext_path, LOGGER_DATA); $this->ext_path = $ext_path; @@ -61,6 +64,8 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo } $this->auth = $auth_plugin; $this->folder_hash = ''; + $this->data = $data; + $this->getDir(); if($this->auth->browser) { @@ -116,7 +121,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo $modulename = \App::$module; if ($this->red_path === '/' && $name === $modulename) { - return new Directory('/' . $modulename, $this->auth); + return new Directory('/' . $modulename, [], $this->auth); } $x = $this->FileData($this->ext_path . '/' . $name, $this->auth); @@ -269,8 +274,8 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo dbesc($f), dbesc(datetime_convert()), dbesc(datetime_convert()), - '', - '', + '', + '', dbesc($allow_cid), dbesc($allow_gid), dbesc($deny_cid), @@ -293,7 +298,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo else { $size = file_put_contents($f, $data); } - + // delete attach entry if file_put_contents() failed if ($size === false) { logger('file_put_contents() failed to ' . $f); @@ -374,7 +379,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo $args = array( 'resource_id' => $hash, 'album' => $album, 'os_syspath' => $f, 'os_path' => $xpath['os_path'], 'display_path' => $xpath['path'], 'filename' => $name, 'getimagesize' => $gis, 'directory' => $direct); $p = photo_upload($c[0], \App::get_observer(), $args); } - + \Zotlabs\Daemon\Master::Summon([ 'Thumbnail' , $hash ]); $sync = attach_export_data($c[0], $hash); @@ -402,13 +407,14 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo if ($r) { - // When initiated from DAV, set the 'force' flag on attach_mkdir(). This will cause the operation to report success even if the - // folder already exists. + // When initiated from DAV, set the 'force' flag on attach_mkdir(). This will cause the operation to report success even if the + // folder already exists. require_once('include/attach.php'); $result = attach_mkdir($r[0], $this->auth->observer, array('filename' => $name, 'folder' => $this->folder_hash, 'force' => true)); if($result['success']) { + $sync = attach_export_data($r[0],$result['data']['hash']); logger('createDirectory: attach_export_data returns $sync:' . print_r($sync, true), LOGGER_DEBUG); @@ -476,15 +482,16 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo public function moveInto($targetName,$sourcePath, DAV\INode $sourceNode) { - if(! $this->auth->owner_id) { - return false; - } + $channel_id = $this->auth->owner_id; + // Files have $sourceNode->data['hash'] set. For directories rely on $sourceNode->folder_hash. + $resource_id = ((isset($sourceNode->data['hash'])) ? $sourceNode->data['hash'] : $sourceNode->folder_hash); + $new_folder_hash = $this->folder_hash; - if(! ($sourceNode->data && $sourceNode->data->hash)) { + if(!$channel_id && !$resource_id) return false; - } - return attach_move($this->auth->owner_id, $sourceNode->data->hash, $this->folder_hash); + $ret = attach_move($channel_id, $resource_id, $new_folder_hash); + return $ret['success']; } @@ -515,6 +522,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo $file = trim($file, '/'); $path_arr = explode('/', $file); + if (! $path_arr) return; @@ -609,6 +617,9 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo $file = trim($file, '/'); $path_arr = explode('/', $file); + $cat = $_REQUEST['cat']; + + if (! $path_arr) return null; @@ -679,7 +690,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo $_SESSION['cloud_sort'] = 'name'; switch($_SESSION['cloud_sort']) { - case 'size': + case 'size': $suffix = ' order by is_dir desc, filesize asc '; break; // The following provides inconsistent results for directories because we re-calculate the date for directories based on the most recent change @@ -692,17 +703,34 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo break; } - $r = q("select $prefix id, uid, hash, filename, filetype, filesize, revision, folder, flags, is_dir, created, edited from attach where folder = '%s' and uid = %d $perms $suffix", - dbesc($folder), - intval($channel_id) - ); + if ($cat) { + $r = q("select $prefix attach.id, attach.uid, attach.hash, attach.filename, attach.is_photo, + attach.filetype, attach.filesize, attach.revision, attach.folder, attach.creator, + attach.flags, attach.is_dir, attach.created, attach.edited, attach.display_path, + attach.allow_cid, attach.allow_gid, attach.deny_cid, attach.deny_gid from attach + left join term on attach.id = term.oid + where term.term = '%s' and attach.uid = %d $perms $suffix", + dbesc($cat), + intval($channel_id) + ); + } + else { + $r = q("select $prefix attach.id, attach.uid, attach.hash, attach.filename, attach.is_photo, + attach.filetype, attach.filesize, attach.revision, attach.folder, attach.creator, + attach.flags, attach.is_dir, attach.created, attach.edited, attach.display_path, + attach.allow_cid, attach.allow_gid, attach.deny_cid, attach.deny_gid from attach + where folder = '%s' and uid = %d $perms $suffix", + dbesc($folder), + intval($channel_id) + ); + } foreach ($r as $rr) { if(\App::$module === 'cloud' && (strpos($rr['filename'],'.') === 0) && (! get_pconfig($channel_id,'system','show_dot_files')) ) continue; // @FIXME I don't think we use revisions currently in attach structures. - // In case we see any in the wild provide a unique filename. This + // In case we see any in the wild provide a unique filename. This // name may or may not be accessible if($rr['revision']) @@ -710,13 +738,12 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo //logger('filename: ' . $rr['filename'], LOGGER_DEBUG); if (intval($rr['is_dir'])) { - $ret[] = new Directory($path . '/' . $rr['filename'], $auth); + $ret[] = new Directory($path . '/' . $rr['filename'], $rr, $auth); } else { $ret[] = new File($path . '/' . $rr['filename'], $rr, $auth); } } - return $ret; } @@ -738,15 +765,14 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo return $ret; } - $r = q("SELECT channel_id, channel_address, profile.publish FROM channel left join profile on profile.uid = channel.channel_id WHERE channel_removed = 0 AND channel_system = 0 AND (channel_pageflags & %d) = 0", + $r = q("SELECT channel_id, channel_address, profile.publish FROM channel left join profile on profile.uid = channel.channel_id WHERE channel_removed = 0 AND channel_system = 0 AND (channel_pageflags & %d) = 0 and profile.is_default = 1", intval(PAGE_HIDDEN) ); - if ($r) { foreach ($r as $rr) { - if (perm_is_allowed($rr['channel_id'], $auth->observer, 'view_storage') && $rr['publish']) { + if ((perm_is_allowed($rr['channel_id'], $auth->observer, 'view_storage') && $rr['publish'])|| $rr['channel_id'] == $this->auth->channel_id) { logger('found channel: /cloud/' . $rr['channel_address'], LOGGER_DATA); - $ret[] = new Directory($rr['channel_address'], $auth); + $ret[] = new Directory($rr['channel_address'], [], $auth); } } } @@ -778,7 +804,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo } if ((! $file) || ($file === '/')) { - return new Directory('/', $auth); + return new Directory('/', [], $auth); } $file = trim($file, '/'); @@ -848,7 +874,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo if ($test) return true; // final component was a directory. - return new Directory($file, $auth); + return new Directory($file, [], $auth); } if ($errors) { @@ -867,7 +893,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo return true; if (intval($r[0]['is_dir'])) { - return new Directory($path . '/' . $r[0]['filename'], $auth); + return new Directory($path . '/' . $r[0]['filename'], [], $auth); } else { return new File($path . '/' . $r[0]['filename'], $r[0], $auth); @@ -888,7 +914,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo $used = 0; $limit = 0; $free = 0; - + if ($this->auth->owner_id) { $channel = channelx_by_n($this->auth->owner_id); if($channel) { @@ -919,5 +945,4 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo return [ (int) $used, (int) $free ]; } - } diff --git a/Zotlabs/Update/_1240.php b/Zotlabs/Update/_1240.php new file mode 100644 index 000000000..d007c9fa9 --- /dev/null +++ b/Zotlabs/Update/_1240.php @@ -0,0 +1,34 @@ +<?php + +namespace Zotlabs\Update; + +class _1240 { + + function run() { + + q("START TRANSACTION"); + + // remove broken xchan entries + $r0 = dbq("DELETE FROM xchan WHERE xchan_hash = ''"); + + // remove broken hubloc entries + $r1 = dbq("DELETE FROM hubloc WHERE hubloc_hash = ''"); + + // fix legacy zot hubloc_id_url + $r2 = dbq("UPDATE hubloc + SET hubloc_id_url = CONCAT(hubloc_url, '/channel/', SUBSTRING(hubloc_addr FROM 1 FOR POSITION('@' IN hubloc_addr) -1)) + WHERE hubloc_network = 'zot' + AND hubloc_id_url = ''" + ); + + if($r0 && $r1 && $r2) { + q("COMMIT"); + return UPDATE_SUCCESS; + } + + q("ROLLBACK"); + return UPDATE_FAILED; + + } + +} diff --git a/Zotlabs/Update/_1241.php b/Zotlabs/Update/_1241.php new file mode 100644 index 000000000..1b2024aad --- /dev/null +++ b/Zotlabs/Update/_1241.php @@ -0,0 +1,24 @@ +<?php + +namespace Zotlabs\Update; + +class _1241 { + + function run() { + + q("START TRANSACTION"); + + // remove duplicated profile photos + $r = dbq("DELETE FROM photo WHERE imgscale IN (4, 5, 6) AND photo_usage = 0"); + + if($r) { + q("COMMIT"); + return UPDATE_SUCCESS; + } + + q("ROLLBACK"); + return UPDATE_FAILED; + + } + +} diff --git a/Zotlabs/Update/_1242.php b/Zotlabs/Update/_1242.php new file mode 100644 index 000000000..c2c9a66d0 --- /dev/null +++ b/Zotlabs/Update/_1242.php @@ -0,0 +1,21 @@ +<?php + +namespace Zotlabs\Update; + +class _1242 { + + function run() { + $p = dbq("SELECT * FROM pconfig WHERE k LIKE '%password%'"); + foreach ($p as $pp) { + if ($pp['v'][0] === '{') { + $a = json_decode($pp['v'], true); + if (isset($a['encrypted'])) { + $v = crypto_unencapsulate($a, get_config('system', 'prvkey')); + set_pconfig($pp['uid'], $pp['cat'], $pp['k'], obscurify($v)); + } + } + } + return UPDATE_SUCCESS; + } + +}
\ No newline at end of file diff --git a/Zotlabs/Update/_1243.php b/Zotlabs/Update/_1243.php new file mode 100644 index 000000000..850cb1d6c --- /dev/null +++ b/Zotlabs/Update/_1243.php @@ -0,0 +1,17 @@ +<?php + +namespace Zotlabs\Update; + +class _1243 { + + function run() { + + $x = get_config('system','filesystem_storage_thumbnails'); + del_config('system','filesystem_storage_thumbnails'); + if ($x !== false) + set_config('system','photo_storage_type', intval($x)); + + return UPDATE_SUCCESS; + } + +} diff --git a/Zotlabs/Update/_1244.php b/Zotlabs/Update/_1244.php new file mode 100644 index 000000000..0aebe4013 --- /dev/null +++ b/Zotlabs/Update/_1244.php @@ -0,0 +1,15 @@ +<?php + +namespace Zotlabs\Update; + +require_once('include/account.php'); + +class _1244 { + + function run() { + + return verify_register_scheme(); + + } + +} diff --git a/Zotlabs/Update/_1245.php b/Zotlabs/Update/_1245.php new file mode 100644 index 000000000..8212fde08 --- /dev/null +++ b/Zotlabs/Update/_1245.php @@ -0,0 +1,29 @@ +<?php + +namespace Zotlabs\Update; + +class _1245 { + + function run() { + + if(ACTIVE_DBTYPE == DBTYPE_MYSQL) { + return UPDATE_SUCCESS; + } + + if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + q("START TRANSACTION"); + + $r = dbq("create index hubloc_hash on hubloc (hubloc_hash)"); + + if($r) { + q("COMMIT"); + return UPDATE_SUCCESS; + } + + q("ROLLBACK"); + return UPDATE_FAILED; + } + + } + +} diff --git a/Zotlabs/Update/_1246.php b/Zotlabs/Update/_1246.php new file mode 100644 index 000000000..3023c45dd --- /dev/null +++ b/Zotlabs/Update/_1246.php @@ -0,0 +1,24 @@ +<?php + +namespace Zotlabs\Update; + +class _1246 { + + function run() { + + q("START TRANSACTION"); + + $r1 = dbq("UPDATE xchan SET xchan_deleted = 2 WHERE xchan_network = 'zot' AND xchan_deleted = 0"); + $r2 = dbq("UPDATE hubloc SET hubloc_deleted = 2 WHERE hubloc_network = 'zot' AND hubloc_deleted = 0"); + + if($r1 && $r2) { + q("COMMIT"); + return UPDATE_SUCCESS; + } + + q("ROLLBACK"); + return UPDATE_FAILED; + + } + +} diff --git a/Zotlabs/Update/_1247.php b/Zotlabs/Update/_1247.php new file mode 100644 index 000000000..e776a52f4 --- /dev/null +++ b/Zotlabs/Update/_1247.php @@ -0,0 +1,23 @@ +<?php + +namespace Zotlabs\Update; + +class _1247 { + + function run() { + + q("START TRANSACTION"); + + $r = dbq("DELETE FROM updates WHERE ud_addr = '' OR ud_hash = '' OR ud_guid = ''"); + + if($r) { + q("COMMIT"); + return UPDATE_SUCCESS; + } + + q("ROLLBACK"); + return UPDATE_FAILED; + + } + +} diff --git a/Zotlabs/Update/_1248.php b/Zotlabs/Update/_1248.php new file mode 100644 index 000000000..b61c6ed39 --- /dev/null +++ b/Zotlabs/Update/_1248.php @@ -0,0 +1,27 @@ +<?php + +namespace Zotlabs\Update; + +class _1248 { + + function run() { + + q("START TRANSACTION"); + + // remove possible bogus entries from xconfig + $r = dbq("DELETE FROM xconfig WHERE xchan = ''"); + + // remove gnu social app - it has been moved to addons unmaintained + $r1 = dbq("DELETE FROM app WHERE app_plugin = 'gnusoc'"); + + if($r && $r1) { + q("COMMIT"); + return UPDATE_SUCCESS; + } + + q("ROLLBACK"); + return UPDATE_FAILED; + + } + +} diff --git a/Zotlabs/Web/HTTPSig.php b/Zotlabs/Web/HTTPSig.php index 792556a10..35b18c763 100644 --- a/Zotlabs/Web/HTTPSig.php +++ b/Zotlabs/Web/HTTPSig.php @@ -3,8 +3,9 @@ namespace Zotlabs\Web; use Zotlabs\Lib\ActivityStreams; +use Zotlabs\Lib\Crypto; +use Zotlabs\Lib\Keyutils; use Zotlabs\Lib\Webfinger; -use Zotlabs\Web\HTTPHeaders; use Zotlabs\Lib\Libzot; /** @@ -151,30 +152,32 @@ class HTTPSig { $result['signer'] = $sig_block['keyId']; - $key = self::get_key($key,$keytype,$result['signer']); + $cached_key = self::get_key($key,$keytype,$result['signer']); - if(! ($key && $key['public_key'])) { + if(! ($cached_key && $cached_key['public_key'])) { return $result; } - $x = rsa_verify($signed_data,$sig_block['signature'],$key['public_key'],$algorithm); + $x = Crypto::verify($signed_data,$sig_block['signature'],$cached_key['public_key'],$algorithm); logger('verified: ' . $x, LOGGER_DEBUG); + $fetched_key = ''; + if(! $x) { // try again, ignoring the local actor (xchan) cache and refetching the key // from its source - $fkey = self::get_key($key,$keytype,$result['signer'],true); + $fetched_key = self::get_key($key,$keytype,$result['signer'],true); - if ($fkey && $fkey['public_key']) { - $y = rsa_verify($signed_data,$sig_block['signature'],$fkey['public_key'],$algorithm); + if ($fetched_key && $fetched_key['public_key']) { + $y = Crypto::verify($signed_data,$sig_block['signature'],$fetched_key['public_key'],$algorithm); logger('verified: (cache reload) ' . $x, LOGGER_DEBUG); } if (! $y) { - logger('verify failed for ' . $result['signer'] . ' alg=' . $algorithm . (($fkey['public_key']) ? '' : ' no key')); + logger('verify failed for ' . $result['signer'] . ' alg=' . $algorithm . (($fetched_key['public_key']) ? '' : ' no key')); $sig_block['signature'] = base64_encode($sig_block['signature']); logger('affected sigblock: ' . print_r($sig_block,true)); logger('headers: ' . print_r($headers,true)); @@ -184,6 +187,8 @@ class HTTPSig { } + $key = (($fetched_key) ? $fetched_key : $cached_key); + $result['portable_id'] = $key['portable_id']; $result['header_valid'] = true; @@ -223,7 +228,7 @@ class HTTPSig { } if($keytype === 'zot6') { - $key = self::get_zotfinger_key($id,$force); + $key = self::get_zotfinger_key($id); if($key) { return $key; } @@ -242,13 +247,13 @@ class HTTPSig { } - function convertKey($key) { + static function convertKey($key) { - if(strstr($key,'RSA ')) { - return rsatopem($key); + if(strstr($key,'RSA ')) { + return Keyutils::rsaToPem($key); } elseif(substr($key,0,5) === 'data:') { - return convert_salmon_key($key); + return Keyutils::convertSalmonKey($key); } else { return $key; @@ -265,7 +270,7 @@ class HTTPSig { * false if no pub key found, otherwise return the pub key */ - function get_activitystreams_key($id) { + static function get_activitystreams_key($id) { // remove fragment @@ -296,7 +301,7 @@ class HTTPSig { } - function get_webfinger_key($id) { + static function get_webfinger_key($id) { $x = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_addr = '%s' or hubloc_id_url = '%s'", dbesc(str_replace('acct:','',$id)), @@ -331,7 +336,7 @@ class HTTPSig { return (($key['public_key']) ? $key : false); } - function get_zotfinger_key($id) { + static function get_zotfinger_key($id) { $x = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_addr = '%s' or hubloc_id_url = '%s' and hubloc_network = 'zot6'", dbesc(str_replace('acct:','',$id)), @@ -413,7 +418,7 @@ class HTTPSig { $headerval = 'keyId="' . $keyid . '",algorithm="' . $algorithm . '",headers="' . $x['headers'] . '",signature="' . $x['signature'] . '"'; if($encryption) { - $x = crypto_encapsulate($headerval,$encryption['key'],$encryption['algorithm']); + $x = Crypto::encapsulate($headerval,$encryption['key'],$encryption['algorithm']); if(is_array($x)) { $headerval = 'iv="' . $x['iv'] . '",key="' . $x['key'] . '",alg="' . $x['alg'] . '",data="' . $x['data'] . '"'; } @@ -453,7 +458,7 @@ class HTTPSig { foreach($headers as $h) { header($h); } - } + } } @@ -487,7 +492,7 @@ class HTTPSig { $headers = rtrim($headers,"\n"); } - $sig = base64_encode(rsa_sign($headers,$prvkey,$alg)); + $sig = base64_encode(Crypto::sign($headers,$prvkey,$alg)); $ret['headers'] = $fields; $ret['signature'] = $sig; @@ -563,7 +568,7 @@ class HTTPSig { $data = $matches[1]; if($iv && $key && $alg && $data) { - return crypto_unencapsulate([ 'encrypted' => true, 'iv' => $iv, 'key' => $key, 'alg' => $alg, 'data' => $data ] , $prvkey); + return Crypto::unencapsulate([ 'encrypted' => true, 'iv' => $iv, 'key' => $key, 'alg' => $alg, 'data' => $data ] , $prvkey); } return ''; diff --git a/Zotlabs/Web/Router.php b/Zotlabs/Web/Router.php index 96bf131b8..a6a841ccb 100644 --- a/Zotlabs/Web/Router.php +++ b/Zotlabs/Web/Router.php @@ -2,6 +2,7 @@ namespace Zotlabs\Web; +use App; use Zotlabs\Extend\Route; use Exception; @@ -43,7 +44,7 @@ class Router { */ function __construct() { - $module = \App::$module; + $module = App::$module; $modname = "Zotlabs\\Module\\" . ucfirst($module); if(strlen($module)) { @@ -60,7 +61,7 @@ class Router { include_once($route[0]); if(class_exists($modname)) { $this->controller = new $modname; - \App::$module_loaded = true; + App::$module_loaded = true; } } } @@ -68,15 +69,15 @@ class Router { // legacy plugins - this can be removed when they have all been converted - if(! (\App::$module_loaded)) { - if(is_array(\App::$plugins) && in_array($module,\App::$plugins) && file_exists("addon/{$module}/{$module}.php")) { + if(! (App::$module_loaded)) { + if(is_array(App::$plugins) && in_array($module, App::$plugins) && file_exists("addon/{$module}/{$module}.php")) { include_once("addon/{$module}/{$module}.php"); if(class_exists($modname)) { $this->controller = new $modname; - \App::$module_loaded = true; + App::$module_loaded = true; } elseif(function_exists($module . '_module')) { - \App::$module_loaded = true; + App::$module_loaded = true; } } } @@ -86,40 +87,40 @@ class Router { * Otherwise, look for the standard program module */ - if(! (\App::$module_loaded)) { + if(! (App::$module_loaded)) { try { $filename = 'Zotlabs/SiteModule/'. ucfirst($module). '.php'; if(file_exists($filename)) { // This won't be picked up by the autoloader, so load it explicitly require_once($filename); $this->controller = new $modname; - \App::$module_loaded = true; + App::$module_loaded = true; } else { $filename = 'Zotlabs/Module/'. ucfirst($module). '.php'; if(file_exists($filename)) { $this->controller = new $modname; - \App::$module_loaded = true; + App::$module_loaded = true; } } - if(! \App::$module_loaded) - throw new \Exception('Module not found'); + if(! App::$module_loaded) + throw new Exception('Module not found'); } - catch(\Exception $e) { + catch(Exception $e) { if(file_exists("mod/site/{$module}.php")) { include_once("mod/site/{$module}.php"); - \App::$module_loaded = true; + App::$module_loaded = true; } elseif(file_exists("mod/{$module}.php")) { include_once("mod/{$module}.php"); - \App::$module_loaded = true; + App::$module_loaded = true; } } } $x = [ 'module' => $module, - 'installed' => \App::$module_loaded, + 'installed' => App::$module_loaded, 'controller' => $this->controller ]; /** @@ -136,7 +137,7 @@ class Router { */ call_hooks('module_loaded', $x); if($x['installed']) { - \App::$module_loaded = true; + App::$module_loaded = true; $this->controller = $x['controller']; } @@ -144,7 +145,7 @@ class Router { * The URL provided does not resolve to a valid module. */ - if(! (\App::$module_loaded)) { + if(! (App::$module_loaded)) { // undo the setting of a letsencrypt acme-challenge rewrite rule // which blocks access to our .well-known routes. @@ -160,7 +161,7 @@ class Router { $x = [ 'module' => $module, - 'installed' => \App::$module_loaded, + 'installed' => App::$module_loaded, 'controller' => $this->controller ]; call_hooks('page_not_found',$x); @@ -181,14 +182,14 @@ class Router { header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found'); $tpl = get_markup_template('404.tpl'); - \App::$page['content'] = replace_macros($tpl, array( + App::$page['content'] = replace_macros($tpl, array( '$message' => t('Page not found.') )); // pretend this is a module so it will initialise the theme - \App::$module = '404'; - \App::$module_loaded = true; - \App::$error = true; + App::$module = '404'; + App::$module_loaded = true; + App::$error = true; } } } @@ -203,9 +204,9 @@ class Router { * Call module functions */ - if(\App::$module_loaded) { + if(App::$module_loaded) { - \App::$page['page_title'] = \App::$module; + App::$page['page_title'] = App::$module; $placeholder = ''; /* @@ -216,13 +217,13 @@ class Router { */ $arr = array('init' => true, 'replace' => false); - call_hooks(\App::$module . '_mod_init', $arr); + call_hooks(App::$module . '_mod_init', $arr); if(! $arr['replace']) { if($this->controller && method_exists($this->controller,'init')) { $this->controller->init(); } - elseif(function_exists(\App::$module . '_init')) { - $func = \App::$module . '_init'; + elseif(function_exists(App::$module . '_init')) { + $func = App::$module . '_init'; $func($a); } } @@ -258,41 +259,41 @@ class Router { $func = str_replace('-', '_', $current_theme[0]) . '_init'; $func($a); } - elseif (x(\App::$theme_info, 'extends') && file_exists('view/theme/' . \App::$theme_info['extends'] . '/php/theme.php')) { - require_once('view/theme/' . \App::$theme_info['extends'] . '/php/theme.php'); - if(function_exists(str_replace('-', '_', \App::$theme_info['extends']) . '_init')) { - $func = str_replace('-', '_', \App::$theme_info['extends']) . '_init'; + elseif (x(App::$theme_info, 'extends') && file_exists('view/theme/' . App::$theme_info['extends'] . '/php/theme.php')) { + require_once('view/theme/' . App::$theme_info['extends'] . '/php/theme.php'); + if(function_exists(str_replace('-', '_', App::$theme_info['extends']) . '_init')) { + $func = str_replace('-', '_', App::$theme_info['extends']) . '_init'; $func($a); } } - if(($_SERVER['REQUEST_METHOD'] === 'POST') && (! \App::$error) && (! x($_POST, 'auth-params'))) { - call_hooks(\App::$module . '_mod_post', $_POST); + if(($_SERVER['REQUEST_METHOD'] === 'POST') && (! App::$error) && (! x($_POST, 'auth-params'))) { + call_hooks(App::$module . '_mod_post', $_POST); if($this->controller && method_exists($this->controller,'post')) { $this->controller->post(); } - elseif(function_exists(\App::$module . '_post')) { - $func = \App::$module . '_post'; + elseif(function_exists(App::$module . '_post')) { + $func = App::$module . '_post'; $func($a); } } - if(! \App::$error) { - $arr = array('content' => \App::$page['content'], 'replace' => false); - call_hooks(\App::$module . '_mod_content', $arr); + if(! App::$error) { + $arr = array('content' => App::$page['content'], 'replace' => false); + call_hooks(App::$module . '_mod_content', $arr); if(! $arr['replace']) { if($this->controller && method_exists($this->controller,'get')) { $arr = array('content' => $this->controller->get()); } - elseif(function_exists(\App::$module . '_content')) { - $func = \App::$module . '_content'; + elseif(function_exists(App::$module . '_content')) { + $func = App::$module . '_content'; $arr = array('content' => $func($a)); } } - call_hooks(\App::$module . '_mod_aftercontent', $arr); - \App::$page['content'] = (($arr['replace']) ? $arr['content'] : \App::$page['content'] . $arr['content']); + call_hooks(App::$module . '_mod_aftercontent', $arr); + App::$page['content'] = ((isset($arr['replace'])) ? $arr['content'] : App::$page['content'] . $arr['content']); } } } diff --git a/Zotlabs/Web/Session.php b/Zotlabs/Web/Session.php index fe0a3fbf9..6f92a0319 100644 --- a/Zotlabs/Web/Session.php +++ b/Zotlabs/Web/Session.php @@ -29,34 +29,44 @@ class Session { /* * Set our session storage functions. */ - + if($this->custom_handler) { /* Custom handler (files, memached, redis..) */ $session_save_handler = strval(get_config('system', 'session_save_handler', Null)); $session_save_path = strval(get_config('system', 'session_save_path', Null)); - $session_gc_probability = intval(get_config('system', 'session_gc_probability', 1)); - $session_gc_divisor = intval(get_config('system', 'session_gc_divisor', 100)); - if(!$session_save_handler || !$session_save_path) { - logger('Session save handler or path not set.',LOGGER_NORMAL,LOG_ERR); + + if(is_null($session_save_handler) || is_null($session_save_path)) { + logger('Session save handler or path not set', LOGGER_NORMAL, LOG_ERR); } else { - ini_set('session.save_handler', $session_save_handler); - ini_set('session.save_path', $session_save_path); - ini_set('session.gc_probability', $session_gc_probability); - ini_set('session.gc_divisor', $session_gc_divisor); + // Check if custom sessions backend exists + $clsname = '\Zotlabs\Web\Session' . ucfirst(strtolower($session_save_handler)); + if (class_exists($clsname)) { + $handler = new $clsname($session_save_path); + } + else { + ini_set('session.save_handler', $session_save_handler); + ini_set('session.save_path', $session_save_path); + ini_set('session.gc_probability', intval(get_config('system', 'session_gc_probability', 1))); + ini_set('session.gc_divisor', intval(get_config('system', 'session_gc_divisor', 100))); + } } } else { - $handler = new \Zotlabs\Web\SessionHandler(); + $handler = new SessionHandler(); + } + + if (isset($handler)) { $this->handler = $handler; - $x = session_set_save_handler($handler,false); - if(! $x) - logger('Session save handler initialisation failed.',LOGGER_NORMAL,LOG_ERR); + $x = session_set_save_handler($handler, false); + if(! $x) + logger('Session save handler initialisation failed.',LOGGER_NORMAL,LOG_ERR); } + // Force cookies to be secure (https only) if this site is SSL enabled. // Must be done before session_start(). diff --git a/Zotlabs/Web/SessionRedis.php b/Zotlabs/Web/SessionRedis.php new file mode 100644 index 000000000..66eb7a02d --- /dev/null +++ b/Zotlabs/Web/SessionRedis.php @@ -0,0 +1,123 @@ +<?php
+
+namespace Zotlabs\Web;
+
+
+class SessionRedis implements \SessionHandlerInterface {
+
+ private $redis = null;
+
+
+ function __construct($connection) {
+
+ $this->redis = new \Redis();
+
+ $credentials = parse_url($connection);
+
+ try {
+ if (isset($credentials['path']))
+ $this->redis->connect($credentials['path']);
+ else {
+
+ if (isset($credentials['query']))
+ parse_str($credentials['query'], $vars);
+ else
+ $vars = [];
+
+ $this->redis->connect(
+ (isset($credentials['scheme']) ? $credentials['scheme'] . '://' : '') . $credentials['host'],
+ (isset($credentials['port']) ? $credentials['port'] : 6379),
+ (isset($vars['timeout']) ? $vars['timeout'] : 1),
+ null,
+ 0,
+ (isset($vars['read_timeout']) ? $vars['read_timeout'] : 0)
+ );
+
+ if (isset($vars['auth']))
+ $this->redis->auth($vars['auth']);
+ }
+ }
+ catch(\RedisException $ex) {
+ logger('Error connecting to Redis: ' . $ex->getMessage());
+ }
+ }
+
+
+ function open($s, $n) {
+
+ return true;
+ }
+
+ // IMPORTANT: if we read the session and it doesn't exist, create an empty record.
+ // We rely on this due to differing PHP implementation of session_regenerate_id()
+ // some which call read explicitly and some that do not. So we call it explicitly
+ // just after sid regeneration to force a record to exist.
+
+ function read($id) {
+
+ if ($id) {
+ $data = $this->redis->get($id);
+
+ if ($data)
+ return $data;
+ else
+ $this->redis->setEx($id, 300, '');
+ }
+
+ return '';
+ }
+
+
+ function write($id, $data) {
+
+ // Pretend everything is hunky-dory, even though it isn't.
+ // There probably isn't anything we can do about it in any event.
+ // See: https://stackoverflow.com/a/43636110
+
+ if(! $id || ! $data)
+ return true;
+
+
+ // Unless we authenticate somehow, only keep a session for 5 minutes
+ // The viewer can extend this by performing any web action using the
+ // original cookie, but this allows us to cleanup the hundreds or
+ // thousands of empty sessions left around from web crawlers which are
+ // assigned cookies on each page that they never use.
+
+ $expire = 300;
+
+ if($_SESSION) {
+ if(array_key_exists('remember_me',$_SESSION) && intval($_SESSION['remember_me']))
+ $expire = 60 * 60 * 24 * 365;
+ elseif(local_channel())
+ $expire = 60 * 60 * 24 * 3;
+ elseif(remote_channel())
+ $expire = 60 * 60 * 24 * 1;
+ }
+
+ $this->redis->setEx($id, $expire, $data);
+
+ return true;
+ }
+
+
+ function close() {
+
+ return true;
+ }
+
+
+ function destroy ($id) {
+
+ $this->redis->del($id);
+
+ return true;
+ }
+
+
+ function gc($expire) {
+
+ return true;
+ }
+
+}
diff --git a/Zotlabs/Web/WebServer.php b/Zotlabs/Web/WebServer.php index ac792dd69..de0d5a883 100644 --- a/Zotlabs/Web/WebServer.php +++ b/Zotlabs/Web/WebServer.php @@ -112,7 +112,7 @@ class WebServer { // now that we've been through the module content, see if the page reported // a permission problem and if so, a 403 response would seem to be in order. - if(is_array($_SESSION['sysmsg']) && stristr(implode("", $_SESSION['sysmsg']), t('Permission denied'))) { + if(isset($_SESSION['sysmsg']) && is_array($_SESSION['sysmsg']) && stristr(implode("", $_SESSION['sysmsg']), t('Permission denied'))) { header($_SERVER['SERVER_PROTOCOL'] . ' 403 ' . t('Permission denied.')); } @@ -137,9 +137,9 @@ class WebServer { private function create_channel_links() { - /* Initialise the Link: response header if this is a channel page. + /* Initialise the Link: response header if this is a channel page. * This cannot be done inside the channel module because some protocol - * addons over-ride the module functions and these links are common + * addons over-ride the module functions and these links are common * to all protocol drivers; thus doing it here avoids duplication. */ @@ -156,7 +156,7 @@ class WebServer { 'url' => z_root() . '/.well-known/webfinger?f=&resource=acct%3A' . argv(1) . '%40' . \App::get_hostname() ], ]; - $x = [ 'channel_address' => argv(1), 'channel_links' => \App::$channel_links ]; + $x = [ 'channel_address' => argv(1), 'channel_links' => \App::$channel_links ]; call_hooks('channel_links', $x ); \App::$channel_links = $x['channel_links']; header('Link: ' . \App::get_channel_links()); diff --git a/Zotlabs/Widget/Activity.php b/Zotlabs/Widget/Activity.php index 04e9fc4b1..5d9795c7e 100644 --- a/Zotlabs/Widget/Activity.php +++ b/Zotlabs/Widget/Activity.php @@ -35,7 +35,7 @@ class Activity { } } foreach($contributors as $k => $v) { - $arr[] = [ 'author_xchan' => $k, 'total' => $v ]; + $arr[] = [ 'author_xchan' => $k, 'total' => $v ]; } usort($arr,'total_sort'); xchan_query($arr); @@ -43,19 +43,19 @@ class Activity { $x = [ 'entries' => $arr ]; call_hooks('activity_widget',$x); - $arr = $x['entries']; + $arr = $x['entries']; if($arr) { $o .= '<div class="widget">'; - $o .= '<h3>' . t('Activity','widget') . '</h3><ul class="nav nav-pills flex-column">'; + $o .= '<h3>' . t('Activity','widget') . '</h3><ul class="nav rounded-pill flex-column">'; foreach($arr as $rv) { - $o .= '<li class="nav-item"><a class="nav-link" href="network?f=&xchan=' . urlencode($rv['author_xchan']) . '" ><span class="badge badge-secondary float-right">' . ((intval($rv['total'])) ? intval($rv['total']) : '') . '</span><img src="' . $rv['author']['xchan_photo_s'] . '" class="menu-img-1" /> ' . $rv['author']['xchan_name'] . '</a></li>'; + $o .= '<li class="nav-item"><a class="nav-link" href="network?f=&xchan=' . urlencode($rv['author_xchan']) . '" ><span class="badge bg-secondary float-end">' . ((intval($rv['total'])) ? intval($rv['total']) : '') . '</span><img src="' . $rv['author']['xchan_photo_s'] . '" class="menu-img-1" /> ' . $rv['author']['xchan_name'] . '</a></li>'; } $o .= '</ul></div>'; } return $o; } -} +} diff --git a/Zotlabs/Widget/Activity_filter.php b/Zotlabs/Widget/Activity_filter.php index 002a642cb..b7a69752e 100644 --- a/Zotlabs/Widget/Activity_filter.php +++ b/Zotlabs/Widget/Activity_filter.php @@ -12,10 +12,14 @@ class Activity_filter { if(! local_channel()) return ''; - $cmd = \App::$cmd; - $filter_active = false; - - $tabs = []; + $filter_active = ''; + $dm_active = ''; + $events_active = ''; + $polls_active = ''; + $starred_active = ''; + $conv_active = ''; + $tabs = []; + $cmd = \App::$cmd; if(x($_GET,'dm')) { $dm_active = (($_GET['dm'] == 1) ? 'active' : ''); @@ -64,6 +68,8 @@ class Activity_filter { ); if($groups) { + $group_active = ''; + foreach($groups as $g) { if(x($_GET,'gid')) { $group_active = (($_GET['gid'] == $g['id']) ? 'active' : ''); @@ -95,6 +101,8 @@ class Activity_filter { $channel = App::get_channel(); if($forums) { + $forum_active = ''; + foreach($forums as $f) { if(x($_GET,'pf') && x($_GET,'cid')) { $forum_active = ((x($_GET,'pf') && $_GET['cid'] == $f['abook_id']) ? 'active' : ''); @@ -103,10 +111,10 @@ class Activity_filter { $fsub[] = [ 'label' => $f['xchan_name'], 'img' => $f['xchan_photo_s'], - 'url' => (($f['private_forum']) ? $f['xchan_url'] . '/?f=&zid=' . $channel['xchan_addr'] : z_root() . '/' . $cmd . '/?f=&pf=1&cid=' . $f['abook_id']), + 'url' => ((isset($f['private_forum'])) ? $f['xchan_url'] . '/?f=&zid=' . $channel['xchan_addr'] : z_root() . '/' . $cmd . '/?f=&pf=1&cid=' . $f['abook_id']), 'sel' => $forum_active, 'title' => t('Show posts to this forum'), - 'lock' => (($f['private_forum']) ? 'lock' : '') + 'lock' => ((isset($f['private_forum'])) ? 'lock' : '') ]; } @@ -160,6 +168,8 @@ class Activity_filter { ); if($terms) { + $file_active = ''; + foreach($terms as $t) { if(x($_GET,'file')) { $file_active = (($_GET['file'] == $t['term']) ? 'active' : ''); diff --git a/Zotlabs/Widget/Appcategories.php b/Zotlabs/Widget/Appcategories.php index aebd144d0..e916f095f 100644 --- a/Zotlabs/Widget/Appcategories.php +++ b/Zotlabs/Widget/Appcategories.php @@ -40,7 +40,7 @@ class Appcategories { $terms[] = array('name' => $rr['term'], 'selected' => (($selected == $rr['term']) ? 'selected' : '')); return replace_macros(get_markup_template('categories_widget.tpl'),array( - '$title' => t('Categories'), + '$title' => t('App Categories'), '$desc' => '', '$sel_all' => (($selected == '') ? 'selected' : ''), '$all' => t('Everything'), diff --git a/Zotlabs/Widget/Appstore.php b/Zotlabs/Widget/Appstore.php index 6a00ac06a..da05c0b62 100644 --- a/Zotlabs/Widget/Appstore.php +++ b/Zotlabs/Widget/Appstore.php @@ -6,12 +6,11 @@ namespace Zotlabs\Widget; class Appstore { function widget($arr) { - $store = ((argc() > 1 && argv(1) === 'available') ? 1 : 0); - return replace_macros(get_markup_template('appstore.tpl'), [ + return replace_macros(get_markup_template('appstore.tpl'), [ '$title' => t('App Collections'), '$options' => [ - [ z_root() . '/apps', t('Installed apps'), 1 - $store ], - [ z_root() . '/apps/available', t('Available Apps'), $store ] + [z_root() . '/apps', t('Installed apps'), ((argc() == 1 && argv(0) === 'apps') ? 1 : 0)], + [z_root() . '/apps/available', t('Available Apps'), ((argc() > 1 && argv(1) === 'available') ? 1 : 0)] ] ]); } diff --git a/Zotlabs/Widget/Categories.php b/Zotlabs/Widget/Categories.php index 82c37cd0c..94ad469da 100644 --- a/Zotlabs/Widget/Categories.php +++ b/Zotlabs/Widget/Categories.php @@ -21,7 +21,9 @@ class Categories { if(($articles) && (! Apps::system_app_installed(App::$profile['profile_uid'],'Articles'))) return ''; - if((! App::$profile['profile_uid']) + $files = ((array_key_exists('files',$arr) && $arr['files']) ? true : false); + + if((! App::$profile['profile_uid']) || (! perm_is_allowed(App::$profile['profile_uid'],get_observer_hash(),(($cards || $articles) ? 'view_pages' : 'view_stream')))) { return ''; } @@ -29,12 +31,14 @@ class Categories { $cat = ((x($_REQUEST,'cat')) ? htmlspecialchars($_REQUEST['cat'],ENT_COMPAT,'UTF-8') : ''); $srchurl = (($cards) ? App::$argv[0] . '/' . App::$argv[1] : App::$query_string); $srchurl = rtrim(preg_replace('/cat\=[^\&].*?(\&|$)/is','',$srchurl),'&'); - $srchurl = str_replace(array('?f=','&f='),array('',''),$srchurl); + $srchurl = str_replace(array('?f=','&f=', '/?'),array('', '', ''),$srchurl); if($cards) return cardcategories_widget($srchurl, $cat); elseif($articles) return articlecategories_widget($srchurl, $cat); + elseif($files) + return filecategories_widget($srchurl, $cat); else return categories_widget($srchurl, $cat); diff --git a/Zotlabs/Widget/Conversations.php b/Zotlabs/Widget/Conversations.php index 267d50fa0..3dc260b50 100644 --- a/Zotlabs/Widget/Conversations.php +++ b/Zotlabs/Widget/Conversations.php @@ -9,67 +9,154 @@ class Conversations { if (! local_channel()) return; - if(argc() > 1) { + switch(argv(1)) { + case 'inbox': + $mailbox = 'inbox'; + $header = t('Received Messages'); + break; + case 'outbox': + $mailbox = 'outbox'; + $header = t('Sent Messages'); + break; + default: + $mailbox = 'combined'; + $header = t('Conversations'); + break; + } + + $o = ''; + + // private_messages_list() can do other more complicated stuff, for now keep it simple + $r = self::private_messages_list(local_channel(), $mailbox, \App::$pager['start'], \App::$pager['itemspage']); + + if(! $r) { + info( t('No messages.') . EOL); + return $o; + } + + $messages = []; + + foreach($r as $rr) { + + $selected = ((argc() == 3) ? intval(argv(2)) == intval($rr['id']) : $r[0]['id'] == $rr['id']); + + $messages[] = [ + 'mailbox' => $mailbox, + 'id' => $rr['id'], + 'from_name' => $rr['from']['xchan_name'], + 'from_url' => chanlink_hash($rr['from_xchan']), + 'from_photo' => $rr['from']['xchan_photo_s'], + 'to_name' => $rr['to']['xchan_name'], + 'to_url' => chanlink_hash($rr['to_xchan']), + 'to_photo' => $rr['to']['xchan_photo_s'], + 'subject' => (($rr['seen']) ? $rr['title'] : '<strong>' . $rr['title'] . '</strong>'), + 'delete' => t('Delete conversation'), + 'body' => $rr['body'], + 'date' => datetime_convert('UTC',date_default_timezone_get(),$rr['created'], 'c'), + 'seen' => $rr['seen'], + 'selected' => ((argv(1) != 'new') ? $selected : '') + ]; + } + + $tpl = get_markup_template('mail_head.tpl'); + $o .= replace_macros($tpl, [ + '$header' => $header, + '$messages' => $messages + ]); + + return $o; + } + + function private_messages_list($uid, $mailbox = '', $start = 0, $numitems = 0) { + + $where = ''; + $limit = ''; + + $t0 = dba_timer(); + + if($numitems) + $limit = " LIMIT " . intval($numitems) . " OFFSET " . intval($start); + + if($mailbox !== '') { + $x = q("select channel_hash from channel where channel_id = %d limit 1", + intval($uid) + ); + + if(! $x) + return array(); + + $channel_hash = dbesc($x[0]['channel_hash']); + $local_channel = intval(local_channel()); + + switch($mailbox) { - switch(argv(1)) { case 'inbox': - $mailbox = 'inbox'; - $header = t('Received Messages'); + $sql = "SELECT * FROM mail WHERE channel_id = $local_channel AND from_xchan != '$channel_hash' ORDER BY created DESC $limit"; break; + case 'outbox': - $mailbox = 'outbox'; - $header = t('Sent Messages'); + $sql = "SELECT * FROM mail WHERE channel_id = $local_channel AND from_xchan = '$channel_hash' ORDER BY created DESC $limit"; break; + + case 'combined': default: - $mailbox = 'combined'; - $header = t('Conversations'); + $parents = q("SELECT mail.parent_mid FROM mail LEFT JOIN conv ON mail.conv_guid = conv.guid WHERE mail.mid = mail.parent_mid AND mail.channel_id = %d ORDER BY conv.updated DESC $limit", + intval($local_channel) + ); break; } - require_once('include/message.php'); + } - $o = ''; + $r = null; - // private_messages_list() can do other more complicated stuff, for now keep it simple - $r = private_messages_list(local_channel(), $mailbox, \App::$pager['start'], \App::$pager['itemspage']); + if($parents) { + foreach($parents as $parent) { + $all = q("SELECT * FROM mail WHERE parent_mid = '%s' AND channel_id = %d ORDER BY created DESC limit 1", + dbesc($parent['parent_mid']), + intval($local_channel) + ); - if(! $r) { - info( t('No messages.') . EOL); - return $o; + if($all) { + foreach($all as $single) { + $r[] = $single; + } + } } + } + elseif($sql) { + $r = q($sql); + } - $messages = []; - - foreach($r as $rr) { - - $selected = ((argc() == 3) ? intval(argv(2)) == intval($rr['id']) : $r[0]['id'] == $rr['id']); - - $messages[] = [ - 'mailbox' => $mailbox, - 'id' => $rr['id'], - 'from_name' => $rr['from']['xchan_name'], - 'from_url' => chanlink_hash($rr['from_xchan']), - 'from_photo' => $rr['from']['xchan_photo_s'], - 'to_name' => $rr['to']['xchan_name'], - 'to_url' => chanlink_hash($rr['to_xchan']), - 'to_photo' => $rr['to']['xchan_photo_s'], - 'subject' => (($rr['seen']) ? $rr['title'] : '<strong>' . $rr['title'] . '</strong>'), - 'delete' => t('Delete conversation'), - 'body' => $rr['body'], - 'date' => datetime_convert('UTC',date_default_timezone_get(),$rr['created'], 'c'), - 'seen' => $rr['seen'], - 'selected' => ((argv(1) != 'new') ? $selected : '') - ]; - } + if(! $r) { + return array(); + } - $tpl = get_markup_template('mail_head.tpl'); - $o .= replace_macros($tpl, [ - '$header' => $header, - '$messages' => $messages - ]); + $chans = array(); + foreach($r as $rr) { + $s = "'" . dbesc(trim($rr['from_xchan'])) . "'"; + if(! in_array($s,$chans)) + $chans[] = $s; + $s = "'" . dbesc(trim($rr['to_xchan'])) . "'"; + if(! in_array($s,$chans)) + $chans[] = $s; + } + $c = q("select * from xchan where xchan_hash in (" . protect_sprintf(implode(',',$chans)) . ")"); + + foreach($r as $k => $rr) { + $r[$k]['from'] = find_xchan_in_array($rr['from_xchan'],$c); + $r[$k]['to'] = find_xchan_in_array($rr['to_xchan'],$c); + $r[$k]['seen'] = intval($rr['mail_seen']); + if(intval($r[$k]['mail_obscured'])) { + if($r[$k]['title']) + $r[$k]['title'] = base64url_decode(str_rot47($r[$k]['title'])); + if($r[$k]['body']) + $r[$k]['body'] = base64url_decode(str_rot47($r[$k]['body'])); + } } - return $o; + + return $r; } } diff --git a/Zotlabs/Widget/Cover_photo.php b/Zotlabs/Widget/Cover_photo.php index 955048992..97323ea8c 100644 --- a/Zotlabs/Widget/Cover_photo.php +++ b/Zotlabs/Widget/Cover_photo.php @@ -9,7 +9,7 @@ class Cover_photo { require_once('include/channel.php'); $o = ''; - if(\App::$module == 'channel' && $_REQUEST['mid']) + if(\App::$module == 'channel' && isset($_REQUEST['mid'])) return ''; $channel_id = 0; diff --git a/Zotlabs/Widget/Dirsort.php b/Zotlabs/Widget/Dirsort.php index e75a00e50..2fb38b7df 100644 --- a/Zotlabs/Widget/Dirsort.php +++ b/Zotlabs/Widget/Dirsort.php @@ -2,10 +2,10 @@ namespace Zotlabs\Widget; -require_once('include/dir_fns.php'); +use Zotlabs\Lib\Libzotdir; class Dirsort { function widget($arr) { - return dir_sort_links(); + return Libzotdir::dir_sort_links(); } } diff --git a/Zotlabs/Widget/Dirtags.php b/Zotlabs/Widget/Dirtags.php index f211d5942..246c47dde 100644 --- a/Zotlabs/Widget/Dirtags.php +++ b/Zotlabs/Widget/Dirtags.php @@ -2,8 +2,6 @@ namespace Zotlabs\Widget; -require_once('include/dir_fns.php'); - class Dirtags { function widget($arr) { diff --git a/Zotlabs/Widget/Forums.php b/Zotlabs/Widget/Forums.php index d3e2f2534..2af7347f1 100644 --- a/Zotlabs/Widget/Forums.php +++ b/Zotlabs/Widget/Forums.php @@ -34,7 +34,7 @@ class Forums { intval(local_channel()) ); - if($x2) { + if($x2) { $xf = ids_to_querystr($x2,'xchan',true); // private forums @@ -47,7 +47,7 @@ class Forums { } } - $sql_extra = (($xf) ? " and ( xchan_hash in (" . $xf . ") or xchan_pubforum = 1 ) " : " and xchan_pubforum = 1 "); + $sql_extra = (($xf) ? " and ( xchan_hash in (" . $xf . ") or xchan_pubforum = 1 ) " : " and xchan_pubforum = 1 "); @@ -64,7 +64,7 @@ class Forums { // There also should be a way to update this via ajax. for($x = 0; $x < count($r1); $x ++) { - $r = q("select sum(item_unseen) as unseen from item + $r = q("select sum(item_unseen) as unseen from item where uid = %d and owner_xchan = '%s' and item_unseen = 1 $perms_sql ", intval(local_channel()), dbesc($r1[$x]['xchan_hash']) @@ -109,12 +109,12 @@ class Forums { } } } - + if($unseen && (! intval($rr['unseen']))) continue; - $o .= '<li class="nav-item"><a class="nav-link" href="' . $link . '" ><span class="badge badge-secondary float-right">' . ((intval($rr['unseen'])) ? intval($rr['unseen']) : '') . '</span><img class ="menu-img-1" src="' . $rr['xchan_photo_s'] . '" /> ' . $rr['xchan_name'] . '</a></li>'; + $o .= '<li class="nav-item"><a class="nav-link" href="' . $link . '" ><span class="badge bg-secondary float-end">' . ((intval($rr['unseen'])) ? intval($rr['unseen']) : '') . '</span><img class ="menu-img-1" src="' . $rr['xchan_photo_s'] . '" /> ' . $rr['xchan_name'] . '</a></li>'; } $o .= '</ul></div>'; } diff --git a/Zotlabs/Widget/Hq_controls.php b/Zotlabs/Widget/Hq_controls.php index 0caa54a1a..91335fd76 100644 --- a/Zotlabs/Widget/Hq_controls.php +++ b/Zotlabs/Widget/Hq_controls.php @@ -2,24 +2,43 @@ namespace Zotlabs\Widget; +use Zotlabs\Lib\Apps; + + class Hq_controls { - function widget($arr) { + function widget($options) { if (! local_channel()) return; + $entries = [ + 'toggle_editor' => [ + 'label' => t('Toggle post editor'), + 'href' => '#', + 'class' => 'btn jot-toggle', + 'type' => 'button', + 'icon' => 'pencil', + 'extra' => 'data-toggle="button"' + ] + ]; + + if(Apps::system_app_installed(local_channel(), 'Notes')) { + $entries['toggle_notes'] = [ + 'label' => t('Toggle personal notes'), + 'href' => '#', + 'class' => 'btn notes-toggle', + 'type' => 'button', + 'icon' => 'sticky-note-o', + 'extra' => 'data-toggle="button"' + ]; + } + return replace_macros(get_markup_template('hq_controls.tpl'), [ - '$title' => t('HQ Control Panel'), - '$menu' => [ - 'create' => [ - 'label' => t('Create a new post'), - 'id' => 'jot-toggle', - 'href' => '#', - 'class' => '' - ] - ] + '$entries' => $entries, + '$wrapper_class' => $options['wrapper_class'], + '$entry_class' => $options['entry_class'] ] ); } diff --git a/Zotlabs/Widget/Mailmenu.php b/Zotlabs/Widget/Mailmenu.php index 512f7d9c0..ca022c807 100644 --- a/Zotlabs/Widget/Mailmenu.php +++ b/Zotlabs/Widget/Mailmenu.php @@ -14,7 +14,7 @@ class Mailmenu { '$combined' => array( 'label' => t('Combined View'), 'url' => z_root() . '/mail/combined', - 'sel' => (argv(1) == 'combined'), + 'sel' => (argv(1) == 'combined' || argc() == 1), ), '$inbox' => array( 'label' => t('Inbox'), @@ -26,11 +26,13 @@ class Mailmenu { 'url' => z_root() . '/mail/outbox', 'sel' => (argv(1) == 'outbox'), ), +/* '$new' => array( 'label' => t('New Message'), 'url' => z_root() . '/mail/new', 'sel'=> (argv(1) == 'new'), ) +*/ )); } } diff --git a/Zotlabs/Widget/Messages.php b/Zotlabs/Widget/Messages.php new file mode 100644 index 000000000..eb3a07da1 --- /dev/null +++ b/Zotlabs/Widget/Messages.php @@ -0,0 +1,236 @@ +<?php + +namespace Zotlabs\Widget; + +use App; +use Zotlabs\Lib\IConfig; + +class Messages { + + public static function widget($arr) { + if (!local_channel()) + return EMPTY_STR; + + $page = self::get_messages_page([]); + + $_SESSION['messages_loadtime'] = datetime_convert(); + + $tpl = get_markup_template('messages_widget.tpl'); + $o = replace_macros($tpl, [ + '$entries' => $page['entries'], + '$offset' => $page['offset'], + '$feature_star' => feature_enabled(local_channel(), 'star_posts'), + '$strings' => [ + 'messages_title' => t('Public and restricted messages'), + 'direct_messages_title' => t('Direct messages'), + 'starred_messages_title' => t('Starred messages'), + 'notice_messages_title' => t('Notices'), + 'loading' => t('Loading'), + 'empty' => t('No messages') + ] + ]); + + return $o; + } + + public static function get_messages_page($options) { + if (!local_channel()) + return; + + if ($options['offset'] == -1) { + return; + } + + if ($options['type'] == 'notification') { + return self::get_notices_page($options); + } + + $channel = App::get_channel(); + $item_normal = item_normal(); + $entries = []; + $limit = 30; + $dummy_order_sql = ''; + + $offset = 0; + if ($options['offset']) { + $offset = intval($options['offset']); + } + + $loadtime = (($offset) ? $_SESSION['messages_loadtime'] : datetime_convert()); + + switch($options['type']) { + case 'direct': + $type_sql = ' AND item_private = 2 '; + // $dummy_order_sql has no other meaning but to trick + // some mysql backends into using the right index. + $dummy_order_sql = ', received DESC '; + break; + case 'starred': + $type_sql = ' AND item_starred = 1 '; + break; + default: + $type_sql = ' AND item_private IN (0, 1) '; + } + + $items = q("SELECT * FROM item WHERE uid = %d + AND created <= '%s' + $type_sql + AND item_thread_top = 1 + $item_normal + ORDER BY created DESC $dummy_order_sql + LIMIT $limit OFFSET $offset", + intval(local_channel()), + dbescdate($loadtime) + ); + + xchan_query($items, false); + + $i = 0; + $entries = []; + + foreach($items as $item) { + + $info = ''; + if ($options['type'] == 'direct') { + $info .= self::get_dm_recipients($channel, $item); + } + + if($item['owner_xchan'] !== $item['author_xchan']) { + $info .= t('via') . ' ' . $item['owner']['xchan_name']; + } + + $summary = $item['title']; + if (!$summary) { + $summary = $item['summary']; + } + if (!$summary) { + $summary = htmlentities(html2plain(bbcode($item['body'], ['drop_media' => true]), 75, true), ENT_QUOTES, 'UTF-8', false); + } + if (!$summary) { + $summary = '...'; + } + $summary = substr_words($summary, 68); + + switch(intval($item['item_private'])) { + case 1: + $icon = '<i class="fa fa-lock"></i>'; + break; + case 2: + $icon = '<i class="fa fa-envelope-o"></i>'; + break; + default: + $icon = ''; + } + + $entries[$i]['author_name'] = $item['author']['xchan_name']; + $entries[$i]['author_addr'] = (($item['author']['xchan_addr']) ? $item['author']['xchan_addr'] : $item['author']['xchan_url']); + $entries[$i]['info'] = $info; + $entries[$i]['created'] = datetime_convert('UTC', date_default_timezone_get(), $item['created']); + $entries[$i]['summary'] = $summary; + $entries[$i]['b64mid'] = gen_link_id($item['mid']); + $entries[$i]['href'] = z_root() . '/hq/' . gen_link_id($item['mid']); + $entries[$i]['icon'] = $icon; + + $i++; + } + + $result = [ + 'offset' => ((count($entries) < $limit) ? -1 : intval($offset + $limit)), + 'entries' => $entries + ]; + + return $result; + } + + public static function get_dm_recipients($channel, $item) { + + if($channel['channel_hash'] === $item['owner']['xchan_hash']) { + // we are the owner, get the recipients from the item + $recips = expand_acl($item['allow_cid']); + if (is_array($recips)) { + array_unshift($recips, $item['owner']['xchan_hash']); + $column = 'xchan_hash'; + } + } + else { + $recips = IConfig::Get($item, 'activitypub', 'recips'); + if (isset($recips['to']) && is_array($recips['to'])) { + $recips = $recips['to']; + array_unshift($recips, $item['owner']['xchan_url']); + $column = 'xchan_url'; + } + else { + $hookinfo = [ + 'item' => $item, + 'recips' => null, + 'column' => '' + ]; + + call_hooks('direct_message_recipients', $hookinfo); + + $recips = $hookinfo['recips']; + $column = $hookinfo['column']; + } + } + + if(is_array($recips)) { + stringify_array_elms($recips, true); + + $query_str = implode(',', $recips); + $xchans = dbq("SELECT DISTINCT xchan_name FROM xchan WHERE $column IN ($query_str) AND xchan_deleted = 0"); + foreach($xchans as $xchan) { + $recipients .= $xchan['xchan_name'] . ', '; + } + } + + return trim($recipients, ', '); + } + + public static function get_notices_page($options) { + + if (!local_channel()) + return; + + $limit = 30; + + $offset = 0; + if ($options['offset']) { + $offset = intval($options['offset']); + } + + $notices = q("SELECT * FROM notify WHERE uid = %d + ORDER BY created DESC LIMIT $limit OFFSET $offset", + intval(local_channel()) + ); + + $i = 0; + $entries = []; + + foreach($notices as $notice) { + + $summary = trim(strip_tags(bbcode($notice['msg']))); + + if(strpos($summary, $notice['xname']) === 0) { + $summary = substr($summary, strlen($notice['xname']) + 1); + } + + $entries[$i]['author_name'] = $notice['xname']; + $entries[$i]['author_addr'] = $notice['url']; + $entries[$i]['info'] = ''; + $entries[$i]['created'] = datetime_convert('UTC', date_default_timezone_get(), $notice['created']); + $entries[$i]['summary'] = $summary; + $entries[$i]['b64mid'] = basename($notice['link']); + $entries[$i]['href'] = z_root() . '/hq/' . basename($notice['link']); + $entries[$i]['icon'] = ''; + + $i++; + } + + $result = [ + 'offset' => ((count($entries) < $limit) ? -1 : intval($offset + $limit)), + 'entries' => $entries + ]; + + return $result; + } +} diff --git a/Zotlabs/Widget/Notes.php b/Zotlabs/Widget/Notes.php index 238008d81..05c1a0292 100644 --- a/Zotlabs/Widget/Notes.php +++ b/Zotlabs/Widget/Notes.php @@ -21,7 +21,8 @@ class Notes { '$banner' => t('Notes'), '$text' => $text, '$save' => t('Save'), - '$app' => ((isset($arr['app'])) ? true : false) + '$app' => ((isset($arr['app'])) ? true : false), + '$hidden' => ((isset($arr['hidden'])) ? true : false) )); return $o; diff --git a/Zotlabs/Widget/Notifications.php b/Zotlabs/Widget/Notifications.php index e2a543f80..a818ae40a 100644 --- a/Zotlabs/Widget/Notifications.php +++ b/Zotlabs/Widget/Notifications.php @@ -13,11 +13,11 @@ class Notifications { 'type' => 'network', 'icon' => 'th', 'severity' => 'secondary', - 'label' => t('New Network Activity'), - 'title' => t('New Network Activity Notifications'), + 'label' => t('Network'), + 'title' => t('New network activity notifications'), 'viewall' => [ 'url' => 'network', - 'label' => t('View your network activity') + 'label' => t('Network stream') ], 'markall' => [ 'label' => t('Mark all notifications read') @@ -33,11 +33,11 @@ class Notifications { 'type' => 'home', 'icon' => 'home', 'severity' => 'danger', - 'label' => t('New Home Activity'), - 'title' => t('New Home Activity Notifications'), + 'label' => t('Home'), + 'title' => t('New home activity notifications'), 'viewall' => [ 'url' => 'channel/' . $channel['channel_address'], - 'label' => t('View your home activity') + 'label' => t('Home stream') ], 'markall' => [ 'label' => t('Mark all notifications seen') @@ -52,11 +52,11 @@ class Notifications { 'type' => 'dm', 'icon' => 'envelope', 'severity' => 'danger', - 'label' => t('New Direct Messages'), - 'title' => t('New Direct Messages Notifications'), + 'label' => t('Direct Messages'), + 'title' => t('New direct messages notifications'), 'viewall' => [ 'url' => 'network/?dm=1', - 'label' => t('View your direct messages') + 'label' => t('Direct messages stream') ], 'markall' => [ 'label' => t('Mark all notifications read') @@ -68,26 +68,11 @@ class Notifications { ]; $notifications[] = [ - 'type' => 'mail', - 'icon' => 'envelope', - 'severity' => 'danger', - 'label' => t('New Mails'), - 'title' => t('New Mails Notifications'), - 'viewall' => [ - 'url' => 'mail/combined', - 'label' => t('View your private mails') - ], - 'markall' => [ - 'label' => t('Mark all messages seen') - ] - ]; - - $notifications[] = [ 'type' => 'all_events', 'icon' => 'calendar', 'severity' => 'secondary', - 'label' => t('New Events'), - 'title' => t('New Events Notifications'), + 'label' => t('Events'), + 'title' => t('New events notifications'), 'viewall' => [ 'url' => 'cdav/calendar', 'label' => t('View events') @@ -102,7 +87,7 @@ class Notifications { 'icon' => 'users', 'severity' => 'danger', 'label' => t('New Connections'), - 'title' => t('New Connections Notifications'), + 'title' => t('New connections notifications'), 'viewall' => [ 'url' => 'connections', 'label' => t('View all connections') @@ -113,8 +98,8 @@ class Notifications { 'type' => 'files', 'icon' => 'folder', 'severity' => 'danger', - 'label' => t('New Files'), - 'title' => t('New Files Notifications'), + 'label' => t('Files'), + 'title' => t('New files notifications'), ]; $notifications[] = [ @@ -149,8 +134,8 @@ class Notifications { 'type' => 'register', 'icon' => 'user-o', 'severity' => 'danger', - 'label' => t('New Registrations'), - 'title' => t('New Registrations Notifications'), + 'label' => t('Registrations'), + 'title' => t('New registrations notifications'), ]; } @@ -160,10 +145,10 @@ class Notifications { 'icon' => 'globe', 'severity' => 'secondary', 'label' => t('Public Stream'), - 'title' => t('Public Stream Notifications'), + 'title' => t('New public stream notifications'), 'viewall' => [ 'url' => 'pubstream', - 'label' => t('View the public stream') + 'label' => t('Public stream') ], 'markall' => [ 'label' => t('Mark all notifications seen') @@ -175,16 +160,14 @@ class Notifications { ]; } - $o = replace_macros(get_markup_template('notifications_widget.tpl'), array( - '$module' => \App::$module, + $o = replace_macros(get_markup_template('notifications_widget.tpl'), [ '$notifications' => $notifications, '$no_notifications' => t('Sorry, you have got no notifications at the moment'), '$loading' => t('Loading'), - '$startpage' => $channel['channel_startpage'] - )); + ]); return $o; } } - + diff --git a/Zotlabs/Widget/Pinned.php b/Zotlabs/Widget/Pinned.php index 0a7806908..66d06bbd3 100644 --- a/Zotlabs/Widget/Pinned.php +++ b/Zotlabs/Widget/Pinned.php @@ -40,15 +40,15 @@ class Pinned { $observer = \App::get_observer(); foreach($items as $item) { - - $midb64 = 'b64.' . base64url_encode($item['mid']); - - if(in_array($observer['xchan_hash'], get_pconfig($item['uid'], 'pinned_hide', $midb64, []))) + + $midb64 = gen_link_id($item['mid']); + + if(isset($observer['xchan_hash']) && in_array($observer['xchan_hash'], get_pconfig($item['uid'], 'pinned_hide', $midb64, []))) continue; - + $author = channelx_by_hash($item['author_xchan']); $owner = channelx_by_hash($item['owner_xchan']); - + $profile_avatar = $author['xchan_photo_m']; $profile_link = chanlink_hash($item['author_xchan']); $profile_name = $author['xchan_name']; @@ -67,18 +67,18 @@ class Pinned { $conv_responses['attendno'] = [ 'title' => t('Not attending','title') ]; $conv_responses['attendmaybe'] = [ 'title' => t('Might attend','title') ]; if($commentable && $observer) { - $attend = array( t('I will attend'), t('I will not attend'), t('I might attend')); + $attend = [ t('I will attend'), t('I will not attend'), t('I might attend') ]; $isevent = true; } } - + $consensus = (intval($item['item_consensus']) ? true : false); if($consensus) { $conv_responses['agree'] = [ 'title' => t('Agree','title') ]; $conv_responses['disagree'] = [ 'title' => t('Disagree','title') ]; $conv_responses['abstain'] = [ 'title' => t('Abstain','title') ]; if($commentable && $observer) { - $conlabels = array( t('I agree'), t('I disagree'), t('I abstain')); + $conlabels = [ t('I agree'), t('I disagree'), t('I abstain') ]; $canvote = true; } } @@ -87,25 +87,24 @@ class Pinned { $verified = (intval($item['item_verified']) ? t('Message signature validated') : ''); $forged = ((! intval($item['item_verified']) && $item['sig']) ? t('Message signature incorrect') : ''); - + $shareable = ((local_channel() && \App::$profile_uid == local_channel() && $item['item_private'] != 1) ? true : false); if ($shareable) { // This actually turns out not to be possible in some protocol stacks without opening up hundreds of new issues. // Will allow it only for uri resolvable sources. if(strpos($item['mid'],'http') === 0) { - $share = []; //Not yet ready for primetime - //$share = array( t('Repeat This'), t('repeat')); + $share = []; // Isn't yet ready for primetime + //$share = [ t('Repeat This'), t('repeat') ]; } - $embed = array( t('Share This'), t('share')); + $embed = [ t('Share This'), t('share') ]; } - - if(strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC','now - 12 hours')) > 0) - $is_new = true; + + $is_new = boolval(strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC','now - 12 hours')) > 0); $body = prepare_body($item,true); - + $str = [ - 'item_type' => intval($item['item_type']), + 'item_type' => intval($item['item_type']), 'body' => $body['html'], 'tags' => $body['tags'], 'categories' => $body['categories'], @@ -116,9 +115,9 @@ class Pinned { 'id' => $item['id'], 'mids' => json_encode([ $midb64 ]), 'isevent' => $isevent, - 'attend' => $attend, + 'attend' => $attend, 'consensus' => $consensus, - 'conlabels' => $conlabels, + 'conlabels' => ($canvote ? $conlabels : []), 'canvote' => $canvote, 'linktitle' => sprintf( t('View %s\'s profile - %s'), $profile_name, ($author['xchan_addr'] ? $author['xchan_addr'] : $author['xchan_url']) ), 'olinktitle' => sprintf( t('View %s\'s profile - %s'), $owner['xchan_name'], ($owner['xchan_addr'] ? $owner['xchan_addr'] : $owner['xchan_url']) ), @@ -135,7 +134,6 @@ class Pinned { 'localtime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'r'), 'editedtime' => (($item['edited'] != $item['created']) ? sprintf( t('last edited: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['edited'], 'r') ) : ''), 'expiretime' => ($item['expires'] > NULL_DATE ? sprintf( t('Expires: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['expires'], 'r') ) : ''), - 'lock' => $lock, 'verified' => $verified, 'forged' => $forged, 'location' => $location, @@ -150,65 +148,65 @@ class Pinned { 'event' => $body['event'], 'has_tags' => (($body['tags'] || $body['categories'] || $body['mentions'] || $body['attachments'] || $body['folders']) ? true : false), // Item toolbar buttons - 'share' => $share, - 'embed' => $embed, + 'share' => (isset($share) && count($share) ? $share : false), + 'embed' => (isset($embed) && count($embed) ? $embed : false), 'plink' => get_plink($item), 'pinned' => t('Pinned post'), - 'pinme' => (($observer['xchan_hash'] == $owner['xchan_hash']) ? t('Unpin from the top') : ''), - 'hide' => (! $is_new && $observer && ($observer['xchan_hash'] != $owner['xchan_hash']) ? t("Don't show") : ''), + 'pinme' => (isset($observer['xchan_hash']) && $observer['xchan_hash'] == $owner['xchan_hash'] ? t('Unpin from the top') : ''), + 'hide' => (! $is_new && isset($observer['xchan_hash']) && $observer['xchan_hash'] != $owner['xchan_hash'] ? t("Don't show") : ''), // end toolbar buttons 'modal_dismiss' => t('Close'), 'responses' => $conv_responses ]; - - $tpl = get_markup_template('pinned_item.tpl'); + + $tpl = get_markup_template('pinned_item.tpl'); $ret['html'] .= replace_macros($tpl, $str); } return $ret; } - + /* * @brief List pinned items depend on type * * @param $types * @return array of pinned items * - */ + */ private function list($types) { if(empty($types) || (! is_array($types))) return []; - + $item_types = array_intersect($this->allowed_types, $types); if(empty($item_types)) return []; - + $mids_list = []; - + foreach($item_types as $type) { - + $mids = get_pconfig($this->uid, 'pinned', $type, []); foreach($mids as $mid) { - if(! empty($mid) && strpos($mid,'b64.') === 0) - $mids_list[] = @base64url_decode(substr($mid,4)); + if(!empty($mid)) + $mids_list[] = unpack_link_id($mid); } } if(empty($mids_list)) return []; - + $r = q("SELECT * FROM item WHERE mid IN ( '%s' ) AND uid = %d AND id = parent AND item_private = 0 ORDER BY created DESC", dbesc(implode(",", $mids_list)), intval($this->uid) ); if($r) return $r; - + return []; } - + /* * @brief List activities on item * @@ -216,7 +214,7 @@ class Pinned { * @param array $conv_responses * @return array * - */ + */ private function activity($item, &$conv_responses) { foreach(array_keys($conv_responses) as $verb) { @@ -258,23 +256,23 @@ class Pinned { unset($conv_responses[$verb]); continue; } - + $conv_responses[$verb]['count'] = count($r); $conv_responses[$verb]['button'] = get_response_button_text($verb, $conv_responses[$verb]['count']); - + foreach($r as $rr) { - + $author = q("SELECT * FROM xchan WHERE xchan_hash = '%s' LIMIT 1", dbesc($rr['author_xchan']) ); $name = (($author && $author[0]['xchan_name']) ? $author[0]['xchan_name'] : t('Unknown')); $conv_responses[$verb]['list'][] = (($rr['author_xchan'] && $author && $author[0]['xchan_photo_s']) ? - '<a class="dropdown-item" href="' . chanlink_hash($rr['author_xchan']) . '">' . '<img class="menu-img-1" src="' . zid($author[0]['xchan_photo_s']) . '" alt="' . urlencode($name) . '" /> ' . $name . '</a>' : + '<a class="dropdown-item" href="' . chanlink_hash($rr['author_xchan']) . '">' . '<img class="menu-img-1" src="' . zid($author[0]['xchan_photo_s']) . '" alt="' . urlencode($name) . '" /> ' . $name . '</a>' : '<a class="dropdown-item" href="#" class="disabled">' . $name . '</a>' ); } } - + $conv_responses['count'] = count($conv_responses); } } diff --git a/Zotlabs/Zot/Auth.php b/Zotlabs/Zot/Auth.php deleted file mode 100644 index 8d198f506..000000000 --- a/Zotlabs/Zot/Auth.php +++ /dev/null @@ -1,361 +0,0 @@ -<?php - -namespace Zotlabs\Zot; - -class Auth { - - protected $test; - protected $test_results; - protected $debug_msg; - - protected $address; - protected $desturl; - protected $sec; - protected $version; - protected $delegate; - protected $success; - protected $delegate_success; - - protected $remote; - protected $remote_service_class; - protected $remote_level; - protected $remote_hub; - protected $dnt; - - function __construct($req) { - - - $this->test = ((array_key_exists('test',$req)) ? intval($req['test']) : 0); - $this->test_results = array('success' => false); - $this->debug_msg = ''; - - $this->success = false; - $this->address = $req['auth']; - $this->desturl = $req['dest']; - $this->sec = $req['sec']; - $this->version = $req['version']; - $this->delegate = $req['delegate']; - - $c = get_sys_channel(); - if(! $c) { - logger('unable to obtain response (sys) channel'); - $this->Debug('no local channels found.'); - $this->Finalise(); - } - - if(strpbrk($this->sec,'.:')) { - logger('illegal security context'); - $this->Debug('illegal security context.'); - $this->Finalise(); - } - - $x = $this->GetHublocs($this->address); - - if($x) { - foreach($x as $xx) { - if($this->Verify($c,$xx)) - break; - } - } - - /** - * @FIXME we really want to save the return_url in the session before we - * visit rmagic. This does however prevent a recursion if you visit - * rmagic directly, as it would otherwise send you back here again. - * But z_root() probably isn't where you really want to go. - */ - - if(strstr($this->desturl,z_root() . '/rmagic')) - goaway(z_root()); - - $this->Finalise(); - - } - - function GetHublocs($address) { - - // Try and find a hubloc for the person attempting to auth. - // Since we're matching by address, we have to return all entries - // some of which may be from re-installed hubs; and we'll need to - // try each sequentially to see if one can pass the test - - $x = q("select * from hubloc left join xchan on xchan_hash = hubloc_hash - where hubloc_addr = '%s' order by hubloc_id desc", - dbesc($address) - ); - - if(! $x) { - // finger them if they can't be found. - $j = Finger::run($address, null); - if ($j['success']) { - import_xchan($j); - $x = q("select * from hubloc left join xchan on xchan_hash = hubloc_hash - where hubloc_addr = '%s' order by hubloc_id desc", - dbesc($address) - ); - } - } - if(! $x) { - logger('mod_zot: auth: unable to finger ' . $address); - $this->Debug('no hubloc found for ' . $address . ' and probing failed.'); - $this->Finalise(); - } - - return $x; - } - - - function Verify($channel,$hubloc) { - - logger('auth request received from ' . $hubloc['hubloc_addr'] ); - - $this->remote = remote_channel(); - $this->remote_service_class = ''; - $this->remote_level = 0; - $this->remote_hub = $hubloc['hubloc_url']; - $this->dnt = 0; - - if(! $this->sec) { - logger('missing security context.'); - if($this->test) - $this->Debug('missing security context.'); - return false; - } - - - // check credentials and access - - // If they are already authenticated and haven't changed credentials, - // we can save an expensive network round trip and improve performance. - - // Also check that they are coming from the same site as they authenticated with originally. - - $already_authed = (((remote_channel()) && ($hubloc['hubloc_hash'] == remote_channel()) - && ($hubloc['hubloc_url'] === $_SESSION['remote_hub'])) ? true : false); - - if($this->delegate && $this->delegate !== $_SESSION['delegate_channel']) - $already_authed = false; - - if($already_authed) - return true; - - if(local_channel()) { - - // tell them to logout if they're logged in locally as anything but the target remote account - // in which case just shut up because they don't need to be doing this at all. - - if (\App::$channel['channel_hash'] == $hubloc['xchan_hash']) { - return true; - } - else { - logger('already authenticated locally as somebody else.'); - notice( t('Remote authentication blocked. You are logged into this site locally. Please logout and retry.') . EOL); - if($this->test) { - $this->Debug('already logged in locally with a conflicting identity.'); - return false; - } - } - return false; - } - - // Auth packets MUST use ultra top-secret hush-hush mode - e.g. the entire packet is encrypted using the - // site private key - // The actual channel sending the packet ($c[0]) is not important, but this provides a - // generic zot packet with a sender which can be verified - - $x = q("select site_crypto from site where site_url = '%s' limit 1", - dbesc($hubloc['hubloc_url']) - ); - - $p = zot_build_packet($channel,$type = 'auth_check', - array(array('guid' => $hubloc['hubloc_guid'],'guid_sig' => $hubloc['hubloc_guid_sig'])), - $hubloc['hubloc_sitekey'], (($x) ? $x[0]['site_crypto'] : ''), $this->sec); - - $this->Debug('auth check packet created using sitekey ' . $hubloc['hubloc_sitekey']); - $this->Debug('packet contents: ' . $p); - - $result = zot_zot($hubloc['hubloc_callback'],$p); - if(! $result['success']) { - logger('auth_check callback failed.'); - if($this->test) - $this->Debug('auth check request to your site returned .' . print_r($result, true)); - return false; - } - - $j = json_decode($result['body'], true); - if(! $j) { - logger('auth_check json data malformed.'); - if($this->test) - $this->Debug('json malformed: ' . $result['body']); - return false; - } - - $this->Debug('auth check request returned ' . print_r($j, true)); - - if(! $j['success']) - return false; - - // legit response, but we do need to check that this wasn't answered by a man-in-middle - - if (! rsa_verify($this->sec . $hubloc['xchan_hash'],base64url_decode($j['confirm']),$hubloc['xchan_pubkey'])) { - logger('final confirmation failed.'); - if($this->test) - $this->Debug('final confirmation failed. ' . $sec . print_r($j,true) . print_r($hubloc,true)); - return false; - } - - if (array_key_exists('service_class',$j)) - $this->remote_service_class = $j['service_class']; - if (array_key_exists('level',$j)) - $this->remote_level = $j['level']; - if (array_key_exists('DNT',$j)) - $this->dnt = $j['DNT']; - - - // log them in - - if ($this->test) { - // testing only - return the success result - $this->test_results['success'] = true; - $this->Debug('Authentication Success!'); - $this->Finalise(); - } - - $_SESSION['authenticated'] = 1; - - // check for delegation and if all is well, log them in locally with delegation restrictions - - $this->delegate_success = false; - - if($this->delegate) { - $r = q("select * from channel left join xchan on channel_hash = xchan_hash where xchan_addr = '%s' limit 1", - dbesc($this->delegate) - ); - if ($r && intval($r[0]['channel_id'])) { - $allowed = perm_is_allowed($r[0]['channel_id'],$hubloc['xchan_hash'],'delegate'); - if($allowed) { - $_SESSION['delegate_channel'] = $r[0]['channel_id']; - $_SESSION['delegate'] = $hubloc['xchan_hash']; - $_SESSION['account_id'] = intval($r[0]['channel_account_id']); - require_once('include/security.php'); - // this will set the local_channel authentication in the session - change_channel($r[0]['channel_id']); - $this->delegate_success = true; - } - } - } - - if (! $this->delegate_success) { - // normal visitor (remote_channel) login session credentials - $_SESSION['visitor_id'] = $hubloc['xchan_hash']; - $_SESSION['my_url'] = $hubloc['xchan_url']; - $_SESSION['my_address'] = $this->address; - $_SESSION['remote_service_class'] = $this->remote_service_class; - $_SESSION['remote_level'] = $this->remote_level; - $_SESSION['remote_hub'] = $this->remote_hub; - $_SESSION['DNT'] = $this->dnt; - } - - $arr = array('xchan' => $hubloc, 'url' => $this->desturl, 'session' => $_SESSION); - call_hooks('magic_auth_success',$arr); - \App::set_observer($hubloc); - require_once('include/security.php'); - \App::set_groups(init_groups_visitor($_SESSION['visitor_id'])); - info(sprintf( t('Welcome %s. Remote authentication successful.'),$hubloc['xchan_name'])); - logger('mod_zot: auth success from ' . $hubloc['xchan_addr']); - $this->success = true; - return true; - } - - function Debug($msg) { - $this->debug_msg .= $msg . EOL; - } - - - function Finalise() { - - if($this->test) { - $this->test_results['message'] = $this->debug_msg; - json_return_and_die($this->test_results); - } - - goaway($this->desturl); - } - -} - - -/** - * - * Magic Auth - * ========== - * - * So-called "magic auth" takes place by a special exchange. On the site where the "channel to be authenticated" lives (e.g. $mysite), - * a redirection is made via $mysite/magic to the zot endpoint of the remote site ($remotesite) with special GET parameters. - * - * The endpoint is typically https://$remotesite/post - or whatever was specified as the callback url in prior communications - * (we will bootstrap an address and fetch a zot info packet if possible where no prior communications exist) - * - * Five GET parameters are supplied: - * * auth => the urlencoded webbie (channel@host.domain) of the channel requesting access - * * dest => the desired destination URL (urlencoded) - * * sec => a random string which is also stored on $mysite for use during the verification phase. - * * version => the zot revision - * * delegate => optional urlencoded webbie of a local channel to invoke delegation rights for - * - * * test => (optional 1 or 0 - debugs the authentication exchange and returns a json response instead of redirecting the browser session) - * - * When this packet is received, an "auth-check" zot message is sent to $mysite. - * (e.g. if $_GET['auth'] is foobar@podunk.edu, a zot packet is sent to the podunk.edu zot endpoint, which is typically /post) - * If no information has been recorded about the requesting identity a zot information packet will be retrieved before - * continuing. - * - * The sender of this packet is an arbitrary/random site channel. The recipients will be a single recipient corresponding - * to the guid and guid_sig we have associated with the requesting auth identity - * - * \code{.json} - * { - * "type":"auth_check", - * "sender":{ - * "guid":"kgVFf_...", - * "guid_sig":"PT9-TApz...", - * "url":"http:\/\/podunk.edu", - * "url_sig":"T8Bp7j...", - * "sitekey":"aMtgKTiirXrICP..." - * }, - * "recipients":{ - * { - * "guid":"ZHSqb...", - * "guid_sig":"JsAAXi..." - * } - * } - * "callback":"\/post", - * "version":1, - * "secret":"1eaa661", - * "secret_sig":"eKV968b1..." - * } - * \endcode - * - * auth_check messages MUST use encapsulated encryption. This message is sent to the origination site, which checks the 'secret' to see - * if it is the same as the 'sec' which it passed originally. It also checks the secret_sig which is the secret signed by the - * destination channel's private key and base64url encoded. If everything checks out, a json packet is returned: - * - * \code{.json} - * { - * "success":1, - * "confirm":"q0Ysovd1u...", - * "service_class":(optional) - * "level":(optional) - * "DNT": (optional do-not-track - 1 or 0) - * } - * \endcode - * - * 'confirm' in this case is the base64url encoded RSA signature of the concatenation of 'secret' with the - * base64url encoded whirlpool hash of the requestor's guid and guid_sig; signed with the source channel private key. - * This prevents a man-in-the-middle from inserting a rogue success packet. Upon receipt and successful - * verification of this packet, the destination site will redirect to the original destination URL and indicate a successful remote login. - * Service_class can be used by cooperating sites to provide different access rights based on account rights and subscription plans. It is - * a string whose contents are not defined by protocol. Example: "basic" or "gold". - * - * @param[in,out] \App &$a - */ diff --git a/Zotlabs/Zot/Finger.php b/Zotlabs/Zot/Finger.php deleted file mode 100644 index 778b701cd..000000000 --- a/Zotlabs/Zot/Finger.php +++ /dev/null @@ -1,154 +0,0 @@ -<?php - -namespace Zotlabs\Zot; - -use Zotlabs\Web\HTTPSig; - -/** - * @brief Finger - * - */ -class Finger { - - static private $token; - - /** - * @brief Look up information about channel. - * - * @param string $webbie - * does not have to be host qualified e.g. 'foo' is treated as 'foo\@thishub' - * @param array $channel - * (optional), if supplied permissions will be enumerated specifically for $channel - * @param boolean $autofallback - * fallback/failover to http if https connection cannot be established. Default is true. - * - * @return zotinfo array (with 'success' => true) or array('success' => false); - */ - - static public function run($webbie, $channel = null, $autofallback = true) { - - $ret = array('success' => false); - - self::$token = random_string(); - - if (strpos($webbie, '@') === false) { - $address = $webbie; - $host = \App::get_hostname(); - } else { - $address = substr($webbie,0,strpos($webbie,'@')); - $host = substr($webbie,strpos($webbie,'@')+1); - if(strpos($host,'/')) - $host = substr($host,0,strpos($host,'/')); - } - - $xchan_addr = $address . '@' . $host; - - if ((! $address) || (! $xchan_addr)) { - logger('zot_finger: no address :' . $webbie); - - return $ret; - } - - logger('using xchan_addr: ' . $xchan_addr, LOGGER_DATA, LOG_DEBUG); - - // potential issue here; the xchan_addr points to the primary hub. - // The webbie we were called with may not, so it might not be found - // unless we query for hubloc_addr instead of xchan_addr - - $r = q("select xchan.*, hubloc.* from xchan - left join hubloc on xchan_hash = hubloc_hash - where xchan_addr = '%s' and hubloc_primary = 1 and hubloc_deleted = 0 and hubloc_network = 'zot' limit 1", - dbesc($xchan_addr) - ); - - if($r) { - $url = $r[0]['hubloc_url']; - - if($r[0]['hubloc_network'] && $r[0]['hubloc_network'] !== 'zot') { - logger('zot_finger: alternate network: ' . $webbie); - logger('url: ' . $url . ', net: ' . var_export($r[0]['hubloc_network'],true), LOGGER_DATA, LOG_DEBUG); - return $ret; - } - } else { - $url = 'https://' . $host; - } - - $m = parse_url($url); - if($m) { - $parsed_host = strtolower($m['host']); - } - - $rhs = '/.well-known/zot-info'; - $https = ((strpos($url,'https://') === 0) ? true : false); - - logger('zot_finger: ' . $address . ' at ' . $url, LOGGER_DEBUG); - - if ($channel) { - $postvars = array( - 'address' => $address, - 'target' => $channel['channel_guid'], - 'target_sig' => $channel['channel_guid_sig'], - 'key' => $channel['channel_pubkey'], - 'token' => self::$token - ); - - $headers = []; - $headers['X-Zot-Channel'] = $channel['channel_address'] . '@' . \App::get_hostname(); - $headers['X-Zot-Nonce'] = random_string(); - $headers['Host'] = $parsed_host; - - $xhead = HTTPSig::create_sig($headers,$channel['channel_prvkey'],'acct:' . channel_reddress($channel)); - - $retries = 0; - - $result = z_post_url($url . $rhs,$postvars,$retries, [ 'headers' => $xhead ]); - - if ((! $result['success']) && ($autofallback)) { - if ($https) { - logger('zot_finger: https failed. falling back to http'); - $result = z_post_url('http://' . $host . $rhs,$postvars, $retries, [ 'headers' => $xhead ]); - } - } - } - else { - $rhs .= '?f=&address=' . urlencode($address) . '&token=' . self::$token; - - $result = z_fetch_url($url . $rhs); - if((! $result['success']) && ($autofallback)) { - if($https) { - logger('zot_finger: https failed. falling back to http'); - $result = z_fetch_url('http://' . $host . $rhs); - } - } - } - - if(! $result['success']) { - logger('zot_finger: no results'); - - return $ret; - } - - $x = json_decode($result['body'], true); - - $verify = HTTPSig::verify($result,(($x) ? $x['key'] : '')); - - if($x && (! $verify['header_valid'])) { - $signed_token = ((is_array($x) && array_key_exists('signed_token', $x)) ? $x['signed_token'] : null); - if($signed_token) { - $valid = rsa_verify('token.' . self::$token, base64url_decode($signed_token), $x['key']); - if(! $valid) { - logger('invalid signed token: ' . $url . $rhs, LOGGER_NORMAL, LOG_ERR); - - return $ret; - } - } - else { - logger('No signed token from ' . $url . $rhs, LOGGER_NORMAL, LOG_WARNING); - return $ret; - } - } - - return $x; - } - -} diff --git a/Zotlabs/Zot/IHandler.php b/Zotlabs/Zot/IHandler.php deleted file mode 100644 index dd82f5be6..000000000 --- a/Zotlabs/Zot/IHandler.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -namespace Zotlabs\Zot; - -interface IHandler { - - function Ping(); - - function Pickup($data); - - function Notify($data); - - function Request($data); - - function Rekey($sender,$data); - - function AuthCheck($data,$encrypted); - - function Purge($sender,$recipients); - - function Refresh($sender,$recipients); - -} - diff --git a/Zotlabs/Zot/Receiver.php b/Zotlabs/Zot/Receiver.php deleted file mode 100644 index c521c9d64..000000000 --- a/Zotlabs/Zot/Receiver.php +++ /dev/null @@ -1,301 +0,0 @@ -<?php - -namespace Zotlabs\Zot; - -class Receiver { - - protected $data; - protected $encrypted; - protected $error; - protected $messagetype; - protected $sender; - protected $validated; - protected $recipients; - protected $response; - protected $handler; - - function __construct($data,$prvkey,$handler) { - - $this->error = false; - $this->validated = false; - $this->messagetype = ''; - $this->response = array('success' => false); - - $this->handler = $handler; - - if(! is_array($data)) - $data = json_decode($data,true); - - if($data && is_array($data)) { - $this->encrypted = ((array_key_exists('iv',$data)) ? true : false); - - if($this->encrypted) { - $this->data = @json_decode(@crypto_unencapsulate($data,$prvkey),true); - } - if(! $this->data) - $this->data = $data; - - if($this->data && is_array($this->data) && array_key_exists('type',$this->data)) - $this->messagetype = $this->data['type']; - } - if(! $this->messagetype) - $this->error = true; - - if($this->data) { - $this->sender = ((array_key_exists('sender',$this->data)) ? $this->data['sender'] : null); - $this->recipients = ((array_key_exists('recipients',$this->data)) ? $this->data['recipients'] : null); - } - - if($this->sender) - $this->ValidateSender(); - - $this->Dispatch(); - } - - function ValidateSender() { - $hubs = zot_gethub($this->sender,true); - if (! $hubs) { - - /* Have never seen this guid or this guid coming from this location. Check it and register it. */ - /* (!!) this will validate the sender. */ - - $result = zot_register_hub($this->sender); - - if ((! $result['success']) || (! ($hubs = zot_gethub($this->sender,true)))) { - $this->response['message'] = 'Hub not available.'; - json_return_and_die($this->response); - } - } - foreach($hubs as $hub) { - update_hub_connected($hub,((array_key_exists('sitekey',$this->sender)) ? $this->sender['sitekey'] : '')); - } - $this->validated = true; - } - - - function Dispatch() { - - /* Handle tasks which don't require sender validation */ - - switch($this->messagetype) { - case 'ping': - /* no validation needed */ - $this->handler->Ping(); - break; - case 'pickup': - /* perform site validation, as opposed to sender validation */ - $this->handler->Pickup($this->data); - break; - - default: - if(! $this->validated) { - $this->response['message'] = 'Sender not valid'; - json_return_and_die($this->response); - } - break; - } - - /* Now handle tasks which require sender validation */ - - switch($this->messagetype) { - - case 'auth_check': - $this->handler->AuthCheck($this->data,$this->encrypted); - break; - - case 'request': - $this->handler->Request($this->data); - break; - - case 'purge': - $this->handler->Purge($this->sender,$this->recipients); - break; - - case 'refresh': - case 'force_refresh': - $this->handler->Refresh($this->sender,$this->recipients); - break; - - case 'notify': - $this->handler->Notify($this->data); - break; - - case 'rekey': - $this->handler->Rekey($this->sender, $this->data); - break; - - default: - $this->response['message'] = 'Not implemented'; - json_return_and_die($this->response); - break; - } - - } -} - - - -/** - * @brief zot communications and messaging. - * - * Sender HTTP posts to this endpoint ($site/post typically) with 'data' parameter set to json zot message packet. - * This packet is optionally encrypted, which we will discover if the json has an 'iv' element. - * $contents => array( 'alg' => 'aes256cbc', 'iv' => initialisation vector, 'key' => decryption key, 'data' => encrypted data); - * $contents->iv and $contents->key are random strings encrypted with this site's RSA public key and then base64url encoded. - * - * Once decrypted, one will find the normal json_encoded zot message packet. - * - * Defined packet types are: notify, purge, refresh, force_refresh, auth_check, ping, and pickup - * - * Standard packet: (used by notify, purge, refresh, force_refresh, and auth_check) - * \code{.json} - * { - * "type": "notify", - * "sender":{ - * "guid":"kgVFf_1...", - * "guid_sig":"PT9-TApzp...", - * "url":"http:\/\/podunk.edu", - * "url_sig":"T8Bp7j5...", - * }, - * "recipients": { optional recipient array }, - * "callback":"\/post", - * "version":"1.2", - * "encryption":["aes256cbc"], - * "secret":"1eaa...", - * "secret_sig": "df89025470fac8..." - * } - * \endcode - * - * Signature fields are all signed with the sender channel private key and base64url encoded. - * Recipients are arrays of guid and guid_sig, which were previously signed with the recipients private - * key and base64url encoded and later obtained via channel discovery. Absence of recipients indicates - * a public message or visible to all potential listeners on this site. - * - * "pickup" packet: - * The pickup packet is sent in response to a notify packet from another site - * \code{.json} - * { - * "type":"pickup", - * "url":"http:\/\/example.com", - * "callback":"http:\/\/example.com\/post", - * "callback_sig":"teE1_fLI...", - * "secret":"1eaa...", - * "secret_sig":"O7nB4_..." - * } - * \endcode - * - * In the pickup packet, the sig fields correspond to the respective data - * element signed with this site's system private key and then base64url encoded. - * The "secret" is the same as the original secret from the notify packet. - * - * If verification is successful, a json structure is returned containing a - * success indicator and an array of type 'pickup'. - * Each pickup element contains the original notify request and a message field - * whose contents are dependent on the message type. - * - * This JSON array is AES encapsulated using the site public key of the site - * that sent the initial zot pickup packet. - * Using the above example, this would be example.com. - * - * \code{.json} - * { - * "success":1, - * "pickup":{ - * "notify":{ - * "type":"notify", - * "sender":{ - * "guid":"kgVFf_...", - * "guid_sig":"PT9-TApz...", - * "url":"http:\/\/z.podunk.edu", - * "url_sig":"T8Bp7j5D..." - * }, - * "callback":"\/post", - * "version":1, - * "secret":"1eaa661..." - * }, - * "message":{ - * "type":"activity", - * "message_id":"10b049ce384cbb2da9467319bc98169ab36290b8bbb403aa0c0accd9cb072e76@podunk.edu", - * "message_top":"10b049ce384cbb2da9467319bc98169ab36290b8bbb403aa0c0accd9cb072e76@podunk.edu", - * "message_parent":"10b049ce384cbb2da9467319bc98169ab36290b8bbb403aa0c0accd9cb072e76@podunk.edu", - * "created":"2012-11-20 04:04:16", - * "edited":"2012-11-20 04:04:16", - * "title":"", - * "body":"Hi Nickordo", - * "app":"", - * "verb":"post", - * "object_type":"", - * "target_type":"", - * "permalink":"", - * "location":"", - * "longlat":"", - * "owner":{ - * "name":"Indigo", - * "address":"indigo@podunk.edu", - * "url":"http:\/\/podunk.edu", - * "photo":{ - * "mimetype":"image\/jpeg", - * "src":"http:\/\/podunk.edu\/photo\/profile\/m\/5" - * }, - * "guid":"kgVFf_...", - * "guid_sig":"PT9-TAp...", - * }, - * "author":{ - * "name":"Indigo", - * "address":"indigo@podunk.edu", - * "url":"http:\/\/podunk.edu", - * "photo":{ - * "mimetype":"image\/jpeg", - * "src":"http:\/\/podunk.edu\/photo\/profile\/m\/5" - * }, - * "guid":"kgVFf_...", - * "guid_sig":"PT9-TAp..." - * } - * } - * } - * } - * \endcode - * - * Currently defined message types are 'activity', 'mail', 'profile', 'location' - * and 'channel_sync', which each have different content schemas. - * - * Ping packet: - * A ping packet does not require any parameters except the type. It may or may - * not be encrypted. - * - * \code{.json} - * { - * "type": "ping" - * } - * \endcode - * - * On receipt of a ping packet a ping response will be returned: - * - * \code{.json} - * { - * "success" : 1, - * "site" { - * "url": "http:\/\/podunk.edu", - * "url_sig": "T8Bp7j5...", - * "sitekey": "-----BEGIN PUBLIC KEY----- - * MIICIjANBgkqhkiG9w0BAQE..." - * } - * } - * \endcode - * - * The ping packet can be used to verify that a site has not been re-installed, and to - * initiate corrective action if it has. The url_sig is signed with the site private key - * and base64url encoded - and this should verify with the enclosed sitekey. Failure to - * verify indicates the site is corrupt or otherwise unable to communicate using zot. - * This return packet is not otherwise verified, so should be compared with other - * results obtained from this site which were verified prior to taking action. For instance - * if you have one verified result with this signature and key, and other records for this - * url which have different signatures and keys, it indicates that the site was re-installed - * and corrective action may commence (remove or mark invalid any entries with different - * signatures). - * If you have no records which match this url_sig and key - no corrective action should - * be taken as this packet may have been returned by an imposter. - * - * @param[in,out] App &$a - */ - diff --git a/Zotlabs/Zot/ZotHandler.php b/Zotlabs/Zot/ZotHandler.php deleted file mode 100644 index ab8815b3d..000000000 --- a/Zotlabs/Zot/ZotHandler.php +++ /dev/null @@ -1,39 +0,0 @@ -<?php - -namespace Zotlabs\Zot; - -class ZotHandler implements IHandler { - - function Ping() { - zot_reply_ping(); - } - - function Pickup($data) { - zot_reply_pickup($data); - } - - function Notify($data) { - zot_reply_notify($data); - } - - function Request($data) { - zot_reply_message_request($data); - } - - function Rekey($sender,$data) { - zot_rekey_request($sender,$data); - } - - function AuthCheck($data,$encrypted) { - zot_reply_auth_check($data,$encrypted); - } - - function Purge($sender,$recipients) { - zot_reply_purge($sender,$recipients); - } - - function Refresh($sender,$recipients) { - zot_reply_refresh($sender,$recipients); - } - -} diff --git a/Zotlabs/Zot6/Finger.php b/Zotlabs/Zot6/Finger.php deleted file mode 100644 index 22ce4685d..000000000 --- a/Zotlabs/Zot6/Finger.php +++ /dev/null @@ -1,145 +0,0 @@ -<?php - -namespace Zotlabs\Zot6; - -/** - * @brief Finger - * - */ -class Finger { - - static private $token; - - /** - * @brief Look up information about channel. - * - * @param string $webbie - * does not have to be host qualified e.g. 'foo' is treated as 'foo\@thishub' - * @param array $channel - * (optional), if supplied permissions will be enumerated specifically for $channel - * @param boolean $autofallback - * fallback/failover to http if https connection cannot be established. Default is true. - * - * @return zotinfo array (with 'success' => true) or array('success' => false); - */ - - static public function run($webbie, $channel = null, $autofallback = true) { - - $ret = array('success' => false); - - self::$token = random_string(); - - if (strpos($webbie, '@') === false) { - $address = $webbie; - $host = \App::get_hostname(); - } else { - $address = substr($webbie,0,strpos($webbie,'@')); - $host = substr($webbie,strpos($webbie,'@')+1); - if(strpos($host,'/')) - $host = substr($host,0,strpos($host,'/')); - } - - $xchan_addr = $address . '@' . $host; - - if ((! $address) || (! $xchan_addr)) { - logger('zot_finger: no address :' . $webbie); - - return $ret; - } - - logger('using xchan_addr: ' . $xchan_addr, LOGGER_DATA, LOG_DEBUG); - - // potential issue here; the xchan_addr points to the primary hub. - // The webbie we were called with may not, so it might not be found - // unless we query for hubloc_addr instead of xchan_addr - - $r = q("select xchan.*, hubloc.* from xchan - left join hubloc on xchan_hash = hubloc_hash - where xchan_addr = '%s' and hubloc_primary = 1 limit 1", - dbesc($xchan_addr) - ); - - if($r) { - $url = $r[0]['hubloc_url']; - - if($r[0]['hubloc_network'] && $r[0]['hubloc_network'] !== 'zot') { - logger('zot_finger: alternate network: ' . $webbie); - logger('url: ' . $url . ', net: ' . var_export($r[0]['hubloc_network'],true), LOGGER_DATA, LOG_DEBUG); - return $ret; - } - } else { - $url = 'https://' . $host; - } - - $rhs = '/.well-known/zot-info'; - $https = ((strpos($url,'https://') === 0) ? true : false); - - logger('zot_finger: ' . $address . ' at ' . $url, LOGGER_DEBUG); - - if ($channel) { - $postvars = array( - 'address' => $address, - 'target' => $channel['channel_guid'], - 'target_sig' => $channel['channel_guid_sig'], - 'key' => $channel['channel_pubkey'], - 'token' => self::$token - ); - - $headers = []; - $headers['X-Zot-Channel'] = $channel['channel_address'] . '@' . \App::get_hostname(); - $headers['X-Zot-Nonce'] = random_string(); - $xhead = HTTPSig::create_sig($headers,$channel['channel_prvkey'],'acct:' . channel_reddress($channel)); - - $retries = 0; - - $result = z_post_url($url . $rhs,$postvars,$retries, [ 'headers' => $xhead ]); - - if ((! $result['success']) && ($autofallback)) { - if ($https) { - logger('zot_finger: https failed. falling back to http'); - $result = z_post_url('http://' . $host . $rhs,$postvars, $retries, [ 'headers' => $xhead ]); - } - } - } - else { - $rhs .= '?f=&address=' . urlencode($address) . '&token=' . self::$token; - - $result = z_fetch_url($url . $rhs); - if((! $result['success']) && ($autofallback)) { - if($https) { - logger('zot_finger: https failed. falling back to http'); - $result = z_fetch_url('http://' . $host . $rhs); - } - } - } - - if(! $result['success']) { - logger('zot_finger: no results'); - - return $ret; - } - - $x = json_decode($result['body'], true); - - $verify = HTTPSig::verify($result,(($x) ? $x['key'] : '')); - - if($x && (! $verify['header_valid'])) { - $signed_token = ((is_array($x) && array_key_exists('signed_token', $x)) ? $x['signed_token'] : null); - if($signed_token) { - $valid = zot_verify('token.' . self::$token, base64url_decode($signed_token), $x['key']); - if(! $valid) { - logger('invalid signed token: ' . $url . $rhs, LOGGER_NORMAL, LOG_ERR); - - return $ret; - } - } - else { - logger('No signed token from ' . $url . $rhs, LOGGER_NORMAL, LOG_WARNING); - return $ret; - } - } - - return $x; - } - -} diff --git a/Zotlabs/Zot6/IHandler.php b/Zotlabs/Zot6/IHandler.php index 53b6caa89..4e7738926 100644 --- a/Zotlabs/Zot6/IHandler.php +++ b/Zotlabs/Zot6/IHandler.php @@ -4,15 +4,13 @@ namespace Zotlabs\Zot6; interface IHandler { - function Notify($data,$hub); + function Notify($data, $hub); - function Request($data,$hub); + function Rekey($sender, $data, $hub); - function Rekey($sender,$data,$hub); + function Refresh($sender, $recipients, $hub, $force); - function Refresh($sender,$recipients,$hub); - - function Purge($sender,$recipients,$hub); + function Purge($sender, $recipients, $hub); } diff --git a/Zotlabs/Zot6/Receiver.php b/Zotlabs/Zot6/Receiver.php index a9a7ab0df..964c61651 100644 --- a/Zotlabs/Zot6/Receiver.php +++ b/Zotlabs/Zot6/Receiver.php @@ -3,10 +3,10 @@ namespace Zotlabs\Zot6; use Zotlabs\Lib\Config; +use Zotlabs\Lib\Crypto; use Zotlabs\Lib\Libzot; use Zotlabs\Web\HTTPSig; - class Receiver { protected $data; @@ -70,7 +70,7 @@ class Receiver { $this->encrypted = ((array_key_exists('encrypted',$this->data) && intval($this->data['encrypted'])) ? true : false); if ($this->encrypted && $this->prvkey) { - $uncrypted = crypto_unencapsulate($this->data,$this->prvkey); + $uncrypted = Crypto::unencapsulate($this->data,$this->prvkey); if ($uncrypted) { $this->data = json_decode($uncrypted,true); } @@ -88,7 +88,7 @@ class Receiver { if ($this->error) { // make timing attacks on the decryption engine a bit more difficult usleep(mt_rand(10000,100000)); - return($this->response); + return($this->response); } if ($this->data) { @@ -126,7 +126,7 @@ class Receiver { $x = Libzot::register_hub($this->sigdata['signer']); if($x['success']) { $hub = Libzot::valid_hub($this->sender,$this->site_id); - } + } if(! $hub) { $this->response['message'] = 'sender unknown'; return false; @@ -168,26 +168,25 @@ class Receiver { } } return $result; - } - + } + function Dispatch() { switch ($this->messagetype) { - - case 'request': - $this->response = $this->handler->Request($this->data,$this->hub); - break; - case 'purge': - $this->response = $this->handler->Purge($this->sender,$this->recipients,$this->hub); + $this->response = $this->handler->Purge($this->sender, $this->recipients, $this->hub); break; case 'refresh': - $this->response = $this->handler->Refresh($this->sender,$this->recipients,$this->hub); + $this->response = $this->handler->Refresh($this->sender, $this->recipients, $this->hub, false); + break; + + case 'force_refresh': + $this->response = $this->handler->Refresh($this->sender, $this->recipients, $this->hub, true); break; case 'rekey': - $this->response = $this->handler->Rekey($this->sender, $this->data,$this->hub); + $this->response = $this->handler->Rekey($this->sender, $this->data, $this->hub); break; case 'activity': @@ -195,7 +194,7 @@ class Receiver { case 'sync': default: if ($this->sender) { - $this->response = $this->handler->Notify($this->data,$this->hub); + $this->response = $this->handler->Notify($this->data, $this->hub); } break; @@ -207,13 +206,13 @@ class Receiver { $this->EncryptResponse(); } - return($this->response); + return($this->response); } function EncryptResponse() { $algorithm = Libzot::best_algorithm($this->hub['site_crypto']); if ($algorithm) { - $this->response = crypto_encapsulate(json_encode($this->response),$this->hub['hubloc_sitekey'], $algorithm); + $this->response = Crypto::encapsulate(json_encode($this->response),$this->hub['hubloc_sitekey'], $algorithm); } } diff --git a/Zotlabs/Zot6/Zot6Handler.php b/Zotlabs/Zot6/Zot6Handler.php index d717b147b..4dc410f52 100644 --- a/Zotlabs/Zot6/Zot6Handler.php +++ b/Zotlabs/Zot6/Zot6Handler.php @@ -7,24 +7,20 @@ use Zotlabs\Lib\Queue; class Zot6Handler implements IHandler { - function Notify($data,$hub) { - return self::reply_notify($data,$hub); + function Notify($data, $hub) { + return self::reply_notify($data, $hub); } - function Request($data,$hub) { - return self::reply_message_request($data,$hub); + function Rekey($sender, $data, $hub) { + return self::reply_rekey_request($sender, $data, $hub); } - function Rekey($sender,$data,$hub) { - return self::reply_rekey_request($sender,$data,$hub); + function Refresh($sender, $recipients, $hub, $force) { + return self::reply_refresh($sender, $recipients, $hub, $force); } - function Refresh($sender,$recipients,$hub) { - return self::reply_refresh($sender,$recipients,$hub); - } - - function Purge($sender,$recipients,$hub) { - return self::reply_purge($sender,$recipients,$hub); + function Purge($sender, $recipients, $hub) { + return self::reply_purge($sender, $recipients, $hub); } @@ -38,7 +34,7 @@ class Zot6Handler implements IHandler { logger('notify received from ' . $hub['hubloc_url']); - $x = Libzot::fetch($data); + $x = Libzot::fetch($data, $hub); $ret['delivery_report'] = $x; @@ -62,7 +58,7 @@ class Zot6Handler implements IHandler { * * @return array */ - static function reply_refresh($sender, $recipients, $hub) { + static function reply_refresh($sender, $recipients, $hub, $force) { $ret = array('success' => false); if($recipients) { @@ -75,87 +71,18 @@ class Zot6Handler implements IHandler { where xchan_hash ='%s' limit 1", dbesc($recip) ); - /// @FIXME $msgtype is undefined - $x = Libzot::refresh( [ 'hubloc_id_url' => $hub['hubloc_id_url'] ], $r[0], (($msgtype === 'force_refresh') ? true : false)); + $x = Libzot::refresh([ 'hubloc_id_url' => $hub['hubloc_id_url']], $r[0], $force); } } else { // system wide refresh - /// @FIXME $msgtype is undefined - $x = Libzot::refresh( [ 'hubloc_id_url' => $hub['hubloc_id_url'] ], null, (($msgtype === 'force_refresh') ? true : false)); + $x = Libzot::refresh(['hubloc_id_url' => $hub['hubloc_id_url']], null, $force); } $ret['success'] = true; return $ret; } - - - /** - * @brief Process a message request. - * - * If a site receives a comment to a post but finds they have no parent to attach it with, they - * may send a 'request' packet containing the message_id of the missing parent. This is the handler - * for that packet. We will create a message_list array of the entire conversation starting with - * the missing parent and invoke delivery to the sender of the packet. - * - * Zotlabs/Daemon/Deliver.php (for local delivery) and - * mod/post.php???? @fixme (for web delivery) detect the existence of - * this 'message_list' at the destination and split it into individual messages which are - * processed/delivered in order. - * - * @param array $data - * @param array $hub - * @return array - */ - static function reply_message_request($data, $hub) { - $ret = [ 'success' => false ]; - - $message_id = EMPTY_STR; - - if(array_key_exists('data',$data)) - $ptr = $data['data']; - if(is_array($ptr) && array_key_exists(0,$ptr)) { - $ptr = $ptr[0]; - } - if(is_string($ptr)) { - $message_id = $ptr; - } - if(is_array($ptr) && array_key_exists('id',$ptr)) { - $message_id = $ptr['id']; - } - - if (! $message_id) { - $ret['message'] = 'no message_id'; - logger('no message_id'); - return $ret; - } - - $sender = $hub['hubloc_hash']; - - /* - * Find the local channel in charge of this post (the first and only recipient of the request packet) - */ - - $arr = $data['recipients'][0]; - - $c = q("select * from channel left join xchan on channel_hash = xchan_hash where channel_hash = '%s' limit 1", - dbesc($arr['portable_id']) - ); - if (! $c) { - logger('recipient channel not found.'); - $ret['message'] .= 'recipient not found.' . EOL; - return $ret; - } - - /* - * fetch the requested conversation - */ - $messages = zot_feed($c[0]['channel_id'], $sender, [ 'message_id' => $data['message_id'], 'encoding' => 'activitystreams' ]); - - return (($messages) ? : [] ); - } - static function rekey_request($sender,$data,$hub) { $ret = array('success' => false); @@ -174,7 +101,7 @@ class Zot6Handler implements IHandler { $old = null; if(Libzot::verify($data['old_guid'],$data['old_guid_sig'],$data['old_key'])) { - $oldhash = make_xchan_hash($data['old_guid'],$data['old_key']); + $oldhash = Libzot::make_xchan_hash($data['old_guid'],$data['old_key']); $old = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($oldhash) ); @@ -223,21 +150,25 @@ class Zot6Handler implements IHandler { $ret = array('success' => false); + if (! $sender) { + return $ret; + } + if ($recipients) { // basically this means "unfriend" foreach ($recipients as $recip) { - $r = q("select channel.*,xchan.* from channel + $channel = q("select channel.*,xchan.* from channel left join xchan on channel_hash = xchan_hash where channel_hash = '%s' limit 1", dbesc($recip) ); - if ($r) { - $r = q("select abook_id from abook where uid = %d and abook_xchan = '%s' limit 1", - intval($r[0]['channel_id']), + if ($channel) { + $abook = q("select abook_id from abook where abook_channel = %d and abook_xchan = '%s' limit 1", + intval($channel[0]['channel_id']), dbesc($sender) ); - if ($r) { - contact_remove($r[0]['channel_id'],$r[0]['abook_id']); + if ($abook) { + contact_remove($channel[0]['channel_id'],$abook[0]['abook_id']); } } } |