diff options
Diffstat (limited to 'include/items.php')
-rwxr-xr-x | include/items.php | 581 |
1 files changed, 311 insertions, 270 deletions
diff --git a/include/items.php b/include/items.php index 139c637e5..cc209aacf 100755 --- a/include/items.php +++ b/include/items.php @@ -8,6 +8,7 @@ use Zotlabs\Lib as Zlib; require_once('include/bbcode.php'); require_once('include/oembed.php'); require_once('include/crypto.php'); +require_once('include/message.php'); require_once('include/feedutils.php'); require_once('include/photo/photo_driver.php'); require_once('include/permissions.php'); @@ -175,6 +176,19 @@ function item_normal() { and item.item_blocked = 0 "; } +function item_normal_search() { + return " and item.item_hidden = 0 and item.item_type in (0,3,6) and item.item_deleted = 0 + and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0 + and item.item_blocked = 0 "; +} + +function item_normal_update() { + return " and item.item_hidden = 0 and item.item_type = 0 + and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0 + and item.item_blocked = 0 "; +} + + /** * @brief * @@ -212,6 +226,11 @@ function can_comment_on_post($observer_xchan, $item) { // logger('can_comment_on_post: comment_policy: ' . $item['comment_policy'], LOGGER_DEBUG); + $x = [ 'observer_hash' => $observer_xchan, 'item' => $item, 'allowed' => 'unset' ]; + call_hooks('can_comment_on_post',$x); + if($x['allowed'] !== 'unset') + return $x['allowed']; + if(! $observer_xchan) return false; @@ -248,8 +267,6 @@ function can_comment_on_post($observer_xchan, $item) { } if(strstr($item['comment_policy'],'network:') && strstr($item['comment_policy'],'red')) return true; - if(strstr($item['comment_policy'],'network:') && strstr($item['comment_policy'],'diaspora')) - return true; if(strstr($item['comment_policy'],'site:') && strstr($item['comment_policy'],App::get_hostname())) return true; @@ -298,11 +315,13 @@ function add_source_route($iid, $hash) { * or other processing is performed. * * @param array $arr + * @param boolean $allow_code (optional) default false + * @param boolean $deliver (optional) default true * @returns array * * \e boolean \b success true or false * * \e array \b activity the resulting activity if successful */ -function post_activity_item($arr,$allow_code = false,$deliver = true) { +function post_activity_item($arr, $allow_code = false, $deliver = true) { $ret = array('success' => false); @@ -328,25 +347,14 @@ function post_activity_item($arr,$allow_code = false,$deliver = true) { return $ret; } - $arr['public_policy'] = ((x($_REQUEST,'public_policy')) ? escape_tags($_REQUEST['public_policy']) : map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_stream'),true)); + $arr['public_policy'] = ((array_key_exists('public_policy',$arr)) ? escape_tags($arr['public_policy']) : map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_stream'),true)); + if($arr['public_policy']) $arr['item_private'] = 1; if(! array_key_exists('mimetype',$arr)) $arr['mimetype'] = 'text/bbcode'; - if(array_key_exists('item_private',$arr) && $arr['item_private']) { - - $arr['body'] = trim(z_input_filter($arr['uid'],$arr['body'],$arr['mimetype'])); - - if($channel) { - if($channel['channel_hash'] === $arr['author_xchan']) { - $arr['sig'] = base64url_encode(rsa_sign($arr['body'],$channel['channel_prvkey'])); - $arr['item_verified'] = 1; - } - } - } - $arr['mid'] = ((x($arr,'mid')) ? $arr['mid'] : item_message_id()); $arr['parent_mid'] = ((x($arr,'parent_mid')) ? $arr['parent_mid'] : $arr['mid']); $arr['thr_parent'] = ((x($arr,'thr_parent')) ? $arr['thr_parent'] : $arr['mid']); @@ -359,10 +367,13 @@ function post_activity_item($arr,$allow_code = false,$deliver = true) { if(($is_comment) && ($arr['obj_type'] === ACTIVITY_OBJ_NOTE)) $arr['obj_type'] = ACTIVITY_OBJ_COMMENT; - $arr['allow_cid'] = ((x($arr,'allow_cid')) ? $arr['allow_cid'] : $channel['channel_allow_cid']); - $arr['allow_gid'] = ((x($arr,'allow_gid')) ? $arr['allow_gid'] : $channel['channel_allow_gid']); - $arr['deny_cid'] = ((x($arr,'deny_cid')) ? $arr['deny_cid'] : $channel['channel_deny_cid']); - $arr['deny_gid'] = ((x($arr,'deny_gid')) ? $arr['deny_gid'] : $channel['channel_deny_gid']); + if(! ( array_key_exists('allow_cid',$arr) || array_key_exists('allow_gid',$arr) + || array_key_exists('deny_cid',$arr) || array_key_exists('deny_gid',$arr))) { + $arr['allow_cid'] = $channel['channel_allow_cid']; + $arr['allow_gid'] = $channel['channel_allow_gid']; + $arr['deny_cid'] = $channel['channel_deny_cid']; + $arr['deny_gid'] = $channel['channel_deny_gid']; + } $arr['comment_policy'] = map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'post_comments')); @@ -424,7 +435,7 @@ function validate_item_elements($message,$arr) { /** - * @brief Limit lenght on imported system messages. + * @brief Limit length on imported system messages. * * The purpose of this function is to apply system message length limits to * imported messages without including any embedded photos in the length. @@ -538,12 +549,7 @@ function get_item_elements($x,$allow_code = false) { $arr = array(); - if($allow_code) - $arr['body'] = $x['body']; - else - $arr['body'] = (($x['body']) ? htmlspecialchars($x['body'],ENT_COMPAT,'UTF-8',false) : ''); - - $key = get_config('system','pubkey'); + $arr['body'] = $x['body']; $maxlen = get_max_import_size(); @@ -598,11 +604,6 @@ function get_item_elements($x,$allow_code = false) { $arr['sig'] = (($x['signature']) ? htmlspecialchars($x['signature'], ENT_COMPAT,'UTF-8',false) : ''); - if(array_key_exists('diaspora_signature',$x) && is_array($x['diaspora_signature'])) - $x['diaspora_signature'] = json_encode($x['diaspora_signature']); - - $arr['diaspora_meta'] = (($x['diaspora_signature']) ? $x['diaspora_signature'] : ''); - $arr['obj'] = activity_sanitise($x['object']); $arr['target'] = activity_sanitise($x['target']); @@ -625,6 +626,9 @@ function get_item_elements($x,$allow_code = false) { if(in_array('notshown',$x['flags'])) $arr['item_notshown'] = 1; + if(in_array('obscured',$x['flags'])) + $arr['item_obscured'] = 1; + // hidden item are no longer propagated - notshown may be a suitable alternative if(in_array('hidden',$x['flags'])) @@ -645,26 +649,62 @@ function get_item_elements($x,$allow_code = false) { return array(); // save a potentially expensive lookup if author == owner + if($arr['author_xchan'] === make_xchan_hash($x['owner']['guid'],$x['owner']['guid_sig'])) $arr['owner_xchan'] = $arr['author_xchan']; else { $xchan_hash = import_author_xchan($x['owner']); - if($xchan_hash) + if($xchan_hash) { $arr['owner_xchan'] = $xchan_hash; - else + } + else { return array(); + } } + // Check signature on the body text received. + // This presents an issue that we aren't verifying the text that is actually displayed + // on this site. We are however verifying the received text was exactly as received. + // We have every right to strip content that poses a security risk. You are welcome to + // create a plugin to verify the content after filtering if this offends you. + if($arr['sig']) { + + // check the supplied signature against the supplied content. + // Note that we will purify the content which could change it. + $r = q("select xchan_pubkey from xchan where xchan_hash = '%s' limit 1", dbesc($arr['author_xchan']) ); - if($r && rsa_verify($x['body'],base64url_decode($arr['sig']),$r[0]['xchan_pubkey'])) - $arr['item_verified'] = 1; - else - logger('get_item_elements: message verification failed.'); + if($r) { + if($r[0]['xchan_pubkey']) { + if(rsa_verify($x['body'],base64url_decode($arr['sig']),$r[0]['xchan_pubkey'])) { + $arr['item_verified'] = 1; + } + else { + logger('get_item_elements: message verification failed.'); + } + } + else { + + // If we don't have a public key, strip the signature so it won't show as invalid. + // This won't happen in normal use, but could happen if import_author_xchan() + // failed to load the zot-info packet due to a server failure and had + // to create an alternate xchan with network 'unknown' + + unset($arr['sig']); + } + } } + // if the input is markdown, remove one level of html escaping. + // It will be re-applied in item_store() and/or item_store_update(). + // Do this after signature checking as the original signature + // was generated on the escaped content. + + if($arr['mimetype'] === 'text/markdown') + $arr['body'] = \Zotlabs\Lib\MarkdownSoap::unescape($arr['body']); + if(array_key_exists('revision',$x)) { // extended export encoding @@ -691,7 +731,7 @@ function get_item_elements($x,$allow_code = false) { // local only $arr['item_relay'] = $x['item_relay']; $arr['item_mentionsme'] = $x['item_mentionsme']; $arr['item_nocomment'] = $x['item_nocomment']; - // local only $arr['item_obscured'] = $x['item_obscured']; + $arr['item_obscured'] = $x['item_obscured']; // local only $arr['item_verified'] = $x['item_verified']; $arr['item_retained'] = $x['item_retained']; $arr['item_rss'] = $x['item_rss']; @@ -764,51 +804,26 @@ function import_author_xchan($x) { if($arr['xchan_hash']) return $arr['xchan_hash']; + $y = false; + if((! array_key_exists('network', $x)) || ($x['network'] === 'zot')) { $y = import_author_zot($x); } - if(! $y) - $y = import_author_diaspora($x); + + // if we were told that it's a zot connection, don't probe/import anything else + if(array_key_exists('network',$x) && $x['network'] === 'zot') + return $y; if($x['network'] === 'rss') { $y = import_author_rss($x); } - if($x['network'] === 'unknown') { + if(! $y) { $y = import_author_unknown($x); } - return(($y) ? $y : false); -} - -/** - * @brief Imports an author from Diaspora. - * - * @param array $x an associative array with - * * \e string \b address - * @return boolean|string false on error, otherwise xchan_hash of the new entry - */ -function import_author_diaspora($x) { - if(! $x['address']) - return false; - - $r = q("select * from xchan where xchan_addr = '%s' limit 1", - dbesc($x['address']) - ); - if($r) { - logger('in_cache: ' . $x['address'], LOGGER_DATA); - return $r[0]['xchan_hash']; - } - - if(discover_by_webbie($x['address'])) { - $r = q("select xchan_hash from xchan where xchan_addr = '%s' limit 1", - dbesc($x['address']) - ); - if($r) - return $r[0]['xchan_hash']; - } + return($y); - return false; } /** @@ -820,6 +835,7 @@ function import_author_diaspora($x) { * * \e string \b guid * @return boolean|string */ + function import_author_rss($x) { if(! $x['url']) return false; @@ -867,6 +883,11 @@ function import_author_rss($x) { function import_author_unknown($x) { + $arr = [ 'author' => $x, 'result' => false ]; + call_hooks('import_author', $arr); + if($arr['result']) + return $arr['result']; + if(! $x['url']) return false; @@ -896,7 +917,7 @@ function import_author_unknown($x) { $photos = import_xchan_photo($x['photo']['src'],$x['url']); if($photos) { - $r = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_url = '%s' and xchan_network = 'unknown'", + $r = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s' and xchan_network = 'unknown'", dbesc(datetime_convert()), dbesc($photos[0]), dbesc($photos[1]), @@ -936,13 +957,6 @@ function encode_item($item,$mirror = false) { $key = get_config('system','prvkey'); - if(array_key_exists('item_obscured',$item) && intval($item['item_obscured'])) { - if($item['title']) - $item['title'] = crypto_unencapsulate(json_decode($item['title'],true),$key); - if($item['body']) - $item['body'] = crypto_unencapsulate(json_decode($item['body'],true),$key); - } - // If we're trying to backup an item so that it's recoverable or for export/imprt, // add all the attributes we need to recover it @@ -1033,17 +1047,7 @@ function encode_item($item,$mirror = false) { if($item['iconfig']) $x['meta'] = encode_item_meta($item['iconfig'],$mirror); - if($item['diaspora_meta']) { - $z = json_decode($item['diaspora_meta'],true); - if($z) { - if(is_array($z) && array_key_exists('iv',$z)) - $x['diaspora_signature'] = crypto_unencapsulate($z,$key); - else - $x['diaspora_signature'] = $z; - if(! is_array($z)) - logger('encode_item: diaspora meta is not an array: ' . print_r($z,true)); - } - } + logger('encode_item: ' . print_r($x,true), LOGGER_DATA); return $x; @@ -1116,7 +1120,7 @@ function encode_item_xchan($xchan) { $ret['address'] = $xchan['xchan_addr']; $ret['url'] = $xchan['xchan_url']; $ret['network'] = $xchan['xchan_network']; - $ret['photo'] = array('mimetype' => $xchan['xchan_photo_mimetype'], 'src' => $xchan['xchan_photo_m']); + $ret['photo'] = [ 'mimetype' => $xchan['xchan_photo_mimetype'], 'src' => $xchan['xchan_photo_m'] ]; $ret['guid'] = $xchan['xchan_guid']; $ret['guid_sig'] = $xchan['xchan_guid_sig']; @@ -1126,7 +1130,7 @@ function encode_item_xchan($xchan) { function encode_item_terms($terms,$mirror = false) { $ret = array(); - $allowed_export_terms = array( TERM_UNKNOWN, TERM_HASHTAG, TERM_MENTION, TERM_CATEGORY, TERM_BOOKMARK, TERM_COMMUNITYTAG ); + $allowed_export_terms = array( TERM_UNKNOWN, TERM_HASHTAG, TERM_MENTION, TERM_CATEGORY, TERM_BOOKMARK, TERM_COMMUNITYTAG, TERM_FORUM ); if($mirror) { $allowed_export_terms[] = TERM_PCATEGORY; @@ -1174,7 +1178,7 @@ function decode_item_meta($meta) { * @return string */ function termtype($t) { - $types = array('unknown','hashtag','mention','category','private_category','file','search','thing','bookmark', 'hierarchy', 'communitytag'); + $types = array('unknown','hashtag','mention','category','private_category','file','search','thing','bookmark', 'hierarchy', 'communitytag', 'forum'); return(($types[$t]) ? $types[$t] : 'unknown'); } @@ -1223,6 +1227,9 @@ function decode_tags($t) { case 'communitytag': $tag['ttype'] = TERM_COMMUNITYTAG; break; + case 'forum': + $tag['ttype'] = TERM_FORUM; + break; default: case 'unknown': $tag['ttype'] = TERM_UNKNOWN; @@ -1300,6 +1307,8 @@ function encode_item_flags($item) { $ret[] = 'nsfw'; if(intval($item['item_consensus'])) $ret[] = 'consensus'; + if(intval($item['item_obscured'])) + $ret[] = 'obscured'; if(intval($item['item_private'])) $ret[] = 'private'; @@ -1322,11 +1331,12 @@ function encode_mail($item,$extended = false) { $x['message_parent'] = $item['parent_mid']; $x['created'] = $item['created']; $x['expires'] = $item['expires']; - $x['diaspora_meta'] = $item['diaspora_meta']; $x['title'] = $item['title']; $x['body'] = $item['body']; $x['from'] = encode_item_xchan($item['from']); $x['to'] = encode_item_xchan($item['to']); + $x['raw'] = $item['mail_raw']; + $x['mimetype'] = $item['mail_mimetype']; if($item['attach']) $x['attach'] = json_decode($item['attach'],true); @@ -1360,9 +1370,16 @@ function get_mail_elements($x) { $arr = array(); - $arr['body'] = (($x['body']) ? htmlspecialchars($x['body'], ENT_COMPAT,'UTF-8',false) : ''); - $arr['title'] = (($x['title'])? htmlspecialchars($x['title'],ENT_COMPAT,'UTF-8',false) : ''); + if(intval($x['raw'])) { + $arr['mail_raw'] = intval($x['raw']); + $arr['body'] = $x['body']; + } + else { + $arr['body'] = (($x['body']) ? htmlspecialchars($x['body'], ENT_COMPAT,'UTF-8',false) : ''); + } + $arr['title'] = (($x['title'])? htmlspecialchars($x['title'],ENT_COMPAT,'UTF-8',false) : ''); + $arr['mail_mimetype'] = (($x['mimetype']) ? htmlspecialchars($x['mimetype'],ENT_COMPAT,'UTF-8',false) : 'text/bbcode'); $arr['conv_guid'] = (($x['conv_guid'])? htmlspecialchars($x['conv_guid'],ENT_COMPAT,'UTF-8',false) : ''); $arr['created'] = datetime_convert('UTC','UTC',$x['created']); @@ -1452,6 +1469,26 @@ function get_profile_elements($x) { } + + +function item_sign(&$item) { + + if(array_key_exists('sig',$item) && $item['sig']) + return; + + $r = q("select channel_prvkey from channel where channel_id = %d and channel_hash = '%s' ", + intval($item['uid']), + dbesc($item['author_xchan']) + ); + if(! $r) + return; + + $item['sig'] = base64url_encode(rsa_sign($item['body'],$r[0]['channel_prvkey'])); + $item['item_verified'] = 1; + +} + + /** * @brief * @@ -1475,7 +1512,7 @@ function item_store($arr, $allow_exec = false, $deliver = true) { if(array_key_exists('cancel',$arr) && $arr['cancel']) { logger('cancelled by plugin'); return $ret; - } + } if(! $arr['uid']) { logger('item_store: no uid'); @@ -1515,7 +1552,6 @@ function item_store($arr, $allow_exec = false, $deliver = true) { $arr['title'] = ((array_key_exists('title',$arr) && strlen($arr['title'])) ? trim($arr['title']) : ''); $arr['body'] = ((array_key_exists('body',$arr) && strlen($arr['body'])) ? trim($arr['body']) : ''); - $arr['diaspora_meta'] = ((x($arr,'diaspora_meta')) ? $arr['diaspora_meta'] : ''); $arr['allow_cid'] = ((x($arr,'allow_cid')) ? trim($arr['allow_cid']) : ''); $arr['allow_gid'] = ((x($arr,'allow_gid')) ? trim($arr['allow_gid']) : ''); $arr['deny_cid'] = ((x($arr,'deny_cid')) ? trim($arr['deny_cid']) : ''); @@ -1529,35 +1565,30 @@ function item_store($arr, $allow_exec = false, $deliver = true) { // obsolete, but needed so as not to throw not-null constraints on some database driveres $arr['item_flags'] = ((x($arr,'item_flags')) ? intval($arr['item_flags']) : 0 ); - // only detect language if we have text content, and if the post is private but not yet - // obscured, make it so. - if((! array_key_exists('item_obscured',$arr)) || $arr['item_obscured'] == 0) { - $arr['lang'] = detect_language($arr['body']); - // apply the input filter here - if it is obscured it has been filtered already - $arr['body'] = trim(z_input_filter($arr['uid'],$arr['body'],$arr['mimetype'])); + $arr['lang'] = detect_language($arr['body']); - if(local_channel() && (local_channel() == $arr['uid']) && (! $arr['sig'])) { - $channel = App::get_channel(); - if($channel['channel_hash'] === $arr['author_xchan']) { - $arr['sig'] = base64url_encode(rsa_sign($arr['body'],$channel['channel_prvkey'])); - $arr['item_verified'] = 1; - } - } + // apply the input filter here - $allowed_languages = get_pconfig($arr['uid'],'system','allowed_languages'); + $arr['body'] = trim(z_input_filter($arr['body'],$arr['mimetype'],$allow_exec)); - if((is_array($allowed_languages)) && ($arr['lang']) && (! array_key_exists($arr['lang'],$allowed_languages))) { - $translate = array('item' => $arr, 'from' => $arr['lang'], 'to' => $allowed_languages, 'translated' => false); - call_hooks('item_translate', $translate); - if((! $translate['translated']) && (intval(get_pconfig($arr['uid'],'system','reject_disallowed_languages')))) { - logger('item_store: language ' . $arr['lang'] . ' not accepted for uid ' . $arr['uid']); - $ret['message'] = 'language not accepted'; - return $ret; - } - $arr = $translate['item']; + item_sign($arr); + + if(! array_key_exists('sig',$arr)) + $arr['sig'] = ''; + + $allowed_languages = get_pconfig($arr['uid'],'system','allowed_languages'); + + if((is_array($allowed_languages)) && ($arr['lang']) && (! array_key_exists($arr['lang'],$allowed_languages))) { + $translate = array('item' => $arr, 'from' => $arr['lang'], 'to' => $allowed_languages, 'translated' => false); + call_hooks('item_translate', $translate); + if((! $translate['translated']) && (intval(get_pconfig($arr['uid'],'system','reject_disallowed_languages')))) { + logger('item_store: language ' . $arr['lang'] . ' not accepted for uid ' . $arr['uid']); + $ret['message'] = 'language not accepted'; + return $ret; } + $arr = $translate['item']; } if((x($arr,'obj')) && is_array($arr['obj'])) { @@ -1578,7 +1609,6 @@ function item_store($arr, $allow_exec = false, $deliver = true) { $arr['aid'] = ((x($arr,'aid')) ? intval($arr['aid']) : 0); $arr['mid'] = ((x($arr,'mid')) ? notags(trim($arr['mid'])) : random_string()); $arr['revision'] = ((x($arr,'revision') && intval($arr['revision']) > 0) ? intval($arr['revision']) : 0); -logger('revision: ' . $arr['revision']); $arr['author_xchan'] = ((x($arr,'author_xchan')) ? notags(trim($arr['author_xchan'])) : ''); $arr['owner_xchan'] = ((x($arr,'owner_xchan')) ? notags(trim($arr['owner_xchan'])) : ''); @@ -1715,7 +1745,7 @@ logger('revision: ' . $arr['revision']); if($r[0]['owner_xchan'] !== $arr['owner_xchan']) { $arr['owner_xchan'] = $r[0]['owner_xchan']; -// $uplinked_comment = true; + // $uplinked_comment = true; } // if the parent is private, force privacy for the entire conversation @@ -1804,9 +1834,12 @@ logger('revision: ' . $arr['revision']); intval($arr['revision']) ); - if($r && count($r)) { + if($r) { + // This will gives us a fresh copy of what's now in the DB and undo the db escaping, + // which really messes up the notifications + $current_post = $r[0]['id']; - $arr = $r[0]; // This will gives us a fresh copy of what's now in the DB and undo the db escaping, which really messes up the notifications + $arr = $r[0]; logger('item_store: created item ' . $current_post, LOGGER_DEBUG); } else { @@ -1865,7 +1898,7 @@ logger('revision: ' . $arr['revision']); // update the commented timestamp on the parent - unless this is potentially a clone of an older item // which we don't wish to bring to the surface. As the queue only holds deliveries for 3 days, it's - // suspected of being an older cloned item if the creation time is older than that. + // suspected of being an older cloned item if the creation time is older than that. if($arr['created'] > datetime_convert('','','now - 4 days')) { $z = q("select max(created) as commented from item where parent_mid = '%s' and uid = %d and item_delayed = 0 ", @@ -1911,7 +1944,7 @@ function item_store_update($arr,$allow_exec = false, $deliver = true) { if(array_key_exists('cancel',$arr) && $arr['cancel']) { logger('cancelled by plugin'); return $ret; - } + } if(! intval($arr['uid'])) { logger('item_store_update: no uid'); @@ -1953,46 +1986,38 @@ function item_store_update($arr,$allow_exec = false, $deliver = true) { return $ret; } - if((! array_key_exists('item_obscured', $arr)) || $arr['item_obscured'] == 0) { + $arr['lang'] = detect_language($arr['body']); - $arr['lang'] = detect_language($arr['body']); + // apply the input filter here - // apply the input filter here - if it is obscured it has been filtered already - $arr['body'] = trim(z_input_filter($arr['uid'],$arr['body'],$arr['mimetype'])); + $arr['body'] = trim(z_input_filter($arr['body'],$arr['mimetype'],$allow_exec)); - if(local_channel() && (local_channel() == $arr['uid']) && (! $arr['sig'])) { - $channel = App::get_channel(); - if($channel['channel_hash'] === $arr['author_xchan']) { - $arr['sig'] = base64url_encode(rsa_sign($arr['body'],$channel['channel_prvkey'])); - $arr['item_verified'] = 1; - } - } + item_sign($arr); - $allowed_languages = get_pconfig($arr['uid'],'system','allowed_languages'); + $allowed_languages = get_pconfig($arr['uid'],'system','allowed_languages'); - if((is_array($allowed_languages)) && ($arr['lang']) && (! array_key_exists($arr['lang'],$allowed_languages))) { - $translate = array('item' => $arr, 'from' => $arr['lang'], 'to' => $allowed_languages, 'translated' => false); - call_hooks('item_translate', $translate); - if((! $translate['translated']) && (intval(get_pconfig($arr['uid'],'system','reject_disallowed_languages')))) { - logger('item_store: language ' . $arr['lang'] . ' not accepted for uid ' . $arr['uid']); - $ret['message'] = 'language not accepted'; - return $ret; - } - $arr = $translate['item']; + if((is_array($allowed_languages)) && ($arr['lang']) && (! array_key_exists($arr['lang'],$allowed_languages))) { + $translate = array('item' => $arr, 'from' => $arr['lang'], 'to' => $allowed_languages, 'translated' => false); + call_hooks('item_translate', $translate); + if((! $translate['translated']) && (intval(get_pconfig($arr['uid'],'system','reject_disallowed_languages')))) { + logger('item_store: language ' . $arr['lang'] . ' not accepted for uid ' . $arr['uid']); + $ret['message'] = 'language not accepted'; + return $ret; } + $arr = $translate['item']; } - if((x($arr,'obj')) && is_array($arr['obj'])) { + if((array_key_exists('obj',$arr)) && is_array($arr['obj'])) { activity_sanitise($arr['obj']); $arr['obj'] = json_encode($arr['obj']); } - if((x($arr,'target')) && is_array($arr['target'])) { + if((array_key_exists('target',$arr)) && is_array($arr['target'])) { activity_sanitise($arr['target']); $arr['target'] = json_encode($arr['target']); } - if((x($arr,'attach')) && is_array($arr['attach'])) { + if((array_key_exists('attach',$arr)) && is_array($arr['attach'])) { activity_sanitise($arr['attach']); $arr['attach'] = json_encode($arr['attach']); } @@ -2025,7 +2050,7 @@ function item_store_update($arr,$allow_exec = false, $deliver = true) { $arr['changed'] = $orig[0]['changed']; $arr['route'] = ((array_key_exists('route',$arr)) ? trim($arr['route']) : $orig[0]['route']); - $arr['diaspora_meta'] = ((x($arr,'diaspora_meta')) ? $arr['diaspora_meta'] : $orig[0]['diaspora_meta']); + $arr['location'] = ((x($arr,'location')) ? notags(trim($arr['location'])) : $orig[0]['location']); $arr['coord'] = ((x($arr,'coord')) ? notags(trim($arr['coord'])) : $orig[0]['coord']); $arr['verb'] = ((x($arr,'verb')) ? notags(trim($arr['verb'])) : $orig[0]['verb']); @@ -2184,55 +2209,6 @@ function item_store_update($arr,$allow_exec = false, $deliver = true) { -function store_diaspora_comment_sig($datarray, $channel, $parent_item, $post_id, $walltowall = false) { - - // We won't be able to sign Diaspora comments for authenticated visitors - // - we don't have their private key - - // since Diaspora doesn't handle edits we can only do this for the original text and not update it. - - require_once('include/markdown.php'); - $signed_body = bb2diaspora_itembody($datarray,$walltowall); - - if($walltowall) { - logger('wall to wall comment',LOGGER_DEBUG); - // post will come across with the owner's identity. Throw a preamble onto the post to indicate the true author. - $signed_body = "\n\n" - . '![' . $datarray['author']['xchan_name'] . '](' . $datarray['author']['xchan_photo_m'] . ')' - . '[' . $datarray['author']['xchan_name'] . '](' . $datarray['author']['xchan_url'] . ')' . "\n\n" - . $signed_body; - } - - logger('storing diaspora comment signature',LOGGER_DEBUG); - - $diaspora_handle = channel_reddress($channel); - - $signed_text = $datarray['mid'] . ';' . $parent_item['mid'] . ';' . $signed_body . ';' . $diaspora_handle; - - - if( $channel && $channel['channel_prvkey'] ) - $authorsig = base64_encode(rsa_sign($signed_text, $channel['channel_prvkey'], 'sha256')); - else - $authorsig = ''; - - $x = array('signer' => $diaspora_handle, 'body' => $signed_body, 'signed_text' => $signed_text, 'signature' => $authorsig); - - $y = json_encode($x); - - $r = q("update item set diaspora_meta = '%s' where id = %d", - dbesc($y), - intval($post_id) - ); - - - if(! $r) - logger('store_diaspora_comment_sig: DB write failed'); - - return; -} - - - function send_status_notifications($post_id,$item) { // only send notifications for comments @@ -2484,7 +2460,7 @@ function tag_deliver($uid, $item_id) { * Now we've got those out of the way. Let's see if this is a post that's tagged for re-delivery */ - $terms = get_terms_oftype($item['term'],TERM_MENTION); + $terms = array_merge(get_terms_oftype($item['term'],TERM_MENTION),get_terms_oftype($item['term'],TERM_FORUM)); if($terms) logger('tag_deliver: post mentions: ' . print_r($terms,true), LOGGER_DATA); @@ -2511,15 +2487,7 @@ function tag_deliver($uid, $item_id) { // Now let's check if this mention was inside a reshare so we don't spam a forum // If it's private we may have to unobscure it momentarily so that we can parse it. - $body = ''; - - if(intval($item['item_obscured'])) { - $key = get_config('system','prvkey'); - if($item['body']) - $body = crypto_unencapsulate(json_decode($item['body'],true),$key); - } - else - $body = $item['body']; + $body = $item['body']; $body = preg_replace('/\[share(.*?)\[\/share\]/','',$body); @@ -2527,22 +2495,46 @@ function tag_deliver($uid, $item_id) { $plustagged = false; $matches = array(); - $pattern = '/@\!?\[zrl\=' . preg_quote($term['url'],'/') . '\]' . preg_quote($term['term'],'/') . '\[\/zrl\]/'; + $pattern = '/[\!@]\!?\[zrl\=' . preg_quote($term['url'],'/') . '\]' . preg_quote($term['term'],'/') . '\[\/zrl\]/'; if(preg_match($pattern,$body,$matches)) $tagged = true; - $pattern = '/@\!?\[zrl\=([^\]]*?)\]((?:.(?!\[zrl\=))*?)\+\[\/zrl\]/'; + // original red forum tagging sequence @forumname+ + // standard forum tagging sequence !forumname + + $pluspattern = '/@\!?\[zrl\=([^\]]*?)\]((?:.(?!\[zrl\=))*?)\+\[\/zrl\]/'; + + $forumpattern = '/\!\!?\[zrl\=([^\]]*?)\]((?:.(?!\[zrl\=))*?)\[\/zrl\]/'; + + $found = false; - if(preg_match_all($pattern,$body,$matches,PREG_SET_ORDER)) { - $max_forums = get_config('system','max_tagged_forums'); - if(! $max_forums) - $max_forums = 2; - $matched_forums = 0; + $max_forums = get_config('system','max_tagged_forums'); + if(! $max_forums) + $max_forums = 2; + $matched_forums = 0; + $matches = array(); + + if(preg_match_all($pluspattern,$body,$matches,PREG_SET_ORDER)) { foreach($matches as $match) { $matched_forums ++; if($term['url'] === $match[1] && $term['term'] === $match[2]) { if($matched_forums <= $max_forums) { $plustagged = true; + $found = true; + break; + } + logger('forum ' . $term['term'] . ' exceeded max_tagged_forums - ignoring'); + } + } + } + + if(preg_match_all($forumpattern,$body,$matches,PREG_SET_ORDER)) { + foreach($matches as $match) { + $matched_forums ++; + if($term['url'] === $match[1] && $term['term'] === $match[2]) { + if($matched_forums <= $max_forums) { + $plustagged = true; + $found = true; break; } logger('forum ' . $term['term'] . ' exceeded max_tagged_forums - ignoring'); @@ -2641,7 +2633,8 @@ function tgroup_check($uid,$item) { if(! $u) return false; - $terms = get_terms_oftype($item['term'],TERM_MENTION); + + $terms = array_merge(get_terms_oftype($item['term'],TERM_MENTION),get_terms_oftype($item['term'],TERM_FORUM)); if($terms) logger('tgroup_check: post mentions: ' . print_r($terms,true), LOGGER_DATA); @@ -2670,25 +2663,36 @@ function tgroup_check($uid,$item) { $body = $item['body']; - if(array_key_exists('item_obscured',$item) && intval($item['item_obscured']) && $body) { - $key = get_config('system','prvkey'); - $body = crypto_unencapsulate(json_decode($body,true),$key); - } - $body = preg_replace('/\[share(.*?)\[\/share\]/','',$body); -// $pattern = '/@\!?\[zrl\=' . preg_quote($term['url'],'/') . '\]' . preg_quote($term['term'] . '+','/') . '\[\/zrl\]/'; - $pattern = '/@\!?\[zrl\=([^\]]*?)\]((?:.(?!\[zrl\=))*?)\+\[\/zrl\]/'; + $pluspattern = '/@\!?\[zrl\=([^\]]*?)\]((?:.(?!\[zrl\=))*?)\+\[\/zrl\]/'; + + $forumpattern = '/\!\!?\[zrl\=([^\]]*?)\]((?:.(?!\[zrl\=))*?)\[\/zrl\]/'; + $found = false; + + $max_forums = get_config('system','max_tagged_forums'); + if(! $max_forums) + $max_forums = 2; + $matched_forums = 0; $matches = array(); - if(preg_match_all($pattern,$body,$matches,PREG_SET_ORDER)) { - $max_forums = get_config('system','max_tagged_forums'); - if(! $max_forums) - $max_forums = 2; - $matched_forums = 0; + if(preg_match_all($pluspattern,$body,$matches,PREG_SET_ORDER)) { + foreach($matches as $match) { + $matched_forums ++; + if($term['url'] === $match[1] && $term['term'] === $match[2]) { + if($matched_forums <= $max_forums) { + $found = true; + break; + } + logger('forum ' . $term['term'] . ' exceeded max_tagged_forums - ignoring'); + } + } + } + + if(preg_match_all($forumpattern,$body,$matches,PREG_SET_ORDER)) { foreach($matches as $match) { $matched_forums ++; if($term['url'] === $match[1] && $term['term'] === $match[2]) { @@ -2768,7 +2772,6 @@ function start_delivery_chain($channel, $item, $item_id, $parent) { $item_origin = 1; $item_uplink = 0; $item_nocomment = 0; - $item_obscured = 0; $flag_bits = $item['item_flags']; @@ -2791,11 +2794,10 @@ function start_delivery_chain($channel, $item, $item_id, $parent) { $title = $item['title']; $body = $item['body']; - $r = q("update item set item_uplink = %d, item_nocomment = %d, item_obscured = %d, item_flags = %d, owner_xchan = '%s', allow_cid = '%s', allow_gid = '%s', + $r = q("update item set item_uplink = %d, item_nocomment = %d, item_flags = %d, owner_xchan = '%s', allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s', item_private = %d, public_policy = '%s', comment_policy = '%s', title = '%s', body = '%s', item_wall = %d, item_origin = %d where id = %d", intval($item_uplink), intval($item_nocomment), - intval($item_obscured), intval($flag_bits), dbesc($channel['channel_hash']), dbesc($channel['channel_allow_cid']), @@ -2980,13 +2982,21 @@ function mail_store($arr) { return 0; } + $channel = channelx_by_n($arr['channel_id']); + if(! $arr['mail_obscured']) { if((strpos($arr['body'],'<') !== false) || (strpos($arr['body'],'>') !== false)) $arr['body'] = escape_tags($arr['body']); } - if(array_key_exists('attach',$arr) && is_array($arr['attach'])) - $arr['attach'] = json_encode($arr['attach']); + if(array_key_exists('attach',$arr)) { + if(is_array($arr['attach'])) { + $arr['attach'] = json_encode($arr['attach']); + } + } + else { + $arr['attach'] = ''; + } $arr['account_id'] = ((x($arr,'account_id')) ? intval($arr['account_id']) : 0); $arr['mid'] = ((x($arr,'mid')) ? notags(trim($arr['mid'])) : random_string()); @@ -2997,15 +3007,41 @@ function mail_store($arr) { $arr['title'] = ((x($arr,'title')) ? trim($arr['title']) : ''); $arr['parent_mid'] = ((x($arr,'parent_mid')) ? notags(trim($arr['parent_mid'])) : ''); $arr['body'] = ((x($arr,'body')) ? trim($arr['body']) : ''); + $arr['sig'] = ((x($arr,'sig')) ? trim($arr['sig']) : ''); $arr['conv_guid'] = ((x($arr,'conv_guid')) ? trim($arr['conv_guid']) : ''); + $arr['mail_mimetype'] = ((x($arr,'mail_mimetype')) ? trim($arr['mail_mimetype']) : 'text/bbcode'); $arr['mail_flags'] = ((x($arr,'mail_flags')) ? intval($arr['mail_flags']) : 0 ); + $arr['mail_raw'] = ((x($arr,'mail_raw')) ? intval($arr['mail_raw']) : 0 ); + + - if(! $arr['parent_mid']) { + if($arr['parent_mid']) { + $parent_item = q("select * from mail where mid = '%s' and channel_id = %d limit 1", + dbesc($arr['parent_mid']), + intval($arr['channel_id']) + ); + if(($parent_item) && (! $arr['conv_guid'])) { + $arr['conv_guid'] = $parent_item[0]['conv_guid']; + } + } + else { logger('mail_store: missing parent'); $arr['parent_mid'] = $arr['mid']; } + if($arr['from_xchan'] === $channel['channel_hash']) + $conversant = $arr['to_xchan']; + else + $conversant = $arr['from_xchan']; + + + if(! $arr['conv_guid']) { + $x = create_conversation($channel,$conversant,(($arr['title']) ? base64url_decode(str_rot47($arr['title'])) : '')); + $arr['conv_guid'] = (($x) ? $x['guid'] : ''); + } + + $r = q("SELECT id FROM mail WHERE mid = '%s' AND channel_id = %d LIMIT 1", dbesc($arr['mid']), intval($arr['channel_id']) @@ -3070,6 +3106,14 @@ function mail_store($arr) { Zlib\Enotify::submit($notif_params); } + if($arr['conv_guid']) { + $c = q("update conv set updated = '%s' where guid = '%s' and uid = %d", + dbesc(datetime_convert()), + dbesc($arr['conv_guid']), + intval($arr['channel_id']) + ); + } + call_hooks('post_mail_end',$arr); return $current_post; } @@ -3769,7 +3813,7 @@ function zot_feed($uid,$observer_hash,$arr) { if(! is_sys_channel($uid)) $sql_extra = item_permissions_sql($uid,$observer_hash); - $limit = " LIMIT 100 "; + $limit = " LIMIT 5000 "; if($mindate > NULL_DATE) { $sql_extra .= " and ( created > '$mindate' or changed > '$mindate' ) "; @@ -3781,15 +3825,7 @@ function zot_feed($uid,$observer_hash,$arr) { } - $items = array(); - - /** @FIXME re-unite these SQL statements. There is no need for them to be separate. The mySQL is convoluted with misuse of group by. As it stands, there is a slight difference where the postgres version doesn't remove the duplicate parents up to 100. In practice this doesn't matter. It could be made to match behavior by adding "distinct on (parent) " to the front of the selection list, at a not-worth-it performance penalty (page temp results to disk). duplicates are still ignored in the in() clause, you just get less than 100 parents if there are many children. */ - - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $groupby = ''; - } else { - $groupby = 'GROUP BY parent'; - } + $items = []; $item_normal = item_normal(); @@ -3798,7 +3834,7 @@ function zot_feed($uid,$observer_hash,$arr) { WHERE uid != %d $item_normal AND item_wall = 1 - and item_private = 0 $sql_extra $groupby ORDER BY created ASC $limit", + and item_private = 0 $sql_extra ORDER BY created ASC $limit", intval($uid) ); } @@ -3806,19 +3842,25 @@ function zot_feed($uid,$observer_hash,$arr) { $r = q("SELECT parent, created, postopts from item WHERE uid = %d $item_normal AND item_wall = 1 - $sql_extra $groupby ORDER BY created ASC $limit", + $sql_extra ORDER BY created ASC $limit", intval($uid) ); } + $parents = []; + if($r) { - for($x = 0; $x < count($r); $x ++) { - if(strpos($r[$x]['postopts'],'nodeliver') !== false) { - unset($r[$x]); - } + foreach($r as $rv) { + if(array_key_exists($rv['parent'],$parents)) + continue; + if(strpos($rv['postopts'],'nodeliver') !== false) + continue; + $parents[$rv['parent']] = $rv; + if(count($parents) > 200) + break; } - $parents_str = ids_to_querystr($r,'parent'); + $parents_str = ids_to_querystr($parents,'parent'); $sys_query = ((is_sys_channel($uid)) ? $sql_extra : ''); $item_normal = item_normal(); @@ -3936,6 +3978,10 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C } } + if($channel && intval($arr['compat']) === 1) { + $sql_extra = " AND author_xchan = '" . $channel['channel_hash'] . "' and item_private = 0 "; + } + if ($arr['datequery']) { $sql_extra3 .= protect_sprintf(sprintf(" AND item.created <= '%s' ", dbesc(datetime_convert('UTC','UTC',$arr['datequery'])))); } @@ -3943,11 +3989,6 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C $sql_extra3 .= protect_sprintf(sprintf(" AND item.created >= '%s' ", dbesc(datetime_convert('UTC','UTC',$arr['datequery2'])))); } - if(! array_key_exists('nouveau',$arr)) { - $sql_extra2 = " AND item.parent = item.id "; -// $sql_extra3 = ''; - } - if($arr['search']) { if(strpos($arr['search'],'#') === 0) @@ -4019,7 +4060,8 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C if($arr['item_type'] === '*') $item_restrict = ''; - if ($arr['nouveau'] && ($client_mode & CLIENT_MODE_LOAD) && $channel) { + if ((($arr['compat']) || ($arr['nouveau'] && ($client_mode & CLIENT_MODE_LOAD))) && $channel) { + // "New Item View" - show all items unthreaded in reverse created date order $items = q("SELECT item.*, item.id AS item_id FROM item @@ -4119,6 +4161,8 @@ function webpage_to_namespace($webpage) { $page_type = 'BUILDBLOCK'; elseif($webpage == ITEM_TYPE_PDL) $page_type = 'PDL'; + elseif($webpage == ITEM_TYPE_CARD) + $page_type = 'CARD'; elseif($webpage == ITEM_TYPE_DOC) $page_type = 'docfile'; else @@ -4314,10 +4358,7 @@ function sync_an_item($channel_id,$item_id) { if($r) { xchan_query($r); $sync_item = fetch_post_tags($r); - $rid = q("select * from item_id where iid = %d", - intval($item_id) - ); - build_sync_packet($channel_d,array('item' => array(encode_item($sync_item[0],true)),'item_id' => $rid)); + build_sync_packet($channel_d,array('item' => array(encode_item($sync_item[0],true)))); } } @@ -4475,12 +4516,12 @@ function item_create_edit_activity($post) { $new_item['id'] = 0; $new_item['parent'] = 0; $new_item['mid'] = item_message_id(); - + $new_item['body'] = sprintf( t('[Edited %s]'), (($update_item['item_thread_top']) ? t('Post','edit_activity') : t('Comment','edit_activity'))); $new_item['body'] .= "\n\n"; $new_item['body'] .= $update_item['body']; - + $new_item['sig'] = ''; $new_item['verb'] = ACTIVITY_UPDATE; @@ -4506,10 +4547,10 @@ function item_create_edit_activity($post) { array('rel' => 'photo', 'type' => $item_author['xchan_photo_mimetype'], 'href' => $item_author['xchan_photo_m'])), ), )); - - $x = post_activity_item($new_item); + + $x = post_activity_item($new_item); $post_id = $x['id']; if($post_id) { @@ -4524,5 +4565,5 @@ function item_create_edit_activity($post) { } \Zotlabs\Daemon\Master::Summon(array('Notifier', 'edit_activity', $post_id)); - + } |