File manager - Edit - /home/jardides/www/Jardi-design/images/administrator/legacy.tar
Back
image.php 0000604 00000104023 15247162136 0006342 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andreas Fischer <bantu@owncloud.com> * @author Bartek Przybylski <bart.p.pl@gmail.com> * @author Bart Visscher <bartv@thisnet.nl> * @author Björn Schießle <bjoern@schiessle.org> * @author Byron Marohn <combustible@live.com> * @author Christopher Schäpers <kondou@ts.unde.re> * @author Georg Ehrke <georg@owncloud.com> * @author j-ed <juergen@eisfair.org> * @author Joas Schilling <coding@schilljs.com> * @author Johannes Willnecker <johannes@willnecker.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Olivier Paroz <github@oparoz.com> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Class for basic image manipulation */ class OC_Image implements \OCP\IImage { /** @var false|resource */ protected $resource = false; // tmp resource. /** @var int */ protected $imageType = IMAGETYPE_PNG; // Default to png if file type isn't evident. /** @var string */ protected $mimeType = 'image/png'; // Default to png /** @var int */ protected $bitDepth = 24; /** @var null|string */ protected $filePath = null; /** @var finfo */ private $fileInfo; /** @var \OCP\ILogger */ private $logger; /** @var \OCP\IConfig */ private $config; /** @var array */ private $exif; /** * Get mime type for an image file. * * @param string|null $filePath The path to a local image file. * @return string The mime type if the it could be determined, otherwise an empty string. */ static public function getMimeTypeForFile($filePath) { // exif_imagetype throws "read error!" if file is less than 12 byte if ($filePath !== null && filesize($filePath) > 11) { $imageType = exif_imagetype($filePath); } else { $imageType = false; } return $imageType ? image_type_to_mime_type($imageType) : ''; } /** * Constructor. * * @param resource|string $imageRef The path to a local file, a base64 encoded string or a resource created by * an imagecreate* function. * @param \OCP\ILogger $logger * @param \OCP\IConfig $config */ public function __construct($imageRef = null, \OCP\ILogger $logger = null, \OCP\IConfig $config = null) { $this->logger = $logger; if ($logger === null) { $this->logger = \OC::$server->getLogger(); } $this->config = $config; if ($config === null) { $this->config = \OC::$server->getConfig(); } if (\OC_Util::fileInfoLoaded()) { $this->fileInfo = new finfo(FILEINFO_MIME_TYPE); } if ($imageRef !== null) { $this->load($imageRef); } } /** * Determine whether the object contains an image resource. * * @return bool */ public function valid() { // apparently you can't name a method 'empty'... return is_resource($this->resource); } /** * Returns the MIME type of the image or an empty string if no image is loaded. * * @return string */ public function mimeType() { return $this->valid() ? $this->mimeType : ''; } /** * Returns the width of the image or -1 if no image is loaded. * * @return int */ public function width() { return $this->valid() ? imagesx($this->resource) : -1; } /** * Returns the height of the image or -1 if no image is loaded. * * @return int */ public function height() { return $this->valid() ? imagesy($this->resource) : -1; } /** * Returns the width when the image orientation is top-left. * * @return int */ public function widthTopLeft() { $o = $this->getOrientation(); $this->logger->debug('OC_Image->widthTopLeft() Orientation: ' . $o, array('app' => 'core')); switch ($o) { case -1: case 1: case 2: // Not tested case 3: case 4: // Not tested return $this->width(); case 5: // Not tested case 6: case 7: // Not tested case 8: return $this->height(); } return $this->width(); } /** * Returns the height when the image orientation is top-left. * * @return int */ public function heightTopLeft() { $o = $this->getOrientation(); $this->logger->debug('OC_Image->heightTopLeft() Orientation: ' . $o, array('app' => 'core')); switch ($o) { case -1: case 1: case 2: // Not tested case 3: case 4: // Not tested return $this->height(); case 5: // Not tested case 6: case 7: // Not tested case 8: return $this->width(); } return $this->height(); } /** * Outputs the image. * * @param string $mimeType * @return bool */ public function show($mimeType = null) { if ($mimeType === null) { $mimeType = $this->mimeType(); } header('Content-Type: ' . $mimeType); return $this->_output(null, $mimeType); } /** * Saves the image. * * @param string $filePath * @param string $mimeType * @return bool */ public function save($filePath = null, $mimeType = null) { if ($mimeType === null) { $mimeType = $this->mimeType(); } if ($filePath === null) { if ($this->filePath === null) { $this->logger->error(__METHOD__ . '(): called with no path.', array('app' => 'core')); return false; } else { $filePath = $this->filePath; } } return $this->_output($filePath, $mimeType); } /** * Outputs/saves the image. * * @param string $filePath * @param string $mimeType * @return bool * @throws Exception */ private function _output($filePath = null, $mimeType = null) { if ($filePath) { if (!file_exists(dirname($filePath))) { mkdir(dirname($filePath), 0777, true); } $isWritable = is_writable(dirname($filePath)); if (!$isWritable) { $this->logger->error(__METHOD__ . '(): Directory \'' . dirname($filePath) . '\' is not writable.', array('app' => 'core')); return false; } elseif ($isWritable && file_exists($filePath) && !is_writable($filePath)) { $this->logger->error(__METHOD__ . '(): File \'' . $filePath . '\' is not writable.', array('app' => 'core')); return false; } } if (!$this->valid()) { return false; } $imageType = $this->imageType; if ($mimeType !== null) { switch ($mimeType) { case 'image/gif': $imageType = IMAGETYPE_GIF; break; case 'image/jpeg': $imageType = IMAGETYPE_JPEG; break; case 'image/png': $imageType = IMAGETYPE_PNG; break; case 'image/x-xbitmap': $imageType = IMAGETYPE_XBM; break; case 'image/bmp': case 'image/x-ms-bmp': $imageType = IMAGETYPE_BMP; break; default: throw new Exception('\OC_Image::_output(): "' . $mimeType . '" is not supported when forcing a specific output format'); } } switch ($imageType) { case IMAGETYPE_GIF: $retVal = imagegif($this->resource, $filePath); break; case IMAGETYPE_JPEG: $retVal = imagejpeg($this->resource, $filePath, $this->getJpegQuality()); break; case IMAGETYPE_PNG: $retVal = imagepng($this->resource, $filePath); break; case IMAGETYPE_XBM: if (function_exists('imagexbm')) { $retVal = imagexbm($this->resource, $filePath); } else { throw new Exception('\OC_Image::_output(): imagexbm() is not supported.'); } break; case IMAGETYPE_WBMP: $retVal = imagewbmp($this->resource, $filePath); break; case IMAGETYPE_BMP: $retVal = imagebmp($this->resource, $filePath, $this->bitDepth); break; default: $retVal = imagepng($this->resource, $filePath); } return $retVal; } /** * Prints the image when called as $image(). */ public function __invoke() { return $this->show(); } /** * @return resource Returns the image resource in any. */ public function resource() { return $this->resource; } /** * @return null|string Returns the raw image data. */ public function data() { if (!$this->valid()) { return null; } ob_start(); switch ($this->mimeType) { case "image/png": $res = imagepng($this->resource); break; case "image/jpeg": $quality = $this->getJpegQuality(); if ($quality !== null) { $res = imagejpeg($this->resource, null, $quality); } else { $res = imagejpeg($this->resource); } break; case "image/gif": $res = imagegif($this->resource); break; default: $res = imagepng($this->resource); $this->logger->info('OC_Image->data. Could not guess mime-type, defaulting to png', array('app' => 'core')); break; } if (!$res) { $this->logger->error('OC_Image->data. Error getting image data.', array('app' => 'core')); } return ob_get_clean(); } /** * @return string - base64 encoded, which is suitable for embedding in a VCard. */ public function __toString() { return base64_encode($this->data()); } /** * @return int|null */ protected function getJpegQuality() { $quality = $this->config->getAppValue('preview', 'jpeg_quality', 90); if ($quality !== null) { $quality = min(100, max(10, (int) $quality)); } return $quality; } /** * (I'm open for suggestions on better method name ;) * Get the orientation based on EXIF data. * * @return int The orientation or -1 if no EXIF data is available. */ public function getOrientation() { if ($this->exif !== null) { return $this->exif['Orientation']; } if ($this->imageType !== IMAGETYPE_JPEG) { $this->logger->debug('OC_Image->fixOrientation() Image is not a JPEG.', array('app' => 'core')); return -1; } if (!is_callable('exif_read_data')) { $this->logger->debug('OC_Image->fixOrientation() Exif module not enabled.', array('app' => 'core')); return -1; } if (!$this->valid()) { $this->logger->debug('OC_Image->fixOrientation() No image loaded.', array('app' => 'core')); return -1; } if (is_null($this->filePath) || !is_readable($this->filePath)) { $this->logger->debug('OC_Image->fixOrientation() No readable file path set.', array('app' => 'core')); return -1; } $exif = @exif_read_data($this->filePath, 'IFD0'); if (!$exif) { return -1; } if (!isset($exif['Orientation'])) { return -1; } $this->exif = $exif; return $exif['Orientation']; } public function readExif($data) { if (!is_callable('exif_read_data')) { $this->logger->debug('OC_Image->fixOrientation() Exif module not enabled.', array('app' => 'core')); return; } if (!$this->valid()) { $this->logger->debug('OC_Image->fixOrientation() No image loaded.', array('app' => 'core')); return; } $exif = @exif_read_data('data://image/jpeg;base64,' . base64_encode($data)); if (!$exif) { return; } if (!isset($exif['Orientation'])) { return; } $this->exif = $exif; } /** * (I'm open for suggestions on better method name ;) * Fixes orientation based on EXIF data. * * @return bool. */ public function fixOrientation() { $o = $this->getOrientation(); $this->logger->debug('OC_Image->fixOrientation() Orientation: ' . $o, array('app' => 'core')); $rotate = 0; $flip = false; switch ($o) { case -1: return false; //Nothing to fix case 1: $rotate = 0; break; case 2: $rotate = 0; $flip = true; break; case 3: $rotate = 180; break; case 4: $rotate = 180; $flip = true; break; case 5: $rotate = 90; $flip = true; break; case 6: $rotate = 270; break; case 7: $rotate = 270; $flip = true; break; case 8: $rotate = 90; break; } if($flip && function_exists('imageflip')) { imageflip($this->resource, IMG_FLIP_HORIZONTAL); } if ($rotate) { $res = imagerotate($this->resource, $rotate, 0); if ($res) { if (imagealphablending($res, true)) { if (imagesavealpha($res, true)) { imagedestroy($this->resource); $this->resource = $res; return true; } else { $this->logger->debug('OC_Image->fixOrientation() Error during alpha-saving', array('app' => 'core')); return false; } } else { $this->logger->debug('OC_Image->fixOrientation() Error during alpha-blending', array('app' => 'core')); return false; } } else { $this->logger->debug('OC_Image->fixOrientation() Error during orientation fixing', array('app' => 'core')); return false; } } return false; } /** * Loads an image from a local file, a base64 encoded string or a resource created by an imagecreate* function. * * @param resource|string $imageRef The path to a local file, a base64 encoded string or a resource created by an imagecreate* function or a file resource (file handle ). * @return resource|false An image resource or false on error */ public function load($imageRef) { if (is_resource($imageRef)) { if (get_resource_type($imageRef) === 'gd') { $this->resource = $imageRef; return $this->resource; } elseif (in_array(get_resource_type($imageRef), array('file', 'stream'))) { return $this->loadFromFileHandle($imageRef); } } elseif ($this->loadFromBase64($imageRef) !== false) { return $this->resource; } elseif ($this->loadFromFile($imageRef) !== false) { return $this->resource; } elseif ($this->loadFromData($imageRef) !== false) { return $this->resource; } $this->logger->debug(__METHOD__ . '(): could not load anything. Giving up!', array('app' => 'core')); return false; } /** * Loads an image from an open file handle. * It is the responsibility of the caller to position the pointer at the correct place and to close the handle again. * * @param resource $handle * @return resource|false An image resource or false on error */ public function loadFromFileHandle($handle) { $contents = stream_get_contents($handle); if ($this->loadFromData($contents)) { return $this->resource; } return false; } /** * Loads an image from a local file. * * @param bool|string $imagePath The path to a local file. * @return bool|resource An image resource or false on error */ public function loadFromFile($imagePath = false) { // exif_imagetype throws "read error!" if file is less than 12 byte if (!@is_file($imagePath) || !file_exists($imagePath) || filesize($imagePath) < 12 || !is_readable($imagePath)) { return false; } $iType = exif_imagetype($imagePath); switch ($iType) { case IMAGETYPE_GIF: if (imagetypes() & IMG_GIF) { $this->resource = imagecreatefromgif($imagePath); // Preserve transparency imagealphablending($this->resource, true); imagesavealpha($this->resource, true); } else { $this->logger->debug('OC_Image->loadFromFile, GIF images not supported: ' . $imagePath, array('app' => 'core')); } break; case IMAGETYPE_JPEG: if (imagetypes() & IMG_JPG) { if (getimagesize($imagePath) !== false) { $this->resource = @imagecreatefromjpeg($imagePath); } else { $this->logger->debug('OC_Image->loadFromFile, JPG image not valid: ' . $imagePath, array('app' => 'core')); } } else { $this->logger->debug('OC_Image->loadFromFile, JPG images not supported: ' . $imagePath, array('app' => 'core')); } break; case IMAGETYPE_PNG: if (imagetypes() & IMG_PNG) { $this->resource = @imagecreatefrompng($imagePath); // Preserve transparency imagealphablending($this->resource, true); imagesavealpha($this->resource, true); } else { $this->logger->debug('OC_Image->loadFromFile, PNG images not supported: ' . $imagePath, array('app' => 'core')); } break; case IMAGETYPE_XBM: if (imagetypes() & IMG_XPM) { $this->resource = @imagecreatefromxbm($imagePath); } else { $this->logger->debug('OC_Image->loadFromFile, XBM/XPM images not supported: ' . $imagePath, array('app' => 'core')); } break; case IMAGETYPE_WBMP: if (imagetypes() & IMG_WBMP) { $this->resource = @imagecreatefromwbmp($imagePath); } else { $this->logger->debug('OC_Image->loadFromFile, WBMP images not supported: ' . $imagePath, array('app' => 'core')); } break; case IMAGETYPE_BMP: $this->resource = $this->imagecreatefrombmp($imagePath); break; /* case IMAGETYPE_TIFF_II: // (intel byte order) break; case IMAGETYPE_TIFF_MM: // (motorola byte order) break; case IMAGETYPE_JPC: break; case IMAGETYPE_JP2: break; case IMAGETYPE_JPX: break; case IMAGETYPE_JB2: break; case IMAGETYPE_SWC: break; case IMAGETYPE_IFF: break; case IMAGETYPE_ICO: break; case IMAGETYPE_SWF: break; case IMAGETYPE_PSD: break; */ default: // this is mostly file created from encrypted file $this->resource = imagecreatefromstring(\OC\Files\Filesystem::file_get_contents(\OC\Files\Filesystem::getLocalPath($imagePath))); $iType = IMAGETYPE_PNG; $this->logger->debug('OC_Image->loadFromFile, Default', array('app' => 'core')); break; } if ($this->valid()) { $this->imageType = $iType; $this->mimeType = image_type_to_mime_type($iType); $this->filePath = $imagePath; } return $this->resource; } /** * Loads an image from a string of data. * * @param string $str A string of image data as read from a file. * @return bool|resource An image resource or false on error */ public function loadFromData($str) { if (is_resource($str)) { return false; } $this->resource = @imagecreatefromstring($str); if ($this->fileInfo) { $this->mimeType = $this->fileInfo->buffer($str); } if (is_resource($this->resource)) { imagealphablending($this->resource, false); imagesavealpha($this->resource, true); } if (!$this->resource) { $this->logger->debug('OC_Image->loadFromFile, could not load', array('app' => 'core')); return false; } return $this->resource; } /** * Loads an image from a base64 encoded string. * * @param string $str A string base64 encoded string of image data. * @return bool|resource An image resource or false on error */ public function loadFromBase64($str) { if (!is_string($str)) { return false; } $data = base64_decode($str); if ($data) { // try to load from string data $this->resource = @imagecreatefromstring($data); if ($this->fileInfo) { $this->mimeType = $this->fileInfo->buffer($data); } if (!$this->resource) { $this->logger->debug('OC_Image->loadFromBase64, could not load', array('app' => 'core')); return false; } return $this->resource; } else { return false; } } /** * Create a new image from file or URL * * @link http://www.programmierer-forum.de/function-imagecreatefrombmp-laeuft-mit-allen-bitraten-t143137.htm * @version 1.00 * @param string $fileName <p> * Path to the BMP image. * </p> * @return bool|resource an image resource identifier on success, <b>FALSE</b> on errors. */ private function imagecreatefrombmp($fileName) { if (!($fh = fopen($fileName, 'rb'))) { $this->logger->warning('imagecreatefrombmp: Can not open ' . $fileName, array('app' => 'core')); return false; } // read file header $meta = unpack('vtype/Vfilesize/Vreserved/Voffset', fread($fh, 14)); // check for bitmap if ($meta['type'] != 19778) { fclose($fh); $this->logger->warning('imagecreatefrombmp: Can not open ' . $fileName . ' is not a bitmap!', array('app' => 'core')); return false; } // read image header $meta += unpack('Vheadersize/Vwidth/Vheight/vplanes/vbits/Vcompression/Vimagesize/Vxres/Vyres/Vcolors/Vimportant', fread($fh, 40)); // read additional 16bit header if ($meta['bits'] == 16) { $meta += unpack('VrMask/VgMask/VbMask', fread($fh, 12)); } // set bytes and padding $meta['bytes'] = $meta['bits'] / 8; $this->bitDepth = $meta['bits']; //remember the bit depth for the imagebmp call $meta['decal'] = 4 - (4 * (($meta['width'] * $meta['bytes'] / 4) - floor($meta['width'] * $meta['bytes'] / 4))); if ($meta['decal'] == 4) { $meta['decal'] = 0; } // obtain imagesize if ($meta['imagesize'] < 1) { $meta['imagesize'] = $meta['filesize'] - $meta['offset']; // in rare cases filesize is equal to offset so we need to read physical size if ($meta['imagesize'] < 1) { $meta['imagesize'] = @filesize($fileName) - $meta['offset']; if ($meta['imagesize'] < 1) { fclose($fh); $this->logger->warning('imagecreatefrombmp: Can not obtain file size of ' . $fileName . ' is not a bitmap!', array('app' => 'core')); return false; } } } // calculate colors $meta['colors'] = !$meta['colors'] ? pow(2, $meta['bits']) : $meta['colors']; // read color palette $palette = array(); if ($meta['bits'] < 16) { $palette = unpack('l' . $meta['colors'], fread($fh, $meta['colors'] * 4)); // in rare cases the color value is signed if ($palette[1] < 0) { foreach ($palette as $i => $color) { $palette[$i] = $color + 16777216; } } } // create gd image $im = imagecreatetruecolor($meta['width'], $meta['height']); if ($im == false) { fclose($fh); $this->logger->warning( 'imagecreatefrombmp: imagecreatetruecolor failed for file "' . $fileName . '" with dimensions ' . $meta['width'] . 'x' . $meta['height'], array('app' => 'core')); return false; } $data = fread($fh, $meta['imagesize']); $p = 0; $vide = chr(0); $y = $meta['height'] - 1; $error = 'imagecreatefrombmp: ' . $fileName . ' has not enough data!'; // loop through the image data beginning with the lower left corner while ($y >= 0) { $x = 0; while ($x < $meta['width']) { switch ($meta['bits']) { case 32: case 24: if (!($part = substr($data, $p, 3))) { $this->logger->warning($error, array('app' => 'core')); return $im; } $color = @unpack('V', $part . $vide); break; case 16: if (!($part = substr($data, $p, 2))) { fclose($fh); $this->logger->warning($error, array('app' => 'core')); return $im; } $color = @unpack('v', $part); $color[1] = (($color[1] & 0xf800) >> 8) * 65536 + (($color[1] & 0x07e0) >> 3) * 256 + (($color[1] & 0x001f) << 3); break; case 8: $color = @unpack('n', $vide . substr($data, $p, 1)); $color[1] = (isset($palette[$color[1] + 1])) ? $palette[$color[1] + 1] : $palette[1]; break; case 4: $color = @unpack('n', $vide . substr($data, floor($p), 1)); $color[1] = ($p * 2) % 2 == 0 ? $color[1] >> 4 : $color[1] & 0x0F; $color[1] = (isset($palette[$color[1] + 1])) ? $palette[$color[1] + 1] : $palette[1]; break; case 1: $color = @unpack('n', $vide . substr($data, floor($p), 1)); switch (($p * 8) % 8) { case 0: $color[1] = $color[1] >> 7; break; case 1: $color[1] = ($color[1] & 0x40) >> 6; break; case 2: $color[1] = ($color[1] & 0x20) >> 5; break; case 3: $color[1] = ($color[1] & 0x10) >> 4; break; case 4: $color[1] = ($color[1] & 0x8) >> 3; break; case 5: $color[1] = ($color[1] & 0x4) >> 2; break; case 6: $color[1] = ($color[1] & 0x2) >> 1; break; case 7: $color[1] = ($color[1] & 0x1); break; } $color[1] = (isset($palette[$color[1] + 1])) ? $palette[$color[1] + 1] : $palette[1]; break; default: fclose($fh); $this->logger->warning('imagecreatefrombmp: ' . $fileName . ' has ' . $meta['bits'] . ' bits and this is not supported!', array('app' => 'core')); return false; } imagesetpixel($im, $x, $y, $color[1]); $x++; $p += $meta['bytes']; } $y--; $p += $meta['decal']; } fclose($fh); return $im; } /** * Resizes the image preserving ratio. * * @param integer $maxSize The maximum size of either the width or height. * @return bool */ public function resize($maxSize) { if (!$this->valid()) { $this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core')); return false; } $widthOrig = imagesx($this->resource); $heightOrig = imagesy($this->resource); $ratioOrig = $widthOrig / $heightOrig; if ($ratioOrig > 1) { $newHeight = round($maxSize / $ratioOrig); $newWidth = $maxSize; } else { $newWidth = round($maxSize * $ratioOrig); $newHeight = $maxSize; } $this->preciseResize(round($newWidth), round($newHeight)); return true; } /** * @param int $width * @param int $height * @return bool */ public function preciseResize($width, $height) { if (!$this->valid()) { $this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core')); return false; } $widthOrig = imagesx($this->resource); $heightOrig = imagesy($this->resource); $process = imagecreatetruecolor($width, $height); if ($process == false) { $this->logger->error(__METHOD__ . '(): Error creating true color image', array('app' => 'core')); imagedestroy($process); return false; } // preserve transparency if ($this->imageType == IMAGETYPE_GIF or $this->imageType == IMAGETYPE_PNG) { imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127)); imagealphablending($process, false); imagesavealpha($process, true); } imagecopyresampled($process, $this->resource, 0, 0, 0, 0, $width, $height, $widthOrig, $heightOrig); if ($process == false) { $this->logger->error(__METHOD__ . '(): Error re-sampling process image', array('app' => 'core')); imagedestroy($process); return false; } imagedestroy($this->resource); $this->resource = $process; return true; } /** * Crops the image to the middle square. If the image is already square it just returns. * * @param int $size maximum size for the result (optional) * @return bool for success or failure */ public function centerCrop($size = 0) { if (!$this->valid()) { $this->logger->error('OC_Image->centerCrop, No image loaded', array('app' => 'core')); return false; } $widthOrig = imagesx($this->resource); $heightOrig = imagesy($this->resource); if ($widthOrig === $heightOrig and $size == 0) { return true; } $ratioOrig = $widthOrig / $heightOrig; $width = $height = min($widthOrig, $heightOrig); if ($ratioOrig > 1) { $x = ($widthOrig / 2) - ($width / 2); $y = 0; } else { $y = ($heightOrig / 2) - ($height / 2); $x = 0; } if ($size > 0) { $targetWidth = $size; $targetHeight = $size; } else { $targetWidth = $width; $targetHeight = $height; } $process = imagecreatetruecolor($targetWidth, $targetHeight); if ($process == false) { $this->logger->error('OC_Image->centerCrop, Error creating true color image', array('app' => 'core')); imagedestroy($process); return false; } // preserve transparency if ($this->imageType == IMAGETYPE_GIF or $this->imageType == IMAGETYPE_PNG) { imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127)); imagealphablending($process, false); imagesavealpha($process, true); } imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $targetWidth, $targetHeight, $width, $height); if ($process == false) { $this->logger->error('OC_Image->centerCrop, Error re-sampling process image ' . $width . 'x' . $height, array('app' => 'core')); imagedestroy($process); return false; } imagedestroy($this->resource); $this->resource = $process; return true; } /** * Crops the image from point $x$y with dimension $wx$h. * * @param int $x Horizontal position * @param int $y Vertical position * @param int $w Width * @param int $h Height * @return bool for success or failure */ public function crop($x, $y, $w, $h) { if (!$this->valid()) { $this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core')); return false; } $process = imagecreatetruecolor($w, $h); if ($process == false) { $this->logger->error(__METHOD__ . '(): Error creating true color image', array('app' => 'core')); imagedestroy($process); return false; } // preserve transparency if ($this->imageType == IMAGETYPE_GIF or $this->imageType == IMAGETYPE_PNG) { imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127)); imagealphablending($process, false); imagesavealpha($process, true); } imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $w, $h, $w, $h); if ($process == false) { $this->logger->error(__METHOD__ . '(): Error re-sampling process image ' . $w . 'x' . $h, array('app' => 'core')); imagedestroy($process); return false; } imagedestroy($this->resource); $this->resource = $process; return true; } /** * Resizes the image to fit within a boundary while preserving ratio. * * Warning: Images smaller than $maxWidth x $maxHeight will end up being scaled up * * @param integer $maxWidth * @param integer $maxHeight * @return bool */ public function fitIn($maxWidth, $maxHeight) { if (!$this->valid()) { $this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core')); return false; } $widthOrig = imagesx($this->resource); $heightOrig = imagesy($this->resource); $ratio = $widthOrig / $heightOrig; $newWidth = min($maxWidth, $ratio * $maxHeight); $newHeight = min($maxHeight, $maxWidth / $ratio); $this->preciseResize(round($newWidth), round($newHeight)); return true; } /** * Shrinks larger images to fit within specified boundaries while preserving ratio. * * @param integer $maxWidth * @param integer $maxHeight * @return bool */ public function scaleDownToFit($maxWidth, $maxHeight) { if (!$this->valid()) { $this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core')); return false; } $widthOrig = imagesx($this->resource); $heightOrig = imagesy($this->resource); if ($widthOrig > $maxWidth || $heightOrig > $maxHeight) { return $this->fitIn($maxWidth, $maxHeight); } return false; } /** * Destroys the current image and resets the object */ public function destroy() { if ($this->valid()) { imagedestroy($this->resource); } $this->resource = null; } public function __destruct() { $this->destroy(); } } if (!function_exists('imagebmp')) { /** * Output a BMP image to either the browser or a file * * @link http://www.ugia.cn/wp-data/imagebmp.php * @author legend <legendsky@hotmail.com> * @link http://www.programmierer-forum.de/imagebmp-gute-funktion-gefunden-t143716.htm * @author mgutt <marc@gutt.it> * @version 1.00 * @param resource $im * @param string $fileName [optional] <p>The path to save the file to.</p> * @param int $bit [optional] <p>Bit depth, (default is 24).</p> * @param int $compression [optional] * @return bool <b>TRUE</b> on success or <b>FALSE</b> on failure. */ function imagebmp($im, $fileName = '', $bit = 24, $compression = 0) { if (!in_array($bit, array(1, 4, 8, 16, 24, 32))) { $bit = 24; } else if ($bit == 32) { $bit = 24; } $bits = pow(2, $bit); imagetruecolortopalette($im, true, $bits); $width = imagesx($im); $height = imagesy($im); $colorsNum = imagecolorstotal($im); $rgbQuad = ''; if ($bit <= 8) { for ($i = 0; $i < $colorsNum; $i++) { $colors = imagecolorsforindex($im, $i); $rgbQuad .= chr($colors['blue']) . chr($colors['green']) . chr($colors['red']) . "\0"; } $bmpData = ''; if ($compression == 0 || $bit < 8) { $compression = 0; $extra = ''; $padding = 4 - ceil($width / (8 / $bit)) % 4; if ($padding % 4 != 0) { $extra = str_repeat("\0", $padding); } for ($j = $height - 1; $j >= 0; $j--) { $i = 0; while ($i < $width) { $bin = 0; $limit = $width - $i < 8 / $bit ? (8 / $bit - $width + $i) * $bit : 0; for ($k = 8 - $bit; $k >= $limit; $k -= $bit) { $index = imagecolorat($im, $i, $j); $bin |= $index << $k; $i++; } $bmpData .= chr($bin); } $bmpData .= $extra; } } // RLE8 else if ($compression == 1 && $bit == 8) { for ($j = $height - 1; $j >= 0; $j--) { $lastIndex = "\0"; $sameNum = 0; for ($i = 0; $i <= $width; $i++) { $index = imagecolorat($im, $i, $j); if ($index !== $lastIndex || $sameNum > 255) { if ($sameNum != 0) { $bmpData .= chr($sameNum) . chr($lastIndex); } $lastIndex = $index; $sameNum = 1; } else { $sameNum++; } } $bmpData .= "\0\0"; } $bmpData .= "\0\1"; } $sizeQuad = strlen($rgbQuad); $sizeData = strlen($bmpData); } else { $extra = ''; $padding = 4 - ($width * ($bit / 8)) % 4; if ($padding % 4 != 0) { $extra = str_repeat("\0", $padding); } $bmpData = ''; for ($j = $height - 1; $j >= 0; $j--) { for ($i = 0; $i < $width; $i++) { $index = imagecolorat($im, $i, $j); $colors = imagecolorsforindex($im, $index); if ($bit == 16) { $bin = 0 << $bit; $bin |= ($colors['red'] >> 3) << 10; $bin |= ($colors['green'] >> 3) << 5; $bin |= $colors['blue'] >> 3; $bmpData .= pack("v", $bin); } else { $bmpData .= pack("c*", $colors['blue'], $colors['green'], $colors['red']); } } $bmpData .= $extra; } $sizeQuad = 0; $sizeData = strlen($bmpData); $colorsNum = 0; } $fileHeader = 'BM' . pack('V3', 54 + $sizeQuad + $sizeData, 0, 54 + $sizeQuad); $infoHeader = pack('V3v2V*', 0x28, $width, $height, 1, $bit, $compression, $sizeData, 0, 0, $colorsNum, 0); if ($fileName != '') { $fp = fopen($fileName, 'wb'); fwrite($fp, $fileHeader . $infoHeader . $rgbQuad . $bmpData); fclose($fp); return true; } echo $fileHeader . $infoHeader . $rgbQuad . $bmpData; return true; } } if (!function_exists('exif_imagetype')) { /** * Workaround if exif_imagetype does not exist * * @link http://www.php.net/manual/en/function.exif-imagetype.php#80383 * @param string $fileName * @return string|boolean */ function exif_imagetype($fileName) { if (($info = getimagesize($fileName)) !== false) { return $info[2]; } return false; } } template.php 0000604 00000031511 15247162136 0007074 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Brice Maron <brice@bmaron.net> * @author Frank Karlitschek <frank@karlitschek.de> * @author Hendrik Leppelsack <hendrik@leppelsack.de> * @author Individual IT Services <info@individual-it.net> * @author Jakob Sack <mail@jakobsack.de> * @author Jan-Christoph Borchardt <hey@jancborchardt.net> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Raghu Nayyar <hey@raghunayyar.com> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ use OC\TemplateLayout; require_once __DIR__.'/template/functions.php'; /** * This class provides the templates for ownCloud. */ class OC_Template extends \OC\Template\Base { /** @var string */ private $renderAs; // Create a full page? /** @var string */ private $path; // The path to the template /** @var array */ private $headers = array(); //custom headers /** @var string */ protected $app; // app id protected static $initTemplateEngineFirstRun = true; /** * Constructor * * @param string $app app providing the template * @param string $name of the template file (without suffix) * @param string $renderAs If $renderAs is set, OC_Template will try to * produce a full page in the according layout. For * now, $renderAs can be set to "guest", "user" or * "admin". * @param bool $registerCall = true */ public function __construct( $app, $name, $renderAs = "", $registerCall = true ) { // Read the selected theme from the config file self::initTemplateEngine($renderAs); $theme = OC_Util::getTheme(); $requestToken = (OC::$server->getSession() && $registerCall) ? \OCP\Util::callRegister() : ''; $parts = explode('/', $app); // fix translation when app is something like core/lostpassword $l10n = \OC::$server->getL10N($parts[0]); /** @var \OCP\Defaults $themeDefaults */ $themeDefaults = \OC::$server->query(\OCP\Defaults::class); list($path, $template) = $this->findTemplate($theme, $app, $name); // Set the private data $this->renderAs = $renderAs; $this->path = $path; $this->app = $app; parent::__construct($template, $requestToken, $l10n, $themeDefaults); } /** * @param string $renderAs */ public static function initTemplateEngine($renderAs) { if (self::$initTemplateEngineFirstRun){ //apps that started before the template initialization can load their own scripts/styles //so to make sure this scripts/styles here are loaded first we use OC_Util::addScript() with $prepend=true //meaning the last script/style in this list will be loaded first if (\OC::$server->getSystemConfig()->getValue ('installed', false) && $renderAs !== 'error' && !\OCP\Util::needUpgrade()) { if (\OC::$server->getConfig ()->getAppValue ( 'core', 'backgroundjobs_mode', 'ajax' ) == 'ajax') { OC_Util::addScript ( 'backgroundjobs', null, true ); } } OC_Util::addStyle('jquery-ui-fixes',null,true); OC_Util::addVendorStyle('jquery-ui/themes/base/jquery-ui',null,true); OC_Util::addStyle('server', null, true); OC_Util::addVendorStyle('select2/select2', null, true); OC_Util::addStyle('jquery.ocdialog'); OC_Util::addTranslations("core", null, true); OC_Util::addScript('search', 'search', true); OC_Util::addScript('merged-template-prepend', null, true); OC_Util::addScript('jquery-ui-fixes'); OC_Util::addScript('files/fileinfo'); OC_Util::addScript('files/client'); OC_Util::addScript('contactsmenu'); if (\OC::$server->getConfig()->getSystemValue('debug')) { // Add the stuff we need always // following logic will import all vendor libraries that are // specified in core/js/core.json $fileContent = file_get_contents(OC::$SERVERROOT . '/core/js/core.json'); if($fileContent !== false) { $coreDependencies = json_decode($fileContent, true); foreach(array_reverse($coreDependencies['vendor']) as $vendorLibrary) { //remove trailing ".js" as addVendorScript will append it OC_Util::addVendorScript( substr($vendorLibrary, 0, strlen($vendorLibrary) - 3),null,true); } } else { throw new \Exception('Cannot read core/js/core.json'); } } else { // Import all (combined) default vendor libraries OC_Util::addVendorScript('core', null, true); } if (\OC::$server->getRequest()->isUserAgent([\OC\AppFramework\Http\Request::USER_AGENT_IE])) { // polyfill for btoa/atob for IE friends OC_Util::addVendorScript('base64/base64'); // shim for the davclient.js library \OCP\Util::addScript('files/iedavclient'); } self::$initTemplateEngineFirstRun = false; } } /** * find the template with the given name * @param string $name of the template file (without suffix) * * Will select the template file for the selected theme. * Checking all the possible locations. * @param string $theme * @param string $app * @return string[] */ protected function findTemplate($theme, $app, $name) { // Check if it is a app template or not. if( $app !== '' ) { $dirs = $this->getAppTemplateDirs($theme, $app, OC::$SERVERROOT, OC_App::getAppPath($app)); } else { $dirs = $this->getCoreTemplateDirs($theme, OC::$SERVERROOT); } $locator = new \OC\Template\TemplateFileLocator( $dirs ); $template = $locator->find($name); $path = $locator->getPath(); return array($path, $template); } /** * Add a custom element to the header * @param string $tag tag name of the element * @param array $attributes array of attributes for the element * @param string $text the text content for the element. If $text is null then the * element will be written as empty element. So use "" to get a closing tag. */ public function addHeader($tag, $attributes, $text=null) { $this->headers[]= array( 'tag' => $tag, 'attributes' => $attributes, 'text' => $text ); } /** * Process the template * @return boolean|string * * This function process the template. If $this->renderAs is set, it * will produce a full page. */ public function fetchPage($additionalParams = null) { $data = parent::fetchPage($additionalParams); if( $this->renderAs ) { $page = new TemplateLayout($this->renderAs, $this->app); // Add custom headers $headers = ''; foreach(OC_Util::$headers as $header) { $headers .= '<'.\OCP\Util::sanitizeHTML($header['tag']); foreach($header['attributes'] as $name=>$value) { $headers .= ' '.\OCP\Util::sanitizeHTML($name).'="'.\OCP\Util::sanitizeHTML($value).'"'; } if ($header['text'] !== null) { $headers .= '>'.\OCP\Util::sanitizeHTML($header['text']).'</'.\OCP\Util::sanitizeHTML($header['tag']).'>'; } else { $headers .= '/>'; } } $page->assign('headers', $headers); $page->assign('content', $data); return $page->fetchPage(); } return $data; } /** * Include template * * @param string $file * @param array|null $additionalParams * @return string returns content of included template * * Includes another template. use <?php echo $this->inc('template'); ?> to * do this. */ public function inc( $file, $additionalParams = null ) { return $this->load($this->path.$file.'.php', $additionalParams); } /** * Shortcut to print a simple page for users * @param string $application The application we render the template for * @param string $name Name of the template * @param array $parameters Parameters for the template * @return boolean|null */ public static function printUserPage( $application, $name, $parameters = array() ) { $content = new OC_Template( $application, $name, "user" ); foreach( $parameters as $key => $value ) { $content->assign( $key, $value ); } print $content->printPage(); } /** * Shortcut to print a simple page for admins * @param string $application The application we render the template for * @param string $name Name of the template * @param array $parameters Parameters for the template * @return bool */ public static function printAdminPage( $application, $name, $parameters = array() ) { $content = new OC_Template( $application, $name, "admin" ); foreach( $parameters as $key => $value ) { $content->assign( $key, $value ); } return $content->printPage(); } /** * Shortcut to print a simple page for guests * @param string $application The application we render the template for * @param string $name Name of the template * @param array|string $parameters Parameters for the template * @return bool */ public static function printGuestPage( $application, $name, $parameters = array() ) { $content = new OC_Template( $application, $name, "guest" ); foreach( $parameters as $key => $value ) { $content->assign( $key, $value ); } return $content->printPage(); } /** * Print a fatal error page and terminates the script * @param string $error_msg The error message to show * @param string $hint An optional hint message - needs to be properly escaped */ public static function printErrorPage( $error_msg, $hint = '' ) { if (\OC_App::isEnabled('theming') && !\OC_App::isAppLoaded('theming')) { \OC_App::loadApp('theming'); } if ($error_msg === $hint) { // If the hint is the same as the message there is no need to display it twice. $hint = ''; } try { $content = new \OC_Template( '', 'error', 'error', false ); $errors = array(array('error' => $error_msg, 'hint' => $hint)); $content->assign( 'errors', $errors ); $content->printPage(); } catch (\Exception $e) { $logger = \OC::$server->getLogger(); $logger->error("$error_msg $hint", ['app' => 'core']); $logger->logException($e, ['app' => 'core']); header(self::getHttpProtocol() . ' 500 Internal Server Error'); header('Content-Type: text/plain; charset=utf-8'); print("$error_msg $hint"); } die(); } /** * print error page using Exception details * @param Exception | Throwable $exception */ public static function printExceptionErrorPage($exception, $fetchPage = false) { try { $request = \OC::$server->getRequest(); $content = new \OC_Template('', 'exception', 'error', false); $content->assign('errorClass', get_class($exception)); $content->assign('errorMsg', $exception->getMessage()); $content->assign('errorCode', $exception->getCode()); $content->assign('file', $exception->getFile()); $content->assign('line', $exception->getLine()); $content->assign('trace', $exception->getTraceAsString()); $content->assign('debugMode', \OC::$server->getSystemConfig()->getValue('debug', false)); $content->assign('remoteAddr', $request->getRemoteAddress()); $content->assign('requestID', $request->getId()); if ($fetchPage) { return $content->fetchPage(); } $content->printPage(); } catch (\Exception $e) { $logger = \OC::$server->getLogger(); $logger->logException($exception, ['app' => 'core']); $logger->logException($e, ['app' => 'core']); header(self::getHttpProtocol() . ' 500 Internal Server Error'); header('Content-Type: text/plain; charset=utf-8'); print("Internal Server Error\n\n"); print("The server encountered an internal error and was unable to complete your request.\n"); print("Please contact the server administrator if this error reappears multiple times, please include the technical details below in your report.\n"); print("More details can be found in the server log.\n"); } die(); } /** * This is only here to reduce the dependencies in case of an exception to * still be able to print a plain error message. * * Returns the used HTTP protocol. * * @return string HTTP protocol. HTTP/2, HTTP/1.1 or HTTP/1.0. * @internal Don't use this - use AppFramework\Http\Request->getHttpProtocol instead */ protected static function getHttpProtocol() { $claimedProtocol = strtoupper($_SERVER['SERVER_PROTOCOL']); $validProtocols = [ 'HTTP/1.0', 'HTTP/1.1', 'HTTP/2', ]; if(in_array($claimedProtocol, $validProtocols, true)) { return $claimedProtocol; } return 'HTTP/1.1'; } } defaults.php 0000604 00000020106 15247162136 0007066 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Jan-Christoph Borchardt <hey@jancborchardt.net> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Pascal de Bruijn <pmjdebruijn@pcode.nl> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author scolebrook <scolebrook@mac.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Volkan Gezer <volkangezer@gmail.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ class OC_Defaults { private $theme; private $l; private $defaultEntity; private $defaultName; private $defaultTitle; private $defaultBaseUrl; private $defaultSyncClientUrl; private $defaultiOSClientUrl; private $defaultiTunesAppId; private $defaultAndroidClientUrl; private $defaultDocBaseUrl; private $defaultDocVersion; private $defaultSlogan; private $defaultLogoClaim; private $defaultColorPrimary; public function __construct() { $this->l = \OC::$server->getL10N('lib'); $this->defaultEntity = 'Nextcloud'; /* e.g. company name, used for footers and copyright notices */ $this->defaultName = 'Nextcloud'; /* short name, used when referring to the software */ $this->defaultTitle = 'Nextcloud'; /* can be a longer name, for titles */ $this->defaultBaseUrl = 'https://nextcloud.com'; $this->defaultSyncClientUrl = 'https://nextcloud.com/install/#install-clients'; $this->defaultiOSClientUrl = 'https://itunes.apple.com/us/app/nextcloud/id1125420102?mt=8'; $this->defaultiTunesAppId = '1125420102'; $this->defaultAndroidClientUrl = 'https://play.google.com/store/apps/details?id=com.nextcloud.client'; $this->defaultDocBaseUrl = 'https://docs.nextcloud.com'; $this->defaultDocVersion = '12'; // used to generate doc links $this->defaultSlogan = $this->l->t('a safe home for all your data'); $this->defaultLogoClaim = ''; $this->defaultColorPrimary = '#0082c9'; $themePath = OC::$SERVERROOT . '/themes/' . OC_Util::getTheme() . '/defaults.php'; if (file_exists($themePath)) { // prevent defaults.php from printing output ob_start(); require_once $themePath; ob_end_clean(); if (class_exists('OC_Theme')) { $this->theme = new OC_Theme(); } } } /** * @param string $method */ private function themeExist($method) { if (isset($this->theme) && method_exists($this->theme, $method)) { return true; } return false; } /** * Returns the base URL * @return string URL */ public function getBaseUrl() { if ($this->themeExist('getBaseUrl')) { return $this->theme->getBaseUrl(); } else { return $this->defaultBaseUrl; } } /** * Returns the URL where the sync clients are listed * @return string URL */ public function getSyncClientUrl() { if ($this->themeExist('getSyncClientUrl')) { return $this->theme->getSyncClientUrl(); } else { return $this->defaultSyncClientUrl; } } /** * Returns the URL to the App Store for the iOS Client * @return string URL */ public function getiOSClientUrl() { if ($this->themeExist('getiOSClientUrl')) { return $this->theme->getiOSClientUrl(); } else { return $this->defaultiOSClientUrl; } } /** * Returns the AppId for the App Store for the iOS Client * @return string AppId */ public function getiTunesAppId() { if ($this->themeExist('getiTunesAppId')) { return $this->theme->getiTunesAppId(); } else { return $this->defaultiTunesAppId; } } /** * Returns the URL to Google Play for the Android Client * @return string URL */ public function getAndroidClientUrl() { if ($this->themeExist('getAndroidClientUrl')) { return $this->theme->getAndroidClientUrl(); } else { return $this->defaultAndroidClientUrl; } } /** * Returns the documentation URL * @return string URL */ public function getDocBaseUrl() { if ($this->themeExist('getDocBaseUrl')) { return $this->theme->getDocBaseUrl(); } else { return $this->defaultDocBaseUrl; } } /** * Returns the title * @return string title */ public function getTitle() { if ($this->themeExist('getTitle')) { return $this->theme->getTitle(); } else { return $this->defaultTitle; } } /** * Returns the short name of the software * @return string title */ public function getName() { if ($this->themeExist('getName')) { return $this->theme->getName(); } else { return $this->defaultName; } } /** * Returns the short name of the software containing HTML strings * @return string title */ public function getHTMLName() { if ($this->themeExist('getHTMLName')) { return $this->theme->getHTMLName(); } else { return $this->defaultName; } } /** * Returns entity (e.g. company name) - used for footer, copyright * @return string entity name */ public function getEntity() { if ($this->themeExist('getEntity')) { return $this->theme->getEntity(); } else { return $this->defaultEntity; } } /** * Returns slogan * @return string slogan */ public function getSlogan() { if ($this->themeExist('getSlogan')) { return $this->theme->getSlogan(); } else { return $this->defaultSlogan; } } /** * Returns logo claim * @return string logo claim */ public function getLogoClaim() { if ($this->themeExist('getLogoClaim')) { return $this->theme->getLogoClaim(); } else { return $this->defaultLogoClaim; } } /** * Returns short version of the footer * @return string short footer */ public function getShortFooter() { if ($this->themeExist('getShortFooter')) { $footer = $this->theme->getShortFooter(); } else { $footer = '<a href="'. $this->getBaseUrl() . '" target="_blank"' . ' rel="noreferrer">' .$this->getEntity() . '</a>'. ' – ' . $this->getSlogan(); } return $footer; } /** * Returns long version of the footer * @return string long footer */ public function getLongFooter() { if ($this->themeExist('getLongFooter')) { $footer = $this->theme->getLongFooter(); } else { $footer = $this->getShortFooter(); } return $footer; } /** * @param string $key * @return string URL to doc with key */ public function buildDocLinkToKey($key) { if ($this->themeExist('buildDocLinkToKey')) { return $this->theme->buildDocLinkToKey($key); } return $this->getDocBaseUrl() . '/server/' . $this->defaultDocVersion . '/go.php?to=' . $key; } /** * Returns primary color * @return string */ public function getColorPrimary() { if ($this->themeExist('getColorPrimary')) { return $this->theme->getColorPrimary(); } if ($this->themeExist('getMailHeaderColor')) { return $this->theme->getMailHeaderColor(); } return $this->defaultColorPrimary; } /** * @return array scss variables to overwrite */ public function getScssVariables() { if($this->themeExist('getScssVariables')) { return $this->theme->getScssVariables(); } return []; } public function shouldReplaceIcons() { return false; } /** * Themed logo url * * @param bool $useSvg Whether to point to the SVG image or a fallback * @return string */ public function getLogo($useSvg = true) { if ($this->themeExist('getLogo')) { return $this->theme->getLogo($useSvg); } if($useSvg) { $logo = \OC::$server->getURLGenerator()->imagePath('core', 'logo.svg'); } else { $logo = \OC::$server->getURLGenerator()->imagePath('core', 'logo.png'); } return $logo . '?v=' . hash('sha1', implode('.', \OCP\Util::getVersion())); } } filechunking.php 0000604 00000011325 15247162136 0007730 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Felix Moeller <mail@felixmoeller.de> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ class OC_FileChunking { protected $info; protected $cache; /** * TTL of chunks * * @var int */ protected $ttl; static public function decodeName($name) { preg_match('/(?P<name>.*)-chunking-(?P<transferid>\d+)-(?P<chunkcount>\d+)-(?P<index>\d+)/', $name, $matches); return $matches; } /** * @param string[] $info */ public function __construct($info) { $this->info = $info; $this->ttl = \OC::$server->getConfig()->getSystemValue('cache_chunk_gc_ttl', 86400); } public function getPrefix() { $name = $this->info['name']; $transferid = $this->info['transferid']; return $name.'-chunking-'.$transferid.'-'; } protected function getCache() { if (!isset($this->cache)) { $this->cache = new \OC\Cache\File(); } return $this->cache; } /** * Stores the given $data under the given $key - the number of stored bytes is returned * * @param string $index * @param resource $data * @return int */ public function store($index, $data) { $cache = $this->getCache(); $name = $this->getPrefix().$index; $cache->set($name, $data, $this->ttl); return $cache->size($name); } public function isComplete() { $prefix = $this->getPrefix(); $cache = $this->getCache(); $chunkcount = (int)$this->info['chunkcount']; for($i=($chunkcount-1); $i >= 0; $i--) { if (!$cache->hasKey($prefix.$i)) { return false; } } return true; } /** * Assembles the chunks into the file specified by the path. * Chunks are deleted afterwards. * * @param resource $f target path * * @return integer assembled file size * * @throws \OC\InsufficientStorageException when file could not be fully * assembled due to lack of free space */ public function assemble($f) { $cache = $this->getCache(); $prefix = $this->getPrefix(); $count = 0; for ($i = 0; $i < $this->info['chunkcount']; $i++) { $chunk = $cache->get($prefix.$i); // remove after reading to directly save space $cache->remove($prefix.$i); $count += fwrite($f, $chunk); // let php release the memory to work around memory exhausted error with php 5.6 $chunk = null; } return $count; } /** * Returns the size of the chunks already present * @return integer size in bytes */ public function getCurrentSize() { $cache = $this->getCache(); $prefix = $this->getPrefix(); $total = 0; for ($i = 0; $i < $this->info['chunkcount']; $i++) { $total += $cache->size($prefix.$i); } return $total; } /** * Removes all chunks which belong to this transmission */ public function cleanup() { $cache = $this->getCache(); $prefix = $this->getPrefix(); for($i=0; $i < $this->info['chunkcount']; $i++) { $cache->remove($prefix.$i); } } /** * Removes one specific chunk * @param string $index */ public function remove($index) { $cache = $this->getCache(); $prefix = $this->getPrefix(); $cache->remove($prefix.$index); } /** * Assembles the chunks into the file specified by the path. * Also triggers the relevant hooks and proxies. * * @param \OC\Files\Storage\Storage $storage storage * @param string $path target path relative to the storage * @return bool true on success or false if file could not be created * * @throws \OC\ServerNotAvailableException */ public function file_assemble($storage, $path) { // use file_put_contents as method because that best matches what this function does if (\OC\Files\Filesystem::isValidPath($path)) { $target = $storage->fopen($path, 'w'); if ($target) { $count = $this->assemble($target); fclose($target); return $count > 0; } else { return false; } } return false; } } app.php 0000604 00000104211 15247162136 0006037 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * @copyright Copyright (c) 2016, Lukas Reschke <lukas@statuscode.ch> * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Björn Schießle <bjoern@schiessle.org> * @author Borjan Tchakaloff <borjan@tchakaloff.fr> * @author Brice Maron <brice@bmaron.net> * @author Christopher Schäpers <kondou@ts.unde.re> * @author Felix Moeller <mail@felixmoeller.de> * @author Frank Karlitschek <frank@karlitschek.de> * @author Georg Ehrke <georg@owncloud.com> * @author Jakob Sack <mail@jakobsack.de> * @author Jan-Christoph Borchardt <hey@jancborchardt.net> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Kamil Domanski <kdomanski@kdemail.net> * @author Klaas Freitag <freitag@owncloud.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Markus Goetz <markus@woboq.com> * @author Morris Jobke <hey@morrisjobke.de> * @author RealRancor <Fisch.666@gmx.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Sam Tuke <mail@samtuke.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * @author Tom Needham <tom@owncloud.com> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ use OC\App\DependencyAnalyzer; use OC\App\InfoParser; use OC\App\Platform; use OC\Installer; use OC\Repair; use OCP\App\ManagerEvent; /** * This class manages the apps. It allows them to register and integrate in the * ownCloud ecosystem. Furthermore, this class is responsible for installing, * upgrading and removing apps. */ class OC_App { static private $appVersion = []; static private $adminForms = array(); static private $personalForms = array(); static private $appInfo = array(); static private $appTypes = array(); static private $loadedApps = array(); static private $altLogin = array(); static private $alreadyRegistered = []; const officialApp = 200; /** * clean the appId * * @param string|boolean $app AppId that needs to be cleaned * @return string */ public static function cleanAppId($app) { return str_replace(array('\0', '/', '\\', '..'), '', $app); } /** * Check if an app is loaded * * @param string $app * @return bool */ public static function isAppLoaded($app) { return in_array($app, self::$loadedApps, true); } /** * loads all apps * * @param string[] | string | null $types * @return bool * * This function walks through the ownCloud directory and loads all apps * it can find. A directory contains an app if the file /appinfo/info.xml * exists. * * if $types is set, only apps of those types will be loaded */ public static function loadApps($types = null) { if (\OC::$server->getSystemConfig()->getValue('maintenance', false)) { return false; } // Load the enabled apps here $apps = self::getEnabledApps(); // Add each apps' folder as allowed class path foreach($apps as $app) { $path = self::getAppPath($app); if($path !== false) { self::registerAutoloading($app, $path); } } // prevent app.php from printing output ob_start(); foreach ($apps as $app) { if ((is_null($types) or self::isType($app, $types)) && !in_array($app, self::$loadedApps)) { self::loadApp($app); } } ob_end_clean(); return true; } /** * load a single app * * @param string $app */ public static function loadApp($app) { self::$loadedApps[] = $app; $appPath = self::getAppPath($app); if($appPath === false) { return; } // in case someone calls loadApp() directly self::registerAutoloading($app, $appPath); if (is_file($appPath . '/appinfo/app.php')) { \OC::$server->getEventLogger()->start('load_app_' . $app, 'Load app: ' . $app); self::requireAppFile($app); if (self::isType($app, array('authentication'))) { // since authentication apps affect the "is app enabled for group" check, // the enabled apps cache needs to be cleared to make sure that the // next time getEnableApps() is called it will also include apps that were // enabled for groups self::$enabledAppsCache = array(); } \OC::$server->getEventLogger()->end('load_app_' . $app); } $info = self::getAppInfo($app); if (!empty($info['activity']['filters'])) { foreach ($info['activity']['filters'] as $filter) { \OC::$server->getActivityManager()->registerFilter($filter); } } if (!empty($info['activity']['settings'])) { foreach ($info['activity']['settings'] as $setting) { \OC::$server->getActivityManager()->registerSetting($setting); } } if (!empty($info['activity']['providers'])) { foreach ($info['activity']['providers'] as $provider) { \OC::$server->getActivityManager()->registerProvider($provider); } } } /** * @internal * @param string $app * @param string $path */ public static function registerAutoloading($app, $path) { $key = $app . '-' . $path; if(isset(self::$alreadyRegistered[$key])) { return; } self::$alreadyRegistered[$key] = true; // Register on PSR-4 composer autoloader $appNamespace = \OC\AppFramework\App::buildAppNamespace($app); \OC::$server->registerNamespace($app, $appNamespace); \OC::$composerAutoloader->addPsr4($appNamespace . '\\', $path . '/lib/', true); if (defined('PHPUNIT_RUN') || defined('CLI_TEST_RUN')) { \OC::$composerAutoloader->addPsr4($appNamespace . '\\Tests\\', $path . '/tests/', true); } // Register on legacy autoloader \OC::$loader->addValidRoot($path); } /** * Load app.php from the given app * * @param string $app app name */ private static function requireAppFile($app) { try { // encapsulated here to avoid variable scope conflicts require_once $app . '/appinfo/app.php'; } catch (Error $ex) { \OC::$server->getLogger()->logException($ex); $blacklist = \OC::$server->getAppManager()->getAlwaysEnabledApps(); if (!in_array($app, $blacklist)) { self::disable($app); } } } /** * check if an app is of a specific type * * @param string $app * @param string|array $types * @return bool */ public static function isType($app, $types) { if (is_string($types)) { $types = array($types); } $appTypes = self::getAppTypes($app); foreach ($types as $type) { if (array_search($type, $appTypes) !== false) { return true; } } return false; } /** * get the types of an app * * @param string $app * @return array */ private static function getAppTypes($app) { //load the cache if (count(self::$appTypes) == 0) { self::$appTypes = \OC::$server->getAppConfig()->getValues(false, 'types'); } if (isset(self::$appTypes[$app])) { return explode(',', self::$appTypes[$app]); } else { return array(); } } /** * read app types from info.xml and cache them in the database */ public static function setAppTypes($app) { $appData = self::getAppInfo($app); if(!is_array($appData)) { return; } if (isset($appData['types'])) { $appTypes = implode(',', $appData['types']); } else { $appTypes = ''; $appData['types'] = []; } \OC::$server->getAppConfig()->setValue($app, 'types', $appTypes); if (\OC::$server->getAppManager()->hasProtectedAppType($appData['types'])) { $enabled = \OC::$server->getAppConfig()->getValue($app, 'enabled', 'yes'); if ($enabled !== 'yes' && $enabled !== 'no') { \OC::$server->getAppConfig()->setValue($app, 'enabled', 'yes'); } } } /** * check if app is shipped * * @param string $appId the id of the app to check * @return bool * * Check if an app that is installed is a shipped app or installed from the appstore. */ public static function isShipped($appId) { return \OC::$server->getAppManager()->isShipped($appId); } /** * get all enabled apps */ protected static $enabledAppsCache = array(); /** * Returns apps enabled for the current user. * * @param bool $forceRefresh whether to refresh the cache * @param bool $all whether to return apps for all users, not only the * currently logged in one * @return string[] */ public static function getEnabledApps($forceRefresh = false, $all = false) { if (!\OC::$server->getSystemConfig()->getValue('installed', false)) { return array(); } // in incognito mode or when logged out, $user will be false, // which is also the case during an upgrade $appManager = \OC::$server->getAppManager(); if ($all) { $user = null; } else { $user = \OC::$server->getUserSession()->getUser(); } if (is_null($user)) { $apps = $appManager->getInstalledApps(); } else { $apps = $appManager->getEnabledAppsForUser($user); } $apps = array_filter($apps, function ($app) { return $app !== 'files';//we add this manually }); sort($apps); array_unshift($apps, 'files'); return $apps; } /** * checks whether or not an app is enabled * * @param string $app app * @return bool * * This function checks whether or not an app is enabled. */ public static function isEnabled($app) { return \OC::$server->getAppManager()->isEnabledForUser($app); } /** * enables an app * * @param string $appId * @param array $groups (optional) when set, only these groups will have access to the app * @throws \Exception * @return void * * This function set an app as enabled in appconfig. */ public function enable($appId, $groups = null) { self::$enabledAppsCache = []; // flush // Check if app is already downloaded $installer = new Installer( \OC::$server->getAppFetcher(), \OC::$server->getHTTPClientService(), \OC::$server->getTempManager(), \OC::$server->getLogger(), \OC::$server->getConfig() ); $isDownloaded = $installer->isDownloaded($appId); if(!$isDownloaded) { $installer->downloadApp($appId); } $installer->installApp($appId); $appManager = \OC::$server->getAppManager(); if (!is_null($groups)) { $groupManager = \OC::$server->getGroupManager(); $groupsList = []; foreach ($groups as $group) { $groupItem = $groupManager->get($group); if ($groupItem instanceof \OCP\IGroup) { $groupsList[] = $groupManager->get($group); } } $appManager->enableAppForGroups($appId, $groupsList); } else { $appManager->enableApp($appId); } } /** * @param string $app * @return bool */ public static function removeApp($app) { if (self::isShipped($app)) { return false; } $installer = new Installer( \OC::$server->getAppFetcher(), \OC::$server->getHTTPClientService(), \OC::$server->getTempManager(), \OC::$server->getLogger(), \OC::$server->getConfig() ); return $installer->removeApp($app); } /** * This function set an app as disabled in appconfig. * * @param string $app app * @throws Exception */ public static function disable($app) { // flush self::$enabledAppsCache = array(); // run uninstall steps $appData = OC_App::getAppInfo($app); if (!is_null($appData)) { OC_App::executeRepairSteps($app, $appData['repair-steps']['uninstall']); } // emit disable hook - needed anymore ? \OC_Hook::emit('OC_App', 'pre_disable', array('app' => $app)); // finally disable it $appManager = \OC::$server->getAppManager(); $appManager->disableApp($app); } // This is private as well. It simply works, so don't ask for more details private static function proceedNavigation($list) { usort($list, function($a, $b) { if (isset($a['order']) && isset($b['order'])) { return ($a['order'] < $b['order']) ? -1 : 1; } else if (isset($a['order']) || isset($b['order'])) { return isset($a['order']) ? -1 : 1; } else { return ($a['name'] < $b['name']) ? -1 : 1; } }); $activeApp = OC::$server->getNavigationManager()->getActiveEntry(); foreach ($list as $index => &$navEntry) { if ($navEntry['id'] == $activeApp) { $navEntry['active'] = true; } else { $navEntry['active'] = false; } } unset($navEntry); return $list; } /** * Get the path where to install apps * * @return string|false */ public static function getInstallPath() { if (\OC::$server->getSystemConfig()->getValue('appstoreenabled', true) == false) { return false; } foreach (OC::$APPSROOTS as $dir) { if (isset($dir['writable']) && $dir['writable'] === true) { return $dir['path']; } } \OCP\Util::writeLog('core', 'No application directories are marked as writable.', \OCP\Util::ERROR); return null; } /** * search for an app in all app-directories * * @param string $appId * @return false|string */ public static function findAppInDirectories($appId) { $sanitizedAppId = self::cleanAppId($appId); if($sanitizedAppId !== $appId) { return false; } static $app_dir = array(); if (isset($app_dir[$appId])) { return $app_dir[$appId]; } $possibleApps = array(); foreach (OC::$APPSROOTS as $dir) { if (file_exists($dir['path'] . '/' . $appId)) { $possibleApps[] = $dir; } } if (empty($possibleApps)) { return false; } elseif (count($possibleApps) === 1) { $dir = array_shift($possibleApps); $app_dir[$appId] = $dir; return $dir; } else { $versionToLoad = array(); foreach ($possibleApps as $possibleApp) { $version = self::getAppVersionByPath($possibleApp['path']); if (empty($versionToLoad) || version_compare($version, $versionToLoad['version'], '>')) { $versionToLoad = array( 'dir' => $possibleApp, 'version' => $version, ); } } $app_dir[$appId] = $versionToLoad['dir']; return $versionToLoad['dir']; //TODO - write test } } /** * Get the directory for the given app. * If the app is defined in multiple directories, the first one is taken. (false if not found) * * @param string $appId * @return string|false */ public static function getAppPath($appId) { if ($appId === null || trim($appId) === '') { return false; } if (($dir = self::findAppInDirectories($appId)) != false) { return $dir['path'] . '/' . $appId; } return false; } /** * Get the path for the given app on the access * If the app is defined in multiple directories, the first one is taken. (false if not found) * * @param string $appId * @return string|false */ public static function getAppWebPath($appId) { if (($dir = self::findAppInDirectories($appId)) != false) { return OC::$WEBROOT . $dir['url'] . '/' . $appId; } return false; } /** * get the last version of the app from appinfo/info.xml * * @param string $appId * @param bool $useCache * @return string */ public static function getAppVersion($appId, $useCache = true) { if($useCache && isset(self::$appVersion[$appId])) { return self::$appVersion[$appId]; } $file = self::getAppPath($appId); self::$appVersion[$appId] = ($file !== false) ? self::getAppVersionByPath($file) : '0'; return self::$appVersion[$appId]; } /** * get app's version based on it's path * * @param string $path * @return string */ public static function getAppVersionByPath($path) { $infoFile = $path . '/appinfo/info.xml'; $appData = self::getAppInfo($infoFile, true); return isset($appData['version']) ? $appData['version'] : ''; } /** * Read all app metadata from the info.xml file * * @param string $appId id of the app or the path of the info.xml file * @param bool $path * @param string $lang * @return array|null * @note all data is read from info.xml, not just pre-defined fields */ public static function getAppInfo($appId, $path = false, $lang = null) { if ($path) { $file = $appId; } else { if ($lang === null && isset(self::$appInfo[$appId])) { return self::$appInfo[$appId]; } $appPath = self::getAppPath($appId); if($appPath === false) { return null; } $file = $appPath . '/appinfo/info.xml'; } $parser = new InfoParser(\OC::$server->getMemCacheFactory()->create('core.appinfo')); $data = $parser->parse($file); if (is_array($data)) { $data = OC_App::parseAppInfo($data, $lang); } if(isset($data['ocsid'])) { $storedId = \OC::$server->getConfig()->getAppValue($appId, 'ocsid'); if($storedId !== '' && $storedId !== $data['ocsid']) { $data['ocsid'] = $storedId; } } if ($lang === null) { self::$appInfo[$appId] = $data; } return $data; } /** * Returns the navigation * * @return array * * This function returns an array containing all entries added. The * entries are sorted by the key 'order' ascending. Additional to the keys * given for each app the following keys exist: * - active: boolean, signals if the user is on this navigation entry */ public static function getNavigation() { $entries = OC::$server->getNavigationManager()->getAll(); return self::proceedNavigation($entries); } /** * Returns the Settings Navigation * * @return string[] * * This function returns an array containing all settings pages added. The * entries are sorted by the key 'order' ascending. */ public static function getSettingsNavigation() { $entries = OC::$server->getNavigationManager()->getAll('settings'); return self::proceedNavigation($entries); } /** * get the id of loaded app * * @return string */ public static function getCurrentApp() { $request = \OC::$server->getRequest(); $script = substr($request->getScriptName(), strlen(OC::$WEBROOT) + 1); $topFolder = substr($script, 0, strpos($script, '/')); if (empty($topFolder)) { $path_info = $request->getPathInfo(); if ($path_info) { $topFolder = substr($path_info, 1, strpos($path_info, '/', 1) - 1); } } if ($topFolder == 'apps') { $length = strlen($topFolder); return substr($script, $length + 1, strpos($script, '/', $length + 1) - $length - 1); } else { return $topFolder; } } /** * @param string $type * @return array */ public static function getForms($type) { $forms = array(); switch ($type) { case 'admin': $source = self::$adminForms; break; case 'personal': $source = self::$personalForms; break; default: return array(); } foreach ($source as $form) { $forms[] = include $form; } return $forms; } /** * register an admin form to be shown * * @param string $app * @param string $page */ public static function registerAdmin($app, $page) { self::$adminForms[] = $app . '/' . $page . '.php'; } /** * register a personal form to be shown * @param string $app * @param string $page */ public static function registerPersonal($app, $page) { self::$personalForms[] = $app . '/' . $page . '.php'; } /** * @param array $entry */ public static function registerLogIn(array $entry) { self::$altLogin[] = $entry; } /** * @return array */ public static function getAlternativeLogIns() { return self::$altLogin; } /** * get a list of all apps in the apps folder * * @return array an array of app names (string IDs) * @todo: change the name of this method to getInstalledApps, which is more accurate */ public static function getAllApps() { $apps = array(); foreach (OC::$APPSROOTS as $apps_dir) { if (!is_readable($apps_dir['path'])) { \OCP\Util::writeLog('core', 'unable to read app folder : ' . $apps_dir['path'], \OCP\Util::WARN); continue; } $dh = opendir($apps_dir['path']); if (is_resource($dh)) { while (($file = readdir($dh)) !== false) { if ($file[0] != '.' and is_dir($apps_dir['path'] . '/' . $file) and is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')) { $apps[] = $file; } } } } return $apps; } /** * List all apps, this is used in apps.php * * @return array */ public function listAllApps() { $installedApps = OC_App::getAllApps(); //we don't want to show configuration for these $blacklist = \OC::$server->getAppManager()->getAlwaysEnabledApps(); $appList = array(); $langCode = \OC::$server->getL10N('core')->getLanguageCode(); $urlGenerator = \OC::$server->getURLGenerator(); foreach ($installedApps as $app) { if (array_search($app, $blacklist) === false) { $info = OC_App::getAppInfo($app, false, $langCode); if (!is_array($info)) { \OCP\Util::writeLog('core', 'Could not read app info file for app "' . $app . '"', \OCP\Util::ERROR); continue; } if (!isset($info['name'])) { \OCP\Util::writeLog('core', 'App id "' . $app . '" has no name in appinfo', \OCP\Util::ERROR); continue; } $enabled = \OC::$server->getAppConfig()->getValue($app, 'enabled', 'no'); $info['groups'] = null; if ($enabled === 'yes') { $active = true; } else if ($enabled === 'no') { $active = false; } else { $active = true; $info['groups'] = $enabled; } $info['active'] = $active; if (self::isShipped($app)) { $info['internal'] = true; $info['level'] = self::officialApp; $info['removable'] = false; } else { $info['internal'] = false; $info['removable'] = true; } $appPath = self::getAppPath($app); if($appPath !== false) { $appIcon = $appPath . '/img/' . $app . '.svg'; if (file_exists($appIcon)) { $info['preview'] = \OC::$server->getURLGenerator()->imagePath($app, $app . '.svg'); $info['previewAsIcon'] = true; } else { $appIcon = $appPath . '/img/app.svg'; if (file_exists($appIcon)) { $info['preview'] = \OC::$server->getURLGenerator()->imagePath($app, 'app.svg'); $info['previewAsIcon'] = true; } } } // fix documentation if (isset($info['documentation']) && is_array($info['documentation'])) { foreach ($info['documentation'] as $key => $url) { // If it is not an absolute URL we assume it is a key // i.e. admin-ldap will get converted to go.php?to=admin-ldap if (stripos($url, 'https://') !== 0 && stripos($url, 'http://') !== 0) { $url = $urlGenerator->linkToDocs($url); } $info['documentation'][$key] = $url; } } $info['version'] = OC_App::getAppVersion($app); $appList[] = $info; } } return $appList; } /** * Returns the internal app ID or false * @param string $ocsID * @return string|false */ public static function getInternalAppIdByOcs($ocsID) { if(is_numeric($ocsID)) { $idArray = \OC::$server->getAppConfig()->getValues(false, 'ocsid'); if(array_search($ocsID, $idArray)) { return array_search($ocsID, $idArray); } } return false; } public static function shouldUpgrade($app) { $versions = self::getAppVersions(); $currentVersion = OC_App::getAppVersion($app); if ($currentVersion && isset($versions[$app])) { $installedVersion = $versions[$app]; if (!version_compare($currentVersion, $installedVersion, '=')) { return true; } } return false; } /** * Adjust the number of version parts of $version1 to match * the number of version parts of $version2. * * @param string $version1 version to adjust * @param string $version2 version to take the number of parts from * @return string shortened $version1 */ private static function adjustVersionParts($version1, $version2) { $version1 = explode('.', $version1); $version2 = explode('.', $version2); // reduce $version1 to match the number of parts in $version2 while (count($version1) > count($version2)) { array_pop($version1); } // if $version1 does not have enough parts, add some while (count($version1) < count($version2)) { $version1[] = '0'; } return implode('.', $version1); } /** * Check whether the current ownCloud version matches the given * application's version requirements. * * The comparison is made based on the number of parts that the * app info version has. For example for ownCloud 6.0.3 if the * app info version is expecting version 6.0, the comparison is * made on the first two parts of the ownCloud version. * This means that it's possible to specify "requiremin" => 6 * and "requiremax" => 6 and it will still match ownCloud 6.0.3. * * @param string $ocVersion ownCloud version to check against * @param array $appInfo app info (from xml) * * @return boolean true if compatible, otherwise false */ public static function isAppCompatible($ocVersion, $appInfo) { $requireMin = ''; $requireMax = ''; if (isset($appInfo['dependencies']['nextcloud']['@attributes']['min-version'])) { $requireMin = $appInfo['dependencies']['nextcloud']['@attributes']['min-version']; } elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['min-version'])) { $requireMin = $appInfo['dependencies']['owncloud']['@attributes']['min-version']; } else if (isset($appInfo['requiremin'])) { $requireMin = $appInfo['requiremin']; } else if (isset($appInfo['require'])) { $requireMin = $appInfo['require']; } if (isset($appInfo['dependencies']['nextcloud']['@attributes']['max-version'])) { $requireMax = $appInfo['dependencies']['nextcloud']['@attributes']['max-version']; } elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['max-version'])) { $requireMax = $appInfo['dependencies']['owncloud']['@attributes']['max-version']; } else if (isset($appInfo['requiremax'])) { $requireMax = $appInfo['requiremax']; } if (is_array($ocVersion)) { $ocVersion = implode('.', $ocVersion); } if (!empty($requireMin) && version_compare(self::adjustVersionParts($ocVersion, $requireMin), $requireMin, '<') ) { return false; } if (!empty($requireMax) && version_compare(self::adjustVersionParts($ocVersion, $requireMax), $requireMax, '>') ) { return false; } return true; } /** * get the installed version of all apps */ public static function getAppVersions() { static $versions; if(!$versions) { $appConfig = \OC::$server->getAppConfig(); $versions = $appConfig->getValues(false, 'installed_version'); } return $versions; } /** * @param string $app * @param \OCP\IConfig $config * @param \OCP\IL10N $l * @return bool * * @throws Exception if app is not compatible with this version of ownCloud * @throws Exception if no app-name was specified */ public function installApp($app, \OCP\IConfig $config, \OCP\IL10N $l) { if ($app !== false) { // check if the app is compatible with this version of ownCloud $info = self::getAppInfo($app); if(!is_array($info)) { throw new \Exception( $l->t('App "%s" cannot be installed because appinfo file cannot be read.', [$info['name']] ) ); } $version = \OCP\Util::getVersion(); if (!self::isAppCompatible($version, $info)) { throw new \Exception( $l->t('App "%s" cannot be installed because it is not compatible with this version of the server.', array($info['name']) ) ); } // check for required dependencies self::checkAppDependencies($config, $l, $info); $config->setAppValue($app, 'enabled', 'yes'); if (isset($appData['id'])) { $config->setAppValue($app, 'ocsid', $appData['id']); } if(isset($info['settings']) && is_array($info['settings'])) { $appPath = self::getAppPath($app); self::registerAutoloading($app, $appPath); \OC::$server->getSettingsManager()->setupSettings($info['settings']); } \OC_Hook::emit('OC_App', 'post_enable', array('app' => $app)); } else { if(empty($appName) ) { throw new \Exception($l->t("No app name specified")); } else { throw new \Exception($l->t("App '%s' could not be installed!", $appName)); } } return $app; } /** * update the database for the app and call the update script * * @param string $appId * @return bool */ public static function updateApp($appId) { $appPath = self::getAppPath($appId); if($appPath === false) { return false; } $appData = self::getAppInfo($appId); self::executeRepairSteps($appId, $appData['repair-steps']['pre-migration']); if (file_exists($appPath . '/appinfo/database.xml')) { OC_DB::updateDbFromStructure($appPath . '/appinfo/database.xml'); } self::executeRepairSteps($appId, $appData['repair-steps']['post-migration']); self::setupLiveMigrations($appId, $appData['repair-steps']['live-migration']); unset(self::$appVersion[$appId]); // run upgrade code if (file_exists($appPath . '/appinfo/update.php')) { self::loadApp($appId); include $appPath . '/appinfo/update.php'; } self::registerAutoloading($appId, $appPath); self::setupBackgroundJobs($appData['background-jobs']); if(isset($appData['settings']) && is_array($appData['settings'])) { \OC::$server->getSettingsManager()->setupSettings($appData['settings']); } //set remote/public handlers if (array_key_exists('ocsid', $appData)) { \OC::$server->getConfig()->setAppValue($appId, 'ocsid', $appData['ocsid']); } elseif(\OC::$server->getConfig()->getAppValue($appId, 'ocsid', null) !== null) { \OC::$server->getConfig()->deleteAppValue($appId, 'ocsid'); } foreach ($appData['remote'] as $name => $path) { \OC::$server->getConfig()->setAppValue('core', 'remote_' . $name, $appId . '/' . $path); } foreach ($appData['public'] as $name => $path) { \OC::$server->getConfig()->setAppValue('core', 'public_' . $name, $appId . '/' . $path); } self::setAppTypes($appId); $version = \OC_App::getAppVersion($appId); \OC::$server->getAppConfig()->setValue($appId, 'installed_version', $version); \OC::$server->getEventDispatcher()->dispatch(ManagerEvent::EVENT_APP_UPDATE, new ManagerEvent( ManagerEvent::EVENT_APP_UPDATE, $appId )); return true; } /** * @param string $appId * @param string[] $steps * @throws \OC\NeedsUpdateException */ public static function executeRepairSteps($appId, array $steps) { if (empty($steps)) { return; } // load the app self::loadApp($appId); $dispatcher = OC::$server->getEventDispatcher(); // load the steps $r = new Repair([], $dispatcher); foreach ($steps as $step) { try { $r->addStep($step); } catch (Exception $ex) { $r->emit('\OC\Repair', 'error', [$ex->getMessage()]); \OC::$server->getLogger()->logException($ex); } } // run the steps $r->run(); } public static function setupBackgroundJobs(array $jobs) { $queue = \OC::$server->getJobList(); foreach ($jobs as $job) { $queue->add($job); } } /** * @param string $appId * @param string[] $steps */ private static function setupLiveMigrations($appId, array $steps) { $queue = \OC::$server->getJobList(); foreach ($steps as $step) { $queue->add('OC\Migration\BackgroundRepair', [ 'app' => $appId, 'step' => $step]); } } /** * @param string $appId * @return \OC\Files\View|false */ public static function getStorage($appId) { if (OC_App::isEnabled($appId)) { //sanity check if (\OC::$server->getUserSession()->isLoggedIn()) { $view = new \OC\Files\View('/' . OC_User::getUser()); if (!$view->file_exists($appId)) { $view->mkdir($appId); } return new \OC\Files\View('/' . OC_User::getUser() . '/' . $appId); } else { \OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ', user not logged in', \OCP\Util::ERROR); return false; } } else { \OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ' not enabled', \OCP\Util::ERROR); return false; } } protected static function findBestL10NOption($options, $lang) { $fallback = $similarLangFallback = $englishFallback = false; $lang = strtolower($lang); $similarLang = $lang; if (strpos($similarLang, '_')) { // For "de_DE" we want to find "de" and the other way around $similarLang = substr($lang, 0, strpos($lang, '_')); } foreach ($options as $option) { if (is_array($option)) { if ($fallback === false) { $fallback = $option['@value']; } if (!isset($option['@attributes']['lang'])) { continue; } $attributeLang = strtolower($option['@attributes']['lang']); if ($attributeLang === $lang) { return $option['@value']; } if ($attributeLang === $similarLang) { $similarLangFallback = $option['@value']; } else if (strpos($attributeLang, $similarLang . '_') === 0) { if ($similarLangFallback === false) { $similarLangFallback = $option['@value']; } } } else { $englishFallback = $option; } } if ($similarLangFallback !== false) { return $similarLangFallback; } else if ($englishFallback !== false) { return $englishFallback; } return (string) $fallback; } /** * parses the app data array and enhanced the 'description' value * * @param array $data the app data * @param string $lang * @return array improved app data */ public static function parseAppInfo(array $data, $lang = null) { if ($lang && isset($data['name']) && is_array($data['name'])) { $data['name'] = self::findBestL10NOption($data['name'], $lang); } if ($lang && isset($data['summary']) && is_array($data['summary'])) { $data['summary'] = self::findBestL10NOption($data['summary'], $lang); } if ($lang && isset($data['description']) && is_array($data['description'])) { $data['description'] = trim(self::findBestL10NOption($data['description'], $lang)); } else if (isset($data['description']) && is_string($data['description'])) { $data['description'] = trim($data['description']); } else { $data['description'] = ''; } return $data; } /** * @param \OCP\IConfig $config * @param \OCP\IL10N $l * @param array $info * @throws \Exception */ public static function checkAppDependencies($config, $l, $info) { $dependencyAnalyzer = new DependencyAnalyzer(new Platform($config), $l); $missing = $dependencyAnalyzer->analyze($info); if (!empty($missing)) { $missingMsg = join(PHP_EOL, $missing); throw new \Exception( $l->t('App "%s" cannot be installed because the following dependencies are not fulfilled: %s', [$info['name'], $missingMsg] ) ); } } } db/statementwrapper.php 0000604 00000006601 15247162136 0011255 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * small wrapper around \Doctrine\DBAL\Driver\Statement to make it behave, more like an MDB2 Statement * * @method boolean bindValue(mixed $param, mixed $value, integer $type = null); * @method string errorCode(); * @method array errorInfo(); * @method integer rowCount(); * @method array fetchAll(integer $fetchMode = null); */ class OC_DB_StatementWrapper { /** * @var \Doctrine\DBAL\Driver\Statement */ private $statement = null; private $isManipulation = false; private $lastArguments = array(); /** * @param boolean $isManipulation */ public function __construct($statement, $isManipulation) { $this->statement = $statement; $this->isManipulation = $isManipulation; } /** * pass all other function directly to the \Doctrine\DBAL\Driver\Statement */ public function __call($name,$arguments) { return call_user_func_array(array($this->statement,$name), $arguments); } /** * make execute return the result instead of a bool * * @param array $input * @return \OC_DB_StatementWrapper|int */ public function execute($input= []) { $this->lastArguments = $input; if (count($input) > 0) { $result = $this->statement->execute($input); } else { $result = $this->statement->execute(); } if ($result === false) { return false; } if ($this->isManipulation) { $count = $this->statement->rowCount(); return $count; } else { return $this; } } /** * provide an alias for fetch * * @return mixed */ public function fetchRow() { return $this->statement->fetch(); } /** * Provide a simple fetchOne. * * fetch single column from the next row * @param int $column the column number to fetch * @return string */ public function fetchOne($column = 0) { return $this->statement->fetchColumn($column); } /** * Binds a PHP variable to a corresponding named or question mark placeholder in the * SQL statement that was use to prepare the statement. * * @param mixed $column Either the placeholder name or the 1-indexed placeholder index * @param mixed $variable The variable to bind * @param integer|null $type one of the PDO::PARAM_* constants * @param integer|null $length max length when using an OUT bind * @return boolean */ public function bindParam($column, &$variable, $type = null, $length = null){ return $this->statement->bindParam($column, $variable, $type, $length); } } db.php 0000604 00000016757 15247162136 0005665 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andreas Fischer <bantu@owncloud.com> * @author Bart Visscher <bartv@thisnet.nl> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * This class manages the access to the database. It basically is a wrapper for * Doctrine with some adaptions. */ class OC_DB { /** * get MDB2 schema manager * * @return \OC\DB\MDB2SchemaManager */ private static function getMDB2SchemaManager() { return new \OC\DB\MDB2SchemaManager(\OC::$server->getDatabaseConnection()); } /** * Prepare a SQL query * @param string $query Query string * @param int $limit * @param int $offset * @param bool $isManipulation * @throws \OC\DatabaseException * @return OC_DB_StatementWrapper prepared SQL query * * SQL query via Doctrine prepare(), needs to be execute()'d! */ static public function prepare( $query , $limit = null, $offset = null, $isManipulation = null) { $connection = \OC::$server->getDatabaseConnection(); if ($isManipulation === null) { //try to guess, so we return the number of rows on manipulations $isManipulation = self::isManipulation($query); } // return the result try { $result =$connection->prepare($query, $limit, $offset); } catch (\Doctrine\DBAL\DBALException $e) { throw new \OC\DatabaseException($e->getMessage(), $query); } // differentiate between query and manipulation $result = new OC_DB_StatementWrapper($result, $isManipulation); return $result; } /** * tries to guess the type of statement based on the first 10 characters * the current check allows some whitespace but does not work with IF EXISTS or other more complex statements * * @param string $sql * @return bool */ static public function isManipulation( $sql ) { $selectOccurrence = stripos($sql, 'SELECT'); if ($selectOccurrence !== false && $selectOccurrence < 10) { return false; } $insertOccurrence = stripos($sql, 'INSERT'); if ($insertOccurrence !== false && $insertOccurrence < 10) { return true; } $updateOccurrence = stripos($sql, 'UPDATE'); if ($updateOccurrence !== false && $updateOccurrence < 10) { return true; } $deleteOccurrence = stripos($sql, 'DELETE'); if ($deleteOccurrence !== false && $deleteOccurrence < 10) { return true; } return false; } /** * execute a prepared statement, on error write log and throw exception * @param mixed $stmt OC_DB_StatementWrapper, * an array with 'sql' and optionally 'limit' and 'offset' keys * .. or a simple sql query string * @param array $parameters * @return OC_DB_StatementWrapper * @throws \OC\DatabaseException */ static public function executeAudited( $stmt, array $parameters = null) { if (is_string($stmt)) { // convert to an array with 'sql' if (stripos($stmt, 'LIMIT') !== false) { //OFFSET requires LIMIT, so we only need to check for LIMIT // TODO try to convert LIMIT OFFSET notation to parameters $message = 'LIMIT and OFFSET are forbidden for portability reasons,' . ' pass an array with \'limit\' and \'offset\' instead'; throw new \OC\DatabaseException($message); } $stmt = array('sql' => $stmt, 'limit' => null, 'offset' => null); } if (is_array($stmt)) { // convert to prepared statement if ( ! array_key_exists('sql', $stmt) ) { $message = 'statement array must at least contain key \'sql\''; throw new \OC\DatabaseException($message); } if ( ! array_key_exists('limit', $stmt) ) { $stmt['limit'] = null; } if ( ! array_key_exists('limit', $stmt) ) { $stmt['offset'] = null; } $stmt = self::prepare($stmt['sql'], $stmt['limit'], $stmt['offset']); } self::raiseExceptionOnError($stmt, 'Could not prepare statement'); if ($stmt instanceof OC_DB_StatementWrapper) { $result = $stmt->execute($parameters); self::raiseExceptionOnError($result, 'Could not execute statement'); } else { if (is_object($stmt)) { $message = 'Expected a prepared statement or array got ' . get_class($stmt); } else { $message = 'Expected a prepared statement or array got ' . gettype($stmt); } throw new \OC\DatabaseException($message); } return $result; } /** * saves database schema to xml file * @param string $file name of file * @param int $mode * @return bool * * TODO: write more documentation */ public static function getDbStructure($file) { $schemaManager = self::getMDB2SchemaManager(); return $schemaManager->getDbStructure($file); } /** * Creates tables from XML file * @param string $file file to read structure from * @return bool * * TODO: write more documentation */ public static function createDbFromStructure( $file ) { $schemaManager = self::getMDB2SchemaManager(); $result = $schemaManager->createDbFromStructure($file); return $result; } /** * update the database schema * @param string $file file to read structure from * @throws Exception * @return string|boolean */ public static function updateDbFromStructure($file) { $schemaManager = self::getMDB2SchemaManager(); try { $result = $schemaManager->updateDbFromStructure($file); } catch (Exception $e) { \OCP\Util::writeLog('core', 'Failed to update database structure ('.$e.')', \OCP\Util::FATAL); throw $e; } return $result; } /** * remove all tables defined in a database structure xml file * @param string $file the xml file describing the tables */ public static function removeDBStructure($file) { $schemaManager = self::getMDB2SchemaManager(); $schemaManager->removeDBStructure($file); } /** * check if a result is an error and throws an exception, works with \Doctrine\DBAL\DBALException * @param mixed $result * @param string $message * @return void * @throws \OC\DatabaseException */ public static function raiseExceptionOnError($result, $message = null) { if($result === false) { if ($message === null) { $message = self::getErrorMessage(); } else { $message .= ', Root cause:' . self::getErrorMessage(); } throw new \OC\DatabaseException($message, \OC::$server->getDatabaseConnection()->errorCode()); } } /** * returns the error code and message as a string for logging * works with DoctrineException * @return string */ public static function getErrorMessage() { $connection = \OC::$server->getDatabaseConnection(); return $connection->getError(); } /** * Checks if a table exists in the database - the database prefix will be prepended * * @param string $table * @return bool * @throws \OC\DatabaseException */ public static function tableExists($table) { $connection = \OC::$server->getDatabaseConnection(); return $connection->tableExists($table); } } helper.php 0000604 00000044417 15247162136 0006551 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Björn Schießle <bjoern@schiessle.org> * @author Christopher Schäpers <kondou@ts.unde.re> * @author Clark Tomlinson <fallen013@gmail.com> * @author Fabian Henze <flyser42@gmx.de> * @author Felix Moeller <mail@felixmoeller.de> * @author Georg Ehrke <georg@owncloud.com> * @author Jakob Sack <mail@jakobsack.de> * @author Jan-Christoph Borchardt <hey@jancborchardt.net> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Michael Gapczynski <GapczynskiM@gmail.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Olivier Paroz <github@oparoz.com> * @author Pellaeon Lin <nfsmwlin@gmail.com> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Simon Könnecke <simonkoennecke@gmail.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ use Symfony\Component\Process\ExecutableFinder; /** * Collection of useful functions */ class OC_Helper { private static $templateManager; /** * Creates an absolute url for public use * @param string $service id * @param bool $add_slash * @return string the url * * Returns a absolute url to the given service. */ public static function linkToPublic($service, $add_slash = false) { if ($service === 'files') { $url = OC::$server->getURLGenerator()->getAbsoluteURL('/s'); } else { $url = OC::$server->getURLGenerator()->getAbsoluteURL(OC::$server->getURLGenerator()->linkTo('', 'public.php').'?service='.$service); } return $url . (($add_slash && $service[strlen($service) - 1] != '/') ? '/' : ''); } /** * Make a human file size * @param int $bytes file size in bytes * @return string a human readable file size * * Makes 2048 to 2 kB. */ public static function humanFileSize($bytes) { if ($bytes < 0) { return "?"; } if ($bytes < 1024) { return "$bytes B"; } $bytes = round($bytes / 1024, 0); if ($bytes < 1024) { return "$bytes KB"; } $bytes = round($bytes / 1024, 1); if ($bytes < 1024) { return "$bytes MB"; } $bytes = round($bytes / 1024, 1); if ($bytes < 1024) { return "$bytes GB"; } $bytes = round($bytes / 1024, 1); if ($bytes < 1024) { return "$bytes TB"; } $bytes = round($bytes / 1024, 1); return "$bytes PB"; } /** * Make a php file size * @param int $bytes file size in bytes * @return string a php parseable file size * * Makes 2048 to 2k and 2^41 to 2048G */ public static function phpFileSize($bytes) { if ($bytes < 0) { return "?"; } if ($bytes < 1024) { return $bytes . "B"; } $bytes = round($bytes / 1024, 1); if ($bytes < 1024) { return $bytes . "K"; } $bytes = round($bytes / 1024, 1); if ($bytes < 1024) { return $bytes . "M"; } $bytes = round($bytes / 1024, 1); return $bytes . "G"; } /** * Make a computer file size * @param string $str file size in human readable format * @return float a file size in bytes * * Makes 2kB to 2048. * * Inspired by: http://www.php.net/manual/en/function.filesize.php#92418 */ public static function computerFileSize($str) { $str = strtolower($str); if (is_numeric($str)) { return floatval($str); } $bytes_array = array( 'b' => 1, 'k' => 1024, 'kb' => 1024, 'mb' => 1024 * 1024, 'm' => 1024 * 1024, 'gb' => 1024 * 1024 * 1024, 'g' => 1024 * 1024 * 1024, 'tb' => 1024 * 1024 * 1024 * 1024, 't' => 1024 * 1024 * 1024 * 1024, 'pb' => 1024 * 1024 * 1024 * 1024 * 1024, 'p' => 1024 * 1024 * 1024 * 1024 * 1024, ); $bytes = floatval($str); if (preg_match('#([kmgtp]?b?)$#si', $str, $matches) && !empty($bytes_array[$matches[1]])) { $bytes *= $bytes_array[$matches[1]]; } else { return false; } $bytes = round($bytes); return $bytes; } /** * Recursive copying of folders * @param string $src source folder * @param string $dest target folder * */ static function copyr($src, $dest) { if (is_dir($src)) { if (!is_dir($dest)) { mkdir($dest); } $files = scandir($src); foreach ($files as $file) { if ($file != "." && $file != "..") { self::copyr("$src/$file", "$dest/$file"); } } } elseif (file_exists($src) && !\OC\Files\Filesystem::isFileBlacklisted($src)) { copy($src, $dest); } } /** * Recursive deletion of folders * @param string $dir path to the folder * @param bool $deleteSelf if set to false only the content of the folder will be deleted * @return bool */ static function rmdirr($dir, $deleteSelf = true) { if (is_dir($dir)) { $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST ); foreach ($files as $fileInfo) { /** @var SplFileInfo $fileInfo */ if ($fileInfo->isLink()) { unlink($fileInfo->getPathname()); } else if ($fileInfo->isDir()) { rmdir($fileInfo->getRealPath()); } else { unlink($fileInfo->getRealPath()); } } if ($deleteSelf) { rmdir($dir); } } elseif (file_exists($dir)) { if ($deleteSelf) { unlink($dir); } } if (!$deleteSelf) { return true; } return !file_exists($dir); } /** * @return \OC\Files\Type\TemplateManager */ static public function getFileTemplateManager() { if (!self::$templateManager) { self::$templateManager = new \OC\Files\Type\TemplateManager(); } return self::$templateManager; } /** * detect if a given program is found in the search PATH * * @param string $name * @param bool $path * @internal param string $program name * @internal param string $optional search path, defaults to $PATH * @return bool true if executable program found in path */ public static function canExecute($name, $path = false) { // path defaults to PATH from environment if not set if ($path === false) { $path = getenv("PATH"); } // we look for an executable file of that name $exts = [""]; $check_fn = "is_executable"; // Default check will be done with $path directories : $dirs = explode(PATH_SEPARATOR, $path); // WARNING : We have to check if open_basedir is enabled : $obd = OC::$server->getIniWrapper()->getString('open_basedir'); if ($obd != "none") { $obd_values = explode(PATH_SEPARATOR, $obd); if (count($obd_values) > 0 and $obd_values[0]) { // open_basedir is in effect ! // We need to check if the program is in one of these dirs : $dirs = $obd_values; } } foreach ($dirs as $dir) { foreach ($exts as $ext) { if ($check_fn("$dir/$name" . $ext)) return true; } } return false; } /** * copy the contents of one stream to another * * @param resource $source * @param resource $target * @return array the number of bytes copied and result */ public static function streamCopy($source, $target) { if (!$source or !$target) { return array(0, false); } $bufSize = 8192; $result = true; $count = 0; while (!feof($source)) { $buf = fread($source, $bufSize); $bytesWritten = fwrite($target, $buf); if ($bytesWritten !== false) { $count += $bytesWritten; } // note: strlen is expensive so only use it when necessary, // on the last block if ($bytesWritten === false || ($bytesWritten < $bufSize && $bytesWritten < strlen($buf)) ) { // write error, could be disk full ? $result = false; break; } } return array($count, $result); } /** * Adds a suffix to the name in case the file exists * * @param string $path * @param string $filename * @return string */ public static function buildNotExistingFileName($path, $filename) { $view = \OC\Files\Filesystem::getView(); return self::buildNotExistingFileNameForView($path, $filename, $view); } /** * Adds a suffix to the name in case the file exists * * @param string $path * @param string $filename * @return string */ public static function buildNotExistingFileNameForView($path, $filename, \OC\Files\View $view) { if ($path === '/') { $path = ''; } if ($pos = strrpos($filename, '.')) { $name = substr($filename, 0, $pos); $ext = substr($filename, $pos); } else { $name = $filename; $ext = ''; } $newpath = $path . '/' . $filename; if ($view->file_exists($newpath)) { if (preg_match_all('/\((\d+)\)/', $name, $matches, PREG_OFFSET_CAPTURE)) { //Replace the last "(number)" with "(number+1)" $last_match = count($matches[0]) - 1; $counter = $matches[1][$last_match][0] + 1; $offset = $matches[0][$last_match][1]; $match_length = strlen($matches[0][$last_match][0]); } else { $counter = 2; $match_length = 0; $offset = false; } do { if ($offset) { //Replace the last "(number)" with "(number+1)" $newname = substr_replace($name, '(' . $counter . ')', $offset, $match_length); } else { $newname = $name . ' (' . $counter . ')'; } $newpath = $path . '/' . $newname . $ext; $counter++; } while ($view->file_exists($newpath)); } return $newpath; } /** * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is. * * @param array $input The array to work on * @param int $case Either MB_CASE_UPPER or MB_CASE_LOWER (default) * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8 * @return array * * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is. * based on http://www.php.net/manual/en/function.array-change-key-case.php#107715 * */ public static function mb_array_change_key_case($input, $case = MB_CASE_LOWER, $encoding = 'UTF-8') { $case = ($case != MB_CASE_UPPER) ? MB_CASE_LOWER : MB_CASE_UPPER; $ret = array(); foreach ($input as $k => $v) { $ret[mb_convert_case($k, $case, $encoding)] = $v; } return $ret; } /** * performs a search in a nested array * @param array $haystack the array to be searched * @param string $needle the search string * @param string $index optional, only search this key name * @return mixed the key of the matching field, otherwise false * * performs a search in a nested array * * taken from http://www.php.net/manual/en/function.array-search.php#97645 */ public static function recursiveArraySearch($haystack, $needle, $index = null) { $aIt = new RecursiveArrayIterator($haystack); $it = new RecursiveIteratorIterator($aIt); while ($it->valid()) { if (((isset($index) AND ($it->key() == $index)) OR (!isset($index))) AND ($it->current() == $needle)) { return $aIt->key(); } $it->next(); } return false; } /** * calculates the maximum upload size respecting system settings, free space and user quota * * @param string $dir the current folder where the user currently operates * @param int $freeSpace the number of bytes free on the storage holding $dir, if not set this will be received from the storage directly * @return int number of bytes representing */ public static function maxUploadFilesize($dir, $freeSpace = null) { if (is_null($freeSpace) || $freeSpace < 0){ $freeSpace = self::freeSpace($dir); } return min($freeSpace, self::uploadLimit()); } /** * Calculate free space left within user quota * * @param string $dir the current folder where the user currently operates * @return int number of bytes representing */ public static function freeSpace($dir) { $freeSpace = \OC\Files\Filesystem::free_space($dir); if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) { $freeSpace = max($freeSpace, 0); return $freeSpace; } else { return (INF > 0)? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188 } } /** * Calculate PHP upload limit * * @return int PHP upload file size limit */ public static function uploadLimit() { $ini = \OC::$server->getIniWrapper(); $upload_max_filesize = OCP\Util::computerFileSize($ini->get('upload_max_filesize')); $post_max_size = OCP\Util::computerFileSize($ini->get('post_max_size')); if ((int)$upload_max_filesize === 0 and (int)$post_max_size === 0) { return INF; } elseif ((int)$upload_max_filesize === 0 or (int)$post_max_size === 0) { return max($upload_max_filesize, $post_max_size); //only the non 0 value counts } else { return min($upload_max_filesize, $post_max_size); } } /** * Checks if a function is available * * @param string $function_name * @return bool */ public static function is_function_enabled($function_name) { if (!function_exists($function_name)) { return false; } $ini = \OC::$server->getIniWrapper(); $disabled = explode(',', $ini->get('disable_functions')); $disabled = array_map('trim', $disabled); if (in_array($function_name, $disabled)) { return false; } $disabled = explode(',', $ini->get('suhosin.executor.func.blacklist')); $disabled = array_map('trim', $disabled); if (in_array($function_name, $disabled)) { return false; } return true; } /** * Try to find a program * * @param string $program * @return null|string */ public static function findBinaryPath($program) { $memcache = \OC::$server->getMemCacheFactory()->create('findBinaryPath'); if ($memcache->hasKey($program)) { return $memcache->get($program); } $result = null; if (self::is_function_enabled('exec')) { $exeSniffer = new ExecutableFinder(); // Returns null if nothing is found $result = $exeSniffer->find($program); if (empty($result)) { $paths = getenv('PATH'); if (empty($paths)) { $paths = '/usr/local/bin /usr/bin /opt/bin /bin'; } else { $paths = str_replace(':',' ',getenv('PATH')); } $command = 'find ' . $paths . ' -name ' . escapeshellarg($program) . ' 2> /dev/null'; exec($command, $output, $returnCode); if (count($output) > 0) { $result = escapeshellcmd($output[0]); } } } // store the value for 5 minutes $memcache->set($program, $result, 300); return $result; } /** * Calculate the disc space for the given path * * @param string $path * @param \OCP\Files\FileInfo $rootInfo (optional) * @return array * @throws \OCP\Files\NotFoundException */ public static function getStorageInfo($path, $rootInfo = null) { // return storage info without adding mount points $includeExtStorage = \OC::$server->getSystemConfig()->getValue('quota_include_external_storage', false); if (!$rootInfo) { $rootInfo = \OC\Files\Filesystem::getFileInfo($path, $includeExtStorage ? 'ext' : false); } if (!$rootInfo instanceof \OCP\Files\FileInfo) { throw new \OCP\Files\NotFoundException(); } $used = $rootInfo->getSize(); if ($used < 0) { $used = 0; } $quota = \OCP\Files\FileInfo::SPACE_UNLIMITED; $storage = $rootInfo->getStorage(); $sourceStorage = $storage; if ($storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) { $includeExtStorage = false; $sourceStorage = $storage->getSourceStorage(); } if ($includeExtStorage) { if ($storage->instanceOfStorage('\OC\Files\Storage\Home') || $storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage') ) { /** @var \OC\Files\Storage\Home $storage */ $user = $storage->getUser(); } else { $user = \OC::$server->getUserSession()->getUser()->getUID(); } if ($user) { $quota = OC_Util::getUserQuota($user); } else { $quota = \OCP\Files\FileInfo::SPACE_UNLIMITED; } if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) { // always get free space / total space from root + mount points return self::getGlobalStorageInfo(); } } // TODO: need a better way to get total space from storage if ($sourceStorage->instanceOfStorage('\OC\Files\Storage\Wrapper\Quota')) { /** @var \OC\Files\Storage\Wrapper\Quota $storage */ $quota = $sourceStorage->getQuota(); } $free = $sourceStorage->free_space($rootInfo->getInternalPath()); if ($free >= 0) { $total = $free + $used; } else { $total = $free; //either unknown or unlimited } if ($total > 0) { if ($quota > 0 && $total > $quota) { $total = $quota; } // prevent division by zero or error codes (negative values) $relative = round(($used / $total) * 10000) / 100; } else { $relative = 0; } $ownerId = $storage->getOwner($path); $ownerDisplayName = ''; $owner = \OC::$server->getUserManager()->get($ownerId); if($owner) { $ownerDisplayName = $owner->getDisplayName(); } return [ 'free' => $free, 'used' => $used, 'quota' => $quota, 'total' => $total, 'relative' => $relative, 'owner' => $ownerId, 'ownerDisplayName' => $ownerDisplayName, ]; } /** * Get storage info including all mount points and quota * * @return array */ private static function getGlobalStorageInfo() { $quota = OC_Util::getUserQuota(\OCP\User::getUser()); $rootInfo = \OC\Files\Filesystem::getFileInfo('', 'ext'); $used = $rootInfo['size']; if ($used < 0) { $used = 0; } $total = $quota; $free = $quota - $used; if ($total > 0) { if ($quota > 0 && $total > $quota) { $total = $quota; } // prevent division by zero or error codes (negative values) $relative = round(($used / $total) * 10000) / 100; } else { $relative = 0; } return array('free' => $free, 'used' => $used, 'total' => $total, 'relative' => $relative); } /** * Returns whether the config file is set manually to read-only * @return bool */ public static function isReadOnlyConfigEnabled() { return \OC::$server->getConfig()->getSystemValue('config_is_read_only', false); } } util.php 0000604 00000136043 15247162136 0006244 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Adam Williamson <awilliam@redhat.com> * @author Andreas Fischer <bantu@owncloud.com> * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Birk Borkason <daniel.niccoli@gmail.com> * @author Björn Schießle <bjoern@schiessle.org> * @author Brice Maron <brice@bmaron.net> * @author Christopher Schäpers <kondou@ts.unde.re> * @author Christoph Wurst <christoph@owncloud.com> * @author Clark Tomlinson <fallen013@gmail.com> * @author cmeh <cmeh@users.noreply.github.com> * @author Felix Anand Epp <work@felixepp.de> * @author Florin Peter <github@florin-peter.de> * @author Frank Karlitschek <frank@karlitschek.de> * @author Georg Ehrke <georg@owncloud.com> * @author helix84 <helix84@centrum.sk> * @author Individual IT Services <info@individual-it.net> * @author Jakob Sack <mail@jakobsack.de> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Markus Goetz <markus@woboq.com> * @author Martin Mattel <martin.mattel@diemattels.at> * @author Marvin Thomas Rabe <mrabe@marvinrabe.de> * @author Michael Gapczynski <GapczynskiM@gmail.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Stefan Rado <owncloud@sradonia.net> * @author Stefan Weil <sw@weilnetz.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * @author Victor Dubiniuk <dubiniuk@owncloud.com> * @author Vincent Petry <pvince81@owncloud.com> * @author Volkan Gezer <volkangezer@gmail.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ use OCP\IConfig; use OCP\IGroupManager; use OCP\IUser; class OC_Util { public static $scripts = array(); public static $styles = array(); public static $headers = array(); private static $rootMounted = false; private static $fsSetup = false; /** @var array Local cache of version.php */ private static $versionCache = null; protected static function getAppManager() { return \OC::$server->getAppManager(); } private static function initLocalStorageRootFS() { // mount local file backend as root $configDataDirectory = \OC::$server->getSystemConfig()->getValue("datadirectory", OC::$SERVERROOT . "/data"); //first set up the local "root" storage \OC\Files\Filesystem::initMountManager(); if (!self::$rootMounted) { \OC\Files\Filesystem::mount('\OC\Files\Storage\Local', array('datadir' => $configDataDirectory), '/'); self::$rootMounted = true; } } /** * mounting an object storage as the root fs will in essence remove the * necessity of a data folder being present. * TODO make home storage aware of this and use the object storage instead of local disk access * * @param array $config containing 'class' and optional 'arguments' */ private static function initObjectStoreRootFS($config) { // check misconfiguration if (empty($config['class'])) { \OCP\Util::writeLog('files', 'No class given for objectstore', \OCP\Util::ERROR); } if (!isset($config['arguments'])) { $config['arguments'] = array(); } // instantiate object store implementation $name = $config['class']; if (strpos($name, 'OCA\\') === 0 && substr_count($name, '\\') >= 2) { $segments = explode('\\', $name); OC_App::loadApp(strtolower($segments[1])); } $config['arguments']['objectstore'] = new $config['class']($config['arguments']); // mount with plain / root object store implementation $config['class'] = '\OC\Files\ObjectStore\ObjectStoreStorage'; // mount object storage as root \OC\Files\Filesystem::initMountManager(); if (!self::$rootMounted) { \OC\Files\Filesystem::mount($config['class'], $config['arguments'], '/'); self::$rootMounted = true; } } /** * mounting an object storage as the root fs will in essence remove the * necessity of a data folder being present. * * @param array $config containing 'class' and optional 'arguments' */ private static function initObjectStoreMultibucketRootFS($config) { // check misconfiguration if (empty($config['class'])) { \OCP\Util::writeLog('files', 'No class given for objectstore', \OCP\Util::ERROR); } if (!isset($config['arguments'])) { $config['arguments'] = array(); } // instantiate object store implementation $name = $config['class']; if (strpos($name, 'OCA\\') === 0 && substr_count($name, '\\') >= 2) { $segments = explode('\\', $name); OC_App::loadApp(strtolower($segments[1])); } if (!isset($config['arguments']['bucket'])) { $config['arguments']['bucket'] = ''; } // put the root FS always in first bucket for multibucket configuration $config['arguments']['bucket'] .= '0'; $config['arguments']['objectstore'] = new $config['class']($config['arguments']); // mount with plain / root object store implementation $config['class'] = '\OC\Files\ObjectStore\ObjectStoreStorage'; // mount object storage as root \OC\Files\Filesystem::initMountManager(); if (!self::$rootMounted) { \OC\Files\Filesystem::mount($config['class'], $config['arguments'], '/'); self::$rootMounted = true; } } /** * Can be set up * * @param string $user * @return boolean * @description configure the initial filesystem based on the configuration */ public static function setupFS($user = '') { //setting up the filesystem twice can only lead to trouble if (self::$fsSetup) { return false; } \OC::$server->getEventLogger()->start('setup_fs', 'Setup filesystem'); // If we are not forced to load a specific user we load the one that is logged in if ($user === null) { $user = ''; } else if ($user == "" && \OC::$server->getUserSession()->isLoggedIn()) { $user = OC_User::getUser(); } // load all filesystem apps before, so no setup-hook gets lost OC_App::loadApps(array('filesystem')); // the filesystem will finish when $user is not empty, // mark fs setup here to avoid doing the setup from loading // OC_Filesystem if ($user != '') { self::$fsSetup = true; } \OC\Files\Filesystem::initMountManager(); \OC\Files\Filesystem::logWarningWhenAddingStorageWrapper(false); \OC\Files\Filesystem::addStorageWrapper('mount_options', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) { if ($storage->instanceOfStorage('\OC\Files\Storage\Common')) { /** @var \OC\Files\Storage\Common $storage */ $storage->setMountOptions($mount->getOptions()); } return $storage; }); \OC\Files\Filesystem::addStorageWrapper('enable_sharing', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) { if (!$mount->getOption('enable_sharing', true)) { return new \OC\Files\Storage\Wrapper\PermissionsMask([ 'storage' => $storage, 'mask' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_SHARE ]); } return $storage; }); // install storage availability wrapper, before most other wrappers \OC\Files\Filesystem::addStorageWrapper('oc_availability', function ($mountPoint, $storage) { if (!$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage') && !$storage->isLocal()) { return new \OC\Files\Storage\Wrapper\Availability(['storage' => $storage]); } return $storage; }); \OC\Files\Filesystem::addStorageWrapper('oc_encoding', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) { if ($mount->getOption('encoding_compatibility', false) && !$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage') && !$storage->isLocal()) { return new \OC\Files\Storage\Wrapper\Encoding(['storage' => $storage]); } return $storage; }); \OC\Files\Filesystem::addStorageWrapper('oc_quota', function ($mountPoint, $storage) { // set up quota for home storages, even for other users // which can happen when using sharing /** * @var \OC\Files\Storage\Storage $storage */ if ($storage->instanceOfStorage('\OC\Files\Storage\Home') || $storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage') ) { /** @var \OC\Files\Storage\Home $storage */ if (is_object($storage->getUser())) { $user = $storage->getUser()->getUID(); $quota = OC_Util::getUserQuota($user); if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) { return new \OC\Files\Storage\Wrapper\Quota(array('storage' => $storage, 'quota' => $quota, 'root' => 'files')); } } } return $storage; }); OC_Hook::emit('OC_Filesystem', 'preSetup', array('user' => $user)); \OC\Files\Filesystem::logWarningWhenAddingStorageWrapper(true); //check if we are using an object storage $objectStore = \OC::$server->getSystemConfig()->getValue('objectstore', null); $objectStoreMultibucket = \OC::$server->getSystemConfig()->getValue('objectstore_multibucket', null); // use the same order as in ObjectHomeMountProvider if (isset($objectStoreMultibucket)) { self::initObjectStoreMultibucketRootFS($objectStoreMultibucket); } elseif (isset($objectStore)) { self::initObjectStoreRootFS($objectStore); } else { self::initLocalStorageRootFS(); } if ($user != '' && !OCP\User::userExists($user)) { \OC::$server->getEventLogger()->end('setup_fs'); return false; } //if we aren't logged in, there is no use to set up the filesystem if ($user != "") { $userDir = '/' . $user . '/files'; //jail the user into his "home" directory \OC\Files\Filesystem::init($user, $userDir); OC_Hook::emit('OC_Filesystem', 'setup', array('user' => $user, 'user_dir' => $userDir)); } \OC::$server->getEventLogger()->end('setup_fs'); return true; } /** * check if a password is required for each public link * * @return boolean */ public static function isPublicLinkPasswordRequired() { $appConfig = \OC::$server->getAppConfig(); $enforcePassword = $appConfig->getValue('core', 'shareapi_enforce_links_password', 'no'); return ($enforcePassword === 'yes') ? true : false; } /** * check if sharing is disabled for the current user * @param IConfig $config * @param IGroupManager $groupManager * @param IUser|null $user * @return bool */ public static function isSharingDisabledForUser(IConfig $config, IGroupManager $groupManager, $user) { if ($config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') { $groupsList = $config->getAppValue('core', 'shareapi_exclude_groups_list', ''); $excludedGroups = json_decode($groupsList); if (is_null($excludedGroups)) { $excludedGroups = explode(',', $groupsList); $newValue = json_encode($excludedGroups); $config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue); } $usersGroups = $groupManager->getUserGroupIds($user); if (!empty($usersGroups)) { $remainingGroups = array_diff($usersGroups, $excludedGroups); // if the user is only in groups which are disabled for sharing then // sharing is also disabled for the user if (empty($remainingGroups)) { return true; } } } return false; } /** * check if share API enforces a default expire date * * @return boolean */ public static function isDefaultExpireDateEnforced() { $isDefaultExpireDateEnabled = \OCP\Config::getAppValue('core', 'shareapi_default_expire_date', 'no'); $enforceDefaultExpireDate = false; if ($isDefaultExpireDateEnabled === 'yes') { $value = \OCP\Config::getAppValue('core', 'shareapi_enforce_expire_date', 'no'); $enforceDefaultExpireDate = ($value === 'yes') ? true : false; } return $enforceDefaultExpireDate; } /** * Get the quota of a user * * @param string $userId * @return int Quota bytes */ public static function getUserQuota($userId) { $user = \OC::$server->getUserManager()->get($userId); if (is_null($user)) { return \OCP\Files\FileInfo::SPACE_UNLIMITED; } $userQuota = $user->getQuota(); if($userQuota === 'none') { return \OCP\Files\FileInfo::SPACE_UNLIMITED; } return OC_Helper::computerFileSize($userQuota); } /** * copies the skeleton to the users /files * * @param String $userId * @param \OCP\Files\Folder $userDirectory * @throws \RuntimeException */ public static function copySkeleton($userId, \OCP\Files\Folder $userDirectory) { $skeletonDirectory = \OC::$server->getConfig()->getSystemValue('skeletondirectory', \OC::$SERVERROOT . '/core/skeleton'); $instanceId = \OC::$server->getConfig()->getSystemValue('instanceid', ''); if ($instanceId === null) { throw new \RuntimeException('no instance id!'); } $appdata = 'appdata_' . $instanceId; if ($userId === $appdata) { throw new \RuntimeException('username is reserved name: ' . $appdata); } if (!empty($skeletonDirectory)) { \OCP\Util::writeLog( 'files_skeleton', 'copying skeleton for '.$userId.' from '.$skeletonDirectory.' to '.$userDirectory->getFullPath('/'), \OCP\Util::DEBUG ); self::copyr($skeletonDirectory, $userDirectory); // update the file cache $userDirectory->getStorage()->getScanner()->scan('', \OC\Files\Cache\Scanner::SCAN_RECURSIVE); } } /** * copies a directory recursively by using streams * * @param string $source * @param \OCP\Files\Folder $target * @return void */ public static function copyr($source, \OCP\Files\Folder $target) { $logger = \OC::$server->getLogger(); // Verify if folder exists $dir = opendir($source); if($dir === false) { $logger->error(sprintf('Could not opendir "%s"', $source), ['app' => 'core']); return; } // Copy the files while (false !== ($file = readdir($dir))) { if (!\OC\Files\Filesystem::isIgnoredDir($file)) { if (is_dir($source . '/' . $file)) { $child = $target->newFolder($file); self::copyr($source . '/' . $file, $child); } else { $child = $target->newFile($file); $sourceStream = fopen($source . '/' . $file, 'r'); if($sourceStream === false) { $logger->error(sprintf('Could not fopen "%s"', $source . '/' . $file), ['app' => 'core']); closedir($dir); return; } stream_copy_to_stream($sourceStream, $child->fopen('w')); } } } closedir($dir); } /** * @return void */ public static function tearDownFS() { \OC\Files\Filesystem::tearDown(); \OC::$server->getRootFolder()->clearCache(); self::$fsSetup = false; self::$rootMounted = false; } /** * get the current installed version of ownCloud * * @return array */ public static function getVersion() { OC_Util::loadVersion(); return self::$versionCache['OC_Version']; } /** * get the current installed version string of ownCloud * * @return string */ public static function getVersionString() { OC_Util::loadVersion(); return self::$versionCache['OC_VersionString']; } /** * @deprecated the value is of no use anymore * @return string */ public static function getEditionString() { return ''; } /** * @description get the update channel of the current installed of ownCloud. * @return string */ public static function getChannel() { OC_Util::loadVersion(); return \OC::$server->getConfig()->getSystemValue('updater.release.channel', self::$versionCache['OC_Channel']); } /** * @description get the build number of the current installed of ownCloud. * @return string */ public static function getBuild() { OC_Util::loadVersion(); return self::$versionCache['OC_Build']; } /** * @description load the version.php into the session as cache */ private static function loadVersion() { if (self::$versionCache !== null) { return; } $timestamp = filemtime(OC::$SERVERROOT . '/version.php'); require OC::$SERVERROOT . '/version.php'; /** @var $timestamp int */ self::$versionCache['OC_Version_Timestamp'] = $timestamp; /** @var $OC_Version string */ self::$versionCache['OC_Version'] = $OC_Version; /** @var $OC_VersionString string */ self::$versionCache['OC_VersionString'] = $OC_VersionString; /** @var $OC_Build string */ self::$versionCache['OC_Build'] = $OC_Build; /** @var $OC_Channel string */ self::$versionCache['OC_Channel'] = $OC_Channel; } /** * generates a path for JS/CSS files. If no application is provided it will create the path for core. * * @param string $application application to get the files from * @param string $directory directory within this application (css, js, vendor, etc) * @param string $file the file inside of the above folder * @return string the path */ private static function generatePath($application, $directory, $file) { if (is_null($file)) { $file = $application; $application = ""; } if (!empty($application)) { return "$application/$directory/$file"; } else { return "$directory/$file"; } } /** * add a javascript file * * @param string $application application id * @param string|null $file filename * @param bool $prepend prepend the Script to the beginning of the list * @return void */ public static function addScript($application, $file = null, $prepend = false) { $path = OC_Util::generatePath($application, 'js', $file); // core js files need separate handling if ($application !== 'core' && $file !== null) { self::addTranslations ( $application ); } self::addExternalResource($application, $prepend, $path, "script"); } /** * add a javascript file from the vendor sub folder * * @param string $application application id * @param string|null $file filename * @param bool $prepend prepend the Script to the beginning of the list * @return void */ public static function addVendorScript($application, $file = null, $prepend = false) { $path = OC_Util::generatePath($application, 'vendor', $file); self::addExternalResource($application, $prepend, $path, "script"); } /** * add a translation JS file * * @param string $application application id * @param string $languageCode language code, defaults to the current language * @param bool $prepend prepend the Script to the beginning of the list */ public static function addTranslations($application, $languageCode = null, $prepend = false) { if (is_null($languageCode)) { $languageCode = \OC::$server->getL10NFactory()->findLanguage($application); } if (!empty($application)) { $path = "$application/l10n/$languageCode"; } else { $path = "l10n/$languageCode"; } self::addExternalResource($application, $prepend, $path, "script"); } /** * add a css file * * @param string $application application id * @param string|null $file filename * @param bool $prepend prepend the Style to the beginning of the list * @return void */ public static function addStyle($application, $file = null, $prepend = false) { $path = OC_Util::generatePath($application, 'css', $file); self::addExternalResource($application, $prepend, $path, "style"); } /** * add a css file from the vendor sub folder * * @param string $application application id * @param string|null $file filename * @param bool $prepend prepend the Style to the beginning of the list * @return void */ public static function addVendorStyle($application, $file = null, $prepend = false) { $path = OC_Util::generatePath($application, 'vendor', $file); self::addExternalResource($application, $prepend, $path, "style"); } /** * add an external resource css/js file * * @param string $application application id * @param bool $prepend prepend the file to the beginning of the list * @param string $path * @param string $type (script or style) * @return void */ private static function addExternalResource($application, $prepend, $path, $type = "script") { if ($type === "style") { if (!in_array($path, self::$styles)) { if ($prepend === true) { array_unshift ( self::$styles, $path ); } else { self::$styles[] = $path; } } } elseif ($type === "script") { if (!in_array($path, self::$scripts)) { if ($prepend === true) { array_unshift ( self::$scripts, $path ); } else { self::$scripts [] = $path; } } } } /** * Add a custom element to the header * If $text is null then the element will be written as empty element. * So use "" to get a closing tag. * @param string $tag tag name of the element * @param array $attributes array of attributes for the element * @param string $text the text content for the element */ public static function addHeader($tag, $attributes, $text=null) { self::$headers[] = array( 'tag' => $tag, 'attributes' => $attributes, 'text' => $text ); } /** * formats a timestamp in the "right" way * * @param int $timestamp * @param bool $dateOnly option to omit time from the result * @param DateTimeZone|string $timeZone where the given timestamp shall be converted to * @return string timestamp * * @deprecated Use \OC::$server->query('DateTimeFormatter') instead */ public static function formatDate($timestamp, $dateOnly = false, $timeZone = null) { if ($timeZone !== null && !$timeZone instanceof \DateTimeZone) { $timeZone = new \DateTimeZone($timeZone); } /** @var \OC\DateTimeFormatter $formatter */ $formatter = \OC::$server->query('DateTimeFormatter'); if ($dateOnly) { return $formatter->formatDate($timestamp, 'long', $timeZone); } return $formatter->formatDateTime($timestamp, 'long', 'long', $timeZone); } /** * check if the current server configuration is suitable for ownCloud * * @param \OC\SystemConfig $config * @return array arrays with error messages and hints */ public static function checkServer(\OC\SystemConfig $config) { $l = \OC::$server->getL10N('lib'); $errors = array(); $CONFIG_DATADIRECTORY = $config->getValue('datadirectory', OC::$SERVERROOT . '/data'); if (!self::needUpgrade($config) && $config->getValue('installed', false)) { // this check needs to be done every time $errors = self::checkDataDirectoryValidity($CONFIG_DATADIRECTORY); } // Assume that if checkServer() succeeded before in this session, then all is fine. if (\OC::$server->getSession()->exists('checkServer_succeeded') && \OC::$server->getSession()->get('checkServer_succeeded')) { return $errors; } $webServerRestart = false; $setup = new \OC\Setup($config, \OC::$server->getIniWrapper(), \OC::$server->getL10N('lib'), \OC::$server->query(\OCP\Defaults::class), \OC::$server->getLogger(), \OC::$server->getSecureRandom()); $urlGenerator = \OC::$server->getURLGenerator(); $availableDatabases = $setup->getSupportedDatabases(); if (empty($availableDatabases)) { $errors[] = array( 'error' => $l->t('No database drivers (sqlite, mysql, or postgresql) installed.'), 'hint' => '' //TODO: sane hint ); $webServerRestart = true; } // Check if config folder is writable. if(!OC_Helper::isReadOnlyConfigEnabled()) { if (!is_writable(OC::$configDir) or !is_readable(OC::$configDir)) { $errors[] = array( 'error' => $l->t('Cannot write into "config" directory'), 'hint' => $l->t('This can usually be fixed by giving the webserver write access to the config directory. See %s', [$urlGenerator->linkToDocs('admin-dir_permissions')]) ); } } // Check if there is a writable install folder. if ($config->getValue('appstoreenabled', true)) { if (OC_App::getInstallPath() === null || !is_writable(OC_App::getInstallPath()) || !is_readable(OC_App::getInstallPath()) ) { $errors[] = array( 'error' => $l->t('Cannot write into "apps" directory'), 'hint' => $l->t('This can usually be fixed by giving the webserver write access to the apps directory' . ' or disabling the appstore in the config file. See %s', [$urlGenerator->linkToDocs('admin-dir_permissions')]) ); } } // Create root dir. if ($config->getValue('installed', false)) { if (!is_dir($CONFIG_DATADIRECTORY)) { $success = @mkdir($CONFIG_DATADIRECTORY); if ($success) { $errors = array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY)); } else { $errors[] = [ 'error' => $l->t('Cannot create "data" directory'), 'hint' => $l->t('This can usually be fixed by giving the webserver write access to the root directory. See %s', [$urlGenerator->linkToDocs('admin-dir_permissions')]) ]; } } else if (!is_writable($CONFIG_DATADIRECTORY) or !is_readable($CONFIG_DATADIRECTORY)) { //common hint for all file permissions error messages $permissionsHint = $l->t('Permissions can usually be fixed by giving the webserver write access to the root directory. See %s.', [$urlGenerator->linkToDocs('admin-dir_permissions')]); $errors[] = [ 'error' => 'Your data directory is not writable', 'hint' => $permissionsHint ]; } else { $errors = array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY)); } } if (!OC_Util::isSetLocaleWorking()) { $errors[] = array( 'error' => $l->t('Setting locale to %s failed', array('en_US.UTF-8/fr_FR.UTF-8/es_ES.UTF-8/de_DE.UTF-8/ru_RU.UTF-8/' . 'pt_BR.UTF-8/it_IT.UTF-8/ja_JP.UTF-8/zh_CN.UTF-8')), 'hint' => $l->t('Please install one of these locales on your system and restart your webserver.') ); } // Contains the dependencies that should be checked against // classes = class_exists // functions = function_exists // defined = defined // ini = ini_get // If the dependency is not found the missing module name is shown to the EndUser // When adding new checks always verify that they pass on Travis as well // for ini settings, see https://github.com/owncloud/administration/blob/master/travis-ci/custom.ini $dependencies = array( 'classes' => array( 'ZipArchive' => 'zip', 'DOMDocument' => 'dom', 'XMLWriter' => 'XMLWriter', 'XMLReader' => 'XMLReader', ), 'functions' => [ 'xml_parser_create' => 'libxml', 'mb_strcut' => 'mb multibyte', 'ctype_digit' => 'ctype', 'json_encode' => 'JSON', 'gd_info' => 'GD', 'gzencode' => 'zlib', 'iconv' => 'iconv', 'simplexml_load_string' => 'SimpleXML', 'hash' => 'HASH Message Digest Framework', 'curl_init' => 'cURL', 'openssl_verify' => 'OpenSSL', ], 'defined' => array( 'PDO::ATTR_DRIVER_NAME' => 'PDO' ), 'ini' => [ 'default_charset' => 'UTF-8', ], ); $missingDependencies = array(); $invalidIniSettings = []; $moduleHint = $l->t('Please ask your server administrator to install the module.'); /** * FIXME: The dependency check does not work properly on HHVM on the moment * and prevents installation. Once HHVM is more compatible with our * approach to check for these values we should re-enable those * checks. */ $iniWrapper = \OC::$server->getIniWrapper(); if (!self::runningOnHhvm()) { foreach ($dependencies['classes'] as $class => $module) { if (!class_exists($class)) { $missingDependencies[] = $module; } } foreach ($dependencies['functions'] as $function => $module) { if (!function_exists($function)) { $missingDependencies[] = $module; } } foreach ($dependencies['defined'] as $defined => $module) { if (!defined($defined)) { $missingDependencies[] = $module; } } foreach ($dependencies['ini'] as $setting => $expected) { if (is_bool($expected)) { if ($iniWrapper->getBool($setting) !== $expected) { $invalidIniSettings[] = [$setting, $expected]; } } if (is_int($expected)) { if ($iniWrapper->getNumeric($setting) !== $expected) { $invalidIniSettings[] = [$setting, $expected]; } } if (is_string($expected)) { if (strtolower($iniWrapper->getString($setting)) !== strtolower($expected)) { $invalidIniSettings[] = [$setting, $expected]; } } } } foreach($missingDependencies as $missingDependency) { $errors[] = array( 'error' => $l->t('PHP module %s not installed.', array($missingDependency)), 'hint' => $moduleHint ); $webServerRestart = true; } foreach($invalidIniSettings as $setting) { if(is_bool($setting[1])) { $setting[1] = ($setting[1]) ? 'on' : 'off'; } $errors[] = [ 'error' => $l->t('PHP setting "%s" is not set to "%s".', [$setting[0], var_export($setting[1], true)]), 'hint' => $l->t('Adjusting this setting in php.ini will make Nextcloud run again') ]; $webServerRestart = true; } /** * The mbstring.func_overload check can only be performed if the mbstring * module is installed as it will return null if the checking setting is * not available and thus a check on the boolean value fails. * * TODO: Should probably be implemented in the above generic dependency * check somehow in the long-term. */ if($iniWrapper->getBool('mbstring.func_overload') !== null && $iniWrapper->getBool('mbstring.func_overload') === true) { $errors[] = array( 'error' => $l->t('mbstring.func_overload is set to "%s" instead of the expected value "0"', [$iniWrapper->getString('mbstring.func_overload')]), 'hint' => $l->t('To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini') ); } if(function_exists('xml_parser_create') && LIBXML_LOADED_VERSION < 20700 ) { $version = LIBXML_LOADED_VERSION; $major = floor($version/10000); $version -= ($major * 10000); $minor = floor($version/100); $version -= ($minor * 100); $patch = $version; $errors[] = array( 'error' => $l->t('libxml2 2.7.0 is at least required. Currently %s is installed.', [$major . '.' . $minor . '.' . $patch]), 'hint' => $l->t('To fix this issue update your libxml2 version and restart your web server.') ); } if (!self::isAnnotationsWorking()) { $errors[] = array( 'error' => $l->t('PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible.'), 'hint' => $l->t('This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator.') ); } if (!\OC::$CLI && $webServerRestart) { $errors[] = array( 'error' => $l->t('PHP modules have been installed, but they are still listed as missing?'), 'hint' => $l->t('Please ask your server administrator to restart the web server.') ); } $errors = array_merge($errors, self::checkDatabaseVersion()); // Cache the result of this function \OC::$server->getSession()->set('checkServer_succeeded', count($errors) == 0); return $errors; } /** * Check the database version * * @return array errors array */ public static function checkDatabaseVersion() { $l = \OC::$server->getL10N('lib'); $errors = array(); $dbType = \OC::$server->getSystemConfig()->getValue('dbtype', 'sqlite'); if ($dbType === 'pgsql') { // check PostgreSQL version try { $result = \OC_DB::executeAudited('SHOW SERVER_VERSION'); $data = $result->fetchRow(); if (isset($data['server_version'])) { $version = $data['server_version']; if (version_compare($version, '9.0.0', '<')) { $errors[] = array( 'error' => $l->t('PostgreSQL >= 9 required'), 'hint' => $l->t('Please upgrade your database version') ); } } } catch (\Doctrine\DBAL\DBALException $e) { $logger = \OC::$server->getLogger(); $logger->warning('Error occurred while checking PostgreSQL version, assuming >= 9'); $logger->logException($e); } } return $errors; } /** * Check for correct file permissions of data directory * * @param string $dataDirectory * @return array arrays with error messages and hints */ public static function checkDataDirectoryPermissions($dataDirectory) { $l = \OC::$server->getL10N('lib'); $errors = array(); $permissionsModHint = $l->t('Please change the permissions to 0770 so that the directory' . ' cannot be listed by other users.'); $perms = substr(decoct(@fileperms($dataDirectory)), -3); if (substr($perms, -1) !== '0') { chmod($dataDirectory, 0770); clearstatcache(); $perms = substr(decoct(@fileperms($dataDirectory)), -3); if ($perms[2] !== '0') { $errors[] = [ 'error' => $l->t('Your data directory is readable by other users'), 'hint' => $permissionsModHint ]; } } return $errors; } /** * Check that the data directory exists and is valid by * checking the existence of the ".ocdata" file. * * @param string $dataDirectory data directory path * @return array errors found */ public static function checkDataDirectoryValidity($dataDirectory) { $l = \OC::$server->getL10N('lib'); $errors = []; if ($dataDirectory[0] !== '/') { $errors[] = [ 'error' => $l->t('Your data directory must be an absolute path'), 'hint' => $l->t('Check the value of "datadirectory" in your configuration') ]; } if (!file_exists($dataDirectory . '/.ocdata')) { $errors[] = [ 'error' => $l->t('Your data directory is invalid'), 'hint' => $l->t('Ensure there is a file called ".ocdata"' . ' in the root of the data directory.') ]; } return $errors; } /** * Check if the user is logged in, redirects to home if not. With * redirect URL parameter to the request URI. * * @return void */ public static function checkLoggedIn() { // Check if we are a user if (!\OC::$server->getUserSession()->isLoggedIn()) { header('Location: ' . \OC::$server->getURLGenerator()->linkToRoute( 'core.login.showLoginForm', [ 'redirect_url' => \OC::$server->getRequest()->getRequestUri(), ] ) ); exit(); } // Redirect to 2FA challenge selection if 2FA challenge was not solved yet if (\OC::$server->getTwoFactorAuthManager()->needsSecondFactor(\OC::$server->getUserSession()->getUser())) { header('Location: ' . \OC::$server->getURLGenerator()->linkToRoute('core.TwoFactorChallenge.selectChallenge')); exit(); } } /** * Check if the user is a admin, redirects to home if not * * @return void */ public static function checkAdminUser() { OC_Util::checkLoggedIn(); if (!OC_User::isAdminUser(OC_User::getUser())) { header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php')); exit(); } } /** * Check if the user is a subadmin, redirects to home if not * * @return null|boolean $groups where the current user is subadmin */ public static function checkSubAdminUser() { OC_Util::checkLoggedIn(); $userObject = \OC::$server->getUserSession()->getUser(); $isSubAdmin = false; if($userObject !== null) { $isSubAdmin = \OC::$server->getGroupManager()->getSubAdmin()->isSubAdmin($userObject); } if (!$isSubAdmin) { header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php')); exit(); } return true; } /** * Returns the URL of the default page * based on the system configuration and * the apps visible for the current user * * @return string URL */ public static function getDefaultPageUrl() { $urlGenerator = \OC::$server->getURLGenerator(); // Deny the redirect if the URL contains a @ // This prevents unvalidated redirects like ?redirect_url=:user@domain.com if (isset($_REQUEST['redirect_url']) && strpos($_REQUEST['redirect_url'], '@') === false) { $location = $urlGenerator->getAbsoluteURL(urldecode($_REQUEST['redirect_url'])); } else { $defaultPage = \OC::$server->getAppConfig()->getValue('core', 'defaultpage'); if ($defaultPage) { $location = $urlGenerator->getAbsoluteURL($defaultPage); } else { $appId = 'files'; $defaultApps = explode(',', \OCP\Config::getSystemValue('defaultapp', 'files')); // find the first app that is enabled for the current user foreach ($defaultApps as $defaultApp) { $defaultApp = OC_App::cleanAppId(strip_tags($defaultApp)); if (static::getAppManager()->isEnabledForUser($defaultApp)) { $appId = $defaultApp; break; } } if(\OC::$server->getConfig()->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true') { $location = $urlGenerator->getAbsoluteURL('/apps/' . $appId . '/'); } else { $location = $urlGenerator->getAbsoluteURL('/index.php/apps/' . $appId . '/'); } } } return $location; } /** * Redirect to the user default page * * @return void */ public static function redirectToDefaultPage() { $location = self::getDefaultPageUrl(); header('Location: ' . $location); exit(); } /** * get an id unique for this instance * * @return string */ public static function getInstanceId() { $id = \OC::$server->getSystemConfig()->getValue('instanceid', null); if (is_null($id)) { // We need to guarantee at least one letter in instanceid so it can be used as the session_name $id = 'oc' . \OC::$server->getSecureRandom()->generate(10, \OCP\Security\ISecureRandom::CHAR_LOWER.\OCP\Security\ISecureRandom::CHAR_DIGITS); \OC::$server->getSystemConfig()->setValue('instanceid', $id); } return $id; } /** * Public function to sanitize HTML * * This function is used to sanitize HTML and should be applied on any * string or array of strings before displaying it on a web page. * * @param string|array $value * @return string|array an array of sanitized strings or a single sanitized string, depends on the input parameter. */ public static function sanitizeHTML($value) { if (is_array($value)) { $value = array_map(function($value) { return self::sanitizeHTML($value); }, $value); } else { // Specify encoding for PHP<5.4 $value = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8'); } return $value; } /** * Public function to encode url parameters * * This function is used to encode path to file before output. * Encoding is done according to RFC 3986 with one exception: * Character '/' is preserved as is. * * @param string $component part of URI to encode * @return string */ public static function encodePath($component) { $encoded = rawurlencode($component); $encoded = str_replace('%2F', '/', $encoded); return $encoded; } public function createHtaccessTestFile(\OCP\IConfig $config) { // php dev server does not support htaccess if (php_sapi_name() === 'cli-server') { return false; } // testdata $fileName = '/htaccesstest.txt'; $testContent = 'This is used for testing whether htaccess is properly enabled to disallow access from the outside. This file can be safely removed.'; // creating a test file $testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName; if (file_exists($testFile)) {// already running this test, possible recursive call return false; } $fp = @fopen($testFile, 'w'); if (!$fp) { throw new OC\HintException('Can\'t create test file to check for working .htaccess file.', 'Make sure it is possible for the webserver to write to ' . $testFile); } fwrite($fp, $testContent); fclose($fp); return $testContent; } /** * Check if the .htaccess file is working * @param \OCP\IConfig $config * @return bool * @throws Exception * @throws \OC\HintException If the test file can't get written. */ public function isHtaccessWorking(\OCP\IConfig $config) { if (\OC::$CLI || !$config->getSystemValue('check_for_working_htaccess', true)) { return true; } $testContent = $this->createHtaccessTestFile($config); if ($testContent === false) { return false; } $fileName = '/htaccesstest.txt'; $testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName; // accessing the file via http $url = \OC::$server->getURLGenerator()->getAbsoluteURL(OC::$WEBROOT . '/data' . $fileName); try { $content = \OC::$server->getHTTPClientService()->newClient()->get($url)->getBody(); } catch (\Exception $e) { $content = false; } // cleanup @unlink($testFile); /* * If the content is not equal to test content our .htaccess * is working as required */ return $content !== $testContent; } /** * Check if the setlocal call does not work. This can happen if the right * local packages are not available on the server. * * @return bool */ public static function isSetLocaleWorking() { \Patchwork\Utf8\Bootup::initLocale(); if ('' === basename('§')) { return false; } return true; } /** * Check if it's possible to get the inline annotations * * @return bool */ public static function isAnnotationsWorking() { $reflection = new \ReflectionMethod(__METHOD__); $docs = $reflection->getDocComment(); return (is_string($docs) && strlen($docs) > 50); } /** * Check if the PHP module fileinfo is loaded. * * @return bool */ public static function fileInfoLoaded() { return function_exists('finfo_open'); } /** * clear all levels of output buffering * * @return void */ public static function obEnd() { while (ob_get_level()) { ob_end_clean(); } } /** * Checks whether the server is running on Mac OS X * * @return bool true if running on Mac OS X, false otherwise */ public static function runningOnMac() { return (strtoupper(substr(PHP_OS, 0, 6)) === 'DARWIN'); } /** * Checks whether server is running on HHVM * * @return bool True if running on HHVM, false otherwise */ public static function runningOnHhvm() { return defined('HHVM_VERSION'); } /** * Handles the case that there may not be a theme, then check if a "default" * theme exists and take that one * * @return string the theme */ public static function getTheme() { $theme = \OC::$server->getSystemConfig()->getValue("theme", ''); if ($theme === '') { if (is_dir(OC::$SERVERROOT . '/themes/default')) { $theme = 'default'; } } return $theme; } /** * Clear a single file from the opcode cache * This is useful for writing to the config file * in case the opcode cache does not re-validate files * Returns true if successful, false if unsuccessful: * caller should fall back on clearing the entire cache * with clearOpcodeCache() if unsuccessful * * @param string $path the path of the file to clear from the cache * @return bool true if underlying function returns true, otherwise false */ public static function deleteFromOpcodeCache($path) { $ret = false; if ($path) { // APC >= 3.1.1 if (function_exists('apc_delete_file')) { $ret = @apc_delete_file($path); } // Zend OpCache >= 7.0.0, PHP >= 5.5.0 if (function_exists('opcache_invalidate')) { $ret = opcache_invalidate($path); } } return $ret; } /** * Clear the opcode cache if one exists * This is necessary for writing to the config file * in case the opcode cache does not re-validate files * * @return void */ public static function clearOpcodeCache() { // APC if (function_exists('apc_clear_cache')) { apc_clear_cache(); } // Zend Opcache if (function_exists('accelerator_reset')) { accelerator_reset(); } // XCache if (function_exists('xcache_clear_cache')) { if (\OC::$server->getIniWrapper()->getBool('xcache.admin.enable_auth')) { \OCP\Util::writeLog('core', 'XCache opcode cache will not be cleared because "xcache.admin.enable_auth" is enabled.', \OCP\Util::WARN); } else { @xcache_clear_cache(XC_TYPE_PHP, 0); } } // Opcache (PHP >= 5.5) if (function_exists('opcache_reset')) { opcache_reset(); } } /** * Normalize a unicode string * * @param string $value a not normalized string * @return bool|string */ public static function normalizeUnicode($value) { if(Normalizer::isNormalized($value)) { return $value; } $normalizedValue = Normalizer::normalize($value); if ($normalizedValue === null || $normalizedValue === false) { \OC::$server->getLogger()->warning('normalizing failed for "' . $value . '"', ['app' => 'core']); return $value; } return $normalizedValue; } /** * @param boolean|string $file * @return string */ public static function basename($file) { $file = rtrim($file, '/'); $t = explode('/', $file); return array_pop($t); } /** * A human readable string is generated based on version and build number * * @return string */ public static function getHumanVersion() { $version = OC_Util::getVersionString(); $build = OC_Util::getBuild(); if (!empty($build) and OC_Util::getChannel() === 'daily') { $version .= ' Build:' . $build; } return $version; } /** * Returns whether the given file name is valid * * @param string $file file name to check * @return bool true if the file name is valid, false otherwise * @deprecated use \OC\Files\View::verifyPath() */ public static function isValidFileName($file) { $trimmed = trim($file); if ($trimmed === '') { return false; } if (\OC\Files\Filesystem::isIgnoredDir($trimmed)) { return false; } // detect part files if (preg_match('/' . \OCP\Files\FileInfo::BLACKLIST_FILES_REGEX . '/', $trimmed) !== 0) { return false; } foreach (str_split($trimmed) as $char) { if (strpos(\OCP\Constants::FILENAME_INVALID_CHARS, $char) !== false) { return false; } } return true; } /** * Check whether the instance needs to perform an upgrade, * either when the core version is higher or any app requires * an upgrade. * * @param \OC\SystemConfig $config * @return bool whether the core or any app needs an upgrade * @throws \OC\HintException When the upgrade from the given version is not allowed */ public static function needUpgrade(\OC\SystemConfig $config) { if ($config->getValue('installed', false)) { $installedVersion = $config->getValue('version', '0.0.0'); $currentVersion = implode('.', \OCP\Util::getVersion()); $versionDiff = version_compare($currentVersion, $installedVersion); if ($versionDiff > 0) { return true; } else if ($config->getValue('debug', false) && $versionDiff < 0) { // downgrade with debug $installedMajor = explode('.', $installedVersion); $installedMajor = $installedMajor[0] . '.' . $installedMajor[1]; $currentMajor = explode('.', $currentVersion); $currentMajor = $currentMajor[0] . '.' . $currentMajor[1]; if ($installedMajor === $currentMajor) { // Same major, allow downgrade for developers return true; } else { // downgrade attempt, throw exception throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')'); } } else if ($versionDiff < 0) { // downgrade attempt, throw exception throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')'); } // also check for upgrades for apps (independently from the user) $apps = \OC_App::getEnabledApps(false, true); $shouldUpgrade = false; foreach ($apps as $app) { if (\OC_App::shouldUpgrade($app)) { $shouldUpgrade = true; break; } } return $shouldUpgrade; } else { return false; } } } json.php 0000604 00000013533 15247162136 0006236 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Christoph Wurst <christoph@owncloud.com> * @author Felix Moeller <mail@felixmoeller.de> * @author Georg Ehrke <georg@owncloud.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Class OC_JSON * @deprecated Use a AppFramework JSONResponse instead */ class OC_JSON{ static protected $send_content_type_header = false; /** * set Content-Type header to jsonrequest * @deprecated Use a AppFramework JSONResponse instead */ public static function setContentTypeHeader($type='application/json') { if (!self::$send_content_type_header) { // We send json data header( 'Content-Type: '.$type . '; charset=utf-8'); self::$send_content_type_header = true; } } /** * Check if the app is enabled, send json error msg if not * @param string $app * @deprecated Use the AppFramework instead. It will automatically check if the app is enabled. */ public static function checkAppEnabled($app) { if( !OC_App::isEnabled($app)) { $l = \OC::$server->getL10N('lib'); self::error(array( 'data' => array( 'message' => $l->t('Application is not enabled'), 'error' => 'application_not_enabled' ))); exit(); } } /** * Check if the user is logged in, send json error msg if not * @deprecated Use annotation based ACLs from the AppFramework instead */ public static function checkLoggedIn() { $twoFactorAuthManger = \OC::$server->getTwoFactorAuthManager(); if( !\OC::$server->getUserSession()->isLoggedIn() || $twoFactorAuthManger->needsSecondFactor(\OC::$server->getUserSession()->getUser())) { $l = \OC::$server->getL10N('lib'); http_response_code(\OCP\AppFramework\Http::STATUS_UNAUTHORIZED); self::error(array( 'data' => array( 'message' => $l->t('Authentication error'), 'error' => 'authentication_error' ))); exit(); } } /** * Check an ajax get/post call if the request token is valid, send json error msg if not. * @deprecated Use annotation based CSRF checks from the AppFramework instead */ public static function callCheck() { if(!\OC::$server->getRequest()->passesStrictCookieCheck()) { header('Location: '.\OC::$WEBROOT); exit(); } if( !(\OC::$server->getRequest()->passesCSRFCheck())) { $l = \OC::$server->getL10N('lib'); self::error(array( 'data' => array( 'message' => $l->t('Token expired. Please reload page.'), 'error' => 'token_expired' ))); exit(); } } /** * Check if the user is a admin, send json error msg if not. * @deprecated Use annotation based ACLs from the AppFramework instead */ public static function checkAdminUser() { if( !OC_User::isAdminUser(OC_User::getUser())) { $l = \OC::$server->getL10N('lib'); self::error(array( 'data' => array( 'message' => $l->t('Authentication error'), 'error' => 'authentication_error' ))); exit(); } } /** * Check is a given user exists - send json error msg if not * @param string $user * @deprecated Use a AppFramework JSONResponse instead */ public static function checkUserExists($user) { if (!OCP\User::userExists($user)) { $l = \OC::$server->getL10N('lib'); OCP\JSON::error(array('data' => array('message' => $l->t('Unknown user'), 'error' => 'unknown_user' ))); exit; } } /** * Check if the user is a subadmin, send json error msg if not * @deprecated Use annotation based ACLs from the AppFramework instead */ public static function checkSubAdminUser() { $userObject = \OC::$server->getUserSession()->getUser(); $isSubAdmin = false; if($userObject !== null) { $isSubAdmin = \OC::$server->getGroupManager()->getSubAdmin()->isSubAdmin($userObject); } if(!$isSubAdmin) { $l = \OC::$server->getL10N('lib'); self::error(array( 'data' => array( 'message' => $l->t('Authentication error'), 'error' => 'authentication_error' ))); exit(); } } /** * Send json error msg * @deprecated Use a AppFramework JSONResponse instead */ public static function error($data = array()) { $data['status'] = 'error'; self::encodedPrint($data); } /** * Send json success msg * @deprecated Use a AppFramework JSONResponse instead */ public static function success($data = array()) { $data['status'] = 'success'; self::encodedPrint($data); } /** * Convert OC_L10N_String to string, for use in json encodings */ protected static function to_string(&$value) { if ($value instanceof OC_L10N_String) { $value = (string)$value; } } /** * Encode and print $data in json format * @deprecated Use a AppFramework JSONResponse instead */ public static function encodedPrint($data, $setContentType=true) { if($setContentType) { self::setContentTypeHeader(); } echo self::encode($data); } /** * Encode JSON * @deprecated Use a AppFramework JSONResponse instead */ public static function encode($data) { if (is_array($data)) { array_walk_recursive($data, array('OC_JSON', 'to_string')); } return json_encode($data, JSON_HEX_TAG); } } l10n/string.php 0000604 00000004227 15247162136 0007345 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Jakob Sack <mail@jakobsack.de> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ class OC_L10N_String implements JsonSerializable { /** @var \OC\L10N\L10N */ protected $l10n; /** @var string */ protected $text; /** @var array */ protected $parameters; /** @var integer */ protected $count; /** * @param \OC\L10N\L10N $l10n * @param string|string[] $text * @param array $parameters * @param int $count */ public function __construct(\OC\L10N\L10N $l10n, $text, $parameters, $count = 1) { $this->l10n = $l10n; $this->text = $text; $this->parameters = $parameters; $this->count = $count; } public function __toString() { $translations = $this->l10n->getTranslations(); $text = $this->text; if(array_key_exists($this->text, $translations)) { if(is_array($translations[$this->text])) { $fn = $this->l10n->getPluralFormFunction(); $id = $fn($this->count); $text = $translations[$this->text][$id]; } else{ $text = $translations[$this->text]; } } // Replace %n first (won't interfere with vsprintf) $text = str_replace('%n', $this->count, $text); return vsprintf($text, $this->parameters); } public function jsonSerialize() { return $this->__toString(); } } hook.php 0000604 00000010366 15247162136 0006226 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Jakob Sack <mail@jakobsack.de> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Sam Tuke <mail@samtuke.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ class OC_Hook{ public static $thrownExceptions = []; static private $registered = array(); /** * connects a function to a hook * * @param string $signalClass class name of emitter * @param string $signalName name of signal * @param string|object $slotClass class name of slot * @param string $slotName name of slot * @return bool * * This function makes it very easy to connect to use hooks. * * TODO: write example */ static public function connect($signalClass, $signalName, $slotClass, $slotName ) { // If we're trying to connect to an emitting class that isn't // yet registered, register it if( !array_key_exists($signalClass, self::$registered )) { self::$registered[$signalClass] = array(); } // If we're trying to connect to an emitting method that isn't // yet registered, register it with the emitting class if( !array_key_exists( $signalName, self::$registered[$signalClass] )) { self::$registered[$signalClass][$signalName] = array(); } // don't connect hooks twice foreach (self::$registered[$signalClass][$signalName] as $hook) { if ($hook['class'] === $slotClass and $hook['name'] === $slotName) { return false; } } // Connect the hook handler to the requested emitter self::$registered[$signalClass][$signalName][] = array( "class" => $slotClass, "name" => $slotName ); // No chance for failure ;-) return true; } /** * emits a signal * * @param string $signalClass class name of emitter * @param string $signalName name of signal * @param mixed $params default: array() array with additional data * @return bool true if slots exists or false if not * @throws \OC\HintException * @throws \OC\ServerNotAvailableException Emits a signal. To get data from the slot use references! * * TODO: write example */ static public function emit($signalClass, $signalName, $params = []) { // Return false if no hook handlers are listening to this // emitting class if( !array_key_exists($signalClass, self::$registered )) { return false; } // Return false if no hook handlers are listening to this // emitting method if( !array_key_exists( $signalName, self::$registered[$signalClass] )) { return false; } // Call all slots foreach( self::$registered[$signalClass][$signalName] as $i ) { try { call_user_func( array( $i["class"], $i["name"] ), $params ); } catch (Exception $e){ self::$thrownExceptions[] = $e; \OC::$server->getLogger()->logException($e); if($e instanceof \OC\HintException) { throw $e; } if($e instanceof \OC\ServerNotAvailableException) { throw $e; } } } return true; } /** * clear hooks * @param string $signalClass * @param string $signalName */ static public function clear($signalClass='', $signalName='') { if ($signalClass) { if ($signalName) { self::$registered[$signalClass][$signalName]=array(); }else{ self::$registered[$signalClass]=array(); } }else{ self::$registered=array(); } } /** * DO NOT USE! * For unit tests ONLY! */ static public function getHooks() { return self::$registered; } } api.php 0000604 00000034115 15247162136 0006035 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Björn Schießle <bjoern@schiessle.org> * @author Christoph Wurst <christoph@owncloud.com> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Michael Gapczynski <GapczynskiM@gmail.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Tom Needham <tom@owncloud.com> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ use OCP\API; use OCP\AppFramework\Http; class OC_API { /** * API authentication levels */ /** @deprecated Use \OCP\API::GUEST_AUTH instead */ const GUEST_AUTH = 0; /** @deprecated Use \OCP\API::USER_AUTH instead */ const USER_AUTH = 1; /** @deprecated Use \OCP\API::SUBADMIN_AUTH instead */ const SUBADMIN_AUTH = 2; /** @deprecated Use \OCP\API::ADMIN_AUTH instead */ const ADMIN_AUTH = 3; /** * API Response Codes */ /** @deprecated Use \OCP\API::RESPOND_UNAUTHORISED instead */ const RESPOND_UNAUTHORISED = 997; /** @deprecated Use \OCP\API::RESPOND_SERVER_ERROR instead */ const RESPOND_SERVER_ERROR = 996; /** @deprecated Use \OCP\API::RESPOND_NOT_FOUND instead */ const RESPOND_NOT_FOUND = 998; /** @deprecated Use \OCP\API::RESPOND_UNKNOWN_ERROR instead */ const RESPOND_UNKNOWN_ERROR = 999; /** * api actions */ protected static $actions = array(); private static $logoutRequired = false; private static $isLoggedIn = false; /** * registers an api call * @param string $method the http method * @param string $url the url to match * @param callable $action the function to run * @param string $app the id of the app registering the call * @param int $authLevel the level of authentication required for the call * @param array $defaults * @param array $requirements */ public static function register($method, $url, $action, $app, $authLevel = API::USER_AUTH, $defaults = array(), $requirements = array()) { $name = strtolower($method).$url; $name = str_replace(array('/', '{', '}'), '_', $name); if(!isset(self::$actions[$name])) { $oldCollection = OC::$server->getRouter()->getCurrentCollection(); OC::$server->getRouter()->useCollection('ocs'); OC::$server->getRouter()->create($name, $url) ->method($method) ->defaults($defaults) ->requirements($requirements) ->action('OC_API', 'call'); self::$actions[$name] = array(); OC::$server->getRouter()->useCollection($oldCollection); } self::$actions[$name][] = array('app' => $app, 'action' => $action, 'authlevel' => $authLevel); } /** * handles an api call * @param array $parameters */ public static function call($parameters) { $request = \OC::$server->getRequest(); $method = $request->getMethod(); // Prepare the request variables if($method === 'PUT') { $parameters['_put'] = $request->getParams(); } else if($method === 'DELETE') { $parameters['_delete'] = $request->getParams(); } $name = $parameters['_route']; // Foreach registered action $responses = array(); foreach(self::$actions[$name] as $action) { // Check authentication and availability if(!self::isAuthorised($action)) { $responses[] = array( 'app' => $action['app'], 'response' => new OC_OCS_Result(null, API::RESPOND_UNAUTHORISED, 'Unauthorised'), 'shipped' => OC_App::isShipped($action['app']), ); continue; } if(!is_callable($action['action'])) { $responses[] = array( 'app' => $action['app'], 'response' => new OC_OCS_Result(null, API::RESPOND_NOT_FOUND, 'Api method not found'), 'shipped' => OC_App::isShipped($action['app']), ); continue; } // Run the action $responses[] = array( 'app' => $action['app'], 'response' => call_user_func($action['action'], $parameters), 'shipped' => OC_App::isShipped($action['app']), ); } $response = self::mergeResponses($responses); $format = self::requestedFormat(); if (self::$logoutRequired) { \OC::$server->getUserSession()->logout(); } self::respond($response, $format); } /** * merge the returned result objects into one response * @param array $responses * @return OC_OCS_Result */ public static function mergeResponses($responses) { // Sort into shipped and third-party $shipped = array( 'succeeded' => array(), 'failed' => array(), ); $thirdparty = array( 'succeeded' => array(), 'failed' => array(), ); foreach($responses as $response) { if($response['shipped'] || ($response['app'] === 'core')) { if($response['response']->succeeded()) { $shipped['succeeded'][$response['app']] = $response; } else { $shipped['failed'][$response['app']] = $response; } } else { if($response['response']->succeeded()) { $thirdparty['succeeded'][$response['app']] = $response; } else { $thirdparty['failed'][$response['app']] = $response; } } } // Remove any error responses if there is one shipped response that succeeded if(!empty($shipped['failed'])) { // Which shipped response do we use if they all failed? // They may have failed for different reasons (different status codes) // Which response code should we return? // Maybe any that are not \OCP\API::RESPOND_SERVER_ERROR // Merge failed responses if more than one $data = array(); foreach($shipped['failed'] as $failure) { $data = array_merge_recursive($data, $failure['response']->getData()); } $picked = reset($shipped['failed']); $code = $picked['response']->getStatusCode(); $meta = $picked['response']->getMeta(); $headers = $picked['response']->getHeaders(); $response = new OC_OCS_Result($data, $code, $meta['message'], $headers); return $response; } elseif(!empty($shipped['succeeded'])) { $responses = array_merge($shipped['succeeded'], $thirdparty['succeeded']); } elseif(!empty($thirdparty['failed'])) { // Merge failed responses if more than one $data = array(); foreach($thirdparty['failed'] as $failure) { $data = array_merge_recursive($data, $failure['response']->getData()); } $picked = reset($thirdparty['failed']); $code = $picked['response']->getStatusCode(); $meta = $picked['response']->getMeta(); $headers = $picked['response']->getHeaders(); $response = new OC_OCS_Result($data, $code, $meta['message'], $headers); return $response; } else { $responses = $thirdparty['succeeded']; } // Merge the successful responses $data = []; $codes = []; $header = []; foreach($responses as $response) { if($response['shipped']) { $data = array_merge_recursive($response['response']->getData(), $data); } else { $data = array_merge_recursive($data, $response['response']->getData()); } $header = array_merge_recursive($header, $response['response']->getHeaders()); $codes[] = ['code' => $response['response']->getStatusCode(), 'meta' => $response['response']->getMeta()]; } // Use any non 100 status codes $statusCode = 100; $statusMessage = null; foreach($codes as $code) { if($code['code'] != 100) { $statusCode = $code['code']; $statusMessage = $code['meta']['message']; break; } } return new OC_OCS_Result($data, $statusCode, $statusMessage, $header); } /** * authenticate the api call * @param array $action the action details as supplied to OC_API::register() * @return bool */ private static function isAuthorised($action) { $level = $action['authlevel']; switch($level) { case API::GUEST_AUTH: // Anyone can access return true; case API::USER_AUTH: // User required return self::loginUser(); case API::SUBADMIN_AUTH: // Check for subadmin $user = self::loginUser(); if(!$user) { return false; } else { $userObject = \OC::$server->getUserSession()->getUser(); if($userObject === null) { return false; } $isSubAdmin = \OC::$server->getGroupManager()->getSubAdmin()->isSubAdmin($userObject); $admin = OC_User::isAdminUser($user); if($isSubAdmin || $admin) { return true; } else { return false; } } case API::ADMIN_AUTH: // Check for admin $user = self::loginUser(); if(!$user) { return false; } else { return OC_User::isAdminUser($user); } default: // oops looks like invalid level supplied return false; } } /** * http basic auth * @return string|false (username, or false on failure) */ private static function loginUser() { if(self::$isLoggedIn === true) { return \OC_User::getUser(); } // reuse existing login $loggedIn = \OC::$server->getUserSession()->isLoggedIn(); if ($loggedIn === true) { if (\OC::$server->getTwoFactorAuthManager()->needsSecondFactor(\OC::$server->getUserSession()->getUser())) { // Do not allow access to OCS until the 2FA challenge was solved successfully return false; } $ocsApiRequest = isset($_SERVER['HTTP_OCS_APIREQUEST']) ? $_SERVER['HTTP_OCS_APIREQUEST'] === 'true' : false; if ($ocsApiRequest) { // initialize the user's filesystem \OC_Util::setupFS(\OC_User::getUser()); self::$isLoggedIn = true; return OC_User::getUser(); } return false; } // basic auth - because OC_User::login will create a new session we shall only try to login // if user and pass are set $userSession = \OC::$server->getUserSession(); $request = \OC::$server->getRequest(); try { if ($userSession->tryTokenLogin($request) || $userSession->tryBasicAuthLogin($request, \OC::$server->getBruteForceThrottler())) { self::$logoutRequired = true; } else { return false; } // initialize the user's filesystem \OC_Util::setupFS(\OC_User::getUser()); self::$isLoggedIn = true; return \OC_User::getUser(); } catch (\OC\User\LoginException $e) { return false; } } /** * respond to a call * @param OC_OCS_Result $result * @param string $format the format xml|json */ public static function respond($result, $format='xml') { $request = \OC::$server->getRequest(); // Send 401 headers if unauthorised if($result->getStatusCode() === API::RESPOND_UNAUTHORISED) { // If request comes from JS return dummy auth request if($request->getHeader('X-Requested-With') === 'XMLHttpRequest') { header('WWW-Authenticate: DummyBasic realm="Authorisation Required"'); } else { header('WWW-Authenticate: Basic realm="Authorisation Required"'); } header('HTTP/1.0 401 Unauthorized'); } foreach($result->getHeaders() as $name => $value) { header($name . ': ' . $value); } $meta = $result->getMeta(); $data = $result->getData(); if (self::isV2($request)) { $statusCode = self::mapStatusCodes($result->getStatusCode()); if (!is_null($statusCode)) { $meta['statuscode'] = $statusCode; OC_Response::setStatus($statusCode); } } self::setContentType($format); $body = self::renderResult($format, $meta, $data); echo $body; } /** * @param XMLWriter $writer */ private static function toXML($array, $writer) { foreach($array as $k => $v) { if ($k[0] === '@') { $writer->writeAttribute(substr($k, 1), $v); continue; } else if (is_numeric($k)) { $k = 'element'; } if(is_array($v)) { $writer->startElement($k); self::toXML($v, $writer); $writer->endElement(); } else { $writer->writeElement($k, $v); } } } /** * @return string */ public static function requestedFormat() { $formats = array('json', 'xml'); $format = !empty($_GET['format']) && in_array($_GET['format'], $formats) ? $_GET['format'] : 'xml'; return $format; } /** * Based on the requested format the response content type is set * @param string $format */ public static function setContentType($format = null) { $format = is_null($format) ? self::requestedFormat() : $format; if ($format === 'xml') { header('Content-type: text/xml; charset=UTF-8'); return; } if ($format === 'json') { header('Content-Type: application/json; charset=utf-8'); return; } header('Content-Type: application/octet-stream; charset=utf-8'); } /** * @param \OCP\IRequest $request * @return bool */ protected static function isV2(\OCP\IRequest $request) { $script = $request->getScriptName(); return substr($script, -11) === '/ocs/v2.php'; } /** * @param integer $sc * @return int */ public static function mapStatusCodes($sc) { switch ($sc) { case API::RESPOND_NOT_FOUND: return Http::STATUS_NOT_FOUND; case API::RESPOND_SERVER_ERROR: return Http::STATUS_INTERNAL_SERVER_ERROR; case API::RESPOND_UNKNOWN_ERROR: return Http::STATUS_INTERNAL_SERVER_ERROR; case API::RESPOND_UNAUTHORISED: // already handled for v1 return null; case 100: return Http::STATUS_OK; } // any 2xx, 4xx and 5xx will be used as is if ($sc >= 200 && $sc < 600) { return $sc; } return Http::STATUS_BAD_REQUEST; } /** * @param string $format * @return string */ public static function renderResult($format, $meta, $data) { $response = array( 'ocs' => array( 'meta' => $meta, 'data' => $data, ), ); if ($format == 'json') { return OC_JSON::encode($response); } $writer = new XMLWriter(); $writer->openMemory(); $writer->setIndent(true); $writer->startDocument(); self::toXML($response, $writer); $writer->endDocument(); return $writer->outputMemory(true); } } user.php 0000604 00000040552 15247162136 0006244 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Aldo "xoen" Giambelluca <xoen@xoen.org> * @author Andreas Fischer <bantu@owncloud.com> * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bartek Przybylski <bart.p.pl@gmail.com> * @author Bart Visscher <bartv@thisnet.nl> * @author Björn Schießle <bjoern@schiessle.org> * @author Christoph Wurst <christoph@owncloud.com> * @author Georg Ehrke <georg@owncloud.com> * @author Jakob Sack <mail@jakobsack.de> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author shkdee <louis.traynard@m4x.org> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Tom Needham <tom@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * This class provides wrapper methods for user management. Multiple backends are * supported. User management operations are delegated to the configured backend for * execution. * * Note that &run is deprecated and won't work anymore. * * Hooks provided: * pre_createUser(&run, uid, password) * post_createUser(uid, password) * pre_deleteUser(&run, uid) * post_deleteUser(uid) * pre_setPassword(&run, uid, password, recoveryPassword) * post_setPassword(uid, password, recoveryPassword) * pre_login(&run, uid, password) * post_login(uid) * logout() */ class OC_User { /** * @return \OC\User\Session */ public static function getUserSession() { return OC::$server->getUserSession(); } private static $_usedBackends = array(); private static $_setupedBackends = array(); // bool, stores if a user want to access a resource anonymously, e.g if they open a public link private static $incognitoMode = false; /** * Adds the backend to the list of used backends * * @param string|\OCP\UserInterface $backend default: database The backend to use for user management * @return bool * * Set the User Authentication Module */ public static function useBackend($backend = 'database') { if ($backend instanceof \OCP\UserInterface) { self::$_usedBackends[get_class($backend)] = $backend; \OC::$server->getUserManager()->registerBackend($backend); } else { // You'll never know what happens if (null === $backend OR !is_string($backend)) { $backend = 'database'; } // Load backend switch ($backend) { case 'database': case 'mysql': case 'sqlite': \OCP\Util::writeLog('core', 'Adding user backend ' . $backend . '.', \OCP\Util::DEBUG); self::$_usedBackends[$backend] = new \OC\User\Database(); \OC::$server->getUserManager()->registerBackend(self::$_usedBackends[$backend]); break; case 'dummy': self::$_usedBackends[$backend] = new \Test\Util\User\Dummy(); \OC::$server->getUserManager()->registerBackend(self::$_usedBackends[$backend]); break; default: \OCP\Util::writeLog('core', 'Adding default user backend ' . $backend . '.', \OCP\Util::DEBUG); $className = 'OC_USER_' . strtoupper($backend); self::$_usedBackends[$backend] = new $className(); \OC::$server->getUserManager()->registerBackend(self::$_usedBackends[$backend]); break; } } return true; } /** * remove all used backends */ public static function clearBackends() { self::$_usedBackends = array(); \OC::$server->getUserManager()->clearBackends(); } /** * setup the configured backends in config.php */ public static function setupBackends() { OC_App::loadApps(['prelogin']); $backends = \OC::$server->getSystemConfig()->getValue('user_backends', []); if (isset($backends['default']) && !$backends['default']) { // clear default backends self::clearBackends(); } foreach ($backends as $i => $config) { if (!is_array($config)) { continue; } $class = $config['class']; $arguments = $config['arguments']; if (class_exists($class)) { if (array_search($i, self::$_setupedBackends) === false) { // make a reflection object $reflectionObj = new ReflectionClass($class); // use Reflection to create a new instance, using the $args $backend = $reflectionObj->newInstanceArgs($arguments); self::useBackend($backend); self::$_setupedBackends[] = $i; } else { \OCP\Util::writeLog('core', 'User backend ' . $class . ' already initialized.', \OCP\Util::DEBUG); } } else { \OCP\Util::writeLog('core', 'User backend ' . $class . ' not found.', \OCP\Util::ERROR); } } } /** * Try to login a user using the magic cookie (remember login) * * @deprecated use \OCP\IUserSession::loginWithCookie() * @param string $uid The username of the user to log in * @param string $token * @param string $oldSessionId * @return bool */ public static function loginWithCookie($uid, $token, $oldSessionId) { return self::getUserSession()->loginWithCookie($uid, $token, $oldSessionId); } /** * Try to login a user, assuming authentication * has already happened (e.g. via Single Sign On). * * Log in a user and regenerate a new session. * * @param \OCP\Authentication\IApacheBackend $backend * @return bool */ public static function loginWithApache(\OCP\Authentication\IApacheBackend $backend) { $uid = $backend->getCurrentUserId(); $run = true; OC_Hook::emit("OC_User", "pre_login", array("run" => &$run, "uid" => $uid)); if ($uid) { if (self::getUser() !== $uid) { self::setUserId($uid); $setUidAsDisplayName = true; if($backend instanceof \OCP\UserInterface && $backend->implementsActions(OC_User_Backend::GET_DISPLAYNAME)) { $backendDisplayName = $backend->getDisplayName($uid); if(is_string($backendDisplayName) && trim($backendDisplayName) !== '') { $setUidAsDisplayName = false; } } if($setUidAsDisplayName) { self::setDisplayName($uid); } $userSession = self::getUserSession(); $userSession->setLoginName($uid); $request = OC::$server->getRequest(); $userSession->createSessionToken($request, $uid, $uid); // setup the filesystem OC_Util::setupFS($uid); // first call the post_login hooks, the login-process needs to be // completed before we can safely create the users folder. // For example encryption needs to initialize the users keys first // before we can create the user folder with the skeleton files OC_Hook::emit("OC_User", "post_login", array("uid" => $uid, 'password' => '')); //trigger creation of user home and /files folder \OC::$server->getUserFolder($uid); } return true; } return false; } /** * Verify with Apache whether user is authenticated. * * @return boolean|null * true: authenticated * false: not authenticated * null: not handled / no backend available */ public static function handleApacheAuth() { $backend = self::findFirstActiveUsedBackend(); if ($backend) { OC_App::loadApps(); //setup extra user backends self::setupBackends(); self::unsetMagicInCookie(); return self::loginWithApache($backend); } return null; } /** * Sets user id for session and triggers emit * * @param string $uid */ public static function setUserId($uid) { $userSession = \OC::$server->getUserSession(); $userManager = \OC::$server->getUserManager(); if ($user = $userManager->get($uid)) { $userSession->setUser($user); } else { \OC::$server->getSession()->set('user_id', $uid); } } /** * Sets user display name for session * * @param string $uid * @param string $displayName * @return bool Whether the display name could get set */ public static function setDisplayName($uid, $displayName = null) { if (is_null($displayName)) { $displayName = $uid; } $user = \OC::$server->getUserManager()->get($uid); if ($user) { return $user->setDisplayName($displayName); } else { return false; } } /** * Check if the user is logged in, considers also the HTTP basic credentials * * @deprecated use \OC::$server->getUserSession()->isLoggedIn() * @return bool */ public static function isLoggedIn() { return \OC::$server->getUserSession()->isLoggedIn(); } /** * set incognito mode, e.g. if a user wants to open a public link * * @param bool $status */ public static function setIncognitoMode($status) { self::$incognitoMode = $status; } /** * get incognito mode status * * @return bool */ public static function isIncognitoMode() { return self::$incognitoMode; } /** * Returns the current logout URL valid for the currently logged-in user * * @param \OCP\IURLGenerator $urlGenerator * @return string */ public static function getLogoutUrl(\OCP\IURLGenerator $urlGenerator) { $backend = self::findFirstActiveUsedBackend(); if ($backend) { return $backend->getLogoutUrl(); } $logoutUrl = $urlGenerator->linkToRouteAbsolute( 'core.login.logout', [ 'requesttoken' => \OCP\Util::callRegister(), ] ); return $logoutUrl; } /** * Check if the user is an admin user * * @param string $uid uid of the admin * @return bool */ public static function isAdminUser($uid) { $group = \OC::$server->getGroupManager()->get('admin'); $user = \OC::$server->getUserManager()->get($uid); if ($group && $user && $group->inGroup($user) && self::$incognitoMode === false) { return true; } return false; } /** * get the user id of the user currently logged in. * * @return string|bool uid or false */ public static function getUser() { $uid = \OC::$server->getSession() ? \OC::$server->getSession()->get('user_id') : null; if (!is_null($uid) && self::$incognitoMode === false) { return $uid; } else { return false; } } /** * get the display name of the user currently logged in. * * @param string $uid * @return string uid or false */ public static function getDisplayName($uid = null) { if ($uid) { $user = \OC::$server->getUserManager()->get($uid); if ($user) { return $user->getDisplayName(); } else { return $uid; } } else { $user = self::getUserSession()->getUser(); if ($user) { return $user->getDisplayName(); } else { return false; } } } /** * Autogenerate a password * * @return string * * generates a password */ public static function generatePassword() { return \OC::$server->getSecureRandom()->generate(30); } /** * Set password * * @param string $uid The username * @param string $password The new password * @param string $recoveryPassword for the encryption app to reset encryption keys * @return bool * * Change the password of a user */ public static function setPassword($uid, $password, $recoveryPassword = null) { $user = \OC::$server->getUserManager()->get($uid); if ($user) { return $user->setPassword($password, $recoveryPassword); } else { return false; } } /** * Check whether user can change his avatar * * @param string $uid The username * @return bool * * Check whether a specified user can change his avatar */ public static function canUserChangeAvatar($uid) { $user = \OC::$server->getUserManager()->get($uid); if ($user) { return $user->canChangeAvatar(); } else { return false; } } /** * Check whether user can change his password * * @param string $uid The username * @return bool * * Check whether a specified user can change his password */ public static function canUserChangePassword($uid) { $user = \OC::$server->getUserManager()->get($uid); if ($user) { return $user->canChangePassword(); } else { return false; } } /** * Check whether user can change his display name * * @param string $uid The username * @return bool * * Check whether a specified user can change his display name */ public static function canUserChangeDisplayName($uid) { $user = \OC::$server->getUserManager()->get($uid); if ($user) { return $user->canChangeDisplayName(); } else { return false; } } /** * Check if the password is correct * * @param string $uid The username * @param string $password The password * @return string|false user id a string on success, false otherwise * * Check if the password is correct without logging in the user * returns the user id or false */ public static function checkPassword($uid, $password) { $manager = \OC::$server->getUserManager(); $username = $manager->checkPassword($uid, $password); if ($username !== false) { return $username->getUID(); } return false; } /** * @param string $uid The username * @return string * * returns the path to the users home directory * @deprecated Use \OC::$server->getUserManager->getHome() */ public static function getHome($uid) { $user = \OC::$server->getUserManager()->get($uid); if ($user) { return $user->getHome(); } else { return \OC::$server->getSystemConfig()->getValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $uid; } } /** * Get a list of all users * * @return array an array of all uids * * Get a list of all users. * @param string $search * @param integer $limit * @param integer $offset */ public static function getUsers($search = '', $limit = null, $offset = null) { $users = \OC::$server->getUserManager()->search($search, $limit, $offset); $uids = array(); foreach ($users as $user) { $uids[] = $user->getUID(); } return $uids; } /** * Get a list of all users display name * * @param string $search * @param int $limit * @param int $offset * @return array associative array with all display names (value) and corresponding uids (key) * * Get a list of all display names and user ids. * @deprecated Use \OC::$server->getUserManager->searchDisplayName($search, $limit, $offset) instead. */ public static function getDisplayNames($search = '', $limit = null, $offset = null) { $displayNames = array(); $users = \OC::$server->getUserManager()->searchDisplayName($search, $limit, $offset); foreach ($users as $user) { $displayNames[$user->getUID()] = $user->getDisplayName(); } return $displayNames; } /** * check if a user exists * * @param string $uid the username * @return boolean */ public static function userExists($uid) { return \OC::$server->getUserManager()->userExists($uid); } /** * disables a user * * @param string $uid the user to disable */ public static function disableUser($uid) { $user = \OC::$server->getUserManager()->get($uid); if ($user) { $user->setEnabled(false); } } /** * enable a user * * @param string $uid */ public static function enableUser($uid) { $user = \OC::$server->getUserManager()->get($uid); if ($user) { $user->setEnabled(true); } } /** * checks if a user is enabled * * @param string $uid * @return bool */ public static function isEnabled($uid) { $user = \OC::$server->getUserManager()->get($uid); if ($user) { return $user->isEnabled(); } else { return false; } } /** * Set cookie value to use in next page load * * @param string $username username to be set * @param string $token */ public static function setMagicInCookie($username, $token) { self::getUserSession()->setMagicInCookie($username, $token); } /** * Remove cookie for "remember username" */ public static function unsetMagicInCookie() { self::getUserSession()->unsetMagicInCookie(); } /** * Returns the first active backend from self::$_usedBackends. * * @return OCP\Authentication\IApacheBackend|null if no backend active, otherwise OCP\Authentication\IApacheBackend */ private static function findFirstActiveUsedBackend() { foreach (self::$_usedBackends as $backend) { if ($backend instanceof OCP\Authentication\IApacheBackend) { if ($backend->isSessionActive()) { return $backend; } } } return null; } } ocs/result.php 0000604 00000001620 15247162136 0007361 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * @deprecated Since 9.1.0 use \OC\OCS\Result */ class OC_OCS_Result extends \OC\OCS\Result { } ocs/privatedata.php 0000604 00000001637 15247162136 0010357 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * @deprecated Since 9.1.0 use \OC\OCS\PrivateData */ class OC_OCS_Privatedata extends \OC\OCS\PrivateData { } user/interface.php 0000604 00000001763 15247162136 0010205 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Interface OC_User_Interface * @deprecated use the public \OCP\UserInterface instead */ interface OC_User_Interface extends \OCP\UserInterface {} user/backend.php 0000604 00000004623 15247162136 0007632 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Aldo "xoen" Giambelluca <xoen@xoen.org> * @author Dominik Schmidt <dev@dominik-schmidt.de> * @author Jakob Sack <mail@jakobsack.de> * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Sam Tuke <mail@samtuke.com> * @author Tigran Mkrtchyan <tigran.mkrtchyan@desy.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * error code for functions not provided by the user backend * @deprecated Use \OC_User_Backend::NOT_IMPLEMENTED instead */ define('OC_USER_BACKEND_NOT_IMPLEMENTED', -501); /** * actions that user backends can define */ /** @deprecated Use \OC_User_Backend::CREATE_USER instead */ define('OC_USER_BACKEND_CREATE_USER', 1 << 0); /** @deprecated Use \OC_User_Backend::SET_PASSWORD instead */ define('OC_USER_BACKEND_SET_PASSWORD', 1 << 4); /** @deprecated Use \OC_User_Backend::CHECK_PASSWORD instead */ define('OC_USER_BACKEND_CHECK_PASSWORD', 1 << 8); /** @deprecated Use \OC_User_Backend::GET_HOME instead */ define('OC_USER_BACKEND_GET_HOME', 1 << 12); /** @deprecated Use \OC_User_Backend::GET_DISPLAYNAME instead */ define('OC_USER_BACKEND_GET_DISPLAYNAME', 1 << 16); /** @deprecated Use \OC_User_Backend::SET_DISPLAYNAME instead */ define('OC_USER_BACKEND_SET_DISPLAYNAME', 1 << 20); /** @deprecated Use \OC_User_Backend::PROVIDE_AVATAR instead */ define('OC_USER_BACKEND_PROVIDE_AVATAR', 1 << 24); /** @deprecated Use \OC_User_Backend::COUNT_USERS instead */ define('OC_USER_BACKEND_COUNT_USERS', 1 << 28); /** * Abstract base class for user management. Provides methods for querying backend * capabilities. */ abstract class OC_User_Backend extends \OC\User\Backend implements \OCP\UserInterface { } response.php 0000604 00000022002 15247162136 0007112 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andreas Fischer <bantu@owncloud.com> * @author Bart Visscher <bartv@thisnet.nl> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Stefan Weil <sw@weilnetz.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ class OC_Response { const STATUS_FOUND = 304; const STATUS_NOT_MODIFIED = 304; const STATUS_TEMPORARY_REDIRECT = 307; const STATUS_BAD_REQUEST = 400; const STATUS_FORBIDDEN = 403; const STATUS_NOT_FOUND = 404; const STATUS_INTERNAL_SERVER_ERROR = 500; const STATUS_SERVICE_UNAVAILABLE = 503; /** * Enable response caching by sending correct HTTP headers * @param integer $cache_time time to cache the response * >0 cache time in seconds * 0 and <0 enable default browser caching * null cache indefinitely */ static public function enableCaching($cache_time = null) { if (is_numeric($cache_time)) { header('Pragma: public');// enable caching in IE if ($cache_time > 0) { self::setExpiresHeader('PT'.$cache_time.'S'); header('Cache-Control: max-age='.$cache_time.', must-revalidate'); } else { self::setExpiresHeader(0); header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); } } else { header('Cache-Control: cache'); header('Pragma: cache'); } } /** * disable browser caching * @see enableCaching with cache_time = 0 */ static public function disableCaching() { self::enableCaching(0); } /** * Set response status * @param int $status a HTTP status code, see also the STATUS constants */ static public function setStatus($status) { $protocol = \OC::$server->getRequest()->getHttpProtocol(); switch($status) { case self::STATUS_NOT_MODIFIED: $status = $status . ' Not Modified'; break; case self::STATUS_TEMPORARY_REDIRECT: if ($protocol == 'HTTP/1.1') { $status = $status . ' Temporary Redirect'; break; } else { $status = self::STATUS_FOUND; // fallthrough } case self::STATUS_FOUND; $status = $status . ' Found'; break; case self::STATUS_NOT_FOUND; $status = $status . ' Not Found'; break; case self::STATUS_INTERNAL_SERVER_ERROR; $status = $status . ' Internal Server Error'; break; case self::STATUS_SERVICE_UNAVAILABLE; $status = $status . ' Service Unavailable'; break; } header($protocol.' '.$status); } /** * Send redirect response * @param string $location to redirect to */ static public function redirect($location) { self::setStatus(self::STATUS_TEMPORARY_REDIRECT); header('Location: '.$location); } /** * Set response expire time * @param string|DateTime $expires date-time when the response expires * string for DateInterval from now * DateTime object when to expire response */ static public function setExpiresHeader($expires) { if (is_string($expires) && $expires[0] == 'P') { $interval = $expires; $expires = new DateTime('now'); $expires->add(new DateInterval($interval)); } if ($expires instanceof DateTime) { $expires->setTimezone(new DateTimeZone('GMT')); $expires = $expires->format(DateTime::RFC2822); } header('Expires: '.$expires); } /** * Checks and set ETag header, when the request matches sends a * 'not modified' response * @param string $etag token to use for modification check */ static public function setETagHeader($etag) { if (empty($etag)) { return; } $etag = '"'.$etag.'"'; if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && trim($_SERVER['HTTP_IF_NONE_MATCH']) == $etag) { self::setStatus(self::STATUS_NOT_MODIFIED); exit; } header('ETag: '.$etag); } /** * Checks and set Last-Modified header, when the request matches sends a * 'not modified' response * @param int|DateTime|string $lastModified time when the response was last modified */ static public function setLastModifiedHeader($lastModified) { if (empty($lastModified)) { return; } if (is_int($lastModified)) { $lastModified = gmdate(DateTime::RFC2822, $lastModified); } if ($lastModified instanceof DateTime) { $lastModified = $lastModified->format(DateTime::RFC2822); } if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && trim($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $lastModified) { self::setStatus(self::STATUS_NOT_MODIFIED); exit; } header('Last-Modified: '.$lastModified); } /** * Sets the content disposition header (with possible workarounds) * @param string $filename file name * @param string $type disposition type, either 'attachment' or 'inline' */ static public function setContentDispositionHeader( $filename, $type = 'attachment' ) { if (\OC::$server->getRequest()->isUserAgent( [ \OC\AppFramework\Http\Request::USER_AGENT_IE, \OC\AppFramework\Http\Request::USER_AGENT_ANDROID_MOBILE_CHROME, \OC\AppFramework\Http\Request::USER_AGENT_FREEBOX, ])) { header( 'Content-Disposition: ' . rawurlencode($type) . '; filename="' . rawurlencode( $filename ) . '"' ); } else { header( 'Content-Disposition: ' . rawurlencode($type) . '; filename*=UTF-8\'\'' . rawurlencode( $filename ) . '; filename="' . rawurlencode( $filename ) . '"' ); } } /** * Sets the content length header (with possible workarounds) * @param string|int|float $length Length to be sent */ static public function setContentLengthHeader($length) { if (PHP_INT_SIZE === 4) { if ($length > PHP_INT_MAX && stripos(PHP_SAPI, 'apache') === 0) { // Apache PHP SAPI casts Content-Length headers to PHP integers. // This enforces a limit of PHP_INT_MAX (2147483647 on 32-bit // platforms). So, if the length is greater than PHP_INT_MAX, // we just do not send a Content-Length header to prevent // bodies from being received incompletely. return; } // Convert signed integer or float to unsigned base-10 string. $lfh = new \OC\LargeFileHelper; $length = $lfh->formatUnsignedInteger($length); } header('Content-Length: '.$length); } /** * Send file as response, checking and setting caching headers * @param string $filepath of file to send * @deprecated 8.1.0 - Use \OCP\AppFramework\Http\StreamResponse or another AppFramework controller instead */ static public function sendFile($filepath) { $fp = fopen($filepath, 'rb'); if ($fp) { self::setLastModifiedHeader(filemtime($filepath)); self::setETagHeader(md5_file($filepath)); self::setContentLengthHeader(filesize($filepath)); fpassthru($fp); } else { self::setStatus(self::STATUS_NOT_FOUND); } } /** * This function adds some security related headers to all requests served via base.php * The implementation of this function has to happen here to ensure that all third-party * components (e.g. SabreDAV) also benefit from this headers. */ public static function addSecurityHeaders() { /** * FIXME: Content Security Policy for legacy ownCloud components. This * can be removed once \OCP\AppFramework\Http\Response from the AppFramework * is used everywhere. * @see \OCP\AppFramework\Http\Response::getHeaders */ $policy = 'default-src \'self\'; ' . 'script-src \'self\' \'unsafe-eval\' \'nonce-'.\OC::$server->getContentSecurityPolicyNonceManager()->getNonce().'\'; ' . 'style-src \'self\' \'unsafe-inline\'; ' . 'frame-src *; ' . 'img-src * data: blob:; ' . 'font-src \'self\' data:; ' . 'media-src *; ' . 'connect-src *; ' . 'object-src \'none\'; ' . 'base-uri \'self\'; '; header('Content-Security-Policy:' . $policy); header('X-Frame-Options: SAMEORIGIN'); // Disallow iFraming from other domains // Send fallback headers for installations that don't have the possibility to send // custom headers on the webserver side if(getenv('modHeadersAvailable') !== 'true') { header('X-XSS-Protection: 1; mode=block'); // Enforce browser based XSS filters header('X-Content-Type-Options: nosniff'); // Disable sniffing the content type for IE header('X-Robots-Tag: none'); // https://developers.google.com/webmasters/control-crawl-index/docs/robots_meta_tag header('X-Download-Options: noopen'); // https://msdn.microsoft.com/en-us/library/jj542450(v=vs.85).aspx header('X-Permitted-Cross-Domain-Policies: none'); // https://www.adobe.com/devnet/adobe-media-server/articles/cross-domain-xml-for-streaming.html } } } eventsource.php 0000604 00000007527 15247162136 0007635 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Felix Moeller <mail@felixmoeller.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * wrapper for server side events (http://en.wikipedia.org/wiki/Server-sent_events) * includes a fallback for older browsers and IE * * use server side events with caution, to many open requests can hang the server */ class OC_EventSource implements \OCP\IEventSource { /** * @var bool */ private $fallback; /** * @var int */ private $fallBackId = 0; /** * @var bool */ private $started = false; protected function init() { if ($this->started) { return; } $this->started = true; // prevent php output buffering, caching and nginx buffering OC_Util::obEnd(); header('Cache-Control: no-cache'); header('X-Accel-Buffering: no'); $this->fallback = isset($_GET['fallback']) and $_GET['fallback'] == 'true'; if ($this->fallback) { $this->fallBackId = (int)$_GET['fallback_id']; /** * FIXME: The default content-security-policy of ownCloud forbids inline * JavaScript for security reasons. IE starting on Windows 10 will * however also obey the CSP which will break the event source fallback. * * As a workaround thus we set a custom policy which allows the execution * of inline JavaScript. * * @link https://github.com/owncloud/core/issues/14286 */ header("Content-Security-Policy: default-src 'none'; script-src 'unsafe-inline'"); header("Content-Type: text/html"); echo str_repeat('<span></span>' . PHP_EOL, 10); //dummy data to keep IE happy } else { header("Content-Type: text/event-stream"); } if(!\OC::$server->getRequest()->passesStrictCookieCheck()) { header('Location: '.\OC::$WEBROOT); exit(); } if (!(\OC::$server->getRequest()->passesCSRFCheck())) { $this->send('error', 'Possible CSRF attack. Connection will be closed.'); $this->close(); exit(); } flush(); } /** * send a message to the client * * @param string $type * @param mixed $data * * @throws \BadMethodCallException * if only one parameter is given, a typeless message will be send with that parameter as data */ public function send($type, $data = null) { if ($data and !preg_match('/^[A-Za-z0-9_]+$/', $type)) { throw new BadMethodCallException('Type needs to be alphanumeric ('. $type .')'); } $this->init(); if (is_null($data)) { $data = $type; $type = null; } if ($this->fallback) { $response = '<script type="text/javascript">window.parent.OC.EventSource.fallBackCallBack(' . $this->fallBackId . ',"' . $type . '",' . OCP\JSON::encode($data) . ')</script>' . PHP_EOL; echo $response; } else { if ($type) { echo 'event: ' . $type . PHP_EOL; } echo 'data: ' . OCP\JSON::encode($data) . PHP_EOL; } echo PHP_EOL; flush(); } /** * close the connection of the event source */ public function close() { $this->send('__internal__', 'close'); //server side closing can be an issue, let the client do it } } group/backend.php 0000604 00000004133 15247162136 0010004 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Jakob Sack <mail@jakobsack.de> * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * error code for functions not provided by the group backend * @deprecated Use \OC_Group_Backend::NOT_IMPLEMENTED instead */ define('OC_GROUP_BACKEND_NOT_IMPLEMENTED', -501); /** * actions that user backends can define */ /** @deprecated Use \OC_Group_Backend::CREATE_GROUP instead */ define('OC_GROUP_BACKEND_CREATE_GROUP', 0x00000001); /** @deprecated Use \OC_Group_Backend::DELETE_GROUP instead */ define('OC_GROUP_BACKEND_DELETE_GROUP', 0x00000010); /** @deprecated Use \OC_Group_Backend::ADD_TO_GROUP instead */ define('OC_GROUP_BACKEND_ADD_TO_GROUP', 0x00000100); /** @deprecated Use \OC_Group_Backend::REMOVE_FROM_GOUP instead */ define('OC_GROUP_BACKEND_REMOVE_FROM_GOUP', 0x00001000); /** @deprecated Obsolete */ define('OC_GROUP_BACKEND_GET_DISPLAYNAME', 0x00010000); //OBSOLETE /** @deprecated Use \OC_Group_Backend::COUNT_USERS instead */ define('OC_GROUP_BACKEND_COUNT_USERS', 0x00100000); /** * Abstract base class for user management * @deprecated Since 9.1.0 use \OC\Group\Backend */ abstract class OC_Group_Backend extends \OC\Group\Backend { } files.php 0000604 00000032432 15247162136 0006366 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Björn Schießle <bjoern@schiessle.org> * @author Clark Tomlinson <fallen013@gmail.com> * @author Frank Karlitschek <frank@karlitschek.de> * @author Jakob Sack <mail@jakobsack.de> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Michael Gapczynski <GapczynskiM@gmail.com> * @author Nicolai Ehemann <en@enlightened.de> * @author Piotr Filiciak <piotr@filiciak.pl> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thibaut GRIDEL <tgridel@free.fr> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Victor Dubiniuk <dubiniuk@owncloud.com> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ use OC\Files\View; use OC\Streamer; use OCP\Lock\ILockingProvider; /** * Class for file server access * */ class OC_Files { const FILE = 1; const ZIP_FILES = 2; const ZIP_DIR = 3; const UPLOAD_MIN_LIMIT_BYTES = 1048576; // 1 MiB private static $multipartBoundary = ''; /** * @return string */ private static function getBoundary() { if (empty(self::$multipartBoundary)) { self::$multipartBoundary = md5(mt_rand()); } return self::$multipartBoundary; } /** * @param string $filename * @param string $name * @param array $rangeArray ('from'=>int,'to'=>int), ... */ private static function sendHeaders($filename, $name, array $rangeArray) { OC_Response::setContentDispositionHeader($name, 'attachment'); header('Content-Transfer-Encoding: binary', true); OC_Response::disableCaching(); $fileSize = \OC\Files\Filesystem::filesize($filename); $type = \OC::$server->getMimeTypeDetector()->getSecureMimeType(\OC\Files\Filesystem::getMimeType($filename)); if ($fileSize > -1) { if (!empty($rangeArray)) { header('HTTP/1.1 206 Partial Content', true); header('Accept-Ranges: bytes', true); if (count($rangeArray) > 1) { $type = 'multipart/byteranges; boundary='.self::getBoundary(); // no Content-Length header here } else { header(sprintf('Content-Range: bytes %d-%d/%d', $rangeArray[0]['from'], $rangeArray[0]['to'], $fileSize), true); OC_Response::setContentLengthHeader($rangeArray[0]['to'] - $rangeArray[0]['from'] + 1); } } else { OC_Response::setContentLengthHeader($fileSize); } } header('Content-Type: '.$type, true); } /** * return the content of a file or return a zip file containing multiple files * * @param string $dir * @param string $files ; separated list of files to download * @param array $params ; 'head' boolean to only send header of the request ; 'range' http range header */ public static function get($dir, $files, $params = null) { $view = \OC\Files\Filesystem::getView(); $getType = self::FILE; $filename = $dir; try { if (is_array($files) && count($files) === 1) { $files = $files[0]; } if (!is_array($files)) { $filename = $dir . '/' . $files; if (!$view->is_dir($filename)) { self::getSingleFile($view, $dir, $files, is_null($params) ? array() : $params); return; } } $name = 'download'; if (is_array($files)) { $getType = self::ZIP_FILES; $basename = basename($dir); if ($basename) { $name = $basename; } $filename = $dir . '/' . $name; } else { $filename = $dir . '/' . $files; $getType = self::ZIP_DIR; // downloading root ? if ($files !== '') { $name = $files; } } $streamer = new Streamer(); OC_Util::obEnd(); self::lockFiles($view, $dir, $files); $streamer->sendHeaders($name); $executionTime = intval(OC::$server->getIniWrapper()->getNumeric('max_execution_time')); if (strpos(@ini_get('disable_functions'), 'set_time_limit') === false) { @set_time_limit(0); } ignore_user_abort(true); if ($getType === self::ZIP_FILES) { foreach ($files as $file) { $file = $dir . '/' . $file; if (\OC\Files\Filesystem::is_file($file)) { $fileSize = \OC\Files\Filesystem::filesize($file); $fileTime = \OC\Files\Filesystem::filemtime($file); $fh = \OC\Files\Filesystem::fopen($file, 'r'); $streamer->addFileFromStream($fh, basename($file), $fileSize, $fileTime); fclose($fh); } elseif (\OC\Files\Filesystem::is_dir($file)) { $streamer->addDirRecursive($file); } } } elseif ($getType === self::ZIP_DIR) { $file = $dir . '/' . $files; $streamer->addDirRecursive($file); } $streamer->finalize(); set_time_limit($executionTime); self::unlockAllTheFiles($dir, $files, $getType, $view, $filename); } catch (\OCP\Lock\LockedException $ex) { self::unlockAllTheFiles($dir, $files, $getType, $view, $filename); OC::$server->getLogger()->logException($ex); $l = \OC::$server->getL10N('core'); $hint = method_exists($ex, 'getHint') ? $ex->getHint() : ''; \OC_Template::printErrorPage($l->t('File is currently busy, please try again later'), $hint); } catch (\OCP\Files\ForbiddenException $ex) { self::unlockAllTheFiles($dir, $files, $getType, $view, $filename); OC::$server->getLogger()->logException($ex); $l = \OC::$server->getL10N('core'); \OC_Template::printErrorPage($l->t('Can\'t read file'), $ex->getMessage()); } catch (\Exception $ex) { self::unlockAllTheFiles($dir, $files, $getType, $view, $filename); OC::$server->getLogger()->logException($ex); $l = \OC::$server->getL10N('core'); $hint = method_exists($ex, 'getHint') ? $ex->getHint() : ''; \OC_Template::printErrorPage($l->t('Can\'t read file'), $hint); } } /** * @param string $rangeHeaderPos * @param int $fileSize * @return array $rangeArray ('from'=>int,'to'=>int), ... */ private static function parseHttpRangeHeader($rangeHeaderPos, $fileSize) { $rArray=explode(',', $rangeHeaderPos); $minOffset = 0; $ind = 0; $rangeArray = array(); foreach ($rArray as $value) { $ranges = explode('-', $value); if (is_numeric($ranges[0])) { if ($ranges[0] < $minOffset) { // case: bytes=500-700,601-999 $ranges[0] = $minOffset; } if ($ind > 0 && $rangeArray[$ind-1]['to']+1 == $ranges[0]) { // case: bytes=500-600,601-999 $ind--; $ranges[0] = $rangeArray[$ind]['from']; } } if (is_numeric($ranges[0]) && is_numeric($ranges[1]) && $ranges[0] < $fileSize && $ranges[0] <= $ranges[1]) { // case: x-x if ($ranges[1] >= $fileSize) { $ranges[1] = $fileSize-1; } $rangeArray[$ind++] = array( 'from' => $ranges[0], 'to' => $ranges[1], 'size' => $fileSize ); $minOffset = $ranges[1] + 1; if ($minOffset >= $fileSize) { break; } } elseif (is_numeric($ranges[0]) && $ranges[0] < $fileSize) { // case: x- $rangeArray[$ind++] = array( 'from' => $ranges[0], 'to' => $fileSize-1, 'size' => $fileSize ); break; } elseif (is_numeric($ranges[1])) { // case: -x if ($ranges[1] > $fileSize) { $ranges[1] = $fileSize; } $rangeArray[$ind++] = array( 'from' => $fileSize-$ranges[1], 'to' => $fileSize-1, 'size' => $fileSize ); break; } } return $rangeArray; } /** * @param View $view * @param string $name * @param string $dir * @param array $params ; 'head' boolean to only send header of the request ; 'range' http range header */ private static function getSingleFile($view, $dir, $name, $params) { $filename = $dir . '/' . $name; OC_Util::obEnd(); $view->lockFile($filename, ILockingProvider::LOCK_SHARED); $rangeArray = array(); if (isset($params['range']) && substr($params['range'], 0, 6) === 'bytes=') { $rangeArray = self::parseHttpRangeHeader(substr($params['range'], 6), \OC\Files\Filesystem::filesize($filename)); } if (\OC\Files\Filesystem::isReadable($filename)) { self::sendHeaders($filename, $name, $rangeArray); } elseif (!\OC\Files\Filesystem::file_exists($filename)) { header("HTTP/1.1 404 Not Found"); $tmpl = new OC_Template('', '404', 'guest'); $tmpl->printPage(); exit(); } else { header("HTTP/1.1 403 Forbidden"); die('403 Forbidden'); } if (isset($params['head']) && $params['head']) { return; } if (!empty($rangeArray)) { try { if (count($rangeArray) == 1) { $view->readfilePart($filename, $rangeArray[0]['from'], $rangeArray[0]['to']); } else { // check if file is seekable (if not throw UnseekableException) // we have to check it before body contents $view->readfilePart($filename, $rangeArray[0]['size'], $rangeArray[0]['size']); $type = \OC::$server->getMimeTypeDetector()->getSecureMimeType(\OC\Files\Filesystem::getMimeType($filename)); foreach ($rangeArray as $range) { echo "\r\n--".self::getBoundary()."\r\n". "Content-type: ".$type."\r\n". "Content-range: bytes ".$range['from']."-".$range['to']."/".$range['size']."\r\n\r\n"; $view->readfilePart($filename, $range['from'], $range['to']); } echo "\r\n--".self::getBoundary()."--\r\n"; } } catch (\OCP\Files\UnseekableException $ex) { // file is unseekable header_remove('Accept-Ranges'); header_remove('Content-Range'); header("HTTP/1.1 200 OK"); self::sendHeaders($filename, $name, array()); $view->readfile($filename); } } else { $view->readfile($filename); } } /** * @param View $view * @param string $dir * @param string[]|string $files */ public static function lockFiles($view, $dir, $files) { if (!is_array($files)) { $file = $dir . '/' . $files; $files = [$file]; } foreach ($files as $file) { $file = $dir . '/' . $file; $view->lockFile($file, ILockingProvider::LOCK_SHARED); if ($view->is_dir($file)) { $contents = $view->getDirectoryContent($file); $contents = array_map(function($fileInfo) use ($file) { /** @var \OCP\Files\FileInfo $fileInfo */ return $file . '/' . $fileInfo->getName(); }, $contents); self::lockFiles($view, $dir, $contents); } } } /** * set the maximum upload size limit for apache hosts using .htaccess * * @param int $size file size in bytes * @param array $files override '.htaccess' and '.user.ini' locations * @return bool false on failure, size on success */ public static function setUploadLimit($size, $files = []) { //don't allow user to break his config $size = intval($size); if ($size < self::UPLOAD_MIN_LIMIT_BYTES) { return false; } $size = OC_Helper::phpFileSize($size); $phpValueKeys = array( 'upload_max_filesize', 'post_max_size' ); // default locations if not overridden by $files $files = array_merge([ '.htaccess' => OC::$SERVERROOT . '/.htaccess', '.user.ini' => OC::$SERVERROOT . '/.user.ini' ], $files); $updateFiles = [ $files['.htaccess'] => [ 'pattern' => '/php_value %1$s (\S)*/', 'setting' => 'php_value %1$s %2$s' ], $files['.user.ini'] => [ 'pattern' => '/%1$s=(\S)*/', 'setting' => '%1$s=%2$s' ] ]; $success = true; foreach ($updateFiles as $filename => $patternMap) { // suppress warnings from fopen() $handle = @fopen($filename, 'r+'); if (!$handle) { \OCP\Util::writeLog('files', 'Can\'t write upload limit to ' . $filename . '. Please check the file permissions', \OCP\Util::WARN); $success = false; continue; // try to update as many files as possible } $content = ''; while (!feof($handle)) { $content .= fread($handle, 1000); } foreach ($phpValueKeys as $key) { $pattern = vsprintf($patternMap['pattern'], [$key]); $setting = vsprintf($patternMap['setting'], [$key, $size]); $hasReplaced = 0; $newContent = preg_replace($pattern, $setting, $content, 2, $hasReplaced); if ($newContent !== null) { $content = $newContent; } if ($hasReplaced === 0) { $content .= "\n" . $setting; } } // write file back ftruncate($handle, 0); rewind($handle); fwrite($handle, $content); fclose($handle); } if ($success) { return OC_Helper::computerFileSize($size); } return false; } /** * @param string $dir * @param $files * @param integer $getType * @param View $view * @param string $filename */ private static function unlockAllTheFiles($dir, $files, $getType, $view, $filename) { if ($getType === self::FILE) { $view->unlockFile($filename, ILockingProvider::LOCK_SHARED); } if ($getType === self::ZIP_FILES) { foreach ($files as $file) { $file = $dir . '/' . $file; $view->unlockFile($file, ILockingProvider::LOCK_SHARED); } } if ($getType === self::ZIP_DIR) { $file = $dir . '/' . $files; $view->unlockFile($file, ILockingProvider::LOCK_SHARED); } } } template/functions.php 0000604 00000017025 15247162136 0011110 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Prints a sanitized string * @param string $string the string which will be escaped and printed */ function p($string) { print(\OCP\Util::sanitizeHTML($string)); } /** * Prints an unsanitized string - usage of this function may result into XSS. * Consider using p() instead. * @param string|array $string the string which will be printed as it is */ function print_unescaped($string) { print($string); } /** * Shortcut for adding scripts to a page * @param string $app the appname * @param string|string[] $file the filename, * if an array is given it will add all scripts */ function script($app, $file = null) { if(is_array($file)) { foreach($file as $f) { OC_Util::addScript($app, $f); } } else { OC_Util::addScript($app, $file); } } /** * Shortcut for adding vendor scripts to a page * @param string $app the appname * @param string|string[] $file the filename, * if an array is given it will add all scripts */ function vendor_script($app, $file = null) { if(is_array($file)) { foreach($file as $f) { OC_Util::addVendorScript($app, $f); } } else { OC_Util::addVendorScript($app, $file); } } /** * Shortcut for adding styles to a page * @param string $app the appname * @param string|string[] $file the filename, * if an array is given it will add all styles */ function style($app, $file = null) { if(is_array($file)) { foreach($file as $f) { OC_Util::addStyle($app, $f); } } else { OC_Util::addStyle($app, $file); } } /** * Shortcut for adding vendor styles to a page * @param string $app the appname * @param string|string[] $file the filename, * if an array is given it will add all styles */ function vendor_style($app, $file = null) { if(is_array($file)) { foreach($file as $f) { OC_Util::addVendorStyle($app, $f); } } else { OC_Util::addVendorStyle($app, $file); } } /** * Shortcut for adding translations to a page * @param string $app the appname * if an array is given it will add all styles */ function translation($app) { OC_Util::addTranslations($app); } /** * Shortcut for HTML imports * @param string $app the appname * @param string|string[] $file the path relative to the app's component folder, * if an array is given it will add all components */ function component($app, $file) { if(is_array($file)) { foreach($file as $f) { $url = link_to($app, 'component/' . $f . '.html'); OC_Util::addHeader('link', array('rel' => 'import', 'href' => $url)); } } else { $url = link_to($app, 'component/' . $file . '.html'); OC_Util::addHeader('link', array('rel' => 'import', 'href' => $url)); } } /** * make \OCP\IURLGenerator::linkTo available as a simple function * @param string $app app * @param string $file file * @param array $args array with param=>value, will be appended to the returned url * @return string link to the file * * For further information have a look at \OCP\IURLGenerator::linkTo */ function link_to( $app, $file, $args = array() ) { return \OC::$server->getURLGenerator()->linkTo($app, $file, $args); } /** * @param $key * @return string url to the online documentation */ function link_to_docs($key) { return \OC::$server->getURLGenerator()->linkToDocs($key); } /** * make \OCP\IURLGenerator::imagePath available as a simple function * @param string $app app * @param string $image image * @return string link to the image * * For further information have a look at \OCP\IURLGenerator::imagePath */ function image_path( $app, $image ) { return \OC::$server->getURLGenerator()->imagePath( $app, $image ); } /** * make OC_Helper::mimetypeIcon available as a simple function * @param string $mimetype mimetype * @return string link to the image */ function mimetype_icon( $mimetype ) { return \OC::$server->getMimeTypeDetector()->mimeTypeIcon( $mimetype ); } /** * make preview_icon available as a simple function * Returns the path to the preview of the image. * @param string $path path of file * @return link to the preview */ function preview_icon( $path ) { return \OC::$server->getURLGenerator()->linkToRoute('core.Preview.getPreview', ['x' => 32, 'y' => 32, 'file' => $path]); } /** * @param string $path */ function publicPreview_icon ( $path, $token ) { return \OC::$server->getURLGenerator()->linkToRoute('files_sharing.PublicPreview.getPreview', ['x' => 32, 'y' => 32, 'file' => $path, 't' => $token]); } /** * make OC_Helper::humanFileSize available as a simple function * @param int $bytes size in bytes * @return string size as string * * For further information have a look at OC_Helper::humanFileSize */ function human_file_size( $bytes ) { return OC_Helper::humanFileSize( $bytes ); } /** * Strips the timestamp of its time value * @param int $timestamp UNIX timestamp to strip * @return $timestamp without time value */ function strip_time($timestamp){ $date = new \DateTime("@{$timestamp}"); $date->setTime(0, 0, 0); return intval($date->format('U')); } /** * Formats timestamp relatively to the current time using * a human-friendly format like "x minutes ago" or "yesterday" * @param int $timestamp timestamp to format * @param int $fromTime timestamp to compare from, defaults to current time * @param bool $dateOnly whether to strip time information * @return string timestamp */ function relative_modified_date($timestamp, $fromTime = null, $dateOnly = false) { /** @var \OC\DateTimeFormatter $formatter */ $formatter = \OC::$server->query('DateTimeFormatter'); if ($dateOnly){ return $formatter->formatDateSpan($timestamp, $fromTime); } return $formatter->formatTimeSpan($timestamp, $fromTime); } function html_select_options($options, $selected, $params=array()) { if (!is_array($selected)) { $selected=array($selected); } if (isset($params['combine']) && $params['combine']) { $options = array_combine($options, $options); } $value_name = $label_name = false; if (isset($params['value'])) { $value_name = $params['value']; } if (isset($params['label'])) { $label_name = $params['label']; } $html = ''; foreach($options as $value => $label) { if ($value_name && is_array($label)) { $value = $label[$value_name]; } if ($label_name && is_array($label)) { $label = $label[$label_name]; } $select = in_array($value, $selected) ? ' selected="selected"' : ''; $html .= '<option value="' . \OCP\Util::sanitizeHTML($value) . '"' . $select . '>' . \OCP\Util::sanitizeHTML($label) . '</option>'."\n"; } return $html; }
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Generation time: 0 |
proxy
|
phpinfo
|
Settings