summaryrefslogtreecommitdiffstats
path: root/lib/private
diff options
context:
space:
mode:
Diffstat (limited to 'lib/private')
-rw-r--r--lib/private/AppFramework/Http/Request.php10
-rw-r--r--lib/private/Files/Config/CachedMountInfo.php6
-rw-r--r--lib/private/Files/Config/LazyStorageMountInfo.php8
-rw-r--r--lib/private/Files/Config/UserMountCache.php104
-rw-r--r--lib/private/Files/FileInfo.php23
-rw-r--r--lib/private/Files/ObjectStore/S3ObjectTrait.php8
-rw-r--r--lib/private/Files/Storage/Local.php27
-rw-r--r--lib/private/Preview/EMF.php33
-rw-r--r--lib/private/Preview/Generator.php10
-rw-r--r--lib/private/Preview/Office.php60
-rw-r--r--lib/private/PreviewManager.php60
-rw-r--r--lib/private/Repair.php2
-rw-r--r--lib/private/Repair/AddMetadataMigrationJob.php43
-rw-r--r--lib/private/Repair/RepairMimeTypes.php11
-rw-r--r--lib/private/Search/Filter/GroupFilter.php5
-rw-r--r--lib/private/Search/Filter/UserFilter.php5
-rw-r--r--lib/private/Search/FilterFactory.php4
-rw-r--r--lib/private/Search/SearchComposer.php18
-rw-r--r--lib/private/Server.php4
-rw-r--r--lib/private/Setup.php2
-rw-r--r--lib/private/User/AvailabilityCoordinator.php11
21 files changed, 316 insertions, 138 deletions
diff --git a/lib/private/AppFramework/Http/Request.php b/lib/private/AppFramework/Http/Request.php
index 26a76e0da27..1186753ac73 100644
--- a/lib/private/AppFramework/Http/Request.php
+++ b/lib/private/AppFramework/Http/Request.php
@@ -593,9 +593,11 @@ class Request implements \ArrayAccess, \Countable, IRequest {
// only have one default, so we cannot ship an insecure product out of the box
]);
- foreach ($forwardedForHeaders as $header) {
+ // Read the x-forwarded-for headers and values in reverse order as per
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For#selecting_an_ip_address
+ foreach (array_reverse($forwardedForHeaders) as $header) {
if (isset($this->server[$header])) {
- foreach (explode(',', $this->server[$header]) as $IP) {
+ foreach (array_reverse(explode(',', $this->server[$header])) as $IP) {
$IP = trim($IP);
// remove brackets from IPv6 addresses
@@ -603,6 +605,10 @@ class Request implements \ArrayAccess, \Countable, IRequest {
$IP = substr($IP, 1, -1);
}
+ if ($this->isTrustedProxy($trustedProxies, $IP)) {
+ continue;
+ }
+
if (filter_var($IP, FILTER_VALIDATE_IP) !== false) {
return $IP;
}
diff --git a/lib/private/Files/Config/CachedMountInfo.php b/lib/private/Files/Config/CachedMountInfo.php
index 43c9fae63ec..7c97135a565 100644
--- a/lib/private/Files/Config/CachedMountInfo.php
+++ b/lib/private/Files/Config/CachedMountInfo.php
@@ -35,6 +35,7 @@ class CachedMountInfo implements ICachedMountInfo {
protected ?int $mountId;
protected string $rootInternalPath;
protected string $mountProvider;
+ protected string $key;
/**
* CachedMountInfo constructor.
@@ -65,6 +66,7 @@ class CachedMountInfo implements ICachedMountInfo {
throw new \Exception("Mount provider $mountProvider name exceeds the limit of 128 characters");
}
$this->mountProvider = $mountProvider;
+ $this->key = $rootId . '::' . $mountPoint;
}
/**
@@ -132,4 +134,8 @@ class CachedMountInfo implements ICachedMountInfo {
public function getMountProvider(): string {
return $this->mountProvider;
}
+
+ public function getKey(): string {
+ return $this->key;
+ }
}
diff --git a/lib/private/Files/Config/LazyStorageMountInfo.php b/lib/private/Files/Config/LazyStorageMountInfo.php
index 78055a2cdb8..7e4acb2e129 100644
--- a/lib/private/Files/Config/LazyStorageMountInfo.php
+++ b/lib/private/Files/Config/LazyStorageMountInfo.php
@@ -39,6 +39,7 @@ class LazyStorageMountInfo extends CachedMountInfo {
$this->rootId = 0;
$this->storageId = 0;
$this->mountPoint = '';
+ $this->key = '';
}
/**
@@ -87,4 +88,11 @@ class LazyStorageMountInfo extends CachedMountInfo {
public function getMountProvider(): string {
return $this->mount->getMountProvider();
}
+
+ public function getKey(): string {
+ if (!$this->key) {
+ $this->key = $this->getRootId() . '::' . $this->getMountPoint();
+ }
+ return $this->key;
+ }
}
diff --git a/lib/private/Files/Config/UserMountCache.php b/lib/private/Files/Config/UserMountCache.php
index 7502d65d044..2fb7a7d83f4 100644
--- a/lib/private/Files/Config/UserMountCache.php
+++ b/lib/private/Files/Config/UserMountCache.php
@@ -29,13 +29,11 @@
namespace OC\Files\Config;
use OCP\Cache\CappedMemoryCache;
-use OCA\Files_Sharing\SharedMount;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Diagnostics\IEventLogger;
use OCP\Files\Config\ICachedMountFileInfo;
use OCP\Files\Config\ICachedMountInfo;
use OCP\Files\Config\IUserMountCache;
-use OCP\Files\Mount\IMountPoint;
use OCP\Files\NotFoundException;
use OCP\IDBConnection;
use OCP\IUser;
@@ -78,41 +76,27 @@ class UserMountCache implements IUserMountCache {
public function registerMounts(IUser $user, array $mounts, array $mountProviderClasses = null) {
$this->eventLogger->start('fs:setup:user:register', 'Registering mounts for user');
- // filter out non-proper storages coming from unit tests
- $mounts = array_filter($mounts, function (IMountPoint $mount) {
- return $mount instanceof SharedMount || ($mount->getStorage() && $mount->getStorage()->getCache());
- });
- /** @var ICachedMountInfo[] $newMounts */
- $newMounts = array_map(function (IMountPoint $mount) use ($user) {
+ /** @var array<string, ICachedMountInfo> $newMounts */
+ $newMounts = [];
+ foreach ($mounts as $mount) {
// filter out any storages which aren't scanned yet since we aren't interested in files from those storages (yet)
- if ($mount->getStorageRootId() === -1) {
- return null;
- } else {
- return new LazyStorageMountInfo($user, $mount);
+ if ($mount->getStorageRootId() !== -1) {
+ $mountInfo = new LazyStorageMountInfo($user, $mount);
+ $newMounts[$mountInfo->getKey()] = $mountInfo;
}
- }, $mounts);
- $newMounts = array_values(array_filter($newMounts));
- $newMountKeys = array_map(function (ICachedMountInfo $mount) {
- return $mount->getRootId() . '::' . $mount->getMountPoint();
- }, $newMounts);
- $newMounts = array_combine($newMountKeys, $newMounts);
+ }
$cachedMounts = $this->getMountsForUser($user);
if (is_array($mountProviderClasses)) {
$cachedMounts = array_filter($cachedMounts, function (ICachedMountInfo $mountInfo) use ($mountProviderClasses, $newMounts) {
// for existing mounts that didn't have a mount provider set
// we still want the ones that map to new mounts
- $mountKey = $mountInfo->getRootId() . '::' . $mountInfo->getMountPoint();
- if ($mountInfo->getMountProvider() === '' && isset($newMounts[$mountKey])) {
+ if ($mountInfo->getMountProvider() === '' && isset($newMounts[$mountInfo->getKey()])) {
return true;
}
return in_array($mountInfo->getMountProvider(), $mountProviderClasses);
});
}
- $cachedRootKeys = array_map(function (ICachedMountInfo $mount) {
- return $mount->getRootId() . '::' . $mount->getMountPoint();
- }, $cachedMounts);
- $cachedMounts = array_combine($cachedRootKeys, $cachedMounts);
$addedMounts = [];
$removedMounts = [];
@@ -131,46 +115,44 @@ class UserMountCache implements IUserMountCache {
$changedMounts = $this->findChangedMounts($newMounts, $cachedMounts);
- $this->connection->beginTransaction();
- try {
- foreach ($addedMounts as $mount) {
- $this->addToCache($mount);
- /** @psalm-suppress InvalidArgument */
- $this->mountsForUsers[$user->getUID()][] = $mount;
- }
- foreach ($removedMounts as $mount) {
- $this->removeFromCache($mount);
- $index = array_search($mount, $this->mountsForUsers[$user->getUID()]);
- unset($this->mountsForUsers[$user->getUID()][$index]);
- }
- foreach ($changedMounts as $mount) {
- $this->updateCachedMount($mount);
+ if ($addedMounts || $removedMounts || $changedMounts) {
+ $this->connection->beginTransaction();
+ $userUID = $user->getUID();
+ try {
+ foreach ($addedMounts as $mount) {
+ $this->addToCache($mount);
+ /** @psalm-suppress InvalidArgument */
+ $this->mountsForUsers[$userUID][$mount->getKey()] = $mount;
+ }
+ foreach ($removedMounts as $mount) {
+ $this->removeFromCache($mount);
+ unset($this->mountsForUsers[$userUID][$mount->getKey()]);
+ }
+ foreach ($changedMounts as $mount) {
+ $this->updateCachedMount($mount);
+ /** @psalm-suppress InvalidArgument */
+ $this->mountsForUsers[$userUID][$mount->getKey()] = $mount;
+ }
+ $this->connection->commit();
+ } catch (\Throwable $e) {
+ $this->connection->rollBack();
+ throw $e;
}
- $this->connection->commit();
- } catch (\Throwable $e) {
- $this->connection->rollBack();
- throw $e;
}
$this->eventLogger->end('fs:setup:user:register');
}
/**
- * @param ICachedMountInfo[] $newMounts
- * @param ICachedMountInfo[] $cachedMounts
+ * @param array<string, ICachedMountInfo> $newMounts
+ * @param array<string, ICachedMountInfo> $cachedMounts
* @return ICachedMountInfo[]
*/
private function findChangedMounts(array $newMounts, array $cachedMounts) {
- $new = [];
- foreach ($newMounts as $mount) {
- $new[$mount->getRootId() . '::' . $mount->getMountPoint()] = $mount;
- }
$changed = [];
- foreach ($cachedMounts as $cachedMount) {
- $key = $cachedMount->getRootId() . '::' . $cachedMount->getMountPoint();
- if (isset($new[$key])) {
- $newMount = $new[$key];
+ foreach ($cachedMounts as $key => $cachedMount) {
+ if (isset($newMounts[$key])) {
+ $newMount = $newMounts[$key];
if (
- $newMount->getMountPoint() !== $cachedMount->getMountPoint() ||
$newMount->getStorageId() !== $cachedMount->getStorageId() ||
$newMount->getMountId() !== $cachedMount->getMountId() ||
$newMount->getMountProvider() !== $cachedMount->getMountProvider()
@@ -247,20 +229,28 @@ class UserMountCache implements IUserMountCache {
* @return ICachedMountInfo[]
*/
public function getMountsForUser(IUser $user) {
- if (!isset($this->mountsForUsers[$user->getUID()])) {
+ $userUID = $user->getUID();
+ if (!isset($this->mountsForUsers[$userUID])) {
$builder = $this->connection->getQueryBuilder();
$query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id', 'f.path', 'mount_provider_class')
->from('mounts', 'm')
->innerJoin('m', 'filecache', 'f', $builder->expr()->eq('m.root_id', 'f.fileid'))
- ->where($builder->expr()->eq('user_id', $builder->createPositionalParameter($user->getUID())));
+ ->where($builder->expr()->eq('user_id', $builder->createPositionalParameter($userUID)));
$result = $query->execute();
$rows = $result->fetchAll();
$result->closeCursor();
- $this->mountsForUsers[$user->getUID()] = array_filter(array_map([$this, 'dbRowToMountInfo'], $rows));
+ $this->mountsForUsers[$userUID] = [];
+ /** @var array<string, ICachedMountInfo> $mounts */
+ foreach ($rows as $row) {
+ $mount = $this->dbRowToMountInfo($row);
+ if ($mount !== null) {
+ $this->mountsForUsers[$userUID][$mount->getKey()] = $mount;
+ }
+ }
}
- return $this->mountsForUsers[$user->getUID()];
+ return $this->mountsForUsers[$userUID];
}
/**
diff --git a/lib/private/Files/FileInfo.php b/lib/private/Files/FileInfo.php
index 4ef32861f24..5ba2f27b78b 100644
--- a/lib/private/Files/FileInfo.php
+++ b/lib/private/Files/FileInfo.php
@@ -123,21 +123,14 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess {
*/
#[\ReturnTypeWillChange]
public function offsetGet($offset) {
- if ($offset === 'type') {
- return $this->getType();
- } elseif ($offset === 'etag') {
- return $this->getEtag();
- } elseif ($offset === 'size') {
- return $this->getSize();
- } elseif ($offset === 'mtime') {
- return $this->getMTime();
- } elseif ($offset === 'permissions') {
- return $this->getPermissions();
- } elseif (isset($this->data[$offset])) {
- return $this->data[$offset];
- } else {
- return null;
- }
+ return match ($offset) {
+ 'type' => $this->getType(),
+ 'etag' => $this->getEtag(),
+ 'size' => $this->getSize(),
+ 'mtime' => $this->getMTime(),
+ 'permissions' => $this->getPermissions(),
+ default => $this->data[$offset] ?? null,
+ };
}
/**
diff --git a/lib/private/Files/ObjectStore/S3ObjectTrait.php b/lib/private/Files/ObjectStore/S3ObjectTrait.php
index e9c52f11936..217e1a1a2ff 100644
--- a/lib/private/Files/ObjectStore/S3ObjectTrait.php
+++ b/lib/private/Files/ObjectStore/S3ObjectTrait.php
@@ -191,6 +191,11 @@ trait S3ObjectTrait {
}
public function copyObject($from, $to, array $options = []) {
+ $sourceMetadata = $this->getConnection()->headObject([
+ 'Bucket' => $this->getBucket(),
+ 'Key' => $from,
+ ] + $this->getSSECParameters());
+
$copy = new MultipartCopy($this->getConnection(), [
"source_bucket" => $this->getBucket(),
"source_key" => $from
@@ -198,7 +203,8 @@ trait S3ObjectTrait {
"bucket" => $this->getBucket(),
"key" => $to,
"acl" => "private",
- "params" => $this->getSSECParameters() + $this->getSSECParameters(true)
+ "params" => $this->getSSECParameters() + $this->getSSECParameters(true),
+ "source_metadata" => $sourceMetadata
], $options));
$copy->copy();
}
diff --git a/lib/private/Files/Storage/Local.php b/lib/private/Files/Storage/Local.php
index eeb9e11b24e..0fca853da59 100644
--- a/lib/private/Files/Storage/Local.php
+++ b/lib/private/Files/Storage/Local.php
@@ -74,6 +74,8 @@ class Local extends \OC\Files\Storage\Common {
protected bool $unlinkOnTruncate;
+ protected bool $caseInsensitive = false;
+
public function __construct($arguments) {
if (!isset($arguments['datadir']) || !is_string($arguments['datadir'])) {
throw new \InvalidArgumentException('No data directory set for local storage');
@@ -93,6 +95,7 @@ class Local extends \OC\Files\Storage\Common {
$this->config = \OC::$server->get(IConfig::class);
$this->mimeTypeDetector = \OC::$server->get(IMimeTypeDetector::class);
$this->defUMask = $this->config->getSystemValue('localstorage.umask', 0022);
+ $this->caseInsensitive = $this->config->getSystemValueBool('localstorage.case_insensitive', false);
// support Write-Once-Read-Many file systems
$this->unlinkOnTruncate = $this->config->getSystemValueBool('localstorage.unlink_on_truncate', false);
@@ -162,6 +165,9 @@ class Local extends \OC\Files\Storage\Common {
}
public function is_dir($path) {
+ if ($this->caseInsensitive && !$this->file_exists($path)) {
+ return false;
+ }
if (str_ends_with($path, '/')) {
$path = substr($path, 0, -1);
}
@@ -169,6 +175,9 @@ class Local extends \OC\Files\Storage\Common {
}
public function is_file($path) {
+ if ($this->caseInsensitive && !$this->file_exists($path)) {
+ return false;
+ }
return is_file($this->getSourcePath($path));
}
@@ -271,7 +280,13 @@ class Local extends \OC\Files\Storage\Common {
}
public function file_exists($path) {
- return file_exists($this->getSourcePath($path));
+ if ($this->caseInsensitive) {
+ $fullPath = $this->getSourcePath($path);
+ $content = scandir(dirname($fullPath), SCANDIR_SORT_NONE);
+ return is_array($content) && array_search(basename($fullPath), $content) !== false;
+ } else {
+ return file_exists($this->getSourcePath($path));
+ }
}
public function filemtime($path) {
@@ -372,6 +387,11 @@ class Local extends \OC\Files\Storage\Common {
}
if (@rename($this->getSourcePath($source), $this->getSourcePath($target))) {
+ if ($this->caseInsensitive) {
+ if (mb_strtolower($target) === mb_strtolower($source) && !$this->file_exists($target)) {
+ return false;
+ }
+ }
return true;
}
@@ -388,6 +408,11 @@ class Local extends \OC\Files\Storage\Common {
}
$result = copy($this->getSourcePath($source), $this->getSourcePath($target));
umask($oldMask);
+ if ($this->caseInsensitive) {
+ if (mb_strtolower($target) === mb_strtolower($source) && !$this->file_exists($target)) {
+ return false;
+ }
+ }
return $result;
}
}
diff --git a/lib/private/Preview/EMF.php b/lib/private/Preview/EMF.php
new file mode 100644
index 00000000000..2b5f40e66af
--- /dev/null
+++ b/lib/private/Preview/EMF.php
@@ -0,0 +1,33 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright 2023 Daniel Kesselberg <mail@danielkesselberg.de>
+ *
+ * @author Daniel Kesselberg <mail@danielkesselberg.de>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\Preview;
+
+class EMF extends Office {
+ public function getMimeType(): string {
+ return '/image\/emf/';
+ }
+}
diff --git a/lib/private/Preview/Generator.php b/lib/private/Preview/Generator.php
index 958f58e2a01..695d4a3357f 100644
--- a/lib/private/Preview/Generator.php
+++ b/lib/private/Preview/Generator.php
@@ -257,9 +257,15 @@ class Generator {
*/
public static function getHardwareConcurrency(): int {
static $width;
+
if (!isset($width)) {
- if (is_file("/proc/cpuinfo")) {
- $width = substr_count(file_get_contents("/proc/cpuinfo"), "processor");
+ if (function_exists('ini_get')) {
+ $openBasedir = ini_get('open_basedir');
+ if (empty($openBasedir) || strpos($openBasedir, '/proc/cpuinfo') !== false) {
+ $width = is_readable('/proc/cpuinfo') ? substr_count(file_get_contents('/proc/cpuinfo'), 'processor') : 0;
+ } else {
+ $width = 0;
+ }
} else {
$width = 0;
}
diff --git a/lib/private/Preview/Office.php b/lib/private/Preview/Office.php
index 3ba7c5a21a0..68499a6fea6 100644
--- a/lib/private/Preview/Office.php
+++ b/lib/private/Preview/Office.php
@@ -31,7 +31,8 @@ namespace OC\Preview;
use OCP\Files\File;
use OCP\Files\FileInfo;
use OCP\IImage;
-use Psr\Log\LoggerInterface;
+use OCP\ITempManager;
+use OCP\Server;
abstract class Office extends ProviderV2 {
/**
@@ -49,51 +50,60 @@ abstract class Office extends ProviderV2 {
return null;
}
- $absPath = $this->getLocalFile($file);
-
- $tmpDir = \OC::$server->getTempManager()->getTempBaseDir();
+ $tempManager = Server::get(ITempManager::class);
- $defaultParameters = ' -env:UserInstallation=file://' . escapeshellarg($tmpDir . '/owncloud-' . \OC_Util::getInstanceId() . '/') . ' --headless --nologo --nofirststartwizard --invisible --norestore --convert-to png --outdir ';
- $clParameters = \OC::$server->getConfig()->getSystemValue('preview_office_cl_parameters', $defaultParameters);
+ // The file to generate the preview for.
+ $absPath = $this->getLocalFile($file);
- $cmd = $this->options['officeBinary'] . $clParameters . escapeshellarg($tmpDir) . ' ' . escapeshellarg($absPath);
+ // The destination for the LibreOffice user profile.
+ // LibreOffice can rune once per user profile and therefore instance id and file id are included.
+ $profile = $tempManager->getTemporaryFolder(
+ 'nextcloud-office-profile-' . \OC_Util::getInstanceId() . '-' . $file->getId()
+ );
- exec($cmd, $output, $returnCode);
+ // The destination for the LibreOffice convert result.
+ $outdir = $tempManager->getTemporaryFolder(
+ 'nextcloud-office-preview-' . \OC_Util::getInstanceId() . '-' . $file->getId()
+ );
- if ($returnCode !== 0) {
+ if ($profile === false || $outdir === false) {
$this->cleanTmpFiles();
return null;
}
- //create imagick object from png
- $pngPreview = null;
- try {
- [$dirname, , , $filename] = array_values(pathinfo($absPath));
- $pngPreview = $tmpDir . '/' . $filename . '.png';
+ $parameters = [
+ $this->options['officeBinary'],
+ '-env:UserInstallation=file://' . escapeshellarg($profile),
+ '--headless',
+ '--nologo',
+ '--nofirststartwizard',
+ '--invisible',
+ '--norestore',
+ '--convert-to png',
+ '--outdir ' . escapeshellarg($outdir),
+ escapeshellarg($absPath),
+ ];
- $png = new \Imagick($pngPreview . '[0]');
- $png->setImageFormat('jpg');
- } catch (\Exception $e) {
+ $cmd = implode(' ', $parameters);
+ exec($cmd, $output, $returnCode);
+
+ if ($returnCode !== 0) {
$this->cleanTmpFiles();
- unlink($pngPreview);
- \OC::$server->get(LoggerInterface::class)->error($e->getMessage(), [
- 'exception' => $e,
- 'app' => 'core',
- ]);
return null;
}
+ $preview = $outdir . pathinfo($absPath, PATHINFO_FILENAME) . '.png';
+
$image = new \OCP\Image();
- $image->loadFromData((string) $png);
+ $image->loadFromFile($preview);
$this->cleanTmpFiles();
- unlink($pngPreview);
if ($image->valid()) {
$image->scaleDownToFit($maxX, $maxY);
-
return $image;
}
+
return null;
}
}
diff --git a/lib/private/PreviewManager.php b/lib/private/PreviewManager.php
index 3af6848686e..aedcbbce335 100644
--- a/lib/private/PreviewManager.php
+++ b/lib/private/PreviewManager.php
@@ -366,7 +366,7 @@ class PreviewManager implements IPreview {
$this->registerCoreProvider(Preview\OpenDocument::class, '/application\/vnd.oasis.opendocument.*/');
$this->registerCoreProvider(Preview\Imaginary::class, Preview\Imaginary::supportedMimeTypes());
- // SVG, Office and Bitmap require imagick
+ // SVG and Bitmap require imagick
if ($this->imagickSupport->hasExtension()) {
$imagickProviders = [
'SVG' => ['mimetype' => '/image\/svg\+xml/', 'class' => Preview\SVG::class],
@@ -391,27 +391,10 @@ class PreviewManager implements IPreview {
$this->registerCoreProvider($class, $provider['mimetype']);
}
}
-
- if ($this->imagickSupport->supportsFormat('PDF')) {
- // Office requires openoffice or libreoffice
- $officeBinary = $this->config->getSystemValue('preview_libreoffice_path', null);
- if (!is_string($officeBinary)) {
- $officeBinary = $this->binaryFinder->findBinaryPath('libreoffice');
- }
- if (!is_string($officeBinary)) {
- $officeBinary = $this->binaryFinder->findBinaryPath('openoffice');
- }
-
- if (is_string($officeBinary)) {
- $this->registerCoreProvider(Preview\MSOfficeDoc::class, '/application\/msword/', ["officeBinary" => $officeBinary]);
- $this->registerCoreProvider(Preview\MSOffice2003::class, '/application\/vnd.ms-.*/', ["officeBinary" => $officeBinary]);
- $this->registerCoreProvider(Preview\MSOffice2007::class, '/application\/vnd.openxmlformats-officedocument.*/', ["officeBinary" => $officeBinary]);
- $this->registerCoreProvider(Preview\OpenDocument::class, '/application\/vnd.oasis.opendocument.*/', ["officeBinary" => $officeBinary]);
- $this->registerCoreProvider(Preview\StarOffice::class, '/application\/vnd.sun.xml.*/', ["officeBinary" => $officeBinary]);
- }
- }
}
+ $this->registerCoreProvidersOffice();
+
// Video requires avconv or ffmpeg
if (in_array(Preview\Movie::class, $this->getEnabledDefaultProvider())) {
$movieBinary = $this->config->getSystemValue('preview_ffmpeg_path', null);
@@ -429,6 +412,43 @@ class PreviewManager implements IPreview {
}
}
+ private function registerCoreProvidersOffice(): void {
+ $officeProviders = [
+ ['mimetype' => '/application\/msword/', 'class' => Preview\MSOfficeDoc::class],
+ ['mimetype' => '/application\/vnd.ms-.*/', 'class' => Preview\MSOffice2003::class],
+ ['mimetype' => '/application\/vnd.openxmlformats-officedocument.*/', 'class' => Preview\MSOffice2007::class],
+ ['mimetype' => '/application\/vnd.oasis.opendocument.*/', 'class' => Preview\OpenDocument::class],
+ ['mimetype' => '/application\/vnd.sun.xml.*/', 'class' => Preview\StarOffice::class],
+ ['mimetype' => '/image\/emf/', 'class' => Preview\EMF::class],
+ ];
+
+ $findBinary = true;
+ $officeBinary = false;
+
+ foreach ($officeProviders as $provider) {
+ $class = $provider['class'];
+ if (!in_array(trim($class, '\\'), $this->getEnabledDefaultProvider())) {
+ continue;
+ }
+
+ if ($findBinary) {
+ // Office requires openoffice or libreoffice
+ $officeBinary = $this->config->getSystemValue('preview_libreoffice_path', false);
+ if ($officeBinary === false) {
+ $officeBinary = $this->binaryFinder->findBinaryPath('libreoffice');
+ }
+ if ($officeBinary === false) {
+ $officeBinary = $this->binaryFinder->findBinaryPath('openoffice');
+ }
+ $findBinary = false;
+ }
+
+ if ($officeBinary) {
+ $this->registerCoreProvider($class, $provider['mimetype'], ['officeBinary' => $officeBinary]);
+ }
+ }
+ }
+
private function registerBootstrapProviders(): void {
$context = $this->bootstrapCoordinator->getRegistrationContext();
diff --git a/lib/private/Repair.php b/lib/private/Repair.php
index 967d079d6db..e70c3f7e39d 100644
--- a/lib/private/Repair.php
+++ b/lib/private/Repair.php
@@ -46,6 +46,7 @@ use OC\DB\Connection;
use OC\DB\ConnectionAdapter;
use OC\Repair\AddBruteForceCleanupJob;
use OC\Repair\AddCleanupUpdaterBackupsJob;
+use OC\Repair\AddMetadataMigrationJob;
use OC\Repair\CleanTags;
use OC\Repair\ClearFrontendCaches;
use OC\Repair\ClearGeneratedAvatarCache;
@@ -211,6 +212,7 @@ class Repair implements IOutput {
\OCP\Server::get(CleanUpAbandonedApps::class),
\OCP\Server::get(AddMissingSecretJob::class),
\OCP\Server::get(AddRemoveOldTasksBackgroundJob::class),
+ \OCP\Server::get(AddMetadataMigrationJob::class),
];
}
diff --git a/lib/private/Repair/AddMetadataMigrationJob.php b/lib/private/Repair/AddMetadataMigrationJob.php
new file mode 100644
index 00000000000..f73768721dc
--- /dev/null
+++ b/lib/private/Repair/AddMetadataMigrationJob.php
@@ -0,0 +1,43 @@
+<?php
+/**
+ * @copyright Copyright (c) 2018 Morris Jobke <hey@morrisjobke.de>
+ *
+ * @author Morris Jobke <hey@morrisjobke.de>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+namespace OC\Repair;
+
+use OC\Core\BackgroundJobs\MigrateMetadataJob;
+use OCP\BackgroundJob\IJobList;
+use OCP\Migration\IOutput;
+use OCP\Migration\IRepairStep;
+
+class AddMetadataMigrationJob implements IRepairStep {
+ public function __construct(
+ private IJobList $jobList,
+ ) {
+ }
+
+ public function getName() {
+ return 'Queue a job to migrate legacy metadata tables';
+ }
+
+ public function run(IOutput $output) {
+ $this->jobList->add(MigrateMetadataJob::class);
+ }
+}
diff --git a/lib/private/Repair/RepairMimeTypes.php b/lib/private/Repair/RepairMimeTypes.php
index ee5a84ad65c..cab8d8ca02a 100644
--- a/lib/private/Repair/RepairMimeTypes.php
+++ b/lib/private/Repair/RepairMimeTypes.php
@@ -229,6 +229,13 @@ class RepairMimeTypes implements IRepairStep {
return $this->updateMimetypes($updatedMimetypes);
}
+ private function introduceEnhancedMetafileFormatType() {
+ $updatedMimetypes = [
+ 'emf' => 'image/emf',
+ ];
+
+ return $this->updateMimetypes($updatedMimetypes);
+ }
/**
* Fix mime types
@@ -286,5 +293,9 @@ class RepairMimeTypes implements IRepairStep {
if (version_compare($ocVersionFromBeforeUpdate, '26.0.0.1', '<') && $this->introduceAsciidocType()) {
$out->info('Fixed AsciiDoc mime types');
}
+
+ if (version_compare($ocVersionFromBeforeUpdate, '28.0.0.5', '<') && $this->introduceEnhancedMetafileFormatType()) {
+ $out->info('Fixed Enhanced Metafile Format mime types');
+ }
}
}
diff --git a/lib/private/Search/Filter/GroupFilter.php b/lib/private/Search/Filter/GroupFilter.php
index 6b42492824c..f0b34a360ca 100644
--- a/lib/private/Search/Filter/GroupFilter.php
+++ b/lib/private/Search/Filter/GroupFilter.php
@@ -38,10 +38,11 @@ class GroupFilter implements IFilter {
string $value,
IGroupManager $groupManager,
) {
- $this->group = $groupManager->get($value);
- if ($this->group === null) {
+ $group = $groupManager->get($value);
+ if ($group === null) {
throw new InvalidArgumentException('Group '.$value.' not found');
}
+ $this->group = $group;
}
public function get(): IGroup {
diff --git a/lib/private/Search/Filter/UserFilter.php b/lib/private/Search/Filter/UserFilter.php
index 1624b60d1a3..963d5e123ac 100644
--- a/lib/private/Search/Filter/UserFilter.php
+++ b/lib/private/Search/Filter/UserFilter.php
@@ -38,10 +38,11 @@ class UserFilter implements IFilter {
string $value,
IUserManager $userManager,
) {
- $this->user = $userManager->get($value);
- if ($this->user === null) {
+ $user = $userManager->get($value);
+ if ($user === null) {
throw new InvalidArgumentException('User '.$value.' not found');
}
+ $this->user = $user;
}
public function get(): IUser {
diff --git a/lib/private/Search/FilterFactory.php b/lib/private/Search/FilterFactory.php
index 19afb2c04be..2e96dfb7960 100644
--- a/lib/private/Search/FilterFactory.php
+++ b/lib/private/Search/FilterFactory.php
@@ -32,6 +32,8 @@ use OCP\IUserManager;
use RuntimeException;
final class FilterFactory {
+ private const PERSON_TYPE_SEPARATOR = '/';
+
public static function get(string $type, string|array $filter): IFilter {
return match ($type) {
FilterDefinition::TYPE_BOOL => new Filter\BooleanFilter($filter),
@@ -48,7 +50,7 @@ final class FilterFactory {
}
private static function getPerson(string $person): IFilter {
- $parts = explode('_', $person, 2);
+ $parts = explode(self::PERSON_TYPE_SEPARATOR, $person, 2);
return match (count($parts)) {
1 => self::get(FilterDefinition::TYPE_NC_USER, $person),
diff --git a/lib/private/Search/SearchComposer.php b/lib/private/Search/SearchComposer.php
index b92fdd250d8..b20d47aa94e 100644
--- a/lib/private/Search/SearchComposer.php
+++ b/lib/private/Search/SearchComposer.php
@@ -81,13 +81,13 @@ class SearchComposer {
private LoggerInterface $logger
) {
$this->commonFilters = [
- 'term' => new FilterDefinition('term', FilterDefinition::TYPE_STRING),
- 'since' => new FilterDefinition('since', FilterDefinition::TYPE_DATETIME),
- 'until' => new FilterDefinition('until', FilterDefinition::TYPE_DATETIME),
- 'title-only' => new FilterDefinition('title-only', FilterDefinition::TYPE_BOOL, false),
- 'person' => new FilterDefinition('person', FilterDefinition::TYPE_PERSON),
- 'places' => new FilterDefinition('places', FilterDefinition::TYPE_STRINGS, false),
- 'provider' => new FilterDefinition('provider', FilterDefinition::TYPE_STRING, false),
+ IFilter::BUILTIN_TERM => new FilterDefinition(IFilter::BUILTIN_TERM, FilterDefinition::TYPE_STRING),
+ IFilter::BUILTIN_SINCE => new FilterDefinition(IFilter::BUILTIN_SINCE, FilterDefinition::TYPE_DATETIME),
+ IFilter::BUILTIN_UNTIL => new FilterDefinition(IFilter::BUILTIN_UNTIL, FilterDefinition::TYPE_DATETIME),
+ IFilter::BUILTIN_TITLE_ONLY => new FilterDefinition(IFilter::BUILTIN_TITLE_ONLY, FilterDefinition::TYPE_BOOL, false),
+ IFilter::BUILTIN_PERSON => new FilterDefinition(IFilter::BUILTIN_PERSON, FilterDefinition::TYPE_PERSON),
+ IFilter::BUILTIN_PLACES => new FilterDefinition(IFilter::BUILTIN_PLACES, FilterDefinition::TYPE_STRINGS, false),
+ IFilter::BUILTIN_PROVIDER => new FilterDefinition(IFilter::BUILTIN_PROVIDER, FilterDefinition::TYPE_STRING, false),
];
}
@@ -189,7 +189,7 @@ class SearchComposer {
$triggers += $provider->getAlternateIds();
$filters = $provider->getSupportedFilters();
} else {
- $filters = ['term'];
+ $filters = [IFilter::BUILTIN_TERM];
}
return [
@@ -310,7 +310,7 @@ class SearchComposer {
$provider = $this->providers[$providerId]['provider'];
$supportedFilters = $provider instanceof IFilteringProvider
? $provider->getSupportedFilters()
- : ['term'];
+ : [IFilter::BUILTIN_TERM];
return in_array($filterDefinition->name(), $supportedFilters, true);
}
diff --git a/lib/private/Server.php b/lib/private/Server.php
index 80f9ba8bdd1..2dd97412eba 100644
--- a/lib/private/Server.php
+++ b/lib/private/Server.php
@@ -158,6 +158,7 @@ use OC\Tagging\TagMapper;
use OC\Talk\Broker;
use OC\Template\JSCombiner;
use OC\Translation\TranslationManager;
+use OC\User\AvailabilityCoordinator;
use OC\User\DisplayNameCache;
use OC\User\Listeners\BeforeUserDeletedListener;
use OC\User\Listeners\UserChangedListener;
@@ -265,6 +266,7 @@ use OCP\User\Events\UserChangedEvent;
use OCP\User\Events\UserLoggedInEvent;
use OCP\User\Events\UserLoggedInWithCookieEvent;
use OCP\User\Events\UserLoggedOutEvent;
+use OCP\User\IAvailabilityCoordinator;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
@@ -1435,6 +1437,8 @@ class Server extends ServerContainer implements IServerContainer {
$this->registerAlias(IProfileManager::class, ProfileManager::class);
+ $this->registerAlias(IAvailabilityCoordinator::class, AvailabilityCoordinator::class);
+
$this->connectDispatcher();
}
diff --git a/lib/private/Setup.php b/lib/private/Setup.php
index 4ef89e14158..e763b6d39c1 100644
--- a/lib/private/Setup.php
+++ b/lib/private/Setup.php
@@ -539,7 +539,7 @@ class Setup {
$content .= "\n Options -MultiViews";
$content .= "\n RewriteRule ^core/js/oc.js$ index.php [PT,E=PATH_INFO:$1]";
$content .= "\n RewriteRule ^core/preview.png$ index.php [PT,E=PATH_INFO:$1]";
- $content .= "\n RewriteCond %{REQUEST_FILENAME} !\\.(css|js|mjs|svg|gif|png|html|ttf|woff2?|ico|jpg|jpeg|map|webm|mp4|mp3|ogg|wav|wasm|tflite)$";
+ $content .= "\n RewriteCond %{REQUEST_FILENAME} !\\.(css|js|mjs|svg|gif|png|html|ttf|woff2?|ico|jpg|jpeg|map|webm|mp4|mp3|ogg|wav|flac|wasm|tflite)$";
$content .= "\n RewriteCond %{REQUEST_FILENAME} !/core/ajax/update\\.php";
$content .= "\n RewriteCond %{REQUEST_FILENAME} !/core/img/(favicon\\.ico|manifest\\.json)$";
$content .= "\n RewriteCond %{REQUEST_FILENAME} !/(cron|public|remote|status)\\.php";
diff --git a/lib/private/User/AvailabilityCoordinator.php b/lib/private/User/AvailabilityCoordinator.php
index fe0db92fd0f..8e6b73bd56d 100644
--- a/lib/private/User/AvailabilityCoordinator.php
+++ b/lib/private/User/AvailabilityCoordinator.php
@@ -27,10 +27,12 @@ declare(strict_types=1);
namespace OC\User;
use JsonException;
+use OCA\DAV\AppInfo\Application;
use OCA\DAV\Db\AbsenceMapper;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\ICache;
use OCP\ICacheFactory;
+use OCP\IConfig;
use OCP\IUser;
use OCP\User\IAvailabilityCoordinator;
use OCP\User\IOutOfOfficeData;
@@ -42,11 +44,20 @@ class AvailabilityCoordinator implements IAvailabilityCoordinator {
public function __construct(
ICacheFactory $cacheFactory,
private AbsenceMapper $absenceMapper,
+ private IConfig $config,
private LoggerInterface $logger,
) {
$this->cache = $cacheFactory->createLocal('OutOfOfficeData');
}
+ public function isEnabled(): bool {
+ return $this->config->getAppValue(
+ Application::APP_ID,
+ 'hide_absence_settings',
+ 'no',
+ ) === 'no';
+ }
+
private function getCachedOutOfOfficeData(IUser $user): ?OutOfOfficeData {
$cachedString = $this->cache->get($user->getUID());
if ($cachedString === null) {