Completed
Push — 16.1 ( dce808...4651d6 )
by Nathan
68:40 queued 52:57
created

SharingUi::get_home_dir()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * EGroupware API: VFS sharing
4
 *
5
 * @link http://www.egroupware.org
6
 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
7
 * @package api
8
 * @subpackage Vfs
9
 * @author Ralf Becker <[email protected]>
10
 * @copyright (c) 2014-16 by Ralf Becker <[email protected]>
11
 * @version $Id$
12
 */
13
14
namespace EGroupware\Api\Vfs;
15
16
use EGroupware\Api;
17
use EGroupware\Api\Vfs;
18
19
use filemanager_ui;
20
21
/**
22
 * VFS sharing
23
 *
24
 * Token generation uses openssl_random_pseudo_bytes, if available, otherwise
25
 * mt_rand based Api\Auth::randomstring is used.
26
 *
27
 * Existing user sessions are kept whenever possible by an additional mount into regular VFS:
28
 * - share owner is current user (no problems with rights, they simply match)
29
 * - share owner has owner-right for share: we create a temp. eACL for current user
30
 * --> in all other cases session will be replaced with one of the anonymous user,
31
 *     as we dont support mounting with rights of share owner (VFS uses Vfs::$user!)
32
 *
33
 * @todo handle mounts of an entry directory /apps/$app/$id
34
 * @todo handle mounts inside shared directory (they get currently lost)
35
 * @todo handle absolute symlinks (wont work as we use share as root)
36
 */
