/owncloud/lib/files/mapper.php

https://github.com/agrias/TerpTaskerRed · PHP · 239 lines · 150 code · 41 blank · 48 comment · 19 complexity · b21cc76b686d4f6eb7504df8d94f438c MD5 · raw file

  1. <?php
  2. namespace OC\Files;
  3. /**
  4. * class Mapper is responsible to translate logical paths to physical paths and reverse
  5. */
  6. class Mapper
  7. {
  8. private $unchangedPhysicalRoot;
  9. public function __construct($rootDir) {
  10. $this->unchangedPhysicalRoot = $rootDir;
  11. }
  12. /**
  13. * @param string $logicPath
  14. * @param bool $create indicates if the generated physical name shall be stored in the database or not
  15. * @return string the physical path
  16. */
  17. public function logicToPhysical($logicPath, $create) {
  18. $physicalPath = $this->resolveLogicPath($logicPath);
  19. if ($physicalPath !== null) {
  20. return $physicalPath;
  21. }
  22. return $this->create($logicPath, $create);
  23. }
  24. /**
  25. * @param string $physicalPath
  26. * @return string
  27. */
  28. public function physicalToLogic($physicalPath) {
  29. $logicPath = $this->resolvePhysicalPath($physicalPath);
  30. if ($logicPath !== null) {
  31. return $logicPath;
  32. }
  33. $this->insert($physicalPath, $physicalPath);
  34. return $physicalPath;
  35. }
  36. /**
  37. * @param string $path
  38. * @param bool $isLogicPath indicates if $path is logical or physical
  39. * @param $recursive
  40. * @return void
  41. */
  42. public function removePath($path, $isLogicPath, $recursive) {
  43. if ($recursive) {
  44. $path=$path.'%';
  45. }
  46. if ($isLogicPath) {
  47. $query = \OC_DB::prepare('DELETE FROM `*PREFIX*file_map` WHERE `logic_path` LIKE ?');
  48. $query->execute(array($path));
  49. } else {
  50. $query = \OC_DB::prepare('DELETE FROM `*PREFIX*file_map` WHERE `physic_path` LIKE ?');
  51. $query->execute(array($path));
  52. }
  53. }
  54. /**
  55. * @param $path1
  56. * @param $path2
  57. * @throws \Exception
  58. */
  59. public function copy($path1, $path2)
  60. {
  61. $path1 = $this->stripLast($path1);
  62. $path2 = $this->stripLast($path2);
  63. $physicPath1 = $this->logicToPhysical($path1, true);
  64. $physicPath2 = $this->logicToPhysical($path2, true);
  65. $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*file_map` WHERE `logic_path` LIKE ?');
  66. $result = $query->execute(array($path1.'%'));
  67. $updateQuery = \OC_DB::prepare('UPDATE `*PREFIX*file_map`'
  68. .' SET `logic_path` = ?'
  69. .' , `logic_path_hash` = ?'
  70. .' , `physic_path` = ?'
  71. .' , `physic_path_hash` = ?'
  72. .' WHERE `logic_path` = ?');
  73. while( $row = $result->fetchRow()) {
  74. $currentLogic = $row['logic_path'];
  75. $currentPhysic = $row['physic_path'];
  76. $newLogic = $path2.$this->stripRootFolder($currentLogic, $path1);
  77. $newPhysic = $physicPath2.$this->stripRootFolder($currentPhysic, $physicPath1);
  78. if ($path1 !== $currentLogic) {
  79. try {
  80. $updateQuery->execute(array($newLogic, md5($newLogic), $newPhysic, md5($newPhysic), $currentLogic));
  81. } catch (\Exception $e) {
  82. error_log('Mapper::Copy failed '.$currentLogic.' -> '.$newLogic.'\n'.$e);
  83. throw $e;
  84. }
  85. }
  86. }
  87. }
  88. /**
  89. * @param $path
  90. * @param $root
  91. * @return bool|string
  92. */
  93. public function stripRootFolder($path, $root) {
  94. if (strpos($path, $root) !== 0) {
  95. // throw exception ???
  96. return false;
  97. }
  98. if (strlen($path) > strlen($root)) {
  99. return substr($path, strlen($root));
  100. }
  101. return '';
  102. }
  103. private function stripLast($path) {
  104. if (substr($path, -1) == '/') {
  105. $path = substr_replace($path, '', -1);
  106. }
  107. return $path;
  108. }
  109. private function resolveLogicPath($logicPath) {
  110. $logicPath = $this->stripLast($logicPath);
  111. $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*file_map` WHERE `logic_path_hash` = ?');
  112. $result = $query->execute(array(md5($logicPath)));
  113. $result = $result->fetchRow();
  114. if ($result === false) {
  115. return null;
  116. }
  117. return $result['physic_path'];
  118. }
  119. private function resolvePhysicalPath($physicalPath) {
  120. $physicalPath = $this->stripLast($physicalPath);
  121. $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*file_map` WHERE `physic_path_hash` = ?');
  122. $result = $query->execute(array(md5($physicalPath)));
  123. $result = $result->fetchRow();
  124. return $result['logic_path'];
  125. }
  126. private function create($logicPath, $store) {
  127. $logicPath = $this->stripLast($logicPath);
  128. $index = 0;
  129. // create the slugified path
  130. $physicalPath = $this->slugifyPath($logicPath);
  131. // detect duplicates
  132. while ($this->resolvePhysicalPath($physicalPath) !== null) {
  133. $physicalPath = $this->slugifyPath($logicPath, $index++);
  134. }
  135. // insert the new path mapping if requested
  136. if ($store) {
  137. $this->insert($logicPath, $physicalPath);
  138. }
  139. return $physicalPath;
  140. }
  141. private function insert($logicPath, $physicalPath) {
  142. $query = \OC_DB::prepare('INSERT INTO `*PREFIX*file_map`(`logic_path`, `physic_path`, `logic_path_hash`, `physic_path_hash`) VALUES(?, ?, ?, ?)');
  143. $query->execute(array($logicPath, $physicalPath, md5($logicPath), md5($physicalPath)));
  144. }
  145. public function slugifyPath($path, $index=null) {
  146. $path = $this->stripRootFolder($path, $this->unchangedPhysicalRoot);
  147. $pathElements = explode('/', $path);
  148. $sluggedElements = array();
  149. $last= end($pathElements);
  150. foreach ($pathElements as $pathElement) {
  151. // remove empty elements
  152. if (empty($pathElement)) {
  153. continue;
  154. }
  155. $sluggedElements[] = self::slugify($pathElement);
  156. }
  157. // apply index to file name
  158. if ($index !== null) {
  159. $last= array_pop($sluggedElements);
  160. // if filename contains periods - add index number before last period
  161. if (preg_match('~\.[^\.]+$~i',$last,$extension)){
  162. array_push($sluggedElements, substr($last,0,-(strlen($extension[0]))).'-'.$index.$extension[0]);
  163. } else {
  164. // if filename doesn't contain periods add index ofter the last char
  165. array_push($sluggedElements, $last.'-'.$index);
  166. }
  167. }
  168. $sluggedPath = $this->unchangedPhysicalRoot.implode('/', $sluggedElements);
  169. return $this->stripLast($sluggedPath);
  170. }
  171. /**
  172. * Modifies a string to remove all non ASCII characters and spaces.
  173. *
  174. * @param string $text
  175. * @return string
  176. */
  177. private function slugify($text)
  178. {
  179. // replace non letter or digits or dots by -
  180. $text = preg_replace('~[^\\pL\d\.]+~u', '-', $text);
  181. // trim
  182. $text = trim($text, '-');
  183. // transliterate
  184. if (function_exists('iconv')) {
  185. $text = iconv('utf-8', 'us-ascii//TRANSLIT//IGNORE', $text);
  186. }
  187. // lowercase
  188. $text = strtolower($text);
  189. // remove unwanted characters
  190. $text = preg_replace('~[^-\w\.]+~', '', $text);
  191. // trim ending dots (for security reasons and win compatibility)
  192. $text = preg_replace('~\.+$~', '', $text);
  193. if (empty($text)) {
  194. return uniqid();
  195. }
  196. return $text;
  197. }
  198. }