OpenMarshal/npm-WebDAV-Server

View on GitHub
src/helper/v2/IfParser.ts

Summary

Maintainability
A
1 hr
Test Coverage
import { RequestContext } from '../../server/v2/RequestContext'
import { ReturnCallback } from '../../index.v2';
import { Resource } from '../../manager/v2/fileSystem/Resource'
import { Path } from '../../manager/v2/Path'
import * as url from 'url'

type FnReturn = ReturnCallback<boolean>

function NoLock()
{
    return function(resource : Resource, callback : FnReturn) {
        resource.lockManager((e, lm) => {
            if(e)
                return callback(e, false);
            
            lm.getLocks((e, locks) => {
                callback(e, locks ? locks.length === 0 : false);
            })
        })
    }
}

function Token(token : string)
{
    return function(resource : Resource, callback : FnReturn) {
        resource.lockManager((e, lm) => {
            if(e)
                return callback(e, false);
            
            lm.getLock(token, (e, lock) => callback(e, !!lock && !e));
        })
    }
}

function Tag(tag : string)
{
    return function(resource : Resource, callback : FnReturn) {
        resource.etag((e, etag) => callback(e, !e && etag === tag));
    }
}

function Not(filter)
{
    return function(resource : Resource, callback : FnReturn) {
        filter(resource, (e, v) => {
            callback(e, !v);
        })
    }
}

function parseInternal(group : string)
{
    const rex = /((not)|\[([^\]]+)\]|<(DAV:no-lock)>|<([^>]+)>|([^\s]+))/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[5] || match[6])
        { // lock-token
            add(Token(match[5] || match[6]));
        }
        else if(match[3])
        { // tag
            add(Tag(match[3]));
        }
        else if(match[4])
        { // DAV:no-lock
            add(NoLock());
        }
        match = rex.exec(group);
    }

    if(andArray.length)
        return (r, callback) => callback(null, true);

    return function(resource : Resource, 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(resource, done));
    };
}

export function extractOneToken(ifHeader : string)
{
    const match = /^\s*\(\s*<?([^\)\s>]+)>?\s*\)\s*$/.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 : {
        path : string,
        actions : (resource : Resource, callback : FnReturn) => void
    }[] = [];
    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 === 0)
        return (ctx : RequestContext, resource : Resource, callback : ReturnCallback<boolean>) => callback(null, true);

    return function(ctx : RequestContext, resource : Resource, callback : ReturnCallback<boolean>) {
        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(resource, done);
            else
            {
                const sPath = new Path(a.path);
                ctx.server.getFileSystem(sPath, (fs, _, sub) => {
                    a.actions(fs.resource(ctx, sPath), done);
                })
            }
        })
    }
}