jsdav-ext
Version:
jsDAV allows you to easily add WebDAV support to a NodeJS application. jsDAV is meant to cover the entire standard, and attempts to allow integration using an easy to understand API.
507 lines (457 loc) • 15.1 kB
JavaScript
/*
* @package jsDAV
* @subpackage DAV
* @copyright Copyright(c) 2011 Ajax.org B.V. <info AT ajax DOT org>
* @author Mike de Boer <info AT mikedeboer DOT nl>
* @license http://github.com/mikedeboer/jsDAV/blob/master/LICENSE MIT License
*/
"use strict";
/**
* Main Exception class.
*
* This class defines a getHTTPCode method, which should return the appropriate
* HTTP code for the Exception occured.
* The default for this is 500.
*
* This class also allows you to generate custom xml data for your exceptions.
* This will be displayed in the 'error' element in the failing response.
*/
exports.jsDAV_Exception = function(msg, extra) {
/**
* The HTTP statuscode for this exception
*
* @return int
*/
this.code = 500;
/**
* The type of this exception, usually the same as the class name.
*/
this.type = "jsDAV_Exception";
/**
* The corresponding message for this exception
*
* @return int
*/
this.message = msg || this.type;
/**
* This method allows the exception to return any extra HTTP response headers.
*
* The headers must be returned as an array.
*
* @return array
*/
this.getHTTPHeaders = function(handler, cbheaders) { cbheaders(null, {}); };
/**
* This method allows the exception to include additonal information into the
* WebDAV error response
*
* @param {jsDAV_Handler} handler
* @param {DOMElement} errorNode
* @return void
*/
this.serialize = function(handler, errorNode) {return errorNode;};
this.toString = function(handler, cbtostring) {
var self = this;
var headers = "";
if (handler) {
headers = "Headers: ";
this.getHTTPHeaders(handler, function(err, h) {
for (var i in h)
headers += "\t" + i + ": " + h[i] + "\n";
afterHeaders();
});
}
else {
afterHeaders();
}
function afterHeaders() {
var msg = headers + "HTTP code: " + self.code + "\n"
+ "Exception type: " + self.type + "\n"
+ "Message: " + self.message;
return cbtostring ? cbtostring(null, msg) : msg;
}
};
};
require("util").inherits(exports.jsDAV_Exception, Error);
exports.notImplementedYet = function() {
return new exports.NotImplemented("This feature has not been implemented yet!");
};
/**
* BadRequest
*
* The BadRequest is thrown when the user submitted an invalid HTTP request
* BadRequest
*/
exports.BadRequest = function(msg, extra) {
this.code = 400;
this.type = "BadRequest";
this.message = msg || this.type;
};
exports.BadRequest.prototype = new exports.jsDAV_Exception();
/**
* Conflict
*
* A 409 Conflict is thrown when a user tried to make a directory over an existing
* file or in a parent directory that doesn't exist.
*/
exports.Conflict = function(msg, extra) {
this.code = 409;
this.type = "Conflict";
this.message = msg || this.type;
};
exports.Conflict.prototype = new exports.jsDAV_Exception();
/**
* This exception is thrown when a client attempts to set conflicting
* permissions.
*/
exports.AceConflict = function(msg, extra) {
this.msg = msg || this.msg;
/**
* Adds in extra information in the xml response.
*
* This method adds the {DAV:}no-ace-conflict element as defined in rfc3744
*
* @param jsDAV_Handler handler
* @param {String} errorNode
* @return void
*/
this.serialize = function(handler, errorNode) {
return errorNode + "<d:no-ace-conflict/>";
};
};
exports.AceConflict.prototype = new exports.Conflict();
/**
* Locked
*
* The 423 is thrown when a client tried to access a resource that was locked,
* without supplying a valid lock token
*/
exports.Locked = function(lock) {
this.code = 423;
this.type = this.message = "Locked";
this.lock = lock;
this.serialize = function(handler, errorNode) {
if (this.lock) {
return errorNode + "<d:lock-token-submitted><d:href>" + this.lock.uri
+ "</d:href></d:lock-token-submitted>";
}
return errorNode;
};
};
exports.Locked.prototype = new exports.jsDAV_Exception();
/**
* ConflictingLock
*
* Similar to Exception_Locked, this exception thrown when a LOCK request
* was made, on a resource which was already locked
*/
exports.ConflictingLock = function(lock) {
this.type = this.message = "ConflictingLock";
this.lock = lock;
this.serialize = function(handler, errorNode) {
if (this.lock) {
return errorNode + "<d:no-conflicting-lock><d:href>" + this.lock.uri
+ "</d:href></d:no-conflicting-lock>";
}
return errorNode;
};
};
exports.ConflictingLock.prototype = new exports.Locked();
/**
* FileNotFound
*
* This Exception is thrown when a Node couldn't be found. It returns HTTP error
* code 404
*/
exports.NotFound =
exports.FileNotFound = function(msg, extra) {
this.code = 404;
this.type = "FileNotFound";
this.message = msg || this.type;
};
exports.FileNotFound.prototype = new exports.jsDAV_Exception();
/**
* Forbidden
*
* This exception is thrown whenever a user tries to do an operation he's not
* allowed to
*/
exports.Forbidden = function(msg, extra) {
this.code = 403;
this.type = "Forbidden";
this.message = msg || this.type;
};
exports.Forbidden.prototype = new exports.jsDAV_Exception();
/**
* NeedPrivileges
*
* The 403-need privileges is thrown when a user didn't have the appropriate
* permissions to perform an operation
*/
exports.NeedPrivileges = function(uri, privileges) {
/**
* The relevant uri
*
* @var string
*/
this.uri = uri
/**
* The privileges the user didn't have.
*
* @var array
*/
this.privileges = privileges;
this.msg = "User did not have the required privileges (" + privileges.join("") + ") for path '" + uri + "'";
/**
* Adds in extra information in the xml response.
*
* This method adds the {DAV:}need-privileges element as defined in rfc3744
*
* @param jsDAV_Handler handler
* @param {String} errorNode
* @return void
*/
this.serialize = function(handler, errorNode) {
var out = ["<d:need-privileges>"];
var privilege, privilegeParts;
for (var i = 0, l = this.privileges.length; i < l; ++i) {
privilege = this.privileges[i];
privilegeParts = privilege.match(/^\{([^}]*)\}(.*)$/);
out.push("<d:resource><d:href>", handler.server.getBaseUri() + this.uri,
"</d:href><d:privilege><d:" + privilegeParts[2] + "/></d:privilege></d:resource>");
}
return errorNode + out.join("") + "</d:need-privileges>";
};
};
exports.NeedPrivileges.prototype = new exports.Forbidden();
/**
* InsufficientStorage
*
* This Exception can be thrown, when for example a harddisk is full or a quota
* is exceeded
*/
exports.InsufficientStorage = function(msg, extra) {
this.code = 507;
this.type = "InsufficientStorage";
this.message = msg || this.type;
};
exports.InsufficientStorage.prototype = new exports.jsDAV_Exception();
/**
* InvalidResourceType
*
* This exception is thrown when the user tried to create a new collection, with
* a special resourcetype value that was not recognized by the server.
*
* See RFC5689 section 3.3
*/
exports.InvalidResourceType = function(msg, extra) {
this.type = "InvalidResourceType";
this.message = msg || this.type;
this.serialize = function(handler, errorNode) {
return errorNode + "<d:valid-resourcetype />";
};
};
exports.InvalidResourceType.prototype = new exports.Forbidden();
/**
* LockTokenMatchesRequestUri
*
* This exception is thrown by UNLOCK if a supplied lock-token is invalid
*/
exports.LockTokenMatchesRequestUri = function() {
this.type = "LockTokenMatchesRequestUri";
this.message = "The locktoken supplied does not match any locks on this entity";
this.serialize = function(handler, errorNode) {
return errorNode + "<d:lock-token-matches-request-uri />";
};
};
exports.LockTokenMatchesRequestUri.prototype = new exports.Conflict();
/**
* MethodNotAllowed
*
* The 405 is thrown when a client tried to create a directory on an already existing directory
*/
exports.MethodNotAllowed = function(msg, extra) {
this.code = 405;
this.type = "MethodNotAllowed";
this.message = msg || this.type;
this.getHTTPHeaders = function(handler, cbmethods) {
handler.getAllowedMethods(handler.getRequestUri(), function(err, methods) {
cbmethods(err, {
"Allow" : methods.join(", ").toUpperCase()
});
});
};
};
exports.MethodNotAllowed.prototype = new exports.jsDAV_Exception();
/**
* NotAuthenticated
*
* This exception is thrown when the client did not provide valid
* authentication credentials.
*/
exports.NotAuthenticated = function(msg, extra) {
this.code = 401;
this.type = "NotAuthenticated";
this.message = msg || this.type;
this.headers = {};
this.addHeader = function(name, value) {
this.headers[name] = value;
};
this.getHTTPHeaders = function(handler, cbheaders) {
cbheaders(null, this.headers);
};
};
exports.NotAuthenticated.prototype = new exports.jsDAV_Exception();
/**
* NotImplemented
*
* This exception is thrown when the client tried to call an unsupported HTTP
* method or other feature
*/
exports.NotImplemented = function(msg, extra) {
this.code = 501;
this.type = "NotImplemented";
this.message = msg || this.type;
};
exports.NotImplemented.prototype = new exports.jsDAV_Exception();
/**
* Payment Required
*
* The PaymentRequired exception may be thrown in a case where a user must pay
* to access a certain resource or operation.
*/
exports.PaymentRequired = function(msg, extra) {
this.code = 402;
this.type = "PaymentRequired";
this.message = msg || this.type;
};
exports.PaymentRequired.prototype = new exports.jsDAV_Exception();
/**
* PreconditionFailed
*
* This exception is normally thrown when a client submitted a conditional request,
* like for example an If, If-None-Match or If-Match header, which caused the HTTP
* request to not execute (the condition of the header failed)
*/
exports.PreconditionFailed = function(msg, extra) {
this.code = 412;
this.type = "PreconditionFailed";
this.message = msg || this.type;
this.header = extra;
this.serialize = function(handler, errorNode) {
return this.header
? errorNode + "<a:header>" + this.header + "</a:header>"
: errorNode;
};
};
exports.PreconditionFailed.prototype = new exports.jsDAV_Exception();
/**
* This exception is thrown when a user tries to set a privilege that's marked
* as abstract.
*/
exports.NoAbstract = function(msg, extra) {
this.msg = msg || this.msg;
/**
* Adds in extra information in the xml response.
*
* This method adds the {DAV:}no-abstract element as defined in rfc3744
*
* @param jsDAV_Handler handler
* @param {String} errorNode
* @return void
*/
this.serialize = function(server, errorNode) {
return errorNode + "<d:no-abstract/>";
};
};
exports.NoAbstract.prototype = new exports.PreconditionFailed();
/**
* If a client tried to set a privilege assigned to a non-existant principal,
* this exception will be thrown.
*/
exports.NotRecognizedPrincipal = function(msg, extra) {
this.msg = msg || this.msg;
/**
* Adds in extra information in the xml response.
*
* This method adds the {DAV:}recognized-principal element as defined in rfc3744
*
* @param jsDAV_Handler handler
* @param {String} errorNode
* @return void
*/
this.serialize = function(handler, errorNode) {
return errorNode + "<d:recognized-principal/>";
};
};
exports.NotRecognizedPrincipal.prototype = new exports.PreconditionFailed();
/**
* If a client tried to set a privilege that doesn't exist, this exception will
* be thrown.
*/
exports.NotSupportedPrivilege = function(msg, extra) {
this.msg = msg || this.msg;
/**
* Adds in extra information in the xml response.
*
* This method adds the {DAV:}not-supported-privilege element as defined in rfc3744
*
* @param jsDAV_Handler handler
* @param {String} errorNode
* @return void
*/
this.serialize = function(handler, errorNode) {
return errorNode + "<d:not-supported-privilege/>";
};
};
exports.NotSupportedPrivilege.prototype = new exports.PreconditionFailed();
/**
* ReportNotImplemented
*
* This exception is thrown when the client requested an unknown report through
* the REPORT method
*/
exports.ReportNotImplemented = function(msg, extra) {
this.type = "ReportNotImplemented";
this.message = msg || this.type;
this.serialize = function(handler, errorNode) {
return errorNode + "<d:supported-report/>";
};
};
exports.ReportNotImplemented.prototype = new exports.NotImplemented();
/**
* RequestedRangeNotSatisfiable
*
* This exception is normally thrown when the user
* request a range that is out of the entity bounds.
*/
exports.RequestedRangeNotSatisfiable = function(msg, extra) {
this.code = 416;
this.type = "RequestedRangeNotSatisfiable";
this.message = msg || this.type;
};
exports.RequestedRangeNotSatisfiable.prototype = new exports.jsDAV_Exception();
/**
* ServiceUnavailable
*
* This exception is thrown in case the service
* is currently not available (e.g. down for maintenance).
*/
exports.ServiceUnavailable = function(msg, extra) {
this.code = 503;
this.type = "ServiceUnavailable";
this.message = msg || this.type;
};
exports.ServiceUnavailable.prototype = new exports.jsDAV_Exception();
/**
* UnSupportedMediaType
*
* The 415 Unsupported Media Type status code is generally sent back when the client
* tried to call an HTTP method, with a body the server didn't understand
*/
exports.UnsupportedMediaType = function(msg, extra) {
this.code = 415;
this.type = "UnsupportedMediaType";
this.message = msg || this.type;
};
exports.UnsupportedMediaType.prototype = new exports.jsDAV_Exception();