You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

node.php 7.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. <?php
  2. /**
  3. * ownCloud
  4. *
  5. * @author Jakob Sack
  6. * @copyright 2011 Jakob Sack kde@jakobsack.de
  7. *
  8. * This library is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  10. * License as published by the Free Software Foundation; either
  11. * version 3 of the License, or any later version.
  12. *
  13. * This library 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
  19. * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  20. *
  21. */
  22. abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IProperties {
  23. const GETETAG_PROPERTYNAME = '{DAV:}getetag';
  24. const LASTMODIFIED_PROPERTYNAME = '{DAV:}lastmodified';
  25. /**
  26. * Allow configuring the method used to generate Etags
  27. *
  28. * @var array(class_name, function_name)
  29. */
  30. public static $ETagFunction = null;
  31. /**
  32. * is kept public to allow overwrite for unit testing
  33. *
  34. * @var \OC\Files\View
  35. */
  36. public $fileView;
  37. /**
  38. * The path to the current node
  39. *
  40. * @var string
  41. */
  42. protected $path;
  43. /**
  44. * node fileinfo cache
  45. * @var array
  46. */
  47. protected $fileinfo_cache;
  48. /**
  49. * node properties cache
  50. * @var array
  51. */
  52. protected $property_cache = null;
  53. /**
  54. * @brief Sets up the node, expects a full path name
  55. * @param string $path
  56. * @return void
  57. */
  58. public function __construct($path) {
  59. $this->path = $path;
  60. }
  61. /**
  62. * @brief Returns the name of the node
  63. * @return string
  64. */
  65. public function getName() {
  66. list(, $name) = Sabre_DAV_URLUtil::splitPath($this->path);
  67. return $name;
  68. }
  69. /**
  70. * @brief Renames the node
  71. * @param string $name The new name
  72. * @return void
  73. */
  74. public function setName($name) {
  75. $fs = $this->getFS();
  76. // rename is only allowed if the update privilege is granted
  77. if (!$fs->isUpdatable($this->path)) {
  78. throw new \Sabre_DAV_Exception_Forbidden();
  79. }
  80. list($parentPath, ) = Sabre_DAV_URLUtil::splitPath($this->path);
  81. list(, $newName) = Sabre_DAV_URLUtil::splitPath($name);
  82. if (!\OCP\Util::isValidFileName($newName)) {
  83. throw new \Sabre_DAV_Exception_BadRequest();
  84. }
  85. $newPath = $parentPath . '/' . $newName;
  86. $oldPath = $this->path;
  87. $fs->rename($this->path, $newPath);
  88. $this->path = $newPath;
  89. $query = OC_DB::prepare( 'UPDATE `*PREFIX*properties` SET `propertypath` = ?'
  90. .' WHERE `userid` = ? AND `propertypath` = ?' );
  91. $query->execute( array( $newPath, OC_User::getUser(), $oldPath ));
  92. }
  93. public function setFileinfoCache($fileinfo_cache)
  94. {
  95. $this->fileinfo_cache = $fileinfo_cache;
  96. }
  97. /**
  98. * @brief Ensure that the fileinfo cache is filled
  99. * @note Uses OC_FileCache or a direct stat
  100. */
  101. protected function getFileinfoCache() {
  102. if (!isset($this->fileinfo_cache)) {
  103. if ($fileinfo_cache = \OC\Files\Filesystem::getFileInfo($this->path)) {
  104. } else {
  105. $fileinfo_cache = \OC\Files\Filesystem::stat($this->path);
  106. }
  107. $this->fileinfo_cache = $fileinfo_cache;
  108. }
  109. }
  110. public function setPropertyCache($property_cache)
  111. {
  112. $this->property_cache = $property_cache;
  113. }
  114. /**
  115. * @brief Returns the last modification time, as a unix timestamp
  116. * @return int timestamp as integer
  117. */
  118. public function getLastModified() {
  119. $this->getFileinfoCache();
  120. $timestamp = $this->fileinfo_cache['mtime'];
  121. if (!empty($timestamp)) {
  122. return (int)$timestamp;
  123. }
  124. return $timestamp;
  125. }
  126. /**
  127. * sets the last modification time of the file (mtime) to the value given
  128. * in the second parameter or to now if the second param is empty.
  129. * Even if the modification time is set to a custom value the access time is set to now.
  130. */
  131. public function touch($mtime) {
  132. \OC\Files\Filesystem::touch($this->path, $mtime);
  133. }
  134. /**
  135. * @brief Updates properties on this node,
  136. * @see Sabre_DAV_IProperties::updateProperties
  137. * @return boolean
  138. */
  139. public function updateProperties($properties) {
  140. $existing = $this->getProperties(array());
  141. foreach($properties as $propertyName => $propertyValue) {
  142. // If it was null, we need to delete the property
  143. if (is_null($propertyValue)) {
  144. if(array_key_exists( $propertyName, $existing )) {
  145. $query = OC_DB::prepare( 'DELETE FROM `*PREFIX*properties`'
  146. .' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?' );
  147. $query->execute( array( OC_User::getUser(), $this->path, $propertyName ));
  148. }
  149. }
  150. else {
  151. if( strcmp( $propertyName, self::GETETAG_PROPERTYNAME) === 0 ) {
  152. \OC\Files\Filesystem::putFileInfo($this->path, array('etag'=> $propertyValue));
  153. } elseif( strcmp( $propertyName, self::LASTMODIFIED_PROPERTYNAME) === 0 ) {
  154. $this->touch($propertyValue);
  155. } else {
  156. if(!array_key_exists( $propertyName, $existing )) {
  157. $query = OC_DB::prepare( 'INSERT INTO `*PREFIX*properties`'
  158. .' (`userid`,`propertypath`,`propertyname`,`propertyvalue`) VALUES(?,?,?,?)' );
  159. $query->execute( array( OC_User::getUser(), $this->path, $propertyName,$propertyValue ));
  160. } else {
  161. $query = OC_DB::prepare( 'UPDATE `*PREFIX*properties` SET `propertyvalue` = ?'
  162. .' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?' );
  163. $query->execute( array( $propertyValue,OC_User::getUser(), $this->path, $propertyName ));
  164. }
  165. }
  166. }
  167. }
  168. $this->setPropertyCache(null);
  169. return true;
  170. }
  171. /**
  172. * removes all properties for this node and user
  173. */
  174. public function removeProperties() {
  175. $query = OC_DB::prepare( 'DELETE FROM `*PREFIX*properties`'
  176. .' WHERE `userid` = ? AND `propertypath` = ?' );
  177. $query->execute( array( OC_User::getUser(), $this->path));
  178. $this->setPropertyCache(null);
  179. }
  180. /**
  181. * @brief Returns a list of properties for this nodes.;
  182. * @param array $properties
  183. * @return array
  184. * @note The properties list is a list of propertynames the client
  185. * requested, encoded as xmlnamespace#tagName, for example:
  186. * http://www.example.org/namespace#author If the array is empty, all
  187. * properties should be returned
  188. */
  189. public function getProperties($properties) {
  190. if (is_null($this->property_cache)) {
  191. $sql = 'SELECT * FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ?';
  192. $result = OC_DB::executeAudited( $sql, array( OC_User::getUser(), $this->path ) );
  193. $this->property_cache = array();
  194. while( $row = $result->fetchRow()) {
  195. $this->property_cache[$row['propertyname']] = $row['propertyvalue'];
  196. }
  197. // Don't call the static getETagPropertyForPath, its result is not cached
  198. $this->getFileinfoCache();
  199. if ($this->fileinfo_cache['etag']) {
  200. $this->property_cache[self::GETETAG_PROPERTYNAME] = '"'.$this->fileinfo_cache['etag'].'"';
  201. } else {
  202. $this->property_cache[self::GETETAG_PROPERTYNAME] = null;
  203. }
  204. }
  205. // if the array was empty, we need to return everything
  206. if(count($properties) == 0) {
  207. return $this->property_cache;
  208. }
  209. $props = array();
  210. foreach($properties as $property) {
  211. if (isset($this->property_cache[$property])) {
  212. $props[$property] = $this->property_cache[$property];
  213. }
  214. }
  215. return $props;
  216. }
  217. /**
  218. * Returns the ETag surrounded by double-quotes for this path.
  219. * @param string $path Path of the file
  220. * @return string|null Returns null if the ETag can not effectively be determined
  221. */
  222. protected function getETagPropertyForPath($path) {
  223. $data = $this->getFS()->getFileInfo($path);
  224. if (isset($data['etag'])) {
  225. return '"'.$data['etag'].'"';
  226. }
  227. return null;
  228. }
  229. protected function getFS() {
  230. if (is_null($this->fileView)) {
  231. $this->fileView = \OC\Files\Filesystem::getView();
  232. }
  233. return $this->fileView;
  234. }
  235. /**
  236. * @return string|null
  237. */
  238. public function getFileId()
  239. {
  240. $this->getFileinfoCache();
  241. if (isset($this->fileinfo_cache['fileid'])) {
  242. $instanceId = OC_Util::getInstanceId();
  243. $id = sprintf('%08d', $this->fileinfo_cache['fileid']);
  244. return $id . $instanceId;
  245. }
  246. return null;
  247. }
  248. }