@@ -544,7 +544,7 @@ |
||
| 544 | 544 | * @param array $input The array to work on |
| 545 | 545 | * @param int $case Either MB_CASE_UPPER or MB_CASE_LOWER (default) |
| 546 | 546 | * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8 |
| 547 | - * @return array |
|
| 547 | + * @return string |
|
| 548 | 548 | * @since 4.5.0 |
| 549 | 549 | */ |
| 550 | 550 | public static function mb_array_change_key_case($input, $case = MB_CASE_LOWER, $encoding = 'UTF-8') { |
@@ -57,654 +57,654 @@ |
||
| 57 | 57 | * @since 4.0.0 |
| 58 | 58 | */ |
| 59 | 59 | class Util { |
| 60 | - // consts for Logging |
|
| 61 | - const DEBUG=0; |
|
| 62 | - const INFO=1; |
|
| 63 | - const WARN=2; |
|
| 64 | - const ERROR=3; |
|
| 65 | - const FATAL=4; |
|
| 66 | - |
|
| 67 | - /** \OCP\Share\IManager */ |
|
| 68 | - private static $shareManager; |
|
| 69 | - |
|
| 70 | - /** |
|
| 71 | - * get the current installed version of ownCloud |
|
| 72 | - * @return array |
|
| 73 | - * @since 4.0.0 |
|
| 74 | - */ |
|
| 75 | - public static function getVersion() { |
|
| 76 | - return(\OC_Util::getVersion()); |
|
| 77 | - } |
|
| 60 | + // consts for Logging |
|
| 61 | + const DEBUG=0; |
|
| 62 | + const INFO=1; |
|
| 63 | + const WARN=2; |
|
| 64 | + const ERROR=3; |
|
| 65 | + const FATAL=4; |
|
| 66 | + |
|
| 67 | + /** \OCP\Share\IManager */ |
|
| 68 | + private static $shareManager; |
|
| 69 | + |
|
| 70 | + /** |
|
| 71 | + * get the current installed version of ownCloud |
|
| 72 | + * @return array |
|
| 73 | + * @since 4.0.0 |
|
| 74 | + */ |
|
| 75 | + public static function getVersion() { |
|
| 76 | + return(\OC_Util::getVersion()); |
|
| 77 | + } |
|
| 78 | 78 | |
| 79 | - /** |
|
| 80 | - * Set current update channel |
|
| 81 | - * @param string $channel |
|
| 82 | - * @since 8.1.0 |
|
| 83 | - */ |
|
| 84 | - public static function setChannel($channel) { |
|
| 85 | - \OC::$server->getConfig()->setSystemValue('updater.release.channel', $channel); |
|
| 86 | - } |
|
| 79 | + /** |
|
| 80 | + * Set current update channel |
|
| 81 | + * @param string $channel |
|
| 82 | + * @since 8.1.0 |
|
| 83 | + */ |
|
| 84 | + public static function setChannel($channel) { |
|
| 85 | + \OC::$server->getConfig()->setSystemValue('updater.release.channel', $channel); |
|
| 86 | + } |
|
| 87 | 87 | |
| 88 | - /** |
|
| 89 | - * Get current update channel |
|
| 90 | - * @return string |
|
| 91 | - * @since 8.1.0 |
|
| 92 | - */ |
|
| 93 | - public static function getChannel() { |
|
| 94 | - return \OC_Util::getChannel(); |
|
| 95 | - } |
|
| 96 | - |
|
| 97 | - /** |
|
| 98 | - * send an email |
|
| 99 | - * @param string $toaddress |
|
| 100 | - * @param string $toname |
|
| 101 | - * @param string $subject |
|
| 102 | - * @param string $mailtext |
|
| 103 | - * @param string $fromaddress |
|
| 104 | - * @param string $fromname |
|
| 105 | - * @param int $html |
|
| 106 | - * @param string $altbody |
|
| 107 | - * @param string $ccaddress |
|
| 108 | - * @param string $ccname |
|
| 109 | - * @param string $bcc |
|
| 110 | - * @deprecated 8.1.0 Use \OCP\Mail\IMailer instead |
|
| 111 | - * @since 4.0.0 |
|
| 112 | - */ |
|
| 113 | - public static function sendMail($toaddress, $toname, $subject, $mailtext, $fromaddress, $fromname, |
|
| 114 | - $html = 0, $altbody = '', $ccaddress = '', $ccname = '', $bcc = '') { |
|
| 115 | - $mailer = \OC::$server->getMailer(); |
|
| 116 | - $message = $mailer->createMessage(); |
|
| 117 | - $message->setTo([$toaddress => $toname]); |
|
| 118 | - $message->setSubject($subject); |
|
| 119 | - $message->setPlainBody($mailtext); |
|
| 120 | - $message->setFrom([$fromaddress => $fromname]); |
|
| 121 | - if($html === 1) { |
|
| 122 | - $message->setHTMLBody($altbody); |
|
| 123 | - } |
|
| 124 | - |
|
| 125 | - if($altbody === '') { |
|
| 126 | - $message->setHTMLBody($mailtext); |
|
| 127 | - $message->setPlainBody(''); |
|
| 128 | - } else { |
|
| 129 | - $message->setHtmlBody($mailtext); |
|
| 130 | - $message->setPlainBody($altbody); |
|
| 131 | - } |
|
| 132 | - |
|
| 133 | - if(!empty($ccaddress)) { |
|
| 134 | - if(!empty($ccname)) { |
|
| 135 | - $message->setCc([$ccaddress => $ccname]); |
|
| 136 | - } else { |
|
| 137 | - $message->setCc([$ccaddress]); |
|
| 138 | - } |
|
| 139 | - } |
|
| 140 | - if(!empty($bcc)) { |
|
| 141 | - $message->setBcc([$bcc]); |
|
| 142 | - } |
|
| 143 | - |
|
| 144 | - $mailer->send($message); |
|
| 145 | - } |
|
| 146 | - |
|
| 147 | - /** |
|
| 148 | - * write a message in the log |
|
| 149 | - * @param string $app |
|
| 150 | - * @param string $message |
|
| 151 | - * @param int $level |
|
| 152 | - * @since 4.0.0 |
|
| 153 | - */ |
|
| 154 | - public static function writeLog( $app, $message, $level ) { |
|
| 155 | - $context = ['app' => $app]; |
|
| 156 | - \OC::$server->getLogger()->log($level, $message, $context); |
|
| 157 | - } |
|
| 158 | - |
|
| 159 | - /** |
|
| 160 | - * write exception into the log |
|
| 161 | - * @param string $app app name |
|
| 162 | - * @param \Exception $ex exception to log |
|
| 163 | - * @param int $level log level, defaults to \OCP\Util::FATAL |
|
| 164 | - * @since ....0.0 - parameter $level was added in 7.0.0 |
|
| 165 | - * @deprecated 8.2.0 use logException of \OCP\ILogger |
|
| 166 | - */ |
|
| 167 | - public static function logException( $app, \Exception $ex, $level = \OCP\Util::FATAL ) { |
|
| 168 | - \OC::$server->getLogger()->logException($ex, ['app' => $app]); |
|
| 169 | - } |
|
| 170 | - |
|
| 171 | - /** |
|
| 172 | - * check if sharing is disabled for the current user |
|
| 173 | - * |
|
| 174 | - * @return boolean |
|
| 175 | - * @since 7.0.0 |
|
| 176 | - * @deprecated 9.1.0 Use \OC::$server->getShareManager()->sharingDisabledForUser |
|
| 177 | - */ |
|
| 178 | - public static function isSharingDisabledForUser() { |
|
| 179 | - if (self::$shareManager === null) { |
|
| 180 | - self::$shareManager = \OC::$server->getShareManager(); |
|
| 181 | - } |
|
| 182 | - |
|
| 183 | - $user = \OC::$server->getUserSession()->getUser(); |
|
| 184 | - if ($user !== null) { |
|
| 185 | - $user = $user->getUID(); |
|
| 186 | - } |
|
| 187 | - |
|
| 188 | - return self::$shareManager->sharingDisabledForUser($user); |
|
| 189 | - } |
|
| 190 | - |
|
| 191 | - /** |
|
| 192 | - * get l10n object |
|
| 193 | - * @param string $application |
|
| 194 | - * @param string|null $language |
|
| 195 | - * @return \OCP\IL10N |
|
| 196 | - * @since 6.0.0 - parameter $language was added in 8.0.0 |
|
| 197 | - */ |
|
| 198 | - public static function getL10N($application, $language = null) { |
|
| 199 | - return \OC::$server->getL10N($application, $language); |
|
| 200 | - } |
|
| 201 | - |
|
| 202 | - /** |
|
| 203 | - * add a css file |
|
| 204 | - * @param string $application |
|
| 205 | - * @param string $file |
|
| 206 | - * @since 4.0.0 |
|
| 207 | - */ |
|
| 208 | - public static function addStyle( $application, $file = null ) { |
|
| 209 | - \OC_Util::addStyle( $application, $file ); |
|
| 210 | - } |
|
| 211 | - |
|
| 212 | - /** |
|
| 213 | - * add a javascript file |
|
| 214 | - * @param string $application |
|
| 215 | - * @param string $file |
|
| 216 | - * @since 4.0.0 |
|
| 217 | - */ |
|
| 218 | - public static function addScript( $application, $file = null ) { |
|
| 219 | - \OC_Util::addScript( $application, $file ); |
|
| 220 | - } |
|
| 221 | - |
|
| 222 | - /** |
|
| 223 | - * Add a translation JS file |
|
| 224 | - * @param string $application application id |
|
| 225 | - * @param string $languageCode language code, defaults to the current locale |
|
| 226 | - * @since 8.0.0 |
|
| 227 | - */ |
|
| 228 | - public static function addTranslations($application, $languageCode = null) { |
|
| 229 | - \OC_Util::addTranslations($application, $languageCode); |
|
| 230 | - } |
|
| 231 | - |
|
| 232 | - /** |
|
| 233 | - * Add a custom element to the header |
|
| 234 | - * If $text is null then the element will be written as empty element. |
|
| 235 | - * So use "" to get a closing tag. |
|
| 236 | - * @param string $tag tag name of the element |
|
| 237 | - * @param array $attributes array of attributes for the element |
|
| 238 | - * @param string $text the text content for the element |
|
| 239 | - * @since 4.0.0 |
|
| 240 | - */ |
|
| 241 | - public static function addHeader($tag, $attributes, $text=null) { |
|
| 242 | - \OC_Util::addHeader($tag, $attributes, $text); |
|
| 243 | - } |
|
| 244 | - |
|
| 245 | - /** |
|
| 246 | - * formats a timestamp in the "right" way |
|
| 247 | - * @param int $timestamp $timestamp |
|
| 248 | - * @param bool $dateOnly option to omit time from the result |
|
| 249 | - * @param DateTimeZone|string $timeZone where the given timestamp shall be converted to |
|
| 250 | - * @return string timestamp |
|
| 251 | - * |
|
| 252 | - * @deprecated 8.0.0 Use \OC::$server->query('DateTimeFormatter') instead |
|
| 253 | - * @since 4.0.0 |
|
| 254 | - */ |
|
| 255 | - public static function formatDate($timestamp, $dateOnly=false, $timeZone = null) { |
|
| 256 | - return(\OC_Util::formatDate($timestamp, $dateOnly, $timeZone)); |
|
| 257 | - } |
|
| 258 | - |
|
| 259 | - /** |
|
| 260 | - * check if some encrypted files are stored |
|
| 261 | - * @return bool |
|
| 262 | - * |
|
| 263 | - * @deprecated 8.1.0 No longer required |
|
| 264 | - * @since 6.0.0 |
|
| 265 | - */ |
|
| 266 | - public static function encryptedFiles() { |
|
| 267 | - return false; |
|
| 268 | - } |
|
| 269 | - |
|
| 270 | - /** |
|
| 271 | - * Creates an absolute url to the given app and file. |
|
| 272 | - * @param string $app app |
|
| 273 | - * @param string $file file |
|
| 274 | - * @param array $args array with param=>value, will be appended to the returned url |
|
| 275 | - * The value of $args will be urlencoded |
|
| 276 | - * @return string the url |
|
| 277 | - * @since 4.0.0 - parameter $args was added in 4.5.0 |
|
| 278 | - */ |
|
| 279 | - public static function linkToAbsolute( $app, $file, $args = array() ) { |
|
| 280 | - $urlGenerator = \OC::$server->getURLGenerator(); |
|
| 281 | - return $urlGenerator->getAbsoluteURL( |
|
| 282 | - $urlGenerator->linkTo($app, $file, $args) |
|
| 283 | - ); |
|
| 284 | - } |
|
| 285 | - |
|
| 286 | - /** |
|
| 287 | - * Creates an absolute url for remote use. |
|
| 288 | - * @param string $service id |
|
| 289 | - * @return string the url |
|
| 290 | - * @since 4.0.0 |
|
| 291 | - */ |
|
| 292 | - public static function linkToRemote( $service ) { |
|
| 293 | - $urlGenerator = \OC::$server->getURLGenerator(); |
|
| 294 | - $remoteBase = $urlGenerator->linkTo('', 'remote.php') . '/' . $service; |
|
| 295 | - return $urlGenerator->getAbsoluteURL( |
|
| 296 | - $remoteBase . (($service[strlen($service) - 1] != '/') ? '/' : '') |
|
| 297 | - ); |
|
| 298 | - } |
|
| 299 | - |
|
| 300 | - /** |
|
| 301 | - * Creates an absolute url for public use |
|
| 302 | - * @param string $service id |
|
| 303 | - * @return string the url |
|
| 304 | - * @since 4.5.0 |
|
| 305 | - */ |
|
| 306 | - public static function linkToPublic($service) { |
|
| 307 | - return \OC_Helper::linkToPublic($service); |
|
| 308 | - } |
|
| 309 | - |
|
| 310 | - /** |
|
| 311 | - * Creates an url using a defined route |
|
| 312 | - * @param string $route |
|
| 313 | - * @param array $parameters |
|
| 314 | - * @internal param array $args with param=>value, will be appended to the returned url |
|
| 315 | - * @return string the url |
|
| 316 | - * @deprecated 8.1.0 Use \OC::$server->getURLGenerator()->linkToRoute($route, $parameters) |
|
| 317 | - * @since 5.0.0 |
|
| 318 | - */ |
|
| 319 | - public static function linkToRoute( $route, $parameters = array() ) { |
|
| 320 | - return \OC::$server->getURLGenerator()->linkToRoute($route, $parameters); |
|
| 321 | - } |
|
| 322 | - |
|
| 323 | - /** |
|
| 324 | - * Creates an url to the given app and file |
|
| 325 | - * @param string $app app |
|
| 326 | - * @param string $file file |
|
| 327 | - * @param array $args array with param=>value, will be appended to the returned url |
|
| 328 | - * The value of $args will be urlencoded |
|
| 329 | - * @return string the url |
|
| 330 | - * @deprecated 8.1.0 Use \OC::$server->getURLGenerator()->linkTo($app, $file, $args) |
|
| 331 | - * @since 4.0.0 - parameter $args was added in 4.5.0 |
|
| 332 | - */ |
|
| 333 | - public static function linkTo( $app, $file, $args = array() ) { |
|
| 334 | - return \OC::$server->getURLGenerator()->linkTo($app, $file, $args); |
|
| 335 | - } |
|
| 336 | - |
|
| 337 | - /** |
|
| 338 | - * Returns the server host, even if the website uses one or more reverse proxy |
|
| 339 | - * @return string the server host |
|
| 340 | - * @deprecated 8.1.0 Use \OCP\IRequest::getServerHost |
|
| 341 | - * @since 4.0.0 |
|
| 342 | - */ |
|
| 343 | - public static function getServerHost() { |
|
| 344 | - return \OC::$server->getRequest()->getServerHost(); |
|
| 345 | - } |
|
| 346 | - |
|
| 347 | - /** |
|
| 348 | - * Returns the server host name without an eventual port number |
|
| 349 | - * @return string the server hostname |
|
| 350 | - * @since 5.0.0 |
|
| 351 | - */ |
|
| 352 | - public static function getServerHostName() { |
|
| 353 | - $host_name = self::getServerHost(); |
|
| 354 | - // strip away port number (if existing) |
|
| 355 | - $colon_pos = strpos($host_name, ':'); |
|
| 356 | - if ($colon_pos != FALSE) { |
|
| 357 | - $host_name = substr($host_name, 0, $colon_pos); |
|
| 358 | - } |
|
| 359 | - return $host_name; |
|
| 360 | - } |
|
| 361 | - |
|
| 362 | - /** |
|
| 363 | - * Returns the default email address |
|
| 364 | - * @param string $user_part the user part of the address |
|
| 365 | - * @return string the default email address |
|
| 366 | - * |
|
| 367 | - * Assembles a default email address (using the server hostname |
|
| 368 | - * and the given user part, and returns it |
|
| 369 | - * Example: when given lostpassword-noreply as $user_part param, |
|
| 370 | - * and is currently accessed via http(s)://example.com/, |
|
| 371 | - * it would return '[email protected]' |
|
| 372 | - * |
|
| 373 | - * If the configuration value 'mail_from_address' is set in |
|
| 374 | - * config.php, this value will override the $user_part that |
|
| 375 | - * is passed to this function |
|
| 376 | - * @since 5.0.0 |
|
| 377 | - */ |
|
| 378 | - public static function getDefaultEmailAddress($user_part) { |
|
| 379 | - $config = \OC::$server->getConfig(); |
|
| 380 | - $user_part = $config->getSystemValue('mail_from_address', $user_part); |
|
| 381 | - $host_name = self::getServerHostName(); |
|
| 382 | - $host_name = $config->getSystemValue('mail_domain', $host_name); |
|
| 383 | - $defaultEmailAddress = $user_part.'@'.$host_name; |
|
| 384 | - |
|
| 385 | - $mailer = \OC::$server->getMailer(); |
|
| 386 | - if ($mailer->validateMailAddress($defaultEmailAddress)) { |
|
| 387 | - return $defaultEmailAddress; |
|
| 388 | - } |
|
| 389 | - |
|
| 390 | - // in case we cannot build a valid email address from the hostname let's fallback to 'localhost.localdomain' |
|
| 391 | - return $user_part.'@localhost.localdomain'; |
|
| 392 | - } |
|
| 393 | - |
|
| 394 | - /** |
|
| 395 | - * Returns the server protocol. It respects reverse proxy servers and load balancers |
|
| 396 | - * @return string the server protocol |
|
| 397 | - * @deprecated 8.1.0 Use \OCP\IRequest::getServerProtocol |
|
| 398 | - * @since 4.5.0 |
|
| 399 | - */ |
|
| 400 | - public static function getServerProtocol() { |
|
| 401 | - return \OC::$server->getRequest()->getServerProtocol(); |
|
| 402 | - } |
|
| 403 | - |
|
| 404 | - /** |
|
| 405 | - * Returns the request uri, even if the website uses one or more reverse proxies |
|
| 406 | - * @return string the request uri |
|
| 407 | - * @deprecated 8.1.0 Use \OCP\IRequest::getRequestUri |
|
| 408 | - * @since 5.0.0 |
|
| 409 | - */ |
|
| 410 | - public static function getRequestUri() { |
|
| 411 | - return \OC::$server->getRequest()->getRequestUri(); |
|
| 412 | - } |
|
| 413 | - |
|
| 414 | - /** |
|
| 415 | - * Returns the script name, even if the website uses one or more reverse proxies |
|
| 416 | - * @return string the script name |
|
| 417 | - * @deprecated 8.1.0 Use \OCP\IRequest::getScriptName |
|
| 418 | - * @since 5.0.0 |
|
| 419 | - */ |
|
| 420 | - public static function getScriptName() { |
|
| 421 | - return \OC::$server->getRequest()->getScriptName(); |
|
| 422 | - } |
|
| 423 | - |
|
| 424 | - /** |
|
| 425 | - * Creates path to an image |
|
| 426 | - * @param string $app app |
|
| 427 | - * @param string $image image name |
|
| 428 | - * @return string the url |
|
| 429 | - * @deprecated 8.1.0 Use \OC::$server->getURLGenerator()->imagePath($app, $image) |
|
| 430 | - * @since 4.0.0 |
|
| 431 | - */ |
|
| 432 | - public static function imagePath( $app, $image ) { |
|
| 433 | - return \OC::$server->getURLGenerator()->imagePath($app, $image); |
|
| 434 | - } |
|
| 435 | - |
|
| 436 | - /** |
|
| 437 | - * Make a human file size (2048 to 2 kB) |
|
| 438 | - * @param int $bytes file size in bytes |
|
| 439 | - * @return string a human readable file size |
|
| 440 | - * @since 4.0.0 |
|
| 441 | - */ |
|
| 442 | - public static function humanFileSize( $bytes ) { |
|
| 443 | - return(\OC_Helper::humanFileSize( $bytes )); |
|
| 444 | - } |
|
| 445 | - |
|
| 446 | - /** |
|
| 447 | - * Make a computer file size (2 kB to 2048) |
|
| 448 | - * @param string $str file size in a fancy format |
|
| 449 | - * @return int a file size in bytes |
|
| 450 | - * |
|
| 451 | - * Inspired by: http://www.php.net/manual/en/function.filesize.php#92418 |
|
| 452 | - * @since 4.0.0 |
|
| 453 | - */ |
|
| 454 | - public static function computerFileSize( $str ) { |
|
| 455 | - return(\OC_Helper::computerFileSize( $str )); |
|
| 456 | - } |
|
| 457 | - |
|
| 458 | - /** |
|
| 459 | - * connects a function to a hook |
|
| 460 | - * |
|
| 461 | - * @param string $signalClass class name of emitter |
|
| 462 | - * @param string $signalName name of signal |
|
| 463 | - * @param string|object $slotClass class name of slot |
|
| 464 | - * @param string $slotName name of slot |
|
| 465 | - * @return bool |
|
| 466 | - * |
|
| 467 | - * This function makes it very easy to connect to use hooks. |
|
| 468 | - * |
|
| 469 | - * TODO: write example |
|
| 470 | - * @since 4.0.0 |
|
| 471 | - */ |
|
| 472 | - static public function connectHook($signalClass, $signalName, $slotClass, $slotName ) { |
|
| 473 | - return(\OC_Hook::connect($signalClass, $signalName, $slotClass, $slotName )); |
|
| 474 | - } |
|
| 475 | - |
|
| 476 | - /** |
|
| 477 | - * Emits a signal. To get data from the slot use references! |
|
| 478 | - * @param string $signalclass class name of emitter |
|
| 479 | - * @param string $signalname name of signal |
|
| 480 | - * @param array $params default: array() array with additional data |
|
| 481 | - * @return bool true if slots exists or false if not |
|
| 482 | - * |
|
| 483 | - * TODO: write example |
|
| 484 | - * @since 4.0.0 |
|
| 485 | - */ |
|
| 486 | - static public function emitHook( $signalclass, $signalname, $params = array()) { |
|
| 487 | - return(\OC_Hook::emit( $signalclass, $signalname, $params )); |
|
| 488 | - } |
|
| 489 | - |
|
| 490 | - /** |
|
| 491 | - * Cached encrypted CSRF token. Some static unit-tests of ownCloud compare |
|
| 492 | - * multiple OC_Template elements which invoke `callRegister`. If the value |
|
| 493 | - * would not be cached these unit-tests would fail. |
|
| 494 | - * @var string |
|
| 495 | - */ |
|
| 496 | - private static $token = ''; |
|
| 497 | - |
|
| 498 | - /** |
|
| 499 | - * Register an get/post call. This is important to prevent CSRF attacks |
|
| 500 | - * @since 4.5.0 |
|
| 501 | - */ |
|
| 502 | - public static function callRegister() { |
|
| 503 | - if(self::$token === '') { |
|
| 504 | - self::$token = \OC::$server->getCsrfTokenManager()->getToken()->getEncryptedValue(); |
|
| 505 | - } |
|
| 506 | - return self::$token; |
|
| 507 | - } |
|
| 508 | - |
|
| 509 | - /** |
|
| 510 | - * Check an ajax get/post call if the request token is valid. exit if not. |
|
| 511 | - * @since 4.5.0 |
|
| 512 | - * @deprecated 9.0.0 Use annotations based on the app framework. |
|
| 513 | - */ |
|
| 514 | - public static function callCheck() { |
|
| 515 | - if(!\OC::$server->getRequest()->passesStrictCookieCheck()) { |
|
| 516 | - header('Location: '.\OC::$WEBROOT); |
|
| 517 | - exit(); |
|
| 518 | - } |
|
| 519 | - |
|
| 520 | - if (!(\OC::$server->getRequest()->passesCSRFCheck())) { |
|
| 521 | - exit(); |
|
| 522 | - } |
|
| 523 | - } |
|
| 524 | - |
|
| 525 | - /** |
|
| 526 | - * Used to sanitize HTML |
|
| 527 | - * |
|
| 528 | - * This function is used to sanitize HTML and should be applied on any |
|
| 529 | - * string or array of strings before displaying it on a web page. |
|
| 530 | - * |
|
| 531 | - * @param string|array $value |
|
| 532 | - * @return string|array an array of sanitized strings or a single sanitized string, depends on the input parameter. |
|
| 533 | - * @since 4.5.0 |
|
| 534 | - */ |
|
| 535 | - public static function sanitizeHTML($value) { |
|
| 536 | - return \OC_Util::sanitizeHTML($value); |
|
| 537 | - } |
|
| 538 | - |
|
| 539 | - /** |
|
| 540 | - * Public function to encode url parameters |
|
| 541 | - * |
|
| 542 | - * This function is used to encode path to file before output. |
|
| 543 | - * Encoding is done according to RFC 3986 with one exception: |
|
| 544 | - * Character '/' is preserved as is. |
|
| 545 | - * |
|
| 546 | - * @param string $component part of URI to encode |
|
| 547 | - * @return string |
|
| 548 | - * @since 6.0.0 |
|
| 549 | - */ |
|
| 550 | - public static function encodePath($component) { |
|
| 551 | - return(\OC_Util::encodePath($component)); |
|
| 552 | - } |
|
| 553 | - |
|
| 554 | - /** |
|
| 555 | - * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is. |
|
| 556 | - * |
|
| 557 | - * @param array $input The array to work on |
|
| 558 | - * @param int $case Either MB_CASE_UPPER or MB_CASE_LOWER (default) |
|
| 559 | - * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8 |
|
| 560 | - * @return array |
|
| 561 | - * @since 4.5.0 |
|
| 562 | - */ |
|
| 563 | - public static function mb_array_change_key_case($input, $case = MB_CASE_LOWER, $encoding = 'UTF-8') { |
|
| 564 | - return(\OC_Helper::mb_array_change_key_case($input, $case, $encoding)); |
|
| 565 | - } |
|
| 566 | - |
|
| 567 | - /** |
|
| 568 | - * replaces a copy of string delimited by the start and (optionally) length parameters with the string given in replacement. |
|
| 569 | - * |
|
| 570 | - * @param string $string The input string. Opposite to the PHP build-in function does not accept an array. |
|
| 571 | - * @param string $replacement The replacement string. |
|
| 572 | - * @param int $start If start is positive, the replacing will begin at the start'th offset into string. If start is negative, the replacing will begin at the start'th character from the end of string. |
|
| 573 | - * @param int $length Length of the part to be replaced |
|
| 574 | - * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8 |
|
| 575 | - * @return string |
|
| 576 | - * @since 4.5.0 |
|
| 577 | - * @deprecated 8.2.0 Use substr_replace() instead. |
|
| 578 | - */ |
|
| 579 | - public static function mb_substr_replace($string, $replacement, $start, $length = null, $encoding = 'UTF-8') { |
|
| 580 | - return substr_replace($string, $replacement, $start, $length); |
|
| 581 | - } |
|
| 582 | - |
|
| 583 | - /** |
|
| 584 | - * Replace all occurrences of the search string with the replacement string |
|
| 585 | - * |
|
| 586 | - * @param string $search The value being searched for, otherwise known as the needle. String. |
|
| 587 | - * @param string $replace The replacement string. |
|
| 588 | - * @param string $subject The string or array being searched and replaced on, otherwise known as the haystack. |
|
| 589 | - * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8 |
|
| 590 | - * @param int $count If passed, this will be set to the number of replacements performed. |
|
| 591 | - * @return string |
|
| 592 | - * @since 4.5.0 |
|
| 593 | - * @deprecated 8.2.0 Use str_replace() instead. |
|
| 594 | - */ |
|
| 595 | - public static function mb_str_replace($search, $replace, $subject, $encoding = 'UTF-8', &$count = null) { |
|
| 596 | - return str_replace($search, $replace, $subject, $count); |
|
| 597 | - } |
|
| 598 | - |
|
| 599 | - /** |
|
| 600 | - * performs a search in a nested array |
|
| 601 | - * |
|
| 602 | - * @param array $haystack the array to be searched |
|
| 603 | - * @param string $needle the search string |
|
| 604 | - * @param int $index optional, only search this key name |
|
| 605 | - * @return mixed the key of the matching field, otherwise false |
|
| 606 | - * @since 4.5.0 |
|
| 607 | - */ |
|
| 608 | - public static function recursiveArraySearch($haystack, $needle, $index = null) { |
|
| 609 | - return(\OC_Helper::recursiveArraySearch($haystack, $needle, $index)); |
|
| 610 | - } |
|
| 611 | - |
|
| 612 | - /** |
|
| 613 | - * calculates the maximum upload size respecting system settings, free space and user quota |
|
| 614 | - * |
|
| 615 | - * @param string $dir the current folder where the user currently operates |
|
| 616 | - * @param int $free the number of bytes free on the storage holding $dir, if not set this will be received from the storage directly |
|
| 617 | - * @return int number of bytes representing |
|
| 618 | - * @since 5.0.0 |
|
| 619 | - */ |
|
| 620 | - public static function maxUploadFilesize($dir, $free = null) { |
|
| 621 | - return \OC_Helper::maxUploadFilesize($dir, $free); |
|
| 622 | - } |
|
| 623 | - |
|
| 624 | - /** |
|
| 625 | - * Calculate free space left within user quota |
|
| 626 | - * @param string $dir the current folder where the user currently operates |
|
| 627 | - * @return int number of bytes representing |
|
| 628 | - * @since 7.0.0 |
|
| 629 | - */ |
|
| 630 | - public static function freeSpace($dir) { |
|
| 631 | - return \OC_Helper::freeSpace($dir); |
|
| 632 | - } |
|
| 633 | - |
|
| 634 | - /** |
|
| 635 | - * Calculate PHP upload limit |
|
| 636 | - * |
|
| 637 | - * @return int number of bytes representing |
|
| 638 | - * @since 7.0.0 |
|
| 639 | - */ |
|
| 640 | - public static function uploadLimit() { |
|
| 641 | - return \OC_Helper::uploadLimit(); |
|
| 642 | - } |
|
| 643 | - |
|
| 644 | - /** |
|
| 645 | - * Returns whether the given file name is valid |
|
| 646 | - * @param string $file file name to check |
|
| 647 | - * @return bool true if the file name is valid, false otherwise |
|
| 648 | - * @deprecated 8.1.0 use \OC\Files\View::verifyPath() |
|
| 649 | - * @since 7.0.0 |
|
| 650 | - */ |
|
| 651 | - public static function isValidFileName($file) { |
|
| 652 | - return \OC_Util::isValidFileName($file); |
|
| 653 | - } |
|
| 654 | - |
|
| 655 | - /** |
|
| 656 | - * Generates a cryptographic secure pseudo-random string |
|
| 657 | - * @param int $length of the random string |
|
| 658 | - * @return string |
|
| 659 | - * @deprecated 8.0.0 Use \OC::$server->getSecureRandom()->getMediumStrengthGenerator()->generate($length); instead |
|
| 660 | - * @since 7.0.0 |
|
| 661 | - */ |
|
| 662 | - public static function generateRandomBytes($length = 30) { |
|
| 663 | - return \OC::$server->getSecureRandom()->generate($length, \OCP\Security\ISecureRandom::CHAR_LOWER.\OCP\Security\ISecureRandom::CHAR_DIGITS); |
|
| 664 | - } |
|
| 665 | - |
|
| 666 | - /** |
|
| 667 | - * Compare two strings to provide a natural sort |
|
| 668 | - * @param string $a first string to compare |
|
| 669 | - * @param string $b second string to compare |
|
| 670 | - * @return -1 if $b comes before $a, 1 if $a comes before $b |
|
| 671 | - * or 0 if the strings are identical |
|
| 672 | - * @since 7.0.0 |
|
| 673 | - */ |
|
| 674 | - public static function naturalSortCompare($a, $b) { |
|
| 675 | - return \OC\NaturalSort::getInstance()->compare($a, $b); |
|
| 676 | - } |
|
| 677 | - |
|
| 678 | - /** |
|
| 679 | - * check if a password is required for each public link |
|
| 680 | - * @return boolean |
|
| 681 | - * @since 7.0.0 |
|
| 682 | - */ |
|
| 683 | - public static function isPublicLinkPasswordRequired() { |
|
| 684 | - return \OC_Util::isPublicLinkPasswordRequired(); |
|
| 685 | - } |
|
| 686 | - |
|
| 687 | - /** |
|
| 688 | - * check if share API enforces a default expire date |
|
| 689 | - * @return boolean |
|
| 690 | - * @since 8.0.0 |
|
| 691 | - */ |
|
| 692 | - public static function isDefaultExpireDateEnforced() { |
|
| 693 | - return \OC_Util::isDefaultExpireDateEnforced(); |
|
| 694 | - } |
|
| 695 | - |
|
| 696 | - protected static $needUpgradeCache = null; |
|
| 697 | - |
|
| 698 | - /** |
|
| 699 | - * Checks whether the current version needs upgrade. |
|
| 700 | - * |
|
| 701 | - * @return bool true if upgrade is needed, false otherwise |
|
| 702 | - * @since 7.0.0 |
|
| 703 | - */ |
|
| 704 | - public static function needUpgrade() { |
|
| 705 | - if (!isset(self::$needUpgradeCache)) { |
|
| 706 | - self::$needUpgradeCache=\OC_Util::needUpgrade(\OC::$server->getConfig()); |
|
| 707 | - } |
|
| 708 | - return self::$needUpgradeCache; |
|
| 709 | - } |
|
| 88 | + /** |
|
| 89 | + * Get current update channel |
|
| 90 | + * @return string |
|
| 91 | + * @since 8.1.0 |
|
| 92 | + */ |
|
| 93 | + public static function getChannel() { |
|
| 94 | + return \OC_Util::getChannel(); |
|
| 95 | + } |
|
| 96 | + |
|
| 97 | + /** |
|
| 98 | + * send an email |
|
| 99 | + * @param string $toaddress |
|
| 100 | + * @param string $toname |
|
| 101 | + * @param string $subject |
|
| 102 | + * @param string $mailtext |
|
| 103 | + * @param string $fromaddress |
|
| 104 | + * @param string $fromname |
|
| 105 | + * @param int $html |
|
| 106 | + * @param string $altbody |
|
| 107 | + * @param string $ccaddress |
|
| 108 | + * @param string $ccname |
|
| 109 | + * @param string $bcc |
|
| 110 | + * @deprecated 8.1.0 Use \OCP\Mail\IMailer instead |
|
| 111 | + * @since 4.0.0 |
|
| 112 | + */ |
|
| 113 | + public static function sendMail($toaddress, $toname, $subject, $mailtext, $fromaddress, $fromname, |
|
| 114 | + $html = 0, $altbody = '', $ccaddress = '', $ccname = '', $bcc = '') { |
|
| 115 | + $mailer = \OC::$server->getMailer(); |
|
| 116 | + $message = $mailer->createMessage(); |
|
| 117 | + $message->setTo([$toaddress => $toname]); |
|
| 118 | + $message->setSubject($subject); |
|
| 119 | + $message->setPlainBody($mailtext); |
|
| 120 | + $message->setFrom([$fromaddress => $fromname]); |
|
| 121 | + if($html === 1) { |
|
| 122 | + $message->setHTMLBody($altbody); |
|
| 123 | + } |
|
| 124 | + |
|
| 125 | + if($altbody === '') { |
|
| 126 | + $message->setHTMLBody($mailtext); |
|
| 127 | + $message->setPlainBody(''); |
|
| 128 | + } else { |
|
| 129 | + $message->setHtmlBody($mailtext); |
|
| 130 | + $message->setPlainBody($altbody); |
|
| 131 | + } |
|
| 132 | + |
|
| 133 | + if(!empty($ccaddress)) { |
|
| 134 | + if(!empty($ccname)) { |
|
| 135 | + $message->setCc([$ccaddress => $ccname]); |
|
| 136 | + } else { |
|
| 137 | + $message->setCc([$ccaddress]); |
|
| 138 | + } |
|
| 139 | + } |
|
| 140 | + if(!empty($bcc)) { |
|
| 141 | + $message->setBcc([$bcc]); |
|
| 142 | + } |
|
| 143 | + |
|
| 144 | + $mailer->send($message); |
|
| 145 | + } |
|
| 146 | + |
|
| 147 | + /** |
|
| 148 | + * write a message in the log |
|
| 149 | + * @param string $app |
|
| 150 | + * @param string $message |
|
| 151 | + * @param int $level |
|
| 152 | + * @since 4.0.0 |
|
| 153 | + */ |
|
| 154 | + public static function writeLog( $app, $message, $level ) { |
|
| 155 | + $context = ['app' => $app]; |
|
| 156 | + \OC::$server->getLogger()->log($level, $message, $context); |
|
| 157 | + } |
|
| 158 | + |
|
| 159 | + /** |
|
| 160 | + * write exception into the log |
|
| 161 | + * @param string $app app name |
|
| 162 | + * @param \Exception $ex exception to log |
|
| 163 | + * @param int $level log level, defaults to \OCP\Util::FATAL |
|
| 164 | + * @since ....0.0 - parameter $level was added in 7.0.0 |
|
| 165 | + * @deprecated 8.2.0 use logException of \OCP\ILogger |
|
| 166 | + */ |
|
| 167 | + public static function logException( $app, \Exception $ex, $level = \OCP\Util::FATAL ) { |
|
| 168 | + \OC::$server->getLogger()->logException($ex, ['app' => $app]); |
|
| 169 | + } |
|
| 170 | + |
|
| 171 | + /** |
|
| 172 | + * check if sharing is disabled for the current user |
|
| 173 | + * |
|
| 174 | + * @return boolean |
|
| 175 | + * @since 7.0.0 |
|
| 176 | + * @deprecated 9.1.0 Use \OC::$server->getShareManager()->sharingDisabledForUser |
|
| 177 | + */ |
|
| 178 | + public static function isSharingDisabledForUser() { |
|
| 179 | + if (self::$shareManager === null) { |
|
| 180 | + self::$shareManager = \OC::$server->getShareManager(); |
|
| 181 | + } |
|
| 182 | + |
|
| 183 | + $user = \OC::$server->getUserSession()->getUser(); |
|
| 184 | + if ($user !== null) { |
|
| 185 | + $user = $user->getUID(); |
|
| 186 | + } |
|
| 187 | + |
|
| 188 | + return self::$shareManager->sharingDisabledForUser($user); |
|
| 189 | + } |
|
| 190 | + |
|
| 191 | + /** |
|
| 192 | + * get l10n object |
|
| 193 | + * @param string $application |
|
| 194 | + * @param string|null $language |
|
| 195 | + * @return \OCP\IL10N |
|
| 196 | + * @since 6.0.0 - parameter $language was added in 8.0.0 |
|
| 197 | + */ |
|
| 198 | + public static function getL10N($application, $language = null) { |
|
| 199 | + return \OC::$server->getL10N($application, $language); |
|
| 200 | + } |
|
| 201 | + |
|
| 202 | + /** |
|
| 203 | + * add a css file |
|
| 204 | + * @param string $application |
|
| 205 | + * @param string $file |
|
| 206 | + * @since 4.0.0 |
|
| 207 | + */ |
|
| 208 | + public static function addStyle( $application, $file = null ) { |
|
| 209 | + \OC_Util::addStyle( $application, $file ); |
|
| 210 | + } |
|
| 211 | + |
|
| 212 | + /** |
|
| 213 | + * add a javascript file |
|
| 214 | + * @param string $application |
|
| 215 | + * @param string $file |
|
| 216 | + * @since 4.0.0 |
|
| 217 | + */ |
|
| 218 | + public static function addScript( $application, $file = null ) { |
|
| 219 | + \OC_Util::addScript( $application, $file ); |
|
| 220 | + } |
|
| 221 | + |
|
| 222 | + /** |
|
| 223 | + * Add a translation JS file |
|
| 224 | + * @param string $application application id |
|
| 225 | + * @param string $languageCode language code, defaults to the current locale |
|
| 226 | + * @since 8.0.0 |
|
| 227 | + */ |
|
| 228 | + public static function addTranslations($application, $languageCode = null) { |
|
| 229 | + \OC_Util::addTranslations($application, $languageCode); |
|
| 230 | + } |
|
| 231 | + |
|
| 232 | + /** |
|
| 233 | + * Add a custom element to the header |
|
| 234 | + * If $text is null then the element will be written as empty element. |
|
| 235 | + * So use "" to get a closing tag. |
|
| 236 | + * @param string $tag tag name of the element |
|
| 237 | + * @param array $attributes array of attributes for the element |
|
| 238 | + * @param string $text the text content for the element |
|
| 239 | + * @since 4.0.0 |
|
| 240 | + */ |
|
| 241 | + public static function addHeader($tag, $attributes, $text=null) { |
|
| 242 | + \OC_Util::addHeader($tag, $attributes, $text); |
|
| 243 | + } |
|
| 244 | + |
|
| 245 | + /** |
|
| 246 | + * formats a timestamp in the "right" way |
|
| 247 | + * @param int $timestamp $timestamp |
|
| 248 | + * @param bool $dateOnly option to omit time from the result |
|
| 249 | + * @param DateTimeZone|string $timeZone where the given timestamp shall be converted to |
|
| 250 | + * @return string timestamp |
|
| 251 | + * |
|
| 252 | + * @deprecated 8.0.0 Use \OC::$server->query('DateTimeFormatter') instead |
|
| 253 | + * @since 4.0.0 |
|
| 254 | + */ |
|
| 255 | + public static function formatDate($timestamp, $dateOnly=false, $timeZone = null) { |
|
| 256 | + return(\OC_Util::formatDate($timestamp, $dateOnly, $timeZone)); |
|
| 257 | + } |
|
| 258 | + |
|
| 259 | + /** |
|
| 260 | + * check if some encrypted files are stored |
|
| 261 | + * @return bool |
|
| 262 | + * |
|
| 263 | + * @deprecated 8.1.0 No longer required |
|
| 264 | + * @since 6.0.0 |
|
| 265 | + */ |
|
| 266 | + public static function encryptedFiles() { |
|
| 267 | + return false; |
|
| 268 | + } |
|
| 269 | + |
|
| 270 | + /** |
|
| 271 | + * Creates an absolute url to the given app and file. |
|
| 272 | + * @param string $app app |
|
| 273 | + * @param string $file file |
|
| 274 | + * @param array $args array with param=>value, will be appended to the returned url |
|
| 275 | + * The value of $args will be urlencoded |
|
| 276 | + * @return string the url |
|
| 277 | + * @since 4.0.0 - parameter $args was added in 4.5.0 |
|
| 278 | + */ |
|
| 279 | + public static function linkToAbsolute( $app, $file, $args = array() ) { |
|
| 280 | + $urlGenerator = \OC::$server->getURLGenerator(); |
|
| 281 | + return $urlGenerator->getAbsoluteURL( |
|
| 282 | + $urlGenerator->linkTo($app, $file, $args) |
|
| 283 | + ); |
|
| 284 | + } |
|
| 285 | + |
|
| 286 | + /** |
|
| 287 | + * Creates an absolute url for remote use. |
|
| 288 | + * @param string $service id |
|
| 289 | + * @return string the url |
|
| 290 | + * @since 4.0.0 |
|
| 291 | + */ |
|
| 292 | + public static function linkToRemote( $service ) { |
|
| 293 | + $urlGenerator = \OC::$server->getURLGenerator(); |
|
| 294 | + $remoteBase = $urlGenerator->linkTo('', 'remote.php') . '/' . $service; |
|
| 295 | + return $urlGenerator->getAbsoluteURL( |
|
| 296 | + $remoteBase . (($service[strlen($service) - 1] != '/') ? '/' : '') |
|
| 297 | + ); |
|
| 298 | + } |
|
| 299 | + |
|
| 300 | + /** |
|
| 301 | + * Creates an absolute url for public use |
|
| 302 | + * @param string $service id |
|
| 303 | + * @return string the url |
|
| 304 | + * @since 4.5.0 |
|
| 305 | + */ |
|
| 306 | + public static function linkToPublic($service) { |
|
| 307 | + return \OC_Helper::linkToPublic($service); |
|
| 308 | + } |
|
| 309 | + |
|
| 310 | + /** |
|
| 311 | + * Creates an url using a defined route |
|
| 312 | + * @param string $route |
|
| 313 | + * @param array $parameters |
|
| 314 | + * @internal param array $args with param=>value, will be appended to the returned url |
|
| 315 | + * @return string the url |
|
| 316 | + * @deprecated 8.1.0 Use \OC::$server->getURLGenerator()->linkToRoute($route, $parameters) |
|
| 317 | + * @since 5.0.0 |
|
| 318 | + */ |
|
| 319 | + public static function linkToRoute( $route, $parameters = array() ) { |
|
| 320 | + return \OC::$server->getURLGenerator()->linkToRoute($route, $parameters); |
|
| 321 | + } |
|
| 322 | + |
|
| 323 | + /** |
|
| 324 | + * Creates an url to the given app and file |
|
| 325 | + * @param string $app app |
|
| 326 | + * @param string $file file |
|
| 327 | + * @param array $args array with param=>value, will be appended to the returned url |
|
| 328 | + * The value of $args will be urlencoded |
|
| 329 | + * @return string the url |
|
| 330 | + * @deprecated 8.1.0 Use \OC::$server->getURLGenerator()->linkTo($app, $file, $args) |
|
| 331 | + * @since 4.0.0 - parameter $args was added in 4.5.0 |
|
| 332 | + */ |
|
| 333 | + public static function linkTo( $app, $file, $args = array() ) { |
|
| 334 | + return \OC::$server->getURLGenerator()->linkTo($app, $file, $args); |
|
| 335 | + } |
|
| 336 | + |
|
| 337 | + /** |
|
| 338 | + * Returns the server host, even if the website uses one or more reverse proxy |
|
| 339 | + * @return string the server host |
|
| 340 | + * @deprecated 8.1.0 Use \OCP\IRequest::getServerHost |
|
| 341 | + * @since 4.0.0 |
|
| 342 | + */ |
|
| 343 | + public static function getServerHost() { |
|
| 344 | + return \OC::$server->getRequest()->getServerHost(); |
|
| 345 | + } |
|
| 346 | + |
|
| 347 | + /** |
|
| 348 | + * Returns the server host name without an eventual port number |
|
| 349 | + * @return string the server hostname |
|
| 350 | + * @since 5.0.0 |
|
| 351 | + */ |
|
| 352 | + public static function getServerHostName() { |
|
| 353 | + $host_name = self::getServerHost(); |
|
| 354 | + // strip away port number (if existing) |
|
| 355 | + $colon_pos = strpos($host_name, ':'); |
|
| 356 | + if ($colon_pos != FALSE) { |
|
| 357 | + $host_name = substr($host_name, 0, $colon_pos); |
|
| 358 | + } |
|
| 359 | + return $host_name; |
|
| 360 | + } |
|
| 361 | + |
|
| 362 | + /** |
|
| 363 | + * Returns the default email address |
|
| 364 | + * @param string $user_part the user part of the address |
|
| 365 | + * @return string the default email address |
|
| 366 | + * |
|
| 367 | + * Assembles a default email address (using the server hostname |
|
| 368 | + * and the given user part, and returns it |
|
| 369 | + * Example: when given lostpassword-noreply as $user_part param, |
|
| 370 | + * and is currently accessed via http(s)://example.com/, |
|
| 371 | + * it would return '[email protected]' |
|
| 372 | + * |
|
| 373 | + * If the configuration value 'mail_from_address' is set in |
|
| 374 | + * config.php, this value will override the $user_part that |
|
| 375 | + * is passed to this function |
|
| 376 | + * @since 5.0.0 |
|
| 377 | + */ |
|
| 378 | + public static function getDefaultEmailAddress($user_part) { |
|
| 379 | + $config = \OC::$server->getConfig(); |
|
| 380 | + $user_part = $config->getSystemValue('mail_from_address', $user_part); |
|
| 381 | + $host_name = self::getServerHostName(); |
|
| 382 | + $host_name = $config->getSystemValue('mail_domain', $host_name); |
|
| 383 | + $defaultEmailAddress = $user_part.'@'.$host_name; |
|
| 384 | + |
|
| 385 | + $mailer = \OC::$server->getMailer(); |
|
| 386 | + if ($mailer->validateMailAddress($defaultEmailAddress)) { |
|
| 387 | + return $defaultEmailAddress; |
|
| 388 | + } |
|
| 389 | + |
|
| 390 | + // in case we cannot build a valid email address from the hostname let's fallback to 'localhost.localdomain' |
|
| 391 | + return $user_part.'@localhost.localdomain'; |
|
| 392 | + } |
|
| 393 | + |
|
| 394 | + /** |
|
| 395 | + * Returns the server protocol. It respects reverse proxy servers and load balancers |
|
| 396 | + * @return string the server protocol |
|
| 397 | + * @deprecated 8.1.0 Use \OCP\IRequest::getServerProtocol |
|
| 398 | + * @since 4.5.0 |
|
| 399 | + */ |
|
| 400 | + public static function getServerProtocol() { |
|
| 401 | + return \OC::$server->getRequest()->getServerProtocol(); |
|
| 402 | + } |
|
| 403 | + |
|
| 404 | + /** |
|
| 405 | + * Returns the request uri, even if the website uses one or more reverse proxies |
|
| 406 | + * @return string the request uri |
|
| 407 | + * @deprecated 8.1.0 Use \OCP\IRequest::getRequestUri |
|
| 408 | + * @since 5.0.0 |
|
| 409 | + */ |
|
| 410 | + public static function getRequestUri() { |
|
| 411 | + return \OC::$server->getRequest()->getRequestUri(); |
|
| 412 | + } |
|
| 413 | + |
|
| 414 | + /** |
|
| 415 | + * Returns the script name, even if the website uses one or more reverse proxies |
|
| 416 | + * @return string the script name |
|
| 417 | + * @deprecated 8.1.0 Use \OCP\IRequest::getScriptName |
|
| 418 | + * @since 5.0.0 |
|
| 419 | + */ |
|
| 420 | + public static function getScriptName() { |
|
| 421 | + return \OC::$server->getRequest()->getScriptName(); |
|
| 422 | + } |
|
| 423 | + |
|
| 424 | + /** |
|
| 425 | + * Creates path to an image |
|
| 426 | + * @param string $app app |
|
| 427 | + * @param string $image image name |
|
| 428 | + * @return string the url |
|
| 429 | + * @deprecated 8.1.0 Use \OC::$server->getURLGenerator()->imagePath($app, $image) |
|
| 430 | + * @since 4.0.0 |
|
| 431 | + */ |
|
| 432 | + public static function imagePath( $app, $image ) { |
|
| 433 | + return \OC::$server->getURLGenerator()->imagePath($app, $image); |
|
| 434 | + } |
|
| 435 | + |
|
| 436 | + /** |
|
| 437 | + * Make a human file size (2048 to 2 kB) |
|
| 438 | + * @param int $bytes file size in bytes |
|
| 439 | + * @return string a human readable file size |
|
| 440 | + * @since 4.0.0 |
|
| 441 | + */ |
|
| 442 | + public static function humanFileSize( $bytes ) { |
|
| 443 | + return(\OC_Helper::humanFileSize( $bytes )); |
|
| 444 | + } |
|
| 445 | + |
|
| 446 | + /** |
|
| 447 | + * Make a computer file size (2 kB to 2048) |
|
| 448 | + * @param string $str file size in a fancy format |
|
| 449 | + * @return int a file size in bytes |
|
| 450 | + * |
|
| 451 | + * Inspired by: http://www.php.net/manual/en/function.filesize.php#92418 |
|
| 452 | + * @since 4.0.0 |
|
| 453 | + */ |
|
| 454 | + public static function computerFileSize( $str ) { |
|
| 455 | + return(\OC_Helper::computerFileSize( $str )); |
|
| 456 | + } |
|
| 457 | + |
|
| 458 | + /** |
|
| 459 | + * connects a function to a hook |
|
| 460 | + * |
|
| 461 | + * @param string $signalClass class name of emitter |
|
| 462 | + * @param string $signalName name of signal |
|
| 463 | + * @param string|object $slotClass class name of slot |
|
| 464 | + * @param string $slotName name of slot |
|
| 465 | + * @return bool |
|
| 466 | + * |
|
| 467 | + * This function makes it very easy to connect to use hooks. |
|
| 468 | + * |
|
| 469 | + * TODO: write example |
|
| 470 | + * @since 4.0.0 |
|
| 471 | + */ |
|
| 472 | + static public function connectHook($signalClass, $signalName, $slotClass, $slotName ) { |
|
| 473 | + return(\OC_Hook::connect($signalClass, $signalName, $slotClass, $slotName )); |
|
| 474 | + } |
|
| 475 | + |
|
| 476 | + /** |
|
| 477 | + * Emits a signal. To get data from the slot use references! |
|
| 478 | + * @param string $signalclass class name of emitter |
|
| 479 | + * @param string $signalname name of signal |
|
| 480 | + * @param array $params default: array() array with additional data |
|
| 481 | + * @return bool true if slots exists or false if not |
|
| 482 | + * |
|
| 483 | + * TODO: write example |
|
| 484 | + * @since 4.0.0 |
|
| 485 | + */ |
|
| 486 | + static public function emitHook( $signalclass, $signalname, $params = array()) { |
|
| 487 | + return(\OC_Hook::emit( $signalclass, $signalname, $params )); |
|
| 488 | + } |
|
| 489 | + |
|
| 490 | + /** |
|
| 491 | + * Cached encrypted CSRF token. Some static unit-tests of ownCloud compare |
|
| 492 | + * multiple OC_Template elements which invoke `callRegister`. If the value |
|
| 493 | + * would not be cached these unit-tests would fail. |
|
| 494 | + * @var string |
|
| 495 | + */ |
|
| 496 | + private static $token = ''; |
|
| 497 | + |
|
| 498 | + /** |
|
| 499 | + * Register an get/post call. This is important to prevent CSRF attacks |
|
| 500 | + * @since 4.5.0 |
|
| 501 | + */ |
|
| 502 | + public static function callRegister() { |
|
| 503 | + if(self::$token === '') { |
|
| 504 | + self::$token = \OC::$server->getCsrfTokenManager()->getToken()->getEncryptedValue(); |
|
| 505 | + } |
|
| 506 | + return self::$token; |
|
| 507 | + } |
|
| 508 | + |
|
| 509 | + /** |
|
| 510 | + * Check an ajax get/post call if the request token is valid. exit if not. |
|
| 511 | + * @since 4.5.0 |
|
| 512 | + * @deprecated 9.0.0 Use annotations based on the app framework. |
|
| 513 | + */ |
|
| 514 | + public static function callCheck() { |
|
| 515 | + if(!\OC::$server->getRequest()->passesStrictCookieCheck()) { |
|
| 516 | + header('Location: '.\OC::$WEBROOT); |
|
| 517 | + exit(); |
|
| 518 | + } |
|
| 519 | + |
|
| 520 | + if (!(\OC::$server->getRequest()->passesCSRFCheck())) { |
|
| 521 | + exit(); |
|
| 522 | + } |
|
| 523 | + } |
|
| 524 | + |
|
| 525 | + /** |
|
| 526 | + * Used to sanitize HTML |
|
| 527 | + * |
|
| 528 | + * This function is used to sanitize HTML and should be applied on any |
|
| 529 | + * string or array of strings before displaying it on a web page. |
|
| 530 | + * |
|
| 531 | + * @param string|array $value |
|
| 532 | + * @return string|array an array of sanitized strings or a single sanitized string, depends on the input parameter. |
|
| 533 | + * @since 4.5.0 |
|
| 534 | + */ |
|
| 535 | + public static function sanitizeHTML($value) { |
|
| 536 | + return \OC_Util::sanitizeHTML($value); |
|
| 537 | + } |
|
| 538 | + |
|
| 539 | + /** |
|
| 540 | + * Public function to encode url parameters |
|
| 541 | + * |
|
| 542 | + * This function is used to encode path to file before output. |
|
| 543 | + * Encoding is done according to RFC 3986 with one exception: |
|
| 544 | + * Character '/' is preserved as is. |
|
| 545 | + * |
|
| 546 | + * @param string $component part of URI to encode |
|
| 547 | + * @return string |
|
| 548 | + * @since 6.0.0 |
|
| 549 | + */ |
|
| 550 | + public static function encodePath($component) { |
|
| 551 | + return(\OC_Util::encodePath($component)); |
|
| 552 | + } |
|
| 553 | + |
|
| 554 | + /** |
|
| 555 | + * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is. |
|
| 556 | + * |
|
| 557 | + * @param array $input The array to work on |
|
| 558 | + * @param int $case Either MB_CASE_UPPER or MB_CASE_LOWER (default) |
|
| 559 | + * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8 |
|
| 560 | + * @return array |
|
| 561 | + * @since 4.5.0 |
|
| 562 | + */ |
|
| 563 | + public static function mb_array_change_key_case($input, $case = MB_CASE_LOWER, $encoding = 'UTF-8') { |
|
| 564 | + return(\OC_Helper::mb_array_change_key_case($input, $case, $encoding)); |
|
| 565 | + } |
|
| 566 | + |
|
| 567 | + /** |
|
| 568 | + * replaces a copy of string delimited by the start and (optionally) length parameters with the string given in replacement. |
|
| 569 | + * |
|
| 570 | + * @param string $string The input string. Opposite to the PHP build-in function does not accept an array. |
|
| 571 | + * @param string $replacement The replacement string. |
|
| 572 | + * @param int $start If start is positive, the replacing will begin at the start'th offset into string. If start is negative, the replacing will begin at the start'th character from the end of string. |
|
| 573 | + * @param int $length Length of the part to be replaced |
|
| 574 | + * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8 |
|
| 575 | + * @return string |
|
| 576 | + * @since 4.5.0 |
|
| 577 | + * @deprecated 8.2.0 Use substr_replace() instead. |
|
| 578 | + */ |
|
| 579 | + public static function mb_substr_replace($string, $replacement, $start, $length = null, $encoding = 'UTF-8') { |
|
| 580 | + return substr_replace($string, $replacement, $start, $length); |
|
| 581 | + } |
|
| 582 | + |
|
| 583 | + /** |
|
| 584 | + * Replace all occurrences of the search string with the replacement string |
|
| 585 | + * |
|
| 586 | + * @param string $search The value being searched for, otherwise known as the needle. String. |
|
| 587 | + * @param string $replace The replacement string. |
|
| 588 | + * @param string $subject The string or array being searched and replaced on, otherwise known as the haystack. |
|
| 589 | + * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8 |
|
| 590 | + * @param int $count If passed, this will be set to the number of replacements performed. |
|
| 591 | + * @return string |
|
| 592 | + * @since 4.5.0 |
|
| 593 | + * @deprecated 8.2.0 Use str_replace() instead. |
|
| 594 | + */ |
|
| 595 | + public static function mb_str_replace($search, $replace, $subject, $encoding = 'UTF-8', &$count = null) { |
|
| 596 | + return str_replace($search, $replace, $subject, $count); |
|
| 597 | + } |
|
| 598 | + |
|
| 599 | + /** |
|
| 600 | + * performs a search in a nested array |
|
| 601 | + * |
|
| 602 | + * @param array $haystack the array to be searched |
|
| 603 | + * @param string $needle the search string |
|
| 604 | + * @param int $index optional, only search this key name |
|
| 605 | + * @return mixed the key of the matching field, otherwise false |
|
| 606 | + * @since 4.5.0 |
|
| 607 | + */ |
|
| 608 | + public static function recursiveArraySearch($haystack, $needle, $index = null) { |
|
| 609 | + return(\OC_Helper::recursiveArraySearch($haystack, $needle, $index)); |
|
| 610 | + } |
|
| 611 | + |
|
| 612 | + /** |
|
| 613 | + * calculates the maximum upload size respecting system settings, free space and user quota |
|
| 614 | + * |
|
| 615 | + * @param string $dir the current folder where the user currently operates |
|
| 616 | + * @param int $free the number of bytes free on the storage holding $dir, if not set this will be received from the storage directly |
|
| 617 | + * @return int number of bytes representing |
|
| 618 | + * @since 5.0.0 |
|
| 619 | + */ |
|
| 620 | + public static function maxUploadFilesize($dir, $free = null) { |
|
| 621 | + return \OC_Helper::maxUploadFilesize($dir, $free); |
|
| 622 | + } |
|
| 623 | + |
|
| 624 | + /** |
|
| 625 | + * Calculate free space left within user quota |
|
| 626 | + * @param string $dir the current folder where the user currently operates |
|
| 627 | + * @return int number of bytes representing |
|
| 628 | + * @since 7.0.0 |
|
| 629 | + */ |
|
| 630 | + public static function freeSpace($dir) { |
|
| 631 | + return \OC_Helper::freeSpace($dir); |
|
| 632 | + } |
|
| 633 | + |
|
| 634 | + /** |
|
| 635 | + * Calculate PHP upload limit |
|
| 636 | + * |
|
| 637 | + * @return int number of bytes representing |
|
| 638 | + * @since 7.0.0 |
|
| 639 | + */ |
|
| 640 | + public static function uploadLimit() { |
|
| 641 | + return \OC_Helper::uploadLimit(); |
|
| 642 | + } |
|
| 643 | + |
|
| 644 | + /** |
|
| 645 | + * Returns whether the given file name is valid |
|
| 646 | + * @param string $file file name to check |
|
| 647 | + * @return bool true if the file name is valid, false otherwise |
|
| 648 | + * @deprecated 8.1.0 use \OC\Files\View::verifyPath() |
|
| 649 | + * @since 7.0.0 |
|
| 650 | + */ |
|
| 651 | + public static function isValidFileName($file) { |
|
| 652 | + return \OC_Util::isValidFileName($file); |
|
| 653 | + } |
|
| 654 | + |
|
| 655 | + /** |
|
| 656 | + * Generates a cryptographic secure pseudo-random string |
|
| 657 | + * @param int $length of the random string |
|
| 658 | + * @return string |
|
| 659 | + * @deprecated 8.0.0 Use \OC::$server->getSecureRandom()->getMediumStrengthGenerator()->generate($length); instead |
|
| 660 | + * @since 7.0.0 |
|
| 661 | + */ |
|
| 662 | + public static function generateRandomBytes($length = 30) { |
|
| 663 | + return \OC::$server->getSecureRandom()->generate($length, \OCP\Security\ISecureRandom::CHAR_LOWER.\OCP\Security\ISecureRandom::CHAR_DIGITS); |
|
| 664 | + } |
|
| 665 | + |
|
| 666 | + /** |
|
| 667 | + * Compare two strings to provide a natural sort |
|
| 668 | + * @param string $a first string to compare |
|
| 669 | + * @param string $b second string to compare |
|
| 670 | + * @return -1 if $b comes before $a, 1 if $a comes before $b |
|
| 671 | + * or 0 if the strings are identical |
|
| 672 | + * @since 7.0.0 |
|
| 673 | + */ |
|
| 674 | + public static function naturalSortCompare($a, $b) { |
|
| 675 | + return \OC\NaturalSort::getInstance()->compare($a, $b); |
|
| 676 | + } |
|
| 677 | + |
|
| 678 | + /** |
|
| 679 | + * check if a password is required for each public link |
|
| 680 | + * @return boolean |
|
| 681 | + * @since 7.0.0 |
|
| 682 | + */ |
|
| 683 | + public static function isPublicLinkPasswordRequired() { |
|
| 684 | + return \OC_Util::isPublicLinkPasswordRequired(); |
|
| 685 | + } |
|
| 686 | + |
|
| 687 | + /** |
|
| 688 | + * check if share API enforces a default expire date |
|
| 689 | + * @return boolean |
|
| 690 | + * @since 8.0.0 |
|
| 691 | + */ |
|
| 692 | + public static function isDefaultExpireDateEnforced() { |
|
| 693 | + return \OC_Util::isDefaultExpireDateEnforced(); |
|
| 694 | + } |
|
| 695 | + |
|
| 696 | + protected static $needUpgradeCache = null; |
|
| 697 | + |
|
| 698 | + /** |
|
| 699 | + * Checks whether the current version needs upgrade. |
|
| 700 | + * |
|
| 701 | + * @return bool true if upgrade is needed, false otherwise |
|
| 702 | + * @since 7.0.0 |
|
| 703 | + */ |
|
| 704 | + public static function needUpgrade() { |
|
| 705 | + if (!isset(self::$needUpgradeCache)) { |
|
| 706 | + self::$needUpgradeCache=\OC_Util::needUpgrade(\OC::$server->getConfig()); |
|
| 707 | + } |
|
| 708 | + return self::$needUpgradeCache; |
|
| 709 | + } |
|
| 710 | 710 | } |
@@ -58,11 +58,11 @@ discard block |
||
| 58 | 58 | */ |
| 59 | 59 | class Util { |
| 60 | 60 | // consts for Logging |
| 61 | - const DEBUG=0; |
|
| 62 | - const INFO=1; |
|
| 63 | - const WARN=2; |
|
| 64 | - const ERROR=3; |
|
| 65 | - const FATAL=4; |
|
| 61 | + const DEBUG = 0; |
|
| 62 | + const INFO = 1; |
|
| 63 | + const WARN = 2; |
|
| 64 | + const ERROR = 3; |
|
| 65 | + const FATAL = 4; |
|
| 66 | 66 | |
| 67 | 67 | /** \OCP\Share\IManager */ |
| 68 | 68 | private static $shareManager; |
@@ -118,11 +118,11 @@ discard block |
||
| 118 | 118 | $message->setSubject($subject); |
| 119 | 119 | $message->setPlainBody($mailtext); |
| 120 | 120 | $message->setFrom([$fromaddress => $fromname]); |
| 121 | - if($html === 1) { |
|
| 121 | + if ($html === 1) { |
|
| 122 | 122 | $message->setHTMLBody($altbody); |
| 123 | 123 | } |
| 124 | 124 | |
| 125 | - if($altbody === '') { |
|
| 125 | + if ($altbody === '') { |
|
| 126 | 126 | $message->setHTMLBody($mailtext); |
| 127 | 127 | $message->setPlainBody(''); |
| 128 | 128 | } else { |
@@ -130,14 +130,14 @@ discard block |
||
| 130 | 130 | $message->setPlainBody($altbody); |
| 131 | 131 | } |
| 132 | 132 | |
| 133 | - if(!empty($ccaddress)) { |
|
| 134 | - if(!empty($ccname)) { |
|
| 133 | + if (!empty($ccaddress)) { |
|
| 134 | + if (!empty($ccname)) { |
|
| 135 | 135 | $message->setCc([$ccaddress => $ccname]); |
| 136 | 136 | } else { |
| 137 | 137 | $message->setCc([$ccaddress]); |
| 138 | 138 | } |
| 139 | 139 | } |
| 140 | - if(!empty($bcc)) { |
|
| 140 | + if (!empty($bcc)) { |
|
| 141 | 141 | $message->setBcc([$bcc]); |
| 142 | 142 | } |
| 143 | 143 | |
@@ -151,7 +151,7 @@ discard block |
||
| 151 | 151 | * @param int $level |
| 152 | 152 | * @since 4.0.0 |
| 153 | 153 | */ |
| 154 | - public static function writeLog( $app, $message, $level ) { |
|
| 154 | + public static function writeLog($app, $message, $level) { |
|
| 155 | 155 | $context = ['app' => $app]; |
| 156 | 156 | \OC::$server->getLogger()->log($level, $message, $context); |
| 157 | 157 | } |
@@ -164,7 +164,7 @@ discard block |
||
| 164 | 164 | * @since ....0.0 - parameter $level was added in 7.0.0 |
| 165 | 165 | * @deprecated 8.2.0 use logException of \OCP\ILogger |
| 166 | 166 | */ |
| 167 | - public static function logException( $app, \Exception $ex, $level = \OCP\Util::FATAL ) { |
|
| 167 | + public static function logException($app, \Exception $ex, $level = \OCP\Util::FATAL) { |
|
| 168 | 168 | \OC::$server->getLogger()->logException($ex, ['app' => $app]); |
| 169 | 169 | } |
| 170 | 170 | |
@@ -205,8 +205,8 @@ discard block |
||
| 205 | 205 | * @param string $file |
| 206 | 206 | * @since 4.0.0 |
| 207 | 207 | */ |
| 208 | - public static function addStyle( $application, $file = null ) { |
|
| 209 | - \OC_Util::addStyle( $application, $file ); |
|
| 208 | + public static function addStyle($application, $file = null) { |
|
| 209 | + \OC_Util::addStyle($application, $file); |
|
| 210 | 210 | } |
| 211 | 211 | |
| 212 | 212 | /** |
@@ -215,8 +215,8 @@ discard block |
||
| 215 | 215 | * @param string $file |
| 216 | 216 | * @since 4.0.0 |
| 217 | 217 | */ |
| 218 | - public static function addScript( $application, $file = null ) { |
|
| 219 | - \OC_Util::addScript( $application, $file ); |
|
| 218 | + public static function addScript($application, $file = null) { |
|
| 219 | + \OC_Util::addScript($application, $file); |
|
| 220 | 220 | } |
| 221 | 221 | |
| 222 | 222 | /** |
@@ -238,7 +238,7 @@ discard block |
||
| 238 | 238 | * @param string $text the text content for the element |
| 239 | 239 | * @since 4.0.0 |
| 240 | 240 | */ |
| 241 | - public static function addHeader($tag, $attributes, $text=null) { |
|
| 241 | + public static function addHeader($tag, $attributes, $text = null) { |
|
| 242 | 242 | \OC_Util::addHeader($tag, $attributes, $text); |
| 243 | 243 | } |
| 244 | 244 | |
@@ -252,7 +252,7 @@ discard block |
||
| 252 | 252 | * @deprecated 8.0.0 Use \OC::$server->query('DateTimeFormatter') instead |
| 253 | 253 | * @since 4.0.0 |
| 254 | 254 | */ |
| 255 | - public static function formatDate($timestamp, $dateOnly=false, $timeZone = null) { |
|
| 255 | + public static function formatDate($timestamp, $dateOnly = false, $timeZone = null) { |
|
| 256 | 256 | return(\OC_Util::formatDate($timestamp, $dateOnly, $timeZone)); |
| 257 | 257 | } |
| 258 | 258 | |
@@ -276,7 +276,7 @@ discard block |
||
| 276 | 276 | * @return string the url |
| 277 | 277 | * @since 4.0.0 - parameter $args was added in 4.5.0 |
| 278 | 278 | */ |
| 279 | - public static function linkToAbsolute( $app, $file, $args = array() ) { |
|
| 279 | + public static function linkToAbsolute($app, $file, $args = array()) { |
|
| 280 | 280 | $urlGenerator = \OC::$server->getURLGenerator(); |
| 281 | 281 | return $urlGenerator->getAbsoluteURL( |
| 282 | 282 | $urlGenerator->linkTo($app, $file, $args) |
@@ -289,11 +289,11 @@ discard block |
||
| 289 | 289 | * @return string the url |
| 290 | 290 | * @since 4.0.0 |
| 291 | 291 | */ |
| 292 | - public static function linkToRemote( $service ) { |
|
| 292 | + public static function linkToRemote($service) { |
|
| 293 | 293 | $urlGenerator = \OC::$server->getURLGenerator(); |
| 294 | - $remoteBase = $urlGenerator->linkTo('', 'remote.php') . '/' . $service; |
|
| 294 | + $remoteBase = $urlGenerator->linkTo('', 'remote.php').'/'.$service; |
|
| 295 | 295 | return $urlGenerator->getAbsoluteURL( |
| 296 | - $remoteBase . (($service[strlen($service) - 1] != '/') ? '/' : '') |
|
| 296 | + $remoteBase.(($service[strlen($service) - 1] != '/') ? '/' : '') |
|
| 297 | 297 | ); |
| 298 | 298 | } |
| 299 | 299 | |
@@ -316,7 +316,7 @@ discard block |
||
| 316 | 316 | * @deprecated 8.1.0 Use \OC::$server->getURLGenerator()->linkToRoute($route, $parameters) |
| 317 | 317 | * @since 5.0.0 |
| 318 | 318 | */ |
| 319 | - public static function linkToRoute( $route, $parameters = array() ) { |
|
| 319 | + public static function linkToRoute($route, $parameters = array()) { |
|
| 320 | 320 | return \OC::$server->getURLGenerator()->linkToRoute($route, $parameters); |
| 321 | 321 | } |
| 322 | 322 | |
@@ -330,7 +330,7 @@ discard block |
||
| 330 | 330 | * @deprecated 8.1.0 Use \OC::$server->getURLGenerator()->linkTo($app, $file, $args) |
| 331 | 331 | * @since 4.0.0 - parameter $args was added in 4.5.0 |
| 332 | 332 | */ |
| 333 | - public static function linkTo( $app, $file, $args = array() ) { |
|
| 333 | + public static function linkTo($app, $file, $args = array()) { |
|
| 334 | 334 | return \OC::$server->getURLGenerator()->linkTo($app, $file, $args); |
| 335 | 335 | } |
| 336 | 336 | |
@@ -429,7 +429,7 @@ discard block |
||
| 429 | 429 | * @deprecated 8.1.0 Use \OC::$server->getURLGenerator()->imagePath($app, $image) |
| 430 | 430 | * @since 4.0.0 |
| 431 | 431 | */ |
| 432 | - public static function imagePath( $app, $image ) { |
|
| 432 | + public static function imagePath($app, $image) { |
|
| 433 | 433 | return \OC::$server->getURLGenerator()->imagePath($app, $image); |
| 434 | 434 | } |
| 435 | 435 | |
@@ -439,8 +439,8 @@ discard block |
||
| 439 | 439 | * @return string a human readable file size |
| 440 | 440 | * @since 4.0.0 |
| 441 | 441 | */ |
| 442 | - public static function humanFileSize( $bytes ) { |
|
| 443 | - return(\OC_Helper::humanFileSize( $bytes )); |
|
| 442 | + public static function humanFileSize($bytes) { |
|
| 443 | + return(\OC_Helper::humanFileSize($bytes)); |
|
| 444 | 444 | } |
| 445 | 445 | |
| 446 | 446 | /** |
@@ -451,8 +451,8 @@ discard block |
||
| 451 | 451 | * Inspired by: http://www.php.net/manual/en/function.filesize.php#92418 |
| 452 | 452 | * @since 4.0.0 |
| 453 | 453 | */ |
| 454 | - public static function computerFileSize( $str ) { |
|
| 455 | - return(\OC_Helper::computerFileSize( $str )); |
|
| 454 | + public static function computerFileSize($str) { |
|
| 455 | + return(\OC_Helper::computerFileSize($str)); |
|
| 456 | 456 | } |
| 457 | 457 | |
| 458 | 458 | /** |
@@ -469,8 +469,8 @@ discard block |
||
| 469 | 469 | * TODO: write example |
| 470 | 470 | * @since 4.0.0 |
| 471 | 471 | */ |
| 472 | - static public function connectHook($signalClass, $signalName, $slotClass, $slotName ) { |
|
| 473 | - return(\OC_Hook::connect($signalClass, $signalName, $slotClass, $slotName )); |
|
| 472 | + static public function connectHook($signalClass, $signalName, $slotClass, $slotName) { |
|
| 473 | + return(\OC_Hook::connect($signalClass, $signalName, $slotClass, $slotName)); |
|
| 474 | 474 | } |
| 475 | 475 | |
| 476 | 476 | /** |
@@ -483,8 +483,8 @@ discard block |
||
| 483 | 483 | * TODO: write example |
| 484 | 484 | * @since 4.0.0 |
| 485 | 485 | */ |
| 486 | - static public function emitHook( $signalclass, $signalname, $params = array()) { |
|
| 487 | - return(\OC_Hook::emit( $signalclass, $signalname, $params )); |
|
| 486 | + static public function emitHook($signalclass, $signalname, $params = array()) { |
|
| 487 | + return(\OC_Hook::emit($signalclass, $signalname, $params)); |
|
| 488 | 488 | } |
| 489 | 489 | |
| 490 | 490 | /** |
@@ -500,7 +500,7 @@ discard block |
||
| 500 | 500 | * @since 4.5.0 |
| 501 | 501 | */ |
| 502 | 502 | public static function callRegister() { |
| 503 | - if(self::$token === '') { |
|
| 503 | + if (self::$token === '') { |
|
| 504 | 504 | self::$token = \OC::$server->getCsrfTokenManager()->getToken()->getEncryptedValue(); |
| 505 | 505 | } |
| 506 | 506 | return self::$token; |
@@ -512,7 +512,7 @@ discard block |
||
| 512 | 512 | * @deprecated 9.0.0 Use annotations based on the app framework. |
| 513 | 513 | */ |
| 514 | 514 | public static function callCheck() { |
| 515 | - if(!\OC::$server->getRequest()->passesStrictCookieCheck()) { |
|
| 515 | + if (!\OC::$server->getRequest()->passesStrictCookieCheck()) { |
|
| 516 | 516 | header('Location: '.\OC::$WEBROOT); |
| 517 | 517 | exit(); |
| 518 | 518 | } |
@@ -703,7 +703,7 @@ discard block |
||
| 703 | 703 | */ |
| 704 | 704 | public static function needUpgrade() { |
| 705 | 705 | if (!isset(self::$needUpgradeCache)) { |
| 706 | - self::$needUpgradeCache=\OC_Util::needUpgrade(\OC::$server->getConfig()); |
|
| 706 | + self::$needUpgradeCache = \OC_Util::needUpgrade(\OC::$server->getConfig()); |
|
| 707 | 707 | } |
| 708 | 708 | return self::$needUpgradeCache; |
| 709 | 709 | } |
@@ -200,7 +200,7 @@ discard block |
||
| 200 | 200 | * and does not take the chroot into account ) |
| 201 | 201 | * |
| 202 | 202 | * @param string $path |
| 203 | - * @return \OCP\Files\Mount\IMountPoint |
|
| 203 | + * @return Mount\MountPoint|null |
|
| 204 | 204 | */ |
| 205 | 205 | public function getMount($path) { |
| 206 | 206 | return Filesystem::getMountManager()->find($this->getAbsolutePath($path)); |
@@ -963,7 +963,7 @@ discard block |
||
| 963 | 963 | |
| 964 | 964 | /** |
| 965 | 965 | * @param string $path |
| 966 | - * @return bool|string |
|
| 966 | + * @return string|false |
|
| 967 | 967 | * @throws \OCP\Files\InvalidPathException |
| 968 | 968 | */ |
| 969 | 969 | public function toTmpFile($path) { |
@@ -1078,7 +1078,7 @@ discard block |
||
| 1078 | 1078 | * @param string $path |
| 1079 | 1079 | * @param array $hooks (optional) |
| 1080 | 1080 | * @param mixed $extraParam (optional) |
| 1081 | - * @return mixed |
|
| 1081 | + * @return string |
|
| 1082 | 1082 | * @throws \Exception |
| 1083 | 1083 | * |
| 1084 | 1084 | * This method takes requests for basic filesystem functions (e.g. reading & writing |
@@ -2086,7 +2086,7 @@ discard block |
||
| 2086 | 2086 | |
| 2087 | 2087 | /** |
| 2088 | 2088 | * @param string $filename |
| 2089 | - * @return array |
|
| 2089 | + * @return string[] |
|
| 2090 | 2090 | * @throws \OC\User\NoUserException |
| 2091 | 2091 | * @throws NotFoundException |
| 2092 | 2092 | */ |
@@ -84,2038 +84,2038 @@ |
||
| 84 | 84 | * \OC\Files\Storage\Storage object |
| 85 | 85 | */ |
| 86 | 86 | class View { |
| 87 | - /** @var string */ |
|
| 88 | - private $fakeRoot = ''; |
|
| 89 | - |
|
| 90 | - /** |
|
| 91 | - * @var \OCP\Lock\ILockingProvider |
|
| 92 | - */ |
|
| 93 | - private $lockingProvider; |
|
| 94 | - |
|
| 95 | - private $lockingEnabled; |
|
| 96 | - |
|
| 97 | - private $updaterEnabled = true; |
|
| 98 | - |
|
| 99 | - private $userManager; |
|
| 100 | - |
|
| 101 | - /** |
|
| 102 | - * @param string $root |
|
| 103 | - * @throws \Exception If $root contains an invalid path |
|
| 104 | - */ |
|
| 105 | - public function __construct($root = '') { |
|
| 106 | - if (is_null($root)) { |
|
| 107 | - throw new \InvalidArgumentException('Root can\'t be null'); |
|
| 108 | - } |
|
| 109 | - if (!Filesystem::isValidPath($root)) { |
|
| 110 | - throw new \Exception(); |
|
| 111 | - } |
|
| 112 | - |
|
| 113 | - $this->fakeRoot = $root; |
|
| 114 | - $this->lockingProvider = \OC::$server->getLockingProvider(); |
|
| 115 | - $this->lockingEnabled = !($this->lockingProvider instanceof \OC\Lock\NoopLockingProvider); |
|
| 116 | - $this->userManager = \OC::$server->getUserManager(); |
|
| 117 | - } |
|
| 118 | - |
|
| 119 | - public function getAbsolutePath($path = '/') { |
|
| 120 | - if ($path === null) { |
|
| 121 | - return null; |
|
| 122 | - } |
|
| 123 | - $this->assertPathLength($path); |
|
| 124 | - if ($path === '') { |
|
| 125 | - $path = '/'; |
|
| 126 | - } |
|
| 127 | - if ($path[0] !== '/') { |
|
| 128 | - $path = '/' . $path; |
|
| 129 | - } |
|
| 130 | - return $this->fakeRoot . $path; |
|
| 131 | - } |
|
| 132 | - |
|
| 133 | - /** |
|
| 134 | - * change the root to a fake root |
|
| 135 | - * |
|
| 136 | - * @param string $fakeRoot |
|
| 137 | - * @return boolean|null |
|
| 138 | - */ |
|
| 139 | - public function chroot($fakeRoot) { |
|
| 140 | - if (!$fakeRoot == '') { |
|
| 141 | - if ($fakeRoot[0] !== '/') { |
|
| 142 | - $fakeRoot = '/' . $fakeRoot; |
|
| 143 | - } |
|
| 144 | - } |
|
| 145 | - $this->fakeRoot = $fakeRoot; |
|
| 146 | - } |
|
| 147 | - |
|
| 148 | - /** |
|
| 149 | - * get the fake root |
|
| 150 | - * |
|
| 151 | - * @return string |
|
| 152 | - */ |
|
| 153 | - public function getRoot() { |
|
| 154 | - return $this->fakeRoot; |
|
| 155 | - } |
|
| 156 | - |
|
| 157 | - /** |
|
| 158 | - * get path relative to the root of the view |
|
| 159 | - * |
|
| 160 | - * @param string $path |
|
| 161 | - * @return string |
|
| 162 | - */ |
|
| 163 | - public function getRelativePath($path) { |
|
| 164 | - $this->assertPathLength($path); |
|
| 165 | - if ($this->fakeRoot == '') { |
|
| 166 | - return $path; |
|
| 167 | - } |
|
| 168 | - |
|
| 169 | - if (rtrim($path, '/') === rtrim($this->fakeRoot, '/')) { |
|
| 170 | - return '/'; |
|
| 171 | - } |
|
| 172 | - |
|
| 173 | - // missing slashes can cause wrong matches! |
|
| 174 | - $root = rtrim($this->fakeRoot, '/') . '/'; |
|
| 175 | - |
|
| 176 | - if (strpos($path, $root) !== 0) { |
|
| 177 | - return null; |
|
| 178 | - } else { |
|
| 179 | - $path = substr($path, strlen($this->fakeRoot)); |
|
| 180 | - if (strlen($path) === 0) { |
|
| 181 | - return '/'; |
|
| 182 | - } else { |
|
| 183 | - return $path; |
|
| 184 | - } |
|
| 185 | - } |
|
| 186 | - } |
|
| 187 | - |
|
| 188 | - /** |
|
| 189 | - * get the mountpoint of the storage object for a path |
|
| 190 | - * ( note: because a storage is not always mounted inside the fakeroot, the |
|
| 191 | - * returned mountpoint is relative to the absolute root of the filesystem |
|
| 192 | - * and does not take the chroot into account ) |
|
| 193 | - * |
|
| 194 | - * @param string $path |
|
| 195 | - * @return string |
|
| 196 | - */ |
|
| 197 | - public function getMountPoint($path) { |
|
| 198 | - return Filesystem::getMountPoint($this->getAbsolutePath($path)); |
|
| 199 | - } |
|
| 200 | - |
|
| 201 | - /** |
|
| 202 | - * get the mountpoint of the storage object for a path |
|
| 203 | - * ( note: because a storage is not always mounted inside the fakeroot, the |
|
| 204 | - * returned mountpoint is relative to the absolute root of the filesystem |
|
| 205 | - * and does not take the chroot into account ) |
|
| 206 | - * |
|
| 207 | - * @param string $path |
|
| 208 | - * @return \OCP\Files\Mount\IMountPoint |
|
| 209 | - */ |
|
| 210 | - public function getMount($path) { |
|
| 211 | - return Filesystem::getMountManager()->find($this->getAbsolutePath($path)); |
|
| 212 | - } |
|
| 213 | - |
|
| 214 | - /** |
|
| 215 | - * resolve a path to a storage and internal path |
|
| 216 | - * |
|
| 217 | - * @param string $path |
|
| 218 | - * @return array an array consisting of the storage and the internal path |
|
| 219 | - */ |
|
| 220 | - public function resolvePath($path) { |
|
| 221 | - $a = $this->getAbsolutePath($path); |
|
| 222 | - $p = Filesystem::normalizePath($a); |
|
| 223 | - return Filesystem::resolvePath($p); |
|
| 224 | - } |
|
| 225 | - |
|
| 226 | - /** |
|
| 227 | - * return the path to a local version of the file |
|
| 228 | - * we need this because we can't know if a file is stored local or not from |
|
| 229 | - * outside the filestorage and for some purposes a local file is needed |
|
| 230 | - * |
|
| 231 | - * @param string $path |
|
| 232 | - * @return string |
|
| 233 | - */ |
|
| 234 | - public function getLocalFile($path) { |
|
| 235 | - $parent = substr($path, 0, strrpos($path, '/')); |
|
| 236 | - $path = $this->getAbsolutePath($path); |
|
| 237 | - list($storage, $internalPath) = Filesystem::resolvePath($path); |
|
| 238 | - if (Filesystem::isValidPath($parent) and $storage) { |
|
| 239 | - return $storage->getLocalFile($internalPath); |
|
| 240 | - } else { |
|
| 241 | - return null; |
|
| 242 | - } |
|
| 243 | - } |
|
| 244 | - |
|
| 245 | - /** |
|
| 246 | - * @param string $path |
|
| 247 | - * @return string |
|
| 248 | - */ |
|
| 249 | - public function getLocalFolder($path) { |
|
| 250 | - $parent = substr($path, 0, strrpos($path, '/')); |
|
| 251 | - $path = $this->getAbsolutePath($path); |
|
| 252 | - list($storage, $internalPath) = Filesystem::resolvePath($path); |
|
| 253 | - if (Filesystem::isValidPath($parent) and $storage) { |
|
| 254 | - return $storage->getLocalFolder($internalPath); |
|
| 255 | - } else { |
|
| 256 | - return null; |
|
| 257 | - } |
|
| 258 | - } |
|
| 259 | - |
|
| 260 | - /** |
|
| 261 | - * the following functions operate with arguments and return values identical |
|
| 262 | - * to those of their PHP built-in equivalents. Mostly they are merely wrappers |
|
| 263 | - * for \OC\Files\Storage\Storage via basicOperation(). |
|
| 264 | - */ |
|
| 265 | - public function mkdir($path) { |
|
| 266 | - return $this->basicOperation('mkdir', $path, array('create', 'write')); |
|
| 267 | - } |
|
| 268 | - |
|
| 269 | - /** |
|
| 270 | - * remove mount point |
|
| 271 | - * |
|
| 272 | - * @param \OC\Files\Mount\MoveableMount $mount |
|
| 273 | - * @param string $path relative to data/ |
|
| 274 | - * @return boolean |
|
| 275 | - */ |
|
| 276 | - protected function removeMount($mount, $path) { |
|
| 277 | - if ($mount instanceof MoveableMount) { |
|
| 278 | - // cut of /user/files to get the relative path to data/user/files |
|
| 279 | - $pathParts = explode('/', $path, 4); |
|
| 280 | - $relPath = '/' . $pathParts[3]; |
|
| 281 | - $this->lockFile($relPath, ILockingProvider::LOCK_SHARED, true); |
|
| 282 | - \OC_Hook::emit( |
|
| 283 | - Filesystem::CLASSNAME, "umount", |
|
| 284 | - array(Filesystem::signal_param_path => $relPath) |
|
| 285 | - ); |
|
| 286 | - $this->changeLock($relPath, ILockingProvider::LOCK_EXCLUSIVE, true); |
|
| 287 | - $result = $mount->removeMount(); |
|
| 288 | - $this->changeLock($relPath, ILockingProvider::LOCK_SHARED, true); |
|
| 289 | - if ($result) { |
|
| 290 | - \OC_Hook::emit( |
|
| 291 | - Filesystem::CLASSNAME, "post_umount", |
|
| 292 | - array(Filesystem::signal_param_path => $relPath) |
|
| 293 | - ); |
|
| 294 | - } |
|
| 295 | - $this->unlockFile($relPath, ILockingProvider::LOCK_SHARED, true); |
|
| 296 | - return $result; |
|
| 297 | - } else { |
|
| 298 | - // do not allow deleting the storage's root / the mount point |
|
| 299 | - // because for some storages it might delete the whole contents |
|
| 300 | - // but isn't supposed to work that way |
|
| 301 | - return false; |
|
| 302 | - } |
|
| 303 | - } |
|
| 304 | - |
|
| 305 | - public function disableCacheUpdate() { |
|
| 306 | - $this->updaterEnabled = false; |
|
| 307 | - } |
|
| 308 | - |
|
| 309 | - public function enableCacheUpdate() { |
|
| 310 | - $this->updaterEnabled = true; |
|
| 311 | - } |
|
| 312 | - |
|
| 313 | - protected function writeUpdate(Storage $storage, $internalPath, $time = null) { |
|
| 314 | - if ($this->updaterEnabled) { |
|
| 315 | - if (is_null($time)) { |
|
| 316 | - $time = time(); |
|
| 317 | - } |
|
| 318 | - $storage->getUpdater()->update($internalPath, $time); |
|
| 319 | - } |
|
| 320 | - } |
|
| 321 | - |
|
| 322 | - protected function removeUpdate(Storage $storage, $internalPath) { |
|
| 323 | - if ($this->updaterEnabled) { |
|
| 324 | - $storage->getUpdater()->remove($internalPath); |
|
| 325 | - } |
|
| 326 | - } |
|
| 327 | - |
|
| 328 | - protected function renameUpdate(Storage $sourceStorage, Storage $targetStorage, $sourceInternalPath, $targetInternalPath) { |
|
| 329 | - if ($this->updaterEnabled) { |
|
| 330 | - $targetStorage->getUpdater()->renameFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); |
|
| 331 | - } |
|
| 332 | - } |
|
| 333 | - |
|
| 334 | - /** |
|
| 335 | - * @param string $path |
|
| 336 | - * @return bool|mixed |
|
| 337 | - */ |
|
| 338 | - public function rmdir($path) { |
|
| 339 | - $absolutePath = $this->getAbsolutePath($path); |
|
| 340 | - $mount = Filesystem::getMountManager()->find($absolutePath); |
|
| 341 | - if ($mount->getInternalPath($absolutePath) === '') { |
|
| 342 | - return $this->removeMount($mount, $absolutePath); |
|
| 343 | - } |
|
| 344 | - if ($this->is_dir($path)) { |
|
| 345 | - $result = $this->basicOperation('rmdir', $path, array('delete')); |
|
| 346 | - } else { |
|
| 347 | - $result = false; |
|
| 348 | - } |
|
| 349 | - |
|
| 350 | - if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete |
|
| 351 | - $storage = $mount->getStorage(); |
|
| 352 | - $internalPath = $mount->getInternalPath($absolutePath); |
|
| 353 | - $storage->getUpdater()->remove($internalPath); |
|
| 354 | - } |
|
| 355 | - return $result; |
|
| 356 | - } |
|
| 357 | - |
|
| 358 | - /** |
|
| 359 | - * @param string $path |
|
| 360 | - * @return resource |
|
| 361 | - */ |
|
| 362 | - public function opendir($path) { |
|
| 363 | - return $this->basicOperation('opendir', $path, array('read')); |
|
| 364 | - } |
|
| 365 | - |
|
| 366 | - /** |
|
| 367 | - * @param $handle |
|
| 368 | - * @return mixed |
|
| 369 | - */ |
|
| 370 | - public function readdir($handle) { |
|
| 371 | - $fsLocal = new Storage\Local(array('datadir' => '/')); |
|
| 372 | - return $fsLocal->readdir($handle); |
|
| 373 | - } |
|
| 374 | - |
|
| 375 | - /** |
|
| 376 | - * @param string $path |
|
| 377 | - * @return bool|mixed |
|
| 378 | - */ |
|
| 379 | - public function is_dir($path) { |
|
| 380 | - if ($path == '/') { |
|
| 381 | - return true; |
|
| 382 | - } |
|
| 383 | - return $this->basicOperation('is_dir', $path); |
|
| 384 | - } |
|
| 385 | - |
|
| 386 | - /** |
|
| 387 | - * @param string $path |
|
| 388 | - * @return bool|mixed |
|
| 389 | - */ |
|
| 390 | - public function is_file($path) { |
|
| 391 | - if ($path == '/') { |
|
| 392 | - return false; |
|
| 393 | - } |
|
| 394 | - return $this->basicOperation('is_file', $path); |
|
| 395 | - } |
|
| 396 | - |
|
| 397 | - /** |
|
| 398 | - * @param string $path |
|
| 399 | - * @return mixed |
|
| 400 | - */ |
|
| 401 | - public function stat($path) { |
|
| 402 | - return $this->basicOperation('stat', $path); |
|
| 403 | - } |
|
| 404 | - |
|
| 405 | - /** |
|
| 406 | - * @param string $path |
|
| 407 | - * @return mixed |
|
| 408 | - */ |
|
| 409 | - public function filetype($path) { |
|
| 410 | - return $this->basicOperation('filetype', $path); |
|
| 411 | - } |
|
| 412 | - |
|
| 413 | - /** |
|
| 414 | - * @param string $path |
|
| 415 | - * @return mixed |
|
| 416 | - */ |
|
| 417 | - public function filesize($path) { |
|
| 418 | - return $this->basicOperation('filesize', $path); |
|
| 419 | - } |
|
| 420 | - |
|
| 421 | - /** |
|
| 422 | - * @param string $path |
|
| 423 | - * @return bool|mixed |
|
| 424 | - * @throws \OCP\Files\InvalidPathException |
|
| 425 | - */ |
|
| 426 | - public function readfile($path) { |
|
| 427 | - $this->assertPathLength($path); |
|
| 428 | - @ob_end_clean(); |
|
| 429 | - $handle = $this->fopen($path, 'rb'); |
|
| 430 | - if ($handle) { |
|
| 431 | - $chunkSize = 8192; // 8 kB chunks |
|
| 432 | - while (!feof($handle)) { |
|
| 433 | - echo fread($handle, $chunkSize); |
|
| 434 | - flush(); |
|
| 435 | - } |
|
| 436 | - fclose($handle); |
|
| 437 | - $size = $this->filesize($path); |
|
| 438 | - return $size; |
|
| 439 | - } |
|
| 440 | - return false; |
|
| 441 | - } |
|
| 442 | - |
|
| 443 | - /** |
|
| 444 | - * @param string $path |
|
| 445 | - * @param int $from |
|
| 446 | - * @param int $to |
|
| 447 | - * @return bool|mixed |
|
| 448 | - * @throws \OCP\Files\InvalidPathException |
|
| 449 | - * @throws \OCP\Files\UnseekableException |
|
| 450 | - */ |
|
| 451 | - public function readfilePart($path, $from, $to) { |
|
| 452 | - $this->assertPathLength($path); |
|
| 453 | - @ob_end_clean(); |
|
| 454 | - $handle = $this->fopen($path, 'rb'); |
|
| 455 | - if ($handle) { |
|
| 456 | - if (fseek($handle, $from) === 0) { |
|
| 457 | - $chunkSize = 8192; // 8 kB chunks |
|
| 458 | - $end = $to + 1; |
|
| 459 | - while (!feof($handle) && ftell($handle) < $end) { |
|
| 460 | - $len = $end - ftell($handle); |
|
| 461 | - if ($len > $chunkSize) { |
|
| 462 | - $len = $chunkSize; |
|
| 463 | - } |
|
| 464 | - echo fread($handle, $len); |
|
| 465 | - flush(); |
|
| 466 | - } |
|
| 467 | - $size = ftell($handle) - $from; |
|
| 468 | - return $size; |
|
| 469 | - } |
|
| 470 | - |
|
| 471 | - throw new \OCP\Files\UnseekableException('fseek error'); |
|
| 472 | - } |
|
| 473 | - return false; |
|
| 474 | - } |
|
| 475 | - |
|
| 476 | - /** |
|
| 477 | - * @param string $path |
|
| 478 | - * @return mixed |
|
| 479 | - */ |
|
| 480 | - public function isCreatable($path) { |
|
| 481 | - return $this->basicOperation('isCreatable', $path); |
|
| 482 | - } |
|
| 483 | - |
|
| 484 | - /** |
|
| 485 | - * @param string $path |
|
| 486 | - * @return mixed |
|
| 487 | - */ |
|
| 488 | - public function isReadable($path) { |
|
| 489 | - return $this->basicOperation('isReadable', $path); |
|
| 490 | - } |
|
| 491 | - |
|
| 492 | - /** |
|
| 493 | - * @param string $path |
|
| 494 | - * @return mixed |
|
| 495 | - */ |
|
| 496 | - public function isUpdatable($path) { |
|
| 497 | - return $this->basicOperation('isUpdatable', $path); |
|
| 498 | - } |
|
| 499 | - |
|
| 500 | - /** |
|
| 501 | - * @param string $path |
|
| 502 | - * @return bool|mixed |
|
| 503 | - */ |
|
| 504 | - public function isDeletable($path) { |
|
| 505 | - $absolutePath = $this->getAbsolutePath($path); |
|
| 506 | - $mount = Filesystem::getMountManager()->find($absolutePath); |
|
| 507 | - if ($mount->getInternalPath($absolutePath) === '') { |
|
| 508 | - return $mount instanceof MoveableMount; |
|
| 509 | - } |
|
| 510 | - return $this->basicOperation('isDeletable', $path); |
|
| 511 | - } |
|
| 512 | - |
|
| 513 | - /** |
|
| 514 | - * @param string $path |
|
| 515 | - * @return mixed |
|
| 516 | - */ |
|
| 517 | - public function isSharable($path) { |
|
| 518 | - return $this->basicOperation('isSharable', $path); |
|
| 519 | - } |
|
| 520 | - |
|
| 521 | - /** |
|
| 522 | - * @param string $path |
|
| 523 | - * @return bool|mixed |
|
| 524 | - */ |
|
| 525 | - public function file_exists($path) { |
|
| 526 | - if ($path == '/') { |
|
| 527 | - return true; |
|
| 528 | - } |
|
| 529 | - return $this->basicOperation('file_exists', $path); |
|
| 530 | - } |
|
| 531 | - |
|
| 532 | - /** |
|
| 533 | - * @param string $path |
|
| 534 | - * @return mixed |
|
| 535 | - */ |
|
| 536 | - public function filemtime($path) { |
|
| 537 | - return $this->basicOperation('filemtime', $path); |
|
| 538 | - } |
|
| 539 | - |
|
| 540 | - /** |
|
| 541 | - * @param string $path |
|
| 542 | - * @param int|string $mtime |
|
| 543 | - * @return bool |
|
| 544 | - */ |
|
| 545 | - public function touch($path, $mtime = null) { |
|
| 546 | - if (!is_null($mtime) and !is_numeric($mtime)) { |
|
| 547 | - $mtime = strtotime($mtime); |
|
| 548 | - } |
|
| 549 | - |
|
| 550 | - $hooks = array('touch'); |
|
| 551 | - |
|
| 552 | - if (!$this->file_exists($path)) { |
|
| 553 | - $hooks[] = 'create'; |
|
| 554 | - $hooks[] = 'write'; |
|
| 555 | - } |
|
| 556 | - $result = $this->basicOperation('touch', $path, $hooks, $mtime); |
|
| 557 | - if (!$result) { |
|
| 558 | - // If create file fails because of permissions on external storage like SMB folders, |
|
| 559 | - // check file exists and return false if not. |
|
| 560 | - if (!$this->file_exists($path)) { |
|
| 561 | - return false; |
|
| 562 | - } |
|
| 563 | - if (is_null($mtime)) { |
|
| 564 | - $mtime = time(); |
|
| 565 | - } |
|
| 566 | - //if native touch fails, we emulate it by changing the mtime in the cache |
|
| 567 | - $this->putFileInfo($path, array('mtime' => $mtime)); |
|
| 568 | - } |
|
| 569 | - return true; |
|
| 570 | - } |
|
| 571 | - |
|
| 572 | - /** |
|
| 573 | - * @param string $path |
|
| 574 | - * @return mixed |
|
| 575 | - */ |
|
| 576 | - public function file_get_contents($path) { |
|
| 577 | - return $this->basicOperation('file_get_contents', $path, array('read')); |
|
| 578 | - } |
|
| 579 | - |
|
| 580 | - /** |
|
| 581 | - * @param bool $exists |
|
| 582 | - * @param string $path |
|
| 583 | - * @param bool $run |
|
| 584 | - */ |
|
| 585 | - protected function emit_file_hooks_pre($exists, $path, &$run) { |
|
| 586 | - if (!$exists) { |
|
| 587 | - \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_create, array( |
|
| 588 | - Filesystem::signal_param_path => $this->getHookPath($path), |
|
| 589 | - Filesystem::signal_param_run => &$run, |
|
| 590 | - )); |
|
| 591 | - } else { |
|
| 592 | - \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_update, array( |
|
| 593 | - Filesystem::signal_param_path => $this->getHookPath($path), |
|
| 594 | - Filesystem::signal_param_run => &$run, |
|
| 595 | - )); |
|
| 596 | - } |
|
| 597 | - \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_write, array( |
|
| 598 | - Filesystem::signal_param_path => $this->getHookPath($path), |
|
| 599 | - Filesystem::signal_param_run => &$run, |
|
| 600 | - )); |
|
| 601 | - } |
|
| 602 | - |
|
| 603 | - /** |
|
| 604 | - * @param bool $exists |
|
| 605 | - * @param string $path |
|
| 606 | - */ |
|
| 607 | - protected function emit_file_hooks_post($exists, $path) { |
|
| 608 | - if (!$exists) { |
|
| 609 | - \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_create, array( |
|
| 610 | - Filesystem::signal_param_path => $this->getHookPath($path), |
|
| 611 | - )); |
|
| 612 | - } else { |
|
| 613 | - \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_update, array( |
|
| 614 | - Filesystem::signal_param_path => $this->getHookPath($path), |
|
| 615 | - )); |
|
| 616 | - } |
|
| 617 | - \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_write, array( |
|
| 618 | - Filesystem::signal_param_path => $this->getHookPath($path), |
|
| 619 | - )); |
|
| 620 | - } |
|
| 621 | - |
|
| 622 | - /** |
|
| 623 | - * @param string $path |
|
| 624 | - * @param mixed $data |
|
| 625 | - * @return bool|mixed |
|
| 626 | - * @throws \Exception |
|
| 627 | - */ |
|
| 628 | - public function file_put_contents($path, $data) { |
|
| 629 | - if (is_resource($data)) { //not having to deal with streams in file_put_contents makes life easier |
|
| 630 | - $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); |
|
| 631 | - if (Filesystem::isValidPath($path) |
|
| 632 | - and !Filesystem::isFileBlacklisted($path) |
|
| 633 | - ) { |
|
| 634 | - $path = $this->getRelativePath($absolutePath); |
|
| 635 | - |
|
| 636 | - $this->lockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 637 | - |
|
| 638 | - $exists = $this->file_exists($path); |
|
| 639 | - $run = true; |
|
| 640 | - if ($this->shouldEmitHooks($path)) { |
|
| 641 | - $this->emit_file_hooks_pre($exists, $path, $run); |
|
| 642 | - } |
|
| 643 | - if (!$run) { |
|
| 644 | - $this->unlockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 645 | - return false; |
|
| 646 | - } |
|
| 647 | - |
|
| 648 | - $this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE); |
|
| 649 | - |
|
| 650 | - /** @var \OC\Files\Storage\Storage $storage */ |
|
| 651 | - list($storage, $internalPath) = $this->resolvePath($path); |
|
| 652 | - $target = $storage->fopen($internalPath, 'w'); |
|
| 653 | - if ($target) { |
|
| 654 | - list (, $result) = \OC_Helper::streamCopy($data, $target); |
|
| 655 | - fclose($target); |
|
| 656 | - fclose($data); |
|
| 657 | - |
|
| 658 | - $this->writeUpdate($storage, $internalPath); |
|
| 659 | - |
|
| 660 | - $this->changeLock($path, ILockingProvider::LOCK_SHARED); |
|
| 661 | - |
|
| 662 | - if ($this->shouldEmitHooks($path) && $result !== false) { |
|
| 663 | - $this->emit_file_hooks_post($exists, $path); |
|
| 664 | - } |
|
| 665 | - $this->unlockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 666 | - return $result; |
|
| 667 | - } else { |
|
| 668 | - $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE); |
|
| 669 | - return false; |
|
| 670 | - } |
|
| 671 | - } else { |
|
| 672 | - return false; |
|
| 673 | - } |
|
| 674 | - } else { |
|
| 675 | - $hooks = ($this->file_exists($path)) ? array('update', 'write') : array('create', 'write'); |
|
| 676 | - return $this->basicOperation('file_put_contents', $path, $hooks, $data); |
|
| 677 | - } |
|
| 678 | - } |
|
| 679 | - |
|
| 680 | - /** |
|
| 681 | - * @param string $path |
|
| 682 | - * @return bool|mixed |
|
| 683 | - */ |
|
| 684 | - public function unlink($path) { |
|
| 685 | - if ($path === '' || $path === '/') { |
|
| 686 | - // do not allow deleting the root |
|
| 687 | - return false; |
|
| 688 | - } |
|
| 689 | - $postFix = (substr($path, -1, 1) === '/') ? '/' : ''; |
|
| 690 | - $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); |
|
| 691 | - $mount = Filesystem::getMountManager()->find($absolutePath . $postFix); |
|
| 692 | - if ($mount and $mount->getInternalPath($absolutePath) === '') { |
|
| 693 | - return $this->removeMount($mount, $absolutePath); |
|
| 694 | - } |
|
| 695 | - $result = $this->basicOperation('unlink', $path, array('delete')); |
|
| 696 | - if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete |
|
| 697 | - $storage = $mount->getStorage(); |
|
| 698 | - $internalPath = $mount->getInternalPath($absolutePath); |
|
| 699 | - $storage->getUpdater()->remove($internalPath); |
|
| 700 | - return true; |
|
| 701 | - } else { |
|
| 702 | - return $result; |
|
| 703 | - } |
|
| 704 | - } |
|
| 705 | - |
|
| 706 | - /** |
|
| 707 | - * @param string $directory |
|
| 708 | - * @return bool|mixed |
|
| 709 | - */ |
|
| 710 | - public function deleteAll($directory) { |
|
| 711 | - return $this->rmdir($directory); |
|
| 712 | - } |
|
| 713 | - |
|
| 714 | - /** |
|
| 715 | - * Rename/move a file or folder from the source path to target path. |
|
| 716 | - * |
|
| 717 | - * @param string $path1 source path |
|
| 718 | - * @param string $path2 target path |
|
| 719 | - * |
|
| 720 | - * @return bool|mixed |
|
| 721 | - */ |
|
| 722 | - public function rename($path1, $path2) { |
|
| 723 | - $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1)); |
|
| 724 | - $absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2)); |
|
| 725 | - $result = false; |
|
| 726 | - if ( |
|
| 727 | - Filesystem::isValidPath($path2) |
|
| 728 | - and Filesystem::isValidPath($path1) |
|
| 729 | - and !Filesystem::isFileBlacklisted($path2) |
|
| 730 | - ) { |
|
| 731 | - $path1 = $this->getRelativePath($absolutePath1); |
|
| 732 | - $path2 = $this->getRelativePath($absolutePath2); |
|
| 733 | - $exists = $this->file_exists($path2); |
|
| 734 | - |
|
| 735 | - if ($path1 == null or $path2 == null) { |
|
| 736 | - return false; |
|
| 737 | - } |
|
| 738 | - |
|
| 739 | - $this->lockFile($path1, ILockingProvider::LOCK_SHARED, true); |
|
| 740 | - try { |
|
| 741 | - $this->lockFile($path2, ILockingProvider::LOCK_SHARED, true); |
|
| 742 | - } catch (LockedException $e) { |
|
| 743 | - $this->unlockFile($path1, ILockingProvider::LOCK_SHARED); |
|
| 744 | - throw $e; |
|
| 745 | - } |
|
| 746 | - |
|
| 747 | - $run = true; |
|
| 748 | - if ($this->shouldEmitHooks($path1) && (Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2))) { |
|
| 749 | - // if it was a rename from a part file to a regular file it was a write and not a rename operation |
|
| 750 | - $this->emit_file_hooks_pre($exists, $path2, $run); |
|
| 751 | - } elseif ($this->shouldEmitHooks($path1)) { |
|
| 752 | - \OC_Hook::emit( |
|
| 753 | - Filesystem::CLASSNAME, Filesystem::signal_rename, |
|
| 754 | - array( |
|
| 755 | - Filesystem::signal_param_oldpath => $this->getHookPath($path1), |
|
| 756 | - Filesystem::signal_param_newpath => $this->getHookPath($path2), |
|
| 757 | - Filesystem::signal_param_run => &$run |
|
| 758 | - ) |
|
| 759 | - ); |
|
| 760 | - } |
|
| 761 | - if ($run) { |
|
| 762 | - $this->verifyPath(dirname($path2), basename($path2)); |
|
| 763 | - |
|
| 764 | - $manager = Filesystem::getMountManager(); |
|
| 765 | - $mount1 = $this->getMount($path1); |
|
| 766 | - $mount2 = $this->getMount($path2); |
|
| 767 | - $storage1 = $mount1->getStorage(); |
|
| 768 | - $storage2 = $mount2->getStorage(); |
|
| 769 | - $internalPath1 = $mount1->getInternalPath($absolutePath1); |
|
| 770 | - $internalPath2 = $mount2->getInternalPath($absolutePath2); |
|
| 771 | - |
|
| 772 | - $this->changeLock($path1, ILockingProvider::LOCK_EXCLUSIVE, true); |
|
| 773 | - $this->changeLock($path2, ILockingProvider::LOCK_EXCLUSIVE, true); |
|
| 774 | - |
|
| 775 | - if ($internalPath1 === '' and $mount1 instanceof MoveableMount) { |
|
| 776 | - if ($this->isTargetAllowed($absolutePath2)) { |
|
| 777 | - /** |
|
| 778 | - * @var \OC\Files\Mount\MountPoint | \OC\Files\Mount\MoveableMount $mount1 |
|
| 779 | - */ |
|
| 780 | - $sourceMountPoint = $mount1->getMountPoint(); |
|
| 781 | - $result = $mount1->moveMount($absolutePath2); |
|
| 782 | - $manager->moveMount($sourceMountPoint, $mount1->getMountPoint()); |
|
| 783 | - } else { |
|
| 784 | - $result = false; |
|
| 785 | - } |
|
| 786 | - // moving a file/folder within the same mount point |
|
| 787 | - } elseif ($storage1 === $storage2) { |
|
| 788 | - if ($storage1) { |
|
| 789 | - $result = $storage1->rename($internalPath1, $internalPath2); |
|
| 790 | - } else { |
|
| 791 | - $result = false; |
|
| 792 | - } |
|
| 793 | - // moving a file/folder between storages (from $storage1 to $storage2) |
|
| 794 | - } else { |
|
| 795 | - $result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2); |
|
| 796 | - } |
|
| 797 | - |
|
| 798 | - if ((Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) { |
|
| 799 | - // if it was a rename from a part file to a regular file it was a write and not a rename operation |
|
| 800 | - |
|
| 801 | - $this->writeUpdate($storage2, $internalPath2); |
|
| 802 | - } else if ($result) { |
|
| 803 | - if ($internalPath1 !== '') { // don't do a cache update for moved mounts |
|
| 804 | - $this->renameUpdate($storage1, $storage2, $internalPath1, $internalPath2); |
|
| 805 | - } |
|
| 806 | - } |
|
| 807 | - |
|
| 808 | - $this->changeLock($path1, ILockingProvider::LOCK_SHARED, true); |
|
| 809 | - $this->changeLock($path2, ILockingProvider::LOCK_SHARED, true); |
|
| 810 | - |
|
| 811 | - if ((Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) { |
|
| 812 | - if ($this->shouldEmitHooks()) { |
|
| 813 | - $this->emit_file_hooks_post($exists, $path2); |
|
| 814 | - } |
|
| 815 | - } elseif ($result) { |
|
| 816 | - if ($this->shouldEmitHooks($path1) and $this->shouldEmitHooks($path2)) { |
|
| 817 | - \OC_Hook::emit( |
|
| 818 | - Filesystem::CLASSNAME, |
|
| 819 | - Filesystem::signal_post_rename, |
|
| 820 | - array( |
|
| 821 | - Filesystem::signal_param_oldpath => $this->getHookPath($path1), |
|
| 822 | - Filesystem::signal_param_newpath => $this->getHookPath($path2) |
|
| 823 | - ) |
|
| 824 | - ); |
|
| 825 | - } |
|
| 826 | - } |
|
| 827 | - } |
|
| 828 | - $this->unlockFile($path1, ILockingProvider::LOCK_SHARED, true); |
|
| 829 | - $this->unlockFile($path2, ILockingProvider::LOCK_SHARED, true); |
|
| 830 | - } |
|
| 831 | - return $result; |
|
| 832 | - } |
|
| 833 | - |
|
| 834 | - /** |
|
| 835 | - * Copy a file/folder from the source path to target path |
|
| 836 | - * |
|
| 837 | - * @param string $path1 source path |
|
| 838 | - * @param string $path2 target path |
|
| 839 | - * @param bool $preserveMtime whether to preserve mtime on the copy |
|
| 840 | - * |
|
| 841 | - * @return bool|mixed |
|
| 842 | - */ |
|
| 843 | - public function copy($path1, $path2, $preserveMtime = false) { |
|
| 844 | - $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1)); |
|
| 845 | - $absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2)); |
|
| 846 | - $result = false; |
|
| 847 | - if ( |
|
| 848 | - Filesystem::isValidPath($path2) |
|
| 849 | - and Filesystem::isValidPath($path1) |
|
| 850 | - and !Filesystem::isFileBlacklisted($path2) |
|
| 851 | - ) { |
|
| 852 | - $path1 = $this->getRelativePath($absolutePath1); |
|
| 853 | - $path2 = $this->getRelativePath($absolutePath2); |
|
| 854 | - |
|
| 855 | - if ($path1 == null or $path2 == null) { |
|
| 856 | - return false; |
|
| 857 | - } |
|
| 858 | - $run = true; |
|
| 859 | - |
|
| 860 | - $this->lockFile($path2, ILockingProvider::LOCK_SHARED); |
|
| 861 | - $this->lockFile($path1, ILockingProvider::LOCK_SHARED); |
|
| 862 | - $lockTypePath1 = ILockingProvider::LOCK_SHARED; |
|
| 863 | - $lockTypePath2 = ILockingProvider::LOCK_SHARED; |
|
| 864 | - |
|
| 865 | - try { |
|
| 866 | - |
|
| 867 | - $exists = $this->file_exists($path2); |
|
| 868 | - if ($this->shouldEmitHooks()) { |
|
| 869 | - \OC_Hook::emit( |
|
| 870 | - Filesystem::CLASSNAME, |
|
| 871 | - Filesystem::signal_copy, |
|
| 872 | - array( |
|
| 873 | - Filesystem::signal_param_oldpath => $this->getHookPath($path1), |
|
| 874 | - Filesystem::signal_param_newpath => $this->getHookPath($path2), |
|
| 875 | - Filesystem::signal_param_run => &$run |
|
| 876 | - ) |
|
| 877 | - ); |
|
| 878 | - $this->emit_file_hooks_pre($exists, $path2, $run); |
|
| 879 | - } |
|
| 880 | - if ($run) { |
|
| 881 | - $mount1 = $this->getMount($path1); |
|
| 882 | - $mount2 = $this->getMount($path2); |
|
| 883 | - $storage1 = $mount1->getStorage(); |
|
| 884 | - $internalPath1 = $mount1->getInternalPath($absolutePath1); |
|
| 885 | - $storage2 = $mount2->getStorage(); |
|
| 886 | - $internalPath2 = $mount2->getInternalPath($absolutePath2); |
|
| 887 | - |
|
| 888 | - $this->changeLock($path2, ILockingProvider::LOCK_EXCLUSIVE); |
|
| 889 | - $lockTypePath2 = ILockingProvider::LOCK_EXCLUSIVE; |
|
| 890 | - |
|
| 891 | - if ($mount1->getMountPoint() == $mount2->getMountPoint()) { |
|
| 892 | - if ($storage1) { |
|
| 893 | - $result = $storage1->copy($internalPath1, $internalPath2); |
|
| 894 | - } else { |
|
| 895 | - $result = false; |
|
| 896 | - } |
|
| 897 | - } else { |
|
| 898 | - $result = $storage2->copyFromStorage($storage1, $internalPath1, $internalPath2); |
|
| 899 | - } |
|
| 900 | - |
|
| 901 | - $this->writeUpdate($storage2, $internalPath2); |
|
| 902 | - |
|
| 903 | - $this->changeLock($path2, ILockingProvider::LOCK_SHARED); |
|
| 904 | - $lockTypePath2 = ILockingProvider::LOCK_SHARED; |
|
| 905 | - |
|
| 906 | - if ($this->shouldEmitHooks() && $result !== false) { |
|
| 907 | - \OC_Hook::emit( |
|
| 908 | - Filesystem::CLASSNAME, |
|
| 909 | - Filesystem::signal_post_copy, |
|
| 910 | - array( |
|
| 911 | - Filesystem::signal_param_oldpath => $this->getHookPath($path1), |
|
| 912 | - Filesystem::signal_param_newpath => $this->getHookPath($path2) |
|
| 913 | - ) |
|
| 914 | - ); |
|
| 915 | - $this->emit_file_hooks_post($exists, $path2); |
|
| 916 | - } |
|
| 917 | - |
|
| 918 | - } |
|
| 919 | - } catch (\Exception $e) { |
|
| 920 | - $this->unlockFile($path2, $lockTypePath2); |
|
| 921 | - $this->unlockFile($path1, $lockTypePath1); |
|
| 922 | - throw $e; |
|
| 923 | - } |
|
| 924 | - |
|
| 925 | - $this->unlockFile($path2, $lockTypePath2); |
|
| 926 | - $this->unlockFile($path1, $lockTypePath1); |
|
| 927 | - |
|
| 928 | - } |
|
| 929 | - return $result; |
|
| 930 | - } |
|
| 931 | - |
|
| 932 | - /** |
|
| 933 | - * @param string $path |
|
| 934 | - * @param string $mode 'r' or 'w' |
|
| 935 | - * @return resource |
|
| 936 | - */ |
|
| 937 | - public function fopen($path, $mode) { |
|
| 938 | - $mode = str_replace('b', '', $mode); // the binary flag is a windows only feature which we do not support |
|
| 939 | - $hooks = array(); |
|
| 940 | - switch ($mode) { |
|
| 941 | - case 'r': |
|
| 942 | - $hooks[] = 'read'; |
|
| 943 | - break; |
|
| 944 | - case 'r+': |
|
| 945 | - case 'w+': |
|
| 946 | - case 'x+': |
|
| 947 | - case 'a+': |
|
| 948 | - $hooks[] = 'read'; |
|
| 949 | - $hooks[] = 'write'; |
|
| 950 | - break; |
|
| 951 | - case 'w': |
|
| 952 | - case 'x': |
|
| 953 | - case 'a': |
|
| 954 | - $hooks[] = 'write'; |
|
| 955 | - break; |
|
| 956 | - default: |
|
| 957 | - \OCP\Util::writeLog('core', 'invalid mode (' . $mode . ') for ' . $path, \OCP\Util::ERROR); |
|
| 958 | - } |
|
| 959 | - |
|
| 960 | - if ($mode !== 'r' && $mode !== 'w') { |
|
| 961 | - \OC::$server->getLogger()->info('Trying to open a file with a mode other than "r" or "w" can cause severe performance issues with some backends'); |
|
| 962 | - } |
|
| 963 | - |
|
| 964 | - return $this->basicOperation('fopen', $path, $hooks, $mode); |
|
| 965 | - } |
|
| 966 | - |
|
| 967 | - /** |
|
| 968 | - * @param string $path |
|
| 969 | - * @return bool|string |
|
| 970 | - * @throws \OCP\Files\InvalidPathException |
|
| 971 | - */ |
|
| 972 | - public function toTmpFile($path) { |
|
| 973 | - $this->assertPathLength($path); |
|
| 974 | - if (Filesystem::isValidPath($path)) { |
|
| 975 | - $source = $this->fopen($path, 'r'); |
|
| 976 | - if ($source) { |
|
| 977 | - $extension = pathinfo($path, PATHINFO_EXTENSION); |
|
| 978 | - $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($extension); |
|
| 979 | - file_put_contents($tmpFile, $source); |
|
| 980 | - return $tmpFile; |
|
| 981 | - } else { |
|
| 982 | - return false; |
|
| 983 | - } |
|
| 984 | - } else { |
|
| 985 | - return false; |
|
| 986 | - } |
|
| 987 | - } |
|
| 988 | - |
|
| 989 | - /** |
|
| 990 | - * @param string $tmpFile |
|
| 991 | - * @param string $path |
|
| 992 | - * @return bool|mixed |
|
| 993 | - * @throws \OCP\Files\InvalidPathException |
|
| 994 | - */ |
|
| 995 | - public function fromTmpFile($tmpFile, $path) { |
|
| 996 | - $this->assertPathLength($path); |
|
| 997 | - if (Filesystem::isValidPath($path)) { |
|
| 998 | - |
|
| 999 | - // Get directory that the file is going into |
|
| 1000 | - $filePath = dirname($path); |
|
| 1001 | - |
|
| 1002 | - // Create the directories if any |
|
| 1003 | - if (!$this->file_exists($filePath)) { |
|
| 1004 | - $result = $this->createParentDirectories($filePath); |
|
| 1005 | - if ($result === false) { |
|
| 1006 | - return false; |
|
| 1007 | - } |
|
| 1008 | - } |
|
| 1009 | - |
|
| 1010 | - $source = fopen($tmpFile, 'r'); |
|
| 1011 | - if ($source) { |
|
| 1012 | - $result = $this->file_put_contents($path, $source); |
|
| 1013 | - // $this->file_put_contents() might have already closed |
|
| 1014 | - // the resource, so we check it, before trying to close it |
|
| 1015 | - // to avoid messages in the error log. |
|
| 1016 | - if (is_resource($source)) { |
|
| 1017 | - fclose($source); |
|
| 1018 | - } |
|
| 1019 | - unlink($tmpFile); |
|
| 1020 | - return $result; |
|
| 1021 | - } else { |
|
| 1022 | - return false; |
|
| 1023 | - } |
|
| 1024 | - } else { |
|
| 1025 | - return false; |
|
| 1026 | - } |
|
| 1027 | - } |
|
| 1028 | - |
|
| 1029 | - |
|
| 1030 | - /** |
|
| 1031 | - * @param string $path |
|
| 1032 | - * @return mixed |
|
| 1033 | - * @throws \OCP\Files\InvalidPathException |
|
| 1034 | - */ |
|
| 1035 | - public function getMimeType($path) { |
|
| 1036 | - $this->assertPathLength($path); |
|
| 1037 | - return $this->basicOperation('getMimeType', $path); |
|
| 1038 | - } |
|
| 1039 | - |
|
| 1040 | - /** |
|
| 1041 | - * @param string $type |
|
| 1042 | - * @param string $path |
|
| 1043 | - * @param bool $raw |
|
| 1044 | - * @return bool|null|string |
|
| 1045 | - */ |
|
| 1046 | - public function hash($type, $path, $raw = false) { |
|
| 1047 | - $postFix = (substr($path, -1, 1) === '/') ? '/' : ''; |
|
| 1048 | - $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); |
|
| 1049 | - if (Filesystem::isValidPath($path)) { |
|
| 1050 | - $path = $this->getRelativePath($absolutePath); |
|
| 1051 | - if ($path == null) { |
|
| 1052 | - return false; |
|
| 1053 | - } |
|
| 1054 | - if ($this->shouldEmitHooks($path)) { |
|
| 1055 | - \OC_Hook::emit( |
|
| 1056 | - Filesystem::CLASSNAME, |
|
| 1057 | - Filesystem::signal_read, |
|
| 1058 | - array(Filesystem::signal_param_path => $this->getHookPath($path)) |
|
| 1059 | - ); |
|
| 1060 | - } |
|
| 1061 | - list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix); |
|
| 1062 | - if ($storage) { |
|
| 1063 | - $result = $storage->hash($type, $internalPath, $raw); |
|
| 1064 | - return $result; |
|
| 1065 | - } |
|
| 1066 | - } |
|
| 1067 | - return null; |
|
| 1068 | - } |
|
| 1069 | - |
|
| 1070 | - /** |
|
| 1071 | - * @param string $path |
|
| 1072 | - * @return mixed |
|
| 1073 | - * @throws \OCP\Files\InvalidPathException |
|
| 1074 | - */ |
|
| 1075 | - public function free_space($path = '/') { |
|
| 1076 | - $this->assertPathLength($path); |
|
| 1077 | - return $this->basicOperation('free_space', $path); |
|
| 1078 | - } |
|
| 1079 | - |
|
| 1080 | - /** |
|
| 1081 | - * abstraction layer for basic filesystem functions: wrapper for \OC\Files\Storage\Storage |
|
| 1082 | - * |
|
| 1083 | - * @param string $operation |
|
| 1084 | - * @param string $path |
|
| 1085 | - * @param array $hooks (optional) |
|
| 1086 | - * @param mixed $extraParam (optional) |
|
| 1087 | - * @return mixed |
|
| 1088 | - * @throws \Exception |
|
| 1089 | - * |
|
| 1090 | - * This method takes requests for basic filesystem functions (e.g. reading & writing |
|
| 1091 | - * files), processes hooks and proxies, sanitises paths, and finally passes them on to |
|
| 1092 | - * \OC\Files\Storage\Storage for delegation to a storage backend for execution |
|
| 1093 | - */ |
|
| 1094 | - private function basicOperation($operation, $path, $hooks = [], $extraParam = null) { |
|
| 1095 | - $postFix = (substr($path, -1, 1) === '/') ? '/' : ''; |
|
| 1096 | - $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); |
|
| 1097 | - if (Filesystem::isValidPath($path) |
|
| 1098 | - and !Filesystem::isFileBlacklisted($path) |
|
| 1099 | - ) { |
|
| 1100 | - $path = $this->getRelativePath($absolutePath); |
|
| 1101 | - if ($path == null) { |
|
| 1102 | - return false; |
|
| 1103 | - } |
|
| 1104 | - |
|
| 1105 | - if (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks)) { |
|
| 1106 | - // always a shared lock during pre-hooks so the hook can read the file |
|
| 1107 | - $this->lockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 1108 | - } |
|
| 1109 | - |
|
| 1110 | - $run = $this->runHooks($hooks, $path); |
|
| 1111 | - /** @var \OC\Files\Storage\Storage $storage */ |
|
| 1112 | - list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix); |
|
| 1113 | - if ($run and $storage) { |
|
| 1114 | - if (in_array('write', $hooks) || in_array('delete', $hooks)) { |
|
| 1115 | - $this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE); |
|
| 1116 | - } |
|
| 1117 | - try { |
|
| 1118 | - if (!is_null($extraParam)) { |
|
| 1119 | - $result = $storage->$operation($internalPath, $extraParam); |
|
| 1120 | - } else { |
|
| 1121 | - $result = $storage->$operation($internalPath); |
|
| 1122 | - } |
|
| 1123 | - } catch (\Exception $e) { |
|
| 1124 | - if (in_array('write', $hooks) || in_array('delete', $hooks)) { |
|
| 1125 | - $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE); |
|
| 1126 | - } else if (in_array('read', $hooks)) { |
|
| 1127 | - $this->unlockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 1128 | - } |
|
| 1129 | - throw $e; |
|
| 1130 | - } |
|
| 1131 | - |
|
| 1132 | - if ($result && in_array('delete', $hooks) and $result) { |
|
| 1133 | - $this->removeUpdate($storage, $internalPath); |
|
| 1134 | - } |
|
| 1135 | - if ($result && in_array('write', $hooks) and $operation !== 'fopen') { |
|
| 1136 | - $this->writeUpdate($storage, $internalPath); |
|
| 1137 | - } |
|
| 1138 | - if ($result && in_array('touch', $hooks)) { |
|
| 1139 | - $this->writeUpdate($storage, $internalPath, $extraParam); |
|
| 1140 | - } |
|
| 1141 | - |
|
| 1142 | - if ((in_array('write', $hooks) || in_array('delete', $hooks)) && ($operation !== 'fopen' || $result === false)) { |
|
| 1143 | - $this->changeLock($path, ILockingProvider::LOCK_SHARED); |
|
| 1144 | - } |
|
| 1145 | - |
|
| 1146 | - $unlockLater = false; |
|
| 1147 | - if ($this->lockingEnabled && $operation === 'fopen' && is_resource($result)) { |
|
| 1148 | - $unlockLater = true; |
|
| 1149 | - // make sure our unlocking callback will still be called if connection is aborted |
|
| 1150 | - ignore_user_abort(true); |
|
| 1151 | - $result = CallbackWrapper::wrap($result, null, null, function () use ($hooks, $path) { |
|
| 1152 | - if (in_array('write', $hooks)) { |
|
| 1153 | - $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE); |
|
| 1154 | - } else if (in_array('read', $hooks)) { |
|
| 1155 | - $this->unlockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 1156 | - } |
|
| 1157 | - }); |
|
| 1158 | - } |
|
| 1159 | - |
|
| 1160 | - if ($this->shouldEmitHooks($path) && $result !== false) { |
|
| 1161 | - if ($operation != 'fopen') { //no post hooks for fopen, the file stream is still open |
|
| 1162 | - $this->runHooks($hooks, $path, true); |
|
| 1163 | - } |
|
| 1164 | - } |
|
| 1165 | - |
|
| 1166 | - if (!$unlockLater |
|
| 1167 | - && (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks)) |
|
| 1168 | - ) { |
|
| 1169 | - $this->unlockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 1170 | - } |
|
| 1171 | - return $result; |
|
| 1172 | - } else { |
|
| 1173 | - $this->unlockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 1174 | - } |
|
| 1175 | - } |
|
| 1176 | - return null; |
|
| 1177 | - } |
|
| 1178 | - |
|
| 1179 | - /** |
|
| 1180 | - * get the path relative to the default root for hook usage |
|
| 1181 | - * |
|
| 1182 | - * @param string $path |
|
| 1183 | - * @return string |
|
| 1184 | - */ |
|
| 1185 | - private function getHookPath($path) { |
|
| 1186 | - if (!Filesystem::getView()) { |
|
| 1187 | - return $path; |
|
| 1188 | - } |
|
| 1189 | - return Filesystem::getView()->getRelativePath($this->getAbsolutePath($path)); |
|
| 1190 | - } |
|
| 1191 | - |
|
| 1192 | - private function shouldEmitHooks($path = '') { |
|
| 1193 | - if ($path && Cache\Scanner::isPartialFile($path)) { |
|
| 1194 | - return false; |
|
| 1195 | - } |
|
| 1196 | - if (!Filesystem::$loaded) { |
|
| 1197 | - return false; |
|
| 1198 | - } |
|
| 1199 | - $defaultRoot = Filesystem::getRoot(); |
|
| 1200 | - if ($defaultRoot === null) { |
|
| 1201 | - return false; |
|
| 1202 | - } |
|
| 1203 | - if ($this->fakeRoot === $defaultRoot) { |
|
| 1204 | - return true; |
|
| 1205 | - } |
|
| 1206 | - $fullPath = $this->getAbsolutePath($path); |
|
| 1207 | - |
|
| 1208 | - if ($fullPath === $defaultRoot) { |
|
| 1209 | - return true; |
|
| 1210 | - } |
|
| 1211 | - |
|
| 1212 | - return (strlen($fullPath) > strlen($defaultRoot)) && (substr($fullPath, 0, strlen($defaultRoot) + 1) === $defaultRoot . '/'); |
|
| 1213 | - } |
|
| 1214 | - |
|
| 1215 | - /** |
|
| 1216 | - * @param string[] $hooks |
|
| 1217 | - * @param string $path |
|
| 1218 | - * @param bool $post |
|
| 1219 | - * @return bool |
|
| 1220 | - */ |
|
| 1221 | - private function runHooks($hooks, $path, $post = false) { |
|
| 1222 | - $relativePath = $path; |
|
| 1223 | - $path = $this->getHookPath($path); |
|
| 1224 | - $prefix = ($post) ? 'post_' : ''; |
|
| 1225 | - $run = true; |
|
| 1226 | - if ($this->shouldEmitHooks($relativePath)) { |
|
| 1227 | - foreach ($hooks as $hook) { |
|
| 1228 | - if ($hook != 'read') { |
|
| 1229 | - \OC_Hook::emit( |
|
| 1230 | - Filesystem::CLASSNAME, |
|
| 1231 | - $prefix . $hook, |
|
| 1232 | - array( |
|
| 1233 | - Filesystem::signal_param_run => &$run, |
|
| 1234 | - Filesystem::signal_param_path => $path |
|
| 1235 | - ) |
|
| 1236 | - ); |
|
| 1237 | - } elseif (!$post) { |
|
| 1238 | - \OC_Hook::emit( |
|
| 1239 | - Filesystem::CLASSNAME, |
|
| 1240 | - $prefix . $hook, |
|
| 1241 | - array( |
|
| 1242 | - Filesystem::signal_param_path => $path |
|
| 1243 | - ) |
|
| 1244 | - ); |
|
| 1245 | - } |
|
| 1246 | - } |
|
| 1247 | - } |
|
| 1248 | - return $run; |
|
| 1249 | - } |
|
| 1250 | - |
|
| 1251 | - /** |
|
| 1252 | - * check if a file or folder has been updated since $time |
|
| 1253 | - * |
|
| 1254 | - * @param string $path |
|
| 1255 | - * @param int $time |
|
| 1256 | - * @return bool |
|
| 1257 | - */ |
|
| 1258 | - public function hasUpdated($path, $time) { |
|
| 1259 | - return $this->basicOperation('hasUpdated', $path, array(), $time); |
|
| 1260 | - } |
|
| 1261 | - |
|
| 1262 | - /** |
|
| 1263 | - * @param string $ownerId |
|
| 1264 | - * @return \OC\User\User |
|
| 1265 | - */ |
|
| 1266 | - private function getUserObjectForOwner($ownerId) { |
|
| 1267 | - $owner = $this->userManager->get($ownerId); |
|
| 1268 | - if ($owner instanceof IUser) { |
|
| 1269 | - return $owner; |
|
| 1270 | - } else { |
|
| 1271 | - return new User($ownerId, null); |
|
| 1272 | - } |
|
| 1273 | - } |
|
| 1274 | - |
|
| 1275 | - /** |
|
| 1276 | - * Get file info from cache |
|
| 1277 | - * |
|
| 1278 | - * If the file is not in cached it will be scanned |
|
| 1279 | - * If the file has changed on storage the cache will be updated |
|
| 1280 | - * |
|
| 1281 | - * @param \OC\Files\Storage\Storage $storage |
|
| 1282 | - * @param string $internalPath |
|
| 1283 | - * @param string $relativePath |
|
| 1284 | - * @return array|bool |
|
| 1285 | - */ |
|
| 1286 | - private function getCacheEntry($storage, $internalPath, $relativePath) { |
|
| 1287 | - $cache = $storage->getCache($internalPath); |
|
| 1288 | - $data = $cache->get($internalPath); |
|
| 1289 | - $watcher = $storage->getWatcher($internalPath); |
|
| 1290 | - |
|
| 1291 | - try { |
|
| 1292 | - // if the file is not in the cache or needs to be updated, trigger the scanner and reload the data |
|
| 1293 | - if (!$data || $data['size'] === -1) { |
|
| 1294 | - $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED); |
|
| 1295 | - if (!$storage->file_exists($internalPath)) { |
|
| 1296 | - $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); |
|
| 1297 | - return false; |
|
| 1298 | - } |
|
| 1299 | - $scanner = $storage->getScanner($internalPath); |
|
| 1300 | - $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); |
|
| 1301 | - $data = $cache->get($internalPath); |
|
| 1302 | - $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); |
|
| 1303 | - } else if (!Cache\Scanner::isPartialFile($internalPath) && $watcher->needsUpdate($internalPath, $data)) { |
|
| 1304 | - $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED); |
|
| 1305 | - $watcher->update($internalPath, $data); |
|
| 1306 | - $storage->getPropagator()->propagateChange($internalPath, time()); |
|
| 1307 | - $data = $cache->get($internalPath); |
|
| 1308 | - $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); |
|
| 1309 | - } |
|
| 1310 | - } catch (LockedException $e) { |
|
| 1311 | - // if the file is locked we just use the old cache info |
|
| 1312 | - } |
|
| 1313 | - |
|
| 1314 | - return $data; |
|
| 1315 | - } |
|
| 1316 | - |
|
| 1317 | - /** |
|
| 1318 | - * get the filesystem info |
|
| 1319 | - * |
|
| 1320 | - * @param string $path |
|
| 1321 | - * @param boolean|string $includeMountPoints true to add mountpoint sizes, |
|
| 1322 | - * 'ext' to add only ext storage mount point sizes. Defaults to true. |
|
| 1323 | - * defaults to true |
|
| 1324 | - * @return \OC\Files\FileInfo|false False if file does not exist |
|
| 1325 | - */ |
|
| 1326 | - public function getFileInfo($path, $includeMountPoints = true) { |
|
| 1327 | - $this->assertPathLength($path); |
|
| 1328 | - if (!Filesystem::isValidPath($path)) { |
|
| 1329 | - return false; |
|
| 1330 | - } |
|
| 1331 | - if (Cache\Scanner::isPartialFile($path)) { |
|
| 1332 | - return $this->getPartFileInfo($path); |
|
| 1333 | - } |
|
| 1334 | - $relativePath = $path; |
|
| 1335 | - $path = Filesystem::normalizePath($this->fakeRoot . '/' . $path); |
|
| 1336 | - |
|
| 1337 | - $mount = Filesystem::getMountManager()->find($path); |
|
| 1338 | - $storage = $mount->getStorage(); |
|
| 1339 | - $internalPath = $mount->getInternalPath($path); |
|
| 1340 | - if ($storage) { |
|
| 1341 | - $data = $this->getCacheEntry($storage, $internalPath, $relativePath); |
|
| 1342 | - |
|
| 1343 | - if (!$data instanceof ICacheEntry) { |
|
| 1344 | - return false; |
|
| 1345 | - } |
|
| 1346 | - |
|
| 1347 | - if ($mount instanceof MoveableMount && $internalPath === '') { |
|
| 1348 | - $data['permissions'] |= \OCP\Constants::PERMISSION_DELETE; |
|
| 1349 | - } |
|
| 1350 | - |
|
| 1351 | - $owner = $this->getUserObjectForOwner($storage->getOwner($internalPath)); |
|
| 1352 | - $info = new FileInfo($path, $storage, $internalPath, $data, $mount, $owner); |
|
| 1353 | - |
|
| 1354 | - if ($data and isset($data['fileid'])) { |
|
| 1355 | - if ($includeMountPoints and $data['mimetype'] === 'httpd/unix-directory') { |
|
| 1356 | - //add the sizes of other mount points to the folder |
|
| 1357 | - $extOnly = ($includeMountPoints === 'ext'); |
|
| 1358 | - $mounts = Filesystem::getMountManager()->findIn($path); |
|
| 1359 | - $info->setSubMounts(array_filter($mounts, function (IMountPoint $mount) use ($extOnly) { |
|
| 1360 | - $subStorage = $mount->getStorage(); |
|
| 1361 | - return !($extOnly && $subStorage instanceof \OCA\Files_Sharing\SharedStorage); |
|
| 1362 | - })); |
|
| 1363 | - } |
|
| 1364 | - } |
|
| 1365 | - |
|
| 1366 | - return $info; |
|
| 1367 | - } |
|
| 1368 | - |
|
| 1369 | - return false; |
|
| 1370 | - } |
|
| 1371 | - |
|
| 1372 | - /** |
|
| 1373 | - * get the content of a directory |
|
| 1374 | - * |
|
| 1375 | - * @param string $directory path under datadirectory |
|
| 1376 | - * @param string $mimetype_filter limit returned content to this mimetype or mimepart |
|
| 1377 | - * @return FileInfo[] |
|
| 1378 | - */ |
|
| 1379 | - public function getDirectoryContent($directory, $mimetype_filter = '') { |
|
| 1380 | - $this->assertPathLength($directory); |
|
| 1381 | - if (!Filesystem::isValidPath($directory)) { |
|
| 1382 | - return []; |
|
| 1383 | - } |
|
| 1384 | - $path = $this->getAbsolutePath($directory); |
|
| 1385 | - $path = Filesystem::normalizePath($path); |
|
| 1386 | - $mount = $this->getMount($directory); |
|
| 1387 | - $storage = $mount->getStorage(); |
|
| 1388 | - $internalPath = $mount->getInternalPath($path); |
|
| 1389 | - if ($storage) { |
|
| 1390 | - $cache = $storage->getCache($internalPath); |
|
| 1391 | - $user = \OC_User::getUser(); |
|
| 1392 | - |
|
| 1393 | - $data = $this->getCacheEntry($storage, $internalPath, $directory); |
|
| 1394 | - |
|
| 1395 | - if (!$data instanceof ICacheEntry || !isset($data['fileid']) || !($data->getPermissions() && Constants::PERMISSION_READ)) { |
|
| 1396 | - return []; |
|
| 1397 | - } |
|
| 1398 | - |
|
| 1399 | - $folderId = $data['fileid']; |
|
| 1400 | - $contents = $cache->getFolderContentsById($folderId); //TODO: mimetype_filter |
|
| 1401 | - |
|
| 1402 | - $sharingDisabled = \OCP\Util::isSharingDisabledForUser(); |
|
| 1403 | - /** |
|
| 1404 | - * @var \OC\Files\FileInfo[] $files |
|
| 1405 | - */ |
|
| 1406 | - $files = array_map(function (ICacheEntry $content) use ($path, $storage, $mount, $sharingDisabled) { |
|
| 1407 | - if ($sharingDisabled) { |
|
| 1408 | - $content['permissions'] = $content['permissions'] & ~\OCP\Constants::PERMISSION_SHARE; |
|
| 1409 | - } |
|
| 1410 | - $owner = $this->getUserObjectForOwner($storage->getOwner($content['path'])); |
|
| 1411 | - return new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content, $mount, $owner); |
|
| 1412 | - }, $contents); |
|
| 1413 | - |
|
| 1414 | - //add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders |
|
| 1415 | - $mounts = Filesystem::getMountManager()->findIn($path); |
|
| 1416 | - $dirLength = strlen($path); |
|
| 1417 | - foreach ($mounts as $mount) { |
|
| 1418 | - $mountPoint = $mount->getMountPoint(); |
|
| 1419 | - $subStorage = $mount->getStorage(); |
|
| 1420 | - if ($subStorage) { |
|
| 1421 | - $subCache = $subStorage->getCache(''); |
|
| 1422 | - |
|
| 1423 | - $rootEntry = $subCache->get(''); |
|
| 1424 | - if (!$rootEntry) { |
|
| 1425 | - $subScanner = $subStorage->getScanner(''); |
|
| 1426 | - try { |
|
| 1427 | - $subScanner->scanFile(''); |
|
| 1428 | - } catch (\OCP\Files\StorageNotAvailableException $e) { |
|
| 1429 | - continue; |
|
| 1430 | - } catch (\OCP\Files\StorageInvalidException $e) { |
|
| 1431 | - continue; |
|
| 1432 | - } catch (\Exception $e) { |
|
| 1433 | - // sometimes when the storage is not available it can be any exception |
|
| 1434 | - \OCP\Util::writeLog( |
|
| 1435 | - 'core', |
|
| 1436 | - 'Exception while scanning storage "' . $subStorage->getId() . '": ' . |
|
| 1437 | - get_class($e) . ': ' . $e->getMessage(), |
|
| 1438 | - \OCP\Util::ERROR |
|
| 1439 | - ); |
|
| 1440 | - continue; |
|
| 1441 | - } |
|
| 1442 | - $rootEntry = $subCache->get(''); |
|
| 1443 | - } |
|
| 1444 | - |
|
| 1445 | - if ($rootEntry && ($rootEntry->getPermissions() && Constants::PERMISSION_READ)) { |
|
| 1446 | - $relativePath = trim(substr($mountPoint, $dirLength), '/'); |
|
| 1447 | - if ($pos = strpos($relativePath, '/')) { |
|
| 1448 | - //mountpoint inside subfolder add size to the correct folder |
|
| 1449 | - $entryName = substr($relativePath, 0, $pos); |
|
| 1450 | - foreach ($files as &$entry) { |
|
| 1451 | - if ($entry->getName() === $entryName) { |
|
| 1452 | - $entry->addSubEntry($rootEntry, $mountPoint); |
|
| 1453 | - } |
|
| 1454 | - } |
|
| 1455 | - } else { //mountpoint in this folder, add an entry for it |
|
| 1456 | - $rootEntry['name'] = $relativePath; |
|
| 1457 | - $rootEntry['type'] = $rootEntry['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file'; |
|
| 1458 | - $permissions = $rootEntry['permissions']; |
|
| 1459 | - // do not allow renaming/deleting the mount point if they are not shared files/folders |
|
| 1460 | - // for shared files/folders we use the permissions given by the owner |
|
| 1461 | - if ($mount instanceof MoveableMount) { |
|
| 1462 | - $rootEntry['permissions'] = $permissions | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE; |
|
| 1463 | - } else { |
|
| 1464 | - $rootEntry['permissions'] = $permissions & (\OCP\Constants::PERMISSION_ALL - (\OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE)); |
|
| 1465 | - } |
|
| 1466 | - |
|
| 1467 | - //remove any existing entry with the same name |
|
| 1468 | - foreach ($files as $i => $file) { |
|
| 1469 | - if ($file['name'] === $rootEntry['name']) { |
|
| 1470 | - unset($files[$i]); |
|
| 1471 | - break; |
|
| 1472 | - } |
|
| 1473 | - } |
|
| 1474 | - $rootEntry['path'] = substr(Filesystem::normalizePath($path . '/' . $rootEntry['name']), strlen($user) + 2); // full path without /$user/ |
|
| 1475 | - |
|
| 1476 | - // if sharing was disabled for the user we remove the share permissions |
|
| 1477 | - if (\OCP\Util::isSharingDisabledForUser()) { |
|
| 1478 | - $rootEntry['permissions'] = $rootEntry['permissions'] & ~\OCP\Constants::PERMISSION_SHARE; |
|
| 1479 | - } |
|
| 1480 | - |
|
| 1481 | - $owner = $this->getUserObjectForOwner($subStorage->getOwner('')); |
|
| 1482 | - $files[] = new FileInfo($path . '/' . $rootEntry['name'], $subStorage, '', $rootEntry, $mount, $owner); |
|
| 1483 | - } |
|
| 1484 | - } |
|
| 1485 | - } |
|
| 1486 | - } |
|
| 1487 | - |
|
| 1488 | - if ($mimetype_filter) { |
|
| 1489 | - $files = array_filter($files, function (FileInfo $file) use ($mimetype_filter) { |
|
| 1490 | - if (strpos($mimetype_filter, '/')) { |
|
| 1491 | - return $file->getMimetype() === $mimetype_filter; |
|
| 1492 | - } else { |
|
| 1493 | - return $file->getMimePart() === $mimetype_filter; |
|
| 1494 | - } |
|
| 1495 | - }); |
|
| 1496 | - } |
|
| 1497 | - |
|
| 1498 | - return $files; |
|
| 1499 | - } else { |
|
| 1500 | - return []; |
|
| 1501 | - } |
|
| 1502 | - } |
|
| 1503 | - |
|
| 1504 | - /** |
|
| 1505 | - * change file metadata |
|
| 1506 | - * |
|
| 1507 | - * @param string $path |
|
| 1508 | - * @param array|\OCP\Files\FileInfo $data |
|
| 1509 | - * @return int |
|
| 1510 | - * |
|
| 1511 | - * returns the fileid of the updated file |
|
| 1512 | - */ |
|
| 1513 | - public function putFileInfo($path, $data) { |
|
| 1514 | - $this->assertPathLength($path); |
|
| 1515 | - if ($data instanceof FileInfo) { |
|
| 1516 | - $data = $data->getData(); |
|
| 1517 | - } |
|
| 1518 | - $path = Filesystem::normalizePath($this->fakeRoot . '/' . $path); |
|
| 1519 | - /** |
|
| 1520 | - * @var \OC\Files\Storage\Storage $storage |
|
| 1521 | - * @var string $internalPath |
|
| 1522 | - */ |
|
| 1523 | - list($storage, $internalPath) = Filesystem::resolvePath($path); |
|
| 1524 | - if ($storage) { |
|
| 1525 | - $cache = $storage->getCache($path); |
|
| 1526 | - |
|
| 1527 | - if (!$cache->inCache($internalPath)) { |
|
| 1528 | - $scanner = $storage->getScanner($internalPath); |
|
| 1529 | - $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); |
|
| 1530 | - } |
|
| 1531 | - |
|
| 1532 | - return $cache->put($internalPath, $data); |
|
| 1533 | - } else { |
|
| 1534 | - return -1; |
|
| 1535 | - } |
|
| 1536 | - } |
|
| 1537 | - |
|
| 1538 | - /** |
|
| 1539 | - * search for files with the name matching $query |
|
| 1540 | - * |
|
| 1541 | - * @param string $query |
|
| 1542 | - * @return FileInfo[] |
|
| 1543 | - */ |
|
| 1544 | - public function search($query) { |
|
| 1545 | - return $this->searchCommon('search', array('%' . $query . '%')); |
|
| 1546 | - } |
|
| 1547 | - |
|
| 1548 | - /** |
|
| 1549 | - * search for files with the name matching $query |
|
| 1550 | - * |
|
| 1551 | - * @param string $query |
|
| 1552 | - * @return FileInfo[] |
|
| 1553 | - */ |
|
| 1554 | - public function searchRaw($query) { |
|
| 1555 | - return $this->searchCommon('search', array($query)); |
|
| 1556 | - } |
|
| 1557 | - |
|
| 1558 | - /** |
|
| 1559 | - * search for files by mimetype |
|
| 1560 | - * |
|
| 1561 | - * @param string $mimetype |
|
| 1562 | - * @return FileInfo[] |
|
| 1563 | - */ |
|
| 1564 | - public function searchByMime($mimetype) { |
|
| 1565 | - return $this->searchCommon('searchByMime', array($mimetype)); |
|
| 1566 | - } |
|
| 1567 | - |
|
| 1568 | - /** |
|
| 1569 | - * search for files by tag |
|
| 1570 | - * |
|
| 1571 | - * @param string|int $tag name or tag id |
|
| 1572 | - * @param string $userId owner of the tags |
|
| 1573 | - * @return FileInfo[] |
|
| 1574 | - */ |
|
| 1575 | - public function searchByTag($tag, $userId) { |
|
| 1576 | - return $this->searchCommon('searchByTag', array($tag, $userId)); |
|
| 1577 | - } |
|
| 1578 | - |
|
| 1579 | - /** |
|
| 1580 | - * @param string $method cache method |
|
| 1581 | - * @param array $args |
|
| 1582 | - * @return FileInfo[] |
|
| 1583 | - */ |
|
| 1584 | - private function searchCommon($method, $args) { |
|
| 1585 | - $files = array(); |
|
| 1586 | - $rootLength = strlen($this->fakeRoot); |
|
| 1587 | - |
|
| 1588 | - $mount = $this->getMount(''); |
|
| 1589 | - $mountPoint = $mount->getMountPoint(); |
|
| 1590 | - $storage = $mount->getStorage(); |
|
| 1591 | - if ($storage) { |
|
| 1592 | - $cache = $storage->getCache(''); |
|
| 1593 | - |
|
| 1594 | - $results = call_user_func_array(array($cache, $method), $args); |
|
| 1595 | - foreach ($results as $result) { |
|
| 1596 | - if (substr($mountPoint . $result['path'], 0, $rootLength + 1) === $this->fakeRoot . '/') { |
|
| 1597 | - $internalPath = $result['path']; |
|
| 1598 | - $path = $mountPoint . $result['path']; |
|
| 1599 | - $result['path'] = substr($mountPoint . $result['path'], $rootLength); |
|
| 1600 | - $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath)); |
|
| 1601 | - $files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner); |
|
| 1602 | - } |
|
| 1603 | - } |
|
| 1604 | - |
|
| 1605 | - $mounts = Filesystem::getMountManager()->findIn($this->fakeRoot); |
|
| 1606 | - foreach ($mounts as $mount) { |
|
| 1607 | - $mountPoint = $mount->getMountPoint(); |
|
| 1608 | - $storage = $mount->getStorage(); |
|
| 1609 | - if ($storage) { |
|
| 1610 | - $cache = $storage->getCache(''); |
|
| 1611 | - |
|
| 1612 | - $relativeMountPoint = substr($mountPoint, $rootLength); |
|
| 1613 | - $results = call_user_func_array(array($cache, $method), $args); |
|
| 1614 | - if ($results) { |
|
| 1615 | - foreach ($results as $result) { |
|
| 1616 | - $internalPath = $result['path']; |
|
| 1617 | - $result['path'] = rtrim($relativeMountPoint . $result['path'], '/'); |
|
| 1618 | - $path = rtrim($mountPoint . $internalPath, '/'); |
|
| 1619 | - $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath)); |
|
| 1620 | - $files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner); |
|
| 1621 | - } |
|
| 1622 | - } |
|
| 1623 | - } |
|
| 1624 | - } |
|
| 1625 | - } |
|
| 1626 | - return $files; |
|
| 1627 | - } |
|
| 1628 | - |
|
| 1629 | - /** |
|
| 1630 | - * Get the owner for a file or folder |
|
| 1631 | - * |
|
| 1632 | - * @param string $path |
|
| 1633 | - * @return string the user id of the owner |
|
| 1634 | - * @throws NotFoundException |
|
| 1635 | - */ |
|
| 1636 | - public function getOwner($path) { |
|
| 1637 | - $info = $this->getFileInfo($path); |
|
| 1638 | - if (!$info) { |
|
| 1639 | - throw new NotFoundException($path . ' not found while trying to get owner'); |
|
| 1640 | - } |
|
| 1641 | - return $info->getOwner()->getUID(); |
|
| 1642 | - } |
|
| 1643 | - |
|
| 1644 | - /** |
|
| 1645 | - * get the ETag for a file or folder |
|
| 1646 | - * |
|
| 1647 | - * @param string $path |
|
| 1648 | - * @return string |
|
| 1649 | - */ |
|
| 1650 | - public function getETag($path) { |
|
| 1651 | - /** |
|
| 1652 | - * @var Storage\Storage $storage |
|
| 1653 | - * @var string $internalPath |
|
| 1654 | - */ |
|
| 1655 | - list($storage, $internalPath) = $this->resolvePath($path); |
|
| 1656 | - if ($storage) { |
|
| 1657 | - return $storage->getETag($internalPath); |
|
| 1658 | - } else { |
|
| 1659 | - return null; |
|
| 1660 | - } |
|
| 1661 | - } |
|
| 1662 | - |
|
| 1663 | - /** |
|
| 1664 | - * Get the path of a file by id, relative to the view |
|
| 1665 | - * |
|
| 1666 | - * Note that the resulting path is not guarantied to be unique for the id, multiple paths can point to the same file |
|
| 1667 | - * |
|
| 1668 | - * @param int $id |
|
| 1669 | - * @throws NotFoundException |
|
| 1670 | - * @return string |
|
| 1671 | - */ |
|
| 1672 | - public function getPath($id) { |
|
| 1673 | - $id = (int)$id; |
|
| 1674 | - $manager = Filesystem::getMountManager(); |
|
| 1675 | - $mounts = $manager->findIn($this->fakeRoot); |
|
| 1676 | - $mounts[] = $manager->find($this->fakeRoot); |
|
| 1677 | - // reverse the array so we start with the storage this view is in |
|
| 1678 | - // which is the most likely to contain the file we're looking for |
|
| 1679 | - $mounts = array_reverse($mounts); |
|
| 1680 | - foreach ($mounts as $mount) { |
|
| 1681 | - /** |
|
| 1682 | - * @var \OC\Files\Mount\MountPoint $mount |
|
| 1683 | - */ |
|
| 1684 | - if ($mount->getStorage()) { |
|
| 1685 | - $cache = $mount->getStorage()->getCache(); |
|
| 1686 | - $internalPath = $cache->getPathById($id); |
|
| 1687 | - if (is_string($internalPath)) { |
|
| 1688 | - $fullPath = $mount->getMountPoint() . $internalPath; |
|
| 1689 | - if (!is_null($path = $this->getRelativePath($fullPath))) { |
|
| 1690 | - return $path; |
|
| 1691 | - } |
|
| 1692 | - } |
|
| 1693 | - } |
|
| 1694 | - } |
|
| 1695 | - throw new NotFoundException(sprintf('File with id "%s" has not been found.', $id)); |
|
| 1696 | - } |
|
| 1697 | - |
|
| 1698 | - /** |
|
| 1699 | - * @param string $path |
|
| 1700 | - * @throws InvalidPathException |
|
| 1701 | - */ |
|
| 1702 | - private function assertPathLength($path) { |
|
| 1703 | - $maxLen = min(PHP_MAXPATHLEN, 4000); |
|
| 1704 | - // Check for the string length - performed using isset() instead of strlen() |
|
| 1705 | - // because isset() is about 5x-40x faster. |
|
| 1706 | - if (isset($path[$maxLen])) { |
|
| 1707 | - $pathLen = strlen($path); |
|
| 1708 | - throw new \OCP\Files\InvalidPathException("Path length($pathLen) exceeds max path length($maxLen): $path"); |
|
| 1709 | - } |
|
| 1710 | - } |
|
| 1711 | - |
|
| 1712 | - /** |
|
| 1713 | - * check if it is allowed to move a mount point to a given target. |
|
| 1714 | - * It is not allowed to move a mount point into a different mount point or |
|
| 1715 | - * into an already shared folder |
|
| 1716 | - * |
|
| 1717 | - * @param string $target path |
|
| 1718 | - * @return boolean |
|
| 1719 | - */ |
|
| 1720 | - private function isTargetAllowed($target) { |
|
| 1721 | - |
|
| 1722 | - list($targetStorage, $targetInternalPath) = \OC\Files\Filesystem::resolvePath($target); |
|
| 1723 | - if (!$targetStorage->instanceOfStorage('\OCP\Files\IHomeStorage')) { |
|
| 1724 | - \OCP\Util::writeLog('files', |
|
| 1725 | - 'It is not allowed to move one mount point into another one', |
|
| 1726 | - \OCP\Util::DEBUG); |
|
| 1727 | - return false; |
|
| 1728 | - } |
|
| 1729 | - |
|
| 1730 | - // note: cannot use the view because the target is already locked |
|
| 1731 | - $fileId = (int)$targetStorage->getCache()->getId($targetInternalPath); |
|
| 1732 | - if ($fileId === -1) { |
|
| 1733 | - // target might not exist, need to check parent instead |
|
| 1734 | - $fileId = (int)$targetStorage->getCache()->getId(dirname($targetInternalPath)); |
|
| 1735 | - } |
|
| 1736 | - |
|
| 1737 | - // check if any of the parents were shared by the current owner (include collections) |
|
| 1738 | - $shares = \OCP\Share::getItemShared( |
|
| 1739 | - 'folder', |
|
| 1740 | - $fileId, |
|
| 1741 | - \OCP\Share::FORMAT_NONE, |
|
| 1742 | - null, |
|
| 1743 | - true |
|
| 1744 | - ); |
|
| 1745 | - |
|
| 1746 | - if (count($shares) > 0) { |
|
| 1747 | - \OCP\Util::writeLog('files', |
|
| 1748 | - 'It is not allowed to move one mount point into a shared folder', |
|
| 1749 | - \OCP\Util::DEBUG); |
|
| 1750 | - return false; |
|
| 1751 | - } |
|
| 1752 | - |
|
| 1753 | - return true; |
|
| 1754 | - } |
|
| 1755 | - |
|
| 1756 | - /** |
|
| 1757 | - * Get a fileinfo object for files that are ignored in the cache (part files) |
|
| 1758 | - * |
|
| 1759 | - * @param string $path |
|
| 1760 | - * @return \OCP\Files\FileInfo |
|
| 1761 | - */ |
|
| 1762 | - private function getPartFileInfo($path) { |
|
| 1763 | - $mount = $this->getMount($path); |
|
| 1764 | - $storage = $mount->getStorage(); |
|
| 1765 | - $internalPath = $mount->getInternalPath($this->getAbsolutePath($path)); |
|
| 1766 | - $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath)); |
|
| 1767 | - return new FileInfo( |
|
| 1768 | - $this->getAbsolutePath($path), |
|
| 1769 | - $storage, |
|
| 1770 | - $internalPath, |
|
| 1771 | - [ |
|
| 1772 | - 'fileid' => null, |
|
| 1773 | - 'mimetype' => $storage->getMimeType($internalPath), |
|
| 1774 | - 'name' => basename($path), |
|
| 1775 | - 'etag' => null, |
|
| 1776 | - 'size' => $storage->filesize($internalPath), |
|
| 1777 | - 'mtime' => $storage->filemtime($internalPath), |
|
| 1778 | - 'encrypted' => false, |
|
| 1779 | - 'permissions' => \OCP\Constants::PERMISSION_ALL |
|
| 1780 | - ], |
|
| 1781 | - $mount, |
|
| 1782 | - $owner |
|
| 1783 | - ); |
|
| 1784 | - } |
|
| 1785 | - |
|
| 1786 | - /** |
|
| 1787 | - * @param string $path |
|
| 1788 | - * @param string $fileName |
|
| 1789 | - * @throws InvalidPathException |
|
| 1790 | - */ |
|
| 1791 | - public function verifyPath($path, $fileName) { |
|
| 1792 | - try { |
|
| 1793 | - /** @type \OCP\Files\Storage $storage */ |
|
| 1794 | - list($storage, $internalPath) = $this->resolvePath($path); |
|
| 1795 | - $storage->verifyPath($internalPath, $fileName); |
|
| 1796 | - } catch (ReservedWordException $ex) { |
|
| 1797 | - $l = \OC::$server->getL10N('lib'); |
|
| 1798 | - throw new InvalidPathException($l->t('File name is a reserved word')); |
|
| 1799 | - } catch (InvalidCharacterInPathException $ex) { |
|
| 1800 | - $l = \OC::$server->getL10N('lib'); |
|
| 1801 | - throw new InvalidPathException($l->t('File name contains at least one invalid character')); |
|
| 1802 | - } catch (FileNameTooLongException $ex) { |
|
| 1803 | - $l = \OC::$server->getL10N('lib'); |
|
| 1804 | - throw new InvalidPathException($l->t('File name is too long')); |
|
| 1805 | - } catch (InvalidDirectoryException $ex) { |
|
| 1806 | - $l = \OC::$server->getL10N('lib'); |
|
| 1807 | - throw new InvalidPathException($l->t('Dot files are not allowed')); |
|
| 1808 | - } catch (EmptyFileNameException $ex) { |
|
| 1809 | - $l = \OC::$server->getL10N('lib'); |
|
| 1810 | - throw new InvalidPathException($l->t('Empty filename is not allowed')); |
|
| 1811 | - } |
|
| 1812 | - } |
|
| 1813 | - |
|
| 1814 | - /** |
|
| 1815 | - * get all parent folders of $path |
|
| 1816 | - * |
|
| 1817 | - * @param string $path |
|
| 1818 | - * @return string[] |
|
| 1819 | - */ |
|
| 1820 | - private function getParents($path) { |
|
| 1821 | - $path = trim($path, '/'); |
|
| 1822 | - if (!$path) { |
|
| 1823 | - return []; |
|
| 1824 | - } |
|
| 1825 | - |
|
| 1826 | - $parts = explode('/', $path); |
|
| 1827 | - |
|
| 1828 | - // remove the single file |
|
| 1829 | - array_pop($parts); |
|
| 1830 | - $result = array('/'); |
|
| 1831 | - $resultPath = ''; |
|
| 1832 | - foreach ($parts as $part) { |
|
| 1833 | - if ($part) { |
|
| 1834 | - $resultPath .= '/' . $part; |
|
| 1835 | - $result[] = $resultPath; |
|
| 1836 | - } |
|
| 1837 | - } |
|
| 1838 | - return $result; |
|
| 1839 | - } |
|
| 1840 | - |
|
| 1841 | - /** |
|
| 1842 | - * Returns the mount point for which to lock |
|
| 1843 | - * |
|
| 1844 | - * @param string $absolutePath absolute path |
|
| 1845 | - * @param bool $useParentMount true to return parent mount instead of whatever |
|
| 1846 | - * is mounted directly on the given path, false otherwise |
|
| 1847 | - * @return \OC\Files\Mount\MountPoint mount point for which to apply locks |
|
| 1848 | - */ |
|
| 1849 | - private function getMountForLock($absolutePath, $useParentMount = false) { |
|
| 1850 | - $results = []; |
|
| 1851 | - $mount = Filesystem::getMountManager()->find($absolutePath); |
|
| 1852 | - if (!$mount) { |
|
| 1853 | - return $results; |
|
| 1854 | - } |
|
| 1855 | - |
|
| 1856 | - if ($useParentMount) { |
|
| 1857 | - // find out if something is mounted directly on the path |
|
| 1858 | - $internalPath = $mount->getInternalPath($absolutePath); |
|
| 1859 | - if ($internalPath === '') { |
|
| 1860 | - // resolve the parent mount instead |
|
| 1861 | - $mount = Filesystem::getMountManager()->find(dirname($absolutePath)); |
|
| 1862 | - } |
|
| 1863 | - } |
|
| 1864 | - |
|
| 1865 | - return $mount; |
|
| 1866 | - } |
|
| 1867 | - |
|
| 1868 | - /** |
|
| 1869 | - * Lock the given path |
|
| 1870 | - * |
|
| 1871 | - * @param string $path the path of the file to lock, relative to the view |
|
| 1872 | - * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE |
|
| 1873 | - * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage |
|
| 1874 | - * |
|
| 1875 | - * @return bool False if the path is excluded from locking, true otherwise |
|
| 1876 | - * @throws \OCP\Lock\LockedException if the path is already locked |
|
| 1877 | - */ |
|
| 1878 | - private function lockPath($path, $type, $lockMountPoint = false) { |
|
| 1879 | - $absolutePath = $this->getAbsolutePath($path); |
|
| 1880 | - $absolutePath = Filesystem::normalizePath($absolutePath); |
|
| 1881 | - if (!$this->shouldLockFile($absolutePath)) { |
|
| 1882 | - return false; |
|
| 1883 | - } |
|
| 1884 | - |
|
| 1885 | - $mount = $this->getMountForLock($absolutePath, $lockMountPoint); |
|
| 1886 | - if ($mount) { |
|
| 1887 | - try { |
|
| 1888 | - $storage = $mount->getStorage(); |
|
| 1889 | - if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) { |
|
| 1890 | - $storage->acquireLock( |
|
| 1891 | - $mount->getInternalPath($absolutePath), |
|
| 1892 | - $type, |
|
| 1893 | - $this->lockingProvider |
|
| 1894 | - ); |
|
| 1895 | - } |
|
| 1896 | - } catch (\OCP\Lock\LockedException $e) { |
|
| 1897 | - // rethrow with the a human-readable path |
|
| 1898 | - throw new \OCP\Lock\LockedException( |
|
| 1899 | - $this->getPathRelativeToFiles($absolutePath), |
|
| 1900 | - $e |
|
| 1901 | - ); |
|
| 1902 | - } |
|
| 1903 | - } |
|
| 1904 | - |
|
| 1905 | - return true; |
|
| 1906 | - } |
|
| 1907 | - |
|
| 1908 | - /** |
|
| 1909 | - * Change the lock type |
|
| 1910 | - * |
|
| 1911 | - * @param string $path the path of the file to lock, relative to the view |
|
| 1912 | - * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE |
|
| 1913 | - * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage |
|
| 1914 | - * |
|
| 1915 | - * @return bool False if the path is excluded from locking, true otherwise |
|
| 1916 | - * @throws \OCP\Lock\LockedException if the path is already locked |
|
| 1917 | - */ |
|
| 1918 | - public function changeLock($path, $type, $lockMountPoint = false) { |
|
| 1919 | - $path = Filesystem::normalizePath($path); |
|
| 1920 | - $absolutePath = $this->getAbsolutePath($path); |
|
| 1921 | - $absolutePath = Filesystem::normalizePath($absolutePath); |
|
| 1922 | - if (!$this->shouldLockFile($absolutePath)) { |
|
| 1923 | - return false; |
|
| 1924 | - } |
|
| 1925 | - |
|
| 1926 | - $mount = $this->getMountForLock($absolutePath, $lockMountPoint); |
|
| 1927 | - if ($mount) { |
|
| 1928 | - try { |
|
| 1929 | - $storage = $mount->getStorage(); |
|
| 1930 | - if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) { |
|
| 1931 | - $storage->changeLock( |
|
| 1932 | - $mount->getInternalPath($absolutePath), |
|
| 1933 | - $type, |
|
| 1934 | - $this->lockingProvider |
|
| 1935 | - ); |
|
| 1936 | - } |
|
| 1937 | - } catch (\OCP\Lock\LockedException $e) { |
|
| 1938 | - // rethrow with the a human-readable path |
|
| 1939 | - throw new \OCP\Lock\LockedException( |
|
| 1940 | - $this->getPathRelativeToFiles($absolutePath), |
|
| 1941 | - $e |
|
| 1942 | - ); |
|
| 1943 | - } |
|
| 1944 | - } |
|
| 1945 | - |
|
| 1946 | - return true; |
|
| 1947 | - } |
|
| 1948 | - |
|
| 1949 | - /** |
|
| 1950 | - * Unlock the given path |
|
| 1951 | - * |
|
| 1952 | - * @param string $path the path of the file to unlock, relative to the view |
|
| 1953 | - * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE |
|
| 1954 | - * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage |
|
| 1955 | - * |
|
| 1956 | - * @return bool False if the path is excluded from locking, true otherwise |
|
| 1957 | - */ |
|
| 1958 | - private function unlockPath($path, $type, $lockMountPoint = false) { |
|
| 1959 | - $absolutePath = $this->getAbsolutePath($path); |
|
| 1960 | - $absolutePath = Filesystem::normalizePath($absolutePath); |
|
| 1961 | - if (!$this->shouldLockFile($absolutePath)) { |
|
| 1962 | - return false; |
|
| 1963 | - } |
|
| 1964 | - |
|
| 1965 | - $mount = $this->getMountForLock($absolutePath, $lockMountPoint); |
|
| 1966 | - if ($mount) { |
|
| 1967 | - $storage = $mount->getStorage(); |
|
| 1968 | - if ($storage && $storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) { |
|
| 1969 | - $storage->releaseLock( |
|
| 1970 | - $mount->getInternalPath($absolutePath), |
|
| 1971 | - $type, |
|
| 1972 | - $this->lockingProvider |
|
| 1973 | - ); |
|
| 1974 | - } |
|
| 1975 | - } |
|
| 1976 | - |
|
| 1977 | - return true; |
|
| 1978 | - } |
|
| 1979 | - |
|
| 1980 | - /** |
|
| 1981 | - * Lock a path and all its parents up to the root of the view |
|
| 1982 | - * |
|
| 1983 | - * @param string $path the path of the file to lock relative to the view |
|
| 1984 | - * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE |
|
| 1985 | - * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage |
|
| 1986 | - * |
|
| 1987 | - * @return bool False if the path is excluded from locking, true otherwise |
|
| 1988 | - */ |
|
| 1989 | - public function lockFile($path, $type, $lockMountPoint = false) { |
|
| 1990 | - $absolutePath = $this->getAbsolutePath($path); |
|
| 1991 | - $absolutePath = Filesystem::normalizePath($absolutePath); |
|
| 1992 | - if (!$this->shouldLockFile($absolutePath)) { |
|
| 1993 | - return false; |
|
| 1994 | - } |
|
| 1995 | - |
|
| 1996 | - $this->lockPath($path, $type, $lockMountPoint); |
|
| 1997 | - |
|
| 1998 | - $parents = $this->getParents($path); |
|
| 1999 | - foreach ($parents as $parent) { |
|
| 2000 | - $this->lockPath($parent, ILockingProvider::LOCK_SHARED); |
|
| 2001 | - } |
|
| 2002 | - |
|
| 2003 | - return true; |
|
| 2004 | - } |
|
| 2005 | - |
|
| 2006 | - /** |
|
| 2007 | - * Unlock a path and all its parents up to the root of the view |
|
| 2008 | - * |
|
| 2009 | - * @param string $path the path of the file to lock relative to the view |
|
| 2010 | - * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE |
|
| 2011 | - * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage |
|
| 2012 | - * |
|
| 2013 | - * @return bool False if the path is excluded from locking, true otherwise |
|
| 2014 | - */ |
|
| 2015 | - public function unlockFile($path, $type, $lockMountPoint = false) { |
|
| 2016 | - $absolutePath = $this->getAbsolutePath($path); |
|
| 2017 | - $absolutePath = Filesystem::normalizePath($absolutePath); |
|
| 2018 | - if (!$this->shouldLockFile($absolutePath)) { |
|
| 2019 | - return false; |
|
| 2020 | - } |
|
| 2021 | - |
|
| 2022 | - $this->unlockPath($path, $type, $lockMountPoint); |
|
| 2023 | - |
|
| 2024 | - $parents = $this->getParents($path); |
|
| 2025 | - foreach ($parents as $parent) { |
|
| 2026 | - $this->unlockPath($parent, ILockingProvider::LOCK_SHARED); |
|
| 2027 | - } |
|
| 2028 | - |
|
| 2029 | - return true; |
|
| 2030 | - } |
|
| 2031 | - |
|
| 2032 | - /** |
|
| 2033 | - * Only lock files in data/user/files/ |
|
| 2034 | - * |
|
| 2035 | - * @param string $path Absolute path to the file/folder we try to (un)lock |
|
| 2036 | - * @return bool |
|
| 2037 | - */ |
|
| 2038 | - protected function shouldLockFile($path) { |
|
| 2039 | - $path = Filesystem::normalizePath($path); |
|
| 2040 | - |
|
| 2041 | - $pathSegments = explode('/', $path); |
|
| 2042 | - if (isset($pathSegments[2])) { |
|
| 2043 | - // E.g.: /username/files/path-to-file |
|
| 2044 | - return ($pathSegments[2] === 'files') && (count($pathSegments) > 3); |
|
| 2045 | - } |
|
| 2046 | - |
|
| 2047 | - return true; |
|
| 2048 | - } |
|
| 2049 | - |
|
| 2050 | - /** |
|
| 2051 | - * Shortens the given absolute path to be relative to |
|
| 2052 | - * "$user/files". |
|
| 2053 | - * |
|
| 2054 | - * @param string $absolutePath absolute path which is under "files" |
|
| 2055 | - * |
|
| 2056 | - * @return string path relative to "files" with trimmed slashes or null |
|
| 2057 | - * if the path was NOT relative to files |
|
| 2058 | - * |
|
| 2059 | - * @throws \InvalidArgumentException if the given path was not under "files" |
|
| 2060 | - * @since 8.1.0 |
|
| 2061 | - */ |
|
| 2062 | - public function getPathRelativeToFiles($absolutePath) { |
|
| 2063 | - $path = Filesystem::normalizePath($absolutePath); |
|
| 2064 | - $parts = explode('/', trim($path, '/'), 3); |
|
| 2065 | - // "$user", "files", "path/to/dir" |
|
| 2066 | - if (!isset($parts[1]) || $parts[1] !== 'files') { |
|
| 2067 | - throw new \InvalidArgumentException('$absolutePath must be relative to "files"'); |
|
| 2068 | - } |
|
| 2069 | - if (isset($parts[2])) { |
|
| 2070 | - return $parts[2]; |
|
| 2071 | - } |
|
| 2072 | - return ''; |
|
| 2073 | - } |
|
| 2074 | - |
|
| 2075 | - /** |
|
| 2076 | - * @param string $filename |
|
| 2077 | - * @return array |
|
| 2078 | - * @throws \OC\User\NoUserException |
|
| 2079 | - * @throws NotFoundException |
|
| 2080 | - */ |
|
| 2081 | - public function getUidAndFilename($filename) { |
|
| 2082 | - $info = $this->getFileInfo($filename); |
|
| 2083 | - if (!$info instanceof \OCP\Files\FileInfo) { |
|
| 2084 | - throw new NotFoundException($this->getAbsolutePath($filename) . ' not found'); |
|
| 2085 | - } |
|
| 2086 | - $uid = $info->getOwner()->getUID(); |
|
| 2087 | - if ($uid != \OCP\User::getUser()) { |
|
| 2088 | - Filesystem::initMountPoints($uid); |
|
| 2089 | - $ownerView = new View('/' . $uid . '/files'); |
|
| 2090 | - try { |
|
| 2091 | - $filename = $ownerView->getPath($info['fileid']); |
|
| 2092 | - } catch (NotFoundException $e) { |
|
| 2093 | - throw new NotFoundException('File with id ' . $info['fileid'] . ' not found for user ' . $uid); |
|
| 2094 | - } |
|
| 2095 | - } |
|
| 2096 | - return [$uid, $filename]; |
|
| 2097 | - } |
|
| 2098 | - |
|
| 2099 | - /** |
|
| 2100 | - * Creates parent non-existing folders |
|
| 2101 | - * |
|
| 2102 | - * @param string $filePath |
|
| 2103 | - * @return bool |
|
| 2104 | - */ |
|
| 2105 | - private function createParentDirectories($filePath) { |
|
| 2106 | - $directoryParts = explode('/', $filePath); |
|
| 2107 | - $directoryParts = array_filter($directoryParts); |
|
| 2108 | - foreach ($directoryParts as $key => $part) { |
|
| 2109 | - $currentPathElements = array_slice($directoryParts, 0, $key); |
|
| 2110 | - $currentPath = '/' . implode('/', $currentPathElements); |
|
| 2111 | - if ($this->is_file($currentPath)) { |
|
| 2112 | - return false; |
|
| 2113 | - } |
|
| 2114 | - if (!$this->file_exists($currentPath)) { |
|
| 2115 | - $this->mkdir($currentPath); |
|
| 2116 | - } |
|
| 2117 | - } |
|
| 2118 | - |
|
| 2119 | - return true; |
|
| 2120 | - } |
|
| 87 | + /** @var string */ |
|
| 88 | + private $fakeRoot = ''; |
|
| 89 | + |
|
| 90 | + /** |
|
| 91 | + * @var \OCP\Lock\ILockingProvider |
|
| 92 | + */ |
|
| 93 | + private $lockingProvider; |
|
| 94 | + |
|
| 95 | + private $lockingEnabled; |
|
| 96 | + |
|
| 97 | + private $updaterEnabled = true; |
|
| 98 | + |
|
| 99 | + private $userManager; |
|
| 100 | + |
|
| 101 | + /** |
|
| 102 | + * @param string $root |
|
| 103 | + * @throws \Exception If $root contains an invalid path |
|
| 104 | + */ |
|
| 105 | + public function __construct($root = '') { |
|
| 106 | + if (is_null($root)) { |
|
| 107 | + throw new \InvalidArgumentException('Root can\'t be null'); |
|
| 108 | + } |
|
| 109 | + if (!Filesystem::isValidPath($root)) { |
|
| 110 | + throw new \Exception(); |
|
| 111 | + } |
|
| 112 | + |
|
| 113 | + $this->fakeRoot = $root; |
|
| 114 | + $this->lockingProvider = \OC::$server->getLockingProvider(); |
|
| 115 | + $this->lockingEnabled = !($this->lockingProvider instanceof \OC\Lock\NoopLockingProvider); |
|
| 116 | + $this->userManager = \OC::$server->getUserManager(); |
|
| 117 | + } |
|
| 118 | + |
|
| 119 | + public function getAbsolutePath($path = '/') { |
|
| 120 | + if ($path === null) { |
|
| 121 | + return null; |
|
| 122 | + } |
|
| 123 | + $this->assertPathLength($path); |
|
| 124 | + if ($path === '') { |
|
| 125 | + $path = '/'; |
|
| 126 | + } |
|
| 127 | + if ($path[0] !== '/') { |
|
| 128 | + $path = '/' . $path; |
|
| 129 | + } |
|
| 130 | + return $this->fakeRoot . $path; |
|
| 131 | + } |
|
| 132 | + |
|
| 133 | + /** |
|
| 134 | + * change the root to a fake root |
|
| 135 | + * |
|
| 136 | + * @param string $fakeRoot |
|
| 137 | + * @return boolean|null |
|
| 138 | + */ |
|
| 139 | + public function chroot($fakeRoot) { |
|
| 140 | + if (!$fakeRoot == '') { |
|
| 141 | + if ($fakeRoot[0] !== '/') { |
|
| 142 | + $fakeRoot = '/' . $fakeRoot; |
|
| 143 | + } |
|
| 144 | + } |
|
| 145 | + $this->fakeRoot = $fakeRoot; |
|
| 146 | + } |
|
| 147 | + |
|
| 148 | + /** |
|
| 149 | + * get the fake root |
|
| 150 | + * |
|
| 151 | + * @return string |
|
| 152 | + */ |
|
| 153 | + public function getRoot() { |
|
| 154 | + return $this->fakeRoot; |
|
| 155 | + } |
|
| 156 | + |
|
| 157 | + /** |
|
| 158 | + * get path relative to the root of the view |
|
| 159 | + * |
|
| 160 | + * @param string $path |
|
| 161 | + * @return string |
|
| 162 | + */ |
|
| 163 | + public function getRelativePath($path) { |
|
| 164 | + $this->assertPathLength($path); |
|
| 165 | + if ($this->fakeRoot == '') { |
|
| 166 | + return $path; |
|
| 167 | + } |
|
| 168 | + |
|
| 169 | + if (rtrim($path, '/') === rtrim($this->fakeRoot, '/')) { |
|
| 170 | + return '/'; |
|
| 171 | + } |
|
| 172 | + |
|
| 173 | + // missing slashes can cause wrong matches! |
|
| 174 | + $root = rtrim($this->fakeRoot, '/') . '/'; |
|
| 175 | + |
|
| 176 | + if (strpos($path, $root) !== 0) { |
|
| 177 | + return null; |
|
| 178 | + } else { |
|
| 179 | + $path = substr($path, strlen($this->fakeRoot)); |
|
| 180 | + if (strlen($path) === 0) { |
|
| 181 | + return '/'; |
|
| 182 | + } else { |
|
| 183 | + return $path; |
|
| 184 | + } |
|
| 185 | + } |
|
| 186 | + } |
|
| 187 | + |
|
| 188 | + /** |
|
| 189 | + * get the mountpoint of the storage object for a path |
|
| 190 | + * ( note: because a storage is not always mounted inside the fakeroot, the |
|
| 191 | + * returned mountpoint is relative to the absolute root of the filesystem |
|
| 192 | + * and does not take the chroot into account ) |
|
| 193 | + * |
|
| 194 | + * @param string $path |
|
| 195 | + * @return string |
|
| 196 | + */ |
|
| 197 | + public function getMountPoint($path) { |
|
| 198 | + return Filesystem::getMountPoint($this->getAbsolutePath($path)); |
|
| 199 | + } |
|
| 200 | + |
|
| 201 | + /** |
|
| 202 | + * get the mountpoint of the storage object for a path |
|
| 203 | + * ( note: because a storage is not always mounted inside the fakeroot, the |
|
| 204 | + * returned mountpoint is relative to the absolute root of the filesystem |
|
| 205 | + * and does not take the chroot into account ) |
|
| 206 | + * |
|
| 207 | + * @param string $path |
|
| 208 | + * @return \OCP\Files\Mount\IMountPoint |
|
| 209 | + */ |
|
| 210 | + public function getMount($path) { |
|
| 211 | + return Filesystem::getMountManager()->find($this->getAbsolutePath($path)); |
|
| 212 | + } |
|
| 213 | + |
|
| 214 | + /** |
|
| 215 | + * resolve a path to a storage and internal path |
|
| 216 | + * |
|
| 217 | + * @param string $path |
|
| 218 | + * @return array an array consisting of the storage and the internal path |
|
| 219 | + */ |
|
| 220 | + public function resolvePath($path) { |
|
| 221 | + $a = $this->getAbsolutePath($path); |
|
| 222 | + $p = Filesystem::normalizePath($a); |
|
| 223 | + return Filesystem::resolvePath($p); |
|
| 224 | + } |
|
| 225 | + |
|
| 226 | + /** |
|
| 227 | + * return the path to a local version of the file |
|
| 228 | + * we need this because we can't know if a file is stored local or not from |
|
| 229 | + * outside the filestorage and for some purposes a local file is needed |
|
| 230 | + * |
|
| 231 | + * @param string $path |
|
| 232 | + * @return string |
|
| 233 | + */ |
|
| 234 | + public function getLocalFile($path) { |
|
| 235 | + $parent = substr($path, 0, strrpos($path, '/')); |
|
| 236 | + $path = $this->getAbsolutePath($path); |
|
| 237 | + list($storage, $internalPath) = Filesystem::resolvePath($path); |
|
| 238 | + if (Filesystem::isValidPath($parent) and $storage) { |
|
| 239 | + return $storage->getLocalFile($internalPath); |
|
| 240 | + } else { |
|
| 241 | + return null; |
|
| 242 | + } |
|
| 243 | + } |
|
| 244 | + |
|
| 245 | + /** |
|
| 246 | + * @param string $path |
|
| 247 | + * @return string |
|
| 248 | + */ |
|
| 249 | + public function getLocalFolder($path) { |
|
| 250 | + $parent = substr($path, 0, strrpos($path, '/')); |
|
| 251 | + $path = $this->getAbsolutePath($path); |
|
| 252 | + list($storage, $internalPath) = Filesystem::resolvePath($path); |
|
| 253 | + if (Filesystem::isValidPath($parent) and $storage) { |
|
| 254 | + return $storage->getLocalFolder($internalPath); |
|
| 255 | + } else { |
|
| 256 | + return null; |
|
| 257 | + } |
|
| 258 | + } |
|
| 259 | + |
|
| 260 | + /** |
|
| 261 | + * the following functions operate with arguments and return values identical |
|
| 262 | + * to those of their PHP built-in equivalents. Mostly they are merely wrappers |
|
| 263 | + * for \OC\Files\Storage\Storage via basicOperation(). |
|
| 264 | + */ |
|
| 265 | + public function mkdir($path) { |
|
| 266 | + return $this->basicOperation('mkdir', $path, array('create', 'write')); |
|
| 267 | + } |
|
| 268 | + |
|
| 269 | + /** |
|
| 270 | + * remove mount point |
|
| 271 | + * |
|
| 272 | + * @param \OC\Files\Mount\MoveableMount $mount |
|
| 273 | + * @param string $path relative to data/ |
|
| 274 | + * @return boolean |
|
| 275 | + */ |
|
| 276 | + protected function removeMount($mount, $path) { |
|
| 277 | + if ($mount instanceof MoveableMount) { |
|
| 278 | + // cut of /user/files to get the relative path to data/user/files |
|
| 279 | + $pathParts = explode('/', $path, 4); |
|
| 280 | + $relPath = '/' . $pathParts[3]; |
|
| 281 | + $this->lockFile($relPath, ILockingProvider::LOCK_SHARED, true); |
|
| 282 | + \OC_Hook::emit( |
|
| 283 | + Filesystem::CLASSNAME, "umount", |
|
| 284 | + array(Filesystem::signal_param_path => $relPath) |
|
| 285 | + ); |
|
| 286 | + $this->changeLock($relPath, ILockingProvider::LOCK_EXCLUSIVE, true); |
|
| 287 | + $result = $mount->removeMount(); |
|
| 288 | + $this->changeLock($relPath, ILockingProvider::LOCK_SHARED, true); |
|
| 289 | + if ($result) { |
|
| 290 | + \OC_Hook::emit( |
|
| 291 | + Filesystem::CLASSNAME, "post_umount", |
|
| 292 | + array(Filesystem::signal_param_path => $relPath) |
|
| 293 | + ); |
|
| 294 | + } |
|
| 295 | + $this->unlockFile($relPath, ILockingProvider::LOCK_SHARED, true); |
|
| 296 | + return $result; |
|
| 297 | + } else { |
|
| 298 | + // do not allow deleting the storage's root / the mount point |
|
| 299 | + // because for some storages it might delete the whole contents |
|
| 300 | + // but isn't supposed to work that way |
|
| 301 | + return false; |
|
| 302 | + } |
|
| 303 | + } |
|
| 304 | + |
|
| 305 | + public function disableCacheUpdate() { |
|
| 306 | + $this->updaterEnabled = false; |
|
| 307 | + } |
|
| 308 | + |
|
| 309 | + public function enableCacheUpdate() { |
|
| 310 | + $this->updaterEnabled = true; |
|
| 311 | + } |
|
| 312 | + |
|
| 313 | + protected function writeUpdate(Storage $storage, $internalPath, $time = null) { |
|
| 314 | + if ($this->updaterEnabled) { |
|
| 315 | + if (is_null($time)) { |
|
| 316 | + $time = time(); |
|
| 317 | + } |
|
| 318 | + $storage->getUpdater()->update($internalPath, $time); |
|
| 319 | + } |
|
| 320 | + } |
|
| 321 | + |
|
| 322 | + protected function removeUpdate(Storage $storage, $internalPath) { |
|
| 323 | + if ($this->updaterEnabled) { |
|
| 324 | + $storage->getUpdater()->remove($internalPath); |
|
| 325 | + } |
|
| 326 | + } |
|
| 327 | + |
|
| 328 | + protected function renameUpdate(Storage $sourceStorage, Storage $targetStorage, $sourceInternalPath, $targetInternalPath) { |
|
| 329 | + if ($this->updaterEnabled) { |
|
| 330 | + $targetStorage->getUpdater()->renameFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); |
|
| 331 | + } |
|
| 332 | + } |
|
| 333 | + |
|
| 334 | + /** |
|
| 335 | + * @param string $path |
|
| 336 | + * @return bool|mixed |
|
| 337 | + */ |
|
| 338 | + public function rmdir($path) { |
|
| 339 | + $absolutePath = $this->getAbsolutePath($path); |
|
| 340 | + $mount = Filesystem::getMountManager()->find($absolutePath); |
|
| 341 | + if ($mount->getInternalPath($absolutePath) === '') { |
|
| 342 | + return $this->removeMount($mount, $absolutePath); |
|
| 343 | + } |
|
| 344 | + if ($this->is_dir($path)) { |
|
| 345 | + $result = $this->basicOperation('rmdir', $path, array('delete')); |
|
| 346 | + } else { |
|
| 347 | + $result = false; |
|
| 348 | + } |
|
| 349 | + |
|
| 350 | + if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete |
|
| 351 | + $storage = $mount->getStorage(); |
|
| 352 | + $internalPath = $mount->getInternalPath($absolutePath); |
|
| 353 | + $storage->getUpdater()->remove($internalPath); |
|
| 354 | + } |
|
| 355 | + return $result; |
|
| 356 | + } |
|
| 357 | + |
|
| 358 | + /** |
|
| 359 | + * @param string $path |
|
| 360 | + * @return resource |
|
| 361 | + */ |
|
| 362 | + public function opendir($path) { |
|
| 363 | + return $this->basicOperation('opendir', $path, array('read')); |
|
| 364 | + } |
|
| 365 | + |
|
| 366 | + /** |
|
| 367 | + * @param $handle |
|
| 368 | + * @return mixed |
|
| 369 | + */ |
|
| 370 | + public function readdir($handle) { |
|
| 371 | + $fsLocal = new Storage\Local(array('datadir' => '/')); |
|
| 372 | + return $fsLocal->readdir($handle); |
|
| 373 | + } |
|
| 374 | + |
|
| 375 | + /** |
|
| 376 | + * @param string $path |
|
| 377 | + * @return bool|mixed |
|
| 378 | + */ |
|
| 379 | + public function is_dir($path) { |
|
| 380 | + if ($path == '/') { |
|
| 381 | + return true; |
|
| 382 | + } |
|
| 383 | + return $this->basicOperation('is_dir', $path); |
|
| 384 | + } |
|
| 385 | + |
|
| 386 | + /** |
|
| 387 | + * @param string $path |
|
| 388 | + * @return bool|mixed |
|
| 389 | + */ |
|
| 390 | + public function is_file($path) { |
|
| 391 | + if ($path == '/') { |
|
| 392 | + return false; |
|
| 393 | + } |
|
| 394 | + return $this->basicOperation('is_file', $path); |
|
| 395 | + } |
|
| 396 | + |
|
| 397 | + /** |
|
| 398 | + * @param string $path |
|
| 399 | + * @return mixed |
|
| 400 | + */ |
|
| 401 | + public function stat($path) { |
|
| 402 | + return $this->basicOperation('stat', $path); |
|
| 403 | + } |
|
| 404 | + |
|
| 405 | + /** |
|
| 406 | + * @param string $path |
|
| 407 | + * @return mixed |
|
| 408 | + */ |
|
| 409 | + public function filetype($path) { |
|
| 410 | + return $this->basicOperation('filetype', $path); |
|
| 411 | + } |
|
| 412 | + |
|
| 413 | + /** |
|
| 414 | + * @param string $path |
|
| 415 | + * @return mixed |
|
| 416 | + */ |
|
| 417 | + public function filesize($path) { |
|
| 418 | + return $this->basicOperation('filesize', $path); |
|
| 419 | + } |
|
| 420 | + |
|
| 421 | + /** |
|
| 422 | + * @param string $path |
|
| 423 | + * @return bool|mixed |
|
| 424 | + * @throws \OCP\Files\InvalidPathException |
|
| 425 | + */ |
|
| 426 | + public function readfile($path) { |
|
| 427 | + $this->assertPathLength($path); |
|
| 428 | + @ob_end_clean(); |
|
| 429 | + $handle = $this->fopen($path, 'rb'); |
|
| 430 | + if ($handle) { |
|
| 431 | + $chunkSize = 8192; // 8 kB chunks |
|
| 432 | + while (!feof($handle)) { |
|
| 433 | + echo fread($handle, $chunkSize); |
|
| 434 | + flush(); |
|
| 435 | + } |
|
| 436 | + fclose($handle); |
|
| 437 | + $size = $this->filesize($path); |
|
| 438 | + return $size; |
|
| 439 | + } |
|
| 440 | + return false; |
|
| 441 | + } |
|
| 442 | + |
|
| 443 | + /** |
|
| 444 | + * @param string $path |
|
| 445 | + * @param int $from |
|
| 446 | + * @param int $to |
|
| 447 | + * @return bool|mixed |
|
| 448 | + * @throws \OCP\Files\InvalidPathException |
|
| 449 | + * @throws \OCP\Files\UnseekableException |
|
| 450 | + */ |
|
| 451 | + public function readfilePart($path, $from, $to) { |
|
| 452 | + $this->assertPathLength($path); |
|
| 453 | + @ob_end_clean(); |
|
| 454 | + $handle = $this->fopen($path, 'rb'); |
|
| 455 | + if ($handle) { |
|
| 456 | + if (fseek($handle, $from) === 0) { |
|
| 457 | + $chunkSize = 8192; // 8 kB chunks |
|
| 458 | + $end = $to + 1; |
|
| 459 | + while (!feof($handle) && ftell($handle) < $end) { |
|
| 460 | + $len = $end - ftell($handle); |
|
| 461 | + if ($len > $chunkSize) { |
|
| 462 | + $len = $chunkSize; |
|
| 463 | + } |
|
| 464 | + echo fread($handle, $len); |
|
| 465 | + flush(); |
|
| 466 | + } |
|
| 467 | + $size = ftell($handle) - $from; |
|
| 468 | + return $size; |
|
| 469 | + } |
|
| 470 | + |
|
| 471 | + throw new \OCP\Files\UnseekableException('fseek error'); |
|
| 472 | + } |
|
| 473 | + return false; |
|
| 474 | + } |
|
| 475 | + |
|
| 476 | + /** |
|
| 477 | + * @param string $path |
|
| 478 | + * @return mixed |
|
| 479 | + */ |
|
| 480 | + public function isCreatable($path) { |
|
| 481 | + return $this->basicOperation('isCreatable', $path); |
|
| 482 | + } |
|
| 483 | + |
|
| 484 | + /** |
|
| 485 | + * @param string $path |
|
| 486 | + * @return mixed |
|
| 487 | + */ |
|
| 488 | + public function isReadable($path) { |
|
| 489 | + return $this->basicOperation('isReadable', $path); |
|
| 490 | + } |
|
| 491 | + |
|
| 492 | + /** |
|
| 493 | + * @param string $path |
|
| 494 | + * @return mixed |
|
| 495 | + */ |
|
| 496 | + public function isUpdatable($path) { |
|
| 497 | + return $this->basicOperation('isUpdatable', $path); |
|
| 498 | + } |
|
| 499 | + |
|
| 500 | + /** |
|
| 501 | + * @param string $path |
|
| 502 | + * @return bool|mixed |
|
| 503 | + */ |
|
| 504 | + public function isDeletable($path) { |
|
| 505 | + $absolutePath = $this->getAbsolutePath($path); |
|
| 506 | + $mount = Filesystem::getMountManager()->find($absolutePath); |
|
| 507 | + if ($mount->getInternalPath($absolutePath) === '') { |
|
| 508 | + return $mount instanceof MoveableMount; |
|
| 509 | + } |
|
| 510 | + return $this->basicOperation('isDeletable', $path); |
|
| 511 | + } |
|
| 512 | + |
|
| 513 | + /** |
|
| 514 | + * @param string $path |
|
| 515 | + * @return mixed |
|
| 516 | + */ |
|
| 517 | + public function isSharable($path) { |
|
| 518 | + return $this->basicOperation('isSharable', $path); |
|
| 519 | + } |
|
| 520 | + |
|
| 521 | + /** |
|
| 522 | + * @param string $path |
|
| 523 | + * @return bool|mixed |
|
| 524 | + */ |
|
| 525 | + public function file_exists($path) { |
|
| 526 | + if ($path == '/') { |
|
| 527 | + return true; |
|
| 528 | + } |
|
| 529 | + return $this->basicOperation('file_exists', $path); |
|
| 530 | + } |
|
| 531 | + |
|
| 532 | + /** |
|
| 533 | + * @param string $path |
|
| 534 | + * @return mixed |
|
| 535 | + */ |
|
| 536 | + public function filemtime($path) { |
|
| 537 | + return $this->basicOperation('filemtime', $path); |
|
| 538 | + } |
|
| 539 | + |
|
| 540 | + /** |
|
| 541 | + * @param string $path |
|
| 542 | + * @param int|string $mtime |
|
| 543 | + * @return bool |
|
| 544 | + */ |
|
| 545 | + public function touch($path, $mtime = null) { |
|
| 546 | + if (!is_null($mtime) and !is_numeric($mtime)) { |
|
| 547 | + $mtime = strtotime($mtime); |
|
| 548 | + } |
|
| 549 | + |
|
| 550 | + $hooks = array('touch'); |
|
| 551 | + |
|
| 552 | + if (!$this->file_exists($path)) { |
|
| 553 | + $hooks[] = 'create'; |
|
| 554 | + $hooks[] = 'write'; |
|
| 555 | + } |
|
| 556 | + $result = $this->basicOperation('touch', $path, $hooks, $mtime); |
|
| 557 | + if (!$result) { |
|
| 558 | + // If create file fails because of permissions on external storage like SMB folders, |
|
| 559 | + // check file exists and return false if not. |
|
| 560 | + if (!$this->file_exists($path)) { |
|
| 561 | + return false; |
|
| 562 | + } |
|
| 563 | + if (is_null($mtime)) { |
|
| 564 | + $mtime = time(); |
|
| 565 | + } |
|
| 566 | + //if native touch fails, we emulate it by changing the mtime in the cache |
|
| 567 | + $this->putFileInfo($path, array('mtime' => $mtime)); |
|
| 568 | + } |
|
| 569 | + return true; |
|
| 570 | + } |
|
| 571 | + |
|
| 572 | + /** |
|
| 573 | + * @param string $path |
|
| 574 | + * @return mixed |
|
| 575 | + */ |
|
| 576 | + public function file_get_contents($path) { |
|
| 577 | + return $this->basicOperation('file_get_contents', $path, array('read')); |
|
| 578 | + } |
|
| 579 | + |
|
| 580 | + /** |
|
| 581 | + * @param bool $exists |
|
| 582 | + * @param string $path |
|
| 583 | + * @param bool $run |
|
| 584 | + */ |
|
| 585 | + protected function emit_file_hooks_pre($exists, $path, &$run) { |
|
| 586 | + if (!$exists) { |
|
| 587 | + \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_create, array( |
|
| 588 | + Filesystem::signal_param_path => $this->getHookPath($path), |
|
| 589 | + Filesystem::signal_param_run => &$run, |
|
| 590 | + )); |
|
| 591 | + } else { |
|
| 592 | + \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_update, array( |
|
| 593 | + Filesystem::signal_param_path => $this->getHookPath($path), |
|
| 594 | + Filesystem::signal_param_run => &$run, |
|
| 595 | + )); |
|
| 596 | + } |
|
| 597 | + \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_write, array( |
|
| 598 | + Filesystem::signal_param_path => $this->getHookPath($path), |
|
| 599 | + Filesystem::signal_param_run => &$run, |
|
| 600 | + )); |
|
| 601 | + } |
|
| 602 | + |
|
| 603 | + /** |
|
| 604 | + * @param bool $exists |
|
| 605 | + * @param string $path |
|
| 606 | + */ |
|
| 607 | + protected function emit_file_hooks_post($exists, $path) { |
|
| 608 | + if (!$exists) { |
|
| 609 | + \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_create, array( |
|
| 610 | + Filesystem::signal_param_path => $this->getHookPath($path), |
|
| 611 | + )); |
|
| 612 | + } else { |
|
| 613 | + \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_update, array( |
|
| 614 | + Filesystem::signal_param_path => $this->getHookPath($path), |
|
| 615 | + )); |
|
| 616 | + } |
|
| 617 | + \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_write, array( |
|
| 618 | + Filesystem::signal_param_path => $this->getHookPath($path), |
|
| 619 | + )); |
|
| 620 | + } |
|
| 621 | + |
|
| 622 | + /** |
|
| 623 | + * @param string $path |
|
| 624 | + * @param mixed $data |
|
| 625 | + * @return bool|mixed |
|
| 626 | + * @throws \Exception |
|
| 627 | + */ |
|
| 628 | + public function file_put_contents($path, $data) { |
|
| 629 | + if (is_resource($data)) { //not having to deal with streams in file_put_contents makes life easier |
|
| 630 | + $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); |
|
| 631 | + if (Filesystem::isValidPath($path) |
|
| 632 | + and !Filesystem::isFileBlacklisted($path) |
|
| 633 | + ) { |
|
| 634 | + $path = $this->getRelativePath($absolutePath); |
|
| 635 | + |
|
| 636 | + $this->lockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 637 | + |
|
| 638 | + $exists = $this->file_exists($path); |
|
| 639 | + $run = true; |
|
| 640 | + if ($this->shouldEmitHooks($path)) { |
|
| 641 | + $this->emit_file_hooks_pre($exists, $path, $run); |
|
| 642 | + } |
|
| 643 | + if (!$run) { |
|
| 644 | + $this->unlockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 645 | + return false; |
|
| 646 | + } |
|
| 647 | + |
|
| 648 | + $this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE); |
|
| 649 | + |
|
| 650 | + /** @var \OC\Files\Storage\Storage $storage */ |
|
| 651 | + list($storage, $internalPath) = $this->resolvePath($path); |
|
| 652 | + $target = $storage->fopen($internalPath, 'w'); |
|
| 653 | + if ($target) { |
|
| 654 | + list (, $result) = \OC_Helper::streamCopy($data, $target); |
|
| 655 | + fclose($target); |
|
| 656 | + fclose($data); |
|
| 657 | + |
|
| 658 | + $this->writeUpdate($storage, $internalPath); |
|
| 659 | + |
|
| 660 | + $this->changeLock($path, ILockingProvider::LOCK_SHARED); |
|
| 661 | + |
|
| 662 | + if ($this->shouldEmitHooks($path) && $result !== false) { |
|
| 663 | + $this->emit_file_hooks_post($exists, $path); |
|
| 664 | + } |
|
| 665 | + $this->unlockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 666 | + return $result; |
|
| 667 | + } else { |
|
| 668 | + $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE); |
|
| 669 | + return false; |
|
| 670 | + } |
|
| 671 | + } else { |
|
| 672 | + return false; |
|
| 673 | + } |
|
| 674 | + } else { |
|
| 675 | + $hooks = ($this->file_exists($path)) ? array('update', 'write') : array('create', 'write'); |
|
| 676 | + return $this->basicOperation('file_put_contents', $path, $hooks, $data); |
|
| 677 | + } |
|
| 678 | + } |
|
| 679 | + |
|
| 680 | + /** |
|
| 681 | + * @param string $path |
|
| 682 | + * @return bool|mixed |
|
| 683 | + */ |
|
| 684 | + public function unlink($path) { |
|
| 685 | + if ($path === '' || $path === '/') { |
|
| 686 | + // do not allow deleting the root |
|
| 687 | + return false; |
|
| 688 | + } |
|
| 689 | + $postFix = (substr($path, -1, 1) === '/') ? '/' : ''; |
|
| 690 | + $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); |
|
| 691 | + $mount = Filesystem::getMountManager()->find($absolutePath . $postFix); |
|
| 692 | + if ($mount and $mount->getInternalPath($absolutePath) === '') { |
|
| 693 | + return $this->removeMount($mount, $absolutePath); |
|
| 694 | + } |
|
| 695 | + $result = $this->basicOperation('unlink', $path, array('delete')); |
|
| 696 | + if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete |
|
| 697 | + $storage = $mount->getStorage(); |
|
| 698 | + $internalPath = $mount->getInternalPath($absolutePath); |
|
| 699 | + $storage->getUpdater()->remove($internalPath); |
|
| 700 | + return true; |
|
| 701 | + } else { |
|
| 702 | + return $result; |
|
| 703 | + } |
|
| 704 | + } |
|
| 705 | + |
|
| 706 | + /** |
|
| 707 | + * @param string $directory |
|
| 708 | + * @return bool|mixed |
|
| 709 | + */ |
|
| 710 | + public function deleteAll($directory) { |
|
| 711 | + return $this->rmdir($directory); |
|
| 712 | + } |
|
| 713 | + |
|
| 714 | + /** |
|
| 715 | + * Rename/move a file or folder from the source path to target path. |
|
| 716 | + * |
|
| 717 | + * @param string $path1 source path |
|
| 718 | + * @param string $path2 target path |
|
| 719 | + * |
|
| 720 | + * @return bool|mixed |
|
| 721 | + */ |
|
| 722 | + public function rename($path1, $path2) { |
|
| 723 | + $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1)); |
|
| 724 | + $absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2)); |
|
| 725 | + $result = false; |
|
| 726 | + if ( |
|
| 727 | + Filesystem::isValidPath($path2) |
|
| 728 | + and Filesystem::isValidPath($path1) |
|
| 729 | + and !Filesystem::isFileBlacklisted($path2) |
|
| 730 | + ) { |
|
| 731 | + $path1 = $this->getRelativePath($absolutePath1); |
|
| 732 | + $path2 = $this->getRelativePath($absolutePath2); |
|
| 733 | + $exists = $this->file_exists($path2); |
|
| 734 | + |
|
| 735 | + if ($path1 == null or $path2 == null) { |
|
| 736 | + return false; |
|
| 737 | + } |
|
| 738 | + |
|
| 739 | + $this->lockFile($path1, ILockingProvider::LOCK_SHARED, true); |
|
| 740 | + try { |
|
| 741 | + $this->lockFile($path2, ILockingProvider::LOCK_SHARED, true); |
|
| 742 | + } catch (LockedException $e) { |
|
| 743 | + $this->unlockFile($path1, ILockingProvider::LOCK_SHARED); |
|
| 744 | + throw $e; |
|
| 745 | + } |
|
| 746 | + |
|
| 747 | + $run = true; |
|
| 748 | + if ($this->shouldEmitHooks($path1) && (Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2))) { |
|
| 749 | + // if it was a rename from a part file to a regular file it was a write and not a rename operation |
|
| 750 | + $this->emit_file_hooks_pre($exists, $path2, $run); |
|
| 751 | + } elseif ($this->shouldEmitHooks($path1)) { |
|
| 752 | + \OC_Hook::emit( |
|
| 753 | + Filesystem::CLASSNAME, Filesystem::signal_rename, |
|
| 754 | + array( |
|
| 755 | + Filesystem::signal_param_oldpath => $this->getHookPath($path1), |
|
| 756 | + Filesystem::signal_param_newpath => $this->getHookPath($path2), |
|
| 757 | + Filesystem::signal_param_run => &$run |
|
| 758 | + ) |
|
| 759 | + ); |
|
| 760 | + } |
|
| 761 | + if ($run) { |
|
| 762 | + $this->verifyPath(dirname($path2), basename($path2)); |
|
| 763 | + |
|
| 764 | + $manager = Filesystem::getMountManager(); |
|
| 765 | + $mount1 = $this->getMount($path1); |
|
| 766 | + $mount2 = $this->getMount($path2); |
|
| 767 | + $storage1 = $mount1->getStorage(); |
|
| 768 | + $storage2 = $mount2->getStorage(); |
|
| 769 | + $internalPath1 = $mount1->getInternalPath($absolutePath1); |
|
| 770 | + $internalPath2 = $mount2->getInternalPath($absolutePath2); |
|
| 771 | + |
|
| 772 | + $this->changeLock($path1, ILockingProvider::LOCK_EXCLUSIVE, true); |
|
| 773 | + $this->changeLock($path2, ILockingProvider::LOCK_EXCLUSIVE, true); |
|
| 774 | + |
|
| 775 | + if ($internalPath1 === '' and $mount1 instanceof MoveableMount) { |
|
| 776 | + if ($this->isTargetAllowed($absolutePath2)) { |
|
| 777 | + /** |
|
| 778 | + * @var \OC\Files\Mount\MountPoint | \OC\Files\Mount\MoveableMount $mount1 |
|
| 779 | + */ |
|
| 780 | + $sourceMountPoint = $mount1->getMountPoint(); |
|
| 781 | + $result = $mount1->moveMount($absolutePath2); |
|
| 782 | + $manager->moveMount($sourceMountPoint, $mount1->getMountPoint()); |
|
| 783 | + } else { |
|
| 784 | + $result = false; |
|
| 785 | + } |
|
| 786 | + // moving a file/folder within the same mount point |
|
| 787 | + } elseif ($storage1 === $storage2) { |
|
| 788 | + if ($storage1) { |
|
| 789 | + $result = $storage1->rename($internalPath1, $internalPath2); |
|
| 790 | + } else { |
|
| 791 | + $result = false; |
|
| 792 | + } |
|
| 793 | + // moving a file/folder between storages (from $storage1 to $storage2) |
|
| 794 | + } else { |
|
| 795 | + $result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2); |
|
| 796 | + } |
|
| 797 | + |
|
| 798 | + if ((Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) { |
|
| 799 | + // if it was a rename from a part file to a regular file it was a write and not a rename operation |
|
| 800 | + |
|
| 801 | + $this->writeUpdate($storage2, $internalPath2); |
|
| 802 | + } else if ($result) { |
|
| 803 | + if ($internalPath1 !== '') { // don't do a cache update for moved mounts |
|
| 804 | + $this->renameUpdate($storage1, $storage2, $internalPath1, $internalPath2); |
|
| 805 | + } |
|
| 806 | + } |
|
| 807 | + |
|
| 808 | + $this->changeLock($path1, ILockingProvider::LOCK_SHARED, true); |
|
| 809 | + $this->changeLock($path2, ILockingProvider::LOCK_SHARED, true); |
|
| 810 | + |
|
| 811 | + if ((Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) { |
|
| 812 | + if ($this->shouldEmitHooks()) { |
|
| 813 | + $this->emit_file_hooks_post($exists, $path2); |
|
| 814 | + } |
|
| 815 | + } elseif ($result) { |
|
| 816 | + if ($this->shouldEmitHooks($path1) and $this->shouldEmitHooks($path2)) { |
|
| 817 | + \OC_Hook::emit( |
|
| 818 | + Filesystem::CLASSNAME, |
|
| 819 | + Filesystem::signal_post_rename, |
|
| 820 | + array( |
|
| 821 | + Filesystem::signal_param_oldpath => $this->getHookPath($path1), |
|
| 822 | + Filesystem::signal_param_newpath => $this->getHookPath($path2) |
|
| 823 | + ) |
|
| 824 | + ); |
|
| 825 | + } |
|
| 826 | + } |
|
| 827 | + } |
|
| 828 | + $this->unlockFile($path1, ILockingProvider::LOCK_SHARED, true); |
|
| 829 | + $this->unlockFile($path2, ILockingProvider::LOCK_SHARED, true); |
|
| 830 | + } |
|
| 831 | + return $result; |
|
| 832 | + } |
|
| 833 | + |
|
| 834 | + /** |
|
| 835 | + * Copy a file/folder from the source path to target path |
|
| 836 | + * |
|
| 837 | + * @param string $path1 source path |
|
| 838 | + * @param string $path2 target path |
|
| 839 | + * @param bool $preserveMtime whether to preserve mtime on the copy |
|
| 840 | + * |
|
| 841 | + * @return bool|mixed |
|
| 842 | + */ |
|
| 843 | + public function copy($path1, $path2, $preserveMtime = false) { |
|
| 844 | + $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1)); |
|
| 845 | + $absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2)); |
|
| 846 | + $result = false; |
|
| 847 | + if ( |
|
| 848 | + Filesystem::isValidPath($path2) |
|
| 849 | + and Filesystem::isValidPath($path1) |
|
| 850 | + and !Filesystem::isFileBlacklisted($path2) |
|
| 851 | + ) { |
|
| 852 | + $path1 = $this->getRelativePath($absolutePath1); |
|
| 853 | + $path2 = $this->getRelativePath($absolutePath2); |
|
| 854 | + |
|
| 855 | + if ($path1 == null or $path2 == null) { |
|
| 856 | + return false; |
|
| 857 | + } |
|
| 858 | + $run = true; |
|
| 859 | + |
|
| 860 | + $this->lockFile($path2, ILockingProvider::LOCK_SHARED); |
|
| 861 | + $this->lockFile($path1, ILockingProvider::LOCK_SHARED); |
|
| 862 | + $lockTypePath1 = ILockingProvider::LOCK_SHARED; |
|
| 863 | + $lockTypePath2 = ILockingProvider::LOCK_SHARED; |
|
| 864 | + |
|
| 865 | + try { |
|
| 866 | + |
|
| 867 | + $exists = $this->file_exists($path2); |
|
| 868 | + if ($this->shouldEmitHooks()) { |
|
| 869 | + \OC_Hook::emit( |
|
| 870 | + Filesystem::CLASSNAME, |
|
| 871 | + Filesystem::signal_copy, |
|
| 872 | + array( |
|
| 873 | + Filesystem::signal_param_oldpath => $this->getHookPath($path1), |
|
| 874 | + Filesystem::signal_param_newpath => $this->getHookPath($path2), |
|
| 875 | + Filesystem::signal_param_run => &$run |
|
| 876 | + ) |
|
| 877 | + ); |
|
| 878 | + $this->emit_file_hooks_pre($exists, $path2, $run); |
|
| 879 | + } |
|
| 880 | + if ($run) { |
|
| 881 | + $mount1 = $this->getMount($path1); |
|
| 882 | + $mount2 = $this->getMount($path2); |
|
| 883 | + $storage1 = $mount1->getStorage(); |
|
| 884 | + $internalPath1 = $mount1->getInternalPath($absolutePath1); |
|
| 885 | + $storage2 = $mount2->getStorage(); |
|
| 886 | + $internalPath2 = $mount2->getInternalPath($absolutePath2); |
|
| 887 | + |
|
| 888 | + $this->changeLock($path2, ILockingProvider::LOCK_EXCLUSIVE); |
|
| 889 | + $lockTypePath2 = ILockingProvider::LOCK_EXCLUSIVE; |
|
| 890 | + |
|
| 891 | + if ($mount1->getMountPoint() == $mount2->getMountPoint()) { |
|
| 892 | + if ($storage1) { |
|
| 893 | + $result = $storage1->copy($internalPath1, $internalPath2); |
|
| 894 | + } else { |
|
| 895 | + $result = false; |
|
| 896 | + } |
|
| 897 | + } else { |
|
| 898 | + $result = $storage2->copyFromStorage($storage1, $internalPath1, $internalPath2); |
|
| 899 | + } |
|
| 900 | + |
|
| 901 | + $this->writeUpdate($storage2, $internalPath2); |
|
| 902 | + |
|
| 903 | + $this->changeLock($path2, ILockingProvider::LOCK_SHARED); |
|
| 904 | + $lockTypePath2 = ILockingProvider::LOCK_SHARED; |
|
| 905 | + |
|
| 906 | + if ($this->shouldEmitHooks() && $result !== false) { |
|
| 907 | + \OC_Hook::emit( |
|
| 908 | + Filesystem::CLASSNAME, |
|
| 909 | + Filesystem::signal_post_copy, |
|
| 910 | + array( |
|
| 911 | + Filesystem::signal_param_oldpath => $this->getHookPath($path1), |
|
| 912 | + Filesystem::signal_param_newpath => $this->getHookPath($path2) |
|
| 913 | + ) |
|
| 914 | + ); |
|
| 915 | + $this->emit_file_hooks_post($exists, $path2); |
|
| 916 | + } |
|
| 917 | + |
|
| 918 | + } |
|
| 919 | + } catch (\Exception $e) { |
|
| 920 | + $this->unlockFile($path2, $lockTypePath2); |
|
| 921 | + $this->unlockFile($path1, $lockTypePath1); |
|
| 922 | + throw $e; |
|
| 923 | + } |
|
| 924 | + |
|
| 925 | + $this->unlockFile($path2, $lockTypePath2); |
|
| 926 | + $this->unlockFile($path1, $lockTypePath1); |
|
| 927 | + |
|
| 928 | + } |
|
| 929 | + return $result; |
|
| 930 | + } |
|
| 931 | + |
|
| 932 | + /** |
|
| 933 | + * @param string $path |
|
| 934 | + * @param string $mode 'r' or 'w' |
|
| 935 | + * @return resource |
|
| 936 | + */ |
|
| 937 | + public function fopen($path, $mode) { |
|
| 938 | + $mode = str_replace('b', '', $mode); // the binary flag is a windows only feature which we do not support |
|
| 939 | + $hooks = array(); |
|
| 940 | + switch ($mode) { |
|
| 941 | + case 'r': |
|
| 942 | + $hooks[] = 'read'; |
|
| 943 | + break; |
|
| 944 | + case 'r+': |
|
| 945 | + case 'w+': |
|
| 946 | + case 'x+': |
|
| 947 | + case 'a+': |
|
| 948 | + $hooks[] = 'read'; |
|
| 949 | + $hooks[] = 'write'; |
|
| 950 | + break; |
|
| 951 | + case 'w': |
|
| 952 | + case 'x': |
|
| 953 | + case 'a': |
|
| 954 | + $hooks[] = 'write'; |
|
| 955 | + break; |
|
| 956 | + default: |
|
| 957 | + \OCP\Util::writeLog('core', 'invalid mode (' . $mode . ') for ' . $path, \OCP\Util::ERROR); |
|
| 958 | + } |
|
| 959 | + |
|
| 960 | + if ($mode !== 'r' && $mode !== 'w') { |
|
| 961 | + \OC::$server->getLogger()->info('Trying to open a file with a mode other than "r" or "w" can cause severe performance issues with some backends'); |
|
| 962 | + } |
|
| 963 | + |
|
| 964 | + return $this->basicOperation('fopen', $path, $hooks, $mode); |
|
| 965 | + } |
|
| 966 | + |
|
| 967 | + /** |
|
| 968 | + * @param string $path |
|
| 969 | + * @return bool|string |
|
| 970 | + * @throws \OCP\Files\InvalidPathException |
|
| 971 | + */ |
|
| 972 | + public function toTmpFile($path) { |
|
| 973 | + $this->assertPathLength($path); |
|
| 974 | + if (Filesystem::isValidPath($path)) { |
|
| 975 | + $source = $this->fopen($path, 'r'); |
|
| 976 | + if ($source) { |
|
| 977 | + $extension = pathinfo($path, PATHINFO_EXTENSION); |
|
| 978 | + $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($extension); |
|
| 979 | + file_put_contents($tmpFile, $source); |
|
| 980 | + return $tmpFile; |
|
| 981 | + } else { |
|
| 982 | + return false; |
|
| 983 | + } |
|
| 984 | + } else { |
|
| 985 | + return false; |
|
| 986 | + } |
|
| 987 | + } |
|
| 988 | + |
|
| 989 | + /** |
|
| 990 | + * @param string $tmpFile |
|
| 991 | + * @param string $path |
|
| 992 | + * @return bool|mixed |
|
| 993 | + * @throws \OCP\Files\InvalidPathException |
|
| 994 | + */ |
|
| 995 | + public function fromTmpFile($tmpFile, $path) { |
|
| 996 | + $this->assertPathLength($path); |
|
| 997 | + if (Filesystem::isValidPath($path)) { |
|
| 998 | + |
|
| 999 | + // Get directory that the file is going into |
|
| 1000 | + $filePath = dirname($path); |
|
| 1001 | + |
|
| 1002 | + // Create the directories if any |
|
| 1003 | + if (!$this->file_exists($filePath)) { |
|
| 1004 | + $result = $this->createParentDirectories($filePath); |
|
| 1005 | + if ($result === false) { |
|
| 1006 | + return false; |
|
| 1007 | + } |
|
| 1008 | + } |
|
| 1009 | + |
|
| 1010 | + $source = fopen($tmpFile, 'r'); |
|
| 1011 | + if ($source) { |
|
| 1012 | + $result = $this->file_put_contents($path, $source); |
|
| 1013 | + // $this->file_put_contents() might have already closed |
|
| 1014 | + // the resource, so we check it, before trying to close it |
|
| 1015 | + // to avoid messages in the error log. |
|
| 1016 | + if (is_resource($source)) { |
|
| 1017 | + fclose($source); |
|
| 1018 | + } |
|
| 1019 | + unlink($tmpFile); |
|
| 1020 | + return $result; |
|
| 1021 | + } else { |
|
| 1022 | + return false; |
|
| 1023 | + } |
|
| 1024 | + } else { |
|
| 1025 | + return false; |
|
| 1026 | + } |
|
| 1027 | + } |
|
| 1028 | + |
|
| 1029 | + |
|
| 1030 | + /** |
|
| 1031 | + * @param string $path |
|
| 1032 | + * @return mixed |
|
| 1033 | + * @throws \OCP\Files\InvalidPathException |
|
| 1034 | + */ |
|
| 1035 | + public function getMimeType($path) { |
|
| 1036 | + $this->assertPathLength($path); |
|
| 1037 | + return $this->basicOperation('getMimeType', $path); |
|
| 1038 | + } |
|
| 1039 | + |
|
| 1040 | + /** |
|
| 1041 | + * @param string $type |
|
| 1042 | + * @param string $path |
|
| 1043 | + * @param bool $raw |
|
| 1044 | + * @return bool|null|string |
|
| 1045 | + */ |
|
| 1046 | + public function hash($type, $path, $raw = false) { |
|
| 1047 | + $postFix = (substr($path, -1, 1) === '/') ? '/' : ''; |
|
| 1048 | + $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); |
|
| 1049 | + if (Filesystem::isValidPath($path)) { |
|
| 1050 | + $path = $this->getRelativePath($absolutePath); |
|
| 1051 | + if ($path == null) { |
|
| 1052 | + return false; |
|
| 1053 | + } |
|
| 1054 | + if ($this->shouldEmitHooks($path)) { |
|
| 1055 | + \OC_Hook::emit( |
|
| 1056 | + Filesystem::CLASSNAME, |
|
| 1057 | + Filesystem::signal_read, |
|
| 1058 | + array(Filesystem::signal_param_path => $this->getHookPath($path)) |
|
| 1059 | + ); |
|
| 1060 | + } |
|
| 1061 | + list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix); |
|
| 1062 | + if ($storage) { |
|
| 1063 | + $result = $storage->hash($type, $internalPath, $raw); |
|
| 1064 | + return $result; |
|
| 1065 | + } |
|
| 1066 | + } |
|
| 1067 | + return null; |
|
| 1068 | + } |
|
| 1069 | + |
|
| 1070 | + /** |
|
| 1071 | + * @param string $path |
|
| 1072 | + * @return mixed |
|
| 1073 | + * @throws \OCP\Files\InvalidPathException |
|
| 1074 | + */ |
|
| 1075 | + public function free_space($path = '/') { |
|
| 1076 | + $this->assertPathLength($path); |
|
| 1077 | + return $this->basicOperation('free_space', $path); |
|
| 1078 | + } |
|
| 1079 | + |
|
| 1080 | + /** |
|
| 1081 | + * abstraction layer for basic filesystem functions: wrapper for \OC\Files\Storage\Storage |
|
| 1082 | + * |
|
| 1083 | + * @param string $operation |
|
| 1084 | + * @param string $path |
|
| 1085 | + * @param array $hooks (optional) |
|
| 1086 | + * @param mixed $extraParam (optional) |
|
| 1087 | + * @return mixed |
|
| 1088 | + * @throws \Exception |
|
| 1089 | + * |
|
| 1090 | + * This method takes requests for basic filesystem functions (e.g. reading & writing |
|
| 1091 | + * files), processes hooks and proxies, sanitises paths, and finally passes them on to |
|
| 1092 | + * \OC\Files\Storage\Storage for delegation to a storage backend for execution |
|
| 1093 | + */ |
|
| 1094 | + private function basicOperation($operation, $path, $hooks = [], $extraParam = null) { |
|
| 1095 | + $postFix = (substr($path, -1, 1) === '/') ? '/' : ''; |
|
| 1096 | + $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); |
|
| 1097 | + if (Filesystem::isValidPath($path) |
|
| 1098 | + and !Filesystem::isFileBlacklisted($path) |
|
| 1099 | + ) { |
|
| 1100 | + $path = $this->getRelativePath($absolutePath); |
|
| 1101 | + if ($path == null) { |
|
| 1102 | + return false; |
|
| 1103 | + } |
|
| 1104 | + |
|
| 1105 | + if (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks)) { |
|
| 1106 | + // always a shared lock during pre-hooks so the hook can read the file |
|
| 1107 | + $this->lockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 1108 | + } |
|
| 1109 | + |
|
| 1110 | + $run = $this->runHooks($hooks, $path); |
|
| 1111 | + /** @var \OC\Files\Storage\Storage $storage */ |
|
| 1112 | + list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix); |
|
| 1113 | + if ($run and $storage) { |
|
| 1114 | + if (in_array('write', $hooks) || in_array('delete', $hooks)) { |
|
| 1115 | + $this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE); |
|
| 1116 | + } |
|
| 1117 | + try { |
|
| 1118 | + if (!is_null($extraParam)) { |
|
| 1119 | + $result = $storage->$operation($internalPath, $extraParam); |
|
| 1120 | + } else { |
|
| 1121 | + $result = $storage->$operation($internalPath); |
|
| 1122 | + } |
|
| 1123 | + } catch (\Exception $e) { |
|
| 1124 | + if (in_array('write', $hooks) || in_array('delete', $hooks)) { |
|
| 1125 | + $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE); |
|
| 1126 | + } else if (in_array('read', $hooks)) { |
|
| 1127 | + $this->unlockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 1128 | + } |
|
| 1129 | + throw $e; |
|
| 1130 | + } |
|
| 1131 | + |
|
| 1132 | + if ($result && in_array('delete', $hooks) and $result) { |
|
| 1133 | + $this->removeUpdate($storage, $internalPath); |
|
| 1134 | + } |
|
| 1135 | + if ($result && in_array('write', $hooks) and $operation !== 'fopen') { |
|
| 1136 | + $this->writeUpdate($storage, $internalPath); |
|
| 1137 | + } |
|
| 1138 | + if ($result && in_array('touch', $hooks)) { |
|
| 1139 | + $this->writeUpdate($storage, $internalPath, $extraParam); |
|
| 1140 | + } |
|
| 1141 | + |
|
| 1142 | + if ((in_array('write', $hooks) || in_array('delete', $hooks)) && ($operation !== 'fopen' || $result === false)) { |
|
| 1143 | + $this->changeLock($path, ILockingProvider::LOCK_SHARED); |
|
| 1144 | + } |
|
| 1145 | + |
|
| 1146 | + $unlockLater = false; |
|
| 1147 | + if ($this->lockingEnabled && $operation === 'fopen' && is_resource($result)) { |
|
| 1148 | + $unlockLater = true; |
|
| 1149 | + // make sure our unlocking callback will still be called if connection is aborted |
|
| 1150 | + ignore_user_abort(true); |
|
| 1151 | + $result = CallbackWrapper::wrap($result, null, null, function () use ($hooks, $path) { |
|
| 1152 | + if (in_array('write', $hooks)) { |
|
| 1153 | + $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE); |
|
| 1154 | + } else if (in_array('read', $hooks)) { |
|
| 1155 | + $this->unlockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 1156 | + } |
|
| 1157 | + }); |
|
| 1158 | + } |
|
| 1159 | + |
|
| 1160 | + if ($this->shouldEmitHooks($path) && $result !== false) { |
|
| 1161 | + if ($operation != 'fopen') { //no post hooks for fopen, the file stream is still open |
|
| 1162 | + $this->runHooks($hooks, $path, true); |
|
| 1163 | + } |
|
| 1164 | + } |
|
| 1165 | + |
|
| 1166 | + if (!$unlockLater |
|
| 1167 | + && (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks)) |
|
| 1168 | + ) { |
|
| 1169 | + $this->unlockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 1170 | + } |
|
| 1171 | + return $result; |
|
| 1172 | + } else { |
|
| 1173 | + $this->unlockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 1174 | + } |
|
| 1175 | + } |
|
| 1176 | + return null; |
|
| 1177 | + } |
|
| 1178 | + |
|
| 1179 | + /** |
|
| 1180 | + * get the path relative to the default root for hook usage |
|
| 1181 | + * |
|
| 1182 | + * @param string $path |
|
| 1183 | + * @return string |
|
| 1184 | + */ |
|
| 1185 | + private function getHookPath($path) { |
|
| 1186 | + if (!Filesystem::getView()) { |
|
| 1187 | + return $path; |
|
| 1188 | + } |
|
| 1189 | + return Filesystem::getView()->getRelativePath($this->getAbsolutePath($path)); |
|
| 1190 | + } |
|
| 1191 | + |
|
| 1192 | + private function shouldEmitHooks($path = '') { |
|
| 1193 | + if ($path && Cache\Scanner::isPartialFile($path)) { |
|
| 1194 | + return false; |
|
| 1195 | + } |
|
| 1196 | + if (!Filesystem::$loaded) { |
|
| 1197 | + return false; |
|
| 1198 | + } |
|
| 1199 | + $defaultRoot = Filesystem::getRoot(); |
|
| 1200 | + if ($defaultRoot === null) { |
|
| 1201 | + return false; |
|
| 1202 | + } |
|
| 1203 | + if ($this->fakeRoot === $defaultRoot) { |
|
| 1204 | + return true; |
|
| 1205 | + } |
|
| 1206 | + $fullPath = $this->getAbsolutePath($path); |
|
| 1207 | + |
|
| 1208 | + if ($fullPath === $defaultRoot) { |
|
| 1209 | + return true; |
|
| 1210 | + } |
|
| 1211 | + |
|
| 1212 | + return (strlen($fullPath) > strlen($defaultRoot)) && (substr($fullPath, 0, strlen($defaultRoot) + 1) === $defaultRoot . '/'); |
|
| 1213 | + } |
|
| 1214 | + |
|
| 1215 | + /** |
|
| 1216 | + * @param string[] $hooks |
|
| 1217 | + * @param string $path |
|
| 1218 | + * @param bool $post |
|
| 1219 | + * @return bool |
|
| 1220 | + */ |
|
| 1221 | + private function runHooks($hooks, $path, $post = false) { |
|
| 1222 | + $relativePath = $path; |
|
| 1223 | + $path = $this->getHookPath($path); |
|
| 1224 | + $prefix = ($post) ? 'post_' : ''; |
|
| 1225 | + $run = true; |
|
| 1226 | + if ($this->shouldEmitHooks($relativePath)) { |
|
| 1227 | + foreach ($hooks as $hook) { |
|
| 1228 | + if ($hook != 'read') { |
|
| 1229 | + \OC_Hook::emit( |
|
| 1230 | + Filesystem::CLASSNAME, |
|
| 1231 | + $prefix . $hook, |
|
| 1232 | + array( |
|
| 1233 | + Filesystem::signal_param_run => &$run, |
|
| 1234 | + Filesystem::signal_param_path => $path |
|
| 1235 | + ) |
|
| 1236 | + ); |
|
| 1237 | + } elseif (!$post) { |
|
| 1238 | + \OC_Hook::emit( |
|
| 1239 | + Filesystem::CLASSNAME, |
|
| 1240 | + $prefix . $hook, |
|
| 1241 | + array( |
|
| 1242 | + Filesystem::signal_param_path => $path |
|
| 1243 | + ) |
|
| 1244 | + ); |
|
| 1245 | + } |
|
| 1246 | + } |
|
| 1247 | + } |
|
| 1248 | + return $run; |
|
| 1249 | + } |
|
| 1250 | + |
|
| 1251 | + /** |
|
| 1252 | + * check if a file or folder has been updated since $time |
|
| 1253 | + * |
|
| 1254 | + * @param string $path |
|
| 1255 | + * @param int $time |
|
| 1256 | + * @return bool |
|
| 1257 | + */ |
|
| 1258 | + public function hasUpdated($path, $time) { |
|
| 1259 | + return $this->basicOperation('hasUpdated', $path, array(), $time); |
|
| 1260 | + } |
|
| 1261 | + |
|
| 1262 | + /** |
|
| 1263 | + * @param string $ownerId |
|
| 1264 | + * @return \OC\User\User |
|
| 1265 | + */ |
|
| 1266 | + private function getUserObjectForOwner($ownerId) { |
|
| 1267 | + $owner = $this->userManager->get($ownerId); |
|
| 1268 | + if ($owner instanceof IUser) { |
|
| 1269 | + return $owner; |
|
| 1270 | + } else { |
|
| 1271 | + return new User($ownerId, null); |
|
| 1272 | + } |
|
| 1273 | + } |
|
| 1274 | + |
|
| 1275 | + /** |
|
| 1276 | + * Get file info from cache |
|
| 1277 | + * |
|
| 1278 | + * If the file is not in cached it will be scanned |
|
| 1279 | + * If the file has changed on storage the cache will be updated |
|
| 1280 | + * |
|
| 1281 | + * @param \OC\Files\Storage\Storage $storage |
|
| 1282 | + * @param string $internalPath |
|
| 1283 | + * @param string $relativePath |
|
| 1284 | + * @return array|bool |
|
| 1285 | + */ |
|
| 1286 | + private function getCacheEntry($storage, $internalPath, $relativePath) { |
|
| 1287 | + $cache = $storage->getCache($internalPath); |
|
| 1288 | + $data = $cache->get($internalPath); |
|
| 1289 | + $watcher = $storage->getWatcher($internalPath); |
|
| 1290 | + |
|
| 1291 | + try { |
|
| 1292 | + // if the file is not in the cache or needs to be updated, trigger the scanner and reload the data |
|
| 1293 | + if (!$data || $data['size'] === -1) { |
|
| 1294 | + $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED); |
|
| 1295 | + if (!$storage->file_exists($internalPath)) { |
|
| 1296 | + $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); |
|
| 1297 | + return false; |
|
| 1298 | + } |
|
| 1299 | + $scanner = $storage->getScanner($internalPath); |
|
| 1300 | + $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); |
|
| 1301 | + $data = $cache->get($internalPath); |
|
| 1302 | + $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); |
|
| 1303 | + } else if (!Cache\Scanner::isPartialFile($internalPath) && $watcher->needsUpdate($internalPath, $data)) { |
|
| 1304 | + $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED); |
|
| 1305 | + $watcher->update($internalPath, $data); |
|
| 1306 | + $storage->getPropagator()->propagateChange($internalPath, time()); |
|
| 1307 | + $data = $cache->get($internalPath); |
|
| 1308 | + $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); |
|
| 1309 | + } |
|
| 1310 | + } catch (LockedException $e) { |
|
| 1311 | + // if the file is locked we just use the old cache info |
|
| 1312 | + } |
|
| 1313 | + |
|
| 1314 | + return $data; |
|
| 1315 | + } |
|
| 1316 | + |
|
| 1317 | + /** |
|
| 1318 | + * get the filesystem info |
|
| 1319 | + * |
|
| 1320 | + * @param string $path |
|
| 1321 | + * @param boolean|string $includeMountPoints true to add mountpoint sizes, |
|
| 1322 | + * 'ext' to add only ext storage mount point sizes. Defaults to true. |
|
| 1323 | + * defaults to true |
|
| 1324 | + * @return \OC\Files\FileInfo|false False if file does not exist |
|
| 1325 | + */ |
|
| 1326 | + public function getFileInfo($path, $includeMountPoints = true) { |
|
| 1327 | + $this->assertPathLength($path); |
|
| 1328 | + if (!Filesystem::isValidPath($path)) { |
|
| 1329 | + return false; |
|
| 1330 | + } |
|
| 1331 | + if (Cache\Scanner::isPartialFile($path)) { |
|
| 1332 | + return $this->getPartFileInfo($path); |
|
| 1333 | + } |
|
| 1334 | + $relativePath = $path; |
|
| 1335 | + $path = Filesystem::normalizePath($this->fakeRoot . '/' . $path); |
|
| 1336 | + |
|
| 1337 | + $mount = Filesystem::getMountManager()->find($path); |
|
| 1338 | + $storage = $mount->getStorage(); |
|
| 1339 | + $internalPath = $mount->getInternalPath($path); |
|
| 1340 | + if ($storage) { |
|
| 1341 | + $data = $this->getCacheEntry($storage, $internalPath, $relativePath); |
|
| 1342 | + |
|
| 1343 | + if (!$data instanceof ICacheEntry) { |
|
| 1344 | + return false; |
|
| 1345 | + } |
|
| 1346 | + |
|
| 1347 | + if ($mount instanceof MoveableMount && $internalPath === '') { |
|
| 1348 | + $data['permissions'] |= \OCP\Constants::PERMISSION_DELETE; |
|
| 1349 | + } |
|
| 1350 | + |
|
| 1351 | + $owner = $this->getUserObjectForOwner($storage->getOwner($internalPath)); |
|
| 1352 | + $info = new FileInfo($path, $storage, $internalPath, $data, $mount, $owner); |
|
| 1353 | + |
|
| 1354 | + if ($data and isset($data['fileid'])) { |
|
| 1355 | + if ($includeMountPoints and $data['mimetype'] === 'httpd/unix-directory') { |
|
| 1356 | + //add the sizes of other mount points to the folder |
|
| 1357 | + $extOnly = ($includeMountPoints === 'ext'); |
|
| 1358 | + $mounts = Filesystem::getMountManager()->findIn($path); |
|
| 1359 | + $info->setSubMounts(array_filter($mounts, function (IMountPoint $mount) use ($extOnly) { |
|
| 1360 | + $subStorage = $mount->getStorage(); |
|
| 1361 | + return !($extOnly && $subStorage instanceof \OCA\Files_Sharing\SharedStorage); |
|
| 1362 | + })); |
|
| 1363 | + } |
|
| 1364 | + } |
|
| 1365 | + |
|
| 1366 | + return $info; |
|
| 1367 | + } |
|
| 1368 | + |
|
| 1369 | + return false; |
|
| 1370 | + } |
|
| 1371 | + |
|
| 1372 | + /** |
|
| 1373 | + * get the content of a directory |
|
| 1374 | + * |
|
| 1375 | + * @param string $directory path under datadirectory |
|
| 1376 | + * @param string $mimetype_filter limit returned content to this mimetype or mimepart |
|
| 1377 | + * @return FileInfo[] |
|
| 1378 | + */ |
|
| 1379 | + public function getDirectoryContent($directory, $mimetype_filter = '') { |
|
| 1380 | + $this->assertPathLength($directory); |
|
| 1381 | + if (!Filesystem::isValidPath($directory)) { |
|
| 1382 | + return []; |
|
| 1383 | + } |
|
| 1384 | + $path = $this->getAbsolutePath($directory); |
|
| 1385 | + $path = Filesystem::normalizePath($path); |
|
| 1386 | + $mount = $this->getMount($directory); |
|
| 1387 | + $storage = $mount->getStorage(); |
|
| 1388 | + $internalPath = $mount->getInternalPath($path); |
|
| 1389 | + if ($storage) { |
|
| 1390 | + $cache = $storage->getCache($internalPath); |
|
| 1391 | + $user = \OC_User::getUser(); |
|
| 1392 | + |
|
| 1393 | + $data = $this->getCacheEntry($storage, $internalPath, $directory); |
|
| 1394 | + |
|
| 1395 | + if (!$data instanceof ICacheEntry || !isset($data['fileid']) || !($data->getPermissions() && Constants::PERMISSION_READ)) { |
|
| 1396 | + return []; |
|
| 1397 | + } |
|
| 1398 | + |
|
| 1399 | + $folderId = $data['fileid']; |
|
| 1400 | + $contents = $cache->getFolderContentsById($folderId); //TODO: mimetype_filter |
|
| 1401 | + |
|
| 1402 | + $sharingDisabled = \OCP\Util::isSharingDisabledForUser(); |
|
| 1403 | + /** |
|
| 1404 | + * @var \OC\Files\FileInfo[] $files |
|
| 1405 | + */ |
|
| 1406 | + $files = array_map(function (ICacheEntry $content) use ($path, $storage, $mount, $sharingDisabled) { |
|
| 1407 | + if ($sharingDisabled) { |
|
| 1408 | + $content['permissions'] = $content['permissions'] & ~\OCP\Constants::PERMISSION_SHARE; |
|
| 1409 | + } |
|
| 1410 | + $owner = $this->getUserObjectForOwner($storage->getOwner($content['path'])); |
|
| 1411 | + return new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content, $mount, $owner); |
|
| 1412 | + }, $contents); |
|
| 1413 | + |
|
| 1414 | + //add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders |
|
| 1415 | + $mounts = Filesystem::getMountManager()->findIn($path); |
|
| 1416 | + $dirLength = strlen($path); |
|
| 1417 | + foreach ($mounts as $mount) { |
|
| 1418 | + $mountPoint = $mount->getMountPoint(); |
|
| 1419 | + $subStorage = $mount->getStorage(); |
|
| 1420 | + if ($subStorage) { |
|
| 1421 | + $subCache = $subStorage->getCache(''); |
|
| 1422 | + |
|
| 1423 | + $rootEntry = $subCache->get(''); |
|
| 1424 | + if (!$rootEntry) { |
|
| 1425 | + $subScanner = $subStorage->getScanner(''); |
|
| 1426 | + try { |
|
| 1427 | + $subScanner->scanFile(''); |
|
| 1428 | + } catch (\OCP\Files\StorageNotAvailableException $e) { |
|
| 1429 | + continue; |
|
| 1430 | + } catch (\OCP\Files\StorageInvalidException $e) { |
|
| 1431 | + continue; |
|
| 1432 | + } catch (\Exception $e) { |
|
| 1433 | + // sometimes when the storage is not available it can be any exception |
|
| 1434 | + \OCP\Util::writeLog( |
|
| 1435 | + 'core', |
|
| 1436 | + 'Exception while scanning storage "' . $subStorage->getId() . '": ' . |
|
| 1437 | + get_class($e) . ': ' . $e->getMessage(), |
|
| 1438 | + \OCP\Util::ERROR |
|
| 1439 | + ); |
|
| 1440 | + continue; |
|
| 1441 | + } |
|
| 1442 | + $rootEntry = $subCache->get(''); |
|
| 1443 | + } |
|
| 1444 | + |
|
| 1445 | + if ($rootEntry && ($rootEntry->getPermissions() && Constants::PERMISSION_READ)) { |
|
| 1446 | + $relativePath = trim(substr($mountPoint, $dirLength), '/'); |
|
| 1447 | + if ($pos = strpos($relativePath, '/')) { |
|
| 1448 | + //mountpoint inside subfolder add size to the correct folder |
|
| 1449 | + $entryName = substr($relativePath, 0, $pos); |
|
| 1450 | + foreach ($files as &$entry) { |
|
| 1451 | + if ($entry->getName() === $entryName) { |
|
| 1452 | + $entry->addSubEntry($rootEntry, $mountPoint); |
|
| 1453 | + } |
|
| 1454 | + } |
|
| 1455 | + } else { //mountpoint in this folder, add an entry for it |
|
| 1456 | + $rootEntry['name'] = $relativePath; |
|
| 1457 | + $rootEntry['type'] = $rootEntry['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file'; |
|
| 1458 | + $permissions = $rootEntry['permissions']; |
|
| 1459 | + // do not allow renaming/deleting the mount point if they are not shared files/folders |
|
| 1460 | + // for shared files/folders we use the permissions given by the owner |
|
| 1461 | + if ($mount instanceof MoveableMount) { |
|
| 1462 | + $rootEntry['permissions'] = $permissions | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE; |
|
| 1463 | + } else { |
|
| 1464 | + $rootEntry['permissions'] = $permissions & (\OCP\Constants::PERMISSION_ALL - (\OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE)); |
|
| 1465 | + } |
|
| 1466 | + |
|
| 1467 | + //remove any existing entry with the same name |
|
| 1468 | + foreach ($files as $i => $file) { |
|
| 1469 | + if ($file['name'] === $rootEntry['name']) { |
|
| 1470 | + unset($files[$i]); |
|
| 1471 | + break; |
|
| 1472 | + } |
|
| 1473 | + } |
|
| 1474 | + $rootEntry['path'] = substr(Filesystem::normalizePath($path . '/' . $rootEntry['name']), strlen($user) + 2); // full path without /$user/ |
|
| 1475 | + |
|
| 1476 | + // if sharing was disabled for the user we remove the share permissions |
|
| 1477 | + if (\OCP\Util::isSharingDisabledForUser()) { |
|
| 1478 | + $rootEntry['permissions'] = $rootEntry['permissions'] & ~\OCP\Constants::PERMISSION_SHARE; |
|
| 1479 | + } |
|
| 1480 | + |
|
| 1481 | + $owner = $this->getUserObjectForOwner($subStorage->getOwner('')); |
|
| 1482 | + $files[] = new FileInfo($path . '/' . $rootEntry['name'], $subStorage, '', $rootEntry, $mount, $owner); |
|
| 1483 | + } |
|
| 1484 | + } |
|
| 1485 | + } |
|
| 1486 | + } |
|
| 1487 | + |
|
| 1488 | + if ($mimetype_filter) { |
|
| 1489 | + $files = array_filter($files, function (FileInfo $file) use ($mimetype_filter) { |
|
| 1490 | + if (strpos($mimetype_filter, '/')) { |
|
| 1491 | + return $file->getMimetype() === $mimetype_filter; |
|
| 1492 | + } else { |
|
| 1493 | + return $file->getMimePart() === $mimetype_filter; |
|
| 1494 | + } |
|
| 1495 | + }); |
|
| 1496 | + } |
|
| 1497 | + |
|
| 1498 | + return $files; |
|
| 1499 | + } else { |
|
| 1500 | + return []; |
|
| 1501 | + } |
|
| 1502 | + } |
|
| 1503 | + |
|
| 1504 | + /** |
|
| 1505 | + * change file metadata |
|
| 1506 | + * |
|
| 1507 | + * @param string $path |
|
| 1508 | + * @param array|\OCP\Files\FileInfo $data |
|
| 1509 | + * @return int |
|
| 1510 | + * |
|
| 1511 | + * returns the fileid of the updated file |
|
| 1512 | + */ |
|
| 1513 | + public function putFileInfo($path, $data) { |
|
| 1514 | + $this->assertPathLength($path); |
|
| 1515 | + if ($data instanceof FileInfo) { |
|
| 1516 | + $data = $data->getData(); |
|
| 1517 | + } |
|
| 1518 | + $path = Filesystem::normalizePath($this->fakeRoot . '/' . $path); |
|
| 1519 | + /** |
|
| 1520 | + * @var \OC\Files\Storage\Storage $storage |
|
| 1521 | + * @var string $internalPath |
|
| 1522 | + */ |
|
| 1523 | + list($storage, $internalPath) = Filesystem::resolvePath($path); |
|
| 1524 | + if ($storage) { |
|
| 1525 | + $cache = $storage->getCache($path); |
|
| 1526 | + |
|
| 1527 | + if (!$cache->inCache($internalPath)) { |
|
| 1528 | + $scanner = $storage->getScanner($internalPath); |
|
| 1529 | + $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); |
|
| 1530 | + } |
|
| 1531 | + |
|
| 1532 | + return $cache->put($internalPath, $data); |
|
| 1533 | + } else { |
|
| 1534 | + return -1; |
|
| 1535 | + } |
|
| 1536 | + } |
|
| 1537 | + |
|
| 1538 | + /** |
|
| 1539 | + * search for files with the name matching $query |
|
| 1540 | + * |
|
| 1541 | + * @param string $query |
|
| 1542 | + * @return FileInfo[] |
|
| 1543 | + */ |
|
| 1544 | + public function search($query) { |
|
| 1545 | + return $this->searchCommon('search', array('%' . $query . '%')); |
|
| 1546 | + } |
|
| 1547 | + |
|
| 1548 | + /** |
|
| 1549 | + * search for files with the name matching $query |
|
| 1550 | + * |
|
| 1551 | + * @param string $query |
|
| 1552 | + * @return FileInfo[] |
|
| 1553 | + */ |
|
| 1554 | + public function searchRaw($query) { |
|
| 1555 | + return $this->searchCommon('search', array($query)); |
|
| 1556 | + } |
|
| 1557 | + |
|
| 1558 | + /** |
|
| 1559 | + * search for files by mimetype |
|
| 1560 | + * |
|
| 1561 | + * @param string $mimetype |
|
| 1562 | + * @return FileInfo[] |
|
| 1563 | + */ |
|
| 1564 | + public function searchByMime($mimetype) { |
|
| 1565 | + return $this->searchCommon('searchByMime', array($mimetype)); |
|
| 1566 | + } |
|
| 1567 | + |
|
| 1568 | + /** |
|
| 1569 | + * search for files by tag |
|
| 1570 | + * |
|
| 1571 | + * @param string|int $tag name or tag id |
|
| 1572 | + * @param string $userId owner of the tags |
|
| 1573 | + * @return FileInfo[] |
|
| 1574 | + */ |
|
| 1575 | + public function searchByTag($tag, $userId) { |
|
| 1576 | + return $this->searchCommon('searchByTag', array($tag, $userId)); |
|
| 1577 | + } |
|
| 1578 | + |
|
| 1579 | + /** |
|
| 1580 | + * @param string $method cache method |
|
| 1581 | + * @param array $args |
|
| 1582 | + * @return FileInfo[] |
|
| 1583 | + */ |
|
| 1584 | + private function searchCommon($method, $args) { |
|
| 1585 | + $files = array(); |
|
| 1586 | + $rootLength = strlen($this->fakeRoot); |
|
| 1587 | + |
|
| 1588 | + $mount = $this->getMount(''); |
|
| 1589 | + $mountPoint = $mount->getMountPoint(); |
|
| 1590 | + $storage = $mount->getStorage(); |
|
| 1591 | + if ($storage) { |
|
| 1592 | + $cache = $storage->getCache(''); |
|
| 1593 | + |
|
| 1594 | + $results = call_user_func_array(array($cache, $method), $args); |
|
| 1595 | + foreach ($results as $result) { |
|
| 1596 | + if (substr($mountPoint . $result['path'], 0, $rootLength + 1) === $this->fakeRoot . '/') { |
|
| 1597 | + $internalPath = $result['path']; |
|
| 1598 | + $path = $mountPoint . $result['path']; |
|
| 1599 | + $result['path'] = substr($mountPoint . $result['path'], $rootLength); |
|
| 1600 | + $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath)); |
|
| 1601 | + $files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner); |
|
| 1602 | + } |
|
| 1603 | + } |
|
| 1604 | + |
|
| 1605 | + $mounts = Filesystem::getMountManager()->findIn($this->fakeRoot); |
|
| 1606 | + foreach ($mounts as $mount) { |
|
| 1607 | + $mountPoint = $mount->getMountPoint(); |
|
| 1608 | + $storage = $mount->getStorage(); |
|
| 1609 | + if ($storage) { |
|
| 1610 | + $cache = $storage->getCache(''); |
|
| 1611 | + |
|
| 1612 | + $relativeMountPoint = substr($mountPoint, $rootLength); |
|
| 1613 | + $results = call_user_func_array(array($cache, $method), $args); |
|
| 1614 | + if ($results) { |
|
| 1615 | + foreach ($results as $result) { |
|
| 1616 | + $internalPath = $result['path']; |
|
| 1617 | + $result['path'] = rtrim($relativeMountPoint . $result['path'], '/'); |
|
| 1618 | + $path = rtrim($mountPoint . $internalPath, '/'); |
|
| 1619 | + $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath)); |
|
| 1620 | + $files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner); |
|
| 1621 | + } |
|
| 1622 | + } |
|
| 1623 | + } |
|
| 1624 | + } |
|
| 1625 | + } |
|
| 1626 | + return $files; |
|
| 1627 | + } |
|
| 1628 | + |
|
| 1629 | + /** |
|
| 1630 | + * Get the owner for a file or folder |
|
| 1631 | + * |
|
| 1632 | + * @param string $path |
|
| 1633 | + * @return string the user id of the owner |
|
| 1634 | + * @throws NotFoundException |
|
| 1635 | + */ |
|
| 1636 | + public function getOwner($path) { |
|
| 1637 | + $info = $this->getFileInfo($path); |
|
| 1638 | + if (!$info) { |
|
| 1639 | + throw new NotFoundException($path . ' not found while trying to get owner'); |
|
| 1640 | + } |
|
| 1641 | + return $info->getOwner()->getUID(); |
|
| 1642 | + } |
|
| 1643 | + |
|
| 1644 | + /** |
|
| 1645 | + * get the ETag for a file or folder |
|
| 1646 | + * |
|
| 1647 | + * @param string $path |
|
| 1648 | + * @return string |
|
| 1649 | + */ |
|
| 1650 | + public function getETag($path) { |
|
| 1651 | + /** |
|
| 1652 | + * @var Storage\Storage $storage |
|
| 1653 | + * @var string $internalPath |
|
| 1654 | + */ |
|
| 1655 | + list($storage, $internalPath) = $this->resolvePath($path); |
|
| 1656 | + if ($storage) { |
|
| 1657 | + return $storage->getETag($internalPath); |
|
| 1658 | + } else { |
|
| 1659 | + return null; |
|
| 1660 | + } |
|
| 1661 | + } |
|
| 1662 | + |
|
| 1663 | + /** |
|
| 1664 | + * Get the path of a file by id, relative to the view |
|
| 1665 | + * |
|
| 1666 | + * Note that the resulting path is not guarantied to be unique for the id, multiple paths can point to the same file |
|
| 1667 | + * |
|
| 1668 | + * @param int $id |
|
| 1669 | + * @throws NotFoundException |
|
| 1670 | + * @return string |
|
| 1671 | + */ |
|
| 1672 | + public function getPath($id) { |
|
| 1673 | + $id = (int)$id; |
|
| 1674 | + $manager = Filesystem::getMountManager(); |
|
| 1675 | + $mounts = $manager->findIn($this->fakeRoot); |
|
| 1676 | + $mounts[] = $manager->find($this->fakeRoot); |
|
| 1677 | + // reverse the array so we start with the storage this view is in |
|
| 1678 | + // which is the most likely to contain the file we're looking for |
|
| 1679 | + $mounts = array_reverse($mounts); |
|
| 1680 | + foreach ($mounts as $mount) { |
|
| 1681 | + /** |
|
| 1682 | + * @var \OC\Files\Mount\MountPoint $mount |
|
| 1683 | + */ |
|
| 1684 | + if ($mount->getStorage()) { |
|
| 1685 | + $cache = $mount->getStorage()->getCache(); |
|
| 1686 | + $internalPath = $cache->getPathById($id); |
|
| 1687 | + if (is_string($internalPath)) { |
|
| 1688 | + $fullPath = $mount->getMountPoint() . $internalPath; |
|
| 1689 | + if (!is_null($path = $this->getRelativePath($fullPath))) { |
|
| 1690 | + return $path; |
|
| 1691 | + } |
|
| 1692 | + } |
|
| 1693 | + } |
|
| 1694 | + } |
|
| 1695 | + throw new NotFoundException(sprintf('File with id "%s" has not been found.', $id)); |
|
| 1696 | + } |
|
| 1697 | + |
|
| 1698 | + /** |
|
| 1699 | + * @param string $path |
|
| 1700 | + * @throws InvalidPathException |
|
| 1701 | + */ |
|
| 1702 | + private function assertPathLength($path) { |
|
| 1703 | + $maxLen = min(PHP_MAXPATHLEN, 4000); |
|
| 1704 | + // Check for the string length - performed using isset() instead of strlen() |
|
| 1705 | + // because isset() is about 5x-40x faster. |
|
| 1706 | + if (isset($path[$maxLen])) { |
|
| 1707 | + $pathLen = strlen($path); |
|
| 1708 | + throw new \OCP\Files\InvalidPathException("Path length($pathLen) exceeds max path length($maxLen): $path"); |
|
| 1709 | + } |
|
| 1710 | + } |
|
| 1711 | + |
|
| 1712 | + /** |
|
| 1713 | + * check if it is allowed to move a mount point to a given target. |
|
| 1714 | + * It is not allowed to move a mount point into a different mount point or |
|
| 1715 | + * into an already shared folder |
|
| 1716 | + * |
|
| 1717 | + * @param string $target path |
|
| 1718 | + * @return boolean |
|
| 1719 | + */ |
|
| 1720 | + private function isTargetAllowed($target) { |
|
| 1721 | + |
|
| 1722 | + list($targetStorage, $targetInternalPath) = \OC\Files\Filesystem::resolvePath($target); |
|
| 1723 | + if (!$targetStorage->instanceOfStorage('\OCP\Files\IHomeStorage')) { |
|
| 1724 | + \OCP\Util::writeLog('files', |
|
| 1725 | + 'It is not allowed to move one mount point into another one', |
|
| 1726 | + \OCP\Util::DEBUG); |
|
| 1727 | + return false; |
|
| 1728 | + } |
|
| 1729 | + |
|
| 1730 | + // note: cannot use the view because the target is already locked |
|
| 1731 | + $fileId = (int)$targetStorage->getCache()->getId($targetInternalPath); |
|
| 1732 | + if ($fileId === -1) { |
|
| 1733 | + // target might not exist, need to check parent instead |
|
| 1734 | + $fileId = (int)$targetStorage->getCache()->getId(dirname($targetInternalPath)); |
|
| 1735 | + } |
|
| 1736 | + |
|
| 1737 | + // check if any of the parents were shared by the current owner (include collections) |
|
| 1738 | + $shares = \OCP\Share::getItemShared( |
|
| 1739 | + 'folder', |
|
| 1740 | + $fileId, |
|
| 1741 | + \OCP\Share::FORMAT_NONE, |
|
| 1742 | + null, |
|
| 1743 | + true |
|
| 1744 | + ); |
|
| 1745 | + |
|
| 1746 | + if (count($shares) > 0) { |
|
| 1747 | + \OCP\Util::writeLog('files', |
|
| 1748 | + 'It is not allowed to move one mount point into a shared folder', |
|
| 1749 | + \OCP\Util::DEBUG); |
|
| 1750 | + return false; |
|
| 1751 | + } |
|
| 1752 | + |
|
| 1753 | + return true; |
|
| 1754 | + } |
|
| 1755 | + |
|
| 1756 | + /** |
|
| 1757 | + * Get a fileinfo object for files that are ignored in the cache (part files) |
|
| 1758 | + * |
|
| 1759 | + * @param string $path |
|
| 1760 | + * @return \OCP\Files\FileInfo |
|
| 1761 | + */ |
|
| 1762 | + private function getPartFileInfo($path) { |
|
| 1763 | + $mount = $this->getMount($path); |
|
| 1764 | + $storage = $mount->getStorage(); |
|
| 1765 | + $internalPath = $mount->getInternalPath($this->getAbsolutePath($path)); |
|
| 1766 | + $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath)); |
|
| 1767 | + return new FileInfo( |
|
| 1768 | + $this->getAbsolutePath($path), |
|
| 1769 | + $storage, |
|
| 1770 | + $internalPath, |
|
| 1771 | + [ |
|
| 1772 | + 'fileid' => null, |
|
| 1773 | + 'mimetype' => $storage->getMimeType($internalPath), |
|
| 1774 | + 'name' => basename($path), |
|
| 1775 | + 'etag' => null, |
|
| 1776 | + 'size' => $storage->filesize($internalPath), |
|
| 1777 | + 'mtime' => $storage->filemtime($internalPath), |
|
| 1778 | + 'encrypted' => false, |
|
| 1779 | + 'permissions' => \OCP\Constants::PERMISSION_ALL |
|
| 1780 | + ], |
|
| 1781 | + $mount, |
|
| 1782 | + $owner |
|
| 1783 | + ); |
|
| 1784 | + } |
|
| 1785 | + |
|
| 1786 | + /** |
|
| 1787 | + * @param string $path |
|
| 1788 | + * @param string $fileName |
|
| 1789 | + * @throws InvalidPathException |
|
| 1790 | + */ |
|
| 1791 | + public function verifyPath($path, $fileName) { |
|
| 1792 | + try { |
|
| 1793 | + /** @type \OCP\Files\Storage $storage */ |
|
| 1794 | + list($storage, $internalPath) = $this->resolvePath($path); |
|
| 1795 | + $storage->verifyPath($internalPath, $fileName); |
|
| 1796 | + } catch (ReservedWordException $ex) { |
|
| 1797 | + $l = \OC::$server->getL10N('lib'); |
|
| 1798 | + throw new InvalidPathException($l->t('File name is a reserved word')); |
|
| 1799 | + } catch (InvalidCharacterInPathException $ex) { |
|
| 1800 | + $l = \OC::$server->getL10N('lib'); |
|
| 1801 | + throw new InvalidPathException($l->t('File name contains at least one invalid character')); |
|
| 1802 | + } catch (FileNameTooLongException $ex) { |
|
| 1803 | + $l = \OC::$server->getL10N('lib'); |
|
| 1804 | + throw new InvalidPathException($l->t('File name is too long')); |
|
| 1805 | + } catch (InvalidDirectoryException $ex) { |
|
| 1806 | + $l = \OC::$server->getL10N('lib'); |
|
| 1807 | + throw new InvalidPathException($l->t('Dot files are not allowed')); |
|
| 1808 | + } catch (EmptyFileNameException $ex) { |
|
| 1809 | + $l = \OC::$server->getL10N('lib'); |
|
| 1810 | + throw new InvalidPathException($l->t('Empty filename is not allowed')); |
|
| 1811 | + } |
|
| 1812 | + } |
|
| 1813 | + |
|
| 1814 | + /** |
|
| 1815 | + * get all parent folders of $path |
|
| 1816 | + * |
|
| 1817 | + * @param string $path |
|
| 1818 | + * @return string[] |
|
| 1819 | + */ |
|
| 1820 | + private function getParents($path) { |
|
| 1821 | + $path = trim($path, '/'); |
|
| 1822 | + if (!$path) { |
|
| 1823 | + return []; |
|
| 1824 | + } |
|
| 1825 | + |
|
| 1826 | + $parts = explode('/', $path); |
|
| 1827 | + |
|
| 1828 | + // remove the single file |
|
| 1829 | + array_pop($parts); |
|
| 1830 | + $result = array('/'); |
|
| 1831 | + $resultPath = ''; |
|
| 1832 | + foreach ($parts as $part) { |
|
| 1833 | + if ($part) { |
|
| 1834 | + $resultPath .= '/' . $part; |
|
| 1835 | + $result[] = $resultPath; |
|
| 1836 | + } |
|
| 1837 | + } |
|
| 1838 | + return $result; |
|
| 1839 | + } |
|
| 1840 | + |
|
| 1841 | + /** |
|
| 1842 | + * Returns the mount point for which to lock |
|
| 1843 | + * |
|
| 1844 | + * @param string $absolutePath absolute path |
|
| 1845 | + * @param bool $useParentMount true to return parent mount instead of whatever |
|
| 1846 | + * is mounted directly on the given path, false otherwise |
|
| 1847 | + * @return \OC\Files\Mount\MountPoint mount point for which to apply locks |
|
| 1848 | + */ |
|
| 1849 | + private function getMountForLock($absolutePath, $useParentMount = false) { |
|
| 1850 | + $results = []; |
|
| 1851 | + $mount = Filesystem::getMountManager()->find($absolutePath); |
|
| 1852 | + if (!$mount) { |
|
| 1853 | + return $results; |
|
| 1854 | + } |
|
| 1855 | + |
|
| 1856 | + if ($useParentMount) { |
|
| 1857 | + // find out if something is mounted directly on the path |
|
| 1858 | + $internalPath = $mount->getInternalPath($absolutePath); |
|
| 1859 | + if ($internalPath === '') { |
|
| 1860 | + // resolve the parent mount instead |
|
| 1861 | + $mount = Filesystem::getMountManager()->find(dirname($absolutePath)); |
|
| 1862 | + } |
|
| 1863 | + } |
|
| 1864 | + |
|
| 1865 | + return $mount; |
|
| 1866 | + } |
|
| 1867 | + |
|
| 1868 | + /** |
|
| 1869 | + * Lock the given path |
|
| 1870 | + * |
|
| 1871 | + * @param string $path the path of the file to lock, relative to the view |
|
| 1872 | + * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE |
|
| 1873 | + * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage |
|
| 1874 | + * |
|
| 1875 | + * @return bool False if the path is excluded from locking, true otherwise |
|
| 1876 | + * @throws \OCP\Lock\LockedException if the path is already locked |
|
| 1877 | + */ |
|
| 1878 | + private function lockPath($path, $type, $lockMountPoint = false) { |
|
| 1879 | + $absolutePath = $this->getAbsolutePath($path); |
|
| 1880 | + $absolutePath = Filesystem::normalizePath($absolutePath); |
|
| 1881 | + if (!$this->shouldLockFile($absolutePath)) { |
|
| 1882 | + return false; |
|
| 1883 | + } |
|
| 1884 | + |
|
| 1885 | + $mount = $this->getMountForLock($absolutePath, $lockMountPoint); |
|
| 1886 | + if ($mount) { |
|
| 1887 | + try { |
|
| 1888 | + $storage = $mount->getStorage(); |
|
| 1889 | + if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) { |
|
| 1890 | + $storage->acquireLock( |
|
| 1891 | + $mount->getInternalPath($absolutePath), |
|
| 1892 | + $type, |
|
| 1893 | + $this->lockingProvider |
|
| 1894 | + ); |
|
| 1895 | + } |
|
| 1896 | + } catch (\OCP\Lock\LockedException $e) { |
|
| 1897 | + // rethrow with the a human-readable path |
|
| 1898 | + throw new \OCP\Lock\LockedException( |
|
| 1899 | + $this->getPathRelativeToFiles($absolutePath), |
|
| 1900 | + $e |
|
| 1901 | + ); |
|
| 1902 | + } |
|
| 1903 | + } |
|
| 1904 | + |
|
| 1905 | + return true; |
|
| 1906 | + } |
|
| 1907 | + |
|
| 1908 | + /** |
|
| 1909 | + * Change the lock type |
|
| 1910 | + * |
|
| 1911 | + * @param string $path the path of the file to lock, relative to the view |
|
| 1912 | + * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE |
|
| 1913 | + * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage |
|
| 1914 | + * |
|
| 1915 | + * @return bool False if the path is excluded from locking, true otherwise |
|
| 1916 | + * @throws \OCP\Lock\LockedException if the path is already locked |
|
| 1917 | + */ |
|
| 1918 | + public function changeLock($path, $type, $lockMountPoint = false) { |
|
| 1919 | + $path = Filesystem::normalizePath($path); |
|
| 1920 | + $absolutePath = $this->getAbsolutePath($path); |
|
| 1921 | + $absolutePath = Filesystem::normalizePath($absolutePath); |
|
| 1922 | + if (!$this->shouldLockFile($absolutePath)) { |
|
| 1923 | + return false; |
|
| 1924 | + } |
|
| 1925 | + |
|
| 1926 | + $mount = $this->getMountForLock($absolutePath, $lockMountPoint); |
|
| 1927 | + if ($mount) { |
|
| 1928 | + try { |
|
| 1929 | + $storage = $mount->getStorage(); |
|
| 1930 | + if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) { |
|
| 1931 | + $storage->changeLock( |
|
| 1932 | + $mount->getInternalPath($absolutePath), |
|
| 1933 | + $type, |
|
| 1934 | + $this->lockingProvider |
|
| 1935 | + ); |
|
| 1936 | + } |
|
| 1937 | + } catch (\OCP\Lock\LockedException $e) { |
|
| 1938 | + // rethrow with the a human-readable path |
|
| 1939 | + throw new \OCP\Lock\LockedException( |
|
| 1940 | + $this->getPathRelativeToFiles($absolutePath), |
|
| 1941 | + $e |
|
| 1942 | + ); |
|
| 1943 | + } |
|
| 1944 | + } |
|
| 1945 | + |
|
| 1946 | + return true; |
|
| 1947 | + } |
|
| 1948 | + |
|
| 1949 | + /** |
|
| 1950 | + * Unlock the given path |
|
| 1951 | + * |
|
| 1952 | + * @param string $path the path of the file to unlock, relative to the view |
|
| 1953 | + * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE |
|
| 1954 | + * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage |
|
| 1955 | + * |
|
| 1956 | + * @return bool False if the path is excluded from locking, true otherwise |
|
| 1957 | + */ |
|
| 1958 | + private function unlockPath($path, $type, $lockMountPoint = false) { |
|
| 1959 | + $absolutePath = $this->getAbsolutePath($path); |
|
| 1960 | + $absolutePath = Filesystem::normalizePath($absolutePath); |
|
| 1961 | + if (!$this->shouldLockFile($absolutePath)) { |
|
| 1962 | + return false; |
|
| 1963 | + } |
|
| 1964 | + |
|
| 1965 | + $mount = $this->getMountForLock($absolutePath, $lockMountPoint); |
|
| 1966 | + if ($mount) { |
|
| 1967 | + $storage = $mount->getStorage(); |
|
| 1968 | + if ($storage && $storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) { |
|
| 1969 | + $storage->releaseLock( |
|
| 1970 | + $mount->getInternalPath($absolutePath), |
|
| 1971 | + $type, |
|
| 1972 | + $this->lockingProvider |
|
| 1973 | + ); |
|
| 1974 | + } |
|
| 1975 | + } |
|
| 1976 | + |
|
| 1977 | + return true; |
|
| 1978 | + } |
|
| 1979 | + |
|
| 1980 | + /** |
|
| 1981 | + * Lock a path and all its parents up to the root of the view |
|
| 1982 | + * |
|
| 1983 | + * @param string $path the path of the file to lock relative to the view |
|
| 1984 | + * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE |
|
| 1985 | + * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage |
|
| 1986 | + * |
|
| 1987 | + * @return bool False if the path is excluded from locking, true otherwise |
|
| 1988 | + */ |
|
| 1989 | + public function lockFile($path, $type, $lockMountPoint = false) { |
|
| 1990 | + $absolutePath = $this->getAbsolutePath($path); |
|
| 1991 | + $absolutePath = Filesystem::normalizePath($absolutePath); |
|
| 1992 | + if (!$this->shouldLockFile($absolutePath)) { |
|
| 1993 | + return false; |
|
| 1994 | + } |
|
| 1995 | + |
|
| 1996 | + $this->lockPath($path, $type, $lockMountPoint); |
|
| 1997 | + |
|
| 1998 | + $parents = $this->getParents($path); |
|
| 1999 | + foreach ($parents as $parent) { |
|
| 2000 | + $this->lockPath($parent, ILockingProvider::LOCK_SHARED); |
|
| 2001 | + } |
|
| 2002 | + |
|
| 2003 | + return true; |
|
| 2004 | + } |
|
| 2005 | + |
|
| 2006 | + /** |
|
| 2007 | + * Unlock a path and all its parents up to the root of the view |
|
| 2008 | + * |
|
| 2009 | + * @param string $path the path of the file to lock relative to the view |
|
| 2010 | + * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE |
|
| 2011 | + * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage |
|
| 2012 | + * |
|
| 2013 | + * @return bool False if the path is excluded from locking, true otherwise |
|
| 2014 | + */ |
|
| 2015 | + public function unlockFile($path, $type, $lockMountPoint = false) { |
|
| 2016 | + $absolutePath = $this->getAbsolutePath($path); |
|
| 2017 | + $absolutePath = Filesystem::normalizePath($absolutePath); |
|
| 2018 | + if (!$this->shouldLockFile($absolutePath)) { |
|
| 2019 | + return false; |
|
| 2020 | + } |
|
| 2021 | + |
|
| 2022 | + $this->unlockPath($path, $type, $lockMountPoint); |
|
| 2023 | + |
|
| 2024 | + $parents = $this->getParents($path); |
|
| 2025 | + foreach ($parents as $parent) { |
|
| 2026 | + $this->unlockPath($parent, ILockingProvider::LOCK_SHARED); |
|
| 2027 | + } |
|
| 2028 | + |
|
| 2029 | + return true; |
|
| 2030 | + } |
|
| 2031 | + |
|
| 2032 | + /** |
|
| 2033 | + * Only lock files in data/user/files/ |
|
| 2034 | + * |
|
| 2035 | + * @param string $path Absolute path to the file/folder we try to (un)lock |
|
| 2036 | + * @return bool |
|
| 2037 | + */ |
|
| 2038 | + protected function shouldLockFile($path) { |
|
| 2039 | + $path = Filesystem::normalizePath($path); |
|
| 2040 | + |
|
| 2041 | + $pathSegments = explode('/', $path); |
|
| 2042 | + if (isset($pathSegments[2])) { |
|
| 2043 | + // E.g.: /username/files/path-to-file |
|
| 2044 | + return ($pathSegments[2] === 'files') && (count($pathSegments) > 3); |
|
| 2045 | + } |
|
| 2046 | + |
|
| 2047 | + return true; |
|
| 2048 | + } |
|
| 2049 | + |
|
| 2050 | + /** |
|
| 2051 | + * Shortens the given absolute path to be relative to |
|
| 2052 | + * "$user/files". |
|
| 2053 | + * |
|
| 2054 | + * @param string $absolutePath absolute path which is under "files" |
|
| 2055 | + * |
|
| 2056 | + * @return string path relative to "files" with trimmed slashes or null |
|
| 2057 | + * if the path was NOT relative to files |
|
| 2058 | + * |
|
| 2059 | + * @throws \InvalidArgumentException if the given path was not under "files" |
|
| 2060 | + * @since 8.1.0 |
|
| 2061 | + */ |
|
| 2062 | + public function getPathRelativeToFiles($absolutePath) { |
|
| 2063 | + $path = Filesystem::normalizePath($absolutePath); |
|
| 2064 | + $parts = explode('/', trim($path, '/'), 3); |
|
| 2065 | + // "$user", "files", "path/to/dir" |
|
| 2066 | + if (!isset($parts[1]) || $parts[1] !== 'files') { |
|
| 2067 | + throw new \InvalidArgumentException('$absolutePath must be relative to "files"'); |
|
| 2068 | + } |
|
| 2069 | + if (isset($parts[2])) { |
|
| 2070 | + return $parts[2]; |
|
| 2071 | + } |
|
| 2072 | + return ''; |
|
| 2073 | + } |
|
| 2074 | + |
|
| 2075 | + /** |
|
| 2076 | + * @param string $filename |
|
| 2077 | + * @return array |
|
| 2078 | + * @throws \OC\User\NoUserException |
|
| 2079 | + * @throws NotFoundException |
|
| 2080 | + */ |
|
| 2081 | + public function getUidAndFilename($filename) { |
|
| 2082 | + $info = $this->getFileInfo($filename); |
|
| 2083 | + if (!$info instanceof \OCP\Files\FileInfo) { |
|
| 2084 | + throw new NotFoundException($this->getAbsolutePath($filename) . ' not found'); |
|
| 2085 | + } |
|
| 2086 | + $uid = $info->getOwner()->getUID(); |
|
| 2087 | + if ($uid != \OCP\User::getUser()) { |
|
| 2088 | + Filesystem::initMountPoints($uid); |
|
| 2089 | + $ownerView = new View('/' . $uid . '/files'); |
|
| 2090 | + try { |
|
| 2091 | + $filename = $ownerView->getPath($info['fileid']); |
|
| 2092 | + } catch (NotFoundException $e) { |
|
| 2093 | + throw new NotFoundException('File with id ' . $info['fileid'] . ' not found for user ' . $uid); |
|
| 2094 | + } |
|
| 2095 | + } |
|
| 2096 | + return [$uid, $filename]; |
|
| 2097 | + } |
|
| 2098 | + |
|
| 2099 | + /** |
|
| 2100 | + * Creates parent non-existing folders |
|
| 2101 | + * |
|
| 2102 | + * @param string $filePath |
|
| 2103 | + * @return bool |
|
| 2104 | + */ |
|
| 2105 | + private function createParentDirectories($filePath) { |
|
| 2106 | + $directoryParts = explode('/', $filePath); |
|
| 2107 | + $directoryParts = array_filter($directoryParts); |
|
| 2108 | + foreach ($directoryParts as $key => $part) { |
|
| 2109 | + $currentPathElements = array_slice($directoryParts, 0, $key); |
|
| 2110 | + $currentPath = '/' . implode('/', $currentPathElements); |
|
| 2111 | + if ($this->is_file($currentPath)) { |
|
| 2112 | + return false; |
|
| 2113 | + } |
|
| 2114 | + if (!$this->file_exists($currentPath)) { |
|
| 2115 | + $this->mkdir($currentPath); |
|
| 2116 | + } |
|
| 2117 | + } |
|
| 2118 | + |
|
| 2119 | + return true; |
|
| 2120 | + } |
|
| 2121 | 2121 | } |
@@ -125,9 +125,9 @@ discard block |
||
| 125 | 125 | $path = '/'; |
| 126 | 126 | } |
| 127 | 127 | if ($path[0] !== '/') { |
| 128 | - $path = '/' . $path; |
|
| 128 | + $path = '/'.$path; |
|
| 129 | 129 | } |
| 130 | - return $this->fakeRoot . $path; |
|
| 130 | + return $this->fakeRoot.$path; |
|
| 131 | 131 | } |
| 132 | 132 | |
| 133 | 133 | /** |
@@ -139,7 +139,7 @@ discard block |
||
| 139 | 139 | public function chroot($fakeRoot) { |
| 140 | 140 | if (!$fakeRoot == '') { |
| 141 | 141 | if ($fakeRoot[0] !== '/') { |
| 142 | - $fakeRoot = '/' . $fakeRoot; |
|
| 142 | + $fakeRoot = '/'.$fakeRoot; |
|
| 143 | 143 | } |
| 144 | 144 | } |
| 145 | 145 | $this->fakeRoot = $fakeRoot; |
@@ -171,7 +171,7 @@ discard block |
||
| 171 | 171 | } |
| 172 | 172 | |
| 173 | 173 | // missing slashes can cause wrong matches! |
| 174 | - $root = rtrim($this->fakeRoot, '/') . '/'; |
|
| 174 | + $root = rtrim($this->fakeRoot, '/').'/'; |
|
| 175 | 175 | |
| 176 | 176 | if (strpos($path, $root) !== 0) { |
| 177 | 177 | return null; |
@@ -277,7 +277,7 @@ discard block |
||
| 277 | 277 | if ($mount instanceof MoveableMount) { |
| 278 | 278 | // cut of /user/files to get the relative path to data/user/files |
| 279 | 279 | $pathParts = explode('/', $path, 4); |
| 280 | - $relPath = '/' . $pathParts[3]; |
|
| 280 | + $relPath = '/'.$pathParts[3]; |
|
| 281 | 281 | $this->lockFile($relPath, ILockingProvider::LOCK_SHARED, true); |
| 282 | 282 | \OC_Hook::emit( |
| 283 | 283 | Filesystem::CLASSNAME, "umount", |
@@ -688,7 +688,7 @@ discard block |
||
| 688 | 688 | } |
| 689 | 689 | $postFix = (substr($path, -1, 1) === '/') ? '/' : ''; |
| 690 | 690 | $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); |
| 691 | - $mount = Filesystem::getMountManager()->find($absolutePath . $postFix); |
|
| 691 | + $mount = Filesystem::getMountManager()->find($absolutePath.$postFix); |
|
| 692 | 692 | if ($mount and $mount->getInternalPath($absolutePath) === '') { |
| 693 | 693 | return $this->removeMount($mount, $absolutePath); |
| 694 | 694 | } |
@@ -954,7 +954,7 @@ discard block |
||
| 954 | 954 | $hooks[] = 'write'; |
| 955 | 955 | break; |
| 956 | 956 | default: |
| 957 | - \OCP\Util::writeLog('core', 'invalid mode (' . $mode . ') for ' . $path, \OCP\Util::ERROR); |
|
| 957 | + \OCP\Util::writeLog('core', 'invalid mode ('.$mode.') for '.$path, \OCP\Util::ERROR); |
|
| 958 | 958 | } |
| 959 | 959 | |
| 960 | 960 | if ($mode !== 'r' && $mode !== 'w') { |
@@ -1058,7 +1058,7 @@ discard block |
||
| 1058 | 1058 | array(Filesystem::signal_param_path => $this->getHookPath($path)) |
| 1059 | 1059 | ); |
| 1060 | 1060 | } |
| 1061 | - list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix); |
|
| 1061 | + list($storage, $internalPath) = Filesystem::resolvePath($absolutePath.$postFix); |
|
| 1062 | 1062 | if ($storage) { |
| 1063 | 1063 | $result = $storage->hash($type, $internalPath, $raw); |
| 1064 | 1064 | return $result; |
@@ -1109,7 +1109,7 @@ discard block |
||
| 1109 | 1109 | |
| 1110 | 1110 | $run = $this->runHooks($hooks, $path); |
| 1111 | 1111 | /** @var \OC\Files\Storage\Storage $storage */ |
| 1112 | - list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix); |
|
| 1112 | + list($storage, $internalPath) = Filesystem::resolvePath($absolutePath.$postFix); |
|
| 1113 | 1113 | if ($run and $storage) { |
| 1114 | 1114 | if (in_array('write', $hooks) || in_array('delete', $hooks)) { |
| 1115 | 1115 | $this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE); |
@@ -1148,7 +1148,7 @@ discard block |
||
| 1148 | 1148 | $unlockLater = true; |
| 1149 | 1149 | // make sure our unlocking callback will still be called if connection is aborted |
| 1150 | 1150 | ignore_user_abort(true); |
| 1151 | - $result = CallbackWrapper::wrap($result, null, null, function () use ($hooks, $path) { |
|
| 1151 | + $result = CallbackWrapper::wrap($result, null, null, function() use ($hooks, $path) { |
|
| 1152 | 1152 | if (in_array('write', $hooks)) { |
| 1153 | 1153 | $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE); |
| 1154 | 1154 | } else if (in_array('read', $hooks)) { |
@@ -1209,7 +1209,7 @@ discard block |
||
| 1209 | 1209 | return true; |
| 1210 | 1210 | } |
| 1211 | 1211 | |
| 1212 | - return (strlen($fullPath) > strlen($defaultRoot)) && (substr($fullPath, 0, strlen($defaultRoot) + 1) === $defaultRoot . '/'); |
|
| 1212 | + return (strlen($fullPath) > strlen($defaultRoot)) && (substr($fullPath, 0, strlen($defaultRoot) + 1) === $defaultRoot.'/'); |
|
| 1213 | 1213 | } |
| 1214 | 1214 | |
| 1215 | 1215 | /** |
@@ -1228,7 +1228,7 @@ discard block |
||
| 1228 | 1228 | if ($hook != 'read') { |
| 1229 | 1229 | \OC_Hook::emit( |
| 1230 | 1230 | Filesystem::CLASSNAME, |
| 1231 | - $prefix . $hook, |
|
| 1231 | + $prefix.$hook, |
|
| 1232 | 1232 | array( |
| 1233 | 1233 | Filesystem::signal_param_run => &$run, |
| 1234 | 1234 | Filesystem::signal_param_path => $path |
@@ -1237,7 +1237,7 @@ discard block |
||
| 1237 | 1237 | } elseif (!$post) { |
| 1238 | 1238 | \OC_Hook::emit( |
| 1239 | 1239 | Filesystem::CLASSNAME, |
| 1240 | - $prefix . $hook, |
|
| 1240 | + $prefix.$hook, |
|
| 1241 | 1241 | array( |
| 1242 | 1242 | Filesystem::signal_param_path => $path |
| 1243 | 1243 | ) |
@@ -1332,7 +1332,7 @@ discard block |
||
| 1332 | 1332 | return $this->getPartFileInfo($path); |
| 1333 | 1333 | } |
| 1334 | 1334 | $relativePath = $path; |
| 1335 | - $path = Filesystem::normalizePath($this->fakeRoot . '/' . $path); |
|
| 1335 | + $path = Filesystem::normalizePath($this->fakeRoot.'/'.$path); |
|
| 1336 | 1336 | |
| 1337 | 1337 | $mount = Filesystem::getMountManager()->find($path); |
| 1338 | 1338 | $storage = $mount->getStorage(); |
@@ -1356,7 +1356,7 @@ discard block |
||
| 1356 | 1356 | //add the sizes of other mount points to the folder |
| 1357 | 1357 | $extOnly = ($includeMountPoints === 'ext'); |
| 1358 | 1358 | $mounts = Filesystem::getMountManager()->findIn($path); |
| 1359 | - $info->setSubMounts(array_filter($mounts, function (IMountPoint $mount) use ($extOnly) { |
|
| 1359 | + $info->setSubMounts(array_filter($mounts, function(IMountPoint $mount) use ($extOnly) { |
|
| 1360 | 1360 | $subStorage = $mount->getStorage(); |
| 1361 | 1361 | return !($extOnly && $subStorage instanceof \OCA\Files_Sharing\SharedStorage); |
| 1362 | 1362 | })); |
@@ -1403,12 +1403,12 @@ discard block |
||
| 1403 | 1403 | /** |
| 1404 | 1404 | * @var \OC\Files\FileInfo[] $files |
| 1405 | 1405 | */ |
| 1406 | - $files = array_map(function (ICacheEntry $content) use ($path, $storage, $mount, $sharingDisabled) { |
|
| 1406 | + $files = array_map(function(ICacheEntry $content) use ($path, $storage, $mount, $sharingDisabled) { |
|
| 1407 | 1407 | if ($sharingDisabled) { |
| 1408 | 1408 | $content['permissions'] = $content['permissions'] & ~\OCP\Constants::PERMISSION_SHARE; |
| 1409 | 1409 | } |
| 1410 | 1410 | $owner = $this->getUserObjectForOwner($storage->getOwner($content['path'])); |
| 1411 | - return new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content, $mount, $owner); |
|
| 1411 | + return new FileInfo($path.'/'.$content['name'], $storage, $content['path'], $content, $mount, $owner); |
|
| 1412 | 1412 | }, $contents); |
| 1413 | 1413 | |
| 1414 | 1414 | //add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders |
@@ -1433,8 +1433,8 @@ discard block |
||
| 1433 | 1433 | // sometimes when the storage is not available it can be any exception |
| 1434 | 1434 | \OCP\Util::writeLog( |
| 1435 | 1435 | 'core', |
| 1436 | - 'Exception while scanning storage "' . $subStorage->getId() . '": ' . |
|
| 1437 | - get_class($e) . ': ' . $e->getMessage(), |
|
| 1436 | + 'Exception while scanning storage "'.$subStorage->getId().'": '. |
|
| 1437 | + get_class($e).': '.$e->getMessage(), |
|
| 1438 | 1438 | \OCP\Util::ERROR |
| 1439 | 1439 | ); |
| 1440 | 1440 | continue; |
@@ -1471,7 +1471,7 @@ discard block |
||
| 1471 | 1471 | break; |
| 1472 | 1472 | } |
| 1473 | 1473 | } |
| 1474 | - $rootEntry['path'] = substr(Filesystem::normalizePath($path . '/' . $rootEntry['name']), strlen($user) + 2); // full path without /$user/ |
|
| 1474 | + $rootEntry['path'] = substr(Filesystem::normalizePath($path.'/'.$rootEntry['name']), strlen($user) + 2); // full path without /$user/ |
|
| 1475 | 1475 | |
| 1476 | 1476 | // if sharing was disabled for the user we remove the share permissions |
| 1477 | 1477 | if (\OCP\Util::isSharingDisabledForUser()) { |
@@ -1479,14 +1479,14 @@ discard block |
||
| 1479 | 1479 | } |
| 1480 | 1480 | |
| 1481 | 1481 | $owner = $this->getUserObjectForOwner($subStorage->getOwner('')); |
| 1482 | - $files[] = new FileInfo($path . '/' . $rootEntry['name'], $subStorage, '', $rootEntry, $mount, $owner); |
|
| 1482 | + $files[] = new FileInfo($path.'/'.$rootEntry['name'], $subStorage, '', $rootEntry, $mount, $owner); |
|
| 1483 | 1483 | } |
| 1484 | 1484 | } |
| 1485 | 1485 | } |
| 1486 | 1486 | } |
| 1487 | 1487 | |
| 1488 | 1488 | if ($mimetype_filter) { |
| 1489 | - $files = array_filter($files, function (FileInfo $file) use ($mimetype_filter) { |
|
| 1489 | + $files = array_filter($files, function(FileInfo $file) use ($mimetype_filter) { |
|
| 1490 | 1490 | if (strpos($mimetype_filter, '/')) { |
| 1491 | 1491 | return $file->getMimetype() === $mimetype_filter; |
| 1492 | 1492 | } else { |
@@ -1515,7 +1515,7 @@ discard block |
||
| 1515 | 1515 | if ($data instanceof FileInfo) { |
| 1516 | 1516 | $data = $data->getData(); |
| 1517 | 1517 | } |
| 1518 | - $path = Filesystem::normalizePath($this->fakeRoot . '/' . $path); |
|
| 1518 | + $path = Filesystem::normalizePath($this->fakeRoot.'/'.$path); |
|
| 1519 | 1519 | /** |
| 1520 | 1520 | * @var \OC\Files\Storage\Storage $storage |
| 1521 | 1521 | * @var string $internalPath |
@@ -1542,7 +1542,7 @@ discard block |
||
| 1542 | 1542 | * @return FileInfo[] |
| 1543 | 1543 | */ |
| 1544 | 1544 | public function search($query) { |
| 1545 | - return $this->searchCommon('search', array('%' . $query . '%')); |
|
| 1545 | + return $this->searchCommon('search', array('%'.$query.'%')); |
|
| 1546 | 1546 | } |
| 1547 | 1547 | |
| 1548 | 1548 | /** |
@@ -1593,10 +1593,10 @@ discard block |
||
| 1593 | 1593 | |
| 1594 | 1594 | $results = call_user_func_array(array($cache, $method), $args); |
| 1595 | 1595 | foreach ($results as $result) { |
| 1596 | - if (substr($mountPoint . $result['path'], 0, $rootLength + 1) === $this->fakeRoot . '/') { |
|
| 1596 | + if (substr($mountPoint.$result['path'], 0, $rootLength + 1) === $this->fakeRoot.'/') { |
|
| 1597 | 1597 | $internalPath = $result['path']; |
| 1598 | - $path = $mountPoint . $result['path']; |
|
| 1599 | - $result['path'] = substr($mountPoint . $result['path'], $rootLength); |
|
| 1598 | + $path = $mountPoint.$result['path']; |
|
| 1599 | + $result['path'] = substr($mountPoint.$result['path'], $rootLength); |
|
| 1600 | 1600 | $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath)); |
| 1601 | 1601 | $files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner); |
| 1602 | 1602 | } |
@@ -1614,8 +1614,8 @@ discard block |
||
| 1614 | 1614 | if ($results) { |
| 1615 | 1615 | foreach ($results as $result) { |
| 1616 | 1616 | $internalPath = $result['path']; |
| 1617 | - $result['path'] = rtrim($relativeMountPoint . $result['path'], '/'); |
|
| 1618 | - $path = rtrim($mountPoint . $internalPath, '/'); |
|
| 1617 | + $result['path'] = rtrim($relativeMountPoint.$result['path'], '/'); |
|
| 1618 | + $path = rtrim($mountPoint.$internalPath, '/'); |
|
| 1619 | 1619 | $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath)); |
| 1620 | 1620 | $files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner); |
| 1621 | 1621 | } |
@@ -1636,7 +1636,7 @@ discard block |
||
| 1636 | 1636 | public function getOwner($path) { |
| 1637 | 1637 | $info = $this->getFileInfo($path); |
| 1638 | 1638 | if (!$info) { |
| 1639 | - throw new NotFoundException($path . ' not found while trying to get owner'); |
|
| 1639 | + throw new NotFoundException($path.' not found while trying to get owner'); |
|
| 1640 | 1640 | } |
| 1641 | 1641 | return $info->getOwner()->getUID(); |
| 1642 | 1642 | } |
@@ -1670,7 +1670,7 @@ discard block |
||
| 1670 | 1670 | * @return string |
| 1671 | 1671 | */ |
| 1672 | 1672 | public function getPath($id) { |
| 1673 | - $id = (int)$id; |
|
| 1673 | + $id = (int) $id; |
|
| 1674 | 1674 | $manager = Filesystem::getMountManager(); |
| 1675 | 1675 | $mounts = $manager->findIn($this->fakeRoot); |
| 1676 | 1676 | $mounts[] = $manager->find($this->fakeRoot); |
@@ -1685,7 +1685,7 @@ discard block |
||
| 1685 | 1685 | $cache = $mount->getStorage()->getCache(); |
| 1686 | 1686 | $internalPath = $cache->getPathById($id); |
| 1687 | 1687 | if (is_string($internalPath)) { |
| 1688 | - $fullPath = $mount->getMountPoint() . $internalPath; |
|
| 1688 | + $fullPath = $mount->getMountPoint().$internalPath; |
|
| 1689 | 1689 | if (!is_null($path = $this->getRelativePath($fullPath))) { |
| 1690 | 1690 | return $path; |
| 1691 | 1691 | } |
@@ -1728,10 +1728,10 @@ discard block |
||
| 1728 | 1728 | } |
| 1729 | 1729 | |
| 1730 | 1730 | // note: cannot use the view because the target is already locked |
| 1731 | - $fileId = (int)$targetStorage->getCache()->getId($targetInternalPath); |
|
| 1731 | + $fileId = (int) $targetStorage->getCache()->getId($targetInternalPath); |
|
| 1732 | 1732 | if ($fileId === -1) { |
| 1733 | 1733 | // target might not exist, need to check parent instead |
| 1734 | - $fileId = (int)$targetStorage->getCache()->getId(dirname($targetInternalPath)); |
|
| 1734 | + $fileId = (int) $targetStorage->getCache()->getId(dirname($targetInternalPath)); |
|
| 1735 | 1735 | } |
| 1736 | 1736 | |
| 1737 | 1737 | // check if any of the parents were shared by the current owner (include collections) |
@@ -1831,7 +1831,7 @@ discard block |
||
| 1831 | 1831 | $resultPath = ''; |
| 1832 | 1832 | foreach ($parts as $part) { |
| 1833 | 1833 | if ($part) { |
| 1834 | - $resultPath .= '/' . $part; |
|
| 1834 | + $resultPath .= '/'.$part; |
|
| 1835 | 1835 | $result[] = $resultPath; |
| 1836 | 1836 | } |
| 1837 | 1837 | } |
@@ -2081,16 +2081,16 @@ discard block |
||
| 2081 | 2081 | public function getUidAndFilename($filename) { |
| 2082 | 2082 | $info = $this->getFileInfo($filename); |
| 2083 | 2083 | if (!$info instanceof \OCP\Files\FileInfo) { |
| 2084 | - throw new NotFoundException($this->getAbsolutePath($filename) . ' not found'); |
|
| 2084 | + throw new NotFoundException($this->getAbsolutePath($filename).' not found'); |
|
| 2085 | 2085 | } |
| 2086 | 2086 | $uid = $info->getOwner()->getUID(); |
| 2087 | 2087 | if ($uid != \OCP\User::getUser()) { |
| 2088 | 2088 | Filesystem::initMountPoints($uid); |
| 2089 | - $ownerView = new View('/' . $uid . '/files'); |
|
| 2089 | + $ownerView = new View('/'.$uid.'/files'); |
|
| 2090 | 2090 | try { |
| 2091 | 2091 | $filename = $ownerView->getPath($info['fileid']); |
| 2092 | 2092 | } catch (NotFoundException $e) { |
| 2093 | - throw new NotFoundException('File with id ' . $info['fileid'] . ' not found for user ' . $uid); |
|
| 2093 | + throw new NotFoundException('File with id '.$info['fileid'].' not found for user '.$uid); |
|
| 2094 | 2094 | } |
| 2095 | 2095 | } |
| 2096 | 2096 | return [$uid, $filename]; |
@@ -2107,7 +2107,7 @@ discard block |
||
| 2107 | 2107 | $directoryParts = array_filter($directoryParts); |
| 2108 | 2108 | foreach ($directoryParts as $key => $part) { |
| 2109 | 2109 | $currentPathElements = array_slice($directoryParts, 0, $key); |
| 2110 | - $currentPath = '/' . implode('/', $currentPathElements); |
|
| 2110 | + $currentPath = '/'.implode('/', $currentPathElements); |
|
| 2111 | 2111 | if ($this->is_file($currentPath)) { |
| 2112 | 2112 | return false; |
| 2113 | 2113 | } |
@@ -37,7 +37,6 @@ |
||
| 37 | 37 | /** |
| 38 | 38 | * RemoveOldCalendarShares constructor. |
| 39 | 39 | * |
| 40 | - * @param IDBConnection $db |
|
| 41 | 40 | */ |
| 42 | 41 | public function __construct(IDBConnection $connection) { |
| 43 | 42 | $this->connection = $connection; |
@@ -32,73 +32,73 @@ |
||
| 32 | 32 | */ |
| 33 | 33 | class RemoveOldShares implements IRepairStep { |
| 34 | 34 | |
| 35 | - /** @var IDBConnection */ |
|
| 36 | - protected $connection; |
|
| 37 | - |
|
| 38 | - /** |
|
| 39 | - * RemoveOldCalendarShares constructor. |
|
| 40 | - * |
|
| 41 | - * @param IDBConnection $db |
|
| 42 | - */ |
|
| 43 | - public function __construct(IDBConnection $connection) { |
|
| 44 | - $this->connection = $connection; |
|
| 45 | - } |
|
| 46 | - |
|
| 47 | - /** |
|
| 48 | - * @return string |
|
| 49 | - */ |
|
| 50 | - public function getName() { |
|
| 51 | - return 'Remove old (< 9.0) calendar/contact shares'; |
|
| 52 | - } |
|
| 53 | - |
|
| 54 | - /** |
|
| 55 | - * @param IOutput $output |
|
| 56 | - */ |
|
| 57 | - public function run(IOutput $output) { |
|
| 58 | - $output->startProgress(4); |
|
| 59 | - |
|
| 60 | - $this->removeCalendarShares($output); |
|
| 61 | - $this->removeContactShares($output); |
|
| 62 | - |
|
| 63 | - $output->finishProgress(); |
|
| 64 | - } |
|
| 65 | - |
|
| 66 | - /** |
|
| 67 | - * @param IOutput $output |
|
| 68 | - */ |
|
| 69 | - private function removeCalendarShares(IOutput $output) { |
|
| 70 | - $qb = $this->connection->getQueryBuilder(); |
|
| 71 | - $qb->delete('share') |
|
| 72 | - ->where($qb->expr()->eq('item_type', $qb->createNamedParameter('calendar'))); |
|
| 73 | - $qb->execute(); |
|
| 74 | - |
|
| 75 | - $output->advance(); |
|
| 76 | - |
|
| 77 | - $qb = $this->connection->getQueryBuilder(); |
|
| 78 | - $qb->delete('share') |
|
| 79 | - ->where($qb->expr()->eq('item_type', $qb->createNamedParameter('event'))); |
|
| 80 | - $qb->execute(); |
|
| 81 | - |
|
| 82 | - $output->advance(); |
|
| 83 | - } |
|
| 84 | - |
|
| 85 | - /** |
|
| 86 | - * @param IOutput $output |
|
| 87 | - */ |
|
| 88 | - private function removeContactShares(IOutput $output) { |
|
| 89 | - $qb = $this->connection->getQueryBuilder(); |
|
| 90 | - $qb->delete('share') |
|
| 91 | - ->where($qb->expr()->eq('item_type', $qb->createNamedParameter('contact'))); |
|
| 92 | - $qb->execute(); |
|
| 93 | - |
|
| 94 | - $output->advance(); |
|
| 95 | - |
|
| 96 | - $qb = $this->connection->getQueryBuilder(); |
|
| 97 | - $qb->delete('share') |
|
| 98 | - ->where($qb->expr()->eq('item_type', $qb->createNamedParameter('addressbook'))); |
|
| 99 | - $qb->execute(); |
|
| 100 | - |
|
| 101 | - $output->advance(); |
|
| 102 | - } |
|
| 35 | + /** @var IDBConnection */ |
|
| 36 | + protected $connection; |
|
| 37 | + |
|
| 38 | + /** |
|
| 39 | + * RemoveOldCalendarShares constructor. |
|
| 40 | + * |
|
| 41 | + * @param IDBConnection $db |
|
| 42 | + */ |
|
| 43 | + public function __construct(IDBConnection $connection) { |
|
| 44 | + $this->connection = $connection; |
|
| 45 | + } |
|
| 46 | + |
|
| 47 | + /** |
|
| 48 | + * @return string |
|
| 49 | + */ |
|
| 50 | + public function getName() { |
|
| 51 | + return 'Remove old (< 9.0) calendar/contact shares'; |
|
| 52 | + } |
|
| 53 | + |
|
| 54 | + /** |
|
| 55 | + * @param IOutput $output |
|
| 56 | + */ |
|
| 57 | + public function run(IOutput $output) { |
|
| 58 | + $output->startProgress(4); |
|
| 59 | + |
|
| 60 | + $this->removeCalendarShares($output); |
|
| 61 | + $this->removeContactShares($output); |
|
| 62 | + |
|
| 63 | + $output->finishProgress(); |
|
| 64 | + } |
|
| 65 | + |
|
| 66 | + /** |
|
| 67 | + * @param IOutput $output |
|
| 68 | + */ |
|
| 69 | + private function removeCalendarShares(IOutput $output) { |
|
| 70 | + $qb = $this->connection->getQueryBuilder(); |
|
| 71 | + $qb->delete('share') |
|
| 72 | + ->where($qb->expr()->eq('item_type', $qb->createNamedParameter('calendar'))); |
|
| 73 | + $qb->execute(); |
|
| 74 | + |
|
| 75 | + $output->advance(); |
|
| 76 | + |
|
| 77 | + $qb = $this->connection->getQueryBuilder(); |
|
| 78 | + $qb->delete('share') |
|
| 79 | + ->where($qb->expr()->eq('item_type', $qb->createNamedParameter('event'))); |
|
| 80 | + $qb->execute(); |
|
| 81 | + |
|
| 82 | + $output->advance(); |
|
| 83 | + } |
|
| 84 | + |
|
| 85 | + /** |
|
| 86 | + * @param IOutput $output |
|
| 87 | + */ |
|
| 88 | + private function removeContactShares(IOutput $output) { |
|
| 89 | + $qb = $this->connection->getQueryBuilder(); |
|
| 90 | + $qb->delete('share') |
|
| 91 | + ->where($qb->expr()->eq('item_type', $qb->createNamedParameter('contact'))); |
|
| 92 | + $qb->execute(); |
|
| 93 | + |
|
| 94 | + $output->advance(); |
|
| 95 | + |
|
| 96 | + $qb = $this->connection->getQueryBuilder(); |
|
| 97 | + $qb->delete('share') |
|
| 98 | + ->where($qb->expr()->eq('item_type', $qb->createNamedParameter('addressbook'))); |
|
| 99 | + $qb->execute(); |
|
| 100 | + |
|
| 101 | + $output->advance(); |
|
| 102 | + } |
|
| 103 | 103 | } |
| 104 | 104 | |
@@ -303,6 +303,7 @@ |
||
| 303 | 303 | * get default share folder |
| 304 | 304 | * |
| 305 | 305 | * @param \OC\Files\View |
| 306 | + * @param View $view |
|
| 306 | 307 | * @return string |
| 307 | 308 | */ |
| 308 | 309 | public static function getShareFolder($view = null) { |
@@ -36,242 +36,242 @@ |
||
| 36 | 36 | |
| 37 | 37 | class Helper { |
| 38 | 38 | |
| 39 | - public static function registerHooks() { |
|
| 40 | - \OCP\Util::connectHook('OC_Filesystem', 'post_rename', '\OCA\Files_Sharing\Updater', 'renameHook'); |
|
| 41 | - \OCP\Util::connectHook('OC_Filesystem', 'post_delete', '\OCA\Files_Sharing\Hooks', 'unshareChildren'); |
|
| 42 | - |
|
| 43 | - \OCP\Util::connectHook('OC_User', 'post_deleteUser', '\OCA\Files_Sharing\Hooks', 'deleteUser'); |
|
| 44 | - } |
|
| 45 | - |
|
| 46 | - /** |
|
| 47 | - * Sets up the filesystem and user for public sharing |
|
| 48 | - * @param string $token string share token |
|
| 49 | - * @param string $relativePath optional path relative to the share |
|
| 50 | - * @param string $password optional password |
|
| 51 | - * @return array |
|
| 52 | - */ |
|
| 53 | - public static function setupFromToken($token, $relativePath = null, $password = null) { |
|
| 54 | - \OC_User::setIncognitoMode(true); |
|
| 55 | - |
|
| 56 | - $shareManager = \OC::$server->getShareManager(); |
|
| 57 | - |
|
| 58 | - try { |
|
| 59 | - $share = $shareManager->getShareByToken($token); |
|
| 60 | - } catch (ShareNotFound $e) { |
|
| 61 | - \OC_Response::setStatus(404); |
|
| 62 | - \OCP\Util::writeLog('core-preview', 'Passed token parameter is not valid', \OCP\Util::DEBUG); |
|
| 63 | - exit; |
|
| 64 | - } |
|
| 65 | - |
|
| 66 | - \OCP\JSON::checkUserExists($share->getShareOwner()); |
|
| 67 | - \OC_Util::tearDownFS(); |
|
| 68 | - \OC_Util::setupFS($share->getShareOwner()); |
|
| 69 | - |
|
| 70 | - |
|
| 71 | - try { |
|
| 72 | - $path = Filesystem::getPath($share->getNodeId()); |
|
| 73 | - } catch (NotFoundException $e) { |
|
| 74 | - \OCP\Util::writeLog('share', 'could not resolve linkItem', \OCP\Util::DEBUG); |
|
| 75 | - \OC_Response::setStatus(404); |
|
| 76 | - \OCP\JSON::error(array('success' => false)); |
|
| 77 | - exit(); |
|
| 78 | - } |
|
| 79 | - |
|
| 80 | - if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK && $share->getPassword() !== null) { |
|
| 81 | - if (!self::authenticate($share, $password)) { |
|
| 82 | - \OC_Response::setStatus(403); |
|
| 83 | - \OCP\JSON::error(array('success' => false)); |
|
| 84 | - exit(); |
|
| 85 | - } |
|
| 86 | - } |
|
| 87 | - |
|
| 88 | - $basePath = $path; |
|
| 89 | - |
|
| 90 | - if ($relativePath !== null && Filesystem::isReadable($basePath . $relativePath)) { |
|
| 91 | - $path .= Filesystem::normalizePath($relativePath); |
|
| 92 | - } |
|
| 93 | - |
|
| 94 | - return array( |
|
| 95 | - 'share' => $share, |
|
| 96 | - 'basePath' => $basePath, |
|
| 97 | - 'realPath' => $path |
|
| 98 | - ); |
|
| 99 | - } |
|
| 100 | - |
|
| 101 | - /** |
|
| 102 | - * Authenticate link item with the given password |
|
| 103 | - * or with the session if no password was given. |
|
| 104 | - * @param \OCP\Share\IShare $share |
|
| 105 | - * @param string $password optional password |
|
| 106 | - * |
|
| 107 | - * @return boolean true if authorized, false otherwise |
|
| 108 | - */ |
|
| 109 | - public static function authenticate($share, $password = null) { |
|
| 110 | - $shareManager = \OC::$server->getShareManager(); |
|
| 111 | - |
|
| 112 | - if ($password !== null) { |
|
| 113 | - if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { |
|
| 114 | - if ($shareManager->checkPassword($share, $password)) { |
|
| 115 | - \OC::$server->getSession()->set('public_link_authenticated', (string)$share->getId()); |
|
| 116 | - return true; |
|
| 117 | - } |
|
| 118 | - } |
|
| 119 | - } else { |
|
| 120 | - // not authenticated ? |
|
| 121 | - if (\OC::$server->getSession()->exists('public_link_authenticated') |
|
| 122 | - && \OC::$server->getSession()->get('public_link_authenticated') !== (string)$share->getId()) { |
|
| 123 | - return true; |
|
| 124 | - } |
|
| 125 | - } |
|
| 126 | - return false; |
|
| 127 | - } |
|
| 128 | - |
|
| 129 | - public static function getSharesFromItem($target) { |
|
| 130 | - $result = array(); |
|
| 131 | - $owner = Filesystem::getOwner($target); |
|
| 132 | - Filesystem::initMountPoints($owner); |
|
| 133 | - $info = Filesystem::getFileInfo($target); |
|
| 134 | - $ownerView = new View('/'.$owner.'/files'); |
|
| 135 | - if ( $owner != User::getUser() ) { |
|
| 136 | - $path = $ownerView->getPath($info['fileid']); |
|
| 137 | - } else { |
|
| 138 | - $path = $target; |
|
| 139 | - } |
|
| 140 | - |
|
| 141 | - |
|
| 142 | - $ids = array(); |
|
| 143 | - while ($path !== dirname($path)) { |
|
| 144 | - $info = $ownerView->getFileInfo($path); |
|
| 145 | - if ($info instanceof \OC\Files\FileInfo) { |
|
| 146 | - $ids[] = $info['fileid']; |
|
| 147 | - } else { |
|
| 148 | - \OCP\Util::writeLog('sharing', 'No fileinfo available for: ' . $path, \OCP\Util::WARN); |
|
| 149 | - } |
|
| 150 | - $path = dirname($path); |
|
| 151 | - } |
|
| 152 | - |
|
| 153 | - if (!empty($ids)) { |
|
| 154 | - |
|
| 155 | - $idList = array_chunk($ids, 99, true); |
|
| 156 | - |
|
| 157 | - foreach ($idList as $subList) { |
|
| 158 | - $statement = "SELECT `share_with`, `share_type`, `file_target` FROM `*PREFIX*share` WHERE `file_source` IN (" . implode(',', $subList) . ") AND `share_type` IN (0, 1, 2)"; |
|
| 159 | - $query = \OCP\DB::prepare($statement); |
|
| 160 | - $r = $query->execute(); |
|
| 161 | - $result = array_merge($result, $r->fetchAll()); |
|
| 162 | - } |
|
| 163 | - } |
|
| 164 | - |
|
| 165 | - return $result; |
|
| 166 | - } |
|
| 167 | - |
|
| 168 | - /** |
|
| 169 | - * get the UID of the owner of the file and the path to the file relative to |
|
| 170 | - * owners files folder |
|
| 171 | - * |
|
| 172 | - * @param $filename |
|
| 173 | - * @return array |
|
| 174 | - * @throws \OC\User\NoUserException |
|
| 175 | - */ |
|
| 176 | - public static function getUidAndFilename($filename) { |
|
| 177 | - $uid = Filesystem::getOwner($filename); |
|
| 178 | - $userManager = \OC::$server->getUserManager(); |
|
| 179 | - // if the user with the UID doesn't exists, e.g. because the UID points |
|
| 180 | - // to a remote user with a federated cloud ID we use the current logged-in |
|
| 181 | - // user. We need a valid local user to create the share |
|
| 182 | - if (!$userManager->userExists($uid)) { |
|
| 183 | - $uid = User::getUser(); |
|
| 184 | - } |
|
| 185 | - Filesystem::initMountPoints($uid); |
|
| 186 | - if ( $uid != User::getUser() ) { |
|
| 187 | - $info = Filesystem::getFileInfo($filename); |
|
| 188 | - $ownerView = new View('/'.$uid.'/files'); |
|
| 189 | - try { |
|
| 190 | - $filename = $ownerView->getPath($info['fileid']); |
|
| 191 | - } catch (NotFoundException $e) { |
|
| 192 | - $filename = null; |
|
| 193 | - } |
|
| 194 | - } |
|
| 195 | - return [$uid, $filename]; |
|
| 196 | - } |
|
| 197 | - |
|
| 198 | - /** |
|
| 199 | - * Format a path to be relative to the /user/files/ directory |
|
| 200 | - * @param string $path the absolute path |
|
| 201 | - * @return string e.g. turns '/admin/files/test.txt' into 'test.txt' |
|
| 202 | - */ |
|
| 203 | - public static function stripUserFilesPath($path) { |
|
| 204 | - $trimmed = ltrim($path, '/'); |
|
| 205 | - $split = explode('/', $trimmed); |
|
| 206 | - |
|
| 207 | - // it is not a file relative to data/user/files |
|
| 208 | - if (count($split) < 3 || $split[1] !== 'files') { |
|
| 209 | - return false; |
|
| 210 | - } |
|
| 211 | - |
|
| 212 | - $sliced = array_slice($split, 2); |
|
| 213 | - $relPath = implode('/', $sliced); |
|
| 214 | - |
|
| 215 | - return $relPath; |
|
| 216 | - } |
|
| 217 | - |
|
| 218 | - /** |
|
| 219 | - * check if file name already exists and generate unique target |
|
| 220 | - * |
|
| 221 | - * @param string $path |
|
| 222 | - * @param array $excludeList |
|
| 223 | - * @param View $view |
|
| 224 | - * @return string $path |
|
| 225 | - */ |
|
| 226 | - public static function generateUniqueTarget($path, $excludeList, $view) { |
|
| 227 | - $pathinfo = pathinfo($path); |
|
| 228 | - $ext = (isset($pathinfo['extension'])) ? '.'.$pathinfo['extension'] : ''; |
|
| 229 | - $name = $pathinfo['filename']; |
|
| 230 | - $dir = $pathinfo['dirname']; |
|
| 231 | - $i = 2; |
|
| 232 | - while ($view->file_exists($path) || in_array($path, $excludeList)) { |
|
| 233 | - $path = Filesystem::normalizePath($dir . '/' . $name . ' ('.$i.')' . $ext); |
|
| 234 | - $i++; |
|
| 235 | - } |
|
| 236 | - |
|
| 237 | - return $path; |
|
| 238 | - } |
|
| 239 | - |
|
| 240 | - /** |
|
| 241 | - * get default share folder |
|
| 242 | - * |
|
| 243 | - * @param \OC\Files\View |
|
| 244 | - * @return string |
|
| 245 | - */ |
|
| 246 | - public static function getShareFolder($view = null) { |
|
| 247 | - if ($view === null) { |
|
| 248 | - $view = Filesystem::getView(); |
|
| 249 | - } |
|
| 250 | - $shareFolder = \OC::$server->getConfig()->getSystemValue('share_folder', '/'); |
|
| 251 | - $shareFolder = Filesystem::normalizePath($shareFolder); |
|
| 252 | - |
|
| 253 | - if (!$view->file_exists($shareFolder)) { |
|
| 254 | - $dir = ''; |
|
| 255 | - $subdirs = explode('/', $shareFolder); |
|
| 256 | - foreach ($subdirs as $subdir) { |
|
| 257 | - $dir = $dir . '/' . $subdir; |
|
| 258 | - if (!$view->is_dir($dir)) { |
|
| 259 | - $view->mkdir($dir); |
|
| 260 | - } |
|
| 261 | - } |
|
| 262 | - } |
|
| 263 | - |
|
| 264 | - return $shareFolder; |
|
| 265 | - |
|
| 266 | - } |
|
| 267 | - |
|
| 268 | - /** |
|
| 269 | - * set default share folder |
|
| 270 | - * |
|
| 271 | - * @param string $shareFolder |
|
| 272 | - */ |
|
| 273 | - public static function setShareFolder($shareFolder) { |
|
| 274 | - \OC::$server->getConfig()->setSystemValue('share_folder', $shareFolder); |
|
| 275 | - } |
|
| 39 | + public static function registerHooks() { |
|
| 40 | + \OCP\Util::connectHook('OC_Filesystem', 'post_rename', '\OCA\Files_Sharing\Updater', 'renameHook'); |
|
| 41 | + \OCP\Util::connectHook('OC_Filesystem', 'post_delete', '\OCA\Files_Sharing\Hooks', 'unshareChildren'); |
|
| 42 | + |
|
| 43 | + \OCP\Util::connectHook('OC_User', 'post_deleteUser', '\OCA\Files_Sharing\Hooks', 'deleteUser'); |
|
| 44 | + } |
|
| 45 | + |
|
| 46 | + /** |
|
| 47 | + * Sets up the filesystem and user for public sharing |
|
| 48 | + * @param string $token string share token |
|
| 49 | + * @param string $relativePath optional path relative to the share |
|
| 50 | + * @param string $password optional password |
|
| 51 | + * @return array |
|
| 52 | + */ |
|
| 53 | + public static function setupFromToken($token, $relativePath = null, $password = null) { |
|
| 54 | + \OC_User::setIncognitoMode(true); |
|
| 55 | + |
|
| 56 | + $shareManager = \OC::$server->getShareManager(); |
|
| 57 | + |
|
| 58 | + try { |
|
| 59 | + $share = $shareManager->getShareByToken($token); |
|
| 60 | + } catch (ShareNotFound $e) { |
|
| 61 | + \OC_Response::setStatus(404); |
|
| 62 | + \OCP\Util::writeLog('core-preview', 'Passed token parameter is not valid', \OCP\Util::DEBUG); |
|
| 63 | + exit; |
|
| 64 | + } |
|
| 65 | + |
|
| 66 | + \OCP\JSON::checkUserExists($share->getShareOwner()); |
|
| 67 | + \OC_Util::tearDownFS(); |
|
| 68 | + \OC_Util::setupFS($share->getShareOwner()); |
|
| 69 | + |
|
| 70 | + |
|
| 71 | + try { |
|
| 72 | + $path = Filesystem::getPath($share->getNodeId()); |
|
| 73 | + } catch (NotFoundException $e) { |
|
| 74 | + \OCP\Util::writeLog('share', 'could not resolve linkItem', \OCP\Util::DEBUG); |
|
| 75 | + \OC_Response::setStatus(404); |
|
| 76 | + \OCP\JSON::error(array('success' => false)); |
|
| 77 | + exit(); |
|
| 78 | + } |
|
| 79 | + |
|
| 80 | + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK && $share->getPassword() !== null) { |
|
| 81 | + if (!self::authenticate($share, $password)) { |
|
| 82 | + \OC_Response::setStatus(403); |
|
| 83 | + \OCP\JSON::error(array('success' => false)); |
|
| 84 | + exit(); |
|
| 85 | + } |
|
| 86 | + } |
|
| 87 | + |
|
| 88 | + $basePath = $path; |
|
| 89 | + |
|
| 90 | + if ($relativePath !== null && Filesystem::isReadable($basePath . $relativePath)) { |
|
| 91 | + $path .= Filesystem::normalizePath($relativePath); |
|
| 92 | + } |
|
| 93 | + |
|
| 94 | + return array( |
|
| 95 | + 'share' => $share, |
|
| 96 | + 'basePath' => $basePath, |
|
| 97 | + 'realPath' => $path |
|
| 98 | + ); |
|
| 99 | + } |
|
| 100 | + |
|
| 101 | + /** |
|
| 102 | + * Authenticate link item with the given password |
|
| 103 | + * or with the session if no password was given. |
|
| 104 | + * @param \OCP\Share\IShare $share |
|
| 105 | + * @param string $password optional password |
|
| 106 | + * |
|
| 107 | + * @return boolean true if authorized, false otherwise |
|
| 108 | + */ |
|
| 109 | + public static function authenticate($share, $password = null) { |
|
| 110 | + $shareManager = \OC::$server->getShareManager(); |
|
| 111 | + |
|
| 112 | + if ($password !== null) { |
|
| 113 | + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { |
|
| 114 | + if ($shareManager->checkPassword($share, $password)) { |
|
| 115 | + \OC::$server->getSession()->set('public_link_authenticated', (string)$share->getId()); |
|
| 116 | + return true; |
|
| 117 | + } |
|
| 118 | + } |
|
| 119 | + } else { |
|
| 120 | + // not authenticated ? |
|
| 121 | + if (\OC::$server->getSession()->exists('public_link_authenticated') |
|
| 122 | + && \OC::$server->getSession()->get('public_link_authenticated') !== (string)$share->getId()) { |
|
| 123 | + return true; |
|
| 124 | + } |
|
| 125 | + } |
|
| 126 | + return false; |
|
| 127 | + } |
|
| 128 | + |
|
| 129 | + public static function getSharesFromItem($target) { |
|
| 130 | + $result = array(); |
|
| 131 | + $owner = Filesystem::getOwner($target); |
|
| 132 | + Filesystem::initMountPoints($owner); |
|
| 133 | + $info = Filesystem::getFileInfo($target); |
|
| 134 | + $ownerView = new View('/'.$owner.'/files'); |
|
| 135 | + if ( $owner != User::getUser() ) { |
|
| 136 | + $path = $ownerView->getPath($info['fileid']); |
|
| 137 | + } else { |
|
| 138 | + $path = $target; |
|
| 139 | + } |
|
| 140 | + |
|
| 141 | + |
|
| 142 | + $ids = array(); |
|
| 143 | + while ($path !== dirname($path)) { |
|
| 144 | + $info = $ownerView->getFileInfo($path); |
|
| 145 | + if ($info instanceof \OC\Files\FileInfo) { |
|
| 146 | + $ids[] = $info['fileid']; |
|
| 147 | + } else { |
|
| 148 | + \OCP\Util::writeLog('sharing', 'No fileinfo available for: ' . $path, \OCP\Util::WARN); |
|
| 149 | + } |
|
| 150 | + $path = dirname($path); |
|
| 151 | + } |
|
| 152 | + |
|
| 153 | + if (!empty($ids)) { |
|
| 154 | + |
|
| 155 | + $idList = array_chunk($ids, 99, true); |
|
| 156 | + |
|
| 157 | + foreach ($idList as $subList) { |
|
| 158 | + $statement = "SELECT `share_with`, `share_type`, `file_target` FROM `*PREFIX*share` WHERE `file_source` IN (" . implode(',', $subList) . ") AND `share_type` IN (0, 1, 2)"; |
|
| 159 | + $query = \OCP\DB::prepare($statement); |
|
| 160 | + $r = $query->execute(); |
|
| 161 | + $result = array_merge($result, $r->fetchAll()); |
|
| 162 | + } |
|
| 163 | + } |
|
| 164 | + |
|
| 165 | + return $result; |
|
| 166 | + } |
|
| 167 | + |
|
| 168 | + /** |
|
| 169 | + * get the UID of the owner of the file and the path to the file relative to |
|
| 170 | + * owners files folder |
|
| 171 | + * |
|
| 172 | + * @param $filename |
|
| 173 | + * @return array |
|
| 174 | + * @throws \OC\User\NoUserException |
|
| 175 | + */ |
|
| 176 | + public static function getUidAndFilename($filename) { |
|
| 177 | + $uid = Filesystem::getOwner($filename); |
|
| 178 | + $userManager = \OC::$server->getUserManager(); |
|
| 179 | + // if the user with the UID doesn't exists, e.g. because the UID points |
|
| 180 | + // to a remote user with a federated cloud ID we use the current logged-in |
|
| 181 | + // user. We need a valid local user to create the share |
|
| 182 | + if (!$userManager->userExists($uid)) { |
|
| 183 | + $uid = User::getUser(); |
|
| 184 | + } |
|
| 185 | + Filesystem::initMountPoints($uid); |
|
| 186 | + if ( $uid != User::getUser() ) { |
|
| 187 | + $info = Filesystem::getFileInfo($filename); |
|
| 188 | + $ownerView = new View('/'.$uid.'/files'); |
|
| 189 | + try { |
|
| 190 | + $filename = $ownerView->getPath($info['fileid']); |
|
| 191 | + } catch (NotFoundException $e) { |
|
| 192 | + $filename = null; |
|
| 193 | + } |
|
| 194 | + } |
|
| 195 | + return [$uid, $filename]; |
|
| 196 | + } |
|
| 197 | + |
|
| 198 | + /** |
|
| 199 | + * Format a path to be relative to the /user/files/ directory |
|
| 200 | + * @param string $path the absolute path |
|
| 201 | + * @return string e.g. turns '/admin/files/test.txt' into 'test.txt' |
|
| 202 | + */ |
|
| 203 | + public static function stripUserFilesPath($path) { |
|
| 204 | + $trimmed = ltrim($path, '/'); |
|
| 205 | + $split = explode('/', $trimmed); |
|
| 206 | + |
|
| 207 | + // it is not a file relative to data/user/files |
|
| 208 | + if (count($split) < 3 || $split[1] !== 'files') { |
|
| 209 | + return false; |
|
| 210 | + } |
|
| 211 | + |
|
| 212 | + $sliced = array_slice($split, 2); |
|
| 213 | + $relPath = implode('/', $sliced); |
|
| 214 | + |
|
| 215 | + return $relPath; |
|
| 216 | + } |
|
| 217 | + |
|
| 218 | + /** |
|
| 219 | + * check if file name already exists and generate unique target |
|
| 220 | + * |
|
| 221 | + * @param string $path |
|
| 222 | + * @param array $excludeList |
|
| 223 | + * @param View $view |
|
| 224 | + * @return string $path |
|
| 225 | + */ |
|
| 226 | + public static function generateUniqueTarget($path, $excludeList, $view) { |
|
| 227 | + $pathinfo = pathinfo($path); |
|
| 228 | + $ext = (isset($pathinfo['extension'])) ? '.'.$pathinfo['extension'] : ''; |
|
| 229 | + $name = $pathinfo['filename']; |
|
| 230 | + $dir = $pathinfo['dirname']; |
|
| 231 | + $i = 2; |
|
| 232 | + while ($view->file_exists($path) || in_array($path, $excludeList)) { |
|
| 233 | + $path = Filesystem::normalizePath($dir . '/' . $name . ' ('.$i.')' . $ext); |
|
| 234 | + $i++; |
|
| 235 | + } |
|
| 236 | + |
|
| 237 | + return $path; |
|
| 238 | + } |
|
| 239 | + |
|
| 240 | + /** |
|
| 241 | + * get default share folder |
|
| 242 | + * |
|
| 243 | + * @param \OC\Files\View |
|
| 244 | + * @return string |
|
| 245 | + */ |
|
| 246 | + public static function getShareFolder($view = null) { |
|
| 247 | + if ($view === null) { |
|
| 248 | + $view = Filesystem::getView(); |
|
| 249 | + } |
|
| 250 | + $shareFolder = \OC::$server->getConfig()->getSystemValue('share_folder', '/'); |
|
| 251 | + $shareFolder = Filesystem::normalizePath($shareFolder); |
|
| 252 | + |
|
| 253 | + if (!$view->file_exists($shareFolder)) { |
|
| 254 | + $dir = ''; |
|
| 255 | + $subdirs = explode('/', $shareFolder); |
|
| 256 | + foreach ($subdirs as $subdir) { |
|
| 257 | + $dir = $dir . '/' . $subdir; |
|
| 258 | + if (!$view->is_dir($dir)) { |
|
| 259 | + $view->mkdir($dir); |
|
| 260 | + } |
|
| 261 | + } |
|
| 262 | + } |
|
| 263 | + |
|
| 264 | + return $shareFolder; |
|
| 265 | + |
|
| 266 | + } |
|
| 267 | + |
|
| 268 | + /** |
|
| 269 | + * set default share folder |
|
| 270 | + * |
|
| 271 | + * @param string $shareFolder |
|
| 272 | + */ |
|
| 273 | + public static function setShareFolder($shareFolder) { |
|
| 274 | + \OC::$server->getConfig()->setSystemValue('share_folder', $shareFolder); |
|
| 275 | + } |
|
| 276 | 276 | |
| 277 | 277 | } |
@@ -87,7 +87,7 @@ discard block |
||
| 87 | 87 | |
| 88 | 88 | $basePath = $path; |
| 89 | 89 | |
| 90 | - if ($relativePath !== null && Filesystem::isReadable($basePath . $relativePath)) { |
|
| 90 | + if ($relativePath !== null && Filesystem::isReadable($basePath.$relativePath)) { |
|
| 91 | 91 | $path .= Filesystem::normalizePath($relativePath); |
| 92 | 92 | } |
| 93 | 93 | |
@@ -112,14 +112,14 @@ discard block |
||
| 112 | 112 | if ($password !== null) { |
| 113 | 113 | if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { |
| 114 | 114 | if ($shareManager->checkPassword($share, $password)) { |
| 115 | - \OC::$server->getSession()->set('public_link_authenticated', (string)$share->getId()); |
|
| 115 | + \OC::$server->getSession()->set('public_link_authenticated', (string) $share->getId()); |
|
| 116 | 116 | return true; |
| 117 | 117 | } |
| 118 | 118 | } |
| 119 | 119 | } else { |
| 120 | 120 | // not authenticated ? |
| 121 | 121 | if (\OC::$server->getSession()->exists('public_link_authenticated') |
| 122 | - && \OC::$server->getSession()->get('public_link_authenticated') !== (string)$share->getId()) { |
|
| 122 | + && \OC::$server->getSession()->get('public_link_authenticated') !== (string) $share->getId()) { |
|
| 123 | 123 | return true; |
| 124 | 124 | } |
| 125 | 125 | } |
@@ -132,7 +132,7 @@ discard block |
||
| 132 | 132 | Filesystem::initMountPoints($owner); |
| 133 | 133 | $info = Filesystem::getFileInfo($target); |
| 134 | 134 | $ownerView = new View('/'.$owner.'/files'); |
| 135 | - if ( $owner != User::getUser() ) { |
|
| 135 | + if ($owner != User::getUser()) { |
|
| 136 | 136 | $path = $ownerView->getPath($info['fileid']); |
| 137 | 137 | } else { |
| 138 | 138 | $path = $target; |
@@ -145,7 +145,7 @@ discard block |
||
| 145 | 145 | if ($info instanceof \OC\Files\FileInfo) { |
| 146 | 146 | $ids[] = $info['fileid']; |
| 147 | 147 | } else { |
| 148 | - \OCP\Util::writeLog('sharing', 'No fileinfo available for: ' . $path, \OCP\Util::WARN); |
|
| 148 | + \OCP\Util::writeLog('sharing', 'No fileinfo available for: '.$path, \OCP\Util::WARN); |
|
| 149 | 149 | } |
| 150 | 150 | $path = dirname($path); |
| 151 | 151 | } |
@@ -155,7 +155,7 @@ discard block |
||
| 155 | 155 | $idList = array_chunk($ids, 99, true); |
| 156 | 156 | |
| 157 | 157 | foreach ($idList as $subList) { |
| 158 | - $statement = "SELECT `share_with`, `share_type`, `file_target` FROM `*PREFIX*share` WHERE `file_source` IN (" . implode(',', $subList) . ") AND `share_type` IN (0, 1, 2)"; |
|
| 158 | + $statement = "SELECT `share_with`, `share_type`, `file_target` FROM `*PREFIX*share` WHERE `file_source` IN (".implode(',', $subList).") AND `share_type` IN (0, 1, 2)"; |
|
| 159 | 159 | $query = \OCP\DB::prepare($statement); |
| 160 | 160 | $r = $query->execute(); |
| 161 | 161 | $result = array_merge($result, $r->fetchAll()); |
@@ -183,7 +183,7 @@ discard block |
||
| 183 | 183 | $uid = User::getUser(); |
| 184 | 184 | } |
| 185 | 185 | Filesystem::initMountPoints($uid); |
| 186 | - if ( $uid != User::getUser() ) { |
|
| 186 | + if ($uid != User::getUser()) { |
|
| 187 | 187 | $info = Filesystem::getFileInfo($filename); |
| 188 | 188 | $ownerView = new View('/'.$uid.'/files'); |
| 189 | 189 | try { |
@@ -230,7 +230,7 @@ discard block |
||
| 230 | 230 | $dir = $pathinfo['dirname']; |
| 231 | 231 | $i = 2; |
| 232 | 232 | while ($view->file_exists($path) || in_array($path, $excludeList)) { |
| 233 | - $path = Filesystem::normalizePath($dir . '/' . $name . ' ('.$i.')' . $ext); |
|
| 233 | + $path = Filesystem::normalizePath($dir.'/'.$name.' ('.$i.')'.$ext); |
|
| 234 | 234 | $i++; |
| 235 | 235 | } |
| 236 | 236 | |
@@ -254,7 +254,7 @@ discard block |
||
| 254 | 254 | $dir = ''; |
| 255 | 255 | $subdirs = explode('/', $shareFolder); |
| 256 | 256 | foreach ($subdirs as $subdir) { |
| 257 | - $dir = $dir . '/' . $subdir; |
|
| 257 | + $dir = $dir.'/'.$subdir; |
|
| 258 | 258 | if (!$view->is_dir($dir)) { |
| 259 | 259 | $view->mkdir($dir); |
| 260 | 260 | } |
@@ -273,6 +273,10 @@ discard block |
||
| 273 | 273 | return '/^' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/'; |
| 274 | 274 | } |
| 275 | 275 | |
| 276 | + /** |
|
| 277 | + * @param integer $step |
|
| 278 | + * @param integer $max |
|
| 279 | + */ |
|
| 276 | 280 | protected function emit($sql, $step, $max) { |
| 277 | 281 | if ($this->noEmit) { |
| 278 | 282 | return; |
@@ -283,6 +287,10 @@ discard block |
||
| 283 | 287 | $this->dispatcher->dispatch('\OC\DB\Migrator::executeSql', new GenericEvent($sql, [$step+1, $max])); |
| 284 | 288 | } |
| 285 | 289 | |
| 290 | + /** |
|
| 291 | + * @param integer $step |
|
| 292 | + * @param integer $max |
|
| 293 | + */ |
|
| 286 | 294 | private function emitCheckStep($tableName, $step, $max) { |
| 287 | 295 | if(is_null($this->dispatcher)) { |
| 288 | 296 | return; |
@@ -43,271 +43,271 @@ |
||
| 43 | 43 | |
| 44 | 44 | class Migrator { |
| 45 | 45 | |
| 46 | - /** |
|
| 47 | - * @var \Doctrine\DBAL\Connection $connection |
|
| 48 | - */ |
|
| 49 | - protected $connection; |
|
| 50 | - |
|
| 51 | - /** |
|
| 52 | - * @var ISecureRandom |
|
| 53 | - */ |
|
| 54 | - private $random; |
|
| 55 | - |
|
| 56 | - /** @var IConfig */ |
|
| 57 | - protected $config; |
|
| 58 | - |
|
| 59 | - /** @var EventDispatcher */ |
|
| 60 | - private $dispatcher; |
|
| 61 | - |
|
| 62 | - /** @var bool */ |
|
| 63 | - private $noEmit = false; |
|
| 64 | - |
|
| 65 | - /** |
|
| 66 | - * @param \Doctrine\DBAL\Connection|Connection $connection |
|
| 67 | - * @param ISecureRandom $random |
|
| 68 | - * @param IConfig $config |
|
| 69 | - * @param EventDispatcher $dispatcher |
|
| 70 | - */ |
|
| 71 | - public function __construct(\Doctrine\DBAL\Connection $connection, |
|
| 72 | - ISecureRandom $random, |
|
| 73 | - IConfig $config, |
|
| 74 | - EventDispatcher $dispatcher = null) { |
|
| 75 | - $this->connection = $connection; |
|
| 76 | - $this->random = $random; |
|
| 77 | - $this->config = $config; |
|
| 78 | - $this->dispatcher = $dispatcher; |
|
| 79 | - } |
|
| 80 | - |
|
| 81 | - /** |
|
| 82 | - * @param \Doctrine\DBAL\Schema\Schema $targetSchema |
|
| 83 | - */ |
|
| 84 | - public function migrate(Schema $targetSchema) { |
|
| 85 | - $this->noEmit = true; |
|
| 86 | - $this->applySchema($targetSchema); |
|
| 87 | - } |
|
| 88 | - |
|
| 89 | - /** |
|
| 90 | - * @param \Doctrine\DBAL\Schema\Schema $targetSchema |
|
| 91 | - * @return string |
|
| 92 | - */ |
|
| 93 | - public function generateChangeScript(Schema $targetSchema) { |
|
| 94 | - $schemaDiff = $this->getDiff($targetSchema, $this->connection); |
|
| 95 | - |
|
| 96 | - $script = ''; |
|
| 97 | - $sqls = $schemaDiff->toSql($this->connection->getDatabasePlatform()); |
|
| 98 | - foreach ($sqls as $sql) { |
|
| 99 | - $script .= $this->convertStatementToScript($sql); |
|
| 100 | - } |
|
| 101 | - |
|
| 102 | - return $script; |
|
| 103 | - } |
|
| 104 | - |
|
| 105 | - /** |
|
| 106 | - * @param Schema $targetSchema |
|
| 107 | - * @throws \OC\DB\MigrationException |
|
| 108 | - */ |
|
| 109 | - public function checkMigrate(Schema $targetSchema) { |
|
| 110 | - $this->noEmit = true; |
|
| 111 | - /**@var \Doctrine\DBAL\Schema\Table[] $tables */ |
|
| 112 | - $tables = $targetSchema->getTables(); |
|
| 113 | - $filterExpression = $this->getFilterExpression(); |
|
| 114 | - $this->connection->getConfiguration()-> |
|
| 115 | - setFilterSchemaAssetsExpression($filterExpression); |
|
| 116 | - $existingTables = $this->connection->getSchemaManager()->listTableNames(); |
|
| 117 | - |
|
| 118 | - $step = 0; |
|
| 119 | - foreach ($tables as $table) { |
|
| 120 | - if (strpos($table->getName(), '.')) { |
|
| 121 | - list(, $tableName) = explode('.', $table->getName()); |
|
| 122 | - } else { |
|
| 123 | - $tableName = $table->getName(); |
|
| 124 | - } |
|
| 125 | - $this->emitCheckStep($tableName, $step++, count($tables)); |
|
| 126 | - // don't need to check for new tables |
|
| 127 | - if (array_search($tableName, $existingTables) !== false) { |
|
| 128 | - $this->checkTableMigrate($table); |
|
| 129 | - } |
|
| 130 | - } |
|
| 131 | - } |
|
| 132 | - |
|
| 133 | - /** |
|
| 134 | - * Create a unique name for the temporary table |
|
| 135 | - * |
|
| 136 | - * @param string $name |
|
| 137 | - * @return string |
|
| 138 | - */ |
|
| 139 | - protected function generateTemporaryTableName($name) { |
|
| 140 | - return $this->config->getSystemValue('dbtableprefix', 'oc_') . $name . '_' . $this->random->generate(13, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS); |
|
| 141 | - } |
|
| 142 | - |
|
| 143 | - /** |
|
| 144 | - * Check the migration of a table on a copy so we can detect errors before messing with the real table |
|
| 145 | - * |
|
| 146 | - * @param \Doctrine\DBAL\Schema\Table $table |
|
| 147 | - * @throws \OC\DB\MigrationException |
|
| 148 | - */ |
|
| 149 | - protected function checkTableMigrate(Table $table) { |
|
| 150 | - $name = $table->getName(); |
|
| 151 | - $tmpName = $this->generateTemporaryTableName($name); |
|
| 152 | - |
|
| 153 | - $this->copyTable($name, $tmpName); |
|
| 154 | - |
|
| 155 | - //create the migration schema for the temporary table |
|
| 156 | - $tmpTable = $this->renameTableSchema($table, $tmpName); |
|
| 157 | - $schemaConfig = new SchemaConfig(); |
|
| 158 | - $schemaConfig->setName($this->connection->getDatabase()); |
|
| 159 | - $schema = new Schema(array($tmpTable), array(), $schemaConfig); |
|
| 160 | - |
|
| 161 | - try { |
|
| 162 | - $this->applySchema($schema); |
|
| 163 | - $this->dropTable($tmpName); |
|
| 164 | - } catch (DBALException $e) { |
|
| 165 | - // pgsql needs to commit it's failed transaction before doing anything else |
|
| 166 | - if ($this->connection->isTransactionActive()) { |
|
| 167 | - $this->connection->commit(); |
|
| 168 | - } |
|
| 169 | - $this->dropTable($tmpName); |
|
| 170 | - throw new MigrationException($table->getName(), $e->getMessage()); |
|
| 171 | - } |
|
| 172 | - } |
|
| 173 | - |
|
| 174 | - /** |
|
| 175 | - * @param \Doctrine\DBAL\Schema\Table $table |
|
| 176 | - * @param string $newName |
|
| 177 | - * @return \Doctrine\DBAL\Schema\Table |
|
| 178 | - */ |
|
| 179 | - protected function renameTableSchema(Table $table, $newName) { |
|
| 180 | - /** |
|
| 181 | - * @var \Doctrine\DBAL\Schema\Index[] $indexes |
|
| 182 | - */ |
|
| 183 | - $indexes = $table->getIndexes(); |
|
| 184 | - $newIndexes = array(); |
|
| 185 | - foreach ($indexes as $index) { |
|
| 186 | - if ($index->isPrimary()) { |
|
| 187 | - // do not rename primary key |
|
| 188 | - $indexName = $index->getName(); |
|
| 189 | - } else { |
|
| 190 | - // avoid conflicts in index names |
|
| 191 | - $indexName = $this->config->getSystemValue('dbtableprefix', 'oc_') . $this->random->generate(13, ISecureRandom::CHAR_LOWER); |
|
| 192 | - } |
|
| 193 | - $newIndexes[] = new Index($indexName, $index->getColumns(), $index->isUnique(), $index->isPrimary()); |
|
| 194 | - } |
|
| 195 | - |
|
| 196 | - // foreign keys are not supported so we just set it to an empty array |
|
| 197 | - return new Table($newName, $table->getColumns(), $newIndexes, array(), 0, $table->getOptions()); |
|
| 198 | - } |
|
| 199 | - |
|
| 200 | - /** |
|
| 201 | - * @param Schema $targetSchema |
|
| 202 | - * @param \Doctrine\DBAL\Connection $connection |
|
| 203 | - * @return \Doctrine\DBAL\Schema\SchemaDiff |
|
| 204 | - * @throws DBALException |
|
| 205 | - */ |
|
| 206 | - protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) { |
|
| 207 | - // adjust varchar columns with a length higher then getVarcharMaxLength to clob |
|
| 208 | - foreach ($targetSchema->getTables() as $table) { |
|
| 209 | - foreach ($table->getColumns() as $column) { |
|
| 210 | - if ($column->getType() instanceof StringType) { |
|
| 211 | - if ($column->getLength() > $connection->getDatabasePlatform()->getVarcharMaxLength()) { |
|
| 212 | - $column->setType(Type::getType('text')); |
|
| 213 | - $column->setLength(null); |
|
| 214 | - } |
|
| 215 | - } |
|
| 216 | - } |
|
| 217 | - } |
|
| 218 | - |
|
| 219 | - $filterExpression = $this->getFilterExpression(); |
|
| 220 | - $this->connection->getConfiguration()-> |
|
| 221 | - setFilterSchemaAssetsExpression($filterExpression); |
|
| 222 | - $sourceSchema = $connection->getSchemaManager()->createSchema(); |
|
| 223 | - |
|
| 224 | - // remove tables we don't know about |
|
| 225 | - /** @var $table \Doctrine\DBAL\Schema\Table */ |
|
| 226 | - foreach ($sourceSchema->getTables() as $table) { |
|
| 227 | - if (!$targetSchema->hasTable($table->getName())) { |
|
| 228 | - $sourceSchema->dropTable($table->getName()); |
|
| 229 | - } |
|
| 230 | - } |
|
| 231 | - // remove sequences we don't know about |
|
| 232 | - foreach ($sourceSchema->getSequences() as $table) { |
|
| 233 | - if (!$targetSchema->hasSequence($table->getName())) { |
|
| 234 | - $sourceSchema->dropSequence($table->getName()); |
|
| 235 | - } |
|
| 236 | - } |
|
| 237 | - |
|
| 238 | - $comparator = new Comparator(); |
|
| 239 | - return $comparator->compare($sourceSchema, $targetSchema); |
|
| 240 | - } |
|
| 241 | - |
|
| 242 | - /** |
|
| 243 | - * @param \Doctrine\DBAL\Schema\Schema $targetSchema |
|
| 244 | - * @param \Doctrine\DBAL\Connection $connection |
|
| 245 | - */ |
|
| 246 | - protected function applySchema(Schema $targetSchema, \Doctrine\DBAL\Connection $connection = null) { |
|
| 247 | - if (is_null($connection)) { |
|
| 248 | - $connection = $this->connection; |
|
| 249 | - } |
|
| 250 | - |
|
| 251 | - $schemaDiff = $this->getDiff($targetSchema, $connection); |
|
| 252 | - |
|
| 253 | - $connection->beginTransaction(); |
|
| 254 | - $sqls = $schemaDiff->toSql($connection->getDatabasePlatform()); |
|
| 255 | - $step = 0; |
|
| 256 | - foreach ($sqls as $sql) { |
|
| 257 | - $this->emit($sql, $step++, count($sqls)); |
|
| 258 | - $connection->query($sql); |
|
| 259 | - } |
|
| 260 | - $connection->commit(); |
|
| 261 | - } |
|
| 262 | - |
|
| 263 | - /** |
|
| 264 | - * @param string $sourceName |
|
| 265 | - * @param string $targetName |
|
| 266 | - */ |
|
| 267 | - protected function copyTable($sourceName, $targetName) { |
|
| 268 | - $quotedSource = $this->connection->quoteIdentifier($sourceName); |
|
| 269 | - $quotedTarget = $this->connection->quoteIdentifier($targetName); |
|
| 270 | - |
|
| 271 | - $this->connection->exec('CREATE TABLE ' . $quotedTarget . ' (LIKE ' . $quotedSource . ')'); |
|
| 272 | - $this->connection->exec('INSERT INTO ' . $quotedTarget . ' SELECT * FROM ' . $quotedSource); |
|
| 273 | - } |
|
| 274 | - |
|
| 275 | - /** |
|
| 276 | - * @param string $name |
|
| 277 | - */ |
|
| 278 | - protected function dropTable($name) { |
|
| 279 | - $this->connection->exec('DROP TABLE ' . $this->connection->quoteIdentifier($name)); |
|
| 280 | - } |
|
| 281 | - |
|
| 282 | - /** |
|
| 283 | - * @param $statement |
|
| 284 | - * @return string |
|
| 285 | - */ |
|
| 286 | - protected function convertStatementToScript($statement) { |
|
| 287 | - $script = $statement . ';'; |
|
| 288 | - $script .= PHP_EOL; |
|
| 289 | - $script .= PHP_EOL; |
|
| 290 | - return $script; |
|
| 291 | - } |
|
| 292 | - |
|
| 293 | - protected function getFilterExpression() { |
|
| 294 | - return '/^' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/'; |
|
| 295 | - } |
|
| 296 | - |
|
| 297 | - protected function emit($sql, $step, $max) { |
|
| 298 | - if ($this->noEmit) { |
|
| 299 | - return; |
|
| 300 | - } |
|
| 301 | - if(is_null($this->dispatcher)) { |
|
| 302 | - return; |
|
| 303 | - } |
|
| 304 | - $this->dispatcher->dispatch('\OC\DB\Migrator::executeSql', new GenericEvent($sql, [$step+1, $max])); |
|
| 305 | - } |
|
| 306 | - |
|
| 307 | - private function emitCheckStep($tableName, $step, $max) { |
|
| 308 | - if(is_null($this->dispatcher)) { |
|
| 309 | - return; |
|
| 310 | - } |
|
| 311 | - $this->dispatcher->dispatch('\OC\DB\Migrator::checkTable', new GenericEvent($tableName, [$step+1, $max])); |
|
| 312 | - } |
|
| 46 | + /** |
|
| 47 | + * @var \Doctrine\DBAL\Connection $connection |
|
| 48 | + */ |
|
| 49 | + protected $connection; |
|
| 50 | + |
|
| 51 | + /** |
|
| 52 | + * @var ISecureRandom |
|
| 53 | + */ |
|
| 54 | + private $random; |
|
| 55 | + |
|
| 56 | + /** @var IConfig */ |
|
| 57 | + protected $config; |
|
| 58 | + |
|
| 59 | + /** @var EventDispatcher */ |
|
| 60 | + private $dispatcher; |
|
| 61 | + |
|
| 62 | + /** @var bool */ |
|
| 63 | + private $noEmit = false; |
|
| 64 | + |
|
| 65 | + /** |
|
| 66 | + * @param \Doctrine\DBAL\Connection|Connection $connection |
|
| 67 | + * @param ISecureRandom $random |
|
| 68 | + * @param IConfig $config |
|
| 69 | + * @param EventDispatcher $dispatcher |
|
| 70 | + */ |
|
| 71 | + public function __construct(\Doctrine\DBAL\Connection $connection, |
|
| 72 | + ISecureRandom $random, |
|
| 73 | + IConfig $config, |
|
| 74 | + EventDispatcher $dispatcher = null) { |
|
| 75 | + $this->connection = $connection; |
|
| 76 | + $this->random = $random; |
|
| 77 | + $this->config = $config; |
|
| 78 | + $this->dispatcher = $dispatcher; |
|
| 79 | + } |
|
| 80 | + |
|
| 81 | + /** |
|
| 82 | + * @param \Doctrine\DBAL\Schema\Schema $targetSchema |
|
| 83 | + */ |
|
| 84 | + public function migrate(Schema $targetSchema) { |
|
| 85 | + $this->noEmit = true; |
|
| 86 | + $this->applySchema($targetSchema); |
|
| 87 | + } |
|
| 88 | + |
|
| 89 | + /** |
|
| 90 | + * @param \Doctrine\DBAL\Schema\Schema $targetSchema |
|
| 91 | + * @return string |
|
| 92 | + */ |
|
| 93 | + public function generateChangeScript(Schema $targetSchema) { |
|
| 94 | + $schemaDiff = $this->getDiff($targetSchema, $this->connection); |
|
| 95 | + |
|
| 96 | + $script = ''; |
|
| 97 | + $sqls = $schemaDiff->toSql($this->connection->getDatabasePlatform()); |
|
| 98 | + foreach ($sqls as $sql) { |
|
| 99 | + $script .= $this->convertStatementToScript($sql); |
|
| 100 | + } |
|
| 101 | + |
|
| 102 | + return $script; |
|
| 103 | + } |
|
| 104 | + |
|
| 105 | + /** |
|
| 106 | + * @param Schema $targetSchema |
|
| 107 | + * @throws \OC\DB\MigrationException |
|
| 108 | + */ |
|
| 109 | + public function checkMigrate(Schema $targetSchema) { |
|
| 110 | + $this->noEmit = true; |
|
| 111 | + /**@var \Doctrine\DBAL\Schema\Table[] $tables */ |
|
| 112 | + $tables = $targetSchema->getTables(); |
|
| 113 | + $filterExpression = $this->getFilterExpression(); |
|
| 114 | + $this->connection->getConfiguration()-> |
|
| 115 | + setFilterSchemaAssetsExpression($filterExpression); |
|
| 116 | + $existingTables = $this->connection->getSchemaManager()->listTableNames(); |
|
| 117 | + |
|
| 118 | + $step = 0; |
|
| 119 | + foreach ($tables as $table) { |
|
| 120 | + if (strpos($table->getName(), '.')) { |
|
| 121 | + list(, $tableName) = explode('.', $table->getName()); |
|
| 122 | + } else { |
|
| 123 | + $tableName = $table->getName(); |
|
| 124 | + } |
|
| 125 | + $this->emitCheckStep($tableName, $step++, count($tables)); |
|
| 126 | + // don't need to check for new tables |
|
| 127 | + if (array_search($tableName, $existingTables) !== false) { |
|
| 128 | + $this->checkTableMigrate($table); |
|
| 129 | + } |
|
| 130 | + } |
|
| 131 | + } |
|
| 132 | + |
|
| 133 | + /** |
|
| 134 | + * Create a unique name for the temporary table |
|
| 135 | + * |
|
| 136 | + * @param string $name |
|
| 137 | + * @return string |
|
| 138 | + */ |
|
| 139 | + protected function generateTemporaryTableName($name) { |
|
| 140 | + return $this->config->getSystemValue('dbtableprefix', 'oc_') . $name . '_' . $this->random->generate(13, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS); |
|
| 141 | + } |
|
| 142 | + |
|
| 143 | + /** |
|
| 144 | + * Check the migration of a table on a copy so we can detect errors before messing with the real table |
|
| 145 | + * |
|
| 146 | + * @param \Doctrine\DBAL\Schema\Table $table |
|
| 147 | + * @throws \OC\DB\MigrationException |
|
| 148 | + */ |
|
| 149 | + protected function checkTableMigrate(Table $table) { |
|
| 150 | + $name = $table->getName(); |
|
| 151 | + $tmpName = $this->generateTemporaryTableName($name); |
|
| 152 | + |
|
| 153 | + $this->copyTable($name, $tmpName); |
|
| 154 | + |
|
| 155 | + //create the migration schema for the temporary table |
|
| 156 | + $tmpTable = $this->renameTableSchema($table, $tmpName); |
|
| 157 | + $schemaConfig = new SchemaConfig(); |
|
| 158 | + $schemaConfig->setName($this->connection->getDatabase()); |
|
| 159 | + $schema = new Schema(array($tmpTable), array(), $schemaConfig); |
|
| 160 | + |
|
| 161 | + try { |
|
| 162 | + $this->applySchema($schema); |
|
| 163 | + $this->dropTable($tmpName); |
|
| 164 | + } catch (DBALException $e) { |
|
| 165 | + // pgsql needs to commit it's failed transaction before doing anything else |
|
| 166 | + if ($this->connection->isTransactionActive()) { |
|
| 167 | + $this->connection->commit(); |
|
| 168 | + } |
|
| 169 | + $this->dropTable($tmpName); |
|
| 170 | + throw new MigrationException($table->getName(), $e->getMessage()); |
|
| 171 | + } |
|
| 172 | + } |
|
| 173 | + |
|
| 174 | + /** |
|
| 175 | + * @param \Doctrine\DBAL\Schema\Table $table |
|
| 176 | + * @param string $newName |
|
| 177 | + * @return \Doctrine\DBAL\Schema\Table |
|
| 178 | + */ |
|
| 179 | + protected function renameTableSchema(Table $table, $newName) { |
|
| 180 | + /** |
|
| 181 | + * @var \Doctrine\DBAL\Schema\Index[] $indexes |
|
| 182 | + */ |
|
| 183 | + $indexes = $table->getIndexes(); |
|
| 184 | + $newIndexes = array(); |
|
| 185 | + foreach ($indexes as $index) { |
|
| 186 | + if ($index->isPrimary()) { |
|
| 187 | + // do not rename primary key |
|
| 188 | + $indexName = $index->getName(); |
|
| 189 | + } else { |
|
| 190 | + // avoid conflicts in index names |
|
| 191 | + $indexName = $this->config->getSystemValue('dbtableprefix', 'oc_') . $this->random->generate(13, ISecureRandom::CHAR_LOWER); |
|
| 192 | + } |
|
| 193 | + $newIndexes[] = new Index($indexName, $index->getColumns(), $index->isUnique(), $index->isPrimary()); |
|
| 194 | + } |
|
| 195 | + |
|
| 196 | + // foreign keys are not supported so we just set it to an empty array |
|
| 197 | + return new Table($newName, $table->getColumns(), $newIndexes, array(), 0, $table->getOptions()); |
|
| 198 | + } |
|
| 199 | + |
|
| 200 | + /** |
|
| 201 | + * @param Schema $targetSchema |
|
| 202 | + * @param \Doctrine\DBAL\Connection $connection |
|
| 203 | + * @return \Doctrine\DBAL\Schema\SchemaDiff |
|
| 204 | + * @throws DBALException |
|
| 205 | + */ |
|
| 206 | + protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) { |
|
| 207 | + // adjust varchar columns with a length higher then getVarcharMaxLength to clob |
|
| 208 | + foreach ($targetSchema->getTables() as $table) { |
|
| 209 | + foreach ($table->getColumns() as $column) { |
|
| 210 | + if ($column->getType() instanceof StringType) { |
|
| 211 | + if ($column->getLength() > $connection->getDatabasePlatform()->getVarcharMaxLength()) { |
|
| 212 | + $column->setType(Type::getType('text')); |
|
| 213 | + $column->setLength(null); |
|
| 214 | + } |
|
| 215 | + } |
|
| 216 | + } |
|
| 217 | + } |
|
| 218 | + |
|
| 219 | + $filterExpression = $this->getFilterExpression(); |
|
| 220 | + $this->connection->getConfiguration()-> |
|
| 221 | + setFilterSchemaAssetsExpression($filterExpression); |
|
| 222 | + $sourceSchema = $connection->getSchemaManager()->createSchema(); |
|
| 223 | + |
|
| 224 | + // remove tables we don't know about |
|
| 225 | + /** @var $table \Doctrine\DBAL\Schema\Table */ |
|
| 226 | + foreach ($sourceSchema->getTables() as $table) { |
|
| 227 | + if (!$targetSchema->hasTable($table->getName())) { |
|
| 228 | + $sourceSchema->dropTable($table->getName()); |
|
| 229 | + } |
|
| 230 | + } |
|
| 231 | + // remove sequences we don't know about |
|
| 232 | + foreach ($sourceSchema->getSequences() as $table) { |
|
| 233 | + if (!$targetSchema->hasSequence($table->getName())) { |
|
| 234 | + $sourceSchema->dropSequence($table->getName()); |
|
| 235 | + } |
|
| 236 | + } |
|
| 237 | + |
|
| 238 | + $comparator = new Comparator(); |
|
| 239 | + return $comparator->compare($sourceSchema, $targetSchema); |
|
| 240 | + } |
|
| 241 | + |
|
| 242 | + /** |
|
| 243 | + * @param \Doctrine\DBAL\Schema\Schema $targetSchema |
|
| 244 | + * @param \Doctrine\DBAL\Connection $connection |
|
| 245 | + */ |
|
| 246 | + protected function applySchema(Schema $targetSchema, \Doctrine\DBAL\Connection $connection = null) { |
|
| 247 | + if (is_null($connection)) { |
|
| 248 | + $connection = $this->connection; |
|
| 249 | + } |
|
| 250 | + |
|
| 251 | + $schemaDiff = $this->getDiff($targetSchema, $connection); |
|
| 252 | + |
|
| 253 | + $connection->beginTransaction(); |
|
| 254 | + $sqls = $schemaDiff->toSql($connection->getDatabasePlatform()); |
|
| 255 | + $step = 0; |
|
| 256 | + foreach ($sqls as $sql) { |
|
| 257 | + $this->emit($sql, $step++, count($sqls)); |
|
| 258 | + $connection->query($sql); |
|
| 259 | + } |
|
| 260 | + $connection->commit(); |
|
| 261 | + } |
|
| 262 | + |
|
| 263 | + /** |
|
| 264 | + * @param string $sourceName |
|
| 265 | + * @param string $targetName |
|
| 266 | + */ |
|
| 267 | + protected function copyTable($sourceName, $targetName) { |
|
| 268 | + $quotedSource = $this->connection->quoteIdentifier($sourceName); |
|
| 269 | + $quotedTarget = $this->connection->quoteIdentifier($targetName); |
|
| 270 | + |
|
| 271 | + $this->connection->exec('CREATE TABLE ' . $quotedTarget . ' (LIKE ' . $quotedSource . ')'); |
|
| 272 | + $this->connection->exec('INSERT INTO ' . $quotedTarget . ' SELECT * FROM ' . $quotedSource); |
|
| 273 | + } |
|
| 274 | + |
|
| 275 | + /** |
|
| 276 | + * @param string $name |
|
| 277 | + */ |
|
| 278 | + protected function dropTable($name) { |
|
| 279 | + $this->connection->exec('DROP TABLE ' . $this->connection->quoteIdentifier($name)); |
|
| 280 | + } |
|
| 281 | + |
|
| 282 | + /** |
|
| 283 | + * @param $statement |
|
| 284 | + * @return string |
|
| 285 | + */ |
|
| 286 | + protected function convertStatementToScript($statement) { |
|
| 287 | + $script = $statement . ';'; |
|
| 288 | + $script .= PHP_EOL; |
|
| 289 | + $script .= PHP_EOL; |
|
| 290 | + return $script; |
|
| 291 | + } |
|
| 292 | + |
|
| 293 | + protected function getFilterExpression() { |
|
| 294 | + return '/^' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/'; |
|
| 295 | + } |
|
| 296 | + |
|
| 297 | + protected function emit($sql, $step, $max) { |
|
| 298 | + if ($this->noEmit) { |
|
| 299 | + return; |
|
| 300 | + } |
|
| 301 | + if(is_null($this->dispatcher)) { |
|
| 302 | + return; |
|
| 303 | + } |
|
| 304 | + $this->dispatcher->dispatch('\OC\DB\Migrator::executeSql', new GenericEvent($sql, [$step+1, $max])); |
|
| 305 | + } |
|
| 306 | + |
|
| 307 | + private function emitCheckStep($tableName, $step, $max) { |
|
| 308 | + if(is_null($this->dispatcher)) { |
|
| 309 | + return; |
|
| 310 | + } |
|
| 311 | + $this->dispatcher->dispatch('\OC\DB\Migrator::checkTable', new GenericEvent($tableName, [$step+1, $max])); |
|
| 312 | + } |
|
| 313 | 313 | } |
@@ -137,7 +137,7 @@ discard block |
||
| 137 | 137 | * @return string |
| 138 | 138 | */ |
| 139 | 139 | protected function generateTemporaryTableName($name) { |
| 140 | - return $this->config->getSystemValue('dbtableprefix', 'oc_') . $name . '_' . $this->random->generate(13, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS); |
|
| 140 | + return $this->config->getSystemValue('dbtableprefix', 'oc_').$name.'_'.$this->random->generate(13, ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_DIGITS); |
|
| 141 | 141 | } |
| 142 | 142 | |
| 143 | 143 | /** |
@@ -188,7 +188,7 @@ discard block |
||
| 188 | 188 | $indexName = $index->getName(); |
| 189 | 189 | } else { |
| 190 | 190 | // avoid conflicts in index names |
| 191 | - $indexName = $this->config->getSystemValue('dbtableprefix', 'oc_') . $this->random->generate(13, ISecureRandom::CHAR_LOWER); |
|
| 191 | + $indexName = $this->config->getSystemValue('dbtableprefix', 'oc_').$this->random->generate(13, ISecureRandom::CHAR_LOWER); |
|
| 192 | 192 | } |
| 193 | 193 | $newIndexes[] = new Index($indexName, $index->getColumns(), $index->isUnique(), $index->isPrimary()); |
| 194 | 194 | } |
@@ -268,15 +268,15 @@ discard block |
||
| 268 | 268 | $quotedSource = $this->connection->quoteIdentifier($sourceName); |
| 269 | 269 | $quotedTarget = $this->connection->quoteIdentifier($targetName); |
| 270 | 270 | |
| 271 | - $this->connection->exec('CREATE TABLE ' . $quotedTarget . ' (LIKE ' . $quotedSource . ')'); |
|
| 272 | - $this->connection->exec('INSERT INTO ' . $quotedTarget . ' SELECT * FROM ' . $quotedSource); |
|
| 271 | + $this->connection->exec('CREATE TABLE '.$quotedTarget.' (LIKE '.$quotedSource.')'); |
|
| 272 | + $this->connection->exec('INSERT INTO '.$quotedTarget.' SELECT * FROM '.$quotedSource); |
|
| 273 | 273 | } |
| 274 | 274 | |
| 275 | 275 | /** |
| 276 | 276 | * @param string $name |
| 277 | 277 | */ |
| 278 | 278 | protected function dropTable($name) { |
| 279 | - $this->connection->exec('DROP TABLE ' . $this->connection->quoteIdentifier($name)); |
|
| 279 | + $this->connection->exec('DROP TABLE '.$this->connection->quoteIdentifier($name)); |
|
| 280 | 280 | } |
| 281 | 281 | |
| 282 | 282 | /** |
@@ -284,30 +284,30 @@ discard block |
||
| 284 | 284 | * @return string |
| 285 | 285 | */ |
| 286 | 286 | protected function convertStatementToScript($statement) { |
| 287 | - $script = $statement . ';'; |
|
| 287 | + $script = $statement.';'; |
|
| 288 | 288 | $script .= PHP_EOL; |
| 289 | 289 | $script .= PHP_EOL; |
| 290 | 290 | return $script; |
| 291 | 291 | } |
| 292 | 292 | |
| 293 | 293 | protected function getFilterExpression() { |
| 294 | - return '/^' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/'; |
|
| 294 | + return '/^'.preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')).'/'; |
|
| 295 | 295 | } |
| 296 | 296 | |
| 297 | 297 | protected function emit($sql, $step, $max) { |
| 298 | 298 | if ($this->noEmit) { |
| 299 | 299 | return; |
| 300 | 300 | } |
| 301 | - if(is_null($this->dispatcher)) { |
|
| 301 | + if (is_null($this->dispatcher)) { |
|
| 302 | 302 | return; |
| 303 | 303 | } |
| 304 | - $this->dispatcher->dispatch('\OC\DB\Migrator::executeSql', new GenericEvent($sql, [$step+1, $max])); |
|
| 304 | + $this->dispatcher->dispatch('\OC\DB\Migrator::executeSql', new GenericEvent($sql, [$step + 1, $max])); |
|
| 305 | 305 | } |
| 306 | 306 | |
| 307 | 307 | private function emitCheckStep($tableName, $step, $max) { |
| 308 | - if(is_null($this->dispatcher)) { |
|
| 308 | + if (is_null($this->dispatcher)) { |
|
| 309 | 309 | return; |
| 310 | 310 | } |
| 311 | - $this->dispatcher->dispatch('\OC\DB\Migrator::checkTable', new GenericEvent($tableName, [$step+1, $max])); |
|
| 311 | + $this->dispatcher->dispatch('\OC\DB\Migrator::checkTable', new GenericEvent($tableName, [$step + 1, $max])); |
|
| 312 | 312 | } |
| 313 | 313 | } |
@@ -103,6 +103,7 @@ discard block |
||
| 103 | 103 | |
| 104 | 104 | /** |
| 105 | 105 | * Chceks if the ownCloud server can connect to a specific URL using both HTTPS and HTTP |
| 106 | + * @param string $sitename |
|
| 106 | 107 | * @return bool |
| 107 | 108 | */ |
| 108 | 109 | private function isSiteReachable($sitename) { |
@@ -285,7 +286,7 @@ discard block |
||
| 285 | 286 | |
| 286 | 287 | /** |
| 287 | 288 | * @NoCSRFRequired |
| 288 | - * @return DataResponse |
|
| 289 | + * @return DataDisplayResponse |
|
| 289 | 290 | */ |
| 290 | 291 | public function getFailedIntegrityCheckFiles() { |
| 291 | 292 | if(!$this->checker->isCodeCheckEnforced()) { |
@@ -45,268 +45,268 @@ discard block |
||
| 45 | 45 | * @package OC\Settings\Controller |
| 46 | 46 | */ |
| 47 | 47 | class CheckSetupController extends Controller { |
| 48 | - /** @var IConfig */ |
|
| 49 | - private $config; |
|
| 50 | - /** @var IClientService */ |
|
| 51 | - private $clientService; |
|
| 52 | - /** @var \OC_Util */ |
|
| 53 | - private $util; |
|
| 54 | - /** @var IURLGenerator */ |
|
| 55 | - private $urlGenerator; |
|
| 56 | - /** @var IL10N */ |
|
| 57 | - private $l10n; |
|
| 58 | - /** @var Checker */ |
|
| 59 | - private $checker; |
|
| 60 | - /** @var ILogger */ |
|
| 61 | - private $logger; |
|
| 62 | - |
|
| 63 | - /** |
|
| 64 | - * @param string $AppName |
|
| 65 | - * @param IRequest $request |
|
| 66 | - * @param IConfig $config |
|
| 67 | - * @param IClientService $clientService |
|
| 68 | - * @param IURLGenerator $urlGenerator |
|
| 69 | - * @param \OC_Util $util |
|
| 70 | - * @param IL10N $l10n |
|
| 71 | - * @param Checker $checker |
|
| 72 | - * @param ILogger $logger |
|
| 73 | - */ |
|
| 74 | - public function __construct($AppName, |
|
| 75 | - IRequest $request, |
|
| 76 | - IConfig $config, |
|
| 77 | - IClientService $clientService, |
|
| 78 | - IURLGenerator $urlGenerator, |
|
| 79 | - \OC_Util $util, |
|
| 80 | - IL10N $l10n, |
|
| 81 | - Checker $checker, |
|
| 82 | - ILogger $logger) { |
|
| 83 | - parent::__construct($AppName, $request); |
|
| 84 | - $this->config = $config; |
|
| 85 | - $this->clientService = $clientService; |
|
| 86 | - $this->util = $util; |
|
| 87 | - $this->urlGenerator = $urlGenerator; |
|
| 88 | - $this->l10n = $l10n; |
|
| 89 | - $this->checker = $checker; |
|
| 90 | - $this->logger = $logger; |
|
| 91 | - } |
|
| 92 | - |
|
| 93 | - /** |
|
| 94 | - * Checks if the ownCloud server can connect to the internet using HTTPS and HTTP |
|
| 95 | - * @return bool |
|
| 96 | - */ |
|
| 97 | - private function isInternetConnectionWorking() { |
|
| 98 | - if ($this->config->getSystemValue('has_internet_connection', true) === false) { |
|
| 99 | - return false; |
|
| 100 | - } |
|
| 101 | - |
|
| 102 | - $siteArray = ['www.nextcloud.com', |
|
| 103 | - 'www.google.com', |
|
| 104 | - 'www.github.com']; |
|
| 105 | - |
|
| 106 | - foreach($siteArray as $site) { |
|
| 107 | - if ($this->isSiteReachable($site)) { |
|
| 108 | - return true; |
|
| 109 | - } |
|
| 110 | - } |
|
| 111 | - return false; |
|
| 112 | - } |
|
| 113 | - |
|
| 114 | - /** |
|
| 115 | - * Chceks if the ownCloud server can connect to a specific URL using both HTTPS and HTTP |
|
| 116 | - * @return bool |
|
| 117 | - */ |
|
| 118 | - private function isSiteReachable($sitename) { |
|
| 119 | - $httpSiteName = 'http://' . $sitename . '/'; |
|
| 120 | - $httpsSiteName = 'https://' . $sitename . '/'; |
|
| 121 | - |
|
| 122 | - try { |
|
| 123 | - $client = $this->clientService->newClient(); |
|
| 124 | - $client->get($httpSiteName); |
|
| 125 | - $client->get($httpsSiteName); |
|
| 126 | - } catch (\Exception $e) { |
|
| 127 | - $this->logger->logException($e, ['app' => 'internet_connection_check']); |
|
| 128 | - return false; |
|
| 129 | - } |
|
| 130 | - return true; |
|
| 131 | - } |
|
| 132 | - |
|
| 133 | - /** |
|
| 134 | - * Checks whether a local memcache is installed or not |
|
| 135 | - * @return bool |
|
| 136 | - */ |
|
| 137 | - private function isMemcacheConfigured() { |
|
| 138 | - return $this->config->getSystemValue('memcache.local', null) !== null; |
|
| 139 | - } |
|
| 140 | - |
|
| 141 | - /** |
|
| 142 | - * Whether /dev/urandom is available to the PHP controller |
|
| 143 | - * |
|
| 144 | - * @return bool |
|
| 145 | - */ |
|
| 146 | - private function isUrandomAvailable() { |
|
| 147 | - if(@file_exists('/dev/urandom')) { |
|
| 148 | - $file = fopen('/dev/urandom', 'rb'); |
|
| 149 | - if($file) { |
|
| 150 | - fclose($file); |
|
| 151 | - return true; |
|
| 152 | - } |
|
| 153 | - } |
|
| 154 | - |
|
| 155 | - return false; |
|
| 156 | - } |
|
| 157 | - |
|
| 158 | - /** |
|
| 159 | - * Public for the sake of unit-testing |
|
| 160 | - * |
|
| 161 | - * @return array |
|
| 162 | - */ |
|
| 163 | - protected function getCurlVersion() { |
|
| 164 | - return curl_version(); |
|
| 165 | - } |
|
| 166 | - |
|
| 167 | - /** |
|
| 168 | - * Check if the used SSL lib is outdated. Older OpenSSL and NSS versions do |
|
| 169 | - * have multiple bugs which likely lead to problems in combination with |
|
| 170 | - * functionality required by ownCloud such as SNI. |
|
| 171 | - * |
|
| 172 | - * @link https://github.com/owncloud/core/issues/17446#issuecomment-122877546 |
|
| 173 | - * @link https://bugzilla.redhat.com/show_bug.cgi?id=1241172 |
|
| 174 | - * @return string |
|
| 175 | - */ |
|
| 176 | - private function isUsedTlsLibOutdated() { |
|
| 177 | - // Don't run check when: |
|
| 178 | - // 1. Server has `has_internet_connection` set to false |
|
| 179 | - // 2. AppStore AND S2S is disabled |
|
| 180 | - if(!$this->config->getSystemValue('has_internet_connection', true)) { |
|
| 181 | - return ''; |
|
| 182 | - } |
|
| 183 | - if(!$this->config->getSystemValue('appstoreenabled', true) |
|
| 184 | - && $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'no' |
|
| 185 | - && $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes') === 'no') { |
|
| 186 | - return ''; |
|
| 187 | - } |
|
| 188 | - |
|
| 189 | - $versionString = $this->getCurlVersion(); |
|
| 190 | - if(isset($versionString['ssl_version'])) { |
|
| 191 | - $versionString = $versionString['ssl_version']; |
|
| 192 | - } else { |
|
| 193 | - return ''; |
|
| 194 | - } |
|
| 195 | - |
|
| 196 | - $features = (string)$this->l10n->t('installing and updating apps via the app store or Federated Cloud Sharing'); |
|
| 197 | - if(!$this->config->getSystemValue('appstoreenabled', true)) { |
|
| 198 | - $features = (string)$this->l10n->t('Federated Cloud Sharing'); |
|
| 199 | - } |
|
| 200 | - |
|
| 201 | - // Check if at least OpenSSL after 1.01d or 1.0.2b |
|
| 202 | - if(strpos($versionString, 'OpenSSL/') === 0) { |
|
| 203 | - $majorVersion = substr($versionString, 8, 5); |
|
| 204 | - $patchRelease = substr($versionString, 13, 6); |
|
| 205 | - |
|
| 206 | - if(($majorVersion === '1.0.1' && ord($patchRelease) < ord('d')) || |
|
| 207 | - ($majorVersion === '1.0.2' && ord($patchRelease) < ord('b'))) { |
|
| 208 | - return (string) $this->l10n->t('cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably.', ['OpenSSL', $versionString, $features]); |
|
| 209 | - } |
|
| 210 | - } |
|
| 211 | - |
|
| 212 | - // Check if NSS and perform heuristic check |
|
| 213 | - if(strpos($versionString, 'NSS/') === 0) { |
|
| 214 | - try { |
|
| 215 | - $firstClient = $this->clientService->newClient(); |
|
| 216 | - $firstClient->get('https://www.owncloud.org/'); |
|
| 217 | - |
|
| 218 | - $secondClient = $this->clientService->newClient(); |
|
| 219 | - $secondClient->get('https://owncloud.org/'); |
|
| 220 | - } catch (ClientException $e) { |
|
| 221 | - if($e->getResponse()->getStatusCode() === 400) { |
|
| 222 | - return (string) $this->l10n->t('cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably.', ['NSS', $versionString, $features]); |
|
| 223 | - } |
|
| 224 | - } |
|
| 225 | - } |
|
| 226 | - |
|
| 227 | - return ''; |
|
| 228 | - } |
|
| 229 | - |
|
| 230 | - /** |
|
| 231 | - * Whether the version is outdated |
|
| 232 | - * |
|
| 233 | - * @return bool |
|
| 234 | - */ |
|
| 235 | - protected function isPhpOutdated() { |
|
| 236 | - if (version_compare(PHP_VERSION, '5.5.0') === -1) { |
|
| 237 | - return true; |
|
| 238 | - } |
|
| 239 | - |
|
| 240 | - return false; |
|
| 241 | - } |
|
| 242 | - |
|
| 243 | - /** |
|
| 244 | - * Whether the php version is still supported (at time of release) |
|
| 245 | - * according to: https://secure.php.net/supported-versions.php |
|
| 246 | - * |
|
| 247 | - * @return array |
|
| 248 | - */ |
|
| 249 | - private function isPhpSupported() { |
|
| 250 | - return ['eol' => $this->isPhpOutdated(), 'version' => PHP_VERSION]; |
|
| 251 | - } |
|
| 252 | - |
|
| 253 | - /** |
|
| 254 | - * Check if the reverse proxy configuration is working as expected |
|
| 255 | - * |
|
| 256 | - * @return bool |
|
| 257 | - */ |
|
| 258 | - private function forwardedForHeadersWorking() { |
|
| 259 | - $trustedProxies = $this->config->getSystemValue('trusted_proxies', []); |
|
| 260 | - $remoteAddress = $this->request->getRemoteAddress(); |
|
| 261 | - |
|
| 262 | - if (is_array($trustedProxies) && in_array($remoteAddress, $trustedProxies)) { |
|
| 263 | - return false; |
|
| 264 | - } |
|
| 265 | - |
|
| 266 | - // either not enabled or working correctly |
|
| 267 | - return true; |
|
| 268 | - } |
|
| 269 | - |
|
| 270 | - /** |
|
| 271 | - * Checks if the correct memcache module for PHP is installed. Only |
|
| 272 | - * fails if memcached is configured and the working module is not installed. |
|
| 273 | - * |
|
| 274 | - * @return bool |
|
| 275 | - */ |
|
| 276 | - private function isCorrectMemcachedPHPModuleInstalled() { |
|
| 277 | - if ($this->config->getSystemValue('memcache.distributed', null) !== '\OC\Memcache\Memcached') { |
|
| 278 | - return true; |
|
| 279 | - } |
|
| 280 | - |
|
| 281 | - // there are two different memcached modules for PHP |
|
| 282 | - // we only support memcached and not memcache |
|
| 283 | - // https://code.google.com/p/memcached/wiki/PHPClientComparison |
|
| 284 | - return !(!extension_loaded('memcached') && extension_loaded('memcache')); |
|
| 285 | - } |
|
| 286 | - |
|
| 287 | - /** |
|
| 288 | - * @return RedirectResponse |
|
| 289 | - */ |
|
| 290 | - public function rescanFailedIntegrityCheck() { |
|
| 291 | - $this->checker->runInstanceVerification(); |
|
| 292 | - return new RedirectResponse( |
|
| 293 | - $this->urlGenerator->linkToRoute('settings.AdminSettings.index') |
|
| 294 | - ); |
|
| 295 | - } |
|
| 296 | - |
|
| 297 | - /** |
|
| 298 | - * @NoCSRFRequired |
|
| 299 | - * @return DataResponse |
|
| 300 | - */ |
|
| 301 | - public function getFailedIntegrityCheckFiles() { |
|
| 302 | - if(!$this->checker->isCodeCheckEnforced()) { |
|
| 303 | - return new DataDisplayResponse('Integrity checker has been disabled. Integrity cannot be verified.'); |
|
| 304 | - } |
|
| 305 | - |
|
| 306 | - $completeResults = $this->checker->getResults(); |
|
| 307 | - |
|
| 308 | - if(!empty($completeResults)) { |
|
| 309 | - $formattedTextResponse = 'Technical information |
|
| 48 | + /** @var IConfig */ |
|
| 49 | + private $config; |
|
| 50 | + /** @var IClientService */ |
|
| 51 | + private $clientService; |
|
| 52 | + /** @var \OC_Util */ |
|
| 53 | + private $util; |
|
| 54 | + /** @var IURLGenerator */ |
|
| 55 | + private $urlGenerator; |
|
| 56 | + /** @var IL10N */ |
|
| 57 | + private $l10n; |
|
| 58 | + /** @var Checker */ |
|
| 59 | + private $checker; |
|
| 60 | + /** @var ILogger */ |
|
| 61 | + private $logger; |
|
| 62 | + |
|
| 63 | + /** |
|
| 64 | + * @param string $AppName |
|
| 65 | + * @param IRequest $request |
|
| 66 | + * @param IConfig $config |
|
| 67 | + * @param IClientService $clientService |
|
| 68 | + * @param IURLGenerator $urlGenerator |
|
| 69 | + * @param \OC_Util $util |
|
| 70 | + * @param IL10N $l10n |
|
| 71 | + * @param Checker $checker |
|
| 72 | + * @param ILogger $logger |
|
| 73 | + */ |
|
| 74 | + public function __construct($AppName, |
|
| 75 | + IRequest $request, |
|
| 76 | + IConfig $config, |
|
| 77 | + IClientService $clientService, |
|
| 78 | + IURLGenerator $urlGenerator, |
|
| 79 | + \OC_Util $util, |
|
| 80 | + IL10N $l10n, |
|
| 81 | + Checker $checker, |
|
| 82 | + ILogger $logger) { |
|
| 83 | + parent::__construct($AppName, $request); |
|
| 84 | + $this->config = $config; |
|
| 85 | + $this->clientService = $clientService; |
|
| 86 | + $this->util = $util; |
|
| 87 | + $this->urlGenerator = $urlGenerator; |
|
| 88 | + $this->l10n = $l10n; |
|
| 89 | + $this->checker = $checker; |
|
| 90 | + $this->logger = $logger; |
|
| 91 | + } |
|
| 92 | + |
|
| 93 | + /** |
|
| 94 | + * Checks if the ownCloud server can connect to the internet using HTTPS and HTTP |
|
| 95 | + * @return bool |
|
| 96 | + */ |
|
| 97 | + private function isInternetConnectionWorking() { |
|
| 98 | + if ($this->config->getSystemValue('has_internet_connection', true) === false) { |
|
| 99 | + return false; |
|
| 100 | + } |
|
| 101 | + |
|
| 102 | + $siteArray = ['www.nextcloud.com', |
|
| 103 | + 'www.google.com', |
|
| 104 | + 'www.github.com']; |
|
| 105 | + |
|
| 106 | + foreach($siteArray as $site) { |
|
| 107 | + if ($this->isSiteReachable($site)) { |
|
| 108 | + return true; |
|
| 109 | + } |
|
| 110 | + } |
|
| 111 | + return false; |
|
| 112 | + } |
|
| 113 | + |
|
| 114 | + /** |
|
| 115 | + * Chceks if the ownCloud server can connect to a specific URL using both HTTPS and HTTP |
|
| 116 | + * @return bool |
|
| 117 | + */ |
|
| 118 | + private function isSiteReachable($sitename) { |
|
| 119 | + $httpSiteName = 'http://' . $sitename . '/'; |
|
| 120 | + $httpsSiteName = 'https://' . $sitename . '/'; |
|
| 121 | + |
|
| 122 | + try { |
|
| 123 | + $client = $this->clientService->newClient(); |
|
| 124 | + $client->get($httpSiteName); |
|
| 125 | + $client->get($httpsSiteName); |
|
| 126 | + } catch (\Exception $e) { |
|
| 127 | + $this->logger->logException($e, ['app' => 'internet_connection_check']); |
|
| 128 | + return false; |
|
| 129 | + } |
|
| 130 | + return true; |
|
| 131 | + } |
|
| 132 | + |
|
| 133 | + /** |
|
| 134 | + * Checks whether a local memcache is installed or not |
|
| 135 | + * @return bool |
|
| 136 | + */ |
|
| 137 | + private function isMemcacheConfigured() { |
|
| 138 | + return $this->config->getSystemValue('memcache.local', null) !== null; |
|
| 139 | + } |
|
| 140 | + |
|
| 141 | + /** |
|
| 142 | + * Whether /dev/urandom is available to the PHP controller |
|
| 143 | + * |
|
| 144 | + * @return bool |
|
| 145 | + */ |
|
| 146 | + private function isUrandomAvailable() { |
|
| 147 | + if(@file_exists('/dev/urandom')) { |
|
| 148 | + $file = fopen('/dev/urandom', 'rb'); |
|
| 149 | + if($file) { |
|
| 150 | + fclose($file); |
|
| 151 | + return true; |
|
| 152 | + } |
|
| 153 | + } |
|
| 154 | + |
|
| 155 | + return false; |
|
| 156 | + } |
|
| 157 | + |
|
| 158 | + /** |
|
| 159 | + * Public for the sake of unit-testing |
|
| 160 | + * |
|
| 161 | + * @return array |
|
| 162 | + */ |
|
| 163 | + protected function getCurlVersion() { |
|
| 164 | + return curl_version(); |
|
| 165 | + } |
|
| 166 | + |
|
| 167 | + /** |
|
| 168 | + * Check if the used SSL lib is outdated. Older OpenSSL and NSS versions do |
|
| 169 | + * have multiple bugs which likely lead to problems in combination with |
|
| 170 | + * functionality required by ownCloud such as SNI. |
|
| 171 | + * |
|
| 172 | + * @link https://github.com/owncloud/core/issues/17446#issuecomment-122877546 |
|
| 173 | + * @link https://bugzilla.redhat.com/show_bug.cgi?id=1241172 |
|
| 174 | + * @return string |
|
| 175 | + */ |
|
| 176 | + private function isUsedTlsLibOutdated() { |
|
| 177 | + // Don't run check when: |
|
| 178 | + // 1. Server has `has_internet_connection` set to false |
|
| 179 | + // 2. AppStore AND S2S is disabled |
|
| 180 | + if(!$this->config->getSystemValue('has_internet_connection', true)) { |
|
| 181 | + return ''; |
|
| 182 | + } |
|
| 183 | + if(!$this->config->getSystemValue('appstoreenabled', true) |
|
| 184 | + && $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'no' |
|
| 185 | + && $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes') === 'no') { |
|
| 186 | + return ''; |
|
| 187 | + } |
|
| 188 | + |
|
| 189 | + $versionString = $this->getCurlVersion(); |
|
| 190 | + if(isset($versionString['ssl_version'])) { |
|
| 191 | + $versionString = $versionString['ssl_version']; |
|
| 192 | + } else { |
|
| 193 | + return ''; |
|
| 194 | + } |
|
| 195 | + |
|
| 196 | + $features = (string)$this->l10n->t('installing and updating apps via the app store or Federated Cloud Sharing'); |
|
| 197 | + if(!$this->config->getSystemValue('appstoreenabled', true)) { |
|
| 198 | + $features = (string)$this->l10n->t('Federated Cloud Sharing'); |
|
| 199 | + } |
|
| 200 | + |
|
| 201 | + // Check if at least OpenSSL after 1.01d or 1.0.2b |
|
| 202 | + if(strpos($versionString, 'OpenSSL/') === 0) { |
|
| 203 | + $majorVersion = substr($versionString, 8, 5); |
|
| 204 | + $patchRelease = substr($versionString, 13, 6); |
|
| 205 | + |
|
| 206 | + if(($majorVersion === '1.0.1' && ord($patchRelease) < ord('d')) || |
|
| 207 | + ($majorVersion === '1.0.2' && ord($patchRelease) < ord('b'))) { |
|
| 208 | + return (string) $this->l10n->t('cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably.', ['OpenSSL', $versionString, $features]); |
|
| 209 | + } |
|
| 210 | + } |
|
| 211 | + |
|
| 212 | + // Check if NSS and perform heuristic check |
|
| 213 | + if(strpos($versionString, 'NSS/') === 0) { |
|
| 214 | + try { |
|
| 215 | + $firstClient = $this->clientService->newClient(); |
|
| 216 | + $firstClient->get('https://www.owncloud.org/'); |
|
| 217 | + |
|
| 218 | + $secondClient = $this->clientService->newClient(); |
|
| 219 | + $secondClient->get('https://owncloud.org/'); |
|
| 220 | + } catch (ClientException $e) { |
|
| 221 | + if($e->getResponse()->getStatusCode() === 400) { |
|
| 222 | + return (string) $this->l10n->t('cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably.', ['NSS', $versionString, $features]); |
|
| 223 | + } |
|
| 224 | + } |
|
| 225 | + } |
|
| 226 | + |
|
| 227 | + return ''; |
|
| 228 | + } |
|
| 229 | + |
|
| 230 | + /** |
|
| 231 | + * Whether the version is outdated |
|
| 232 | + * |
|
| 233 | + * @return bool |
|
| 234 | + */ |
|
| 235 | + protected function isPhpOutdated() { |
|
| 236 | + if (version_compare(PHP_VERSION, '5.5.0') === -1) { |
|
| 237 | + return true; |
|
| 238 | + } |
|
| 239 | + |
|
| 240 | + return false; |
|
| 241 | + } |
|
| 242 | + |
|
| 243 | + /** |
|
| 244 | + * Whether the php version is still supported (at time of release) |
|
| 245 | + * according to: https://secure.php.net/supported-versions.php |
|
| 246 | + * |
|
| 247 | + * @return array |
|
| 248 | + */ |
|
| 249 | + private function isPhpSupported() { |
|
| 250 | + return ['eol' => $this->isPhpOutdated(), 'version' => PHP_VERSION]; |
|
| 251 | + } |
|
| 252 | + |
|
| 253 | + /** |
|
| 254 | + * Check if the reverse proxy configuration is working as expected |
|
| 255 | + * |
|
| 256 | + * @return bool |
|
| 257 | + */ |
|
| 258 | + private function forwardedForHeadersWorking() { |
|
| 259 | + $trustedProxies = $this->config->getSystemValue('trusted_proxies', []); |
|
| 260 | + $remoteAddress = $this->request->getRemoteAddress(); |
|
| 261 | + |
|
| 262 | + if (is_array($trustedProxies) && in_array($remoteAddress, $trustedProxies)) { |
|
| 263 | + return false; |
|
| 264 | + } |
|
| 265 | + |
|
| 266 | + // either not enabled or working correctly |
|
| 267 | + return true; |
|
| 268 | + } |
|
| 269 | + |
|
| 270 | + /** |
|
| 271 | + * Checks if the correct memcache module for PHP is installed. Only |
|
| 272 | + * fails if memcached is configured and the working module is not installed. |
|
| 273 | + * |
|
| 274 | + * @return bool |
|
| 275 | + */ |
|
| 276 | + private function isCorrectMemcachedPHPModuleInstalled() { |
|
| 277 | + if ($this->config->getSystemValue('memcache.distributed', null) !== '\OC\Memcache\Memcached') { |
|
| 278 | + return true; |
|
| 279 | + } |
|
| 280 | + |
|
| 281 | + // there are two different memcached modules for PHP |
|
| 282 | + // we only support memcached and not memcache |
|
| 283 | + // https://code.google.com/p/memcached/wiki/PHPClientComparison |
|
| 284 | + return !(!extension_loaded('memcached') && extension_loaded('memcache')); |
|
| 285 | + } |
|
| 286 | + |
|
| 287 | + /** |
|
| 288 | + * @return RedirectResponse |
|
| 289 | + */ |
|
| 290 | + public function rescanFailedIntegrityCheck() { |
|
| 291 | + $this->checker->runInstanceVerification(); |
|
| 292 | + return new RedirectResponse( |
|
| 293 | + $this->urlGenerator->linkToRoute('settings.AdminSettings.index') |
|
| 294 | + ); |
|
| 295 | + } |
|
| 296 | + |
|
| 297 | + /** |
|
| 298 | + * @NoCSRFRequired |
|
| 299 | + * @return DataResponse |
|
| 300 | + */ |
|
| 301 | + public function getFailedIntegrityCheckFiles() { |
|
| 302 | + if(!$this->checker->isCodeCheckEnforced()) { |
|
| 303 | + return new DataDisplayResponse('Integrity checker has been disabled. Integrity cannot be verified.'); |
|
| 304 | + } |
|
| 305 | + |
|
| 306 | + $completeResults = $this->checker->getResults(); |
|
| 307 | + |
|
| 308 | + if(!empty($completeResults)) { |
|
| 309 | + $formattedTextResponse = 'Technical information |
|
| 310 | 310 | ===================== |
| 311 | 311 | The following list covers which files have failed the integrity check. Please read |
| 312 | 312 | the previous linked documentation to learn more about the errors and how to fix |
@@ -315,64 +315,64 @@ discard block |
||
| 315 | 315 | Results |
| 316 | 316 | ======= |
| 317 | 317 | '; |
| 318 | - foreach($completeResults as $context => $contextResult) { |
|
| 319 | - $formattedTextResponse .= "- $context\n"; |
|
| 320 | - |
|
| 321 | - foreach($contextResult as $category => $result) { |
|
| 322 | - $formattedTextResponse .= "\t- $category\n"; |
|
| 323 | - if($category !== 'EXCEPTION') { |
|
| 324 | - foreach ($result as $key => $results) { |
|
| 325 | - $formattedTextResponse .= "\t\t- $key\n"; |
|
| 326 | - } |
|
| 327 | - } else { |
|
| 328 | - foreach ($result as $key => $results) { |
|
| 329 | - $formattedTextResponse .= "\t\t- $results\n"; |
|
| 330 | - } |
|
| 331 | - } |
|
| 332 | - |
|
| 333 | - } |
|
| 334 | - } |
|
| 335 | - |
|
| 336 | - $formattedTextResponse .= ' |
|
| 318 | + foreach($completeResults as $context => $contextResult) { |
|
| 319 | + $formattedTextResponse .= "- $context\n"; |
|
| 320 | + |
|
| 321 | + foreach($contextResult as $category => $result) { |
|
| 322 | + $formattedTextResponse .= "\t- $category\n"; |
|
| 323 | + if($category !== 'EXCEPTION') { |
|
| 324 | + foreach ($result as $key => $results) { |
|
| 325 | + $formattedTextResponse .= "\t\t- $key\n"; |
|
| 326 | + } |
|
| 327 | + } else { |
|
| 328 | + foreach ($result as $key => $results) { |
|
| 329 | + $formattedTextResponse .= "\t\t- $results\n"; |
|
| 330 | + } |
|
| 331 | + } |
|
| 332 | + |
|
| 333 | + } |
|
| 334 | + } |
|
| 335 | + |
|
| 336 | + $formattedTextResponse .= ' |
|
| 337 | 337 | Raw output |
| 338 | 338 | ========== |
| 339 | 339 | '; |
| 340 | - $formattedTextResponse .= print_r($completeResults, true); |
|
| 341 | - } else { |
|
| 342 | - $formattedTextResponse = 'No errors have been found.'; |
|
| 343 | - } |
|
| 344 | - |
|
| 345 | - |
|
| 346 | - $response = new DataDisplayResponse( |
|
| 347 | - $formattedTextResponse, |
|
| 348 | - Http::STATUS_OK, |
|
| 349 | - [ |
|
| 350 | - 'Content-Type' => 'text/plain', |
|
| 351 | - ] |
|
| 352 | - ); |
|
| 353 | - |
|
| 354 | - return $response; |
|
| 355 | - } |
|
| 356 | - |
|
| 357 | - /** |
|
| 358 | - * @return DataResponse |
|
| 359 | - */ |
|
| 360 | - public function check() { |
|
| 361 | - return new DataResponse( |
|
| 362 | - [ |
|
| 363 | - 'serverHasInternetConnection' => $this->isInternetConnectionWorking(), |
|
| 364 | - 'isMemcacheConfigured' => $this->isMemcacheConfigured(), |
|
| 365 | - 'memcacheDocs' => $this->urlGenerator->linkToDocs('admin-performance'), |
|
| 366 | - 'isUrandomAvailable' => $this->isUrandomAvailable(), |
|
| 367 | - 'securityDocs' => $this->urlGenerator->linkToDocs('admin-security'), |
|
| 368 | - 'isUsedTlsLibOutdated' => $this->isUsedTlsLibOutdated(), |
|
| 369 | - 'phpSupported' => $this->isPhpSupported(), |
|
| 370 | - 'forwardedForHeadersWorking' => $this->forwardedForHeadersWorking(), |
|
| 371 | - 'reverseProxyDocs' => $this->urlGenerator->linkToDocs('admin-reverse-proxy'), |
|
| 372 | - 'isCorrectMemcachedPHPModuleInstalled' => $this->isCorrectMemcachedPHPModuleInstalled(), |
|
| 373 | - 'hasPassedCodeIntegrityCheck' => $this->checker->hasPassedCheck(), |
|
| 374 | - 'codeIntegrityCheckerDocumentation' => $this->urlGenerator->linkToDocs('admin-code-integrity'), |
|
| 375 | - ] |
|
| 376 | - ); |
|
| 377 | - } |
|
| 340 | + $formattedTextResponse .= print_r($completeResults, true); |
|
| 341 | + } else { |
|
| 342 | + $formattedTextResponse = 'No errors have been found.'; |
|
| 343 | + } |
|
| 344 | + |
|
| 345 | + |
|
| 346 | + $response = new DataDisplayResponse( |
|
| 347 | + $formattedTextResponse, |
|
| 348 | + Http::STATUS_OK, |
|
| 349 | + [ |
|
| 350 | + 'Content-Type' => 'text/plain', |
|
| 351 | + ] |
|
| 352 | + ); |
|
| 353 | + |
|
| 354 | + return $response; |
|
| 355 | + } |
|
| 356 | + |
|
| 357 | + /** |
|
| 358 | + * @return DataResponse |
|
| 359 | + */ |
|
| 360 | + public function check() { |
|
| 361 | + return new DataResponse( |
|
| 362 | + [ |
|
| 363 | + 'serverHasInternetConnection' => $this->isInternetConnectionWorking(), |
|
| 364 | + 'isMemcacheConfigured' => $this->isMemcacheConfigured(), |
|
| 365 | + 'memcacheDocs' => $this->urlGenerator->linkToDocs('admin-performance'), |
|
| 366 | + 'isUrandomAvailable' => $this->isUrandomAvailable(), |
|
| 367 | + 'securityDocs' => $this->urlGenerator->linkToDocs('admin-security'), |
|
| 368 | + 'isUsedTlsLibOutdated' => $this->isUsedTlsLibOutdated(), |
|
| 369 | + 'phpSupported' => $this->isPhpSupported(), |
|
| 370 | + 'forwardedForHeadersWorking' => $this->forwardedForHeadersWorking(), |
|
| 371 | + 'reverseProxyDocs' => $this->urlGenerator->linkToDocs('admin-reverse-proxy'), |
|
| 372 | + 'isCorrectMemcachedPHPModuleInstalled' => $this->isCorrectMemcachedPHPModuleInstalled(), |
|
| 373 | + 'hasPassedCodeIntegrityCheck' => $this->checker->hasPassedCheck(), |
|
| 374 | + 'codeIntegrityCheckerDocumentation' => $this->urlGenerator->linkToDocs('admin-code-integrity'), |
|
| 375 | + ] |
|
| 376 | + ); |
|
| 377 | + } |
|
| 378 | 378 | } |
@@ -103,7 +103,7 @@ discard block |
||
| 103 | 103 | 'www.google.com', |
| 104 | 104 | 'www.github.com']; |
| 105 | 105 | |
| 106 | - foreach($siteArray as $site) { |
|
| 106 | + foreach ($siteArray as $site) { |
|
| 107 | 107 | if ($this->isSiteReachable($site)) { |
| 108 | 108 | return true; |
| 109 | 109 | } |
@@ -116,8 +116,8 @@ discard block |
||
| 116 | 116 | * @return bool |
| 117 | 117 | */ |
| 118 | 118 | private function isSiteReachable($sitename) { |
| 119 | - $httpSiteName = 'http://' . $sitename . '/'; |
|
| 120 | - $httpsSiteName = 'https://' . $sitename . '/'; |
|
| 119 | + $httpSiteName = 'http://'.$sitename.'/'; |
|
| 120 | + $httpsSiteName = 'https://'.$sitename.'/'; |
|
| 121 | 121 | |
| 122 | 122 | try { |
| 123 | 123 | $client = $this->clientService->newClient(); |
@@ -144,9 +144,9 @@ discard block |
||
| 144 | 144 | * @return bool |
| 145 | 145 | */ |
| 146 | 146 | private function isUrandomAvailable() { |
| 147 | - if(@file_exists('/dev/urandom')) { |
|
| 147 | + if (@file_exists('/dev/urandom')) { |
|
| 148 | 148 | $file = fopen('/dev/urandom', 'rb'); |
| 149 | - if($file) { |
|
| 149 | + if ($file) { |
|
| 150 | 150 | fclose($file); |
| 151 | 151 | return true; |
| 152 | 152 | } |
@@ -177,40 +177,40 @@ discard block |
||
| 177 | 177 | // Don't run check when: |
| 178 | 178 | // 1. Server has `has_internet_connection` set to false |
| 179 | 179 | // 2. AppStore AND S2S is disabled |
| 180 | - if(!$this->config->getSystemValue('has_internet_connection', true)) { |
|
| 180 | + if (!$this->config->getSystemValue('has_internet_connection', true)) { |
|
| 181 | 181 | return ''; |
| 182 | 182 | } |
| 183 | - if(!$this->config->getSystemValue('appstoreenabled', true) |
|
| 183 | + if (!$this->config->getSystemValue('appstoreenabled', true) |
|
| 184 | 184 | && $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'no' |
| 185 | 185 | && $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes') === 'no') { |
| 186 | 186 | return ''; |
| 187 | 187 | } |
| 188 | 188 | |
| 189 | 189 | $versionString = $this->getCurlVersion(); |
| 190 | - if(isset($versionString['ssl_version'])) { |
|
| 190 | + if (isset($versionString['ssl_version'])) { |
|
| 191 | 191 | $versionString = $versionString['ssl_version']; |
| 192 | 192 | } else { |
| 193 | 193 | return ''; |
| 194 | 194 | } |
| 195 | 195 | |
| 196 | - $features = (string)$this->l10n->t('installing and updating apps via the app store or Federated Cloud Sharing'); |
|
| 197 | - if(!$this->config->getSystemValue('appstoreenabled', true)) { |
|
| 198 | - $features = (string)$this->l10n->t('Federated Cloud Sharing'); |
|
| 196 | + $features = (string) $this->l10n->t('installing and updating apps via the app store or Federated Cloud Sharing'); |
|
| 197 | + if (!$this->config->getSystemValue('appstoreenabled', true)) { |
|
| 198 | + $features = (string) $this->l10n->t('Federated Cloud Sharing'); |
|
| 199 | 199 | } |
| 200 | 200 | |
| 201 | 201 | // Check if at least OpenSSL after 1.01d or 1.0.2b |
| 202 | - if(strpos($versionString, 'OpenSSL/') === 0) { |
|
| 202 | + if (strpos($versionString, 'OpenSSL/') === 0) { |
|
| 203 | 203 | $majorVersion = substr($versionString, 8, 5); |
| 204 | 204 | $patchRelease = substr($versionString, 13, 6); |
| 205 | 205 | |
| 206 | - if(($majorVersion === '1.0.1' && ord($patchRelease) < ord('d')) || |
|
| 206 | + if (($majorVersion === '1.0.1' && ord($patchRelease) < ord('d')) || |
|
| 207 | 207 | ($majorVersion === '1.0.2' && ord($patchRelease) < ord('b'))) { |
| 208 | 208 | return (string) $this->l10n->t('cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably.', ['OpenSSL', $versionString, $features]); |
| 209 | 209 | } |
| 210 | 210 | } |
| 211 | 211 | |
| 212 | 212 | // Check if NSS and perform heuristic check |
| 213 | - if(strpos($versionString, 'NSS/') === 0) { |
|
| 213 | + if (strpos($versionString, 'NSS/') === 0) { |
|
| 214 | 214 | try { |
| 215 | 215 | $firstClient = $this->clientService->newClient(); |
| 216 | 216 | $firstClient->get('https://www.owncloud.org/'); |
@@ -218,7 +218,7 @@ discard block |
||
| 218 | 218 | $secondClient = $this->clientService->newClient(); |
| 219 | 219 | $secondClient->get('https://owncloud.org/'); |
| 220 | 220 | } catch (ClientException $e) { |
| 221 | - if($e->getResponse()->getStatusCode() === 400) { |
|
| 221 | + if ($e->getResponse()->getStatusCode() === 400) { |
|
| 222 | 222 | return (string) $this->l10n->t('cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably.', ['NSS', $versionString, $features]); |
| 223 | 223 | } |
| 224 | 224 | } |
@@ -299,13 +299,13 @@ discard block |
||
| 299 | 299 | * @return DataResponse |
| 300 | 300 | */ |
| 301 | 301 | public function getFailedIntegrityCheckFiles() { |
| 302 | - if(!$this->checker->isCodeCheckEnforced()) { |
|
| 302 | + if (!$this->checker->isCodeCheckEnforced()) { |
|
| 303 | 303 | return new DataDisplayResponse('Integrity checker has been disabled. Integrity cannot be verified.'); |
| 304 | 304 | } |
| 305 | 305 | |
| 306 | 306 | $completeResults = $this->checker->getResults(); |
| 307 | 307 | |
| 308 | - if(!empty($completeResults)) { |
|
| 308 | + if (!empty($completeResults)) { |
|
| 309 | 309 | $formattedTextResponse = 'Technical information |
| 310 | 310 | ===================== |
| 311 | 311 | The following list covers which files have failed the integrity check. Please read |
@@ -315,12 +315,12 @@ discard block |
||
| 315 | 315 | Results |
| 316 | 316 | ======= |
| 317 | 317 | '; |
| 318 | - foreach($completeResults as $context => $contextResult) { |
|
| 318 | + foreach ($completeResults as $context => $contextResult) { |
|
| 319 | 319 | $formattedTextResponse .= "- $context\n"; |
| 320 | 320 | |
| 321 | - foreach($contextResult as $category => $result) { |
|
| 321 | + foreach ($contextResult as $category => $result) { |
|
| 322 | 322 | $formattedTextResponse .= "\t- $category\n"; |
| 323 | - if($category !== 'EXCEPTION') { |
|
| 323 | + if ($category !== 'EXCEPTION') { |
|
| 324 | 324 | foreach ($result as $key => $results) { |
| 325 | 325 | $formattedTextResponse .= "\t\t- $key\n"; |
| 326 | 326 | } |
@@ -219,7 +219,7 @@ |
||
| 219 | 219 | * @param array $groupSharesById |
| 220 | 220 | * @param array $subShares |
| 221 | 221 | * |
| 222 | - * @return true if the share is valid, false if it needs repair |
|
| 222 | + * @return boolean if the share is valid, false if it needs repair |
|
| 223 | 223 | */ |
| 224 | 224 | private function isThisShareValid($groupSharesById, $subShares) { |
| 225 | 225 | $foundTargets = []; |
@@ -43,315 +43,315 @@ |
||
| 43 | 43 | */ |
| 44 | 44 | class RepairUnmergedShares implements IRepairStep { |
| 45 | 45 | |
| 46 | - /** @var \OCP\IConfig */ |
|
| 47 | - protected $config; |
|
| 48 | - |
|
| 49 | - /** @var \OCP\IDBConnection */ |
|
| 50 | - protected $connection; |
|
| 51 | - |
|
| 52 | - /** @var IUserManager */ |
|
| 53 | - protected $userManager; |
|
| 54 | - |
|
| 55 | - /** @var IGroupManager */ |
|
| 56 | - protected $groupManager; |
|
| 57 | - |
|
| 58 | - /** @var IQueryBuilder */ |
|
| 59 | - private $queryGetSharesWithUsers; |
|
| 60 | - |
|
| 61 | - /** @var IQueryBuilder */ |
|
| 62 | - private $queryUpdateSharePermissionsAndTarget; |
|
| 63 | - |
|
| 64 | - /** @var IQueryBuilder */ |
|
| 65 | - private $queryUpdateShareInBatch; |
|
| 66 | - |
|
| 67 | - /** |
|
| 68 | - * @param \OCP\IConfig $config |
|
| 69 | - * @param \OCP\IDBConnection $connection |
|
| 70 | - */ |
|
| 71 | - public function __construct( |
|
| 72 | - IConfig $config, |
|
| 73 | - IDBConnection $connection, |
|
| 74 | - IUserManager $userManager, |
|
| 75 | - IGroupManager $groupManager |
|
| 76 | - ) { |
|
| 77 | - $this->connection = $connection; |
|
| 78 | - $this->config = $config; |
|
| 79 | - $this->userManager = $userManager; |
|
| 80 | - $this->groupManager = $groupManager; |
|
| 81 | - } |
|
| 82 | - |
|
| 83 | - public function getName() { |
|
| 84 | - return 'Repair unmerged shares'; |
|
| 85 | - } |
|
| 86 | - |
|
| 87 | - /** |
|
| 88 | - * Builds prepared queries for reuse |
|
| 89 | - */ |
|
| 90 | - private function buildPreparedQueries() { |
|
| 91 | - /** |
|
| 92 | - * Retrieve shares for a given user/group and share type |
|
| 93 | - */ |
|
| 94 | - $query = $this->connection->getQueryBuilder(); |
|
| 95 | - $query |
|
| 96 | - ->select('item_source', 'id', 'file_target', 'permissions', 'parent', 'share_type', 'stime') |
|
| 97 | - ->from('share') |
|
| 98 | - ->where($query->expr()->eq('share_type', $query->createParameter('shareType'))) |
|
| 99 | - ->andWhere($query->expr()->in('share_with', $query->createParameter('shareWiths'))) |
|
| 100 | - ->andWhere($query->expr()->in('item_type', $query->createParameter('itemTypes'))) |
|
| 101 | - ->orderBy('item_source', 'ASC') |
|
| 102 | - ->addOrderBy('stime', 'ASC'); |
|
| 103 | - |
|
| 104 | - $this->queryGetSharesWithUsers = $query; |
|
| 105 | - |
|
| 106 | - /** |
|
| 107 | - * Updates the file_target to the given value for all given share ids. |
|
| 108 | - * |
|
| 109 | - * This updates several shares in bulk which is faster than individually. |
|
| 110 | - */ |
|
| 111 | - $query = $this->connection->getQueryBuilder(); |
|
| 112 | - $query->update('share') |
|
| 113 | - ->set('file_target', $query->createParameter('file_target')) |
|
| 114 | - ->where($query->expr()->in('id', $query->createParameter('ids'))); |
|
| 115 | - |
|
| 116 | - $this->queryUpdateShareInBatch = $query; |
|
| 117 | - |
|
| 118 | - /** |
|
| 119 | - * Updates the share permissions and target path of a single share. |
|
| 120 | - */ |
|
| 121 | - $query = $this->connection->getQueryBuilder(); |
|
| 122 | - $query->update('share') |
|
| 123 | - ->set('permissions', $query->createParameter('permissions')) |
|
| 124 | - ->set('file_target', $query->createParameter('file_target')) |
|
| 125 | - ->where($query->expr()->eq('id', $query->createParameter('shareid'))); |
|
| 126 | - |
|
| 127 | - $this->queryUpdateSharePermissionsAndTarget = $query; |
|
| 128 | - |
|
| 129 | - } |
|
| 130 | - |
|
| 131 | - private function getSharesWithUser($shareType, $shareWiths) { |
|
| 132 | - $groupedShares = []; |
|
| 133 | - |
|
| 134 | - $query = $this->queryGetSharesWithUsers; |
|
| 135 | - $query->setParameter('shareWiths', $shareWiths, IQueryBuilder::PARAM_STR_ARRAY); |
|
| 136 | - $query->setParameter('shareType', $shareType); |
|
| 137 | - $query->setParameter('itemTypes', ['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY); |
|
| 138 | - |
|
| 139 | - $shares = $query->execute()->fetchAll(); |
|
| 140 | - |
|
| 141 | - // group by item_source |
|
| 142 | - foreach ($shares as $share) { |
|
| 143 | - if (!isset($groupedShares[$share['item_source']])) { |
|
| 144 | - $groupedShares[$share['item_source']] = []; |
|
| 145 | - } |
|
| 146 | - $groupedShares[$share['item_source']][] = $share; |
|
| 147 | - } |
|
| 148 | - return $groupedShares; |
|
| 149 | - } |
|
| 150 | - |
|
| 151 | - private function isPotentialDuplicateName($name) { |
|
| 152 | - return (preg_match('/\(\d+\)(\.[^\.]+)?$/', $name) === 1); |
|
| 153 | - } |
|
| 154 | - |
|
| 155 | - /** |
|
| 156 | - * Decide on the best target name based on all group shares and subshares, |
|
| 157 | - * goal is to increase the likeliness that the chosen name matches what |
|
| 158 | - * the user is expecting. |
|
| 159 | - * |
|
| 160 | - * For this, we discard the entries with parenthesis "(2)". |
|
| 161 | - * In case the user also renamed the duplicates to a legitimate name, this logic |
|
| 162 | - * will still pick the most recent one as it's the one the user is most likely to |
|
| 163 | - * remember renaming. |
|
| 164 | - * |
|
| 165 | - * If no suitable subshare is found, use the least recent group share instead. |
|
| 166 | - * |
|
| 167 | - * @param array $groupShares group share entries |
|
| 168 | - * @param array $subShares sub share entries |
|
| 169 | - * |
|
| 170 | - * @return string chosen target name |
|
| 171 | - */ |
|
| 172 | - private function findBestTargetName($groupShares, $subShares) { |
|
| 173 | - $pickedShare = null; |
|
| 174 | - // sort by stime, this also properly sorts the direct user share if any |
|
| 175 | - @usort($subShares, function($a, $b) { |
|
| 176 | - return ((int)$a['stime'] - (int)$b['stime']); |
|
| 177 | - }); |
|
| 178 | - |
|
| 179 | - foreach ($subShares as $subShare) { |
|
| 180 | - // skip entries that have parenthesis with numbers |
|
| 181 | - if ($this->isPotentialDuplicateName($subShare['file_target'])) { |
|
| 182 | - continue; |
|
| 183 | - } |
|
| 184 | - // pick any share found that would match, the last being the most recent |
|
| 185 | - $pickedShare = $subShare; |
|
| 186 | - } |
|
| 187 | - |
|
| 188 | - // no suitable subshare found |
|
| 189 | - if ($pickedShare === null) { |
|
| 190 | - // use least recent group share target instead |
|
| 191 | - $pickedShare = $groupShares[0]; |
|
| 192 | - } |
|
| 193 | - |
|
| 194 | - return $pickedShare['file_target']; |
|
| 195 | - } |
|
| 196 | - |
|
| 197 | - /** |
|
| 198 | - * Fix the given received share represented by the set of group shares |
|
| 199 | - * and matching sub shares |
|
| 200 | - * |
|
| 201 | - * @param array $groupShares group share entries |
|
| 202 | - * @param array $subShares sub share entries |
|
| 203 | - * |
|
| 204 | - * @return boolean false if the share was not repaired, true if it was |
|
| 205 | - */ |
|
| 206 | - private function fixThisShare($groupShares, $subShares) { |
|
| 207 | - if (empty($subShares)) { |
|
| 208 | - return false; |
|
| 209 | - } |
|
| 210 | - |
|
| 211 | - $groupSharesById = []; |
|
| 212 | - foreach ($groupShares as $groupShare) { |
|
| 213 | - $groupSharesById[$groupShare['id']] = $groupShare; |
|
| 214 | - } |
|
| 215 | - |
|
| 216 | - if ($this->isThisShareValid($groupSharesById, $subShares)) { |
|
| 217 | - return false; |
|
| 218 | - } |
|
| 219 | - |
|
| 220 | - $targetPath = $this->findBestTargetName($groupShares, $subShares); |
|
| 221 | - |
|
| 222 | - // check whether the user opted out completely of all subshares |
|
| 223 | - $optedOut = true; |
|
| 224 | - foreach ($subShares as $subShare) { |
|
| 225 | - if ((int)$subShare['permissions'] !== 0) { |
|
| 226 | - $optedOut = false; |
|
| 227 | - break; |
|
| 228 | - } |
|
| 229 | - } |
|
| 230 | - |
|
| 231 | - $shareIds = []; |
|
| 232 | - foreach ($subShares as $subShare) { |
|
| 233 | - // only if the user deleted some subshares but not all, adjust the permissions of that subshare |
|
| 234 | - if (!$optedOut && (int)$subShare['permissions'] === 0 && (int)$subShare['share_type'] === DefaultShareProvider::SHARE_TYPE_USERGROUP) { |
|
| 235 | - // set permissions from parent group share |
|
| 236 | - $permissions = $groupSharesById[$subShare['parent']]['permissions']; |
|
| 237 | - |
|
| 238 | - // fix permissions and target directly |
|
| 239 | - $query = $this->queryUpdateSharePermissionsAndTarget; |
|
| 240 | - $query->setParameter('shareid', $subShare['id']); |
|
| 241 | - $query->setParameter('file_target', $targetPath); |
|
| 242 | - $query->setParameter('permissions', $permissions); |
|
| 243 | - $query->execute(); |
|
| 244 | - } else { |
|
| 245 | - // gather share ids for bulk target update |
|
| 246 | - if ($subShare['file_target'] !== $targetPath) { |
|
| 247 | - $shareIds[] = (int)$subShare['id']; |
|
| 248 | - } |
|
| 249 | - } |
|
| 250 | - } |
|
| 251 | - |
|
| 252 | - if (!empty($shareIds)) { |
|
| 253 | - $query = $this->queryUpdateShareInBatch; |
|
| 254 | - $query->setParameter('ids', $shareIds, IQueryBuilder::PARAM_INT_ARRAY); |
|
| 255 | - $query->setParameter('file_target', $targetPath); |
|
| 256 | - $query->execute(); |
|
| 257 | - } |
|
| 258 | - |
|
| 259 | - return true; |
|
| 260 | - } |
|
| 261 | - |
|
| 262 | - /** |
|
| 263 | - * Checks whether the number of group shares is balanced with the child subshares. |
|
| 264 | - * If all group shares have exactly one subshare, and the target of every subshare |
|
| 265 | - * is the same, then the share is valid. |
|
| 266 | - * If however there is a group share entry that has no matching subshare, it means |
|
| 267 | - * we're in the bogus situation and the whole share must be repaired |
|
| 268 | - * |
|
| 269 | - * @param array $groupSharesById |
|
| 270 | - * @param array $subShares |
|
| 271 | - * |
|
| 272 | - * @return true if the share is valid, false if it needs repair |
|
| 273 | - */ |
|
| 274 | - private function isThisShareValid($groupSharesById, $subShares) { |
|
| 275 | - $foundTargets = []; |
|
| 276 | - |
|
| 277 | - // every group share needs to have exactly one matching subshare |
|
| 278 | - foreach ($subShares as $subShare) { |
|
| 279 | - $foundTargets[$subShare['file_target']] = true; |
|
| 280 | - if (count($foundTargets) > 1) { |
|
| 281 | - // not all the same target path value => invalid |
|
| 282 | - return false; |
|
| 283 | - } |
|
| 284 | - if (isset($groupSharesById[$subShare['parent']])) { |
|
| 285 | - // remove it from the list as we found it |
|
| 286 | - unset($groupSharesById[$subShare['parent']]); |
|
| 287 | - } |
|
| 288 | - } |
|
| 289 | - |
|
| 290 | - // if we found one subshare per group entry, the set will be empty. |
|
| 291 | - // If not empty, it means that one of the group shares did not have |
|
| 292 | - // a matching subshare entry. |
|
| 293 | - return empty($groupSharesById); |
|
| 294 | - } |
|
| 295 | - |
|
| 296 | - /** |
|
| 297 | - * Detect unmerged received shares and merge them properly |
|
| 298 | - */ |
|
| 299 | - private function fixUnmergedShares(IOutput $out, IUser $user) { |
|
| 300 | - $groups = $this->groupManager->getUserGroupIds($user); |
|
| 301 | - if (empty($groups)) { |
|
| 302 | - // user is in no groups, so can't have received group shares |
|
| 303 | - return; |
|
| 304 | - } |
|
| 305 | - |
|
| 306 | - // get all subshares grouped by item source |
|
| 307 | - $subSharesByItemSource = $this->getSharesWithUser(DefaultShareProvider::SHARE_TYPE_USERGROUP, [$user->getUID()]); |
|
| 308 | - |
|
| 309 | - // because sometimes one wants to give the user more permissions than the group share |
|
| 310 | - $userSharesByItemSource = $this->getSharesWithUser(Constants::SHARE_TYPE_USER, [$user->getUID()]); |
|
| 311 | - |
|
| 312 | - if (empty($subSharesByItemSource) && empty($userSharesByItemSource)) { |
|
| 313 | - // nothing to repair for this user, no need to do extra queries |
|
| 314 | - return; |
|
| 315 | - } |
|
| 316 | - |
|
| 317 | - $groupSharesByItemSource = $this->getSharesWithUser(Constants::SHARE_TYPE_GROUP, $groups); |
|
| 318 | - if (empty($groupSharesByItemSource) && empty($userSharesByItemSource)) { |
|
| 319 | - // nothing to repair for this user |
|
| 320 | - return; |
|
| 321 | - } |
|
| 322 | - |
|
| 323 | - foreach ($groupSharesByItemSource as $itemSource => $groupShares) { |
|
| 324 | - $subShares = []; |
|
| 325 | - if (isset($subSharesByItemSource[$itemSource])) { |
|
| 326 | - $subShares = $subSharesByItemSource[$itemSource]; |
|
| 327 | - } |
|
| 328 | - |
|
| 329 | - if (isset($userSharesByItemSource[$itemSource])) { |
|
| 330 | - // add it to the subshares to get a similar treatment |
|
| 331 | - $subShares = array_merge($subShares, $userSharesByItemSource[$itemSource]); |
|
| 332 | - } |
|
| 333 | - |
|
| 334 | - $this->fixThisShare($groupShares, $subShares); |
|
| 335 | - } |
|
| 336 | - } |
|
| 337 | - |
|
| 338 | - public function run(IOutput $output) { |
|
| 339 | - $ocVersionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0'); |
|
| 340 | - if (version_compare($ocVersionFromBeforeUpdate, '9.1.0.16', '<')) { |
|
| 341 | - // this situation was only possible between 9.0.0 and 9.0.3 included |
|
| 342 | - |
|
| 343 | - $function = function(IUser $user) use ($output) { |
|
| 344 | - $this->fixUnmergedShares($output, $user); |
|
| 345 | - $output->advance(); |
|
| 346 | - }; |
|
| 347 | - |
|
| 348 | - $this->buildPreparedQueries(); |
|
| 349 | - |
|
| 350 | - $output->startProgress($this->userManager->countUsers()); |
|
| 351 | - |
|
| 352 | - $this->userManager->callForAllUsers($function); |
|
| 353 | - |
|
| 354 | - $output->finishProgress(); |
|
| 355 | - } |
|
| 356 | - } |
|
| 46 | + /** @var \OCP\IConfig */ |
|
| 47 | + protected $config; |
|
| 48 | + |
|
| 49 | + /** @var \OCP\IDBConnection */ |
|
| 50 | + protected $connection; |
|
| 51 | + |
|
| 52 | + /** @var IUserManager */ |
|
| 53 | + protected $userManager; |
|
| 54 | + |
|
| 55 | + /** @var IGroupManager */ |
|
| 56 | + protected $groupManager; |
|
| 57 | + |
|
| 58 | + /** @var IQueryBuilder */ |
|
| 59 | + private $queryGetSharesWithUsers; |
|
| 60 | + |
|
| 61 | + /** @var IQueryBuilder */ |
|
| 62 | + private $queryUpdateSharePermissionsAndTarget; |
|
| 63 | + |
|
| 64 | + /** @var IQueryBuilder */ |
|
| 65 | + private $queryUpdateShareInBatch; |
|
| 66 | + |
|
| 67 | + /** |
|
| 68 | + * @param \OCP\IConfig $config |
|
| 69 | + * @param \OCP\IDBConnection $connection |
|
| 70 | + */ |
|
| 71 | + public function __construct( |
|
| 72 | + IConfig $config, |
|
| 73 | + IDBConnection $connection, |
|
| 74 | + IUserManager $userManager, |
|
| 75 | + IGroupManager $groupManager |
|
| 76 | + ) { |
|
| 77 | + $this->connection = $connection; |
|
| 78 | + $this->config = $config; |
|
| 79 | + $this->userManager = $userManager; |
|
| 80 | + $this->groupManager = $groupManager; |
|
| 81 | + } |
|
| 82 | + |
|
| 83 | + public function getName() { |
|
| 84 | + return 'Repair unmerged shares'; |
|
| 85 | + } |
|
| 86 | + |
|
| 87 | + /** |
|
| 88 | + * Builds prepared queries for reuse |
|
| 89 | + */ |
|
| 90 | + private function buildPreparedQueries() { |
|
| 91 | + /** |
|
| 92 | + * Retrieve shares for a given user/group and share type |
|
| 93 | + */ |
|
| 94 | + $query = $this->connection->getQueryBuilder(); |
|
| 95 | + $query |
|
| 96 | + ->select('item_source', 'id', 'file_target', 'permissions', 'parent', 'share_type', 'stime') |
|
| 97 | + ->from('share') |
|
| 98 | + ->where($query->expr()->eq('share_type', $query->createParameter('shareType'))) |
|
| 99 | + ->andWhere($query->expr()->in('share_with', $query->createParameter('shareWiths'))) |
|
| 100 | + ->andWhere($query->expr()->in('item_type', $query->createParameter('itemTypes'))) |
|
| 101 | + ->orderBy('item_source', 'ASC') |
|
| 102 | + ->addOrderBy('stime', 'ASC'); |
|
| 103 | + |
|
| 104 | + $this->queryGetSharesWithUsers = $query; |
|
| 105 | + |
|
| 106 | + /** |
|
| 107 | + * Updates the file_target to the given value for all given share ids. |
|
| 108 | + * |
|
| 109 | + * This updates several shares in bulk which is faster than individually. |
|
| 110 | + */ |
|
| 111 | + $query = $this->connection->getQueryBuilder(); |
|
| 112 | + $query->update('share') |
|
| 113 | + ->set('file_target', $query->createParameter('file_target')) |
|
| 114 | + ->where($query->expr()->in('id', $query->createParameter('ids'))); |
|
| 115 | + |
|
| 116 | + $this->queryUpdateShareInBatch = $query; |
|
| 117 | + |
|
| 118 | + /** |
|
| 119 | + * Updates the share permissions and target path of a single share. |
|
| 120 | + */ |
|
| 121 | + $query = $this->connection->getQueryBuilder(); |
|
| 122 | + $query->update('share') |
|
| 123 | + ->set('permissions', $query->createParameter('permissions')) |
|
| 124 | + ->set('file_target', $query->createParameter('file_target')) |
|
| 125 | + ->where($query->expr()->eq('id', $query->createParameter('shareid'))); |
|
| 126 | + |
|
| 127 | + $this->queryUpdateSharePermissionsAndTarget = $query; |
|
| 128 | + |
|
| 129 | + } |
|
| 130 | + |
|
| 131 | + private function getSharesWithUser($shareType, $shareWiths) { |
|
| 132 | + $groupedShares = []; |
|
| 133 | + |
|
| 134 | + $query = $this->queryGetSharesWithUsers; |
|
| 135 | + $query->setParameter('shareWiths', $shareWiths, IQueryBuilder::PARAM_STR_ARRAY); |
|
| 136 | + $query->setParameter('shareType', $shareType); |
|
| 137 | + $query->setParameter('itemTypes', ['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY); |
|
| 138 | + |
|
| 139 | + $shares = $query->execute()->fetchAll(); |
|
| 140 | + |
|
| 141 | + // group by item_source |
|
| 142 | + foreach ($shares as $share) { |
|
| 143 | + if (!isset($groupedShares[$share['item_source']])) { |
|
| 144 | + $groupedShares[$share['item_source']] = []; |
|
| 145 | + } |
|
| 146 | + $groupedShares[$share['item_source']][] = $share; |
|
| 147 | + } |
|
| 148 | + return $groupedShares; |
|
| 149 | + } |
|
| 150 | + |
|
| 151 | + private function isPotentialDuplicateName($name) { |
|
| 152 | + return (preg_match('/\(\d+\)(\.[^\.]+)?$/', $name) === 1); |
|
| 153 | + } |
|
| 154 | + |
|
| 155 | + /** |
|
| 156 | + * Decide on the best target name based on all group shares and subshares, |
|
| 157 | + * goal is to increase the likeliness that the chosen name matches what |
|
| 158 | + * the user is expecting. |
|
| 159 | + * |
|
| 160 | + * For this, we discard the entries with parenthesis "(2)". |
|
| 161 | + * In case the user also renamed the duplicates to a legitimate name, this logic |
|
| 162 | + * will still pick the most recent one as it's the one the user is most likely to |
|
| 163 | + * remember renaming. |
|
| 164 | + * |
|
| 165 | + * If no suitable subshare is found, use the least recent group share instead. |
|
| 166 | + * |
|
| 167 | + * @param array $groupShares group share entries |
|
| 168 | + * @param array $subShares sub share entries |
|
| 169 | + * |
|
| 170 | + * @return string chosen target name |
|
| 171 | + */ |
|
| 172 | + private function findBestTargetName($groupShares, $subShares) { |
|
| 173 | + $pickedShare = null; |
|
| 174 | + // sort by stime, this also properly sorts the direct user share if any |
|
| 175 | + @usort($subShares, function($a, $b) { |
|
| 176 | + return ((int)$a['stime'] - (int)$b['stime']); |
|
| 177 | + }); |
|
| 178 | + |
|
| 179 | + foreach ($subShares as $subShare) { |
|
| 180 | + // skip entries that have parenthesis with numbers |
|
| 181 | + if ($this->isPotentialDuplicateName($subShare['file_target'])) { |
|
| 182 | + continue; |
|
| 183 | + } |
|
| 184 | + // pick any share found that would match, the last being the most recent |
|
| 185 | + $pickedShare = $subShare; |
|
| 186 | + } |
|
| 187 | + |
|
| 188 | + // no suitable subshare found |
|
| 189 | + if ($pickedShare === null) { |
|
| 190 | + // use least recent group share target instead |
|
| 191 | + $pickedShare = $groupShares[0]; |
|
| 192 | + } |
|
| 193 | + |
|
| 194 | + return $pickedShare['file_target']; |
|
| 195 | + } |
|
| 196 | + |
|
| 197 | + /** |
|
| 198 | + * Fix the given received share represented by the set of group shares |
|
| 199 | + * and matching sub shares |
|
| 200 | + * |
|
| 201 | + * @param array $groupShares group share entries |
|
| 202 | + * @param array $subShares sub share entries |
|
| 203 | + * |
|
| 204 | + * @return boolean false if the share was not repaired, true if it was |
|
| 205 | + */ |
|
| 206 | + private function fixThisShare($groupShares, $subShares) { |
|
| 207 | + if (empty($subShares)) { |
|
| 208 | + return false; |
|
| 209 | + } |
|
| 210 | + |
|
| 211 | + $groupSharesById = []; |
|
| 212 | + foreach ($groupShares as $groupShare) { |
|
| 213 | + $groupSharesById[$groupShare['id']] = $groupShare; |
|
| 214 | + } |
|
| 215 | + |
|
| 216 | + if ($this->isThisShareValid($groupSharesById, $subShares)) { |
|
| 217 | + return false; |
|
| 218 | + } |
|
| 219 | + |
|
| 220 | + $targetPath = $this->findBestTargetName($groupShares, $subShares); |
|
| 221 | + |
|
| 222 | + // check whether the user opted out completely of all subshares |
|
| 223 | + $optedOut = true; |
|
| 224 | + foreach ($subShares as $subShare) { |
|
| 225 | + if ((int)$subShare['permissions'] !== 0) { |
|
| 226 | + $optedOut = false; |
|
| 227 | + break; |
|
| 228 | + } |
|
| 229 | + } |
|
| 230 | + |
|
| 231 | + $shareIds = []; |
|
| 232 | + foreach ($subShares as $subShare) { |
|
| 233 | + // only if the user deleted some subshares but not all, adjust the permissions of that subshare |
|
| 234 | + if (!$optedOut && (int)$subShare['permissions'] === 0 && (int)$subShare['share_type'] === DefaultShareProvider::SHARE_TYPE_USERGROUP) { |
|
| 235 | + // set permissions from parent group share |
|
| 236 | + $permissions = $groupSharesById[$subShare['parent']]['permissions']; |
|
| 237 | + |
|
| 238 | + // fix permissions and target directly |
|
| 239 | + $query = $this->queryUpdateSharePermissionsAndTarget; |
|
| 240 | + $query->setParameter('shareid', $subShare['id']); |
|
| 241 | + $query->setParameter('file_target', $targetPath); |
|
| 242 | + $query->setParameter('permissions', $permissions); |
|
| 243 | + $query->execute(); |
|
| 244 | + } else { |
|
| 245 | + // gather share ids for bulk target update |
|
| 246 | + if ($subShare['file_target'] !== $targetPath) { |
|
| 247 | + $shareIds[] = (int)$subShare['id']; |
|
| 248 | + } |
|
| 249 | + } |
|
| 250 | + } |
|
| 251 | + |
|
| 252 | + if (!empty($shareIds)) { |
|
| 253 | + $query = $this->queryUpdateShareInBatch; |
|
| 254 | + $query->setParameter('ids', $shareIds, IQueryBuilder::PARAM_INT_ARRAY); |
|
| 255 | + $query->setParameter('file_target', $targetPath); |
|
| 256 | + $query->execute(); |
|
| 257 | + } |
|
| 258 | + |
|
| 259 | + return true; |
|
| 260 | + } |
|
| 261 | + |
|
| 262 | + /** |
|
| 263 | + * Checks whether the number of group shares is balanced with the child subshares. |
|
| 264 | + * If all group shares have exactly one subshare, and the target of every subshare |
|
| 265 | + * is the same, then the share is valid. |
|
| 266 | + * If however there is a group share entry that has no matching subshare, it means |
|
| 267 | + * we're in the bogus situation and the whole share must be repaired |
|
| 268 | + * |
|
| 269 | + * @param array $groupSharesById |
|
| 270 | + * @param array $subShares |
|
| 271 | + * |
|
| 272 | + * @return true if the share is valid, false if it needs repair |
|
| 273 | + */ |
|
| 274 | + private function isThisShareValid($groupSharesById, $subShares) { |
|
| 275 | + $foundTargets = []; |
|
| 276 | + |
|
| 277 | + // every group share needs to have exactly one matching subshare |
|
| 278 | + foreach ($subShares as $subShare) { |
|
| 279 | + $foundTargets[$subShare['file_target']] = true; |
|
| 280 | + if (count($foundTargets) > 1) { |
|
| 281 | + // not all the same target path value => invalid |
|
| 282 | + return false; |
|
| 283 | + } |
|
| 284 | + if (isset($groupSharesById[$subShare['parent']])) { |
|
| 285 | + // remove it from the list as we found it |
|
| 286 | + unset($groupSharesById[$subShare['parent']]); |
|
| 287 | + } |
|
| 288 | + } |
|
| 289 | + |
|
| 290 | + // if we found one subshare per group entry, the set will be empty. |
|
| 291 | + // If not empty, it means that one of the group shares did not have |
|
| 292 | + // a matching subshare entry. |
|
| 293 | + return empty($groupSharesById); |
|
| 294 | + } |
|
| 295 | + |
|
| 296 | + /** |
|
| 297 | + * Detect unmerged received shares and merge them properly |
|
| 298 | + */ |
|
| 299 | + private function fixUnmergedShares(IOutput $out, IUser $user) { |
|
| 300 | + $groups = $this->groupManager->getUserGroupIds($user); |
|
| 301 | + if (empty($groups)) { |
|
| 302 | + // user is in no groups, so can't have received group shares |
|
| 303 | + return; |
|
| 304 | + } |
|
| 305 | + |
|
| 306 | + // get all subshares grouped by item source |
|
| 307 | + $subSharesByItemSource = $this->getSharesWithUser(DefaultShareProvider::SHARE_TYPE_USERGROUP, [$user->getUID()]); |
|
| 308 | + |
|
| 309 | + // because sometimes one wants to give the user more permissions than the group share |
|
| 310 | + $userSharesByItemSource = $this->getSharesWithUser(Constants::SHARE_TYPE_USER, [$user->getUID()]); |
|
| 311 | + |
|
| 312 | + if (empty($subSharesByItemSource) && empty($userSharesByItemSource)) { |
|
| 313 | + // nothing to repair for this user, no need to do extra queries |
|
| 314 | + return; |
|
| 315 | + } |
|
| 316 | + |
|
| 317 | + $groupSharesByItemSource = $this->getSharesWithUser(Constants::SHARE_TYPE_GROUP, $groups); |
|
| 318 | + if (empty($groupSharesByItemSource) && empty($userSharesByItemSource)) { |
|
| 319 | + // nothing to repair for this user |
|
| 320 | + return; |
|
| 321 | + } |
|
| 322 | + |
|
| 323 | + foreach ($groupSharesByItemSource as $itemSource => $groupShares) { |
|
| 324 | + $subShares = []; |
|
| 325 | + if (isset($subSharesByItemSource[$itemSource])) { |
|
| 326 | + $subShares = $subSharesByItemSource[$itemSource]; |
|
| 327 | + } |
|
| 328 | + |
|
| 329 | + if (isset($userSharesByItemSource[$itemSource])) { |
|
| 330 | + // add it to the subshares to get a similar treatment |
|
| 331 | + $subShares = array_merge($subShares, $userSharesByItemSource[$itemSource]); |
|
| 332 | + } |
|
| 333 | + |
|
| 334 | + $this->fixThisShare($groupShares, $subShares); |
|
| 335 | + } |
|
| 336 | + } |
|
| 337 | + |
|
| 338 | + public function run(IOutput $output) { |
|
| 339 | + $ocVersionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0'); |
|
| 340 | + if (version_compare($ocVersionFromBeforeUpdate, '9.1.0.16', '<')) { |
|
| 341 | + // this situation was only possible between 9.0.0 and 9.0.3 included |
|
| 342 | + |
|
| 343 | + $function = function(IUser $user) use ($output) { |
|
| 344 | + $this->fixUnmergedShares($output, $user); |
|
| 345 | + $output->advance(); |
|
| 346 | + }; |
|
| 347 | + |
|
| 348 | + $this->buildPreparedQueries(); |
|
| 349 | + |
|
| 350 | + $output->startProgress($this->userManager->countUsers()); |
|
| 351 | + |
|
| 352 | + $this->userManager->callForAllUsers($function); |
|
| 353 | + |
|
| 354 | + $output->finishProgress(); |
|
| 355 | + } |
|
| 356 | + } |
|
| 357 | 357 | } |
@@ -173,7 +173,7 @@ discard block |
||
| 173 | 173 | $pickedShare = null; |
| 174 | 174 | // sort by stime, this also properly sorts the direct user share if any |
| 175 | 175 | @usort($subShares, function($a, $b) { |
| 176 | - return ((int)$a['stime'] - (int)$b['stime']); |
|
| 176 | + return ((int) $a['stime'] - (int) $b['stime']); |
|
| 177 | 177 | }); |
| 178 | 178 | |
| 179 | 179 | foreach ($subShares as $subShare) { |
@@ -222,7 +222,7 @@ discard block |
||
| 222 | 222 | // check whether the user opted out completely of all subshares |
| 223 | 223 | $optedOut = true; |
| 224 | 224 | foreach ($subShares as $subShare) { |
| 225 | - if ((int)$subShare['permissions'] !== 0) { |
|
| 225 | + if ((int) $subShare['permissions'] !== 0) { |
|
| 226 | 226 | $optedOut = false; |
| 227 | 227 | break; |
| 228 | 228 | } |
@@ -231,7 +231,7 @@ discard block |
||
| 231 | 231 | $shareIds = []; |
| 232 | 232 | foreach ($subShares as $subShare) { |
| 233 | 233 | // only if the user deleted some subshares but not all, adjust the permissions of that subshare |
| 234 | - if (!$optedOut && (int)$subShare['permissions'] === 0 && (int)$subShare['share_type'] === DefaultShareProvider::SHARE_TYPE_USERGROUP) { |
|
| 234 | + if (!$optedOut && (int) $subShare['permissions'] === 0 && (int) $subShare['share_type'] === DefaultShareProvider::SHARE_TYPE_USERGROUP) { |
|
| 235 | 235 | // set permissions from parent group share |
| 236 | 236 | $permissions = $groupSharesById[$subShare['parent']]['permissions']; |
| 237 | 237 | |
@@ -244,7 +244,7 @@ discard block |
||
| 244 | 244 | } else { |
| 245 | 245 | // gather share ids for bulk target update |
| 246 | 246 | if ($subShare['file_target'] !== $targetPath) { |
| 247 | - $shareIds[] = (int)$subShare['id']; |
|
| 247 | + $shareIds[] = (int) $subShare['id']; |
|
| 248 | 248 | } |
| 249 | 249 | } |
| 250 | 250 | } |
@@ -1104,7 +1104,7 @@ |
||
| 1104 | 1104 | } |
| 1105 | 1105 | |
| 1106 | 1106 | /** |
| 1107 | - * @param array $reqs |
|
| 1107 | + * @param string[] $reqs |
|
| 1108 | 1108 | * @return bool |
| 1109 | 1109 | */ |
| 1110 | 1110 | private function checkRequirements($reqs) { |
@@ -37,1324 +37,1324 @@ |
||
| 37 | 37 | use OC\ServerNotAvailableException; |
| 38 | 38 | |
| 39 | 39 | class Wizard extends LDAPUtility { |
| 40 | - /** @var \OCP\IL10N */ |
|
| 41 | - static protected $l; |
|
| 42 | - protected $access; |
|
| 43 | - protected $cr; |
|
| 44 | - protected $configuration; |
|
| 45 | - protected $result; |
|
| 46 | - protected $resultCache = array(); |
|
| 47 | - |
|
| 48 | - const LRESULT_PROCESSED_OK = 2; |
|
| 49 | - const LRESULT_PROCESSED_INVALID = 3; |
|
| 50 | - const LRESULT_PROCESSED_SKIP = 4; |
|
| 51 | - |
|
| 52 | - const LFILTER_LOGIN = 2; |
|
| 53 | - const LFILTER_USER_LIST = 3; |
|
| 54 | - const LFILTER_GROUP_LIST = 4; |
|
| 55 | - |
|
| 56 | - const LFILTER_MODE_ASSISTED = 2; |
|
| 57 | - const LFILTER_MODE_RAW = 1; |
|
| 58 | - |
|
| 59 | - const LDAP_NW_TIMEOUT = 4; |
|
| 60 | - |
|
| 61 | - /** |
|
| 62 | - * Constructor |
|
| 63 | - * @param Configuration $configuration an instance of Configuration |
|
| 64 | - * @param ILDAPWrapper $ldap an instance of ILDAPWrapper |
|
| 65 | - * @param Access $access |
|
| 66 | - */ |
|
| 67 | - public function __construct(Configuration $configuration, ILDAPWrapper $ldap, Access $access) { |
|
| 68 | - parent::__construct($ldap); |
|
| 69 | - $this->configuration = $configuration; |
|
| 70 | - if(is_null(Wizard::$l)) { |
|
| 71 | - Wizard::$l = \OC::$server->getL10N('user_ldap'); |
|
| 72 | - } |
|
| 73 | - $this->access = $access; |
|
| 74 | - $this->result = new WizardResult(); |
|
| 75 | - } |
|
| 76 | - |
|
| 77 | - public function __destruct() { |
|
| 78 | - if($this->result->hasChanges()) { |
|
| 79 | - $this->configuration->saveConfiguration(); |
|
| 80 | - } |
|
| 81 | - } |
|
| 82 | - |
|
| 83 | - /** |
|
| 84 | - * counts entries in the LDAP directory |
|
| 85 | - * |
|
| 86 | - * @param string $filter the LDAP search filter |
|
| 87 | - * @param string $type a string being either 'users' or 'groups'; |
|
| 88 | - * @return bool|int |
|
| 89 | - * @throws \Exception |
|
| 90 | - */ |
|
| 91 | - public function countEntries($filter, $type) { |
|
| 92 | - $reqs = array('ldapHost', 'ldapPort', 'ldapBase'); |
|
| 93 | - if($type === 'users') { |
|
| 94 | - $reqs[] = 'ldapUserFilter'; |
|
| 95 | - } |
|
| 96 | - if(!$this->checkRequirements($reqs)) { |
|
| 97 | - throw new \Exception('Requirements not met', 400); |
|
| 98 | - } |
|
| 99 | - |
|
| 100 | - $attr = array('dn'); // default |
|
| 101 | - $limit = 1001; |
|
| 102 | - if($type === 'groups') { |
|
| 103 | - $result = $this->access->countGroups($filter, $attr, $limit); |
|
| 104 | - } else if($type === 'users') { |
|
| 105 | - $result = $this->access->countUsers($filter, $attr, $limit); |
|
| 106 | - } else if ($type === 'objects') { |
|
| 107 | - $result = $this->access->countObjects($limit); |
|
| 108 | - } else { |
|
| 109 | - throw new \Exception('internal error: invalid object type', 500); |
|
| 110 | - } |
|
| 111 | - |
|
| 112 | - return $result; |
|
| 113 | - } |
|
| 114 | - |
|
| 115 | - /** |
|
| 116 | - * formats the return value of a count operation to the string to be |
|
| 117 | - * inserted. |
|
| 118 | - * |
|
| 119 | - * @param bool|int $count |
|
| 120 | - * @return int|string |
|
| 121 | - */ |
|
| 122 | - private function formatCountResult($count) { |
|
| 123 | - $formatted = ($count !== false) ? $count : 0; |
|
| 124 | - if($formatted > 1000) { |
|
| 125 | - $formatted = '> 1000'; |
|
| 126 | - } |
|
| 127 | - return $formatted; |
|
| 128 | - } |
|
| 129 | - |
|
| 130 | - public function countGroups() { |
|
| 131 | - $filter = $this->configuration->ldapGroupFilter; |
|
| 132 | - |
|
| 133 | - if(empty($filter)) { |
|
| 134 | - $output = self::$l->n('%s group found', '%s groups found', 0, array(0)); |
|
| 135 | - $this->result->addChange('ldap_group_count', $output); |
|
| 136 | - return $this->result; |
|
| 137 | - } |
|
| 138 | - |
|
| 139 | - try { |
|
| 140 | - $groupsTotal = $this->formatCountResult($this->countEntries($filter, 'groups')); |
|
| 141 | - } catch (\Exception $e) { |
|
| 142 | - //400 can be ignored, 500 is forwarded |
|
| 143 | - if($e->getCode() === 500) { |
|
| 144 | - throw $e; |
|
| 145 | - } |
|
| 146 | - return false; |
|
| 147 | - } |
|
| 148 | - $output = self::$l->n('%s group found', '%s groups found', $groupsTotal, array($groupsTotal)); |
|
| 149 | - $this->result->addChange('ldap_group_count', $output); |
|
| 150 | - return $this->result; |
|
| 151 | - } |
|
| 152 | - |
|
| 153 | - /** |
|
| 154 | - * @return WizardResult |
|
| 155 | - * @throws \Exception |
|
| 156 | - */ |
|
| 157 | - public function countUsers() { |
|
| 158 | - $filter = $this->access->getFilterForUserCount(); |
|
| 159 | - |
|
| 160 | - $usersTotal = $this->formatCountResult($this->countEntries($filter, 'users')); |
|
| 161 | - $output = self::$l->n('%s user found', '%s users found', $usersTotal, array($usersTotal)); |
|
| 162 | - $this->result->addChange('ldap_user_count', $output); |
|
| 163 | - return $this->result; |
|
| 164 | - } |
|
| 165 | - |
|
| 166 | - /** |
|
| 167 | - * counts any objects in the currently set base dn |
|
| 168 | - * |
|
| 169 | - * @return WizardResult |
|
| 170 | - * @throws \Exception |
|
| 171 | - */ |
|
| 172 | - public function countInBaseDN() { |
|
| 173 | - // we don't need to provide a filter in this case |
|
| 174 | - $total = $this->countEntries(null, 'objects'); |
|
| 175 | - if($total === false) { |
|
| 176 | - throw new \Exception('invalid results received'); |
|
| 177 | - } |
|
| 178 | - $this->result->addChange('ldap_test_base', $total); |
|
| 179 | - return $this->result; |
|
| 180 | - } |
|
| 181 | - |
|
| 182 | - /** |
|
| 183 | - * counts users with a specified attribute |
|
| 184 | - * @param string $attr |
|
| 185 | - * @param bool $existsCheck |
|
| 186 | - * @return int|bool |
|
| 187 | - */ |
|
| 188 | - public function countUsersWithAttribute($attr, $existsCheck = false) { |
|
| 189 | - if(!$this->checkRequirements(array('ldapHost', |
|
| 190 | - 'ldapPort', |
|
| 191 | - 'ldapBase', |
|
| 192 | - 'ldapUserFilter', |
|
| 193 | - ))) { |
|
| 194 | - return false; |
|
| 195 | - } |
|
| 196 | - |
|
| 197 | - $filter = $this->access->combineFilterWithAnd(array( |
|
| 198 | - $this->configuration->ldapUserFilter, |
|
| 199 | - $attr . '=*' |
|
| 200 | - )); |
|
| 201 | - |
|
| 202 | - $limit = ($existsCheck === false) ? null : 1; |
|
| 203 | - |
|
| 204 | - return $this->access->countUsers($filter, array('dn'), $limit); |
|
| 205 | - } |
|
| 206 | - |
|
| 207 | - /** |
|
| 208 | - * detects the display name attribute. If a setting is already present that |
|
| 209 | - * returns at least one hit, the detection will be canceled. |
|
| 210 | - * @return WizardResult|bool |
|
| 211 | - * @throws \Exception |
|
| 212 | - */ |
|
| 213 | - public function detectUserDisplayNameAttribute() { |
|
| 214 | - if(!$this->checkRequirements(array('ldapHost', |
|
| 215 | - 'ldapPort', |
|
| 216 | - 'ldapBase', |
|
| 217 | - 'ldapUserFilter', |
|
| 218 | - ))) { |
|
| 219 | - return false; |
|
| 220 | - } |
|
| 221 | - |
|
| 222 | - $attr = $this->configuration->ldapUserDisplayName; |
|
| 223 | - if ($attr !== '' && $attr !== 'displayName') { |
|
| 224 | - // most likely not the default value with upper case N, |
|
| 225 | - // verify it still produces a result |
|
| 226 | - $count = intval($this->countUsersWithAttribute($attr, true)); |
|
| 227 | - if($count > 0) { |
|
| 228 | - //no change, but we sent it back to make sure the user interface |
|
| 229 | - //is still correct, even if the ajax call was cancelled meanwhile |
|
| 230 | - $this->result->addChange('ldap_display_name', $attr); |
|
| 231 | - return $this->result; |
|
| 232 | - } |
|
| 233 | - } |
|
| 234 | - |
|
| 235 | - // first attribute that has at least one result wins |
|
| 236 | - $displayNameAttrs = array('displayname', 'cn'); |
|
| 237 | - foreach ($displayNameAttrs as $attr) { |
|
| 238 | - $count = intval($this->countUsersWithAttribute($attr, true)); |
|
| 239 | - |
|
| 240 | - if($count > 0) { |
|
| 241 | - $this->applyFind('ldap_display_name', $attr); |
|
| 242 | - return $this->result; |
|
| 243 | - } |
|
| 244 | - }; |
|
| 245 | - |
|
| 246 | - throw new \Exception(self::$l->t('Could not detect user display name attribute. Please specify it yourself in advanced ldap settings.')); |
|
| 247 | - } |
|
| 248 | - |
|
| 249 | - /** |
|
| 250 | - * detects the most often used email attribute for users applying to the |
|
| 251 | - * user list filter. If a setting is already present that returns at least |
|
| 252 | - * one hit, the detection will be canceled. |
|
| 253 | - * @return WizardResult|bool |
|
| 254 | - */ |
|
| 255 | - public function detectEmailAttribute() { |
|
| 256 | - if(!$this->checkRequirements(array('ldapHost', |
|
| 257 | - 'ldapPort', |
|
| 258 | - 'ldapBase', |
|
| 259 | - 'ldapUserFilter', |
|
| 260 | - ))) { |
|
| 261 | - return false; |
|
| 262 | - } |
|
| 263 | - |
|
| 264 | - $attr = $this->configuration->ldapEmailAttribute; |
|
| 265 | - if ($attr !== '') { |
|
| 266 | - $count = intval($this->countUsersWithAttribute($attr, true)); |
|
| 267 | - if($count > 0) { |
|
| 268 | - return false; |
|
| 269 | - } |
|
| 270 | - $writeLog = true; |
|
| 271 | - } else { |
|
| 272 | - $writeLog = false; |
|
| 273 | - } |
|
| 274 | - |
|
| 275 | - $emailAttributes = array('mail', 'mailPrimaryAddress'); |
|
| 276 | - $winner = ''; |
|
| 277 | - $maxUsers = 0; |
|
| 278 | - foreach($emailAttributes as $attr) { |
|
| 279 | - $count = $this->countUsersWithAttribute($attr); |
|
| 280 | - if($count > $maxUsers) { |
|
| 281 | - $maxUsers = $count; |
|
| 282 | - $winner = $attr; |
|
| 283 | - } |
|
| 284 | - } |
|
| 285 | - |
|
| 286 | - if($winner !== '') { |
|
| 287 | - $this->applyFind('ldap_email_attr', $winner); |
|
| 288 | - if($writeLog) { |
|
| 289 | - \OCP\Util::writeLog('user_ldap', 'The mail attribute has ' . |
|
| 290 | - 'automatically been reset, because the original value ' . |
|
| 291 | - 'did not return any results.', \OCP\Util::INFO); |
|
| 292 | - } |
|
| 293 | - } |
|
| 294 | - |
|
| 295 | - return $this->result; |
|
| 296 | - } |
|
| 297 | - |
|
| 298 | - /** |
|
| 299 | - * @return WizardResult |
|
| 300 | - * @throws \Exception |
|
| 301 | - */ |
|
| 302 | - public function determineAttributes() { |
|
| 303 | - if(!$this->checkRequirements(array('ldapHost', |
|
| 304 | - 'ldapPort', |
|
| 305 | - 'ldapBase', |
|
| 306 | - 'ldapUserFilter', |
|
| 307 | - ))) { |
|
| 308 | - return false; |
|
| 309 | - } |
|
| 310 | - |
|
| 311 | - $attributes = $this->getUserAttributes(); |
|
| 312 | - |
|
| 313 | - natcasesort($attributes); |
|
| 314 | - $attributes = array_values($attributes); |
|
| 315 | - |
|
| 316 | - $this->result->addOptions('ldap_loginfilter_attributes', $attributes); |
|
| 317 | - |
|
| 318 | - $selected = $this->configuration->ldapLoginFilterAttributes; |
|
| 319 | - if(is_array($selected) && !empty($selected)) { |
|
| 320 | - $this->result->addChange('ldap_loginfilter_attributes', $selected); |
|
| 321 | - } |
|
| 322 | - |
|
| 323 | - return $this->result; |
|
| 324 | - } |
|
| 325 | - |
|
| 326 | - /** |
|
| 327 | - * detects the available LDAP attributes |
|
| 328 | - * @return array|false The instance's WizardResult instance |
|
| 329 | - * @throws \Exception |
|
| 330 | - */ |
|
| 331 | - private function getUserAttributes() { |
|
| 332 | - if(!$this->checkRequirements(array('ldapHost', |
|
| 333 | - 'ldapPort', |
|
| 334 | - 'ldapBase', |
|
| 335 | - 'ldapUserFilter', |
|
| 336 | - ))) { |
|
| 337 | - return false; |
|
| 338 | - } |
|
| 339 | - $cr = $this->getConnection(); |
|
| 340 | - if(!$cr) { |
|
| 341 | - throw new \Exception('Could not connect to LDAP'); |
|
| 342 | - } |
|
| 343 | - |
|
| 344 | - $base = $this->configuration->ldapBase[0]; |
|
| 345 | - $filter = $this->configuration->ldapUserFilter; |
|
| 346 | - $rr = $this->ldap->search($cr, $base, $filter, array(), 1, 1); |
|
| 347 | - if(!$this->ldap->isResource($rr)) { |
|
| 348 | - return false; |
|
| 349 | - } |
|
| 350 | - $er = $this->ldap->firstEntry($cr, $rr); |
|
| 351 | - $attributes = $this->ldap->getAttributes($cr, $er); |
|
| 352 | - $pureAttributes = array(); |
|
| 353 | - for($i = 0; $i < $attributes['count']; $i++) { |
|
| 354 | - $pureAttributes[] = $attributes[$i]; |
|
| 355 | - } |
|
| 356 | - |
|
| 357 | - return $pureAttributes; |
|
| 358 | - } |
|
| 359 | - |
|
| 360 | - /** |
|
| 361 | - * detects the available LDAP groups |
|
| 362 | - * @return WizardResult|false the instance's WizardResult instance |
|
| 363 | - */ |
|
| 364 | - public function determineGroupsForGroups() { |
|
| 365 | - return $this->determineGroups('ldap_groupfilter_groups', |
|
| 366 | - 'ldapGroupFilterGroups', |
|
| 367 | - false); |
|
| 368 | - } |
|
| 369 | - |
|
| 370 | - /** |
|
| 371 | - * detects the available LDAP groups |
|
| 372 | - * @return WizardResult|false the instance's WizardResult instance |
|
| 373 | - */ |
|
| 374 | - public function determineGroupsForUsers() { |
|
| 375 | - return $this->determineGroups('ldap_userfilter_groups', |
|
| 376 | - 'ldapUserFilterGroups'); |
|
| 377 | - } |
|
| 378 | - |
|
| 379 | - /** |
|
| 380 | - * detects the available LDAP groups |
|
| 381 | - * @param string $dbKey |
|
| 382 | - * @param string $confKey |
|
| 383 | - * @param bool $testMemberOf |
|
| 384 | - * @return WizardResult|false the instance's WizardResult instance |
|
| 385 | - * @throws \Exception |
|
| 386 | - */ |
|
| 387 | - private function determineGroups($dbKey, $confKey, $testMemberOf = true) { |
|
| 388 | - if(!$this->checkRequirements(array('ldapHost', |
|
| 389 | - 'ldapPort', |
|
| 390 | - 'ldapBase', |
|
| 391 | - ))) { |
|
| 392 | - return false; |
|
| 393 | - } |
|
| 394 | - $cr = $this->getConnection(); |
|
| 395 | - if(!$cr) { |
|
| 396 | - throw new \Exception('Could not connect to LDAP'); |
|
| 397 | - } |
|
| 398 | - |
|
| 399 | - $this->fetchGroups($dbKey, $confKey); |
|
| 400 | - |
|
| 401 | - if($testMemberOf) { |
|
| 402 | - $this->configuration->hasMemberOfFilterSupport = $this->testMemberOf(); |
|
| 403 | - $this->result->markChange(); |
|
| 404 | - if(!$this->configuration->hasMemberOfFilterSupport) { |
|
| 405 | - throw new \Exception('memberOf is not supported by the server'); |
|
| 406 | - } |
|
| 407 | - } |
|
| 408 | - |
|
| 409 | - return $this->result; |
|
| 410 | - } |
|
| 411 | - |
|
| 412 | - /** |
|
| 413 | - * fetches all groups from LDAP and adds them to the result object |
|
| 414 | - * |
|
| 415 | - * @param string $dbKey |
|
| 416 | - * @param string $confKey |
|
| 417 | - * @return array $groupEntries |
|
| 418 | - * @throws \Exception |
|
| 419 | - */ |
|
| 420 | - public function fetchGroups($dbKey, $confKey) { |
|
| 421 | - $obclasses = array('posixGroup', 'group', 'zimbraDistributionList', 'groupOfNames'); |
|
| 422 | - |
|
| 423 | - $filterParts = array(); |
|
| 424 | - foreach($obclasses as $obclass) { |
|
| 425 | - $filterParts[] = 'objectclass='.$obclass; |
|
| 426 | - } |
|
| 427 | - //we filter for everything |
|
| 428 | - //- that looks like a group and |
|
| 429 | - //- has the group display name set |
|
| 430 | - $filter = $this->access->combineFilterWithOr($filterParts); |
|
| 431 | - $filter = $this->access->combineFilterWithAnd(array($filter, 'cn=*')); |
|
| 432 | - |
|
| 433 | - $groupNames = array(); |
|
| 434 | - $groupEntries = array(); |
|
| 435 | - $limit = 400; |
|
| 436 | - $offset = 0; |
|
| 437 | - do { |
|
| 438 | - // we need to request dn additionally here, otherwise memberOf |
|
| 439 | - // detection will fail later |
|
| 440 | - $result = $this->access->searchGroups($filter, array('cn', 'dn'), $limit, $offset); |
|
| 441 | - foreach($result as $item) { |
|
| 442 | - if(!isset($item['cn']) && !is_array($item['cn']) && !isset($item['cn'][0])) { |
|
| 443 | - // just in case - no issue known |
|
| 444 | - continue; |
|
| 445 | - } |
|
| 446 | - $groupNames[] = $item['cn'][0]; |
|
| 447 | - $groupEntries[] = $item; |
|
| 448 | - } |
|
| 449 | - $offset += $limit; |
|
| 450 | - } while ($this->access->hasMoreResults()); |
|
| 451 | - |
|
| 452 | - if(count($groupNames) > 0) { |
|
| 453 | - natsort($groupNames); |
|
| 454 | - $this->result->addOptions($dbKey, array_values($groupNames)); |
|
| 455 | - } else { |
|
| 456 | - throw new \Exception(self::$l->t('Could not find the desired feature')); |
|
| 457 | - } |
|
| 458 | - |
|
| 459 | - $setFeatures = $this->configuration->$confKey; |
|
| 460 | - if(is_array($setFeatures) && !empty($setFeatures)) { |
|
| 461 | - //something is already configured? pre-select it. |
|
| 462 | - $this->result->addChange($dbKey, $setFeatures); |
|
| 463 | - } |
|
| 464 | - return $groupEntries; |
|
| 465 | - } |
|
| 466 | - |
|
| 467 | - public function determineGroupMemberAssoc() { |
|
| 468 | - if(!$this->checkRequirements(array('ldapHost', |
|
| 469 | - 'ldapPort', |
|
| 470 | - 'ldapGroupFilter', |
|
| 471 | - ))) { |
|
| 472 | - return false; |
|
| 473 | - } |
|
| 474 | - $attribute = $this->detectGroupMemberAssoc(); |
|
| 475 | - if($attribute === false) { |
|
| 476 | - return false; |
|
| 477 | - } |
|
| 478 | - $this->configuration->setConfiguration(array('ldapGroupMemberAssocAttr' => $attribute)); |
|
| 479 | - $this->result->addChange('ldap_group_member_assoc_attribute', $attribute); |
|
| 480 | - |
|
| 481 | - return $this->result; |
|
| 482 | - } |
|
| 483 | - |
|
| 484 | - /** |
|
| 485 | - * Detects the available object classes |
|
| 486 | - * @return WizardResult|false the instance's WizardResult instance |
|
| 487 | - * @throws \Exception |
|
| 488 | - */ |
|
| 489 | - public function determineGroupObjectClasses() { |
|
| 490 | - if(!$this->checkRequirements(array('ldapHost', |
|
| 491 | - 'ldapPort', |
|
| 492 | - 'ldapBase', |
|
| 493 | - ))) { |
|
| 494 | - return false; |
|
| 495 | - } |
|
| 496 | - $cr = $this->getConnection(); |
|
| 497 | - if(!$cr) { |
|
| 498 | - throw new \Exception('Could not connect to LDAP'); |
|
| 499 | - } |
|
| 500 | - |
|
| 501 | - $obclasses = array('groupOfNames', 'groupOfUniqueNames', 'group', 'posixGroup', '*'); |
|
| 502 | - $this->determineFeature($obclasses, |
|
| 503 | - 'objectclass', |
|
| 504 | - 'ldap_groupfilter_objectclass', |
|
| 505 | - 'ldapGroupFilterObjectclass', |
|
| 506 | - false); |
|
| 507 | - |
|
| 508 | - return $this->result; |
|
| 509 | - } |
|
| 510 | - |
|
| 511 | - /** |
|
| 512 | - * detects the available object classes |
|
| 513 | - * @return WizardResult |
|
| 514 | - * @throws \Exception |
|
| 515 | - */ |
|
| 516 | - public function determineUserObjectClasses() { |
|
| 517 | - if(!$this->checkRequirements(array('ldapHost', |
|
| 518 | - 'ldapPort', |
|
| 519 | - 'ldapBase', |
|
| 520 | - ))) { |
|
| 521 | - return false; |
|
| 522 | - } |
|
| 523 | - $cr = $this->getConnection(); |
|
| 524 | - if(!$cr) { |
|
| 525 | - throw new \Exception('Could not connect to LDAP'); |
|
| 526 | - } |
|
| 527 | - |
|
| 528 | - $obclasses = array('inetOrgPerson', 'person', 'organizationalPerson', |
|
| 529 | - 'user', 'posixAccount', '*'); |
|
| 530 | - $filter = $this->configuration->ldapUserFilter; |
|
| 531 | - //if filter is empty, it is probably the first time the wizard is called |
|
| 532 | - //then, apply suggestions. |
|
| 533 | - $this->determineFeature($obclasses, |
|
| 534 | - 'objectclass', |
|
| 535 | - 'ldap_userfilter_objectclass', |
|
| 536 | - 'ldapUserFilterObjectclass', |
|
| 537 | - empty($filter)); |
|
| 538 | - |
|
| 539 | - return $this->result; |
|
| 540 | - } |
|
| 541 | - |
|
| 542 | - /** |
|
| 543 | - * @return WizardResult|false |
|
| 544 | - * @throws \Exception |
|
| 545 | - */ |
|
| 546 | - public function getGroupFilter() { |
|
| 547 | - if(!$this->checkRequirements(array('ldapHost', |
|
| 548 | - 'ldapPort', |
|
| 549 | - 'ldapBase', |
|
| 550 | - ))) { |
|
| 551 | - return false; |
|
| 552 | - } |
|
| 553 | - //make sure the use display name is set |
|
| 554 | - $displayName = $this->configuration->ldapGroupDisplayName; |
|
| 555 | - if ($displayName === '') { |
|
| 556 | - $d = $this->configuration->getDefaults(); |
|
| 557 | - $this->applyFind('ldap_group_display_name', |
|
| 558 | - $d['ldap_group_display_name']); |
|
| 559 | - } |
|
| 560 | - $filter = $this->composeLdapFilter(self::LFILTER_GROUP_LIST); |
|
| 561 | - |
|
| 562 | - $this->applyFind('ldap_group_filter', $filter); |
|
| 563 | - return $this->result; |
|
| 564 | - } |
|
| 565 | - |
|
| 566 | - /** |
|
| 567 | - * @return WizardResult|false |
|
| 568 | - * @throws \Exception |
|
| 569 | - */ |
|
| 570 | - public function getUserListFilter() { |
|
| 571 | - if(!$this->checkRequirements(array('ldapHost', |
|
| 572 | - 'ldapPort', |
|
| 573 | - 'ldapBase', |
|
| 574 | - ))) { |
|
| 575 | - return false; |
|
| 576 | - } |
|
| 577 | - //make sure the use display name is set |
|
| 578 | - $displayName = $this->configuration->ldapUserDisplayName; |
|
| 579 | - if ($displayName === '') { |
|
| 580 | - $d = $this->configuration->getDefaults(); |
|
| 581 | - $this->applyFind('ldap_display_name', $d['ldap_display_name']); |
|
| 582 | - } |
|
| 583 | - $filter = $this->composeLdapFilter(self::LFILTER_USER_LIST); |
|
| 584 | - if(!$filter) { |
|
| 585 | - throw new \Exception('Cannot create filter'); |
|
| 586 | - } |
|
| 587 | - |
|
| 588 | - $this->applyFind('ldap_userlist_filter', $filter); |
|
| 589 | - return $this->result; |
|
| 590 | - } |
|
| 591 | - |
|
| 592 | - /** |
|
| 593 | - * @return bool|WizardResult |
|
| 594 | - * @throws \Exception |
|
| 595 | - */ |
|
| 596 | - public function getUserLoginFilter() { |
|
| 597 | - if(!$this->checkRequirements(array('ldapHost', |
|
| 598 | - 'ldapPort', |
|
| 599 | - 'ldapBase', |
|
| 600 | - 'ldapUserFilter', |
|
| 601 | - ))) { |
|
| 602 | - return false; |
|
| 603 | - } |
|
| 604 | - |
|
| 605 | - $filter = $this->composeLdapFilter(self::LFILTER_LOGIN); |
|
| 606 | - if(!$filter) { |
|
| 607 | - throw new \Exception('Cannot create filter'); |
|
| 608 | - } |
|
| 609 | - |
|
| 610 | - $this->applyFind('ldap_login_filter', $filter); |
|
| 611 | - return $this->result; |
|
| 612 | - } |
|
| 613 | - |
|
| 614 | - /** |
|
| 615 | - * @return bool|WizardResult |
|
| 616 | - * @param string $loginName |
|
| 617 | - * @throws \Exception |
|
| 618 | - */ |
|
| 619 | - public function testLoginName($loginName) { |
|
| 620 | - if(!$this->checkRequirements(array('ldapHost', |
|
| 621 | - 'ldapPort', |
|
| 622 | - 'ldapBase', |
|
| 623 | - 'ldapLoginFilter', |
|
| 624 | - ))) { |
|
| 625 | - return false; |
|
| 626 | - } |
|
| 627 | - |
|
| 628 | - $cr = $this->access->connection->getConnectionResource(); |
|
| 629 | - if(!$this->ldap->isResource($cr)) { |
|
| 630 | - throw new \Exception('connection error'); |
|
| 631 | - } |
|
| 632 | - |
|
| 633 | - if(mb_strpos($this->access->connection->ldapLoginFilter, '%uid', 0, 'UTF-8') |
|
| 634 | - === false) { |
|
| 635 | - throw new \Exception('missing placeholder'); |
|
| 636 | - } |
|
| 637 | - |
|
| 638 | - $users = $this->access->countUsersByLoginName($loginName); |
|
| 639 | - if($this->ldap->errno($cr) !== 0) { |
|
| 640 | - throw new \Exception($this->ldap->error($cr)); |
|
| 641 | - } |
|
| 642 | - $filter = str_replace('%uid', $loginName, $this->access->connection->ldapLoginFilter); |
|
| 643 | - $this->result->addChange('ldap_test_loginname', $users); |
|
| 644 | - $this->result->addChange('ldap_test_effective_filter', $filter); |
|
| 645 | - return $this->result; |
|
| 646 | - } |
|
| 647 | - |
|
| 648 | - /** |
|
| 649 | - * Tries to determine the port, requires given Host, User DN and Password |
|
| 650 | - * @return WizardResult|false WizardResult on success, false otherwise |
|
| 651 | - * @throws \Exception |
|
| 652 | - */ |
|
| 653 | - public function guessPortAndTLS() { |
|
| 654 | - if(!$this->checkRequirements(array('ldapHost', |
|
| 655 | - ))) { |
|
| 656 | - return false; |
|
| 657 | - } |
|
| 658 | - $this->checkHost(); |
|
| 659 | - $portSettings = $this->getPortSettingsToTry(); |
|
| 660 | - |
|
| 661 | - if(!is_array($portSettings)) { |
|
| 662 | - throw new \Exception(print_r($portSettings, true)); |
|
| 663 | - } |
|
| 664 | - |
|
| 665 | - //proceed from the best configuration and return on first success |
|
| 666 | - foreach($portSettings as $setting) { |
|
| 667 | - $p = $setting['port']; |
|
| 668 | - $t = $setting['tls']; |
|
| 669 | - \OCP\Util::writeLog('user_ldap', 'Wiz: trying port '. $p . ', TLS '. $t, \OCP\Util::DEBUG); |
|
| 670 | - //connectAndBind may throw Exception, it needs to be catched by the |
|
| 671 | - //callee of this method |
|
| 672 | - |
|
| 673 | - try { |
|
| 674 | - $settingsFound = $this->connectAndBind($p, $t); |
|
| 675 | - } catch (\Exception $e) { |
|
| 676 | - // any reply other than -1 (= cannot connect) is already okay, |
|
| 677 | - // because then we found the server |
|
| 678 | - // unavailable startTLS returns -11 |
|
| 679 | - if($e->getCode() > 0) { |
|
| 680 | - $settingsFound = true; |
|
| 681 | - } else { |
|
| 682 | - throw $e; |
|
| 683 | - } |
|
| 684 | - } |
|
| 685 | - |
|
| 686 | - if ($settingsFound === true) { |
|
| 687 | - $config = array( |
|
| 688 | - 'ldapPort' => $p, |
|
| 689 | - 'ldapTLS' => intval($t) |
|
| 690 | - ); |
|
| 691 | - $this->configuration->setConfiguration($config); |
|
| 692 | - \OCP\Util::writeLog('user_ldap', 'Wiz: detected Port ' . $p, \OCP\Util::DEBUG); |
|
| 693 | - $this->result->addChange('ldap_port', $p); |
|
| 694 | - return $this->result; |
|
| 695 | - } |
|
| 696 | - } |
|
| 697 | - |
|
| 698 | - //custom port, undetected (we do not brute force) |
|
| 699 | - return false; |
|
| 700 | - } |
|
| 701 | - |
|
| 702 | - /** |
|
| 703 | - * tries to determine a base dn from User DN or LDAP Host |
|
| 704 | - * @return WizardResult|false WizardResult on success, false otherwise |
|
| 705 | - */ |
|
| 706 | - public function guessBaseDN() { |
|
| 707 | - if(!$this->checkRequirements(array('ldapHost', |
|
| 708 | - 'ldapPort', |
|
| 709 | - ))) { |
|
| 710 | - return false; |
|
| 711 | - } |
|
| 712 | - |
|
| 713 | - //check whether a DN is given in the agent name (99.9% of all cases) |
|
| 714 | - $base = null; |
|
| 715 | - $i = stripos($this->configuration->ldapAgentName, 'dc='); |
|
| 716 | - if($i !== false) { |
|
| 717 | - $base = substr($this->configuration->ldapAgentName, $i); |
|
| 718 | - if($this->testBaseDN($base)) { |
|
| 719 | - $this->applyFind('ldap_base', $base); |
|
| 720 | - return $this->result; |
|
| 721 | - } |
|
| 722 | - } |
|
| 723 | - |
|
| 724 | - //this did not help :( |
|
| 725 | - //Let's see whether we can parse the Host URL and convert the domain to |
|
| 726 | - //a base DN |
|
| 727 | - $helper = new Helper(\OC::$server->getConfig()); |
|
| 728 | - $domain = $helper->getDomainFromURL($this->configuration->ldapHost); |
|
| 729 | - if(!$domain) { |
|
| 730 | - return false; |
|
| 731 | - } |
|
| 732 | - |
|
| 733 | - $dparts = explode('.', $domain); |
|
| 734 | - while(count($dparts) > 0) { |
|
| 735 | - $base2 = 'dc=' . implode(',dc=', $dparts); |
|
| 736 | - if ($base !== $base2 && $this->testBaseDN($base2)) { |
|
| 737 | - $this->applyFind('ldap_base', $base2); |
|
| 738 | - return $this->result; |
|
| 739 | - } |
|
| 740 | - array_shift($dparts); |
|
| 741 | - } |
|
| 742 | - |
|
| 743 | - return false; |
|
| 744 | - } |
|
| 745 | - |
|
| 746 | - /** |
|
| 747 | - * sets the found value for the configuration key in the WizardResult |
|
| 748 | - * as well as in the Configuration instance |
|
| 749 | - * @param string $key the configuration key |
|
| 750 | - * @param string $value the (detected) value |
|
| 751 | - * |
|
| 752 | - */ |
|
| 753 | - private function applyFind($key, $value) { |
|
| 754 | - $this->result->addChange($key, $value); |
|
| 755 | - $this->configuration->setConfiguration(array($key => $value)); |
|
| 756 | - } |
|
| 757 | - |
|
| 758 | - /** |
|
| 759 | - * Checks, whether a port was entered in the Host configuration |
|
| 760 | - * field. In this case the port will be stripped off, but also stored as |
|
| 761 | - * setting. |
|
| 762 | - */ |
|
| 763 | - private function checkHost() { |
|
| 764 | - $host = $this->configuration->ldapHost; |
|
| 765 | - $hostInfo = parse_url($host); |
|
| 766 | - |
|
| 767 | - //removes Port from Host |
|
| 768 | - if(is_array($hostInfo) && isset($hostInfo['port'])) { |
|
| 769 | - $port = $hostInfo['port']; |
|
| 770 | - $host = str_replace(':'.$port, '', $host); |
|
| 771 | - $this->applyFind('ldap_host', $host); |
|
| 772 | - $this->applyFind('ldap_port', $port); |
|
| 773 | - } |
|
| 774 | - } |
|
| 775 | - |
|
| 776 | - /** |
|
| 777 | - * tries to detect the group member association attribute which is |
|
| 778 | - * one of 'uniqueMember', 'memberUid', 'member' |
|
| 779 | - * @return string|false, string with the attribute name, false on error |
|
| 780 | - * @throws \Exception |
|
| 781 | - */ |
|
| 782 | - private function detectGroupMemberAssoc() { |
|
| 783 | - $possibleAttrs = array('uniqueMember', 'memberUid', 'member'); |
|
| 784 | - $filter = $this->configuration->ldapGroupFilter; |
|
| 785 | - if(empty($filter)) { |
|
| 786 | - return false; |
|
| 787 | - } |
|
| 788 | - $cr = $this->getConnection(); |
|
| 789 | - if(!$cr) { |
|
| 790 | - throw new \Exception('Could not connect to LDAP'); |
|
| 791 | - } |
|
| 792 | - $base = $this->configuration->ldapBase[0]; |
|
| 793 | - $rr = $this->ldap->search($cr, $base, $filter, $possibleAttrs, 0, 1000); |
|
| 794 | - if(!$this->ldap->isResource($rr)) { |
|
| 795 | - return false; |
|
| 796 | - } |
|
| 797 | - $er = $this->ldap->firstEntry($cr, $rr); |
|
| 798 | - while(is_resource($er)) { |
|
| 799 | - $this->ldap->getDN($cr, $er); |
|
| 800 | - $attrs = $this->ldap->getAttributes($cr, $er); |
|
| 801 | - $result = array(); |
|
| 802 | - $possibleAttrsCount = count($possibleAttrs); |
|
| 803 | - for($i = 0; $i < $possibleAttrsCount; $i++) { |
|
| 804 | - if(isset($attrs[$possibleAttrs[$i]])) { |
|
| 805 | - $result[$possibleAttrs[$i]] = $attrs[$possibleAttrs[$i]]['count']; |
|
| 806 | - } |
|
| 807 | - } |
|
| 808 | - if(!empty($result)) { |
|
| 809 | - natsort($result); |
|
| 810 | - return key($result); |
|
| 811 | - } |
|
| 812 | - |
|
| 813 | - $er = $this->ldap->nextEntry($cr, $er); |
|
| 814 | - } |
|
| 815 | - |
|
| 816 | - return false; |
|
| 817 | - } |
|
| 818 | - |
|
| 819 | - /** |
|
| 820 | - * Checks whether for a given BaseDN results will be returned |
|
| 821 | - * @param string $base the BaseDN to test |
|
| 822 | - * @return bool true on success, false otherwise |
|
| 823 | - * @throws \Exception |
|
| 824 | - */ |
|
| 825 | - private function testBaseDN($base) { |
|
| 826 | - $cr = $this->getConnection(); |
|
| 827 | - if(!$cr) { |
|
| 828 | - throw new \Exception('Could not connect to LDAP'); |
|
| 829 | - } |
|
| 830 | - |
|
| 831 | - //base is there, let's validate it. If we search for anything, we should |
|
| 832 | - //get a result set > 0 on a proper base |
|
| 833 | - $rr = $this->ldap->search($cr, $base, 'objectClass=*', array('dn'), 0, 1); |
|
| 834 | - if(!$this->ldap->isResource($rr)) { |
|
| 835 | - $errorNo = $this->ldap->errno($cr); |
|
| 836 | - $errorMsg = $this->ldap->error($cr); |
|
| 837 | - \OCP\Util::writeLog('user_ldap', 'Wiz: Could not search base '.$base. |
|
| 838 | - ' Error '.$errorNo.': '.$errorMsg, \OCP\Util::INFO); |
|
| 839 | - return false; |
|
| 840 | - } |
|
| 841 | - $entries = $this->ldap->countEntries($cr, $rr); |
|
| 842 | - return ($entries !== false) && ($entries > 0); |
|
| 843 | - } |
|
| 844 | - |
|
| 845 | - /** |
|
| 846 | - * Checks whether the server supports memberOf in LDAP Filter. |
|
| 847 | - * Note: at least in OpenLDAP, availability of memberOf is dependent on |
|
| 848 | - * a configured objectClass. I.e. not necessarily for all available groups |
|
| 849 | - * memberOf does work. |
|
| 850 | - * |
|
| 851 | - * @return bool true if it does, false otherwise |
|
| 852 | - * @throws \Exception |
|
| 853 | - */ |
|
| 854 | - private function testMemberOf() { |
|
| 855 | - $cr = $this->getConnection(); |
|
| 856 | - if(!$cr) { |
|
| 857 | - throw new \Exception('Could not connect to LDAP'); |
|
| 858 | - } |
|
| 859 | - $result = $this->access->countUsers('memberOf=*', array('memberOf'), 1); |
|
| 860 | - if(is_int($result) && $result > 0) { |
|
| 861 | - return true; |
|
| 862 | - } |
|
| 863 | - return false; |
|
| 864 | - } |
|
| 865 | - |
|
| 866 | - /** |
|
| 867 | - * creates an LDAP Filter from given configuration |
|
| 868 | - * @param integer $filterType int, for which use case the filter shall be created |
|
| 869 | - * can be any of self::LFILTER_USER_LIST, self::LFILTER_LOGIN or |
|
| 870 | - * self::LFILTER_GROUP_LIST |
|
| 871 | - * @return string|false string with the filter on success, false otherwise |
|
| 872 | - * @throws \Exception |
|
| 873 | - */ |
|
| 874 | - private function composeLdapFilter($filterType) { |
|
| 875 | - $filter = ''; |
|
| 876 | - $parts = 0; |
|
| 877 | - switch ($filterType) { |
|
| 878 | - case self::LFILTER_USER_LIST: |
|
| 879 | - $objcs = $this->configuration->ldapUserFilterObjectclass; |
|
| 880 | - //glue objectclasses |
|
| 881 | - if(is_array($objcs) && count($objcs) > 0) { |
|
| 882 | - $filter .= '(|'; |
|
| 883 | - foreach($objcs as $objc) { |
|
| 884 | - $filter .= '(objectclass=' . $objc . ')'; |
|
| 885 | - } |
|
| 886 | - $filter .= ')'; |
|
| 887 | - $parts++; |
|
| 888 | - } |
|
| 889 | - //glue group memberships |
|
| 890 | - if($this->configuration->hasMemberOfFilterSupport) { |
|
| 891 | - $cns = $this->configuration->ldapUserFilterGroups; |
|
| 892 | - if(is_array($cns) && count($cns) > 0) { |
|
| 893 | - $filter .= '(|'; |
|
| 894 | - $cr = $this->getConnection(); |
|
| 895 | - if(!$cr) { |
|
| 896 | - throw new \Exception('Could not connect to LDAP'); |
|
| 897 | - } |
|
| 898 | - $base = $this->configuration->ldapBase[0]; |
|
| 899 | - foreach($cns as $cn) { |
|
| 900 | - $rr = $this->ldap->search($cr, $base, 'cn=' . $cn, array('dn', 'primaryGroupToken')); |
|
| 901 | - if(!$this->ldap->isResource($rr)) { |
|
| 902 | - continue; |
|
| 903 | - } |
|
| 904 | - $er = $this->ldap->firstEntry($cr, $rr); |
|
| 905 | - $attrs = $this->ldap->getAttributes($cr, $er); |
|
| 906 | - $dn = $this->ldap->getDN($cr, $er); |
|
| 907 | - if ($dn == false || $dn === '') { |
|
| 908 | - continue; |
|
| 909 | - } |
|
| 910 | - $filterPart = '(memberof=' . $dn . ')'; |
|
| 911 | - if(isset($attrs['primaryGroupToken'])) { |
|
| 912 | - $pgt = $attrs['primaryGroupToken'][0]; |
|
| 913 | - $primaryFilterPart = '(primaryGroupID=' . $pgt .')'; |
|
| 914 | - $filterPart = '(|' . $filterPart . $primaryFilterPart . ')'; |
|
| 915 | - } |
|
| 916 | - $filter .= $filterPart; |
|
| 917 | - } |
|
| 918 | - $filter .= ')'; |
|
| 919 | - } |
|
| 920 | - $parts++; |
|
| 921 | - } |
|
| 922 | - //wrap parts in AND condition |
|
| 923 | - if($parts > 1) { |
|
| 924 | - $filter = '(&' . $filter . ')'; |
|
| 925 | - } |
|
| 926 | - if ($filter === '') { |
|
| 927 | - $filter = '(objectclass=*)'; |
|
| 928 | - } |
|
| 929 | - break; |
|
| 930 | - |
|
| 931 | - case self::LFILTER_GROUP_LIST: |
|
| 932 | - $objcs = $this->configuration->ldapGroupFilterObjectclass; |
|
| 933 | - //glue objectclasses |
|
| 934 | - if(is_array($objcs) && count($objcs) > 0) { |
|
| 935 | - $filter .= '(|'; |
|
| 936 | - foreach($objcs as $objc) { |
|
| 937 | - $filter .= '(objectclass=' . $objc . ')'; |
|
| 938 | - } |
|
| 939 | - $filter .= ')'; |
|
| 940 | - $parts++; |
|
| 941 | - } |
|
| 942 | - //glue group memberships |
|
| 943 | - $cns = $this->configuration->ldapGroupFilterGroups; |
|
| 944 | - if(is_array($cns) && count($cns) > 0) { |
|
| 945 | - $filter .= '(|'; |
|
| 946 | - foreach($cns as $cn) { |
|
| 947 | - $filter .= '(cn=' . $cn . ')'; |
|
| 948 | - } |
|
| 949 | - $filter .= ')'; |
|
| 950 | - } |
|
| 951 | - $parts++; |
|
| 952 | - //wrap parts in AND condition |
|
| 953 | - if($parts > 1) { |
|
| 954 | - $filter = '(&' . $filter . ')'; |
|
| 955 | - } |
|
| 956 | - break; |
|
| 957 | - |
|
| 958 | - case self::LFILTER_LOGIN: |
|
| 959 | - $ulf = $this->configuration->ldapUserFilter; |
|
| 960 | - $loginpart = '=%uid'; |
|
| 961 | - $filterUsername = ''; |
|
| 962 | - $userAttributes = $this->getUserAttributes(); |
|
| 963 | - $userAttributes = array_change_key_case(array_flip($userAttributes)); |
|
| 964 | - $parts = 0; |
|
| 965 | - |
|
| 966 | - if($this->configuration->ldapLoginFilterUsername === '1') { |
|
| 967 | - $attr = ''; |
|
| 968 | - if(isset($userAttributes['uid'])) { |
|
| 969 | - $attr = 'uid'; |
|
| 970 | - } else if(isset($userAttributes['samaccountname'])) { |
|
| 971 | - $attr = 'samaccountname'; |
|
| 972 | - } else if(isset($userAttributes['cn'])) { |
|
| 973 | - //fallback |
|
| 974 | - $attr = 'cn'; |
|
| 975 | - } |
|
| 976 | - if ($attr !== '') { |
|
| 977 | - $filterUsername = '(' . $attr . $loginpart . ')'; |
|
| 978 | - $parts++; |
|
| 979 | - } |
|
| 980 | - } |
|
| 981 | - |
|
| 982 | - $filterEmail = ''; |
|
| 983 | - if($this->configuration->ldapLoginFilterEmail === '1') { |
|
| 984 | - $filterEmail = '(|(mailPrimaryAddress=%uid)(mail=%uid))'; |
|
| 985 | - $parts++; |
|
| 986 | - } |
|
| 987 | - |
|
| 988 | - $filterAttributes = ''; |
|
| 989 | - $attrsToFilter = $this->configuration->ldapLoginFilterAttributes; |
|
| 990 | - if(is_array($attrsToFilter) && count($attrsToFilter) > 0) { |
|
| 991 | - $filterAttributes = '(|'; |
|
| 992 | - foreach($attrsToFilter as $attribute) { |
|
| 993 | - $filterAttributes .= '(' . $attribute . $loginpart . ')'; |
|
| 994 | - } |
|
| 995 | - $filterAttributes .= ')'; |
|
| 996 | - $parts++; |
|
| 997 | - } |
|
| 998 | - |
|
| 999 | - $filterLogin = ''; |
|
| 1000 | - if($parts > 1) { |
|
| 1001 | - $filterLogin = '(|'; |
|
| 1002 | - } |
|
| 1003 | - $filterLogin .= $filterUsername; |
|
| 1004 | - $filterLogin .= $filterEmail; |
|
| 1005 | - $filterLogin .= $filterAttributes; |
|
| 1006 | - if($parts > 1) { |
|
| 1007 | - $filterLogin .= ')'; |
|
| 1008 | - } |
|
| 1009 | - |
|
| 1010 | - $filter = '(&'.$ulf.$filterLogin.')'; |
|
| 1011 | - break; |
|
| 1012 | - } |
|
| 1013 | - |
|
| 1014 | - \OCP\Util::writeLog('user_ldap', 'Wiz: Final filter '.$filter, \OCP\Util::DEBUG); |
|
| 1015 | - |
|
| 1016 | - return $filter; |
|
| 1017 | - } |
|
| 1018 | - |
|
| 1019 | - /** |
|
| 1020 | - * Connects and Binds to an LDAP Server |
|
| 1021 | - * @param int $port the port to connect with |
|
| 1022 | - * @param bool $tls whether startTLS is to be used |
|
| 1023 | - * @param bool $ncc |
|
| 1024 | - * @return bool |
|
| 1025 | - * @throws \Exception |
|
| 1026 | - */ |
|
| 1027 | - private function connectAndBind($port = 389, $tls = false, $ncc = false) { |
|
| 1028 | - if($ncc) { |
|
| 1029 | - //No certificate check |
|
| 1030 | - //FIXME: undo afterwards |
|
| 1031 | - putenv('LDAPTLS_REQCERT=never'); |
|
| 1032 | - } |
|
| 1033 | - |
|
| 1034 | - //connect, does not really trigger any server communication |
|
| 1035 | - \OCP\Util::writeLog('user_ldap', 'Wiz: Checking Host Info ', \OCP\Util::DEBUG); |
|
| 1036 | - $host = $this->configuration->ldapHost; |
|
| 1037 | - $hostInfo = parse_url($host); |
|
| 1038 | - if(!$hostInfo) { |
|
| 1039 | - throw new \Exception(self::$l->t('Invalid Host')); |
|
| 1040 | - } |
|
| 1041 | - \OCP\Util::writeLog('user_ldap', 'Wiz: Attempting to connect ', \OCP\Util::DEBUG); |
|
| 1042 | - $cr = $this->ldap->connect($host, $port); |
|
| 1043 | - if(!is_resource($cr)) { |
|
| 1044 | - throw new \Exception(self::$l->t('Invalid Host')); |
|
| 1045 | - } |
|
| 1046 | - |
|
| 1047 | - \OCP\Util::writeLog('user_ldap', 'Wiz: Setting LDAP Options ', \OCP\Util::DEBUG); |
|
| 1048 | - //set LDAP options |
|
| 1049 | - $this->ldap->setOption($cr, LDAP_OPT_PROTOCOL_VERSION, 3); |
|
| 1050 | - $this->ldap->setOption($cr, LDAP_OPT_REFERRALS, 0); |
|
| 1051 | - $this->ldap->setOption($cr, LDAP_OPT_NETWORK_TIMEOUT, self::LDAP_NW_TIMEOUT); |
|
| 1052 | - |
|
| 1053 | - try { |
|
| 1054 | - if($tls) { |
|
| 1055 | - $isTlsWorking = @$this->ldap->startTls($cr); |
|
| 1056 | - if(!$isTlsWorking) { |
|
| 1057 | - return false; |
|
| 1058 | - } |
|
| 1059 | - } |
|
| 1060 | - |
|
| 1061 | - \OCP\Util::writeLog('user_ldap', 'Wiz: Attemping to Bind ', \OCP\Util::DEBUG); |
|
| 1062 | - //interesting part: do the bind! |
|
| 1063 | - $login = $this->ldap->bind($cr, |
|
| 1064 | - $this->configuration->ldapAgentName, |
|
| 1065 | - $this->configuration->ldapAgentPassword |
|
| 1066 | - ); |
|
| 1067 | - $errNo = $this->ldap->errno($cr); |
|
| 1068 | - $error = ldap_error($cr); |
|
| 1069 | - $this->ldap->unbind($cr); |
|
| 1070 | - } catch(ServerNotAvailableException $e) { |
|
| 1071 | - return false; |
|
| 1072 | - } |
|
| 1073 | - |
|
| 1074 | - if($login === true) { |
|
| 1075 | - $this->ldap->unbind($cr); |
|
| 1076 | - if($ncc) { |
|
| 1077 | - throw new \Exception('Certificate cannot be validated.'); |
|
| 1078 | - } |
|
| 1079 | - \OCP\Util::writeLog('user_ldap', 'Wiz: Bind successful to Port '. $port . ' TLS ' . intval($tls), \OCP\Util::DEBUG); |
|
| 1080 | - return true; |
|
| 1081 | - } |
|
| 1082 | - |
|
| 1083 | - if($errNo === -1 || ($errNo === 2 && $ncc)) { |
|
| 1084 | - //host, port or TLS wrong |
|
| 1085 | - return false; |
|
| 1086 | - } else if ($errNo === 2) { |
|
| 1087 | - return $this->connectAndBind($port, $tls, true); |
|
| 1088 | - } |
|
| 1089 | - throw new \Exception($error, $errNo); |
|
| 1090 | - } |
|
| 1091 | - |
|
| 1092 | - /** |
|
| 1093 | - * checks whether a valid combination of agent and password has been |
|
| 1094 | - * provided (either two values or nothing for anonymous connect) |
|
| 1095 | - * @return bool, true if everything is fine, false otherwise |
|
| 1096 | - */ |
|
| 1097 | - private function checkAgentRequirements() { |
|
| 1098 | - $agent = $this->configuration->ldapAgentName; |
|
| 1099 | - $pwd = $this->configuration->ldapAgentPassword; |
|
| 1100 | - |
|
| 1101 | - return |
|
| 1102 | - ($agent !== '' && $pwd !== '') |
|
| 1103 | - || ($agent === '' && $pwd === '') |
|
| 1104 | - ; |
|
| 1105 | - } |
|
| 1106 | - |
|
| 1107 | - /** |
|
| 1108 | - * @param array $reqs |
|
| 1109 | - * @return bool |
|
| 1110 | - */ |
|
| 1111 | - private function checkRequirements($reqs) { |
|
| 1112 | - $this->checkAgentRequirements(); |
|
| 1113 | - foreach($reqs as $option) { |
|
| 1114 | - $value = $this->configuration->$option; |
|
| 1115 | - if(empty($value)) { |
|
| 1116 | - return false; |
|
| 1117 | - } |
|
| 1118 | - } |
|
| 1119 | - return true; |
|
| 1120 | - } |
|
| 1121 | - |
|
| 1122 | - /** |
|
| 1123 | - * does a cumulativeSearch on LDAP to get different values of a |
|
| 1124 | - * specified attribute |
|
| 1125 | - * @param string[] $filters array, the filters that shall be used in the search |
|
| 1126 | - * @param string $attr the attribute of which a list of values shall be returned |
|
| 1127 | - * @param int $dnReadLimit the amount of how many DNs should be analyzed. |
|
| 1128 | - * The lower, the faster |
|
| 1129 | - * @param string $maxF string. if not null, this variable will have the filter that |
|
| 1130 | - * yields most result entries |
|
| 1131 | - * @return array|false an array with the values on success, false otherwise |
|
| 1132 | - */ |
|
| 1133 | - public function cumulativeSearchOnAttribute($filters, $attr, $dnReadLimit = 3, &$maxF = null) { |
|
| 1134 | - $dnRead = array(); |
|
| 1135 | - $foundItems = array(); |
|
| 1136 | - $maxEntries = 0; |
|
| 1137 | - if(!is_array($this->configuration->ldapBase) |
|
| 1138 | - || !isset($this->configuration->ldapBase[0])) { |
|
| 1139 | - return false; |
|
| 1140 | - } |
|
| 1141 | - $base = $this->configuration->ldapBase[0]; |
|
| 1142 | - $cr = $this->getConnection(); |
|
| 1143 | - if(!$this->ldap->isResource($cr)) { |
|
| 1144 | - return false; |
|
| 1145 | - } |
|
| 1146 | - $lastFilter = null; |
|
| 1147 | - if(isset($filters[count($filters)-1])) { |
|
| 1148 | - $lastFilter = $filters[count($filters)-1]; |
|
| 1149 | - } |
|
| 1150 | - foreach($filters as $filter) { |
|
| 1151 | - if($lastFilter === $filter && count($foundItems) > 0) { |
|
| 1152 | - //skip when the filter is a wildcard and results were found |
|
| 1153 | - continue; |
|
| 1154 | - } |
|
| 1155 | - // 20k limit for performance and reason |
|
| 1156 | - $rr = $this->ldap->search($cr, $base, $filter, array($attr), 0, 20000); |
|
| 1157 | - if(!$this->ldap->isResource($rr)) { |
|
| 1158 | - continue; |
|
| 1159 | - } |
|
| 1160 | - $entries = $this->ldap->countEntries($cr, $rr); |
|
| 1161 | - $getEntryFunc = 'firstEntry'; |
|
| 1162 | - if(($entries !== false) && ($entries > 0)) { |
|
| 1163 | - if(!is_null($maxF) && $entries > $maxEntries) { |
|
| 1164 | - $maxEntries = $entries; |
|
| 1165 | - $maxF = $filter; |
|
| 1166 | - } |
|
| 1167 | - $dnReadCount = 0; |
|
| 1168 | - do { |
|
| 1169 | - $entry = $this->ldap->$getEntryFunc($cr, $rr); |
|
| 1170 | - $getEntryFunc = 'nextEntry'; |
|
| 1171 | - if(!$this->ldap->isResource($entry)) { |
|
| 1172 | - continue 2; |
|
| 1173 | - } |
|
| 1174 | - $rr = $entry; //will be expected by nextEntry next round |
|
| 1175 | - $attributes = $this->ldap->getAttributes($cr, $entry); |
|
| 1176 | - $dn = $this->ldap->getDN($cr, $entry); |
|
| 1177 | - if($dn === false || in_array($dn, $dnRead)) { |
|
| 1178 | - continue; |
|
| 1179 | - } |
|
| 1180 | - $newItems = array(); |
|
| 1181 | - $state = $this->getAttributeValuesFromEntry($attributes, |
|
| 1182 | - $attr, |
|
| 1183 | - $newItems); |
|
| 1184 | - $dnReadCount++; |
|
| 1185 | - $foundItems = array_merge($foundItems, $newItems); |
|
| 1186 | - $this->resultCache[$dn][$attr] = $newItems; |
|
| 1187 | - $dnRead[] = $dn; |
|
| 1188 | - } while(($state === self::LRESULT_PROCESSED_SKIP |
|
| 1189 | - || $this->ldap->isResource($entry)) |
|
| 1190 | - && ($dnReadLimit === 0 || $dnReadCount < $dnReadLimit)); |
|
| 1191 | - } |
|
| 1192 | - } |
|
| 1193 | - |
|
| 1194 | - return array_unique($foundItems); |
|
| 1195 | - } |
|
| 1196 | - |
|
| 1197 | - /** |
|
| 1198 | - * determines if and which $attr are available on the LDAP server |
|
| 1199 | - * @param string[] $objectclasses the objectclasses to use as search filter |
|
| 1200 | - * @param string $attr the attribute to look for |
|
| 1201 | - * @param string $dbkey the dbkey of the setting the feature is connected to |
|
| 1202 | - * @param string $confkey the confkey counterpart for the $dbkey as used in the |
|
| 1203 | - * Configuration class |
|
| 1204 | - * @param bool $po whether the objectClass with most result entries |
|
| 1205 | - * shall be pre-selected via the result |
|
| 1206 | - * @return array|false list of found items. |
|
| 1207 | - * @throws \Exception |
|
| 1208 | - */ |
|
| 1209 | - private function determineFeature($objectclasses, $attr, $dbkey, $confkey, $po = false) { |
|
| 1210 | - $cr = $this->getConnection(); |
|
| 1211 | - if(!$cr) { |
|
| 1212 | - throw new \Exception('Could not connect to LDAP'); |
|
| 1213 | - } |
|
| 1214 | - $p = 'objectclass='; |
|
| 1215 | - foreach($objectclasses as $key => $value) { |
|
| 1216 | - $objectclasses[$key] = $p.$value; |
|
| 1217 | - } |
|
| 1218 | - $maxEntryObjC = ''; |
|
| 1219 | - |
|
| 1220 | - //how deep to dig? |
|
| 1221 | - //When looking for objectclasses, testing few entries is sufficient, |
|
| 1222 | - $dig = 3; |
|
| 1223 | - |
|
| 1224 | - $availableFeatures = |
|
| 1225 | - $this->cumulativeSearchOnAttribute($objectclasses, $attr, |
|
| 1226 | - $dig, $maxEntryObjC); |
|
| 1227 | - if(is_array($availableFeatures) |
|
| 1228 | - && count($availableFeatures) > 0) { |
|
| 1229 | - natcasesort($availableFeatures); |
|
| 1230 | - //natcasesort keeps indices, but we must get rid of them for proper |
|
| 1231 | - //sorting in the web UI. Therefore: array_values |
|
| 1232 | - $this->result->addOptions($dbkey, array_values($availableFeatures)); |
|
| 1233 | - } else { |
|
| 1234 | - throw new \Exception(self::$l->t('Could not find the desired feature')); |
|
| 1235 | - } |
|
| 1236 | - |
|
| 1237 | - $setFeatures = $this->configuration->$confkey; |
|
| 1238 | - if(is_array($setFeatures) && !empty($setFeatures)) { |
|
| 1239 | - //something is already configured? pre-select it. |
|
| 1240 | - $this->result->addChange($dbkey, $setFeatures); |
|
| 1241 | - } else if ($po && $maxEntryObjC !== '') { |
|
| 1242 | - //pre-select objectclass with most result entries |
|
| 1243 | - $maxEntryObjC = str_replace($p, '', $maxEntryObjC); |
|
| 1244 | - $this->applyFind($dbkey, $maxEntryObjC); |
|
| 1245 | - $this->result->addChange($dbkey, $maxEntryObjC); |
|
| 1246 | - } |
|
| 1247 | - |
|
| 1248 | - return $availableFeatures; |
|
| 1249 | - } |
|
| 1250 | - |
|
| 1251 | - /** |
|
| 1252 | - * appends a list of values fr |
|
| 1253 | - * @param resource $result the return value from ldap_get_attributes |
|
| 1254 | - * @param string $attribute the attribute values to look for |
|
| 1255 | - * @param array &$known new values will be appended here |
|
| 1256 | - * @return int, state on of the class constants LRESULT_PROCESSED_OK, |
|
| 1257 | - * LRESULT_PROCESSED_INVALID or LRESULT_PROCESSED_SKIP |
|
| 1258 | - */ |
|
| 1259 | - private function getAttributeValuesFromEntry($result, $attribute, &$known) { |
|
| 1260 | - if(!is_array($result) |
|
| 1261 | - || !isset($result['count']) |
|
| 1262 | - || !$result['count'] > 0) { |
|
| 1263 | - return self::LRESULT_PROCESSED_INVALID; |
|
| 1264 | - } |
|
| 1265 | - |
|
| 1266 | - // strtolower on all keys for proper comparison |
|
| 1267 | - $result = \OCP\Util::mb_array_change_key_case($result); |
|
| 1268 | - $attribute = strtolower($attribute); |
|
| 1269 | - if(isset($result[$attribute])) { |
|
| 1270 | - foreach($result[$attribute] as $key => $val) { |
|
| 1271 | - if($key === 'count') { |
|
| 1272 | - continue; |
|
| 1273 | - } |
|
| 1274 | - if(!in_array($val, $known)) { |
|
| 1275 | - $known[] = $val; |
|
| 1276 | - } |
|
| 1277 | - } |
|
| 1278 | - return self::LRESULT_PROCESSED_OK; |
|
| 1279 | - } else { |
|
| 1280 | - return self::LRESULT_PROCESSED_SKIP; |
|
| 1281 | - } |
|
| 1282 | - } |
|
| 1283 | - |
|
| 1284 | - /** |
|
| 1285 | - * @return bool|mixed |
|
| 1286 | - */ |
|
| 1287 | - private function getConnection() { |
|
| 1288 | - if(!is_null($this->cr)) { |
|
| 1289 | - return $this->cr; |
|
| 1290 | - } |
|
| 1291 | - |
|
| 1292 | - $cr = $this->ldap->connect( |
|
| 1293 | - $this->configuration->ldapHost, |
|
| 1294 | - $this->configuration->ldapPort |
|
| 1295 | - ); |
|
| 1296 | - |
|
| 1297 | - $this->ldap->setOption($cr, LDAP_OPT_PROTOCOL_VERSION, 3); |
|
| 1298 | - $this->ldap->setOption($cr, LDAP_OPT_REFERRALS, 0); |
|
| 1299 | - $this->ldap->setOption($cr, LDAP_OPT_NETWORK_TIMEOUT, self::LDAP_NW_TIMEOUT); |
|
| 1300 | - if($this->configuration->ldapTLS === 1) { |
|
| 1301 | - $this->ldap->startTls($cr); |
|
| 1302 | - } |
|
| 1303 | - |
|
| 1304 | - $lo = @$this->ldap->bind($cr, |
|
| 1305 | - $this->configuration->ldapAgentName, |
|
| 1306 | - $this->configuration->ldapAgentPassword); |
|
| 1307 | - if($lo === true) { |
|
| 1308 | - $this->$cr = $cr; |
|
| 1309 | - return $cr; |
|
| 1310 | - } |
|
| 1311 | - |
|
| 1312 | - return false; |
|
| 1313 | - } |
|
| 1314 | - |
|
| 1315 | - /** |
|
| 1316 | - * @return array |
|
| 1317 | - */ |
|
| 1318 | - private function getDefaultLdapPortSettings() { |
|
| 1319 | - static $settings = array( |
|
| 1320 | - array('port' => 7636, 'tls' => false), |
|
| 1321 | - array('port' => 636, 'tls' => false), |
|
| 1322 | - array('port' => 7389, 'tls' => true), |
|
| 1323 | - array('port' => 389, 'tls' => true), |
|
| 1324 | - array('port' => 7389, 'tls' => false), |
|
| 1325 | - array('port' => 389, 'tls' => false), |
|
| 1326 | - ); |
|
| 1327 | - return $settings; |
|
| 1328 | - } |
|
| 1329 | - |
|
| 1330 | - /** |
|
| 1331 | - * @return array |
|
| 1332 | - */ |
|
| 1333 | - private function getPortSettingsToTry() { |
|
| 1334 | - //389 ← LDAP / Unencrypted or StartTLS |
|
| 1335 | - //636 ← LDAPS / SSL |
|
| 1336 | - //7xxx ← UCS. need to be checked first, because both ports may be open |
|
| 1337 | - $host = $this->configuration->ldapHost; |
|
| 1338 | - $port = intval($this->configuration->ldapPort); |
|
| 1339 | - $portSettings = array(); |
|
| 1340 | - |
|
| 1341 | - //In case the port is already provided, we will check this first |
|
| 1342 | - if($port > 0) { |
|
| 1343 | - $hostInfo = parse_url($host); |
|
| 1344 | - if(!(is_array($hostInfo) |
|
| 1345 | - && isset($hostInfo['scheme']) |
|
| 1346 | - && stripos($hostInfo['scheme'], 'ldaps') !== false)) { |
|
| 1347 | - $portSettings[] = array('port' => $port, 'tls' => true); |
|
| 1348 | - } |
|
| 1349 | - $portSettings[] =array('port' => $port, 'tls' => false); |
|
| 1350 | - } |
|
| 1351 | - |
|
| 1352 | - //default ports |
|
| 1353 | - $portSettings = array_merge($portSettings, |
|
| 1354 | - $this->getDefaultLdapPortSettings()); |
|
| 1355 | - |
|
| 1356 | - return $portSettings; |
|
| 1357 | - } |
|
| 40 | + /** @var \OCP\IL10N */ |
|
| 41 | + static protected $l; |
|
| 42 | + protected $access; |
|
| 43 | + protected $cr; |
|
| 44 | + protected $configuration; |
|
| 45 | + protected $result; |
|
| 46 | + protected $resultCache = array(); |
|
| 47 | + |
|
| 48 | + const LRESULT_PROCESSED_OK = 2; |
|
| 49 | + const LRESULT_PROCESSED_INVALID = 3; |
|
| 50 | + const LRESULT_PROCESSED_SKIP = 4; |
|
| 51 | + |
|
| 52 | + const LFILTER_LOGIN = 2; |
|
| 53 | + const LFILTER_USER_LIST = 3; |
|
| 54 | + const LFILTER_GROUP_LIST = 4; |
|
| 55 | + |
|
| 56 | + const LFILTER_MODE_ASSISTED = 2; |
|
| 57 | + const LFILTER_MODE_RAW = 1; |
|
| 58 | + |
|
| 59 | + const LDAP_NW_TIMEOUT = 4; |
|
| 60 | + |
|
| 61 | + /** |
|
| 62 | + * Constructor |
|
| 63 | + * @param Configuration $configuration an instance of Configuration |
|
| 64 | + * @param ILDAPWrapper $ldap an instance of ILDAPWrapper |
|
| 65 | + * @param Access $access |
|
| 66 | + */ |
|
| 67 | + public function __construct(Configuration $configuration, ILDAPWrapper $ldap, Access $access) { |
|
| 68 | + parent::__construct($ldap); |
|
| 69 | + $this->configuration = $configuration; |
|
| 70 | + if(is_null(Wizard::$l)) { |
|
| 71 | + Wizard::$l = \OC::$server->getL10N('user_ldap'); |
|
| 72 | + } |
|
| 73 | + $this->access = $access; |
|
| 74 | + $this->result = new WizardResult(); |
|
| 75 | + } |
|
| 76 | + |
|
| 77 | + public function __destruct() { |
|
| 78 | + if($this->result->hasChanges()) { |
|
| 79 | + $this->configuration->saveConfiguration(); |
|
| 80 | + } |
|
| 81 | + } |
|
| 82 | + |
|
| 83 | + /** |
|
| 84 | + * counts entries in the LDAP directory |
|
| 85 | + * |
|
| 86 | + * @param string $filter the LDAP search filter |
|
| 87 | + * @param string $type a string being either 'users' or 'groups'; |
|
| 88 | + * @return bool|int |
|
| 89 | + * @throws \Exception |
|
| 90 | + */ |
|
| 91 | + public function countEntries($filter, $type) { |
|
| 92 | + $reqs = array('ldapHost', 'ldapPort', 'ldapBase'); |
|
| 93 | + if($type === 'users') { |
|
| 94 | + $reqs[] = 'ldapUserFilter'; |
|
| 95 | + } |
|
| 96 | + if(!$this->checkRequirements($reqs)) { |
|
| 97 | + throw new \Exception('Requirements not met', 400); |
|
| 98 | + } |
|
| 99 | + |
|
| 100 | + $attr = array('dn'); // default |
|
| 101 | + $limit = 1001; |
|
| 102 | + if($type === 'groups') { |
|
| 103 | + $result = $this->access->countGroups($filter, $attr, $limit); |
|
| 104 | + } else if($type === 'users') { |
|
| 105 | + $result = $this->access->countUsers($filter, $attr, $limit); |
|
| 106 | + } else if ($type === 'objects') { |
|
| 107 | + $result = $this->access->countObjects($limit); |
|
| 108 | + } else { |
|
| 109 | + throw new \Exception('internal error: invalid object type', 500); |
|
| 110 | + } |
|
| 111 | + |
|
| 112 | + return $result; |
|
| 113 | + } |
|
| 114 | + |
|
| 115 | + /** |
|
| 116 | + * formats the return value of a count operation to the string to be |
|
| 117 | + * inserted. |
|
| 118 | + * |
|
| 119 | + * @param bool|int $count |
|
| 120 | + * @return int|string |
|
| 121 | + */ |
|
| 122 | + private function formatCountResult($count) { |
|
| 123 | + $formatted = ($count !== false) ? $count : 0; |
|
| 124 | + if($formatted > 1000) { |
|
| 125 | + $formatted = '> 1000'; |
|
| 126 | + } |
|
| 127 | + return $formatted; |
|
| 128 | + } |
|
| 129 | + |
|
| 130 | + public function countGroups() { |
|
| 131 | + $filter = $this->configuration->ldapGroupFilter; |
|
| 132 | + |
|
| 133 | + if(empty($filter)) { |
|
| 134 | + $output = self::$l->n('%s group found', '%s groups found', 0, array(0)); |
|
| 135 | + $this->result->addChange('ldap_group_count', $output); |
|
| 136 | + return $this->result; |
|
| 137 | + } |
|
| 138 | + |
|
| 139 | + try { |
|
| 140 | + $groupsTotal = $this->formatCountResult($this->countEntries($filter, 'groups')); |
|
| 141 | + } catch (\Exception $e) { |
|
| 142 | + //400 can be ignored, 500 is forwarded |
|
| 143 | + if($e->getCode() === 500) { |
|
| 144 | + throw $e; |
|
| 145 | + } |
|
| 146 | + return false; |
|
| 147 | + } |
|
| 148 | + $output = self::$l->n('%s group found', '%s groups found', $groupsTotal, array($groupsTotal)); |
|
| 149 | + $this->result->addChange('ldap_group_count', $output); |
|
| 150 | + return $this->result; |
|
| 151 | + } |
|
| 152 | + |
|
| 153 | + /** |
|
| 154 | + * @return WizardResult |
|
| 155 | + * @throws \Exception |
|
| 156 | + */ |
|
| 157 | + public function countUsers() { |
|
| 158 | + $filter = $this->access->getFilterForUserCount(); |
|
| 159 | + |
|
| 160 | + $usersTotal = $this->formatCountResult($this->countEntries($filter, 'users')); |
|
| 161 | + $output = self::$l->n('%s user found', '%s users found', $usersTotal, array($usersTotal)); |
|
| 162 | + $this->result->addChange('ldap_user_count', $output); |
|
| 163 | + return $this->result; |
|
| 164 | + } |
|
| 165 | + |
|
| 166 | + /** |
|
| 167 | + * counts any objects in the currently set base dn |
|
| 168 | + * |
|
| 169 | + * @return WizardResult |
|
| 170 | + * @throws \Exception |
|
| 171 | + */ |
|
| 172 | + public function countInBaseDN() { |
|
| 173 | + // we don't need to provide a filter in this case |
|
| 174 | + $total = $this->countEntries(null, 'objects'); |
|
| 175 | + if($total === false) { |
|
| 176 | + throw new \Exception('invalid results received'); |
|
| 177 | + } |
|
| 178 | + $this->result->addChange('ldap_test_base', $total); |
|
| 179 | + return $this->result; |
|
| 180 | + } |
|
| 181 | + |
|
| 182 | + /** |
|
| 183 | + * counts users with a specified attribute |
|
| 184 | + * @param string $attr |
|
| 185 | + * @param bool $existsCheck |
|
| 186 | + * @return int|bool |
|
| 187 | + */ |
|
| 188 | + public function countUsersWithAttribute($attr, $existsCheck = false) { |
|
| 189 | + if(!$this->checkRequirements(array('ldapHost', |
|
| 190 | + 'ldapPort', |
|
| 191 | + 'ldapBase', |
|
| 192 | + 'ldapUserFilter', |
|
| 193 | + ))) { |
|
| 194 | + return false; |
|
| 195 | + } |
|
| 196 | + |
|
| 197 | + $filter = $this->access->combineFilterWithAnd(array( |
|
| 198 | + $this->configuration->ldapUserFilter, |
|
| 199 | + $attr . '=*' |
|
| 200 | + )); |
|
| 201 | + |
|
| 202 | + $limit = ($existsCheck === false) ? null : 1; |
|
| 203 | + |
|
| 204 | + return $this->access->countUsers($filter, array('dn'), $limit); |
|
| 205 | + } |
|
| 206 | + |
|
| 207 | + /** |
|
| 208 | + * detects the display name attribute. If a setting is already present that |
|
| 209 | + * returns at least one hit, the detection will be canceled. |
|
| 210 | + * @return WizardResult|bool |
|
| 211 | + * @throws \Exception |
|
| 212 | + */ |
|
| 213 | + public function detectUserDisplayNameAttribute() { |
|
| 214 | + if(!$this->checkRequirements(array('ldapHost', |
|
| 215 | + 'ldapPort', |
|
| 216 | + 'ldapBase', |
|
| 217 | + 'ldapUserFilter', |
|
| 218 | + ))) { |
|
| 219 | + return false; |
|
| 220 | + } |
|
| 221 | + |
|
| 222 | + $attr = $this->configuration->ldapUserDisplayName; |
|
| 223 | + if ($attr !== '' && $attr !== 'displayName') { |
|
| 224 | + // most likely not the default value with upper case N, |
|
| 225 | + // verify it still produces a result |
|
| 226 | + $count = intval($this->countUsersWithAttribute($attr, true)); |
|
| 227 | + if($count > 0) { |
|
| 228 | + //no change, but we sent it back to make sure the user interface |
|
| 229 | + //is still correct, even if the ajax call was cancelled meanwhile |
|
| 230 | + $this->result->addChange('ldap_display_name', $attr); |
|
| 231 | + return $this->result; |
|
| 232 | + } |
|
| 233 | + } |
|
| 234 | + |
|
| 235 | + // first attribute that has at least one result wins |
|
| 236 | + $displayNameAttrs = array('displayname', 'cn'); |
|
| 237 | + foreach ($displayNameAttrs as $attr) { |
|
| 238 | + $count = intval($this->countUsersWithAttribute($attr, true)); |
|
| 239 | + |
|
| 240 | + if($count > 0) { |
|
| 241 | + $this->applyFind('ldap_display_name', $attr); |
|
| 242 | + return $this->result; |
|
| 243 | + } |
|
| 244 | + }; |
|
| 245 | + |
|
| 246 | + throw new \Exception(self::$l->t('Could not detect user display name attribute. Please specify it yourself in advanced ldap settings.')); |
|
| 247 | + } |
|
| 248 | + |
|
| 249 | + /** |
|
| 250 | + * detects the most often used email attribute for users applying to the |
|
| 251 | + * user list filter. If a setting is already present that returns at least |
|
| 252 | + * one hit, the detection will be canceled. |
|
| 253 | + * @return WizardResult|bool |
|
| 254 | + */ |
|
| 255 | + public function detectEmailAttribute() { |
|
| 256 | + if(!$this->checkRequirements(array('ldapHost', |
|
| 257 | + 'ldapPort', |
|
| 258 | + 'ldapBase', |
|
| 259 | + 'ldapUserFilter', |
|
| 260 | + ))) { |
|
| 261 | + return false; |
|
| 262 | + } |
|
| 263 | + |
|
| 264 | + $attr = $this->configuration->ldapEmailAttribute; |
|
| 265 | + if ($attr !== '') { |
|
| 266 | + $count = intval($this->countUsersWithAttribute($attr, true)); |
|
| 267 | + if($count > 0) { |
|
| 268 | + return false; |
|
| 269 | + } |
|
| 270 | + $writeLog = true; |
|
| 271 | + } else { |
|
| 272 | + $writeLog = false; |
|
| 273 | + } |
|
| 274 | + |
|
| 275 | + $emailAttributes = array('mail', 'mailPrimaryAddress'); |
|
| 276 | + $winner = ''; |
|
| 277 | + $maxUsers = 0; |
|
| 278 | + foreach($emailAttributes as $attr) { |
|
| 279 | + $count = $this->countUsersWithAttribute($attr); |
|
| 280 | + if($count > $maxUsers) { |
|
| 281 | + $maxUsers = $count; |
|
| 282 | + $winner = $attr; |
|
| 283 | + } |
|
| 284 | + } |
|
| 285 | + |
|
| 286 | + if($winner !== '') { |
|
| 287 | + $this->applyFind('ldap_email_attr', $winner); |
|
| 288 | + if($writeLog) { |
|
| 289 | + \OCP\Util::writeLog('user_ldap', 'The mail attribute has ' . |
|
| 290 | + 'automatically been reset, because the original value ' . |
|
| 291 | + 'did not return any results.', \OCP\Util::INFO); |
|
| 292 | + } |
|
| 293 | + } |
|
| 294 | + |
|
| 295 | + return $this->result; |
|
| 296 | + } |
|
| 297 | + |
|
| 298 | + /** |
|
| 299 | + * @return WizardResult |
|
| 300 | + * @throws \Exception |
|
| 301 | + */ |
|
| 302 | + public function determineAttributes() { |
|
| 303 | + if(!$this->checkRequirements(array('ldapHost', |
|
| 304 | + 'ldapPort', |
|
| 305 | + 'ldapBase', |
|
| 306 | + 'ldapUserFilter', |
|
| 307 | + ))) { |
|
| 308 | + return false; |
|
| 309 | + } |
|
| 310 | + |
|
| 311 | + $attributes = $this->getUserAttributes(); |
|
| 312 | + |
|
| 313 | + natcasesort($attributes); |
|
| 314 | + $attributes = array_values($attributes); |
|
| 315 | + |
|
| 316 | + $this->result->addOptions('ldap_loginfilter_attributes', $attributes); |
|
| 317 | + |
|
| 318 | + $selected = $this->configuration->ldapLoginFilterAttributes; |
|
| 319 | + if(is_array($selected) && !empty($selected)) { |
|
| 320 | + $this->result->addChange('ldap_loginfilter_attributes', $selected); |
|
| 321 | + } |
|
| 322 | + |
|
| 323 | + return $this->result; |
|
| 324 | + } |
|
| 325 | + |
|
| 326 | + /** |
|
| 327 | + * detects the available LDAP attributes |
|
| 328 | + * @return array|false The instance's WizardResult instance |
|
| 329 | + * @throws \Exception |
|
| 330 | + */ |
|
| 331 | + private function getUserAttributes() { |
|
| 332 | + if(!$this->checkRequirements(array('ldapHost', |
|
| 333 | + 'ldapPort', |
|
| 334 | + 'ldapBase', |
|
| 335 | + 'ldapUserFilter', |
|
| 336 | + ))) { |
|
| 337 | + return false; |
|
| 338 | + } |
|
| 339 | + $cr = $this->getConnection(); |
|
| 340 | + if(!$cr) { |
|
| 341 | + throw new \Exception('Could not connect to LDAP'); |
|
| 342 | + } |
|
| 343 | + |
|
| 344 | + $base = $this->configuration->ldapBase[0]; |
|
| 345 | + $filter = $this->configuration->ldapUserFilter; |
|
| 346 | + $rr = $this->ldap->search($cr, $base, $filter, array(), 1, 1); |
|
| 347 | + if(!$this->ldap->isResource($rr)) { |
|
| 348 | + return false; |
|
| 349 | + } |
|
| 350 | + $er = $this->ldap->firstEntry($cr, $rr); |
|
| 351 | + $attributes = $this->ldap->getAttributes($cr, $er); |
|
| 352 | + $pureAttributes = array(); |
|
| 353 | + for($i = 0; $i < $attributes['count']; $i++) { |
|
| 354 | + $pureAttributes[] = $attributes[$i]; |
|
| 355 | + } |
|
| 356 | + |
|
| 357 | + return $pureAttributes; |
|
| 358 | + } |
|
| 359 | + |
|
| 360 | + /** |
|
| 361 | + * detects the available LDAP groups |
|
| 362 | + * @return WizardResult|false the instance's WizardResult instance |
|
| 363 | + */ |
|
| 364 | + public function determineGroupsForGroups() { |
|
| 365 | + return $this->determineGroups('ldap_groupfilter_groups', |
|
| 366 | + 'ldapGroupFilterGroups', |
|
| 367 | + false); |
|
| 368 | + } |
|
| 369 | + |
|
| 370 | + /** |
|
| 371 | + * detects the available LDAP groups |
|
| 372 | + * @return WizardResult|false the instance's WizardResult instance |
|
| 373 | + */ |
|
| 374 | + public function determineGroupsForUsers() { |
|
| 375 | + return $this->determineGroups('ldap_userfilter_groups', |
|
| 376 | + 'ldapUserFilterGroups'); |
|
| 377 | + } |
|
| 378 | + |
|
| 379 | + /** |
|
| 380 | + * detects the available LDAP groups |
|
| 381 | + * @param string $dbKey |
|
| 382 | + * @param string $confKey |
|
| 383 | + * @param bool $testMemberOf |
|
| 384 | + * @return WizardResult|false the instance's WizardResult instance |
|
| 385 | + * @throws \Exception |
|
| 386 | + */ |
|
| 387 | + private function determineGroups($dbKey, $confKey, $testMemberOf = true) { |
|
| 388 | + if(!$this->checkRequirements(array('ldapHost', |
|
| 389 | + 'ldapPort', |
|
| 390 | + 'ldapBase', |
|
| 391 | + ))) { |
|
| 392 | + return false; |
|
| 393 | + } |
|
| 394 | + $cr = $this->getConnection(); |
|
| 395 | + if(!$cr) { |
|
| 396 | + throw new \Exception('Could not connect to LDAP'); |
|
| 397 | + } |
|
| 398 | + |
|
| 399 | + $this->fetchGroups($dbKey, $confKey); |
|
| 400 | + |
|
| 401 | + if($testMemberOf) { |
|
| 402 | + $this->configuration->hasMemberOfFilterSupport = $this->testMemberOf(); |
|
| 403 | + $this->result->markChange(); |
|
| 404 | + if(!$this->configuration->hasMemberOfFilterSupport) { |
|
| 405 | + throw new \Exception('memberOf is not supported by the server'); |
|
| 406 | + } |
|
| 407 | + } |
|
| 408 | + |
|
| 409 | + return $this->result; |
|
| 410 | + } |
|
| 411 | + |
|
| 412 | + /** |
|
| 413 | + * fetches all groups from LDAP and adds them to the result object |
|
| 414 | + * |
|
| 415 | + * @param string $dbKey |
|
| 416 | + * @param string $confKey |
|
| 417 | + * @return array $groupEntries |
|
| 418 | + * @throws \Exception |
|
| 419 | + */ |
|
| 420 | + public function fetchGroups($dbKey, $confKey) { |
|
| 421 | + $obclasses = array('posixGroup', 'group', 'zimbraDistributionList', 'groupOfNames'); |
|
| 422 | + |
|
| 423 | + $filterParts = array(); |
|
| 424 | + foreach($obclasses as $obclass) { |
|
| 425 | + $filterParts[] = 'objectclass='.$obclass; |
|
| 426 | + } |
|
| 427 | + //we filter for everything |
|
| 428 | + //- that looks like a group and |
|
| 429 | + //- has the group display name set |
|
| 430 | + $filter = $this->access->combineFilterWithOr($filterParts); |
|
| 431 | + $filter = $this->access->combineFilterWithAnd(array($filter, 'cn=*')); |
|
| 432 | + |
|
| 433 | + $groupNames = array(); |
|
| 434 | + $groupEntries = array(); |
|
| 435 | + $limit = 400; |
|
| 436 | + $offset = 0; |
|
| 437 | + do { |
|
| 438 | + // we need to request dn additionally here, otherwise memberOf |
|
| 439 | + // detection will fail later |
|
| 440 | + $result = $this->access->searchGroups($filter, array('cn', 'dn'), $limit, $offset); |
|
| 441 | + foreach($result as $item) { |
|
| 442 | + if(!isset($item['cn']) && !is_array($item['cn']) && !isset($item['cn'][0])) { |
|
| 443 | + // just in case - no issue known |
|
| 444 | + continue; |
|
| 445 | + } |
|
| 446 | + $groupNames[] = $item['cn'][0]; |
|
| 447 | + $groupEntries[] = $item; |
|
| 448 | + } |
|
| 449 | + $offset += $limit; |
|
| 450 | + } while ($this->access->hasMoreResults()); |
|
| 451 | + |
|
| 452 | + if(count($groupNames) > 0) { |
|
| 453 | + natsort($groupNames); |
|
| 454 | + $this->result->addOptions($dbKey, array_values($groupNames)); |
|
| 455 | + } else { |
|
| 456 | + throw new \Exception(self::$l->t('Could not find the desired feature')); |
|
| 457 | + } |
|
| 458 | + |
|
| 459 | + $setFeatures = $this->configuration->$confKey; |
|
| 460 | + if(is_array($setFeatures) && !empty($setFeatures)) { |
|
| 461 | + //something is already configured? pre-select it. |
|
| 462 | + $this->result->addChange($dbKey, $setFeatures); |
|
| 463 | + } |
|
| 464 | + return $groupEntries; |
|
| 465 | + } |
|
| 466 | + |
|
| 467 | + public function determineGroupMemberAssoc() { |
|
| 468 | + if(!$this->checkRequirements(array('ldapHost', |
|
| 469 | + 'ldapPort', |
|
| 470 | + 'ldapGroupFilter', |
|
| 471 | + ))) { |
|
| 472 | + return false; |
|
| 473 | + } |
|
| 474 | + $attribute = $this->detectGroupMemberAssoc(); |
|
| 475 | + if($attribute === false) { |
|
| 476 | + return false; |
|
| 477 | + } |
|
| 478 | + $this->configuration->setConfiguration(array('ldapGroupMemberAssocAttr' => $attribute)); |
|
| 479 | + $this->result->addChange('ldap_group_member_assoc_attribute', $attribute); |
|
| 480 | + |
|
| 481 | + return $this->result; |
|
| 482 | + } |
|
| 483 | + |
|
| 484 | + /** |
|
| 485 | + * Detects the available object classes |
|
| 486 | + * @return WizardResult|false the instance's WizardResult instance |
|
| 487 | + * @throws \Exception |
|
| 488 | + */ |
|
| 489 | + public function determineGroupObjectClasses() { |
|
| 490 | + if(!$this->checkRequirements(array('ldapHost', |
|
| 491 | + 'ldapPort', |
|
| 492 | + 'ldapBase', |
|
| 493 | + ))) { |
|
| 494 | + return false; |
|
| 495 | + } |
|
| 496 | + $cr = $this->getConnection(); |
|
| 497 | + if(!$cr) { |
|
| 498 | + throw new \Exception('Could not connect to LDAP'); |
|
| 499 | + } |
|
| 500 | + |
|
| 501 | + $obclasses = array('groupOfNames', 'groupOfUniqueNames', 'group', 'posixGroup', '*'); |
|
| 502 | + $this->determineFeature($obclasses, |
|
| 503 | + 'objectclass', |
|
| 504 | + 'ldap_groupfilter_objectclass', |
|
| 505 | + 'ldapGroupFilterObjectclass', |
|
| 506 | + false); |
|
| 507 | + |
|
| 508 | + return $this->result; |
|
| 509 | + } |
|
| 510 | + |
|
| 511 | + /** |
|
| 512 | + * detects the available object classes |
|
| 513 | + * @return WizardResult |
|
| 514 | + * @throws \Exception |
|
| 515 | + */ |
|
| 516 | + public function determineUserObjectClasses() { |
|
| 517 | + if(!$this->checkRequirements(array('ldapHost', |
|
| 518 | + 'ldapPort', |
|
| 519 | + 'ldapBase', |
|
| 520 | + ))) { |
|
| 521 | + return false; |
|
| 522 | + } |
|
| 523 | + $cr = $this->getConnection(); |
|
| 524 | + if(!$cr) { |
|
| 525 | + throw new \Exception('Could not connect to LDAP'); |
|
| 526 | + } |
|
| 527 | + |
|
| 528 | + $obclasses = array('inetOrgPerson', 'person', 'organizationalPerson', |
|
| 529 | + 'user', 'posixAccount', '*'); |
|
| 530 | + $filter = $this->configuration->ldapUserFilter; |
|
| 531 | + //if filter is empty, it is probably the first time the wizard is called |
|
| 532 | + //then, apply suggestions. |
|
| 533 | + $this->determineFeature($obclasses, |
|
| 534 | + 'objectclass', |
|
| 535 | + 'ldap_userfilter_objectclass', |
|
| 536 | + 'ldapUserFilterObjectclass', |
|
| 537 | + empty($filter)); |
|
| 538 | + |
|
| 539 | + return $this->result; |
|
| 540 | + } |
|
| 541 | + |
|
| 542 | + /** |
|
| 543 | + * @return WizardResult|false |
|
| 544 | + * @throws \Exception |
|
| 545 | + */ |
|
| 546 | + public function getGroupFilter() { |
|
| 547 | + if(!$this->checkRequirements(array('ldapHost', |
|
| 548 | + 'ldapPort', |
|
| 549 | + 'ldapBase', |
|
| 550 | + ))) { |
|
| 551 | + return false; |
|
| 552 | + } |
|
| 553 | + //make sure the use display name is set |
|
| 554 | + $displayName = $this->configuration->ldapGroupDisplayName; |
|
| 555 | + if ($displayName === '') { |
|
| 556 | + $d = $this->configuration->getDefaults(); |
|
| 557 | + $this->applyFind('ldap_group_display_name', |
|
| 558 | + $d['ldap_group_display_name']); |
|
| 559 | + } |
|
| 560 | + $filter = $this->composeLdapFilter(self::LFILTER_GROUP_LIST); |
|
| 561 | + |
|
| 562 | + $this->applyFind('ldap_group_filter', $filter); |
|
| 563 | + return $this->result; |
|
| 564 | + } |
|
| 565 | + |
|
| 566 | + /** |
|
| 567 | + * @return WizardResult|false |
|
| 568 | + * @throws \Exception |
|
| 569 | + */ |
|
| 570 | + public function getUserListFilter() { |
|
| 571 | + if(!$this->checkRequirements(array('ldapHost', |
|
| 572 | + 'ldapPort', |
|
| 573 | + 'ldapBase', |
|
| 574 | + ))) { |
|
| 575 | + return false; |
|
| 576 | + } |
|
| 577 | + //make sure the use display name is set |
|
| 578 | + $displayName = $this->configuration->ldapUserDisplayName; |
|
| 579 | + if ($displayName === '') { |
|
| 580 | + $d = $this->configuration->getDefaults(); |
|
| 581 | + $this->applyFind('ldap_display_name', $d['ldap_display_name']); |
|
| 582 | + } |
|
| 583 | + $filter = $this->composeLdapFilter(self::LFILTER_USER_LIST); |
|
| 584 | + if(!$filter) { |
|
| 585 | + throw new \Exception('Cannot create filter'); |
|
| 586 | + } |
|
| 587 | + |
|
| 588 | + $this->applyFind('ldap_userlist_filter', $filter); |
|
| 589 | + return $this->result; |
|
| 590 | + } |
|
| 591 | + |
|
| 592 | + /** |
|
| 593 | + * @return bool|WizardResult |
|
| 594 | + * @throws \Exception |
|
| 595 | + */ |
|
| 596 | + public function getUserLoginFilter() { |
|
| 597 | + if(!$this->checkRequirements(array('ldapHost', |
|
| 598 | + 'ldapPort', |
|
| 599 | + 'ldapBase', |
|
| 600 | + 'ldapUserFilter', |
|
| 601 | + ))) { |
|
| 602 | + return false; |
|
| 603 | + } |
|
| 604 | + |
|
| 605 | + $filter = $this->composeLdapFilter(self::LFILTER_LOGIN); |
|
| 606 | + if(!$filter) { |
|
| 607 | + throw new \Exception('Cannot create filter'); |
|
| 608 | + } |
|
| 609 | + |
|
| 610 | + $this->applyFind('ldap_login_filter', $filter); |
|
| 611 | + return $this->result; |
|
| 612 | + } |
|
| 613 | + |
|
| 614 | + /** |
|
| 615 | + * @return bool|WizardResult |
|
| 616 | + * @param string $loginName |
|
| 617 | + * @throws \Exception |
|
| 618 | + */ |
|
| 619 | + public function testLoginName($loginName) { |
|
| 620 | + if(!$this->checkRequirements(array('ldapHost', |
|
| 621 | + 'ldapPort', |
|
| 622 | + 'ldapBase', |
|
| 623 | + 'ldapLoginFilter', |
|
| 624 | + ))) { |
|
| 625 | + return false; |
|
| 626 | + } |
|
| 627 | + |
|
| 628 | + $cr = $this->access->connection->getConnectionResource(); |
|
| 629 | + if(!$this->ldap->isResource($cr)) { |
|
| 630 | + throw new \Exception('connection error'); |
|
| 631 | + } |
|
| 632 | + |
|
| 633 | + if(mb_strpos($this->access->connection->ldapLoginFilter, '%uid', 0, 'UTF-8') |
|
| 634 | + === false) { |
|
| 635 | + throw new \Exception('missing placeholder'); |
|
| 636 | + } |
|
| 637 | + |
|
| 638 | + $users = $this->access->countUsersByLoginName($loginName); |
|
| 639 | + if($this->ldap->errno($cr) !== 0) { |
|
| 640 | + throw new \Exception($this->ldap->error($cr)); |
|
| 641 | + } |
|
| 642 | + $filter = str_replace('%uid', $loginName, $this->access->connection->ldapLoginFilter); |
|
| 643 | + $this->result->addChange('ldap_test_loginname', $users); |
|
| 644 | + $this->result->addChange('ldap_test_effective_filter', $filter); |
|
| 645 | + return $this->result; |
|
| 646 | + } |
|
| 647 | + |
|
| 648 | + /** |
|
| 649 | + * Tries to determine the port, requires given Host, User DN and Password |
|
| 650 | + * @return WizardResult|false WizardResult on success, false otherwise |
|
| 651 | + * @throws \Exception |
|
| 652 | + */ |
|
| 653 | + public function guessPortAndTLS() { |
|
| 654 | + if(!$this->checkRequirements(array('ldapHost', |
|
| 655 | + ))) { |
|
| 656 | + return false; |
|
| 657 | + } |
|
| 658 | + $this->checkHost(); |
|
| 659 | + $portSettings = $this->getPortSettingsToTry(); |
|
| 660 | + |
|
| 661 | + if(!is_array($portSettings)) { |
|
| 662 | + throw new \Exception(print_r($portSettings, true)); |
|
| 663 | + } |
|
| 664 | + |
|
| 665 | + //proceed from the best configuration and return on first success |
|
| 666 | + foreach($portSettings as $setting) { |
|
| 667 | + $p = $setting['port']; |
|
| 668 | + $t = $setting['tls']; |
|
| 669 | + \OCP\Util::writeLog('user_ldap', 'Wiz: trying port '. $p . ', TLS '. $t, \OCP\Util::DEBUG); |
|
| 670 | + //connectAndBind may throw Exception, it needs to be catched by the |
|
| 671 | + //callee of this method |
|
| 672 | + |
|
| 673 | + try { |
|
| 674 | + $settingsFound = $this->connectAndBind($p, $t); |
|
| 675 | + } catch (\Exception $e) { |
|
| 676 | + // any reply other than -1 (= cannot connect) is already okay, |
|
| 677 | + // because then we found the server |
|
| 678 | + // unavailable startTLS returns -11 |
|
| 679 | + if($e->getCode() > 0) { |
|
| 680 | + $settingsFound = true; |
|
| 681 | + } else { |
|
| 682 | + throw $e; |
|
| 683 | + } |
|
| 684 | + } |
|
| 685 | + |
|
| 686 | + if ($settingsFound === true) { |
|
| 687 | + $config = array( |
|
| 688 | + 'ldapPort' => $p, |
|
| 689 | + 'ldapTLS' => intval($t) |
|
| 690 | + ); |
|
| 691 | + $this->configuration->setConfiguration($config); |
|
| 692 | + \OCP\Util::writeLog('user_ldap', 'Wiz: detected Port ' . $p, \OCP\Util::DEBUG); |
|
| 693 | + $this->result->addChange('ldap_port', $p); |
|
| 694 | + return $this->result; |
|
| 695 | + } |
|
| 696 | + } |
|
| 697 | + |
|
| 698 | + //custom port, undetected (we do not brute force) |
|
| 699 | + return false; |
|
| 700 | + } |
|
| 701 | + |
|
| 702 | + /** |
|
| 703 | + * tries to determine a base dn from User DN or LDAP Host |
|
| 704 | + * @return WizardResult|false WizardResult on success, false otherwise |
|
| 705 | + */ |
|
| 706 | + public function guessBaseDN() { |
|
| 707 | + if(!$this->checkRequirements(array('ldapHost', |
|
| 708 | + 'ldapPort', |
|
| 709 | + ))) { |
|
| 710 | + return false; |
|
| 711 | + } |
|
| 712 | + |
|
| 713 | + //check whether a DN is given in the agent name (99.9% of all cases) |
|
| 714 | + $base = null; |
|
| 715 | + $i = stripos($this->configuration->ldapAgentName, 'dc='); |
|
| 716 | + if($i !== false) { |
|
| 717 | + $base = substr($this->configuration->ldapAgentName, $i); |
|
| 718 | + if($this->testBaseDN($base)) { |
|
| 719 | + $this->applyFind('ldap_base', $base); |
|
| 720 | + return $this->result; |
|
| 721 | + } |
|
| 722 | + } |
|
| 723 | + |
|
| 724 | + //this did not help :( |
|
| 725 | + //Let's see whether we can parse the Host URL and convert the domain to |
|
| 726 | + //a base DN |
|
| 727 | + $helper = new Helper(\OC::$server->getConfig()); |
|
| 728 | + $domain = $helper->getDomainFromURL($this->configuration->ldapHost); |
|
| 729 | + if(!$domain) { |
|
| 730 | + return false; |
|
| 731 | + } |
|
| 732 | + |
|
| 733 | + $dparts = explode('.', $domain); |
|
| 734 | + while(count($dparts) > 0) { |
|
| 735 | + $base2 = 'dc=' . implode(',dc=', $dparts); |
|
| 736 | + if ($base !== $base2 && $this->testBaseDN($base2)) { |
|
| 737 | + $this->applyFind('ldap_base', $base2); |
|
| 738 | + return $this->result; |
|
| 739 | + } |
|
| 740 | + array_shift($dparts); |
|
| 741 | + } |
|
| 742 | + |
|
| 743 | + return false; |
|
| 744 | + } |
|
| 745 | + |
|
| 746 | + /** |
|
| 747 | + * sets the found value for the configuration key in the WizardResult |
|
| 748 | + * as well as in the Configuration instance |
|
| 749 | + * @param string $key the configuration key |
|
| 750 | + * @param string $value the (detected) value |
|
| 751 | + * |
|
| 752 | + */ |
|
| 753 | + private function applyFind($key, $value) { |
|
| 754 | + $this->result->addChange($key, $value); |
|
| 755 | + $this->configuration->setConfiguration(array($key => $value)); |
|
| 756 | + } |
|
| 757 | + |
|
| 758 | + /** |
|
| 759 | + * Checks, whether a port was entered in the Host configuration |
|
| 760 | + * field. In this case the port will be stripped off, but also stored as |
|
| 761 | + * setting. |
|
| 762 | + */ |
|
| 763 | + private function checkHost() { |
|
| 764 | + $host = $this->configuration->ldapHost; |
|
| 765 | + $hostInfo = parse_url($host); |
|
| 766 | + |
|
| 767 | + //removes Port from Host |
|
| 768 | + if(is_array($hostInfo) && isset($hostInfo['port'])) { |
|
| 769 | + $port = $hostInfo['port']; |
|
| 770 | + $host = str_replace(':'.$port, '', $host); |
|
| 771 | + $this->applyFind('ldap_host', $host); |
|
| 772 | + $this->applyFind('ldap_port', $port); |
|
| 773 | + } |
|
| 774 | + } |
|
| 775 | + |
|
| 776 | + /** |
|
| 777 | + * tries to detect the group member association attribute which is |
|
| 778 | + * one of 'uniqueMember', 'memberUid', 'member' |
|
| 779 | + * @return string|false, string with the attribute name, false on error |
|
| 780 | + * @throws \Exception |
|
| 781 | + */ |
|
| 782 | + private function detectGroupMemberAssoc() { |
|
| 783 | + $possibleAttrs = array('uniqueMember', 'memberUid', 'member'); |
|
| 784 | + $filter = $this->configuration->ldapGroupFilter; |
|
| 785 | + if(empty($filter)) { |
|
| 786 | + return false; |
|
| 787 | + } |
|
| 788 | + $cr = $this->getConnection(); |
|
| 789 | + if(!$cr) { |
|
| 790 | + throw new \Exception('Could not connect to LDAP'); |
|
| 791 | + } |
|
| 792 | + $base = $this->configuration->ldapBase[0]; |
|
| 793 | + $rr = $this->ldap->search($cr, $base, $filter, $possibleAttrs, 0, 1000); |
|
| 794 | + if(!$this->ldap->isResource($rr)) { |
|
| 795 | + return false; |
|
| 796 | + } |
|
| 797 | + $er = $this->ldap->firstEntry($cr, $rr); |
|
| 798 | + while(is_resource($er)) { |
|
| 799 | + $this->ldap->getDN($cr, $er); |
|
| 800 | + $attrs = $this->ldap->getAttributes($cr, $er); |
|
| 801 | + $result = array(); |
|
| 802 | + $possibleAttrsCount = count($possibleAttrs); |
|
| 803 | + for($i = 0; $i < $possibleAttrsCount; $i++) { |
|
| 804 | + if(isset($attrs[$possibleAttrs[$i]])) { |
|
| 805 | + $result[$possibleAttrs[$i]] = $attrs[$possibleAttrs[$i]]['count']; |
|
| 806 | + } |
|
| 807 | + } |
|
| 808 | + if(!empty($result)) { |
|
| 809 | + natsort($result); |
|
| 810 | + return key($result); |
|
| 811 | + } |
|
| 812 | + |
|
| 813 | + $er = $this->ldap->nextEntry($cr, $er); |
|
| 814 | + } |
|
| 815 | + |
|
| 816 | + return false; |
|
| 817 | + } |
|
| 818 | + |
|
| 819 | + /** |
|
| 820 | + * Checks whether for a given BaseDN results will be returned |
|
| 821 | + * @param string $base the BaseDN to test |
|
| 822 | + * @return bool true on success, false otherwise |
|
| 823 | + * @throws \Exception |
|
| 824 | + */ |
|
| 825 | + private function testBaseDN($base) { |
|
| 826 | + $cr = $this->getConnection(); |
|
| 827 | + if(!$cr) { |
|
| 828 | + throw new \Exception('Could not connect to LDAP'); |
|
| 829 | + } |
|
| 830 | + |
|
| 831 | + //base is there, let's validate it. If we search for anything, we should |
|
| 832 | + //get a result set > 0 on a proper base |
|
| 833 | + $rr = $this->ldap->search($cr, $base, 'objectClass=*', array('dn'), 0, 1); |
|
| 834 | + if(!$this->ldap->isResource($rr)) { |
|
| 835 | + $errorNo = $this->ldap->errno($cr); |
|
| 836 | + $errorMsg = $this->ldap->error($cr); |
|
| 837 | + \OCP\Util::writeLog('user_ldap', 'Wiz: Could not search base '.$base. |
|
| 838 | + ' Error '.$errorNo.': '.$errorMsg, \OCP\Util::INFO); |
|
| 839 | + return false; |
|
| 840 | + } |
|
| 841 | + $entries = $this->ldap->countEntries($cr, $rr); |
|
| 842 | + return ($entries !== false) && ($entries > 0); |
|
| 843 | + } |
|
| 844 | + |
|
| 845 | + /** |
|
| 846 | + * Checks whether the server supports memberOf in LDAP Filter. |
|
| 847 | + * Note: at least in OpenLDAP, availability of memberOf is dependent on |
|
| 848 | + * a configured objectClass. I.e. not necessarily for all available groups |
|
| 849 | + * memberOf does work. |
|
| 850 | + * |
|
| 851 | + * @return bool true if it does, false otherwise |
|
| 852 | + * @throws \Exception |
|
| 853 | + */ |
|
| 854 | + private function testMemberOf() { |
|
| 855 | + $cr = $this->getConnection(); |
|
| 856 | + if(!$cr) { |
|
| 857 | + throw new \Exception('Could not connect to LDAP'); |
|
| 858 | + } |
|
| 859 | + $result = $this->access->countUsers('memberOf=*', array('memberOf'), 1); |
|
| 860 | + if(is_int($result) && $result > 0) { |
|
| 861 | + return true; |
|
| 862 | + } |
|
| 863 | + return false; |
|
| 864 | + } |
|
| 865 | + |
|
| 866 | + /** |
|
| 867 | + * creates an LDAP Filter from given configuration |
|
| 868 | + * @param integer $filterType int, for which use case the filter shall be created |
|
| 869 | + * can be any of self::LFILTER_USER_LIST, self::LFILTER_LOGIN or |
|
| 870 | + * self::LFILTER_GROUP_LIST |
|
| 871 | + * @return string|false string with the filter on success, false otherwise |
|
| 872 | + * @throws \Exception |
|
| 873 | + */ |
|
| 874 | + private function composeLdapFilter($filterType) { |
|
| 875 | + $filter = ''; |
|
| 876 | + $parts = 0; |
|
| 877 | + switch ($filterType) { |
|
| 878 | + case self::LFILTER_USER_LIST: |
|
| 879 | + $objcs = $this->configuration->ldapUserFilterObjectclass; |
|
| 880 | + //glue objectclasses |
|
| 881 | + if(is_array($objcs) && count($objcs) > 0) { |
|
| 882 | + $filter .= '(|'; |
|
| 883 | + foreach($objcs as $objc) { |
|
| 884 | + $filter .= '(objectclass=' . $objc . ')'; |
|
| 885 | + } |
|
| 886 | + $filter .= ')'; |
|
| 887 | + $parts++; |
|
| 888 | + } |
|
| 889 | + //glue group memberships |
|
| 890 | + if($this->configuration->hasMemberOfFilterSupport) { |
|
| 891 | + $cns = $this->configuration->ldapUserFilterGroups; |
|
| 892 | + if(is_array($cns) && count($cns) > 0) { |
|
| 893 | + $filter .= '(|'; |
|
| 894 | + $cr = $this->getConnection(); |
|
| 895 | + if(!$cr) { |
|
| 896 | + throw new \Exception('Could not connect to LDAP'); |
|
| 897 | + } |
|
| 898 | + $base = $this->configuration->ldapBase[0]; |
|
| 899 | + foreach($cns as $cn) { |
|
| 900 | + $rr = $this->ldap->search($cr, $base, 'cn=' . $cn, array('dn', 'primaryGroupToken')); |
|
| 901 | + if(!$this->ldap->isResource($rr)) { |
|
| 902 | + continue; |
|
| 903 | + } |
|
| 904 | + $er = $this->ldap->firstEntry($cr, $rr); |
|
| 905 | + $attrs = $this->ldap->getAttributes($cr, $er); |
|
| 906 | + $dn = $this->ldap->getDN($cr, $er); |
|
| 907 | + if ($dn == false || $dn === '') { |
|
| 908 | + continue; |
|
| 909 | + } |
|
| 910 | + $filterPart = '(memberof=' . $dn . ')'; |
|
| 911 | + if(isset($attrs['primaryGroupToken'])) { |
|
| 912 | + $pgt = $attrs['primaryGroupToken'][0]; |
|
| 913 | + $primaryFilterPart = '(primaryGroupID=' . $pgt .')'; |
|
| 914 | + $filterPart = '(|' . $filterPart . $primaryFilterPart . ')'; |
|
| 915 | + } |
|
| 916 | + $filter .= $filterPart; |
|
| 917 | + } |
|
| 918 | + $filter .= ')'; |
|
| 919 | + } |
|
| 920 | + $parts++; |
|
| 921 | + } |
|
| 922 | + //wrap parts in AND condition |
|
| 923 | + if($parts > 1) { |
|
| 924 | + $filter = '(&' . $filter . ')'; |
|
| 925 | + } |
|
| 926 | + if ($filter === '') { |
|
| 927 | + $filter = '(objectclass=*)'; |
|
| 928 | + } |
|
| 929 | + break; |
|
| 930 | + |
|
| 931 | + case self::LFILTER_GROUP_LIST: |
|
| 932 | + $objcs = $this->configuration->ldapGroupFilterObjectclass; |
|
| 933 | + //glue objectclasses |
|
| 934 | + if(is_array($objcs) && count($objcs) > 0) { |
|
| 935 | + $filter .= '(|'; |
|
| 936 | + foreach($objcs as $objc) { |
|
| 937 | + $filter .= '(objectclass=' . $objc . ')'; |
|
| 938 | + } |
|
| 939 | + $filter .= ')'; |
|
| 940 | + $parts++; |
|
| 941 | + } |
|
| 942 | + //glue group memberships |
|
| 943 | + $cns = $this->configuration->ldapGroupFilterGroups; |
|
| 944 | + if(is_array($cns) && count($cns) > 0) { |
|
| 945 | + $filter .= '(|'; |
|
| 946 | + foreach($cns as $cn) { |
|
| 947 | + $filter .= '(cn=' . $cn . ')'; |
|
| 948 | + } |
|
| 949 | + $filter .= ')'; |
|
| 950 | + } |
|
| 951 | + $parts++; |
|
| 952 | + //wrap parts in AND condition |
|
| 953 | + if($parts > 1) { |
|
| 954 | + $filter = '(&' . $filter . ')'; |
|
| 955 | + } |
|
| 956 | + break; |
|
| 957 | + |
|
| 958 | + case self::LFILTER_LOGIN: |
|
| 959 | + $ulf = $this->configuration->ldapUserFilter; |
|
| 960 | + $loginpart = '=%uid'; |
|
| 961 | + $filterUsername = ''; |
|
| 962 | + $userAttributes = $this->getUserAttributes(); |
|
| 963 | + $userAttributes = array_change_key_case(array_flip($userAttributes)); |
|
| 964 | + $parts = 0; |
|
| 965 | + |
|
| 966 | + if($this->configuration->ldapLoginFilterUsername === '1') { |
|
| 967 | + $attr = ''; |
|
| 968 | + if(isset($userAttributes['uid'])) { |
|
| 969 | + $attr = 'uid'; |
|
| 970 | + } else if(isset($userAttributes['samaccountname'])) { |
|
| 971 | + $attr = 'samaccountname'; |
|
| 972 | + } else if(isset($userAttributes['cn'])) { |
|
| 973 | + //fallback |
|
| 974 | + $attr = 'cn'; |
|
| 975 | + } |
|
| 976 | + if ($attr !== '') { |
|
| 977 | + $filterUsername = '(' . $attr . $loginpart . ')'; |
|
| 978 | + $parts++; |
|
| 979 | + } |
|
| 980 | + } |
|
| 981 | + |
|
| 982 | + $filterEmail = ''; |
|
| 983 | + if($this->configuration->ldapLoginFilterEmail === '1') { |
|
| 984 | + $filterEmail = '(|(mailPrimaryAddress=%uid)(mail=%uid))'; |
|
| 985 | + $parts++; |
|
| 986 | + } |
|
| 987 | + |
|
| 988 | + $filterAttributes = ''; |
|
| 989 | + $attrsToFilter = $this->configuration->ldapLoginFilterAttributes; |
|
| 990 | + if(is_array($attrsToFilter) && count($attrsToFilter) > 0) { |
|
| 991 | + $filterAttributes = '(|'; |
|
| 992 | + foreach($attrsToFilter as $attribute) { |
|
| 993 | + $filterAttributes .= '(' . $attribute . $loginpart . ')'; |
|
| 994 | + } |
|
| 995 | + $filterAttributes .= ')'; |
|
| 996 | + $parts++; |
|
| 997 | + } |
|
| 998 | + |
|
| 999 | + $filterLogin = ''; |
|
| 1000 | + if($parts > 1) { |
|
| 1001 | + $filterLogin = '(|'; |
|
| 1002 | + } |
|
| 1003 | + $filterLogin .= $filterUsername; |
|
| 1004 | + $filterLogin .= $filterEmail; |
|
| 1005 | + $filterLogin .= $filterAttributes; |
|
| 1006 | + if($parts > 1) { |
|
| 1007 | + $filterLogin .= ')'; |
|
| 1008 | + } |
|
| 1009 | + |
|
| 1010 | + $filter = '(&'.$ulf.$filterLogin.')'; |
|
| 1011 | + break; |
|
| 1012 | + } |
|
| 1013 | + |
|
| 1014 | + \OCP\Util::writeLog('user_ldap', 'Wiz: Final filter '.$filter, \OCP\Util::DEBUG); |
|
| 1015 | + |
|
| 1016 | + return $filter; |
|
| 1017 | + } |
|
| 1018 | + |
|
| 1019 | + /** |
|
| 1020 | + * Connects and Binds to an LDAP Server |
|
| 1021 | + * @param int $port the port to connect with |
|
| 1022 | + * @param bool $tls whether startTLS is to be used |
|
| 1023 | + * @param bool $ncc |
|
| 1024 | + * @return bool |
|
| 1025 | + * @throws \Exception |
|
| 1026 | + */ |
|
| 1027 | + private function connectAndBind($port = 389, $tls = false, $ncc = false) { |
|
| 1028 | + if($ncc) { |
|
| 1029 | + //No certificate check |
|
| 1030 | + //FIXME: undo afterwards |
|
| 1031 | + putenv('LDAPTLS_REQCERT=never'); |
|
| 1032 | + } |
|
| 1033 | + |
|
| 1034 | + //connect, does not really trigger any server communication |
|
| 1035 | + \OCP\Util::writeLog('user_ldap', 'Wiz: Checking Host Info ', \OCP\Util::DEBUG); |
|
| 1036 | + $host = $this->configuration->ldapHost; |
|
| 1037 | + $hostInfo = parse_url($host); |
|
| 1038 | + if(!$hostInfo) { |
|
| 1039 | + throw new \Exception(self::$l->t('Invalid Host')); |
|
| 1040 | + } |
|
| 1041 | + \OCP\Util::writeLog('user_ldap', 'Wiz: Attempting to connect ', \OCP\Util::DEBUG); |
|
| 1042 | + $cr = $this->ldap->connect($host, $port); |
|
| 1043 | + if(!is_resource($cr)) { |
|
| 1044 | + throw new \Exception(self::$l->t('Invalid Host')); |
|
| 1045 | + } |
|
| 1046 | + |
|
| 1047 | + \OCP\Util::writeLog('user_ldap', 'Wiz: Setting LDAP Options ', \OCP\Util::DEBUG); |
|
| 1048 | + //set LDAP options |
|
| 1049 | + $this->ldap->setOption($cr, LDAP_OPT_PROTOCOL_VERSION, 3); |
|
| 1050 | + $this->ldap->setOption($cr, LDAP_OPT_REFERRALS, 0); |
|
| 1051 | + $this->ldap->setOption($cr, LDAP_OPT_NETWORK_TIMEOUT, self::LDAP_NW_TIMEOUT); |
|
| 1052 | + |
|
| 1053 | + try { |
|
| 1054 | + if($tls) { |
|
| 1055 | + $isTlsWorking = @$this->ldap->startTls($cr); |
|
| 1056 | + if(!$isTlsWorking) { |
|
| 1057 | + return false; |
|
| 1058 | + } |
|
| 1059 | + } |
|
| 1060 | + |
|
| 1061 | + \OCP\Util::writeLog('user_ldap', 'Wiz: Attemping to Bind ', \OCP\Util::DEBUG); |
|
| 1062 | + //interesting part: do the bind! |
|
| 1063 | + $login = $this->ldap->bind($cr, |
|
| 1064 | + $this->configuration->ldapAgentName, |
|
| 1065 | + $this->configuration->ldapAgentPassword |
|
| 1066 | + ); |
|
| 1067 | + $errNo = $this->ldap->errno($cr); |
|
| 1068 | + $error = ldap_error($cr); |
|
| 1069 | + $this->ldap->unbind($cr); |
|
| 1070 | + } catch(ServerNotAvailableException $e) { |
|
| 1071 | + return false; |
|
| 1072 | + } |
|
| 1073 | + |
|
| 1074 | + if($login === true) { |
|
| 1075 | + $this->ldap->unbind($cr); |
|
| 1076 | + if($ncc) { |
|
| 1077 | + throw new \Exception('Certificate cannot be validated.'); |
|
| 1078 | + } |
|
| 1079 | + \OCP\Util::writeLog('user_ldap', 'Wiz: Bind successful to Port '. $port . ' TLS ' . intval($tls), \OCP\Util::DEBUG); |
|
| 1080 | + return true; |
|
| 1081 | + } |
|
| 1082 | + |
|
| 1083 | + if($errNo === -1 || ($errNo === 2 && $ncc)) { |
|
| 1084 | + //host, port or TLS wrong |
|
| 1085 | + return false; |
|
| 1086 | + } else if ($errNo === 2) { |
|
| 1087 | + return $this->connectAndBind($port, $tls, true); |
|
| 1088 | + } |
|
| 1089 | + throw new \Exception($error, $errNo); |
|
| 1090 | + } |
|
| 1091 | + |
|
| 1092 | + /** |
|
| 1093 | + * checks whether a valid combination of agent and password has been |
|
| 1094 | + * provided (either two values or nothing for anonymous connect) |
|
| 1095 | + * @return bool, true if everything is fine, false otherwise |
|
| 1096 | + */ |
|
| 1097 | + private function checkAgentRequirements() { |
|
| 1098 | + $agent = $this->configuration->ldapAgentName; |
|
| 1099 | + $pwd = $this->configuration->ldapAgentPassword; |
|
| 1100 | + |
|
| 1101 | + return |
|
| 1102 | + ($agent !== '' && $pwd !== '') |
|
| 1103 | + || ($agent === '' && $pwd === '') |
|
| 1104 | + ; |
|
| 1105 | + } |
|
| 1106 | + |
|
| 1107 | + /** |
|
| 1108 | + * @param array $reqs |
|
| 1109 | + * @return bool |
|
| 1110 | + */ |
|
| 1111 | + private function checkRequirements($reqs) { |
|
| 1112 | + $this->checkAgentRequirements(); |
|
| 1113 | + foreach($reqs as $option) { |
|
| 1114 | + $value = $this->configuration->$option; |
|
| 1115 | + if(empty($value)) { |
|
| 1116 | + return false; |
|
| 1117 | + } |
|
| 1118 | + } |
|
| 1119 | + return true; |
|
| 1120 | + } |
|
| 1121 | + |
|
| 1122 | + /** |
|
| 1123 | + * does a cumulativeSearch on LDAP to get different values of a |
|
| 1124 | + * specified attribute |
|
| 1125 | + * @param string[] $filters array, the filters that shall be used in the search |
|
| 1126 | + * @param string $attr the attribute of which a list of values shall be returned |
|
| 1127 | + * @param int $dnReadLimit the amount of how many DNs should be analyzed. |
|
| 1128 | + * The lower, the faster |
|
| 1129 | + * @param string $maxF string. if not null, this variable will have the filter that |
|
| 1130 | + * yields most result entries |
|
| 1131 | + * @return array|false an array with the values on success, false otherwise |
|
| 1132 | + */ |
|
| 1133 | + public function cumulativeSearchOnAttribute($filters, $attr, $dnReadLimit = 3, &$maxF = null) { |
|
| 1134 | + $dnRead = array(); |
|
| 1135 | + $foundItems = array(); |
|
| 1136 | + $maxEntries = 0; |
|
| 1137 | + if(!is_array($this->configuration->ldapBase) |
|
| 1138 | + || !isset($this->configuration->ldapBase[0])) { |
|
| 1139 | + return false; |
|
| 1140 | + } |
|
| 1141 | + $base = $this->configuration->ldapBase[0]; |
|
| 1142 | + $cr = $this->getConnection(); |
|
| 1143 | + if(!$this->ldap->isResource($cr)) { |
|
| 1144 | + return false; |
|
| 1145 | + } |
|
| 1146 | + $lastFilter = null; |
|
| 1147 | + if(isset($filters[count($filters)-1])) { |
|
| 1148 | + $lastFilter = $filters[count($filters)-1]; |
|
| 1149 | + } |
|
| 1150 | + foreach($filters as $filter) { |
|
| 1151 | + if($lastFilter === $filter && count($foundItems) > 0) { |
|
| 1152 | + //skip when the filter is a wildcard and results were found |
|
| 1153 | + continue; |
|
| 1154 | + } |
|
| 1155 | + // 20k limit for performance and reason |
|
| 1156 | + $rr = $this->ldap->search($cr, $base, $filter, array($attr), 0, 20000); |
|
| 1157 | + if(!$this->ldap->isResource($rr)) { |
|
| 1158 | + continue; |
|
| 1159 | + } |
|
| 1160 | + $entries = $this->ldap->countEntries($cr, $rr); |
|
| 1161 | + $getEntryFunc = 'firstEntry'; |
|
| 1162 | + if(($entries !== false) && ($entries > 0)) { |
|
| 1163 | + if(!is_null($maxF) && $entries > $maxEntries) { |
|
| 1164 | + $maxEntries = $entries; |
|
| 1165 | + $maxF = $filter; |
|
| 1166 | + } |
|
| 1167 | + $dnReadCount = 0; |
|
| 1168 | + do { |
|
| 1169 | + $entry = $this->ldap->$getEntryFunc($cr, $rr); |
|
| 1170 | + $getEntryFunc = 'nextEntry'; |
|
| 1171 | + if(!$this->ldap->isResource($entry)) { |
|
| 1172 | + continue 2; |
|
| 1173 | + } |
|
| 1174 | + $rr = $entry; //will be expected by nextEntry next round |
|
| 1175 | + $attributes = $this->ldap->getAttributes($cr, $entry); |
|
| 1176 | + $dn = $this->ldap->getDN($cr, $entry); |
|
| 1177 | + if($dn === false || in_array($dn, $dnRead)) { |
|
| 1178 | + continue; |
|
| 1179 | + } |
|
| 1180 | + $newItems = array(); |
|
| 1181 | + $state = $this->getAttributeValuesFromEntry($attributes, |
|
| 1182 | + $attr, |
|
| 1183 | + $newItems); |
|
| 1184 | + $dnReadCount++; |
|
| 1185 | + $foundItems = array_merge($foundItems, $newItems); |
|
| 1186 | + $this->resultCache[$dn][$attr] = $newItems; |
|
| 1187 | + $dnRead[] = $dn; |
|
| 1188 | + } while(($state === self::LRESULT_PROCESSED_SKIP |
|
| 1189 | + || $this->ldap->isResource($entry)) |
|
| 1190 | + && ($dnReadLimit === 0 || $dnReadCount < $dnReadLimit)); |
|
| 1191 | + } |
|
| 1192 | + } |
|
| 1193 | + |
|
| 1194 | + return array_unique($foundItems); |
|
| 1195 | + } |
|
| 1196 | + |
|
| 1197 | + /** |
|
| 1198 | + * determines if and which $attr are available on the LDAP server |
|
| 1199 | + * @param string[] $objectclasses the objectclasses to use as search filter |
|
| 1200 | + * @param string $attr the attribute to look for |
|
| 1201 | + * @param string $dbkey the dbkey of the setting the feature is connected to |
|
| 1202 | + * @param string $confkey the confkey counterpart for the $dbkey as used in the |
|
| 1203 | + * Configuration class |
|
| 1204 | + * @param bool $po whether the objectClass with most result entries |
|
| 1205 | + * shall be pre-selected via the result |
|
| 1206 | + * @return array|false list of found items. |
|
| 1207 | + * @throws \Exception |
|
| 1208 | + */ |
|
| 1209 | + private function determineFeature($objectclasses, $attr, $dbkey, $confkey, $po = false) { |
|
| 1210 | + $cr = $this->getConnection(); |
|
| 1211 | + if(!$cr) { |
|
| 1212 | + throw new \Exception('Could not connect to LDAP'); |
|
| 1213 | + } |
|
| 1214 | + $p = 'objectclass='; |
|
| 1215 | + foreach($objectclasses as $key => $value) { |
|
| 1216 | + $objectclasses[$key] = $p.$value; |
|
| 1217 | + } |
|
| 1218 | + $maxEntryObjC = ''; |
|
| 1219 | + |
|
| 1220 | + //how deep to dig? |
|
| 1221 | + //When looking for objectclasses, testing few entries is sufficient, |
|
| 1222 | + $dig = 3; |
|
| 1223 | + |
|
| 1224 | + $availableFeatures = |
|
| 1225 | + $this->cumulativeSearchOnAttribute($objectclasses, $attr, |
|
| 1226 | + $dig, $maxEntryObjC); |
|
| 1227 | + if(is_array($availableFeatures) |
|
| 1228 | + && count($availableFeatures) > 0) { |
|
| 1229 | + natcasesort($availableFeatures); |
|
| 1230 | + //natcasesort keeps indices, but we must get rid of them for proper |
|
| 1231 | + //sorting in the web UI. Therefore: array_values |
|
| 1232 | + $this->result->addOptions($dbkey, array_values($availableFeatures)); |
|
| 1233 | + } else { |
|
| 1234 | + throw new \Exception(self::$l->t('Could not find the desired feature')); |
|
| 1235 | + } |
|
| 1236 | + |
|
| 1237 | + $setFeatures = $this->configuration->$confkey; |
|
| 1238 | + if(is_array($setFeatures) && !empty($setFeatures)) { |
|
| 1239 | + //something is already configured? pre-select it. |
|
| 1240 | + $this->result->addChange($dbkey, $setFeatures); |
|
| 1241 | + } else if ($po && $maxEntryObjC !== '') { |
|
| 1242 | + //pre-select objectclass with most result entries |
|
| 1243 | + $maxEntryObjC = str_replace($p, '', $maxEntryObjC); |
|
| 1244 | + $this->applyFind($dbkey, $maxEntryObjC); |
|
| 1245 | + $this->result->addChange($dbkey, $maxEntryObjC); |
|
| 1246 | + } |
|
| 1247 | + |
|
| 1248 | + return $availableFeatures; |
|
| 1249 | + } |
|
| 1250 | + |
|
| 1251 | + /** |
|
| 1252 | + * appends a list of values fr |
|
| 1253 | + * @param resource $result the return value from ldap_get_attributes |
|
| 1254 | + * @param string $attribute the attribute values to look for |
|
| 1255 | + * @param array &$known new values will be appended here |
|
| 1256 | + * @return int, state on of the class constants LRESULT_PROCESSED_OK, |
|
| 1257 | + * LRESULT_PROCESSED_INVALID or LRESULT_PROCESSED_SKIP |
|
| 1258 | + */ |
|
| 1259 | + private function getAttributeValuesFromEntry($result, $attribute, &$known) { |
|
| 1260 | + if(!is_array($result) |
|
| 1261 | + || !isset($result['count']) |
|
| 1262 | + || !$result['count'] > 0) { |
|
| 1263 | + return self::LRESULT_PROCESSED_INVALID; |
|
| 1264 | + } |
|
| 1265 | + |
|
| 1266 | + // strtolower on all keys for proper comparison |
|
| 1267 | + $result = \OCP\Util::mb_array_change_key_case($result); |
|
| 1268 | + $attribute = strtolower($attribute); |
|
| 1269 | + if(isset($result[$attribute])) { |
|
| 1270 | + foreach($result[$attribute] as $key => $val) { |
|
| 1271 | + if($key === 'count') { |
|
| 1272 | + continue; |
|
| 1273 | + } |
|
| 1274 | + if(!in_array($val, $known)) { |
|
| 1275 | + $known[] = $val; |
|
| 1276 | + } |
|
| 1277 | + } |
|
| 1278 | + return self::LRESULT_PROCESSED_OK; |
|
| 1279 | + } else { |
|
| 1280 | + return self::LRESULT_PROCESSED_SKIP; |
|
| 1281 | + } |
|
| 1282 | + } |
|
| 1283 | + |
|
| 1284 | + /** |
|
| 1285 | + * @return bool|mixed |
|
| 1286 | + */ |
|
| 1287 | + private function getConnection() { |
|
| 1288 | + if(!is_null($this->cr)) { |
|
| 1289 | + return $this->cr; |
|
| 1290 | + } |
|
| 1291 | + |
|
| 1292 | + $cr = $this->ldap->connect( |
|
| 1293 | + $this->configuration->ldapHost, |
|
| 1294 | + $this->configuration->ldapPort |
|
| 1295 | + ); |
|
| 1296 | + |
|
| 1297 | + $this->ldap->setOption($cr, LDAP_OPT_PROTOCOL_VERSION, 3); |
|
| 1298 | + $this->ldap->setOption($cr, LDAP_OPT_REFERRALS, 0); |
|
| 1299 | + $this->ldap->setOption($cr, LDAP_OPT_NETWORK_TIMEOUT, self::LDAP_NW_TIMEOUT); |
|
| 1300 | + if($this->configuration->ldapTLS === 1) { |
|
| 1301 | + $this->ldap->startTls($cr); |
|
| 1302 | + } |
|
| 1303 | + |
|
| 1304 | + $lo = @$this->ldap->bind($cr, |
|
| 1305 | + $this->configuration->ldapAgentName, |
|
| 1306 | + $this->configuration->ldapAgentPassword); |
|
| 1307 | + if($lo === true) { |
|
| 1308 | + $this->$cr = $cr; |
|
| 1309 | + return $cr; |
|
| 1310 | + } |
|
| 1311 | + |
|
| 1312 | + return false; |
|
| 1313 | + } |
|
| 1314 | + |
|
| 1315 | + /** |
|
| 1316 | + * @return array |
|
| 1317 | + */ |
|
| 1318 | + private function getDefaultLdapPortSettings() { |
|
| 1319 | + static $settings = array( |
|
| 1320 | + array('port' => 7636, 'tls' => false), |
|
| 1321 | + array('port' => 636, 'tls' => false), |
|
| 1322 | + array('port' => 7389, 'tls' => true), |
|
| 1323 | + array('port' => 389, 'tls' => true), |
|
| 1324 | + array('port' => 7389, 'tls' => false), |
|
| 1325 | + array('port' => 389, 'tls' => false), |
|
| 1326 | + ); |
|
| 1327 | + return $settings; |
|
| 1328 | + } |
|
| 1329 | + |
|
| 1330 | + /** |
|
| 1331 | + * @return array |
|
| 1332 | + */ |
|
| 1333 | + private function getPortSettingsToTry() { |
|
| 1334 | + //389 ← LDAP / Unencrypted or StartTLS |
|
| 1335 | + //636 ← LDAPS / SSL |
|
| 1336 | + //7xxx ← UCS. need to be checked first, because both ports may be open |
|
| 1337 | + $host = $this->configuration->ldapHost; |
|
| 1338 | + $port = intval($this->configuration->ldapPort); |
|
| 1339 | + $portSettings = array(); |
|
| 1340 | + |
|
| 1341 | + //In case the port is already provided, we will check this first |
|
| 1342 | + if($port > 0) { |
|
| 1343 | + $hostInfo = parse_url($host); |
|
| 1344 | + if(!(is_array($hostInfo) |
|
| 1345 | + && isset($hostInfo['scheme']) |
|
| 1346 | + && stripos($hostInfo['scheme'], 'ldaps') !== false)) { |
|
| 1347 | + $portSettings[] = array('port' => $port, 'tls' => true); |
|
| 1348 | + } |
|
| 1349 | + $portSettings[] =array('port' => $port, 'tls' => false); |
|
| 1350 | + } |
|
| 1351 | + |
|
| 1352 | + //default ports |
|
| 1353 | + $portSettings = array_merge($portSettings, |
|
| 1354 | + $this->getDefaultLdapPortSettings()); |
|
| 1355 | + |
|
| 1356 | + return $portSettings; |
|
| 1357 | + } |
|
| 1358 | 1358 | |
| 1359 | 1359 | |
| 1360 | 1360 | } |
@@ -67,7 +67,7 @@ discard block |
||
| 67 | 67 | public function __construct(Configuration $configuration, ILDAPWrapper $ldap, Access $access) { |
| 68 | 68 | parent::__construct($ldap); |
| 69 | 69 | $this->configuration = $configuration; |
| 70 | - if(is_null(Wizard::$l)) { |
|
| 70 | + if (is_null(Wizard::$l)) { |
|
| 71 | 71 | Wizard::$l = \OC::$server->getL10N('user_ldap'); |
| 72 | 72 | } |
| 73 | 73 | $this->access = $access; |
@@ -75,7 +75,7 @@ discard block |
||
| 75 | 75 | } |
| 76 | 76 | |
| 77 | 77 | public function __destruct() { |
| 78 | - if($this->result->hasChanges()) { |
|
| 78 | + if ($this->result->hasChanges()) { |
|
| 79 | 79 | $this->configuration->saveConfiguration(); |
| 80 | 80 | } |
| 81 | 81 | } |
@@ -90,18 +90,18 @@ discard block |
||
| 90 | 90 | */ |
| 91 | 91 | public function countEntries($filter, $type) { |
| 92 | 92 | $reqs = array('ldapHost', 'ldapPort', 'ldapBase'); |
| 93 | - if($type === 'users') { |
|
| 93 | + if ($type === 'users') { |
|
| 94 | 94 | $reqs[] = 'ldapUserFilter'; |
| 95 | 95 | } |
| 96 | - if(!$this->checkRequirements($reqs)) { |
|
| 96 | + if (!$this->checkRequirements($reqs)) { |
|
| 97 | 97 | throw new \Exception('Requirements not met', 400); |
| 98 | 98 | } |
| 99 | 99 | |
| 100 | 100 | $attr = array('dn'); // default |
| 101 | 101 | $limit = 1001; |
| 102 | - if($type === 'groups') { |
|
| 103 | - $result = $this->access->countGroups($filter, $attr, $limit); |
|
| 104 | - } else if($type === 'users') { |
|
| 102 | + if ($type === 'groups') { |
|
| 103 | + $result = $this->access->countGroups($filter, $attr, $limit); |
|
| 104 | + } else if ($type === 'users') { |
|
| 105 | 105 | $result = $this->access->countUsers($filter, $attr, $limit); |
| 106 | 106 | } else if ($type === 'objects') { |
| 107 | 107 | $result = $this->access->countObjects($limit); |
@@ -121,7 +121,7 @@ discard block |
||
| 121 | 121 | */ |
| 122 | 122 | private function formatCountResult($count) { |
| 123 | 123 | $formatted = ($count !== false) ? $count : 0; |
| 124 | - if($formatted > 1000) { |
|
| 124 | + if ($formatted > 1000) { |
|
| 125 | 125 | $formatted = '> 1000'; |
| 126 | 126 | } |
| 127 | 127 | return $formatted; |
@@ -130,7 +130,7 @@ discard block |
||
| 130 | 130 | public function countGroups() { |
| 131 | 131 | $filter = $this->configuration->ldapGroupFilter; |
| 132 | 132 | |
| 133 | - if(empty($filter)) { |
|
| 133 | + if (empty($filter)) { |
|
| 134 | 134 | $output = self::$l->n('%s group found', '%s groups found', 0, array(0)); |
| 135 | 135 | $this->result->addChange('ldap_group_count', $output); |
| 136 | 136 | return $this->result; |
@@ -140,7 +140,7 @@ discard block |
||
| 140 | 140 | $groupsTotal = $this->formatCountResult($this->countEntries($filter, 'groups')); |
| 141 | 141 | } catch (\Exception $e) { |
| 142 | 142 | //400 can be ignored, 500 is forwarded |
| 143 | - if($e->getCode() === 500) { |
|
| 143 | + if ($e->getCode() === 500) { |
|
| 144 | 144 | throw $e; |
| 145 | 145 | } |
| 146 | 146 | return false; |
@@ -172,7 +172,7 @@ discard block |
||
| 172 | 172 | public function countInBaseDN() { |
| 173 | 173 | // we don't need to provide a filter in this case |
| 174 | 174 | $total = $this->countEntries(null, 'objects'); |
| 175 | - if($total === false) { |
|
| 175 | + if ($total === false) { |
|
| 176 | 176 | throw new \Exception('invalid results received'); |
| 177 | 177 | } |
| 178 | 178 | $this->result->addChange('ldap_test_base', $total); |
@@ -186,7 +186,7 @@ discard block |
||
| 186 | 186 | * @return int|bool |
| 187 | 187 | */ |
| 188 | 188 | public function countUsersWithAttribute($attr, $existsCheck = false) { |
| 189 | - if(!$this->checkRequirements(array('ldapHost', |
|
| 189 | + if (!$this->checkRequirements(array('ldapHost', |
|
| 190 | 190 | 'ldapPort', |
| 191 | 191 | 'ldapBase', |
| 192 | 192 | 'ldapUserFilter', |
@@ -196,7 +196,7 @@ discard block |
||
| 196 | 196 | |
| 197 | 197 | $filter = $this->access->combineFilterWithAnd(array( |
| 198 | 198 | $this->configuration->ldapUserFilter, |
| 199 | - $attr . '=*' |
|
| 199 | + $attr.'=*' |
|
| 200 | 200 | )); |
| 201 | 201 | |
| 202 | 202 | $limit = ($existsCheck === false) ? null : 1; |
@@ -211,7 +211,7 @@ discard block |
||
| 211 | 211 | * @throws \Exception |
| 212 | 212 | */ |
| 213 | 213 | public function detectUserDisplayNameAttribute() { |
| 214 | - if(!$this->checkRequirements(array('ldapHost', |
|
| 214 | + if (!$this->checkRequirements(array('ldapHost', |
|
| 215 | 215 | 'ldapPort', |
| 216 | 216 | 'ldapBase', |
| 217 | 217 | 'ldapUserFilter', |
@@ -224,7 +224,7 @@ discard block |
||
| 224 | 224 | // most likely not the default value with upper case N, |
| 225 | 225 | // verify it still produces a result |
| 226 | 226 | $count = intval($this->countUsersWithAttribute($attr, true)); |
| 227 | - if($count > 0) { |
|
| 227 | + if ($count > 0) { |
|
| 228 | 228 | //no change, but we sent it back to make sure the user interface |
| 229 | 229 | //is still correct, even if the ajax call was cancelled meanwhile |
| 230 | 230 | $this->result->addChange('ldap_display_name', $attr); |
@@ -237,7 +237,7 @@ discard block |
||
| 237 | 237 | foreach ($displayNameAttrs as $attr) { |
| 238 | 238 | $count = intval($this->countUsersWithAttribute($attr, true)); |
| 239 | 239 | |
| 240 | - if($count > 0) { |
|
| 240 | + if ($count > 0) { |
|
| 241 | 241 | $this->applyFind('ldap_display_name', $attr); |
| 242 | 242 | return $this->result; |
| 243 | 243 | } |
@@ -253,7 +253,7 @@ discard block |
||
| 253 | 253 | * @return WizardResult|bool |
| 254 | 254 | */ |
| 255 | 255 | public function detectEmailAttribute() { |
| 256 | - if(!$this->checkRequirements(array('ldapHost', |
|
| 256 | + if (!$this->checkRequirements(array('ldapHost', |
|
| 257 | 257 | 'ldapPort', |
| 258 | 258 | 'ldapBase', |
| 259 | 259 | 'ldapUserFilter', |
@@ -264,7 +264,7 @@ discard block |
||
| 264 | 264 | $attr = $this->configuration->ldapEmailAttribute; |
| 265 | 265 | if ($attr !== '') { |
| 266 | 266 | $count = intval($this->countUsersWithAttribute($attr, true)); |
| 267 | - if($count > 0) { |
|
| 267 | + if ($count > 0) { |
|
| 268 | 268 | return false; |
| 269 | 269 | } |
| 270 | 270 | $writeLog = true; |
@@ -275,19 +275,19 @@ discard block |
||
| 275 | 275 | $emailAttributes = array('mail', 'mailPrimaryAddress'); |
| 276 | 276 | $winner = ''; |
| 277 | 277 | $maxUsers = 0; |
| 278 | - foreach($emailAttributes as $attr) { |
|
| 278 | + foreach ($emailAttributes as $attr) { |
|
| 279 | 279 | $count = $this->countUsersWithAttribute($attr); |
| 280 | - if($count > $maxUsers) { |
|
| 280 | + if ($count > $maxUsers) { |
|
| 281 | 281 | $maxUsers = $count; |
| 282 | 282 | $winner = $attr; |
| 283 | 283 | } |
| 284 | 284 | } |
| 285 | 285 | |
| 286 | - if($winner !== '') { |
|
| 286 | + if ($winner !== '') { |
|
| 287 | 287 | $this->applyFind('ldap_email_attr', $winner); |
| 288 | - if($writeLog) { |
|
| 289 | - \OCP\Util::writeLog('user_ldap', 'The mail attribute has ' . |
|
| 290 | - 'automatically been reset, because the original value ' . |
|
| 288 | + if ($writeLog) { |
|
| 289 | + \OCP\Util::writeLog('user_ldap', 'The mail attribute has '. |
|
| 290 | + 'automatically been reset, because the original value '. |
|
| 291 | 291 | 'did not return any results.', \OCP\Util::INFO); |
| 292 | 292 | } |
| 293 | 293 | } |
@@ -300,7 +300,7 @@ discard block |
||
| 300 | 300 | * @throws \Exception |
| 301 | 301 | */ |
| 302 | 302 | public function determineAttributes() { |
| 303 | - if(!$this->checkRequirements(array('ldapHost', |
|
| 303 | + if (!$this->checkRequirements(array('ldapHost', |
|
| 304 | 304 | 'ldapPort', |
| 305 | 305 | 'ldapBase', |
| 306 | 306 | 'ldapUserFilter', |
@@ -316,7 +316,7 @@ discard block |
||
| 316 | 316 | $this->result->addOptions('ldap_loginfilter_attributes', $attributes); |
| 317 | 317 | |
| 318 | 318 | $selected = $this->configuration->ldapLoginFilterAttributes; |
| 319 | - if(is_array($selected) && !empty($selected)) { |
|
| 319 | + if (is_array($selected) && !empty($selected)) { |
|
| 320 | 320 | $this->result->addChange('ldap_loginfilter_attributes', $selected); |
| 321 | 321 | } |
| 322 | 322 | |
@@ -329,7 +329,7 @@ discard block |
||
| 329 | 329 | * @throws \Exception |
| 330 | 330 | */ |
| 331 | 331 | private function getUserAttributes() { |
| 332 | - if(!$this->checkRequirements(array('ldapHost', |
|
| 332 | + if (!$this->checkRequirements(array('ldapHost', |
|
| 333 | 333 | 'ldapPort', |
| 334 | 334 | 'ldapBase', |
| 335 | 335 | 'ldapUserFilter', |
@@ -337,20 +337,20 @@ discard block |
||
| 337 | 337 | return false; |
| 338 | 338 | } |
| 339 | 339 | $cr = $this->getConnection(); |
| 340 | - if(!$cr) { |
|
| 340 | + if (!$cr) { |
|
| 341 | 341 | throw new \Exception('Could not connect to LDAP'); |
| 342 | 342 | } |
| 343 | 343 | |
| 344 | 344 | $base = $this->configuration->ldapBase[0]; |
| 345 | 345 | $filter = $this->configuration->ldapUserFilter; |
| 346 | 346 | $rr = $this->ldap->search($cr, $base, $filter, array(), 1, 1); |
| 347 | - if(!$this->ldap->isResource($rr)) { |
|
| 347 | + if (!$this->ldap->isResource($rr)) { |
|
| 348 | 348 | return false; |
| 349 | 349 | } |
| 350 | 350 | $er = $this->ldap->firstEntry($cr, $rr); |
| 351 | 351 | $attributes = $this->ldap->getAttributes($cr, $er); |
| 352 | 352 | $pureAttributes = array(); |
| 353 | - for($i = 0; $i < $attributes['count']; $i++) { |
|
| 353 | + for ($i = 0; $i < $attributes['count']; $i++) { |
|
| 354 | 354 | $pureAttributes[] = $attributes[$i]; |
| 355 | 355 | } |
| 356 | 356 | |
@@ -385,23 +385,23 @@ discard block |
||
| 385 | 385 | * @throws \Exception |
| 386 | 386 | */ |
| 387 | 387 | private function determineGroups($dbKey, $confKey, $testMemberOf = true) { |
| 388 | - if(!$this->checkRequirements(array('ldapHost', |
|
| 388 | + if (!$this->checkRequirements(array('ldapHost', |
|
| 389 | 389 | 'ldapPort', |
| 390 | 390 | 'ldapBase', |
| 391 | 391 | ))) { |
| 392 | 392 | return false; |
| 393 | 393 | } |
| 394 | 394 | $cr = $this->getConnection(); |
| 395 | - if(!$cr) { |
|
| 395 | + if (!$cr) { |
|
| 396 | 396 | throw new \Exception('Could not connect to LDAP'); |
| 397 | 397 | } |
| 398 | 398 | |
| 399 | 399 | $this->fetchGroups($dbKey, $confKey); |
| 400 | 400 | |
| 401 | - if($testMemberOf) { |
|
| 401 | + if ($testMemberOf) { |
|
| 402 | 402 | $this->configuration->hasMemberOfFilterSupport = $this->testMemberOf(); |
| 403 | 403 | $this->result->markChange(); |
| 404 | - if(!$this->configuration->hasMemberOfFilterSupport) { |
|
| 404 | + if (!$this->configuration->hasMemberOfFilterSupport) { |
|
| 405 | 405 | throw new \Exception('memberOf is not supported by the server'); |
| 406 | 406 | } |
| 407 | 407 | } |
@@ -421,7 +421,7 @@ discard block |
||
| 421 | 421 | $obclasses = array('posixGroup', 'group', 'zimbraDistributionList', 'groupOfNames'); |
| 422 | 422 | |
| 423 | 423 | $filterParts = array(); |
| 424 | - foreach($obclasses as $obclass) { |
|
| 424 | + foreach ($obclasses as $obclass) { |
|
| 425 | 425 | $filterParts[] = 'objectclass='.$obclass; |
| 426 | 426 | } |
| 427 | 427 | //we filter for everything |
@@ -438,8 +438,8 @@ discard block |
||
| 438 | 438 | // we need to request dn additionally here, otherwise memberOf |
| 439 | 439 | // detection will fail later |
| 440 | 440 | $result = $this->access->searchGroups($filter, array('cn', 'dn'), $limit, $offset); |
| 441 | - foreach($result as $item) { |
|
| 442 | - if(!isset($item['cn']) && !is_array($item['cn']) && !isset($item['cn'][0])) { |
|
| 441 | + foreach ($result as $item) { |
|
| 442 | + if (!isset($item['cn']) && !is_array($item['cn']) && !isset($item['cn'][0])) { |
|
| 443 | 443 | // just in case - no issue known |
| 444 | 444 | continue; |
| 445 | 445 | } |
@@ -449,7 +449,7 @@ discard block |
||
| 449 | 449 | $offset += $limit; |
| 450 | 450 | } while ($this->access->hasMoreResults()); |
| 451 | 451 | |
| 452 | - if(count($groupNames) > 0) { |
|
| 452 | + if (count($groupNames) > 0) { |
|
| 453 | 453 | natsort($groupNames); |
| 454 | 454 | $this->result->addOptions($dbKey, array_values($groupNames)); |
| 455 | 455 | } else { |
@@ -457,7 +457,7 @@ discard block |
||
| 457 | 457 | } |
| 458 | 458 | |
| 459 | 459 | $setFeatures = $this->configuration->$confKey; |
| 460 | - if(is_array($setFeatures) && !empty($setFeatures)) { |
|
| 460 | + if (is_array($setFeatures) && !empty($setFeatures)) { |
|
| 461 | 461 | //something is already configured? pre-select it. |
| 462 | 462 | $this->result->addChange($dbKey, $setFeatures); |
| 463 | 463 | } |
@@ -465,14 +465,14 @@ discard block |
||
| 465 | 465 | } |
| 466 | 466 | |
| 467 | 467 | public function determineGroupMemberAssoc() { |
| 468 | - if(!$this->checkRequirements(array('ldapHost', |
|
| 468 | + if (!$this->checkRequirements(array('ldapHost', |
|
| 469 | 469 | 'ldapPort', |
| 470 | 470 | 'ldapGroupFilter', |
| 471 | 471 | ))) { |
| 472 | 472 | return false; |
| 473 | 473 | } |
| 474 | 474 | $attribute = $this->detectGroupMemberAssoc(); |
| 475 | - if($attribute === false) { |
|
| 475 | + if ($attribute === false) { |
|
| 476 | 476 | return false; |
| 477 | 477 | } |
| 478 | 478 | $this->configuration->setConfiguration(array('ldapGroupMemberAssocAttr' => $attribute)); |
@@ -487,14 +487,14 @@ discard block |
||
| 487 | 487 | * @throws \Exception |
| 488 | 488 | */ |
| 489 | 489 | public function determineGroupObjectClasses() { |
| 490 | - if(!$this->checkRequirements(array('ldapHost', |
|
| 490 | + if (!$this->checkRequirements(array('ldapHost', |
|
| 491 | 491 | 'ldapPort', |
| 492 | 492 | 'ldapBase', |
| 493 | 493 | ))) { |
| 494 | 494 | return false; |
| 495 | 495 | } |
| 496 | 496 | $cr = $this->getConnection(); |
| 497 | - if(!$cr) { |
|
| 497 | + if (!$cr) { |
|
| 498 | 498 | throw new \Exception('Could not connect to LDAP'); |
| 499 | 499 | } |
| 500 | 500 | |
@@ -514,14 +514,14 @@ discard block |
||
| 514 | 514 | * @throws \Exception |
| 515 | 515 | */ |
| 516 | 516 | public function determineUserObjectClasses() { |
| 517 | - if(!$this->checkRequirements(array('ldapHost', |
|
| 517 | + if (!$this->checkRequirements(array('ldapHost', |
|
| 518 | 518 | 'ldapPort', |
| 519 | 519 | 'ldapBase', |
| 520 | 520 | ))) { |
| 521 | 521 | return false; |
| 522 | 522 | } |
| 523 | 523 | $cr = $this->getConnection(); |
| 524 | - if(!$cr) { |
|
| 524 | + if (!$cr) { |
|
| 525 | 525 | throw new \Exception('Could not connect to LDAP'); |
| 526 | 526 | } |
| 527 | 527 | |
@@ -544,7 +544,7 @@ discard block |
||
| 544 | 544 | * @throws \Exception |
| 545 | 545 | */ |
| 546 | 546 | public function getGroupFilter() { |
| 547 | - if(!$this->checkRequirements(array('ldapHost', |
|
| 547 | + if (!$this->checkRequirements(array('ldapHost', |
|
| 548 | 548 | 'ldapPort', |
| 549 | 549 | 'ldapBase', |
| 550 | 550 | ))) { |
@@ -568,7 +568,7 @@ discard block |
||
| 568 | 568 | * @throws \Exception |
| 569 | 569 | */ |
| 570 | 570 | public function getUserListFilter() { |
| 571 | - if(!$this->checkRequirements(array('ldapHost', |
|
| 571 | + if (!$this->checkRequirements(array('ldapHost', |
|
| 572 | 572 | 'ldapPort', |
| 573 | 573 | 'ldapBase', |
| 574 | 574 | ))) { |
@@ -581,7 +581,7 @@ discard block |
||
| 581 | 581 | $this->applyFind('ldap_display_name', $d['ldap_display_name']); |
| 582 | 582 | } |
| 583 | 583 | $filter = $this->composeLdapFilter(self::LFILTER_USER_LIST); |
| 584 | - if(!$filter) { |
|
| 584 | + if (!$filter) { |
|
| 585 | 585 | throw new \Exception('Cannot create filter'); |
| 586 | 586 | } |
| 587 | 587 | |
@@ -594,7 +594,7 @@ discard block |
||
| 594 | 594 | * @throws \Exception |
| 595 | 595 | */ |
| 596 | 596 | public function getUserLoginFilter() { |
| 597 | - if(!$this->checkRequirements(array('ldapHost', |
|
| 597 | + if (!$this->checkRequirements(array('ldapHost', |
|
| 598 | 598 | 'ldapPort', |
| 599 | 599 | 'ldapBase', |
| 600 | 600 | 'ldapUserFilter', |
@@ -603,7 +603,7 @@ discard block |
||
| 603 | 603 | } |
| 604 | 604 | |
| 605 | 605 | $filter = $this->composeLdapFilter(self::LFILTER_LOGIN); |
| 606 | - if(!$filter) { |
|
| 606 | + if (!$filter) { |
|
| 607 | 607 | throw new \Exception('Cannot create filter'); |
| 608 | 608 | } |
| 609 | 609 | |
@@ -617,7 +617,7 @@ discard block |
||
| 617 | 617 | * @throws \Exception |
| 618 | 618 | */ |
| 619 | 619 | public function testLoginName($loginName) { |
| 620 | - if(!$this->checkRequirements(array('ldapHost', |
|
| 620 | + if (!$this->checkRequirements(array('ldapHost', |
|
| 621 | 621 | 'ldapPort', |
| 622 | 622 | 'ldapBase', |
| 623 | 623 | 'ldapLoginFilter', |
@@ -626,17 +626,17 @@ discard block |
||
| 626 | 626 | } |
| 627 | 627 | |
| 628 | 628 | $cr = $this->access->connection->getConnectionResource(); |
| 629 | - if(!$this->ldap->isResource($cr)) { |
|
| 629 | + if (!$this->ldap->isResource($cr)) { |
|
| 630 | 630 | throw new \Exception('connection error'); |
| 631 | 631 | } |
| 632 | 632 | |
| 633 | - if(mb_strpos($this->access->connection->ldapLoginFilter, '%uid', 0, 'UTF-8') |
|
| 633 | + if (mb_strpos($this->access->connection->ldapLoginFilter, '%uid', 0, 'UTF-8') |
|
| 634 | 634 | === false) { |
| 635 | 635 | throw new \Exception('missing placeholder'); |
| 636 | 636 | } |
| 637 | 637 | |
| 638 | 638 | $users = $this->access->countUsersByLoginName($loginName); |
| 639 | - if($this->ldap->errno($cr) !== 0) { |
|
| 639 | + if ($this->ldap->errno($cr) !== 0) { |
|
| 640 | 640 | throw new \Exception($this->ldap->error($cr)); |
| 641 | 641 | } |
| 642 | 642 | $filter = str_replace('%uid', $loginName, $this->access->connection->ldapLoginFilter); |
@@ -651,22 +651,22 @@ discard block |
||
| 651 | 651 | * @throws \Exception |
| 652 | 652 | */ |
| 653 | 653 | public function guessPortAndTLS() { |
| 654 | - if(!$this->checkRequirements(array('ldapHost', |
|
| 654 | + if (!$this->checkRequirements(array('ldapHost', |
|
| 655 | 655 | ))) { |
| 656 | 656 | return false; |
| 657 | 657 | } |
| 658 | 658 | $this->checkHost(); |
| 659 | 659 | $portSettings = $this->getPortSettingsToTry(); |
| 660 | 660 | |
| 661 | - if(!is_array($portSettings)) { |
|
| 661 | + if (!is_array($portSettings)) { |
|
| 662 | 662 | throw new \Exception(print_r($portSettings, true)); |
| 663 | 663 | } |
| 664 | 664 | |
| 665 | 665 | //proceed from the best configuration and return on first success |
| 666 | - foreach($portSettings as $setting) { |
|
| 666 | + foreach ($portSettings as $setting) { |
|
| 667 | 667 | $p = $setting['port']; |
| 668 | 668 | $t = $setting['tls']; |
| 669 | - \OCP\Util::writeLog('user_ldap', 'Wiz: trying port '. $p . ', TLS '. $t, \OCP\Util::DEBUG); |
|
| 669 | + \OCP\Util::writeLog('user_ldap', 'Wiz: trying port '.$p.', TLS '.$t, \OCP\Util::DEBUG); |
|
| 670 | 670 | //connectAndBind may throw Exception, it needs to be catched by the |
| 671 | 671 | //callee of this method |
| 672 | 672 | |
@@ -676,7 +676,7 @@ discard block |
||
| 676 | 676 | // any reply other than -1 (= cannot connect) is already okay, |
| 677 | 677 | // because then we found the server |
| 678 | 678 | // unavailable startTLS returns -11 |
| 679 | - if($e->getCode() > 0) { |
|
| 679 | + if ($e->getCode() > 0) { |
|
| 680 | 680 | $settingsFound = true; |
| 681 | 681 | } else { |
| 682 | 682 | throw $e; |
@@ -689,7 +689,7 @@ discard block |
||
| 689 | 689 | 'ldapTLS' => intval($t) |
| 690 | 690 | ); |
| 691 | 691 | $this->configuration->setConfiguration($config); |
| 692 | - \OCP\Util::writeLog('user_ldap', 'Wiz: detected Port ' . $p, \OCP\Util::DEBUG); |
|
| 692 | + \OCP\Util::writeLog('user_ldap', 'Wiz: detected Port '.$p, \OCP\Util::DEBUG); |
|
| 693 | 693 | $this->result->addChange('ldap_port', $p); |
| 694 | 694 | return $this->result; |
| 695 | 695 | } |
@@ -704,7 +704,7 @@ discard block |
||
| 704 | 704 | * @return WizardResult|false WizardResult on success, false otherwise |
| 705 | 705 | */ |
| 706 | 706 | public function guessBaseDN() { |
| 707 | - if(!$this->checkRequirements(array('ldapHost', |
|
| 707 | + if (!$this->checkRequirements(array('ldapHost', |
|
| 708 | 708 | 'ldapPort', |
| 709 | 709 | ))) { |
| 710 | 710 | return false; |
@@ -713,9 +713,9 @@ discard block |
||
| 713 | 713 | //check whether a DN is given in the agent name (99.9% of all cases) |
| 714 | 714 | $base = null; |
| 715 | 715 | $i = stripos($this->configuration->ldapAgentName, 'dc='); |
| 716 | - if($i !== false) { |
|
| 716 | + if ($i !== false) { |
|
| 717 | 717 | $base = substr($this->configuration->ldapAgentName, $i); |
| 718 | - if($this->testBaseDN($base)) { |
|
| 718 | + if ($this->testBaseDN($base)) { |
|
| 719 | 719 | $this->applyFind('ldap_base', $base); |
| 720 | 720 | return $this->result; |
| 721 | 721 | } |
@@ -726,13 +726,13 @@ discard block |
||
| 726 | 726 | //a base DN |
| 727 | 727 | $helper = new Helper(\OC::$server->getConfig()); |
| 728 | 728 | $domain = $helper->getDomainFromURL($this->configuration->ldapHost); |
| 729 | - if(!$domain) { |
|
| 729 | + if (!$domain) { |
|
| 730 | 730 | return false; |
| 731 | 731 | } |
| 732 | 732 | |
| 733 | 733 | $dparts = explode('.', $domain); |
| 734 | - while(count($dparts) > 0) { |
|
| 735 | - $base2 = 'dc=' . implode(',dc=', $dparts); |
|
| 734 | + while (count($dparts) > 0) { |
|
| 735 | + $base2 = 'dc='.implode(',dc=', $dparts); |
|
| 736 | 736 | if ($base !== $base2 && $this->testBaseDN($base2)) { |
| 737 | 737 | $this->applyFind('ldap_base', $base2); |
| 738 | 738 | return $this->result; |
@@ -765,7 +765,7 @@ discard block |
||
| 765 | 765 | $hostInfo = parse_url($host); |
| 766 | 766 | |
| 767 | 767 | //removes Port from Host |
| 768 | - if(is_array($hostInfo) && isset($hostInfo['port'])) { |
|
| 768 | + if (is_array($hostInfo) && isset($hostInfo['port'])) { |
|
| 769 | 769 | $port = $hostInfo['port']; |
| 770 | 770 | $host = str_replace(':'.$port, '', $host); |
| 771 | 771 | $this->applyFind('ldap_host', $host); |
@@ -782,30 +782,30 @@ discard block |
||
| 782 | 782 | private function detectGroupMemberAssoc() { |
| 783 | 783 | $possibleAttrs = array('uniqueMember', 'memberUid', 'member'); |
| 784 | 784 | $filter = $this->configuration->ldapGroupFilter; |
| 785 | - if(empty($filter)) { |
|
| 785 | + if (empty($filter)) { |
|
| 786 | 786 | return false; |
| 787 | 787 | } |
| 788 | 788 | $cr = $this->getConnection(); |
| 789 | - if(!$cr) { |
|
| 789 | + if (!$cr) { |
|
| 790 | 790 | throw new \Exception('Could not connect to LDAP'); |
| 791 | 791 | } |
| 792 | 792 | $base = $this->configuration->ldapBase[0]; |
| 793 | 793 | $rr = $this->ldap->search($cr, $base, $filter, $possibleAttrs, 0, 1000); |
| 794 | - if(!$this->ldap->isResource($rr)) { |
|
| 794 | + if (!$this->ldap->isResource($rr)) { |
|
| 795 | 795 | return false; |
| 796 | 796 | } |
| 797 | 797 | $er = $this->ldap->firstEntry($cr, $rr); |
| 798 | - while(is_resource($er)) { |
|
| 798 | + while (is_resource($er)) { |
|
| 799 | 799 | $this->ldap->getDN($cr, $er); |
| 800 | 800 | $attrs = $this->ldap->getAttributes($cr, $er); |
| 801 | 801 | $result = array(); |
| 802 | 802 | $possibleAttrsCount = count($possibleAttrs); |
| 803 | - for($i = 0; $i < $possibleAttrsCount; $i++) { |
|
| 804 | - if(isset($attrs[$possibleAttrs[$i]])) { |
|
| 803 | + for ($i = 0; $i < $possibleAttrsCount; $i++) { |
|
| 804 | + if (isset($attrs[$possibleAttrs[$i]])) { |
|
| 805 | 805 | $result[$possibleAttrs[$i]] = $attrs[$possibleAttrs[$i]]['count']; |
| 806 | 806 | } |
| 807 | 807 | } |
| 808 | - if(!empty($result)) { |
|
| 808 | + if (!empty($result)) { |
|
| 809 | 809 | natsort($result); |
| 810 | 810 | return key($result); |
| 811 | 811 | } |
@@ -824,14 +824,14 @@ discard block |
||
| 824 | 824 | */ |
| 825 | 825 | private function testBaseDN($base) { |
| 826 | 826 | $cr = $this->getConnection(); |
| 827 | - if(!$cr) { |
|
| 827 | + if (!$cr) { |
|
| 828 | 828 | throw new \Exception('Could not connect to LDAP'); |
| 829 | 829 | } |
| 830 | 830 | |
| 831 | 831 | //base is there, let's validate it. If we search for anything, we should |
| 832 | 832 | //get a result set > 0 on a proper base |
| 833 | 833 | $rr = $this->ldap->search($cr, $base, 'objectClass=*', array('dn'), 0, 1); |
| 834 | - if(!$this->ldap->isResource($rr)) { |
|
| 834 | + if (!$this->ldap->isResource($rr)) { |
|
| 835 | 835 | $errorNo = $this->ldap->errno($cr); |
| 836 | 836 | $errorMsg = $this->ldap->error($cr); |
| 837 | 837 | \OCP\Util::writeLog('user_ldap', 'Wiz: Could not search base '.$base. |
@@ -853,11 +853,11 @@ discard block |
||
| 853 | 853 | */ |
| 854 | 854 | private function testMemberOf() { |
| 855 | 855 | $cr = $this->getConnection(); |
| 856 | - if(!$cr) { |
|
| 856 | + if (!$cr) { |
|
| 857 | 857 | throw new \Exception('Could not connect to LDAP'); |
| 858 | 858 | } |
| 859 | 859 | $result = $this->access->countUsers('memberOf=*', array('memberOf'), 1); |
| 860 | - if(is_int($result) && $result > 0) { |
|
| 860 | + if (is_int($result) && $result > 0) { |
|
| 861 | 861 | return true; |
| 862 | 862 | } |
| 863 | 863 | return false; |
@@ -878,27 +878,27 @@ discard block |
||
| 878 | 878 | case self::LFILTER_USER_LIST: |
| 879 | 879 | $objcs = $this->configuration->ldapUserFilterObjectclass; |
| 880 | 880 | //glue objectclasses |
| 881 | - if(is_array($objcs) && count($objcs) > 0) { |
|
| 881 | + if (is_array($objcs) && count($objcs) > 0) { |
|
| 882 | 882 | $filter .= '(|'; |
| 883 | - foreach($objcs as $objc) { |
|
| 884 | - $filter .= '(objectclass=' . $objc . ')'; |
|
| 883 | + foreach ($objcs as $objc) { |
|
| 884 | + $filter .= '(objectclass='.$objc.')'; |
|
| 885 | 885 | } |
| 886 | 886 | $filter .= ')'; |
| 887 | 887 | $parts++; |
| 888 | 888 | } |
| 889 | 889 | //glue group memberships |
| 890 | - if($this->configuration->hasMemberOfFilterSupport) { |
|
| 890 | + if ($this->configuration->hasMemberOfFilterSupport) { |
|
| 891 | 891 | $cns = $this->configuration->ldapUserFilterGroups; |
| 892 | - if(is_array($cns) && count($cns) > 0) { |
|
| 892 | + if (is_array($cns) && count($cns) > 0) { |
|
| 893 | 893 | $filter .= '(|'; |
| 894 | 894 | $cr = $this->getConnection(); |
| 895 | - if(!$cr) { |
|
| 895 | + if (!$cr) { |
|
| 896 | 896 | throw new \Exception('Could not connect to LDAP'); |
| 897 | 897 | } |
| 898 | 898 | $base = $this->configuration->ldapBase[0]; |
| 899 | - foreach($cns as $cn) { |
|
| 900 | - $rr = $this->ldap->search($cr, $base, 'cn=' . $cn, array('dn', 'primaryGroupToken')); |
|
| 901 | - if(!$this->ldap->isResource($rr)) { |
|
| 899 | + foreach ($cns as $cn) { |
|
| 900 | + $rr = $this->ldap->search($cr, $base, 'cn='.$cn, array('dn', 'primaryGroupToken')); |
|
| 901 | + if (!$this->ldap->isResource($rr)) { |
|
| 902 | 902 | continue; |
| 903 | 903 | } |
| 904 | 904 | $er = $this->ldap->firstEntry($cr, $rr); |
@@ -907,11 +907,11 @@ discard block |
||
| 907 | 907 | if ($dn == false || $dn === '') { |
| 908 | 908 | continue; |
| 909 | 909 | } |
| 910 | - $filterPart = '(memberof=' . $dn . ')'; |
|
| 911 | - if(isset($attrs['primaryGroupToken'])) { |
|
| 910 | + $filterPart = '(memberof='.$dn.')'; |
|
| 911 | + if (isset($attrs['primaryGroupToken'])) { |
|
| 912 | 912 | $pgt = $attrs['primaryGroupToken'][0]; |
| 913 | - $primaryFilterPart = '(primaryGroupID=' . $pgt .')'; |
|
| 914 | - $filterPart = '(|' . $filterPart . $primaryFilterPart . ')'; |
|
| 913 | + $primaryFilterPart = '(primaryGroupID='.$pgt.')'; |
|
| 914 | + $filterPart = '(|'.$filterPart.$primaryFilterPart.')'; |
|
| 915 | 915 | } |
| 916 | 916 | $filter .= $filterPart; |
| 917 | 917 | } |
@@ -920,8 +920,8 @@ discard block |
||
| 920 | 920 | $parts++; |
| 921 | 921 | } |
| 922 | 922 | //wrap parts in AND condition |
| 923 | - if($parts > 1) { |
|
| 924 | - $filter = '(&' . $filter . ')'; |
|
| 923 | + if ($parts > 1) { |
|
| 924 | + $filter = '(&'.$filter.')'; |
|
| 925 | 925 | } |
| 926 | 926 | if ($filter === '') { |
| 927 | 927 | $filter = '(objectclass=*)'; |
@@ -931,27 +931,27 @@ discard block |
||
| 931 | 931 | case self::LFILTER_GROUP_LIST: |
| 932 | 932 | $objcs = $this->configuration->ldapGroupFilterObjectclass; |
| 933 | 933 | //glue objectclasses |
| 934 | - if(is_array($objcs) && count($objcs) > 0) { |
|
| 934 | + if (is_array($objcs) && count($objcs) > 0) { |
|
| 935 | 935 | $filter .= '(|'; |
| 936 | - foreach($objcs as $objc) { |
|
| 937 | - $filter .= '(objectclass=' . $objc . ')'; |
|
| 936 | + foreach ($objcs as $objc) { |
|
| 937 | + $filter .= '(objectclass='.$objc.')'; |
|
| 938 | 938 | } |
| 939 | 939 | $filter .= ')'; |
| 940 | 940 | $parts++; |
| 941 | 941 | } |
| 942 | 942 | //glue group memberships |
| 943 | 943 | $cns = $this->configuration->ldapGroupFilterGroups; |
| 944 | - if(is_array($cns) && count($cns) > 0) { |
|
| 944 | + if (is_array($cns) && count($cns) > 0) { |
|
| 945 | 945 | $filter .= '(|'; |
| 946 | - foreach($cns as $cn) { |
|
| 947 | - $filter .= '(cn=' . $cn . ')'; |
|
| 946 | + foreach ($cns as $cn) { |
|
| 947 | + $filter .= '(cn='.$cn.')'; |
|
| 948 | 948 | } |
| 949 | 949 | $filter .= ')'; |
| 950 | 950 | } |
| 951 | 951 | $parts++; |
| 952 | 952 | //wrap parts in AND condition |
| 953 | - if($parts > 1) { |
|
| 954 | - $filter = '(&' . $filter . ')'; |
|
| 953 | + if ($parts > 1) { |
|
| 954 | + $filter = '(&'.$filter.')'; |
|
| 955 | 955 | } |
| 956 | 956 | break; |
| 957 | 957 | |
@@ -963,47 +963,47 @@ discard block |
||
| 963 | 963 | $userAttributes = array_change_key_case(array_flip($userAttributes)); |
| 964 | 964 | $parts = 0; |
| 965 | 965 | |
| 966 | - if($this->configuration->ldapLoginFilterUsername === '1') { |
|
| 966 | + if ($this->configuration->ldapLoginFilterUsername === '1') { |
|
| 967 | 967 | $attr = ''; |
| 968 | - if(isset($userAttributes['uid'])) { |
|
| 968 | + if (isset($userAttributes['uid'])) { |
|
| 969 | 969 | $attr = 'uid'; |
| 970 | - } else if(isset($userAttributes['samaccountname'])) { |
|
| 970 | + } else if (isset($userAttributes['samaccountname'])) { |
|
| 971 | 971 | $attr = 'samaccountname'; |
| 972 | - } else if(isset($userAttributes['cn'])) { |
|
| 972 | + } else if (isset($userAttributes['cn'])) { |
|
| 973 | 973 | //fallback |
| 974 | 974 | $attr = 'cn'; |
| 975 | 975 | } |
| 976 | 976 | if ($attr !== '') { |
| 977 | - $filterUsername = '(' . $attr . $loginpart . ')'; |
|
| 977 | + $filterUsername = '('.$attr.$loginpart.')'; |
|
| 978 | 978 | $parts++; |
| 979 | 979 | } |
| 980 | 980 | } |
| 981 | 981 | |
| 982 | 982 | $filterEmail = ''; |
| 983 | - if($this->configuration->ldapLoginFilterEmail === '1') { |
|
| 983 | + if ($this->configuration->ldapLoginFilterEmail === '1') { |
|
| 984 | 984 | $filterEmail = '(|(mailPrimaryAddress=%uid)(mail=%uid))'; |
| 985 | 985 | $parts++; |
| 986 | 986 | } |
| 987 | 987 | |
| 988 | 988 | $filterAttributes = ''; |
| 989 | 989 | $attrsToFilter = $this->configuration->ldapLoginFilterAttributes; |
| 990 | - if(is_array($attrsToFilter) && count($attrsToFilter) > 0) { |
|
| 990 | + if (is_array($attrsToFilter) && count($attrsToFilter) > 0) { |
|
| 991 | 991 | $filterAttributes = '(|'; |
| 992 | - foreach($attrsToFilter as $attribute) { |
|
| 993 | - $filterAttributes .= '(' . $attribute . $loginpart . ')'; |
|
| 992 | + foreach ($attrsToFilter as $attribute) { |
|
| 993 | + $filterAttributes .= '('.$attribute.$loginpart.')'; |
|
| 994 | 994 | } |
| 995 | 995 | $filterAttributes .= ')'; |
| 996 | 996 | $parts++; |
| 997 | 997 | } |
| 998 | 998 | |
| 999 | 999 | $filterLogin = ''; |
| 1000 | - if($parts > 1) { |
|
| 1000 | + if ($parts > 1) { |
|
| 1001 | 1001 | $filterLogin = '(|'; |
| 1002 | 1002 | } |
| 1003 | 1003 | $filterLogin .= $filterUsername; |
| 1004 | 1004 | $filterLogin .= $filterEmail; |
| 1005 | 1005 | $filterLogin .= $filterAttributes; |
| 1006 | - if($parts > 1) { |
|
| 1006 | + if ($parts > 1) { |
|
| 1007 | 1007 | $filterLogin .= ')'; |
| 1008 | 1008 | } |
| 1009 | 1009 | |
@@ -1025,7 +1025,7 @@ discard block |
||
| 1025 | 1025 | * @throws \Exception |
| 1026 | 1026 | */ |
| 1027 | 1027 | private function connectAndBind($port = 389, $tls = false, $ncc = false) { |
| 1028 | - if($ncc) { |
|
| 1028 | + if ($ncc) { |
|
| 1029 | 1029 | //No certificate check |
| 1030 | 1030 | //FIXME: undo afterwards |
| 1031 | 1031 | putenv('LDAPTLS_REQCERT=never'); |
@@ -1035,12 +1035,12 @@ discard block |
||
| 1035 | 1035 | \OCP\Util::writeLog('user_ldap', 'Wiz: Checking Host Info ', \OCP\Util::DEBUG); |
| 1036 | 1036 | $host = $this->configuration->ldapHost; |
| 1037 | 1037 | $hostInfo = parse_url($host); |
| 1038 | - if(!$hostInfo) { |
|
| 1038 | + if (!$hostInfo) { |
|
| 1039 | 1039 | throw new \Exception(self::$l->t('Invalid Host')); |
| 1040 | 1040 | } |
| 1041 | 1041 | \OCP\Util::writeLog('user_ldap', 'Wiz: Attempting to connect ', \OCP\Util::DEBUG); |
| 1042 | 1042 | $cr = $this->ldap->connect($host, $port); |
| 1043 | - if(!is_resource($cr)) { |
|
| 1043 | + if (!is_resource($cr)) { |
|
| 1044 | 1044 | throw new \Exception(self::$l->t('Invalid Host')); |
| 1045 | 1045 | } |
| 1046 | 1046 | |
@@ -1051,9 +1051,9 @@ discard block |
||
| 1051 | 1051 | $this->ldap->setOption($cr, LDAP_OPT_NETWORK_TIMEOUT, self::LDAP_NW_TIMEOUT); |
| 1052 | 1052 | |
| 1053 | 1053 | try { |
| 1054 | - if($tls) { |
|
| 1054 | + if ($tls) { |
|
| 1055 | 1055 | $isTlsWorking = @$this->ldap->startTls($cr); |
| 1056 | - if(!$isTlsWorking) { |
|
| 1056 | + if (!$isTlsWorking) { |
|
| 1057 | 1057 | return false; |
| 1058 | 1058 | } |
| 1059 | 1059 | } |
@@ -1067,20 +1067,20 @@ discard block |
||
| 1067 | 1067 | $errNo = $this->ldap->errno($cr); |
| 1068 | 1068 | $error = ldap_error($cr); |
| 1069 | 1069 | $this->ldap->unbind($cr); |
| 1070 | - } catch(ServerNotAvailableException $e) { |
|
| 1070 | + } catch (ServerNotAvailableException $e) { |
|
| 1071 | 1071 | return false; |
| 1072 | 1072 | } |
| 1073 | 1073 | |
| 1074 | - if($login === true) { |
|
| 1074 | + if ($login === true) { |
|
| 1075 | 1075 | $this->ldap->unbind($cr); |
| 1076 | - if($ncc) { |
|
| 1076 | + if ($ncc) { |
|
| 1077 | 1077 | throw new \Exception('Certificate cannot be validated.'); |
| 1078 | 1078 | } |
| 1079 | - \OCP\Util::writeLog('user_ldap', 'Wiz: Bind successful to Port '. $port . ' TLS ' . intval($tls), \OCP\Util::DEBUG); |
|
| 1079 | + \OCP\Util::writeLog('user_ldap', 'Wiz: Bind successful to Port '.$port.' TLS '.intval($tls), \OCP\Util::DEBUG); |
|
| 1080 | 1080 | return true; |
| 1081 | 1081 | } |
| 1082 | 1082 | |
| 1083 | - if($errNo === -1 || ($errNo === 2 && $ncc)) { |
|
| 1083 | + if ($errNo === -1 || ($errNo === 2 && $ncc)) { |
|
| 1084 | 1084 | //host, port or TLS wrong |
| 1085 | 1085 | return false; |
| 1086 | 1086 | } else if ($errNo === 2) { |
@@ -1110,9 +1110,9 @@ discard block |
||
| 1110 | 1110 | */ |
| 1111 | 1111 | private function checkRequirements($reqs) { |
| 1112 | 1112 | $this->checkAgentRequirements(); |
| 1113 | - foreach($reqs as $option) { |
|
| 1113 | + foreach ($reqs as $option) { |
|
| 1114 | 1114 | $value = $this->configuration->$option; |
| 1115 | - if(empty($value)) { |
|
| 1115 | + if (empty($value)) { |
|
| 1116 | 1116 | return false; |
| 1117 | 1117 | } |
| 1118 | 1118 | } |
@@ -1134,33 +1134,33 @@ discard block |
||
| 1134 | 1134 | $dnRead = array(); |
| 1135 | 1135 | $foundItems = array(); |
| 1136 | 1136 | $maxEntries = 0; |
| 1137 | - if(!is_array($this->configuration->ldapBase) |
|
| 1137 | + if (!is_array($this->configuration->ldapBase) |
|
| 1138 | 1138 | || !isset($this->configuration->ldapBase[0])) { |
| 1139 | 1139 | return false; |
| 1140 | 1140 | } |
| 1141 | 1141 | $base = $this->configuration->ldapBase[0]; |
| 1142 | 1142 | $cr = $this->getConnection(); |
| 1143 | - if(!$this->ldap->isResource($cr)) { |
|
| 1143 | + if (!$this->ldap->isResource($cr)) { |
|
| 1144 | 1144 | return false; |
| 1145 | 1145 | } |
| 1146 | 1146 | $lastFilter = null; |
| 1147 | - if(isset($filters[count($filters)-1])) { |
|
| 1148 | - $lastFilter = $filters[count($filters)-1]; |
|
| 1147 | + if (isset($filters[count($filters) - 1])) { |
|
| 1148 | + $lastFilter = $filters[count($filters) - 1]; |
|
| 1149 | 1149 | } |
| 1150 | - foreach($filters as $filter) { |
|
| 1151 | - if($lastFilter === $filter && count($foundItems) > 0) { |
|
| 1150 | + foreach ($filters as $filter) { |
|
| 1151 | + if ($lastFilter === $filter && count($foundItems) > 0) { |
|
| 1152 | 1152 | //skip when the filter is a wildcard and results were found |
| 1153 | 1153 | continue; |
| 1154 | 1154 | } |
| 1155 | 1155 | // 20k limit for performance and reason |
| 1156 | 1156 | $rr = $this->ldap->search($cr, $base, $filter, array($attr), 0, 20000); |
| 1157 | - if(!$this->ldap->isResource($rr)) { |
|
| 1157 | + if (!$this->ldap->isResource($rr)) { |
|
| 1158 | 1158 | continue; |
| 1159 | 1159 | } |
| 1160 | 1160 | $entries = $this->ldap->countEntries($cr, $rr); |
| 1161 | 1161 | $getEntryFunc = 'firstEntry'; |
| 1162 | - if(($entries !== false) && ($entries > 0)) { |
|
| 1163 | - if(!is_null($maxF) && $entries > $maxEntries) { |
|
| 1162 | + if (($entries !== false) && ($entries > 0)) { |
|
| 1163 | + if (!is_null($maxF) && $entries > $maxEntries) { |
|
| 1164 | 1164 | $maxEntries = $entries; |
| 1165 | 1165 | $maxF = $filter; |
| 1166 | 1166 | } |
@@ -1168,13 +1168,13 @@ discard block |
||
| 1168 | 1168 | do { |
| 1169 | 1169 | $entry = $this->ldap->$getEntryFunc($cr, $rr); |
| 1170 | 1170 | $getEntryFunc = 'nextEntry'; |
| 1171 | - if(!$this->ldap->isResource($entry)) { |
|
| 1171 | + if (!$this->ldap->isResource($entry)) { |
|
| 1172 | 1172 | continue 2; |
| 1173 | 1173 | } |
| 1174 | 1174 | $rr = $entry; //will be expected by nextEntry next round |
| 1175 | 1175 | $attributes = $this->ldap->getAttributes($cr, $entry); |
| 1176 | 1176 | $dn = $this->ldap->getDN($cr, $entry); |
| 1177 | - if($dn === false || in_array($dn, $dnRead)) { |
|
| 1177 | + if ($dn === false || in_array($dn, $dnRead)) { |
|
| 1178 | 1178 | continue; |
| 1179 | 1179 | } |
| 1180 | 1180 | $newItems = array(); |
@@ -1185,7 +1185,7 @@ discard block |
||
| 1185 | 1185 | $foundItems = array_merge($foundItems, $newItems); |
| 1186 | 1186 | $this->resultCache[$dn][$attr] = $newItems; |
| 1187 | 1187 | $dnRead[] = $dn; |
| 1188 | - } while(($state === self::LRESULT_PROCESSED_SKIP |
|
| 1188 | + } while (($state === self::LRESULT_PROCESSED_SKIP |
|
| 1189 | 1189 | || $this->ldap->isResource($entry)) |
| 1190 | 1190 | && ($dnReadLimit === 0 || $dnReadCount < $dnReadLimit)); |
| 1191 | 1191 | } |
@@ -1208,11 +1208,11 @@ discard block |
||
| 1208 | 1208 | */ |
| 1209 | 1209 | private function determineFeature($objectclasses, $attr, $dbkey, $confkey, $po = false) { |
| 1210 | 1210 | $cr = $this->getConnection(); |
| 1211 | - if(!$cr) { |
|
| 1211 | + if (!$cr) { |
|
| 1212 | 1212 | throw new \Exception('Could not connect to LDAP'); |
| 1213 | 1213 | } |
| 1214 | 1214 | $p = 'objectclass='; |
| 1215 | - foreach($objectclasses as $key => $value) { |
|
| 1215 | + foreach ($objectclasses as $key => $value) { |
|
| 1216 | 1216 | $objectclasses[$key] = $p.$value; |
| 1217 | 1217 | } |
| 1218 | 1218 | $maxEntryObjC = ''; |
@@ -1224,7 +1224,7 @@ discard block |
||
| 1224 | 1224 | $availableFeatures = |
| 1225 | 1225 | $this->cumulativeSearchOnAttribute($objectclasses, $attr, |
| 1226 | 1226 | $dig, $maxEntryObjC); |
| 1227 | - if(is_array($availableFeatures) |
|
| 1227 | + if (is_array($availableFeatures) |
|
| 1228 | 1228 | && count($availableFeatures) > 0) { |
| 1229 | 1229 | natcasesort($availableFeatures); |
| 1230 | 1230 | //natcasesort keeps indices, but we must get rid of them for proper |
@@ -1235,7 +1235,7 @@ discard block |
||
| 1235 | 1235 | } |
| 1236 | 1236 | |
| 1237 | 1237 | $setFeatures = $this->configuration->$confkey; |
| 1238 | - if(is_array($setFeatures) && !empty($setFeatures)) { |
|
| 1238 | + if (is_array($setFeatures) && !empty($setFeatures)) { |
|
| 1239 | 1239 | //something is already configured? pre-select it. |
| 1240 | 1240 | $this->result->addChange($dbkey, $setFeatures); |
| 1241 | 1241 | } else if ($po && $maxEntryObjC !== '') { |
@@ -1257,7 +1257,7 @@ discard block |
||
| 1257 | 1257 | * LRESULT_PROCESSED_INVALID or LRESULT_PROCESSED_SKIP |
| 1258 | 1258 | */ |
| 1259 | 1259 | private function getAttributeValuesFromEntry($result, $attribute, &$known) { |
| 1260 | - if(!is_array($result) |
|
| 1260 | + if (!is_array($result) |
|
| 1261 | 1261 | || !isset($result['count']) |
| 1262 | 1262 | || !$result['count'] > 0) { |
| 1263 | 1263 | return self::LRESULT_PROCESSED_INVALID; |
@@ -1266,12 +1266,12 @@ discard block |
||
| 1266 | 1266 | // strtolower on all keys for proper comparison |
| 1267 | 1267 | $result = \OCP\Util::mb_array_change_key_case($result); |
| 1268 | 1268 | $attribute = strtolower($attribute); |
| 1269 | - if(isset($result[$attribute])) { |
|
| 1270 | - foreach($result[$attribute] as $key => $val) { |
|
| 1271 | - if($key === 'count') { |
|
| 1269 | + if (isset($result[$attribute])) { |
|
| 1270 | + foreach ($result[$attribute] as $key => $val) { |
|
| 1271 | + if ($key === 'count') { |
|
| 1272 | 1272 | continue; |
| 1273 | 1273 | } |
| 1274 | - if(!in_array($val, $known)) { |
|
| 1274 | + if (!in_array($val, $known)) { |
|
| 1275 | 1275 | $known[] = $val; |
| 1276 | 1276 | } |
| 1277 | 1277 | } |
@@ -1285,7 +1285,7 @@ discard block |
||
| 1285 | 1285 | * @return bool|mixed |
| 1286 | 1286 | */ |
| 1287 | 1287 | private function getConnection() { |
| 1288 | - if(!is_null($this->cr)) { |
|
| 1288 | + if (!is_null($this->cr)) { |
|
| 1289 | 1289 | return $this->cr; |
| 1290 | 1290 | } |
| 1291 | 1291 | |
@@ -1297,14 +1297,14 @@ discard block |
||
| 1297 | 1297 | $this->ldap->setOption($cr, LDAP_OPT_PROTOCOL_VERSION, 3); |
| 1298 | 1298 | $this->ldap->setOption($cr, LDAP_OPT_REFERRALS, 0); |
| 1299 | 1299 | $this->ldap->setOption($cr, LDAP_OPT_NETWORK_TIMEOUT, self::LDAP_NW_TIMEOUT); |
| 1300 | - if($this->configuration->ldapTLS === 1) { |
|
| 1300 | + if ($this->configuration->ldapTLS === 1) { |
|
| 1301 | 1301 | $this->ldap->startTls($cr); |
| 1302 | 1302 | } |
| 1303 | 1303 | |
| 1304 | 1304 | $lo = @$this->ldap->bind($cr, |
| 1305 | 1305 | $this->configuration->ldapAgentName, |
| 1306 | 1306 | $this->configuration->ldapAgentPassword); |
| 1307 | - if($lo === true) { |
|
| 1307 | + if ($lo === true) { |
|
| 1308 | 1308 | $this->$cr = $cr; |
| 1309 | 1309 | return $cr; |
| 1310 | 1310 | } |
@@ -1339,14 +1339,14 @@ discard block |
||
| 1339 | 1339 | $portSettings = array(); |
| 1340 | 1340 | |
| 1341 | 1341 | //In case the port is already provided, we will check this first |
| 1342 | - if($port > 0) { |
|
| 1342 | + if ($port > 0) { |
|
| 1343 | 1343 | $hostInfo = parse_url($host); |
| 1344 | - if(!(is_array($hostInfo) |
|
| 1344 | + if (!(is_array($hostInfo) |
|
| 1345 | 1345 | && isset($hostInfo['scheme']) |
| 1346 | 1346 | && stripos($hostInfo['scheme'], 'ldaps') !== false)) { |
| 1347 | 1347 | $portSettings[] = array('port' => $port, 'tls' => true); |
| 1348 | 1348 | } |
| 1349 | - $portSettings[] =array('port' => $port, 'tls' => false); |
|
| 1349 | + $portSettings[] = array('port' => $port, 'tls' => false); |
|
| 1350 | 1350 | } |
| 1351 | 1351 | |
| 1352 | 1352 | //default ports |
@@ -151,7 +151,6 @@ |
||
| 151 | 151 | /** |
| 152 | 152 | * saves database schema to xml file |
| 153 | 153 | * @param string $file name of file |
| 154 | - * @param int $mode |
|
| 155 | 154 | * @return bool |
| 156 | 155 | * |
| 157 | 156 | * TODO: write more documentation |
@@ -33,210 +33,210 @@ |
||
| 33 | 33 | */ |
| 34 | 34 | class OC_DB { |
| 35 | 35 | |
| 36 | - /** |
|
| 37 | - * get MDB2 schema manager |
|
| 38 | - * |
|
| 39 | - * @return \OC\DB\MDB2SchemaManager |
|
| 40 | - */ |
|
| 41 | - private static function getMDB2SchemaManager() { |
|
| 42 | - return new \OC\DB\MDB2SchemaManager(\OC::$server->getDatabaseConnection()); |
|
| 43 | - } |
|
| 36 | + /** |
|
| 37 | + * get MDB2 schema manager |
|
| 38 | + * |
|
| 39 | + * @return \OC\DB\MDB2SchemaManager |
|
| 40 | + */ |
|
| 41 | + private static function getMDB2SchemaManager() { |
|
| 42 | + return new \OC\DB\MDB2SchemaManager(\OC::$server->getDatabaseConnection()); |
|
| 43 | + } |
|
| 44 | 44 | |
| 45 | - /** |
|
| 46 | - * Prepare a SQL query |
|
| 47 | - * @param string $query Query string |
|
| 48 | - * @param int $limit |
|
| 49 | - * @param int $offset |
|
| 50 | - * @param bool $isManipulation |
|
| 51 | - * @throws \OC\DatabaseException |
|
| 52 | - * @return OC_DB_StatementWrapper prepared SQL query |
|
| 53 | - * |
|
| 54 | - * SQL query via Doctrine prepare(), needs to be execute()'d! |
|
| 55 | - */ |
|
| 56 | - static public function prepare( $query , $limit = null, $offset = null, $isManipulation = null) { |
|
| 57 | - $connection = \OC::$server->getDatabaseConnection(); |
|
| 45 | + /** |
|
| 46 | + * Prepare a SQL query |
|
| 47 | + * @param string $query Query string |
|
| 48 | + * @param int $limit |
|
| 49 | + * @param int $offset |
|
| 50 | + * @param bool $isManipulation |
|
| 51 | + * @throws \OC\DatabaseException |
|
| 52 | + * @return OC_DB_StatementWrapper prepared SQL query |
|
| 53 | + * |
|
| 54 | + * SQL query via Doctrine prepare(), needs to be execute()'d! |
|
| 55 | + */ |
|
| 56 | + static public function prepare( $query , $limit = null, $offset = null, $isManipulation = null) { |
|
| 57 | + $connection = \OC::$server->getDatabaseConnection(); |
|
| 58 | 58 | |
| 59 | - if ($isManipulation === null) { |
|
| 60 | - //try to guess, so we return the number of rows on manipulations |
|
| 61 | - $isManipulation = self::isManipulation($query); |
|
| 62 | - } |
|
| 59 | + if ($isManipulation === null) { |
|
| 60 | + //try to guess, so we return the number of rows on manipulations |
|
| 61 | + $isManipulation = self::isManipulation($query); |
|
| 62 | + } |
|
| 63 | 63 | |
| 64 | - // return the result |
|
| 65 | - try { |
|
| 66 | - $result =$connection->prepare($query, $limit, $offset); |
|
| 67 | - } catch (\Doctrine\DBAL\DBALException $e) { |
|
| 68 | - throw new \OC\DatabaseException($e->getMessage(), $query); |
|
| 69 | - } |
|
| 70 | - // differentiate between query and manipulation |
|
| 71 | - $result = new OC_DB_StatementWrapper($result, $isManipulation); |
|
| 72 | - return $result; |
|
| 73 | - } |
|
| 64 | + // return the result |
|
| 65 | + try { |
|
| 66 | + $result =$connection->prepare($query, $limit, $offset); |
|
| 67 | + } catch (\Doctrine\DBAL\DBALException $e) { |
|
| 68 | + throw new \OC\DatabaseException($e->getMessage(), $query); |
|
| 69 | + } |
|
| 70 | + // differentiate between query and manipulation |
|
| 71 | + $result = new OC_DB_StatementWrapper($result, $isManipulation); |
|
| 72 | + return $result; |
|
| 73 | + } |
|
| 74 | 74 | |
| 75 | - /** |
|
| 76 | - * tries to guess the type of statement based on the first 10 characters |
|
| 77 | - * the current check allows some whitespace but does not work with IF EXISTS or other more complex statements |
|
| 78 | - * |
|
| 79 | - * @param string $sql |
|
| 80 | - * @return bool |
|
| 81 | - */ |
|
| 82 | - static public function isManipulation( $sql ) { |
|
| 83 | - $selectOccurrence = stripos($sql, 'SELECT'); |
|
| 84 | - if ($selectOccurrence !== false && $selectOccurrence < 10) { |
|
| 85 | - return false; |
|
| 86 | - } |
|
| 87 | - $insertOccurrence = stripos($sql, 'INSERT'); |
|
| 88 | - if ($insertOccurrence !== false && $insertOccurrence < 10) { |
|
| 89 | - return true; |
|
| 90 | - } |
|
| 91 | - $updateOccurrence = stripos($sql, 'UPDATE'); |
|
| 92 | - if ($updateOccurrence !== false && $updateOccurrence < 10) { |
|
| 93 | - return true; |
|
| 94 | - } |
|
| 95 | - $deleteOccurrence = stripos($sql, 'DELETE'); |
|
| 96 | - if ($deleteOccurrence !== false && $deleteOccurrence < 10) { |
|
| 97 | - return true; |
|
| 98 | - } |
|
| 99 | - return false; |
|
| 100 | - } |
|
| 75 | + /** |
|
| 76 | + * tries to guess the type of statement based on the first 10 characters |
|
| 77 | + * the current check allows some whitespace but does not work with IF EXISTS or other more complex statements |
|
| 78 | + * |
|
| 79 | + * @param string $sql |
|
| 80 | + * @return bool |
|
| 81 | + */ |
|
| 82 | + static public function isManipulation( $sql ) { |
|
| 83 | + $selectOccurrence = stripos($sql, 'SELECT'); |
|
| 84 | + if ($selectOccurrence !== false && $selectOccurrence < 10) { |
|
| 85 | + return false; |
|
| 86 | + } |
|
| 87 | + $insertOccurrence = stripos($sql, 'INSERT'); |
|
| 88 | + if ($insertOccurrence !== false && $insertOccurrence < 10) { |
|
| 89 | + return true; |
|
| 90 | + } |
|
| 91 | + $updateOccurrence = stripos($sql, 'UPDATE'); |
|
| 92 | + if ($updateOccurrence !== false && $updateOccurrence < 10) { |
|
| 93 | + return true; |
|
| 94 | + } |
|
| 95 | + $deleteOccurrence = stripos($sql, 'DELETE'); |
|
| 96 | + if ($deleteOccurrence !== false && $deleteOccurrence < 10) { |
|
| 97 | + return true; |
|
| 98 | + } |
|
| 99 | + return false; |
|
| 100 | + } |
|
| 101 | 101 | |
| 102 | - /** |
|
| 103 | - * execute a prepared statement, on error write log and throw exception |
|
| 104 | - * @param mixed $stmt OC_DB_StatementWrapper, |
|
| 105 | - * an array with 'sql' and optionally 'limit' and 'offset' keys |
|
| 106 | - * .. or a simple sql query string |
|
| 107 | - * @param array $parameters |
|
| 108 | - * @return OC_DB_StatementWrapper |
|
| 109 | - * @throws \OC\DatabaseException |
|
| 110 | - */ |
|
| 111 | - static public function executeAudited( $stmt, array $parameters = null) { |
|
| 112 | - if (is_string($stmt)) { |
|
| 113 | - // convert to an array with 'sql' |
|
| 114 | - if (stripos($stmt, 'LIMIT') !== false) { //OFFSET requires LIMIT, so we only need to check for LIMIT |
|
| 115 | - // TODO try to convert LIMIT OFFSET notation to parameters |
|
| 116 | - $message = 'LIMIT and OFFSET are forbidden for portability reasons,' |
|
| 117 | - . ' pass an array with \'limit\' and \'offset\' instead'; |
|
| 118 | - throw new \OC\DatabaseException($message); |
|
| 119 | - } |
|
| 120 | - $stmt = array('sql' => $stmt, 'limit' => null, 'offset' => null); |
|
| 121 | - } |
|
| 122 | - if (is_array($stmt)) { |
|
| 123 | - // convert to prepared statement |
|
| 124 | - if ( ! array_key_exists('sql', $stmt) ) { |
|
| 125 | - $message = 'statement array must at least contain key \'sql\''; |
|
| 126 | - throw new \OC\DatabaseException($message); |
|
| 127 | - } |
|
| 128 | - if ( ! array_key_exists('limit', $stmt) ) { |
|
| 129 | - $stmt['limit'] = null; |
|
| 130 | - } |
|
| 131 | - if ( ! array_key_exists('limit', $stmt) ) { |
|
| 132 | - $stmt['offset'] = null; |
|
| 133 | - } |
|
| 134 | - $stmt = self::prepare($stmt['sql'], $stmt['limit'], $stmt['offset']); |
|
| 135 | - } |
|
| 136 | - self::raiseExceptionOnError($stmt, 'Could not prepare statement'); |
|
| 137 | - if ($stmt instanceof OC_DB_StatementWrapper) { |
|
| 138 | - $result = $stmt->execute($parameters); |
|
| 139 | - self::raiseExceptionOnError($result, 'Could not execute statement'); |
|
| 140 | - } else { |
|
| 141 | - if (is_object($stmt)) { |
|
| 142 | - $message = 'Expected a prepared statement or array got ' . get_class($stmt); |
|
| 143 | - } else { |
|
| 144 | - $message = 'Expected a prepared statement or array got ' . gettype($stmt); |
|
| 145 | - } |
|
| 146 | - throw new \OC\DatabaseException($message); |
|
| 147 | - } |
|
| 148 | - return $result; |
|
| 149 | - } |
|
| 102 | + /** |
|
| 103 | + * execute a prepared statement, on error write log and throw exception |
|
| 104 | + * @param mixed $stmt OC_DB_StatementWrapper, |
|
| 105 | + * an array with 'sql' and optionally 'limit' and 'offset' keys |
|
| 106 | + * .. or a simple sql query string |
|
| 107 | + * @param array $parameters |
|
| 108 | + * @return OC_DB_StatementWrapper |
|
| 109 | + * @throws \OC\DatabaseException |
|
| 110 | + */ |
|
| 111 | + static public function executeAudited( $stmt, array $parameters = null) { |
|
| 112 | + if (is_string($stmt)) { |
|
| 113 | + // convert to an array with 'sql' |
|
| 114 | + if (stripos($stmt, 'LIMIT') !== false) { //OFFSET requires LIMIT, so we only need to check for LIMIT |
|
| 115 | + // TODO try to convert LIMIT OFFSET notation to parameters |
|
| 116 | + $message = 'LIMIT and OFFSET are forbidden for portability reasons,' |
|
| 117 | + . ' pass an array with \'limit\' and \'offset\' instead'; |
|
| 118 | + throw new \OC\DatabaseException($message); |
|
| 119 | + } |
|
| 120 | + $stmt = array('sql' => $stmt, 'limit' => null, 'offset' => null); |
|
| 121 | + } |
|
| 122 | + if (is_array($stmt)) { |
|
| 123 | + // convert to prepared statement |
|
| 124 | + if ( ! array_key_exists('sql', $stmt) ) { |
|
| 125 | + $message = 'statement array must at least contain key \'sql\''; |
|
| 126 | + throw new \OC\DatabaseException($message); |
|
| 127 | + } |
|
| 128 | + if ( ! array_key_exists('limit', $stmt) ) { |
|
| 129 | + $stmt['limit'] = null; |
|
| 130 | + } |
|
| 131 | + if ( ! array_key_exists('limit', $stmt) ) { |
|
| 132 | + $stmt['offset'] = null; |
|
| 133 | + } |
|
| 134 | + $stmt = self::prepare($stmt['sql'], $stmt['limit'], $stmt['offset']); |
|
| 135 | + } |
|
| 136 | + self::raiseExceptionOnError($stmt, 'Could not prepare statement'); |
|
| 137 | + if ($stmt instanceof OC_DB_StatementWrapper) { |
|
| 138 | + $result = $stmt->execute($parameters); |
|
| 139 | + self::raiseExceptionOnError($result, 'Could not execute statement'); |
|
| 140 | + } else { |
|
| 141 | + if (is_object($stmt)) { |
|
| 142 | + $message = 'Expected a prepared statement or array got ' . get_class($stmt); |
|
| 143 | + } else { |
|
| 144 | + $message = 'Expected a prepared statement or array got ' . gettype($stmt); |
|
| 145 | + } |
|
| 146 | + throw new \OC\DatabaseException($message); |
|
| 147 | + } |
|
| 148 | + return $result; |
|
| 149 | + } |
|
| 150 | 150 | |
| 151 | - /** |
|
| 152 | - * saves database schema to xml file |
|
| 153 | - * @param string $file name of file |
|
| 154 | - * @param int $mode |
|
| 155 | - * @return bool |
|
| 156 | - * |
|
| 157 | - * TODO: write more documentation |
|
| 158 | - */ |
|
| 159 | - public static function getDbStructure($file) { |
|
| 160 | - $schemaManager = self::getMDB2SchemaManager(); |
|
| 161 | - return $schemaManager->getDbStructure($file); |
|
| 162 | - } |
|
| 151 | + /** |
|
| 152 | + * saves database schema to xml file |
|
| 153 | + * @param string $file name of file |
|
| 154 | + * @param int $mode |
|
| 155 | + * @return bool |
|
| 156 | + * |
|
| 157 | + * TODO: write more documentation |
|
| 158 | + */ |
|
| 159 | + public static function getDbStructure($file) { |
|
| 160 | + $schemaManager = self::getMDB2SchemaManager(); |
|
| 161 | + return $schemaManager->getDbStructure($file); |
|
| 162 | + } |
|
| 163 | 163 | |
| 164 | - /** |
|
| 165 | - * Creates tables from XML file |
|
| 166 | - * @param string $file file to read structure from |
|
| 167 | - * @return bool |
|
| 168 | - * |
|
| 169 | - * TODO: write more documentation |
|
| 170 | - */ |
|
| 171 | - public static function createDbFromStructure( $file ) { |
|
| 172 | - $schemaManager = self::getMDB2SchemaManager(); |
|
| 173 | - $result = $schemaManager->createDbFromStructure($file); |
|
| 174 | - return $result; |
|
| 175 | - } |
|
| 164 | + /** |
|
| 165 | + * Creates tables from XML file |
|
| 166 | + * @param string $file file to read structure from |
|
| 167 | + * @return bool |
|
| 168 | + * |
|
| 169 | + * TODO: write more documentation |
|
| 170 | + */ |
|
| 171 | + public static function createDbFromStructure( $file ) { |
|
| 172 | + $schemaManager = self::getMDB2SchemaManager(); |
|
| 173 | + $result = $schemaManager->createDbFromStructure($file); |
|
| 174 | + return $result; |
|
| 175 | + } |
|
| 176 | 176 | |
| 177 | - /** |
|
| 178 | - * update the database schema |
|
| 179 | - * @param string $file file to read structure from |
|
| 180 | - * @throws Exception |
|
| 181 | - * @return string|boolean |
|
| 182 | - */ |
|
| 183 | - public static function updateDbFromStructure($file) { |
|
| 184 | - $schemaManager = self::getMDB2SchemaManager(); |
|
| 185 | - try { |
|
| 186 | - $result = $schemaManager->updateDbFromStructure($file); |
|
| 187 | - } catch (Exception $e) { |
|
| 188 | - \OCP\Util::writeLog('core', 'Failed to update database structure ('.$e.')', \OCP\Util::FATAL); |
|
| 189 | - throw $e; |
|
| 190 | - } |
|
| 191 | - return $result; |
|
| 192 | - } |
|
| 177 | + /** |
|
| 178 | + * update the database schema |
|
| 179 | + * @param string $file file to read structure from |
|
| 180 | + * @throws Exception |
|
| 181 | + * @return string|boolean |
|
| 182 | + */ |
|
| 183 | + public static function updateDbFromStructure($file) { |
|
| 184 | + $schemaManager = self::getMDB2SchemaManager(); |
|
| 185 | + try { |
|
| 186 | + $result = $schemaManager->updateDbFromStructure($file); |
|
| 187 | + } catch (Exception $e) { |
|
| 188 | + \OCP\Util::writeLog('core', 'Failed to update database structure ('.$e.')', \OCP\Util::FATAL); |
|
| 189 | + throw $e; |
|
| 190 | + } |
|
| 191 | + return $result; |
|
| 192 | + } |
|
| 193 | 193 | |
| 194 | - /** |
|
| 195 | - * remove all tables defined in a database structure xml file |
|
| 196 | - * @param string $file the xml file describing the tables |
|
| 197 | - */ |
|
| 198 | - public static function removeDBStructure($file) { |
|
| 199 | - $schemaManager = self::getMDB2SchemaManager(); |
|
| 200 | - $schemaManager->removeDBStructure($file); |
|
| 201 | - } |
|
| 194 | + /** |
|
| 195 | + * remove all tables defined in a database structure xml file |
|
| 196 | + * @param string $file the xml file describing the tables |
|
| 197 | + */ |
|
| 198 | + public static function removeDBStructure($file) { |
|
| 199 | + $schemaManager = self::getMDB2SchemaManager(); |
|
| 200 | + $schemaManager->removeDBStructure($file); |
|
| 201 | + } |
|
| 202 | 202 | |
| 203 | - /** |
|
| 204 | - * check if a result is an error and throws an exception, works with \Doctrine\DBAL\DBALException |
|
| 205 | - * @param mixed $result |
|
| 206 | - * @param string $message |
|
| 207 | - * @return void |
|
| 208 | - * @throws \OC\DatabaseException |
|
| 209 | - */ |
|
| 210 | - public static function raiseExceptionOnError($result, $message = null) { |
|
| 211 | - if($result === false) { |
|
| 212 | - if ($message === null) { |
|
| 213 | - $message = self::getErrorMessage(); |
|
| 214 | - } else { |
|
| 215 | - $message .= ', Root cause:' . self::getErrorMessage(); |
|
| 216 | - } |
|
| 217 | - throw new \OC\DatabaseException($message, \OC::$server->getDatabaseConnection()->errorCode()); |
|
| 218 | - } |
|
| 219 | - } |
|
| 203 | + /** |
|
| 204 | + * check if a result is an error and throws an exception, works with \Doctrine\DBAL\DBALException |
|
| 205 | + * @param mixed $result |
|
| 206 | + * @param string $message |
|
| 207 | + * @return void |
|
| 208 | + * @throws \OC\DatabaseException |
|
| 209 | + */ |
|
| 210 | + public static function raiseExceptionOnError($result, $message = null) { |
|
| 211 | + if($result === false) { |
|
| 212 | + if ($message === null) { |
|
| 213 | + $message = self::getErrorMessage(); |
|
| 214 | + } else { |
|
| 215 | + $message .= ', Root cause:' . self::getErrorMessage(); |
|
| 216 | + } |
|
| 217 | + throw new \OC\DatabaseException($message, \OC::$server->getDatabaseConnection()->errorCode()); |
|
| 218 | + } |
|
| 219 | + } |
|
| 220 | 220 | |
| 221 | - /** |
|
| 222 | - * returns the error code and message as a string for logging |
|
| 223 | - * works with DoctrineException |
|
| 224 | - * @return string |
|
| 225 | - */ |
|
| 226 | - public static function getErrorMessage() { |
|
| 227 | - $connection = \OC::$server->getDatabaseConnection(); |
|
| 228 | - return $connection->getError(); |
|
| 229 | - } |
|
| 221 | + /** |
|
| 222 | + * returns the error code and message as a string for logging |
|
| 223 | + * works with DoctrineException |
|
| 224 | + * @return string |
|
| 225 | + */ |
|
| 226 | + public static function getErrorMessage() { |
|
| 227 | + $connection = \OC::$server->getDatabaseConnection(); |
|
| 228 | + return $connection->getError(); |
|
| 229 | + } |
|
| 230 | 230 | |
| 231 | - /** |
|
| 232 | - * Checks if a table exists in the database - the database prefix will be prepended |
|
| 233 | - * |
|
| 234 | - * @param string $table |
|
| 235 | - * @return bool |
|
| 236 | - * @throws \OC\DatabaseException |
|
| 237 | - */ |
|
| 238 | - public static function tableExists($table) { |
|
| 239 | - $connection = \OC::$server->getDatabaseConnection(); |
|
| 240 | - return $connection->tableExists($table); |
|
| 241 | - } |
|
| 231 | + /** |
|
| 232 | + * Checks if a table exists in the database - the database prefix will be prepended |
|
| 233 | + * |
|
| 234 | + * @param string $table |
|
| 235 | + * @return bool |
|
| 236 | + * @throws \OC\DatabaseException |
|
| 237 | + */ |
|
| 238 | + public static function tableExists($table) { |
|
| 239 | + $connection = \OC::$server->getDatabaseConnection(); |
|
| 240 | + return $connection->tableExists($table); |
|
| 241 | + } |
|
| 242 | 242 | } |
@@ -53,7 +53,7 @@ discard block |
||
| 53 | 53 | * |
| 54 | 54 | * SQL query via Doctrine prepare(), needs to be execute()'d! |
| 55 | 55 | */ |
| 56 | - static public function prepare( $query , $limit = null, $offset = null, $isManipulation = null) { |
|
| 56 | + static public function prepare($query, $limit = null, $offset = null, $isManipulation = null) { |
|
| 57 | 57 | $connection = \OC::$server->getDatabaseConnection(); |
| 58 | 58 | |
| 59 | 59 | if ($isManipulation === null) { |
@@ -63,7 +63,7 @@ discard block |
||
| 63 | 63 | |
| 64 | 64 | // return the result |
| 65 | 65 | try { |
| 66 | - $result =$connection->prepare($query, $limit, $offset); |
|
| 66 | + $result = $connection->prepare($query, $limit, $offset); |
|
| 67 | 67 | } catch (\Doctrine\DBAL\DBALException $e) { |
| 68 | 68 | throw new \OC\DatabaseException($e->getMessage(), $query); |
| 69 | 69 | } |
@@ -79,7 +79,7 @@ discard block |
||
| 79 | 79 | * @param string $sql |
| 80 | 80 | * @return bool |
| 81 | 81 | */ |
| 82 | - static public function isManipulation( $sql ) { |
|
| 82 | + static public function isManipulation($sql) { |
|
| 83 | 83 | $selectOccurrence = stripos($sql, 'SELECT'); |
| 84 | 84 | if ($selectOccurrence !== false && $selectOccurrence < 10) { |
| 85 | 85 | return false; |
@@ -108,7 +108,7 @@ discard block |
||
| 108 | 108 | * @return OC_DB_StatementWrapper |
| 109 | 109 | * @throws \OC\DatabaseException |
| 110 | 110 | */ |
| 111 | - static public function executeAudited( $stmt, array $parameters = null) { |
|
| 111 | + static public function executeAudited($stmt, array $parameters = null) { |
|
| 112 | 112 | if (is_string($stmt)) { |
| 113 | 113 | // convert to an array with 'sql' |
| 114 | 114 | if (stripos($stmt, 'LIMIT') !== false) { //OFFSET requires LIMIT, so we only need to check for LIMIT |
@@ -121,14 +121,14 @@ discard block |
||
| 121 | 121 | } |
| 122 | 122 | if (is_array($stmt)) { |
| 123 | 123 | // convert to prepared statement |
| 124 | - if ( ! array_key_exists('sql', $stmt) ) { |
|
| 124 | + if (!array_key_exists('sql', $stmt)) { |
|
| 125 | 125 | $message = 'statement array must at least contain key \'sql\''; |
| 126 | 126 | throw new \OC\DatabaseException($message); |
| 127 | 127 | } |
| 128 | - if ( ! array_key_exists('limit', $stmt) ) { |
|
| 128 | + if (!array_key_exists('limit', $stmt)) { |
|
| 129 | 129 | $stmt['limit'] = null; |
| 130 | 130 | } |
| 131 | - if ( ! array_key_exists('limit', $stmt) ) { |
|
| 131 | + if (!array_key_exists('limit', $stmt)) { |
|
| 132 | 132 | $stmt['offset'] = null; |
| 133 | 133 | } |
| 134 | 134 | $stmt = self::prepare($stmt['sql'], $stmt['limit'], $stmt['offset']); |
@@ -139,9 +139,9 @@ discard block |
||
| 139 | 139 | self::raiseExceptionOnError($result, 'Could not execute statement'); |
| 140 | 140 | } else { |
| 141 | 141 | if (is_object($stmt)) { |
| 142 | - $message = 'Expected a prepared statement or array got ' . get_class($stmt); |
|
| 142 | + $message = 'Expected a prepared statement or array got '.get_class($stmt); |
|
| 143 | 143 | } else { |
| 144 | - $message = 'Expected a prepared statement or array got ' . gettype($stmt); |
|
| 144 | + $message = 'Expected a prepared statement or array got '.gettype($stmt); |
|
| 145 | 145 | } |
| 146 | 146 | throw new \OC\DatabaseException($message); |
| 147 | 147 | } |
@@ -168,7 +168,7 @@ discard block |
||
| 168 | 168 | * |
| 169 | 169 | * TODO: write more documentation |
| 170 | 170 | */ |
| 171 | - public static function createDbFromStructure( $file ) { |
|
| 171 | + public static function createDbFromStructure($file) { |
|
| 172 | 172 | $schemaManager = self::getMDB2SchemaManager(); |
| 173 | 173 | $result = $schemaManager->createDbFromStructure($file); |
| 174 | 174 | return $result; |
@@ -208,11 +208,11 @@ discard block |
||
| 208 | 208 | * @throws \OC\DatabaseException |
| 209 | 209 | */ |
| 210 | 210 | public static function raiseExceptionOnError($result, $message = null) { |
| 211 | - if($result === false) { |
|
| 211 | + if ($result === false) { |
|
| 212 | 212 | if ($message === null) { |
| 213 | 213 | $message = self::getErrorMessage(); |
| 214 | 214 | } else { |
| 215 | - $message .= ', Root cause:' . self::getErrorMessage(); |
|
| 215 | + $message .= ', Root cause:'.self::getErrorMessage(); |
|
| 216 | 216 | } |
| 217 | 217 | throw new \OC\DatabaseException($message, \OC::$server->getDatabaseConnection()->errorCode()); |
| 218 | 218 | } |