diff options
Diffstat (limited to 'Zotlabs')
-rw-r--r-- | Zotlabs/Daemon/Channel_purge.php | 2 | ||||
-rw-r--r-- | Zotlabs/Daemon/Cron.php | 49 | ||||
-rw-r--r-- | Zotlabs/Daemon/Expire.php | 4 | ||||
-rw-r--r-- | Zotlabs/Daemon/Importdoc.php | 27 | ||||
-rw-r--r-- | Zotlabs/Daemon/Notifier.php | 26 | ||||
-rw-r--r-- | Zotlabs/Lib/Activity.php | 91 | ||||
-rw-r--r-- | Zotlabs/Lib/Libzot.php | 52 | ||||
-rw-r--r-- | Zotlabs/Lib/Mailer.php | 86 | ||||
-rw-r--r-- | Zotlabs/Lib/ThreadItem.php | 92 | ||||
-rw-r--r-- | Zotlabs/Module/Admin/Accounts.php | 258 | ||||
-rw-r--r-- | Zotlabs/Module/Conversation.php | 3 | ||||
-rw-r--r-- | Zotlabs/Module/Dreport.php | 8 | ||||
-rw-r--r-- | Zotlabs/Module/Editpost.php | 1 | ||||
-rw-r--r-- | Zotlabs/Module/Item.php | 27 | ||||
-rw-r--r-- | Zotlabs/Module/Owa.php | 184 | ||||
-rw-r--r-- | Zotlabs/Module/Pubstream.php | 8 | ||||
-rw-r--r-- | Zotlabs/Module/Sse.php | 3 | ||||
-rw-r--r-- | Zotlabs/Module/Sse_bs.php | 20 | ||||
-rw-r--r-- | Zotlabs/Web/WebServer.php | 2 |
19 files changed, 507 insertions, 436 deletions
diff --git a/Zotlabs/Daemon/Channel_purge.php b/Zotlabs/Daemon/Channel_purge.php index d25edf8ba..7286a791a 100644 --- a/Zotlabs/Daemon/Channel_purge.php +++ b/Zotlabs/Daemon/Channel_purge.php @@ -24,7 +24,7 @@ class Channel_purge { ); if ($r) { foreach ($r as $rv) { - drop_item($rv['id']); + drop_item($rv['id'], uid: $channel_id); } } } while ($r); diff --git a/Zotlabs/Daemon/Cron.php b/Zotlabs/Daemon/Cron.php index 186f3efcf..a38809f45 100644 --- a/Zotlabs/Daemon/Cron.php +++ b/Zotlabs/Daemon/Cron.php @@ -69,19 +69,18 @@ class Cron { // expire any expired items - $r = q("select id,item_wall from item where expires > '2001-01-01 00:00:00' and expires < %s + $r = q("select id, uid, item_wall from item where expires > '2001-01-01 00:00:00' and expires < %s and item_deleted = 0 ", db_utcnow() ); + if ($r) { - require_once('include/items.php'); foreach ($r as $rr) { + // pass uid of the message for permission check as we are running as a daemon process with no session. drop_item($rr['id'], (($rr['item_wall']) ? DROPITEM_PHASE1 : DROPITEM_NORMAL), uid: intval($rr['uid'])); - if ($rr['item_wall']) { // The notifier isn't normally invoked unless item_drop is interactive. Master::Summon(['Notifier', 'drop', $rr['id']]); - if ($interval) { usleep($interval); } @@ -148,37 +147,29 @@ class Cron { // (time travel posts). Restrict to items that have come of age in the last // couple of days to limit the query to something reasonable. - $r = q("select id from item where item_delayed = 1 and created <= %s and created > '%s' ", + $r = q("select * from item where item_delayed = 1 and created <= %s and created > '%s' ", db_utcnow(), dbesc(datetime_convert('UTC', 'UTC', 'now - 2 days')) ); - if ($r) { - foreach ($r as $rr) { - $x = q("update item set item_delayed = 0 where id = %d", - intval($rr['id']) - ); - if ($x) { - $z = q("select * from item where id = %d", - intval($rr['id']) - ); - if ($z) { - xchan_query($z); - $sync_item = fetch_post_tags($z); - Libsync::build_sync_packet($sync_item[0]['uid'], - [ - 'item' => [encode_item($sync_item[0], true)] - ] - ); - } - Master::Summon(array('Notifier', 'wall-new', $rr['id'])); - if ($interval) { - usleep($interval); + if ($r) { + xchan_query($r); + $items = fetch_post_tags($r); + foreach ($items as $item) { + $item['item_delayed'] = 0; + $post = item_store_update($item); + + if($post['success']) { + Master::Summon(['Notifier', 'wall-new', $post['item_id']]); + if (!empty($post['approval_id'])) { + Master::Summon(['Notifier', 'wall-new', $post['approval_id']]); } } - } - } - + if ($interval) { + usleep($interval); + } + } + } // once daily run birthday_updates and then expire in background // FIXME: add birthday updates, both locally and for xprof for use diff --git a/Zotlabs/Daemon/Expire.php b/Zotlabs/Daemon/Expire.php index 3ac3dee3a..dae585578 100644 --- a/Zotlabs/Daemon/Expire.php +++ b/Zotlabs/Daemon/Expire.php @@ -23,13 +23,13 @@ class Expire { // perform final cleanup on previously delete items - $r = q("select id from item where item_deleted = 1 and item_pending_remove = 0 and changed < %s - INTERVAL %s", + $r = q("select id, uid from item where item_deleted = 1 and item_pending_remove = 0 and changed < %s - INTERVAL %s", db_utcnow(), db_quoteinterval('10 DAY') ); if ($r) { foreach ($r as $rr) { - drop_item($rr['id'], DROPITEM_PHASE2); + drop_item($rr['id'], DROPITEM_PHASE2, uid: $rr['uid']); } } diff --git a/Zotlabs/Daemon/Importdoc.php b/Zotlabs/Daemon/Importdoc.php index 8f04e05f8..de571848e 100644 --- a/Zotlabs/Daemon/Importdoc.php +++ b/Zotlabs/Daemon/Importdoc.php @@ -11,6 +11,21 @@ class Importdoc { self::update_docs_dir('doc/*'); + $sys = get_sys_channel(); + + // remove old files that weren't updated (indicates they were most likely deleted). + $i = q("select id from item where uid = %d and item_type = 5 and edited < %s - INTERVAL %s", + intval($sys['channel_id']), + db_utcnow(), + db_quoteinterval('14 DAY') + ); + + if ($i) { + foreach ($i as $iv) { + drop_item($iv['id'], uid: $sys['channel_id']); + } + } + return; } @@ -41,18 +56,6 @@ class Importdoc { } } } - - // remove old files that weren't updated (indicates they were most likely deleted). - $i = q("select * from item where item_type = 5 and edited < %s - %s", - db_utcnow(), - db_quoteinterval('14 DAY', true) - ); - - if ($i) { - foreach ($i as $iv) { - drop_item($iv['id'], DROPITEM_NORMAL, true); - } - } } } diff --git a/Zotlabs/Daemon/Notifier.php b/Zotlabs/Daemon/Notifier.php index 1693f9c9f..043b406cc 100644 --- a/Zotlabs/Daemon/Notifier.php +++ b/Zotlabs/Daemon/Notifier.php @@ -241,11 +241,6 @@ class Notifier { $target_item = $r[0]; - if (in_array($target_item['author']['xchan_network'], ['rss', 'anon', 'token'])) { - logger('notifier: target item author is not a fetchable actor', LOGGER_DEBUG); - return; - } - if (intval($target_item['item_deleted'])) { logger('notifier: target item ITEM_DELETED', LOGGER_DEBUG); } @@ -268,22 +263,9 @@ class Notifier { } - // Check for non published items, but allow an exclusion for transmitting hidden file activities - - if (intval($target_item['item_unpublished']) || intval($target_item['item_delayed']) || - intval($target_item['item_blocked']) || intval($target_item['item_hidden'])) { - logger('notifier: target item not published, so not forwardable', LOGGER_DEBUG); - return; - } - - // follow/unfollow is for internal use only - if (in_array($target_item['verb'], ['Follow', 'Ignore', ACTIVITY_FOLLOW, ACTIVITY_UNFOLLOW])) { - logger('not fowarding follow/unfollow note activity'); - return; - } - - if (strpos($target_item['postopts'], 'nodeliver') !== false) { - logger('notifier: target item is undeliverable', LOGGER_DEBUG); + if (!item_forwardable($target_item)) { + //hz_syslog(print_r($target_item,true)); + logger('notifier: target item not forwardable', LOGGER_DEBUG); return; } @@ -388,7 +370,7 @@ class Notifier { logger('normal (downstream) distribution', LOGGER_DEBUG); } - if ($parent_item && $parent_item['item_private'] !== $target_item['item_private']) { + if (($parent_item && $parent_item['item_private'] !== $target_item['item_private']) || (intval($target_item['item_restrict']) & 1)) { logger('conversation privacy mismatch - downstream delivery prevented'); return; } diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index 54a1b8d2a..10df11174 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -2081,6 +2081,9 @@ class Activity { $i = fetch_post_tags($i); $i[0]['obj'] = $o; + $edited = datetime_convert(); + $i[0]['edited'] = $edited; + // create the new object $newObj = self::build_packet(self::encode_activity($i[0]), $channel, true); @@ -2098,7 +2101,7 @@ class Activity { dbesc(json_encode($o)), intval($relatedItem['id']), dbesc($newObj), - dbesc(datetime_convert()) + dbesc($edited) ); dbq("COMMIT"); @@ -2306,6 +2309,8 @@ class Activity { if ($s['mid'] === $s['parent_mid']) { $s['item_thread_top'] = 1; + $s['item_nocomment'] = 0; + $s['comments_closed'] = NULL_DATE; // it is a parent node - decode the comment policy info if present if ($act->objprop('commentPolicy')) { @@ -2313,7 +2318,7 @@ class Activity { if ($until !== false) { $s['comments_closed'] = datetime_convert('UTC', 'UTC', substr($act->obj['commentPolicy'], $until + 6)); if ($s['comments_closed'] < datetime_convert()) { - $s['nocomment'] = true; + $s['item_nocomment'] = 1; } } @@ -2680,6 +2685,7 @@ class Activity { } } + if (!$ap_rawmsg && array_key_exists('signed', $raw_arr)) { // zap $ap_rawmsg = json_encode($act->data, JSON_UNESCAPED_SLASHES); @@ -2713,8 +2719,7 @@ class Activity { return $hookinfo['s']; } - - static function store($channel, $observer_hash, $act, $item, $fetch_parents = true, $force = false) { + static function store($channel, $observer_hash, $act, $item, $fetch_parents = true, $force = false, $is_collection_operation = false) { $is_sys_channel = is_sys_channel($channel['channel_id']); $is_child_node = false; $parent = null; @@ -2745,6 +2750,8 @@ class Activity { } $allowed = false; + $relay = false; + $permit_mentions = intval(PConfig::Get($channel['channel_id'], 'system','permit_all_mentions') && i_am_mentioned($channel, $item)); if ($is_child_node) { @@ -2771,13 +2778,22 @@ class Activity { return; } + $relay = $channel['channel_hash'] === $parent[0]['owner_xchan']; + + if (str_contains($parent[0]['tgt_type'], 'Collection') && !$relay && !$isCollectionOperation) { + logger('not a collection activity'); + return; + } + if ($parent[0]['obj_type'] === 'Question') { if (in_array($item['obj_type'], ['Note', ACTIVITY_OBJ_COMMENT]) && $item['title'] && (!$item['body'])) { $item['obj_type'] = 'Answer'; + $item['item_hidden'] = 1; } } if ($parent[0]['item_wall']) { + // set the owner to the owner of the parent $item['owner_xchan'] = $parent[0]['owner_xchan']; @@ -3032,17 +3048,48 @@ class Activity { dbesc($item['mid']), intval($item['uid']) ); + if ($r) { if ($item['edited'] > $r[0]['edited']) { $item['id'] = $r[0]['id']; - $x = item_store_update($item); + $x = item_store_update($item, deliver: false); } else { return; } } else { - $x = item_store($item); + $x = item_store($item, deliver: false, addAndSync: false); + } + + if ($x['success']) { + + if ($relay && $channel['channel_hash'] === $x['item']['owner_xchan'] && $x['item']['verb'] !== 'Add' && !$isCollectionOperation) { + $approval = Activity::addToCollection($channel, $act->data, $x['item']['parent_mid'], $x['item'], deliver: false); + } + + if (check_item_source($channel['channel_id'], $x['item']) && in_array($x['item']['obj_type'], ['Event', ACTIVITY_OBJ_EVENT])) { + event_addtocal($x['item_id'], $channel['channel_id']); + } + + tag_deliver($channel['channel_id'], $x['item_id']); + + if ($relay && $is_child_node) { + // We are the owner of this conversation, so send all received comments back downstream + Master::Summon(['Notifier', 'comment-import', $x['item_id']]); + if (!empty($approval['item_id'])) { + Master::Summon(['Notifier', 'comment-import', $approval['item_id']]); + } + } + + $r = q("select * from item where id = %d limit 1", + intval($x['item_id']) + ); + + if ($r) { + send_status_notifications($x['item_id'], $r[0]); + } + sync_an_item($channel['channel_id'], $x['item_id']); } if ($fetch_parents && $parent && !intval($parent[0]['item_private'])) { @@ -3069,28 +3116,6 @@ class Activity { } } } - - if ($x['success']) { - - if (check_item_source($channel['channel_id'], $x['item']) && in_array($x['item']['obj_type'], ['Event', ACTIVITY_OBJ_EVENT])) { - event_addtocal($x['item_id'], $channel['channel_id']); - } - - if ($is_child_node) { - if ($item['owner_xchan'] === $channel['channel_hash']) { - // We are the owner of this conversation, so send all received comments back downstream - Master::Summon(['Notifier', 'comment-import', $x['item_id']]); - } - $r = q("select * from item where id = %d limit 1", - intval($x['item_id']) - ); - if ($r) { - send_status_notifications($x['item_id'], $r[0]); - } - } - sync_an_item($channel['channel_id'], $x['item_id']); - } - } /** @@ -3785,8 +3810,6 @@ class Activity { ->setObjType($object['type']) ->setParentMid(str_replace('/conversation/','/item/', $target)) ->setThrParent(str_replace('/conversation/','/item/', $target)) - // ->setApproved($object['object']['id'] ?? '') - // ->setReplyto(z_root() . '/channel/' . $channel['channel_address']) ->setTgtType('Collection') ->setTarget([ 'id' => str_replace('/item/','/conversation/', $target), @@ -3801,13 +3824,19 @@ class Activity { ->setDenyCid($sourceItem['deny_cid']) ->setDenyGid($sourceItem['deny_gid']) ->setPrivate($sourceItem['item_private']) - ->setNocomment($sourceItem['item_nocomment']) + ->setRestrict($sourceItem['item_restrict']) + ->setHidden($sourceItem['item_hidden']) + ->setDelayed($sourceItem['item_delayed']) + ->setUnpublished($sourceItem['item_unpublished']) + ->setBlocked($sourceItem['item_blocked']) + ->setType($sourceItem['item_type']) ->setCommentPolicy($sourceItem['comment_policy']) ->setPublicPolicy($sourceItem['public_policy']) ->setPostopts($sourceItem['postopts']); } $result = post_activity_item($item->toArray(), deliver: $deliver, channel: $channel, observer: $channel, addAndSync: false); logger('addToCollection: ' . print_r($result, true)); + return $result; } diff --git a/Zotlabs/Lib/Libzot.php b/Zotlabs/Lib/Libzot.php index 3d499bd08..60fb5e034 100644 --- a/Zotlabs/Lib/Libzot.php +++ b/Zotlabs/Lib/Libzot.php @@ -655,6 +655,11 @@ class Libzot { return $ret; } + if (empty($arr['primary_location']['address'])) { + logger('Empty primary location address: ' . print_r($arr, true), LOGGER_DEBUG); + return $ret; + } + /** * @hooks import_xchan * Called when processing the result of zot_finger() to store the result @@ -1148,7 +1153,7 @@ class Libzot { $AS = new ActivityStreams($data); // process add/remove from collection separately, as it requires a target. - // use the raw object, as it will not include actor expansion + // use the data object, as it will not include actor expansion if (in_array($AS->type, ['Add', 'Remove']) && is_array($AS->obj) && array_key_exists('object', $AS->obj) @@ -1227,7 +1232,6 @@ class Libzot { logger('public post'); - // Public post. look for any site members who are or may be accepting posts from this sender // and who are allowed to see them based on the sender's permissions // @fixme; @@ -1294,25 +1298,6 @@ class Libzot { $item['item_private'] = 1; } - if ($item['mid'] === $item['parent_mid']) { - if (is_array($AS->obj) && array_key_exists('commentPolicy', $AS->obj)) { - $p = strstr($AS->obj['commentPolicy'], 'until='); - if ($p !== false) { - $comments_closed_at = datetime_convert('UTC', 'UTC', substr($p, 6)); - if ($comments_closed_at === $item['created']) { - $item['item_nocomment'] = 1; - } - else { - $item['comments_closed'] = $comments_closed_at; - $aritemr['comment_policy'] = trim(str_replace($p, '', $AS->obj['commentPolicy'])); - } - } - else { - $item['comment_policy'] = $AS->obj['commentPolicy']; - } - } - } - if (!empty($AS->meta['hubloc']) || $AS->sigok) { $item['item_verified'] = true; } @@ -1576,7 +1561,7 @@ class Libzot { $conversation_operation = $is_collection_operation && isset($arr['target']['attributedTo']); - if (str_contains($arr['tgt_type'], 'Collection') && !$relay && !$conversation_operation) { + if (isset($arr['tgt_type']) && str_contains($arr['tgt_type'], 'Collection') && !$relay && !$conversation_operation) { $DR->update('not a collection activity'); $result[] = $DR->get(); continue; @@ -1663,22 +1648,24 @@ class Libzot { if (intval($channel['channel_system']) && (!$arr['item_private']) && (!$relay)) { $local_public = true; - $r = q("select xchan_selfcensored from xchan where xchan_hash = '%s' limit 1", - dbesc($sender) - ); - // don't import sys channel posts from selfcensored authors - if ($r && (intval($r[0]['xchan_selfcensored']))) { + $incl = Config::Get('system','pubstream_incl'); + $excl = Config::Get('system','pubstream_excl'); + + if(($incl || $excl) && !MessageFilter::evaluate($arr, $incl, $excl)) { $local_public = false; continue; } - $incl = Config::Get('system','pubstream_incl'); - $excl = Config::Get('system','pubstream_excl'); + $r = q("select xchan_selfcensored, xchan_censored from xchan where xchan_hash = '%s'", + dbesc($sender) + ); - if(($incl || $excl) && !MessageFilter::evaluate($arr, $incl, $excl)) { + // don't import sys channel posts from selfcensored or censored authors + if ($r && ($r[0]['xchan_selfcensored'] || $r[0]['xchan_censored'])) { $local_public = false; continue; } + } $tag_delivery = tgroup_check($channel['channel_id'], $arr); @@ -1740,6 +1727,7 @@ class Libzot { // If this is a poll response, convert the obj_type to our (internal-only) "Answer" type if (in_array($arr['obj_type'], ['Note', ACTIVITY_OBJ_COMMENT]) && $arr['title'] && (!$arr['body'])) { $arr['obj_type'] = 'Answer'; + $arr['item_hidden'] = 1; } } @@ -2008,10 +1996,10 @@ class Libzot { if ((is_array($stored)) && ($stored['id'] != $stored['parent']) && ($stored['author_xchan'] === $channel['channel_hash'])) { - retain_item($stored['item']['parent']); + retain_item($stored['parent']); } - if ($relay && $item_id && $stored['item_blocked'] !== ITEM_MODERATED) { + if ($relay && $item_id && item_forwardable($stored)) { logger('Invoking relay'); Master::Summon(['Notifier', 'relay', intval($item_id)]); if (!empty($approval) && $approval['item_id']) { diff --git a/Zotlabs/Lib/Mailer.php b/Zotlabs/Lib/Mailer.php new file mode 100644 index 000000000..ca2d84d0d --- /dev/null +++ b/Zotlabs/Lib/Mailer.php @@ -0,0 +1,86 @@ +<?php +/** + * Mailer class for sending emails from Hubzilla. + * + * SPDX-FileCopyrightText: 2024 Hubzilla Community + * SPDX-FileContributor: Harald Eilertsen + * + * SPDX-License-Identifier: MIT + */ + +namespace Zotlabs\Lib; + +use App; + +/** + * A class for sending emails. + * + * Based on the previous `z_mail` function, but adaped and made more + * robust and usable as a class. + */ +class Mailer { + + public function __construct(private array $params = []) { + } + + public function deliver(): bool { + + if(empty($this->params['fromEmail'])) { + $this->params['fromEmail'] = Config::Get('system','from_email'); + if(empty($this->params['fromEmail'])) { + $this->params['fromEmail'] = 'Administrator@' . App::get_hostname(); + } + } + + if(empty($this->params['fromName'])) { + $this->params['fromName'] = Config::Get('system','from_email_name'); + if(empty($this->params['fromName'])) { + $this->params['fromName'] = System::get_site_name(); + } + } + + if(empty($this->params['replyTo'])) { + $this->params['replyTo'] = Config::Get('system','reply_address'); + if(empty($this->params['replyTo'])) { + $this->params['replyTo'] = 'noreply@' . App::get_hostname(); + } + } + + if (!isset($this->params['additionalMailHeader'])) { + $this->params['additionalMailHeader'] = ''; + } + + $this->params['sent'] = false; + $this->params['result'] = false; + + /** + * @hooks email_send + * * \e params @see z_mail() + */ + call_hooks('email_send', $this->params); + + if($this->params['sent']) { + logger('notification: z_mail returns ' . (($this->params['result']) ? 'success' : 'failure'), LOGGER_DEBUG); + return $this->params['result']; + } + + $fromName = email_header_encode(html_entity_decode($this->params['fromName'],ENT_QUOTES,'UTF-8'),'UTF-8'); + $messageSubject = email_header_encode(html_entity_decode($this->params['messageSubject'],ENT_QUOTES,'UTF-8'),'UTF-8'); + + $messageHeader = + $this->params['additionalMailHeader'] . + "From: $fromName <{$this->params['fromEmail']}>" . PHP_EOL . + "Reply-To: $fromName <{$this->params['replyTo']}>" . PHP_EOL . + "Content-Type: text/plain; charset=UTF-8"; + + // send the message + $res = mail( + $this->params['toEmail'], // send to address + $messageSubject, // subject + $this->params['textVersion'], + $messageHeader // message headers + ); + logger('notification: z_mail returns ' . (($res) ? 'success' : 'failure'), LOGGER_DEBUG); + return $res; + } +} diff --git a/Zotlabs/Lib/ThreadItem.php b/Zotlabs/Lib/ThreadItem.php index 90a3d3fc8..4b833f4b4 100644 --- a/Zotlabs/Lib/ThreadItem.php +++ b/Zotlabs/Lib/ThreadItem.php @@ -233,45 +233,6 @@ class ThreadItem { $my_responses[$v] = ((isset($conv_responses[$v][$item['mid'] . '-m'])) ? 1 : 0); } -/* - - $like_count = ((x($conv_responses['like'],$item['mid'])) ? $conv_responses['like'][$item['mid']] : ''); - $like_list = ((x($conv_responses['like'],$item['mid'])) ? $conv_responses['like'][$item['mid'] . '-l'] : ''); - if (($like_list) && (count($like_list) > MAX_LIKERS)) { - $like_list_part = array_slice($like_list, 0, MAX_LIKERS); - array_push($like_list_part, '<a class="dropdown-item" href="#" data-toggle="modal" data-target="#likeModal-' . $this->get_id() . '"><b>' . t('View all') . '</b></a>'); - } else { - $like_list_part = ''; - } - $like_button_label = tt('Like','Likes',$like_count,'noun'); - - $repeat_count = ((x($conv_responses['announce'],$item['mid'])) ? $conv_responses['announce'][$item['mid']] : ''); - $repeat_list = ((x($conv_responses['announce'],$item['mid'])) ? $conv_responses['announce'][$item['mid'] . '-l'] : ''); - if (($repeat_list) && (count($repeat_list) > MAX_LIKERS)) { - $repeat_list_part = array_slice($repeat_list, 0, MAX_LIKERS); - array_push($repeat_list_part, '<a class="dropdown-item" href="#" data-toggle="modal" data-target="#repeatModal-' . $this->get_id() . '"><b>' . t('View all') . '</b></a>'); - } else { - $repeat_list_part = ''; - } - $repeat_button_label = tt('Repeat','Repeats',$repeat_count,'noun'); - - $showdislike = ''; - if (feature_enabled($conv->get_profile_owner(),'dislike')) { - $dislike_count = ((x($conv_responses['dislike'],$item['mid'])) ? $conv_responses['dislike'][$item['mid']] : ''); - $dislike_list = ((x($conv_responses['dislike'],$item['mid'])) ? $conv_responses['dislike'][$item['mid'] . '-l'] : ''); - $dislike_button_label = tt('Dislike','Dislikes',$dislike_count,'noun'); - if (($dislike_list) && (count($dislike_list) > MAX_LIKERS)) { - $dislike_list_part = array_slice($dislike_list, 0, MAX_LIKERS); - array_push($dislike_list_part, '<a class="dropdown-item" href="#" data-toggle="modal" data-target="#dislikeModal-' . $this->get_id() . '"><b>' . t('View all') . '</b></a>'); - } else { - $dislike_list_part = ''; - } - - $showdislike = ((x($conv_responses['dislike'],$item['mid'])) ? format_like($conv_responses['dislike'][$item['mid']],$conv_responses['dislike'][$item['mid'] . '-l'],'dislike',$item['mid']) : ''); - } - - $showlike = ((x($conv_responses['like'],$item['mid'])) ? format_like($conv_responses['like'][$item['mid']],$conv_responses['like'][$item['mid'] . '-l'],'like',$item['mid']) : ''); -*/ /* * We should avoid doing this all the time, but it depends on the conversation mode @@ -415,7 +376,6 @@ class ThreadItem { 'template' => $this->get_template(), 'mode' => $mode, 'item_type' => intval($item['item_type']), - //'type' => implode("",array_slice(explode("/",$item['verb']),-1)), 'body' => $body['html'], 'tags' => $body['tags'], 'categories' => $body['categories'], @@ -450,16 +410,15 @@ class ThreadItem { 'sparkle' => $sparkle, 'title' => $item['title'], 'title_tosource' => get_pconfig($conv->get_profile_owner(),'system','title_tosource'), - //'ago' => relative_date($item['created']), 'app' => $item['app'], 'str_app' => sprintf( t('from %s'), $item['app']), 'isotime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'c'), - 'localtime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'r'), - 'editedtime' => (($item['edited'] != $item['created']) ? sprintf( t('last edited: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['edited'], 'r')) : ''), - 'expiretime' => (($item['expires'] > NULL_DATE) ? sprintf( t('Expires: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['expires'], 'r')):''), + 'localtime' => datetime_convert('UTC', date_default_timezone_get(), $item['created']), + 'editedtime' => (($item['edited'] != $item['created']) ? sprintf(t('Last edited %s'), relative_time($item['edited'])) : ''), + 'expiretime' => (($item['expires'] > NULL_DATE) ? sprintf(t('Expires %s'), relative_time($item['expires'])) : ''), 'lock' => $lock, 'locktype' => $locktype, - 'delayed' => $item['item_delayed'], + 'delayed' => (($item['item_delayed']) ? sprintf(t('Published %s'), relative_time($item['created'])) : ''), 'privacy_warning' => $privacy_warning, 'verified' => $verified, 'unverified' => $unverified, @@ -479,7 +438,7 @@ class ThreadItem { 'event' => $body['event'], 'has_tags' => $has_tags, 'reactions' => $this->reactions, -// Item toolbar buttons + // Item toolbar buttons 'emojis' => (($this->is_toplevel() && $this->is_commentable() && $observer && feature_enabled($conv->get_profile_owner(),'emojis')) ? '1' : ''), 'like' => $like, 'dislike' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike : ''), @@ -489,7 +448,7 @@ class ThreadItem { 'embed' => $embed, 'rawmid' => $item['mid'], 'plink' => get_plink($item), - 'edpost' => $edpost, // ((feature_enabled($conv->get_profile_owner(),'edit_posts')) ? $edpost : ''), + 'edpost' => $edpost, 'star' => ((feature_enabled($conv->get_profile_owner(),'star_posts') && ($item['item_type'] == ITEM_TYPE_POST)) ? $star : ''), 'tagger' => ((feature_enabled($conv->get_profile_owner(),'commtag')) ? $tagger : ''), 'filer' => ((feature_enabled($conv->get_profile_owner(),'filing') && ($item['item_type'] == ITEM_TYPE_POST)) ? $filer : ''), @@ -500,7 +459,7 @@ class ThreadItem { 'addtocal' => (($has_event) ? t('Add to Calendar') : ''), 'drop' => $drop, 'dropdown_extras' => $dropdown_extras, -// end toolbar buttons + // end toolbar buttons 'unseen_comments' => $unseen_comments, 'comment_count' => $total_children, 'comment_count_txt' => $comment_count_txt, @@ -508,30 +467,9 @@ class ThreadItem { 'markseen' => t('Mark all comments seen'), 'responses' => $responses, 'my_responses' => $my_responses, - /* - 'like_count' => $like_count, - 'like_list' => $like_list, - 'like_list_part' => $like_list_part, - 'like_button_label' => $like_button_label, - 'like_modal_title' => t('Likes','noun'), - - 'repeat_count' => $repeat_count, - 'repeat_list' => $repeat_list, - 'repeat_list_part' => $repeat_list_part, - 'repeat_button_label' => $repeat_button_label, - 'repeat_modal_title' => t('Repeats','noun'), - - - 'dislike_modal_title' => t('Dislikes','noun'), - 'dislike_count' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike_count : ''), - 'dislike_list' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike_list : ''), - 'dislike_list_part' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike_list_part : ''), - 'dislike_button_label' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike_button_label : ''), -*/ 'modal_dismiss' => t('Close'), - // 'showlike' => $showlike, - // 'showdislike' => $showdislike, 'comment' => ($item['item_delayed'] ? '' : $this->get_comment_box()), + 'no_comment' => (($item['item_thread_top'] && $item['item_nocomment'])? t('Comments disabled') : ''), 'previewing' => ($conv->is_preview() ? true : false ), 'preview_lbl' => t('This is an unsaved preview'), 'wait' => t('Please wait'), @@ -556,15 +494,7 @@ class ThreadItem { $result['children'] = array(); $nb_children = count($children); - $visible_comments = Config::Get('system','expanded_comments'); - if($visible_comments === false) - $visible_comments = 3; - -// needed for scroll to comment from notification but needs more work -// as we do not want to open all comments unless there is actually an #item_xx anchor -// and the url fragment is not sent to the server. -// if(in_array(\App::$module,['display','update_display'])) -// $visible_comments = 99999; + $visible_comments = Config::Get('system', 'expanded_comments', 3); if(($this->get_display_mode() === 'normal') && ($nb_children > 0)) { foreach($children as $child) { @@ -876,7 +806,7 @@ class ThreadItem { '$edatt' => t('Attach/Upload file'), '$edurl' => t('Insert Link'), '$edvideo' => t('Video'), - '$preview' => t('Preview'), // ((feature_enabled($conv->get_profile_owner(),'preview')) ? t('Preview') : ''), + '$preview' => t('Preview'), '$can_upload' => (perm_is_allowed($conv->get_profile_owner(),get_observer_hash(),'write_storage') && $conv->is_uploadable()), '$feature_encrypt' => ((feature_enabled($conv->get_profile_owner(),'content_encrypt')) ? true : false), '$encrypt' => t('Encrypt text'), @@ -897,7 +827,7 @@ class ThreadItem { } /** - * Check if we are a wall to wall item and set the relevant properties + * Check if we are a wall to wall or announce item and set the relevant properties */ protected function check_wall_to_wall() { $conv = $this->get_conversation(); diff --git a/Zotlabs/Module/Admin/Accounts.php b/Zotlabs/Module/Admin/Accounts.php index 6f7cb0311..108231d7d 100644 --- a/Zotlabs/Module/Admin/Accounts.php +++ b/Zotlabs/Module/Admin/Accounts.php @@ -6,133 +6,33 @@ use Zotlabs\Lib\Config; class Accounts { - /** - * @brief Handle POST actions on accounts admin page. - * - * This function is called when on the admin user/account page the form was - * submitted to handle multiple operations at once. If one of the icons next - * to an entry are pressed the function admin_page_accounts() will handle this. - * - */ const MYP = 'ZAR'; // ZAR2x const VERSION = '2.0.0'; - function post() { + /** + * Handle POST actions on accounts admin page. + */ + public function post() { - $pending = ( x($_POST, 'pending') ? $_POST['pending'] : array() ); - $users = ( x($_POST, 'user') ? $_POST['user'] : array() ); - $blocked = ( x($_POST, 'blocked') ? $_POST['blocked'] : array() ); + $pending = x($_POST, 'pending') ? $_POST['pending'] : array(); check_form_security_token_redirectOnErr('/admin/accounts', 'admin_accounts'); - $isajax = is_ajax(); - $rc = 0; - - If (!is_site_admin()) { - if ($isajax) { - killme(); - exit; - } - goaway(z_root() . '/'); - } - - if ($isajax) { - //$debug = print_r($_SESSION[self::MYP],true); - $zarop = (x($_POST['zardo']) && preg_match('/^[ad]{1,1}$/', $_POST['zardo']) ) - ? $_POST['zardo'] : ''; - // zarat arrives with leading underscore _n - $zarat = (x($_POST['zarat']) && preg_match('/^_{1,1}[0-9]{1,6}$/', $_POST['zarat']) ) - ? substr($_POST['zarat'],1) : ''; - $zarse = (x($_POST['zarse']) && preg_match('/^[0-9a-f]{8,8}$/', $_POST['zarse']) ) - ? hex2bin($_POST['zarse']) : ''; - - if ($zarop && $zarat >= 0 && $zarse && $zarse == $_SESSION[self::MYP]['h'][$zarat]) { - - // - if ($zarop == 'd') { - $rd = q("UPDATE register SET reg_vital = 0 WHERE reg_id = %d AND SUBSTR(reg_hash,1,4) = '%s' ", - intval($_SESSION[self::MYP]['i'][$zarat]), - dbesc($_SESSION[self::MYP]['h'][$zarat]) - ); - $rc = '×'; - } - elseif ($zarop == 'a') { - // approval, REGISTER_DENIED by user 0x0040, REGISTER_AGREED by user 0x0020 @Regate - $rd = q("UPDATE register SET reg_flags = (reg_flags & ~ 16), " - . " reg_vital = (CASE (reg_flags & ~ 48) WHEN 0 THEN 0 ELSE 1 END) " - . " WHERE reg_vital = 1 AND reg_id = %d AND SUBSTR(reg_hash,1,4) = '%s' ", - intval($_SESSION[self::MYP]['i'][$zarat]), - dbesc($_SESSION[self::MYP]['h'][$zarat]) - ); - $rc = 0; - $rs = q("SELECT * from register WHERE reg_id = %d ", - intval($_SESSION[self::MYP]['i'][$zarat]) - ); - if ($rs && ($rs[0]['reg_flags'] & ~ 48) == 0) { - // create account - $rc = 'ok'.$rs[0]['reg_id']; - $ac = create_account_from_register($rs[0]); - if ( $ac['success'] ) { - $rc .= '✔'; - - $auto_create = Config::Get('system','auto_channel_create',1); - - if($auto_create) { - $reonar = json_decode($rs[0]['reg_stuff'], true); - // prepare channel creation - if($reonar['chan.name']) - set_aconfig($ac['account']['account_id'], 'register', 'channel_name', $reonar['chan.name']); - - if($reonar['chan.did1']) - set_aconfig($ac['account']['account_id'], 'register', 'channel_address', $reonar['chan.did1']); - - $permissions_role = Config::Get('system','default_permissions_role'); - if($permissions_role) - set_aconfig($ac['account']['account_id'], 'register', 'permissions_role', $permissions_role); - - // create channel - $new_channel = auto_channel_create($ac['account']['account_id']); - - if($new_channel['success']) { - $rc .= ' c,ok' . $new_channel['channel']['channel_id'] . '✔'; - } - else { - $rc .= ' c ×'; - } - } - - - } - } else { - $rc='oh ×'; - } - } - echo json_encode(array('re' => $zarop, 'at' => '_' . $zarat, 'rc' => $rc)); - } + if (is_ajax()) { + $this->handle_ajax_request(); killme(); - exit; } // change to switch structure? // account block/unblock button was submitted if (x($_POST, 'page_accounts_block')) { - for ($i = 0; $i < count($users); $i++) { - // if account is blocked remove blocked bit-flag, otherwise add blocked bit-flag - $op = ($blocked[$i]) ? '& ~' : '| '; - q("UPDATE account SET account_flags = (account_flags $op%d) WHERE account_id = %d", - intval(ACCOUNT_BLOCKED), - intval($users[$i]) - ); - } - notice( sprintf( tt("%s account blocked/unblocked", "%s account blocked/unblocked", count($users)), count($users)) ); + $this->block_unblock_accounts(); } + // account delete button was submitted if (x($_POST, 'page_accounts_delete')) { - foreach ($users as $uid){ - account_remove($uid, true, false); - } - notice( sprintf( tt("%s account deleted", "%s accounts deleted", count($users)), count($users)) ); + $this->delete_accounts(); } // registration approved button was submitted if (x($_POST, 'page_accounts_approve')) { @@ -351,5 +251,143 @@ class Accounts { return $o; } + private function handle_ajax_request(): void { + //$debug = print_r($_SESSION[self::MYP],true); + $zarop = (x($_POST['zardo']) && preg_match('/^[ad]{1,1}$/', $_POST['zardo']) ) + ? $_POST['zardo'] : ''; + // zarat arrives with leading underscore _n + $zarat = (x($_POST['zarat']) && preg_match('/^_{1,1}[0-9]{1,6}$/', $_POST['zarat']) ) + ? substr($_POST['zarat'],1) : ''; + $zarse = (x($_POST['zarse']) && preg_match('/^[0-9a-f]{8,8}$/', $_POST['zarse']) ) + ? hex2bin($_POST['zarse']) : ''; + + if ($zarop && $zarat >= 0 && $zarse && $zarse == $_SESSION[self::MYP]['h'][$zarat]) { + + // + if ($zarop == 'd') { + $rd = q("UPDATE register SET reg_vital = 0 WHERE reg_id = %d AND SUBSTR(reg_hash,1,4) = '%s' ", + intval($_SESSION[self::MYP]['i'][$zarat]), + dbesc($_SESSION[self::MYP]['h'][$zarat]) + ); + $rc = '×'; + } + elseif ($zarop == 'a') { + // approval, REGISTER_DENIED by user 0x0040, REGISTER_AGREED by user 0x0020 @Regate + $rd = q("UPDATE register SET reg_flags = (reg_flags & ~ 16), " + . " reg_vital = (CASE (reg_flags & ~ 48) WHEN 0 THEN 0 ELSE 1 END) " + . " WHERE reg_vital = 1 AND reg_id = %d AND SUBSTR(reg_hash,1,4) = '%s' ", + intval($_SESSION[self::MYP]['i'][$zarat]), + dbesc($_SESSION[self::MYP]['h'][$zarat]) + ); + $rc = 0; + $rs = q("SELECT * from register WHERE reg_id = %d ", + intval($_SESSION[self::MYP]['i'][$zarat]) + ); + if ($rs && ($rs[0]['reg_flags'] & ~ 48) == 0) { + // create account + $rc = 'ok'.$rs[0]['reg_id']; + $ac = create_account_from_register($rs[0]); + if ( $ac['success'] ) { + $rc .= '✔'; + + $auto_create = Config::Get('system','auto_channel_create',1); + + if($auto_create) { + $reonar = json_decode($rs[0]['reg_stuff'], true); + // prepare channel creation + if($reonar['chan.name']) + set_aconfig($ac['account']['account_id'], 'register', 'channel_name', $reonar['chan.name']); + + if($reonar['chan.did1']) + set_aconfig($ac['account']['account_id'], 'register', 'channel_address', $reonar['chan.did1']); + + $permissions_role = Config::Get('system','default_permissions_role'); + if($permissions_role) + set_aconfig($ac['account']['account_id'], 'register', 'permissions_role', $permissions_role); + + // create channel + $new_channel = auto_channel_create($ac['account']['account_id']); + + if($new_channel['success']) { + $rc .= ' c,ok' . $new_channel['channel']['channel_id'] . '✔'; + } + else { + $rc .= ' c ×'; + } + } + + + } + } else { + $rc='oh ×'; + } + } + echo json_encode(array('re' => $zarop, 'at' => '_' . $zarat, 'rc' => $rc)); + } + } + + /** + * Block or unblock accounts given by the `user` and `blocked` POST params. + * + * The post params `user` and `blocked` must be present and arrays of equal + * lengths. The `user` array should contain account id's or the accounts to + * process, and the `blocked` array holds a corresponding boolean value to + * indicate that the account at the same offset in the `user` array is or is + * not blocked. + * + * An account that is _not_ blocked will be blocked, and accounts that _are_ + * blocked will be unblocked. + * + * @SuppressWarnings(PHPMD.ShortVariable) + */ + private function block_unblock_accounts(): void { + if (!isset($_POST['user']) || !isset($_POST['blocked'])) { + return; + } + + $users = $_POST['user']; + $blocked = $_POST['blocked']; + + if (!is_array($users) || !is_array($blocked)) { + return; + } + + foreach($users as $i => $id) { + // if account is blocked remove blocked bit-flag, otherwise add blocked bit-flag + $op = $blocked[$i] ? '& ~' : '| '; + + q("UPDATE account SET account_flags = (account_flags $op%d) WHERE account_id = %d", + intval(ACCOUNT_BLOCKED), + intval($id) + ); + } + + $count = count($users); + $fmt = tt("%s account blocked/unblocked", "%s account blocked/unblocked", $count); + notice(sprintf($fmt, $count)); + } + + /** + * Delete multiple accounts given by the `user` POST param. + */ + private function delete_accounts(): void { + if (!isset($_POST['user'])) { + return; + } + + $users = $_POST['user']; + + if (!is_array($users)) { + return; + } + + foreach ($users as $uid){ + account_remove($uid, true, false); + } + + $count = count($users); + $fmt = tt("%s account deleted", "%s accounts deleted", $count); + notice(sprintf($fmt, $count)); + } } diff --git a/Zotlabs/Module/Conversation.php b/Zotlabs/Module/Conversation.php index c3e6ae5ec..6ceba69f2 100644 --- a/Zotlabs/Module/Conversation.php +++ b/Zotlabs/Module/Conversation.php @@ -36,8 +36,7 @@ class Conversation extends Controller { // 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), + $r = q("select parent_mid from item where uuid = '%s' $item_normal order by item_wall desc limit 1", dbesc($item_id) ); diff --git a/Zotlabs/Module/Dreport.php b/Zotlabs/Module/Dreport.php index 5db607545..d1ffb8027 100644 --- a/Zotlabs/Module/Dreport.php +++ b/Zotlabs/Module/Dreport.php @@ -19,7 +19,7 @@ class Dreport extends \Zotlabs\Web\Controller { $table = 'push'; if($mid) { - $i = q("select id from item where mid = '%s' and uid = %d and ( author_xchan = '%s' or ( owner_xchan = '%s' and item_wall = 1 )) ", + $i = q("select * from item where mid = '%s' and uid = %d and ( author_xchan = '%s' or ( owner_xchan = '%s' and item_wall = 1 )) ", dbesc($mid), intval($channel['channel_id']), dbesc($channel['channel_hash']), @@ -27,6 +27,12 @@ class Dreport extends \Zotlabs\Web\Controller { ); if($i) { \Zotlabs\Daemon\Master::Summon([ 'Notifier', 'edit_post', $i[0]['id'] ]); + + $relatedItem = find_related($i[0]); + if (isset($relatedItem['id'])) { + \Zotlabs\Daemon\Master::Summon([ 'Notifier', 'edit_post', $relatedItem['id'] ]); + } + } } sleep(3); diff --git a/Zotlabs/Module/Editpost.php b/Zotlabs/Module/Editpost.php index 6f524cdb8..678ceb207 100644 --- a/Zotlabs/Module/Editpost.php +++ b/Zotlabs/Module/Editpost.php @@ -86,6 +86,7 @@ class Editpost extends \Zotlabs\Web\Controller { 'bbco_autocomplete'=> 'bbcode', 'return_path' => 'hq', 'button' => t('Submit'), + 'disable_comments' => (($itm[0]['item_thread_top']) ? false : true), 'hide_voting' => true, 'hide_future' => true, 'hide_location' => true, diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php index 8ded7c1d7..f542e44ff 100644 --- a/Zotlabs/Module/Item.php +++ b/Zotlabs/Module/Item.php @@ -53,22 +53,21 @@ class Item extends Controller { if (argc() > 1 && argv(1) !== 'drop') { - $x = q("select uid, item_wall, llink, mid, uuid from item where mid = '%s' or mid = '%s' or uuid = '%s'", - dbesc(z_root() . '/item/' . argv(1)), - dbesc(z_root() . '/activity/' . argv(1)), + $x = q("select uid, item_wall, llink, uuid from item where uuid = '%s' order by item_wall desc", dbesc(argv(1)) ); + if ($x) { - foreach ($x as $xv) { - if (intval($xv['item_wall'])) { - $c = channelx_by_n($xv['uid']); - if ($c) { - goaway(z_root() . '/channel/' . $c['channel_address'] . '?mid=' . $xv['uuid']); - } + if ($x[0]['item_wall']) { + $c = channelx_by_n($x[0]['uid']); + if ($c) { + goaway(z_root() . '/channel/' . $c['channel_address'] . '?mid=' . $x[0]['uuid']); } } + goaway($x[0]['llink']); } + http_status_exit(404, 'Not found'); } @@ -101,7 +100,6 @@ class Item extends Controller { $item_deleted = false; $item_hidden = false; $item_unpublished = false; - $item_delayed = false; $item_pending_remove = false; $item_blocked = false; @@ -179,6 +177,7 @@ class Item extends Controller { $categories = ((x($_REQUEST, 'category')) ? escape_tags($_REQUEST['category']) : ''); $webpage = ((x($_REQUEST, 'webpage')) ? intval($_REQUEST['webpage']) : 0); $item_obscured = ((x($_REQUEST, 'obscured')) ? intval($_REQUEST['obscured']) : 0); + $item_delayed = ((x($_REQUEST, 'delayed')) ? intval($_REQUEST['delayed']) : 0); $pagetitle = ((x($_REQUEST, 'pagetitle')) ? escape_tags($_REQUEST['pagetitle']) : ''); $layout_mid = ((x($_REQUEST, 'layout_mid')) ? escape_tags($_REQUEST['layout_mid']) : ''); $plink = ((x($_REQUEST, 'permalink')) ? escape_tags($_REQUEST['permalink']) : ''); @@ -208,7 +207,6 @@ class Item extends Controller { $expires = NULL_DATE; - $comments_closed = NULL_DATE; $route = ''; $parent_item = null; @@ -455,7 +453,7 @@ class Item extends Controller { $title = escape_tags(trim($_REQUEST['title'])); $summary = escape_tags(trim($_REQUEST['summary'])); $body = trim($_REQUEST['body']); - $item_flags = $orig_post['item_flags']; + $item_flags = $orig_post['item_flags']; $item_origin = $orig_post['item_origin']; $item_unseen = $orig_post['item_unseen']; $item_starred = $orig_post['item_starred']; @@ -469,7 +467,7 @@ class Item extends Controller { $item_mentionsme = $orig_post['item_mentionsme']; $item_nocomment = $orig_post['item_nocomment']; $item_obscured = $orig_post['item_obscured']; - $item_verified = $orig_post['item_verified']; + $item_verified = $orig_post['item_verified']; $item_retained = $orig_post['item_retained']; $item_rss = $orig_post['item_rss']; $item_deleted = $orig_post['item_deleted']; @@ -794,6 +792,7 @@ class Item extends Controller { $item_origin = (($origin) ? 1 : 0); $item_consensus = (($consensus) ? 1 : 0); $item_nocomment = (($nocomment) ? 1 : 0); + $comments_closed = (($nocomment) ? $comments_closed : NULL_DATE); // determine if this is a wall post @@ -1246,8 +1245,6 @@ class Item extends Controller { if ((argc() == 3) && (argv(1) === 'drop') && intval(argv(2))) { - require_once('include/items.php'); - $i = q("select * from item where id = %d limit 1", intval(argv(2)) ); diff --git a/Zotlabs/Module/Owa.php b/Zotlabs/Module/Owa.php index 85467d4f4..21082e166 100644 --- a/Zotlabs/Module/Owa.php +++ b/Zotlabs/Module/Owa.php @@ -18,96 +18,95 @@ use Zotlabs\Web\Controller; class Owa extends Controller { - function init() { + public function init(): void + { $ret = [ 'success' => false ]; - if (array_key_exists('REDIRECT_REMOTE_USER',$_SERVER) && (! array_key_exists('HTTP_AUTHORIZATION',$_SERVER))) { - $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['REDIRECT_REMOTE_USER']; + if (!$this->validateAuthorizationHeader()) { + $this->error('Missing or invalid authorization header.'); } - if (array_key_exists('HTTP_AUTHORIZATION',$_SERVER) && substr(trim($_SERVER['HTTP_AUTHORIZATION']),0,9) === 'Signature') { - $sigblock = HTTPSig::parse_sigheader($_SERVER['HTTP_AUTHORIZATION']); - if ($sigblock) { - $keyId = $sigblock['keyId']; - $parsed = parse_url($keyId); - if (str_starts_with($parsed['scheme'],'http')) { - unset($parsed['fragment']); - unset($parsed['query']); - $keyId = unparse_url($parsed); - } - else { - $keyId = str_replace('acct:', '', $keyId); + $sigblock = HTTPSig::parse_sigheader($_SERVER['HTTP_AUTHORIZATION']); + if ($sigblock) { + $keyId = $sigblock['keyId']; + $parsed = parse_url($keyId); + if (str_starts_with($parsed['scheme'],'http')) { + unset($parsed['fragment']); + unset($parsed['query']); + $keyId = unparse_url($parsed); + } + else { + $keyId = str_replace('acct:', '', $keyId); + } + if ($keyId) { + $r = q("SELECT * FROM hubloc LEFT JOIN xchan ON hubloc_hash = xchan_hash + WHERE (hubloc_addr = '%s' OR hubloc_id_url = '%s' OR xchan_hash = '%s') + AND hubloc_deleted = 0 AND xchan_pubkey != '' + ORDER BY hubloc_id DESC", + dbesc($keyId), + dbesc($keyId), + dbesc($keyId) + ); + if (! $r) { + $found = discover_by_webbie($keyId); + logger('found = ' . print_r($found, true)); + if ($found) { + $r = q("SELECT * FROM hubloc LEFT JOIN xchan ON hubloc_hash = xchan_hash + WHERE (hubloc_addr = '%s' OR hubloc_id_url = '%s' OR xchan_hash = '%s') AND hubloc_deleted = 0 AND xchan_pubkey != '' ORDER BY hubloc_id DESC ", + dbesc($keyId), + dbesc($keyId), + dbesc($keyId) + ); + } } - if ($keyId) { - $r = q("SELECT * FROM hubloc LEFT JOIN xchan ON hubloc_hash = xchan_hash - WHERE (hubloc_addr = '%s' OR hubloc_id_url = '%s' OR xchan_hash = '%s') - AND hubloc_deleted = 0 AND xchan_pubkey != '' - ORDER BY hubloc_id DESC", - dbesc($keyId), - dbesc($keyId), - dbesc($keyId) - ); - if (! $r) { + + if ($r) { + foreach ($r as $hubloc) { + $verified = HTTPSig::verify(file_get_contents('php://input'), $hubloc['xchan_pubkey']); + if ($verified && $verified['header_signed'] && $verified['header_valid'] && ($verified['content_valid'] || (! $verified['content_signed']))) { + logger('OWA header: ' . print_r($verified,true),LOGGER_DATA); + logger('OWA success: ' . $hubloc['hubloc_id_url'],LOGGER_DATA); + $ret['success'] = true; + $token = random_string(32); + Verify::create('owt',0,$token,$hubloc['hubloc_id_url']); + $result = ''; + openssl_public_encrypt($token,$result,$hubloc['xchan_pubkey']); + $ret['encrypted_token'] = base64url_encode($result); + break; + } else { + logger('OWA fail: ' . $hubloc['hubloc_id'] . ' ' . $hubloc['hubloc_id_url']); + } + } + + if (!$ret['success']) { + + // Possible a reinstall? + // In this case we probably already have an old hubloc + // but not the new one yet. + $found = discover_by_webbie($keyId); - logger('found = ' . print_r($found, true)); + if ($found) { $r = q("SELECT * FROM hubloc LEFT JOIN xchan ON hubloc_hash = xchan_hash - WHERE (hubloc_addr = '%s' OR hubloc_id_url = '%s' OR xchan_hash = '%s') AND hubloc_deleted = 0 AND xchan_pubkey != '' ORDER BY hubloc_id DESC ", - dbesc($keyId), - dbesc($keyId), + WHERE (hubloc_addr = '%s' OR hubloc_id_url = '%s') AND hubloc_deleted = 0 ORDER BY hubloc_id DESC LIMIT 1", + dbesc(str_replace('acct:', '', $keyId)), dbesc($keyId) ); - } - } - - if ($r) { - foreach ($r as $hubloc) { - $verified = HTTPSig::verify(file_get_contents('php://input'), $hubloc['xchan_pubkey']); - if ($verified && $verified['header_signed'] && $verified['header_valid'] && ($verified['content_valid'] || (! $verified['content_signed']))) { - logger('OWA header: ' . print_r($verified,true),LOGGER_DATA); - logger('OWA success: ' . $hubloc['hubloc_id_url'],LOGGER_DATA); - $ret['success'] = true; - $token = random_string(32); - Verify::create('owt',0,$token,$hubloc['hubloc_id_url']); - $result = ''; - openssl_public_encrypt($token,$result,$hubloc['xchan_pubkey']); - $ret['encrypted_token'] = base64url_encode($result); - break; - } else { - logger('OWA fail: ' . $hubloc['hubloc_id'] . ' ' . $hubloc['hubloc_id_url']); - } - } - if (!$ret['success']) { - - // Possible a reinstall? - // In this case we probably already have an old hubloc - // but not the new one yet. - - $found = discover_by_webbie($keyId); - - if ($found) { - $r = q("SELECT * FROM hubloc LEFT JOIN xchan ON hubloc_hash = xchan_hash - WHERE (hubloc_addr = '%s' OR hubloc_id_url = '%s') AND hubloc_deleted = 0 ORDER BY hubloc_id DESC LIMIT 1", - dbesc(str_replace('acct:', '', $keyId)), - dbesc($keyId) - ); - - if ($r) { - $verified = HTTPSig::verify(file_get_contents('php://input'), $r[0]['xchan_pubkey']); - if ($verified && $verified['header_signed'] && $verified['header_valid'] && ($verified['content_valid'] || (! $verified['content_signed']))) { - logger('OWA header: ' . print_r($verified,true), LOGGER_DATA); - logger('OWA success: ' . $r[0]['hubloc_id_url'], LOGGER_DATA); - $ret['success'] = true; - $token = random_string(32); - Verify::create('owt', 0, $token, $r[0]['hubloc_id_url']); - $result = ''; - openssl_public_encrypt($token, $result, $r[0]['xchan_pubkey']); - $ret['encrypted_token'] = base64url_encode($result); - } else { - logger('OWA fail: ' . $hubloc['hubloc_id'] . ' ' . $hubloc['hubloc_id_url']); - } + if ($r) { + $verified = HTTPSig::verify(file_get_contents('php://input'), $r[0]['xchan_pubkey']); + if ($verified && $verified['header_signed'] && $verified['header_valid'] && ($verified['content_valid'] || (! $verified['content_signed']))) { + logger('OWA header: ' . print_r($verified,true), LOGGER_DATA); + logger('OWA success: ' . $r[0]['hubloc_id_url'], LOGGER_DATA); + $ret['success'] = true; + $token = random_string(32); + Verify::create('owt', 0, $token, $r[0]['hubloc_id_url']); + $result = ''; + openssl_public_encrypt($token, $result, $r[0]['xchan_pubkey']); + $ret['encrypted_token'] = base64url_encode($result); + } else { + logger('OWA fail: ' . $hubloc['hubloc_id'] . ' ' . $hubloc['hubloc_id_url']); } } } @@ -118,4 +117,33 @@ class Owa extends Controller { json_return_and_die($ret,'application/x-zot+json'); } + + private function validateAuthorizationHeader(): bool + { + if (!empty($_SERVER['HTTP_AUTHORIZATION'])) { + $auth = trim($_SERVER['HTTP_AUTHORIZATION']); + } else if (!empty($_SERVER['REDIRECT_REMOTE_USER'])) { + $auth = trim($_SERVER['REDIRECT_REMOTE_USER']); + } else { + return false; + } + + return strncmp($auth, 'Signature', 9) === 0; + } + + /** + * Terminates the request, and return a json error response. + * + * @Note This function does not return! + * + * @param string $msg The error message for the response. + */ + private function error(string $msg): void { + $ret = [ + 'success' => false, + 'message' => $msg + ]; + + json_return_and_die($ret,'application/x-zot+json'); + } } diff --git a/Zotlabs/Module/Pubstream.php b/Zotlabs/Module/Pubstream.php index 6d8edf4d8..234e73792 100644 --- a/Zotlabs/Module/Pubstream.php +++ b/Zotlabs/Module/Pubstream.php @@ -202,11 +202,9 @@ class Pubstream extends \Zotlabs\Web\Controller { if($mid) { $r = q("SELECT parent AS item_id FROM item left join abook on item.author_xchan = abook.abook_xchan - left join xchan on item.author_xchan = xchan.xchan_hash WHERE item.$identifier = '%s' and item.item_private = 0 $uids $site_firehose_sql $item_normal - and xchan.xchan_censored = 0 and (abook.abook_blocked = 0 or abook.abook_flags is null) $sql_extra $net_query2", dbesc($mid) @@ -216,11 +214,9 @@ class Pubstream extends \Zotlabs\Web\Controller { // Fetch a page full of parent items for this page $r = dbq("SELECT parent AS item_id FROM item left join abook on ( item.author_xchan = abook.abook_xchan $abook_uids ) - left join xchan on item.author_xchan = xchan.xchan_hash WHERE item.item_private = 0 $thread_top $uids $site_firehose_sql $item_normal - and xchan.xchan_censored = 0 and (abook.abook_blocked = 0 or abook.abook_flags is null) $sql_extra $net_query2 ORDER BY $ordering DESC $pager_sql " @@ -231,10 +227,8 @@ class Pubstream extends \Zotlabs\Web\Controller { if($mid) { $r = q("SELECT parent AS item_id FROM item left join abook on item.author_xchan = abook.abook_xchan - left join xchan on item.author_xchan = xchan.xchan_hash WHERE item.$identifier = '%s' and item.item_private = 0 $uids $site_firehose_sql $item_normal_update $simple_update - and xchan.xchan_censored = 0 and (abook.abook_blocked = 0 or abook.abook_flags is null) $sql_extra $net_query2", dbesc($mid) @@ -243,11 +237,9 @@ class Pubstream extends \Zotlabs\Web\Controller { else { $r = dbq("SELECT parent AS item_id FROM item left join abook on item.author_xchan = abook.abook_xchan - left join xchan on item.author_xchan = xchan.xchan_hash WHERE item.item_private = 0 $thread_top $uids $site_firehose_sql $item_normal_update $simple_update - and xchan.xchan_censored = 0 and (abook.abook_blocked = 0 or abook.abook_flags is null) $sql_extra $net_query2" ); diff --git a/Zotlabs/Module/Sse.php b/Zotlabs/Module/Sse.php index fda2f2be4..673457db1 100644 --- a/Zotlabs/Module/Sse.php +++ b/Zotlabs/Module/Sse.php @@ -207,10 +207,9 @@ class Sse extends Controller { if ($result) { XConfig::Set(self::$ob_hash, 'sse', 'notifications', []); - json_return_and_die($result); } - killme(); + json_return_and_die($result); } diff --git a/Zotlabs/Module/Sse_bs.php b/Zotlabs/Module/Sse_bs.php index 09c4ed614..5292abfaa 100644 --- a/Zotlabs/Module/Sse_bs.php +++ b/Zotlabs/Module/Sse_bs.php @@ -22,7 +22,6 @@ class Sse_bs extends Controller { public static $xchans; function init() { - self::$uid = local_channel(); self::$ob_hash = get_observer_hash(); self::$sse_id = false; @@ -43,8 +42,9 @@ class Sse_bs extends Controller { self::$offset = 0; self::$xchans = ''; - if(isset($_REQUEST['sse_rmids'])) - self::mark_read($_REQUEST['sse_rmids']); + if (isset($_REQUEST['sse_rmids'])) { + self::mark_read(explode(',', $_REQUEST['sse_rmids'])); + } if(!empty($_REQUEST['nquery']) && $_REQUEST['nquery'] !== '%') { $nquery = $_REQUEST['nquery']; @@ -207,7 +207,7 @@ class Sse_bs extends Controller { $item_normal $sql_extra $sql_extra2 - ORDER BY created DESC LIMIT $limit OFFSET $offset", + ORDER BY created DESC, received DESC LIMIT $limit OFFSET $offset", intval(self::$uid), dbescdate($_SESSION['sse_loadtime']), dbesc(self::$ob_hash) @@ -290,7 +290,7 @@ class Sse_bs extends Controller { $item_normal $sql_extra $sql_extra2 - ORDER BY created DESC LIMIT $limit OFFSET $offset", + ORDER BY created DESC, received DESC LIMIT $limit OFFSET $offset", intval(self::$uid), dbescdate($_SESSION['sse_loadtime']), dbesc(self::$ob_hash) @@ -373,7 +373,7 @@ class Sse_bs extends Controller { $item_normal $sql_extra $sql_extra2 - ORDER BY created DESC LIMIT $limit OFFSET $offset", + ORDER BY created DESC, received DESC LIMIT $limit OFFSET $offset", intval(self::$uid), dbescdate($_SESSION['sse_loadtime']), dbesc(self::$ob_hash) @@ -481,7 +481,7 @@ class Sse_bs extends Controller { $sql_extra $sql_extra2 $sql_extra3 - ORDER BY created DESC LIMIT $limit OFFSET $offset", + ORDER BY created DESC, received DESC LIMIT $limit OFFSET $offset", dbescdate($_SESSION['sse_loadtime']), dbesc(self::$ob_hash), dbescdate($_SESSION['last_login_date'] ?? $_SESSION['static_loadtime']) @@ -502,13 +502,15 @@ class Sse_bs extends Controller { } } - $r = q("SELECT id FROM item + $r = q("SELECT id, body FROM item WHERE true $uids + AND created <= '%s' AND created > '%s' $item_normal $sql_extra $sql_extra3 AND author_xchan != '%s' LIMIT 100", + dbescdate($_SESSION['sse_loadtime']), dbescdate($_SESSION['last_login_date'] ?? $_SESSION['static_loadtime']), dbesc(self::$ob_hash) ); @@ -677,7 +679,7 @@ class Sse_bs extends Controller { AND author_xchan != '%s' AND item_unseen = 1 $item_normal - ORDER BY created DESC", + ORDER BY created DESC, received DESC", dbesc(ACTIVITY_POST), intval(self::$uid), dbesc(self::$ob_hash) diff --git a/Zotlabs/Web/WebServer.php b/Zotlabs/Web/WebServer.php index 19f14ee8a..d59effc88 100644 --- a/Zotlabs/Web/WebServer.php +++ b/Zotlabs/Web/WebServer.php @@ -61,7 +61,7 @@ class WebServer { if (x($_GET,'zid') && $installed) { \App::$query_string = strip_zids(\App::$query_string); if(! local_channel()) { - if (!isset($_SESSION['my_address']) || $_SESSION['my_address'] != $_GET['zid']) { + if (!isset($_SESSION['my_address'])) { $_SESSION['my_address'] = Text::escape_tags($_GET['zid']); $_SESSION['authenticated'] = 0; } |