diff --git a/lib/manager/v2/fileSystem/FileSystem.d.ts b/lib/manager/v2/fileSystem/FileSystem.d.ts index 593eb7df..2186c2bc 100644 --- a/lib/manager/v2/fileSystem/FileSystem.d.ts +++ b/lib/manager/v2/fileSystem/FileSystem.d.ts @@ -1,8 +1,8 @@ /// -import { AvailableLocksInfo, CopyInfo, CreateInfo, CreationDateInfo, DeleteInfo, DisplayNameInfo, ETagInfo, LastModifiedDateInfo, LockManagerInfo, MimeTypeInfo, MoveInfo, OpenReadStreamInfo, OpenWriteStreamInfo, PropertyManagerInfo, ReadDirInfo, RenameInfo, SizeInfo, TypeInfo } from './ContextInfo'; +import { PrivilegeManagerInfo, AvailableLocksInfo, CopyInfo, CreateInfo, CreationDateInfo, DeleteInfo, DisplayNameInfo, ETagInfo, LastModifiedDateInfo, LockManagerInfo, MimeTypeInfo, MoveInfo, OpenReadStreamInfo, OpenWriteStreamInfo, PropertyManagerInfo, ReadDirInfo, RenameInfo, SizeInfo, TypeInfo } from './ContextInfo'; import { Readable, Writable } from 'stream'; import { RequestContext } from '../../../server/v2/RequestContext'; -import { BasicPrivilege } from '../../../user/v2/privilege/IPrivilegeManager'; +import { BasicPrivilege, PrivilegeManager } from '../../../user/v2/privilege/PrivilegeManager'; import { LockKind } from '../../../resource/lock/LockKind'; import { Lock } from '../../../resource/lock/Lock'; import { Path } from '../Path'; @@ -18,82 +18,92 @@ export declare abstract class FileSystem implements ISerializableFileSystem { serializer(): FileSystemSerializer; contextualize(ctx: RequestContext): ContextualFileSystem; resource(ctx: RequestContext, path: Path): Resource; - fastExistCheckEx(ctx: RequestContext, path: Path, errorCallback: SimpleCallback, callback: () => void): void; - fastExistCheckExReverse(ctx: RequestContext, path: Path, errorCallback: SimpleCallback, callback: () => void): void; - protected fastExistCheck(ctx: RequestContext, path: Path, callback: (exists: boolean) => void): void; + fastExistCheckEx(ctx: RequestContext, _path: Path | string, errorCallback: SimpleCallback, callback: () => void): void; + fastExistCheckExReverse(ctx: RequestContext, _path: Path | string, errorCallback: SimpleCallback, callback: () => void): void; + protected fastExistCheck(ctx: RequestContext, _path: Path | string, callback: (exists: boolean) => void): void; protected _fastExistCheck?(ctx: RequestContext, path: Path, callback: (exists: boolean) => void): void; - create(ctx: RequestContext, path: Path, type: ResourceType, callback: SimpleCallback): void; - create(ctx: RequestContext, path: Path, type: ResourceType, createIntermediates: boolean, callback: SimpleCallback): void; + create(ctx: RequestContext, path: Path | string, type: ResourceType, callback: SimpleCallback): void; + create(ctx: RequestContext, path: Path | string, type: ResourceType, createIntermediates: boolean, callback: SimpleCallback): void; protected _create?(path: Path, ctx: CreateInfo, callback: SimpleCallback): void; - etag(ctx: RequestContext, path: Path, callback: ReturnCallback): void; + etag(ctx: RequestContext, _path: Path | string, callback: ReturnCallback): void; protected _etag?(path: Path, ctx: ETagInfo, callback: ReturnCallback): void; - delete(ctx: RequestContext, path: Path, callback: SimpleCallback): void; - delete(ctx: RequestContext, path: Path, depth: number, callback: SimpleCallback): void; + delete(ctx: RequestContext, path: Path | string, callback: SimpleCallback): void; + delete(ctx: RequestContext, path: Path | string, depth: number, callback: SimpleCallback): void; protected _delete?(path: Path, ctx: DeleteInfo, callback: SimpleCallback): void; - openWriteStream(ctx: RequestContext, path: Path, callback: Return2Callback): void; - openWriteStream(ctx: RequestContext, path: Path, estimatedSize: number, callback: Return2Callback): void; - openWriteStream(ctx: RequestContext, path: Path, targetSource: boolean, callback: Return2Callback): void; - openWriteStream(ctx: RequestContext, path: Path, targetSource: boolean, estimatedSize: number, callback: Return2Callback): void; - openWriteStream(ctx: RequestContext, path: Path, mode: OpenWriteStreamMode, callback: Return2Callback): void; - openWriteStream(ctx: RequestContext, path: Path, mode: OpenWriteStreamMode, estimatedSize: number, callback: Return2Callback): void; - openWriteStream(ctx: RequestContext, path: Path, mode: OpenWriteStreamMode, targetSource: boolean, callback: Return2Callback): void; - openWriteStream(ctx: RequestContext, path: Path, mode: OpenWriteStreamMode, targetSource: boolean, estimatedSize: number, callback: Return2Callback): void; + openWriteStream(ctx: RequestContext, path: Path | string, callback: Return2Callback): void; + openWriteStream(ctx: RequestContext, path: Path | string, estimatedSize: number, callback: Return2Callback): void; + openWriteStream(ctx: RequestContext, path: Path | string, targetSource: boolean, callback: Return2Callback): void; + openWriteStream(ctx: RequestContext, path: Path | string, targetSource: boolean, estimatedSize: number, callback: Return2Callback): void; + openWriteStream(ctx: RequestContext, path: Path | string, mode: OpenWriteStreamMode, callback: Return2Callback): void; + openWriteStream(ctx: RequestContext, path: Path | string, mode: OpenWriteStreamMode, estimatedSize: number, callback: Return2Callback): void; + openWriteStream(ctx: RequestContext, path: Path | string, mode: OpenWriteStreamMode, targetSource: boolean, callback: Return2Callback): void; + openWriteStream(ctx: RequestContext, path: Path | string, mode: OpenWriteStreamMode, targetSource: boolean, estimatedSize: number, callback: Return2Callback): void; protected _openWriteStream?(path: Path, ctx: OpenWriteStreamInfo, callback: ReturnCallback): void; - openReadStream(ctx: RequestContext, path: Path, callback: ReturnCallback): void; - openReadStream(ctx: RequestContext, path: Path, estimatedSize: number, callback: ReturnCallback): void; - openReadStream(ctx: RequestContext, path: Path, targetSource: boolean, callback: ReturnCallback): void; - openReadStream(ctx: RequestContext, path: Path, targetSource: boolean, estimatedSize: number, callback: ReturnCallback): void; + openReadStream(ctx: RequestContext, path: Path | string, callback: ReturnCallback): void; + openReadStream(ctx: RequestContext, path: Path | string, estimatedSize: number, callback: ReturnCallback): void; + openReadStream(ctx: RequestContext, path: Path | string, targetSource: boolean, callback: ReturnCallback): void; + openReadStream(ctx: RequestContext, path: Path | string, targetSource: boolean, estimatedSize: number, callback: ReturnCallback): void; protected _openReadStream?(path: Path, ctx: OpenReadStreamInfo, callback: ReturnCallback): void; - move(ctx: RequestContext, pathFrom: Path, pathTo: Path, callback: ReturnCallback): void; - move(ctx: RequestContext, pathFrom: Path, pathTo: Path, overwrite: boolean, callback: ReturnCallback): void; + move(ctx: RequestContext, pathFrom: Path | string, pathTo: Path | string, callback: ReturnCallback): void; + move(ctx: RequestContext, pathFrom: Path | string, pathTo: Path | string, overwrite: boolean, callback: ReturnCallback): void; protected _move?(pathFrom: Path, pathTo: Path, ctx: MoveInfo, callback: ReturnCallback): void; - copy(ctx: RequestContext, pathFrom: Path, pathTo: Path, callback: ReturnCallback): void; - copy(ctx: RequestContext, pathFrom: Path, pathTo: Path, depth: number, callback: ReturnCallback): void; - copy(ctx: RequestContext, pathFrom: Path, pathTo: Path, overwrite: boolean, callback: ReturnCallback): void; - copy(ctx: RequestContext, pathFrom: Path, pathTo: Path, overwrite: boolean, depth: number, callback: ReturnCallback): void; + copy(ctx: RequestContext, pathFrom: Path | string, pathTo: Path | string, callback: ReturnCallback): void; + copy(ctx: RequestContext, pathFrom: Path | string, pathTo: Path | string, depth: number, callback: ReturnCallback): void; + copy(ctx: RequestContext, pathFrom: Path | string, pathTo: Path | string, overwrite: boolean, callback: ReturnCallback): void; + copy(ctx: RequestContext, pathFrom: Path | string, pathTo: Path | string, overwrite: boolean, depth: number, callback: ReturnCallback): void; protected _copy?(pathFrom: Path, pathTo: Path, ctx: CopyInfo, callback: ReturnCallback): void; - rename(ctx: RequestContext, pathFrom: Path, newName: string, callback: ReturnCallback): void; - rename(ctx: RequestContext, pathFrom: Path, newName: string, overwrite: boolean, callback: ReturnCallback): void; + rename(ctx: RequestContext, pathFrom: Path | string, newName: string, callback: ReturnCallback): void; + rename(ctx: RequestContext, pathFrom: Path | string, newName: string, overwrite: boolean, callback: ReturnCallback): void; protected _rename?(pathFrom: Path, newName: string, ctx: RenameInfo, callback: ReturnCallback): void; - mimeType(ctx: RequestContext, path: Path, callback: ReturnCallback): void; - mimeType(ctx: RequestContext, path: Path, targetSource: boolean, callback: ReturnCallback): void; + mimeType(ctx: RequestContext, path: Path | string, callback: ReturnCallback): void; + mimeType(ctx: RequestContext, path: Path | string, targetSource: boolean, callback: ReturnCallback): void; protected _mimeType?(path: Path, ctx: MimeTypeInfo, callback: ReturnCallback): void; - size(ctx: RequestContext, path: Path, callback: ReturnCallback): void; - size(ctx: RequestContext, path: Path, targetSource: boolean, callback: ReturnCallback): void; + size(ctx: RequestContext, path: Path | string, callback: ReturnCallback): void; + size(ctx: RequestContext, path: Path | string, targetSource: boolean, callback: ReturnCallback): void; protected _size?(path: Path, ctx: SizeInfo, callback: ReturnCallback): void; - availableLocks(ctx: RequestContext, path: Path, callback: ReturnCallback): void; + availableLocks(ctx: RequestContext, path: Path | string, callback: ReturnCallback): void; protected _availableLocks?(path: Path, ctx: AvailableLocksInfo, callback: ReturnCallback): void; - lockManager(ctx: RequestContext, path: Path, callback: ReturnCallback): void; + lockManager(ctx: RequestContext, path: Path | string, callback: ReturnCallback): void; protected abstract _lockManager(path: Path, ctx: LockManagerInfo, callback: ReturnCallback): void; - propertyManager(ctx: RequestContext, path: Path, callback: ReturnCallback): void; + propertyManager(ctx: RequestContext, path: Path | string, callback: ReturnCallback): void; protected abstract _propertyManager(path: Path, ctx: PropertyManagerInfo, callback: ReturnCallback): void; - readDir(ctx: RequestContext, path: Path, callback: ReturnCallback): void; - readDir(ctx: RequestContext, path: Path, retrieveExternalFiles: boolean, callback: ReturnCallback): void; + readDir(ctx: RequestContext, path: Path | string, callback: ReturnCallback): void; + readDir(ctx: RequestContext, path: Path | string, retrieveExternalFiles: boolean, callback: ReturnCallback): void; protected _readDir?(path: Path, ctx: ReadDirInfo, callback: ReturnCallback): void; - creationDate(ctx: RequestContext, path: Path, callback: ReturnCallback): void; + creationDate(ctx: RequestContext, path: Path | string, callback: ReturnCallback): void; protected _creationDate?(path: Path, ctx: CreationDateInfo, callback: ReturnCallback): void; - lastModifiedDate(ctx: RequestContext, path: Path, callback: ReturnCallback): void; + lastModifiedDate(ctx: RequestContext, path: Path | string, callback: ReturnCallback): void; protected _lastModifiedDate?(path: Path, ctx: LastModifiedDateInfo, callback: ReturnCallback): void; - webName(ctx: RequestContext, path: Path, callback: ReturnCallback): void; - displayName(ctx: RequestContext, path: Path, callback: ReturnCallback): void; + webName(ctx: RequestContext, path: Path | string, callback: ReturnCallback): void; + displayName(ctx: RequestContext, path: Path | string, callback: ReturnCallback): void; protected _displayName?(path: Path, ctx: DisplayNameInfo, callback: ReturnCallback): void; - type(ctx: RequestContext, path: Path, callback: ReturnCallback): void; + type(ctx: RequestContext, path: Path | string, callback: ReturnCallback): void; protected abstract _type(path: Path, ctx: TypeInfo, callback: ReturnCallback): void; addSubTree(ctx: RequestContext, subTree: SubTree, callback: SimpleCallback): any; addSubTree(ctx: RequestContext, resourceType: ResourceType, callback: SimpleCallback): any; - addSubTree(ctx: RequestContext, rootPath: Path, subTree: SubTree, callback: SimpleCallback): any; - addSubTree(ctx: RequestContext, rootPath: Path, resourceType: ResourceType, callback: SimpleCallback): any; - listDeepLocks(ctx: RequestContext, startPath: Path, callback: ReturnCallback<{ + addSubTree(ctx: RequestContext, rootPath: Path | string, subTree: SubTree, callback: SimpleCallback): any; + addSubTree(ctx: RequestContext, rootPath: Path | string, resourceType: ResourceType, callback: SimpleCallback): any; + listDeepLocks(ctx: RequestContext, startPath: Path | string, callback: ReturnCallback<{ [path: string]: Lock[]; }>): any; - listDeepLocks(ctx: RequestContext, startPath: Path, depth: number, callback: ReturnCallback<{ + listDeepLocks(ctx: RequestContext, startPath: Path | string, depth: number, callback: ReturnCallback<{ [path: string]: Lock[]; }>): any; getFullPath(ctx: RequestContext, callback: ReturnCallback): any; - getFullPath(ctx: RequestContext, path: Path, callback: ReturnCallback): any; - checkPrivilege(ctx: RequestContext, path: Path, privilege: BasicPrivilege, callback: ReturnCallback): any; - checkPrivilege(ctx: RequestContext, path: Path, privileges: BasicPrivilege[], callback: ReturnCallback): any; - checkPrivilege(ctx: RequestContext, path: Path, privilege: string, callback: ReturnCallback): any; - checkPrivilege(ctx: RequestContext, path: Path, privileges: string[], callback: ReturnCallback): any; + getFullPath(ctx: RequestContext, path: Path | string, callback: ReturnCallback): any; + localize(ctx: RequestContext, fullPath: Path, callback: ReturnCallback): any; + localize(ctx: RequestContext, fullPath: Path[], callback: ReturnCallback): any; + localize(ctx: RequestContext, fullPath: string, callback: ReturnCallback): any; + localize(ctx: RequestContext, fullPath: string[], callback: ReturnCallback): any; + localize(ctx: RequestContext, fullPath: (string | Path)[], callback: ReturnCallback): any; + checkPrivilege(ctx: RequestContext, path: Path | string, privilege: BasicPrivilege, callback: ReturnCallback): any; + checkPrivilege(ctx: RequestContext, path: Path | string, privileges: BasicPrivilege[], callback: ReturnCallback): any; + checkPrivilege(ctx: RequestContext, path: Path | string, privilege: string, callback: ReturnCallback): any; + checkPrivilege(ctx: RequestContext, path: Path | string, privileges: string[], callback: ReturnCallback): any; + checkPrivilege(ctx: RequestContext, path: Path | string, privileges: BasicPrivilege | BasicPrivilege[], callback: ReturnCallback): any; + checkPrivilege(ctx: RequestContext, path: Path | string, privileges: string | string[], callback: ReturnCallback): any; + privilegeManager(ctx: RequestContext, path: Path | string, callback: ReturnCallback): void; + protected _privilegeManager?(path: Path, info: PrivilegeManagerInfo, callback: ReturnCallback): any; + isLocked(ctx: RequestContext, path: Path | string, callback: ReturnCallback): void; serialize(callback: ReturnCallback): void; } diff --git a/lib/manager/v2/fileSystem/FileSystem.js b/lib/manager/v2/fileSystem/FileSystem.js index 0be393a4..66f9c2e7 100644 --- a/lib/manager/v2/fileSystem/FileSystem.js +++ b/lib/manager/v2/fileSystem/FileSystem.js @@ -11,6 +11,26 @@ var ContextualFileSystem_1 = require("./ContextualFileSystem"); var Resource_1 = require("./Resource"); var StandardMethods_1 = require("./StandardMethods"); var crypto = require("crypto"); +var BufferedIsLocked = (function () { + function BufferedIsLocked(fs, ctx, path) { + this.fs = fs; + this.ctx = ctx; + this.path = path; + this._isLocked = null; + } + BufferedIsLocked.prototype.isLocked = function (callback) { + var _this = this; + if (this._isLocked !== null) + return callback(null, this._isLocked); + this.fs.isLocked(this.ctx, this.path, function (e, locked) { + if (e) + return callback(e); + _this._isLocked = locked; + callback(null, locked); + }); + }; + return BufferedIsLocked; +}()); var FileSystem = (function () { function FileSystem(serializer) { this.__serializer = serializer; @@ -24,9 +44,10 @@ var FileSystem = (function () { FileSystem.prototype.resource = function (ctx, path) { return new Resource_1.Resource(path, this, ctx); }; - FileSystem.prototype.fastExistCheckEx = function (ctx, path, errorCallback, callback) { + FileSystem.prototype.fastExistCheckEx = function (ctx, _path, errorCallback, callback) { if (!this._fastExistCheck) return callback(); + var path = new Path_1.Path(_path); this._fastExistCheck(ctx, path, function (exists) { if (!exists) errorCallback(Errors_1.Errors.ResourceNotFound); @@ -34,9 +55,10 @@ var FileSystem = (function () { callback(); }); }; - FileSystem.prototype.fastExistCheckExReverse = function (ctx, path, errorCallback, callback) { + FileSystem.prototype.fastExistCheckExReverse = function (ctx, _path, errorCallback, callback) { if (!this._fastExistCheck) return callback(); + var path = new Path_1.Path(_path); this._fastExistCheck(ctx, path, function (exists) { if (exists) errorCallback(Errors_1.Errors.ResourceAlreadyExists); @@ -44,80 +66,98 @@ var FileSystem = (function () { callback(); }); }; - FileSystem.prototype.fastExistCheck = function (ctx, path, callback) { + FileSystem.prototype.fastExistCheck = function (ctx, _path, callback) { if (!this._fastExistCheck) return callback(true); + var path = new Path_1.Path(_path); this._fastExistCheck(ctx, path, function (exists) { return callback(!!exists); }); }; - FileSystem.prototype.create = function (ctx, path, type, _createIntermediates, _callback) { + FileSystem.prototype.create = function (ctx, _path, type, _createIntermediates, _callback) { var _this = this; var createIntermediates = _callback ? _createIntermediates : false; var callback = _callback ? _callback : _createIntermediates; + var path = new Path_1.Path(_path); if (!this._create) return callback(Errors_1.Errors.InvalidOperation); - var go = function () { - _this._create(path, { - context: ctx, - type: type - }, callback); - }; - this.fastExistCheckExReverse(ctx, path, callback, function () { - _this.type(ctx, path.getParent(), function (e, type) { - if (e === Errors_1.Errors.ResourceNotFound) { - if (!createIntermediates) - return callback(Errors_1.Errors.IntermediateResourceMissing); - _this.getFullPath(ctx, path, function (e, fullPath) { - if (e) - return callback(e); - fullPath = fullPath.getParent(); - ctx.getResource(fullPath, function (e, r) { - if (e) - return callback(e); - r.create(CommonTypes_1.ResourceType.Directory, function (e) { - if (e && e !== Errors_1.Errors.ResourceAlreadyExists) + issuePrivilegeCheck(this, ctx, path, 'canWrite', callback, function () { + var go = function () { + _this._create(path, { + context: ctx, + type: type + }, callback); + }; + _this.isLocked(ctx, path, function (e, locked) { + if (e || locked) + return callback(locked ? Errors_1.Errors.Locked : e); + _this.fastExistCheckExReverse(ctx, path, callback, function () { + _this.type(ctx, path.getParent(), function (e, type) { + if (e === Errors_1.Errors.ResourceNotFound) { + if (!createIntermediates) + return callback(Errors_1.Errors.IntermediateResourceMissing); + _this.getFullPath(ctx, path, function (e, fullPath) { + if (e) return callback(e); - go(); + fullPath = fullPath.getParent(); + ctx.getResource(fullPath, function (e, r) { + if (e) + return callback(e); + r.create(CommonTypes_1.ResourceType.Directory, function (e) { + if (e && e !== Errors_1.Errors.ResourceAlreadyExists) + return callback(e); + go(); + }); + }); }); - }); + return; + } + if (e) + return callback(e); + if (!type.isDirectory) + return callback(Errors_1.Errors.WrongParentTypeForCreation); + go(); }); - return; - } - if (e) - return callback(e); - if (!type.isDirectory) - return callback(Errors_1.Errors.WrongParentTypeForCreation); - go(); + }); }); }); }; - FileSystem.prototype.etag = function (ctx, path, callback) { + FileSystem.prototype.etag = function (ctx, _path, callback) { var _this = this; - this.fastExistCheckEx(ctx, path, callback, function () { - if (!_this._etag) - return _this.lastModifiedDate(ctx, path, function (e, date) { - if (e) - return callback(e); - callback(null, '"' + crypto.createHash('md5').update(date.toString()).digest('hex') + '"'); - }); - _this._etag(path, { - context: ctx - }, callback); + var path = new Path_1.Path(_path); + issuePrivilegeCheck(this, ctx, path, 'canReadProperties', callback, function () { + _this.fastExistCheckEx(ctx, path, callback, function () { + if (!_this._etag) + return _this.lastModifiedDate(ctx, path, function (e, date) { + if (e) + return callback(e); + callback(null, '"' + crypto.createHash('md5').update(date.toString()).digest('hex') + '"'); + }); + _this._etag(path, { + context: ctx + }, callback); + }); }); }; - FileSystem.prototype.delete = function (ctx, path, _depth, _callback) { + FileSystem.prototype.delete = function (ctx, _path, _depth, _callback) { var _this = this; var depth = _callback ? _depth : -1; var callback = _callback ? _callback : _depth; + var path = new Path_1.Path(_path); if (!this._delete) return callback(Errors_1.Errors.InvalidOperation); - this.fastExistCheckEx(ctx, path, callback, function () { - _this._delete(path, { - context: ctx, - depth: depth - }, callback); + issuePrivilegeCheck(this, ctx, path, 'canWrite', callback, function () { + _this.isLocked(ctx, path, function (e, isLocked) { + if (e || isLocked) + return callback(e ? e : Errors_1.Errors.Locked); + _this.fastExistCheckEx(ctx, path, callback, function () { + _this._delete(path, { + context: ctx, + depth: depth + }, callback); + }); + }); }); }; - FileSystem.prototype.openWriteStream = function (ctx, path, _mode, _targetSource, _estimatedSize, _callback) { + FileSystem.prototype.openWriteStream = function (ctx, _path, _mode, _targetSource, _estimatedSize, _callback) { var _this = this; var targetSource = true; for (var _i = 0, _a = [_mode, _targetSource]; _i < _a.length; _i++) { @@ -138,306 +178,491 @@ var FileSystem = (function () { callback = obj; } var mode = _mode && _mode.constructor === String ? _mode : 'mustExist'; + var path = new Path_1.Path(_path); var created = false; if (!this._openWriteStream) return callback(Errors_1.Errors.InvalidOperation); - var go = function (callback) { - _this._openWriteStream(path, { - context: ctx, - estimatedSize: estimatedSize, - targetSource: targetSource, - mode: mode - }, function (e, wStream) { return callback(e, wStream, created); }); - }; - var createAndGo = function (intermediates) { - _this.create(ctx, path, CommonTypes_1.ResourceType.File, intermediates, function (e) { - if (e) - return callback(e); - created = true; - go(callback); + issuePrivilegeCheck(this, ctx, path, targetSource ? 'canWriteContentSource' : 'canWriteContentTranslated', callback, function () { + _this.isLocked(ctx, path, function (e, isLocked) { + if (e || isLocked) + return callback(e ? e : Errors_1.Errors.Locked); + var go = function (callback) { + _this._openWriteStream(path, { + context: ctx, + estimatedSize: estimatedSize, + targetSource: targetSource, + mode: mode + }, function (e, wStream) { return callback(e, wStream, created); }); + }; + var createAndGo = function (intermediates) { + _this.create(ctx, path, CommonTypes_1.ResourceType.File, intermediates, function (e) { + if (e) + return callback(e); + created = true; + go(callback); + }); + }; + switch (mode) { + case 'mustExist': + _this.fastExistCheckEx(ctx, path, callback, function () { return go(callback); }); + break; + case 'mustCreateIntermediates': + case 'mustCreate': + createAndGo(mode === 'mustCreateIntermediates'); + break; + case 'canCreateIntermediates': + case 'canCreate': + go(function (e, wStream) { + if (e === Errors_1.Errors.ResourceNotFound) + createAndGo(mode === 'canCreateIntermediates'); + else + callback(e, wStream); + }); + break; + default: + callback(Errors_1.Errors.IllegalArguments); + break; + } }); - }; - switch (mode) { - case 'mustExist': - this.fastExistCheckEx(ctx, path, callback, function () { return go(callback); }); - break; - case 'mustCreateIntermediates': - case 'mustCreate': - createAndGo(mode === 'mustCreateIntermediates'); - break; - case 'canCreateIntermediates': - case 'canCreate': - go(function (e, wStream) { - if (e === Errors_1.Errors.ResourceNotFound) - createAndGo(mode === 'canCreateIntermediates'); - else - callback(e, wStream); - }); - break; - default: - callback(Errors_1.Errors.IllegalArguments); - break; - } + }); }; - FileSystem.prototype.openReadStream = function (ctx, path, _targetSource, _estimatedSize, _callback) { + FileSystem.prototype.openReadStream = function (ctx, _path, _targetSource, _estimatedSize, _callback) { var _this = this; var targetSource = _targetSource.constructor === Boolean ? _targetSource : true; var estimatedSize = _callback ? _estimatedSize : _estimatedSize ? _targetSource : -1; var callback = _callback ? _callback : _estimatedSize ? _estimatedSize : _targetSource; - this.fastExistCheckEx(ctx, path, callback, function () { - if (!_this._openReadStream) - return callback(Errors_1.Errors.InvalidOperation); - _this._openReadStream(path, { - context: ctx, - estimatedSize: estimatedSize, - targetSource: targetSource - }, callback); + var path = new Path_1.Path(_path); + issuePrivilegeCheck(this, ctx, path, targetSource ? 'canReadContentSource' : 'canReadContentTranslated', callback, function () { + _this.fastExistCheckEx(ctx, path, callback, function () { + if (!_this._openReadStream) + return callback(Errors_1.Errors.InvalidOperation); + _this._openReadStream(path, { + context: ctx, + estimatedSize: estimatedSize, + targetSource: targetSource + }, callback); + }); }); }; - FileSystem.prototype.move = function (ctx, pathFrom, pathTo, _overwrite, _callback) { + FileSystem.prototype.move = function (ctx, _pathFrom, _pathTo, _overwrite, _callback) { var _this = this; var callback = _callback ? _callback : _overwrite; var overwrite = _callback ? _overwrite : false; - var go = function () { - if (_this._move) { - _this._move(pathFrom, pathTo, { - context: ctx, - overwrite: overwrite - }, callback); - return; - } - StandardMethods_1.StandardMethods.standardMove(ctx, pathFrom, _this, pathTo, _this, callback); - }; - this.fastExistCheckEx(ctx, pathFrom, callback, function () { - if (!overwrite) - _this.fastExistCheckExReverse(ctx, pathTo, callback, go); - else - go(); + var pathFrom = new Path_1.Path(_pathFrom); + var pathTo = new Path_1.Path(_pathTo); + issuePrivilegeCheck(this, ctx, pathFrom, 'canRead', callback, function () { + issuePrivilegeCheck(_this, ctx, pathTo, 'canWrite', callback, function () { + _this.isLocked(ctx, pathFrom, function (e, isLocked) { + if (e || isLocked) + return callback(e ? e : Errors_1.Errors.Locked); + _this.isLocked(ctx, pathTo, function (e, isLocked) { + if (e || isLocked) + return callback(e ? e : Errors_1.Errors.Locked); + var go = function () { + if (_this._move) { + _this._move(pathFrom, pathTo, { + context: ctx, + overwrite: overwrite + }, callback); + return; + } + StandardMethods_1.StandardMethods.standardMove(ctx, pathFrom, _this, pathTo, _this, callback); + }; + _this.fastExistCheckEx(ctx, pathFrom, callback, function () { + if (!overwrite) + _this.fastExistCheckExReverse(ctx, pathTo, callback, go); + else + go(); + }); + }); + }); + }); }); }; - FileSystem.prototype.copy = function (ctx, pathFrom, pathTo, _overwrite, _depth, _callback) { + FileSystem.prototype.copy = function (ctx, _pathFrom, _pathTo, _overwrite, _depth, _callback) { var _this = this; var overwrite = _overwrite.constructor === Boolean ? _overwrite : false; var depth = _callback ? _depth : !_depth ? -1 : _overwrite.constructor === Number ? _overwrite : -1; var callback = _callback ? _callback : _depth ? _depth : _overwrite; - if (this._copy) { - var go_1 = function () { - _this._copy(pathFrom, pathTo, { - context: ctx, - depth: depth, - overwrite: overwrite - }, callback); - }; - this.fastExistCheckEx(ctx, pathFrom, callback, function () { - if (!overwrite) - _this.fastExistCheckExReverse(ctx, pathTo, callback, go_1); - else - go_1(); + var pathFrom = new Path_1.Path(_pathFrom); + var pathTo = new Path_1.Path(_pathTo); + issuePrivilegeCheck(this, ctx, pathFrom, 'canRead', callback, function () { + issuePrivilegeCheck(_this, ctx, pathTo, 'canWrite', callback, function () { + _this.isLocked(ctx, pathTo, function (e, isLocked) { + if (e || isLocked) + return callback(e ? e : Errors_1.Errors.Locked); + if (_this._copy) { + var go_1 = function () { + _this._copy(pathFrom, pathTo, { + context: ctx, + depth: depth, + overwrite: overwrite + }, callback); + }; + _this.fastExistCheckEx(ctx, pathFrom, callback, function () { + if (!overwrite) + _this.fastExistCheckExReverse(ctx, pathTo, callback, go_1); + else + go_1(); + }); + } + else + StandardMethods_1.StandardMethods.standardCopy(ctx, pathFrom, _this, pathTo, _this, overwrite, depth, callback); + }); }); - } - else - StandardMethods_1.StandardMethods.standardCopy(ctx, pathFrom, this, pathTo, this, overwrite, depth, callback); + }); }; - FileSystem.prototype.rename = function (ctx, pathFrom, newName, _overwrite, _callback) { + FileSystem.prototype.rename = function (ctx, _pathFrom, newName, _overwrite, _callback) { var _this = this; var overwrite = _callback ? _overwrite : false; var callback = _callback ? _callback : _overwrite; - if (pathFrom.isRoot()) { - this.getFullPath(ctx, function (e, fullPath) { - if (fullPath.isRoot()) - return callback(Errors_1.Errors.InvalidOperation); - var newPath = fullPath.getParent().getChildPath(newName); - ctx.server.getFileSystem(newPath, function (fs, _, subPath) { - var go = function (overwritten) { - ctx.server.setFileSystem(newPath, _this, function (successed) { - if (!successed) - return callback(Errors_1.Errors.InvalidOperation); - ctx.server.removeFileSystem(fullPath, function () { return callback(null, overwritten); }); + var pathFrom = new Path_1.Path(_pathFrom); + issuePrivilegeCheck(this, ctx, pathFrom, ['canRead', 'canWrite'], callback, function () { + _this.isLocked(ctx, pathFrom, function (e, isLocked) { + if (e || isLocked) + return callback(e ? e : Errors_1.Errors.Locked); + if (pathFrom.isRoot()) { + _this.getFullPath(ctx, function (e, fullPath) { + if (fullPath.isRoot()) + return callback(Errors_1.Errors.InvalidOperation); + var newPath = fullPath.getParent().getChildPath(newName); + issuePrivilegeCheck(_this, ctx, newPath, 'canWrite', callback, function () { + ctx.server.getFileSystem(newPath, function (fs, _, subPath) { + var go = function (overwritten) { + ctx.server.setFileSystem(newPath, _this, function (successed) { + if (!successed) + return callback(Errors_1.Errors.InvalidOperation); + ctx.server.removeFileSystem(fullPath, function () { return callback(null, overwritten); }); + }); + }; + if (!subPath.isRoot()) + return go(false); + if (!overwrite) + return callback(Errors_1.Errors.ResourceAlreadyExists); + ctx.server.removeFileSystem(newPath, function () { + go(true); + }); + }); }); - }; - if (!subPath.isRoot()) - return go(false); - if (!overwrite) - return callback(Errors_1.Errors.ResourceAlreadyExists); - ctx.server.removeFileSystem(newPath, function () { - go(true); }); - }); - }); - return; - } - this.fastExistCheckEx(ctx, pathFrom, callback, function () { - _this.fastExistCheckExReverse(ctx, pathFrom.getParent().getChildPath(newName), callback, function () { - if (_this._rename) { - _this._rename(pathFrom, newName, { - context: ctx - }, callback); return; } - _this.move(ctx, pathFrom, pathFrom.getParent().getChildPath(newName), overwrite, callback); + _this.fastExistCheckEx(ctx, pathFrom, callback, function () { + _this.fastExistCheckExReverse(ctx, pathFrom.getParent().getChildPath(newName), callback, function () { + var newPath = pathFrom.getParent().getChildPath(newName); + _this.isLocked(ctx, newPath, function (e, isLocked) { + if (e || isLocked) + return callback(e ? e : Errors_1.Errors.Locked); + issuePrivilegeCheck(_this, ctx, newPath, 'canWrite', callback, function () { + if (_this._rename) { + _this._rename(pathFrom, newName, { + context: ctx, + destinationPath: newPath + }, callback); + return; + } + }); + _this.move(ctx, pathFrom, pathFrom.getParent().getChildPath(newName), overwrite, callback); + }); + }); + }); }); }); }; - FileSystem.prototype.mimeType = function (ctx, path, _targetSource, _callback) { + FileSystem.prototype.mimeType = function (ctx, _path, _targetSource, _callback) { var _this = this; var targetSource = _callback ? _targetSource : true; var callback = _callback ? _callback : _targetSource; - this.fastExistCheckEx(ctx, path, callback, function () { - if (_this._mimeType) { - _this._mimeType(path, { - context: ctx, - targetSource: targetSource - }, callback); - return; - } - StandardMethods_1.StandardMethods.standardMimeType(ctx, _this, path, targetSource, callback); + var path = new Path_1.Path(_path); + issuePrivilegeCheck(this, ctx, path, targetSource ? 'canReadContentSource' : 'canReadContentTranslated', callback, function () { + _this.fastExistCheckEx(ctx, path, callback, function () { + if (_this._mimeType) { + _this._mimeType(path, { + context: ctx, + targetSource: targetSource + }, callback); + return; + } + StandardMethods_1.StandardMethods.standardMimeType(ctx, _this, path, targetSource, callback); + }); }); }; FileSystem.prototype.size = function (ctx, path, _targetSource, _callback) { var _this = this; var targetSource = _callback ? _targetSource : true; var callback = _callback ? _callback : _targetSource; - this.fastExistCheckEx(ctx, path, callback, function () { - if (!_this._size) - return callback(null, 0); - _this._size(path, { - context: ctx, - targetSource: targetSource - }, callback); + var pPath = new Path_1.Path(path); + issuePrivilegeCheck(this, ctx, pPath, targetSource ? 'canReadContentSource' : 'canReadContentTranslated', callback, function () { + _this.fastExistCheckEx(ctx, pPath, callback, function () { + if (!_this._size) + return callback(null, 0); + _this._size(pPath, { + context: ctx, + targetSource: targetSource + }, callback); + }); }); }; FileSystem.prototype.availableLocks = function (ctx, path, callback) { var _this = this; - this.fastExistCheckEx(ctx, path, callback, function () { - if (!_this._availableLocks) - return callback(null, [ - new LockKind_1.LockKind(LockScope_1.LockScope.Exclusive, LockType_1.LockType.Write), - new LockKind_1.LockKind(LockScope_1.LockScope.Shared, LockType_1.LockType.Write) - ]); - _this._availableLocks(path, { - context: ctx - }, callback); + var pPath = new Path_1.Path(path); + issuePrivilegeCheck(this, ctx, pPath, 'canWriteLocks', callback, function () { + _this.fastExistCheckEx(ctx, pPath, callback, function () { + if (!_this._availableLocks) + return callback(null, [ + new LockKind_1.LockKind(LockScope_1.LockScope.Exclusive, LockType_1.LockType.Write), + new LockKind_1.LockKind(LockScope_1.LockScope.Shared, LockType_1.LockType.Write) + ]); + _this._availableLocks(pPath, { + context: ctx + }, callback); + }); }); }; FileSystem.prototype.lockManager = function (ctx, path, callback) { var _this = this; - this.fastExistCheckEx(ctx, path, callback, function () { - _this._lockManager(path, { + var pPath = new Path_1.Path(path); + this.fastExistCheckEx(ctx, pPath, callback, function () { + _this._lockManager(pPath, { context: ctx - }, callback); + }, function (e, lm) { + if (e) + return callback(e); + var buffIsLocked = new BufferedIsLocked(_this, ctx, pPath); + var fs = _this; + callback(null, { + getLocks: function (callback) { + issuePrivilegeCheck(fs, ctx, pPath, 'canReadLocks', callback, function () { + lm.getLocks(callback); + }); + }, + setLock: function (lock, callback) { + issuePrivilegeCheck(fs, ctx, pPath, 'canWriteLocks', callback, function () { + buffIsLocked.isLocked(function (e, isLocked) { + if (e || isLocked) + return callback(e ? e : Errors_1.Errors.Locked); + lm.setLock(lock, callback); + }); + }); + }, + removeLock: function (uuid, callback) { + issuePrivilegeCheck(fs, ctx, pPath, 'canWriteLocks', callback, function () { + buffIsLocked.isLocked(function (e, isLocked) { + if (e || isLocked) + return callback(e ? e : Errors_1.Errors.Locked); + lm.removeLock(uuid, callback); + }); + }); + }, + getLock: function (uuid, callback) { + issuePrivilegeCheck(fs, ctx, pPath, 'canReadLocks', callback, function () { + lm.getLock(uuid, callback); + }); + }, + refresh: function (uuid, timeout, callback) { + issuePrivilegeCheck(fs, ctx, pPath, 'canWriteLocks', callback, function () { + buffIsLocked.isLocked(function (e, isLocked) { + if (e || isLocked) + return callback(e ? e : Errors_1.Errors.Locked); + lm.refresh(uuid, timeout, callback); + }); + }); + } + }); + }); }); }; FileSystem.prototype.propertyManager = function (ctx, path, callback) { var _this = this; - this.fastExistCheckEx(ctx, path, callback, function () { - _this._propertyManager(path, { + var pPath = new Path_1.Path(path); + this.fastExistCheckEx(ctx, pPath, callback, function () { + _this._propertyManager(pPath, { context: ctx - }, callback); + }, function (e, pm) { + if (e) + return callback(e); + var buffIsLocked = new BufferedIsLocked(_this, ctx, pPath); + var fs = _this; + callback(null, { + setProperty: function (name, value, callback) { + issuePrivilegeCheck(fs, ctx, pPath, 'canWriteProperties', callback, function () { + buffIsLocked.isLocked(function (e, isLocked) { + if (e || isLocked) + return callback(e ? e : Errors_1.Errors.Locked); + pm.setProperty(name, value, callback); + }); + }); + }, + getProperty: function (name, callback) { + issuePrivilegeCheck(fs, ctx, pPath, 'canReadProperties', callback, function () { + pm.getProperty(name, callback); + }); + }, + removeProperty: function (name, callback) { + issuePrivilegeCheck(fs, ctx, pPath, 'canWriteProperties', callback, function () { + buffIsLocked.isLocked(function (e, isLocked) { + if (e || isLocked) + return callback(e ? e : Errors_1.Errors.Locked); + pm.removeProperty(name, callback); + }); + }); + }, + getProperties: function (callback, byCopy) { + issuePrivilegeCheck(fs, ctx, pPath, 'canReadProperties', callback, function () { + pm.getProperties(callback, byCopy); + }); + } + }); + }); }); }; FileSystem.prototype.readDir = function (ctx, path, _retrieveExternalFiles, _callback) { var _this = this; var retrieveExternalFiles = _callback ? _retrieveExternalFiles : false; - var callback = _callback ? _callback : _retrieveExternalFiles; - this.fastExistCheckEx(ctx, path, callback, function () { - var next = function (base) { - if (!_this._readDir) - return callback(null, base); - _this._readDir(path, { - context: ctx - }, function (e, paths) { + var __callback = _callback ? _callback : _retrieveExternalFiles; + var pPath = new Path_1.Path(path); + var callback = function (e, data) { + if (e) + return _callback(e); + if (!data) + data = []; + _this.getFullPath(ctx, function (e, fsFullPath) { + new Workflow_1.Workflow() + .each(data, function (path, cb) { + _this.checkPrivilege(ctx, path, 'canReadProperties', function (e, can) { + if (e) + cb(e); + else + cb(null, can ? path : null); + }); + }) + .error(function (e) { return __callback(e); }) + .done(function () { return __callback(null, data.filter(function (p) { return !!p; }).map(function (p) { return p.fileName(); })); }); + }); + }; + issuePrivilegeCheck(this, ctx, pPath, 'canReadProperties', callback, function () { + _this.fastExistCheckEx(ctx, pPath, callback, function () { + var next = function (base) { + if (!_this._readDir) + return callback(null, base); + _this._readDir(pPath, { + context: ctx + }, function (e, paths) { + if (e) + return callback(e); + if (paths.length === 0) + return callback(null, base); + if (paths[0].constructor === String) + base = base.concat(paths.map(function (s) { return pPath.getChildPath(s); })); + else + base = base.concat(paths); + callback(null, base); + }); + }; + if (!retrieveExternalFiles) + return next([]); + _this.getFullPath(ctx, function (e, thisFullPath) { if (e) return callback(e); - if (paths.length === 0) - return callback(null, base); - if (paths[0].constructor === String) - base = base.concat(paths); - else - base = base.concat(paths.map(function (p) { return p.fileName(); })); - callback(null, base); - }); - }; - if (!retrieveExternalFiles) - return next([]); - _this.getFullPath(ctx, function (e, thisFullPath) { - if (e) - return callback(e); - ctx.server.getChildFileSystems(thisFullPath.getChildPath(path), function (fss) { - next(fss.map(function (f) { return f.path.fileName(); })); + ctx.server.getChildFileSystems(thisFullPath.getChildPath(pPath), function (fss) { + _this.localize(ctx, fss.map(function (f) { return f.path; }), function (e, paths) { + if (e) + return callback(e); + next(paths); + }); + }); }); }); }); }; FileSystem.prototype.creationDate = function (ctx, path, callback) { var _this = this; - this.fastExistCheckEx(ctx, path, callback, function () { - if (!_this._creationDate && !_this._lastModifiedDate) - return callback(null, 0); - if (!_this._creationDate) - return _this.lastModifiedDate(ctx, path, callback); - _this._creationDate(path, { - context: ctx - }, callback); + var pPath = new Path_1.Path(path); + issuePrivilegeCheck(this, ctx, pPath, 'canReadProperties', callback, function () { + _this.fastExistCheckEx(ctx, pPath, callback, function () { + if (!_this._creationDate && !_this._lastModifiedDate) + return callback(null, 0); + if (!_this._creationDate) + return _this.lastModifiedDate(ctx, pPath, callback); + _this._creationDate(pPath, { + context: ctx + }, callback); + }); }); }; FileSystem.prototype.lastModifiedDate = function (ctx, path, callback) { var _this = this; - this.fastExistCheckEx(ctx, path, callback, function () { - if (!_this._creationDate && !_this._lastModifiedDate) - return callback(null, 0); - if (!_this._lastModifiedDate) - return _this.creationDate(ctx, path, callback); - _this._lastModifiedDate(path, { - context: ctx - }, callback); + var pPath = new Path_1.Path(path); + issuePrivilegeCheck(this, ctx, pPath, 'canReadProperties', callback, function () { + _this.fastExistCheckEx(ctx, pPath, callback, function () { + if (!_this._creationDate && !_this._lastModifiedDate) + return callback(null, 0); + if (!_this._lastModifiedDate) + return _this.creationDate(ctx, pPath, callback); + _this._lastModifiedDate(pPath, { + context: ctx + }, callback); + }); }); }; FileSystem.prototype.webName = function (ctx, path, callback) { var _this = this; - this.fastExistCheckEx(ctx, path, callback, function () { - if (path.isRoot()) - _this.getFullPath(ctx, function (e, path) { return callback(e, e ? null : path.fileName()); }); - else - callback(null, path.fileName()); + var pPath = new Path_1.Path(path); + issuePrivilegeCheck(this, ctx, pPath, 'canReadProperties', callback, function () { + _this.fastExistCheckEx(ctx, pPath, callback, function () { + if (pPath.isRoot()) + _this.getFullPath(ctx, function (e, pPath) { return callback(e, e ? null : pPath.fileName()); }); + else + callback(null, pPath.fileName()); + }); }); }; FileSystem.prototype.displayName = function (ctx, path, callback) { var _this = this; - this.fastExistCheckEx(ctx, path, callback, function () { - if (!_this._displayName) - return _this.webName(ctx, path, callback); - _this._displayName(path, { - context: ctx - }, callback); + var pPath = new Path_1.Path(path); + issuePrivilegeCheck(this, ctx, pPath, 'canReadProperties', callback, function () { + _this.fastExistCheckEx(ctx, pPath, callback, function () { + if (!_this._displayName) + return _this.webName(ctx, pPath, callback); + _this._displayName(pPath, { + context: ctx + }, callback); + }); }); }; FileSystem.prototype.type = function (ctx, path, callback) { var _this = this; - this.fastExistCheckEx(ctx, path, callback, function () { - _this._type(path, { - context: ctx - }, callback); + var pPath = new Path_1.Path(path); + issuePrivilegeCheck(this, ctx, pPath, 'canReadProperties', callback, function () { + _this.fastExistCheckEx(ctx, pPath, callback, function () { + _this._type(pPath, { + context: ctx + }, callback); + }); }); }; FileSystem.prototype.addSubTree = function (ctx, _rootPath, _tree, _callback) { var _this = this; var callback = _callback ? _callback : _tree; var tree = _callback ? _tree : _rootPath; - var rootPath = _callback ? _rootPath : new Path_1.Path('/'); + var rootPath = _callback ? new Path_1.Path(_rootPath) : new Path_1.Path('/'); if (tree.constructor === CommonTypes_1.ResourceType) { - this.create(ctx, rootPath, tree, callback); + issuePrivilegeCheck(this, ctx, rootPath, 'canWrite', callback, function () { + _this.create(ctx, rootPath, tree, callback); + }); } else { new Workflow_1.Workflow() .each(Object.keys(tree), function (name, cb) { var value = tree[name]; + var childPath = rootPath.getChildPath(name); if (value.constructor === CommonTypes_1.ResourceType) - _this.addSubTree(ctx, rootPath.getChildPath(name), value, cb); + _this.addSubTree(ctx, childPath, value, cb); else - _this.addSubTree(ctx, rootPath.getChildPath(name), CommonTypes_1.ResourceType.Directory, function (e) { + _this.addSubTree(ctx, childPath, CommonTypes_1.ResourceType.Directory, function (e) { if (e) return cb(e); - _this.addSubTree(ctx, rootPath.getChildPath(name), value, cb); + _this.addSubTree(ctx, childPath, value, cb); }); }) .error(callback) @@ -448,64 +673,112 @@ var FileSystem = (function () { var _this = this; var depth = _callback ? _depth : 0; var callback = _callback ? _callback : _depth; - this.lockManager(ctx, startPath, function (e, lm) { - if (e) - return callback(e); - lm.getLocks(function (e, locks) { - if (e) + var pStartPath = new Path_1.Path(startPath); + issuePrivilegeCheck(this, ctx, startPath, 'canReadLocks', callback, function () { + _this.lockManager(ctx, pStartPath, function (e, lm) { + if (e === Errors_1.Errors.ResourceNotFound) { + lm = { + getLocks: function (callback) { + callback(null, []); + } + }; + } + else if (e) return callback(e); - if (depth != -1) - locks = locks.filter(function (f) { return f.depth === -1 || f.depth >= depth; }); - var go = function (fs, parentPath) { - var destDepth = depth === -1 ? -1 : depth + 1; - fs.listDeepLocks(ctx, parentPath, destDepth, function (e, pLocks) { - if (e) - return callback(e); - if (locks && locks.length > 0) - pLocks[startPath.toString()] = locks; - callback(null, pLocks); - }); - }; - if (!startPath.isRoot()) - return go(_this, startPath.getParent()); - _this.getFullPath(ctx, function (e, fsPath) { + lm.getLocks(function (e, locks) { if (e) return callback(e); - if (fsPath.isRoot()) { - var result = {}; - if (locks && locks.length > 0) - result[startPath.toString()] = locks; - return callback(null, result); - } - ctx.server.getFileSystem(fsPath.getParent(), function (fs, _, subPath) { - go(fs, subPath); + if (depth != -1) + locks = locks.filter(function (f) { return f.depth === -1 || f.depth >= depth; }); + var go = function (fs, parentPath) { + var destDepth = depth === -1 ? -1 : depth + 1; + fs.listDeepLocks(ctx, parentPath, destDepth, function (e, pLocks) { + if (e) + return callback(e); + if (locks && locks.length > 0) + pLocks[pStartPath.toString()] = locks; + callback(null, pLocks); + }); + }; + if (!pStartPath.isRoot()) + return go(_this, pStartPath.getParent()); + _this.getFullPath(ctx, function (e, fsPath) { + if (e) + return callback(e); + if (fsPath.isRoot()) { + var result = {}; + if (locks && locks.length > 0) + result[pStartPath.toString()] = locks; + return callback(null, result); + } + ctx.server.getFileSystem(fsPath.getParent(), function (fs, _, subPath) { + go(fs, subPath); + }); }); }); }); }); }; FileSystem.prototype.getFullPath = function (ctx, _path, _callback) { - var path = _callback ? _path : undefined; + var path = _callback ? new Path_1.Path(_path) : undefined; var callback = _callback ? _callback : _path; ctx.server.getFileSystemPath(this, function (fsPath) { callback(null, path ? fsPath.getChildPath(path) : fsPath); }); }; + FileSystem.prototype.localize = function (ctx, fullPath, callback) { + this.getFullPath(ctx, function (e, fsFullPath) { + if (e) + return callback(e); + var paths = fullPath.constructor === Array ? fullPath : [fullPath]; + callback(null, paths + .map(function (p) { return new Path_1.Path(p); }) + .map(function (p) { + for (var i = 0; i < fsFullPath.paths.length; ++i) + p.removeRoot(); + return p; + })); + }); + }; FileSystem.prototype.checkPrivilege = function (ctx, path, privileges, callback) { + var _this = this; if (privileges.constructor === String) privileges = [privileges]; - var resource = this.resource(ctx, path); - new Workflow_1.Workflow() - .each(privileges, function (privilege, cb) { - if (!privilege) - return cb(null, true); - var method = ctx.server.options.privilegeManager[privilege]; - if (!method) - return cb(null, true); - method(ctx, resource, cb); - }) - .error(function (e) { return callback(e, false); }) - .done(function (successes) { return callback(null, successes.every(function (s) { return !!s; })); }); + this.getFullPath(ctx, path, function (e, fullPath) { + _this.privilegeManager(ctx, path, function (e, privilegeManager) { + if (e) + return callback(e); + var resource = _this.resource(ctx, new Path_1.Path(path)); + privilegeManager.can(fullPath, resource, privileges, callback); + }); + }); + }; + FileSystem.prototype.privilegeManager = function (ctx, path, callback) { + if (!this._privilegeManager) + return callback(null, ctx.server.options.privilegeManager); + this._privilegeManager(new Path_1.Path(path), { + context: ctx + }, callback); + }; + FileSystem.prototype.isLocked = function (ctx, path, callback) { + this.listDeepLocks(ctx, path, function (e, locks) { + if (e) + return callback(e); + for (var path_1 in locks) + if (locks[path_1].some(function (l) { return ctx.user.uid !== l.userUid && l.lockKind.scope.isSame(LockScope_1.LockScope.Exclusive); })) + return callback(null, true); + var isShared = false; + for (var path_2 in locks) + for (var _i = 0, _a = locks[path_2]; _i < _a.length; _i++) { + var lock = _a[_i]; + if (lock.lockKind.scope.isSame(LockScope_1.LockScope.Shared)) { + isShared = true; + if (lock.userUid === ctx.user.uid) + return callback(null, false); + } + } + callback(null, isShared); + }); }; FileSystem.prototype.serialize = function (callback) { this.serializer().serialize(this, callback); @@ -513,3 +786,13 @@ var FileSystem = (function () { return FileSystem; }()); exports.FileSystem = FileSystem; +function issuePrivilegeCheck(fs, ctx, path, privilege, badCallback, goodCallback) { + fs.checkPrivilege(ctx, path, privilege, function (e, can) { + if (e) + badCallback(e); + else if (!can) + badCallback(Errors_1.Errors.NotEnoughPrivilege); + else + goodCallback(); + }); +} diff --git a/lib/server/v2/commands/Delete.js b/lib/server/v2/commands/Delete.js index 5f2c1a1c..2732c424 100644 --- a/lib/server/v2/commands/Delete.js +++ b/lib/server/v2/commands/Delete.js @@ -7,16 +7,13 @@ var default_1 = (function () { default_1.prototype.unchunked = function (ctx, data, callback) { ctx.noBodyExpected(function () { ctx.getResource(function (e, r) { - if (e) { - ctx.setCode(WebDAVRequest_1.HTTPCodes.NotFound); - callback(); - return; - } ctx.checkIfHeader(r, function () { //ctx.requirePrivilege([ 'canDelete' ], r, () => { r.delete(function (e) { return process.nextTick(function () { - if (e) - ctx.setCode(WebDAVRequest_1.HTTPCodes.InternalServerError); + if (e) { + if (!ctx.setCodeFromError(e)) + ctx.setCode(WebDAVRequest_1.HTTPCodes.InternalServerError); + } else { ctx.setCode(WebDAVRequest_1.HTTPCodes.OK); //ctx.invokeEvent('delete', r); diff --git a/lib/server/v2/commands/Get.js b/lib/server/v2/commands/Get.js index a5bcee80..a00f115e 100644 --- a/lib/server/v2/commands/Get.js +++ b/lib/server/v2/commands/Get.js @@ -56,7 +56,8 @@ var default_1 = (function () { //ctx.requirePrivilegeEx(targetSource ? [ 'canRead', 'canSource', 'canGetMimeType' ] : [ 'canRead', 'canGetMimeType' ], () => { r.type(function (e, type) { if (e) { - ctx.setCode(e === Errors_1.Errors.ResourceNotFound ? WebDAVRequest_1.HTTPCodes.NotFound : WebDAVRequest_1.HTTPCodes.InternalServerError); + if (!ctx.setCodeFromError(e)) + ctx.setCode(WebDAVRequest_1.HTTPCodes.InternalServerError); return callback(); } if (!type.isFile) { @@ -65,12 +66,14 @@ var default_1 = (function () { } r.mimeType(targetSource, function (e, mimeType) { return process.nextTick(function () { if (e) { - ctx.setCode(e === Errors_1.Errors.ResourceNotFound ? WebDAVRequest_1.HTTPCodes.NotFound : WebDAVRequest_1.HTTPCodes.InternalServerError); + if (!ctx.setCodeFromError(e)) + ctx.setCode(WebDAVRequest_1.HTTPCodes.InternalServerError); return callback(); } r.openReadStream(targetSource, function (e, rstream) { return process.nextTick(function () { if (e) { - ctx.setCode(WebDAVRequest_1.HTTPCodes.MethodNotAllowed); + if (!ctx.setCodeFromError(e)) + ctx.setCode(WebDAVRequest_1.HTTPCodes.MethodNotAllowed); return callback(); } //ctx.invokeEvent('read', r); diff --git a/lib/server/v2/commands/Head.js b/lib/server/v2/commands/Head.js index 1cd74e72..611c068a 100644 --- a/lib/server/v2/commands/Head.js +++ b/lib/server/v2/commands/Head.js @@ -1,7 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var WebDAVRequest_1 = require("../WebDAVRequest"); -var Errors_1 = require("../../../Errors"); var default_1 = (function () { function default_1() { } @@ -13,7 +12,8 @@ var default_1 = (function () { //ctx.requirePrivilege(targetSource ? [ 'canRead', 'canSource', 'canGetMimeType' ] : [ 'canRead', 'canGetMimeType' ], r, () => { r.type(function (e, type) { if (e) { - ctx.setCode(e === Errors_1.Errors.ResourceNotFound ? WebDAVRequest_1.HTTPCodes.NotFound : WebDAVRequest_1.HTTPCodes.InternalServerError); + if (!ctx.setCodeFromError(e)) + ctx.setCode(WebDAVRequest_1.HTTPCodes.InternalServerError); return callback(); } if (!type.isFile) { @@ -22,12 +22,15 @@ var default_1 = (function () { } r.mimeType(targetSource, function (e, mimeType) { return process.nextTick(function () { if (e) { - ctx.setCode(e === Errors_1.Errors.ResourceNotFound ? WebDAVRequest_1.HTTPCodes.NotFound : WebDAVRequest_1.HTTPCodes.InternalServerError); + if (!ctx.setCodeFromError(e)) + ctx.setCode(WebDAVRequest_1.HTTPCodes.InternalServerError); return callback(); } r.size(targetSource, function (e, size) { - if (e) - ctx.setCode(e === Errors_1.Errors.ResourceNotFound ? WebDAVRequest_1.HTTPCodes.NotFound : WebDAVRequest_1.HTTPCodes.InternalServerError); + if (e) { + if (!ctx.setCodeFromError(e)) + ctx.setCode(WebDAVRequest_1.HTTPCodes.InternalServerError); + } else { ctx.setCode(WebDAVRequest_1.HTTPCodes.OK); ctx.response.setHeader('Accept-Ranges', 'bytes'); diff --git a/lib/server/v2/commands/Lock.js b/lib/server/v2/commands/Lock.js index 6982e33a..d9fe8632 100644 --- a/lib/server/v2/commands/Lock.js +++ b/lib/server/v2/commands/Lock.js @@ -59,11 +59,7 @@ function createLock(ctx, data, callback) { var _callback_1 = callback; callback = function (e) { if (e) { - if (e === Errors_1.Errors.Locked) - ctx.setCode(WebDAVRequest_1.HTTPCodes.Locked); - else if (e === Errors_1.Errors.IntermediateResourceMissing || e === Errors_1.Errors.WrongParentTypeForCreation) - ctx.setCode(WebDAVRequest_1.HTTPCodes.Conflict); - else + if (!ctx.setCodeFromError(e)) ctx.setCode(WebDAVRequest_1.HTTPCodes.InternalServerError); } else @@ -94,15 +90,11 @@ function createLock(ctx, data, callback) { } function refreshLock(ctx, lockUUID, callback) { ctx.getResource(function (e, r) { - if (e) { - ctx.setCode(e === Errors_1.Errors.ResourceNotFound ? WebDAVRequest_1.HTTPCodes.NotFound : WebDAVRequest_1.HTTPCodes.InternalServerError); - callback(); - return; - } //ctx.requirePrivilege([ 'canSetLock', 'canGetLock' ], r, () => { r.lockManager(function (e, lm) { if (e) { - ctx.setCode(e === Errors_1.Errors.ResourceNotFound ? WebDAVRequest_1.HTTPCodes.NotFound : WebDAVRequest_1.HTTPCodes.InternalServerError); + if (!ctx.setCodeFromError(e)) + ctx.setCode(WebDAVRequest_1.HTTPCodes.InternalServerError); return callback(); } lm.refresh(lockUUID, ctx.server.options.lockTimeout, function (e, lock) { @@ -126,8 +118,7 @@ var default_1 = (function () { default_1.prototype.unchunked = function (ctx, data, callback) { if (!ctx.user) { ctx.setCode(WebDAVRequest_1.HTTPCodes.Forbidden); - callback(); - return; + return callback(); } if (ctx.headers.contentLength > 0) { createLock(ctx, data, callback); @@ -136,8 +127,7 @@ var default_1 = (function () { var ifHeader = ctx.headers.find('If'); if (!ifHeader) { ctx.setCode(WebDAVRequest_1.HTTPCodes.PreconditionRequired); - callback(); - return; + return callback(); } refreshLock(ctx, IfParser_1.extractOneToken(ifHeader), callback); }; diff --git a/lib/server/v2/commands/Mkcol.js b/lib/server/v2/commands/Mkcol.js index ab152b95..b5bb5b3e 100644 --- a/lib/server/v2/commands/Mkcol.js +++ b/lib/server/v2/commands/Mkcol.js @@ -13,22 +13,19 @@ var default_1 = (function () { ctx.getResource(r.path.getParent(), function (e, rParent) { rParent.type(function (e, parentType) { if (e) { - ctx.setCode(e === Errors_1.Errors.ResourceNotFound ? WebDAVRequest_1.HTTPCodes.Conflict : WebDAVRequest_1.HTTPCodes.InternalServerError); - callback(); - return; + if (!ctx.setCodeFromError(e)) + ctx.setCode(WebDAVRequest_1.HTTPCodes.InternalServerError); + return callback(); } if (!parentType.isDirectory) { ctx.setCode(WebDAVRequest_1.HTTPCodes.Forbidden); - callback(); - return; + return callback(); } r.create(CommonTypes_1.ResourceType.Directory, function (e) { if (e) { - if (e === Errors_1.Errors.WrongParentTypeForCreation) - ctx.setCode(WebDAVRequest_1.HTTPCodes.Conflict); - else if (e === Errors_1.Errors.ResourceAlreadyExists) + if (e === Errors_1.Errors.ResourceAlreadyExists) ctx.setCode(WebDAVRequest_1.HTTPCodes.MethodNotAllowed); - else + else if (!ctx.setCodeFromError(e)) ctx.setCode(WebDAVRequest_1.HTTPCodes.InternalServerError); } else diff --git a/lib/server/v2/commands/Move.js b/lib/server/v2/commands/Move.js index b5e3ec18..9106ef8d 100644 --- a/lib/server/v2/commands/Move.js +++ b/lib/server/v2/commands/Move.js @@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true }); var WebDAVRequest_1 = require("../WebDAVRequest"); var StandardMethods_1 = require("../../../manager/v2/fileSystem/StandardMethods"); var Path_1 = require("../../../manager/v2/Path"); -var Errors_1 = require("../../../Errors"); function execute(ctx, methodName, privilegeName, callback) { ctx.noBodyExpected(function () { ctx.getResource(function (e, r) { @@ -13,8 +12,7 @@ function execute(ctx, methodName, privilegeName, callback) { var destination = ctx.headers.find('destination'); if (!destination) { ctx.setCode(WebDAVRequest_1.HTTPCodes.BadRequest); - callback(); - return; + return callback(); } var startIndex = destination.indexOf('://'); if (startIndex !== -1) { @@ -27,16 +25,10 @@ function execute(ctx, methodName, privilegeName, callback) { return callback(); } var cb = function (e, overwritten) { - if (e === Errors_1.Errors.ResourceNotFound) - ctx.setCode(WebDAVRequest_1.HTTPCodes.NotFound); - else if (e === Errors_1.Errors.InsufficientStorage) - ctx.setCode(WebDAVRequest_1.HTTPCodes.InsufficientStorage); - else if (e === Errors_1.Errors.Locked) - ctx.setCode(WebDAVRequest_1.HTTPCodes.Locked); - else if (e === Errors_1.Errors.ResourceAlreadyExists) - ctx.setCode(WebDAVRequest_1.HTTPCodes.Conflict); - else if (e) - ctx.setCode(WebDAVRequest_1.HTTPCodes.InternalServerError); + if (e) { + if (!ctx.setCodeFromError(e)) + ctx.setCode(WebDAVRequest_1.HTTPCodes.InternalServerError); + } else if (overwritten) ctx.setCode(WebDAVRequest_1.HTTPCodes.NoContent); else diff --git a/lib/server/v2/commands/Propfind.js b/lib/server/v2/commands/Propfind.js index d0a69344..1a023702 100644 --- a/lib/server/v2/commands/Propfind.js +++ b/lib/server/v2/commands/Propfind.js @@ -84,11 +84,7 @@ var default_1 = (function () { if (!e) done(multistatus); else { - if (e === Errors_1.Errors.ResourceNotFound) - ctx.setCode(WebDAVRequest_1.HTTPCodes.NotFound); - else if (e === Errors_1.Errors.BadAuthentication) - ctx.setCode(WebDAVRequest_1.HTTPCodes.Unauthorized); - else + if (!ctx.setCodeFromError(e)) ctx.setCode(WebDAVRequest_1.HTTPCodes.InternalServerError); callback(); } @@ -98,11 +94,7 @@ var default_1 = (function () { //ctx.requirePrivilege('canGetChildren', resource, () => { resource.readDir(true, function (e, children) { return process.nextTick(function () { function err(e) { - if (e === Errors_1.Errors.ResourceNotFound) - ctx.setCode(WebDAVRequest_1.HTTPCodes.NotFound); - else if (e === Errors_1.Errors.BadAuthentication) - ctx.setCode(WebDAVRequest_1.HTTPCodes.Unauthorized); - else + if (!ctx.setCodeFromError(e)) ctx.setCode(WebDAVRequest_1.HTTPCodes.InternalServerError); callback(); } diff --git a/lib/server/v2/commands/Proppatch.js b/lib/server/v2/commands/Proppatch.js index 7fedc2bf..394da370 100644 --- a/lib/server/v2/commands/Proppatch.js +++ b/lib/server/v2/commands/Proppatch.js @@ -4,7 +4,6 @@ var WebDAVRequest_1 = require("../WebDAVRequest"); var http_1 = require("http"); var Workflow_1 = require("../../../helper/Workflow"); var XML_1 = require("../../../helper/XML"); -var Errors_1 = require("../../../Errors"); var default_1 = (function () { function default_1() { } @@ -28,7 +27,7 @@ var default_1 = (function () { }; }; var notify_1 = function (el, error) { - var code = error ? WebDAVRequest_1.HTTPCodes.Conflict : WebDAVRequest_1.HTTPCodes.OK; + var code = error ? WebDAVRequest_1.HTTPCodes.Forbidden : WebDAVRequest_1.HTTPCodes.OK; var propstat = response.ele('D:propstat'); propstat.ele('D:prop').ele(el.name); propstat.ele('D:status').add('HTTP/1.1 ' + code + ' ' + http_1.STATUS_CODES[code]); @@ -51,16 +50,28 @@ var default_1 = (function () { .done(function () { return finalize_1(); }); }); }; - r.propertyManager(function (e, pm) { - if (e) { - ctx.setCode(e === Errors_1.Errors.ResourceNotFound ? WebDAVRequest_1.HTTPCodes.NotFound : WebDAVRequest_1.HTTPCodes.InternalServerError); + r.fs.isLocked(ctx, r.path, function (e, locked) { + if (e || locked) { + if (e) { + if (!ctx.setCodeFromError(e)) + ctx.setCode(WebDAVRequest_1.HTTPCodes.InternalServerError); + } + else if (locked) + ctx.setCode(WebDAVRequest_1.HTTPCodes.Locked); return callback(); } - execute_1('DAV:set', 'setProperty', function (el, callback) { - pm.setProperty(el.name, el.elements, callback); - }); - execute_1('DAV:remove', 'removeProperty', function (el, callback) { - pm.removeProperty(el.name, callback); + r.propertyManager(function (e, pm) { + if (e) { + if (!ctx.setCodeFromError(e)) + ctx.setCode(WebDAVRequest_1.HTTPCodes.InternalServerError); + return callback(); + } + execute_1('DAV:set', 'setProperty', function (el, callback) { + pm.setProperty(el.name, el.elements, callback); + }); + execute_1('DAV:remove', 'removeProperty', function (el, callback) { + pm.removeProperty(el.name, callback); + }); }); }); } diff --git a/lib/server/v2/commands/Put.js b/lib/server/v2/commands/Put.js index 5f2cf00b..22b8ad59 100644 --- a/lib/server/v2/commands/Put.js +++ b/lib/server/v2/commands/Put.js @@ -19,20 +19,19 @@ var default_1 = (function () { mode = 'mustCreate'; } else if (e) { - ctx.setCode(WebDAVRequest_1.HTTPCodes.InternalServerError); - callback(); - return; + if (!ctx.setCodeFromError(e)) + ctx.setCode(WebDAVRequest_1.HTTPCodes.InternalServerError); + return callback(); } else if (!type.isFile) { ctx.setCode(WebDAVRequest_1.HTTPCodes.MethodNotAllowed); - callback(); - return; + return callback(); } r.openWriteStream(mode, targetSource, ctx.headers.contentLength, function (e, wStream, created) { if (e) { - ctx.setCode(e === Errors_1.Errors.IntermediateResourceMissing || e === Errors_1.Errors.WrongParentTypeForCreation ? WebDAVRequest_1.HTTPCodes.Conflict : WebDAVRequest_1.HTTPCodes.InternalServerError); - callback(); - return; + if (!ctx.setCodeFromError(e)) + ctx.setCode(e === Errors_1.Errors.IntermediateResourceMissing || e === Errors_1.Errors.WrongParentTypeForCreation ? WebDAVRequest_1.HTTPCodes.Conflict : WebDAVRequest_1.HTTPCodes.InternalServerError); + return callback(); } inputStream.pipe(wStream); wStream.on('finish', function (e) { @@ -44,7 +43,8 @@ var default_1 = (function () { callback(); }); wStream.on('error', function (e) { - ctx.setCode(WebDAVRequest_1.HTTPCodes.InternalServerError); + if (!ctx.setCodeFromError(e)) + ctx.setCode(WebDAVRequest_1.HTTPCodes.InternalServerError); callback(); }); }); diff --git a/lib/server/v2/commands/Unlock.js b/lib/server/v2/commands/Unlock.js index 741725ac..d95bd4ca 100644 --- a/lib/server/v2/commands/Unlock.js +++ b/lib/server/v2/commands/Unlock.js @@ -1,7 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var WebDAVRequest_1 = require("../WebDAVRequest"); -var Errors_1 = require("../../../Errors"); var default_1 = (function () { function default_1() { } @@ -38,12 +37,16 @@ var default_1 = (function () { }*/ r.lockManager(function (e, lm) { if (e) { - ctx.setCode(e === Errors_1.Errors.ResourceNotFound ? WebDAVRequest_1.HTTPCodes.NotFound : WebDAVRequest_1.HTTPCodes.InternalServerError); + if (!ctx.setCodeFromError(e)) + ctx.setCode(WebDAVRequest_1.HTTPCodes.InternalServerError); return callback(); } lm.getLock(token, function (e, lock) { if (e || !lock) { - ctx.setCode(WebDAVRequest_1.HTTPCodes.Conflict); + if (!lock) + ctx.setCode(WebDAVRequest_1.HTTPCodes.Conflict); + else if (!ctx.setCodeFromError(e)) + ctx.setCode(WebDAVRequest_1.HTTPCodes.InternalServerError); return callback(); } if (!!lock.userUid && lock.userUid !== ctx.user.uid) { @@ -51,8 +54,12 @@ var default_1 = (function () { return callback(); } lm.removeLock(lock.uuid, function (e, done) { - if (e || !done) - ctx.setCode(WebDAVRequest_1.HTTPCodes.Forbidden); + if (e || !done) { + if (!done) + ctx.setCode(WebDAVRequest_1.HTTPCodes.Forbidden); + else if (!ctx.setCodeFromError(e)) + ctx.setCode(WebDAVRequest_1.HTTPCodes.InternalServerError); + } else { //ctx.invokeEvent('unlock', r, lock); ctx.setCode(WebDAVRequest_1.HTTPCodes.NoContent); diff --git a/src/manager/v2/fileSystem/FileSystem.ts b/src/manager/v2/fileSystem/FileSystem.ts index 943bf2e9..1de44062 100644 --- a/src/manager/v2/fileSystem/FileSystem.ts +++ b/src/manager/v2/fileSystem/FileSystem.ts @@ -1,7 +1,7 @@ -import { AvailableLocksInfo, CopyInfo, CreateInfo, CreationDateInfo, DeleteInfo, DisplayNameInfo, ETagInfo, IContextInfo, LastModifiedDateInfo, LockManagerInfo, MimeTypeInfo, MoveInfo, OpenReadStreamInfo, OpenWriteStreamInfo, PropertyManagerInfo, ReadDirInfo, RenameInfo, SizeInfo, TypeInfo, WebNameInfo } from './ContextInfo' +import { PrivilegeManagerInfo, AvailableLocksInfo, CopyInfo, CreateInfo, CreationDateInfo, DeleteInfo, DisplayNameInfo, ETagInfo, IContextInfo, LastModifiedDateInfo, LockManagerInfo, MimeTypeInfo, MoveInfo, OpenReadStreamInfo, OpenWriteStreamInfo, PropertyManagerInfo, ReadDirInfo, RenameInfo, SizeInfo, TypeInfo, WebNameInfo } from './ContextInfo' import { Readable, Writable } from 'stream' import { RequestContext } from '../../../server/v2/RequestContext' -import { BasicPrivilege } from '../../../user/v2/privilege/IPrivilegeManager' +import { BasicPrivilege, PrivilegeManager } from '../../../user/v2/privilege/PrivilegeManager' import { XMLElement } from '../../../helper/XML' import { LockScope } from '../../../resource/lock/LockScope' import { LockType } from '../../../resource/lock/LockType' @@ -10,16 +10,40 @@ import { Workflow } from '../../../helper/Workflow' import { Errors } from '../../../Errors' import { Lock } from '../../../resource/lock/Lock' import { Path } from '../Path' -import { ResourceType, SimpleCallback, Return2Callback, ReturnCallback, SubTree, OpenWriteStreamMode } from './CommonTypes' +import { ResourceType, SimpleCallback, Return2Callback, ReturnCallback, SubTree, OpenWriteStreamMode, ResourcePropertyValue } from './CommonTypes' import { ContextualFileSystem } from './ContextualFileSystem' import { ILockManager } from './LockManager' -import { IPropertyManager } from './PropertyManager' +import { IPropertyManager, PropertyBag } from './PropertyManager' import { Resource } from './Resource' import { StandardMethods } from './StandardMethods' import { ISerializableFileSystem, FileSystemSerializer } from './Serialization' import * as mimeTypes from 'mime-types' import * as crypto from 'crypto' +class BufferedIsLocked +{ + _isLocked : boolean; + + constructor(public fs : FileSystem, public ctx : RequestContext, public path : Path) + { + this._isLocked = null; + } + + isLocked(callback : ReturnCallback) + { + if(this._isLocked !== null) + return callback(null, this._isLocked); + + this.fs.isLocked(this.ctx, this.path, (e, locked) => { + if(e) + return callback(e); + + this._isLocked = locked; + callback(null, locked); + }) + } +} + export abstract class FileSystem implements ISerializableFileSystem { private __serializer; @@ -44,11 +68,12 @@ export abstract class FileSystem implements ISerializableFileSystem return new Resource(path, this, ctx); } - fastExistCheckEx(ctx : RequestContext, path : Path, errorCallback : SimpleCallback, callback : () => void) : void + fastExistCheckEx(ctx : RequestContext, _path : Path | string, errorCallback : SimpleCallback, callback : () => void) : void { if(!this._fastExistCheck) return callback(); + const path = new Path(_path); this._fastExistCheck(ctx, path, (exists) => { if(!exists) errorCallback(Errors.ResourceNotFound); @@ -56,11 +81,12 @@ export abstract class FileSystem implements ISerializableFileSystem callback(); }); } - fastExistCheckExReverse(ctx : RequestContext, path : Path, errorCallback : SimpleCallback, callback : () => void) : void + fastExistCheckExReverse(ctx : RequestContext, _path : Path | string, errorCallback : SimpleCallback, callback : () => void) : void { if(!this._fastExistCheck) return callback(); + const path = new Path(_path); this._fastExistCheck(ctx, path, (exists) => { if(exists) errorCallback(Errors.ResourceAlreadyExists); @@ -68,115 +94,136 @@ export abstract class FileSystem implements ISerializableFileSystem callback(); }); } - protected fastExistCheck(ctx : RequestContext, path : Path, callback : (exists : boolean) => void) : void + protected fastExistCheck(ctx : RequestContext, _path : Path | string, callback : (exists : boolean) => void) : void { if(!this._fastExistCheck) return callback(true); + const path = new Path(_path); this._fastExistCheck(ctx, path, (exists) => callback(!!exists)); } protected _fastExistCheck?(ctx : RequestContext, path : Path, callback : (exists : boolean) => void) : void - create(ctx : RequestContext, path : Path, type : ResourceType, callback : SimpleCallback) : void - create(ctx : RequestContext, path : Path, type : ResourceType, createIntermediates : boolean, callback : SimpleCallback) : void - create(ctx : RequestContext, path : Path, type : ResourceType, _createIntermediates : boolean | SimpleCallback, _callback ?: SimpleCallback) : void + create(ctx : RequestContext, path : Path | string, type : ResourceType, callback : SimpleCallback) : void + create(ctx : RequestContext, path : Path | string, type : ResourceType, createIntermediates : boolean, callback : SimpleCallback) : void + create(ctx : RequestContext, _path : Path | string, type : ResourceType, _createIntermediates : boolean | SimpleCallback, _callback ?: SimpleCallback) : void { const createIntermediates = _callback ? _createIntermediates as boolean : false; const callback = _callback ? _callback : _createIntermediates as SimpleCallback; + const path = new Path(_path); if(!this._create) return callback(Errors.InvalidOperation); - const go = () => { - this._create(path, { - context: ctx, - type - }, callback); - } + issuePrivilegeCheck(this, ctx, path, 'canWrite', callback, () => { + const go = () => { + this._create(path, { + context: ctx, + type + }, callback); + } - this.fastExistCheckExReverse(ctx, path, callback, () => { - this.type(ctx, path.getParent(), (e, type) => { - if(e === Errors.ResourceNotFound) - { - if(!createIntermediates) - return callback(Errors.IntermediateResourceMissing); + this.isLocked(ctx, path, (e, locked) => { + if(e || locked) + return callback(locked ? Errors.Locked : e); + + this.fastExistCheckExReverse(ctx, path, callback, () => { + this.type(ctx, path.getParent(), (e, type) => { + if(e === Errors.ResourceNotFound) + { + if(!createIntermediates) + return callback(Errors.IntermediateResourceMissing); - this.getFullPath(ctx, path, (e, fullPath) => { - if(e) - return callback(e); - - fullPath = fullPath.getParent(); - ctx.getResource(fullPath, (e, r) => { - if(e) - return callback(e); - - r.create(ResourceType.Directory, (e) => { - if(e && e !== Errors.ResourceAlreadyExists) + this.getFullPath(ctx, path, (e, fullPath) => { + if(e) return callback(e); - go(); + fullPath = fullPath.getParent(); + ctx.getResource(fullPath, (e, r) => { + if(e) + return callback(e); + + r.create(ResourceType.Directory, (e) => { + if(e && e !== Errors.ResourceAlreadyExists) + return callback(e); + + go(); + }) + }) }) - }) - }) - return; - } - if(e) - return callback(e); - - if(!type.isDirectory) - return callback(Errors.WrongParentTypeForCreation); + return; + } + if(e) + return callback(e); + + if(!type.isDirectory) + return callback(Errors.WrongParentTypeForCreation); - go(); + go(); + }) + }) }) }) } protected _create?(path : Path, ctx : CreateInfo, callback : SimpleCallback) : void - etag(ctx : RequestContext, path : Path, callback : ReturnCallback) : void + etag(ctx : RequestContext, _path : Path | string, callback : ReturnCallback) : void { - this.fastExistCheckEx(ctx, path, callback, () => { - if(!this._etag) - return this.lastModifiedDate(ctx, path, (e, date) => { - if(e) - return callback(e); - callback(null, '"' + crypto.createHash('md5').update(date.toString()).digest('hex') + '"'); - }) + const path = new Path(_path); - this._etag(path, { - context: ctx - }, callback); + issuePrivilegeCheck(this, ctx, path, 'canReadProperties', callback, () => { + this.fastExistCheckEx(ctx, path, callback, () => { + if(!this._etag) + return this.lastModifiedDate(ctx, path, (e, date) => { + if(e) + return callback(e); + callback(null, '"' + crypto.createHash('md5').update(date.toString()).digest('hex') + '"'); + }) + + this._etag(path, { + context: ctx + }, callback); + }) }) } protected _etag?(path : Path, ctx : ETagInfo, callback : ReturnCallback) : void - delete(ctx : RequestContext, path : Path, callback : SimpleCallback) : void - delete(ctx : RequestContext, path : Path, depth : number, callback : SimpleCallback) : void - delete(ctx : RequestContext, path : Path, _depth : number | SimpleCallback, _callback ?: SimpleCallback) : void + delete(ctx : RequestContext, path : Path | string, callback : SimpleCallback) : void + delete(ctx : RequestContext, path : Path | string, depth : number, callback : SimpleCallback) : void + delete(ctx : RequestContext, _path : Path | string, _depth : number | SimpleCallback, _callback ?: SimpleCallback) : void { const depth = _callback ? _depth as number : -1; const callback = _callback ? _callback : _depth as SimpleCallback; + const path = new Path(_path); if(!this._delete) return callback(Errors.InvalidOperation); - this.fastExistCheckEx(ctx, path, callback, () => { - this._delete(path, { - context: ctx, - depth - }, callback); + issuePrivilegeCheck(this, ctx, path, 'canWrite', callback, () => { + this.isLocked(ctx, path, (e, isLocked) => { + if(e || isLocked) + return callback(e ? e : Errors.Locked); + + this.fastExistCheckEx(ctx, path, callback, () => { + this._delete(path, { + context: ctx, + depth + }, callback); + }) + }) }) } protected _delete?(path : Path, ctx : DeleteInfo, callback : SimpleCallback) : void - openWriteStream(ctx : RequestContext, path : Path, callback : Return2Callback) : void - openWriteStream(ctx : RequestContext, path : Path, estimatedSize : number, callback : Return2Callback) : void - openWriteStream(ctx : RequestContext, path : Path, targetSource : boolean, callback : Return2Callback) : void - openWriteStream(ctx : RequestContext, path : Path, targetSource : boolean, estimatedSize : number, callback : Return2Callback) : void - openWriteStream(ctx : RequestContext, path : Path, mode : OpenWriteStreamMode, callback : Return2Callback) : void - openWriteStream(ctx : RequestContext, path : Path, mode : OpenWriteStreamMode, estimatedSize : number, callback : Return2Callback) : void - openWriteStream(ctx : RequestContext, path : Path, mode : OpenWriteStreamMode, targetSource : boolean, callback : Return2Callback) : void - openWriteStream(ctx : RequestContext, path : Path, mode : OpenWriteStreamMode, targetSource : boolean, estimatedSize : number, callback : Return2Callback) : void - openWriteStream(ctx : RequestContext, path : Path, _mode : OpenWriteStreamMode | boolean | number | Return2Callback, _targetSource ?: boolean | number | Return2Callback, _estimatedSize ?: number | Return2Callback, _callback ?: Return2Callback) : void + openWriteStream(ctx : RequestContext, path : Path | string, callback : Return2Callback) : void + openWriteStream(ctx : RequestContext, path : Path | string, estimatedSize : number, callback : Return2Callback) : void + openWriteStream(ctx : RequestContext, path : Path | string, targetSource : boolean, callback : Return2Callback) : void + openWriteStream(ctx : RequestContext, path : Path | string, targetSource : boolean, estimatedSize : number, callback : Return2Callback) : void + openWriteStream(ctx : RequestContext, path : Path | string, mode : OpenWriteStreamMode, callback : Return2Callback) : void + openWriteStream(ctx : RequestContext, path : Path | string, mode : OpenWriteStreamMode, estimatedSize : number, callback : Return2Callback) : void + openWriteStream(ctx : RequestContext, path : Path | string, mode : OpenWriteStreamMode, targetSource : boolean, callback : Return2Callback) : void + openWriteStream(ctx : RequestContext, path : Path | string, mode : OpenWriteStreamMode, targetSource : boolean, estimatedSize : number, callback : Return2Callback) : void + openWriteStream(ctx : RequestContext, _path : Path | string, _mode : OpenWriteStreamMode | boolean | number | Return2Callback, _targetSource ?: boolean | number | Return2Callback, _estimatedSize ?: number | Return2Callback, _callback ?: Return2Callback) : void { let targetSource = true; for(const obj of [ _mode, _targetSource ]) @@ -194,414 +241,631 @@ export abstract class FileSystem implements ISerializableFileSystem callback = obj as Return2Callback; const mode = _mode && _mode.constructor === String ? _mode as OpenWriteStreamMode : 'mustExist'; + const path = new Path(_path); let created = false; if(!this._openWriteStream) return callback(Errors.InvalidOperation); - const go = (callback : Return2Callback) => - { - this._openWriteStream(path, { - context: ctx, - estimatedSize, - targetSource, - mode - }, (e, wStream) => callback(e, wStream, created)); - } - - const createAndGo = (intermediates : boolean) => - { - this.create(ctx, path, ResourceType.File, intermediates, (e) => { - if(e) - return callback(e); + issuePrivilegeCheck(this, ctx, path, targetSource ? 'canWriteContentSource' : 'canWriteContentTranslated', callback, () => { + this.isLocked(ctx, path, (e, isLocked) => { + if(e || isLocked) + return callback(e ? e : Errors.Locked); - created = true; - go(callback); - }) - } + const go = (callback : Return2Callback) => + { + this._openWriteStream(path, { + context: ctx, + estimatedSize, + targetSource, + mode + }, (e, wStream) => callback(e, wStream, created)); + } - switch(mode) - { - case 'mustExist': - this.fastExistCheckEx(ctx, path, callback, () => go(callback)); - break; - - case 'mustCreateIntermediates': - case 'mustCreate': - createAndGo(mode === 'mustCreateIntermediates'); - break; - - case 'canCreateIntermediates': - case 'canCreate': - go((e, wStream) => { - if(e === Errors.ResourceNotFound) - createAndGo(mode === 'canCreateIntermediates'); - else - callback(e, wStream); - }) - break; - - default: - callback(Errors.IllegalArguments); - break; - } + const createAndGo = (intermediates : boolean) => + { + this.create(ctx, path, ResourceType.File, intermediates, (e) => { + if(e) + return callback(e); + + created = true; + go(callback); + }) + } + + switch(mode) + { + case 'mustExist': + this.fastExistCheckEx(ctx, path, callback, () => go(callback)); + break; + + case 'mustCreateIntermediates': + case 'mustCreate': + createAndGo(mode === 'mustCreateIntermediates'); + break; + + case 'canCreateIntermediates': + case 'canCreate': + go((e, wStream) => { + if(e === Errors.ResourceNotFound) + createAndGo(mode === 'canCreateIntermediates'); + else + callback(e, wStream); + }) + break; + + default: + callback(Errors.IllegalArguments); + break; + } + }) + }) } protected _openWriteStream?(path : Path, ctx : OpenWriteStreamInfo, callback : ReturnCallback) : void - openReadStream(ctx : RequestContext, path : Path, callback : ReturnCallback) : void - openReadStream(ctx : RequestContext, path : Path, estimatedSize : number, callback : ReturnCallback) : void - openReadStream(ctx : RequestContext, path : Path, targetSource : boolean, callback : ReturnCallback) : void - openReadStream(ctx : RequestContext, path : Path, targetSource : boolean, estimatedSize : number, callback : ReturnCallback) : void - openReadStream(ctx : RequestContext, path : Path, _targetSource : boolean | number | ReturnCallback, _estimatedSize ?: number | ReturnCallback, _callback ?: ReturnCallback) : void + openReadStream(ctx : RequestContext, path : Path | string, callback : ReturnCallback) : void + openReadStream(ctx : RequestContext, path : Path | string, estimatedSize : number, callback : ReturnCallback) : void + openReadStream(ctx : RequestContext, path : Path | string, targetSource : boolean, callback : ReturnCallback) : void + openReadStream(ctx : RequestContext, path : Path | string, targetSource : boolean, estimatedSize : number, callback : ReturnCallback) : void + openReadStream(ctx : RequestContext, _path : Path | string, _targetSource : boolean | number | ReturnCallback, _estimatedSize ?: number | ReturnCallback, _callback ?: ReturnCallback) : void { const targetSource = _targetSource.constructor === Boolean ? _targetSource as boolean : true; const estimatedSize = _callback ? _estimatedSize as number : _estimatedSize ? _targetSource as number : -1; const callback = _callback ? _callback : _estimatedSize ? _estimatedSize as ReturnCallback : _targetSource as ReturnCallback; + const path = new Path(_path); - this.fastExistCheckEx(ctx, path, callback, () => { - if(!this._openReadStream) - return callback(Errors.InvalidOperation); - - this._openReadStream(path, { - context: ctx, - estimatedSize, - targetSource - }, callback); + issuePrivilegeCheck(this, ctx, path, targetSource ? 'canReadContentSource' : 'canReadContentTranslated', callback, () => { + this.fastExistCheckEx(ctx, path, callback, () => { + if(!this._openReadStream) + return callback(Errors.InvalidOperation); + + this._openReadStream(path, { + context: ctx, + estimatedSize, + targetSource + }, callback); + }) }) } protected _openReadStream?(path : Path, ctx : OpenReadStreamInfo, callback : ReturnCallback) : void - move(ctx : RequestContext, pathFrom : Path, pathTo : Path, callback : ReturnCallback) : void - move(ctx : RequestContext, pathFrom : Path, pathTo : Path, overwrite : boolean, callback : ReturnCallback) : void - move(ctx : RequestContext, pathFrom : Path, pathTo : Path, _overwrite : boolean | ReturnCallback, _callback ?: ReturnCallback) : void + move(ctx : RequestContext, pathFrom : Path | string, pathTo : Path | string, callback : ReturnCallback) : void + move(ctx : RequestContext, pathFrom : Path | string, pathTo : Path | string, overwrite : boolean, callback : ReturnCallback) : void + move(ctx : RequestContext, _pathFrom : Path | string, _pathTo : Path | string, _overwrite : boolean | ReturnCallback, _callback ?: ReturnCallback) : void { const callback = _callback ? _callback : _overwrite as ReturnCallback; const overwrite = _callback ? _overwrite as boolean : false; + const pathFrom = new Path(_pathFrom); + const pathTo = new Path(_pathTo); - const go = () => - { - if(this._move) - { - this._move(pathFrom, pathTo, { - context: ctx, - overwrite - }, callback); - return; - } + issuePrivilegeCheck(this, ctx, pathFrom, 'canRead', callback, () => { + issuePrivilegeCheck(this, ctx, pathTo, 'canWrite', callback, () => { + this.isLocked(ctx, pathFrom, (e, isLocked) => { + if(e || isLocked) + return callback(e ? e : Errors.Locked); + + this.isLocked(ctx, pathTo, (e, isLocked) => { + if(e || isLocked) + return callback(e ? e : Errors.Locked); + + const go = () => + { + if(this._move) + { + this._move(pathFrom, pathTo, { + context: ctx, + overwrite + }, callback); + return; + } - StandardMethods.standardMove(ctx, pathFrom, this, pathTo, this, callback); - } + StandardMethods.standardMove(ctx, pathFrom, this, pathTo, this, callback); + } - this.fastExistCheckEx(ctx, pathFrom, callback, () => { - if(!overwrite) - this.fastExistCheckExReverse(ctx, pathTo, callback, go); - else - go(); + this.fastExistCheckEx(ctx, pathFrom, callback, () => { + if(!overwrite) + this.fastExistCheckExReverse(ctx, pathTo, callback, go); + else + go(); + }) + }) + }) + }) }) } protected _move?(pathFrom : Path, pathTo : Path, ctx : MoveInfo, callback : ReturnCallback) : void - copy(ctx : RequestContext, pathFrom : Path, pathTo : Path, callback : ReturnCallback) : void - copy(ctx : RequestContext, pathFrom : Path, pathTo : Path, depth : number, callback : ReturnCallback) : void - copy(ctx : RequestContext, pathFrom : Path, pathTo : Path, overwrite : boolean, callback : ReturnCallback) : void - copy(ctx : RequestContext, pathFrom : Path, pathTo : Path, overwrite : boolean, depth : number, callback : ReturnCallback) : void - copy(ctx : RequestContext, pathFrom : Path, pathTo : Path, _overwrite : boolean | number | ReturnCallback, _depth ?: number | ReturnCallback, _callback ?: ReturnCallback) : void + copy(ctx : RequestContext, pathFrom : Path | string, pathTo : Path | string, callback : ReturnCallback) : void + copy(ctx : RequestContext, pathFrom : Path | string, pathTo : Path | string, depth : number, callback : ReturnCallback) : void + copy(ctx : RequestContext, pathFrom : Path | string, pathTo : Path | string, overwrite : boolean, callback : ReturnCallback) : void + copy(ctx : RequestContext, pathFrom : Path | string, pathTo : Path | string, overwrite : boolean, depth : number, callback : ReturnCallback) : void + copy(ctx : RequestContext, _pathFrom : Path | string, _pathTo : Path | string, _overwrite : boolean | number | ReturnCallback, _depth ?: number | ReturnCallback, _callback ?: ReturnCallback) : void { const overwrite = _overwrite.constructor === Boolean ? _overwrite as boolean : false; const depth = _callback ? _depth as number : !_depth ? -1 : _overwrite.constructor === Number ? _overwrite as number : -1; const callback = _callback ? _callback : _depth ? _depth as ReturnCallback : _overwrite as ReturnCallback; + const pathFrom = new Path(_pathFrom); + const pathTo = new Path(_pathTo); - if(this._copy) - { - const go = () => - { - this._copy(pathFrom, pathTo, { - context: ctx, - depth, - overwrite - }, callback); - } - - this.fastExistCheckEx(ctx, pathFrom, callback, () => { - if(!overwrite) - this.fastExistCheckExReverse(ctx, pathTo, callback, go); + issuePrivilegeCheck(this, ctx, pathFrom, 'canRead', callback, () => { + issuePrivilegeCheck(this, ctx, pathTo, 'canWrite', callback, () => { + this.isLocked(ctx, pathTo, (e, isLocked) => { + if(e || isLocked) + return callback(e ? e : Errors.Locked); + + if(this._copy) + { + const go = () => + { + this._copy(pathFrom, pathTo, { + context: ctx, + depth, + overwrite + }, callback); + } + + this.fastExistCheckEx(ctx, pathFrom, callback, () => { + if(!overwrite) + this.fastExistCheckExReverse(ctx, pathTo, callback, go); + else + go(); + }) + } else - go(); + StandardMethods.standardCopy(ctx, pathFrom, this, pathTo, this, overwrite, depth, callback); }) - } - else - StandardMethods.standardCopy(ctx, pathFrom, this, pathTo, this, overwrite, depth, callback); - + }) + }) } protected _copy?(pathFrom : Path, pathTo : Path, ctx : CopyInfo, callback : ReturnCallback) : void - rename(ctx : RequestContext, pathFrom : Path, newName : string, callback : ReturnCallback) : void - rename(ctx : RequestContext, pathFrom : Path, newName : string, overwrite : boolean, callback : ReturnCallback) : void - rename(ctx : RequestContext, pathFrom : Path, newName : string, _overwrite : boolean | ReturnCallback, _callback ?: ReturnCallback) : void + rename(ctx : RequestContext, pathFrom : Path | string, newName : string, callback : ReturnCallback) : void + rename(ctx : RequestContext, pathFrom : Path | string, newName : string, overwrite : boolean, callback : ReturnCallback) : void + rename(ctx : RequestContext, _pathFrom : Path | string, newName : string, _overwrite : boolean | ReturnCallback, _callback ?: ReturnCallback) : void { const overwrite = _callback ? _overwrite as boolean : false; const callback = _callback ? _callback : _overwrite as ReturnCallback; + const pathFrom = new Path(_pathFrom); - if(pathFrom.isRoot()) - { - this.getFullPath(ctx, (e, fullPath) => { - if(fullPath.isRoot()) - return callback(Errors.InvalidOperation); + issuePrivilegeCheck(this, ctx, pathFrom, [ 'canRead', 'canWrite' ], callback, () => { + this.isLocked(ctx, pathFrom, (e, isLocked) => { + if(e || isLocked) + return callback(e ? e : Errors.Locked); - const newPath = fullPath.getParent().getChildPath(newName); - ctx.server.getFileSystem(newPath, (fs, _, subPath) => { - const go = (overwritten : boolean) => - { - ctx.server.setFileSystem(newPath, this, (successed) => { - if(!successed) - return callback(Errors.InvalidOperation); - - ctx.server.removeFileSystem(fullPath, () => callback(null, overwritten)); + if(pathFrom.isRoot()) + { + this.getFullPath(ctx, (e, fullPath) => { + if(fullPath.isRoot()) + return callback(Errors.InvalidOperation); + + const newPath = fullPath.getParent().getChildPath(newName); + issuePrivilegeCheck(this, ctx, newPath, 'canWrite', callback, () => { + ctx.server.getFileSystem(newPath, (fs, _, subPath) => { + const go = (overwritten : boolean) => + { + ctx.server.setFileSystem(newPath, this, (successed) => { + if(!successed) + return callback(Errors.InvalidOperation); + + ctx.server.removeFileSystem(fullPath, () => callback(null, overwritten)); + }) + } + + if(!subPath.isRoot()) + return go(false); + + if(!overwrite) + return callback(Errors.ResourceAlreadyExists); + + ctx.server.removeFileSystem(newPath, () => { + go(true); + }) + }) }) - } + }) + return; + } - if(!subPath.isRoot()) - return go(false); + this.fastExistCheckEx(ctx, pathFrom, callback, () => { + this.fastExistCheckExReverse(ctx, pathFrom.getParent().getChildPath(newName), callback, () => { + const newPath = pathFrom.getParent().getChildPath(newName); + this.isLocked(ctx, newPath, (e, isLocked) => { + if(e || isLocked) + return callback(e ? e : Errors.Locked); + + issuePrivilegeCheck(this, ctx, newPath, 'canWrite', callback, () => { + if(this._rename) + { + this._rename(pathFrom, newName, { + context: ctx, + destinationPath: newPath + }, callback); + return; + } + }) - if(!overwrite) - return callback(Errors.ResourceAlreadyExists); - - ctx.server.removeFileSystem(newPath, () => { - go(true); + this.move(ctx, pathFrom, pathFrom.getParent().getChildPath(newName), overwrite, callback); }) }) + }) }) - return; - } - - this.fastExistCheckEx(ctx, pathFrom, callback, () => { - this.fastExistCheckExReverse(ctx, pathFrom.getParent().getChildPath(newName), callback, () => { - if(this._rename) - { - this._rename(pathFrom, newName, { - context: ctx - }, callback); - return; - } - - this.move(ctx, pathFrom, pathFrom.getParent().getChildPath(newName), overwrite, callback); - }) }) } protected _rename?(pathFrom : Path, newName : string, ctx : RenameInfo, callback : ReturnCallback) : void - mimeType(ctx : RequestContext, path : Path, callback : ReturnCallback) : void - mimeType(ctx : RequestContext, path : Path, targetSource : boolean, callback : ReturnCallback) : void - mimeType(ctx : RequestContext, path : Path, _targetSource : boolean | ReturnCallback, _callback ?: ReturnCallback) : void + mimeType(ctx : RequestContext, path : Path | string, callback : ReturnCallback) : void + mimeType(ctx : RequestContext, path : Path | string, targetSource : boolean, callback : ReturnCallback) : void + mimeType(ctx : RequestContext, _path : Path | string, _targetSource : boolean | ReturnCallback, _callback ?: ReturnCallback) : void { const targetSource = _callback ? _targetSource as boolean : true; const callback = _callback ? _callback : _targetSource as ReturnCallback; + const path = new Path(_path); - this.fastExistCheckEx(ctx, path, callback, () => { - if(this._mimeType) - { - this._mimeType(path, { - context: ctx, - targetSource - }, callback); - return; - } + issuePrivilegeCheck(this, ctx, path, targetSource ? 'canReadContentSource' : 'canReadContentTranslated', callback, () => { + this.fastExistCheckEx(ctx, path, callback, () => { + if(this._mimeType) + { + this._mimeType(path, { + context: ctx, + targetSource + }, callback); + return; + } - StandardMethods.standardMimeType(ctx, this, path, targetSource, callback); + StandardMethods.standardMimeType(ctx, this, path, targetSource, callback); + }) }) } protected _mimeType?(path : Path, ctx : MimeTypeInfo, callback : ReturnCallback) : void - size(ctx : RequestContext, path : Path, callback : ReturnCallback) : void - size(ctx : RequestContext, path : Path, targetSource : boolean, callback : ReturnCallback) : void - size(ctx : RequestContext, path : Path, _targetSource : boolean | ReturnCallback, _callback ?: ReturnCallback) : void + size(ctx : RequestContext, path : Path | string, callback : ReturnCallback) : void + size(ctx : RequestContext, path : Path | string, targetSource : boolean, callback : ReturnCallback) : void + size(ctx : RequestContext, path : Path | string, _targetSource : boolean | ReturnCallback, _callback ?: ReturnCallback) : void { const targetSource = _callback ? _targetSource as boolean : true; const callback = _callback ? _callback : _targetSource as ReturnCallback; + const pPath = new Path(path); - this.fastExistCheckEx(ctx, path, callback, () => { - if(!this._size) - return callback(null, 0); + issuePrivilegeCheck(this, ctx, pPath, targetSource ? 'canReadContentSource' : 'canReadContentTranslated', callback, () => { + this.fastExistCheckEx(ctx, pPath, callback, () => { + if(!this._size) + return callback(null, 0); - this._size(path, { - context: ctx, - targetSource - }, callback); + this._size(pPath, { + context: ctx, + targetSource + }, callback); + }) }) } protected _size?(path : Path, ctx : SizeInfo, callback : ReturnCallback) : void - availableLocks(ctx : RequestContext, path : Path, callback : ReturnCallback) : void + availableLocks(ctx : RequestContext, path : Path | string, callback : ReturnCallback) : void { - this.fastExistCheckEx(ctx, path, callback, () => { - if(!this._availableLocks) - return callback(null, [ - new LockKind(LockScope.Exclusive, LockType.Write), - new LockKind(LockScope.Shared, LockType.Write) - ]); + const pPath = new Path(path); - this._availableLocks(path, { - context: ctx - }, callback); + issuePrivilegeCheck(this, ctx, pPath, 'canWriteLocks', callback, () => { + this.fastExistCheckEx(ctx, pPath, callback, () => { + if(!this._availableLocks) + return callback(null, [ + new LockKind(LockScope.Exclusive, LockType.Write), + new LockKind(LockScope.Shared, LockType.Write) + ]); + + this._availableLocks(pPath, { + context: ctx + }, callback); + }) }) } protected _availableLocks?(path : Path, ctx : AvailableLocksInfo, callback : ReturnCallback) : void - lockManager(ctx : RequestContext, path : Path, callback : ReturnCallback) : void + lockManager(ctx : RequestContext, path : Path | string, callback : ReturnCallback) : void { - this.fastExistCheckEx(ctx, path, callback, () => { - this._lockManager(path, { + const pPath = new Path(path); + + this.fastExistCheckEx(ctx, pPath, callback, () => { + this._lockManager(pPath, { context: ctx - }, callback); + }, (e, lm) => { + if(e) + return callback(e); + + const buffIsLocked = new BufferedIsLocked(this, ctx, pPath); + const fs = this; + + callback(null, { + getLocks(callback : ReturnCallback) : void + { + issuePrivilegeCheck(fs, ctx, pPath, 'canReadLocks', callback, () => { + lm.getLocks(callback); + }) + }, + setLock(lock : Lock, callback : SimpleCallback) : void + { + issuePrivilegeCheck(fs, ctx, pPath, 'canWriteLocks', callback, () => { + buffIsLocked.isLocked((e, isLocked) => { + if(e || isLocked) + return callback(e ? e : Errors.Locked); + + lm.setLock(lock, callback); + }) + }) + }, + removeLock(uuid : string, callback : ReturnCallback) : void + { + issuePrivilegeCheck(fs, ctx, pPath, 'canWriteLocks', callback, () => { + buffIsLocked.isLocked((e, isLocked) => { + if(e || isLocked) + return callback(e ? e : Errors.Locked); + + lm.removeLock(uuid, callback); + }) + }) + }, + getLock(uuid : string, callback : ReturnCallback) : void + { + issuePrivilegeCheck(fs, ctx, pPath, 'canReadLocks', callback, () => { + lm.getLock(uuid, callback); + }) + }, + refresh(uuid : string, timeout : number, callback : ReturnCallback) : void + { + issuePrivilegeCheck(fs, ctx, pPath, 'canWriteLocks', callback, () => { + buffIsLocked.isLocked((e, isLocked) => { + if(e || isLocked) + return callback(e ? e : Errors.Locked); + + lm.refresh(uuid, timeout, callback); + }) + }) + } + }) + }); }) } protected abstract _lockManager(path : Path, ctx : LockManagerInfo, callback : ReturnCallback) : void - propertyManager(ctx : RequestContext, path : Path, callback : ReturnCallback) : void + propertyManager(ctx : RequestContext, path : Path | string, callback : ReturnCallback) : void { - this.fastExistCheckEx(ctx, path, callback, () => { - this._propertyManager(path, { + const pPath = new Path(path); + + this.fastExistCheckEx(ctx, pPath, callback, () => { + this._propertyManager(pPath, { context: ctx - }, callback); + }, (e, pm) => { + if(e) + return callback(e); + + const buffIsLocked = new BufferedIsLocked(this, ctx, pPath); + const fs = this; + + callback(null, { + setProperty(name : string, value : ResourcePropertyValue, callback : SimpleCallback) : void + { + issuePrivilegeCheck(fs, ctx, pPath, 'canWriteProperties', callback, () => { + buffIsLocked.isLocked((e, isLocked) => { + if(e || isLocked) + return callback(e ? e : Errors.Locked); + + pm.setProperty(name, value, callback); + }) + }) + }, + getProperty(name : string, callback : ReturnCallback) : void + { + issuePrivilegeCheck(fs, ctx, pPath, 'canReadProperties', callback, () => { + pm.getProperty(name, callback); + }) + }, + removeProperty(name : string, callback : SimpleCallback) : void + { + issuePrivilegeCheck(fs, ctx, pPath, 'canWriteProperties', callback, () => { + buffIsLocked.isLocked((e, isLocked) => { + if(e || isLocked) + return callback(e ? e : Errors.Locked); + + pm.removeProperty(name, callback); + }) + }) + }, + getProperties(callback : ReturnCallback, byCopy ?: boolean) : void + { + issuePrivilegeCheck(fs, ctx, pPath, 'canReadProperties', callback, () => { + pm.getProperties(callback, byCopy); + }) + } + }) + }); }) } protected abstract _propertyManager(path : Path, ctx : PropertyManagerInfo, callback : ReturnCallback) : void - readDir(ctx : RequestContext, path : Path, callback : ReturnCallback) : void - readDir(ctx : RequestContext, path : Path, retrieveExternalFiles : boolean, callback : ReturnCallback) : void - readDir(ctx : RequestContext, path : Path, _retrieveExternalFiles : boolean | ReturnCallback, _callback ?: ReturnCallback) : void + readDir(ctx : RequestContext, path : Path | string, callback : ReturnCallback) : void + readDir(ctx : RequestContext, path : Path | string, retrieveExternalFiles : boolean, callback : ReturnCallback) : void + readDir(ctx : RequestContext, path : Path | string, _retrieveExternalFiles : boolean | ReturnCallback, _callback ?: ReturnCallback) : void { const retrieveExternalFiles = _callback ? _retrieveExternalFiles as boolean : false; - const callback = _callback ? _callback : _retrieveExternalFiles as ReturnCallback; + const __callback = _callback ? _callback : _retrieveExternalFiles as ReturnCallback; + const pPath = new Path(path); + const callback = (e ?: Error, data ?: Path[]) => { + if(e) + return _callback(e); + if(!data) + data = []; + + this.getFullPath(ctx, (e, fsFullPath) => { + new Workflow() + .each(data, (path, cb) => { + this.checkPrivilege(ctx, path, 'canReadProperties', (e, can) => { + if(e) + cb(e); + else + cb(null, can ? path : null); + }); + }) + .error((e) => __callback(e)) + .done(() => __callback(null, data.filter((p) => !!p).map((p) => p.fileName()))); + }) + } - this.fastExistCheckEx(ctx, path, callback, () => { - const next = (base : string[]) => { - if(!this._readDir) - return callback(null, base); - - this._readDir(path, { - context: ctx - }, (e, paths) => { - if(e) - return callback(e); - - if(paths.length === 0) + issuePrivilegeCheck(this, ctx, pPath, 'canReadProperties', callback, () => { + this.fastExistCheckEx(ctx, pPath, callback, () => { + const next = (base : Path[]) => { + if(!this._readDir) return callback(null, base); - if(paths[0].constructor === String) - base = base.concat(paths as string[]); - else - base = base.concat((paths as Path[]).map((p) => p.fileName())); - - callback(null, base); - }); - } + this._readDir(pPath, { + context: ctx + }, (e, paths) => { + if(e) + return callback(e); + + if(paths.length === 0) + return callback(null, base); + + if(paths[0].constructor === String) + base = base.concat((paths as string[]).map((s) => pPath.getChildPath(s))); + else + base = base.concat(paths as Path[]); + + callback(null, base); + }); + } - if(!retrieveExternalFiles) - return next([]); + if(!retrieveExternalFiles) + return next([]); - this.getFullPath(ctx, (e, thisFullPath) => { - if(e) - return callback(e); - - ctx.server.getChildFileSystems(thisFullPath.getChildPath(path), (fss) => { - next(fss.map((f) => f.path.fileName())); + this.getFullPath(ctx, (e, thisFullPath) => { + if(e) + return callback(e); + + ctx.server.getChildFileSystems(thisFullPath.getChildPath(pPath), (fss) => { + this.localize(ctx, fss.map((f) => f.path), (e, paths) => { + if(e) + return callback(e); + next(paths); + }) + }) }) }) }) } protected _readDir?(path : Path, ctx : ReadDirInfo, callback : ReturnCallback) : void - creationDate(ctx : RequestContext, path : Path, callback : ReturnCallback) : void + creationDate(ctx : RequestContext, path : Path | string, callback : ReturnCallback) : void { - this.fastExistCheckEx(ctx, path, callback, () => { - if(!this._creationDate && !this._lastModifiedDate) - return callback(null, 0); - if(!this._creationDate) - return this.lastModifiedDate(ctx, path, callback); - - this._creationDate(path, { - context: ctx - }, callback); + const pPath = new Path(path); + + issuePrivilegeCheck(this, ctx, pPath, 'canReadProperties', callback, () => { + this.fastExistCheckEx(ctx, pPath, callback, () => { + if(!this._creationDate && !this._lastModifiedDate) + return callback(null, 0); + if(!this._creationDate) + return this.lastModifiedDate(ctx, pPath, callback); + + this._creationDate(pPath, { + context: ctx + }, callback); + }) }) } protected _creationDate?(path : Path, ctx : CreationDateInfo, callback : ReturnCallback) : void - lastModifiedDate(ctx : RequestContext, path : Path, callback : ReturnCallback) : void + lastModifiedDate(ctx : RequestContext, path : Path | string, callback : ReturnCallback) : void { - this.fastExistCheckEx(ctx, path, callback, () => { - if(!this._creationDate && !this._lastModifiedDate) - return callback(null, 0); - if(!this._lastModifiedDate) - return this.creationDate(ctx, path, callback); - - this._lastModifiedDate(path, { - context: ctx - }, callback); + const pPath = new Path(path); + + issuePrivilegeCheck(this, ctx, pPath, 'canReadProperties', callback, () => { + this.fastExistCheckEx(ctx, pPath, callback, () => { + if(!this._creationDate && !this._lastModifiedDate) + return callback(null, 0); + if(!this._lastModifiedDate) + return this.creationDate(ctx, pPath, callback); + + this._lastModifiedDate(pPath, { + context: ctx + }, callback); + }) }) } protected _lastModifiedDate?(path : Path, ctx : LastModifiedDateInfo, callback : ReturnCallback) : void - webName(ctx : RequestContext, path : Path, callback : ReturnCallback) : void + webName(ctx : RequestContext, path : Path | string, callback : ReturnCallback) : void { - this.fastExistCheckEx(ctx, path, callback, () => { - if(path.isRoot()) - this.getFullPath(ctx, (e, path) => callback(e, e ? null : path.fileName())); - else - callback(null, path.fileName()); + const pPath = new Path(path); + + issuePrivilegeCheck(this, ctx, pPath, 'canReadProperties', callback, () => { + this.fastExistCheckEx(ctx, pPath, callback, () => { + if(pPath.isRoot()) + this.getFullPath(ctx, (e, pPath) => callback(e, e ? null : pPath.fileName())); + else + callback(null, pPath.fileName()); + }) }) } - displayName(ctx : RequestContext, path : Path, callback : ReturnCallback) : void + displayName(ctx : RequestContext, path : Path | string, callback : ReturnCallback) : void { - this.fastExistCheckEx(ctx, path, callback, () => { - if(!this._displayName) - return this.webName(ctx, path, callback); - - this._displayName(path, { - context: ctx - }, callback); + const pPath = new Path(path); + + issuePrivilegeCheck(this, ctx, pPath, 'canReadProperties', callback, () => { + this.fastExistCheckEx(ctx, pPath, callback, () => { + if(!this._displayName) + return this.webName(ctx, pPath, callback); + + this._displayName(pPath, { + context: ctx + }, callback); + }) }) } protected _displayName?(path : Path, ctx : DisplayNameInfo, callback : ReturnCallback) : void - type(ctx : RequestContext, path : Path, callback : ReturnCallback) : void + type(ctx : RequestContext, path : Path | string, callback : ReturnCallback) : void { - this.fastExistCheckEx(ctx, path, callback, () => { - this._type(path, { - context: ctx - }, callback); + const pPath = new Path(path); + + issuePrivilegeCheck(this, ctx, pPath, 'canReadProperties', callback, () => { + this.fastExistCheckEx(ctx, pPath, callback, () => { + this._type(pPath, { + context: ctx + }, callback); + }) }) } protected abstract _type(path : Path, ctx : TypeInfo, callback : ReturnCallback) : void addSubTree(ctx : RequestContext, subTree : SubTree, callback : SimpleCallback) addSubTree(ctx : RequestContext, resourceType : ResourceType, callback : SimpleCallback) - addSubTree(ctx : RequestContext, rootPath : Path, subTree : SubTree, callback : SimpleCallback) - addSubTree(ctx : RequestContext, rootPath : Path, resourceType : ResourceType, callback : SimpleCallback) - addSubTree(ctx : RequestContext, _rootPath : Path | SubTree | ResourceType | SimpleCallback, _tree : SubTree | ResourceType | SimpleCallback, _callback ?: SimpleCallback) + addSubTree(ctx : RequestContext, rootPath : Path | string, subTree : SubTree, callback : SimpleCallback) + addSubTree(ctx : RequestContext, rootPath : Path | string, resourceType : ResourceType, callback : SimpleCallback) + addSubTree(ctx : RequestContext, _rootPath : Path | string | SubTree | ResourceType | SimpleCallback, _tree : SubTree | ResourceType | SimpleCallback, _callback ?: SimpleCallback) { const callback = _callback ? _callback : _tree as SimpleCallback; const tree = _callback ? _tree as SubTree | ResourceType : _rootPath as SubTree | ResourceType; - const rootPath = _callback ? _rootPath as Path : new Path('/'); + const rootPath = _callback ? new Path(_rootPath as Path | string) : new Path('/'); if(tree.constructor === ResourceType) { - this.create(ctx, rootPath, tree as ResourceType, callback); + issuePrivilegeCheck(this, ctx, rootPath, 'canWrite', callback, () => { + this.create(ctx, rootPath, tree as ResourceType, callback); + }) } else { new Workflow() .each(Object.keys(tree), (name, cb) => { const value = tree[name]; + const childPath = rootPath.getChildPath(name); if(value.constructor === ResourceType) - this.addSubTree(ctx, rootPath.getChildPath(name), value, cb) + this.addSubTree(ctx, childPath, value, cb) else - this.addSubTree(ctx, rootPath.getChildPath(name), ResourceType.Directory, (e) => { + this.addSubTree(ctx, childPath, ResourceType.Directory, (e) => { if(e) return cb(e); - this.addSubTree(ctx, rootPath.getChildPath(name), value, cb); + this.addSubTree(ctx, childPath, value, cb); }) }) .error(callback) @@ -609,54 +873,66 @@ export abstract class FileSystem implements ISerializableFileSystem } } - listDeepLocks(ctx : RequestContext, startPath : Path, callback : ReturnCallback<{ [path : string] : Lock[] }>) - listDeepLocks(ctx : RequestContext, startPath : Path, depth : number, callback : ReturnCallback<{ [path : string] : Lock[] }>) - listDeepLocks(ctx : RequestContext, startPath : Path, _depth : number | ReturnCallback<{ [path : string] : Lock[] }>, _callback ?: ReturnCallback<{ [path : string] : Lock[] }>) + listDeepLocks(ctx : RequestContext, startPath : Path | string, callback : ReturnCallback<{ [path : string] : Lock[] }>) + listDeepLocks(ctx : RequestContext, startPath : Path | string, depth : number, callback : ReturnCallback<{ [path : string] : Lock[] }>) + listDeepLocks(ctx : RequestContext, startPath : Path | string, _depth : number | ReturnCallback<{ [path : string] : Lock[] }>, _callback ?: ReturnCallback<{ [path : string] : Lock[] }>) { const depth = _callback ? _depth as number : 0; const callback = _callback ? _callback : _depth as ReturnCallback<{ [path : string] : Lock[] }>; - - this.lockManager(ctx, startPath, (e, lm) => { - if(e) - return callback(e); - - lm.getLocks((e, locks) => { - if(e) - return callback(e); - - if(depth != -1) - locks = locks.filter((f) => f.depth === -1 || f.depth >= depth); - - const go = (fs : FileSystem, parentPath : Path) => + const pStartPath = new Path(startPath); + + issuePrivilegeCheck(this, ctx, startPath, 'canReadLocks', callback, () => { + this.lockManager(ctx, pStartPath, (e, lm) => { + if(e === Errors.ResourceNotFound) { - const destDepth = depth === -1 ? -1 : depth + 1; - fs.listDeepLocks(ctx, parentPath, destDepth, (e, pLocks) => { - if(e) - return callback(e); - - if(locks && locks.length > 0) - pLocks[startPath.toString()] = locks; - callback(null, pLocks); - }) + lm = { + getLocks(callback : ReturnCallback) : void + { + callback(null, []); + } + } as ILockManager; } - - if(!startPath.isRoot()) - return go(this, startPath.getParent()); + else if(e) + return callback(e); - this.getFullPath(ctx, (e, fsPath) => { + lm.getLocks((e, locks) => { if(e) return callback(e); - if(fsPath.isRoot()) + if(depth != -1) + locks = locks.filter((f) => f.depth === -1 || f.depth >= depth); + + const go = (fs : FileSystem, parentPath : Path) => { - const result = {}; - if(locks && locks.length > 0) - result[startPath.toString()] = locks; - return callback(null, result); + const destDepth = depth === -1 ? -1 : depth + 1; + fs.listDeepLocks(ctx, parentPath, destDepth, (e, pLocks) => { + if(e) + return callback(e); + + if(locks && locks.length > 0) + pLocks[pStartPath.toString()] = locks; + callback(null, pLocks); + }) } + + if(!pStartPath.isRoot()) + return go(this, pStartPath.getParent()); - ctx.server.getFileSystem(fsPath.getParent(), (fs, _, subPath) => { - go(fs, subPath); + this.getFullPath(ctx, (e, fsPath) => { + if(e) + return callback(e); + + if(fsPath.isRoot()) + { + const result = {}; + if(locks && locks.length > 0) + result[pStartPath.toString()] = locks; + return callback(null, result); + } + + ctx.server.getFileSystem(fsPath.getParent(), (fs, _, subPath) => { + go(fs, subPath); + }) }) }) }) @@ -664,10 +940,10 @@ export abstract class FileSystem implements ISerializableFileSystem } getFullPath(ctx : RequestContext, callback : ReturnCallback) - getFullPath(ctx : RequestContext, path : Path, callback : ReturnCallback) - getFullPath(ctx : RequestContext, _path : Path | ReturnCallback, _callback ?: ReturnCallback) + getFullPath(ctx : RequestContext, path : Path | string, callback : ReturnCallback) + getFullPath(ctx : RequestContext, _path : Path | string | ReturnCallback, _callback ?: ReturnCallback) { - const path = _callback ? _path as Path : undefined; + const path = _callback ? new Path(_path as Path | string) : undefined; const callback = _callback ? _callback : _path as ReturnCallback; ctx.server.getFileSystemPath(this, (fsPath) => { @@ -675,29 +951,87 @@ export abstract class FileSystem implements ISerializableFileSystem }) } - checkPrivilege(ctx : RequestContext, path : Path, privilege : BasicPrivilege, callback : ReturnCallback) - checkPrivilege(ctx : RequestContext, path : Path, privileges : BasicPrivilege[], callback : ReturnCallback) - checkPrivilege(ctx : RequestContext, path : Path, privilege : string, callback : ReturnCallback) - checkPrivilege(ctx : RequestContext, path : Path, privileges : string[], callback : ReturnCallback) - checkPrivilege(ctx : RequestContext, path : Path, privileges : string | string[], callback : ReturnCallback) + localize(ctx : RequestContext, fullPath : Path, callback : ReturnCallback) + localize(ctx : RequestContext, fullPath : Path[], callback : ReturnCallback) + localize(ctx : RequestContext, fullPath : string, callback : ReturnCallback) + localize(ctx : RequestContext, fullPath : string[], callback : ReturnCallback) + localize(ctx : RequestContext, fullPath : (string | Path)[], callback : ReturnCallback) + localize(ctx : RequestContext, fullPath : Path | string | (string | Path)[], callback : ReturnCallback) + { + this.getFullPath(ctx, (e, fsFullPath) => { + if(e) + return callback(e); + + const paths = fullPath.constructor === Array ? fullPath as any[] : [ fullPath as any ]; + + callback(null, paths + .map((p) => new Path(p)) + .map((p) => { + for(let i = 0; i < fsFullPath.paths.length; ++i) + p.removeRoot(); + return p; + }) + ); + }) + } + + checkPrivilege(ctx : RequestContext, path : Path | string, privilege : BasicPrivilege, callback : ReturnCallback) + checkPrivilege(ctx : RequestContext, path : Path | string, privileges : BasicPrivilege[], callback : ReturnCallback) + checkPrivilege(ctx : RequestContext, path : Path | string, privilege : string, callback : ReturnCallback) + checkPrivilege(ctx : RequestContext, path : Path | string, privileges : string[], callback : ReturnCallback) + checkPrivilege(ctx : RequestContext, path : Path | string, privileges : BasicPrivilege | BasicPrivilege[], callback : ReturnCallback) + checkPrivilege(ctx : RequestContext, path : Path | string, privileges : string | string[], callback : ReturnCallback) + checkPrivilege(ctx : RequestContext, path : Path | string, privileges : string | string[] | BasicPrivilege | BasicPrivilege[], callback : ReturnCallback) { if(privileges.constructor === String) privileges = [ privileges as string ]; - const resource = this.resource(ctx, path); - new Workflow() - .each(privileges as string[], (privilege, cb) => { - if(!privilege) - return cb(null, true); - - const method = ctx.server.options.privilegeManager[privilege]; - if(!method) - return cb(null, true); + this.getFullPath(ctx, path, (e, fullPath) => { + this.privilegeManager(ctx, path, (e, privilegeManager) => { + if(e) + return callback(e); - method(ctx, resource, cb); + const resource = this.resource(ctx, new Path(path)); + privilegeManager.can(fullPath, resource, privileges as string[], callback); }) - .error((e) => callback(e, false)) - .done((successes) => callback(null, successes.every((s) => !!s))); + }) + } + + privilegeManager(ctx : RequestContext, path : Path | string, callback : ReturnCallback) + { + if(!this._privilegeManager) + return callback(null, ctx.server.options.privilegeManager); + + this._privilegeManager(new Path(path), { + context: ctx + }, callback); + } + protected _privilegeManager?(path : Path, info : PrivilegeManagerInfo, callback : ReturnCallback) + + isLocked(ctx : RequestContext, path : Path | string, callback : ReturnCallback) + { + this.listDeepLocks(ctx, path, (e, locks) => { + if(e) + return callback(e); + + for(const path in locks) + if(locks[path].some((l) => ctx.user.uid !== l.userUid && l.lockKind.scope.isSame(LockScope.Exclusive))) + return callback(null, true); + + let isShared = false; + for(const path in locks) + for(const lock of locks[path]) + { + if(lock.lockKind.scope.isSame(LockScope.Shared)) + { + isShared = true; + if(lock.userUid === ctx.user.uid) + return callback(null, false); + } + } + + callback(null, isShared); + }) } serialize(callback : ReturnCallback) : void @@ -705,3 +1039,15 @@ export abstract class FileSystem implements ISerializableFileSystem this.serializer().serialize(this, callback); } } + +function issuePrivilegeCheck(fs : FileSystem, ctx : RequestContext, path : Path | string, privilege : BasicPrivilege | BasicPrivilege[], badCallback : SimpleCallback, goodCallback : () => void) +{ + fs.checkPrivilege(ctx, path, privilege, (e, can) => { + if(e) + badCallback(e); + else if(!can) + badCallback(Errors.NotEnoughPrivilege); + else + goodCallback(); + }) +} diff --git a/src/server/v2/commands/Delete.ts b/src/server/v2/commands/Delete.ts index a8567bfe..982c61fe 100644 --- a/src/server/v2/commands/Delete.ts +++ b/src/server/v2/commands/Delete.ts @@ -7,18 +7,14 @@ export default class implements HTTPMethod { ctx.noBodyExpected(() => { ctx.getResource((e, r) => { - if(e) - { - ctx.setCode(HTTPCodes.NotFound) - callback() - return; - } - ctx.checkIfHeader(r, () => { //ctx.requirePrivilege([ 'canDelete' ], r, () => { r.delete((e) => process.nextTick(() => { if(e) - ctx.setCode(HTTPCodes.InternalServerError); + { + if(!ctx.setCodeFromError(e)) + ctx.setCode(HTTPCodes.InternalServerError); + } else { ctx.setCode(HTTPCodes.OK); diff --git a/src/server/v2/commands/Get.ts b/src/server/v2/commands/Get.ts index 25c5752c..c3616f72 100644 --- a/src/server/v2/commands/Get.ts +++ b/src/server/v2/commands/Get.ts @@ -55,7 +55,8 @@ export default class implements HTTPMethod r.type((e, type) => { if(e) { - ctx.setCode(e === Errors.ResourceNotFound ? HTTPCodes.NotFound : HTTPCodes.InternalServerError) + if(!ctx.setCodeFromError(e)) + ctx.setCode(HTTPCodes.InternalServerError) return callback(); } if(!type.isFile) @@ -67,14 +68,16 @@ export default class implements HTTPMethod r.mimeType(targetSource, (e, mimeType) => process.nextTick(() => { if(e) { - ctx.setCode(e === Errors.ResourceNotFound ? HTTPCodes.NotFound : HTTPCodes.InternalServerError) + if(!ctx.setCodeFromError(e)) + ctx.setCode(HTTPCodes.InternalServerError) return callback(); } r.openReadStream(targetSource, (e, rstream) => process.nextTick(() => { if(e) { - ctx.setCode(HTTPCodes.MethodNotAllowed); + if(!ctx.setCodeFromError(e)) + ctx.setCode(HTTPCodes.MethodNotAllowed) return callback(); } //ctx.invokeEvent('read', r); diff --git a/src/server/v2/commands/Head.ts b/src/server/v2/commands/Head.ts index 7824a580..44b5ffc8 100644 --- a/src/server/v2/commands/Head.ts +++ b/src/server/v2/commands/Head.ts @@ -15,7 +15,8 @@ export default class implements HTTPMethod r.type((e, type) => { if(e) { - ctx.setCode(e === Errors.ResourceNotFound ? HTTPCodes.NotFound : HTTPCodes.InternalServerError); + if(!ctx.setCodeFromError(e)) + ctx.setCode(HTTPCodes.InternalServerError) return callback(); } if(!type.isFile) @@ -27,13 +28,17 @@ export default class implements HTTPMethod r.mimeType(targetSource, (e, mimeType) => process.nextTick(() => { if(e) { - ctx.setCode(e === Errors.ResourceNotFound ? HTTPCodes.NotFound : HTTPCodes.InternalServerError); + if(!ctx.setCodeFromError(e)) + ctx.setCode(HTTPCodes.InternalServerError) return callback(); } r.size(targetSource, (e, size) => { if(e) - ctx.setCode(e === Errors.ResourceNotFound ? HTTPCodes.NotFound : HTTPCodes.InternalServerError); + { + if(!ctx.setCodeFromError(e)) + ctx.setCode(HTTPCodes.InternalServerError) + } else { ctx.setCode(HTTPCodes.OK); diff --git a/src/server/v2/commands/Lock.ts b/src/server/v2/commands/Lock.ts index c333c42e..5a844a64 100644 --- a/src/server/v2/commands/Lock.ts +++ b/src/server/v2/commands/Lock.ts @@ -47,6 +47,7 @@ function createLock(ctx : RequestContext, data : Buffer, callback) r.listDeepLocks((e, locks) => { if(e) return callback(e); + if(Object.keys(locks).length > 0) { if(LockScope.Exclusive.isSame(type)) @@ -77,12 +78,8 @@ function createLock(ctx : RequestContext, data : Buffer, callback) callback = (e) => { if(e) { - if(e === Errors.Locked) - ctx.setCode(HTTPCodes.Locked); - else if(e === Errors.IntermediateResourceMissing || e === Errors.WrongParentTypeForCreation) - ctx.setCode(HTTPCodes.Conflict); - else - ctx.setCode(HTTPCodes.InternalServerError); + if(!ctx.setCodeFromError(e)) + ctx.setCode(HTTPCodes.InternalServerError) } else ctx.writeBody(createResponse(ctx, lock)); @@ -117,18 +114,12 @@ function createLock(ctx : RequestContext, data : Buffer, callback) function refreshLock(ctx : RequestContext, lockUUID : string, callback) { ctx.getResource((e, r) => { - if(e) - { - ctx.setCode(e === Errors.ResourceNotFound ? HTTPCodes.NotFound : HTTPCodes.InternalServerError) - callback() - return; - } - //ctx.requirePrivilege([ 'canSetLock', 'canGetLock' ], r, () => { r.lockManager((e, lm) => { if(e) { - ctx.setCode(e === Errors.ResourceNotFound ? HTTPCodes.NotFound : HTTPCodes.InternalServerError); + if(!ctx.setCodeFromError(e)) + ctx.setCode(HTTPCodes.InternalServerError) return callback(); } @@ -157,8 +148,7 @@ export default class implements HTTPMethod if(!ctx.user) { ctx.setCode(HTTPCodes.Forbidden); - callback(); - return; + return callback(); } if(ctx.headers.contentLength > 0) @@ -171,8 +161,7 @@ export default class implements HTTPMethod if(!ifHeader) { ctx.setCode(HTTPCodes.PreconditionRequired); - callback(); - return; + return callback(); } refreshLock(ctx, extractOneToken(ifHeader), callback); diff --git a/src/server/v2/commands/Mkcol.ts b/src/server/v2/commands/Mkcol.ts index c3b71ff9..b22563a5 100644 --- a/src/server/v2/commands/Mkcol.ts +++ b/src/server/v2/commands/Mkcol.ts @@ -15,26 +15,23 @@ export default class implements HTTPMethod rParent.type((e, parentType) => { if(e) { - ctx.setCode(e === Errors.ResourceNotFound ? HTTPCodes.Conflict : HTTPCodes.InternalServerError); - callback(); - return; + if(!ctx.setCodeFromError(e)) + ctx.setCode(HTTPCodes.InternalServerError) + return callback(); } if(!parentType.isDirectory) { ctx.setCode(HTTPCodes.Forbidden); - callback(); - return; + return callback(); } r.create(ResourceType.Directory, (e) => { if(e) { - if(e === Errors.WrongParentTypeForCreation) - ctx.setCode(HTTPCodes.Conflict); - else if(e === Errors.ResourceAlreadyExists) + if(e === Errors.ResourceAlreadyExists) ctx.setCode(HTTPCodes.MethodNotAllowed); - else - ctx.setCode(HTTPCodes.InternalServerError); + else if(!ctx.setCodeFromError(e)) + ctx.setCode(HTTPCodes.InternalServerError) } else ctx.setCode(HTTPCodes.Created) diff --git a/src/server/v2/commands/Move.ts b/src/server/v2/commands/Move.ts index 2ccbd223..e96bdc78 100644 --- a/src/server/v2/commands/Move.ts +++ b/src/server/v2/commands/Move.ts @@ -16,8 +16,7 @@ export function execute(ctx : RequestContext, methodName : string, privilegeName if(!destination) { ctx.setCode(HTTPCodes.BadRequest); - callback(); - return; + return callback(); } const startIndex = destination.indexOf('://'); @@ -36,16 +35,11 @@ export function execute(ctx : RequestContext, methodName : string, privilegeName const cb = (e ?: Error, overwritten ?: boolean) => { - if(e === Errors.ResourceNotFound) - ctx.setCode(HTTPCodes.NotFound); - else if(e === Errors.InsufficientStorage) - ctx.setCode(HTTPCodes.InsufficientStorage); - else if(e === Errors.Locked) - ctx.setCode(HTTPCodes.Locked); - else if(e === Errors.ResourceAlreadyExists) - ctx.setCode(HTTPCodes.Conflict); - else if(e) - ctx.setCode(HTTPCodes.InternalServerError); + if(e) + { + if(!ctx.setCodeFromError(e)) + ctx.setCode(HTTPCodes.InternalServerError) + } else if(overwritten) ctx.setCode(HTTPCodes.NoContent); else diff --git a/src/server/v2/commands/Propfind.ts b/src/server/v2/commands/Propfind.ts index 4e1ad41a..0c7adc79 100644 --- a/src/server/v2/commands/Propfind.ts +++ b/src/server/v2/commands/Propfind.ts @@ -1,6 +1,5 @@ import { HTTPCodes, RequestContext, HTTPMethod } from '../WebDAVRequest' import { XML, XMLElement } from '../../../helper/XML' -import { BasicPrivilege } from '../../../user/v2/privilege/IPrivilegeManager' import { Workflow } from '../../../helper/Workflow' import { ResourceType } from '../../../manager/v2/fileSystem/CommonTypes' import { Resource } from '../../../manager/v2/fileSystem/Resource' @@ -180,12 +179,8 @@ export default class implements HTTPMethod done(multistatus); else { - if(e === Errors.ResourceNotFound) - ctx.setCode(HTTPCodes.NotFound); - else if(e === Errors.BadAuthentication) - ctx.setCode(HTTPCodes.Unauthorized); - else - ctx.setCode(HTTPCodes.InternalServerError); + if(!ctx.setCodeFromError(e)) + ctx.setCode(HTTPCodes.InternalServerError) callback(); } }) @@ -196,12 +191,8 @@ export default class implements HTTPMethod resource.readDir(true, (e, children) => process.nextTick(() => { function err(e) { - if(e === Errors.ResourceNotFound) - ctx.setCode(HTTPCodes.NotFound); - else if(e === Errors.BadAuthentication) - ctx.setCode(HTTPCodes.Unauthorized); - else - ctx.setCode(HTTPCodes.InternalServerError); + if(!ctx.setCodeFromError(e)) + ctx.setCode(HTTPCodes.InternalServerError) callback(); } diff --git a/src/server/v2/commands/Proppatch.ts b/src/server/v2/commands/Proppatch.ts index f4c6247a..ac2b0e36 100644 --- a/src/server/v2/commands/Proppatch.ts +++ b/src/server/v2/commands/Proppatch.ts @@ -35,7 +35,7 @@ export default class implements HTTPMethod const notify = function(el : any, error : any) { - const code = error ? HTTPCodes.Conflict : HTTPCodes.OK; + const code = error ? HTTPCodes.Forbidden : HTTPCodes.OK; const propstat = response.ele('D:propstat'); propstat.ele('D:prop').ele(el.name); propstat.ele('D:status').add('HTTP/1.1 ' + code + ' ' + STATUS_CODES[code]); @@ -64,18 +64,33 @@ export default class implements HTTPMethod }) } - r.propertyManager((e, pm) => { - if(e) + r.fs.isLocked(ctx, r.path, (e, locked) => { + if(e || locked) { - ctx.setCode(e === Errors.ResourceNotFound ? HTTPCodes.NotFound : HTTPCodes.InternalServerError); + if(e) + { + if(!ctx.setCodeFromError(e)) + ctx.setCode(HTTPCodes.InternalServerError) + } + else if(locked) + ctx.setCode(HTTPCodes.Locked); return callback(); } + + r.propertyManager((e, pm) => { + if(e) + { + if(!ctx.setCodeFromError(e)) + ctx.setCode(HTTPCodes.InternalServerError) + return callback(); + } - execute('DAV:set', 'setProperty', (el, callback) => { - pm.setProperty(el.name, el.elements, callback) - }) - execute('DAV:remove', 'removeProperty', (el, callback) => { - pm.removeProperty(el.name, callback) + execute('DAV:set', 'setProperty', (el, callback) => { + pm.setProperty(el.name, el.elements, callback) + }) + execute('DAV:remove', 'removeProperty', (el, callback) => { + pm.removeProperty(el.name, callback) + }) }) }) } diff --git a/src/server/v2/commands/Put.ts b/src/server/v2/commands/Put.ts index 988cd0a1..3e26b46e 100644 --- a/src/server/v2/commands/Put.ts +++ b/src/server/v2/commands/Put.ts @@ -26,23 +26,22 @@ export default class implements HTTPMethod } else if(e) { - ctx.setCode(HTTPCodes.InternalServerError); - callback(); - return; + if(!ctx.setCodeFromError(e)) + ctx.setCode(HTTPCodes.InternalServerError); + return callback(); } else if(!type.isFile) { ctx.setCode(HTTPCodes.MethodNotAllowed); - callback(); - return; + return callback(); } r.openWriteStream(mode, targetSource, ctx.headers.contentLength, (e, wStream, created) => { if(e) { - ctx.setCode(e === Errors.IntermediateResourceMissing || e === Errors.WrongParentTypeForCreation ? HTTPCodes.Conflict : HTTPCodes.InternalServerError); - callback(); - return; + if(!ctx.setCodeFromError(e)) + ctx.setCode(e === Errors.IntermediateResourceMissing || e === Errors.WrongParentTypeForCreation ? HTTPCodes.Conflict : HTTPCodes.InternalServerError); + return callback(); } inputStream.pipe(wStream); @@ -55,7 +54,8 @@ export default class implements HTTPMethod callback(); }); wStream.on('error', (e) => { - ctx.setCode(HTTPCodes.InternalServerError) + if(!ctx.setCodeFromError(e)) + ctx.setCode(HTTPCodes.InternalServerError) callback(); }); }) diff --git a/src/server/v2/commands/Unlock.ts b/src/server/v2/commands/Unlock.ts index 5ec36eaa..c08f9727 100644 --- a/src/server/v2/commands/Unlock.ts +++ b/src/server/v2/commands/Unlock.ts @@ -50,14 +50,18 @@ export default class implements HTTPMethod r.lockManager((e, lm) => { if(e) { - ctx.setCode(e === Errors.ResourceNotFound ? HTTPCodes.NotFound : HTTPCodes.InternalServerError); + if(!ctx.setCodeFromError(e)) + ctx.setCode(HTTPCodes.InternalServerError) return callback(); } lm.getLock(token, (e, lock) => { if(e || !lock) { - ctx.setCode(HTTPCodes.Conflict); + if(!lock) + ctx.setCode(HTTPCodes.Conflict) + else if(!ctx.setCodeFromError(e)) + ctx.setCode(HTTPCodes.InternalServerError) return callback(); } @@ -69,7 +73,12 @@ export default class implements HTTPMethod lm.removeLock(lock.uuid, (e, done) => { if(e || !done) - ctx.setCode(HTTPCodes.Forbidden); + { + if(!done) + ctx.setCode(HTTPCodes.Forbidden); + else if(!ctx.setCodeFromError(e)) + ctx.setCode(HTTPCodes.InternalServerError) + } else { //ctx.invokeEvent('unlock', r, lock);