dav
Version:
WebDAV, CalDAV, and CardDAV client for nodejs and the browser
1,540 lines (1,278 loc) • 221 kB
JavaScript
/**
* Polyfill from developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/find
*/
if (!Array.prototype.find) {
Array.prototype.find = function(predicate) {
if (this == null) {
throw new TypeError('Array.prototype.find called on null or undefined');
}
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
var list = Object(this);
var length = list.length >>> 0;
var thisArg = arguments[1];
var value;
for (var i = 0; i < length; i++) {
value = list[i];
if (predicate.call(thisArg, value, i, list)) {
return value;
}
}
return undefined;
};
}
/**
* Polyfill from developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
*/
if (!Object.assign) {
Object.defineProperty(Object, 'assign', {
enumerable: false,
configurable: true,
writable: true,
value: function(target, firstSource) {
'use strict';
if (target === undefined || target === null) {
throw new TypeError('Cannot convert first argument to object');
}
var to = Object(target);
for (var i = 1; i < arguments.length; i++) {
var nextSource = arguments[i];
if (nextSource === undefined || nextSource === null) {
continue;
}
nextSource = Object(nextSource);
var keysArray = Object.keys(Object(nextSource));
for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
var nextKey = keysArray[nextIndex];
var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
if (desc !== undefined && desc.enumerable) {
to[nextKey] = nextSource[nextKey];
}
}
}
return to;
}
});
}
/**
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* https://raw.github.com/facebook/regenerator/master/LICENSE file. An
* additional grant of patent rights can be found in the PATENTS file in
* the same directory.
*/
!(function(global) {
"use strict";
var hasOwn = Object.prototype.hasOwnProperty;
var undefined; // More compressible than void 0.
var iteratorSymbol =
typeof Symbol === "function" && Symbol.iterator || "@@iterator";
var inModule = typeof module === "object";
var runtime = global.regeneratorRuntime;
if (runtime) {
if (inModule) {
// If regeneratorRuntime is defined globally and we're in a module,
// make the exports object identical to regeneratorRuntime.
module.exports = runtime;
}
// Don't bother evaluating the rest of this file if the runtime was
// already defined globally.
return;
}
// Define the runtime globally (as expected by generated code) as either
// module.exports (if we're in a module) or a new, empty object.
runtime = global.regeneratorRuntime = inModule ? module.exports : {};
function wrap(innerFn, outerFn, self, tryLocsList) {
// If outerFn provided, then outerFn.prototype instanceof Generator.
var generator = Object.create((outerFn || Generator).prototype);
generator._invoke = makeInvokeMethod(
innerFn, self || null,
new Context(tryLocsList || [])
);
return generator;
}
runtime.wrap = wrap;
// Try/catch helper to minimize deoptimizations. Returns a completion
// record like context.tryEntries[i].completion. This interface could
// have been (and was previously) designed to take a closure to be
// invoked without arguments, but in all the cases we care about we
// already have an existing method we want to call, so there's no need
// to create a new function object. We can even get away with assuming
// the method takes exactly one argument, since that happens to be true
// in every case, so we don't have to touch the arguments object. The
// only additional allocation required is the completion record, which
// has a stable shape and so hopefully should be cheap to allocate.
function tryCatch(fn, obj, arg) {
try {
return { type: "normal", arg: fn.call(obj, arg) };
} catch (err) {
return { type: "throw", arg: err };
}
}
var GenStateSuspendedStart = "suspendedStart";
var GenStateSuspendedYield = "suspendedYield";
var GenStateExecuting = "executing";
var GenStateCompleted = "completed";
// Returning this object from the innerFn has the same effect as
// breaking out of the dispatch switch statement.
var ContinueSentinel = {};
// Dummy constructor functions that we use as the .constructor and
// .constructor.prototype properties for functions that return Generator
// objects. For full spec compliance, you may wish to configure your
// minifier not to mangle the names of these two functions.
function Generator() {}
function GeneratorFunction() {}
function GeneratorFunctionPrototype() {}
var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype;
GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype;
GeneratorFunctionPrototype.constructor = GeneratorFunction;
GeneratorFunction.displayName = "GeneratorFunction";
runtime.isGeneratorFunction = function(genFun) {
var ctor = typeof genFun === "function" && genFun.constructor;
return ctor
? ctor === GeneratorFunction ||
// For the native GeneratorFunction constructor, the best we can
// do is to check its .name property.
(ctor.displayName || ctor.name) === "GeneratorFunction"
: false;
};
runtime.mark = function(genFun) {
genFun.__proto__ = GeneratorFunctionPrototype;
genFun.prototype = Object.create(Gp);
return genFun;
};
runtime.async = function(innerFn, outerFn, self, tryLocsList) {
return new Promise(function(resolve, reject) {
var generator = wrap(innerFn, outerFn, self, tryLocsList);
var callNext = step.bind(generator, "next");
var callThrow = step.bind(generator, "throw");
function step(method, arg) {
var record = tryCatch(generator[method], generator, arg);
if (record.type === "throw") {
reject(record.arg);
return;
}
var info = record.arg;
if (info.done) {
resolve(info.value);
} else {
Promise.resolve(info.value).then(callNext, callThrow);
}
}
callNext();
});
};
function makeInvokeMethod(innerFn, self, context) {
var state = GenStateSuspendedStart;
return function invoke(method, arg) {
if (state === GenStateExecuting) {
throw new Error("Generator is already running");
}
if (state === GenStateCompleted) {
// Be forgiving, per 25.3.3.3.3 of the spec:
// https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume
return doneResult();
}
while (true) {
var delegate = context.delegate;
if (delegate) {
if (method === "return" ||
(method === "throw" && delegate.iterator[method] === undefined)) {
// A return or throw (when the delegate iterator has no throw
// method) always terminates the yield* loop.
context.delegate = null;
// If the delegate iterator has a return method, give it a
// chance to clean up.
var returnMethod = delegate.iterator["return"];
if (returnMethod) {
var record = tryCatch(returnMethod, delegate.iterator, arg);
if (record.type === "throw") {
// If the return method threw an exception, let that
// exception prevail over the original return or throw.
method = "throw";
arg = record.arg;
continue;
}
}
if (method === "return") {
// Continue with the outer return, now that the delegate
// iterator has been terminated.
continue;
}
}
var record = tryCatch(
delegate.iterator[method],
delegate.iterator,
arg
);
if (record.type === "throw") {
context.delegate = null;
// Like returning generator.throw(uncaught), but without the
// overhead of an extra function call.
method = "throw";
arg = record.arg;
continue;
}
// Delegate generator ran and handled its own exceptions so
// regardless of what the method was, we continue as if it is
// "next" with an undefined arg.
method = "next";
arg = undefined;
var info = record.arg;
if (info.done) {
context[delegate.resultName] = info.value;
context.next = delegate.nextLoc;
} else {
state = GenStateSuspendedYield;
return info;
}
context.delegate = null;
}
if (method === "next") {
if (state === GenStateSuspendedYield) {
context.sent = arg;
} else {
delete context.sent;
}
} else if (method === "throw") {
if (state === GenStateSuspendedStart) {
state = GenStateCompleted;
throw arg;
}
if (context.dispatchException(arg)) {
// If the dispatched exception was caught by a catch block,
// then let that catch block handle the exception normally.
method = "next";
arg = undefined;
}
} else if (method === "return") {
context.abrupt("return", arg);
}
state = GenStateExecuting;
var record = tryCatch(innerFn, self, context);
if (record.type === "normal") {
// If an exception is thrown from innerFn, we leave state ===
// GenStateExecuting and loop back for another invocation.
state = context.done
? GenStateCompleted
: GenStateSuspendedYield;
var info = {
value: record.arg,
done: context.done
};
if (record.arg === ContinueSentinel) {
if (context.delegate && method === "next") {
// Deliberately forget the last sent value so that we don't
// accidentally pass it on to the delegate.
arg = undefined;
}
} else {
return info;
}
} else if (record.type === "throw") {
state = GenStateCompleted;
// Dispatch the exception by looping back around to the
// context.dispatchException(arg) call above.
method = "throw";
arg = record.arg;
}
}
};
}
function defineGeneratorMethod(method) {
Gp[method] = function(arg) {
return this._invoke(method, arg);
};
}
defineGeneratorMethod("next");
defineGeneratorMethod("throw");
defineGeneratorMethod("return");
Gp[iteratorSymbol] = function() {
return this;
};
Gp.toString = function() {
return "[object Generator]";
};
function pushTryEntry(locs) {
var entry = { tryLoc: locs[0] };
if (1 in locs) {
entry.catchLoc = locs[1];
}
if (2 in locs) {
entry.finallyLoc = locs[2];
entry.afterLoc = locs[3];
}
this.tryEntries.push(entry);
}
function resetTryEntry(entry) {
var record = entry.completion || {};
record.type = "normal";
delete record.arg;
entry.completion = record;
}
function Context(tryLocsList) {
// The root entry object (effectively a try statement without a catch
// or a finally block) gives us a place to store values thrown from
// locations where there is no enclosing try statement.
this.tryEntries = [{ tryLoc: "root" }];
tryLocsList.forEach(pushTryEntry, this);
this.reset();
}
runtime.keys = function(object) {
var keys = [];
for (var key in object) {
keys.push(key);
}
keys.reverse();
// Rather than returning an object with a next method, we keep
// things simple and return the next function itself.
return function next() {
while (keys.length) {
var key = keys.pop();
if (key in object) {
next.value = key;
next.done = false;
return next;
}
}
// To avoid creating an additional object, we just hang the .value
// and .done properties off the next function object itself. This
// also ensures that the minifier will not anonymize the function.
next.done = true;
return next;
};
};
function values(iterable) {
if (iterable) {
var iteratorMethod = iterable[iteratorSymbol];
if (iteratorMethod) {
return iteratorMethod.call(iterable);
}
if (typeof iterable.next === "function") {
return iterable;
}
if (!isNaN(iterable.length)) {
var i = -1, next = function next() {
while (++i < iterable.length) {
if (hasOwn.call(iterable, i)) {
next.value = iterable[i];
next.done = false;
return next;
}
}
next.value = undefined;
next.done = true;
return next;
};
return next.next = next;
}
}
// Return an iterator with no values.
return { next: doneResult };
}
runtime.values = values;
function doneResult() {
return { value: undefined, done: true };
}
Context.prototype = {
constructor: Context,
reset: function() {
this.prev = 0;
this.next = 0;
this.sent = undefined;
this.done = false;
this.delegate = null;
this.tryEntries.forEach(resetTryEntry);
// Pre-initialize at least 20 temporary variables to enable hidden
// class optimizations for simple generators.
for (var tempIndex = 0, tempName;
hasOwn.call(this, tempName = "t" + tempIndex) || tempIndex < 20;
++tempIndex) {
this[tempName] = null;
}
},
stop: function() {
this.done = true;
var rootEntry = this.tryEntries[0];
var rootRecord = rootEntry.completion;
if (rootRecord.type === "throw") {
throw rootRecord.arg;
}
return this.rval;
},
dispatchException: function(exception) {
if (this.done) {
throw exception;
}
var context = this;
function handle(loc, caught) {
record.type = "throw";
record.arg = exception;
context.next = loc;
return !!caught;
}
for (var i = this.tryEntries.length - 1; i >= 0; --i) {
var entry = this.tryEntries[i];
var record = entry.completion;
if (entry.tryLoc === "root") {
// Exception thrown outside of any try block that could handle
// it, so set the completion value of the entire function to
// throw the exception.
return handle("end");
}
if (entry.tryLoc <= this.prev) {
var hasCatch = hasOwn.call(entry, "catchLoc");
var hasFinally = hasOwn.call(entry, "finallyLoc");
if (hasCatch && hasFinally) {
if (this.prev < entry.catchLoc) {
return handle(entry.catchLoc, true);
} else if (this.prev < entry.finallyLoc) {
return handle(entry.finallyLoc);
}
} else if (hasCatch) {
if (this.prev < entry.catchLoc) {
return handle(entry.catchLoc, true);
}
} else if (hasFinally) {
if (this.prev < entry.finallyLoc) {
return handle(entry.finallyLoc);
}
} else {
throw new Error("try statement without catch or finally");
}
}
}
},
abrupt: function(type, arg) {
for (var i = this.tryEntries.length - 1; i >= 0; --i) {
var entry = this.tryEntries[i];
if (entry.tryLoc <= this.prev &&
hasOwn.call(entry, "finallyLoc") &&
this.prev < entry.finallyLoc) {
var finallyEntry = entry;
break;
}
}
if (finallyEntry &&
(type === "break" ||
type === "continue") &&
finallyEntry.tryLoc <= arg &&
arg <= finallyEntry.finallyLoc) {
// Ignore the finally entry if control is not jumping to a
// location outside the try/catch block.
finallyEntry = null;
}
var record = finallyEntry ? finallyEntry.completion : {};
record.type = type;
record.arg = arg;
if (finallyEntry) {
this.next = finallyEntry.finallyLoc;
} else {
this.complete(record);
}
return ContinueSentinel;
},
complete: function(record, afterLoc) {
if (record.type === "throw") {
throw record.arg;
}
if (record.type === "break" ||
record.type === "continue") {
this.next = record.arg;
} else if (record.type === "return") {
this.rval = record.arg;
this.next = "end";
} else if (record.type === "normal" && afterLoc) {
this.next = afterLoc;
}
},
finish: function(finallyLoc) {
for (var i = this.tryEntries.length - 1; i >= 0; --i) {
var entry = this.tryEntries[i];
if (entry.finallyLoc === finallyLoc) {
this.complete(entry.completion, entry.afterLoc);
resetTryEntry(entry);
return ContinueSentinel;
}
}
},
"catch": function(tryLoc) {
for (var i = this.tryEntries.length - 1; i >= 0; --i) {
var entry = this.tryEntries[i];
if (entry.tryLoc === tryLoc) {
var record = entry.completion;
if (record.type === "throw") {
var thrown = record.arg;
resetTryEntry(entry);
}
return thrown;
}
}
// The context.catch method must only be called with a location
// argument that corresponds to a known catch block.
throw new Error("illegal catch attempt");
},
delegateYield: function(iterable, resultName, nextLoc) {
this.delegate = {
iterator: values(iterable),
resultName: resultName,
nextLoc: nextLoc
};
return ContinueSentinel;
}
};
})(
// Among the various tricks for obtaining a reference to the global
// object, this seems to be the most reliable technique that does not
// use indirect eval (which violates Content Security Policy).
typeof global === "object" ? global :
typeof window === "object" ? window :
typeof self === "object" ? self : this
);
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.dav = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
'use strict';
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 _calendars = require('./calendars');
var _contacts = require('./contacts');
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 debug = require('./debug')('dav:accounts');
var defaults = {
accountType: 'caldav',
loadCollections: true,
loadObjects: false
};
/**
* rfc 6764.
*
* @param {dav.Account} account to find root url for.
*/
var serviceDiscovery = _co2['default'].wrap(regeneratorRuntime.mark(function callee$0$0(account, options) {
var endpoint, uri, req, xhr, _location;
return regeneratorRuntime.wrap(function callee$0$0$(context$1$0) {
while (1) switch (context$1$0.prev = context$1$0.next) {
case 0:
debug('Attempt service discovery.');
endpoint = _url2['default'].parse(account.server);
endpoint.protocol = endpoint.protocol || 'http'; // TODO(gareth) https?
uri = _url2['default'].format({
protocol: endpoint.protocol,
host: endpoint.host,
pathname: '/.well-known/' + options.accountType
});
req = request.basic({ method: 'GET' });
context$1$0.prev = 5;
context$1$0.next = 8;
return options.xhr.send(req, uri, { sandbox: options.sandbox });
case 8:
xhr = context$1$0.sent;
if (!(xhr.status >= 300 && xhr.status < 400)) {
context$1$0.next = 14;
break;
}
_location = xhr.getResponseHeader('Location');
if (!(typeof _location === 'string' && _location.length)) {
context$1$0.next = 14;
break;
}
debug('Discovery redirected to ' + _location);
return context$1$0.abrupt('return', _url2['default'].format({
protocol: endpoint.protocol,
host: endpoint.host,
pathname: _location
}));
case 14:
context$1$0.next = 19;
break;
case 16:
context$1$0.prev = 16;
context$1$0.t0 = context$1$0['catch'](5);
debug('Discovery failed... failover to the provided url');
case 19:
return context$1$0.abrupt('return', endpoint.href);
case 20:
case 'end':
return context$1$0.stop();
}
}, callee$0$0, this, [[5, 16]]);
}));
/**
* rfc 5397.
*
* @param {dav.Account} account to get principal url for.
*/
var principalUrl = _co2['default'].wrap(regeneratorRuntime.mark(function callee$0$0(account, options) {
var req, res, container;
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 principal url from context path ' + account.rootUrl + '.');
req = request.propfind({
props: [{ name: 'current-user-principal', namespace: ns.DAV }],
depth: 0,
mergeResponses: true
});
context$1$0.next = 4;
return options.xhr.send(req, account.rootUrl, {
sandbox: options.sandbox
});
case 4:
res = context$1$0.sent;
container = res.props;
debug('Received principal: ' + container.currentUserPrincipal);
return context$1$0.abrupt('return', _url2['default'].resolve(account.rootUrl, container.currentUserPrincipal));
case 8:
case 'end':
return context$1$0.stop();
}
}, callee$0$0, this);
}));
/**
* @param {dav.Account} account to get home url for.
*/
var homeUrl = _co2['default'].wrap(regeneratorRuntime.mark(function callee$0$0(account, options) {
var prop, req, responses, response, container, href;
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 home url from principal url ' + account.principalUrl + '.');
prop = undefined;
if (options.accountType === 'caldav') {
prop = { name: 'calendar-home-set', namespace: ns.CALDAV };
} else if (options.accountType === 'carddav') {
prop = { name: 'addressbook-home-set', namespace: ns.CARDDAV };
}
req = request.propfind({ props: [prop] });
context$1$0.next = 6;
return options.xhr.send(req, account.principalUrl, {
sandbox: options.sandbox
});
case 6:
responses = context$1$0.sent;
response = responses.find(function (response) {
return (0, _fuzzy_url_equals2['default'])(account.principalUrl, response.href);
});
container = response.props;
href = undefined;
if (options.accountType === 'caldav') {
debug('Received home: ' + container.calendarHomeSet);
href = container.calendarHomeSet;
} else if (options.accountType === 'carddav') {
debug('Received home: ' + container.addressbookHomeSet);
href = container.addressbookHomeSet;
}
return context$1$0.abrupt('return', _url2['default'].resolve(account.rootUrl, href));
case 12:
case 'end':
return context$1$0.stop();
}
}, callee$0$0, this);
}));
/**
* Options:
*
* (String) accountType - one of 'caldav' or 'carddav'. Defaults to 'caldav'.
* (Array.<Object>) filters - list of caldav filters to send with request.
* (Boolean) loadCollections - whether or not to load dav collections.
* (Boolean) loadObjects - whether or not to load dav objects.
* (dav.Sandbox) sandbox - optional request sandbox.
* (String) server - some url for server (needn't be base url).
* (String) timezone - VTIMEZONE calendar object.
* (dav.Transport) xhr - request sender.
*
* @return {Promise} a promise that will resolve with a dav.Account object.
*/
exports.createAccount = _co2['default'].wrap(regeneratorRuntime.mark(function callee$0$0(options) {
var account, key, loadCollections, loadObjects, collections;
return regeneratorRuntime.wrap(function callee$0$0$(context$1$0) {
while (1) switch (context$1$0.prev = context$1$0.next) {
case 0:
options = Object.assign({}, defaults, options);
if (typeof options.loadObjects !== 'boolean') {
options.loadObjects = options.loadCollections;
}
account = new _model.Account({
server: options.server,
credentials: options.xhr.credentials
});
context$1$0.next = 5;
return serviceDiscovery(account, options);
case 5:
account.rootUrl = context$1$0.sent;
context$1$0.next = 8;
return principalUrl(account, options);
case 8:
account.principalUrl = context$1$0.sent;
context$1$0.next = 11;
return homeUrl(account, options);
case 11:
account.homeUrl = context$1$0.sent;
if (options.loadCollections) {
context$1$0.next = 14;
break;
}
return context$1$0.abrupt('return', account);
case 14:
key = undefined, loadCollections = undefined, loadObjects = undefined;
if (options.accountType === 'caldav') {
key = 'calendars';
loadCollections = _calendars.listCalendars;
loadObjects = _calendars.listCalendarObjects;
} else if (options.accountType === 'carddav') {
key = 'addressBooks';
loadCollections = _contacts.listAddressBooks;
loadObjects = _contacts.listVCards;
}
context$1$0.next = 18;
return loadCollections(account, options);
case 18:
collections = context$1$0.sent;
account[key] = collections;
if (options.loadObjects) {
context$1$0.next = 22;
break;
}
return context$1$0.abrupt('return', account);
case 22:
context$1$0.next = 24;
return collections.map(_co2['default'].wrap(regeneratorRuntime.mark(function callee$1$0(collection) {
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 loadObjects(collection, options);
case 3:
collection.objects = context$2$0.sent;
context$2$0.next = 9;
break;
case 6:
context$2$0.prev = 6;
context$2$0.t0 = context$2$0['catch'](0);
collection.error = context$2$0.t0;
case 9:
case 'end':
return context$2$0.stop();
}
}, callee$1$0, this, [[0, 6]]);
})));
case 24:
account[key] = account[key].filter(function (collection) {
return !collection.error;
});
return context$1$0.abrupt('return', account);
case 26:
case 'end':
return context$1$0.stop();
}
}, callee$0$0, this);
}));
// http redirect.
},{"./calendars":2,"./contacts":5,"./debug":6,"./fuzzy_url_equals":7,"./model":9,"./namespace":10,"./request":12,"co":24,"url":29}],2:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
exports.createCalendarObject = createCalendarObject;
exports.updateCalendarObject = updateCalendarObject;
exports.deleteCalendarObject = deleteCalendarObject;
exports.syncCalendar = syncCalendar;
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:calendars');
var ICAL_OBJS = new Set(['VEVENT', 'VTODO', 'VJOURNAL', 'VFREEBUSY', 'VTIMEZONE', 'VALARM']);
/**
* @param {dav.Account} account to fetch calendars for.
*/
var listCalendars = _co2['default'].wrap(regeneratorRuntime.mark(function callee$0$0(account, options) {
var req, responses, cals;
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 calendars from home url ' + account.homeUrl);
req = request.propfind({
props: [{ name: 'calendar-description', namespace: ns.CALDAV }, { name: 'calendar-timezone', namespace: ns.CALDAV }, { name: 'displayname', namespace: ns.DAV }, { name: 'getctag', namespace: ns.CALENDAR_SERVER }, { name: 'resourcetype', namespace: ns.DAV }, { name: 'supported-calendar-component-set', namespace: ns.CALDAV }, { 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;
debug('Found ' + responses.length + ' calendars.');
cals = responses.filter(function (res) {
return res.props.resourcetype.includes('calendar');
}).filter(function (res) {
// We only want the calendar if it contains iCalendar objects.
var components = res.props.supportedCalendarComponentSet || [];
return components.reduce(function (hasObjs, component) {
return hasObjs || ICAL_OBJS.has(component);
}, false);
}).map(function (res) {
debug('Found calendar ' + res.props.displayname + ',\n props: ' + JSON.stringify(res.props));
return new _model.Calendar({
data: res,
account: account,
description: res.props.calendarDescription,
timezone: res.props.calendarTimezone,
url: _url2['default'].resolve(account.rootUrl, res.href),
ctag: res.props.getctag,
displayName: res.props.displayname,
components: res.props.supportedCalendarComponentSet,
resourcetype: res.props.resourcetype,
syncToken: res.props.syncToken
});
});
context$1$0.next = 9;
return cals.map(_co2['default'].wrap(regeneratorRuntime.mark(function callee$1$0(cal) {
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(cal, options);
case 2:
cal.reports = context$2$0.sent;
case 3:
case 'end':
return context$2$0.stop();
}
}, callee$1$0, this);
})));
case 9:
return context$1$0.abrupt('return', cals);
case 10:
case 'end':
return context$1$0.stop();
}
}, callee$0$0, this);
}));
exports.listCalendars = listCalendars;
/**
* @param {dav.Calendar} calendar the calendar to put the object on.
* @return {Promise} promise will resolve when the calendar has been created.
*
* Options:
*
* (String) data - rfc 5545 VCALENDAR object.
* (String) filename - name for the calendar ics file.
* (dav.Sandbox) sandbox - optional request sandbox.
* (dav.Transport) xhr - request sender.
*/
function createCalendarObject(calendar, options) {
var objectUrl = _url2['default'].resolve(calendar.url, options.filename);
options.contentType = options.contentType || "text/calendar; charset=utf-8";
return webdav.createObject(objectUrl, options.data, options);
}
;
/**
* @param {dav.CalendarObject} calendarObject updated calendar object.
* @return {Promise} promise will resolve when the calendar has been updated.
*
* Options:
*
* (dav.Sandbox) sandbox - optional request sandbox.
* (dav.Transport) xhr - request sender.
*/
function updateCalendarObject(calendarObject, options) {
options.contentType = options.contentType || "text/calendar; charset=utf-8";
return webdav.updateObject(calendarObject.url, calendarObject.calendarData, calendarObject.etag, options);
}
/**
* @param {dav.CalendarObject} calendarObject target calendar 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 deleteCalendarObject(calendarObject, options) {
return webdav.deleteObject(calendarObject.url, calendarObject.etag, options);
}
/**
* @param {dav.Calendar} calendar the calendar to fetch objects for.
*
* Options:
*
* (Array.<Object>) filters - optional caldav filters.
* (dav.Sandbox) sandbox - optional request sandbox.
* (dav.Transport) xhr - request sender.
*/
var listCalendarObjects = _co2['default'].wrap(regeneratorRuntime.mark(function callee$0$0(calendar, options) {
var filters, 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 calendar ' + calendar.url + ' which belongs to\n ' + calendar.account.credentials.username);
filters = options.filters || [{
type: 'comp-filter',
attrs: { name: 'VCALENDAR' },
children: [{
type: 'comp-filter',
attrs: { name: 'VEVENT' }
}]
}];
req = request.calendarQuery({
depth: 1,
props: [{ name: 'getetag', namespace: ns.DAV }, { name: 'calendar-data', namespace: ns.CALDAV }],
filters: filters
});
context$1$0.next = 5;
return options.xhr.send(req, calendar.url, {
sandbox: options.sandbox
});
case 5:
responses = context$1$0.sent;
return context$1$0.abrupt('return', responses.map(function (res) {
debug('Found calendar object with url ' + res.href);
return new _model.CalendarObject({
data: res,
calendar: calendar,
url: _url2['default'].resolve(calendar.account.rootUrl, res.href),
etag: res.props.getetag,
calendarData: res.props.calendarData
});
}));
case 7:
case 'end':
return context$1$0.stop();
}
}, callee$0$0, this);
}));
exports.listCalendarObjects = listCalendarObjects;
/**
* @param {dav.Calendar} calendar the calendar to fetch updates to.
* @return {Promise} promise will resolve with updated calendar object.
*
* Options:
*
* (Array.<Object>) filters - list of caldav filters to send with request.
* (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.
* (String) timezone - VTIMEZONE calendar object.
* (dav.Transport) xhr - request sender.
*/
function syncCalendar(calendar, options) {
options.basicSync = basicSync;
options.webdavSync = webdavSync;
return webdav.syncCollection(calendar, 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 syncCaldavAccount = _co2['default'].wrap(regeneratorRuntime.mark(function callee$0$0(account) {
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
var cals;
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.calendars) account.calendars = [];
context$1$0.next = 4;
return listCalendars(account, options);
case 4:
cals = context$1$0.sent;
cals.filter(function (cal) {
// Filter the calendars not previously seen.
return account.calendars.every(function (prev) {
return !(0, _fuzzy_url_equals2['default'])(prev.url, cal.url);
});
}).forEach(function (cal) {
// Add them to the account's calendar list.
account.calendars.push(cal);
});
options.loadObjects = true;
context$1$0.next = 9;
return account.calendars.map(_co2['default'].wrap(regeneratorRuntime.mark(function callee$1$0(cal, 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 syncCalendar(cal, 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('Sync calendar ' + cal.displayName + ' failed with ' + context$2$0.t0);
account.calendars.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.syncCaldavAccount = syncCaldavAccount;
var basicSync = _co2['default'].wrap(regeneratorRuntime.mark(function callee$0$0(calendar, 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:
context$1$0.next = 2;
return webdav.isCollectionDirty(calendar, options);
case 2:
sync = context$1$0.sent;
if (sync) {
context$1$0.next = 6;
break;
}
debug('Local ctag matched remote! No need to sync :).');
return context$1$0.abrupt('return', calendar);
case 6:
debug('ctag changed so we need to fetch stuffs.');
context$1$0.next = 9;
return listCalendarObjects(calendar, options);
case 9:
calendar.objects = context$1$0.sent;
return context$1$0.abrupt('return', calendar);
case 11:
case 'end':
return context$1$0.stop();
}
}, callee$0$0, this);
}));
var webdavSync = _co2['default'].wrap(regeneratorRuntime.mark(function callee$0$0(calendar, 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: 'calendar-data', namespace: ns.CALDAV }],
syncLevel: 1,
syncToken: calendar.syncToken
});
context$1$0.next = 3;
return options.xhr.send(req, calendar.url, {
sandbox: options.sandbox
});
case 3:
result = context$1$0.sent;
// TODO(gareth): Handle creations and deletions.
result.responses.forEach(function (response) {
// Find the calendar object that this response corresponds with.
var calendarObject = calendar.objects.filter(function (object) {
return (0, _fuzzy_url_equals2['default'])(object.url, response.href);
})[0];
if (!calendarObject) {
return;
}
calendarObject.etag = response.props.getetag;
calendarObject.calendarData = response.props.calendarData;
});
calendar.syncToken = result.syncToken;
return context$1$0.abrupt('return', calendar);
case 7:
case 'end':
return context$1$0.stop();
}
}, callee$0$0, this);
}));
},{"./debug":6,"./fuzzy_url_equals":7,"./model":9,"./namespace":10,"./request":12,"./webdav":22,"co":24,"url":29}],3:[function(require,module,exports){
/**
* @fileoverview Camelcase something.
*/
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
exports['default'] = camelize;
function camelize(str) {
var delimiter = arguments.length <= 1 || arguments[1] === undefined ? '_' : arguments[1];
var words = str.split(delimiter);
return [words[0]].concat(words.slice(1).map(function (word) {
return word.charAt(0).toUpperCase() + word.slice(1);
})).join('');
}
module.exports = exports['default'];
},{}],4:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
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 }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
var _url = require('url');
var _url2 = _interopRequireDefault(_url);
var _accounts = require('./accounts');
var accounts = _interopRequireWildcard(_accounts);
var _calendars = require('./calendars');
var calendars = _interopRequireWildcard(_calendars);
var _contacts = require('./contacts');
var contacts = _interopRequireWildcard(_contacts);
/**
* @param {dav.Transport} xhr - request sender.
*
* Options:
*
* (String) baseUrl - root url to resolve relative request urls with.
*/
var Client = (function () {
function Client(xhr) {
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
_classCallCheck(this, Client);
this.xhr = xhr;
Object.assign(this, options);
// Expose internal modules for unit testing
this._accounts = accounts;
this._calendars = calendars;
this._contacts = contacts;
}
/**
* @param {dav.Request} req - dav request.
* @param {String} uri - where to send request.
* @return {Promise} a promise that will be resolved with an xhr request
* after its readyState is 4 or the result of applying an optional
* request `transformResponse` function to the xhr object after its
* readyState is 4.
*
* Options:
*
* (Object) sandbox - optional request sandbox.
*/
_createClass(Client, [{
key: 'send',
value: function send(req, uri, options) {
if (this.baseUrl) {
var urlObj = _url2['default'].parse(uri);
uri = _url2['default'].resolve(this.baseUrl, urlObj.path);
}
return this.xhr.send(req, uri, options);
}
}, {
key: 'createAccount',
value: function createAccount() {
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
options.xhr = options.xhr || this.xhr;
return accounts.createAccount(options);
}
}, {
key: 'createCalendarObject',
value: function createCalendarObject(calendar) {
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
options.xhr = options.xhr || this.xhr;
return calendars.createCalendarObject(calendar, options);
}
}, {
key: 'updateCalendarObject',
value: function updateCalendarObject(calendarObject) {
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
options.xhr = options.xhr || this.xhr;
return calendars.updateCalendarObject(calendarObject, options);
}
}, {
key: 'deleteCalendarObject',
value: function deleteCalendarObject(calendarObject) {
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
options.xhr = options.xhr || this.xhr;
return calendars.deleteCalendarObject(calendarObject, options);
}
}, {
key: 'syncCalendar',
value: function syncCalendar(calendar) {
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
options.xhr = options.xhr || this.xhr;
return calendars.syncCalendar(calendar, options);
}
}, {
key: 'syncCaldavAccount',
value: function syncCaldavAccount(account) {
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
options.xhr = options.xhr || this.xhr;
return calendars.syncCaldavAccount(account, options);
}
}, {
key: 'createCard',
value: function createCard(addressBook) {
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
options.xhr = options.xhr || this.xhr;
return contacts.createCard(addressBook, options);
}
}, {
key: 'updateCard',
value: function updateCard(card) {
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
options.xhr = options.xhr || this.xhr;
return contacts.updateCard(card, options);
}
}, {
key: 'deleteCard',
value: function deleteCard(card) {
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
options.xhr = options.xhr || this.xhr;
return contacts.deleteCard(card, options);
}
}, {
key: 'syncAddressBook',
value: function syncAddressBook(addressBook) {
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
options.xhr = options.xhr || this.xhr;
return contacts.syncAddressBook(addressBook, options);
}
}, {
key: 'syncCarddavAccount',
value: function syncCarddavAccount(account) {
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
options.xhr = options.xhr || this.xhr;
return contacts.syncCarddavAccount(account, options);
}
}]);
return Client;
})();
exports.Client = Client;
},{"./accounts":1,"./calendars":2,"./contacts":5,"url":29}],5:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
exports.createCard = createCard;
exports.updateCard = updateCard;
exports.deleteCard = deleteCard;
exports.syncAddressBook = syncAddressBook;
function _interop