dav
Version:
WebDAV, CalDAV, and CardDAV client for nodejs and the browser
358 lines (297 loc) • 11.7 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', {
value: true
});
exports.createCard = createCard;
exports.updateCard = updateCard;
exports.deleteCard = deleteCard;
exports.syncAddressBook = syncAddressBook;
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var _co = require('co');
var _co2 = _interopRequireDefault(_co);
var _url = require('url');
var _url2 = _interopRequireDefault(_url);
var _fuzzy_url_equals = require('./fuzzy_url_equals');
var _fuzzy_url_equals2 = _interopRequireDefault(_fuzzy_url_equals);
var _model = require('./model');
var _namespace = require('./namespace');
var ns = _interopRequireWildcard(_namespace);
var _request = require('./request');
var request = _interopRequireWildcard(_request);
var _webdav = require('./webdav');
var webdav = _interopRequireWildcard(_webdav);
var debug = require('./debug')('dav:contacts');
/**
* @param {dav.Account} account to fetch address books for.
*/
var listAddressBooks = _co2['default'].wrap(regeneratorRuntime.mark(function callee$0$0(account, options) {
var req, responses, addressBooks;
return regeneratorRuntime.wrap(function callee$0$0$(context$1$0) {
while (1) switch (context$1$0.prev = context$1$0.next) {
case 0:
debug('Fetch address books from home url ' + account.homeUrl);
req = request.propfind({
props: [{ name: 'displayname', namespace: ns.DAV }, { name: 'getctag', namespace: ns.CALENDAR_SERVER }, { name: 'resourcetype', namespace: ns.DAV }, { name: 'sync-token', namespace: ns.DAV }],
depth: 1
});
context$1$0.next = 4;
return options.xhr.send(req, account.homeUrl, {
sandbox: options.sandbox
});
case 4:
responses = context$1$0.sent;
addressBooks = responses.filter(function (res) {
return typeof res.props.displayname === 'string';
}).map(function (res) {
debug('Found address book named ' + res.props.displayname + ',\n props: ' + JSON.stringify(res.props));
return new _model.AddressBook({
data: res,
account: account,
url: _url2['default'].resolve(account.rootUrl, res.href),
ctag: res.props.getctag,
displayName: res.props.displayname,
resourcetype: res.props.resourcetype,
syncToken: res.props.syncToken
});
});
context$1$0.next = 8;
return addressBooks.map(_co2['default'].wrap(regeneratorRuntime.mark(function callee$1$0(addressBook) {
return regeneratorRuntime.wrap(function callee$1$0$(context$2$0) {
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
context$2$0.next = 2;
return webdav.supportedReportSet(addressBook, options);
case 2:
addressBook.reports = context$2$0.sent;
case 3:
case 'end':
return context$2$0.stop();
}
}, callee$1$0, this);
})));
case 8:
return context$1$0.abrupt('return', addressBooks);
case 9:
case 'end':
return context$1$0.stop();
}
}, callee$0$0, this);
}));
exports.listAddressBooks = listAddressBooks;
/**
* @param {dav.AddressBook} addressBook the address book to put the object on.
* @return {Promise} promise will resolve when the card has been created.
*
* Options:
*
* (String) data - vcard object.
* (String) filename - name for the address book vcf file.
* (dav.Sandbox) sandbox - optional request sandbox.
* (dav.Transport) xhr - request sender.
*/
function createCard(addressBook, options) {
var objectUrl = _url2['default'].resolve(addressBook.url, options.filename);
return webdav.createObject(objectUrl, options.data, options);
}
/**
* Options:
*
* (dav.Sandbox) sandbox - optional request sandbox.
*/
var listVCards = _co2['default'].wrap(regeneratorRuntime.mark(function callee$0$0(addressBook, options) {
var req, responses;
return regeneratorRuntime.wrap(function callee$0$0$(context$1$0) {
while (1) switch (context$1$0.prev = context$1$0.next) {
case 0:
debug('Doing REPORT on address book ' + addressBook.url + ' which belongs to\n ' + addressBook.account.credentials.username);
req = request.addressBookQuery({
depth: 1,
props: [{ name: 'getetag', namespace: ns.DAV }, { name: 'address-data', namespace: ns.CARDDAV }]
});
context$1$0.next = 4;
return options.xhr.send(req, addressBook.url, {
sandbox: options.sandbox
});
case 4:
responses = context$1$0.sent;
return context$1$0.abrupt('return', responses.map(function (res) {
debug('Found vcard with url ' + res.href);
return new _model.VCard({
data: res,
addressBook: addressBook,
url: _url2['default'].resolve(addressBook.account.rootUrl, res.href),
etag: res.props.getetag,
addressData: res.props.addressData
});
}));
case 6:
case 'end':
return context$1$0.stop();
}
}, callee$0$0, this);
}));
exports.listVCards = listVCards;
/**
* @param {dav.VCard} card updated vcard object.
* @return {Promise} promise will resolve when the card has been updated.
*
* Options:
*
* (dav.Sandbox) sandbox - optional request sandbox.
* (dav.Transport) xhr - request sender.
*/
function updateCard(card, options) {
return webdav.updateObject(card.url, card.addressData, card.etag, options);
}
/**
* @param {dav.VCard} card target vcard object.
* @return {Promise} promise will resolve when the calendar has been deleted.
*
* Options:
*
* (dav.Sandbox) sandbox - optional request sandbox.
* (dav.Transport) xhr - request sender.
*/
function deleteCard(card, options) {
return webdav.deleteObject(card.url, card.etag, options);
}
/**
* @param {dav.Calendar} calendar the calendar to fetch updates to.
* @return {Promise} promise will resolve with updated calendar object.
*
* Options:
*
* (dav.Sandbox) sandbox - optional request sandbox.
* (String) syncMethod - either 'basic' or 'webdav'. If unspecified, will
* try to do webdav sync and failover to basic sync if rfc 6578 is not
* supported by the server.
* (dav.Transport) xhr - request sender.
*/
function syncAddressBook(addressBook, options) {
options.basicSync = basicSync;
options.webdavSync = webdavSync;
return webdav.syncCollection(addressBook, options);
}
/**
* @param {dav.Account} account the account to fetch updates for.
* @return {Promise} promise will resolve with updated account.
*
* Options:
*
* (dav.Sandbox) sandbox - optional request sandbox.
* (dav.Transport) xhr - request sender.
*/
var syncCarddavAccount = _co2['default'].wrap(regeneratorRuntime.mark(function callee$0$0(account) {
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
var addressBooks;
return regeneratorRuntime.wrap(function callee$0$0$(context$1$0) {
while (1) switch (context$1$0.prev = context$1$0.next) {
case 0:
options.loadObjects = false;
if (!account.addressBooks) {
account.addressBooks = [];
}
context$1$0.next = 4;
return listAddressBooks(account, options);
case 4:
addressBooks = context$1$0.sent;
addressBooks.filter(function (addressBook) {
// Filter the address books not previously seen.
return account.addressBooks.every(function (prev) {
return !(0, _fuzzy_url_equals2['default'])(prev.url, addressBook.url);
});
}).forEach(function (addressBook) {
return account.addressBooks.push(addressBook);
});
options.loadObjects = true;
context$1$0.next = 9;
return account.addressBooks.map(_co2['default'].wrap(regeneratorRuntime.mark(function callee$1$0(addressBook, index) {
return regeneratorRuntime.wrap(function callee$1$0$(context$2$0) {
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
context$2$0.prev = 0;
context$2$0.next = 3;
return syncAddressBook(addressBook, options);
case 3:
context$2$0.next = 9;
break;
case 5:
context$2$0.prev = 5;
context$2$0.t0 = context$2$0['catch'](0);
debug('Syncing ' + addressBook.displayName + ' failed with ' + context$2$0.t0);
account.addressBooks.splice(index, 1);
case 9:
case 'end':
return context$2$0.stop();
}
}, callee$1$0, this, [[0, 5]]);
})));
case 9:
return context$1$0.abrupt('return', account);
case 10:
case 'end':
return context$1$0.stop();
}
}, callee$0$0, this);
}));
exports.syncCarddavAccount = syncCarddavAccount;
var basicSync = _co2['default'].wrap(regeneratorRuntime.mark(function callee$0$0(addressBook, options) {
var sync;
return regeneratorRuntime.wrap(function callee$0$0$(context$1$0) {
while (1) switch (context$1$0.prev = context$1$0.next) {
case 0:
sync = webdav.isCollectionDirty(addressBook, options);
if (sync) {
context$1$0.next = 4;
break;
}
debug('Local ctag matched remote! No need to sync :).');
return context$1$0.abrupt('return', addressBook);
case 4:
debug('ctag changed so we need to fetch stuffs.');
context$1$0.next = 7;
return listVCards(addressBook, options);
case 7:
addressBook.objects = context$1$0.sent;
return context$1$0.abrupt('return', addressBook);
case 9:
case 'end':
return context$1$0.stop();
}
}, callee$0$0, this);
}));
var webdavSync = _co2['default'].wrap(regeneratorRuntime.mark(function callee$0$0(addressBook, options) {
var req, result;
return regeneratorRuntime.wrap(function callee$0$0$(context$1$0) {
while (1) switch (context$1$0.prev = context$1$0.next) {
case 0:
req = request.syncCollection({
props: [{ name: 'getetag', namespace: ns.DAV }, { name: 'address-data', namespace: ns.CARDDAV }],
syncLevel: 1,
syncToken: addressBook.syncToken
});
context$1$0.next = 3;
return options.xhr.send(req, addressBook.url, {
sandbox: options.sandbox
});
case 3:
result = context$1$0.sent;
// TODO(gareth): Handle creations and deletions.
result.responses.forEach(function (response) {
// Find the vcard that this response corresponds with.
var vcard = addressBook.objects.filter(function (object) {
return (0, _fuzzy_url_equals2['default'])(object.url, response.href);
})[0];
if (!vcard) return;
vcard.etag = response.props.getetag;
vcard.addressData = response.props.addressData;
});
addressBook.syncToken = result.syncToken;
return context$1$0.abrupt('return', addressBook);
case 7:
case 'end':
return context$1$0.stop();
}
}, callee$0$0, this);
}));