aboutsummaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
authorMario Vavti <mario@mariovavti.com>2018-07-25 10:19:19 +0200
committerMario Vavti <mario@mariovavti.com>2018-07-25 10:19:19 +0200
commit1b1d11dcf1091158232e98abad966d4900e2ccc9 (patch)
tree380d5e04c73391089bf3d658ea4b27eecffa4916 /include
parentb655d04b3474893ee3dea99b77f2e7dd764729a0 (diff)
parent35200e5f1b10cdd18af8f0ea646996e438b97011 (diff)
downloadvolse-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.php7
-rw-r--r--include/attach.php2
-rw-r--r--include/auth.php178
-rw-r--r--include/channel.php20
-rw-r--r--include/connections.php15
-rw-r--r--include/conversation.php43
-rw-r--r--include/features.php42
-rw-r--r--include/feedutils.php12
-rw-r--r--include/follow.php4
-rw-r--r--include/group.php8
-rw-r--r--include/import.php20
-rwxr-xr-xinclude/items.php163
-rw-r--r--include/js_strings.php10
-rw-r--r--include/language.php2
-rw-r--r--include/markdown.php4
-rw-r--r--include/menu.php5
-rw-r--r--include/nav.php42
-rw-r--r--include/network.php31
-rw-r--r--include/oauth.php2
-rwxr-xr-xinclude/oembed.php20
-rw-r--r--include/photos.php59
-rwxr-xr-xinclude/plugin.php10
-rw-r--r--include/security.php8
-rw-r--r--include/text.php408
-rw-r--r--include/zot.php5
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('/([@#!]\&quot\;.*?\&quot\;)/',$s,$match)) {
+ if(preg_match_all('/([@#\!]\&quot\;.*?\&quot\;)/',$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 &amp; 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,'&quot'))
@@ -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) === '#&quot;') && (substr($tag,-6,6) === '&quot;')) {
$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) === '&quot;') && (substr($name,-6,6) === '&quot;')) {
$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'])