FileUtils.php 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * @copyright Copyright (c) 2023 Robin Appelman <robin@icewind.nl>
  5. *
  6. * @license GNU AGPL version 3 or any later version
  7. *
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU Affero General Public License as
  10. * published by the Free Software Foundation, either version 3 of the
  11. * License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU Affero General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Affero General Public License
  19. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. *
  21. */
  22. namespace OC\Core\Command\Info;
  23. use OCA\Circles\MountManager\CircleMount;
  24. use OCA\Files_External\Config\ExternalMountPoint;
  25. use OCA\Files_Sharing\SharedMount;
  26. use OCA\GroupFolders\Mount\GroupMountPoint;
  27. use OCP\Constants;
  28. use OCP\Files\Config\IUserMountCache;
  29. use OCP\Files\FileInfo;
  30. use OCP\Files\Folder;
  31. use OCP\Files\IHomeStorage;
  32. use OCP\Files\IRootFolder;
  33. use OCP\Files\Mount\IMountPoint;
  34. use OCP\Files\Node;
  35. use OCP\Files\NotFoundException;
  36. use OCP\Share\IShare;
  37. use OCP\Util;
  38. use Symfony\Component\Console\Output\OutputInterface;
  39. class FileUtils {
  40. public function __construct(
  41. private IRootFolder $rootFolder,
  42. private IUserMountCache $userMountCache,
  43. ) {
  44. }
  45. /**
  46. * @param FileInfo $file
  47. * @return array<string, Node[]>
  48. * @throws \OCP\Files\NotPermittedException
  49. * @throws \OC\User\NoUserException
  50. */
  51. public function getFilesByUser(FileInfo $file): array {
  52. $id = $file->getId();
  53. if (!$id) {
  54. return [];
  55. }
  56. $mounts = $this->userMountCache->getMountsForFileId($id);
  57. $result = [];
  58. foreach ($mounts as $mount) {
  59. if (isset($result[$mount->getUser()->getUID()])) {
  60. continue;
  61. }
  62. $userFolder = $this->rootFolder->getUserFolder($mount->getUser()->getUID());
  63. $result[$mount->getUser()->getUID()] = $userFolder->getById($id);
  64. }
  65. return $result;
  66. }
  67. /**
  68. * Get file by either id of path
  69. *
  70. * @param string $fileInput
  71. * @return Node|null
  72. */
  73. public function getNode(string $fileInput): ?Node {
  74. if (is_numeric($fileInput)) {
  75. $mounts = $this->userMountCache->getMountsForFileId((int)$fileInput);
  76. if (!$mounts) {
  77. return null;
  78. }
  79. $mount = $mounts[0];
  80. $userFolder = $this->rootFolder->getUserFolder($mount->getUser()->getUID());
  81. $nodes = $userFolder->getById((int)$fileInput);
  82. if (!$nodes) {
  83. return null;
  84. }
  85. return $nodes[0];
  86. } else {
  87. try {
  88. return $this->rootFolder->get($fileInput);
  89. } catch (NotFoundException $e) {
  90. return null;
  91. }
  92. }
  93. }
  94. public function formatPermissions(string $type, int $permissions): string {
  95. if ($permissions == Constants::PERMISSION_ALL || ($type === 'file' && $permissions == (Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE))) {
  96. return "full permissions";
  97. }
  98. $perms = [];
  99. $allPerms = [Constants::PERMISSION_READ => "read", Constants::PERMISSION_UPDATE => "update", Constants::PERMISSION_CREATE => "create", Constants::PERMISSION_DELETE => "delete", Constants::PERMISSION_SHARE => "share"];
  100. foreach ($allPerms as $perm => $name) {
  101. if (($permissions & $perm) === $perm) {
  102. $perms[] = $name;
  103. }
  104. }
  105. return implode(", ", $perms);
  106. }
  107. /**
  108. * @psalm-suppress UndefinedClass
  109. * @psalm-suppress UndefinedInterfaceMethod
  110. */
  111. public function formatMountType(IMountPoint $mountPoint): string {
  112. $storage = $mountPoint->getStorage();
  113. if ($storage && $storage->instanceOfStorage(IHomeStorage::class)) {
  114. return "home storage";
  115. } elseif ($mountPoint instanceof SharedMount) {
  116. $share = $mountPoint->getShare();
  117. $shares = $mountPoint->getGroupedShares();
  118. $sharedBy = array_map(function (IShare $share) {
  119. $shareType = $this->formatShareType($share);
  120. if ($shareType) {
  121. return $share->getSharedBy() . " (via " . $shareType . " " . $share->getSharedWith() . ")";
  122. } else {
  123. return $share->getSharedBy();
  124. }
  125. }, $shares);
  126. $description = "shared by " . implode(', ', $sharedBy);
  127. if ($share->getSharedBy() !== $share->getShareOwner()) {
  128. $description .= " owned by " . $share->getShareOwner();
  129. }
  130. return $description;
  131. } elseif ($mountPoint instanceof GroupMountPoint) {
  132. return "groupfolder " . $mountPoint->getFolderId();
  133. } elseif ($mountPoint instanceof ExternalMountPoint) {
  134. return "external storage " . $mountPoint->getStorageConfig()->getId();
  135. } elseif ($mountPoint instanceof CircleMount) {
  136. return "circle";
  137. }
  138. return get_class($mountPoint);
  139. }
  140. public function formatShareType(IShare $share): ?string {
  141. switch ($share->getShareType()) {
  142. case IShare::TYPE_GROUP:
  143. return "group";
  144. case IShare::TYPE_CIRCLE:
  145. return "circle";
  146. case IShare::TYPE_DECK:
  147. return "deck";
  148. case IShare::TYPE_ROOM:
  149. return "room";
  150. case IShare::TYPE_USER:
  151. return null;
  152. default:
  153. return "Unknown (" . $share->getShareType() . ")";
  154. }
  155. }
  156. /**
  157. * Print out the largest count($sizeLimits) files in the directory tree
  158. *
  159. * @param OutputInterface $output
  160. * @param Folder $node
  161. * @param string $prefix
  162. * @param array $sizeLimits largest items that are still in the queue to be printed, ordered ascending
  163. * @return int how many items we've printed
  164. */
  165. public function outputLargeFilesTree(
  166. OutputInterface $output,
  167. Folder $node,
  168. string $prefix,
  169. array &$sizeLimits,
  170. bool $all,
  171. ): int {
  172. /**
  173. * Algorithm to print the N largest items in a folder without requiring to query or sort the entire three
  174. *
  175. * This is done by keeping a list ($sizeLimits) of size N that contain the largest items outside of this
  176. * folders that are could be printed if there aren't enough items in this folder that are larger.
  177. *
  178. * We loop over the items in this folder by size descending until the size of the item falls before the smallest
  179. * size in $sizeLimits (at that point there are enough items outside this folder to complete the N items).
  180. *
  181. * When encountering a folder, we create an updated $sizeLimits with the largest items in the current folder still
  182. * remaining which we pass into the recursion. (We don't update the current $sizeLimits because that should only
  183. * hold items *outside* of the current folder.)
  184. *
  185. * For every item printed we remove the first item of $sizeLimits are there is no longer room in the output to print
  186. * items that small.
  187. */
  188. $count = 0;
  189. $children = $node->getDirectoryListing();
  190. usort($children, function (Node $a, Node $b) {
  191. return $b->getSize() <=> $a->getSize();
  192. });
  193. foreach ($children as $i => $child) {
  194. if (!$all) {
  195. if (count($sizeLimits) === 0 || $child->getSize() < $sizeLimits[0]) {
  196. return $count;
  197. }
  198. array_shift($sizeLimits);
  199. }
  200. $count += 1;
  201. /** @var Node $child */
  202. $output->writeln("$prefix- " . $child->getName() . ": <info>" . Util::humanFileSize($child->getSize()) . "</info>");
  203. if ($child instanceof Folder) {
  204. $recurseSizeLimits = $sizeLimits;
  205. if (!$all) {
  206. for ($j = 0; $j < count($recurseSizeLimits); $j++) {
  207. if (isset($children[$i + $j + 1])) {
  208. $nextChildSize = $children[$i + $j + 1]->getSize();
  209. if ($nextChildSize > $recurseSizeLimits[0]) {
  210. array_shift($recurseSizeLimits);
  211. $recurseSizeLimits[] = $nextChildSize;
  212. }
  213. }
  214. }
  215. sort($recurseSizeLimits);
  216. }
  217. $recurseCount = $this->outputLargeFilesTree($output, $child, $prefix . " ", $recurseSizeLimits, $all);
  218. $sizeLimits = array_slice($sizeLimits, $recurseCount);
  219. $count += $recurseCount;
  220. }
  221. }
  222. return $count;
  223. }
  224. }