client.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947
  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. if (xml === null) {
  389. return result;
  390. }
  391. var messages = xml.getElementsByTagNameNS('http://sabredav.org/ns', 'message');
  392. var exceptions = xml.getElementsByTagNameNS('http://sabredav.org/ns', 'exception');
  393. if (messages.length) {
  394. result.message = messages[0].textContent;
  395. }
  396. if (exceptions.length) {
  397. result.exception = exceptions[0].textContent;
  398. }
  399. return result;
  400. },
  401. /**
  402. * Returns the default PROPFIND properties to use during a call.
  403. *
  404. * @return {Array.<Object>} array of properties
  405. */
  406. getPropfindProperties: function() {
  407. if (!this._propfindProperties) {
  408. this._propfindProperties = _.map(Client._PROPFIND_PROPERTIES, function(propDef) {
  409. return '{' + propDef[0] + '}' + propDef[1];
  410. });
  411. }
  412. return this._propfindProperties;
  413. },
  414. /**
  415. * Lists the contents of a directory
  416. *
  417. * @param {String} path path to retrieve
  418. * @param {Object} [options] options
  419. * @param {boolean} [options.includeParent=false] set to true to keep
  420. * the parent folder in the result list
  421. * @param {Array} [options.properties] list of Webdav properties to retrieve
  422. *
  423. * @return {Promise} promise
  424. */
  425. getFolderContents: function(path, options) {
  426. if (!path) {
  427. path = '';
  428. }
  429. options = options || {};
  430. var self = this;
  431. var deferred = $.Deferred();
  432. var promise = deferred.promise();
  433. var properties;
  434. if (_.isUndefined(options.properties)) {
  435. properties = this.getPropfindProperties();
  436. } else {
  437. properties = options.properties;
  438. }
  439. this._client.propFind(
  440. this._buildUrl(path),
  441. properties,
  442. 1
  443. ).then(function(result) {
  444. if (self._isSuccessStatus(result.status)) {
  445. var results = self._parseResult(result.body);
  446. if (!options || !options.includeParent) {
  447. // remove root dir, the first entry
  448. results.shift();
  449. }
  450. deferred.resolve(result.status, results);
  451. } else {
  452. result = _.extend(result, self._getSabreException(result));
  453. deferred.reject(result.status, result);
  454. }
  455. });
  456. return promise;
  457. },
  458. /**
  459. * Fetches a flat list of files filtered by a given filter criteria.
  460. * (currently system tags and circles are supported)
  461. *
  462. * @param {Object} filter filter criteria
  463. * @param {Object} [filter.systemTagIds] list of system tag ids to filter by
  464. * @param {bool} [filter.favorite] set it to filter by favorites
  465. * @param {Object} [options] options
  466. * @param {Array} [options.properties] list of Webdav properties to retrieve
  467. *
  468. * @return {Promise} promise
  469. */
  470. getFilteredFiles: function(filter, options) {
  471. options = options || {};
  472. var self = this;
  473. var deferred = $.Deferred();
  474. var promise = deferred.promise();
  475. var properties;
  476. if (_.isUndefined(options.properties)) {
  477. properties = this.getPropfindProperties();
  478. } else {
  479. properties = options.properties;
  480. }
  481. if (!filter ||
  482. (!filter.systemTagIds && _.isUndefined(filter.favorite) && !filter.circlesIds) ) {
  483. throw 'Missing filter argument';
  484. }
  485. // root element with namespaces
  486. var body = '<oc:filter-files ';
  487. var namespace;
  488. for (namespace in this._client.xmlNamespaces) {
  489. body += ' xmlns:' + this._client.xmlNamespaces[namespace] + '="' + namespace + '"';
  490. }
  491. body += '>\n';
  492. // properties query
  493. body += ' <' + this._client.xmlNamespaces['DAV:'] + ':prop>\n';
  494. _.each(properties, function(prop) {
  495. var property = self._client.parseClarkNotation(prop);
  496. body += ' <' + self._client.xmlNamespaces[property.namespace] + ':' + property.name + ' />\n';
  497. });
  498. body += ' </' + this._client.xmlNamespaces['DAV:'] + ':prop>\n';
  499. // rules block
  500. body += ' <oc:filter-rules>\n';
  501. _.each(filter.systemTagIds, function(systemTagIds) {
  502. body += ' <oc:systemtag>' + escapeHTML(systemTagIds) + '</oc:systemtag>\n';
  503. });
  504. _.each(filter.circlesIds, function(circlesIds) {
  505. body += ' <oc:circle>' + escapeHTML(circlesIds) + '</oc:circle>\n';
  506. });
  507. if (filter.favorite) {
  508. body += ' <oc:favorite>' + (filter.favorite ? '1': '0') + '</oc:favorite>\n';
  509. }
  510. body += ' </oc:filter-rules>\n';
  511. // end of root
  512. body += '</oc:filter-files>\n';
  513. this._client.request(
  514. 'REPORT',
  515. this._buildUrl(),
  516. {},
  517. body
  518. ).then(function(result) {
  519. if (self._isSuccessStatus(result.status)) {
  520. var results = self._parseResult(result.body);
  521. deferred.resolve(result.status, results);
  522. } else {
  523. result = _.extend(result, self._getSabreException(result));
  524. deferred.reject(result.status, result);
  525. }
  526. });
  527. return promise;
  528. },
  529. /**
  530. * Returns the file info of a given path.
  531. *
  532. * @param {String} path path
  533. * @param {Array} [options.properties] list of Webdav properties to retrieve
  534. *
  535. * @return {Promise} promise
  536. */
  537. getFileInfo: function(path, options) {
  538. if (!path) {
  539. path = '';
  540. }
  541. options = options || {};
  542. var self = this;
  543. var deferred = $.Deferred();
  544. var promise = deferred.promise();
  545. var properties;
  546. if (_.isUndefined(options.properties)) {
  547. properties = this.getPropfindProperties();
  548. } else {
  549. properties = options.properties;
  550. }
  551. // TODO: headers
  552. this._client.propFind(
  553. this._buildUrl(path),
  554. properties,
  555. 0
  556. ).then(
  557. function(result) {
  558. if (self._isSuccessStatus(result.status)) {
  559. deferred.resolve(result.status, self._parseResult([result.body])[0]);
  560. } else {
  561. result = _.extend(result, self._getSabreException(result));
  562. deferred.reject(result.status, result);
  563. }
  564. }
  565. );
  566. return promise;
  567. },
  568. /**
  569. * Returns the contents of the given file.
  570. *
  571. * @param {String} path path to file
  572. *
  573. * @return {Promise}
  574. */
  575. getFileContents: function(path) {
  576. if (!path) {
  577. throw 'Missing argument "path"';
  578. }
  579. var self = this;
  580. var deferred = $.Deferred();
  581. var promise = deferred.promise();
  582. this._client.request(
  583. 'GET',
  584. this._buildUrl(path)
  585. ).then(
  586. function(result) {
  587. if (self._isSuccessStatus(result.status)) {
  588. deferred.resolve(result.status, result.body);
  589. } else {
  590. result = _.extend(result, self._getSabreException(result));
  591. deferred.reject(result.status, result);
  592. }
  593. }
  594. );
  595. return promise;
  596. },
  597. /**
  598. * Puts the given data into the given file.
  599. *
  600. * @param {String} path path to file
  601. * @param {String} body file body
  602. * @param {Object} [options]
  603. * @param {String} [options.contentType='text/plain'] content type
  604. * @param {bool} [options.overwrite=true] whether to overwrite an existing file
  605. *
  606. * @return {Promise}
  607. */
  608. putFileContents: function(path, body, options) {
  609. if (!path) {
  610. throw 'Missing argument "path"';
  611. }
  612. var self = this;
  613. var deferred = $.Deferred();
  614. var promise = deferred.promise();
  615. options = options || {};
  616. var headers = {};
  617. var contentType = 'text/plain;charset=utf-8';
  618. if (options.contentType) {
  619. contentType = options.contentType;
  620. }
  621. headers['Content-Type'] = contentType;
  622. if (_.isUndefined(options.overwrite) || options.overwrite) {
  623. // will trigger 412 precondition failed if a file already exists
  624. headers['If-None-Match'] = '*';
  625. }
  626. this._client.request(
  627. 'PUT',
  628. this._buildUrl(path),
  629. headers,
  630. body || ''
  631. ).then(
  632. function(result) {
  633. if (self._isSuccessStatus(result.status)) {
  634. deferred.resolve(result.status);
  635. } else {
  636. result = _.extend(result, self._getSabreException(result));
  637. deferred.reject(result.status, result);
  638. }
  639. }
  640. );
  641. return promise;
  642. },
  643. _simpleCall: function(method, path) {
  644. if (!path) {
  645. throw 'Missing argument "path"';
  646. }
  647. var self = this;
  648. var deferred = $.Deferred();
  649. var promise = deferred.promise();
  650. this._client.request(
  651. method,
  652. this._buildUrl(path)
  653. ).then(
  654. function(result) {
  655. if (self._isSuccessStatus(result.status)) {
  656. deferred.resolve(result.status);
  657. } else {
  658. result = _.extend(result, self._getSabreException(result));
  659. deferred.reject(result.status, result);
  660. }
  661. }
  662. );
  663. return promise;
  664. },
  665. /**
  666. * Creates a directory
  667. *
  668. * @param {String} path path to create
  669. *
  670. * @return {Promise}
  671. */
  672. createDirectory: function(path) {
  673. return this._simpleCall('MKCOL', path);
  674. },
  675. /**
  676. * Deletes a file or directory
  677. *
  678. * @param {String} path path to delete
  679. *
  680. * @return {Promise}
  681. */
  682. remove: function(path) {
  683. return this._simpleCall('DELETE', path);
  684. },
  685. /**
  686. * Moves path to another path
  687. *
  688. * @param {String} path path to move
  689. * @param {String} destinationPath destination path
  690. * @param {boolean} [allowOverwrite=false] true to allow overwriting,
  691. * false otherwise
  692. * @param {Object} [headers=null] additional headers
  693. *
  694. * @return {Promise} promise
  695. */
  696. move: function(path, destinationPath, allowOverwrite, headers) {
  697. if (!path) {
  698. throw 'Missing argument "path"';
  699. }
  700. if (!destinationPath) {
  701. throw 'Missing argument "destinationPath"';
  702. }
  703. var self = this;
  704. var deferred = $.Deferred();
  705. var promise = deferred.promise();
  706. headers = _.extend({}, headers, {
  707. 'Destination' : this._buildUrl(destinationPath)
  708. });
  709. if (!allowOverwrite) {
  710. headers.Overwrite = 'F';
  711. }
  712. this._client.request(
  713. 'MOVE',
  714. this._buildUrl(path),
  715. headers
  716. ).then(
  717. function(result) {
  718. if (self._isSuccessStatus(result.status)) {
  719. deferred.resolve(result.status);
  720. } else {
  721. result = _.extend(result, self._getSabreException(result));
  722. deferred.reject(result.status, result);
  723. }
  724. }
  725. );
  726. return promise;
  727. },
  728. /**
  729. * Copies path to another path
  730. *
  731. * @param {String} path path to copy
  732. * @param {String} destinationPath destination path
  733. * @param {boolean} [allowOverwrite=false] true to allow overwriting,
  734. * false otherwise
  735. *
  736. * @return {Promise} promise
  737. */
  738. copy: function (path, destinationPath, allowOverwrite) {
  739. if (!path) {
  740. throw 'Missing argument "path"';
  741. }
  742. if (!destinationPath) {
  743. throw 'Missing argument "destinationPath"';
  744. }
  745. var self = this;
  746. var deferred = $.Deferred();
  747. var promise = deferred.promise();
  748. var headers = {
  749. 'Destination' : this._buildUrl(destinationPath)
  750. };
  751. if (!allowOverwrite) {
  752. headers.Overwrite = 'F';
  753. }
  754. this._client.request(
  755. 'COPY',
  756. this._buildUrl(path),
  757. headers
  758. ).then(
  759. function(response) {
  760. if (self._isSuccessStatus(response.status)) {
  761. deferred.resolve(response.status);
  762. } else {
  763. deferred.reject(response.status);
  764. }
  765. }
  766. );
  767. return promise;
  768. },
  769. /**
  770. * Add a file info parser function
  771. *
  772. * @param {OC.Files.Client~parseFileInfo} parserFunction
  773. */
  774. addFileInfoParser: function(parserFunction) {
  775. this._fileInfoParsers.push(parserFunction);
  776. },
  777. /**
  778. * Returns the dav.Client instance used internally
  779. *
  780. * @since 11.0.0
  781. * @return {dav.Client}
  782. */
  783. getClient: function() {
  784. return this._client;
  785. },
  786. /**
  787. * Returns the user name
  788. *
  789. * @since 11.0.0
  790. * @return {String} userName
  791. */
  792. getUserName: function() {
  793. return this._client.userName;
  794. },
  795. /**
  796. * Returns the password
  797. *
  798. * @since 11.0.0
  799. * @return {String} password
  800. */
  801. getPassword: function() {
  802. return this._client.password;
  803. },
  804. /**
  805. * Returns the base URL
  806. *
  807. * @since 11.0.0
  808. * @return {String} base URL
  809. */
  810. getBaseUrl: function() {
  811. return this._client.baseUrl;
  812. },
  813. /**
  814. * Returns the host
  815. *
  816. * @since 13.0.0
  817. * @return {String} base URL
  818. */
  819. getHost: function() {
  820. return this._host;
  821. }
  822. };
  823. /**
  824. * File info parser function
  825. *
  826. * This function receives a list of Webdav properties as input and
  827. * should return a hash array of parsed properties, if applicable.
  828. *
  829. * @callback OC.Files.Client~parseFileInfo
  830. * @param {Object} XML Webdav properties
  831. * @return {Array} array of parsed property values
  832. */
  833. if (!OC.Files) {
  834. /**
  835. * @namespace OC.Files
  836. *
  837. * @since 8.2
  838. */
  839. OC.Files = {};
  840. }
  841. /**
  842. * Returns the default instance of the files client
  843. *
  844. * @return {OC.Files.Client} default client
  845. *
  846. * @since 8.2
  847. */
  848. OC.Files.getClient = function() {
  849. if (OC.Files._defaultClient) {
  850. return OC.Files._defaultClient;
  851. }
  852. var client = new OC.Files.Client({
  853. host: OC.getHost(),
  854. port: OC.getPort(),
  855. root: OC.linkToRemoteBase('dav') + '/files/' + OC.getCurrentUser().uid,
  856. useHTTPS: OC.getProtocol() === 'https'
  857. });
  858. OC.Files._defaultClient = client;
  859. return client;
  860. };
  861. OC.Files.Client = Client;
  862. })(OC, OC.Files.FileInfo);