diff --git a/lib/helper/IfParser.d.ts b/lib/helper/IfParser.d.ts new file mode 100644 index 00000000..f92e7e2b --- /dev/null +++ b/lib/helper/IfParser.d.ts @@ -0,0 +1,4 @@ +import { IResource, ReturnCallback } from '../resource/IResource'; +import { MethodCallArgs } from '../server/MethodCallArgs'; +export declare function extractOneToken(ifHeader: string): string; +export declare function parseIfHeader(ifHeader: string): (arg: MethodCallArgs, r: IResource, callback: ReturnCallback) => void; diff --git a/lib/helper/IfParser.js b/lib/helper/IfParser.js new file mode 100644 index 00000000..8c6a8ca2 --- /dev/null +++ b/lib/helper/IfParser.js @@ -0,0 +1,128 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var IResource_1 = require("../resource/IResource"); +var url = require("url"); +function NoLock() { + return function (r, callback) { + r.getLocks(function (e, locks) { + callback(e, locks ? locks.length === 0 : false); + }); + }; +} +function Token(token) { + return function (r, callback) { + r.getLock(token, function (e, lock) { + callback(e, !!lock && !e); + }); + }; +} +function Tag(tag) { + return function (r, callback) { + r.lastModifiedDate(function (e, lastModifiedDate) { + callback(e, !e && IResource_1.ETag.createETag(lastModifiedDate) === tag); + }); + }; +} +function Not(filter) { + return function (r, callback) { + filter(r, function (e, v) { + callback(e, !v); + }); + }; +} +function parseInternal(group) { + var rex = /((not)|<([^>]+)>|\[([^\]]+)\]|<(DAV:no-lock)>)/ig; + var match = rex.exec(group); + var isNot = false; + var andArray = []; + function add(filter) { + andArray.push(isNot ? Not(filter) : filter); + isNot = false; + } + while (match) { + if (match[2]) { + isNot = true; + } + else if (match[3]) { + add(Token(match[3])); + } + else if (match[4]) { + add(Tag(match[4])); + } + else if (match[5]) { + add(NoLock()); + } + match = rex.exec(group); + } + if (andArray.length) + return function (r, callback) { return callback(null, true); }; + return function (r, callback) { + var nb = andArray.length; + function done(error, result) { + if (nb <= 0) + return; + if (error) { + nb = -1; + callback(error, false); + return; + } + --nb; + if (nb === 0 || !result) { + nb = -1; + callback(null, result); + } + } + andArray.forEach(function (a) { return a(r, done); }); + }; +} +function extractOneToken(ifHeader) { + var match = /^[ ]*\([ ]*<([^>]+)>[ ]*\)[ ]*$/.exec(ifHeader); + if (!match) + return null; + else + return match[1]; +} +exports.extractOneToken = extractOneToken; +function parseIfHeader(ifHeader) { + var rex = /(?:<([^>]+)>)?\s*\(([^\)]+)\)/g; + var match = rex.exec(ifHeader); + var orArray = []; + var oldPath = undefined; + while (match) { + if (match[1]) + oldPath = url.parse(match[1]).path; + orArray.push({ + path: oldPath, + actions: parseInternal(match[2]) + }); + match = rex.exec(ifHeader); + } + if (orArray.length) + return function (arg, r, callback) { return callback(null, true); }; + return function (arg, r, callback) { + var nb = orArray.length; + function done(error, result) { + if (nb <= 0) + return; + if (error) { + nb = -1; + callback(error, false); + return; + } + --nb; + if (nb === 0 || result) { + nb = -1; + callback(null, result); + } + } + orArray.forEach(function (a) { + if (!a.path) + a.actions(r, done); + else + arg.server.getResourceFromPath(a.path, function (e, resource) { + a.actions(resource, done); + }); + }); + }; +} +exports.parseIfHeader = parseIfHeader; diff --git a/src/helper/IfParser.ts b/src/helper/IfParser.ts new file mode 100644 index 00000000..bb0996e8 --- /dev/null +++ b/src/helper/IfParser.ts @@ -0,0 +1,167 @@ +import { IResource, ReturnCallback, ETag } from '../resource/IResource' +import { MethodCallArgs } from '../server/MethodCallArgs' +import { Errors } from '../Errors' +import * as url from 'url' + +type FnReturn = ReturnCallback + +function NoLock() +{ + return function(r : IResource, callback : FnReturn) { + r.getLocks((e, locks) => { + callback(e, locks ? locks.length === 0 : false); + }) + } +} + +function Token(token : string) +{ + return function(r : IResource, callback : FnReturn) { + r.getLock(token, (e, lock) => { + callback(e, !!lock && !e); + }) + } +} + +function Tag(tag : string) +{ + return function(r : IResource, callback : FnReturn) { + r.lastModifiedDate((e, lastModifiedDate) => { + callback(e, !e && ETag.createETag(lastModifiedDate) === tag); + }) + } +} + +function Not(filter) +{ + return function(r : IResource, callback : FnReturn) { + filter(r, (e, v) => { + callback(e, !v); + }) + } +} + +function parseInternal(group : string) +{ + const rex = /((not)|<([^>]+)>|\[([^\]]+)\]|<(DAV:no-lock)>)/ig; + let match = rex.exec(group); + + let isNot = false; + const andArray = []; + function add(filter) + { + andArray.push(isNot ? Not(filter) : filter); + isNot = false; + } + + while(match) + { + if(match[2]) + { // not + isNot = true; + } + else if(match[3]) + { // lock-token + add(Token(match[3])); + } + else if(match[4]) + { // tag + add(Tag(match[4])); + } + else if(match[5]) + { // DAV:no-lock + add(NoLock()); + } + match = rex.exec(group); + } + + if(andArray.length) + return (r, callback) => callback(null, true); + + return function(r : IResource, callback : FnReturn) { + let nb = andArray.length; + function done(error, result) + { + if(nb <= 0) + return; + if(error) + { + nb = -1; + callback(error, false); + return; + } + --nb; + if(nb === 0 || !result) + { + nb = -1; + callback(null, result); + } + } + + andArray.forEach((a) => a(r, done)); + }; +} + +export function extractOneToken(ifHeader : string) +{ + const match = /^[ ]*\([ ]*<([^>]+)>[ ]*\)[ ]*$/.exec(ifHeader); + if(!match) + return null; + else + return match[1]; +} + +export function parseIfHeader(ifHeader : string) +{ + const rex = /(?:<([^>]+)>)?\s*\(([^\)]+)\)/g; + let match = rex.exec(ifHeader); + + const orArray = []; + let oldPath = undefined; + + while(match) + { + if(match[1]) + oldPath = url.parse(match[1]).path; + + orArray.push({ + path: oldPath, + actions: parseInternal(match[2]) + }) + + match = rex.exec(ifHeader); + } + + if(orArray.length) + return (arg : MethodCallArgs, r : IResource, callback : ReturnCallback) => callback(null, true); + + return function(arg : MethodCallArgs, r : IResource, callback : ReturnCallback) { + let nb = orArray.length; + function done(error, result) + { + if(nb <= 0) + return; + if(error) + { + nb = -1; + callback(error, false); + return; + } + --nb; + if(nb === 0 || result) + { + nb = -1; + callback(null, result); + } + } + + orArray.forEach((a) => { + if(!a.path) + a.actions(r, done); + else + arg.server.getResourceFromPath(a.path, (e, resource) => { + a.actions(resource, done); + }); + }) + } +}