37
class Sharing
38
{
39
	/**
40
	 * Length of base64 encoded token (real length is only 3/4 of it)
41
	 *
42
	 * Dropbox uses just 15 chars (letters/numbers 5-6 bit), php sessions use 32 chars (hex = 4bits),
43
	 * so 32 chars of base64 = 6bits should be plenty.
44
	 */
45
	const TOKEN_LENGTH = 32;
46
47
	/**
48
	 * Name of table used for storing tokens
49
	 */
50
	const TABLE = 'egw_sharing';
51
52
	/**
53
	 * Reference to global db object
54
	 *
55
	 * @var Api\Db
56
	 */
57
	protected static $db;
58
59
	/**
60
	 * Share we are instanciated for
61
	 *
62
	 * @var array
63
	 */
64
	protected $share;
65
66
	/**
67
	 * Modes ATTACH is NOT a sharing mode, but it is traditional mode in email
68
	 */
69
	const ATTACH = 'attach';
70
	const LINK = 'link';
71
	const READONLY = 'share_ro';
72
	const WRITABLE = 'share_rw';
73
74
 	/**
0 ignored issues
show
Coding Style introduced by
There is some trailing whitespace on this line which should be avoided as per coding-style.
Loading history...
75
	 * Modes for sharing files
76
	 *
77
	 * @var array
78
	 */
79
	static $modes = array(
80
		self::ATTACH => array(
81
			'label' => 'Attachment',
82
			'title' => 'Works reliable for total size up to 1-2 MB, might work for 5-10 MB, most likely to fail for >10MB',
83
		),
84
		self::LINK => array(
85
			'label' => 'Download link',
86
			'title' => 'Link is appended to mail allowing recipients to download currently attached version of files',
87
		),
88
		self::READONLY => array(
89
			'label' => 'Readonly share',
90
			'title' => 'Link is appended to mail allowing recipients to download up to date version of files',
91
		),
92
		self::WRITABLE => array(
93
			'label' => 'Writable share',
94
			'title' => 'Link is appended to mail allowing recipients to download or modify up to date version of files (EPL only)'
95
		),
96
	);
97
98
	/**
99
	 * Protected constructor called via self::create_session
100
	 *
101
	 * @param string $token
102
	 * @param array $share
103
	 */
104
	protected function __construct(array $share)
105
	{
106
		self::$db = $GLOBALS['egw']->db;
107
		$this->share = $share;
108
	}
109
110
	/**
111
	 * Get token from url
112
	 */
113
	public static function get_token()
114
	{
115
        // WebDAV has no concept of a query string and clients (including cadaver)
116
        // seem to pass '?' unencoded, so we need to extract the path info out
117
        // of the request URI ourselves
118
        // if request URI contains a full url, remove schema and domain
119
		$matches = null;
120
        if (preg_match('|^https?://[^/]+(/.*)$|', $path_info=$_SERVER['REQUEST_URI'], $matches))
121
        {
122
        	$path_info = $matches[1];
123
        }
124
        $path_info = substr($path_info, strlen($_SERVER['SCRIPT_NAME']));
125
		list(, $token/*, $path*/) = preg_split('|[/?]|', $path_info, 3);
126
127
		return $token;
128
	}
129
130
	/**
131
	 * Get root of share
132
	 *
133
	 * @return string
134
	 */
135
	public function get_root()
136
	{
137
		return $this->share['share_root'];
138
	}
139
140
	/**
141
	 * Create sharing session
142
	 *
143
	 * Certain cases:
144
	 * a) there is not session $keep_session === null
145
	 *    --> create new anon session with just filemanager rights and share as fstab
146
	 * b) there is a session $keep_session === true
147
	 *  b1) current user is share owner (eg. checking the link)
148
	 *      --> mount share under token additionally
149
	 *  b2) current user not share owner
150
	 *  b2a) need/use filemanager UI (eg. directory)
151
	 *       --> destroy current session and continue with a)
152
	 *  b2b) single file or WebDAV
153
	 *       --> modify EGroupware enviroment for that request only, no change in session
154
	 *
155
	 * @param boolean $keep_session =null null: create a new session, true: try mounting it into existing (already verified) session
156
	 * @return string with sessionid, does NOT return if no session created
157
	 */
158
	public static function create_session($keep_session=null)
159
	{
160
		self::$db = $GLOBALS['egw']->db;
161
162
		$token = self::get_token();
163
164
		// are we called from header include, because session did not verify
165
		// --> check if it verifys for our token
166
		if ($token && !$keep_session)
167
		{
168
			$_SERVER['PHP_AUTH_USER'] = $token;
169
			if (!isset($_SERVER['PHP_AUTH_PW'])) $_SERVER['PHP_AUTH_PW'] = '';
170
171
			unset($GLOBALS['egw_info']['flags']['autocreate_session_callback']);
172
			if ($GLOBALS['egw']->session->verify() && isset($GLOBALS['egw']->sharing) &&
173
				$GLOBALS['egw']->sharing->share['share_token'] === $token)
174
			{
175
				return $GLOBALS['egw']->session->sessionid;
176
			}
177
		}
178
179
		if (empty($token) || !($share = self::$db->select(self::TABLE, '*', array(
180
			'share_token' => $token,
181
			'(share_expires IS NULL OR share_expires > '.self::$db->quote(time(), 'date').')',
182
		), __LINE__, __FILE__)->fetch()) ||
183
			!$GLOBALS['egw']->accounts->exists($share['share_owner']))
184
		{
185
			sleep(1);
186
			$status = '404 Not Found';
187
			header("HTTP/1.1 $status");
188
			header("X-WebDAV-Status: $status", true);
189
			echo "Requested resource '/".htmlspecialchars($token)."' does NOT exist!\n";
190
			exit;
191
		}
192
193
		// check password, if required
194
		if ($share['share_passwd'] && (empty($_SERVER['PHP_AUTH_PW']) ||
195
			!(Api\Auth::compare_password($_SERVER['PHP_AUTH_PW'], $share['share_passwd'], 'crypt') ||
196
				Api\Header\Authenticate::decode_password($_SERVER['PHP_AUTH_PW']) &&
197
					Api\Auth::compare_password($_SERVER['PHP_AUTH_PW'], $share['share_passwd'], 'crypt'))))
198
		{
199
			$realm = 'EGroupware share '.$share['share_token'];
200
			header('WWW-Authenticate: Basic realm="'.$realm.'"');
201
			$status = '401 Unauthorized';
202
			header("HTTP/1.1 $status");
203
			header("X-WebDAV-Status: $status", true);
204
			echo "<html>\n<head>\n<title>401 Unauthorized</title>\n<body>\nAuthorization failed.\n</body>\n</html>\n";
205
			exit;
206
		}
207
208
		// need to reset fs_tab, as resolve_url does NOT work with just share mounted
209
		if (count($GLOBALS['egw_info']['server']['vfs_fstab']) <= 1)
210
		{
211
			unset($GLOBALS['egw_info']['server']['vfs_fstab']);	// triggers reset of fstab in mount()
212
			$GLOBALS['egw_info']['server']['vfs_fstab'] = Vfs::mount();
213
			Vfs::clearstatcache();
214
		}
215
		$share['resolve_url'] = Vfs::resolve_url($share['share_path'], true, true, true, true);	// true = fix evtl. contained url parameter
216
		// if share not writable append ro=1 to mount url to make it readonly
217
		if (!self::$db->from_bool($share['share_writable']))
218
		{
219
			$share['resolve_url'] .= (strpos($share['resolve_url'], '?') ? '&' : '?').'ro=1';
220
		}
221
		//_debug_array($share);
222
223
		if ($keep_session)	// add share to existing session
224
		{
225
			$share['share_root'] = '/'.$share['share_token'];
226
227
			// if current user is not the share owner, we cant just mount share
228
			if (Vfs::$user != $share['share_owner'])
229
			{
230
				$keep_session = false;
231
			}
232
		}
233
		if (!$keep_session)	// do NOT change to else, as we might have set $keep_session=false!
234
		{
235
			// only allow filemanager app
236
			$GLOBALS['egw_info']['user']['apps'] = array(
237
				'filemanager' => $GLOBALS['egw_info']['apps']['filemanager']
238
			);
239
240
			$share['share_root'] = '/';
241
			Vfs::$user = $share['share_owner'];
242
		}
243
244
		// mounting share
245
		Vfs::$is_root = true;
246
		if (!Vfs::mount($share['resolve_url'], $share['share_root'], false, false, !$keep_session))
0 ignored issues
show
Bug introduced by
It seems like $share['resolve_url'] can also be of type boolean; however, EGroupware\Api\Vfs::mount() does only seem to accept string|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
Bug introduced by
It seems like $share['share_root'] can also be of type boolean; however, EGroupware\Api\Vfs::mount() does only seem to accept string|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
247
		{
248
			sleep(1);
249
			$status = '404 Not Found';
250
			header("HTTP/1.1 $status");
251
			header("X-WebDAV-Status: $status", true);
252
			echo "Requested resource '/".htmlspecialchars($token)."' does NOT exist!\n";
253
			exit;
254
		}
255
		Vfs::$is_root = false;
256
		Vfs::clearstatcache();
257
258
		// update accessed timestamp
259
		self::$db->update(self::TABLE, array(
260
			'share_last_accessed' => $share['share_last_accessed']=time(),
261
		), array(
262
			'share_id' => $share['share_id'],
263
		), __LINE__, __FILE__);
264
265
		// store sharing object in egw object and therefore in session
266
		$GLOBALS['egw']->sharing = new Sharing($share);
267
268
		// we have a session we want to keep, but share owner is different from current user and we need filemanager UI, or no session
269
		// --> create a new anon session
270
		if ($keep_session === false && $GLOBALS['egw']->sharing->use_filemanager() || is_null($keep_session))
271
		{
272
			// create session without checking auth: create(..., false, false)
273
			if (!($sessionid = $GLOBALS['egw']->session->create('anonymous@'.$GLOBALS['egw_info']['user']['domain'],
274
				'', 'text', false, false)))
275
			{
276
				sleep(1);
277
				$status = '500 Internal Server Error';
278
				header("HTTP/1.1 $status");
279
				header("X-WebDAV-Status: $status", true);
280
				echo "Failed to create session: ".$GLOBALS['egw']->session->reason."\n";
281
				exit;
282
			}
283
			// only allow filemanager app (gets overwritten by session::create)
284
			$GLOBALS['egw_info']['user']['apps'] = array(
285
				'filemanager' => $GLOBALS['egw_info']['apps']['filemanager']
286
			);
287
		}
288
		// we have a session we want to keep, but share owner is different from current user and we dont need filemanager UI
289
		// --> we dont need session and close it, to not modifiy it
290
		elseif ($keep_session === false)
291
		{
292
			$GLOBALS['egw']->session->commit_session();
293
		}
294
		// need to store new fstab and vfs_user in session to allow GET requests / downloads via WebDAV
295
		$GLOBALS['egw_info']['user']['vfs_user'] = Vfs::$user;
296
		$GLOBALS['egw_info']['server']['vfs_fstab'] = Vfs::mount();
297
298
		// update modified egw and egw_info again in session, if neccessary
299
		if ($keep_session || $sessionid)
300
		{
301
			$_SESSION[Api\Session::EGW_INFO_CACHE] = $GLOBALS['egw_info'];
302
			unset($_SESSION[Api\Session::EGW_INFO_CACHE]['flags']);	// dont save the flags, they change on each request
303
304
			$_SESSION[Api\Session::EGW_OBJECT_CACHE] = serialize($GLOBALS['egw']);
305
		}
306
307
		return $sessionid;
308
	}
309
310
	/**
311
	 * Check if we use filemanager UI
312
	 *
313
	 * Only for directories, if browser supports it and filemanager is installed
314
	 *
315
	 * @return boolean
316
	 */
317
	public function use_filemanager()
318
	{
319
		return !(!Vfs::is_dir($this->share['share_root']) || $_SERVER['REQUEST_METHOD'] != 'GET' ||
320
			// or unsupported browsers like ie < 10
321
			Api\Header\UserAgent::type() == 'msie' && Api\Header\UserAgent::version() < 10.0 ||
322
			// or if no filemanager installed (WebDAV has own autoindex)
323
			!file_exists(__DIR__.'/../../../filemanager/inc/class.filemanager_ui.inc.php'));
324
	}
325
326
	/**
327
	 * Server a request on a share specified in REQUEST_URI
328
	 */
329
	public function ServeRequest()
330
	{
331
		// sharing is for a different share, change to current share
332
		if ($this->share['share_token'] !== self::get_token())
333
		{
334
			self::create_session($GLOBALS['egw']->session->session_flags === 'N');
335
336
			return $GLOBALS['egw']->sharing->ServeRequest();
337
		}
338
		// use pure WebDAV for everything but GET requests to directories
339
		if (!$this->use_filemanager())
340
		{
341
			// send a content-disposition header, so browser knows how to name downloaded file
342
			if (!Vfs::is_dir($this->share['share_root']))
343
			{
344
				Api\Header\Content::disposition(Vfs::basename($this->share['share_path']), false);
345
			}
346
			//$GLOBALS['egw']->session->commit_session();
347
			$webdav_server = new Vfs\WebDAV();
348
			$webdav_server->ServeRequest(Vfs::concat($this->share['share_root'], $this->share['share_token']));
349
			return;
350
		}
351
		// run full eTemplate2 UI for directories
352
		$_GET['path'] = $this->share['share_root'];
353
		$GLOBALS['egw_info']['user']['preferences']['filemanager']['nm_view'] = 'tile';
354
		$_GET['cd'] = 'no';
355
		$GLOBALS['egw_info']['flags']['js_link_registry'] = true;
356
		Api\Framework::includeCSS('filemanager', 'sharing');
357
		$ui = new SharingUi();
358
		$ui->index();
359
	}
360
361
	/**
362
	 * Generate a new token
363
	 *
364
	 * @return string
365
	 */
366
	public static function token()
367
	{
368
		// generate random token (using oppenssl if available otherwise mt_rand based Api\Auth::randomstring)
369
		do {
370
			$token = function_exists('openssl_random_pseudo_bytes') ?
371
				base64_encode(openssl_random_pseudo_bytes(3*self::TOKEN_LENGTH/4)) :
372
				Api\Auth::randomstring(self::TOKEN_LENGTH);
373
			// base64 can contain chars not allowed in our vfs-urls eg. / or #
374
		} while ($token != Vfs::encodePathComponent($token));
375
376
		return $token;
377
	}
378
379
	/**
380
	 * Create a new share
381
	 *
382
	 * @param string $path either path in temp_dir or vfs with optional vfs scheme
383
	 * @param string $mode self::LINK: copy file in users tmp-dir or self::READABLE share given vfs file,
384
	 *	if no vfs behave as self::LINK
385
	 * @param string $name filename to use for $mode==self::LINK, default basename of $path
386
	 * @param string|array $recipients one or more recipient email addresses
387
	 * @param array $extra =array() extra data to store
388
	 * @throw Api\Exception\NotFound if $path not found
389
	 * @throw Api\Exception\AssertionFailed if user temp. directory does not exist and can not be created
390
	 * @return array with share data, eg. value for key 'share_token'
391
	 */
392
	public static function create($path, $mode, $name, $recipients, $extra=array())
393
	{
394
		if (!isset(self::$db)) self::$db = $GLOBALS['egw']->db;
395
396
		if (empty($name)) $name = $path;
397
398
		$path2tmp =& Api\Cache::getSession(__CLASS__, 'path2tmp');
399
400
		// allow filesystem path only for temp_dir
401
		$temp_dir = $GLOBALS['egw_info']['server']['temp_dir'].'/';
402
		if (substr($path, 0, strlen($temp_dir)) == $temp_dir)
403
		{
404
			$mode = self::LINK;
405
			$exists = file_exists($path) && is_readable($path);
406
		}
407
		else
408
		{
409
			if(parse_url($path, PHP_URL_SCHEME) !== 'vfs')
410
			{
411
				$path = 'vfs://default'.($path[0] == '/' ? '' : '/').$path;
412
			}
413
			$vfs_path = Vfs::parse_url($path, PHP_URL_PATH);
414
			$exists = Vfs::file_exists($vfs_path) && Vfs::is_readable($vfs_path);
415
		}
416
		// check if file exists and is readable
417
		if (!$exists)
418
		{
419
			throw new Api\Exception\NotFound("'$path' NOT found!");
420
		}
421
		// check if file has been shared before, with identical attributes
422
		if (($mode != self::LINK || isset($path2tmp[$path])) &&
423
			($share = self::$db->select(self::TABLE, '*', $extra+array(
424
				'share_path' => $mode == 'link' ? $path2tmp[$path] : $vfs_path,
425
				'share_owner' => $GLOBALS['egw_info']['user']['account_id'],
426
				'share_expires' => null,
427
				'share_passwd'  => null,
428
				'share_writable'=> false,
429
			), __LINE__, __FILE__)->fetch()))
430
		{
431
			// if yes, just add additional recipients
432
			$share['share_with'] = $share['share_with'] ? explode(',', $share['share_with']) : array();
433
			$need_save = false;
434
			foreach((array)$recipients as $recipient)
435
			{
436
				if (!in_array($recipient, $share['share_with']))
437
				{
438
					$share['share_with'][] = $recipient;
439
					$need_save = true;
440
				}
441
			}
442
			$share['share_with'] = implode(',', $share['share_with']);
443
			if ($need_save)
444
			{
445
				self::$db->update(self::TABLE, array(
446
					'share_with' => $share['share_with'],
447
				), array(
448
					'share_id' => $share['share_id'],
449
				), __LINE__, __FILE__);
450
			}
451
		}
452
		else
453
		{
454
			// if not create new share
455
			if ($mode == 'link')
456
			{
457
				$user_tmp = '/home/'.$GLOBALS['egw_info']['user']['account_lid'].'/.tmp';
458
				if (!Vfs::file_exists($user_tmp) && !Vfs::mkdir($user_tmp, null, STREAM_MKDIR_RECURSIVE))
459
				{
460
					throw new Api\Exception\AssertionFailed("Could NOT create temp. directory '$user_tmp'!");
461
				}
462
				$n = 0;
463
				do {
464
					$tmp_file = Vfs::concat($user_tmp, ($n?$n.'.':'').Vfs::basename($name));
465
				}
466
				while(!(is_dir($path) && Vfs::mkdir($tmp_file, null, STREAM_MKDIR_RECURSIVE) ||
467
					!is_dir($path) && (!Vfs::file_exists($tmp_file) && ($fp = Vfs::fopen($tmp_file, 'x')) ||
468
						// do not copy identical files again to users tmp dir, just re-use them
469
						Vfs::file_exists($tmp_file) && Vfs::compare(Vfs::PREFIX.$tmp_file, $path))) && $n++ < 100);
470
471
				if ($n >= 100)
472
				{
473
					throw new Api\Exception\AssertionFailed("Could NOT create temp. file '$tmp_file'!");
474
				}
475
				if ($fp) fclose($fp);
476
477
				if (is_dir($path) && !Vfs::copy_files(array($path), $tmp_file) ||
478
					!is_dir($path) && !copy($path, Vfs::PREFIX.$tmp_file))
479
				{
480
					throw new Api\Exception\AssertionFailed("Could NOT create temp. file '$tmp_file'!");
481
				}
482
				// store temp. path in session, to be able to add more recipients
483
				$path2tmp[$path] = $tmp_file;
484
485
				$path = $tmp_file;
486
487
				// if not already installed, install periodic cleanup of tmp files
488
				$async = new Api\Asyncservice();
489
				if (!$async->read('egw_sharing-tmp-cleanup'))
490
				{
491
					$async->set_timer(array('day' => 28),'egw_sharing-tmp_cleanup','EGroupware\\Api\\Vfs\\Sharing::tmp_cleanup',null);
492
				}
493
			}
494
495
			$i = 0;
496
			while(true)	// self::token() can return an existing value
497
			{
498
				try {
499
					self::$db->insert(self::TABLE, $share = array(
500
						'share_token' => self::token(),
501
						'share_path' => Vfs::parse_url($path, PHP_URL_PATH),
502
						'share_owner' => $GLOBALS['egw_info']['user']['account_id'],
503
						'share_with' => implode(',', (array)$recipients),
504
						'share_created' => time(),
505
					)+$extra, false, __LINE__, __FILE__);
506
507
					$share['share_id'] = self::$db->get_last_insert_id(self::TABLE, 'share_id');
508
					break;
509
				}
510
				catch(Api\Db\Exception $e) {
511
					if ($i++ > 3) throw $e;
512
					unset($e);
513
				}
514
			}
515
		}
516
		return $share;
517
	}
518
519
	/**
520
	 * Api\Storage\Base instance for egw_sharing table
521
	 *
522
	 * @var Api\Storage\Base
523
	 */
524
	protected static $so;
525
526
	/**
527
	 * Get a so_sql instance initialised for shares
528
	 */
529
	public static function so()
530
	{
531
		if (!isset(self::$so))
532
		{
533
			self::$so = new Api\Storage\Base('phpgwapi', self::TABLE, null, '', true);
534
			self::$so->set_times('string');
535
		}
536
		return self::$so;
537
	}
538
539
	/**
540
	 * Delete specified shares and unlink temp. files
541
	 *
542
	 * @param int|array $keys
543
	 * @return int number of deleted shares
544
	 */
545
	public static function delete($keys)
546
	{
547
		self::$db = $GLOBALS['egw']->db;
548
549
		if (is_scalar($keys)) $keys = array('share_id' => $keys);
550
551
		// get all temp. files, to be able to delete them
552
		$tmp_paths = array();
553
		foreach(self::$db->select(self::TABLE, 'share_path', array(
554
			"share_path LIKE '/home/%/.tmp/%'")+$keys, __LINE__, __FILE__, false) as $row)
555
		{
556
			$tmp_paths[] = $row['share_path'];
557
		}
558
559
		// delete specified shares
560
		self::$db->delete(self::TABLE, $keys, __LINE__, __FILE__);
561
		$deleted = self::$db->affected_rows();
562
563
		// check if temp. files are used elsewhere
564
		if ($tmp_paths)
565
		{
566
			foreach(self::$db->select(self::TABLE, 'share_path,COUNT(*) AS cnt', array(
567
				'share_path' => $tmp_paths,
568
			), __LINE__, __FILE__, false, 'GROUP BY share_path') as $row)
569
			{
570
				if (($key = array_search($row['share_path'], $tmp_paths)))
571
				{
572
					unset($tmp_paths[$key]);
573
				}
574
			}
575
			// if not delete them
576
			foreach($tmp_paths as $path)
577
			{
578
				Vfs::remove($path);
579
			}
580
		}
581
		return $deleted;
582
	}
583
584
	/**
585
	 * Home long to keep temp. files: 100 day
586
	 */
587
	const TMP_KEEP = 8640000;
588
589
	/**.
590
	 * Periodic (monthly) cleanup of temporary sharing files (download link)
591
	 *
592
	 * Exlicit expireds shares are delete, as ones created over 100 days ago and last accessed over 100 days ago.
593
	 */
594
	public static function tmp_cleanup()
595
	{
596
		if (!isset(self::$db)) self::$db = $GLOBALS['egw']->db;
597
		Vfs::$is_root = true;
598
599
		try {
600
			$cols = array(
601
				'share_path',
602
				'MAX(share_expires) AS share_expires',
603
				'MAX(share_created) AS share_created',
604
				'MAX(share_last_accessed) AS share_last_accessed',
605
			);
606
			if (($group_concat = self::$db->group_concat('share_id'))) $cols[] = $group_concat.' AS share_id';
607
			// remove expired tmp-files unconditionally
608
			$having = 'HAVING MAX(share_expires) < '.self::$db->quote(self::$db->to_timestamp(time())).' OR '.
609
				// remove without expiration date, when created over 100 days ago AND
610
				'MAX(share_expires) IS NULL AND MAX(share_created) < '.self::$db->quote(self::$db->to_timestamp(time()-self::TMP_KEEP)). ' AND '.
611
					// (last accessed over 100 days ago OR never)
612
					'(MAX(share_last_accessed) IS NULL OR MAX(share_last_accessed) < '.self::$db->quote(self::$db->to_timestamp(time()-self::TMP_KEEP)).')';
613
614
			foreach(self::$db->select(self::TABLE, $cols, array(
615
				"share_path LIKE '/home/%/.tmp/%'",
616
			), __LINE__, __FILE__, false, 'GROUP BY share_path '.$having) as $row)
617
			{
618
				Vfs::remove($row['share_path']);
619
620
				if ($group_concat)
621
				{
622
					$share_ids = $row['share_id'] ? explode(',', $row['share_id']) : array();
623
				}
624
				else
625
				{
626
					$share_ids = array();
627
					foreach(self::$db->selec(self::TABLE, 'share_id', array(
628
						'share_path' => $row['share_path'],
629
					), __LINE__, __FILE__) as $id)
630
					{
631
						$share_ids[] = $id['share_id'];
632
					}
633
				}
634
				if ($share_ids)
635
				{
636
					self::$db->delete(self::TABLE, array('share_id' => $share_ids), __LINE__, __FILE__);
637
				}
638
			}
639
		}
640
		catch (\Exception $e) {
641
			_egw_log_exception($e);
642
		}
643
		Vfs::$is_root = false;
644
	}
645
646
	/**
647
	 * Generate link from share or share-token
648
	 *
649
	 * @param string|array $share share or share-token
650
	 * @return string
651
	 */
652
	public static function share2link($share)
653
	{
654
		if (is_array($share)) $share = $share['share_token'];
655
656
		$link = Api\Framework::link('/share.php').'/'.$share;
657 View Code Duplication
		if ($link[0] == '/')
658
		{
659
			$link = ($_SERVER['HTTPS'] ? 'https://' : 'http://').
660
				($GLOBALS['egw_info']['server']['hostname'] ?
661
					$GLOBALS['egw_info']['server']['hostname'] : $_SERVER['HTTP_HOST']).
662
				$link;
663
		}
664
		return $link;
665
	}
666
}
667
668
if (file_exists(__DIR__.'/../../../filemanager/inc/class.filemanager_ui.inc.php'))
669
{
670
	require_once __DIR__.'/../../../filemanager/inc/class.filemanager_ui.inc.php';
671
672
	class SharingUi extends filemanager_ui
673
	{
674
		/**
675
		 * Get the configured start directory for the current user
676
		 *
677
		 * @return string
678
		 */
679
		static function get_home_dir()
680
		{
681
			return $GLOBALS['egw']->sharing->get_root();
682
		}
683
684
		/**
685
		 * Context menu
686
		 *
687
		 * @return array
688
		 */
689
		public static function get_actions()
690
		{
691
			$actions = parent::get_actions();
692
			$group = 1;
693
			if(Vfs::is_writable($GLOBALS['egw']->sharing->get_root()))
694
			{
695
				return $actions;
696
			}
697
			$actions+= array(
698
				'egw_copy' => array(
699
					'enabled' => false,
700
					'group' => $group + 0.5,
701
					'hideOnDisabled' => true
702
				),
703
				'egw_copy_add' => array(
704
					'enabled' => false,
705
					'group' => $group + 0.5,
706
					'hideOnDisabled' => true
707
				),
708
				'paste' => array(
709
					'enabled' => false,
710
					'group' => $group + 0.5,
711
					'hideOnDisabled' => true
712
				),
713
			);
714
			return $actions;
715
		}
716
	}
717
}