1<?php 2/** 3 * EGroupware Filemanager: mounting GUI 4 * 5 * @link http://www.egroupware.org/ 6 * @package filemanager 7 * @author Ralf Becker <rb-AT-stylite.de> 8 * @copyright (c) 2010-16 by Ralf Becker <rb-AT-stylite.de> 9 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License 10 * @version $Id$ 11 */ 12 13use EGroupware\Api; 14use EGroupware\Api\Framework; 15use EGroupware\Api\Etemplate; 16use EGroupware\Stylite\Vfs\Versioning; 17use EGroupware\Api\Vfs; 18 19/** 20 * Filemanager: mounting GUI 21 */ 22class filemanager_admin extends filemanager_ui 23{ 24 /** 25 * Functions callable via menuaction 26 * 27 * @var array 28 */ 29 public $public_functions = array( 30 'index' => true, 31 'fsck' => true, 32 ); 33 34 /** 35 * Autheticated user is setup config user 36 * 37 * @var boolean 38 */ 39 static protected $is_setup = false; 40 41 /** 42 * Do we have versioning (Versioning\StreamWrapper class) available and with which schema 43 * 44 * @var string 45 */ 46 protected $versioning; 47 48 /** 49 * Do not allow to (un)mount these 50 * 51 * @var array 52 */ 53 protected static $protected_path = array('/apps', '/templates'); 54 55 /** 56 * Constructor 57 */ 58 function __construct() 59 { 60 // make sure user has admin rights 61 if (!isset($GLOBALS['egw_info']['user']['apps']['admin'])) 62 { 63 throw new Api\Exception\NoPermission\Admin(); 64 } 65 // sudo handling 66 parent::__construct(); 67 self::$is_setup = Api\Cache::getSession('filemanager', 'is_setup'); 68 69 if (class_exists('EGroupware\Stylite\Vfs\Versioning\StreamWrapper')) 70 { 71 $this->versioning = Versioning\StreamWrapper::SCHEME; 72 } 73 } 74 75 /** 76 * Mount GUI 77 * 78 * @param array $content=null 79 * @param string $msg='' 80 */ 81 public function index(array $content=null, $msg='', $msg_type=null) 82 { 83 if (is_array($content)) 84 { 85 //_debug_array($content); 86 if ($content['sudo']) 87 { 88 $msg = $this->sudo($content['user'],$content['password'],self::$is_setup) ? 89 lang('Root access granted.') : lang('Wrong username or password!'); 90 $msg_type = Vfs::$is_root ? 'success' : 'error'; 91 } 92 elseif ($content['etemplates'] && $GLOBALS['egw_info']['user']['apps']['admin']) 93 { 94 $path = '/etemplates'; 95 $url = 'stylite.merge://default/etemplates?merge=.&lang=0&level=1&extension=xet&url=egw'; 96 $backup = Vfs::$is_root; 97 Vfs::$is_root = true; 98 Vfs::mkdir($path); 99 Vfs::chgrp($path, 'Admins'); 100 Vfs::chmod($path, 075); 101 $msg = Vfs::mount($url, $path) ? 102 lang('Successful mounted %1 on %2.',$url,$path) : lang('Error mounting %1 on %2!',$url,$path); 103 Vfs::$is_root = $backup; 104 } 105 elseif (Vfs::$is_root) 106 { 107 if ($content['logout']) 108 { 109 $msg = $this->sudo('','',self::$is_setup) ? 'Logout failed!' : lang('Root access stopped.'); 110 $msg_type = !Vfs::$is_root ? 'success' : 'error'; 111 } 112 if ($content['mounts']['disable'] || self::$is_setup && $content['mounts']['umount']) 113 { 114 if (($unmount = $content['mounts']['umount'])) 115 { 116 $path = @key($content['mounts']['umount']); 117 } 118 else 119 { 120 $path = @key($content['mounts']['disable']); 121 } 122 if (!in_array($path, self::$protected_path) && $path != '/') 123 { 124 $msg = Vfs::umount($path) ? 125 lang('%1 successful unmounted.',$path) : lang('Error unmounting %1!',$path); 126 } 127 else // re-mount / with sqlFS, to disable versioning 128 { 129 $msg = Vfs::mount($url=Vfs\Sqlfs\StreamWrapper::SCHEME.'://default'.$path,$path) ? 130 lang('Successful mounted %1 on %2.',$url,$path) : lang('Error mounting %1 on %2!',$url,$path); 131 } 132 } 133 if (($path = $content['mounts']['path']) && 134 ($content['mounts']['enable'] || self::$is_setup && $content['mounts']['mount'])) 135 { 136 $url = str_replace('$path',$path,$content['mounts']['url']); 137 if (empty($url) && $this->versioning) $url = Versioning\StreamWrapper::PREFIX.$path; 138 139 if ($content['mounts']['enable'] && !$this->versioning) 140 { 141 $msg = lang('Versioning requires <a href="http://www.egroupware.org/products">Stylite EGroupware Enterprise Line (EPL)</a>!'); 142 $msg_type = 'info'; 143 } 144 elseif (!Vfs::file_exists($path) || !Vfs::is_dir($path)) 145 { 146 $msg = lang('Path %1 not found or not a directory!',$path); 147 $msg_type = 'error'; 148 } 149 // dont allow to change mount of /apps or /templates (eg. switching on versioning) 150 elseif (in_array($path, self::$protected_path)) 151 { 152 $msg = lang('Permission denied!'); 153 $msg_type = 'error'; 154 } 155 else 156 { 157 $msg = Vfs::mount($url,$path) ? 158 lang('Successful mounted %1 on %2.',$url,$path) : lang('Error mounting %1 on %2!',$url,$path); 159 } 160 } 161 if ($content['allow_delete_versions'] != $GLOBALS['egw_info']['server']['allow_delete_versions']) 162 { 163 Api\Config::save_value('allow_delete_versions', $content['allow_delete_versions'], 'phpgwapi'); 164 $GLOBALS['egw_info']['server']['allow_delete_versions'] = $content['allow_delete_versions']; 165 $msg = lang('Configuration changed.'); 166 } 167 } 168 // delete old versions and deleted files 169 if ($content['delete-versions']) 170 { 171 if (!Versioning\StreamWrapper::check_delete_version(null)) 172 { 173 $msg = lang('Permission denied')."\n\n".lang('You are NOT allowed to finally delete older versions and deleted files!'); 174 $msg_type = 'error'; 175 } 176 else 177 { 178 // we need to be root to delete files independent of permissions and ownership 179 Vfs::$is_root = true; 180 if (!Vfs::file_exists($content['versionedpath']) || !Vfs::is_dir($content['versionedpath'])) 181 { 182 $msg = lang('Directory "%1" NOT found!', $content['versionedpath']); 183 $msg_type = 'error'; 184 } 185 else 186 { 187 @set_time_limit(0); 188 $starttime = microtime(true); 189 $deleted = $errors = 0; 190 191 // shortcut to efficently delete every old version and deleted file 192 if ($content['versionedpath'] == '/') 193 { 194 $deleted = Versioning\StreamWrapper::purge_all_versioning($content['mtime']); 195 } 196 else 197 { 198 Vfs::find($content['versionedpath'], array( 199 'show-deleted' => true, 200 'hidden' => true, 201 'depth' => true, 202 'path_preg' => '#/\.(attic|versions)/#', 203 )+(!(int)$content['mtime'] ? array() : array( 204 'mtime' => ($content['mtime']<0?'-':'+').(int)$content['mtime'], 205 )), function($path) use (&$deleted, &$errors) 206 { 207 if (($is_dir = Vfs::is_dir($path)) && Vfs::rmdir($path) || 208 !$is_dir && Vfs::unlink($path)) 209 { 210 ++$deleted; 211 } 212 else 213 { 214 ++$errors; 215 } 216 }); 217 } 218 $time = number_format(microtime(true)-$starttime, 1); 219 $msg = ($errors ? lang('%1 errors deleting!', $errors)."\n\n" : ''). 220 lang('%1 files or directories deleted in %2 seconds.', $deleted, $time); 221 $msg_type = $errors ? 'error' : 'info'; 222 } 223 Vfs::$is_root = false; 224 } 225 } 226 } 227 else 228 { 229 // defaults for deleting of older versions 230 $content['versionedpath'] = '/'; 231 $content['mtime'] = 100; 232 } 233 if (true) $content = array( 234 'versionedpath' => $content['versionedpath'], 235 'mtime' => $content['mtime'], 236 ); 237 if ($this->versioning) 238 { 239 // statistical information 240 $content += Versioning\StreamWrapper::summary(); 241 if ($content['total_files']) $content['percent_files'] = number_format(100.0*$content['version_files']/$content['total_files'],1).'%'; 242 if ($content['total_size']) $content['percent_size'] = number_format(100.0*$content['version_size']/$content['total_size'],1).'%'; 243 } 244 if (!($content['is_root']=Vfs::$is_root)) 245 { 246 if (empty($msg)) 247 { 248 $msg = lang('You need to become root, to enable or disable versioning on a directory!'); 249 $msg_type = 'info'; 250 } 251 $readonlys['logout'] = $readonlys['enable'] = $readonlys['allow_delete_versions'] = true; 252 } 253 $content['is_setup'] = self::$is_setup; 254 $content['versioning'] = $this->versioning; 255 $content['allow_delete_versions'] = $GLOBALS['egw_info']['server']['allow_delete_versions']; 256 Framework::message($msg, $msg_type); 257 258 $n = 2; 259 $content['mounts'] = array(); 260 foreach(Vfs::mount() as $path => $url) 261 { 262 $content['mounts'][$n++] = array( 263 'path' => $path, 264 'url' => $url, 265 ); 266 $readonlys["disable[$path]"] = !$this->versioning || !Vfs::$is_root || 267 Vfs::parse_url($url,PHP_URL_SCHEME) != $this->versioning; 268 } 269 $readonlys['umount[/]'] = $readonlys['umount[/apps]'] = true; // do not allow to unmount / or /apps 270 $readonlys['url'] = !self::$is_setup; 271 272 $sel_options['allow_delete_versions'] = array( 273 'root' => lang('Superuser (root)'), 274 'admins' => lang('Administrators'), 275 'everyone' => lang('Everyone'), 276 ); 277 // show [Mount /etemplates] button for admin, if not already mounted and available 278 $readonlys['etemplates'] = !class_exists('\EGroupware\Stylite\Vfs\Merge\StreamWrapper') || 279 ($fs_tab=Vfs::mount($url)) && isset($fs_tab['/etemplates']) || 280 !isset($GLOBALS['egw_info']['user']['apps']['admin']); 281 //_debug_array($content); 282 283 $tpl = new Etemplate('filemanager.admin'); 284 $GLOBALS['egw_info']['flags']['app_header'] = lang('VFS mounts and versioning'); 285 $tpl->exec('filemanager.filemanager_admin.index',$content,$sel_options,$readonlys); 286 } 287 288 /** 289 * Run fsck on sqlfs 290 */ 291 function fsck() 292 { 293 if ($_POST['cancel']) 294 { 295 Framework::redirect_link('/admin/index.php', null, 'admin'); 296 } 297 $check_only = !isset($_POST['fix']); 298 299 if (!($msgs = Vfs\Sqlfs\Utils::fsck($check_only))) 300 { 301 $msgs = lang('Filesystem check reported no problems.'); 302 } 303 $content = '<p>'.implode("</p>\n<p>", (array)$msgs)."</p>\n"; 304 305 $content .= Api\Html::form('<p>'.($check_only&&is_array($msgs) ? 306 Api\Html::submit_button('fix', lang('Fix reported problems')) : ''). 307 Api\Html::submit_button('cancel', lang('Cancel')).'</p>', 308 '','/index.php',array('menuaction'=>'filemanager.filemanager_admin.fsck')); 309 310 $GLOBALS['egw']->framework->render($content, lang('Admin').' - '.lang('Check virtual filesystem'), true); 311 } 312}