diff options
Diffstat (limited to 'Zotlabs')
39 files changed, 972 insertions, 584 deletions
diff --git a/Zotlabs/Daemon/Cron.php b/Zotlabs/Daemon/Cron.php index 640f06102..caae0ce53 100644 --- a/Zotlabs/Daemon/Cron.php +++ b/Zotlabs/Daemon/Cron.php @@ -3,6 +3,7 @@ namespace Zotlabs\Daemon; use Zotlabs\Lib\Libsync; +use Zotlabs\Lib\Libzotdir; class Cron { @@ -35,6 +36,17 @@ class Cron { logger('cron: start'); + // If this is a directory server, request a sync with an upstream + // 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? + + $dirmode = get_config('system', 'directory_mode'); + + if ($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) { + Libzotdir::sync_directories($dirmode); + } + // run queue delivery process in the background Master::Summon(array('Queue')); diff --git a/Zotlabs/Daemon/Cron_daily.php b/Zotlabs/Daemon/Cron_daily.php index 6d62836c7..850d38229 100644 --- a/Zotlabs/Daemon/Cron_daily.php +++ b/Zotlabs/Daemon/Cron_daily.php @@ -94,17 +94,6 @@ class Cron_daily { // expire any expired accounts 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. - // Pull remote changes and push local changes. - // potential issue: how do we keep from creating an endless update loop? - - $dirmode = get_config('system', 'directory_mode'); - - if ($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) { - Libzotdir::sync_directories($dirmode); - } - Master::Summon(array('Expire')); Master::Summon(array('Cli_suggest')); diff --git a/Zotlabs/Daemon/Expire.php b/Zotlabs/Daemon/Expire.php index 5d9f14b28..84a606dc2 100644 --- a/Zotlabs/Daemon/Expire.php +++ b/Zotlabs/Daemon/Expire.php @@ -32,7 +32,6 @@ class Expire { } // physically remove anything that has been deleted for more than two months - /** @FIXME - this is a wretchedly inefficient query */ q("delete from item where item_pending_remove = 1 and changed < %s - INTERVAL %s", db_utcnow(), @@ -59,8 +58,8 @@ class Expire { continue; // service class default (if non-zero) over-rides the site default - $service_class_expire = service_class_fetch($rr['channel_id'], 'expire_days'); + if (intval($service_class_expire)) $channel_expire = $service_class_expire; else @@ -85,7 +84,6 @@ class Expire { // this should probably just fetch the channel_expire_days from the sys channel, // but there's no convenient way to set it. - $expire_days = get_config('system', 'sys_expire_days'); if ($expire_days === false) $expire_days = 30; @@ -96,8 +94,9 @@ class Expire { logger('Expire: sys interval: ' . $expire_days, LOGGER_DEBUG); - if ($expire_days) + if ($expire_days) { item_expire($x['channel_id'], $expire_days, $commented_days); + } logger('Expire: sys: done', LOGGER_DEBUG); } diff --git a/Zotlabs/Daemon/Onedirsync.php b/Zotlabs/Daemon/Onedirsync.php index ea995be9e..b711b0c05 100644 --- a/Zotlabs/Daemon/Onedirsync.php +++ b/Zotlabs/Daemon/Onedirsync.php @@ -15,61 +15,45 @@ class Onedirsync { $update_id = intval($argv[1]); if (!$update_id) { - logger('onedirsync: no update'); + logger('onedirsync: no update id'); return; } - $r = q("select * from updates where ud_id = %d limit 1", + $r = q("select * from updates where ud_id = %d", intval($update_id) ); - if (!$r) - return; - - 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 - // (where we received this update from) ? - // If we have, we don't need to do anything except mark any older entries updated - - $x = q("select * from updates where ud_addr = '%s' and ud_date > '%s' and ( ud_flags & %d )>0 order by ud_date desc limit 1", - dbesc($r[0]['ud_addr']), - dbesc($r[0]['ud_date']), - intval(UPDATE_FLAGS_UPDATED) - ); - 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), - dbesc($x[0]['ud_date']) - ); + if (!$r) { + logger('onedirsync: update id not found'); return; } // ignore doing an update if this ud_addr refers to a known dead hubloc - $h = q("select * from hubloc where hubloc_addr = '%s'", + $h = q("select * from hubloc where hubloc_id_url = '%s' order by hubloc_id desc", dbesc($r[0]['ud_addr']), ); $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; + + // 2023-04-12: Try to update anyway since the info is not always correct + // This might change after all directory servers run the new code. + + // q("update updates set ud_flags = 9 where ud_hash = '%s' and ud_flags != 9", + // dbesc($r[0]['ud_hash']) + //); + + // return; } // 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['hubloc_network'] !== 'zot6') + if ($h && $h['hubloc_network'] !== 'zot6') { return; + } Libzotdir::update_directory_entry($r[0]); diff --git a/Zotlabs/Daemon/Onepoll.php b/Zotlabs/Daemon/Onepoll.php index bde39007e..973bcf402 100644 --- a/Zotlabs/Daemon/Onepoll.php +++ b/Zotlabs/Daemon/Onepoll.php @@ -30,14 +30,10 @@ class Onepoll { $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 $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) + $contacts = q("SELECT abook.*, xchan.* FROM abook + LEFT JOIN xchan ON xchan_hash = abook_xchan + WHERE abook_id = %d", + intval($contact_id) ); if (!$contacts) { diff --git a/Zotlabs/Daemon/Poller.php b/Zotlabs/Daemon/Poller.php index 0fdc3da16..77a428ec3 100644 --- a/Zotlabs/Daemon/Poller.php +++ b/Zotlabs/Daemon/Poller.php @@ -19,24 +19,6 @@ class Poller { $interval = get_config('queueworker', 'queue_interval', 500000); -/* - 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'))) { - 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; @@ -67,6 +49,11 @@ class Poller { : '' ); + $allow_feeds = get_config('system', 'feed_contacts'); + if(!$allow_feeds) { + $sql_extra .= ' and abook_feed = 0 '; + } + $randfunc = db_getfunc('RAND'); $contacts = q("SELECT abook.abook_updated, abook.abook_connected, abook.abook_feed, @@ -76,7 +63,7 @@ class Poller { 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 + where abook_self = 0 and abook_pending = 0 and abook_archived = 0 and abook_blocked = 0 and abook_ignored = 0 $sql_extra AND (( account_flags = %d ) OR ( account_flags = %d )) $abandon_sql ORDER BY $randfunc", intval(ACCOUNT_OK), @@ -183,11 +170,12 @@ class Poller { $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), + $r = q("SELECT * FROM updates WHERE ud_update = 1 AND (ud_last = '%s' OR ud_last > %s - INTERVAL %s)", dbesc(NULL_DATE), - db_utcnow(), db_quoteinterval('7 DAY') + db_utcnow(), + db_quoteinterval('7 DAY') ); + if ($r) { foreach ($r as $rr) { @@ -209,10 +197,6 @@ class Poller { set_config('system', 'lastpoll', datetime_convert()); - //All done - clear the lockfile -/* - @unlink($lockfile); -*/ return; } } diff --git a/Zotlabs/Daemon/Queue.php b/Zotlabs/Daemon/Queue.php index b07fe369c..91aba831a 100644 --- a/Zotlabs/Daemon/Queue.php +++ b/Zotlabs/Daemon/Queue.php @@ -14,7 +14,7 @@ class Queue { // delete all queue items more than 3 days old // but first mark these sites dead if we haven't heard from them in a month - $oldqItems = q("select outq_posturl from outq where outq_created < %s - INTERVAL %s", + $oldqItems = q("select outq_posturl, outq_hash from outq where outq_created < %s - INTERVAL %s", db_utcnow(), db_quoteinterval('3 DAY') ); @@ -29,13 +29,13 @@ class Queue { db_quoteinterval('1 MONTH') ); } - } - logger('Removing ' . count($oldqItems) . ' old queue entries'); - q("DELETE FROM outq WHERE outq_created < %s - INTERVAL %s", - db_utcnow(), - db_quoteinterval('3 DAY') - ); + $old_hashes = ids_to_querystr($oldqItems, 'outq_hash', true); + + logger('Removing ' . count($oldqItems) . ' old queue entries'); + dbq("DELETE FROM outq WHERE outq_hash IN ($old_hashes)"); + + } $deliveries = []; @@ -47,9 +47,10 @@ class Queue { LibQueue::deliver($qItems[0]); } else { - $qItems = q("SELECT * FROM outq WHERE outq_delivered = 0 and outq_scheduled < %s ", + $qItems = q("SELECT outq_hash FROM outq WHERE outq_scheduled < %s ", db_utcnow() ); + if ($qItems) { foreach ($qItems as $qItem) { $deliveries[] = $qItem['outq_hash']; diff --git a/Zotlabs/Daemon/Zotconvo.php b/Zotlabs/Daemon/Zotconvo.php new file mode 100644 index 000000000..16e7f113f --- /dev/null +++ b/Zotlabs/Daemon/Zotconvo.php @@ -0,0 +1,32 @@ +<?php + +namespace Zotlabs\Daemon; + +use Zotlabs\Lib\Libzot; + +class Zotconvo { + + static public function run($argc, $argv) { + + logger('Zotconvo invoked: ' . print_r($argv, true)); + + if ($argc != 3) { + return; + } + + $mid = $argv[2]; + if (!$mid) { + return; + } + + $channel = channelx_by_n(intval($argv[1])); + if (!$channel) { + return; + } + + Libzot::fetch_conversation($channel, $mid); + + return; + + } +} diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index 416aaa0d8..fcf3fb172 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -289,7 +289,7 @@ class Activity { static function encode_item_collection($items, $id, $type, $total = 0) { - if ($total > 30) { + if ($total > App::$pager['itemspage']) { $ret = [ 'id' => z_root() . '/' . $id, 'type' => $type . 'Page', @@ -591,9 +591,9 @@ class Activity { foreach ($item['term'] as $t) { switch ($t['ttype']) { case TERM_HASHTAG: - // id is required so if we don't have a url in the taxonomy, ignore it and keep going. + // 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', 'id' => $t['url'], 'name' => '#' . $t['term']]; + $ret[] = ['type' => 'Hashtag', 'href' => $t['url'], 'name' => '#' . $t['term']]; } break; @@ -622,6 +622,10 @@ class Activity { $atts = ((is_array($item['attach'])) ? $item['attach'] : json_decode($item['attach'], true)); if ($atts) { foreach ($atts as $att) { + if (!isset($att['type'], $att['href'])) { + continue; + } + if (isset($att['type']) && strpos($att['type'], 'image')) { $ret[] = ['type' => 'Image', 'url' => $att['href']]; } @@ -676,7 +680,7 @@ class Activity { $ret = []; - if (isset($item['attachment'])) { + if (isset($item['attachment']) && is_array($item['attachment'])) { $ptr = $item['attachment']; if (!array_key_exists(0, $ptr)) { $ptr = [$ptr]; @@ -1333,7 +1337,8 @@ class Activity { } } - $x = PermissionRoles::role_perms('personal'); + $role = get_pconfig($channel['channel_id'], 'system', 'permissions_role', 'personal'); + $x = PermissionRoles::role_perms($role); $their_perms = Permissions::FilledPerms($x['perms_connect']); if ($contact && $contact['abook_id']) { @@ -1358,10 +1363,15 @@ class Activity { return; case 'Accept': + // They accepted our Follow request. + // Set default permissions except for send_stream and post_wall - // They accepted our Follow request - set default permissions - - set_abconfig($channel['channel_id'], $contact['abook_xchan'], 'system', 'their_perms', $their_perms); + foreach ($their_perms as $k => $v) { + if(in_array($k, ['send_stream', 'post_wall'])) { + $v = 0; // Those will be set once we accept their follow request + } + set_abconfig($channel['channel_id'], $contact['abook_xchan'], 'their_perms', $k, $v); + } $abook_instance = $contact['abook_instance']; @@ -2215,18 +2225,14 @@ class Activity { } static function decode_note($act) { - $response_activity = false; - $s = []; // These activities should have been handled separately in the Inbox module and should not be turned into posts if ( in_array($act->type, ['Follow', 'Accept', 'Reject', 'Create', 'Update']) && - is_array($act->obj) && - array_key_exists('type', $act->obj) && - ($act->obj['type'] === 'Follow' || ActivityStreams::is_an_actor($act->obj['type'])) + ($act->objprop('type') === 'Follow' || ActivityStreams::is_an_actor($act->objprop('type'))) ) { return false; } @@ -2256,7 +2262,7 @@ class Activity { $content = self::get_content($act->obj); } - $s['mid'] = ((is_array($act->obj) && isset($act->obj['id'])) ? $act->obj['id'] : $act->obj); + $s['mid'] = $act->objprop('id') ?: $act->obj; if (!$s['mid']) { return false; @@ -2264,7 +2270,7 @@ class Activity { // Friendica sends the diaspora guid in a nonstandard field via AP // If no uuid is provided we will create an uuid v5 from the mid - $s['uuid'] = ((is_array($act->obj) && isset($act->obj['diaspora:guid'])) ? $act->obj['diaspora:guid'] : uuid_from_url($s['mid'])); + $s['uuid'] = (($act->objprop('diaspora:guid')) ?: uuid_from_url($s['mid'])); $s['parent_mid'] = $act->parent_id; @@ -2272,24 +2278,24 @@ class Activity { $s['created'] = datetime_convert('UTC', 'UTC', $act->data['published']); $s['commented'] = $s['created']; } - elseif (is_array($act->obj) && array_key_exists('published', $act->obj)) { + elseif ($act->objprop('published')) { $s['created'] = datetime_convert('UTC', 'UTC', $act->obj['published']); $s['commented'] = $s['created']; } if (array_key_exists('updated', $act->data)) { $s['edited'] = datetime_convert('UTC', 'UTC', $act->data['updated']); } - elseif (is_array($act->obj) && array_key_exists('updated', $act->obj)) { + elseif ($act->objprop('updated')) { $s['edited'] = datetime_convert('UTC', 'UTC', $act->obj['updated']); } if (array_key_exists('expires', $act->data)) { $s['expires'] = datetime_convert('UTC', 'UTC', $act->data['expires']); } - elseif (is_array($act->obj) && array_key_exists('expires', $act->obj)) { + elseif ($act->objprop('expires')) { $s['expires'] = datetime_convert('UTC', 'UTC', $act->obj['expires']); } - if ($act->type === 'Invite' && is_array($act->obj) && array_key_exists('type', $act->obj) && $act->obj['type'] === 'Event') { + if ($act->type === 'Invite' && $act->objprop('type') === 'Event') { $s['mid'] = $s['parent_mid'] = $act->id; } @@ -2298,9 +2304,18 @@ class Activity { $response_activity = true; $s['mid'] = $act->id; - $s['uuid'] = ((is_array($act->data) && isset($act->data['diaspora:guid'])) ? $act->data['diaspora:guid'] : uuid_from_url($s['mid'])); + $s['uuid'] = ((!empty($act->data['diaspora:guid'])) ? $act->data['diaspora:guid'] : uuid_from_url($s['mid'])); + + if ($act->objprop('inReplyTo')) { + $s['parent_mid'] = $act->objprop('inReplyTo'); + } + + $s['thr_parent'] = $act->objprop('id') ?: $act->obj; - $s['parent_mid'] = ((is_array($act->obj) && isset($act->obj['id'])) ? $act->obj['id'] : $act->obj); + if (empty($s['parent_mid']) || empty($s['thr_parent'])) { + logger('response activity without parent_mid or thr_parent'); + return; + } // over-ride the object timestamp with the activity @@ -2312,7 +2327,7 @@ class Activity { $s['edited'] = datetime_convert('UTC', 'UTC', $act->data['updated']); } - $obj_actor = ((isset($act->obj['actor'])) ? $act->obj['actor'] : $act->get_actor('attributedTo', $act->obj)); + $obj_actor = $act->objprop('actor') ?: $act->get_actor('attributedTo', $act->obj); if (!isset($obj_actor['id'])) { return false; @@ -2331,7 +2346,7 @@ class Activity { } // 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->objprop('type') === 'Event') || ($act->objprop('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']; } @@ -2366,7 +2381,7 @@ class Activity { $s['item_thread_top'] = 1; // it is a parent node - decode the comment policy info if present - if (isset($act->obj['commentPolicy'])) { + if ($act->objprop('commentPolicy')) { $until = strpos($act->obj['commentPolicy'], 'until='); if ($until !== false) { $s['comments_closed'] = datetime_convert('UTC', 'UTC', substr($act->obj['commentPolicy'], $until + 6)); @@ -2392,7 +2407,7 @@ class Activity { $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')); - if (isset($act->obj['quoteUrl'])) { + if ($act->objprop('quoteUrl')) { $quote_bbcode = self::get_quote_bbcode($act->obj['quoteUrl']); if ($s['body']) { @@ -2405,21 +2420,24 @@ class Activity { $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']) { + if ($act->type === 'Update' && $act->objprop('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' && (isset($act->obj['type']) && $act->obj['type'] === 'Tombstone'))) { $s['item_deleted'] = 1; } - $s['obj_type'] = self::activity_obj_decode_mapper($act->obj['type']); + if (isset($act->obj['type'])) { + $s['obj_type'] = self::activity_obj_decode_mapper($act->obj['type']); + } + if ($s['obj_type'] === ACTIVITY_OBJ_NOTE && $s['mid'] !== $s['parent_mid']) { $s['obj_type'] = ACTIVITY_OBJ_COMMENT; } $s['obj'] = $act->obj; - if (is_array($s['obj']) && array_path_exists('actor/id', $s['obj'])) { + if (array_path_exists('actor/id', $s['obj'])) { $s['obj']['actor'] = $s['obj']['actor']['id']; } @@ -2501,23 +2519,21 @@ class Activity { } } - if (array_key_exists('type', $act->obj)) { - // Objects that might have media attachments which aren't already provided in the content element. - // We'll check specific media objects separately. + // Objects that might have media attachments which aren't already provided in the content element. + // We'll check specific media objects separately. - if (in_array($act->obj['type'], ['Article', 'Document', 'Event', 'Note', 'Page', 'Place', 'Question']) && isset($s['attach']) && $s['attach']) { - $s = self::bb_attach($s); - } + if (in_array($act->objprop('type',''), ['Article', 'Document', 'Event', 'Note', 'Page', 'Place', 'Question']) && !empty($s['attach'])) { + $s = self::bb_attach($s); + } - 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 ($act->objprop('type') === 'Question' && in_array($act->type, ['Create', 'Update'])) { + if ($act->objprop('endTime')) { + $s['comments_closed'] = datetime_convert('UTC', 'UTC', $act->obj['endTime']); } } - if (array_key_exists('closed', $act->obj)) { + if ($act->objprop('closed')) { $s['comments_closed'] = datetime_convert('UTC', 'UTC', $act->obj['closed']); } @@ -2536,7 +2552,7 @@ class Activity { // 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->objprop('type') === 'Video') { $vtypes = [ 'video/mp4', @@ -2550,7 +2566,7 @@ class Activity { // try to find a poster to display on the video element - if (array_key_exists('icon',$act->obj)) { + if ($act->objprop('icon')) { if (is_array($act->obj['icon'])) { if (array_key_exists(0,$act->obj['icon'])) { $ptr = $act->obj['icon']; @@ -2571,7 +2587,7 @@ class Activity { $tag = (($poster) ? '[video poster="' . $poster . '"]' : '[video]' ); $ptr = null; - if (array_key_exists('url',$act->obj)) { + if ($act->objprop('url')) { if (is_array($act->obj['url'])) { if (array_key_exists(0,$act->obj['url'])) { $ptr = $act->obj['url']; @@ -2617,7 +2633,7 @@ class Activity { } } - if ($act->obj['type'] === 'Audio') { + if ($act->objprop('type') === 'Audio') { $atypes = [ 'audio/mpeg', @@ -2649,7 +2665,7 @@ class Activity { } - if ($act->obj['type'] === 'Image' && strpos($s['body'], 'zrl=') === false) { + if ($act->objprop('type') === 'Image' && strpos($s['body'], 'zrl=') === false) { $ptr = null; @@ -2678,7 +2694,7 @@ class Activity { } - if ($act->obj['type'] === 'Page' && !$s['body']) { + if ($act->objprop('type') === 'Page' && !$s['body']) { $ptr = null; $purl = EMPTY_STR; @@ -2718,7 +2734,7 @@ class Activity { } } - if (in_array($act->obj['type'], ['Note', 'Article', 'Page'])) { + if (in_array($act->objprop('type', ''), ['Note', 'Article', 'Page'])) { $ptr = null; if (array_key_exists('url', $act->obj)) { @@ -2753,10 +2769,8 @@ class Activity { $s['item_private'] = 0; } - if (is_array($act->obj)) { - if (array_key_exists('directMessage', $act->obj) && intval($act->obj['directMessage'])) { - $s['item_private'] = 2; - } + if ($act->objprop('directMessage')) { + $s['item_private'] = 2; } $ap_rawmsg = ''; @@ -2839,7 +2853,6 @@ class Activity { ]; call_hooks('decode_note', $hookinfo); - return $hookinfo['s']; } @@ -2847,6 +2860,7 @@ class Activity { 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; + $parent = null; // TODO: not implemented // Pleroma scrobbles can be really noisy and contain lots of duplicate activities. Disable them by default. @@ -2863,25 +2877,26 @@ class Activity { } $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", + $parent = q("select * from item where mid = '%s' and uid = %d", dbesc($item['parent_mid']), intval($channel['channel_id']) ); - if ($p) { + + // TODO: if we do not have a parent stop here and move the fetch to background? + + if ($parent && $parent[0]['item_wall']) { // set the owner to the owner of the parent - $item['owner_xchan'] = $p[0]['owner_xchan']; + $item['owner_xchan'] = $parent[0]['owner_xchan']; // quietly reject group comment boosts by group owner // (usually only sent via ActivityPub so groups will work on microblog platforms) // This catches those activities if they slipped in via a conversation fetch - if ($p[0]['parent_mid'] !== $item['parent_mid']) { + if ($parent[0]['parent_mid'] !== $item['parent_mid']) { if ($item['verb'] === 'Announce' && $item['author_xchan'] === $item['owner_xchan']) { logger('group boost activity by group owner rejected'); return; @@ -2891,7 +2906,7 @@ class Activity { // 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']) { + if ($parent[0]['owner_xchan'] === $channel['channel_hash']) { $allowed = false; } else { @@ -2900,7 +2915,7 @@ class Activity { } // TODO: not implemented - /*if (absolutely_no_comments($p[0])) { + /*if (absolutely_no_comments($parent[0])) { $allowed = false; }*/ @@ -2922,13 +2937,14 @@ class Activity { 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 ($parent && $parent[0]['obj_type'] === 'Question') { if ($item['obj_type'] === ACTIVITY_OBJ_COMMENT && $item['title'] && (!$item['body'])) { $item['obj_type'] = 'Answer'; } @@ -2941,7 +2957,7 @@ class Activity { if (perm_is_allowed($channel['channel_id'], ((!empty($item['item_fetched'])) ? $item['author_xchan'] : $observer_hash), 'send_stream') || $is_sys_channel) { $allowed = true; } - // TODO: not implemented + if ($permit_mentions) { $allowed = true; } @@ -3057,34 +3073,25 @@ class Activity { $item['item_verified'] = 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 (!$parent) { if (!plugin_is_installed('pubcrawl')) { return; } - 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); - } + $fetch = false; - if ($fetch) { - $parent = q("select * from item where mid = '%s' and uid = %d limit 1", - dbesc($item['parent_mid']), - intval($item['uid']) - ); - } + // 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", + dbesc($item['parent_mid']), + intval($item['uid']) + ); } } @@ -3093,6 +3100,7 @@ class Activity { return; } +/* if ($parent[0]['parent_mid'] !== $item['parent_mid']) { $item['thr_parent'] = $item['parent_mid']; } @@ -3100,6 +3108,7 @@ class Activity { $item['thr_parent'] = $parent[0]['parent_mid']; } $item['parent_mid'] = $parent[0]['parent_mid']; +*/ /* * @@ -3988,18 +3997,18 @@ class Activity { 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 ", + $hublocs = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash where hubloc_hash = '%s' and hubloc_deleted = 0 order by hubloc_id desc", 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 ", + $hublocs = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash where hubloc_id_url = '%s' and hubloc_deleted = 0 order by hubloc_id desc", 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 ", + $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 order by hubloc_id desc", dbesc($url), dbesc($url) ); @@ -4059,9 +4068,9 @@ class Activity { if ($act->is_valid()) { $content = self::get_content($act->obj); - $ret .= "[share author='" . urlencode($act->actor['name']) . + $ret .= "[share author='" . urlencode($act->actor['name'] ?? $act->actor['preferredUsername']) . "' profile='" . $act->actor['id'] . - "' avatar='" . $act->actor['icon']['url'] . + "' avatar='" . ($act->actor['icon']['url'] ?? z_root() . '/' . get_default_profile_photo(80)) . "' link='" . $act->obj['id'] . "' auth='" . ((is_matrix_url($act->actor['id'])) ? 'true' : 'false') . "' posted='" . $act->obj['published'] . diff --git a/Zotlabs/Lib/ActivityStreams.php b/Zotlabs/Lib/ActivityStreams.php index cfed53b3c..f07f99ac3 100644 --- a/Zotlabs/Lib/ActivityStreams.php +++ b/Zotlabs/Lib/ActivityStreams.php @@ -126,6 +126,7 @@ class ActivityStreams { if ((!$this->parent_id) && is_array($this->obj) && isset($this->obj['inReplyTo'])) { $this->parent_id = $this->obj['inReplyTo']; } + if ((!$this->parent_id) && is_array($this->obj) && isset($this->obj['id'])) { $this->parent_id = $this->obj['id']; } @@ -154,7 +155,7 @@ class ActivityStreams { * @return mixed */ public function objprop(string $property, mixed $default = false): mixed { - $x = $this->get_property_obj($property,$this->obj); + $x = $this->get_property_obj($property, $this->obj); return (isset($x)) ? $x : $default; } diff --git a/Zotlabs/Lib/Connect.php b/Zotlabs/Lib/Connect.php index 802bbe0f5..4de41526b 100644 --- a/Zotlabs/Lib/Connect.php +++ b/Zotlabs/Lib/Connect.php @@ -69,7 +69,8 @@ class Connect { $xchan_hash = ''; $sql_options = (($protocol) ? " and xchan_network = '" . dbesc($protocol) . "' " : ''); - $r = q("SELECT * FROM xchan LEFT JOIN hubloc ON xchan_hash = hubloc_hash WHERE ( xchan_hash = '%s' or xchan_url = '%s' or xchan_addr = '%s') $sql_options ORDER BY hubloc_id DESC", + // We need both, the xchan and the hubloc here hence use JOIN instead of LEFT JOIN + $r = q("SELECT * FROM xchan JOIN hubloc ON xchan_hash = hubloc_hash WHERE ( xchan_hash = '%s' or xchan_url = '%s' or xchan_addr = '%s') $sql_options ORDER BY hubloc_id DESC", dbesc($url), dbesc($url), dbesc($url) diff --git a/Zotlabs/Lib/Libsync.php b/Zotlabs/Lib/Libsync.php index d52b501e4..0d383c697 100644 --- a/Zotlabs/Lib/Libsync.php +++ b/Zotlabs/Lib/Libsync.php @@ -141,7 +141,7 @@ class Libsync { $total = count($synchubs); foreach ($synchubs as $hub) { - $hash = random_string(); + $hash = new_uuid(); $n = Libzot::build_packet($channel, 'sync', $env_recips, json_encode($info), 'hz', $hub['hubloc_sitekey'], $hub['site_crypto']); Queue::insert([ 'hash' => $hash, @@ -771,7 +771,12 @@ class Libsync { static function sync_locations($sender, $arr) { - $ret = []; + $ret = [ + 'change_message' => '', + 'changed' => false, + 'message' => '' + ]; + $what = ''; $changed = false; @@ -786,7 +791,7 @@ class Libsync { if (isset($arr['locations']) && $arr['locations']) { - $xisting = q("select * from hubloc where hubloc_hash = '%s'", + $xisting = q("select * from hubloc where hubloc_hash = '%s' and hubloc_deleted = 0", dbesc($sender['hash']) ); diff --git a/Zotlabs/Lib/Libzot.php b/Zotlabs/Lib/Libzot.php index a8334595f..093670338 100644 --- a/Zotlabs/Lib/Libzot.php +++ b/Zotlabs/Lib/Libzot.php @@ -328,7 +328,7 @@ class Libzot { logger('zot-info: ' . print_r($record, true), LOGGER_DATA, LOG_DEBUG); - $x = self::import_xchan($record['data'], (($force) ? UPDATE_FLAGS_FORCED : UPDATE_FLAGS_UPDATED)); + $x = self::import_xchan($record['data']); if (!$x['success']) { return false; @@ -635,23 +635,18 @@ class Libzot { * all internal data structures which need to be updated as a result. * * @param array $arr => json_decoded discovery packet - * @param int $ud_flags - * Determines whether to create a directory update record if any changes occur, default is UPDATE_FLAGS_UPDATED - * $ud_flags = UPDATE_FLAGS_FORCED indicates a forced refresh where we unconditionally create a directory update record - * this typically occurs once a month for each channel as part of a scheduled ping to notify the directory - * that the channel still exists - * @param array $ud_arr - * If set [typically by update_directory_entry()] indicates a specific update table row and more particularly - * contains a particular address (ud_addr) which needs to be updated in that table. - * + * @return array An associative array with: * * \e boolean \b success boolean true or false * * \e string \b message (optional) error string only if success is false */ - static function import_xchan($arr, $ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) { + static function import_xchan($arr) { - $ret = ['success' => false]; + $ret = [ + 'success' => false, + 'message' => '' + ]; if (!is_array($arr)) { logger('Not an array: ' . print_r($arr, true), LOGGER_DEBUG); @@ -665,7 +660,7 @@ class Libzot { */ call_hooks('import_xchan', $arr); - $dirmode = intval(get_config('system', 'directory_mode')); + $dirmode = intval(get_config('system', 'directory_mode', DIRECTORY_MODE_NORMAL)); $changed = false; $what = ''; @@ -683,15 +678,11 @@ class Libzot { $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'])) { - logger('Unable to verify channel signature for ' . $arr['primary_location']['address']); - return $ret; - } - else { + if (self::verify($arr['id'], $arr['id_sig'], $arr['public_key'])) { $verified = true; } - - if (!$verified) { + else { + logger('Unable to verify channel signature for ' . $xchan_hash . ' (' . $arr['primary_location']['address']) . ')'; $ret['message'] = t('Unable to verify channel signature'); return $ret; } @@ -714,6 +705,7 @@ class Libzot { 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 */ @@ -730,7 +722,7 @@ class Libzot { $hidden_changed = 1; if (isset($arr['adult_content']) && intval($r[0]['xchan_selfcensored']) != intval($arr['adult_content'])) $adult_changed = 1; - if (isset($arr['xchan_deleted']) && intval($r[0]['xchan_deleted']) != intval($arr['deleted'])) + if (isset($arr['deleted']) && intval($r[0]['xchan_deleted']) != intval($arr['deleted'])) $deleted_changed = 1; // new style 6-MAR-2019 @@ -920,30 +912,26 @@ class Libzot { $s = Libsync::sync_locations($arr, $arr); if ($s) { - if (isset($s['change_message'])) + if (!empty($s['change_message'])) $what .= $s['change_message']; - if (isset($s['changed'])) - $changed = $s['changed']; - if (isset($s['message'])) + if (!empty($s['changed'])) + $changed .= $s['changed']; + if (!empty($s['message'])) $ret['message'] .= $s['message']; } - // Which entries in the update table are we interested in updating? - - $address = (($ud_arr && $ud_arr['ud_addr']) ? $ud_arr['ud_addr'] : $arr['primary_location']['address']); - - - // 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)) + if (array_key_exists('site', $arr) && array_key_exists('realm', $arr['site']) && (strpos($arr['site']['realm'], $realm) === false)) { $other_realm = true; + } + $address = $arr['primary_location']['url']; - if ($dirmode != DIRECTORY_MODE_NORMAL) { + // Are we a directory server of some kind? + + 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 @@ -951,7 +939,7 @@ class Libzot { // 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); + $profile_changed = Libzotdir::import_directory_profile($xchan_hash, $arr['profile']); if ($profile_changed) { $what .= 'profile '; $changed = true; @@ -977,21 +965,10 @@ class Libzot { } } - 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); - } - 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 (ud_flags & %d) = 0 ", - intval(UPDATE_FLAGS_UPDATED), - dbesc($address), - intval(UPDATE_FLAGS_UPDATED) - ); - } + // update updates if anything changed bump the ud_date + Libzotdir::update($xchan_hash, $address, $changed); - if (!x($ret, 'message')) { + if (empty($ret['message'])) { $ret['success'] = true; $ret['hash'] = $xchan_hash; } @@ -1398,7 +1375,7 @@ class Libzot { $uids = ids_to_querystr($uids, 'uid'); $z = q("SELECT channel_hash FROM channel WHERE channel_hash != '%s' AND channel_id IN ($uids)", - dbesc($msg['sender']) + dbesc($env['sender']) ); if ($z) { @@ -1746,7 +1723,7 @@ class Libzot { // this is just an exercise in futility. if (perm_is_allowed($channel['channel_id'], $sender, 'send_stream')) { - self::fetch_conversation($channel, $arr['parent_mid']); + Master::Summon(['Zotconvo', $channel['channel_id'], $arr['parent_mid']]); } continue; @@ -2037,18 +2014,12 @@ class Libzot { continue; } - $r = q("select hubloc_hash, hubloc_network from hubloc where hubloc_id_url = '%s' order by hubloc_id desc", - dbesc($AS->actor['id']) - ); - + $r = Activity::get_actor_hublocs($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, hubloc_network from hubloc where hubloc_id_url = '%s'", - dbesc($AS->actor['id']) - ); + $r = Activity::get_actor_hublocs($AS->actor['id']); $r = self::zot_record_preferred($r); } if (!$r) { @@ -2058,22 +2029,29 @@ class Libzot { } if (isset($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; + $ro = Activity::get_actor_hublocs($AS->obj['actor']['id']); + $ro = self::zot_record_preferred($ro); + if (!$ro) { + $y = import_author_xchan(['url' => $AS->obj['actor']['id']]); + if ($y) { + $ro = Activity::get_actor_hublocs($AS->obj['actor']['id']); + $ro = self::zot_record_preferred($ro); + } + if (!$ro) { + logger('FOF Activity: no obj actor'); + continue; + } } } $arr = Activity::decode_note($AS); if (!$arr) { + logger('FOF Activity: could not decode'); continue; } - if ($r) { - $arr['author_xchan'] = $r['hubloc_hash']; - } + $arr['author_xchan'] = $r['hubloc_hash']; if ($signer) { $arr['owner_xchan'] = $signer[0]['hubloc_hash']; @@ -2377,19 +2355,6 @@ class Libzot { 'deleted' => (intval($hub['hubloc_deleted']) ? true : false) ]; - // version compatibility tweaks - - if (!strpos($z['url_sig'], '.')) { - $z['url_sig'] = 'sha256.' . $z['url_sig']; - } - - if (!$z['id_url']) { - $z['id_url'] = $z['url'] . '/channel/' . substr($z['address'], 0, strpos($z['address'], '@')); - } - if (!$z['site_id']) { - $z['site_id'] = Libzot::make_xchan_hash($z['url'], $z['sitekey']); - } - $ret[] = $z; } } @@ -2761,12 +2726,12 @@ class Libzot { $e = $r[0]; $id = $e['channel_id']; - $sys_channel = (intval($e['channel_system']) ? true : false); + $sys_channel = ((empty($e['channel_system'])) ? false : true); $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); - $deleted = (intval($e['xchan_deleted']) ? true : false); + $deleted = ((empty($e['xchan_deleted'])) ? false : true); if ($deleted || $censored || $sys_channel) $searchable = false; @@ -2818,8 +2783,8 @@ class Libzot { // Communication details - $ret['id'] = $e['xchan_guid']; - $ret['id_sig'] = self::sign($e['xchan_guid'], $e['channel_prvkey']); + $ret['id'] = $e['channel_guid']; + $ret['id_sig'] = self::sign($e['channel_guid'], $e['channel_prvkey']); $ret['primary_location'] = [ 'address' => $e['xchan_addr'], @@ -2828,10 +2793,10 @@ class Libzot { 'follow_url' => $e['xchan_follow'], ]; - $ret['public_key'] = $e['xchan_pubkey']; + $ret['public_key'] = $e['channel_pubkey']; $ret['signing_algorithm'] = 'rsa-sha256'; $ret['username'] = $e['channel_address']; - $ret['name'] = $e['xchan_name']; + $ret['name'] = $e['channel_name']; $ret['name_updated'] = $e['xchan_name_date']; $ret['photo'] = [ 'url' => $e['xchan_photo_l'], diff --git a/Zotlabs/Lib/Libzotdir.php b/Zotlabs/Lib/Libzotdir.php index dfedd0bc8..58138850c 100644 --- a/Zotlabs/Lib/Libzotdir.php +++ b/Zotlabs/Lib/Libzotdir.php @@ -172,13 +172,12 @@ class Libzotdir { } /** - * @brief Checks the directory mode of this hub. + * @brief fetches updates from known directories * * 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 - * directly if the rater's signature matches. + * a directory sync packet. Store these all in the DB. + * In the case of updates, we will query each of them asynchronously from a poller task. * * @param int $dirmode; */ @@ -233,6 +232,8 @@ class Libzotdir { if (! $r) return; + $dir_trusted_hosts = array_merge(get_directory_fallback_servers(), get_config('system', 'trusted_directory_servers', [])); + foreach ($r as $rr) { if (! $rr['site_directory']) continue; @@ -264,31 +265,65 @@ 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'])) { + if (empty($t['hash']) || empty($t['host']) || empty($t['address'])) { continue; } - $r = q("select * from updates where ud_guid = '%s' limit 1", - dbesc($t['transaction_id']) + $r = q("select * from updates where ud_hash = '%s' limit 1", + dbesc($t['hash']) ); - if($r) - continue; - $ud_flags = 0; - if (is_array($t['flags']) && in_array('deleted',$t['flags'])) - $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']), - dbesc($t['transaction_id']), - dbesc($t['timestamp']), - intval($ud_flags), - dbesc($t['address']) - ); + if ($r) { + $update = 0; + + // no need to look at updates that originated from our own site + if ($t['host'] === z_root()) { + continue; + } + + // there is more recent xchan information + if ($r[0]['ud_date'] <= $t['timestamp']) { + $update = 1; + } + + // the host is trusted and flags have changed - update flags immediately + if (in_array($t['host'], $dir_trusted_hosts) && + $rr['site_url'] === $t['host'] && + intval($r[0]['ud_flags']) !== intval($t['flags'])) { + + q("UPDATE updates SET ud_update = %d, ud_flags = %d WHERE ud_id = %d", + intval($update), + intval($t['flags']), + dbesc($r[0]['ud_id']) + ); + + q("UPDATE xchan SET xchan_censored = %d WHERE xchan_hash = '%s'", + intval($t['flags']), + dbesc($r[0]['ud_hash']) + ); + + continue; + } + + if (!$update) { + continue; + } + + q("UPDATE updates SET ud_update = %d WHERE ud_id = %d", + intval($update), + dbesc($r[0]['ud_id']) + ); + } + else { + q("insert into updates ( ud_hash, ud_host, ud_date, ud_addr, ud_update, ud_flags ) + values ( '%s', '%s', '%s', '%s', 1, %d) ", + dbesc($t['hash']), + dbesc($t['host']), + dbesc($t['timestamp']), + dbesc($t['address']), + dbesc(in_array($t['host'], $dir_trusted_hosts) ? $t['flags'] : 0) + ); + } } } } @@ -303,8 +338,9 @@ class Libzotdir { * * Ignore updating records marked as deleted. * - * If successful, sets ud_last in the DB to the current datetime for this + * If successful, sets ud_updated in the DB to the current datetime for this * reddress/webbie. + * Else update ud_last so we can stop trying after 7 days (Daemon/Poller.php) * * @param array $ud Entry from update table */ @@ -313,31 +349,47 @@ class Libzotdir { logger('update_directory_entry: ' . print_r($ud,true), LOGGER_DATA); - if ($ud['ud_addr'] && (! ($ud['ud_flags'] & UPDATE_FLAGS_DELETED))) { - $success = false; - $zf = []; + // TODO: remove this check after all directory servers have version > 8.4 + // ud_addr will always be the channel url at that time + $href = ((strpos($ud['ud_addr'], '://') === false) ? Webfinger::zot_url(punify($ud['ud_addr'])) : punify($ud['ud_addr'])); + if($href) { + $zf = Zotfinger::exec($href); + if($zf && array_path_exists('signature/signer',$zf) && $zf['signature']['signer'] === $href && intval($zf['signature']['header_valid'])) { + $xc = Libzot::import_xchan($zf['data']); + + // xchan_hash mismatch - this can happen after a site re-install at the same url + if ($xc['success'] && $xc['hash'] !== $ud['ud_hash']) { + self::delete_by_hash($ud['ud_hash']); + } + + // backwards compatibility: Libzot::import_xchan(), where self::update() is called, + // will fail with versions < 8.4 if the channel has been locally deleted. + // In this case we will update the updates record here without bumping the date + // since we could not verify if anything changed. + if (!$xc['success'] && !empty($zf['data']['deleted_locally'])) { + self::update($ud['ud_hash'], $ud['ud_addr'], false); + } - $href = Webfinger::zot_url(punify($ud['ud_addr'])); - if($href) { - $zf = Zotfinger::exec($href); - } - 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']) { + if(!empty($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']) ); } - q("update updates set ud_last = '%s' where ud_addr = '%s'", - dbesc(datetime_convert()), - dbesc($ud['ud_addr']) - ); + return true; } } + + q("UPDATE updates SET ud_addr = '%s', ud_last = '%s' WHERE ud_hash = '%s'", + dbesc($href ? $href : $ud['ud_addr']), + dbesc(datetime_convert()), + dbesc($ud['ud_hash']) + ); + + return false; } @@ -352,85 +404,78 @@ class Libzotdir { */ static function local_dir_update($uid, $force) { + logger('local_dir_update uid: ' . $uid, LOGGER_DEBUG); - - logger('local_dir_update: uid: ' . $uid, LOGGER_DEBUG); - - $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", + $p = q("select channel.channel_hash, channel_address, channel_timezone, profile.*, xchan.xchan_hidden, xchan.xchan_url from profile left join channel on channel_id = uid left join xchan on channel_hash = xchan_hash where profile.uid = %d and profile.is_default = 1", intval($uid) ); - $profile = array(); + if (!$p) { + logger('profile not found'); + return; + } + + $profile = []; $profile['encoding'] = 'zot'; - if ($p) { - $hash = $p[0]['channel_hash']; - - $profile['description'] = $p[0]['pdesc']; - $profile['birthday'] = $p[0]['dob']; - if ($age = age($p[0]['dob'],$p[0]['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)) - $tags[] = trim($kk); - - if ($tags) - $profile['keywords'] = $tags; - } + $hash = $p[0]['channel_hash']; + + $profile['description'] = $p[0]['pdesc']; + $profile['birthday'] = $p[0]['dob']; + if ($age = age($p[0]['dob'],$p[0]['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)) + $tags[] = trim($kk); + + if ($tags) + $profile['keywords'] = $tags; + } - $hidden = (1 - intval($p[0]['publish'])); + $hidden = (1 - intval($p[0]['publish'])); - logger('hidden: ' . $hidden); + logger('hidden: ' . $hidden); - $r = q("select xchan_hidden from xchan where xchan_hash = '%s'", - dbesc($p[0]['channel_hash']) + if(intval($p[0]['xchan_hidden']) !== $hidden) { + q("update xchan set xchan_hidden = %d where xchan_hash = '%s'", + intval($hidden), + dbesc($hash) ); + } - if(intval($r[0]['xchan_hidden']) != $hidden) { - $r = q("update xchan set xchan_hidden = %d where xchan_hash = '%s'", - intval($hidden), - dbesc($hash) - ); - } - - $arr = [ 'channel_id' => $uid, 'hash' => $hash, 'profile' => $profile ]; - call_hooks('local_dir_update', $arr); - - $address = channel_reddress($p[0]); - - if (perm_is_allowed($uid, '', 'view_profile')) { - self::import_directory_profile($hash, $arr['profile'], $address, 0); - } - else { - // they may have made it private - q("delete from xprof where xprof_hash = '%s'", - dbesc($hash) - ); - q("delete from xtag where xtag_hash = '%s'", - dbesc($hash) - ); - } + $arr = [ 'channel_id' => $uid, 'hash' => $hash, 'profile' => $profile ]; + call_hooks('local_dir_update', $arr); + if (perm_is_allowed($uid, '', 'view_profile')) { + self::import_directory_profile($hash, $arr['profile']); + } + else { + // they may have made it private + q("delete from xprof where xprof_hash = '%s'", + dbesc($hash) + ); + q("delete from xtag where xtag_hash = '%s'", + dbesc($hash) + ); } - $ud_hash = random_string() . '@' . \App::get_hostname(); - self::update_modtime($hash, $ud_hash, channel_reddress($p[0]),(($force) ? UPDATE_FLAGS_FORCED : UPDATE_FLAGS_UPDATED)); + self::update($hash, $p[0]['xchan_url']); } @@ -440,13 +485,10 @@ class Libzotdir { * * @param string $hash * @param array $profile - * @param string $addr - * @param number $ud_flags (optional) UPDATE_FLAGS_UPDATED - * @param number $suppress_update (optional) default 0 * @return boolean $updated if something changed */ - static function import_directory_profile($hash, $profile, $addr, $ud_flags = UPDATE_FLAGS_UPDATED, $suppress_update = 0) { + static function import_directory_profile($hash, $profile) { logger('import_directory_profile', LOGGER_DEBUG); if (! $hash) @@ -479,7 +521,12 @@ class Libzotdir { $clean = array(); if (array_key_exists('keywords', $profile) and is_array($profile['keywords'])) { self::import_directory_keywords($hash,$profile['keywords']); + foreach ($profile['keywords'] as $kw) { + if (in_array($kw, $clean)) { + continue; + } + $kw = trim(htmlspecialchars($kw,ENT_COMPAT, 'UTF-8', false)); $kw = trim($kw, ','); $clean[] = $kw; @@ -586,9 +633,6 @@ class Libzotdir { */ call_hooks('import_directory_profile', $d); - if (($d['update']) && (! $suppress_update)) - self::update_modtime($arr['xprof_hash'],random_string() . '@' . \App::get_hostname(), $addr, $ud_flags); - return $d['update']; } @@ -606,6 +650,10 @@ class Libzotdir { dbesc($hash) ); + $xchan = q("select xchan_censored from xchan where xchan_hash = '%s'", + dbesc($hash) + ); + if($r) { foreach($r as $rr) $existing[] = $rr['xtag_term']; @@ -613,6 +661,10 @@ class Libzotdir { $clean = array(); foreach($keywords as $kw) { + if (in_array($kw, $clean)) { + continue; + } + $kw = trim(htmlspecialchars($kw,ENT_COMPAT, 'UTF-8', false)); $kw = trim($kw, ','); $clean[] = $kw; @@ -627,9 +679,10 @@ class Libzotdir { } foreach($clean as $x) { if(! in_array($x, $existing)) { - $r = q("insert into xtag ( xtag_hash, xtag_term, xtag_flags) values ( '%s' ,'%s', 0 )", + $r = q("insert into xtag ( xtag_hash, xtag_term, xtag_flags) values ( '%s' ,'%s', %d )", dbesc($hash), - dbesc($x) + dbesc($x), + intval($xchan[0]['xchan_censored']) ); } } @@ -639,13 +692,12 @@ class Libzotdir { /** * @brief * - * @param string $hash - * @param string $guid - * @param string $addr - * @param int $flags (optional) default 0 + * @param string $hash the channel hash + * @param string $addr the channel url + * @param bool $bump_date (optional) default true */ - static function update_modtime($hash, $guid, $addr, $flags = 0) { + static function update($hash, $addr, $bump_date = true, $flag = null) { $dirmode = intval(get_config('system', 'directory_mode')); @@ -653,26 +705,70 @@ class Libzotdir { return; } - if (empty($hash) || empty($guid) || empty($addr)) { + if (empty($hash) || 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' )", - dbesc($hash), - dbesc($guid), - dbesc(datetime_convert()), - intval($flags), - dbesc($addr) - ); + $u = q("SELECT * FROM updates WHERE ud_hash = '%s' LIMIT 1", + dbesc($hash) + ); + + $date_sql = ''; + if ($bump_date) { + $date_sql = "ud_date = '" . dbesc(datetime_convert()) . "',"; } - else { - q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and not (ud_flags & %d)>0 ", - intval(UPDATE_FLAGS_UPDATED), + + $flag_sql = ''; + if ($flag !== null) { + $flag_sql = "ud_flags = '" . intval($flag) . "',"; + } + + + if ($u) { + $x = q("UPDATE updates SET $date_sql $flag_sql ud_last = '%s', ud_host = '%s', ud_addr = '%s', ud_update = 0 WHERE ud_id = %d", + dbesc(NULL_DATE), + dbesc(z_root()), dbesc($addr), - intval(UPDATE_FLAGS_UPDATED) + intval($u[0]['ud_id']) ); + + return; + } + + q("INSERT INTO updates (ud_hash, ud_host, ud_date, ud_addr, ud_flags) VALUES ( '%s', '%s', '%s', '%s', %d )", + dbesc($hash), + dbesc(z_root()), + dbesc(datetime_convert()), + dbesc($addr), + intval($flag) + ); + + return; + + } + + + /** + * @brief deletes a entry in updates by hash + * + * @param string $hash the channel hash + * @return boolean + */ + + static function delete_by_hash($hash) { + if (!$hash) { + return false; + } + + $x = q("DELETE FROM updates WHERE ud_hash = '%s'", + dbesc($hash) + ); + + if ($x) { + return true; } + + return false; } } diff --git a/Zotlabs/Lib/Queue.php b/Zotlabs/Lib/Queue.php index 348a2a079..942a633ef 100644 --- a/Zotlabs/Lib/Queue.php +++ b/Zotlabs/Lib/Queue.php @@ -57,7 +57,6 @@ class Queue { outq_priority = outq_priority + %d, outq_scheduled = '%s' WHERE outq_hash = '%s'", - dbesc(datetime_convert()), intval($add_priority), dbesc($next), @@ -85,29 +84,16 @@ class Queue { // entries still exist for it. This fixes an issue where one immediate delivery left everything // else for that site undeliverable since all the other entries had been pushed far into the future. - $x = null; - $sql_quirks = ((get_config('system', 'db_skip_locked_supported')) ? 'SKIP LOCKED' : 'NOWAIT'); - - q("START TRANSACTION"); - - $r = q("SELECT outq_hash FROM outq WHERE outq_posturl = '%s' LIMIT 1 FOR UPDATE $sql_quirks", + $r = q("SELECT outq_hash, outq_posturl FROM outq WHERE outq_posturl = '%s' LIMIT 1", dbesc($record[0]['outq_posturl']) ); if ($r) { - $x = q("UPDATE outq SET outq_scheduled = '%s' WHERE outq_hash = '%s'", - dbesc(datetime_convert()), - dbesc($r[0]['outq_hash']) + $hashes = ids_to_querystr($r, 'outq_hash', true); + $x = q("UPDATE outq SET outq_scheduled = '%s' WHERE outq_hash IN ($hashes)", + dbesc(datetime_convert()) ); } - - if ($x) { - q("COMMIT"); - } - else { - q("ROLLBACK"); - } - } } @@ -260,7 +246,7 @@ class Queue { if($result['success']) { logger('deliver: remote zot delivery succeeded to ' . $outq['outq_posturl']); - Libzot::process_response($outq['outq_posturl'],$result, $outq); + Libzot::process_response($outq['outq_posturl'], $result, $outq); } else { logger('deliver: remote zot delivery failed to ' . $outq['outq_posturl']); diff --git a/Zotlabs/Lib/QueueWorker.php b/Zotlabs/Lib/QueueWorker.php index a1c13ef8a..1c74b42d8 100644 --- a/Zotlabs/Lib/QueueWorker.php +++ b/Zotlabs/Lib/QueueWorker.php @@ -24,7 +24,8 @@ class QueueWorker { // Exceptions for processtimeout ($workermaxage) value. // Currently the value is overriden with 3600 seconds (1h). public static $long_running_cmd = [ - 'Queue' + 'Queue', + 'Expire' ]; private static function qstart() { @@ -147,6 +148,10 @@ class QueueWorker { ); if ($r) { + // TODO: some long running services store their pid in config.procid.daemon + // we could possibly check if a pid exist and check if the process is still alive + // prior to reseting workerq_reservationid + $ids = ids_to_querystr($r, 'workerq_id'); $u = dbq("update workerq set workerq_reservationid = null where workerq_id in ($ids)"); } diff --git a/Zotlabs/Lib/ThreadItem.php b/Zotlabs/Lib/ThreadItem.php index d1b386c42..cf877ed92 100644 --- a/Zotlabs/Lib/ThreadItem.php +++ b/Zotlabs/Lib/ThreadItem.php @@ -197,7 +197,7 @@ class ThreadItem { $response_verbs = array('like'); if(feature_enabled($conv->get_profile_owner(),'dislike')) $response_verbs[] = 'dislike'; - if($item['obj_type'] === ACTIVITY_OBJ_EVENT) { + if(in_array($item['obj_type'], ['Event', ACTIVITY_OBJ_EVENT])) { $response_verbs[] = 'attendyes'; $response_verbs[] = 'attendno'; $response_verbs[] = 'attendmaybe'; @@ -299,7 +299,7 @@ class ThreadItem { } $has_event = false; - if(($item['obj_type'] === ACTIVITY_OBJ_EVENT) && $conv->get_profile_owner() == local_channel()) + if((in_array($item['obj_type'], ['Event', ACTIVITY_OBJ_EVENT])) && $conv->get_profile_owner() == local_channel()) $has_event = true; $like = []; diff --git a/Zotlabs/Module/Admin/Security.php b/Zotlabs/Module/Admin/Security.php index 16045f9ed..499572170 100644 --- a/Zotlabs/Module/Admin/Security.php +++ b/Zotlabs/Module/Admin/Security.php @@ -7,12 +7,12 @@ class Security { function post() { check_form_security_token_redirectOnErr('/admin/security', 'admin_security'); - + $allowed_email = ((x($_POST,'allowed_email')) ? notags(trim($_POST['allowed_email'])) : ''); $not_allowed_email = ((x($_POST,'not_allowed_email')) ? notags(trim($_POST['not_allowed_email'])) : ''); set_config('system','allowed_email', $allowed_email); - set_config('system','not_allowed_email', $not_allowed_email); + set_config('system','not_allowed_email', $not_allowed_email); $block_public = ((x($_POST,'block_public')) ? True : False); set_config('system','block_public',$block_public); @@ -25,22 +25,22 @@ class Security { $ws = $this->trim_array_elems(explode("\n",$_POST['whitelisted_sites'])); set_config('system','whitelisted_sites',$ws); - + $bs = $this->trim_array_elems(explode("\n",$_POST['blacklisted_sites'])); set_config('system','blacklisted_sites',$bs); - + $wc = $this->trim_array_elems(explode("\n",$_POST['whitelisted_channels'])); set_config('system','whitelisted_channels',$wc); - + $bc = $this->trim_array_elems(explode("\n",$_POST['blacklisted_channels'])); set_config('system','blacklisted_channels',$bc); - + $embed_sslonly = ((x($_POST,'embed_sslonly')) ? True : False); set_config('system','embed_sslonly',$embed_sslonly); - + $we = $this->trim_array_elems(explode("\n",$_POST['embed_allow'])); set_config('system','embed_allow',$we); - + $be = $this->trim_array_elems(explode("\n",$_POST['embed_deny'])); set_config('system','embed_deny',$be); @@ -49,47 +49,54 @@ class Security { $inline_pdf = ((x($_POST,'inline_pdf')) ? intval($_POST['inline_pdf']) : 0); set_config('system', 'inline_pdf' , $inline_pdf); - + $ts = ((x($_POST,'transport_security')) ? True : False); set_config('system','transport_security_header',$ts); $cs = ((x($_POST,'content_security')) ? True : False); set_config('system','content_security_policy',$cs); + $trusted_directory_servers = $this->trim_array_elems(explode("\n", $_POST['trusted_directory_servers'])); + set_config('system', 'trusted_directory_servers', $trusted_directory_servers); + goaway(z_root() . '/admin/security'); } - - + + function get() { - + $whitesites = get_config('system','whitelisted_sites'); $whitesites_str = ((is_array($whitesites)) ? implode("\n",$whitesites) : ''); - + $blacksites = get_config('system','blacklisted_sites'); $blacksites_str = ((is_array($blacksites)) ? implode("\n",$blacksites) : ''); - - + + $whitechannels = get_config('system','whitelisted_channels'); $whitechannels_str = ((is_array($whitechannels)) ? implode("\n",$whitechannels) : ''); - + $blackchannels = get_config('system','blacklisted_channels'); $blackchannels_str = ((is_array($blackchannels)) ? implode("\n",$blackchannels) : ''); - - + + $whiteembeds = get_config('system','embed_allow'); $whiteembeds_str = ((is_array($whiteembeds)) ? implode("\n",$whiteembeds) : ''); - + $blackembeds = get_config('system','embed_deny'); $blackembeds_str = ((is_array($blackembeds)) ? implode("\n",$blackembeds) : ''); - + + $trusted_directory_servers = get_config('system', 'trusted_directory_servers'); + $trusted_directory_servers_str = ((is_array($trusted_directory_servers)) ? implode("\n", $trusted_directory_servers) : ''); + $is_dir = (intval(get_config('system', 'directory_mode', DIRECTORY_MODE_NORMAL)) !== DIRECTORY_MODE_NORMAL); + $embed_coop = intval(get_config('system','embed_coop')); - + if((! $whiteembeds) && (! $blackembeds)) { $embedhelp1 = t("By default, unfiltered HTML is allowed in embedded media. This is inherently insecure."); } - $embedhelp2 = t("The recommended setting is to only allow unfiltered HTML from the following sites:"); + $embedhelp2 = t("The recommended setting is to only allow unfiltered HTML from the following sites:"); $embedhelp3 = t("https://youtube.com/<br />https://www.youtube.com/<br />https://youtu.be/<br />https://vimeo.com/<br />https://soundcloud.com/<br />"); $embedhelp4 = t("All other embedded content will be filtered, <strong>unless</strong> embedded content from that site is explicitly blocked."); @@ -99,7 +106,7 @@ class Security { '$page' => t('Security'), '$form_security_token' => get_form_security_token('admin_security'), '$block_public' => array('block_public', t("Block public"), get_config('system','block_public'), t("Check to block public access to all otherwise public personal pages on this site unless you are currently authenticated.")), - '$cloud_noroot' => [ 'cloud_noroot', t('Provide a cloud root directory'), 1 - intval(get_config('system','cloud_disable_siteroot')), t('The cloud root directory lists all channel names which provide public files') ], + '$cloud_noroot' => [ 'cloud_noroot', t('Provide a cloud root directory'), 1 - intval(get_config('system','cloud_disable_siteroot')), t('The cloud root directory lists all channel names which provide public files') ], '$cloud_disksize' => [ 'cloud_disksize', t('Show total disk space available to cloud uploads'), intval(get_config('system','cloud_report_disksize')), '' ], '$transport_security' => array('transport_security', t('Set "Transport Security" HTTP header'),intval(get_config('system','transport_security_header')),''), '$content_security' => array('content_security', t('Set "Content Security Policy" HTTP header'),intval(get_config('system','content_security_policy')),''), @@ -115,6 +122,8 @@ class Security { '$thumbnail_security' => [ 'thumbnail_security', t("Allow SVG thumbnails in file browser"), get_config('system','thumbnail_security',0), t("WARNING: SVG images may contain malicious code.") ], '$inline_pdf' => [ 'inline_pdf', t("Allow embedded (inline) PDF files"), get_config('system','inline_pdf',0), '' ], + '$trusted_directory_servers' => (($is_dir) ? ['trusted_directory_servers', t('Additional trusted directory server URLs'), $trusted_directory_servers_str, t('Accept directory flags (spam, nsfw) from those servers. One per line like https://example.tld')] : ''), + // '$embed_coop' => array('embed_coop', t('Cooperative embed security'), $embed_coop, t('Enable to share embed security with other compatible sites/hubs')), '$submit' => t('Submit') @@ -124,7 +133,7 @@ class Security { function trim_array_elems($arr) { $narr = array(); - + if($arr && is_array($arr)) { for($x = 0; $x < count($arr); $x ++) { $y = trim($arr[$x]); @@ -134,6 +143,6 @@ class Security { } return $narr; } - - + + } diff --git a/Zotlabs/Module/Admin/Site.php b/Zotlabs/Module/Admin/Site.php index 42cf064c9..b24821b28 100644 --- a/Zotlabs/Module/Admin/Site.php +++ b/Zotlabs/Module/Admin/Site.php @@ -67,7 +67,6 @@ class Site { $open_pubstream = ((x($_POST,'open_pubstream')) ? True : False); $login_on_homepage = ((x($_POST,'login_on_homepage')) ? True : False); $enable_context_help = ((x($_POST,'enable_context_help')) ? True : False); - $global_directory = ((x($_POST,'directory_submit_url')) ? notags(trim($_POST['directory_submit_url'])) : ''); $no_community_page = !((x($_POST,'no_community_page')) ? True : False); $default_expire_days = ((array_key_exists('default_expire_days',$_POST)) ? intval($_POST['default_expire_days']) : 0); $active_expire_days = ((array_key_exists('active_expire_days',$_POST)) ? intval($_POST['active_expire_days']) : 7); @@ -181,7 +180,7 @@ class Site { set_config('system', 'admininfo', $admininfo); } set_config('system','siteinfo',$siteinfo); - set_config('system', 'language', $language); + //set_config('system', 'language', $language); set_config('system', 'theme', $theme); // if ( $theme_mobile === '---' ) { // del_config('system', 'mobile_theme'); @@ -206,11 +205,6 @@ class Site { set_config('system','site_firehose', $site_firehose); set_config('system','open_pubstream', $open_pubstream); //set_config('system','force_queue_threshold', $force_queue); - if ($global_directory == '') { - del_config('system', 'directory_submit_url'); - } else { - set_config('system', 'directory_submit_url', $global_directory); - } set_config('system','no_community_page', $no_community_page); set_config('system','no_utf', $no_utf); @@ -283,7 +277,7 @@ class Site { } $dir_choices = null; - $dirmode = get_config('system','directory_mode'); + $dirmode = get_config('system', 'directory_mode', DIRECTORY_MODE_NORMAL); $realm = get_directory_realm(); // directory server should not be set or settable unless we are a directory client @@ -301,6 +295,12 @@ class Site { $dir_choices[$xx['site_url']] = $xx['site_url']; } } + if ($realm === DIRECTORY_REALM) { + $fallback_servers = get_directory_fallback_servers(); + foreach ($fallback_servers as $fallback_server) { + $dir_choices[$fallback_server] = $fallback_server; + } + } } /* Banner */ @@ -425,7 +425,7 @@ class Site { '$banner' => array('banner', t("Banner/Logo"), $banner, t('Unfiltered HTML/CSS/JS is allowed')), '$admininfo' => array('admininfo', t("Administrator Information"), $admininfo, t("Contact information for site administrators. Displayed on siteinfo page. BBCode can be used here")), '$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), + //'$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")), diff --git a/Zotlabs/Module/Conversation.php b/Zotlabs/Module/Conversation.php new file mode 100644 index 000000000..86ce66caa --- /dev/null +++ b/Zotlabs/Module/Conversation.php @@ -0,0 +1,188 @@ +<?php + +namespace Zotlabs\Module; + +use Zotlabs\Web\Controller; +use Zotlabs\Lib\ActivityStreams; +use Zotlabs\Lib\Activity; +use Zotlabs\Lib\Libzot; +use Zotlabs\Web\HTTPSig; +use Zotlabs\Lib\LDSignatures; +use Zotlabs\Lib\ThreadListener; +use Zotlabs\Lib\Config; +use App; + +class Conversation extends Controller { + + public function init() { + + if (ActivityStreams::is_as_request()) { + $item_id = argv(1); + + if (!$item_id) { + http_status_exit(404, 'Not found'); + } + + $portable_id = EMPTY_STR; + + $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; + + // do we have the item (at all)? + + $r = q("select parent_mid from item where mid = '%s' or uuid = '%s' $item_normal order by item_wall desc limit 1", + dbesc(z_root() . '/item/' . $item_id), + dbesc($item_id) + ); + + if (!$r) { + http_status_exit(404, 'Not found'); + } + + // process an authenticated fetch + + $sigdata = HTTPSig::verify(EMPTY_STR); + if ($sigdata['portable_id'] && $sigdata['header_valid']) { + $portable_id = $sigdata['portable_id']; + observer_auth($portable_id); + + // first see if we have a copy of this item's parent owned by the current signer + // include xchans for all zot-like networks - these will have the same guid and public key + + $x = q( + "select * from xchan where xchan_hash = '%s'", + dbesc($sigdata['portable_id']) + ); + + if ($x) { + $xchans = q( + "select xchan_hash from xchan where xchan_hash = '%s' OR ( xchan_guid = '%s' AND xchan_pubkey = '%s' ) ", + dbesc($sigdata['portable_id']), + dbesc($x[0]['xchan_guid']), + dbesc($x[0]['xchan_pubkey']) + ); + + if ($xchans) { + $hashes = ids_to_querystr($xchans, 'xchan_hash', true); + $i = q( + "select id as item_id from item where mid = '%s' $item_normal and owner_xchan in ( " . protect_sprintf($hashes) . " ) limit 1", + dbesc($r[0]['parent_mid']) + ); + } + } + } + elseif (Config::get('system', 'require_authenticated_fetch', false)) { + http_status_exit(403, 'Permission denied'); + } + + // if we don't have a parent id belonging to the signer see if we can obtain one as a visitor that we have permission to access + // with a bias towards those items owned by channels on this site (item_wall = 1) + + $sql_extra = item_permissions_sql(0); + + if (!$i) { + $i = q("select id as item_id from item where mid = '%s' $item_normal $sql_extra order by item_wall desc limit 1", + dbesc($r[0]['parent_mid']) + ); + } + + if (!$i) { + http_status_exit(403, 'Forbidden'); + } + + $parents_str = ids_to_querystr($i, 'item_id'); + $itemspage = 30; + $page = $_REQUEST['page'] ?? 1; + $offset = (intval($page) - 1) * $itemspage; + + $items = dbq("SELECT item.*, + item.id AS item_id, + (SELECT count(*) FROM item WHERE item.parent IN ( $parents_str ) $item_normal) AS total + FROM item WHERE item.parent IN ( $parents_str ) $item_normal order by item.id LIMIT $itemspage OFFSET $offset" + ); + + if (!$items) { + http_status_exit(404, 'Not found'); + } + + xchan_query($items, true); + $items = fetch_post_tags($items); + + $observer = App::get_observer(); + $parent = $items[0]; + $recips = (($parent['owner']['xchan_network'] === 'activitypub') ? get_iconfig($parent['id'], 'activitypub', 'recips', []) : []); + $to = (($recips && array_key_exists('to', $recips) && is_array($recips['to'])) ? $recips['to'] : null); + $nitems = []; + foreach ($items as $i) { + $mids = []; + + if (intval($i['item_private'])) { + if (!$observer) { + continue; + } + /* + // ignore private reshare, possibly from hubzilla + if ($i['verb'] === 'Announce') { + if (!in_array($i['thr_parent'], $mids)) { + $mids[] = $i['thr_parent']; + } + continue; + } + // also ignore any children of the private reshares + if (in_array($i['thr_parent'], $mids)) { + continue; + } + */ + + if ((!$to) || (!in_array($observer['xchan_url'], $to))) { + continue; + } + } + $nitems[] = $i; + } + + if (!$nitems) { + http_status_exit(404, 'Not found'); + } + + App::set_pager_total($nitems[0]['total']); + App::set_pager_itemspage($itemspage); + + $channel = channelx_by_n($nitems[0]['uid']); + + if (!$channel) { + http_status_exit(404, 'Not found'); + } + + if (!perm_is_allowed($channel['channel_id'], get_observer_hash(), 'view_stream')) { + http_status_exit(403, 'Forbidden'); + } + + $i = Activity::encode_item_collection($nitems, App::$query_string, 'OrderedCollection', $nitems[0]['total']); + + if (!$i) { + http_status_exit(404, 'Not found'); + } + + if ($portable_id && (!intval($nitems[0]['item_private']))) { + $c = q("select abook_id from abook where abook_channel = %d and abook_xchan = '%s'", + intval($nitems[0]['uid']), + dbesc($portable_id) + ); + if (!$c) { + ThreadListener::store(z_root() . '/item/' . $item_id, $portable_id); + } + } + + as_return_and_die($i, $channel); + } + + goaway(z_root() . '/item/' . argv(1)); + } +} diff --git a/Zotlabs/Module/Dircensor.php b/Zotlabs/Module/Dircensor.php index 0fa65e948..4ce3a81c4 100644 --- a/Zotlabs/Module/Dircensor.php +++ b/Zotlabs/Module/Dircensor.php @@ -4,6 +4,7 @@ namespace Zotlabs\Module; use App; use Zotlabs\Web\Controller; +use Zotlabs\Lib\Libzotdir; class Dircensor extends Controller { @@ -15,11 +16,12 @@ class Dircensor extends Controller { $dirmode = intval(get_config('system','directory_mode')); - if (! ($dirmode == DIRECTORY_MODE_PRIMARY || $dirmode == DIRECTORY_MODE_STANDALONE)) { + if(!in_array($dirmode, [DIRECTORY_MODE_PRIMARY, DIRECTORY_MODE_SECONDARY, DIRECTORY_MODE_STANDALONE])) { return; } $xchan = argv(1); + if(! $xchan) { return; } @@ -32,19 +34,43 @@ class Dircensor extends Controller { return; } - $val = (($r[0]['xchan_censored']) ? 0 : 1); + $severity = intval($_REQUEST['severity'] ?? 0); + $flag = DIRECTORY_FLAG_OK; + + if ($severity === 1) { + $flag = DIRECTORY_FLAG_UNSAFE; + } + + if ($severity === 2) { + $flag = DIRECTORY_FLAG_HIDDEN; + } + + Libzotdir::update($xchan, $r[0]['xchan_url'], true, $flag); - q("update xchan set xchan_censored = $val where xchan_hash = '%s'", + q("UPDATE xchan SET xchan_censored = %d WHERE xchan_hash = '%s'", + intval($flag), dbesc($xchan) ); - if($val) { + q("UPDATE xtag SET xtag_flags = %d WHERE xtag_hash = '%s'", + intval($flag), + dbesc($xchan) + ); + + if($flag) { info( t('Entry censored') . EOL); } else { - info( t('Entry uncensored') . EOL); + info( t('Entry OK') . EOL); } - + + if (isset($_REQUEST['aj'])) { + json_return_and_die([ + 'success' => 1, + 'flag' => $flag + ]); + } + goaway(z_root() . '/directory'); } diff --git a/Zotlabs/Module/Directory.php b/Zotlabs/Module/Directory.php index 2958e80dc..13353c108 100644 --- a/Zotlabs/Module/Directory.php +++ b/Zotlabs/Module/Directory.php @@ -151,7 +151,7 @@ class Directory extends Controller { $url = ''; - if(($dirmode == DIRECTORY_MODE_PRIMARY) || ($dirmode == DIRECTORY_MODE_STANDALONE)) { + if(in_array($dirmode, [DIRECTORY_MODE_PRIMARY, DIRECTORY_MODE_SECONDARY, DIRECTORY_MODE_STANDALONE])) { $url = z_root() . '/dirsearch'; if (is_site_admin()) { $directory_admin = true; @@ -191,7 +191,10 @@ class Directory extends Controller { if(get_config('system','disable_directory_keywords')) $kw = 0; - $query = $url . '?f=&kw=' . $kw . (($safe_mode != 1) ? '&safe=' . $safe_mode : ''); + if (intval($safe_mode) === 0 && $directory_admin) + $safe_mode = -1; + + $query = $url . '?f=&kw=' . $kw . (($safe_mode < 1) ? '&safe=' . $safe_mode : ''); if($token) $query .= '&t=' . $token; @@ -296,10 +299,11 @@ class Directory extends Controller { $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); + if ($about && $safe_mode > 0) { + $about = strip_tags($about, '<br>'); } + $keywords = ((x($profile,'keywords')) ? $profile['keywords'] : ''); @@ -351,11 +355,15 @@ class Directory extends Controller { 'gender' => $gender, 'pdesc' => $pdesc, 'pdesc_label' => t('Description:'), - 'censor' => (($directory_admin) ? 'dircensor/' . $rr['hash'] : ''), - 'censor_label' => (($rr['censored']) ? t('Uncensor') : t('Censor')), + 'censor' => (($directory_admin) ? 'dircensor/' . $rr['hash'] . '?severity=' . ((intval($rr['censored']) > 0) ? 0 : 1) : ''), + 'censor_label' => t('Unsafe'), + 'censor_class' => ((intval($rr['censored']) === 1) ? 'active' : ''), + 'censor_2' => (($directory_admin) ? 'dircensor/' . $rr['hash'] . '?severity=' . ((intval($rr['censored']) > 1) ? 0 : 2) : ''), + 'censor_2_label' => t('Spam'), + 'censor_2_class' => ((intval($rr['censored']) > 1) ? 'active' : ''), 'marital' => $marital, 'homepage' => $homepage, - 'homepageurl' => (($safe_mode) ? $homepageurl : linkify($homepageurl)), + 'homepageurl' => (($safe_mode > 0) ? $homepageurl : linkify($homepageurl)), 'hometown' => $hometown, 'hometown_label' => t('Hometown:'), 'about' => $about, @@ -436,7 +444,8 @@ class Directory extends Controller { '$reverse' => t('Reverse Alphabetic'), '$date' => t('Newest to Oldest'), '$reversedate' => t('Oldest to Newest'), - '$suggest' => $suggest ? '&suggest=1' : '' + '$suggest' => $suggest ? '&suggest=1' : '', + '$directory_admin' => $directory_admin )); diff --git a/Zotlabs/Module/Dirsearch.php b/Zotlabs/Module/Dirsearch.php index c0df8a1e3..7efac6345 100644 --- a/Zotlabs/Module/Dirsearch.php +++ b/Zotlabs/Module/Dirsearch.php @@ -15,8 +15,7 @@ class Dirsearch extends Controller { $ret = array('success' => false); - // logger('request: ' . print_r($_REQUEST,true)); - + // logger('request: ' . print_r($_REQUEST,true)); $dirmode = intval(get_config('system','directory_mode')); @@ -25,7 +24,6 @@ class Dirsearch extends Controller { json_return_and_die($ret); } - $access_token = $_REQUEST['t'] ?? ''; $token = get_config('system','realm_token'); @@ -41,7 +39,8 @@ class Dirsearch extends Controller { } $sql_extra = ''; - + $keywords_query = ''; + $hub_query = ''; $tables = array('name','address','locale','region','postcode','country','gender','marital','sexual','keywords'); @@ -85,9 +84,7 @@ class Dirsearch extends Controller { // by default use a safe search - $safe = ((x($_REQUEST,'safe'))); // ? intval($_REQUEST['safe']) : 1 ); - if ($safe === false) - $safe = 1; + $safe = $_REQUEST['safe'] ?? 1; if(array_key_exists('sync',$_REQUEST)) { if($_REQUEST['sync']) @@ -102,10 +99,10 @@ class Dirsearch extends Controller { $hub = \App::get_hostname(); } - if($hub) - $hub_query = " and xchan_hash in (select hubloc_hash from hubloc where hubloc_host = '" . protect_sprintf(dbesc($hub)) . "') "; - else - $hub_query = ''; + if($hub) { + $hub_query = " and xchan_hash in (select hubloc_hash from hubloc where hubloc_deleted = 0 and hubloc_host = '" . protect_sprintf(dbesc($hub)) . "') "; + } + $sort_order = ((x($_REQUEST,'order')) ? $_REQUEST['order'] : ''); @@ -131,9 +128,16 @@ class Dirsearch extends Controller { $sql_extra .= $this->dir_query_build($joiner,'xprof_marital',$marital); if($sexual) $sql_extra .= $this->dir_query_build($joiner,'xprof_sexual',$sexual); - if($keywords) + if($keywords && $name) { + // this is a general search $sql_extra .= $this->dir_query_build($joiner,'xprof_keywords',$keywords); - + } + if($keywords && !$name) { + // this is a search for keywords only + $keywords_arr = explode(',', $keywords); + stringify_array_elms($keywords_arr, true); + $keywords_query = " AND xchan_hash IN (SELECT xtag_hash FROM xtag WHERE xtag_term IN (" . protect_sprintf(implode(',', $keywords_arr)) . ")) "; + } // we only support an age range currently. You must set both agege // (greater than or equal) and agele (less than or equal) @@ -177,9 +181,15 @@ class Dirsearch extends Controller { $sql_extra .= " and xchan_addr like '%%" . \App::get_hostname() . "' "; } - $safesql = (($safe > 0) ? " and xchan_censored = 0 and xchan_selfcensored = 0 " : ''); + $safesql = ''; + if($safe > 0) + $safesql = " and xchan_censored = 0 and xchan_selfcensored = 0 "; + + if($safe < 1) + $safesql = " and xchan_censored < 2 and xchan_selfcensored < 2 "; + if($safe < 0) - $safesql = " and ( xchan_censored = 1 OR xchan_selfcensored = 1 ) "; + $safesql = " and xchan_censored < 3 and xchan_selfcensored < 2 "; if($forums) $safesql .= " and xchan_pubforum = " . ((intval($forums)) ? '1 ' : '0 '); @@ -215,26 +225,25 @@ class Dirsearch extends Controller { if($sync) { $spkt = array('transactions' => array()); - $r = q("select * from updates where ud_date >= '%s' and ud_guid != '' and ud_addr != '' order by ud_date desc", + + $r = q("SELECT * FROM updates WHERE ud_update = 0 AND ud_last = '%s' AND ud_date >= '%s' ORDER BY ud_date DESC", + dbesc(NULL_DATE), dbesc($sync) ); + if($r) { foreach($r as $rr) { - $flags = array(); - if($rr['ud_flags'] & UPDATE_FLAGS_DELETED) - $flags[] = 'deleted'; - if($rr['ud_flags'] & UPDATE_FLAGS_FORCED) - $flags[] = 'forced'; - - $spkt['transactions'][] = array( + $spkt['transactions'][] = [ 'hash' => $rr['ud_hash'], 'address' => $rr['ud_addr'], - 'transaction_id' => $rr['ud_guid'], + 'host' => $rr['ud_host'], + 'transaction_id' => $rr['ud_host'], // deprecated 2023-04-12 - can be removed after dir servers at version >= 8.4 'timestamp' => $rr['ud_date'], - 'flags' => $flags - ); + 'flags' => $rr['ud_flags'] + ]; } } + json_return_and_die($spkt); } else { @@ -264,7 +273,7 @@ class Dirsearch extends Controller { 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_id_url = xchan_url and 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 + where hubloc_primary = 1 and hubloc_updated > %s - INTERVAL %s and ( $logic $sql_extra ) $hub_query $keywords_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') diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php index b3b040e96..a5f66d72e 100644 --- a/Zotlabs/Module/Item.php +++ b/Zotlabs/Module/Item.php @@ -63,8 +63,8 @@ class Item extends Controller { // do we have the item (at all)? - $r = q("select * from item where mid = '%s' $item_normal limit 1", - dbesc(z_root() . '/item/' . $item_id) + $r = q("select parent_mid from item where uuid = '%s' $item_normal limit 1", + dbesc($item_id) ); if (!$r) { @@ -84,7 +84,7 @@ class Item extends Controller { } observer_auth($portable_id); - $i = q("select id as item_id from item where mid = '%s' $item_normal and owner_xchan = '%s' limit 1", + $i = q("select id as item_id, uid from item where mid = '%s' $item_normal and owner_xchan = '%s' limit 1", dbesc($r[0]['parent_mid']), dbesc($portable_id) ); @@ -99,7 +99,7 @@ class Item extends Controller { $sql_extra = item_permissions_sql(0); if (!$i) { - $i = q("select id as item_id from item where mid = '%s' $item_normal $sql_extra order by item_wall desc limit 1", + $i = q("select id as item_id, uid from item where mid = '%s' $item_normal $sql_extra order by item_wall desc limit 1", dbesc($r[0]['parent_mid']) ); } @@ -108,57 +108,53 @@ class Item extends Controller { http_status_exit(403, 'Forbidden'); } - $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) - ); + $chan = channelx_by_n($i[0]['uid']); - if (!$items) { + if (!$chan) { http_status_exit(404, 'Not found'); } - xchan_query($items, true); - $items = fetch_post_tags($items, true); - - if (!$items) - http_status_exit(404, 'Not found'); + if (!perm_is_allowed($chan['channel_id'], get_observer_hash(), 'view_stream')) { + http_status_exit(403, 'Forbidden'); + } - $chan = channelx_by_n($items[0]['uid']); + $parents_str = ids_to_querystr($i, 'item_id'); - if (!$chan) - http_status_exit(404, 'Not found'); + $total = q("SELECT count(*) AS count FROM item WHERE parent = %d $item_normal", + intval($parents_str) + ); - if (!perm_is_allowed($chan['channel_id'], get_observer_hash(), 'view_stream')) - http_status_exit(403, 'Forbidden'); + App::set_pager_total($total[0]['count']); + App::set_pager_itemspage(30); + if (App::$pager['total'] > App::$pager['itemspage']) { + // let mod conversation handle this request + App::$query_string = str_replace('item', 'conversation', App::$query_string); + $i = Activity::paged_collection_init(App::$pager['total'], App::$query_string); + as_return_and_die($i ,$chan); + } + else { + $items = q("SELECT item.*, item.id AS item_id FROM item WHERE item.parent = %d $item_normal ORDER BY item.id", + intval($parents_str) + ); - $i = Activity::encode_item_collection($items, 'conversation/' . $item_id, 'OrderedCollection'); + xchan_query($items, true); + $items = fetch_post_tags($items, true); - if (!$i) - http_status_exit(404, 'Not found'); + $i = Activity::encode_item_collection($items, App::$query_string, 'OrderedCollection', App::$pager['total']); + } if ($portable_id && (!intval($items[0]['item_private']))) { - ThreadListener::store(z_root() . '/item/' . $item_id, $portable_id); + $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', - z_root() . ZOT_APSCHEMA_REV - ]], $i); - - $headers = []; - $headers['Content-Type'] = 'application/x-zot+json'; - $x['signature'] = LDSignatures::sign($x, $chan); - $ret = json_encode($x, JSON_UNESCAPED_SLASHES); - $headers['Digest'] = HTTPSig::generate_digest_header($ret); - $headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI']; - $h = HTTPSig::create_sig($headers, $chan['channel_prvkey'], channel_url($chan)); - HTTPSig::set_headers($h); - echo $ret; - killme(); - + as_return_and_die($i ,$chan); } if (ActivityStreams::is_as_request()) { @@ -181,8 +177,7 @@ class Item extends Controller { // do we have the item (at all)? // add preferential bias to item owners (item_wall = 1) - $r = q("select * from item where mid = '%s' or uuid = '%s' $item_normal order by item_wall desc limit 1", - dbesc(z_root() . '/item/' . $item_id), + $r = q("select * from item where uuid = '%s' $item_normal order by item_wall desc limit 1", dbesc($item_id) ); @@ -255,23 +250,7 @@ class Item extends Controller { } } - $x = array_merge(['@context' => [ - ACTIVITYSTREAMS_JSONLD_REV, - 'https://w3id.org/security/v1', - z_root() . ZOT_APSCHEMA_REV - ]], $i); - - $headers = []; - $headers['Content-Type'] = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'; - $x['signature'] = LDSignatures::sign($x, $chan); - $ret = json_encode($x, JSON_UNESCAPED_SLASHES); - $headers['Date'] = datetime_convert('UTC', 'UTC', 'now', 'D, d M Y H:i:s \\G\\M\\T'); - $headers['Digest'] = HTTPSig::generate_digest_header($ret); - $headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI']; - $h = HTTPSig::create_sig($headers, $chan['channel_prvkey'], channel_url($chan)); - HTTPSig::set_headers($h); - echo $ret; - killme(); + as_return_and_die($i ,$chan); } diff --git a/Zotlabs/Module/Like.php b/Zotlabs/Module/Like.php index 2cf792c8d..5779faa19 100644 --- a/Zotlabs/Module/Like.php +++ b/Zotlabs/Module/Like.php @@ -451,7 +451,7 @@ class Like extends Controller { } else { $post_type = (($item['resource_type'] === 'photo') ? t('photo') : t('status')); - if ($item['obj_type'] === ACTIVITY_OBJ_EVENT) + if (in_array($item['obj_type'], ['Event', ACTIVITY_OBJ_EVENT])) $post_type = t('event'); $obj_type = (($item['resource_type'] === 'photo') ? ACTIVITY_OBJ_PHOTO : ACTIVITY_OBJ_NOTE); @@ -507,7 +507,7 @@ class Like extends Controller { $private = (($public) ? 0 : 1); } else { - $arr['parent'] = $item['id']; + $arr['parent'] = $item['parent']; $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]'; @@ -524,7 +524,7 @@ class Like extends Controller { $arr['uid'] = $owner_uid; $arr['item_flags'] = $item['item_flags']; $arr['item_wall'] = $item['item_wall']; - $arr['parent_mid'] = (($extended_like) ? $arr['mid'] : $item['mid']); + $arr['parent_mid'] = (($extended_like) ? $arr['mid'] : $item['parent_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); diff --git a/Zotlabs/Module/Owa.php b/Zotlabs/Module/Owa.php index 3400defd7..02997d3f1 100644 --- a/Zotlabs/Module/Owa.php +++ b/Zotlabs/Module/Owa.php @@ -32,7 +32,8 @@ class Owa extends Controller { $keyId = $sigblock['keyId']; if ($keyId) { $r = q("SELECT * FROM hubloc LEFT JOIN xchan ON hubloc_hash = xchan_hash - WHERE hubloc_id_url = '%s' AND hubloc_deleted = 0 AND xchan_pubkey != '' ORDER BY hubloc_id DESC", + WHERE ( hubloc_addr = '%s' OR hubloc_id_url = '%s' ) AND hubloc_deleted = 0 AND xchan_pubkey != '' ORDER BY hubloc_id DESC", + dbesc(str_replace('acct:', '', $keyId)), dbesc($keyId) ); if (! $r) { diff --git a/Zotlabs/Module/Photo.php b/Zotlabs/Module/Photo.php index 10d2e8f47..fa87d96e5 100644 --- a/Zotlabs/Module/Photo.php +++ b/Zotlabs/Module/Photo.php @@ -271,7 +271,6 @@ class Photo extends \Zotlabs\Web\Controller { $modified = time(); header_remove('Pragma'); - if((isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] === $etag) || (!isset($_SERVER['HTTP_IF_NONE_MATCH']) && isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $_SERVER['HTTP_IF_MODIFIED_SINCE'] === gmdate("D, d M Y H:i:s", $modified) . " GMT")) { header_remove('Expires'); header_remove('Cache-Control'); @@ -307,6 +306,8 @@ class Photo extends \Zotlabs\Web\Controller { // This has performance considerations but we highly recommend you // leave it alone. + + $maxage = $cache_mode['age']; if($cache_mode['exp'] || (! isset($expires)) || (isset($expires) && $expires - 60 < time())) diff --git a/Zotlabs/Module/Profiles.php b/Zotlabs/Module/Profiles.php index e248cd028..ce496252b 100644 --- a/Zotlabs/Module/Profiles.php +++ b/Zotlabs/Module/Profiles.php @@ -217,7 +217,6 @@ class Profiles extends \Zotlabs\Web\Controller { check_form_security_token_redirectOnErr('/profiles', 'profile_edit'); - $is_default = (($orig[0]['is_default']) ? 1 : 0); $profile_name = notags(trim($_POST['profile_name'])); @@ -579,12 +578,12 @@ class Profiles extends \Zotlabs\Web\Controller { $channel = \App::get_channel(); if($namechanged && $is_default) { - // change name on all associated xchans by matching the url - 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']) ); + q("UPDATE channel SET channel_name = '%s' WHERE channel_hash = '%s'", dbesc($name), dbesc($channel['xchan_hash']) diff --git a/Zotlabs/Module/Search.php b/Zotlabs/Module/Search.php index 6601da29d..6b1060570 100644 --- a/Zotlabs/Module/Search.php +++ b/Zotlabs/Module/Search.php @@ -58,7 +58,7 @@ class Search extends Controller { $o .= search($search, 'search-box', '/search', ((local_channel()) ? true : false)); - if (local_channel() && strpos($search, 'https://') === 0 && !$update && !$load) { + if (local_channel() && str_starts_with($search, 'https://') && !$update && !$load) { $url = htmlspecialchars_decode($search); @@ -75,7 +75,7 @@ class Search extends Controller { if ($f) { $mid = $f[0]['message_id']; foreach ($f as $m) { - if (strpos($search, $m['message_id']) === 0) { + if (str_starts_with($url, $m['message_id'])) { $mid = $m['message_id']; break; } diff --git a/Zotlabs/Module/Wfinger.php b/Zotlabs/Module/Wfinger.php index 048fcde3f..43b039530 100644 --- a/Zotlabs/Module/Wfinger.php +++ b/Zotlabs/Module/Wfinger.php @@ -76,7 +76,10 @@ class Wfinger extends \Zotlabs\Web\Controller { } } else { - $r = channelx_by_nick($channel); + // Also provide already deleted channels info here. + // This is required in the case where we need to verify keys + // of updates which we have got via directory sync. + $r = channelx_by_nick($channel, true); } } @@ -94,10 +97,6 @@ class Wfinger extends \Zotlabs\Web\Controller { 'href' => z_root() . '/owa', ], ]; - - - - } if($resource && $r) { diff --git a/Zotlabs/Thumbs/Epubthumb.php b/Zotlabs/Thumbs/Epubthumb.php index 22f1a5e8f..6ebbd8933 100644 --- a/Zotlabs/Thumbs/Epubthumb.php +++ b/Zotlabs/Thumbs/Epubthumb.php @@ -30,9 +30,14 @@ class Epubthumb { */ function Thumb($attach, $preview_style, $height = 300, $width = 300) { + $file = dbunescbin($attach['content']); + if (!$file) { + return; + } + $photo = false; - $ep = new \EPub(dbunescbin($attach['content'])); + $ep = new \EPub($file); $data = $ep->Cover(); if($data['found']) { diff --git a/Zotlabs/Thumbs/Mp3audio.php b/Zotlabs/Thumbs/Mp3audio.php index 000d65b22..c50338fb2 100644 --- a/Zotlabs/Thumbs/Mp3audio.php +++ b/Zotlabs/Thumbs/Mp3audio.php @@ -11,9 +11,16 @@ class Mp3audio { } function Thumb($attach,$preview_style,$height = 300, $width = 300) { + + $file = dbunescbin($attach['content']); + if (!$file) { + return; + } + + $photo = false; $p = new ID3Parser(); - $id = $p->analyze(dbunescbin($attach['content'])); + $id = $p->analyze($file); $photo = isset($id['id3v2']['APIC'][0]['data']) ? $id['id3v2']['APIC'][0]['data'] : null; if(is_null($photo) && isset($id['id3v2']['PIC'][0]['data'])) { diff --git a/Zotlabs/Thumbs/Pdf.php b/Zotlabs/Thumbs/Pdf.php index 11714ad53..6f9111ed3 100644 --- a/Zotlabs/Thumbs/Pdf.php +++ b/Zotlabs/Thumbs/Pdf.php @@ -11,9 +11,13 @@ class Pdf { function Thumb($attach,$preview_style,$height = 300, $width = 300) { + $file = dbunescbin($attach['content']); + if (!$file) { + return; + } + $photo = false; - $file = dbunescbin($attach['content']); $tmpfile = $file . '.pdf'; $outfile = $file . '.jpg'; diff --git a/Zotlabs/Thumbs/Text.php b/Zotlabs/Thumbs/Text.php index 31e27d014..b2f13c578 100644 --- a/Zotlabs/Thumbs/Text.php +++ b/Zotlabs/Thumbs/Text.php @@ -11,11 +11,12 @@ class Text { function Thumb($attach,$preview_style,$height = 300, $width = 300) { - if (empty($attach['content'])) { + $file = dbunescbin($attach['content']); + if (!$file) { return; } - $stream = @fopen(dbunescbin($attach['content']),'rb'); + $stream = @fopen($file,'rb'); if($stream) { $content = trim(stream_get_contents($stream,4096)); $content = str_replace("\r",'',$content); diff --git a/Zotlabs/Thumbs/Video.php b/Zotlabs/Thumbs/Video.php index 15d3ace12..9df27d703 100644 --- a/Zotlabs/Thumbs/Video.php +++ b/Zotlabs/Thumbs/Video.php @@ -11,16 +11,19 @@ class Video { function Thumb($attach,$preview_style,$height = 300, $width = 300) { + $file = dbunescbin($attach['content']); + if (!$file) { + return; + } + $photo = false; $t = explode('/',$attach['filetype']); if($t[1]) $extension = '.' . $t[1]; else - return; + return; - - $file = dbunescbin($attach['content']); $tmpfile = $file . $extension; $outfile = $file . '.jpg'; @@ -40,7 +43,7 @@ class Video { $ffmpeg = trim(shell_exec('which ffmpeg')); - if($ffmpeg) { + if($ffmpeg) { logger('ffmpeg not found in path. Video thumbnails may fail.'); } @@ -59,7 +62,7 @@ class Video { @rename($outfile,$file . '.thumb'); } } - + @unlink($tmpfile); } } diff --git a/Zotlabs/Update/_1256.php b/Zotlabs/Update/_1256.php new file mode 100644 index 000000000..dd6ded225 --- /dev/null +++ b/Zotlabs/Update/_1256.php @@ -0,0 +1,36 @@ +<?php + +namespace Zotlabs\Update; + +class _1256 { + + function run() { + + dbq("START TRANSACTION"); + + $r1 = dbq("TRUNCATE TABLE updates"); + + if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r2a = dbq("ALTER TABLE updates add ud_update numeric(1) NOT NULL DEFAULT '0'"); + $r2b = dbq("CREATE INDEX ud_update ON updates (ud_update)"); + + $r2 = ($r2a && $r2b); + } + + if(ACTIVE_DBTYPE == DBTYPE_MYSQL) { + $r2 = dbq("ALTER TABLE updates add ud_update tinyint(1) NOT NULL DEFAULT '0', + ADD INDEX (ud_update);" + ); + } + + if($r1 && $r2) { + dbq("COMMIT"); + return UPDATE_SUCCESS; + } + + q("ROLLBACK"); + return UPDATE_FAILED; + + } + +} diff --git a/Zotlabs/Update/_1257.php b/Zotlabs/Update/_1257.php new file mode 100644 index 000000000..34c10c372 --- /dev/null +++ b/Zotlabs/Update/_1257.php @@ -0,0 +1,33 @@ +<?php + +namespace Zotlabs\Update; + +class _1257 { + + function run() { + + dbq("START TRANSACTION"); + + $r1 = dbq("TRUNCATE TABLE updates"); + + if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r2 = dbq("ALTER TABLE updates add ud_host text NOT NULL DEFAULT ''"); + } + + if(ACTIVE_DBTYPE == DBTYPE_MYSQL) { + $r2 = dbq("ALTER TABLE updates add ud_host varchar(191) NOT NULL DEFAULT ''"); + } + + $r3 = dbq("ALTER TABLE updates DROP COLUMN ud_guid"); + + if($r1 && $r2 && $r3) { + dbq("COMMIT"); + return UPDATE_SUCCESS; + } + + q("ROLLBACK"); + return UPDATE_FAILED; + + } + +} diff --git a/Zotlabs/Update/_1258.php b/Zotlabs/Update/_1258.php new file mode 100644 index 000000000..13812219e --- /dev/null +++ b/Zotlabs/Update/_1258.php @@ -0,0 +1,14 @@ +<?php + +namespace Zotlabs\Update; + +class _1258 { + + function run() { + + dbq("DELETE FROM pconfig WHERE cat = 'channelreputation'"); + dbq("DELETE FROM iconfig WHERE cat = 'channelreputation'"); + + } + +} diff --git a/Zotlabs/Widget/Pinned.php b/Zotlabs/Widget/Pinned.php index 6d2618deb..bd1c0e462 100644 --- a/Zotlabs/Widget/Pinned.php +++ b/Zotlabs/Widget/Pinned.php @@ -67,7 +67,7 @@ class Pinned { $conv_responses = []; - if($item['obj_type'] === ACTIVITY_OBJ_EVENT) { + if(in_array($item['obj_type'], ['Event', ACTIVITY_OBJ_EVENT])) { $conv_responses['attendyes'] = [ 'title' => t('Attending','title') ]; $conv_responses['attendno'] = [ 'title' => t('Not attending','title') ]; $conv_responses['attendmaybe'] = [ 'title' => t('Might attend','title') ]; |