diff options
author | Mario Vavti <mario@mariovavti.com> | 2018-07-25 10:19:19 +0200 |
---|---|---|
committer | Mario Vavti <mario@mariovavti.com> | 2018-07-25 10:19:19 +0200 |
commit | 1b1d11dcf1091158232e98abad966d4900e2ccc9 (patch) | |
tree | 380d5e04c73391089bf3d658ea4b27eecffa4916 /include | |
parent | b655d04b3474893ee3dea99b77f2e7dd764729a0 (diff) | |
parent | 35200e5f1b10cdd18af8f0ea646996e438b97011 (diff) | |
download | volse-hubzilla-1b1d11dcf1091158232e98abad966d4900e2ccc9.tar.gz volse-hubzilla-1b1d11dcf1091158232e98abad966d4900e2ccc9.tar.bz2 volse-hubzilla-1b1d11dcf1091158232e98abad966d4900e2ccc9.zip |
Merge branch '3.6RC'
Diffstat (limited to 'include')
-rw-r--r-- | include/api_zot.php | 7 | ||||
-rw-r--r-- | include/attach.php | 2 | ||||
-rw-r--r-- | include/auth.php | 178 | ||||
-rw-r--r-- | include/channel.php | 20 | ||||
-rw-r--r-- | include/connections.php | 15 | ||||
-rw-r--r-- | include/conversation.php | 43 | ||||
-rw-r--r-- | include/features.php | 42 | ||||
-rw-r--r-- | include/feedutils.php | 12 | ||||
-rw-r--r-- | include/follow.php | 4 | ||||
-rw-r--r-- | include/group.php | 8 | ||||
-rw-r--r-- | include/import.php | 20 | ||||
-rwxr-xr-x | include/items.php | 163 | ||||
-rw-r--r-- | include/js_strings.php | 10 | ||||
-rw-r--r-- | include/language.php | 2 | ||||
-rw-r--r-- | include/markdown.php | 4 | ||||
-rw-r--r-- | include/menu.php | 5 | ||||
-rw-r--r-- | include/nav.php | 42 | ||||
-rw-r--r-- | include/network.php | 31 | ||||
-rw-r--r-- | include/oauth.php | 2 | ||||
-rwxr-xr-x | include/oembed.php | 20 | ||||
-rw-r--r-- | include/photos.php | 59 | ||||
-rwxr-xr-x | include/plugin.php | 10 | ||||
-rw-r--r-- | include/security.php | 8 | ||||
-rw-r--r-- | include/text.php | 408 | ||||
-rw-r--r-- | include/zot.php | 5 |
25 files changed, 639 insertions, 481 deletions
diff --git a/include/api_zot.php b/include/api_zot.php index 1d30a0845..921242152 100644 --- a/include/api_zot.php +++ b/include/api_zot.php @@ -96,9 +96,10 @@ // json_return_and_die(post_activity_item($_REQUEST)); } else { - $mindate = (($_REQUEST['mindate']) ? datetime_convert('UTC','UTC',$_REQUEST['mindate']) : ''); - if(! $mindate) - $mindate = datetime_convert('UTC','UTC', 'now - 14 days'); + if(array_key_exists('dbegin',$_REQUEST)) + $_REQUEST['datequery2'] = $_REQUEST['dbegin']; + if(array_key_exists('dend',$_REQUEST)) + $_REQUEST['datequery'] = $_REQUEST['dend']; $arr = $_REQUEST; $ret = []; diff --git a/include/attach.php b/include/attach.php index ec72bb946..202412263 100644 --- a/include/attach.php +++ b/include/attach.php @@ -1585,8 +1585,8 @@ function get_cloud_url($channel_id, $channel_name, $attachHash) { } } while ($parentHash); - $url = z_root() . '/cloud/' . $channel_name . '/' . $parentFullPath . find_filename_by_hash($channel_id, $attachHash); + $url = z_root() . '/cloud/' . $channel_name . '/' . $parentFullPath . find_filename_by_hash($channel_id, $attachHash); return $url; } diff --git a/include/auth.php b/include/auth.php index c44eeb8fc..b952754fd 100644 --- a/include/auth.php +++ b/include/auth.php @@ -49,51 +49,91 @@ function account_verify_password($login, $pass) { $channel = null; $xchan = null; - if(! strpos($login,'@')) { - $channel = channelx_by_nick($login); - if(! $channel) { - $x = q("select * from atoken where atoken_name = '%s' and atoken_token = '%s' limit 1", - dbesc($login), - dbesc($pass) - ); - if($x) { - $ret['xchan'] = atoken_xchan($x[0]); - atoken_create_xchan($ret['xchan']); - return $ret; + $addon_auth = [ + 'username' => $login, + 'password' => trim($pass), + 'authenticated' => 0, + 'user_record' => null + ]; + + /** + * + * A plugin indicates successful login by setting 'authenticated' to non-zero value and returning a user record + * Plugins should never set 'authenticated' except to indicate success - as hooks may be chained + * and later plugins should not interfere with an earlier one that succeeded. + * + */ + + call_hooks('authenticate', $addon_auth); + + if(($addon_auth['authenticated']) && is_array($addon_auth['user_record']) && (! empty($addon_auth['user_record']))) { + $ret['account'] = $addon_auth['user_record']; + return $ret; + } + else { + if(! strpos($login,'@')) { + $channel = channelx_by_nick($login); + if(! $channel) { + $x = q("select * from atoken where atoken_name = '%s' and atoken_token = '%s' limit 1", + dbesc($login), + dbesc($pass) + ); + if($x) { + $ret['xchan'] = atoken_xchan($x[0]); + atoken_create_xchan($ret['xchan']); + return $ret; + } } } - } - if($channel) { - $where = " where account_id = " . intval($channel['channel_account_id']) . " "; - } - else { - $where = " where account_email = '" . dbesc($login) . "' "; - } + if($channel) { + $where = " where account_id = " . intval($channel['channel_account_id']) . " "; + } + else { + $where = " where account_email = '" . dbesc($login) . "' "; + } - $a = q("select * from account $where"); - if(! $a) { - return null; - } + $a = q("select * from account $where"); + if(! $a) { + return null; + } - $account = $a[0]; + $account = $a[0]; - // Currently we only verify email address if there is an open registration policy. - // This isn't because of any policy - it's because the workflow gets too complicated if - // you have to verify the email and then go through the account approval workflow before - // letting them login. + // Currently we only verify email address if there is an open registration policy. + // This isn't because of any policy - it's because the workflow gets too complicated if + // you have to verify the email and then go through the account approval workflow before + // letting them login. - if(($email_verify) && ($register_policy == REGISTER_OPEN) && ($account['account_flags'] & ACCOUNT_UNVERIFIED)) { - logger('email verification required for ' . $login); - return ( [ 'reason' => 'unvalidated' ] ); - } + if(($email_verify) && ($register_policy == REGISTER_OPEN) && ($account['account_flags'] & ACCOUNT_UNVERIFIED)) { + logger('email verification required for ' . $login); + return ( [ 'reason' => 'unvalidated' ] ); + } - if(($account['account_flags'] == ACCOUNT_OK) - && (hash('whirlpool',$account['account_salt'] . $pass) === $account['account_password'])) { - logger('password verified for ' . $login); - $ret['account'] = $account; - if($channel) - $ret['channel'] = $channel; - return $ret; + if($channel) { + // Try the authentication plugin again since weve determined we are using the channel login instead of account login + $addon_auth = [ + 'username' => $account['account_email'], + 'password' => trim($pass), + 'authenticated' => 0, + 'user_record' => null + ]; + + call_hooks('authenticate', $addon_auth); + + if(($addon_auth['authenticated']) && is_array($addon_auth['user_record']) && (! empty($addon_auth['user_record']))) { + $ret['account'] = $addon_auth['user_record']; + return $ret; + } + } + + if(($account['account_flags'] == ACCOUNT_OK) + && (hash('whirlpool',$account['account_salt'] . $pass) === $account['account_password'])) { + logger('password verified for ' . $login); + $ret['account'] = $account; + if($channel) + $ret['channel'] = $channel; + return $ret; + } } $error = 'password failed for ' . $login; @@ -242,52 +282,29 @@ else { if((x($_POST, 'auth-params')) && $_POST['auth-params'] === 'login') { - $record = null; - - $addon_auth = array( - 'username' => punify(trim($_POST['username'])), - 'password' => trim($_POST['password']), - 'authenticated' => 0, - 'user_record' => null - ); - - /** - * - * A plugin indicates successful login by setting 'authenticated' to non-zero value and returning a user record - * Plugins should never set 'authenticated' except to indicate success - as hooks may be chained - * and later plugins should not interfere with an earlier one that succeeded. - * - */ - - call_hooks('authenticate', $addon_auth); - $atoken = null; $account = null; + $channel = null; - if(($addon_auth['authenticated']) && (count($addon_auth['user_record']))) { - $account = $addon_auth['user_record']; + $verify = account_verify_password($_POST['username'], $_POST['password']); + if($verify && array_key_exists('reason',$verify) && $verify['reason'] === 'unvalidated') { + notice( t('Email validation is incomplete. Please check your email.')); + goaway(z_root() . '/email_validation/' . bin2hex(punify(trim(escape_tags($_POST['username']))))); + } + elseif($verify) { + $atoken = $verify['xchan']; + $channel = $verify['channel']; + $account = App::$account = $verify['account']; } - else { - $verify = account_verify_password($_POST['username'], $_POST['password']); - if($verify && array_key_exists('reason',$verify) && $verify['reason'] === 'unvalidated') { - notice( t('Email validation is incomplete. Please check your email.')); - goaway(z_root() . '/email_validation/' . bin2hex(punify(trim(escape_tags($_POST['username']))))); - } - elseif($verify) { - $atoken = $verify['xchan']; - $channel = $verify['channel']; - $account = App::$account = $verify['account']; - } - if(App::$account) { - $_SESSION['account_id'] = App::$account['account_id']; - } - elseif($atoken) { - atoken_login($atoken); - } - else { - notice( t('Failed authentication') . EOL); - } + if(App::$account) { + $_SESSION['account_id'] = App::$account['account_id']; + } + elseif($atoken) { + atoken_login($atoken); + } + else { + notice( t('Failed authentication') . EOL); } if(! ($account || $atoken)) { @@ -326,8 +343,9 @@ else { // if we haven't failed up this point, log them in. $_SESSION['last_login_date'] = datetime_convert(); - if(! $atoken) + if(! $atoken) { authenticate_success($account,$channel,true, true); + } } } diff --git a/include/channel.php b/include/channel.php index 11a5c5e63..d7c5a2511 100644 --- a/include/channel.php +++ b/include/channel.php @@ -1710,9 +1710,9 @@ function zid_init() { // try to avoid recursion - but send them home to do a proper magic auth $query = App::$query_string; $query = str_replace(array('?zid=','&zid='),array('?rzid=','&rzid='),$query); - $dest = '/' . urlencode($query); + $dest = '/' . $query; if($r && ($r[0]['hubloc_url'] != z_root()) && (! strstr($dest,'/magic')) && (! strstr($dest,'/rmagic'))) { - goaway($r[0]['hubloc_url'] . '/magic' . '?f=&rev=1&owa=1&dest=' . z_root() . $dest); + goaway($r[0]['hubloc_url'] . '/magic' . '?f=&rev=1&owa=1&bdest=' . bin2hex(z_root() . $dest)); } else logger('No hubloc found.'); @@ -1776,6 +1776,17 @@ function get_default_profile_photo($size = 300) { if(! $scheme) $scheme = 'rainbow_man'; + if(! is_dir('images/default_profile_photos/' . $scheme)) { + $x = [ 'scheme' => $scheme, 'size' => $size, 'url' => '' ]; + call_hooks('default_profile_photo',$x); + if($x['url']) { + return $x['url']; + } + else { + $scheme = 'rainbow_man'; + } + } + return 'images/default_profile_photos/' . $scheme . '/' . $size . '.png'; } @@ -2240,6 +2251,11 @@ function get_zcard_embed($channel, $observer_hash = '', $args = array()) { * - false if no channel with $nick was found */ function channelx_by_nick($nick) { + + // If we are provided a Unicode nickname convert to IDN + + $nick = punify($nick); + $r = q("SELECT * FROM channel left join xchan on channel_hash = xchan_hash WHERE channel_address = '%s' and channel_removed = 0 LIMIT 1", dbesc($nick) ); diff --git a/include/connections.php b/include/connections.php index 5a9e31950..76b26e468 100644 --- a/include/connections.php +++ b/include/connections.php @@ -120,7 +120,7 @@ function vcard_from_xchan($xchan, $observer = null, $mode = '') { App::$profile_uid = $xchan['channel_id']; $url = (($observer) - ? z_root() . '/magic?f=&owa=1&dest=' . $xchan['xchan_url'] . '&addr=' . $xchan['xchan_addr'] + ? z_root() . '/magic?f=&owa=1&bdest=' . bin2hex($xchan['xchan_url']) . '&addr=' . $xchan['xchan_addr'] : $xchan['xchan_url'] ); @@ -373,7 +373,6 @@ function contact_remove($channel_id, $abook_id) { if(intval($abook['abook_self'])) return false; - $r = q("select id from item where (owner_xchan = '%s' or author_xchan = '%s') and uid = %d and item_retained = 0 and item_starred = 0", dbesc($abook['abook_xchan']), dbesc($abook['abook_xchan']), @@ -383,13 +382,17 @@ function contact_remove($channel_id, $abook_id) { $r = fetch_post_tags($r,true); foreach($r as $rr) { - $terms = get_terms_oftype($item['term'],TERM_FILE); - if(! $terms) { - drop_item($rr['id'],false); + $x = q("select uid from term where otype = %d and oid = %d ttype = %d limit 1", + intval(TERM_OBJ_POST), + intval($rr['id']), + intval(TERM_FILE) + ); + if($x) { + continue; } + drop_item($rr['id'],false); } } - q("delete from abook where abook_id = %d and abook_channel = %d", intval($abook['abook_id']), diff --git a/include/conversation.php b/include/conversation.php index 97dd402fc..4997bc2b7 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -509,6 +509,7 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa . ((x($_GET,'cmax')) ? '&cmax=' . $_GET['cmax'] : '') . ((x($_GET,'file')) ? '&file=' . $_GET['file'] : '') . ((x($_GET,'uri')) ? '&uri=' . $_GET['uri'] : '') + . ((x($_GET,'pf')) ? '&pf=' . $_GET['pf'] : '') . "'; var profile_page = " . App::$pager['page'] . "; </script>\r\n"; } } @@ -690,8 +691,10 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa 'delete' => t('Delete'), ); - $star = false; - $isstarred = "unstarred fa-star-o"; + $star = array( + 'toggle' => t("Toggle Star Status"), + 'isstarred' => ((intval($item['item_starred'])) ? true : false), + ); $lock = (($item['item_private'] || strlen($item['allow_cid']) || strlen($item['allow_gid']) || strlen($item['deny_cid']) || strlen($item['deny_gid'])) ? t('Private Message') @@ -773,8 +776,7 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa 'owner_photo' => $owner_photo, 'plink' => get_plink($item,false), 'edpost' => false, - 'isstarred' => $isstarred, - 'star' => $star, + 'star' => ((feature_enabled(local_channel(),'star_posts')) ? $star : ''), 'drop' => $drop, 'vote' => $likebuttons, 'like' => '', @@ -1001,18 +1003,21 @@ function thread_author_menu($item, $mode = '') { $profile_link = chanlink_hash($item['author_xchan']); $contact = false; - if(App::$contacts && array_key_exists($item['author_xchan'],App::$contacts)) - $contact = App::$contacts[$item['author_xchan']]; - else - if($local_channel && $item['author']['xchan_addr']) - $follow_url = z_root() . '/follow/?f=&url=' . urlencode($item['author']['xchan_addr']) . '&interactive=0'; - + if($channel['channel_hash'] !== $item['author_xchan']) { + if(App::$contacts && array_key_exists($item['author_xchan'],App::$contacts)) { + $contact = App::$contacts[$item['author_xchan']]; + } + else { + if($local_channel && $item['author']['xchan_addr'] && (! in_array($item['author']['xchan_network'],[ 'rss', 'anon','unknown' ]))) { + $follow_url = z_root() . '/follow/?f=&url=' . urlencode($item['author']['xchan_addr']) . '&interactive=0'; + } + } - if($item['uid'] > 0 && author_is_pmable($item['author'],$contact)) { - $pm_url = z_root() . '/mail/new/?f=&hash=' . urlencode($item['author_xchan']); + if($item['uid'] > 0 && author_is_pmable($item['author'],$contact)) { + $pm_url = z_root() . '/mail/new/?f=&hash=' . urlencode($item['author_xchan']); + } } - if($contact) { $poke_link = z_root() . '/poke/?f=&c=' . $contact['abook_id']; if (! intval($contact['abook_self'])) @@ -1159,7 +1164,7 @@ function builtin_activity_puller($item, &$conv_responses) { if((activity_match($item['verb'], $verb)) && ($item['id'] != $item['parent'])) { $name = (($item['author']['xchan_name']) ? $item['author']['xchan_name'] : t('Unknown')); $url = (($item['author_xchan'] && $item['author']['xchan_photo_s']) - ? '<a class="dropdown-item" href="' . chanlink_hash($item['author_xchan']) . '">' . '<img class="menu-img-1" src="' . zid($item['author']['xchan_photo_s']) . '" alt="' . urlencode($name) . '" />' . $name . '</a>' + ? '<a class="dropdown-item" href="' . chanlink_hash($item['author_xchan']) . '">' . '<img class="menu-img-1" src="' . zid($item['author']['xchan_photo_s']) . '" alt="' . urlencode($name) . '" /> ' . $name . '</a>' : '<a class="dropdown-item" href="#" class="disabled">' . $name . '</a>' ); @@ -1279,7 +1284,7 @@ function status_editor($a, $x, $popup = false) { if(x($x, 'hide_weblink')) $weblink = false; - $embedPhotos = t('Embed image from photo albums'); + $embedPhotos = t('Embed (existing) photo from your photo albums'); $writefiles = (($mimetype === 'text/bbcode') ? perm_is_allowed($x['profile_uid'], get_observer_hash(), 'write_storage') : false); if(x($x, 'hide_attach')) @@ -1301,6 +1306,8 @@ function status_editor($a, $x, $popup = false) { $id_select = ''; $webpage = ((x($x,'webpage')) ? $x['webpage'] : ''); + + $reset = ((x($x,'reset')) ? $x['reset'] : ''); $feature_auto_save_draft = ((feature_enabled($x['profile_uid'], 'auto_save_draft')) ? "true" : "false"); @@ -1326,6 +1333,7 @@ function status_editor($a, $x, $popup = false) { '$nocomment_enabled' => t('Comments enabled'), '$nocomment_disabled' => t('Comments disabled'), '$auto_save_draft' => $feature_auto_save_draft, + '$reset' => $reset )); $tpl = get_markup_template('jot.tpl'); @@ -1382,7 +1390,7 @@ function status_editor($a, $x, $popup = false) { '$underline' => t('Underline'), '$quote' => t('Quote'), '$code' => t('Code'), - '$attach' => t('Attach file'), + '$attach' => t('Attach/Upload file'), '$weblink' => $weblink, '$embedPhotos' => $embedPhotos, '$embedPhotosModalTitle' => t('Embed an image from your albums'), @@ -1438,7 +1446,8 @@ function status_editor($a, $x, $popup = false) { '$expiryModalCANCEL' => t('Cancel'), '$expanded' => ((x($x, 'expanded')) ? $x['expanded'] : false), '$bbcode' => ((x($x, 'bbcode')) ? $x['bbcode'] : false), - '$parent' => ((array_key_exists('parent',$x) && $x['parent']) ? $x['parent'] : 0) + '$parent' => ((array_key_exists('parent',$x) && $x['parent']) ? $x['parent'] : 0), + '$reset' => $reset )); if ($popup === true) { diff --git a/include/features.php b/include/features.php index c865f6754..03f50c9a4 100644 --- a/include/features.php +++ b/include/features.php @@ -237,8 +237,8 @@ function get_features($filtered = true, $level = (-1)) { [ 'permcats', - t('Permission Groups'), - t('Provide alternate connection permission roles.'), + t('Permission Categories'), + t('Create custom connection permission limits'), false, get_config('feature_lock','permcats'), feature_level('permcats',2), @@ -386,21 +386,39 @@ function get_features($filtered = true, $level = (-1)) { ], [ - 'personal_tab', - t('Network Personal Tab'), - t('Enable tab to display only Network posts that you\'ve interacted on'), + 'order_tab', + t('Alternate Stream Order'), + t('Ability to order the stream by last post date, last comment date or unthreaded activities'), false, - get_config('feature_lock','personal_tab'), - feature_level('personal_tab',1), + get_config('feature_lock','order_tab'), + feature_level('order_tab',2), + ], + + [ + 'name_tab', + t('Contact Filter'), + t('Ability to display only posts of a selected contact'), + false, + get_config('feature_lock','name_tab'), + feature_level('name_tab',1), ], [ - 'new_tab', - t('Network New Tab'), - t('Enable tab to display all new Network activity'), + 'forums_tab', + t('Forum Filter'), + t('Ability to display only posts of a specific forum'), false, - get_config('feature_lock','new_tab'), - feature_level('new_tab',2), + get_config('feature_lock','forums_tab'), + feature_level('forums_tab',1), + ], + + [ + 'personal_tab', + t('Personal Posts Filter'), + t('Ability to display only posts that you\'ve interacted on'), + false, + get_config('feature_lock','personal_tab'), + feature_level('personal_tab',1), ], [ diff --git a/include/feedutils.php b/include/feedutils.php index 023caaad6..afbe4229e 100644 --- a/include/feedutils.php +++ b/include/feedutils.php @@ -1332,6 +1332,12 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { // immediate parent wasn't found. Turn into a top-level post if permissions allow // but save the thread_parent in case we need to refer to it later. + if($importer['channel_system']) { + if( ! \Zotlabs\Lib\MessageFilter::evaluate($datarray,get_config('system','pubstream_incl'),get_config('system','pubstream_excl'))) { + continue; + } + } + if(! post_is_importable($datarray, $contact)) continue; @@ -1482,6 +1488,12 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { $author['owner_avatar'] = $contact['thumb']; } + if($importer['channel_system']) { + if( ! \Zotlabs\Lib\MessageFilter::evaluate($datarray,get_config('system','pubstream_incl'),get_config('system','pubstream_excl'))) { + continue; + } + } + if(! post_is_importable($datarray, $contact)) continue; diff --git a/include/follow.php b/include/follow.php index d803afa3f..964c43ff2 100644 --- a/include/follow.php +++ b/include/follow.php @@ -158,7 +158,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) $feeds = get_config('system','feed_contacts'); - if(($feeds) && ($protocol === '' || $protocol === 'feed')) { + if(($feeds) && ($protocol === '' || $protocol === 'feed' || $protocol === 'rss')) { $d = discover_by_url($url); } else { @@ -204,7 +204,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) $singleton = intval($x['singleton']); $aid = $channel['channel_account_id']; - $hash = get_observer_hash(); + $hash = $channel['channel_hash']; $default_group = $channel['channel_default_group']; if($hash == $xchan_hash) { diff --git a/include/group.php b/include/group.php index 03ebf7ee5..56bf210ff 100644 --- a/include/group.php +++ b/include/group.php @@ -279,14 +279,6 @@ function group_side($every="connections",$each="group",$edit = false, $group_id } $groups = array(); - - $groups[] = array( - 'text' => t('All Channels'), - 'id' => 0, - 'selected' => (($group_id == 0) ? 'group-selected' : ''), - 'href' => $every . (($every === 'network') ? '?f=&gid=0' : ''), - ); - $r = q("SELECT * FROM groups WHERE deleted = 0 AND uid = %d ORDER BY gname ASC", intval($_SESSION['uid']) diff --git a/include/import.php b/include/import.php index fb7826101..4953834c9 100644 --- a/include/import.php +++ b/include/import.php @@ -14,7 +14,7 @@ require_once('include/perm_upgrade.php'); * @param int $seize * @return boolean|array */ -function import_channel($channel, $account_id, $seize) { +function import_channel($channel, $account_id, $seize, $newname = '') { if(! array_key_exists('channel_system',$channel)) { $channel['channel_system'] = (($channel['channel_pageflags'] & 0x1000) ? 1 : 0); @@ -30,6 +30,11 @@ function import_channel($channel, $account_id, $seize) { $channel['channel_hash'] = make_xchan_hash($channel['channel_guid'],$channel['channel_guid_sig']); + if($newname) { + $channel['channel_address'] = $newname; + } + + // Check for duplicate channels $r = q("select * from channel where (channel_guid = '%s' or channel_hash = '%s' or channel_address = '%s' ) limit 1", @@ -1322,20 +1327,23 @@ function sync_files($channel, $files) { ); if($exists) { - if(! dbesc_array($p)) - continue; $str = ''; foreach($p as $k => $v) { + $matches = false; + if(preg_match('/([^a-zA-Z0-9\-\_\.])/',$k,$matches)) { + continue; + } + if($str) $str .= ","; - - $str .= " " . TQUOT . $k . TQUOT . " = '" . $v . "' "; + + $str .= " " . TQUOT . $k . TQUOT . " = '" . (($k === 'content') ? dbescbin($v) : dbesc($v)) . "' "; } $r = dbq("update photo set " . $str . " where id = " . intval($exists[0]['id']) ); } else { - create_table_from_array('photo',$p); + create_table_from_array('photo',$p, [ 'content' ] ); } } } diff --git a/include/items.php b/include/items.php index 0b8ec7075..9dd5d005b 100755 --- a/include/items.php +++ b/include/items.php @@ -19,9 +19,10 @@ require_once('include/permissions.php'); * * @param array $item * @param[out] boolean $private_envelope + * @param boolean $include_groups * @return array containing the recipients */ -function collect_recipients($item, &$private_envelope) { +function collect_recipients($item, &$private_envelope,$include_groups = true) { require_once('include/group.php'); @@ -34,7 +35,12 @@ function collect_recipients($item, &$private_envelope) { $allow_people = expand_acl($item['allow_cid']); - $allow_groups = expand_groups(expand_acl($item['allow_gid'])); + if($include_groups) { + $allow_groups = expand_groups(expand_acl($item['allow_gid'])); + } + else { + $allow_groups = []; + } $recipients = array_unique(array_merge($allow_people,$allow_groups)); @@ -178,7 +184,7 @@ function item_normal() { } function item_normal_search() { - return " and item.item_hidden = 0 and item.item_type in (0,3,6) and item.item_deleted = 0 + return " and item.item_hidden = 0 and item.item_type in (0,3,6,7) 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 and item.obj_type != '" . ACTIVITY_OBJ_FILE . "' "; } @@ -974,12 +980,10 @@ function empty_acl($item) { } function encode_item($item,$mirror = false) { - $x = array(); + $x = []; $x['type'] = 'activity'; $x['encoding'] = 'zot'; -// logger('encode_item: ' . print_r($item,true)); - $r = q("select channel_id from channel where channel_id = %d limit 1", intval($item['uid']) ); @@ -1356,7 +1360,7 @@ function encode_item_flags($item) { } function encode_mail($item,$extended = false) { - $x = array(); + $x = []; $x['type'] = 'mail'; $x['encoding'] = 'zot'; @@ -2948,6 +2952,20 @@ function start_delivery_chain($channel, $item, $item_id, $parent) { } } } + + // This will change the author to the post owner. Useful for RSS feeds which are to be syndicated + // to federated platforms which can't verify the identity of the author. + // This MAY cause you to run afoul of copyright law. + + $rewrite_author = intval(get_abconfig($channel['channel_id'],$item['owner_xchan'],'system','rself')); + if($rewrite_author) { + $item['author_xchan'] = $channel['channel_hash']; + + $r = q("update item set author_xchan = '%s' where id = %d", + dbesc($item['author_xchan']), + intval($item_id) + ); + } } // Change this copy of the post to a forum head message and deliver to all the tgroup members @@ -3007,9 +3025,6 @@ function start_delivery_chain($channel, $item, $item_id, $parent) { intval($item_id) ); - - - if($r) Zotlabs\Daemon\Master::Summon(array('Notifier','tgroup',$item_id)); else { @@ -3032,62 +3047,56 @@ function start_delivery_chain($channel, $item, $item_id, $parent) { * @param array $item */ function check_item_source($uid, $item) { + + logger('source: uid: ' . $uid, LOGGER_DEBUG); + $xchan = (($item['source_xchan']) ? $item['source_xchan'] : $item['owner_xchan']); + $r = q("select * from source where src_channel_id = %d and ( src_xchan = '%s' or src_xchan = '*' ) limit 1", intval($uid), - dbesc(($item['source_xchan']) ? $item['source_xchan'] : $item['owner_xchan']) + dbesc($xchan) ); - if(! $r) + if(! $r) { + logger('source: no source record for this channel and source', LOGGER_DEBUG); return false; + } - $x = q("select abook_their_perms, abook_feed from abook where abook_channel = %d and abook_xchan = '%s' limit 1", + $x = q("select abook_feed from abook where abook_channel = %d and abook_xchan = '%s' limit 1", intval($uid), - dbesc($item['owner_xchan']) + dbesc($xchan) ); - if(! $x) + if(! $x) { + logger('source: not connected to this channel.'); return false; + } - if(! get_abconfig($uid,$item['owner_xchan'],'their_perms','republish')) + if(! get_abconfig($uid,$xchan,'their_perms','republish')) { + logger('source: no republish permission'); return false; + } - if($item['item_private'] && (! intval($x[0]['abook_feed']))) + if($item['item_private'] && (! intval($x[0]['abook_feed']))) { + logger('source: item is private'); return false; + } - if($r[0]['src_channel_xchan'] === $item['owner_xchan']) + if($r[0]['src_channel_xchan'] === $xchan) { + logger('source: cannot source yourself'); return false; + } - - // since we now have connection filters with more features, the source filter is redundant and can probably go away - - if(! $r[0]['src_patt']) + if (! $r[0]['src_patt']) { + logger('source: success'); return true; + } - - require_once('include/html2plain.php'); - $text = prepare_text($item['body'],$item['mimetype']); - $text = html2plain($text); - - $tags = ((count($item['term'])) ? $item['term'] : false); - - $words = explode("\n",$r[0]['src_patt']); - if($words) { - foreach($words as $word) { - $w = trim($word); - if(! $w) - continue; - if(substr($w,0,1) === '#' && $tags) { - foreach($tags as $t) - if((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($w,1)) || (substr($w,1) === '*'))) - return true; - } - elseif((strpos($w,'/') === 0) && preg_match($w,$text)) - return true; - elseif(stristr($text,$w) !== false) - return true; - } + if (\Zotlabs\Lib\MessageFilter::evaluate($item, $r[0]['src_patt'], EMPTY_STR)) { + logger('source: text filter success'); + return true; } + logger('source: filter fail'); return false; } @@ -3105,69 +3114,8 @@ function post_is_importable($item,$abook) { if(! ($abook['abook_incl'] || $abook['abook_excl'])) return true; - require_once('include/html2plain.php'); - - unobscure($item); - - $text = prepare_text($item['body'],$item['mimetype']); - $text = html2plain(($item['title']) ? $item['title'] . ' ' . $text : $text); - - - $lang = null; - - if((strpos($abook['abook_incl'],'lang=') !== false) || (strpos($abook['abook_excl'],'lang=') !== false)) { - $lang = detect_language($text); - } - $tags = ((count($item['term'])) ? $item['term'] : false); - - // exclude always has priority - - $exclude = (($abook['abook_excl']) ? explode("\n",$abook['abook_excl']) : null); - - if($exclude) { - foreach($exclude as $word) { - $word = trim($word); - if(! $word) - continue; - if(substr($word,0,1) === '#' && $tags) { - foreach($tags as $t) - if((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($word,1)) || (substr($word,1) === '*'))) - return false; - } - elseif((strpos($word,'/') === 0) && preg_match($word,$text)) - return false; - elseif((strpos($word,'lang=') === 0) && ($lang) && (strcasecmp($lang,trim(substr($word,5))) == 0)) - return false; - elseif(stristr($text,$word) !== false) - return false; - } - } - - $include = (($abook['abook_incl']) ? explode("\n",$abook['abook_incl']) : null); + return \Zotlabs\Lib\MessageFilter::evaluate($item,$abook['abook_incl'],$abook['abook_excl']); - if($include) { - foreach($include as $word) { - $word = trim($word); - if(! $word) - continue; - if(substr($word,0,1) === '#' && $tags) { - foreach($tags as $t) - if((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($word,1)) || (substr($word,1) === '*'))) - return true; - } - elseif((strpos($word,'/') === 0) && preg_match($word,$text)) - return true; - elseif((strpos($word,'lang=') === 0) && ($lang) && (strcasecmp($lang,trim(substr($word,5))) == 0)) - return true; - elseif(stristr($text,$word) !== false) - return true; - } - } - else { - return true; - } - - return false; } @@ -3561,7 +3509,6 @@ function item_expire($uid,$days,$comment_days = 7) { drop_item($item['id'],false); } -// Zotlabs\Daemon\Master::Summon(array('Notifier','expire',$uid)); } function retain_item($id) { diff --git a/include/js_strings.php b/include/js_strings.php index 936594291..d9038e838 100644 --- a/include/js_strings.php +++ b/include/js_strings.php @@ -23,6 +23,16 @@ function js_strings() { '$linkurl' => t('Please enter a link URL'), '$leavethispage' => t('Unsaved changes. Are you sure you wish to leave this page?'), '$location' => t('Location'), + '$lovely' => t('lovely'), + '$wonderful' => t('wonderful'), + '$fantastic' => t('fantastic'), + '$great' => t('great'), + '$nick_invld1' => t('Your chosen nickname was either already taken or not valid. Please use our suggestion ('), + '$nick_invld2' => t(') or enter a new one.'), + '$nick_valid' => t('Thank you, this nickname is valid.'), + '$name_empty' => t('A channel name is required.'), + '$name_ok1' => t('This is a '), + '$name_ok2' => t(' channel name'), // translatable prefix and suffix strings for jquery.timeago - // using the defaults set below if left untranslated, empty strings if diff --git a/include/language.php b/include/language.php index d0ecd3a85..69a7e3004 100644 --- a/include/language.php +++ b/include/language.php @@ -242,7 +242,7 @@ function tt($singular, $plural, $count, $ctx = ''){ if (! function_exists($f)) $f = 'string_plural_select_default'; - $k = $f($count); + $k = $f(intval($count)); return is_array($t) ? $t[$k] : $t; } diff --git a/include/markdown.php b/include/markdown.php index 431ba84a4..de9862801 100644 --- a/include/markdown.php +++ b/include/markdown.php @@ -75,10 +75,10 @@ function markdown_to_bb($s, $use_zrl = false, $options = []) { // Convert everything that looks like a link to a link if($use_zrl) { $s = str_replace(['[img', '/img]'], ['[zmg', '/zmg]'], $s); - $s = preg_replace("/([^\]\=]|^)(https?\:\/\/)([a-zA-Z0-9\pL\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,\@\(\)]+)/ismu", '$1[zrl=$2$3]$2$3[/zrl]',$s); + $s = preg_replace("/([^\]\=\{]|^)(https?\:\/\/)([a-zA-Z0-9\pL\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,\@\(\)]+)/ismu", '$1[zrl=$2$3]$2$3[/zrl]',$s); } else { - $s = preg_replace("/([^\]\=]|^)(https?\:\/\/)([a-zA-Z0-9\pL\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,\@\(\)]+)/ismu", '$1[url=$2$3]$2$3[/url]',$s); + $s = preg_replace("/([^\]\=\{]|^)(https?\:\/\/)([a-zA-Z0-9\pL\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,\@\(\)]+)/ismu", '$1[url=$2$3]$2$3[/url]',$s); } // remove duplicate adjacent code tags diff --git a/include/menu.php b/include/menu.php index 4add78c39..1a2059451 100644 --- a/include/menu.php +++ b/include/menu.php @@ -81,6 +81,10 @@ function menu_render($menu, $class='', $edit = false, $var = array()) { if ((! $channel_id) && (local_channel())) $channel_id = local_channel(); + $chan = channelx_by_n($channel_id); + if(! $chan) + return ''; + $menu_list = menu_list($channel_id); $menu_names = array(); @@ -110,6 +114,7 @@ function menu_render($menu, $class='', $edit = false, $var = array()) { $ret = replace_macros(get_markup_template('usermenu.tpl'),array( '$menu' => $menu['menu'], '$class' => $class, + '$nick' => $chan['channel_address'], '$edit' => (($edit) ? t("Edit") : ''), '$id' => $menu['menu']['menu_id'], '$items' => $menu['items'], diff --git a/include/nav.php b/include/nav.php index a443c58ff..41358c93e 100644 --- a/include/nav.php +++ b/include/nav.php @@ -17,15 +17,7 @@ function nav($template = 'default') { if(!(x(App::$page,'nav'))) App::$page['nav'] = ''; - $base = z_root(); - - App::$page['htmlhead'] .= <<< EOT -<script>$(document).ready(function() { - $("#nav-search-text").search_autocomplete('$base/acl'); -}); - -</script> -EOT; + App::$page['htmlhead'] .= '<script>$(document).ready(function() { $("#nav-search-text").search_autocomplete(\'' . z_root() . '/acl' . '\');});</script>'; $is_owner = (((local_channel()) && ((App::$profile_uid == local_channel()) || (App::$profile_uid == 0))) ? true : false); @@ -99,8 +91,10 @@ EOT; if(local_channel()) { if(! $_SESSION['delegate']) { - $nav['manage'] = array('manage', t('Channel Manager'), "", t('Manage Your Channels'),'manage_nav_btn'); + $nav['manage'] = array('manage', t('Channel Manager'), "", t('Manage your channels'),'manage_nav_btn'); } + if(feature_enabled(local_channel(),'groups')) + $nav['group'] = array('group', t('Privacy Groups'),"", t('Manage your privacy groups'),'group_nav_btn'); $nav['settings'] = array('settings', t('Settings'),"", t('Account/Channel Settings'),'settings_nav_btn'); @@ -176,21 +170,19 @@ EOT; $nav['help'] = [$help_url, t('Help'), "", t('Help and documentation'), 'help_nav_btn', $context_help, $enable_context_help]; } - $nav['search'] = ['search', t('Search'), "", t('Search site @name, !forum, #tag, ?docs, content')]; - + switch(App::$module) { + case 'network': + $search_form_action = 'network'; + break; + case 'channel': + $search_form_action = 'channel'; + break; + default: + $search_form_action = 'search'; + } - /** - * - * The following nav links are only show to logged in users - * - */ - if(local_channel()) { - if(! $_SESSION['delegate']) { - $nav['manage'] = array('manage', t('Channel Manager'), "", t('Manage Your Channels'),'manage_nav_btn'); - } - $nav['settings'] = array('settings', t('Settings'),"", t('Account/Channel Settings'),'settings_nav_btn'); - } + $nav['search'] = ['search', t('Search'), "", t('Search site @name, !forum, #tag, ?docs, content'), $search_form_action]; /** * Admin page @@ -220,9 +212,9 @@ EOT; //app bin if($is_owner) { - if(get_pconfig(local_channel(), 'system','initial_import_system_apps') === false) { + if(get_pconfig(local_channel(), 'system','import_system_apps') !== datetime_convert('UTC','UTC','now','Y-m-d')) { Zlib\Apps::import_system_apps(); - set_pconfig(local_channel(), 'system','initial_import_system_apps', 1); + set_pconfig(local_channel(), 'system','import_system_apps', datetime_convert('UTC','UTC','now','Y-m-d')); } $syslist = array(); diff --git a/include/network.php b/include/network.php index 93b27b436..91a39a6cb 100644 --- a/include/network.php +++ b/include/network.php @@ -468,13 +468,21 @@ function z_dns_check($h,$check_mx = 0) { && \App::$config['system']['do_not_check_dns']) return true; + // This will match either Windows or Mac ('Darwin') + if(stripos(PHP_OS,'win') !== false) + return true; + + // BSD variants have dns_get_record() but it only works reliably without any options + if(stripos(PHP_OS,'bsd') !== false) + return((@dns_get_record($h) || filter_var($h, FILTER_VALIDATE_IP)) ? true : false); + + // Otherwise we will assume dns_get_record() works as documented - //$opts = DNS_A + DNS_CNAME + DNS_PTR; - //if($check_mx) - // $opts += DNS_MX; - // Specific record type flags are unreliable on FreeBSD and Mac, - // so now we'll ignore these and just check for the existence of any DNS record. - return((@dns_get_record($h) || filter_var($h, FILTER_VALIDATE_IP)) ? true : false); + $opts = DNS_A + DNS_CNAME + DNS_PTR; + if($check_mx) + $opts += DNS_MX; + + return((@dns_get_record($h,$opts) || filter_var($h, FILTER_VALIDATE_IP)) ? true : false); } /** @@ -999,7 +1007,7 @@ function discover_by_url($url, $arr = null) { return false; $network = (($arr['network']) ? $arr['network'] : 'unknown'); - $name = (($arr['name']) ? $arr['name'] : 'unknown'); + $name = (trim($arr['name']) ? trim($arr['name']) : 'unknown'); $photo = (($arr['photo']) ? $arr['photo'] : ''); $addr = (($arr['addr']) ? $arr['addr'] : ''); $guid = $url; @@ -1103,6 +1111,9 @@ function discover_by_url($url, $arr = null) { if(! $name) $name = notags($feed->get_description()); + if(! trim($name)) + $name = 'unknown'; + $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($guid) ); @@ -1213,7 +1224,7 @@ function webfinger_rfc7033($webbie, $zot = false) { if(strpos($webbie,'@')) { $lhs = substr($webbie,0,strpos($webbie,'@')); $rhs = substr($webbie,strpos($webbie,'@')+1); - $resource = 'acct:' . $webbie; + $resource = urlencode('acct:' . $webbie); } else { $m = parse_url($webbie); @@ -1231,7 +1242,7 @@ function webfinger_rfc7033($webbie, $zot = false) { $counter = 0; $s = z_fetch_url('https://' . $rhs . '/.well-known/webfinger?f=&resource=' . $resource . (($zot) ? '&zot=1' : ''), - false, $counter, [ 'headers' => [ 'Accept: application/jrd+json, */*' ] ]); + false, $counter, [ 'headers' => [ 'Accept: application/jrd+json, application/json, */*' ] ]); if($s['success']) { $j = json_decode($s['body'], true); @@ -2052,4 +2063,4 @@ function jsonld_document_loader($url) { } return []; -}
\ No newline at end of file +} diff --git a/include/oauth.php b/include/oauth.php index a3c52bf27..845ec4558 100644 --- a/include/oauth.php +++ b/include/oauth.php @@ -79,7 +79,7 @@ class ZotOAuth1DataStore extends OAuth1DataStore { $k = $consumer; } - $r = q("INSERT INTO tokens (id, secret, client_id, auth_scope, expires) VALUES ('%s','%s','%s','%s', %d)", + $r = q("INSERT INTO tokens (id, secret, client_id, auth_scope, expires, uid) VALUES ('%s','%s','%s','%s', %d, 0)", dbesc($key), dbesc($sec), dbesc($k), diff --git a/include/oembed.php b/include/oembed.php index e677087a2..41ab001d3 100755 --- a/include/oembed.php +++ b/include/oembed.php @@ -105,6 +105,7 @@ function oembed_action($embedurl) { // if the url is embeddable with oembed, return the bbcode link. function oembed_process($url) { + $j = oembed_fetch_url($url); logger('oembed_process: ' . print_r($j,true), LOGGER_DATA, LOG_DEBUG); if($j && $j['type'] !== 'error') @@ -132,6 +133,7 @@ function oembed_fetch_url($embedurl){ } } + $txt = null; // we should try to cache this and avoid a lookup on each render @@ -217,10 +219,19 @@ function oembed_fetch_url($embedurl){ } - $j = json_decode($txt,true); + if(strpos(strtolower($embedurl),'.pdf') !== false) { + $action = 'allow'; + $j = [ 'html' => '<object data="' . $embedurl . '" type="application/pdf" width="500" height="720">' . '<a href="' . $embedurl . '">' . t('View PDF') . '</a></object>', 'width' => 500, 'height' => 720, 'type' => 'pdf' ]; + + } - if(! $j) + if(! $j) { + $j = json_decode($txt,true); + } + + if(! $j) { $j = []; + } if($action === 'filter') { if($j['html']) { @@ -317,6 +328,11 @@ function oembed_format_object($j){ //$ret = "<a href='".$embedurl."'>".$j['title']."</a>"; }; break; + case 'pdf': { + $ret = $j['html']; + break; + } + case "rich": { // not so safe.. $ret.= $jhtml; diff --git a/include/photos.php b/include/photos.php index 9ae0e6874..d0c5f77fc 100644 --- a/include/photos.php +++ b/include/photos.php @@ -80,14 +80,15 @@ function photo_upload($channel, $observer, $args) { if($imagick_path && @file_exists($imagick_path)) { $tmp_name = $args['os_syspath'] . '-001'; $newsize = photo_calculate_scale(array_merge($args['getimagesize'],['max' => $max_thumb])); - $cmd = $imagick_path . ' ' . escapeshellarg(PROJECT_BASE . '/' . $args['os_syspath']) . ' -thumbnail ' . $newsize . ' ' . escapeshellarg(PROJECT_BASE . '/' . $tmp_name); - // logger('imagick thumbnail command: ' . $cmd); + $cmd = $imagick_path . ' ' . escapeshellarg(PROJECT_BASE . '/' . $args['os_syspath']) . ' -resize ' . $newsize . ' ' . escapeshellarg(PROJECT_BASE . '/' . $tmp_name); + logger('imagick thumbnail command: ' . $cmd); for($x = 0; $x < 4; $x ++) { exec($cmd); - if(! file_exists($tmp_name)) { - logger('imagick scale failed. Retrying.'); - continue; + if(file_exists($tmp_name)) { + break; } + logger('imagick scale failed. Retrying.'); + continue; } if(! file_exists($tmp_name)) { logger('imagick scale failed. Abort.'); @@ -96,7 +97,7 @@ function photo_upload($channel, $observer, $args) { $imagedata = @file_get_contents($tmp_name); $filesize = @filesize($args['os_syspath']); - @unlink($tmp_name); +// @unlink($tmp_name); } else { $imagedata = @file_get_contents($args['os_syspath']); @@ -786,17 +787,31 @@ function photos_album_get_db_idstr($channel_id, $album, $remote_xchan = '') { ); } if ($r) { - $arr = array(); - foreach ($r as $rr) { - $arr[] = "'" . dbesc($rr['hash']) . "'" ; - } - $str = implode(',',$arr); - return $str; + return ids_to_querystr($r,'hash',true); } return false; } +function photos_album_get_db_idstr_admin($channel_id, $album) { + + if(! is_site_admin()) + return false; + + $r = q("SELECT hash from attach where uid = %d and folder = '%s' ", + intval($channel_id), + dbesc($album) + ); + + if ($r) { + return ids_to_querystr($r,'hash',true); + } + + return false; +} + + + /** * @brief Creates a new photo item. * @@ -996,3 +1011,23 @@ function profile_photo_set_profile_perms($uid, $profileid = 0) { } } } + +function fetch_image_from_url($url,&$mimetype) { + + $redirects = 0; + $x = z_fetch_url($url,true,$redirects,[ 'novalidate' => true ]); + if($x['success']) { + $hdrs = []; + $h = explode("\n",$x['header']); + foreach ($h as $l) { + list($k,$v) = array_map("trim", explode(":", trim($l), 2)); + $hdrs[strtolower($k)] = $v; + } + if (array_key_exists('content-type', $hdrs)) + $mimetype = $hdrs['content-type']; + + return $x['body']; + } + + return EMPTY_STR; +}
\ No newline at end of file diff --git a/include/plugin.php b/include/plugin.php index 4545e1e8d..734c20d79 100755 --- a/include/plugin.php +++ b/include/plugin.php @@ -53,10 +53,15 @@ function unload_plugin($plugin){ * @return boolean */ function uninstall_plugin($plugin) { + unload_plugin($plugin); - if(! file_exists('addon/' . $plugin . '/' . $plugin . '.php')) + if(! file_exists('addon/' . $plugin . '/' . $plugin . '.php')) { + q("DELETE FROM addon WHERE aname = '%s' ", + dbesc($plugin) + ); return false; + } logger("Addons: uninstalling " . $plugin); //$t = @filemtime('addon/' . $plugin . '/' . $plugin . '.php'); @@ -73,6 +78,7 @@ function uninstall_plugin($plugin) { q("DELETE FROM addon WHERE aname = '%s' ", dbesc($plugin) ); + } /** @@ -553,7 +559,7 @@ function get_widget_info($widget){ $checkpaths = [ "Zotlabs/SiteWidget/$ucwidget.php", - "Zotlibs/Widget/$ucwidget.php", + "Zotlabs/Widget/$ucwidget.php", "addon/$ucwidget/$ucwidget.php", "addon/$widget.php" ]; diff --git a/include/security.php b/include/security.php index 19278d5cb..88988a7c0 100644 --- a/include/security.php +++ b/include/security.php @@ -118,10 +118,10 @@ function atoken_xchan($atoken) { 'xchan_network' => 'unknown', 'xchan_url' => z_root() . '/guest/' . substr($c['channel_hash'],0,16) . '.' . $atoken['atoken_name'], 'xchan_hidden' => 1, - 'xchan_photo_mimetype' => 'image/jpeg', - 'xchan_photo_l' => get_default_profile_photo(300), - 'xchan_photo_m' => get_default_profile_photo(80), - 'xchan_photo_s' => get_default_profile_photo(48) + 'xchan_photo_mimetype' => 'image/png', + 'xchan_photo_l' => z_root() . '/' . get_default_profile_photo(300), + 'xchan_photo_m' => z_root() . '/' . get_default_profile_photo(80), + 'xchan_photo_s' => z_root() . '/' . get_default_profile_photo(48) ]; } diff --git a/include/text.php b/include/text.php index af88c9f9c..e894c5ce5 100644 --- a/include/text.php +++ b/include/text.php @@ -572,7 +572,7 @@ function item_message_id() { $r = q("SELECT id FROM item WHERE mid = '%s' LIMIT 1", dbesc($mid)); - if(count($r)) + if($r) $dups = true; } while($dups == true); @@ -593,7 +593,7 @@ function photo_new_resource() { $r = q("SELECT id FROM photo WHERE resource_id = '%s' LIMIT 1", dbesc($resource)); - if(count($r)) + if($r) $found = true; } while($found === true); @@ -665,7 +665,7 @@ function logger($msg, $level = LOGGER_NORMAL, $priority = LOG_INFO) { $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); $where = basename($stack[0]['file']) . ':' . $stack[0]['line'] . ':' . $stack[1]['function'] . ': '; - $s = datetime_convert('UTC','UTC', 'now', ATOM_TIME) . ':' . log_priority_str($priority) . ':' . session_id() . ':' . $where . $msg . PHP_EOL; + $s = datetime_convert('UTC','UTC', 'now', ATOM_TIME) . ':' . log_priority_str($priority) . ':' . logid() . ':' . $where . $msg . PHP_EOL; $pluginfo = array('filename' => $logfile, 'loglevel' => $level, 'message' => $s,'priority' => $priority, 'logged' => false); if(! (App::$module == 'setup')) @@ -675,6 +675,13 @@ function logger($msg, $level = LOGGER_NORMAL, $priority = LOG_INFO) { @file_put_contents($pluginfo['filename'], $pluginfo['message'], FILE_APPEND); } +function logid() { + $x = session_id(); + if(! $x) + $x = getmypid(); + return substr(hash('whirlpool',$x),0,10); +} + /** * @brief like logger() but with a function backtrace to pinpoint certain classes * of problems which show up deep in the calling stack. @@ -693,7 +700,7 @@ function btlogger($msg, $level = LOGGER_NORMAL, $priority = LOG_INFO) { if(file_exists(BTLOGGER_DEBUG_FILE) && is_writable(BTLOGGER_DEBUG_FILE)) { $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); $where = basename($stack[0]['file']) . ':' . $stack[0]['line'] . ':' . $stack[1]['function'] . ': '; - $s = datetime_convert('UTC','UTC', 'now', ATOM_TIME) . ':' . log_priority_str($priority) . ':' . session_id() . ':' . $where . $msg . PHP_EOL; + $s = datetime_convert('UTC','UTC', 'now', ATOM_TIME) . ':' . log_priority_str($priority) . ':' . logid() . ':' . $where . $msg . PHP_EOL; @file_put_contents(BTLOGGER_DEBUG_FILE, $s, FILE_APPEND); } @@ -764,7 +771,7 @@ function dlogger($msg, $level = 0) { $where = basename($stack[0]['file']) . ':' . $stack[0]['line'] . ':' . $stack[1]['function'] . ': '; - @file_put_contents($logfile, datetime_convert('UTC','UTC', 'now', ATOM_TIME) . ':' . session_id() . ' ' . $where . $msg . PHP_EOL, FILE_APPEND); + @file_put_contents($logfile, datetime_convert('UTC','UTC', 'now', ATOM_TIME) . ':' . logid() . ' ' . $where . $msg . PHP_EOL, FILE_APPEND); } @@ -816,41 +823,36 @@ function get_tags($s) { // match any double quoted tags - if(preg_match_all('/([@#!]\"\;.*?\"\;)/',$s,$match)) { + if(preg_match_all('/([@#\!]\"\;.*?\"\;)/',$s,$match)) { foreach($match[1] as $mtch) { $ret[] = $mtch; } } - // Match full names against @tags including the space between first and last - // We will look these up afterward to see if they are full names or not recognisable. - - // The lookbehind is used to prevent a match in the middle of a word - // '=' needs to be avoided because when the replacement is made (in handle_tag()) it has to be ignored there - // Feel free to allow '=' if the issue with '=' is solved in handle_tag() - // added / ? and [ to avoid issues with hashchars in url paths - - // added ; to single word tags to allow emojis and other unicode character constructs in bbcode - // (this would actually be &#xnnnnn; but the ampersand will have been escaped to & by the time we see it.) + // match bracket mentions - if(preg_match_all('/(?<![a-zA-Z0-9=\pL\/\?])(@[^ \x0D\x0A,:?\[]+ [^ \x0D\x0A@,:?\[]+)/u',$s,$match)) { + if(preg_match_all('/([@!]\!?\{.*?\})/',$s,$match)) { foreach($match[1] as $mtch) { - if(substr($mtch,-1,1) === '.') - $ret[] = substr($mtch,0,-1); - else - $ret[] = $mtch; + $ret[] = $mtch; } } - // Otherwise pull out single word tags. These can be @nickname, @first_last + // Pull out single word tags. These can be @nickname, @first_last // and #hash tags. - if(preg_match_all('/(?<![a-zA-Z0-9=\pL\/\?\;])([@#\!][^ \x0D\x0A,;:?\[]+)/u',$s,$match)) { + if(preg_match_all('/(?<![a-zA-Z0-9=\pL\/\?\;])([@#\!]\!?[^ \x0D\x0A,;:\?\[\{\&]+)/u',$s,$match)) { foreach($match[1] as $mtch) { + + // Cleanup/ignore false positives + + // Just ignore these rather than try and adjust the regex to deal with them + if(in_array($mtch,[ '@!', '!!' ])) + continue; + // likewise for trailing period. Strip it off rather than complicate the regex further. if(substr($mtch,-1,1) === '.') $mtch = substr($mtch,0,-1); // ignore strictly numeric tags like #1 or #^ bookmarks or ## double hash - if((strpos($mtch,'#') === 0) && ( ctype_digit(substr($mtch,1)) || substr($mtch,1,1) === '^') || substr($mtch,1,1) === '#') + if((strpos($mtch,'#') === 0) && ( ctype_digit(substr($mtch,1)) || in_array(substr($mtch,1,1), [ '^', '#' ]))) continue; // or quote remnants from the quoted strings we already picked out earlier if(strpos($mtch,'"')) @@ -875,7 +877,7 @@ function get_tags($s) { usort($ret,'tag_sort_length'); -// logger('get_tags: ' . print_r($ret,true)); + // logger('get_tags: ' . print_r($ret,true)); return $ret; } @@ -1016,28 +1018,37 @@ function chanlink_cid($d) { function magiclink_url($observer,$myaddr,$url) { return (($observer) - ? z_root() . '/magic?f=&owa=1&dest=' . $url . '&addr=' . $myaddr + ? z_root() . '/magic?f=&owa=1&bdest=' . bin2hex($url) . '&addr=' . $myaddr : $url ); } -function micropro($contact, $redirect = false, $class = '', $textmode = false) { +function micropro($contact, $redirect = false, $class = '', $mode = false) { if($contact['click']) $url = '#'; else $url = chanlink_hash($contact['xchan_hash']); - return replace_macros(get_markup_template(($textmode)?'micropro_txt.tpl':'micropro_img.tpl'),array( + + $tpl = 'micropro_img.tpl'; + if($mode === true) + $tpl = 'micropro_txt.tpl'; + if($mode === 'card') + $tpl = 'micropro_card.tpl'; + + return replace_macros(get_markup_template($tpl), array( '$click' => (($contact['click']) ? $contact['click'] : ''), '$class' => $class . (($contact['archived']) ? ' archived' : ''), '$oneway' => (($contact['oneway']) ? true : false), '$url' => $url, '$photo' => $contact['xchan_photo_s'], '$name' => $contact['xchan_name'], + '$addr' => $contact['xchan_addr'], '$title' => $contact['xchan_name'] . ' [' . $contact['xchan_addr'] . ']', + '$network' => sprintf(t('Network: %s'), $contact['xchan_network']) )); } @@ -1420,15 +1431,21 @@ function unobscure_mail(&$item) { function theme_attachments(&$item) { $arr = json_decode($item['attach'],true); + if(is_array($arr) && count($arr)) { $attaches = array(); foreach($arr as $r) { $icon = getIconFromType($r['type']); - $label = (($r['title']) ? urldecode(htmlspecialchars($r['title'], ENT_COMPAT, 'UTF-8')) : t('Unknown Attachment')); + + if($r['title']) + $label = urldecode(htmlspecialchars($r['title'], ENT_COMPAT, 'UTF-8')); + + if(! $label && $r['href']) + $label = basename($r['href']); //some feeds provide an attachment where title an empty space - if($label == ' ') + if(! $label || $label == ' ') $label = t('Unknown Attachment'); $title = t('Size') . ' ' . (($r['length']) ? userReadableSize($r['length']) : t('unknown')); @@ -1437,7 +1454,7 @@ function theme_attachments(&$item) { if(is_foreigner($item['author_xchan'])) $url = $r['href']; else - $url = z_root() . '/magic?f=&owa=1&hash=' . $item['author_xchan'] . '&dest=' . $r['href'] . '/' . $r['revision']; + $url = z_root() . '/magic?f=&owa=1&hash=' . $item['author_xchan'] . '&bdest=' . bin2hex($r['href'] . '/' . $r['revision']); //$s .= '<a href="' . $url . '" title="' . $title . '" class="attachlink" >' . $icon . '</a>'; $attaches[] = array('label' => $label, 'url' => $url, 'icon' => $icon, 'title' => $title); @@ -2025,22 +2042,40 @@ function item_post_type($item) { function undo_post_tagging($s) { $matches = null; + $x = null; // undo tags and mentions $cnt = preg_match_all('/([@#])(\!*)\[zrl=(.*?)\](.*?)\[\/zrl\]/ism',$s,$matches,PREG_SET_ORDER); if($cnt) { foreach($matches as $mtch) { - $s = str_replace($mtch[0], $mtch[1] . $mtch[2] . quote_tag($mtch[4]),$s); + if($mtch[1] === '@') { + $x = q("select xchan_addr, xchan_url from xchan where xchan_url = '%s' limit 1", + dbesc($mtch[3]) + ); + } + if($x) { + $s = str_replace($mtch[0], $mtch[1] . $mtch[2] . '{' . (($x[0]['xchan_addr']) ? $x[0]['xchan_addr'] : $x[0]['xchan_url']) . '}', $s); + } + else { + $s = str_replace($mtch[0], $mtch[1] . $mtch[2] . quote_tag($mtch[4]),$s); + } } } // undo forum tags $cnt = preg_match_all('/\!\[zrl=(.*?)\](.*?)\[\/zrl\]/ism',$s,$matches,PREG_SET_ORDER); if($cnt) { foreach($matches as $mtch) { - $s = str_replace($mtch[0], '!' . quote_tag($mtch[2]),$s); + $x = q("select xchan_addr, xchan_url from xchan where xchan_url = '%s' limit 1", + dbesc($mtch[1]) + ); + if($x) { + $s = str_replace($mtch[0], '!' . '{' . (($x[0]['xchan_addr']) ? $x[0]['xchan_addr'] : $x[0]['xchan_url']) . '}', $s); + } + else { + $s = str_replace($mtch[0], '!' . quote_tag($mtch[2]),$s); + } } } - return $s; } @@ -2187,6 +2222,7 @@ function ids_to_querystr($arr,$idx = 'id',$quote = false) { * @param $arr array * @param $elm array key to extract from sub-array * @param $delim string default ',' + * @param $each filter function to apply to each element before evaluation, default is 'trim'. * @returns string */ @@ -2516,10 +2552,10 @@ function extra_query_args() { * @param[in,out] string &$str_tags string to add the tag to * @param int $profile_uid * @param string $tag the tag to replace - * @param boolean $diaspora default false + * @param boolean $in_network default true * @return boolean true if replaced, false if not replaced */ -function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $diaspora = false) { +function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $in_network = true) { $replaced = false; $r = null; @@ -2530,9 +2566,10 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $d $termtype = ((strpos($tag,'!') === 0) ? TERM_FORUM : $termtype); $termtype = ((strpos($tag,'#^[') === 0) ? TERM_BOOKMARK : $termtype); - //is it a hash tag? - if(strpos($tag,'#') === 0) { - if(strpos($tag,'#^[') === 0) { + // Is it a hashtag of some kind? + + if ( in_array($termtype, [ TERM_HASHTAG, TERM_BOOKMARK ] )) { + if($termtype === TERM_BOOKMARK) { if(preg_match('/#\^\[(url|zrl)(.*?)\](.*?)\[\/(url|zrl)\]/',$tag,$match)) { $basetag = $match[3]; $url = ((substr($match[2],0,1) === '=') ? substr($match[2],1) : $match[3]); @@ -2541,33 +2578,35 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $d } // if the tag is already replaced... elseif((strpos($tag,'[zrl=')) || (strpos($tag,'[url='))) { - //...do nothing + // ...do nothing return $replaced; } + if(! $replaced) { - // base tag has the tags name only + // double-quoted hashtags: base tag has the htmlentity name only if((substr($tag,0,7) === '#"') && (substr($tag,-6,6) === '"')) { $basetag = substr($tag,7); $basetag = substr($basetag,0,-6); } else - $basetag = str_replace('_',' ',substr($tag,1)); + $basetag = substr($tag,1); + + // create text for link - //create text for link $url = z_root() . '/search?tag=' . rawurlencode($basetag); $newtag = '#[zrl=' . z_root() . '/search?tag=' . rawurlencode($basetag) . ']' . $basetag . '[/zrl]'; - //replace tag by the link. Make sure to not replace something in the middle of a word - // The '=' is needed to not replace color codes if the code is also used as a tag - // Much better would be to somehow completely avoiding things in e.g. [color]-tags. - // This would allow writing things like "my favourite tag=#foobar". + + // replace tag by the link. Make sure to not replace something in the middle of a word + $body = preg_replace('/(?<![a-zA-Z0-9=])'.preg_quote($tag,'/').'/', $newtag, $body); $replaced = true; } - //is the link already in str_tags? + + // is the link already in str_tags? if(! stristr($str_tags,$newtag)) { - //append or set str_tags + // append or set str_tags if(strlen($str_tags)) $str_tags .= ','; @@ -2578,148 +2617,97 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $d 'termtype' => $termtype, 'term' => $basetag, 'url' => $url, - 'contact' => $r[0] + 'contact' => [] ]; + } - //is it a person tag? + // END hashtags - $grouptag = false; + // BEGIN mentions - if(strpos($tag,'!') === 0) { - $grouptag = true; - } - if(strpos($tag,'@') === 0 || $grouptag) { + if ( in_array($termtype, [ TERM_MENTION, TERM_FORUM ] )) { - // The @! tag will alter permissions - $exclusive = (((! $grouptag) && (strpos($tag,'!') === 1) && (! $diaspora)) ? true : false); - if(($grouptag) && (strpos($tag,'!!') === 0)) { - $exclusive = true; - } + // The @! tag and !! tag will alter permissions + + // $in_network is set to false to avoid false positives on posts originating + // on a network which does not implement privacy tags or implements them differently. + + $exclusive = (((strpos(substr($tag,1), '!') === 0) && $in_network) ? true : false); //is it already replaced? - if(strpos($tag,'[zrl=')) + if(strpos($tag,'[zrl=') || strpos($tag,'[url=')) return $replaced; - //get the person's name + // get the channel name + // First extract the name or name fragment we are going to replace - $name = substr($tag,(($exclusive) ? 2 : 1)); // The name or name fragment we are going to replace - $newname = $name; // a copy that we can mess with + $name = substr($tag,(($exclusive) ? 2 : 1)); + $newname = $name; // make a copy that we can mess with $tagcid = 0; $r = null; - // is it some generated name? - - $forum = false; - $trailing_plus_name = false; + // is it some generated (autocompleted) name? - // @channel+ is a forum or network delivery tag - - if(substr($newname,-1,1) === '+') { - $forum = true; + if(substr($name,0,1) === '{' && substr($name,-1,1) === '}') { + $newname = substr($name,1); $newname = substr($newname,0,-1); - } - - // Here we're looking for an address book entry as provided by the auto-completer - // of the form something+nnn where nnn is an abook_id or the first chars of xchan_hash - - - // If there's a +nnn in the string make sure there isn't a space preceding it - - $t1 = strpos($newname,' '); - $t2 = strrpos($newname,'+'); - - if($t1 && $t2 && $t1 < $t2) - $t2 = 0; - - if(($t2) && (! $diaspora)) { - //get the id - - $tagcid = urldecode(substr($newname,$t2 + 1)); - if(strrpos($tagcid,' ')) - $tagcid = substr($tagcid,0,strrpos($tagcid,' ')); - - if(strlen($tagcid) < 16) - $abook_id = intval($tagcid); - //remove the next word from tag's name - if(strpos($name,' ')) { - $name = substr($name,0,strpos($name,' ')); - } - - if($abook_id) { // if there was an id - // select channel with that id from the logged in user's address book - $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash - WHERE abook_id = %d AND abook_channel = %d LIMIT 1", - intval($abook_id), - intval($profile_uid) - ); - } - else { - $r = q("SELECT * FROM xchan - WHERE xchan_hash like '%s%%' LIMIT 1", - dbesc($tagcid) - ); - } + $r = q("select * from xchan where xchan_addr = '%s' or xchan_url = '%s' limit 1", + dbesc($newname), + dbesc($newname) + ); } if(! $r) { // look for matching names in the address book - // Two ways to deal with spaces - double quote the name or use underscores - // we see this after input filtering so quotes have been html entity encoded + // Double quote the entire mentioned term to include special characters + // such as spaces and some punctuation. + + // We see this after input filtering so quotes have been html entity encoded if((substr($name,0,6) === '"') && (substr($name,-6,6) === '"')) { $newname = substr($name,6); $newname = substr($newname,0,-6); } - else - $newname = str_replace('_',' ',$name); - - // do this bit over since we started over with $name - if(substr($newname,-1,1) === '+') { - $forum = true; - $newname = substr($newname,0,-1); - } + // select someone from this user's contacts by name - //select someone from this user's contacts by name $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash WHERE xchan_name = '%s' AND abook_channel = %d LIMIT 1", dbesc($newname), intval($profile_uid) ); - if(! $r) { - //select someone by attag or nick and the name passed in - $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash - WHERE xchan_addr like ('%s') AND abook_channel = %d LIMIT 1", - dbesc(((strpos($newname,'@')) ? $newname : $newname . '@%')), - intval($profile_uid) + // select anybody by full hubloc_addr + + if((! $r) && strpos($newname,'@')) { + $r = q("SELECT * FROM xchan left join hubloc on xchan_hash = hubloc_hash + WHERE hubloc_addr = '%s' LIMIT 1", + dbesc($newname) ); } - if(! $r) { - // it's possible somebody has a name ending with '+', which we stripped off as a forum indicator - // This is very rare but we want to get it right. + // select someone by attag or nick and the name passed in + if(! $r) { $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash - WHERE xchan_name = '%s' AND abook_channel = %d LIMIT 1", - dbesc($newname . '+'), + WHERE xchan_addr like ('%s') AND abook_channel = %d LIMIT 1", + dbesc(((strpos($newname,'@')) ? $newname : $newname . '@%')), intval($profile_uid) ); - if($r) - $trailing_plus_name = true; } + } // $r is set if we found something $channel = App::get_channel(); - + if($r) { $profile = $r[0]['xchan_url']; $newname = $r[0]['xchan_name']; @@ -2757,27 +2745,24 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $d } } - if(($exclusive) && (! $access_tag)) { - $access_tag .= 'cid:' . $channel['channel_hash']; - } - - // if there is an url for this channel + // if there is a url for this channel if(isset($profile)) { $replaced = true; //create profile link $profile = str_replace(',','%2c',$profile); $url = $profile; - if($grouptag) { + if($termtype === TERM_FORUM) { $newtag = '!' . (($exclusive) ? '!' : '') . '[zrl=' . $profile . ']' . $newname . '[/zrl]'; $body = str_replace('!' . (($exclusive) ? '!' : '') . $name, $newtag, $body); } else { - $newtag = '@' . (($exclusive) ? '!' : '') . '[zrl=' . $profile . ']' . $newname . (($forum && ! $trailing_plus_name) ? '+' : '') . '[/zrl]'; + // ( $termtype === TERM_MENTION ) + $newtag = '@' . (($exclusive) ? '!' : '') . '[zrl=' . $profile . ']' . $newname . '[/zrl]'; $body = str_replace('@' . (($exclusive) ? '!' : '') . $name, $newtag, $body); } - //append tag to str_tags + // append tag to str_tags if(! stristr($str_tags,$newtag)) { if(strlen($str_tags)) $str_tags .= ','; @@ -2791,14 +2776,14 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $d 'termtype' => $termtype, 'term' => $newname, 'url' => $url, - 'contact' => $r[0] + 'contact' => (($r) ? $r[0] : []) ]; } -function linkify_tags($a, &$body, $uid, $diaspora = false) { - $str_tags = ''; - $tagged = array(); - $results = array(); +function linkify_tags($a, &$body, $uid, $in_network = true) { + $str_tags = EMPTY_STR; + $tagged = []; + $results = []; $tags = get_tags($body); @@ -2806,20 +2791,7 @@ function linkify_tags($a, &$body, $uid, $diaspora = false) { foreach($tags as $tag) { $access_tag = ''; - // If we already tagged 'Robert Johnson', don't try and tag 'Robert'. - // Robert Johnson should be first in the $tags array - - $fullnametagged = false; - for($x = 0; $x < count($tagged); $x ++) { - if(stristr($tagged[$x],$tag . ' ')) { - $fullnametagged = true; - break; - } - } - if($fullnametagged) - continue; - - $success = handle_tag($a, $body, $access_tag, $str_tags, ($uid) ? $uid : App::$profile_uid , $tag, $diaspora); + $success = handle_tag($a, $body, $access_tag, $str_tags, ($uid) ? $uid : App::$profile_uid , $tag, $in_network); $results[] = array('success' => $success, 'access_tag' => $access_tag); if($success['replaced']) $tagged[] = $tag; @@ -3230,21 +3202,33 @@ function array2XML($obj, $array) { * * @param string $table * @param array $arr + * @param array $binary_fields - fields which will be cleansed with dbescbin rather than dbesc; this is critical for postgres * @return boolean|PDOStatement */ -function create_table_from_array($table, $arr) { +function create_table_from_array($table, $arr, $binary_fields = []) { if(! ($arr && $table)) return false; - if(dbesc_array($arr)) { - $r = dbq("INSERT INTO " . TQUOT . $table . TQUOT . " (" . TQUOT - . implode(TQUOT . ', ' . TQUOT, array_keys($arr)) - . TQUOT . ") VALUES ('" - . implode("', '", array_values($arr)) - . "')" - ); + $clean = []; + foreach($arr as $k => $v) { + $matches = false; + if(preg_match('/([^a-zA-Z0-9\-\_\.])/',$k,$matches)) { + return false; + } + if(in_array($k,$binary_fields)) { + $clean[$k] = dbescbin($v); + } + else { + $clean[$k] = dbesc($v); + } } + $r = dbq("INSERT INTO " . TQUOT . $table . TQUOT . " (" . TQUOT + . implode(TQUOT . ', ' . TQUOT, array_keys($clean)) + . TQUOT . ") VALUES ('" + . implode("', '", array_values($clean)) + . "')" + ); return $r; } @@ -3273,9 +3257,9 @@ function cleanup_bbcode($body) { $body = preg_replace_callback('/\[zrl(.*?)\[\/(zrl)\]/ism','\red_escape_codeblock',$body); - $body = preg_replace_callback("/([^\]\='".'"'."\/]|^|\#\^)(https?\:\/\/[a-zA-Z0-9\pL\:\/\-\?\&\;\.\=\@\_\~\#\%\$\!\\ + $body = preg_replace_callback("/([^\]\='".'"'."\/\{]|^|\#\^)(https?\:\/\/[a-zA-Z0-9\pL\:\/\-\?\&\;\.\=\@\_\~\#\%\$\!\\ +\,\(\)]+)/ismu", '\nakedoembed', $body); - $body = preg_replace_callback("/([^\]\='".'"'."\/]|^|\#\^)(https?\:\/\/[a-zA-Z0-9\pL\:\/\-\?\&\;\.\=\@\_\~\#\%\$\!\\ + $body = preg_replace_callback("/([^\]\='".'"'."\/\{]|^|\#\^)(https?\:\/\/[a-zA-Z0-9\pL\:\/\-\?\&\;\.\=\@\_\~\#\%\$\!\\ +\,\(\)]+)/ismu", '\red_zrl_callback', $body); $body = preg_replace_callback('/\[\$b64zrl(.*?)\[\/(zrl)\]/ism','\red_unescape_codeblock',$body); @@ -3344,6 +3328,9 @@ function featured_sort($a,$b) { } +// Be aware that punify will convert domain names and pathnames + + function punify($s) { require_once('vendor/simplepie/simplepie/idn/idna_convert.class.php'); $x = new idna_convert(['encoding' => 'utf8']); @@ -3351,6 +3338,8 @@ function punify($s) { } +// Be aware that unpunify will only convert domain names and not pathnames + function unpunify($s) { require_once('vendor/simplepie/simplepie/idn/idna_convert.class.php'); $x = new idna_convert(['encoding' => 'utf8']); @@ -3358,3 +3347,68 @@ function unpunify($s) { } + +function unique_multidim_array($array, $key) { + $temp_array = array(); + $i = 0; + $key_array = array(); + + foreach($array as $val) { + if (!in_array($val[$key], $key_array)) { + $key_array[$i] = $val[$key]; + $temp_array[$i] = $val; + } + $i++; + } + return $temp_array; +} + +function get_forum_channels($uid) { + + if(! $uid) + return; + + $xf = false; + + $x1 = q("select xchan from abconfig where chan = %d and cat = 'their_perms' and k = 'send_stream' and v = '0'", + intval($uid) + ); + if($x1) { + $xc = ids_to_querystr($x1,'xchan',true); + + $x2 = q("select xchan from abconfig where chan = %d and cat = 'their_perms' and k = 'tag_deliver' and v = '1' and xchan in (" . $xc . ") ", + intval($uid) + ); + + if($x2) { + $xf = ids_to_querystr($x2,'xchan',true); + + // private forums + $x3 = q("select xchan from abconfig where chan = %d and cat = 'their_perms' and k = 'post_wall' and v = '1' and xchan in (" . $xc . ") and not xchan in (" . $xf . ") ", + intval(local_channel()) + ); + if($x3) { + $xf = ids_to_querystr(array_merge($x2,$x3),'xchan',true); + } + } + } + + $sql_extra = (($xf) ? " and ( xchan_hash in (" . $xf . ") or xchan_pubforum = 1 ) " : " and xchan_pubforum = 1 "); + + $r = q("select abook_id, xchan_hash, xchan_name, xchan_url, xchan_photo_s from abook left join xchan on abook_xchan = xchan_hash where xchan_deleted = 0 and abook_channel = %d and abook_pending = 0 and abook_ignored = 0 and abook_blocked = 0 and abook_archived = 0 $sql_extra order by xchan_name", + intval($uid) + ); + + for($x = 0; $x < count($r); $x ++) { + if($x3) { + foreach($x3 as $xx) { + if($r[$x]['xchan_hash'] == $xx['xchan']) { + $r[$x]['private_forum'] = 1; + } + } + } + } + + return $r; + +} diff --git a/include/zot.php b/include/zot.php index 8e14bb37c..19e1298c3 100644 --- a/include/zot.php +++ b/include/zot.php @@ -1787,6 +1787,10 @@ function process_delivery($sender, $arr, $deliveries, $relay, $public = false, $ $local_public = false; continue; } + if(! \Zotlabs\Lib\MessageFilter::evaluate($arr,get_config('system','pubstream_incl'),get_config('system','pubstream_excl'))) { + $local_public = false; + continue; + } } $tag_delivery = tgroup_check($channel['channel_id'],$arr); @@ -1923,6 +1927,7 @@ function process_delivery($sender, $arr, $deliveries, $relay, $public = false, $ continue; } + $r = q("select * from item where mid = '%s' and uid = %d limit 1", dbesc($arr['mid']), intval($channel['channel_id']) |