2016-04-19 03:38:38 +00:00
< ? php
2017-12-23 13:42:23 +00:00
2016-04-19 03:38:38 +00:00
namespace Zotlabs\Module ;
2018-06-05 06:19:16 +00:00
use Zotlabs\Lib\Libzotdir ;
2019-02-14 23:26:37 +00:00
use Zotlabs\Lib\AccessList ;
2019-11-21 02:50:05 +00:00
use Zotlabs\Web\Controller ;
2018-06-05 06:19:16 +00:00
2018-07-03 23:40:54 +00:00
require_once ( 'include/acl_selectors.php' );
2017-12-23 13:42:23 +00:00
/**
* @ brief ACL selector json backend .
*
2016-07-14 00:51:19 +00:00
* This module provides JSON lists of connections and local / remote channels
* ( xchans ) to populate various tools such as the ACL ( AccessControlList ) popup
2017-12-23 13:42:23 +00:00
* and various auto - complete functions ( such as email recipients , search , and
2016-07-14 00:51:19 +00:00
* mention targets .
2017-12-23 13:42:23 +00:00
*
2016-07-14 00:51:19 +00:00
* There are two primary output structural formats . One for the ACL widget and
* the other for auto - completion .
2017-12-23 13:42:23 +00:00
*
* Many of the behaviour variations are triggered on the use of single character
* keys however this functionality has grown in an ad - hoc manner and has gotten
* quite messy over time .
2016-07-14 00:51:19 +00:00
*/
2019-11-21 02:50:05 +00:00
class Acl extends Controller {
2016-04-19 03:38:38 +00:00
2017-02-14 01:51:39 +00:00
function init () {
2017-12-23 13:42:23 +00:00
2019-11-19 23:01:19 +00:00
// logger('mod_acl: ' . print_r($_REQUEST,true),LOGGER_DATA);
2017-12-23 13:42:23 +00:00
2016-07-14 00:51:19 +00:00
$start = ( x ( $_REQUEST , 'start' ) ? $_REQUEST [ 'start' ] : 0 );
$count = ( x ( $_REQUEST , 'count' ) ? $_REQUEST [ 'count' ] : 500 );
$search = ( x ( $_REQUEST , 'search' ) ? $_REQUEST [ 'search' ] : '' );
$type = ( x ( $_REQUEST , 'type' ) ? $_REQUEST [ 'type' ] : '' );
2017-12-23 13:42:23 +00:00
$noforums = ( x ( $_REQUEST , 'n' ) ? $_REQUEST [ 'n' ] : false );
2016-07-14 00:51:19 +00:00
2017-12-23 13:42:23 +00:00
// $type =
2016-07-14 00:51:19 +00:00
// '' => standard ACL request
// 'g' => Groups only ACL request
2017-09-25 02:45:19 +00:00
// 'f' => forums only ACL request
2016-07-14 00:51:19 +00:00
// 'c' => Connections only ACL request or editor (textarea) mention request
// $_REQUEST['search'] contains ACL search text.
// $type =
// 'm' => autocomplete private mail recipient (checks post_mail permission)
// 'a' => autocomplete connections (mod_connections, mod_poke, mod_sources, mod_photos)
// 'x' => nav search bar autocomplete (match any xchan)
2020-06-11 00:10:39 +00:00
// 'z' => autocomplete any xchan, but also include abook_alias, requires non-zero local_channel()
// and also contains xid without urlencode, used specifically by activity_filter widget
2016-07-14 00:51:19 +00:00
// $_REQUEST['query'] contains autocomplete search text.
2016-04-19 03:38:38 +00:00
2016-07-14 00:51:19 +00:00
// The different autocomplete libraries use different names for the search text
2017-02-14 01:51:39 +00:00
// parameter. Internally we'll use $search to represent the search text no matter
2016-07-14 00:51:19 +00:00
// what request variable it was attached to.
2016-04-19 03:38:38 +00:00
2019-11-21 02:50:05 +00:00
if ( array_key_exists ( 'query' , $_REQUEST )) {
2016-04-19 03:38:38 +00:00
$search = $_REQUEST [ 'query' ];
}
2019-11-21 02:50:05 +00:00
if ( ( ! local_channel ()) && ( ! in_array ( $type , [ 'x' , 'c' , 'f' ]))) {
2016-07-14 00:51:19 +00:00
killme ();
2019-11-21 02:50:05 +00:00
}
2016-07-14 02:53:28 +00:00
$permitted = [];
2020-09-07 05:46:07 +00:00
if ( in_array ( $type , [ 'm' , 'a' , 'f' ])) {
2016-07-14 02:53:28 +00:00
// These queries require permission checking. We'll create a simple array of xchan_hash for those with
// the requisite permissions which we can check against.
2018-05-26 22:12:02 +00:00
$x = q ( " select xchan from abconfig where chan = %d and cat = 'system' and k = 'their_perms' and v like '%s' " ,
2016-07-14 02:53:28 +00:00
intval ( local_channel ()),
2018-05-24 05:50:33 +00:00
dbesc (( $type === 'm' ) ? '%post_mail%' : '%tag_deliver%' )
2016-07-14 02:53:28 +00:00
);
$permitted = ids_to_array ( $x , 'xchan' );
}
2019-11-21 02:50:05 +00:00
if ( $search ) {
2018-09-26 00:47:43 +00:00
$sql_extra = " AND pgrp.gname LIKE " . protect_sprintf ( " '% " . dbesc ( $search ) . " %' " ) . " " ;
2020-06-18 00:54:52 +00:00
// sql_extra2 is typically used when we don't have a local_channel - so we are not search abook_alias
$sql_extra2 = " AND ( xchan_name LIKE " . protect_sprintf ( " '% " . dbesc ( $search ) . " %' " ) . " OR xchan_addr LIKE " . protect_sprintf ( " '% " . dbesc ( punify ( $search )) . (( strpos ( $search , '@' ) === false ) ? " %@%' " : " %' " )) . " ) " ;
2019-09-19 22:30:17 +00:00
2016-07-14 00:51:19 +00:00
// This horrible mess is needed because position also returns 0 if nothing is found.
// Would be MUCH easier if it instead returned a very large value
// Otherwise we could just
// order by LEAST(POSITION($search IN xchan_name),POSITION($search IN xchan_addr)).
$order_extra2 = " CASE WHEN xchan_name LIKE "
. protect_sprintf ( " '% " . dbesc ( $search ) . " %' " )
2017-01-12 21:05:36 +00:00
. " then POSITION(' " . protect_sprintf ( dbesc ( $search ))
2018-03-15 01:42:07 +00:00
. " ' IN xchan_name) else position(' " . protect_sprintf ( dbesc ( punify ( $search ))) . " ' IN xchan_addr) end, " ;
2016-07-14 00:51:19 +00:00
2020-06-10 04:41:08 +00:00
$sql_extra3 = " AND ( xchan_addr like " . protect_sprintf ( " '% " . dbesc ( punify ( $search )) . " %' " ) . " OR xchan_name like " . protect_sprintf ( " '% " . dbesc ( $search ) . " %' " ) . " OR abook_alias like " . protect_sprintf ( " '% " . dbesc ( $search ) . " %' " ) . " ) " ;
2019-09-19 22:30:17 +00:00
$sql_extra4 = " AND ( xchan_name LIKE " . protect_sprintf ( " '% " . dbesc ( $search ) . " %' " ) . " OR xchan_addr LIKE " . protect_sprintf ( " '% " . dbesc ( punify ( $search )) . (( strpos ( $search , '@' ) === false ) ? " %@%' " : " %' " )) . " OR abook_alias LIKE " . protect_sprintf ( " '% " . dbesc ( $search ) . " %' " ) . " ) " ;
2016-07-14 00:51:19 +00:00
}
else {
2019-09-19 22:30:17 +00:00
$sql_extra = $sql_extra2 = $sql_extra3 = $sql_extra4 = " " ;
2016-04-19 03:38:38 +00:00
}
2019-11-21 02:50:05 +00:00
$groups = [];
$contacts = [];
2016-04-19 03:38:38 +00:00
2019-11-21 02:50:05 +00:00
if ( $type == '' || $type == 'g' ) {
2017-02-12 23:56:33 +00:00
2017-02-14 01:51:39 +00:00
// Normal privacy groups
2018-09-26 00:47:43 +00:00
$r = q ( " SELECT pgrp.id, pgrp.hash, pgrp.gname
FROM pgrp , pgrp_member
WHERE pgrp . deleted = 0 AND pgrp . uid = % d
AND pgrp_member . gid = pgrp . id
2016-04-19 03:38:38 +00:00
$sql_extra
2018-09-26 00:47:43 +00:00
GROUP BY pgrp . id
ORDER BY pgrp . gname
2016-04-19 03:38:38 +00:00
LIMIT % d OFFSET % d " ,
intval ( local_channel ()),
intval ( $count ),
intval ( $start )
);
2016-06-10 05:07:27 +00:00
2019-11-21 02:50:05 +00:00
if ( $r ) {
2020-06-11 00:10:39 +00:00
foreach ( $r as $g ) {
2019-10-02 01:29:39 +00:00
// logger('acl: group: ' . $g['gname'] . ' members: ' . AccessList::members_xchan(local_channel(),$g['id']));
2019-11-21 02:50:05 +00:00
$groups [] = [
2016-06-10 05:07:27 +00:00
" type " => " g " ,
" photo " => " images/twopeople.png " ,
" name " => $g [ 'gname' ],
" id " => $g [ 'id' ],
" xid " => $g [ 'hash' ],
2019-10-02 01:29:39 +00:00
" uids " => AccessList :: members_xchan ( local_channel (), $g [ 'id' ]),
2016-06-10 05:07:27 +00:00
" link " => ''
2019-11-21 02:50:05 +00:00
];
2016-06-10 05:07:27 +00:00
}
2016-04-19 03:38:38 +00:00
}
}
2019-11-21 02:50:05 +00:00
if ( $type == '' || $type == 'c' || $type === 'f' ) {
2020-09-07 05:46:07 +00:00
2016-04-19 03:38:38 +00:00
// Getting info from the abook is better for local users because it contains info about permissions
2019-11-21 02:50:05 +00:00
if ( local_channel ()) {
2016-07-15 03:26:22 +00:00
2017-02-14 01:51:39 +00:00
// add connections
2019-09-19 22:30:17 +00:00
2019-03-06 01:27:16 +00:00
$r = q ( " SELECT abook_id as id, xchan_hash as hash, xchan_name as name, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, xchan_type, abook_flags, abook_self
2016-04-19 03:38:38 +00:00
FROM abook left join xchan on abook_xchan = xchan_hash
2020-09-07 05:46:07 +00:00
WHERE ( abook_channel = % d $extra_channels_sql ) AND abook_blocked = 0 and abook_pending = 0 and xchan_deleted = 0 $sql_extra4 order by xchan_name asc limit $count " ,
2016-04-19 03:38:38 +00:00
intval ( local_channel ())
);
2019-09-19 22:30:17 +00:00
2019-11-21 02:50:05 +00:00
if ( $r && $r2 ) {
2016-07-15 03:26:22 +00:00
$r = array_merge ( $r2 , $r );
2019-11-21 02:50:05 +00:00
}
2016-04-19 03:38:38 +00:00
}
else { // Visitors
2018-05-24 05:50:33 +00:00
$r = q ( " SELECT xchan_hash as id, xchan_hash as hash, xchan_name as name, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, 0 as abook_flags, 0 as abook_self
2016-04-19 03:38:38 +00:00
FROM xchan left join xlink on xlink_link = xchan_hash
2020-09-07 05:46:07 +00:00
WHERE xlink_xchan = '%s' AND xchan_deleted = 0 $sql_extra2 order by $order_extra2 xchan_name asc limit $count " ,
2016-04-19 03:38:38 +00:00
dbesc ( get_observer_hash ())
);
}
2019-11-21 02:50:05 +00:00
if (( count ( $r ) < 100 ) && $type == 'c' ) {
2020-08-26 03:27:06 +00:00
$r2 = q ( " SELECT xchan_hash as id, xchan_hash as hash, xchan_name as name, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, 0 as abook_flags, 0 as abook_self
2020-09-07 05:46:07 +00:00
FROM xchan WHERE xchan_deleted = 0 and xchan_network != 'unknown' $sql_extra2 order by $order_extra2 xchan_name asc limit $count "
2018-04-26 01:41:19 +00:00
);
2019-11-21 02:50:05 +00:00
if ( $r2 ) {
2018-04-26 01:41:19 +00:00
$r = array_merge ( $r , $r2 );
$r = unique_multidim_array ( $r , 'hash' );
}
2016-04-19 03:38:38 +00:00
}
2020-09-07 05:46:07 +00:00
2016-04-19 03:38:38 +00:00
}
2019-11-21 02:50:05 +00:00
elseif ( $type == 'm' ) {
2016-07-20 20:39:10 +00:00
2019-11-21 02:50:05 +00:00
$r = [];
2016-07-20 20:39:10 +00:00
$z = q ( " SELECT xchan_hash as hash, xchan_name as name, xchan_addr as nick, xchan_photo_s as micro, xchan_url as url
2016-04-19 03:38:38 +00:00
FROM abook left join xchan on abook_xchan = xchan_hash
2016-07-14 02:53:28 +00:00
WHERE abook_channel = % d
2016-04-19 03:38:38 +00:00
and xchan_deleted = 0
$sql_extra3
2016-07-14 02:53:28 +00:00
ORDER BY xchan_name ASC " ,
intval ( local_channel ())
2016-04-19 03:38:38 +00:00
);
2019-11-21 02:50:05 +00:00
if ( $z ) {
foreach ( $z as $zz ) {
if ( in_array ( $zz [ 'hash' ], $permitted )) {
2016-07-14 02:53:28 +00:00
$r [] = $zz ;
}
}
}
2016-04-19 03:38:38 +00:00
}
2019-11-21 02:50:05 +00:00
elseif ( $type == 'a' ) {
2016-04-19 03:38:38 +00:00
2018-05-24 05:50:33 +00:00
$r = q ( " SELECT abook_id as id, xchan_name as name, xchan_hash as hash, xchan_addr as nick, xchan_photo_s as micro, xchan_network as network, xchan_url as url, xchan_addr as attag FROM abook left join xchan on abook_xchan = xchan_hash
2016-04-19 03:38:38 +00:00
WHERE abook_channel = % d
and xchan_deleted = 0
$sql_extra3
ORDER BY xchan_name ASC " ,
intval ( local_channel ())
);
}
2020-06-11 00:10:39 +00:00
elseif ( $type == 'z' ) {
$r = q ( " SELECT xchan_name as name, xchan_hash as hash, xchan_addr as nick, xchan_photo_s as micro, xchan_network as network, xchan_url as url, xchan_addr as attag FROM xchan left join abook on xchan_hash = abook_xchan
WHERE ( abook_channel = % d OR abook_channel IS NULL )
and xchan_deleted = 0
$sql_extra3
ORDER BY xchan_name ASC " ,
intval ( local_channel ())
);
}
2019-11-21 02:50:05 +00:00
elseif ( $type == 'x' ) {
$contacts = [];
2019-11-19 23:01:19 +00:00
$r = $this -> navbar_complete ();
2019-11-21 02:50:05 +00:00
if ( $r ) {
foreach ( $r as $g ) {
$contacts [] = [
2016-04-19 03:38:38 +00:00
" photo " => $g [ 'photo' ],
" name " => $g [ 'name' ],
2019-09-23 03:56:30 +00:00
" nick " => $g [ 'address' ],
2020-06-10 03:42:01 +00:00
'link' => (( $g [ 'address' ]) ? $g [ 'address' ] : $g [ 'url' ]),
2020-06-10 04:34:31 +00:00
'xchan' => $g [ 'hash' ]
2019-11-21 02:50:05 +00:00
];
2016-04-19 03:38:38 +00:00
}
}
2019-11-21 02:50:05 +00:00
$o = [
2016-04-19 03:38:38 +00:00
'start' => $start ,
'count' => $count ,
'items' => $contacts ,
2019-11-21 02:50:05 +00:00
];
json_return_and_die ( $o );
}
else {
$r = [];
2016-04-19 03:38:38 +00:00
}
2019-11-21 02:50:05 +00:00
if ( $r ) {
foreach ( $r as $g ) {
2016-04-19 03:38:38 +00:00
2019-11-21 02:50:05 +00:00
if ( in_array ( $g [ 'network' ],[ 'rss' , 'anon' , 'unknown' ]) && ( $type != 'a' )) {
2017-08-31 01:55:56 +00:00
continue ;
2019-11-21 02:50:05 +00:00
}
2017-08-31 01:55:56 +00:00
2020-06-11 00:10:39 +00:00
// 'z' (activity_filter autocomplete) requires an un-encoded hash to prevent double encoding
if ( $type !== 'z' ) {
$g [ 'hash' ] = urlencode ( $g [ 'hash' ]);
}
2017-09-18 06:54:40 +00:00
2019-11-21 02:50:05 +00:00
if ( ! $g [ 'nick' ]) {
2018-04-26 09:42:48 +00:00
$g [ 'nick' ] = $g [ 'url' ];
2017-09-18 06:54:40 +00:00
}
2019-11-21 02:50:05 +00:00
if ( in_array ( $g [ 'hash' ], $permitted ) && $type === 'f' && ( ! $noforums )) {
$contacts [] = [
2016-04-19 03:38:38 +00:00
" type " => " c " ,
" photo " => " images/twopeople.png " ,
2018-04-26 09:42:48 +00:00
" name " => $g [ 'name' ],
" id " => urlencode ( $g [ 'id' ]),
2017-10-03 22:43:51 +00:00
" xid " => $g [ 'hash' ],
2018-04-24 22:32:24 +00:00
" link " => (( $g [ 'nick' ]) ? $g [ 'nick' ] : $g [ 'url' ]),
2016-04-19 03:38:38 +00:00
" nick " => substr ( $g [ 'nick' ], 0 , strpos ( $g [ 'nick' ], '@' )),
" self " => ( intval ( $g [ 'abook_self' ]) ? 'abook-self' : '' ),
" taggable " => 'taggable' ,
" label " => t ( 'network' )
2019-11-21 02:50:05 +00:00
];
2016-04-19 03:38:38 +00:00
}
2019-11-21 02:50:05 +00:00
if ( $type !== 'f' ) {
$contacts [] = [
2017-09-25 02:45:19 +00:00
" type " => " c " ,
" photo " => $g [ 'micro' ],
" name " => $g [ 'name' ],
2017-10-03 22:43:51 +00:00
" id " => urlencode ( $g [ 'id' ]),
" xid " => $g [ 'hash' ],
2018-04-24 22:32:24 +00:00
" link " => (( $g [ 'nick' ]) ? $g [ 'nick' ] : $g [ 'url' ]),
2018-04-26 09:42:48 +00:00
" nick " => (( strpos ( $g [ 'nick' ], '@' )) ? substr ( $g [ 'nick' ], 0 , strpos ( $g [ 'nick' ], '@' )) : $g [ 'nick' ]),
2017-09-25 02:45:19 +00:00
" self " => ( intval ( $g [ 'abook_self' ]) ? 'abook-self' : '' ),
" taggable " => '' ,
" label " => '' ,
2019-11-21 02:50:05 +00:00
];
2017-09-25 02:45:19 +00:00
}
2016-04-19 03:38:38 +00:00
}
}
$items = array_merge ( $groups , $contacts );
2019-11-21 02:50:05 +00:00
$o = [
2016-04-19 03:38:38 +00:00
'start' => $start ,
'count' => $count ,
'items' => $items ,
2019-11-21 02:50:05 +00:00
];
2017-12-23 13:42:23 +00:00
2019-11-21 02:50:05 +00:00
json_return_and_die ( $o );
2016-04-19 03:38:38 +00:00
}
2017-12-23 13:42:23 +00:00
2019-11-19 23:01:19 +00:00
function navbar_complete () {
2016-04-19 03:38:38 +00:00
// logger('navbar_complete');
2019-11-21 02:50:05 +00:00
if ( observer_prohibited ()) {
2016-04-19 03:38:38 +00:00
return ;
}
$dirmode = intval ( get_config ( 'system' , 'directory_mode' ));
$search = (( x ( $_REQUEST , 'search' )) ? htmlentities ( $_REQUEST [ 'search' ], ENT_COMPAT , 'UTF-8' , false ) : '' );
2019-11-21 02:50:05 +00:00
if ( ! $search || mb_strlen ( $search ) < 2 ) {
return [];
}
2016-04-19 03:38:38 +00:00
$star = false ;
$address = false ;
2019-11-21 02:50:05 +00:00
if ( substr ( $search , 0 , 1 ) === '@' ) {
2016-04-19 03:38:38 +00:00
$search = substr ( $search , 1 );
2019-11-21 02:50:05 +00:00
}
2016-04-19 03:38:38 +00:00
2019-11-21 02:50:05 +00:00
if ( substr ( $search , 0 , 1 ) === '*' ) {
2016-04-19 03:38:38 +00:00
$star = true ;
$search = substr ( $search , 1 );
}
2020-06-11 00:10:39 +00:00
2019-11-21 02:50:05 +00:00
if ( strpos ( $search , '@' ) !== false ) {
2016-04-19 03:38:38 +00:00
$address = true ;
}
2019-09-23 03:56:30 +00:00
2020-09-01 02:24:21 +00:00
$url = z_root () . '/dirsearch' ;
2017-05-11 00:02:56 +00:00
2020-09-01 02:24:21 +00:00
2019-11-19 23:01:19 +00:00
$results = [];
2016-07-14 00:51:19 +00:00
$count = ( x ( $_REQUEST , 'count' ) ? $_REQUEST [ 'count' ] : 100 );
2020-09-01 02:24:21 +00:00
2019-11-21 02:50:05 +00:00
if ( $url ) {
2020-09-01 02:24:21 +00:00
$query = $url . '?f=' ;
2018-03-15 01:42:07 +00:00
$query .= '&name=' . urlencode ( $search ) . " &limit= $count " . (( $address ) ? '&address=' . urlencode ( punify ( $search )) : '' );
2020-06-11 00:10:39 +00:00
2016-04-19 03:38:38 +00:00
$x = z_fetch_url ( $query );
2019-11-21 02:50:05 +00:00
if ( $x [ 'success' ]) {
2016-04-19 03:38:38 +00:00
$t = 0 ;
$j = json_decode ( $x [ 'body' ], true );
2019-11-21 02:50:05 +00:00
if ( $j && $j [ 'results' ]) {
2019-09-23 03:56:30 +00:00
$results = $j [ 'results' ];
2016-04-19 03:38:38 +00:00
}
}
}
2019-09-23 03:56:30 +00:00
return $results ;
2016-04-19 03:38:38 +00:00
}
}