client.js 23 KB

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