client.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943
  1. /*
  2. * Copyright (c) 2015
  3. *
  4. * This file is licensed under the Affero General Public License version 3
  5. * or later.
  6. *
  7. * See the COPYING-README file.
  8. *
  9. */
  10. /* global dav */
  11. (function(OC, FileInfo) {
  12. /**
  13. * @class OC.Files.Client
  14. * @classdesc Client to access files on the server
  15. *
  16. * @param {Object} options
  17. * @param {String} options.host host name
  18. * @param {int} [options.port] port
  19. * @param {boolean} [options.useHTTPS] whether to use https
  20. * @param {String} [options.root] root path
  21. * @param {String} [options.userName] user name
  22. * @param {String} [options.password] password
  23. *
  24. * @since 8.2
  25. */
  26. var Client = function(options) {
  27. this._root = options.root;
  28. if (this._root.charAt(this._root.length - 1) === '/') {
  29. this._root = this._root.substr(0, this._root.length - 1);
  30. }
  31. var url = Client.PROTOCOL_HTTP + '://';
  32. if (options.useHTTPS) {
  33. url = Client.PROTOCOL_HTTPS + '://';
  34. }
  35. url += options.host + this._root;
  36. this._host = options.host;
  37. this._defaultHeaders = options.defaultHeaders || {
  38. 'X-Requested-With': 'XMLHttpRequest',
  39. 'requesttoken': OC.requestToken
  40. };
  41. this._baseUrl = url;
  42. var clientOptions = {
  43. baseUrl: this._baseUrl,
  44. xmlNamespaces: {
  45. 'DAV:': 'd',
  46. 'http://owncloud.org/ns': 'oc',
  47. 'http://nextcloud.org/ns': 'nc',
  48. 'http://open-collaboration-services.org/ns': 'ocs'
  49. }
  50. };
  51. if (options.userName) {
  52. clientOptions.userName = options.userName;
  53. }
  54. if (options.password) {
  55. clientOptions.password = options.password;
  56. }
  57. this._client = new dav.Client(clientOptions);
  58. this._client.xhrProvider = _.bind(this._xhrProvider, this);
  59. };
  60. Client.NS_OWNCLOUD = 'http://owncloud.org/ns';
  61. Client.NS_NEXTCLOUD = 'http://nextcloud.org/ns';
  62. Client.NS_DAV = 'DAV:';
  63. Client.NS_OCS = 'http://open-collaboration-services.org/ns';
  64. Client.PROPERTY_GETLASTMODIFIED = '{' + Client.NS_DAV + '}getlastmodified';
  65. Client.PROPERTY_GETETAG = '{' + Client.NS_DAV + '}getetag';
  66. Client.PROPERTY_GETCONTENTTYPE = '{' + Client.NS_DAV + '}getcontenttype';
  67. Client.PROPERTY_RESOURCETYPE = '{' + Client.NS_DAV + '}resourcetype';
  68. Client.PROPERTY_INTERNAL_FILEID = '{' + Client.NS_OWNCLOUD + '}fileid';
  69. Client.PROPERTY_PERMISSIONS = '{' + Client.NS_OWNCLOUD + '}permissions';
  70. Client.PROPERTY_SIZE = '{' + Client.NS_OWNCLOUD + '}size';
  71. Client.PROPERTY_GETCONTENTLENGTH = '{' + Client.NS_DAV + '}getcontentlength';
  72. Client.PROPERTY_ISENCRYPTED = '{' + Client.NS_DAV + '}is-encrypted';
  73. Client.PROPERTY_SHARE_PERMISSIONS = '{' + Client.NS_OCS + '}share-permissions';
  74. Client.PROTOCOL_HTTP = 'http';
  75. Client.PROTOCOL_HTTPS = 'https';
  76. Client._PROPFIND_PROPERTIES = [
  77. /**
  78. * Modified time
  79. */
  80. [Client.NS_DAV, 'getlastmodified'],
  81. /**
  82. * Etag
  83. */
  84. [Client.NS_DAV, 'getetag'],
  85. /**
  86. * Mime type
  87. */
  88. [Client.NS_DAV, 'getcontenttype'],
  89. /**
  90. * Resource type "collection" for folders, empty otherwise
  91. */
  92. [Client.NS_DAV, 'resourcetype'],
  93. /**
  94. * File id
  95. */
  96. [Client.NS_OWNCLOUD, 'fileid'],
  97. /**
  98. * Letter-coded permissions
  99. */
  100. [Client.NS_OWNCLOUD, 'permissions'],
  101. //[Client.NS_OWNCLOUD, 'downloadURL'],
  102. /**
  103. * Folder sizes
  104. */
  105. [Client.NS_OWNCLOUD, 'size'],
  106. /**
  107. * File sizes
  108. */
  109. [Client.NS_DAV, 'getcontentlength'],
  110. /**
  111. * Preview availability
  112. */
  113. [Client.NS_NEXTCLOUD, 'has-preview'],
  114. /**
  115. * Mount type
  116. */
  117. [Client.NS_NEXTCLOUD, 'mount-type'],
  118. /**
  119. * Encryption state
  120. */
  121. [Client.NS_NEXTCLOUD, 'is-encrypted'],
  122. /**
  123. * Share permissions
  124. */
  125. [Client.NS_OCS, 'share-permissions']
  126. ];
  127. /**
  128. * @memberof OC.Files
  129. */
  130. Client.prototype = {
  131. /**
  132. * Root path of the Webdav endpoint
  133. *
  134. * @type string
  135. */
  136. _root: null,
  137. /**
  138. * Client from the library
  139. *
  140. * @type dav.Client
  141. */
  142. _client: null,
  143. /**
  144. * Array of file info parsing functions.
  145. *
  146. * @type Array<OC.Files.Client~parseFileInfo>
  147. */
  148. _fileInfoParsers: [],
  149. /**
  150. * Returns the configured XHR provider for davclient
  151. * @return {XMLHttpRequest}
  152. */
  153. _xhrProvider: function() {
  154. var headers = this._defaultHeaders;
  155. var xhr = new XMLHttpRequest();
  156. var oldOpen = xhr.open;
  157. // override open() method to add headers
  158. xhr.open = function() {
  159. var result = oldOpen.apply(this, arguments);
  160. _.each(headers, function(value, key) {
  161. xhr.setRequestHeader(key, value);
  162. });
  163. return result;
  164. };
  165. OC.registerXHRForErrorProcessing(xhr);
  166. return xhr;
  167. },
  168. /**
  169. * Prepends the base url to the given path sections
  170. *
  171. * @param {...String} path sections
  172. *
  173. * @return {String} base url + joined path, any leading or trailing slash
  174. * will be kept
  175. */
  176. _buildUrl: function() {
  177. var path = this._buildPath.apply(this, arguments);
  178. if (path.charAt([path.length - 1]) === '/') {
  179. path = path.substr(0, path.length - 1);
  180. }
  181. if (path.charAt(0) === '/') {
  182. path = path.substr(1);
  183. }
  184. return this._baseUrl + '/' + path;
  185. },
  186. /**
  187. * Append the path to the root and also encode path
  188. * sections
  189. *
  190. * @param {...String} path sections
  191. *
  192. * @return {String} joined path, any leading or trailing slash
  193. * will be kept
  194. */
  195. _buildPath: function() {
  196. var path = OC.joinPaths.apply(this, arguments);
  197. var sections = path.split('/');
  198. var i;
  199. for (i = 0; i < sections.length; i++) {
  200. sections[i] = encodeURIComponent(sections[i]);
  201. }
  202. path = sections.join('/');
  203. return path;
  204. },
  205. /**
  206. * Parse headers string into a map
  207. *
  208. * @param {string} headersString headers list as string
  209. *
  210. * @return {Object.<String,Array>} map of header name to header contents
  211. */
  212. _parseHeaders: function(headersString) {
  213. var headerRows = headersString.split('\n');
  214. var headers = {};
  215. for (var i = 0; i < headerRows.length; i++) {
  216. var sepPos = headerRows[i].indexOf(':');
  217. if (sepPos < 0) {
  218. continue;
  219. }
  220. var headerName = headerRows[i].substr(0, sepPos);
  221. var headerValue = headerRows[i].substr(sepPos + 2);
  222. if (!headers[headerName]) {
  223. // make it an array
  224. headers[headerName] = [];
  225. }
  226. headers[headerName].push(headerValue);
  227. }
  228. return headers;
  229. },
  230. /**
  231. * Parses the etag response which is in double quotes.
  232. *
  233. * @param {string} etag etag value in double quotes
  234. *
  235. * @return {string} etag without double quotes
  236. */
  237. _parseEtag: function(etag) {
  238. if (etag.charAt(0) === '"') {
  239. return etag.split('"')[1];
  240. }
  241. return etag;
  242. },
  243. /**
  244. * Parse Webdav result
  245. *
  246. * @param {Object} response XML object
  247. *
  248. * @return {Array.<FileInfo>} array of file info
  249. */
  250. _parseFileInfo: function(response) {
  251. var path = response.href;
  252. if (path.substr(0, this._root.length) === this._root) {
  253. path = path.substr(this._root.length);
  254. }
  255. if (path.charAt(path.length - 1) === '/') {
  256. path = path.substr(0, path.length - 1);
  257. }
  258. path = decodeURIComponent(path);
  259. if (response.propStat.length === 0 || response.propStat[0].status !== 'HTTP/1.1 200 OK') {
  260. return null;
  261. }
  262. var props = response.propStat[0].properties;
  263. var data = {
  264. id: props[Client.PROPERTY_INTERNAL_FILEID],
  265. path: OC.dirname(path) || '/',
  266. name: OC.basename(path),
  267. mtime: (new Date(props[Client.PROPERTY_GETLASTMODIFIED])).getTime()
  268. };
  269. var etagProp = props[Client.PROPERTY_GETETAG];
  270. if (!_.isUndefined(etagProp)) {
  271. data.etag = this._parseEtag(etagProp);
  272. }
  273. var sizeProp = props[Client.PROPERTY_GETCONTENTLENGTH];
  274. if (!_.isUndefined(sizeProp)) {
  275. data.size = parseInt(sizeProp, 10);
  276. }
  277. sizeProp = props[Client.PROPERTY_SIZE];
  278. if (!_.isUndefined(sizeProp)) {
  279. data.size = parseInt(sizeProp, 10);
  280. }
  281. var hasPreviewProp = props['{' + Client.NS_NEXTCLOUD + '}has-preview'];
  282. if (!_.isUndefined(hasPreviewProp)) {
  283. data.hasPreview = hasPreviewProp === 'true';
  284. } else {
  285. data.hasPreview = true;
  286. }
  287. var isEncryptedProp = props['{' + Client.NS_NEXTCLOUD + '}is-encrypted'];
  288. if (!_.isUndefined(isEncryptedProp)) {
  289. data.isEncrypted = isEncryptedProp === '1';
  290. } else {
  291. data.isEncrypted = false;
  292. }
  293. var contentType = props[Client.PROPERTY_GETCONTENTTYPE];
  294. if (!_.isUndefined(contentType)) {
  295. data.mimetype = contentType;
  296. }
  297. var resType = props[Client.PROPERTY_RESOURCETYPE];
  298. var isFile = true;
  299. if (!data.mimetype && resType) {
  300. var xmlvalue = resType[0];
  301. if (xmlvalue.namespaceURI === Client.NS_DAV && xmlvalue.nodeName.split(':')[1] === 'collection') {
  302. data.mimetype = 'httpd/unix-directory';
  303. isFile = false;
  304. }
  305. }
  306. data.permissions = OC.PERMISSION_NONE;
  307. var permissionProp = props[Client.PROPERTY_PERMISSIONS];
  308. if (!_.isUndefined(permissionProp)) {
  309. var permString = permissionProp || '';
  310. data.mountType = null;
  311. for (var i = 0; i < permString.length; i++) {
  312. var c = permString.charAt(i);
  313. switch (c) {
  314. // FIXME: twisted permissions
  315. case 'C':
  316. case 'K':
  317. data.permissions |= OC.PERMISSION_CREATE;
  318. break;
  319. case 'G':
  320. data.permissions |= OC.PERMISSION_READ;
  321. break;
  322. case 'W':
  323. case 'N':
  324. case 'V':
  325. data.permissions |= OC.PERMISSION_UPDATE;
  326. break;
  327. case 'D':
  328. data.permissions |= OC.PERMISSION_DELETE;
  329. break;
  330. case 'R':
  331. data.permissions |= OC.PERMISSION_SHARE;
  332. break;
  333. case 'M':
  334. if (!data.mountType) {
  335. // TODO: how to identify external-root ?
  336. data.mountType = 'external';
  337. }
  338. break;
  339. case 'S':
  340. // TODO: how to identify shared-root ?
  341. data.mountType = 'shared';
  342. break;
  343. }
  344. }
  345. }
  346. var sharePermissionsProp = props[Client.PROPERTY_SHARE_PERMISSIONS];
  347. if (!_.isUndefined(sharePermissionsProp)) {
  348. data.sharePermissions = parseInt(sharePermissionsProp);
  349. }
  350. var mounTypeProp = props['{' + Client.NS_NEXTCLOUD + '}mount-type'];
  351. if (!_.isUndefined(mounTypeProp)) {
  352. data.mountType = mounTypeProp;
  353. }
  354. // extend the parsed data using the custom parsers
  355. _.each(this._fileInfoParsers, function(parserFunction) {
  356. _.extend(data, parserFunction(response) || {});
  357. });
  358. return new FileInfo(data);
  359. },
  360. /**
  361. * Parse Webdav multistatus
  362. *
  363. * @param {Array} responses
  364. */
  365. _parseResult: function(responses) {
  366. var self = this;
  367. return _.map(responses, function(response) {
  368. return self._parseFileInfo(response);
  369. });
  370. },
  371. /**
  372. * Returns whether the given status code means success
  373. *
  374. * @param {int} status status code
  375. *
  376. * @return true if status code is between 200 and 299 included
  377. */
  378. _isSuccessStatus: function(status) {
  379. return status >= 200 && status <= 299;
  380. },
  381. /**
  382. * Parse the Sabre exception out of the given response, if any
  383. *
  384. * @param {Object} response object
  385. * @return {Object} array of parsed message and exception (only the first one)
  386. */
  387. _getSabreException: function(response) {
  388. var result = {};
  389. var xml = response.xhr.responseXML;
  390. var messages = xml.getElementsByTagNameNS('http://sabredav.org/ns', 'message');
  391. var exceptions = xml.getElementsByTagNameNS('http://sabredav.org/ns', 'exception');
  392. if (messages.length) {
  393. result.message = messages[0].textContent;
  394. }
  395. if (exceptions.length) {
  396. result.exception = exceptions[0].textContent;
  397. }
  398. return result;
  399. },
  400. /**
  401. * Returns the default PROPFIND properties to use during a call.
  402. *
  403. * @return {Array.<Object>} array of properties
  404. */
  405. getPropfindProperties: function() {
  406. if (!this._propfindProperties) {
  407. this._propfindProperties = _.map(Client._PROPFIND_PROPERTIES, function(propDef) {
  408. return '{' + propDef[0] + '}' + propDef[1];
  409. });
  410. }
  411. return this._propfindProperties;
  412. },
  413. /**
  414. * Lists the contents of a directory
  415. *
  416. * @param {String} path path to retrieve
  417. * @param {Object} [options] options
  418. * @param {boolean} [options.includeParent=false] set to true to keep
  419. * the parent folder in the result list
  420. * @param {Array} [options.properties] list of Webdav properties to retrieve
  421. *
  422. * @return {Promise} promise
  423. */
  424. getFolderContents: function(path, options) {
  425. if (!path) {
  426. path = '';
  427. }
  428. options = options || {};
  429. var self = this;
  430. var deferred = $.Deferred();
  431. var promise = deferred.promise();
  432. var properties;
  433. if (_.isUndefined(options.properties)) {
  434. properties = this.getPropfindProperties();
  435. } else {
  436. properties = options.properties;
  437. }
  438. this._client.propFind(
  439. this._buildUrl(path),
  440. properties,
  441. 1
  442. ).then(function(result) {
  443. if (self._isSuccessStatus(result.status)) {
  444. var results = self._parseResult(result.body);
  445. if (!options || !options.includeParent) {
  446. // remove root dir, the first entry
  447. results.shift();
  448. }
  449. deferred.resolve(result.status, results);
  450. } else {
  451. result = _.extend(result, self._getSabreException(result));
  452. deferred.reject(result.status, result);
  453. }
  454. });
  455. return promise;
  456. },
  457. /**
  458. * Fetches a flat list of files filtered by a given filter criteria.
  459. * (currently only system tags is supported)
  460. *
  461. * @param {Object} filter filter criteria
  462. * @param {Object} [filter.systemTagIds] list of system tag ids to filter by
  463. * @param {bool} [filter.favorite] set it to filter by favorites
  464. * @param {Object} [options] options
  465. * @param {Array} [options.properties] list of Webdav properties to retrieve
  466. *
  467. * @return {Promise} promise
  468. */
  469. getFilteredFiles: function(filter, options) {
  470. options = options || {};
  471. var self = this;
  472. var deferred = $.Deferred();
  473. var promise = deferred.promise();
  474. var properties;
  475. if (_.isUndefined(options.properties)) {
  476. properties = this.getPropfindProperties();
  477. } else {
  478. properties = options.properties;
  479. }
  480. if (!filter || (!filter.systemTagIds && _.isUndefined(filter.favorite))) {
  481. throw 'Missing filter argument';
  482. }
  483. // root element with namespaces
  484. var body = '<oc:filter-files ';
  485. var namespace;
  486. for (namespace in this._client.xmlNamespaces) {
  487. body += ' xmlns:' + this._client.xmlNamespaces[namespace] + '="' + namespace + '"';
  488. }
  489. body += '>\n';
  490. // properties query
  491. body += ' <' + this._client.xmlNamespaces['DAV:'] + ':prop>\n';
  492. _.each(properties, function(prop) {
  493. var property = self._client.parseClarkNotation(prop);
  494. body += ' <' + self._client.xmlNamespaces[property.namespace] + ':' + property.name + ' />\n';
  495. });
  496. body += ' </' + this._client.xmlNamespaces['DAV:'] + ':prop>\n';
  497. // rules block
  498. body += ' <oc:filter-rules>\n';
  499. _.each(filter.systemTagIds, function(systemTagIds) {
  500. body += ' <oc:systemtag>' + escapeHTML(systemTagIds) + '</oc:systemtag>\n';
  501. });
  502. if (filter.favorite) {
  503. body += ' <oc:favorite>' + (filter.favorite ? '1': '0') + '</oc:favorite>\n';
  504. }
  505. body += ' </oc:filter-rules>\n';
  506. // end of root
  507. body += '</oc:filter-files>\n';
  508. this._client.request(
  509. 'REPORT',
  510. this._buildUrl(),
  511. {},
  512. body
  513. ).then(function(result) {
  514. if (self._isSuccessStatus(result.status)) {
  515. var results = self._parseResult(result.body);
  516. deferred.resolve(result.status, results);
  517. } else {
  518. result = _.extend(result, self._getSabreException(result));
  519. deferred.reject(result.status, result);
  520. }
  521. });
  522. return promise;
  523. },
  524. /**
  525. * Returns the file info of a given path.
  526. *
  527. * @param {String} path path
  528. * @param {Array} [options.properties] list of Webdav properties to retrieve
  529. *
  530. * @return {Promise} promise
  531. */
  532. getFileInfo: function(path, options) {
  533. if (!path) {
  534. path = '';
  535. }
  536. options = options || {};
  537. var self = this;
  538. var deferred = $.Deferred();
  539. var promise = deferred.promise();
  540. var properties;
  541. if (_.isUndefined(options.properties)) {
  542. properties = this.getPropfindProperties();
  543. } else {
  544. properties = options.properties;
  545. }
  546. // TODO: headers
  547. this._client.propFind(
  548. this._buildUrl(path),
  549. properties,
  550. 0
  551. ).then(
  552. function(result) {
  553. if (self._isSuccessStatus(result.status)) {
  554. deferred.resolve(result.status, self._parseResult([result.body])[0]);
  555. } else {
  556. result = _.extend(result, self._getSabreException(result));
  557. deferred.reject(result.status, result);
  558. }
  559. }
  560. );
  561. return promise;
  562. },
  563. /**
  564. * Returns the contents of the given file.
  565. *
  566. * @param {String} path path to file
  567. *
  568. * @return {Promise}
  569. */
  570. getFileContents: function(path) {
  571. if (!path) {
  572. throw 'Missing argument "path"';
  573. }
  574. var self = this;
  575. var deferred = $.Deferred();
  576. var promise = deferred.promise();
  577. this._client.request(
  578. 'GET',
  579. this._buildUrl(path)
  580. ).then(
  581. function(result) {
  582. if (self._isSuccessStatus(result.status)) {
  583. deferred.resolve(result.status, result.body);
  584. } else {
  585. result = _.extend(result, self._getSabreException(result));
  586. deferred.reject(result.status, result);
  587. }
  588. }
  589. );
  590. return promise;
  591. },
  592. /**
  593. * Puts the given data into the given file.
  594. *
  595. * @param {String} path path to file
  596. * @param {String} body file body
  597. * @param {Object} [options]
  598. * @param {String} [options.contentType='text/plain'] content type
  599. * @param {bool} [options.overwrite=true] whether to overwrite an existing file
  600. *
  601. * @return {Promise}
  602. */
  603. putFileContents: function(path, body, options) {
  604. if (!path) {
  605. throw 'Missing argument "path"';
  606. }
  607. var self = this;
  608. var deferred = $.Deferred();
  609. var promise = deferred.promise();
  610. options = options || {};
  611. var headers = {};
  612. var contentType = 'text/plain;charset=utf-8';
  613. if (options.contentType) {
  614. contentType = options.contentType;
  615. }
  616. headers['Content-Type'] = contentType;
  617. if (_.isUndefined(options.overwrite) || options.overwrite) {
  618. // will trigger 412 precondition failed if a file already exists
  619. headers['If-None-Match'] = '*';
  620. }
  621. this._client.request(
  622. 'PUT',
  623. this._buildUrl(path),
  624. headers,
  625. body || ''
  626. ).then(
  627. function(result) {
  628. if (self._isSuccessStatus(result.status)) {
  629. deferred.resolve(result.status);
  630. } else {
  631. result = _.extend(result, self._getSabreException(result));
  632. deferred.reject(result.status, result);
  633. }
  634. }
  635. );
  636. return promise;
  637. },
  638. _simpleCall: function(method, path) {
  639. if (!path) {
  640. throw 'Missing argument "path"';
  641. }
  642. var self = this;
  643. var deferred = $.Deferred();
  644. var promise = deferred.promise();
  645. this._client.request(
  646. method,
  647. this._buildUrl(path)
  648. ).then(
  649. function(result) {
  650. if (self._isSuccessStatus(result.status)) {
  651. deferred.resolve(result.status);
  652. } else {
  653. result = _.extend(result, self._getSabreException(result));
  654. deferred.reject(result.status, result);
  655. }
  656. }
  657. );
  658. return promise;
  659. },
  660. /**
  661. * Creates a directory
  662. *
  663. * @param {String} path path to create
  664. *
  665. * @return {Promise}
  666. */
  667. createDirectory: function(path) {
  668. return this._simpleCall('MKCOL', path);
  669. },
  670. /**
  671. * Deletes a file or directory
  672. *
  673. * @param {String} path path to delete
  674. *
  675. * @return {Promise}
  676. */
  677. remove: function(path) {
  678. return this._simpleCall('DELETE', path);
  679. },
  680. /**
  681. * Moves path to another path
  682. *
  683. * @param {String} path path to move
  684. * @param {String} destinationPath destination path
  685. * @param {boolean} [allowOverwrite=false] true to allow overwriting,
  686. * false otherwise
  687. * @param {Object} [headers=null] additional headers
  688. *
  689. * @return {Promise} promise
  690. */
  691. move: function(path, destinationPath, allowOverwrite, headers) {
  692. if (!path) {
  693. throw 'Missing argument "path"';
  694. }
  695. if (!destinationPath) {
  696. throw 'Missing argument "destinationPath"';
  697. }
  698. var self = this;
  699. var deferred = $.Deferred();
  700. var promise = deferred.promise();
  701. headers = _.extend({}, headers, {
  702. 'Destination' : this._buildUrl(destinationPath)
  703. });
  704. if (!allowOverwrite) {
  705. headers.Overwrite = 'F';
  706. }
  707. this._client.request(
  708. 'MOVE',
  709. this._buildUrl(path),
  710. headers
  711. ).then(
  712. function(result) {
  713. if (self._isSuccessStatus(result.status)) {
  714. deferred.resolve(result.status);
  715. } else {
  716. result = _.extend(result, self._getSabreException(result));
  717. deferred.reject(result.status, result);
  718. }
  719. }
  720. );
  721. return promise;
  722. },
  723. /**
  724. * Copies path to another path
  725. *
  726. * @param {String} path path to copy
  727. * @param {String} destinationPath destination path
  728. * @param {boolean} [allowOverwrite=false] true to allow overwriting,
  729. * false otherwise
  730. *
  731. * @return {Promise} promise
  732. */
  733. copy: function (path, destinationPath, allowOverwrite) {
  734. if (!path) {
  735. throw 'Missing argument "path"';
  736. }
  737. if (!destinationPath) {
  738. throw 'Missing argument "destinationPath"';
  739. }
  740. var self = this;
  741. var deferred = $.Deferred();
  742. var promise = deferred.promise();
  743. var headers = {
  744. 'Destination' : this._buildUrl(destinationPath)
  745. };
  746. if (!allowOverwrite) {
  747. headers.Overwrite = 'F';
  748. }
  749. this._client.request(
  750. 'COPY',
  751. this._buildUrl(path),
  752. headers
  753. ).then(
  754. function(response) {
  755. if (self._isSuccessStatus(response.status)) {
  756. deferred.resolve(response.status);
  757. } else {
  758. deferred.reject(response.status);
  759. }
  760. }
  761. );
  762. return promise;
  763. },
  764. /**
  765. * Add a file info parser function
  766. *
  767. * @param {OC.Files.Client~parseFileInfo>}
  768. */
  769. addFileInfoParser: function(parserFunction) {
  770. this._fileInfoParsers.push(parserFunction);
  771. },
  772. /**
  773. * Returns the dav.Client instance used internally
  774. *
  775. * @since 11.0.0
  776. * @return {dav.Client}
  777. */
  778. getClient: function() {
  779. return this._client;
  780. },
  781. /**
  782. * Returns the user name
  783. *
  784. * @since 11.0.0
  785. * @return {String} userName
  786. */
  787. getUserName: function() {
  788. return this._client.userName;
  789. },
  790. /**
  791. * Returns the password
  792. *
  793. * @since 11.0.0
  794. * @return {String} password
  795. */
  796. getPassword: function() {
  797. return this._client.password;
  798. },
  799. /**
  800. * Returns the base URL
  801. *
  802. * @since 11.0.0
  803. * @return {String} base URL
  804. */
  805. getBaseUrl: function() {
  806. return this._client.baseUrl;
  807. },
  808. /**
  809. * Returns the host
  810. *
  811. * @since 13.0.0
  812. * @return {String} base URL
  813. */
  814. getHost: function() {
  815. return this._host;
  816. }
  817. };
  818. /**
  819. * File info parser function
  820. *
  821. * This function receives a list of Webdav properties as input and
  822. * should return a hash array of parsed properties, if applicable.
  823. *
  824. * @callback OC.Files.Client~parseFileInfo
  825. * @param {Object} XML Webdav properties
  826. * @return {Array} array of parsed property values
  827. */
  828. if (!OC.Files) {
  829. /**
  830. * @namespace OC.Files
  831. *
  832. * @since 8.2
  833. */
  834. OC.Files = {};
  835. }
  836. /**
  837. * Returns the default instance of the files client
  838. *
  839. * @return {OC.Files.Client} default client
  840. *
  841. * @since 8.2
  842. */
  843. OC.Files.getClient = function() {
  844. if (OC.Files._defaultClient) {
  845. return OC.Files._defaultClient;
  846. }
  847. var client = new OC.Files.Client({
  848. host: OC.getHost(),
  849. port: OC.getPort(),
  850. root: OC.linkToRemoteBase('dav') + '/files/' + OC.getCurrentUser().uid,
  851. useHTTPS: OC.getProtocol() === 'https'
  852. });
  853. OC.Files._defaultClient = client;
  854. return client;
  855. };
  856. OC.Files.Client = Client;
  857. })(OC, OC.Files.FileInfo);