2014-11-13 18:31:36 +01:00
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* EGroupware API: VFS sharing
|
|
|
|
*
|
|
|
|
* @link http://www.egroupware.org
|
|
|
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
|
|
|
* @package api
|
2016-03-20 17:19:53 +01:00
|
|
|
* @subpackage Vfs
|
2014-11-13 18:31:36 +01:00
|
|
|
* @author Ralf Becker <rb@stylite.de>
|
2016-03-20 17:19:53 +01:00
|
|
|
* @copyright (c) 2014-16 by Ralf Becker <rb@stylite.de>
|
2014-11-13 18:31:36 +01:00
|
|
|
* @version $Id$
|
|
|
|
*/
|
|
|
|
|
2016-03-20 17:19:53 +01:00
|
|
|
namespace EGroupware\Api\Vfs;
|
|
|
|
|
|
|
|
use EGroupware\Api;
|
|
|
|
use EGroupware\Api\Vfs;
|
|
|
|
use filemanager_ui;
|
|
|
|
|
2014-11-13 18:31:36 +01:00
|
|
|
/**
|
|
|
|
* VFS sharing
|
|
|
|
*
|
|
|
|
* Token generation uses openssl_random_pseudo_bytes, if available, otherwise
|
2016-03-20 17:19:53 +01:00
|
|
|
* mt_rand based Api\Auth::randomstring is used.
|
2014-11-18 13:55:32 +01:00
|
|
|
*
|
2015-01-29 14:54:34 +01:00
|
|
|
* Existing user sessions are kept whenever possible by an additional mount into regular VFS:
|
|
|
|
* - share owner is current user (no problems with rights, they simply match)
|
|
|
|
* - share owner has owner-right for share: we create a temp. eACL for current user
|
|
|
|
* --> in all other cases session will be replaced with one of the anonymous user,
|
|
|
|
* as we dont support mounting with rights of share owner (VFS uses Vfs::$user!)
|
|
|
|
*
|
|
|
|
* @todo handle mounts of an entry directory /apps/$app/$id
|
2014-11-18 13:55:32 +01:00
|
|
|
* @todo handle mounts inside shared directory (they get currently lost)
|
|
|
|
* @todo handle absolute symlinks (wont work as we use share as root)
|
2014-11-13 18:31:36 +01:00
|
|
|
*/
|
2018-05-30 17:37:16 +02:00
|
|
|
class Sharing extends \EGroupware\Api\Sharing
|
2014-11-13 18:31:36 +01:00
|
|
|
{
|
|
|
|
|
2014-12-03 17:25:10 +01:00
|
|
|
/**
|
|
|
|
* Modes ATTACH is NOT a sharing mode, but it is traditional mode in email
|
|
|
|
*/
|
|
|
|
const ATTACH = 'attach';
|
|
|
|
const LINK = 'link';
|
|
|
|
const READONLY = 'share_ro';
|
|
|
|
const WRITABLE = 'share_rw';
|
|
|
|
|
2020-03-25 03:34:04 +01:00
|
|
|
const HIDDEN_UPLOAD = 9; // 8 is the next bitwise flag + 1 for writable
|
|
|
|
const HIDDEN_UPLOAD_DIR = '/Upload';
|
|
|
|
|
2014-12-03 17:25:10 +01:00
|
|
|
/**
|
|
|
|
* Modes for sharing files
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
static $modes = array(
|
|
|
|
self::ATTACH => array(
|
|
|
|
'label' => 'Attachment',
|
|
|
|
'title' => 'Works reliable for total size up to 1-2 MB, might work for 5-10 MB, most likely to fail for >10MB',
|
|
|
|
),
|
|
|
|
self::LINK => array(
|
|
|
|
'label' => 'Download link',
|
|
|
|
'title' => 'Link is appended to mail allowing recipients to download currently attached version of files',
|
|
|
|
),
|
|
|
|
self::READONLY => array(
|
|
|
|
'label' => 'Readonly share',
|
|
|
|
'title' => 'Link is appended to mail allowing recipients to download up to date version of files',
|
|
|
|
),
|
|
|
|
self::WRITABLE => array(
|
|
|
|
'label' => 'Writable share',
|
|
|
|
'title' => 'Link is appended to mail allowing recipients to download or modify up to date version of files (EPL only)'
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
2014-11-18 13:55:32 +01:00
|
|
|
/**
|
|
|
|
* Create sharing session
|
|
|
|
*
|
2015-02-28 23:08:23 +01:00
|
|
|
* Certain cases:
|
|
|
|
* a) there is not session $keep_session === null
|
|
|
|
* --> create new anon session with just filemanager rights and share as fstab
|
|
|
|
* b) there is a session $keep_session === true
|
|
|
|
* b1) current user is share owner (eg. checking the link)
|
|
|
|
* --> mount share under token additionally
|
|
|
|
* b2) current user not share owner
|
|
|
|
* b2a) need/use filemanager UI (eg. directory)
|
|
|
|
* --> destroy current session and continue with a)
|
|
|
|
* b2b) single file or WebDAV
|
|
|
|
* --> modify EGroupware enviroment for that request only, no change in session
|
|
|
|
*
|
|
|
|
* @param boolean $keep_session =null null: create a new session, true: try mounting it into existing (already verified) session
|
2014-11-18 13:55:32 +01:00
|
|
|
* @return string with sessionid, does NOT return if no session created
|
|
|
|
*/
|
2018-05-30 17:37:16 +02:00
|
|
|
public static function setup_share($keep_session, &$share)
|
2014-11-18 13:55:32 +01:00
|
|
|
{
|
2015-03-02 22:09:08 +01:00
|
|
|
// need to reset fs_tab, as resolve_url does NOT work with just share mounted
|
2020-03-05 11:53:26 +01:00
|
|
|
if (empty($GLOBALS['egw_info']['server']['vfs_fstab']) || count($GLOBALS['egw_info']['server']['vfs_fstab']) <= 1)
|
2015-03-02 22:09:08 +01:00
|
|
|
{
|
|
|
|
unset($GLOBALS['egw_info']['server']['vfs_fstab']); // triggers reset of fstab in mount()
|
2016-03-20 17:19:53 +01:00
|
|
|
$GLOBALS['egw_info']['server']['vfs_fstab'] = Vfs::mount();
|
|
|
|
Vfs::clearstatcache();
|
2015-03-02 22:09:08 +01:00
|
|
|
}
|
2016-03-20 17:19:53 +01:00
|
|
|
$share['resolve_url'] = Vfs::resolve_url($share['share_path'], true, true, true, true); // true = fix evtl. contained url parameter
|
2014-11-14 09:50:05 +01:00
|
|
|
// if share not writable append ro=1 to mount url to make it readonly
|
2018-03-21 12:43:25 +01:00
|
|
|
if (!($share['share_writable'] & 1))
|
2014-11-14 09:50:05 +01:00
|
|
|
{
|
|
|
|
$share['resolve_url'] .= (strpos($share['resolve_url'], '?') ? '&' : '?').'ro=1';
|
|
|
|
}
|
2014-11-13 18:31:36 +01:00
|
|
|
//_debug_array($share);
|
|
|
|
|
2020-04-04 00:20:47 +02:00
|
|
|
$share['share_root'] = '/'.Vfs::basename($share['share_path']);
|
2015-01-20 23:11:35 +01:00
|
|
|
if ($keep_session) // add share to existing session
|
|
|
|
{
|
2015-02-28 23:08:23 +01:00
|
|
|
// if current user is not the share owner, we cant just mount share
|
2016-03-20 17:19:53 +01:00
|
|
|
if (Vfs::$user != $share['share_owner'])
|
2015-01-20 23:11:35 +01:00
|
|
|
{
|
2015-02-28 23:08:23 +01:00
|
|
|
$keep_session = false;
|
2015-01-20 23:11:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!$keep_session) // do NOT change to else, as we might have set $keep_session=false!
|
|
|
|
{
|
2019-09-18 19:54:08 +02:00
|
|
|
// only allow filemanager app & collabora
|
2020-03-19 17:41:45 +01:00
|
|
|
// In some cases, $GLOBALS['egw_info']['apps'] is not yet set at all. Set it to app => true, it will be used
|
|
|
|
// in Session->read_repositories() to make sure we get access to these apps when the session loads the apps.
|
2019-09-18 19:54:08 +02:00
|
|
|
$apps = $GLOBALS['egw']->acl->get_user_applications($share['share_owner']);
|
2015-01-20 23:11:35 +01:00
|
|
|
$GLOBALS['egw_info']['user']['apps'] = array(
|
2020-03-19 17:41:45 +01:00
|
|
|
'filemanager' => $GLOBALS['egw_info']['apps']['filemanager'] || true,
|
|
|
|
'collabora' => $GLOBALS['egw_info']['apps']['collabora'] || $apps['collabora']
|
2015-01-20 23:11:35 +01:00
|
|
|
);
|
|
|
|
|
2016-03-20 17:19:53 +01:00
|
|
|
Vfs::$user = $share['share_owner'];
|
2018-03-12 20:50:37 +01:00
|
|
|
|
|
|
|
// Need to re-init stream wrapper, as some of them look at
|
|
|
|
// preferences or permissions
|
|
|
|
$scheme = Vfs\StreamWrapper::scheme2class(Vfs::parse_url($share['resolve_url'],PHP_URL_SCHEME));
|
|
|
|
if($scheme && method_exists($scheme, 'init_static'))
|
|
|
|
{
|
|
|
|
$scheme::init_static();
|
|
|
|
}
|
2015-01-20 23:11:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// mounting share
|
2016-03-20 17:19:53 +01:00
|
|
|
Vfs::$is_root = true;
|
2020-04-04 00:20:47 +02:00
|
|
|
$clear_fstab = !$GLOBALS['egw_info']['user']['account_lid'] || $GLOBALS['egw_info']['user']['account_lid'] == 'anonymous';
|
|
|
|
if (!Vfs::mount($share['resolve_url'], $share['share_root'], false, false, $clear_fstab))
|
2014-11-13 18:31:36 +01:00
|
|
|
{
|
|
|
|
sleep(1);
|
2018-05-03 18:59:22 +02:00
|
|
|
return static::share_fail(
|
|
|
|
'404 Not Found',
|
2019-04-28 11:15:38 +02:00
|
|
|
"Requested resource '/".htmlspecialchars($share['share_token'])."' does NOT exist!\n"
|
2018-05-03 18:59:22 +02:00
|
|
|
);
|
2014-11-13 18:31:36 +01:00
|
|
|
}
|
2020-04-04 00:20:47 +02:00
|
|
|
|
|
|
|
$session_fstab =& Api\Cache::getSession('api', 'fstab');
|
|
|
|
if(!$session_fstab)
|
|
|
|
{
|
|
|
|
$session_fstab = array();
|
|
|
|
}
|
|
|
|
foreach($session_fstab as $mount => $info)
|
|
|
|
{
|
|
|
|
Vfs::mount($info['mount'], $mount, false, false);
|
|
|
|
}
|
|
|
|
static::session_mount($share['share_root'], $share['resolve_url']);
|
|
|
|
|
|
|
|
|
2016-03-20 17:19:53 +01:00
|
|
|
Vfs::$is_root = false;
|
|
|
|
Vfs::clearstatcache();
|
2017-10-31 10:51:59 +01:00
|
|
|
// clear link-cache and load link registry without permission check to access /apps
|
|
|
|
Api\Link::init_static(true);
|
2018-05-03 18:59:22 +02:00
|
|
|
}
|
|
|
|
|
2020-04-04 00:20:47 +02:00
|
|
|
/**
|
|
|
|
* Just temporary until we get the share streamwrapper in
|
|
|
|
* @param $target
|
|
|
|
* @param $mount
|
|
|
|
*/
|
|
|
|
protected static function session_mount($target, $mount)
|
|
|
|
{
|
|
|
|
$session_fstab =& Api\Cache::getSession('api', 'fstab');
|
|
|
|
if(!$session_fstab)
|
|
|
|
{
|
|
|
|
$session_fstab = array();
|
|
|
|
}
|
|
|
|
$session_fstab[$target] = array(
|
|
|
|
'mount' => $mount,
|
|
|
|
'class' => get_called_class()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-05-30 17:37:16 +02:00
|
|
|
protected function after_login()
|
2018-05-03 18:59:22 +02:00
|
|
|
{
|
2018-05-30 17:37:16 +02:00
|
|
|
// only allow filemanager app (gets overwritten by session::create)
|
|
|
|
$GLOBALS['egw_info']['user']['apps'] = array(
|
|
|
|
'filemanager' => $GLOBALS['egw_info']['apps']['filemanager']
|
|
|
|
);
|
|
|
|
// check if sharee has Collabora run rights --> give is to share too
|
2018-07-26 23:08:32 +02:00
|
|
|
$apps = $GLOBALS['egw']->acl->get_user_applications($this->share['share_owner']);
|
2018-05-30 17:37:16 +02:00
|
|
|
if (!empty($apps['collabora']))
|
|
|
|
{
|
|
|
|
$GLOBALS['egw_info']['user']['apps']['collabora'] = $GLOBALS['egw_info']['apps']['collabora'];
|
|
|
|
}
|
2018-05-03 18:59:22 +02:00
|
|
|
}
|
2015-02-28 23:08:23 +01:00
|
|
|
|
2014-11-18 13:55:32 +01:00
|
|
|
/**
|
|
|
|
* Server a request on a share specified in REQUEST_URI
|
|
|
|
*/
|
2018-05-30 17:37:16 +02:00
|
|
|
public function get_ui()
|
2014-11-18 13:55:32 +01:00
|
|
|
{
|
|
|
|
// run full eTemplate2 UI for directories
|
2015-01-20 23:11:35 +01:00
|
|
|
$_GET['path'] = $this->share['share_root'];
|
2015-01-21 20:45:46 +01:00
|
|
|
$GLOBALS['egw_info']['user']['preferences']['filemanager']['nm_view'] = 'tile';
|
2014-12-09 13:08:00 +01:00
|
|
|
$_GET['cd'] = 'no';
|
|
|
|
$GLOBALS['egw_info']['flags']['js_link_registry'] = true;
|
2018-07-18 20:27:27 +02:00
|
|
|
$GLOBALS['egw_info']['flags']['currentapp'] = 'filemanager';
|
2016-04-07 22:42:06 +02:00
|
|
|
Api\Framework::includeCSS('filemanager', 'sharing');
|
2016-03-20 17:19:53 +01:00
|
|
|
$ui = new SharingUi();
|
2014-11-18 13:55:32 +01:00
|
|
|
$ui->index();
|
2014-11-13 18:31:36 +01:00
|
|
|
}
|
|
|
|
|
2014-12-01 21:14:18 +01:00
|
|
|
/**
|
|
|
|
* Create a new share
|
|
|
|
*
|
2020-03-25 03:34:04 +01:00
|
|
|
* @param string $action_id Name of the action used to create the share. Allows for customization.
|
2014-12-01 21:14:18 +01:00
|
|
|
* @param string $path either path in temp_dir or vfs with optional vfs scheme
|
2014-12-03 17:25:10 +01:00
|
|
|
* @param string $mode self::LINK: copy file in users tmp-dir or self::READABLE share given vfs file,
|
2020-03-25 03:34:04 +01:00
|
|
|
* if no vfs behave as self::LINK
|
2014-12-03 17:25:10 +01:00
|
|
|
* @param string $name filename to use for $mode==self::LINK, default basename of $path
|
2014-12-01 21:14:18 +01:00
|
|
|
* @param string|array $recipients one or more recipient email addresses
|
|
|
|
* @param array $extra =array() extra data to store
|
2020-03-25 03:34:04 +01:00
|
|
|
* @return array with share data, eg. value for key 'share_token'
|
2016-05-02 18:57:50 +02:00
|
|
|
* @throw Api\Exception\NotFound if $path not found
|
|
|
|
* @throw Api\Exception\AssertionFailed if user temp. directory does not exist and can not be created
|
2014-12-01 21:14:18 +01:00
|
|
|
*/
|
2020-03-25 03:34:04 +01:00
|
|
|
public static function create(string $action_id, $path, $mode, $name, $recipients, $extra = array())
|
2014-12-01 21:14:18 +01:00
|
|
|
{
|
|
|
|
if (!isset(self::$db)) self::$db = $GLOBALS['egw']->db;
|
|
|
|
|
2016-03-20 17:19:53 +01:00
|
|
|
$path2tmp =& Api\Cache::getSession(__CLASS__, 'path2tmp');
|
2020-03-25 18:39:22 +01:00
|
|
|
$path = static::validate_path($path, $mode);
|
2014-12-01 21:14:18 +01:00
|
|
|
|
2020-03-25 18:39:22 +01:00
|
|
|
if (empty($name)) $name = $path;
|
2020-03-25 03:34:04 +01:00
|
|
|
|
2015-02-11 22:51:59 +01:00
|
|
|
// check if file has been shared before, with identical attributes
|
2018-05-30 17:37:16 +02:00
|
|
|
if (($mode != self::LINK ))
|
2014-12-01 21:14:18 +01:00
|
|
|
{
|
2020-03-25 18:39:22 +01:00
|
|
|
return parent::create($action_id, $path, $mode, $name, $recipients, $extra);
|
2014-12-01 21:14:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// if not create new share
|
|
|
|
if ($mode == 'link')
|
|
|
|
{
|
|
|
|
$user_tmp = '/home/'.$GLOBALS['egw_info']['user']['account_lid'].'/.tmp';
|
2016-03-29 13:01:34 +02:00
|
|
|
if (!Vfs::file_exists($user_tmp) && !Vfs::mkdir($user_tmp, null, STREAM_MKDIR_RECURSIVE))
|
2014-12-01 21:14:18 +01:00
|
|
|
{
|
2016-03-20 17:19:53 +01:00
|
|
|
throw new Api\Exception\AssertionFailed("Could NOT create temp. directory '$user_tmp'!");
|
2014-12-01 21:14:18 +01:00
|
|
|
}
|
|
|
|
$n = 0;
|
|
|
|
do {
|
2016-03-20 17:19:53 +01:00
|
|
|
$tmp_file = Vfs::concat($user_tmp, ($n?$n.'.':'').Vfs::basename($name));
|
2014-12-03 17:25:10 +01:00
|
|
|
}
|
2016-03-29 13:01:34 +02:00
|
|
|
while(!(is_dir($path) && Vfs::mkdir($tmp_file, null, STREAM_MKDIR_RECURSIVE) ||
|
2016-03-20 17:19:53 +01:00
|
|
|
!is_dir($path) && (!Vfs::file_exists($tmp_file) && ($fp = Vfs::fopen($tmp_file, 'x')) ||
|
2014-12-05 12:27:22 +01:00
|
|
|
// do not copy identical files again to users tmp dir, just re-use them
|
2016-03-20 17:19:53 +01:00
|
|
|
Vfs::file_exists($tmp_file) && Vfs::compare(Vfs::PREFIX.$tmp_file, $path))) && $n++ < 100);
|
2014-12-01 21:14:18 +01:00
|
|
|
|
|
|
|
if ($n >= 100)
|
|
|
|
{
|
2016-03-20 17:19:53 +01:00
|
|
|
throw new Api\Exception\AssertionFailed("Could NOT create temp. file '$tmp_file'!");
|
2014-12-01 21:14:18 +01:00
|
|
|
}
|
2014-12-03 17:25:10 +01:00
|
|
|
if ($fp) fclose($fp);
|
2014-12-01 21:14:18 +01:00
|
|
|
|
2016-03-20 17:19:53 +01:00
|
|
|
if (is_dir($path) && !Vfs::copy_files(array($path), $tmp_file) ||
|
|
|
|
!is_dir($path) && !copy($path, Vfs::PREFIX.$tmp_file))
|
2014-12-01 21:14:18 +01:00
|
|
|
{
|
2016-03-20 17:19:53 +01:00
|
|
|
throw new Api\Exception\AssertionFailed("Could NOT create temp. file '$tmp_file'!");
|
2014-12-01 21:14:18 +01:00
|
|
|
}
|
|
|
|
// store temp. path in session, to be able to add more recipients
|
|
|
|
$path2tmp[$path] = $tmp_file;
|
|
|
|
|
2018-01-17 16:32:21 +01:00
|
|
|
$vfs_path = $tmp_file;
|
2014-12-01 21:14:18 +01:00
|
|
|
}
|
|
|
|
|
2020-03-25 03:34:04 +01:00
|
|
|
return parent::create($action_id, $vfs_path, $mode, $name, $recipients, $extra);
|
2014-12-01 21:14:18 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-25 03:34:04 +01:00
|
|
|
/**
|
2020-03-25 18:39:22 +01:00
|
|
|
* Clean and validate the share path
|
2020-03-25 03:34:04 +01:00
|
|
|
*
|
2020-03-25 18:39:22 +01:00
|
|
|
* @param $path Proposed share path
|
|
|
|
* @param $mode Share mode
|
|
|
|
* @return string
|
2020-03-25 03:34:04 +01:00
|
|
|
*
|
|
|
|
* @throws Api\Exception\AssertionFailed
|
2020-03-25 18:39:22 +01:00
|
|
|
* @throws Api\Exception\NotFound
|
2020-03-25 03:34:04 +01:00
|
|
|
* @throws Api\Exception\WrongParameter
|
|
|
|
*/
|
2020-03-25 18:39:22 +01:00
|
|
|
protected static function validate_path($path, &$mode)
|
2020-03-25 03:34:04 +01:00
|
|
|
{
|
2020-03-25 18:39:22 +01:00
|
|
|
// Parent puts the application as a prefix. If we're coming from there, pull it off
|
|
|
|
if(strpos($path, 'filemanager::') === 0)
|
|
|
|
{
|
|
|
|
list(,$path) = explode('::', $path);
|
|
|
|
}
|
2020-03-25 03:34:04 +01:00
|
|
|
|
2020-03-25 18:39:22 +01:00
|
|
|
// allow filesystem path only for temp_dir
|
|
|
|
$temp_dir = $GLOBALS['egw_info']['server']['temp_dir'].'/';
|
|
|
|
if (substr($path, 0, strlen($temp_dir)) == $temp_dir)
|
2020-03-25 03:34:04 +01:00
|
|
|
{
|
2020-03-25 18:39:22 +01:00
|
|
|
$mode = self::LINK;
|
|
|
|
$exists = file_exists($path) && is_readable($path);
|
2020-03-25 03:34:04 +01:00
|
|
|
}
|
2020-03-25 18:39:22 +01:00
|
|
|
else
|
2020-03-25 03:34:04 +01:00
|
|
|
{
|
2020-03-25 18:39:22 +01:00
|
|
|
if(parse_url($path, PHP_URL_SCHEME) !== 'vfs')
|
2020-03-25 03:34:04 +01:00
|
|
|
{
|
2020-03-25 18:39:22 +01:00
|
|
|
$path = 'vfs://default'.($path[0] == '/' ? '' : '/').$path;
|
2020-03-25 03:34:04 +01:00
|
|
|
}
|
|
|
|
|
2020-04-14 19:19:12 +02:00
|
|
|
// We don't allow sharing paths that contain links, resolve to target instead
|
2020-03-25 18:39:22 +01:00
|
|
|
if(($target = Vfs::readlink($path)))
|
|
|
|
{
|
|
|
|
$path = $target;
|
|
|
|
}
|
2020-04-14 19:19:12 +02:00
|
|
|
$check = $path;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
if(($delinked = Vfs::readlink($check)))
|
|
|
|
{
|
|
|
|
if($delinked[strlen($delinked)-1] == '/') $check .='/';
|
|
|
|
$path = str_replace($check, $delinked, $path);
|
|
|
|
if(parse_url($path, PHP_URL_SCHEME) !== 'vfs')
|
|
|
|
{
|
|
|
|
$path = 'vfs://default'.($path[0] == '/' ? '' : '/').$path;
|
|
|
|
}
|
|
|
|
$check = $path;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$check = Vfs::dirname($check);
|
|
|
|
}
|
|
|
|
} while ($check && $check != '/');
|
2020-03-25 03:34:04 +01:00
|
|
|
|
2020-03-25 18:39:22 +01:00
|
|
|
if (($exists = ($stat = Vfs::stat($path)) && Vfs::check_access($path, Vfs::READABLE, $stat)))
|
|
|
|
{
|
|
|
|
// Make sure we get the correct path if sharing from a share
|
|
|
|
if(isset($GLOBALS['egw']->sharing) && $exists)
|
|
|
|
{
|
|
|
|
$resolved_stat = Vfs::parse_url($stat['url']);
|
|
|
|
$path = 'vfs://default'. $resolved_stat['path'];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// check if file exists and is readable
|
|
|
|
if (!$exists)
|
|
|
|
{
|
|
|
|
throw new Api\Exception\NotFound("'$path' NOT found!");
|
|
|
|
}
|
|
|
|
|
|
|
|
return $path;
|
2020-03-25 03:34:04 +01:00
|
|
|
}
|
|
|
|
|
2014-12-09 13:46:38 +01:00
|
|
|
/**
|
|
|
|
* Delete specified shares and unlink temp. files
|
|
|
|
*
|
|
|
|
* @param int|array $keys
|
|
|
|
* @return int number of deleted shares
|
|
|
|
*/
|
|
|
|
public static function delete($keys)
|
|
|
|
{
|
|
|
|
self::$db = $GLOBALS['egw']->db;
|
|
|
|
|
|
|
|
if (is_scalar($keys)) $keys = array('share_id' => $keys);
|
|
|
|
|
|
|
|
// get all temp. files, to be able to delete them
|
|
|
|
$tmp_paths = array();
|
|
|
|
foreach(self::$db->select(self::TABLE, 'share_path', array(
|
|
|
|
"share_path LIKE '/home/%/.tmp/%'")+$keys, __LINE__, __FILE__, false) as $row)
|
|
|
|
{
|
|
|
|
$tmp_paths[] = $row['share_path'];
|
|
|
|
}
|
|
|
|
|
2019-07-18 23:25:28 +02:00
|
|
|
$deleted = parent::delete($keys);
|
2014-12-09 13:46:38 +01:00
|
|
|
|
|
|
|
// check if temp. files are used elsewhere
|
|
|
|
if ($tmp_paths)
|
|
|
|
{
|
|
|
|
foreach(self::$db->select(self::TABLE, 'share_path,COUNT(*) AS cnt', array(
|
|
|
|
'share_path' => $tmp_paths,
|
|
|
|
), __LINE__, __FILE__, false, 'GROUP BY share_path') as $row)
|
|
|
|
{
|
|
|
|
if (($key = array_search($row['share_path'], $tmp_paths)))
|
|
|
|
{
|
|
|
|
unset($tmp_paths[$key]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// if not delete them
|
|
|
|
foreach($tmp_paths as $path)
|
|
|
|
{
|
2016-03-20 17:19:53 +01:00
|
|
|
Vfs::remove($path);
|
2014-12-09 13:46:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return $deleted;
|
|
|
|
}
|
|
|
|
|
2014-12-05 12:27:22 +01:00
|
|
|
/**
|
2019-07-18 23:25:28 +02:00
|
|
|
* Check that a share path still exists (and is readable)
|
2014-12-05 21:18:51 +01:00
|
|
|
*/
|
2019-07-18 23:25:28 +02:00
|
|
|
protected static function check_path($share)
|
2014-12-05 12:27:22 +01:00
|
|
|
{
|
2020-03-06 15:08:34 +01:00
|
|
|
// remove VFS::PREFIX (vfs://default), as Vfs::file_exists returns false if path does NOT start with a /
|
|
|
|
if ($share['share_path'][0] !== '/')
|
|
|
|
{
|
|
|
|
$share['share_path'] = Api\Vfs::parse_url($share['share_path'], PHP_URL_PATH);
|
|
|
|
}
|
2020-03-30 17:35:59 +02:00
|
|
|
Vfs::$is_root = true;
|
|
|
|
$exists = Vfs::file_exists($share['share_path']);
|
|
|
|
Vfs::$is_root = false;
|
|
|
|
return $exists;
|
2014-12-05 12:27:22 +01:00
|
|
|
}
|
|
|
|
|
2019-07-16 21:44:20 +02:00
|
|
|
/**
|
|
|
|
* Get actions for sharing an entry from filemanager
|
|
|
|
*
|
|
|
|
* @param string $appname
|
|
|
|
* @param int $group Current menu group
|
|
|
|
*/
|
|
|
|
public static function get_actions($appname, $group = 6)
|
|
|
|
{
|
|
|
|
$actions = parent::get_actions('filemanager', $group);
|
|
|
|
|
|
|
|
// This one makes no sense for filemanager
|
|
|
|
unset($actions['share']['children']['shareFiles']);
|
|
|
|
|
|
|
|
// Move these around to mesh nicely if collabora is available
|
|
|
|
$actions['share']['children']['shareReadonlyLink']['group'] = 2;
|
|
|
|
$actions['share']['children']['shareReadonlyLink']['order'] = 22;
|
|
|
|
$actions['share']['children']['shareWritable']['group'] = 3;
|
|
|
|
|
|
|
|
// Add in merge to document
|
|
|
|
if (class_exists($appname.'_merge'))
|
|
|
|
{
|
|
|
|
$documents = call_user_func(array($appname.'_merge', 'document_action'),
|
|
|
|
$GLOBALS['egw_info']['user']['preferences'][$appname]['document_dir'],
|
|
|
|
2, 'Insert in document', 'shareDocument_'
|
|
|
|
);
|
|
|
|
$documents['order'] = 25;
|
|
|
|
|
|
|
|
// Mail only
|
|
|
|
if ($documents['children']['message/rfc822'])
|
|
|
|
{
|
|
|
|
// Just email already filtered out
|
|
|
|
$documents['children'] = $documents['children']['message/rfc822']['children'];
|
|
|
|
}
|
|
|
|
foreach($documents['children'] as $key => &$document)
|
|
|
|
{
|
|
|
|
if(strpos($document['target'],'compose_') === FALSE)
|
|
|
|
{
|
|
|
|
unset($documents['children'][$key]);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$document['allowOnMultiple'] = true;
|
|
|
|
$document['onExecute'] = "javaScript:app.$appname.share_merge";
|
|
|
|
}
|
|
|
|
$documents['enabled'] = $documents['enabled'] && (boolean)$documents['children'] && !!($GLOBALS['egw_info']['user']['apps']['stylite']);
|
|
|
|
$actions['share']['children']['shareDocuments'] = $documents;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $actions;
|
|
|
|
}
|
|
|
|
|
2020-03-06 22:48:26 +01:00
|
|
|
/**
|
|
|
|
* Hook callback to watch VFS and remove any shares for files that get moved or removed
|
|
|
|
*/
|
|
|
|
public static function vfsUpdate($data)
|
|
|
|
{
|
|
|
|
$path = $data['location'] == 'vfs_rename' ? $data['from'] : $data['path'];
|
|
|
|
if (parse_url($path, PHP_URL_SCHEME) !== 'vfs')
|
|
|
|
{
|
|
|
|
$path = Api\Vfs::PREFIX . ($path[0] == '/' ? '' : '/') . $path;
|
|
|
|
}
|
|
|
|
if ($data['location'] == 'vfs_rmdir')
|
|
|
|
{
|
|
|
|
// Normally removing a directory removes the files first, so any shares inside the directory would
|
|
|
|
// be handled already, but just in case, get it all.
|
|
|
|
$path .= '%';
|
|
|
|
}
|
|
|
|
|
|
|
|
$shares = array();
|
|
|
|
foreach ($GLOBALS['egw']->db->select(self::TABLE, array(
|
|
|
|
'share_id', 'share_path', 'share_owner'
|
|
|
|
),
|
|
|
|
array(
|
|
|
|
"share_path LIKE '$path'"
|
|
|
|
),
|
|
|
|
__LINE__, __FILE__, false) as $share)
|
|
|
|
{
|
|
|
|
$shares[] = $share;
|
|
|
|
}
|
|
|
|
foreach ($shares as $share)
|
|
|
|
{
|
|
|
|
if ($data['location'] == 'vfs_rename')
|
|
|
|
{
|
|
|
|
if (parse_url($data['to'], PHP_URL_SCHEME) !== 'vfs')
|
|
|
|
{
|
|
|
|
$data['to'] = $path = Api\Vfs::PREFIX . ($data['to'][0] == '/' ? '' : '/') . $data['to'];
|
|
|
|
}
|
|
|
|
$GLOBALS['egw']->db->update(self::TABLE, array(
|
|
|
|
'share_path' => $data['to']
|
|
|
|
), $share, __LINE__, __FILE__);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
static::delete($share['share_id']);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-11-18 13:55:32 +01:00
|
|
|
}
|
|
|
|
|
2016-05-30 16:00:20 +02:00
|
|
|
if (file_exists(__DIR__.'/../../../filemanager/inc/class.filemanager_ui.inc.php'))
|
2014-11-18 13:55:32 +01:00
|
|
|
{
|
2016-05-30 16:00:20 +02:00
|
|
|
require_once __DIR__.'/../../../filemanager/inc/class.filemanager_ui.inc.php';
|
2014-11-18 13:55:32 +01:00
|
|
|
|
2016-03-20 17:19:53 +01:00
|
|
|
class SharingUi extends filemanager_ui
|
2014-11-18 13:55:32 +01:00
|
|
|
{
|
2020-04-01 10:44:49 +02:00
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*
|
|
|
|
* Reimplemented to load filemanager translations
|
|
|
|
*/
|
|
|
|
function __construct()
|
|
|
|
{
|
|
|
|
parent::__construct();
|
|
|
|
|
|
|
|
Api\Translation::add_app('filemanager');
|
|
|
|
}
|
|
|
|
|
2020-03-25 03:34:04 +01:00
|
|
|
/**
|
|
|
|
* Get active view - override so it points to this class
|
|
|
|
*
|
2020-03-25 18:39:22 +01:00
|
|
|
* @return callable
|
2020-03-25 03:34:04 +01:00
|
|
|
*/
|
|
|
|
public static function get_view()
|
|
|
|
{
|
|
|
|
return array(new SharingUi(), 'listview');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Filemanager listview
|
|
|
|
*
|
|
|
|
* @param array $content
|
|
|
|
* @param string $msg
|
|
|
|
*/
|
|
|
|
function listview(array $content=null,$msg=null)
|
|
|
|
{
|
2020-03-25 18:39:22 +01:00
|
|
|
$this->etemplate = $this->etemplate ? $this->etemplate : new Api\Etemplate(static::LIST_TEMPLATE);
|
2020-03-25 03:34:04 +01:00
|
|
|
|
2020-03-25 18:39:22 +01:00
|
|
|
// Override and take over get_rows so we can customize
|
|
|
|
$content['nm']['get_rows'] = '.' . get_class($this) . '.get_rows';
|
2020-03-25 03:34:04 +01:00
|
|
|
|
|
|
|
return parent::listview($content, $msg);
|
|
|
|
}
|
|
|
|
|
2014-11-18 13:55:32 +01:00
|
|
|
/**
|
|
|
|
* Get the configured start directory for the current user
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
static function get_home_dir()
|
|
|
|
{
|
2015-01-20 23:11:35 +01:00
|
|
|
return $GLOBALS['egw']->sharing->get_root();
|
2014-11-18 13:55:32 +01:00
|
|
|
}
|
2017-11-02 16:43:08 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Context menu
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public static function get_actions()
|
|
|
|
{
|
|
|
|
$actions = parent::get_actions();
|
|
|
|
$group = 1;
|
2018-07-26 14:10:53 +02:00
|
|
|
// do not add edit setting action when we are in sharing
|
|
|
|
unset($actions['edit']);
|
2020-03-25 03:34:04 +01:00
|
|
|
if (Vfs::is_writable($GLOBALS['egw']->sharing->get_root()))
|
2017-11-02 16:43:08 +01:00
|
|
|
{
|
|
|
|
return $actions;
|
|
|
|
}
|
|
|
|
$actions+= array(
|
|
|
|
'egw_copy' => array(
|
|
|
|
'enabled' => false,
|
|
|
|
'group' => $group + 0.5,
|
|
|
|
'hideOnDisabled' => true
|
|
|
|
),
|
|
|
|
'egw_copy_add' => array(
|
|
|
|
'enabled' => false,
|
|
|
|
'group' => $group + 0.5,
|
|
|
|
'hideOnDisabled' => true
|
|
|
|
),
|
|
|
|
'paste' => array(
|
|
|
|
'enabled' => false,
|
|
|
|
'group' => $group + 0.5,
|
|
|
|
'hideOnDisabled' => true
|
|
|
|
),
|
|
|
|
);
|
|
|
|
return $actions;
|
|
|
|
}
|
2018-04-09 21:56:37 +02:00
|
|
|
|
|
|
|
protected function get_vfs_options($query)
|
|
|
|
{
|
|
|
|
$options = parent::get_vfs_options($query);
|
|
|
|
|
|
|
|
// Hide symlinks
|
2020-03-25 03:34:04 +01:00
|
|
|
// TODO: This hides everything, see Vfs::_check_add() line 648
|
|
|
|
//$options['type'] = '!l';
|
2018-04-09 21:56:37 +02:00
|
|
|
|
|
|
|
return $options;
|
|
|
|
}
|
2020-03-25 03:34:04 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Callback to fetch the rows for the nextmatch widget
|
|
|
|
*
|
|
|
|
* @param array $query
|
|
|
|
* @param array &$rows
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
function get_rows(&$query, &$rows)
|
|
|
|
{
|
|
|
|
// Check for navigating outside share, redirect back to share
|
2020-03-25 18:39:22 +01:00
|
|
|
if (!Vfs::stat($query['path'],false) || !Vfs::is_dir($query['path']) || !Vfs::check_access($query['path'],Vfs::READABLE))
|
2020-03-25 03:34:04 +01:00
|
|
|
{
|
|
|
|
// only redirect, if it would be to some other location, gives redirect-loop otherwise
|
|
|
|
if ($query['path'] != ($path = static::get_home_dir()))
|
|
|
|
{
|
|
|
|
// we will leave here, since we are not allowed, go back to root
|
|
|
|
// TODO: Give message about it, redirect to home dir
|
|
|
|
}
|
|
|
|
$rows = array();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get file list from parent
|
|
|
|
$total = parent::get_rows($query, $rows);
|
|
|
|
|
|
|
|
return $total;
|
|
|
|
}
|
2014-11-18 13:55:32 +01:00
|
|
|
}
|
2014-11-13 18:31:36 +01:00
|
|
|
}
|