diff options
author | Mario <mario@mariovavti.com> | 2023-03-19 13:55:18 +0000 |
---|---|---|
committer | Mario <mario@mariovavti.com> | 2023-03-19 13:55:18 +0000 |
commit | 89285f1408d21091bb80d45b391ddcbe06ba8d0f (patch) | |
tree | b2eb07d9f3d91d77f89a4565a58e6e5231b20c1c /Zotlabs | |
parent | 0a679e503ef367eda3085c44af103ee53869a94f (diff) | |
parent | 17c0bb2069dcfe35d3febc5bfdb3a7295f15d49c (diff) | |
download | volse-hubzilla-8.2.tar.gz volse-hubzilla-8.2.tar.bz2 volse-hubzilla-8.2.zip |
Merge branch '8.2RC'8.2
Diffstat (limited to 'Zotlabs')
30 files changed, 500 insertions, 352 deletions
diff --git a/Zotlabs/Daemon/Convo.php b/Zotlabs/Daemon/Convo.php index d1a7e4f4d..e05f7539e 100644 --- a/Zotlabs/Daemon/Convo.php +++ b/Zotlabs/Daemon/Convo.php @@ -55,6 +55,7 @@ class Convo { $AS = new ActivityStreams($message); if ($AS->is_valid() && is_array($AS->obj)) { $item = Activity::decode_note($AS); + $item['item_fetched'] = true; Activity::store($channel, $contact['abook_xchan'], $AS, $item); } } diff --git a/Zotlabs/Daemon/Externals.php b/Zotlabs/Daemon/Externals.php index 2c7c7c172..b67a0e286 100644 --- a/Zotlabs/Daemon/Externals.php +++ b/Zotlabs/Daemon/Externals.php @@ -143,6 +143,7 @@ class Externals { $AS = new ActivityStreams($message); if ($AS->is_valid() && is_array($AS->obj)) { $item = Activity::decode_note($AS); + $item['item_fetched'] = true; Activity::store($importer, $contact['hubloc_hash'], $AS, $item); $total++; } diff --git a/Zotlabs/Daemon/Onepoll.php b/Zotlabs/Daemon/Onepoll.php index 0a30a0c7d..bde39007e 100644 --- a/Zotlabs/Daemon/Onepoll.php +++ b/Zotlabs/Daemon/Onepoll.php @@ -172,6 +172,7 @@ class Onepoll { $AS = new ActivityStreams($message); if ($AS->is_valid() && is_array($AS->obj)) { $item = Activity::decode_note($AS); + $item['item_fetched'] = true; Activity::store($importer, $contact['abook_xchan'], $AS, $item); } } diff --git a/Zotlabs/Daemon/Queue.php b/Zotlabs/Daemon/Queue.php index 3eb7d9d23..b07fe369c 100644 --- a/Zotlabs/Daemon/Queue.php +++ b/Zotlabs/Daemon/Queue.php @@ -7,79 +7,56 @@ use Zotlabs\Lib\Queue as LibQueue; class Queue { static public function run($argc, $argv) { - - require_once('include/items.php'); - require_once('include/bbcode.php'); - - if ($argc > 1) - $queue_id = $argv[1]; - else - $queue_id = EMPTY_STR; + $queue_id = ($argc > 1) ? $argv[1] : ''; logger('queue: start'); // 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 - $r = q("select outq_posturl from outq where outq_created < %s - INTERVAL %s", - db_utcnow(), db_quoteinterval('3 DAY') + $oldqItems = q("select outq_posturl from outq where outq_created < %s - INTERVAL %s", + db_utcnow(), + db_quoteinterval('3 DAY') ); - if ($r) { - foreach ($r as $rr) { - $h = parse_url($rr['outq_posturl']); - $desturl = $h['scheme'] . '://' . $h['host'] . (isset($h['port']) ? ':' . $h['port'] : ''); + + if ($oldqItems) { + foreach ($oldqItems as $qItem) { + $h = parse_url($qItem['outq_posturl']); + $site_url = $h['scheme'] . '://' . $h['host'] . ((!empty($h['port'])) ? ':' . $h['port'] : ''); q("update site set site_dead = 1 where site_dead = 0 and site_url = '%s' and site_update < %s - INTERVAL %s", - dbesc($desturl), - db_utcnow(), db_quoteinterval('1 MONTH') + dbesc($site_url), + db_utcnow(), + 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') + db_utcnow(), + db_quoteinterval('3 DAY') ); + $deliveries = []; + if ($queue_id) { - $r = q("SELECT * FROM outq WHERE outq_hash = '%s' LIMIT 1", + $qItems = q("SELECT * FROM outq WHERE outq_hash = '%s' LIMIT 1", dbesc($queue_id) ); + logger('queue deliver: ' . $qItems[0]['outq_hash'] . ' to ' . $qItems[0]['outq_posturl'], LOGGER_DEBUG); + LibQueue::deliver($qItems[0]); } else { - - // For the first 12 hours we'll try to deliver every 15 minutes - // After that, we'll only attempt delivery once per hour. - // This currently only handles the default queue drivers ('zot' or '') which we will group by posturl - // so that we don't start off a thousand deliveries for a couple of dead hubs. - // The zot driver will deliver everything destined for a single hub once contact is made (*if* contact is made). - // Other drivers will have to do something different here and may need their own query. - - // Note: this requires some tweaking as new posts to long dead hubs once a day will keep them in the - // "every 15 minutes" category. We probably need to prioritise them when inserted into the queue - // or just prior to this query based on recent and long-term delivery history. If we have good reason to believe - // the site is permanently down, there's no reason to attempt delivery at all, or at most not more than once - // or twice a day. - - $sqlrandfunc = db_getfunc('rand'); - - $r = q("SELECT *,$sqlrandfunc as rn FROM outq WHERE outq_delivered = 0 and outq_scheduled < %s order by rn limit 1", + $qItems = q("SELECT * FROM outq WHERE outq_delivered = 0 and outq_scheduled < %s ", db_utcnow() ); - while ($r) { - foreach ($r as $rv) { - LibQueue::deliver($rv); + if ($qItems) { + foreach ($qItems as $qItem) { + $deliveries[] = $qItem['outq_hash']; } - $r = q("SELECT *,$sqlrandfunc as rn FROM outq WHERE outq_delivered = 0 and outq_scheduled < %s order by rn limit 1", - db_utcnow() - ); + do_delivery($deliveries, true); } - } - if (!$r) - return; - - foreach ($r as $rv) { - LibQueue::deliver($rv); - } - - return; + } } + } diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index 1a1031909..226f50636 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -595,9 +595,9 @@ class Activity { foreach ($item['term'] as $t) { switch ($t['ttype']) { case TERM_HASHTAG: - // href is required so if we don't have a url in the taxonomy, ignore it and keep going. + // id is required so if we don't have a url in the taxonomy, ignore it and keep going. if ($t['url']) { - $ret[] = ['type' => 'Hashtag', 'href' => $t['url'], 'name' => '#' . $t['term']]; + $ret[] = ['type' => 'Hashtag', 'id' => $t['url'], 'name' => '#' . $t['term']]; } break; @@ -700,6 +700,13 @@ class Activity { if (array_key_exists('name', $att) && $att['name']) { $entry['name'] = html2plain(purify_html($att['name']), 256); } + // Friendica attachments don't match the URL in the body. + // This makes it more difficult to detect image duplication in bb_attach() + // which adds images to plaintext microblog software. For these we need to examine both the + // url and image properties. + if (isset($att['image']) && is_string($att['image']) && isset($att['url']) && $att['image'] !== $att['url']) { + $entry['image'] = $att['image']; + } if ($entry) { $ret[] = $entry; } @@ -822,7 +829,8 @@ class Activity { ]; } - $ret['published'] = ((isset($i['created'])) ? datetime_convert('UTC', 'UTC', $i['created'], ATOM_TIME) : datetime_convert()); + $ret['published'] = datetime_convert('UTC', 'UTC', $i['created'], ATOM_TIME); + if (isset($i['created'], $i['edited']) && $i['created'] !== $i['edited']) { $ret['updated'] = datetime_convert('UTC', 'UTC', $i['edited'], ATOM_TIME); if ($ret['type'] === 'Create') { @@ -1690,6 +1698,8 @@ class Activity { } } + $group_actor = ($person_obj['type'] === 'Group'); + $r = q("select * from xchan join hubloc on xchan_hash = hubloc_hash where xchan_hash = '%s'", dbesc($url) ); @@ -1708,11 +1718,12 @@ class Activity { ); // update existing xchan record - q("update xchan set xchan_name = '%s', xchan_pubkey = '%s', xchan_addr = '%s', xchan_network = 'activitypub', xchan_name_date = '%s' where xchan_hash = '%s'", + q("update xchan set xchan_name = '%s', xchan_pubkey = '%s', xchan_addr = '%s', xchan_network = 'activitypub', xchan_name_date = '%s', xchan_pubforum = %d where xchan_hash = '%s'", dbesc(escape_tags($name)), dbesc(escape_tags($pubkey)), dbesc(escape_tags($webfinger_addr)), dbescdate(datetime_convert()), + intval($group_actor), dbesc($url) ); @@ -1739,7 +1750,8 @@ class Activity { 'xchan_url' => $profile, 'xchan_name' => escape_tags($name), 'xchan_name_date' => datetime_convert(), - 'xchan_network' => 'activitypub' + 'xchan_network' => 'activitypub', + 'xchan_pubforum' => intval($group_actor) ] ); @@ -1768,6 +1780,10 @@ class Activity { dbesc($url) ); if (!$zx) { + // FIXME: we might need to fetch and store this url immediately + // otherwise at least the first post of a yet unknown author might + // be stored with the activitypub url instead of the portable id. + // Another solution could be to fix the items after Gprobe has done its work. Master::Summon(['Gprobe', bin2hex($url)]); } } @@ -2218,7 +2234,6 @@ class Activity { ) { return false; } - // Within our family of projects, Follow/Unfollow of a thread is an internal activity which should not be transmitted, // hence if we receive it - ignore or reject it. // Unfollow is not defined by ActivityStreams, which prefers Undo->Follow. @@ -2514,7 +2529,11 @@ class Activity { if ($act->type === 'Announce') { $s['author_xchan'] = self::get_attributed_to_actor_url($act); $s['mid'] = $act->obj['id']; - $s['parent_mid'] = $act->obj['id']; + + // Do not force new thread if the announce is from a group actor + if ($act->actor['type'] !== 'Group') { + $s['parent_mid'] = $act->obj['id']; + } } // we will need a hook here to extract magnet links e.g. peertube @@ -2589,13 +2608,13 @@ class Activity { if ($mps) { usort($mps,[ '\Zotlabs\Lib\Activity', 'vid_sort' ]); foreach ($mps as $m) { - if (intval($m['height']) < 500 && Activity::media_not_in_body($m['href'],$s['body'])) { + if (intval($m['height']) < 500 && self::media_not_in_body($m['href'],$s['body'])) { $s['body'] = $tag . $m['href'] . '[/video]' . "\r\n" . $s['body']; break; } } } - elseif (is_string($act->obj['url']) && Activity::media_not_in_body($act->obj['url'],$s['body'])) { + elseif (is_string($act->obj['url']) && self::media_not_in_body($act->obj['url'],$s['body'])) { $s['body'] = $tag . $act->obj['url'] . '[/video]' . "\r\n" . $s['body']; } @@ -2843,6 +2862,10 @@ class Activity { $is_child_node = true; } + if (empty($item['item_fetched'])) { + $item['owner_xchan'] = $observer_hash; + } + $allowed = false; // TODO: not implemented @@ -2919,7 +2942,7 @@ class Activity { // The $item['item_fetched'] flag is set in fetch_and_store_parents(). // In this case we should check against author permissions because sender is not owner. - if (perm_is_allowed($channel['channel_id'], ((isset($item['item_fetched']) && $item['item_fetched']) ? $item['author_xchan'] : $observer_hash), 'send_stream') || $is_sys_channel) { + 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 @@ -3228,7 +3251,7 @@ class Activity { $item = $hookinfo['item']; if ($item) { - $item['item_fetched'] = 1; + $item['item_fetched'] = true; if (intval($channel['channel_system']) && intval($item['item_private'])) { $p = []; @@ -3342,7 +3365,7 @@ class Activity { return false; } - */ + static public function fetch_and_store_replies($channel, $arr) { logger('fetching replies'); @@ -3399,6 +3422,7 @@ class Activity { } } +*/ /* this is deprecated and not used anymore static function announce_note($channel, $observer_hash, $act) { @@ -3674,7 +3698,21 @@ class Activity { if ($a['type'] === 'image/svg+xml' && strpos($item['body'], '[/svg]')) { continue; } - if (self::media_not_in_body($a['href'], $item['body'])) { + // Friendica attachment weirdness + // Check both the attachment image and href since they can be different and the one in the href is a different link with different resolution. + // Otheriwse you'll get duplicated images + if (isset($a['image'])) { + if (self::media_not_in_body($a['image'], $item['body']) && self::media_not_in_body($a['href'], $item['body'])) { + if (isset($a['name']) && $a['name']) { + $alt = htmlspecialchars($a['name'], ENT_QUOTES); + $item['body'] = '[img=' . $a['href'] . ']' . $alt . '[/img]' . "\r\n" . $item['body']; + } else { + $item['body'] = '[img]' . $a['href'] . '[/img]' . "\r\n" . $item['body']; + } + } + continue; + } + elseif (self::media_not_in_body($a['href'], $item['body'])) { if (isset($a['name']) && $a['name']) { $alt = htmlspecialchars($a['name'], ENT_QUOTES); $item['body'] = '[img=' . $a['href'] . ']' . $alt . '[/img]' . "\r\n" . $item['body']; diff --git a/Zotlabs/Lib/ActivityStreams.php b/Zotlabs/Lib/ActivityStreams.php index a07fdacb7..cfed53b3c 100644 --- a/Zotlabs/Lib/ActivityStreams.php +++ b/Zotlabs/Lib/ActivityStreams.php @@ -113,7 +113,7 @@ class ActivityStreams { // fetch recursive or embedded activities if ($this->obj && is_array($this->obj) && array_key_exists('object', $this->obj)) { - $this->obj['object'] = $this->get_compound_property($this->obj['object']); + $this->obj['object'] = $this->get_compound_property('object', $this->obj); } if ($this->obj && is_array($this->obj) && isset($this->obj['actor'])) @@ -146,35 +146,58 @@ class ActivityStreams { } /** + * @brief get single property from Activity object + * + * @param string $property + * @param mixed $default return value if property or object not set + * or object is a string id which could not be fetched. + * @return mixed + */ + public function objprop(string $property, mixed $default = false): mixed { + $x = $this->get_property_obj($property,$this->obj); + return (isset($x)) ? $x : $default; + } + + /** * @brief Collects all recipients. * - * @param string $base + * @param mixed $base * @param string $namespace (optional) default empty * @return array */ - function collect_recips($base = '', $namespace = '') { - $x = []; + public function collect_recips(mixed $base = '', string $namespace = ''): array { + $result = []; + $tmp = []; $fields = ['to', 'cc', 'bto', 'bcc', 'audience']; - foreach ($fields as $f) { - $y = $this->get_compound_property($f, $base, $namespace); - if ($y) { - if (!is_array($this->raw_recips)) { - $this->raw_recips = []; - } - - if (!is_array($y)) { - $y = [$y]; - } - $this->raw_recips[$f] = $y; - $x = array_merge($x, $y); + foreach ($fields as $field) { + // don't expand these yet + $values = $this->get_property_obj($field, $base, $namespace); + if ($values) { + $values = force_array($values); + $tmp[$field] = $values; + $result = array_values(array_unique(array_merge($result, $values))); + } + // Merge the object recipients if they exist. + $values = $this->objprop($field); + if ($values) { + $values = force_array($values); + $tmp[$field] = ((isset($tmp[$field])) ? array_merge($tmp[$field], $values) : $values); + $result = array_values(array_unique(array_merge($result, $values))); + } + // remove duplicates + if (isset($tmp[$field])) { + $tmp[$field] = array_values(array_unique($tmp[$field])); } } -// not yet ready for prime time -// $x = $this->expand($x,$base,$namespace); - return $x; + $this->raw_recips = $tmp; + + // not yet ready for prime time + // $result = $this->expand($result,$base,$namespace); + return $result; } + function expand($arr, $base = '', $namespace = '') { $ret = []; @@ -341,6 +364,7 @@ class ActivityStreams { */ function get_compound_property($property, $base = '', $namespace = '', $first = false) { $x = $this->get_property_obj($property, $base, $namespace); + if ($this->is_url($x)) { $y = $this->fetch_property($x); if (is_array($y)) { diff --git a/Zotlabs/Lib/DReport.php b/Zotlabs/Lib/DReport.php index 2263529b2..e22ed65be 100644 --- a/Zotlabs/Lib/DReport.php +++ b/Zotlabs/Lib/DReport.php @@ -94,19 +94,6 @@ class DReport { if(! $c) return false; - // legacy zot recipients add a space and their name to the xchan. remove it if true. - - $legacy_recipient = strpos($dr['recipient'], ' '); - if($legacy_recipient !== false) { - $legacy_recipient_parts = explode(' ', $dr['recipient'], 2); - $rxchan = $legacy_recipient_parts[0]; - } - else { - $rxchan = $dr['recipient']; - } - - - // is the recipient one of our connections, or do we want to store every report? $pcf = get_pconfig($c[0]['channel_id'],'system','dreport_store_all'); @@ -117,7 +104,7 @@ class DReport { // So if a remote site says they can't find us, that's no big surprise // and just creates a lot of extra report noise - if(($dr['location'] !== z_root()) && ($dr['sender'] === $rxchan) && ($dr['status'] === 'recipient not found')) + if(($dr['location'] !== z_root()) && ($dr['sender'] === $dr['recipient']) && ($dr['status'] === 'recipient not found')) return false; // If you have a private post with a recipient list, every single site is going to report @@ -126,14 +113,14 @@ class DReport { // have a channel on that site. $r = q("select hubloc_id from hubloc where hubloc_hash = '%s' and hubloc_url = '%s'", - dbesc($rxchan), + dbesc($dr['recipient']), dbesc($dr['location']) ); if((! $r) && ($dr['status'] === 'recipient_not_found')) return false; $r = q("select abook_id from abook where abook_xchan = '%s' and abook_channel = %d limit 1", - dbesc($rxchan), + dbesc($dr['recipient']), intval($c[0]['channel_id']) ); if($r) diff --git a/Zotlabs/Lib/Enotify.php b/Zotlabs/Lib/Enotify.php index 2015b260d..585761cc4 100644 --- a/Zotlabs/Lib/Enotify.php +++ b/Zotlabs/Lib/Enotify.php @@ -828,7 +828,7 @@ class Enotify { : (($item['obj_type'] === 'Answer') ? sprintf( t('voted on %s\'s poll'), '[bdi]' . $item['owner']['xchan_name'] . '[/bdi]') : sprintf( t('commented on %s\'s post'), '[bdi]' . $item['owner']['xchan_name'] . '[/bdi]')) ); - if($item['verb'] === ACTIVITY_SHARE) { + if($item['verb'] === ACTIVITY_SHARE && empty($item['owner']['xchan_pubforum'])) { $itemem_text = sprintf( t('repeated %s\'s post'), '[bdi]' . $item['author']['xchan_name'] . '[/bdi]'); } @@ -860,7 +860,7 @@ class Enotify { // convert this logic into a json array just like the system notifications - $who = (($item['verb'] === ACTIVITY_SHARE) ? 'owner' : 'author'); + $who = (($item['verb'] === ACTIVITY_SHARE && empty($item['owner']['xchan_pubforum'])) ? 'owner' : 'author'); $body = html2plain(bbcode($item['body'], ['drop_media' => true, 'tryoembed' => false]), 75, true); if ($body) { $body = htmlentities($body, ENT_QUOTES, 'UTF-8', false); @@ -869,7 +869,7 @@ class Enotify { $x = array( 'notify_link' => $item['llink'], 'name' => $item[$who]['xchan_name'], - 'addr' => $item[$who]['xchan_addr'] ?? $item[$who]['xchan_url'], + 'addr' => $item[$who]['xchan_addr'] ? $item[$who]['xchan_addr'] : $item[$who]['xchan_url'], 'url' => $item[$who]['xchan_url'], 'photo' => $item[$who]['xchan_photo_s'], 'when' => (($edit) ? datetime_convert('UTC', date_default_timezone_get(), $item['edited']) : datetime_convert('UTC', date_default_timezone_get(), $item['created'])), diff --git a/Zotlabs/Lib/Libsync.php b/Zotlabs/Lib/Libsync.php index 19361c4ae..d52b501e4 100644 --- a/Zotlabs/Lib/Libsync.php +++ b/Zotlabs/Lib/Libsync.php @@ -766,11 +766,10 @@ class Libsync { * * @param array $sender * @param array $arr - * @param boolean $absolute (optional) default false * @return array */ - static function sync_locations($sender, $arr, $absolute = false) { + static function sync_locations($sender, $arr) { $ret = []; $what = ''; @@ -787,9 +786,6 @@ class Libsync { if (isset($arr['locations']) && $arr['locations']) { - if ($absolute) - Libzot::check_location_move($sender['hash'], $arr['locations']); - $xisting = q("select * from hubloc where hubloc_hash = '%s'", dbesc($sender['hash']) ); @@ -933,14 +929,7 @@ class Libsync { $what .= 'primary_hub '; $changed = true; } - elseif ($absolute) { - // Absolute sync - make sure the current primary is correctly reflected in the xchan - $pr = hubloc_change_primary($r[0]); - if ($pr) { - $what .= 'xchan_primary '; - $changed = true; - } - } + if (intval($r[0]['hubloc_deleted']) && (!intval($location['deleted']))) { q("update hubloc set hubloc_deleted = 0, hubloc_updated = '%s' where hubloc_id_url = '%s'", dbesc(datetime_convert()), @@ -1009,9 +998,9 @@ class Libsync { } } - // get rid of any hubs we have for this channel which weren't reported. + // get rid of any hublocs we have for this channel which weren't reported. - if ($absolute && $xisting) { + if ($xisting) { foreach ($xisting as $x) { if (!array_key_exists('updated', $x)) { logger('Deleting unreferenced hub location ' . $x['hubloc_addr']); diff --git a/Zotlabs/Lib/Libzot.php b/Zotlabs/Lib/Libzot.php index c635fdb17..93d8a39c0 100644 --- a/Zotlabs/Lib/Libzot.php +++ b/Zotlabs/Lib/Libzot.php @@ -101,7 +101,6 @@ class Libzot { static function build_packet($channel, $type = 'activity', $recipients = null, $msg = [], $encoding = 'activitystreams', $remote_key = null, $methods = '') { $sig_method = get_config('system', 'signature_algorithm', 'sha256'); - $data = [ 'type' => $type, 'encoding' => $encoding, @@ -115,9 +114,9 @@ class Libzot { } if ($msg) { - $actor = channel_url($channel); - if ($encoding === 'activitystreams' && array_key_exists('actor', $msg) && is_string($msg['actor']) && $actor === $msg['actor']) { - $msg = JSalmon::sign($msg, $actor, $channel['channel_prvkey']); + $actors = get_hubloc_id_urls_by_x($channel['channel_hash']); + if ($encoding === 'activitystreams' && array_key_exists('actor', $msg) && is_string($msg['actor']) && in_array($msg['actor'], $actors)) { + $msg = JSalmon::sign($msg, $actors[0], $channel['channel_prvkey']); } $data['data'] = $msg; } @@ -1051,19 +1050,9 @@ class Libzot { } if (is_array($x) && array_key_exists('delivery_report', $x) && is_array($x['delivery_report'])) { - foreach ($x['delivery_report'] as $xx) { call_hooks('dreport_process', $xx); if (is_array($xx) && array_key_exists('message_id', $xx) && DReport::is_storable($xx)) { - - // legacy recipients add a space and their name to the xchan. split those if true. - $legacy_recipient = strpos($xx['recipient'], ' '); - if ($legacy_recipient !== false) { - $legacy_recipient_parts = explode(' ', $xx['recipient'], 2); - $xx['recipient'] = $legacy_recipient_parts[0]; - $xx['name'] = $legacy_recipient_parts[1]; - } - q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_name, dreport_result, dreport_time, dreport_xchan ) values ( '%s', '%s', '%s','%s','%s','%s','%s' ) ", dbesc($xx['message_id']), dbesc($xx['location']), @@ -1254,7 +1243,7 @@ class Libzot { return; } - $r = q("select hubloc_hash, hubloc_network, hubloc_url from hubloc where hubloc_id_url = '%s' order by hubloc_id desc", + $r = q("select hubloc_hash, hubloc_network, hubloc_url from hubloc where hubloc_id_url = '%s' and hubloc_deleted = 0 order by hubloc_id desc", dbesc($AS->actor['id']) ); @@ -1262,7 +1251,7 @@ class Libzot { // Author is unknown to this site. Perform channel discovery and try again. $z = discover_by_webbie($AS->actor['id']); if ($z) { - $r = q("select hubloc_hash, hubloc_network, hubloc_url from hubloc where hubloc_id_url = '%s' order by hubloc_id desc", + $r = q("select hubloc_hash, hubloc_network, hubloc_url from hubloc where hubloc_id_url = '%s' and hubloc_deleted = 0 order by hubloc_id desc", dbesc($AS->actor['id']) ); } @@ -1377,7 +1366,7 @@ class Libzot { return false; } $x = self::find_parent($env, $act); - if ($x === $act->id || $x === $act->obj['id']) { + if ($x === $act->id || (is_array($act->obj) && array_key_exists('id', $act->obj) && $x === $act->obj['id'])) { return true; } } @@ -1435,7 +1424,9 @@ class Libzot { $r = []; - $c = q("select channel_id, channel_hash from channel where channel_removed = 0"); + $c = q("select channel_id, channel_hash from channel where channel_hash != '%s' and channel_removed = 0", + dbesc($msg['sender']) + ); if ($c) { foreach ($c as $cc) { @@ -1463,9 +1454,10 @@ class Libzot { if ($tag['type'] === 'Mention' && (strpos($tag['href'], z_root()) !== false)) { $address = basename($tag['href']); if ($address) { - $z = q("select channel_hash as hash from channel where channel_address = '%s' + $z = q("select channel_hash as hash from channel where channel_address = '%s' and channel_hash != '%s' and channel_removed = 0 limit 1", - dbesc($address) + dbesc($address), + dbesc($msg['sender']) ); if ($z) { $r[] = $z[0]['hash']; @@ -1484,9 +1476,10 @@ class Libzot { $thread_parent = self::find_parent($msg, $act); if ($thread_parent) { - $z = q("select channel_hash as hash from channel left join item on channel.channel_id = item.uid where ( item.thr_parent = '%s' OR item.parent_mid = '%s' ) ", + $z = q("select channel_hash as hash from channel left join item on channel.channel_id = item.uid where ( item.thr_parent = '%s' OR item.parent_mid = '%s' ) and channel_hash != '%s'", + dbesc($thread_parent), dbesc($thread_parent), - dbesc($thread_parent) + dbesc($msg['sender']) ); if ($z) { foreach ($z as $zv) { @@ -1980,7 +1973,7 @@ class Libzot { $ret = []; - $signer = q("select hubloc_hash, hubloc_url from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1", + $signer = q("select hubloc_hash, hubloc_url from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' order by hubloc_id desc limit 1", dbesc($a['signature']['signer']) ); @@ -2008,7 +2001,7 @@ class Libzot { continue; } - $r = q("select hubloc_hash, hubloc_network from hubloc where hubloc_id_url = '%s'", + $r = q("select hubloc_hash, hubloc_network from hubloc where hubloc_id_url = '%s' order by hubloc_id desc", dbesc($AS->actor['id']) ); @@ -2313,120 +2306,6 @@ class Libzot { /** - * @brief Processes delivery of profile. - * - * @param string $sender - * @param array $arr - * @param array $deliveries (unused) - * @return void - * @see import_directory_profile() - * - */ - static function process_profile_delivery($sender, $arr, $deliveries) { - - logger('process_profile_delivery', LOGGER_DEBUG); - - $r = q("select xchan_addr from xchan where xchan_hash = '%s' limit 1", - dbesc($sender) - ); - if ($r) { - Libzotdir::import_directory_profile($sender, $arr, $r[0]['xchan_addr'], UPDATE_FLAGS_UPDATED, 0); - } - } - - - /** - * @brief - * - * @param string $sender - * @param array $arr - * @param array $deliveries (unused) deliveries is irrelevant - * @return void - */ - static function process_location_delivery($sender, $arr, $deliveries) { - - // deliveries is irrelevant - logger('process_location_delivery', LOGGER_DEBUG); - - $r = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc($sender) - ); - if ($r) { - $xchan = ['id' => $r[0]['xchan_guid'], 'id_sig' => $r[0]['xchan_guid_sig'], - 'hash' => $r[0]['xchan_hash'], 'public_key' => $r[0]['xchan_pubkey']]; - } - if (array_key_exists('locations', $arr) && $arr['locations']) { - $x = Libsync::sync_locations($xchan, $arr, true); - logger('results: ' . print_r($x, true), LOGGER_DEBUG); - if ($x['changed']) { - //$guid = random_string() . '@' . App::get_hostname(); - Libzotdir::update_modtime($sender, $r[0]['xchan_guid'], $arr['locations'][0]['address'], UPDATE_FLAGS_UPDATED); - } - } - } - - /** - * @brief Checks for a moved channel and sets the channel_moved flag. - * - * Currently the effect of this flag is to turn the channel into 'read-only' mode. - * New content will not be processed (there was still an issue with blocking the - * ability to post comments as of 10-Mar-2016). - * We do not physically remove the channel at this time. The hub admin may choose - * to do so, but is encouraged to allow a grace period of several days in case there - * are any issues migrating content. This packet will generally be received by the - * original site when the basic channel import has been processed. - * - * This will only be executed on the old location - * if a new location is reported and there is only one location record. - * The rest of the hubloc syncronisation will be handled within - * sync_locations - * - * @param string $sender_hash A channel hash - * @param array $locations - * @return void - */ - static function check_location_move($sender_hash, $locations) { - - if (!$locations) - return; - - if (count($locations) != 1) - return; - - $loc = $locations[0]; - - $r = q("select * from channel where channel_hash = '%s' limit 1", - dbesc($sender_hash) - ); - - if (!$r) - return; - - if ($loc['url'] !== z_root()) { - $x = q("update channel set channel_moved = '%s' where channel_hash = '%s' limit 1", - dbesc($loc['url']), - dbesc($sender_hash) - ); - - // federation plugins may wish to notify connections - // of the move on singleton networks - - $arr = [ - 'channel' => $r[0], - 'locations' => $locations - ]; - /** - * @hooks location_move - * Called when a new location has been provided to a UNO channel (indicating a move rather than a clone). - * * \e array \b channel - * * \e array \b locations - */ - call_hooks('location_move', $arr); - } - } - - - /** * @brief Returns an array with all known distinct hubs for this channel. * * @param array $channel an associative array which must contain diff --git a/Zotlabs/Lib/MessageFilter.php b/Zotlabs/Lib/MessageFilter.php index 7d6dcbe8e..e7382c0d5 100644 --- a/Zotlabs/Lib/MessageFilter.php +++ b/Zotlabs/Lib/MessageFilter.php @@ -25,7 +25,7 @@ class MessageFilter { if ($exclude) { foreach ($exclude as $word) { - $word = trim($word); + $word = html_entity_decode(trim($word)); if (! $word) { continue; } @@ -73,7 +73,7 @@ class MessageFilter { if ($include) { foreach ($include as $word) { - $word = trim($word); + $word = html_entity_decode(trim($word)); if (! $word) { continue; } diff --git a/Zotlabs/Lib/Queue.php b/Zotlabs/Lib/Queue.php index c3f9cda20..348a2a079 100644 --- a/Zotlabs/Lib/Queue.php +++ b/Zotlabs/Lib/Queue.php @@ -65,16 +65,51 @@ class Queue { ); } - - static function remove($id,$channel_id = 0) { - logger('queue: remove queue item ' . $id,LOGGER_DEBUG); + public static function remove($id, $channel_id = 0) { + logger('queue: remove queue item ' . $id, LOGGER_DEBUG); $sql_extra = (($channel_id) ? " and outq_channel = " . intval($channel_id) . " " : ''); - q("DELETE FROM outq WHERE outq_hash = '%s' $sql_extra", + // figure out what endpoint it is going to. + $record = q("select outq_posturl from outq where outq_hash = '%s' $sql_extra", dbesc($id) ); - } + if ($record) { + q("DELETE FROM outq WHERE outq_hash = '%s' $sql_extra", + dbesc($id) + ); + + // If there's anything remaining in the queue for this site, move one of them to the next active + // queue run by setting outq_scheduled back to the present. We may be attempting to deliver it + // as a 'piled_up' delivery, but this ensures the site has an active queue entry as long as queued + // 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", + 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']) + ); + } + + if ($x) { + q("COMMIT"); + } + else { + q("ROLLBACK"); + } + + } + } static function remove_by_posturl($posturl) { logger('queue: remove queue posturl ' . $posturl,LOGGER_DEBUG); @@ -84,8 +119,6 @@ class Queue { ); } - - static function set_delivered($id,$channel = 0) { logger('queue: set delivered ' . $id,LOGGER_DEBUG); $sql_extra = (($channel['channel_id']) ? " and outq_channel = " . intval($channel['channel_id']) . " " : ''); @@ -152,17 +185,19 @@ class Queue { $y = q("select site_update, site_dead from site where site_url = '%s' ", dbesc($base) ); - if($y) { - if(intval($y[0]['site_dead'])) { + + if ($y) { + // Don't bother delivering if the site is dead. + // And if we haven't heard from the site in over a month - let them through but 3 strikes you're out. + if (intval($y[0]['site_dead']) || ($y[0]['site_update'] < datetime_convert('UTC', 'UTC', 'now - 1 month') && $outq['outq_priority'] > 20)) { + q("update dreport set dreport_result = '%s' where dreport_queue = '%s'", + dbesc('site dead'), + dbesc($outq['outq_hash']) + ); self::remove_by_posturl($outq['outq_posturl']); logger('dead site ignored ' . $base); return; } - if($y[0]['site_update'] < datetime_convert('UTC','UTC','now - 1 month')) { - self::update($outq['outq_hash'], 10); - logger('immediate delivery deferred for site ' . $base); - return; - } } else { diff --git a/Zotlabs/Lib/QueueWorker.php b/Zotlabs/Lib/QueueWorker.php index fd2ebd7e1..696fb79fc 100644 --- a/Zotlabs/Lib/QueueWorker.php +++ b/Zotlabs/Lib/QueueWorker.php @@ -137,10 +137,26 @@ class QueueWorker { self::$workermaxage = self::$workermaxage > 120 ? self::$workermaxage : 300; } - q("update workerq set workerq_reservationid = null where workerq_reservationid is not null and workerq_processtimeout < %s", + self::qstart(); + + // skip locked is preferred but is not supported by mariadb < 10.6 which is still used a lot - hence make it optional + $sql_quirks = ((get_config('system', 'db_skip_locked_supported')) ? 'SKIP LOCKED' : 'NOWAIT'); + + $r = q("SELECT workerq_id FROM workerq WHERE workerq_reservationid IS NOT NULL AND workerq_processtimeout < %s FOR UPDATE $sql_quirks", db_utcnow() ); + if ($r) { + $ids = ids_to_querystr($r, 'workerq_id'); + $u = dbq("update workerq set workerq_reservationid = null where workerq_id in ($ids)"); + } + + self::qcommit(); + + //q("update workerq set workerq_reservationid = null where workerq_reservationid is not null and workerq_processtimeout < %s", + //db_utcnow() + //); + //usleep(self::$workersleep); $workers = dbq("select count(distinct workerq_reservationid) as total from workerq where workerq_reservationid is not null"); @@ -175,10 +191,10 @@ class QueueWorker { self::qstart(); - // This is probably the better solution but is not supported by mariadb < 10.6 which is still used a lot. - // $work = dbq("SELECT workerq_id FROM workerq WHERE workerq_reservationid IS NULL ORDER BY workerq_priority DESC, workerq_id ASC LIMIT 1 FOR UPDATE SKIP LOCKED;"); + // skip locked is preferred but is not supported by mariadb < 10.6 which is still used a lot - hence make it optional + $sql_quirks = ((get_config('system', 'db_skip_locked_supported')) ? 'SKIP LOCKED' : 'NOWAIT'); - $work = dbq("SELECT workerq_id, workerq_cmd FROM workerq WHERE workerq_reservationid IS NULL ORDER BY workerq_priority DESC, workerq_id ASC LIMIT 1 FOR UPDATE;"); + $work = dbq("SELECT workerq_id, workerq_cmd FROM workerq WHERE workerq_reservationid IS NULL ORDER BY workerq_priority DESC, workerq_id ASC LIMIT 1 FOR UPDATE $sql_quirks"); if (!$work) { self::qcommit(); diff --git a/Zotlabs/Module/Acl.php b/Zotlabs/Module/Acl.php index bcd79c873..b98f5621e 100644 --- a/Zotlabs/Module/Acl.php +++ b/Zotlabs/Module/Acl.php @@ -378,6 +378,7 @@ class Acl extends \Zotlabs\Web\Controller { "name" => $g['name'], "id" => urlencode($g['id']), "xid" => $g['hash'], + "url" => $g['url'], "link" => $clink, "nick" => ((strpos($g['nick'],'@')) ? substr($g['nick'],0,strpos($g['nick'],'@')) : $g['nick']), "self" => (intval($g['abook_self']) ? 'abook-self' : ''), diff --git a/Zotlabs/Module/Cal.php b/Zotlabs/Module/Cal.php index 9049fe7d0..70d8aa755 100644 --- a/Zotlabs/Module/Cal.php +++ b/Zotlabs/Module/Cal.php @@ -65,13 +65,9 @@ class Cal extends Controller { nav_set_selected('Calendar'); - head_add_css('/library/fullcalendar/packages/core/main.min.css'); - head_add_css('/library/fullcalendar/packages/daygrid/main.min.css'); + head_add_js('/library/fullcalendar/dist/index.global.js'); head_add_css('cdav_calendar.css'); - head_add_js('/library/fullcalendar/packages/core/main.min.js'); - head_add_js('/library/fullcalendar/packages/daygrid/main.min.js'); - $sql_extra = permissions_sql($channel['channel_id'], get_observer_hash(), 'event'); if(! perm_is_allowed($channel['channel_id'], get_observer_hash(), 'view_contacts') || App::$profile['hide_friends']) diff --git a/Zotlabs/Module/Cdav.php b/Zotlabs/Module/Cdav.php index 8e77515ce..b6d7ff5a3 100644 --- a/Zotlabs/Module/Cdav.php +++ b/Zotlabs/Module/Cdav.php @@ -901,19 +901,9 @@ class Cdav extends Controller { //Display calendar(s) here if(argc() <= 3 && argv(1) === 'calendar') { - - head_add_css('/library/fullcalendar/packages/core/main.min.css'); - head_add_css('/library/fullcalendar/packages/daygrid/main.min.css'); - head_add_css('/library/fullcalendar/packages/timegrid/main.min.css'); - head_add_css('/library/fullcalendar/packages/list/main.min.css'); + head_add_js('/library/fullcalendar/dist/index.global.js'); head_add_css('cdav_calendar.css'); - head_add_js('/library/fullcalendar/packages/core/main.min.js'); - head_add_js('/library/fullcalendar/packages/interaction/main.min.js'); - head_add_js('/library/fullcalendar/packages/daygrid/main.min.js'); - head_add_js('/library/fullcalendar/packages/timegrid/main.min.js'); - head_add_js('/library/fullcalendar/packages/list/main.min.js'); - $o = ''; $sources = ''; $resource_id = ''; diff --git a/Zotlabs/Module/Channel.php b/Zotlabs/Module/Channel.php index d3d72ca10..748d990cb 100644 --- a/Zotlabs/Module/Channel.php +++ b/Zotlabs/Module/Channel.php @@ -247,9 +247,7 @@ class Channel extends Controller { $channel_acl = ['allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '']; } - if ($perms['post_wall']) { - $x = [ 'is_owner' => $is_owner, 'allow_location' => ((($is_owner || $observer) && (intval(get_pconfig(App::$profile['profile_uid'], 'system', 'use_browser_location')))) ? true : false), @@ -273,6 +271,12 @@ class Channel extends Controller { $o .= status_editor($a, $x, false, 'Channel'); } + // Add pinned content + if (!x($_REQUEST, 'mid') && !$search) { + $pinned = new \Zotlabs\Widget\Pinned; + $r = $pinned->widget(intval(App::$profile['profile_uid']), [ITEM_TYPE_POST]); + $o .= $r['html']; + } } @@ -423,17 +427,14 @@ class Channel extends Controller { $items = []; } - // Add pinned content - if (!x($_REQUEST, 'mid') && !$search) { - $pinned = new \Zotlabs\Widget\Pinned; - $r = $pinned->widget(intval(App::$profile['profile_uid']), [ITEM_TYPE_POST]); - $o .= $r['html']; - } + $mode = (($search) ? 'search' : 'channel'); if ((!$update) && (!$load)) { + + //if we got a decoded hash we must encode it again before handing to javascript $mid = gen_link_id($mid); diff --git a/Zotlabs/Module/Chanview.php b/Zotlabs/Module/Chanview.php index f43432376..e178b27df 100644 --- a/Zotlabs/Module/Chanview.php +++ b/Zotlabs/Module/Chanview.php @@ -16,17 +16,17 @@ class Chanview extends \Zotlabs\Web\Controller { $r = null; - if(isset($_REQUEST['hash']) && $_REQUEST['hash']) { + if(!empty($_REQUEST['hash'])) { $r = q("select * from xchan where xchan_hash = '%s' and xchan_deleted = 0", dbesc($_REQUEST['hash']) ); } - if(isset($_REQUEST['address']) && $_REQUEST['address']) { + if(!empty($_REQUEST['address'])) { $r = q("select * from xchan where xchan_addr = '%s' and xchan_deleted = 0", dbesc(punify($_REQUEST['address'])) ); } - elseif(local_channel() && isset($_REQUEST['cid']) && $_REQUEST['cid']) { + elseif(local_channel() && !empty($_REQUEST['cid'])) { $r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d and abook_id = %d and xchan_deleted = 0", @@ -34,8 +34,7 @@ class Chanview extends \Zotlabs\Web\Controller { intval($_REQUEST['cid']) ); } - elseif(isset($_REQUEST['url']) && $_REQUEST['url']) { - + elseif(!empty($_REQUEST['url'])) { // if somebody re-installed they will have more than one xchan, use the most recent name date as this is // the most useful consistently ascending table item we have. @@ -56,7 +55,7 @@ class Chanview extends \Zotlabs\Web\Controller { if(! App::$poi) { logger('mod_chanview: fallback'); - if(isset($_REQUEST['address']) && $_REQUEST['address']) { + if(!empty($_REQUEST['address'])) { $href = Webfinger::zot_url(punify($_REQUEST['address'])); if($href) { $_REQUEST['url'] = $href; @@ -65,9 +64,9 @@ class Chanview extends \Zotlabs\Web\Controller { $r = null; - if(isset($_REQUEST['url']) && $_REQUEST['url']) { + if(!empty($_REQUEST['url'])) { - $zf = Zotfinger::exec($_REQUEST['url'], null); + $zf = Zotfinger::exec($_REQUEST['url']); if(array_path_exists('signature/signer',$zf) && $zf['signature']['signer'] === $_REQUEST['url'] && intval($zf['signature']['header_valid'])) { Libzot::import_xchan($zf['data']); @@ -79,6 +78,7 @@ class Chanview extends \Zotlabs\Web\Controller { } } if(! $r) { + if(discover_by_webbie($_REQUEST['url'])) { $r = q("select * from xchan where xchan_url = '%s' and xchan_deleted = 0", dbesc($_REQUEST['url']) diff --git a/Zotlabs/Module/Connections.php b/Zotlabs/Module/Connections.php index ea30b9b9e..e0f463c76 100644 --- a/Zotlabs/Module/Connections.php +++ b/Zotlabs/Module/Connections.php @@ -145,6 +145,7 @@ class Connections extends \Zotlabs\Web\Controller { } $search = ((x($_REQUEST,'search')) ? notags(trim($_REQUEST['search'])) : ''); + $search_xchan = ((x($_REQUEST,'search_xchan')) ? notags(trim($_REQUEST['search_xchan'])) : ''); $tabs = array( /* @@ -233,10 +234,15 @@ class Connections extends \Zotlabs\Web\Controller { if($search) { $search_hdr = $search; - $search_txt = dbesc(protect_sprintf(preg_quote($search))); - $searching = true; + $search_txt = (($search_xchan) ? urldecode($search_xchan) : preg_quote($search)); + + if ($search_xchan) { + $sql_extra .= " AND xchan_hash = '" . protect_sprintf(dbesc($search_txt)) . "' "; + } + else { + $sql_extra .= " AND xchan_name LIKE '%%" . protect_sprintf(dbesc($search_txt)) . "%%' "; + } } - $sql_extra .= (($searching) ? protect_sprintf(" AND xchan_name like '%$search_txt%' ") : ""); if(isset($_REQUEST['gid']) && $_REQUEST['gid']) { $sql_extra .= " and xchan_hash in ( select xchan from pgrp_member where gid = " . intval($_REQUEST['gid']) . " and uid = " . intval(local_channel()) . " ) "; @@ -396,7 +402,7 @@ class Connections extends \Zotlabs\Web\Controller { '$search' => $search_hdr, '$label' => t('Search'), '$role_label' => t('Contact role'), - '$desc' => t('Search your connections'), + '$desc' => $search ?? t('Search your connections'), '$finding' => (($searching) ? t('Contact search') . ": '" . $search . "'" : ""), '$submit' => t('Find'), '$edit' => t('Edit'), diff --git a/Zotlabs/Module/Dirsearch.php b/Zotlabs/Module/Dirsearch.php index 34678af57..c0df8a1e3 100644 --- a/Zotlabs/Module/Dirsearch.php +++ b/Zotlabs/Module/Dirsearch.php @@ -263,7 +263,7 @@ class Dirsearch extends Controller { xprof.xprof_homepage as homepage, xprof.xprof_hometown as hometown, xprof.xprof_keywords as keywords - from xchan left join xprof on xchan_hash = xprof_hash left join hubloc on hubloc_hash = xchan_hash + 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 $safesql $order $qlimit", db_utcnow(), diff --git a/Zotlabs/Module/Hq.php b/Zotlabs/Module/Hq.php index 0b2d0ea7d..77d56e9a2 100644 --- a/Zotlabs/Module/Hq.php +++ b/Zotlabs/Module/Hq.php @@ -51,13 +51,20 @@ class Hq extends \Zotlabs\Web\Controller { $o = ''; if($item_hash) { + // select the target item with a bias to our own item + $sql_order = ((local_channel() > $sys['channel_id']) ? 'DESC' : 'ASC'); - $r = q("select id, uid, mid, parent_mid, thr_parent, verb, item_type, item_deleted, item_blocked from item where mid = '%s' limit 1", + $r = q("select id, uid, mid, parent_mid, thr_parent, verb, item_type, item_deleted, item_blocked from item where uid in (%d, %d) and mid = '%s' order by uid $sql_order limit 2", + intval(local_channel()), + intval($sys['channel_id']), dbesc($item_hash) ); if($r) { $target_item = $r[0]; + if (intval($target_item['uid']) === intval($sys['channel_id'])) { + $sys_item = true; + } } //if the item is to be moderated redirect to /moderate @@ -158,19 +165,18 @@ class Hq extends \Zotlabs\Web\Controller { } if($load && $target_item) { - $r = null; - - $r = q("SELECT item.id AS item_id FROM item - WHERE uid = %d - AND mid = '%s' - $item_normal - LIMIT 1", - intval(local_channel()), - dbesc($target_item['parent_mid']) - ); - if(!$r) { - $sys_item = true; + if (!$sys_item) { + $r = q("SELECT item.id AS item_id FROM item + WHERE uid = %d + AND mid = '%s' + $item_normal + LIMIT 1", + intval(local_channel()), + dbesc($target_item['parent_mid']) + ); + } + else { $sql_extra = item_permissions_sql($sys['channel_id']); $r = q("SELECT item.id AS item_id FROM item @@ -184,20 +190,18 @@ class Hq extends \Zotlabs\Web\Controller { } } elseif($update && $target_item) { - $r = null; - - $r = q("SELECT item.parent AS item_id FROM item - WHERE uid = %d - AND parent_mid = '%s' - $item_normal_update - $simple_update - LIMIT 1", - intval(local_channel()), - dbesc($target_item['parent_mid']) - ); - - if(!$r) { - $sys_item = true; + if (!$sys_item) { + $r = q("SELECT item.parent AS item_id FROM item + WHERE uid = %d + AND parent_mid = '%s' + $item_normal_update + $simple_update + LIMIT 1", + intval(local_channel()), + dbesc($target_item['parent_mid']) + ); + } + else { $sql_extra = item_permissions_sql($sys['channel_id']); $r = q("SELECT item.parent AS item_id FROM item @@ -245,6 +249,7 @@ class Hq extends \Zotlabs\Web\Controller { $options['offset'] = $_REQUEST['offset'] ?? 0; $options['type'] = $_REQUEST['type'] ?? ''; + $options['author'] = ((isset($_REQUEST['author'])) ? urldecode($_REQUEST['author']) : ''); $ret = Messages::get_messages_page($options); diff --git a/Zotlabs/Module/Like.php b/Zotlabs/Module/Like.php index 98fa7db5a..ef41faedd 100644 --- a/Zotlabs/Module/Like.php +++ b/Zotlabs/Module/Like.php @@ -546,6 +546,7 @@ class Like extends Controller { $arr['deny_cid'] = $deny_cid; $arr['deny_gid'] = $deny_gid; $arr['item_private'] = $private; + $arr['created'] = datetime_convert(); call_hooks('post_local', $arr); diff --git a/Zotlabs/Module/Magic.php b/Zotlabs/Module/Magic.php index bfd38d2fa..6d997c662 100644 --- a/Zotlabs/Module/Magic.php +++ b/Zotlabs/Module/Magic.php @@ -16,7 +16,7 @@ class Magic extends Controller { 'url' => '', 'message' => '' ]; - + logger('mod_magic: invoked', LOGGER_DEBUG); logger('args: ' . print_r($_REQUEST,true),LOGGER_DATA); @@ -43,12 +43,12 @@ class Magic extends Controller { $basepath = $parsed['scheme'] . '://' . $parsed['host'] . (isset($parsed['port']) ? ':' . $parsed['port'] : ''); $owapath = SConfig::get($basepath,'system','openwebauth', $basepath . '/owa'); - // This is ready-made for a plugin that provides a blacklist or "ask me" before blindly authenticating. + // This is ready-made for a plugin that provides a blacklist or "ask me" before blindly authenticating. // By default, we'll proceed without asking. $arr = [ 'channel_id' => local_channel(), - 'destination' => $dest, + 'destination' => $dest, 'proceed' => true ]; diff --git a/Zotlabs/Module/Notes.php b/Zotlabs/Module/Notes.php index 57b8f30db..2fd719f25 100644 --- a/Zotlabs/Module/Notes.php +++ b/Zotlabs/Module/Notes.php @@ -39,7 +39,7 @@ class Notes extends Controller { } set_pconfig(local_channel(),'notes','text',$body); - $ret['html'] = bbcode($body); + $ret['html'] = bbcode($body, ['tryoembed' => false]); $ret['success'] = true; } diff --git a/Zotlabs/Module/Settings/Account.php b/Zotlabs/Module/Settings/Account.php index 5e1fb176e..85e7c793f 100644 --- a/Zotlabs/Module/Settings/Account.php +++ b/Zotlabs/Module/Settings/Account.php @@ -105,6 +105,7 @@ class Account { '$password1' => array('npassword', t('Enter New Password'), '', ''), '$password2' => array('confirm', t('Confirm New Password'), '', t('Leave password fields blank unless changing')), '$submit' => t('Submit'), + '$mfa' => t('Multi-Factor Authentication'), '$email' => array('email', t('DId2 or Email Address:'), $email, '', '', $attremail), '$email_hidden' => (($attremail) ? $email : ''), '$removeme' => t('Remove Account'), diff --git a/Zotlabs/Module/Settings/Multifactor.php b/Zotlabs/Module/Settings/Multifactor.php new file mode 100644 index 000000000..8b91bc87a --- /dev/null +++ b/Zotlabs/Module/Settings/Multifactor.php @@ -0,0 +1,90 @@ +<?php + +namespace Zotlabs\Module\Settings; + +use App; +use chillerlan\QRCode\QRCode; +use Zotlabs\Lib\AConfig; +use Zotlabs\Lib\System; +use OTPHP\TOTP; +use ParagonIE\ConstantTime\Base32; + + +class Multifactor { + public function post() { + check_form_security_token_redirectOnErr('/settings/multifactor', 'settings_mfa'); + + $account = App::get_account(); + if (!$account) { + return; + } + + if (empty($_POST['password'])) { + notice(t('Password is required') . EOL); + return; + } + + $password = trim($_POST['password']); + if(!account_verify_password($account['account_email'], $password)) { + notice(t('The provided password is not correct') . EOL); + return; + } + + $enable_mfa = isset($_POST['enable_mfa']) ? (int) $_POST['enable_mfa'] : false; + AConfig::Set($account['account_id'], 'system', 'mfa_enabled', $enable_mfa); + if ($enable_mfa) { + $_SESSION['2FA_VERIFIED'] = true; + } + } + + public function get() { + $account = App::get_account(); + if (!$account) { + return ''; + } + + if (!$account['account_external']) { + $otp = TOTP::create(); + $otp->setLabel($account['account_email']); + // $otp->setLabel(rawurlencode(System::get_platform_name())); + $otp->setIssuer(rawurlencode(System::get_platform_name())); + + $mySecret = trim(Base32::encodeUpper(random_bytes(32)), '='); + $otp = TOTP::create($mySecret); + q("UPDATE account set account_external = '%s' where account_id = %d", + dbesc($otp->getSecret()), + intval($account['account_id']) + ); + $account['account_external'] = $otp->getSecret(); + } + + $otp = TOTP::create($account['account_external']); + $otp->setLabel($account['account_email']); + $otp->setIssuer(rawurlencode(System::get_platform_name())); + $uri = $otp->getProvisioningUri(); + return replace_macros(get_markup_template('totp_setup.tpl'), + [ + '$form_security_token' => get_form_security_token("settings_mfa"), + '$title' => t('Account Multi-Factor Authentication'), + '$secret_text' => t('This is your generated secret. It may be used in some cases if the QR image cannot be read. Please store it in a safe place.'), + '$test_title' => t('Please enter the code from your authenticator app'), + '$test_title_sub' => t('You will only be able to enable MFA if the test passes'), + '$qrcode' => (new QRCode())->render($uri), + '$uri' => $uri, + '$secret' => ($account['account_external'] ?? ''), + '$test_pass' => t("Congratulations, the provided code was correct"), + '$test_fail' => t("Incorrect code"), + '$enable_mfa' => [ + 'enable_mfa', + t('Enable Multi-Factor Authentication'), + AConfig::Get($account['account_id'], 'system', 'mfa_enabled'), + t('Logging in will require you to be in possession of your smartphone'), + [t('No'), t('Yes')] + ], + '$password' => ['password', t('Your account password'), '', t('Required')], + '$submit' => t('Submit'), + '$test' => t('Test') + ] + ); + } +} diff --git a/Zotlabs/Module/Totp_check.php b/Zotlabs/Module/Totp_check.php new file mode 100644 index 000000000..8212d3716 --- /dev/null +++ b/Zotlabs/Module/Totp_check.php @@ -0,0 +1,86 @@ +<?php + +namespace Zotlabs\Module; + +use App; +use Zotlabs\Web\Controller; +use OTPHP\TOTP; + +class Totp_check extends Controller { + + public function post() { + $retval = ['status' => false]; + $static = $_POST['totp_code_static'] ?? false; + + if (!local_channel()) { + if ($static) { + goaway(z_root()); + } + + json_return_and_die($retval); + } + + $account = App::get_account(); + if (!$account) { + json_return_and_die($retval); + } + + $secret = $account['account_external']; + $input = (isset($_POST['totp_code'])) ? trim($_POST['totp_code']) : ''; + + if ($secret && $input) { + $otp = TOTP::create($secret); // create TOTP object from the secret. + if ($otp->verify($_POST['totp_code']) || $input === $secret ) { + logger('otp_success'); + $_SESSION['2FA_VERIFIED'] = true; + + if ($static) { + goaway(z_root()); + } + + $retval['status'] = true; + json_return_and_die($retval); + } + logger('otp_fail'); + } + + if ($static) { + if(empty($_SESSION['totp_try_count'])) { + $_SESSION['totp_try_count'] = 1; + } + + if ($_SESSION['totp_try_count'] > 2) { + goaway('logout'); + } + + $_SESSION['totp_try_count']++; + goaway(z_root()); + } + + json_return_and_die($retval); + } + + public function get() { + + if (!local_channel() || App::$module === 'totp_check') { + goaway(z_root()); + } + + $account = App::get_account(); + if (!$account) { + return t('Account not found.'); + } + + $id = $account['account_email']; + + return replace_macros(get_markup_template('totp.tpl'), + [ + '$header' => t('Multifactor Verification'), + '$id' => $id, + '$desc' => t('Please enter the verification key from your authenticator app'), + '$submit' => t('Verify') + ] + ); + } +} + diff --git a/Zotlabs/Widget/Channel_activities.php b/Zotlabs/Widget/Channel_activities.php index 06080f8c8..a799ea81e 100644 --- a/Zotlabs/Widget/Channel_activities.php +++ b/Zotlabs/Widget/Channel_activities.php @@ -179,7 +179,7 @@ class Channel_activities { $account = App::get_account(); - $r = q("SELECT channel_id, channel_name, xchan_photo_s FROM channel + $r = q("SELECT channel_id, channel_name, xchan_addr, xchan_photo_s FROM channel LEFT JOIN xchan ON channel_hash = xchan_hash WHERE channel_account_id = %d AND channel_id != %d AND channel_removed = 0", @@ -223,7 +223,7 @@ class Channel_activities { $i[] = [ 'url' => z_root() . '/manage/' . $rr['channel_id'], 'title' => '', - 'summary' => '<img src="' . $rr['xchan_photo_s'] . '" class="menu-img-2">' . $rr['channel_name'], + 'summary' => '<div class="text-truncate lh-sm"><img src="' . $rr['xchan_photo_s'] . '" class="menu-img-2">' . '<strong>' . $rr['channel_name'] . '</strong><br><small class="opacity-75">' . $rr['xchan_addr'] . '</small></div>', 'footer' => $footer ]; diff --git a/Zotlabs/Widget/Messages.php b/Zotlabs/Widget/Messages.php index 8c413e16e..0a8900c4f 100644 --- a/Zotlabs/Widget/Messages.php +++ b/Zotlabs/Widget/Messages.php @@ -35,7 +35,8 @@ class Messages { 'notice_messages_title' => t('Notices'), 'loading' => t('Loading'), 'empty' => t('No messages'), - 'unseen_count' => t('Unseen') + 'unseen_count' => t('Unseen'), + 'filter' => t('Filter by name or address') ] ]); @@ -47,7 +48,8 @@ class Messages { return; $offset = $options['offset'] ?? 0; - $type = $options['type'] ?? 'default'; + $type = $options['type'] ?? ''; + $author = $options['author'] ?? ''; if ($offset == -1) { return; @@ -63,10 +65,12 @@ class Messages { $entries = []; $limit = 30; $dummy_order_sql = ''; + $author_sql = ''; $loadtime = (($offset) ? $_SESSION['messages_loadtime'] : datetime_convert()); $vnotify = get_pconfig(local_channel(), 'system', 'vnotify', -1); - $vnotify_sql = ''; + $vnotify_sql_c = ''; + $vnotify_sql_i = ''; if (!($vnotify & VNOTIFY_LIKE)) { $vnotify_sql_c = " AND c.verb NOT IN ('" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') "; @@ -77,6 +81,10 @@ class Messages { $vnotify_sql_i = " AND i.verb NOT IN ('" . dbesc(ACTIVITY_DISLIKE) . "') "; } + if($author) { + $author_sql = " AND i.owner_xchan = '" . protect_sprintf(dbesc($author)) . "' "; + } + switch($type) { case 'direct': $type_sql = ' AND i.item_private = 2 '; @@ -100,6 +108,7 @@ class Messages { AND i.created <= '%s' $type_sql AND i.item_thread_top = 1 + $author_sql $item_normal_i ORDER BY i.created DESC $dummy_order_sql LIMIT $limit OFFSET $offset", @@ -153,7 +162,7 @@ class Messages { $summary = '...'; } else { - $summary = substr_words($summary, 68); + $summary = substr_words($summary, 140); } switch(intval($item['item_private'])) { @@ -169,6 +178,7 @@ class Messages { $entries[$i]['author_name'] = $item['author']['xchan_name']; $entries[$i]['author_addr'] = (($item['author']['xchan_addr']) ? $item['author']['xchan_addr'] : $item['author']['xchan_url']); + $entries[$i]['author_img'] = $item['author']['xchan_photo_s']; $entries[$i]['info'] = $info; $entries[$i]['created'] = datetime_convert('UTC', date_default_timezone_get(), $item['created']); $entries[$i]['summary'] = $summary; @@ -240,6 +250,7 @@ class Messages { if (!local_channel()) return; + $limit = 30; $offset = 0; @@ -247,7 +258,14 @@ class Messages { $offset = intval($options['offset']); } - $notices = q("SELECT * FROM notify WHERE uid = %d + $author_url = $options['author'] ?? ''; + $author_sql = ''; + + if($author_url) { + $author_sql = " AND url = '" . protect_sprintf(dbesc($author_url)) . "' "; + } + + $notices = q("SELECT * FROM notify WHERE uid = %d $author_sql ORDER BY created DESC LIMIT $limit OFFSET $offset", intval(local_channel()) ); @@ -265,6 +283,7 @@ class Messages { $entries[$i]['author_name'] = $notice['xname']; $entries[$i]['author_addr'] = $notice['url']; + $entries[$i]['author_img'] = $notice['photo'];// $item['author']['xchan_photo_s']; $entries[$i]['info'] = ''; $entries[$i]['created'] = datetime_convert('UTC', date_default_timezone_get(), $notice['created']); $entries[$i]['summary'] = $summary; diff --git a/Zotlabs/Widget/Pinned.php b/Zotlabs/Widget/Pinned.php index 83036e98c..6d2618deb 100644 --- a/Zotlabs/Widget/Pinned.php +++ b/Zotlabs/Widget/Pinned.php @@ -42,6 +42,8 @@ class Pinned { $observer = \App::get_observer(); + xchan_query($items); + foreach($items as $item) { $midb64 = gen_link_id($item['mid']); @@ -49,8 +51,8 @@ class Pinned { if(isset($observer['xchan_hash']) && in_array($observer['xchan_hash'], get_pconfig($item['uid'], 'pinned_hide', $midb64, []))) continue; - $author = channelx_by_hash($item['author_xchan']); - $owner = channelx_by_hash($item['owner_xchan']); + $author = $item['author']; + $owner = $item['owner']; $profile_avatar = $author['xchan_photo_m']; $profile_link = chanlink_hash($item['author_xchan']); @@ -159,7 +161,9 @@ class Pinned { 'hide' => (! $is_new && isset($observer['xchan_hash']) && $observer['xchan_hash'] != $owner['xchan_hash'] ? t("Don't show") : ''), // end toolbar buttons 'modal_dismiss' => t('Close'), - 'responses' => $conv_responses + 'responses' => $conv_responses, + 'author_id' => (($author['xchan_addr']) ? $author['xchan_addr'] : $author['xchan_url']) + ]; $tpl = get_markup_template('pinned_item.tpl'); |