diff options
Diffstat (limited to 'Zotlabs/Module/Item.php')
-rw-r--r-- | Zotlabs/Module/Item.php | 747 |
1 files changed, 396 insertions, 351 deletions
diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php index 3d13655d2..83e8d609e 100644 --- a/Zotlabs/Module/Item.php +++ b/Zotlabs/Module/Item.php @@ -44,253 +44,30 @@ class Item extends Controller { function init() { if (Libzot::is_zot_request()) { - - $item_id = argv(1); - - if (!$item_id) - http_status_exit(404, 'Not found'); - - $portable_id = EMPTY_STR; - - $item_normal_extra = sprintf(" and not verb in ('Follow', 'Ignore', '%s', '%s') ", - dbesc(ACTIVITY_FOLLOW), - dbesc(ACTIVITY_UNFOLLOW) - ); - - $item_normal = " and item.item_hidden = 0 and item.item_type = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_blocked = 0 $item_normal_extra "; - - $i = null; - - // do we have the item (at all)? - - $r = q("select parent_mid from item where uuid = '%s' $item_normal limit 1", - dbesc($item_id) - ); - - if (!$r) { - http_status_exit(404, 'Not found'); - } - - // process an authenticated fetch - - $sigdata = HTTPSig::verify(($_SERVER['REQUEST_METHOD'] === 'POST') ? file_get_contents('php://input') : EMPTY_STR); - if ($sigdata['portable_id'] && $sigdata['header_valid']) { - $portable_id = $sigdata['portable_id']; - if (!check_channelallowed($portable_id)) { - http_status_exit(403, 'Permission denied'); - } - if (!check_siteallowed($sigdata['signer'])) { - http_status_exit(403, 'Permission denied'); - } - observer_auth($portable_id); - - $i = q("select id as item_id, uid from item where mid = '%s' $item_normal and owner_xchan = '%s' limit 1", - dbesc($r[0]['parent_mid']), - dbesc($portable_id) - ); - } - elseif (Config::get('system', 'require_authenticated_fetch', false)) { - http_status_exit(403, 'Permission denied'); - } - - // if we don't have a parent id belonging to the signer see if we can obtain one as a visitor that we have permission to access - // with a bias towards those items owned by channels on this site (item_wall = 1) - - $sql_extra = item_permissions_sql(0); - - if (!$i) { - $i = q("select id as item_id, uid, item_private from item where mid = '%s' $item_normal $sql_extra order by item_wall desc limit 1", - dbesc($r[0]['parent_mid']) - ); - } - - if (!$i) { - http_status_exit(403, 'Forbidden'); - } - - $chan = channelx_by_n($i[0]['uid']); - - if (!$chan) { - http_status_exit(404, 'Not found'); - } - - if (!perm_is_allowed($chan['channel_id'], get_observer_hash(), 'view_stream')) { - http_status_exit(403, 'Forbidden'); - } - - $parents_str = ids_to_querystr($i, 'item_id'); - - // We won't need to check for privacy mismatches if the verified observer is also owner - $parent_item_private = ((isset($i[0]['item_private'])) ? " and item_private = " . intval($i[0]['item_private']) . " " : ''); - - $total = q("SELECT count(*) AS count FROM item WHERE parent = %d $parent_item_private $item_normal ", - intval($parents_str) - ); - - App::set_pager_total($total[0]['count']); - App::set_pager_itemspage(30); - - if (App::$pager['total'] > App::$pager['itemspage']) { - // let mod conversation handle this request - App::$query_string = str_replace('item', 'conversation', App::$query_string); - $i = Activity::paged_collection_init(App::$pager['total'], App::$query_string); - as_return_and_die($i ,$chan); - } - else { - $items = q("SELECT item.*, item.id AS item_id FROM item WHERE item.parent = %d $parent_item_private $item_normal ORDER BY item.id", - intval($parents_str) - ); - - xchan_query($items, true); - $items = fetch_post_tags($items, true); - - $i = Activity::encode_item_collection($items, App::$query_string, 'OrderedCollection', App::$pager['total']); - } - - if ($portable_id && (!intval($items[0]['item_private']))) { - $c = q("select abook_id from abook where abook_channel = %d and abook_xchan = '%s'", - intval($items[0]['uid']), - dbesc($portable_id) - ); - if (!$c) { - ThreadListener::store(z_root() . '/item/' . $item_id, $portable_id); - } - } - - as_return_and_die($i ,$chan); + $this->init_zot_request(); } if (ActivityStreams::is_as_request()) { - - $item_id = argv(1); - if (!$item_id) - http_status_exit(404, 'Not found'); - - $portable_id = EMPTY_STR; - - $item_normal_extra = sprintf(" and not verb in ('Follow', 'Ignore', '%s', '%s') ", - dbesc(ACTIVITY_FOLLOW), - dbesc(ACTIVITY_UNFOLLOW) - ); - - $item_normal = " and item.item_hidden = 0 and item.item_type = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_blocked = 0 $item_normal_extra "; - - $i = null; - - // do we have the item (at all)? - // add preferential bias to item owners (item_wall = 1) - - $r = q("select * from item where uuid = '%s' $item_normal order by item_wall desc limit 1", - dbesc($item_id) - ); - - if (!$r) { - http_status_exit(404, 'Not found'); - } - - // process an authenticated fetch - - $sigdata = HTTPSig::verify(EMPTY_STR); - if ($sigdata['portable_id'] && $sigdata['header_valid']) { - $portable_id = $sigdata['portable_id']; - if (!check_channelallowed($portable_id)) { - http_status_exit(403, 'Permission denied'); - } - if (!check_siteallowed($sigdata['signer'])) { - http_status_exit(403, 'Permission denied'); - } - observer_auth($portable_id); - - $i = q("select id as item_id from item where mid = '%s' $item_normal and owner_xchan = '%s' limit 1 ", - dbesc($r[0]['parent_mid']), - dbesc($portable_id) - ); - } - elseif (Config::get('system', 'require_authenticated_fetch', false)) { - http_status_exit(403, 'Permission denied'); - } - - // if we don't have a parent id belonging to the signer see if we can obtain one as a visitor that we have permission to access - // with a bias towards those items owned by channels on this site (item_wall = 1) - - $sql_extra = item_permissions_sql(0); - - if (!$i) { - $i = q("select id as item_id from item where mid = '%s' $item_normal $sql_extra order by item_wall desc limit 1", - dbesc($r[0]['parent_mid']) - ); - } - - $bear = Activity::token_from_request(); - if ($bear) { - logger('bear: ' . $bear, LOGGER_DEBUG); - if (!$i) { - $t = q("select * from iconfig where cat = 'ocap' and k = 'relay' and v = '%s'", - dbesc($bear) - ); - if ($t) { - $i = q("select id as item_id from item where uuid = '%s' and id = %d $item_normal limit 1", - dbesc($item_id), - intval($t[0]['iid']) - ); - } - } - } - - if (!$i) { - http_status_exit(403, 'Forbidden'); - } - - // If we get to this point we have determined we can access the original in $r (fetched much further above), so use it. - - xchan_query($r, true); - $items = fetch_post_tags($r, false); - - $chan = channelx_by_n($items[0]['uid']); - - if (!$chan) - http_status_exit(404, 'Not found'); - - if (!perm_is_allowed($chan['channel_id'], get_observer_hash(), 'view_stream')) - http_status_exit(403, 'Forbidden'); - - $i = Activity::encode_item($items[0]); - - if (!$i) - http_status_exit(404, 'Not found'); - - if ($portable_id && (!intval($items[0]['item_private']))) { - $c = q("select abook_id from abook where abook_channel = %d and abook_xchan = '%s'", - intval($items[0]['uid']), - dbesc($portable_id) - ); - if (!$c) { - ThreadListener::store(z_root() . '/item/' . $item_id, $portable_id); - } - } - - as_return_and_die($i ,$chan); - + $this->init_as_request(); } 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'); } @@ -301,7 +78,7 @@ class Item extends Controller { // This will change. Figure out who the observer is and whether or not // they have permission to post here. Else ignore the post. - if ((!local_channel()) && (!remote_channel()) && (!x($_REQUEST, 'anonname'))) + if ((!local_channel()) && (!remote_channel()) && (empty($_POST['anonname']))) return; $uid = local_channel(); @@ -323,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; @@ -331,12 +107,13 @@ class Item extends Controller { * Is this a reply to something? */ - $parent = ((x($_REQUEST, 'parent')) ? intval($_REQUEST['parent']) : 0); - $parent_mid = ((x($_REQUEST, 'parent_mid')) ? trim($_REQUEST['parent_mid']) : ''); - $mode = ((isset($_REQUEST['conv_mode']) && $_REQUEST['conv_mode'] === 'channel') ? 'channel' : 'network'); + $parent = ((!empty($_POST['parent'])) ? intval($_POST['parent']) : 0); + $thr_parent_id = $parent; + $parent_mid = ((!empty($_POST['parent_mid'])) ? trim($_POST['parent_mid']) : ''); + $mode = ((isset($_POST['conv_mode']) && $_POST['conv_mode'] === 'channel') ? 'channel' : 'network'); - $remote_xchan = ((x($_REQUEST, 'remote_xchan')) ? trim($_REQUEST['remote_xchan']) : false); - $r = q("select * from xchan where xchan_hash = '%s' limit 1", + $remote_xchan = ((!empty($_POST['remote_xchan'])) ? trim($_POST['remote_xchan']) : false); + $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($remote_xchan) ); if ($r) @@ -344,7 +121,7 @@ class Item extends Controller { else $remote_xchan = $remote_observer = false; - $profile_uid = ((x($_REQUEST, 'profile_uid')) ? intval($_REQUEST['profile_uid']) : 0); + $profile_uid = ((!empty($_POST['profile_uid'])) ? intval($_POST['profile_uid']) : 0); require_once('include/channel.php'); $sys = get_sys_channel(); @@ -354,25 +131,25 @@ class Item extends Controller { $observer = $sys; } - if (x($_REQUEST, 'dropitems')) { + if (!empty($_POST['dropitems'])) { require_once('include/items.php'); - $arr_drop = explode(',', $_REQUEST['dropitems']); + $arr_drop = explode(',', $_POST['dropitems']); drop_items($arr_drop); $json = ['success' => 1]; echo json_encode($json); killme(); } - call_hooks('post_local_start', $_REQUEST); + call_hooks('post_local_start', $_POST); - // logger('postvars ' . print_r($_REQUEST,true), LOGGER_DATA); + // logger('postvars ' . print_r($_POST,true), LOGGER_DATA); - $api_source = ((x($_REQUEST, 'api_source') && $_REQUEST['api_source']) ? true : false); + $api_source = ((!empty($_POST['api_source'])) ? true : false); - $consensus = $_REQUEST['consensus'] ?? 0; - $nocomment = $_REQUEST['nocomment'] ?? 0; + $consensus = $_POST['consensus'] ?? 0; + $nocomment = $_POST['nocomment'] ?? 0; - $is_poll = ((isset($_REQUEST['poll_answers'][0]) && $_REQUEST['poll_answers'][0]) && (isset($_REQUEST['poll_answers'][1]) && $_REQUEST['poll_answers'][1])); + $is_poll = ((isset($_POST['poll_answers'][0]) && $_POST['poll_answers'][0]) && (isset($_POST['poll_answers'][1]) && $_POST['poll_answers'][1])); // 'origin' (if non-zero) indicates that this network is where the message originated, // for the purpose of relaying comments to other conversation members. @@ -383,42 +160,43 @@ class Item extends Controller { // If you are unsure, it is prudent (and important) to leave it unset. - $origin = (($api_source && array_key_exists('origin', $_REQUEST)) ? intval($_REQUEST['origin']) : 1); + $origin = (($api_source && array_key_exists('origin', $_POST)) ? intval($_REQU_POSTEST['origin']) : 1); // To represent message-ids on other networks - this will create an iconfig record - $namespace = (($api_source && array_key_exists('namespace', $_REQUEST)) ? strip_tags($_REQUEST['namespace']) : ''); - $remote_id = (($api_source && array_key_exists('remote_id', $_REQUEST)) ? strip_tags($_REQUEST['remote_id']) : ''); + $namespace = (($api_source && array_key_exists('namespace', $_POST)) ? strip_tags($_POST['namespace']) : ''); + $remote_id = (($api_source && array_key_exists('remote_id', $_POST)) ? strip_tags($_POST['remote_id']) : ''); $owner_hash = null; - $message_id = ((x($_REQUEST, 'message_id') && $api_source) ? strip_tags($_REQUEST['message_id']) : null); - $created = ((x($_REQUEST, 'created')) ? datetime_convert(date_default_timezone_get(), 'UTC', $_REQUEST['created']) : datetime_convert()); - $post_id = ((x($_REQUEST, 'post_id')) ? intval($_REQUEST['post_id']) : 0); - $app = ((x($_REQUEST, 'source')) ? strip_tags($_REQUEST['source']) : ''); - $return_path = ((x($_REQUEST, 'return')) ? $_REQUEST['return'] : ''); - $preview = ((x($_REQUEST, 'preview')) ? intval($_REQUEST['preview']) : 0); - $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); - $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']) : ''); - $obj_type = ((x($_REQUEST, 'obj_type')) ? escape_tags($_REQUEST['obj_type']) : 'Note'); + $message_id = ((!empty($_POST['message_id']) && $api_source) ? strip_tags($_POST['message_id']) : null); + $created = ((!empty($_POST['created'])) ? datetime_convert(date_default_timezone_get(), 'UTC', $_POST['created']) : datetime_convert()); + $post_id = ((!empty($_POST['post_id'])) ? intval($_POST['post_id']) : 0); + $app = ((!empty($_POST['source'])) ? strip_tags($_POST['source']) : ''); + $return_path = ((!empty($_POST['return'])) ? $_POST['return'] : ''); + $preview = ((!empty($_POST['preview'])) ? intval($_POST['preview']) : 0); + $categories = ((!empty($_POST['category'])) ? escape_tags($_POST['category']) : ''); + $item_type = ((!empty($_POST['webpage'])) ? intval($_POST['webpage']) : ITEM_TYPE_POST); + $item_obscured = ((!empty($_POST['obscured'])) ? intval($_POST['obscured']) : 0); + $item_delayed = ((!empty($_POST['delayed'])) ? intval($_POST['delayed']) : 0); + $pagetitle = ((!empty($_POST['pagetitle'])) ? escape_tags($_POST['pagetitle']) : ''); + $layout_mid = ((!empty($_POST['layout_mid'])) ? escape_tags($_POST['layout_mid']) : ''); + $plink = ((!empty($_POST['permalink'])) ? escape_tags($_POST['permalink']) : null); + $obj_type = ((!empty($_POST['obj_type'])) ? escape_tags($_POST['obj_type']) : 'Note'); // allow API to bulk load a bunch of imported items with sending out a bunch of posts. - $nopush = ((x($_REQUEST, 'nopush')) ? intval($_REQUEST['nopush']) : 0); + $nopush = ((!empty($_POST['nopush'])) ? intval($_POST['nopush']) : 0); /* * Check service class limits */ - if ($uid && !(x($_REQUEST, 'parent')) && !(x($_REQUEST, 'post_id'))) { - $ret = $this->item_check_service_class($uid, (($_REQUEST['webpage'] == ITEM_TYPE_WEBPAGE) ? true : false)); + if ($uid && empty($_POST['parent']) && empty($_POST['post_id'])) { + $ret = $this->item_check_service_class($uid, (($_POST['webpage'] == ITEM_TYPE_WEBPAGE) ? true : false)); if (!$ret['success']) { notice(t($ret['message']) . EOL); if ($api_source) return (['success' => false, 'message' => 'service class exception']); - if (x($_REQUEST, 'return')) + if (!empty($_POST['return'])) goaway(z_root() . "/" . $return_path); killme(); } @@ -430,7 +208,6 @@ class Item extends Controller { $expires = NULL_DATE; - $comments_closed = NULL_DATE; $route = ''; $parent_item = null; @@ -440,8 +217,8 @@ class Item extends Controller { if ($parent || $parent_mid) { - if (!x($_REQUEST, 'type')) - $_REQUEST['type'] = 'net-comment'; + if (empty($_POST['type'])) + $_POST['type'] = 'net-comment'; if ($parent) { $r = q("SELECT * FROM item WHERE id = %d LIMIT 1", @@ -477,7 +254,7 @@ class Item extends Controller { notice(t('Unable to locate original post.') . EOL); if ($api_source) return (['success' => false, 'message' => 'invalid post id']); - if (x($_REQUEST, 'return')) + if (!empty($_POST['return'])) goaway(z_root() . "/" . $return_path); killme(); } @@ -500,7 +277,7 @@ class Item extends Controller { if (!$observer) { $observer = App::get_observer(); if (!$observer) { - $observer = anon_identity_init($_REQUEST); + $observer = anon_identity_init($_POST); if ($observer) { $moderated = true; $remote_xchan = $remote_observer = $observer; @@ -512,7 +289,7 @@ class Item extends Controller { notice(t('Permission denied.') . EOL); if ($api_source) return (['success' => false, 'message' => 'permission denied']); - if (x($_REQUEST, 'return')) + if (!empty($_POST['return'])) goaway(z_root() . "/" . $return_path); killme(); } @@ -531,17 +308,17 @@ class Item extends Controller { notice(t('Permission denied.') . EOL); if ($api_source) return (['success' => false, 'message' => 'permission denied']); - if (x($_REQUEST, 'return')) + if (!empty($_POST['return'])) goaway(z_root() . "/" . $return_path); killme(); } } else { - if (!perm_is_allowed($profile_uid, $observer['xchan_hash'], ($webpage) ? 'write_pages' : 'post_wall')) { + if (!perm_is_allowed($profile_uid, $observer['xchan_hash'], (intval($item_type) === ITEM_TYPE_POST) ? 'post_wall' : 'write_pages')) { notice(t('Permission denied.') . EOL); if ($api_source) return (['success' => false, 'message' => 'permission denied']); - if (x($_REQUEST, 'return')) + if (!empty($_POST['return'])) goaway(z_root() . "/" . $return_path); killme(); } @@ -597,7 +374,7 @@ class Item extends Controller { logger("mod_item: no channel."); if ($api_source) return (['success' => false, 'message' => 'no channel']); - if (x($_REQUEST, 'return')) + if (!empty($_POST['return'])) goaway(z_root() . "/" . $return_path); killme(); } @@ -607,6 +384,7 @@ class Item extends Controller { $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($channel['channel_hash']) ); + if ($r && count($r)) { $owner_xchan = $r[0]; } @@ -614,7 +392,7 @@ class Item extends Controller { logger("mod_item: no owner."); if ($api_source) return (['success' => false, 'message' => 'no owner']); - if (x($_REQUEST, 'return')) + if (!empty($_POST['return'])) goaway(z_root() . "/" . $return_path); killme(); } @@ -648,17 +426,21 @@ class Item extends Controller { $view_policy = \Zotlabs\Access\PermissionLimits::Get($channel['channel_id'], 'view_stream'); $comment_policy = \Zotlabs\Access\PermissionLimits::Get($channel['channel_id'], 'post_comments'); - $public_policy = ((x($_REQUEST, 'public_policy')) ? escape_tags($_REQUEST['public_policy']) : map_scope($view_policy, true)); - if ($webpage) - $public_policy = ''; - if ($public_policy) + $public_policy = ''; + + if (intval($item_type) === ITEM_TYPE_POST) { + $public_policy = ((!empty($_POST['public_policy'])) ? escape_tags($_POST['public_policy']) : map_scope($view_policy, true)); + } + + if ($public_policy) { $private = 1; + } if ($orig_post) { $private = 0; - // webpages are allowed to change ACLs after the fact. Normal conversation items aren't. - if ($webpage) { - $acl->set_from_array($_REQUEST); + // Normal conversation items are not allowed to change ACL. + if (intval($item_type) !== ITEM_TYPE_POST) { + $acl->set_from_array($_POST); } else { $acl->set($orig_post); @@ -674,10 +456,10 @@ class Item extends Controller { $coord = $orig_post['coord']; $verb = $orig_post['verb']; $app = $orig_post['app']; - $title = escape_tags(trim($_REQUEST['title'])); - $summary = escape_tags(trim($_REQUEST['summary'])); - $body = trim($_REQUEST['body']); - $item_flags = $orig_post['item_flags']; + $title = escape_tags(trim($_POST['title'])); + $summary = escape_tags(trim($_POST['summary'])); + $body = trim($_POST['body']); + $item_flags = $orig_post['item_flags']; $item_origin = $orig_post['item_origin']; $item_unseen = $orig_post['item_unseen']; $item_starred = $orig_post['item_starred']; @@ -691,7 +473,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']; @@ -710,14 +492,15 @@ class Item extends Controller { $thr_parent = $orig_post['thr_parent']; $parent_mid = $orig_post['parent_mid']; $plink = $orig_post['plink']; + $owner_hash = $orig_post['owner_xchan']; } else { if (!$walltowall) { - if ((array_key_exists('contact_allow', $_REQUEST)) - || (array_key_exists('group_allow', $_REQUEST)) - || (array_key_exists('contact_deny', $_REQUEST)) - || (array_key_exists('group_deny', $_REQUEST))) { - $acl->set_from_array($_REQUEST); + if ((array_key_exists('contact_allow', $_POST)) + || (array_key_exists('group_allow', $_POST)) + || (array_key_exists('contact_deny', $_POST)) + || (array_key_exists('group_deny', $_POST))) { + $acl->set_from_array($_POST); } elseif (!$api_source) { @@ -732,16 +515,16 @@ class Item extends Controller { } - $location = ((isset($_REQUEST['location'])) ? notags(trim($_REQUEST['location'])) : ''); - $coord = ((isset($_REQUEST['coord'])) ? notags(trim($_REQUEST['coord'])) : ''); - $verb = ((isset($_REQUEST['verb'])) ? notags(trim($_REQUEST['verb'])) : ''); - $title = ((isset($_REQUEST['title'])) ? escape_tags(trim($_REQUEST['title'])) : ''); - $summary = ((isset($_REQUEST['summary'])) ? escape_tags(trim($_REQUEST['summary'])) : ''); - $body = ((isset($_REQUEST['body'])) ? trim($_REQUEST['body']) : ''); - $body .= ((isset($_REQUEST['attachment'])) ? trim($_REQUEST['attachment']) : ''); + $location = ((isset($_POST['location'])) ? notags(trim($_POST['location'])) : ''); + $coord = ((isset($_POST['coord'])) ? notags(trim($_POST['coord'])) : ''); + $verb = ((isset($_POST['verb'])) ? notags(trim($_POST['verb'])) : ''); + $title = ((isset($_POST['title'])) ? escape_tags(trim($_POST['title'])) : ''); + $summary = ((isset($_POST['summary'])) ? escape_tags(trim($_POST['summary'])) : ''); + $body = ((isset($_POST['body'])) ? trim($_POST['body']) : ''); + $body .= ((isset($_POST['attachment'])) ? trim($_POST['attachment']) : ''); $postopts = ''; - $allow_empty = ((array_key_exists('allow_empty', $_REQUEST)) ? intval($_REQUEST['allow_empty']) : 0); + $allow_empty = ((array_key_exists('allow_empty', $_POST)) ? intval($_POST['allow_empty']) : 0); $private = ((isset($private) && $private) ? $private : intval($acl->is_private() || ($public_policy))); @@ -752,7 +535,7 @@ class Item extends Controller { $private = intval($parent_item['item_private']); $public_policy = $parent_item['public_policy']; $owner_hash = $parent_item['owner_xchan']; - $webpage = $parent_item['item_type']; + $item_type = $parent_item['item_type']; } @@ -763,7 +546,7 @@ class Item extends Controller { info(t('Empty post discarded.') . EOL); if ($api_source) return (['success' => false, 'message' => 'no content']); - if (x($_REQUEST, 'return')) + if (!empty($_POST['return'])) goaway(z_root() . "/" . $return_path); killme(); } @@ -771,15 +554,15 @@ class Item extends Controller { if (feature_enabled($profile_uid, 'content_expire')) { - if (x($_REQUEST, 'expire')) { - $expires = datetime_convert(date_default_timezone_get(), 'UTC', $_REQUEST['expire']); + if (!empty($_POST['expire'])) { + $expires = datetime_convert(date_default_timezone_get(), 'UTC', $_POST['expire']); if ($expires <= datetime_convert()) $expires = NULL_DATE; } } - $mimetype = ((isset($_REQUEST['mimetype'])) ? notags(trim($_REQUEST['mimetype'])) : ''); + $mimetype = ((isset($_POST['mimetype'])) ? notags(trim($_POST['mimetype'])) : ''); if (!$mimetype) $mimetype = 'text/bbcode'; @@ -813,7 +596,7 @@ class Item extends Controller { $is_group = get_pconfig($profile_uid, 'system', 'group_actor'); - if ($is_group && $walltowall && !$walltowall_comment && !$webpage) { + if ($is_group && $walltowall && !$walltowall_comment && (intval($item_type) === ITEM_TYPE_POST)) { $groupww = true; $str_contact_allow = $owner_xchan['xchan_hash']; $str_group_allow = ''; @@ -837,8 +620,10 @@ class Item extends Controller { if ($results) { - // Set permissions based on tag replacements - set_linkified_perms($results, $str_contact_allow, $str_group_allow, $profile_uid, $private, $parent_item); + // Set permissions based on tag replacements only if not editing an existing post + if (!$orig_post) { + set_linkified_perms($results, $str_contact_allow, $str_group_allow, $profile_uid, $private, $parent_item); + } foreach ($results as $result) { $success = $result['success']; @@ -1010,42 +795,65 @@ class Item extends Controller { } $item_unseen = ((local_channel() != $profile_uid) ? 1 : 0); - $item_wall = ((isset($_REQUEST['type']) && ($_REQUEST['type'] === 'wall' || $_REQUEST['type'] === 'wall-comment')) ? 1 : 0); + $item_wall = ((isset($_POST['type']) && ($_POST['type'] === 'wall' || $_POST['type'] === 'wall-comment')) ? 1 : 0); $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 + if (in_array($item_type, [ITEM_TYPE_POST, ITEM_TYPE_CARD, ITEM_TYPE_ARTICLE])) { + $item_wall = 1; + } + if ($parent) { $item_wall = $parent_item['item_wall']; } - else { - if (!$webpage) { - $item_wall = 1; - } - } - - if ($moderated) + if ($moderated) { $item_blocked = ITEM_MODERATED; + } - if (!strlen($verb)) + if (!strlen($verb)) { $verb = 'Create'; + } $notify_type = (($parent) ? 'comment-new' : 'wall-new'); $uuid = $uuid ?? $message_id ?? item_message_id(); $mid = $mid ?? z_root() . '/item/' . $uuid; + + if (empty($owner_hash)) { + $owner_hash = $owner_xchan['xchan_hash']; + } + + // Set the conversation target. + if ($owner_hash === $channel['channel_hash']) { + $attributedTo = z_root() . '/channel/' . $channel['channel_address']; + + $conversation = isset($parent_item) ? $parent_item['mid'] : $mid; + $datarray['target'] = [ + 'id' => str_replace('/item/', '/conversation/', $conversation), + 'type' => 'Collection', + 'attributedTo' => $attributedTo, + ]; + $datarray['tgt_type'] = 'Collection'; + } + elseif (!empty($parent_item['target'])) { + $datarray['target'] = $parent_item['target']; + $datarray['tgt_type'] = $parent_item['tgt_type']; + } + if ($is_poll) { $poll = [ 'question' => $body, - 'answers' => $_REQUEST['poll_answers'], - 'multiple_answers' => $_REQUEST['poll_multiple_answers'], - 'expire_value' => $_REQUEST['poll_expire_value'], - 'expire_unit' => $_REQUEST['poll_expire_unit'] + 'answers' => $_POST['poll_answers'], + 'multiple_answers' => $_POST['poll_multiple_answers'], + 'expire_value' => $_POST['poll_expire_value'], + 'expire_unit' => $_POST['poll_expire_unit'] ]; $obj = $this->extract_poll_data($poll, ['item_private' => $private, 'allow_cid' => $str_contact_allow, 'allow_gid' => $str_contact_deny]); } @@ -1058,7 +866,7 @@ class Item extends Controller { $obj['id'] = $mid; $obj['diaspora:guid'] = $uuid; $obj['attributedTo'] = channel_url($channel); - $obj['published'] = $created; + $obj['published'] = datetime_convert('UTC', 'UTC', $created, ATOM_TIME); $obj['name'] = $title; $datarray['obj'] = $obj; @@ -1080,19 +888,13 @@ class Item extends Controller { if ($parent_item) $parent_mid = $parent_item['mid']; - // Fallback so that we always have a thr_parent if (!$thr_parent) $thr_parent = $mid; - $item_thread_top = ((!$parent) ? 1 : 0); - if ((!$plink) && ($item_thread_top)) { - $plink = $mid; - } - if (isset($datarray['obj']) && $datarray['obj']) { $datarray['obj']['id'] = $mid; } @@ -1130,7 +932,7 @@ class Item extends Controller { $datarray['item_unseen'] = intval($item_unseen); $datarray['item_wall'] = intval($item_wall); $datarray['item_origin'] = intval($item_origin); - $datarray['item_type'] = $webpage; + $datarray['item_type'] = $item_type; $datarray['item_private'] = intval($private); $datarray['item_thread_top'] = intval($item_thread_top); $datarray['item_starred'] = intval($item_starred); @@ -1155,7 +957,7 @@ class Item extends Controller { $datarray['public_policy'] = $public_policy; $datarray['comment_policy'] = map_scope($comment_policy); $datarray['term'] = array_unique($post_tags, SORT_REGULAR); - $datarray['plink'] = $plink; + $datarray['plink'] = $plink ?? $mid; $datarray['route'] = $route; // A specific ACL over-rides public_policy completely @@ -1209,14 +1011,14 @@ class Item extends Controller { call_hooks('post_local', $datarray); - if (x($datarray, 'cancel')) { + if (!empty($datarray['cancel'])) { logger('mod_item: post cancelled by plugin or duplicate suppressed.'); if ($return_path) goaway(z_root() . "/" . $return_path); if ($api_source) return (['success' => false, 'message' => 'operation cancelled']); $json = ['cancel' => 1]; - $json['reload'] = z_root() . '/' . $_REQUEST['jsreload']; + $json['reload'] = z_root() . '/' . $_POST['jsreload']; echo json_encode($json); killme(); } @@ -1225,8 +1027,8 @@ class Item extends Controller { if (mb_strlen($datarray['title']) > 191) $datarray['title'] = mb_substr($datarray['title'], 0, 191); - if ($webpage) { - IConfig::Set($datarray, 'system', webpage_to_namespace($webpage), + if (intval($item_type) !== ITEM_TYPE_POST) { + IConfig::Set($datarray, 'system', item_type_to_namespace($item_type), (($pagetitle) ? $pagetitle : basename($datarray['mid'])), true); } elseif ($namespace) { @@ -1243,6 +1045,7 @@ class Item extends Controller { $this->add_listeners($datarray); } + /* sync this is done in item_store_update() if (!$parent) { $r = q("select * from item where id = %d", intval($post_id) @@ -1253,14 +1056,19 @@ class Item extends Controller { Libsync::build_sync_packet($profile_uid, ['item' => [encode_item($sync_item[0], true)]]); } } - if (!$nopush) - Master::Summon(['Notifier', 'edit_post', $post_id]); + */ + if (!$nopush) { + Master::Summon(['Notifier', 'edit_post', $post_id]); + if (intval($x['approval_id'])) { + Master::Summon(['Notifier', 'edit_post', $x['approval_id']]); + } + } if ($api_source) return ($x); - if ((x($_REQUEST, 'return')) && strlen($return_path)) { + if ((!empty($_POST['return'])) && strlen($return_path)) { logger('return: ' . $return_path); if ($return_path === 'hq') { @@ -1279,6 +1087,7 @@ class Item extends Controller { } $post_id = $post['item_id']; + $approval_id = $post['approval_id'] ?? 0; $datarray = $post['item']; @@ -1353,6 +1162,7 @@ class Item extends Controller { killme(); } + /* sync this is done in item_store_update() if ($parent || $datarray['item_private'] == 1) { $r = q("select * from item where id = %d", intval($post_id) @@ -1363,6 +1173,7 @@ class Item extends Controller { Libsync::build_sync_packet($profile_uid, ['item' => [encode_item($sync_item[0], true)]]); } } + */ $datarray['id'] = $post_id; $datarray['llink'] = z_root() . '/display/' . $datarray['uuid']; @@ -1373,8 +1184,12 @@ class Item extends Controller { $nopush = false; } - if (!$nopush) + if (!$nopush) { Master::Summon(['Notifier', $notify_type, $post_id]); + if ($approval_id) { + Master::Summon(['Notifier', $notify_type, $approval_id]); + } + } logger('post_complete'); @@ -1406,11 +1221,12 @@ class Item extends Controller { $json = [ 'success' => 1, 'id' => $post_id, + 'thr_parent_id' => $thr_parent_id, 'html' => conversation($item, $mode, true, 'r_preview'), ]; - if (x($_REQUEST, 'jsreload') && strlen($_REQUEST['jsreload'])) - $json['reload'] = z_root() . '/' . $_REQUEST['jsreload']; + if (!empty($_POST['jsreload'])) + $json['reload'] = z_root() . '/' . $_POST['jsreload']; logger('post_json: ' . print_r($json, true), LOGGER_DEBUG); @@ -1427,10 +1243,7 @@ class Item extends Controller { if ((argc() == 3) && (argv(1) === 'drop') && intval(argv(2))) { - require_once('include/items.php'); - - - $i = q("select id, uid, item_origin, author_xchan, owner_xchan, source_xchan, item_type from item where id = %d limit 1", + $i = q("select * from item where id = %d limit 1", intval(argv(2)) ); @@ -1457,7 +1270,6 @@ class Item extends Controller { $can_delete = true; } - if (!($can_delete || $local_delete)) { notice(t('Permission denied.') . EOL); return; @@ -1473,13 +1285,14 @@ class Item extends Controller { } else { // complex deletion that needs to propagate and be performed in phases - drop_item($i[0]['id'], true, DROPITEM_PHASE1); + drop_item($i[0]['id'], DROPITEM_PHASE1); $complex = true; } $r = q("select * from item where id = %d", intval($i[0]['id']) ); + if ($r) { xchan_query($r); $sync_item = fetch_post_tags($r); @@ -1488,6 +1301,9 @@ class Item extends Controller { if ($complex) { tag_deliver($i[0]['uid'], $i[0]['id']); + if (intval($i[0]['item_wall']) || $i[0]['mid'] !== $i[0]['parent_mid']) { + Master::Summon(['Notifier', 'drop', $i[0]['id']]); + } } } @@ -1671,5 +1487,234 @@ class Item extends Controller { } } + private function init_zot_request() { + + $item_id = argv(1); + + if (!$item_id) + http_status_exit(404, 'Not found'); + + $portable_id = EMPTY_STR; + + $item_normal_extra = sprintf(" and not verb in ('Follow', 'Ignore', '%s', '%s') ", + dbesc(ACTIVITY_FOLLOW), + dbesc(ACTIVITY_UNFOLLOW) + ); + + $item_normal = " and item.item_hidden = 0 and item.item_type = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_blocked = 0 and item.item_uplink = 0 $item_normal_extra "; + + $i = null; + + // do we have the item (at all)? + + $r = q("select parent_mid from item where uuid = '%s' $item_normal limit 1", + dbesc($item_id) + ); + + if (!$r) { + http_status_exit(404, 'Not found'); + } + + // process an authenticated fetch + + $sigdata = HTTPSig::verify(($_SERVER['REQUEST_METHOD'] === 'POST') ? file_get_contents('php://input') : EMPTY_STR); + if ($sigdata['portable_id'] && $sigdata['header_valid']) { + $portable_id = $sigdata['portable_id']; + if (!check_channelallowed($portable_id)) { + http_status_exit(403, 'Permission denied'); + } + if (!check_siteallowed($sigdata['signer'])) { + http_status_exit(403, 'Permission denied'); + } + observer_auth($portable_id); + + $i = q("select id as item_id, uid from item where mid = '%s' $item_normal and owner_xchan = '%s' limit 1", + dbesc($r[0]['parent_mid']), + dbesc($portable_id) + ); + } + elseif (Config::get('system', 'require_authenticated_fetch', false)) { + http_status_exit(403, 'Permission denied'); + } + + // if we don't have a parent id belonging to the signer see if we can obtain one as a visitor that we have permission to access + // with a bias towards those items owned by channels on this site (item_wall = 1) + + $sql_extra = item_permissions_sql(0); + + if (!$i) { + $i = q("select id as item_id, uid, item_private from item where mid = '%s' $item_normal $sql_extra order by item_wall desc limit 1", + dbesc($r[0]['parent_mid']) + ); + } + + if (!$i) { + http_status_exit(403, 'Forbidden'); + } + + $chan = channelx_by_n($i[0]['uid']); + + if (!$chan) { + http_status_exit(404, 'Not found'); + } + + if (!perm_is_allowed($chan['channel_id'], get_observer_hash(), 'view_stream')) { + http_status_exit(403, 'Forbidden'); + } + + $parents_str = ids_to_querystr($i, 'item_id'); + + // We won't need to check for privacy mismatches if the verified observer is also owner + $parent_item_private = ((isset($i[0]['item_private'])) ? " and item_private = " . intval($i[0]['item_private']) . " " : ''); + + $total = q("SELECT count(*) AS count FROM item WHERE parent = %d $parent_item_private $item_normal ", + intval($parents_str) + ); + + App::set_pager_total($total[0]['count']); + App::set_pager_itemspage(30); + + if (App::$pager['total'] > App::$pager['itemspage']) { + // let mod conversation handle this request + App::$query_string = str_replace('item', 'conversation', App::$query_string); + $i = Activity::paged_collection_init(App::$pager['total'], App::$query_string); + as_return_and_die($i ,$chan); + } + else { + $items = q("SELECT item.*, item.id AS item_id FROM item WHERE item.parent = %d $parent_item_private $item_normal ORDER BY item.id", + intval($parents_str) + ); + + xchan_query($items, true); + $items = fetch_post_tags($items, true); + + $i = Activity::encode_item_collection($items, App::$query_string, 'OrderedCollection', App::$pager['total']); + } + + if ($portable_id && (!intval($items[0]['item_private']))) { + $c = q("select abook_id from abook where abook_channel = %d and abook_xchan = '%s'", + intval($items[0]['uid']), + dbesc($portable_id) + ); + if (!$c) { + ThreadListener::store(z_root() . '/item/' . $item_id, $portable_id); + } + } + + as_return_and_die($i ,$chan); + } + + private function init_as_request() { + + $item_id = argv(1); + if (!$item_id) + http_status_exit(404, 'Not found'); + + $portable_id = EMPTY_STR; + + $item_normal_extra = sprintf(" and not verb in ('Follow', 'Ignore', '%s', '%s') ", + dbesc(ACTIVITY_FOLLOW), + dbesc(ACTIVITY_UNFOLLOW) + ); + + $item_normal = " and item.item_hidden = 0 and item.item_type = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_blocked = 0 and item.item_uplink = 0 $item_normal_extra "; + + $i = null; + + // do we have the item (at all)? + // add preferential bias to item owners (item_wall = 1) + + $r = q("select * from item where uuid = '%s' $item_normal order by item_wall desc limit 1", + dbesc($item_id) + ); + + if (!$r) { + http_status_exit(404, 'Not found'); + } + + // process an authenticated fetch + + $sigdata = HTTPSig::verify(EMPTY_STR); + if ($sigdata['portable_id'] && $sigdata['header_valid']) { + $portable_id = $sigdata['portable_id']; + if (!check_channelallowed($portable_id)) { + http_status_exit(403, 'Permission denied'); + } + if (!check_siteallowed($sigdata['signer'])) { + http_status_exit(403, 'Permission denied'); + } + observer_auth($portable_id); + + $i = q("select id as item_id from item where mid = '%s' $item_normal and owner_xchan = '%s' limit 1 ", + dbesc($r[0]['parent_mid']), + dbesc($portable_id) + ); + } + elseif (Config::get('system', 'require_authenticated_fetch', false)) { + http_status_exit(403, 'Permission denied'); + } + + // if we don't have a parent id belonging to the signer see if we can obtain one as a visitor that we have permission to access + // with a bias towards those items owned by channels on this site (item_wall = 1) + + $sql_extra = item_permissions_sql(0); + + if (!$i) { + $i = q("select id as item_id from item where mid = '%s' $item_normal $sql_extra order by item_wall desc limit 1", + dbesc($r[0]['parent_mid']) + ); + } + + $bear = Activity::token_from_request(); + if ($bear) { + logger('bear: ' . $bear, LOGGER_DEBUG); + if (!$i) { + $t = q("select * from iconfig where cat = 'ocap' and k = 'relay' and v = '%s'", + dbesc($bear) + ); + if ($t) { + $i = q("select id as item_id from item where uuid = '%s' and id = %d $item_normal limit 1", + dbesc($item_id), + intval($t[0]['iid']) + ); + } + } + } + + if (!$i) { + http_status_exit(403, 'Forbidden'); + } + + // If we get to this point we have determined we can access the original in $r (fetched much further above), so use it. + + xchan_query($r, true); + $items = fetch_post_tags($r, false); + + $chan = channelx_by_n($items[0]['uid']); + + if (!$chan) + http_status_exit(404, 'Not found'); + + if (!perm_is_allowed($chan['channel_id'], get_observer_hash(), 'view_stream')) + http_status_exit(403, 'Forbidden'); + + $i = Activity::encode_item($items[0]); + + if (!$i) + http_status_exit(404, 'Not found'); + + if ($portable_id && (!intval($items[0]['item_private']))) { + $c = q("select abook_id from abook where abook_channel = %d and abook_xchan = '%s'", + intval($items[0]['uid']), + dbesc($portable_id) + ); + if (!$c) { + ThreadListener::store(z_root() . '/item/' . $item_id, $portable_id); + } + } + + as_return_and_die($i ,$chan); + + } } |