From 74094e3361ebeba29e1cc4450097d60e117ec492 Mon Sep 17 00:00:00 2001 From: Adrien Castex Date: Sun, 20 Aug 2017 12:39:09 +0200 Subject: [PATCH] Added the storage manager to allow to limit the storage --- lib/manager/v2/fileSystem/FileSystem.js | 100 +++++++++++++-- lib/manager/v2/fileSystem/StorageManager.d.ts | 35 ++++++ lib/manager/v2/fileSystem/StorageManager.js | 75 ++++++++++++ lib/manager/v2/fileSystem/export.d.ts | 1 + lib/manager/v2/fileSystem/export.js | 1 + lib/server/v2/WebDAVServerOptions.d.ts | 4 +- lib/server/v2/WebDAVServerOptions.js | 6 +- lib/server/v2/commands/Propfind.js | 1 - src/manager/v2/fileSystem/FileSystem.ts | 112 +++++++++++++++-- src/manager/v2/fileSystem/StorageManager.ts | 115 ++++++++++++++++++ src/manager/v2/fileSystem/export.ts | 1 + src/server/v2/WebDAVServerOptions.ts | 10 +- src/server/v2/commands/Propfind.ts | 3 +- 13 files changed, 439 insertions(+), 25 deletions(-) create mode 100644 lib/manager/v2/fileSystem/StorageManager.d.ts create mode 100644 lib/manager/v2/fileSystem/StorageManager.js create mode 100644 src/manager/v2/fileSystem/StorageManager.ts diff --git a/lib/manager/v2/fileSystem/FileSystem.js b/lib/manager/v2/fileSystem/FileSystem.js index c7fc0e5f..e4cde8f9 100644 --- a/lib/manager/v2/fileSystem/FileSystem.js +++ b/lib/manager/v2/fileSystem/FileSystem.js @@ -1,5 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); +var stream_1 = require("stream"); var LockScope_1 = require("../../../resource/lock/LockScope"); var LockType_1 = require("../../../resource/lock/LockType"); var LockKind_1 = require("../../../resource/lock/LockKind"); @@ -154,10 +155,21 @@ var FileSystem = (function () { this.emit('before-create', ctx, path, { type: type, createIntermediates: createIntermediates }); issuePrivilegeCheck(this, ctx, path, 'canWrite', callback, function () { var go = function () { - _this._create(path, { - context: ctx, - type: type - }, callback); + ctx.server.options.storageManager.evaluateCreate(ctx, _this, path, type, function (size) { + ctx.server.options.storageManager.reserve(ctx, _this, size, function (reserved) { + if (!reserved) + return callback(Errors_1.Errors.InsufficientStorage); + _this._create(path, { + context: ctx, + type: type + }, function (e) { + if (e) + ctx.server.options.storageManager.reserve(ctx, _this, -size, function () { return callback(e); }); + else + callback(); + }); + }); + }); }; _this.isLocked(ctx, path, function (e, locked) { if (e || locked) @@ -239,7 +251,19 @@ var FileSystem = (function () { _this._delete(path, { context: ctx, depth: depth - }, callback); + }, function (e) { + if (!e) { + _this.type(ctx, path, function (e, type) { + ctx.server.options.storageManager.evaluateCreate(ctx, _this, path, type, function (size) { + ctx.server.options.storageManager.reserve(ctx, _this, -size, function () { + callback(); + }); + }); + }); + } + else + callback(e); + }); }); }); }); @@ -279,7 +303,7 @@ var FileSystem = (function () { _this.isLocked(ctx, path, function (e, isLocked) { if (e || isLocked) return callback(e ? e : Errors_1.Errors.Locked); - var go = function (callback) { + var finalGo = function (callback) { _this._openWriteStream(path, { context: ctx, estimatedSize: estimatedSize, @@ -287,6 +311,51 @@ var FileSystem = (function () { mode: mode }, function (e, wStream) { return callback(e, wStream, created); }); }; + var go = function (callback) { + _this.size(ctx, path, true, function (e, size) { + ctx.server.options.storageManager.evaluateContent(ctx, _this, size, function (sizeStored) { + if (estimatedSize === undefined || estimatedSize === null || estimatedSize.constructor === Number && estimatedSize <= 0) { + ctx.server.options.storageManager.available(ctx, _this, function (available) { + if (available === -1) + return finalGo(callback); + if (available === 0) + return callback(Errors_1.Errors.InsufficientStorage); + var nb = 0; + finalGo(function (e, wStream, created) { + if (e) + return callback(e, wStream, created); + var stream = new stream_1.Transform({ + transform: function (chunk, encoding, callback) { + nb += chunk.length; + if (nb > available) + callback(Errors_1.Errors.InsufficientStorage); + else + callback(null, chunk, encoding); + } + }); + stream.pipe(wStream); + stream.on('finish', function () { + ctx.server.options.storageManager.reserve(ctx, _this, nb, function (reserved) { + if (!reserved) + stream.emit('error', Errors_1.Errors.InsufficientStorage); + }); + }); + callback(e, stream, created); + }); + }); + } + else { + ctx.server.options.storageManager.evaluateContent(ctx, _this, estimatedSize, function (estimatedSizeStored) { + ctx.server.options.storageManager.reserve(ctx, _this, estimatedSizeStored - sizeStored, function (reserved) { + if (!reserved) + return callback(Errors_1.Errors.InsufficientStorage); + finalGo(callback); + }); + }); + } + }); + }); + }; var createAndGo = function (intermediates) { _this.create(ctx, path, CommonTypes_1.ResourceType.File, intermediates, function (e) { if (e) @@ -671,8 +740,25 @@ var FileSystem = (function () { }); }, getProperties: function (callback, byCopy) { + var _this = this; issuePrivilegeCheck(fs, ctx, pPath, 'canReadProperties', callback, function () { - pm.getProperties(callback, byCopy); + pm.getProperties(function (e, bag) { + if (!bag) + return callback(e, bag); + ctx.server.options.storageManager.available(ctx, _this, function (availableSize) { + if (availableSize === -1) + return callback(e, bag); + ctx.server.options.storageManager.reserved(ctx, _this, function (reservedSize) { + bag['DAV:quota-available-bytes'] = { + value: availableSize.toString() + }; + bag['DAV:quota-used-bytes'] = { + value: reservedSize.toString() + }; + callback(e, bag); + }); + }); + }, byCopy); }); } }); diff --git a/lib/manager/v2/fileSystem/StorageManager.d.ts b/lib/manager/v2/fileSystem/StorageManager.d.ts new file mode 100644 index 00000000..1bd76ee7 --- /dev/null +++ b/lib/manager/v2/fileSystem/StorageManager.d.ts @@ -0,0 +1,35 @@ +import { RequestContext } from '../../../server/v2/RequestContext'; +import { Path } from '../Path'; +import { ResourceType, ResourcePropertyValue, PropertyAttributes } from './CommonTypes'; +import { FileSystem } from './FileSystem'; +export declare type IStorageManagerEvaluateCallback = (size: number) => void; +export interface IStorageManager { + reserve(ctx: RequestContext, fs: FileSystem, size: number, callback: (reserved: boolean) => void): void; + evaluateCreate(ctx: RequestContext, fs: FileSystem, path: Path, type: ResourceType, callback: IStorageManagerEvaluateCallback): void; + evaluateContent(ctx: RequestContext, fs: FileSystem, expectedSize: number, callback: IStorageManagerEvaluateCallback): void; + evaluateProperty(ctx: RequestContext, fs: FileSystem, name: string, value: ResourcePropertyValue, attributes: PropertyAttributes, callback: IStorageManagerEvaluateCallback): void; + available(ctx: RequestContext, fs: FileSystem, callback: (available: number) => void): void; + reserved(ctx: RequestContext, fs: FileSystem, callback: (reserved: number) => void): void; +} +export declare class NoStorageManager implements IStorageManager { + reserve(ctx: RequestContext, fs: FileSystem, size: number, callback: (reserved: boolean) => void): void; + evaluateCreate(ctx: RequestContext, fs: FileSystem, path: Path, type: ResourceType, callback: IStorageManagerEvaluateCallback): void; + evaluateContent(ctx: RequestContext, fs: FileSystem, expectedSize: number, callback: IStorageManagerEvaluateCallback): void; + evaluateProperty(ctx: RequestContext, fs: FileSystem, name: string, value: ResourcePropertyValue, attributes: PropertyAttributes, callback: IStorageManagerEvaluateCallback): void; + available(ctx: RequestContext, fs: FileSystem, callback: (available: number) => void): void; + reserved(ctx: RequestContext, fs: FileSystem, callback: (reserved: number) => void): void; +} +export declare class PerUserStorageManager implements IStorageManager { + limitPerUser: number; + storage: { + [UUID: string]: number; + }; + constructor(limitPerUser: number); + reserve(ctx: RequestContext, fs: FileSystem, size: number, callback: (reserved: boolean) => void): void; + evaluateCreate(ctx: RequestContext, fs: FileSystem, path: Path, type: ResourceType, callback: IStorageManagerEvaluateCallback): void; + evaluateContent(ctx: RequestContext, fs: FileSystem, expectedSize: number, callback: IStorageManagerEvaluateCallback): void; + evalPropValue(value: ResourcePropertyValue): number; + evaluateProperty(ctx: RequestContext, fs: FileSystem, name: string, value: ResourcePropertyValue, attributes: PropertyAttributes, callback: IStorageManagerEvaluateCallback): void; + available(ctx: RequestContext, fs: FileSystem, callback: (available: number) => void): void; + reserved(ctx: RequestContext, fs: FileSystem, callback: (reserved: number) => void): void; +} diff --git a/lib/manager/v2/fileSystem/StorageManager.js b/lib/manager/v2/fileSystem/StorageManager.js new file mode 100644 index 00000000..3e21bb83 --- /dev/null +++ b/lib/manager/v2/fileSystem/StorageManager.js @@ -0,0 +1,75 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var NoStorageManager = (function () { + function NoStorageManager() { + } + NoStorageManager.prototype.reserve = function (ctx, fs, size, callback) { + callback(true); + }; + NoStorageManager.prototype.evaluateCreate = function (ctx, fs, path, type, callback) { + callback(0); + }; + NoStorageManager.prototype.evaluateContent = function (ctx, fs, expectedSize, callback) { + callback(0); + }; + NoStorageManager.prototype.evaluateProperty = function (ctx, fs, name, value, attributes, callback) { + callback(0); + }; + NoStorageManager.prototype.available = function (ctx, fs, callback) { + callback(-1); + }; + NoStorageManager.prototype.reserved = function (ctx, fs, callback) { + callback(0); + }; + return NoStorageManager; +}()); +exports.NoStorageManager = NoStorageManager; +var PerUserStorageManager = (function () { + function PerUserStorageManager(limitPerUser) { + this.limitPerUser = limitPerUser; + this.storage = {}; + } + PerUserStorageManager.prototype.reserve = function (ctx, fs, size, callback) { + var nb = this.storage[ctx.user.uid]; + if (nb === undefined) + nb = 0; + nb += size; + if (nb > this.limitPerUser) + return callback(false); + this.storage[ctx.user.uid] = Math.max(0, nb); + callback(true); + }; + PerUserStorageManager.prototype.evaluateCreate = function (ctx, fs, path, type, callback) { + fs.getFullPath(ctx, path, function (e, fullPath) { + callback(fullPath.toString().length); + }); + }; + PerUserStorageManager.prototype.evaluateContent = function (ctx, fs, expectedSize, callback) { + callback(expectedSize); + }; + PerUserStorageManager.prototype.evalPropValue = function (value) { + var _this = this; + if (!value) + return 0; + if (value.constructor === String) + return value.length; + if (Array.isArray(value)) + return value.map(function (el) { return _this.evalPropValue(el); }).reduce(function (p, n) { return p + n; }, 0); + var xml = value; + var attributesLength = Object.keys(xml.attributes).map(function (at) { return at.length + xml.attributes[at].length; }).reduce(function (p, n) { return p + n; }, 0); + return xml.name.length + attributesLength + (xml.elements && xml.elements.length > 0 ? this.evalPropValue(xml.elements) : 0); + }; + PerUserStorageManager.prototype.evaluateProperty = function (ctx, fs, name, value, attributes, callback) { + callback(name.length + Object.keys(attributes).map(function (ak) { return attributes[ak].length + ak.length; }).reduce(function (p, n) { return p + n; }, 0) + this.evalPropValue(value)); + }; + PerUserStorageManager.prototype.available = function (ctx, fs, callback) { + var nb = this.storage[ctx.user.uid]; + callback(nb === undefined ? this.limitPerUser : this.limitPerUser - nb); + }; + PerUserStorageManager.prototype.reserved = function (ctx, fs, callback) { + var nb = this.storage[ctx.user.uid]; + callback(nb === undefined ? 0 : nb); + }; + return PerUserStorageManager; +}()); +exports.PerUserStorageManager = PerUserStorageManager; diff --git a/lib/manager/v2/fileSystem/export.d.ts b/lib/manager/v2/fileSystem/export.d.ts index fea24af6..500d31f1 100644 --- a/lib/manager/v2/fileSystem/export.d.ts +++ b/lib/manager/v2/fileSystem/export.d.ts @@ -7,3 +7,4 @@ export * from './Resource'; export * from './Serialization'; export * from './StandardMethods'; export * from './ContextInfo'; +export * from './StorageManager'; diff --git a/lib/manager/v2/fileSystem/export.js b/lib/manager/v2/fileSystem/export.js index f15bd025..0688c3bd 100644 --- a/lib/manager/v2/fileSystem/export.js +++ b/lib/manager/v2/fileSystem/export.js @@ -11,3 +11,4 @@ __export(require("./PropertyManager")); __export(require("./Resource")); __export(require("./Serialization")); __export(require("./StandardMethods")); +__export(require("./StorageManager")); diff --git a/lib/server/v2/WebDAVServerOptions.d.ts b/lib/server/v2/WebDAVServerOptions.d.ts index fa12509d..bcee5511 100644 --- a/lib/server/v2/WebDAVServerOptions.d.ts +++ b/lib/server/v2/WebDAVServerOptions.d.ts @@ -1,9 +1,10 @@ /// +import { IStorageManager } from '../../manager/v2/fileSystem/StorageManager'; +import { FileSystemSerializer } from '../../manager/v2/fileSystem/Serialization'; import { HTTPAuthentication } from '../../user/v2/authentication/HTTPAuthentication'; import { Writable, Readable } from 'stream'; import { PrivilegeManager } from '../../user/v2/privilege/PrivilegeManager'; import { FileSystem } from '../../manager/v2/fileSystem/FileSystem'; -import { FileSystemSerializer } from '../../manager/v2/fileSystem/Serialization'; import * as https from 'https'; export interface IAutoSave { treeFilePath: string; @@ -30,6 +31,7 @@ export declare class WebDAVServerOptions { version?: string; autoSave?: IAutoSave; autoLoad?: IAutoLoad; + storageManager?: IStorageManager; } export default WebDAVServerOptions; export declare function setDefaultServerOptions(options: WebDAVServerOptions): WebDAVServerOptions; diff --git a/lib/server/v2/WebDAVServerOptions.js b/lib/server/v2/WebDAVServerOptions.js index b2486282..2d6cff88 100644 --- a/lib/server/v2/WebDAVServerOptions.js +++ b/lib/server/v2/WebDAVServerOptions.js @@ -1,9 +1,10 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); +var StorageManager_1 = require("../../manager/v2/fileSystem/StorageManager"); var HTTPDigestAuthentication_1 = require("../../user/v2/authentication/HTTPDigestAuthentication"); -var PrivilegeManager_1 = require("../../user/v2/privilege/PrivilegeManager"); -var SimpleUserManager_1 = require("../../user/v2/simple/SimpleUserManager"); var VirtualFileSystem_1 = require("../../manager/v2/instances/VirtualFileSystem"); +var SimpleUserManager_1 = require("../../user/v2/simple/SimpleUserManager"); +var PrivilegeManager_1 = require("../../user/v2/privilege/PrivilegeManager"); var WebDAVServerOptions = (function () { function WebDAVServerOptions() { this.requireAuthentification = false; @@ -19,6 +20,7 @@ var WebDAVServerOptions = (function () { this.version = '1.8.0'; this.autoSave = null; this.autoLoad = null; + this.storageManager = new StorageManager_1.NoStorageManager(); } return WebDAVServerOptions; }()); diff --git a/lib/server/v2/commands/Propfind.js b/lib/server/v2/commands/Propfind.js index 1d823c08..16ad72a9 100644 --- a/lib/server/v2/commands/Propfind.js +++ b/lib/server/v2/commands/Propfind.js @@ -313,7 +313,6 @@ var default_1 = (function () { for (var name_1 in properties) { if (reqBody.mustDisplay(name_1)) { var tag = prop.ele(name_1); - console.log(name_1, tag); if (reqBody.mustDisplayValue(name_1)) { var property = properties[name_1]; if (tag.attributes) diff --git a/src/manager/v2/fileSystem/FileSystem.ts b/src/manager/v2/fileSystem/FileSystem.ts index 3ca3d9e9..5b77f047 100644 --- a/src/manager/v2/fileSystem/FileSystem.ts +++ b/src/manager/v2/fileSystem/FileSystem.ts @@ -1,5 +1,5 @@ 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 { Readable, Writable, Transform } from 'stream' import { RequestContext } from '../../../server/v2/RequestContext' import { FileSystemEvent, WebDAVServer } from '../../../server/v2/webDAVServer/WebDAVServer' import { BasicPrivilege, PrivilegeManager } from '../../../user/v2/privilege/PrivilegeManager' @@ -11,12 +11,14 @@ import { Errors } from '../../../Errors' import { Lock } from '../../../resource/lock/Lock' import { Path } from '../Path' import { ResourceType, SimpleCallback, Return2Callback, ReturnCallback, SubTree, OpenWriteStreamMode, ResourcePropertyValue, PropertyAttributes } from './CommonTypes' +import { XMLElement } from 'xml-js-builder' import { ContextualFileSystem } from './ContextualFileSystem' import { ILockManager } from './LockManager' import { IPropertyManager, PropertyBag } from './PropertyManager' import { Resource } from './Resource' import { StandardMethods } from './StandardMethods' import { ISerializableFileSystem, FileSystemSerializer } from './Serialization' +import { IStorageManager } from './StorageManager' import * as mimeTypes from 'mime-types' import * as crypto from 'crypto' @@ -215,10 +217,22 @@ export abstract class FileSystem implements ISerializableFileSystem issuePrivilegeCheck(this, ctx, path, 'canWrite', callback, () => { const go = () => { - this._create(path, { - context: ctx, - type - }, callback); + ctx.server.options.storageManager.evaluateCreate(ctx, this, path, type, (size) => { + ctx.server.options.storageManager.reserve(ctx, this, size, (reserved) => { + if(!reserved) + return callback(Errors.InsufficientStorage); + + this._create(path, { + context: ctx, + type + }, (e) => { + if(e) + ctx.server.options.storageManager.reserve(ctx, this, -size, () => callback(e)); + else + callback(); + }); + }) + }) } this.isLocked(ctx, path, (e, locked) => { @@ -337,7 +351,20 @@ export abstract class FileSystem implements ISerializableFileSystem this._delete(path, { context: ctx, depth - }, callback); + }, (e) => { + if(!e) + { + this.type(ctx, path, (e, type) => { + ctx.server.options.storageManager.evaluateCreate(ctx, this, path, type, (size) => { + ctx.server.options.storageManager.reserve(ctx, this, -size, () => { + callback(); + }) + }) + }) + } + else + callback(e); + }); }) }) }) @@ -457,7 +484,7 @@ export abstract class FileSystem implements ISerializableFileSystem if(e || isLocked) return callback(e ? e : Errors.Locked); - const go = (callback : Return2Callback) => + const finalGo = (callback : Return2Callback) => { this._openWriteStream(path, { context: ctx, @@ -466,6 +493,57 @@ export abstract class FileSystem implements ISerializableFileSystem mode }, (e, wStream) => callback(e, wStream, created)); } + const go = (callback : Return2Callback) => + { + this.size(ctx, path, true, (e, size) => { + ctx.server.options.storageManager.evaluateContent(ctx, this, size, (sizeStored) => { + if(estimatedSize === undefined || estimatedSize === null || estimatedSize.constructor === Number && estimatedSize <= 0) + { + ctx.server.options.storageManager.available(ctx, this, (available) => { + if(available === -1) + return finalGo(callback); + if(available === 0) + return callback(Errors.InsufficientStorage); + + let nb = 0; + finalGo((e, wStream, created) => { + if(e) + return callback(e, wStream, created); + + const stream = new Transform({ + transform(chunk, encoding, callback) + { + nb += chunk.length; + if(nb > available) + callback(Errors.InsufficientStorage); + else + callback(null, chunk, encoding); + } + }); + stream.pipe(wStream); + stream.on('finish', () => { + ctx.server.options.storageManager.reserve(ctx, this, nb, (reserved) => { + if(!reserved) + stream.emit('error', Errors.InsufficientStorage); + }) + }) + callback(e, stream, created); + }) + }) + } + else + { + ctx.server.options.storageManager.evaluateContent(ctx, this, estimatedSize, (estimatedSizeStored) => { + ctx.server.options.storageManager.reserve(ctx, this, estimatedSizeStored - sizeStored, (reserved) => { + if(!reserved) + return callback(Errors.InsufficientStorage); + finalGo(callback); + }) + }) + } + }) + }) + } const createAndGo = (intermediates : boolean) => { @@ -1094,7 +1172,25 @@ export abstract class FileSystem implements ISerializableFileSystem getProperties(callback : ReturnCallback, byCopy ?: boolean) : void { issuePrivilegeCheck(fs, ctx, pPath, 'canReadProperties', callback, () => { - pm.getProperties(callback, byCopy); + pm.getProperties((e, bag) => { + if(!bag) + return callback(e, bag); + + ctx.server.options.storageManager.available(ctx, this, (availableSize) => { + if(availableSize === -1) + return callback(e, bag); + + ctx.server.options.storageManager.reserved(ctx, this, (reservedSize) => { + bag['DAV:quota-available-bytes'] = { + value: availableSize.toString() + }; + bag['DAV:quota-used-bytes'] = { + value: reservedSize.toString() + }; + callback(e, bag); + }) + }) + }, byCopy); }) } }) diff --git a/src/manager/v2/fileSystem/StorageManager.ts b/src/manager/v2/fileSystem/StorageManager.ts new file mode 100644 index 00000000..ccd62e9b --- /dev/null +++ b/src/manager/v2/fileSystem/StorageManager.ts @@ -0,0 +1,115 @@ +import { RequestContext } from '../../../server/v2/RequestContext' +import { Path } from '../Path' +import { ResourceType, ResourcePropertyValue, PropertyAttributes } from './CommonTypes' +import { XMLElement } from 'xml-js-builder' +import { FileSystem } from './FileSystem' + +export type IStorageManagerEvaluateCallback = (size : number) => void; + +export interface IStorageManager +{ + reserve(ctx : RequestContext, fs : FileSystem, size : number, callback : (reserved : boolean) => void) : void + + evaluateCreate(ctx : RequestContext, fs : FileSystem, path : Path, type : ResourceType, callback : IStorageManagerEvaluateCallback) : void + evaluateContent(ctx : RequestContext, fs : FileSystem, expectedSize : number, callback : IStorageManagerEvaluateCallback) : void + evaluateProperty(ctx : RequestContext, fs : FileSystem, name : string, value : ResourcePropertyValue, attributes : PropertyAttributes, callback : IStorageManagerEvaluateCallback) : void + + available(ctx : RequestContext, fs : FileSystem, callback : (available : number) => void) : void + reserved(ctx : RequestContext, fs : FileSystem, callback : (reserved : number) => void) : void +} + +export class NoStorageManager implements IStorageManager +{ + reserve(ctx : RequestContext, fs : FileSystem, size : number, callback : (reserved : boolean) => void) : void + { + callback(true); + } + + evaluateCreate(ctx : RequestContext, fs : FileSystem, path : Path, type : ResourceType, callback : IStorageManagerEvaluateCallback) : void + { + callback(0); + } + evaluateContent(ctx : RequestContext, fs : FileSystem, expectedSize : number, callback : IStorageManagerEvaluateCallback) : void + { + callback(0); + } + evaluateProperty(ctx : RequestContext, fs : FileSystem, name : string, value : ResourcePropertyValue, attributes : PropertyAttributes, callback : IStorageManagerEvaluateCallback) : void + { + callback(0); + } + + available(ctx : RequestContext, fs : FileSystem, callback : (available : number) => void) : void + { + callback(-1); + } + reserved(ctx : RequestContext, fs : FileSystem, callback : (reserved : number) => void) : void + { + callback(0); + } +} + +export class PerUserStorageManager implements IStorageManager +{ + storage : { + [UUID : string] : number + } + + constructor(public limitPerUser : number) + { + this.storage = {}; + } + + reserve(ctx : RequestContext, fs : FileSystem, size : number, callback : (reserved : boolean) => void) : void + { + let nb = this.storage[ctx.user.uid]; + if(nb === undefined) + nb = 0; + nb += size; + + if(nb > this.limitPerUser) + return callback(false); + + this.storage[ctx.user.uid] = Math.max(0, nb); + callback(true); + } + + evaluateCreate(ctx : RequestContext, fs : FileSystem, path : Path, type : ResourceType, callback : IStorageManagerEvaluateCallback) : void + { + fs.getFullPath(ctx, path, (e, fullPath) => { + callback(fullPath.toString().length); + }) + } + evaluateContent(ctx : RequestContext, fs : FileSystem, expectedSize : number, callback : IStorageManagerEvaluateCallback) : void + { + callback(expectedSize); + } + + evalPropValue(value : ResourcePropertyValue) : number + { + if(!value) + return 0; + if(value.constructor === String) + return (value as String).length; + if(Array.isArray(value)) + return (value as XMLElement[]).map((el) => this.evalPropValue(el)).reduce((p, n) => p + n, 0); + + const xml = value as XMLElement; + const attributesLength = Object.keys(xml.attributes).map((at) => at.length + xml.attributes[at].length).reduce((p, n) => p + n, 0); + return xml.name.length + attributesLength + (xml.elements && xml.elements.length > 0 ? this.evalPropValue(xml.elements) : 0); + } + evaluateProperty(ctx : RequestContext, fs : FileSystem, name : string, value : ResourcePropertyValue, attributes : PropertyAttributes, callback : IStorageManagerEvaluateCallback) : void + { + callback(name.length + Object.keys(attributes).map((ak) => attributes[ak].length + ak.length).reduce((p, n) => p + n, 0) + this.evalPropValue(value)); + } + + available(ctx : RequestContext, fs : FileSystem, callback : (available : number) => void) : void + { + const nb = this.storage[ctx.user.uid]; + callback(nb === undefined ? this.limitPerUser : this.limitPerUser - nb); + } + reserved(ctx : RequestContext, fs : FileSystem, callback : (reserved : number) => void) : void + { + const nb = this.storage[ctx.user.uid]; + callback(nb === undefined ? 0 : nb); + } +} \ No newline at end of file diff --git a/src/manager/v2/fileSystem/export.ts b/src/manager/v2/fileSystem/export.ts index 64a5f62a..2df09f14 100644 --- a/src/manager/v2/fileSystem/export.ts +++ b/src/manager/v2/fileSystem/export.ts @@ -8,3 +8,4 @@ export * from './Resource' export * from './Serialization' export * from './StandardMethods' export * from './ContextInfo' +export * from './StorageManager' diff --git a/src/server/v2/WebDAVServerOptions.ts b/src/server/v2/WebDAVServerOptions.ts index 526df364..49e5f595 100644 --- a/src/server/v2/WebDAVServerOptions.ts +++ b/src/server/v2/WebDAVServerOptions.ts @@ -1,14 +1,15 @@ -import { HTTPBasicAuthentication } from '../../user/v2/authentication/HTTPBasicAuthentication' +import { IStorageManager, NoStorageManager } from '../../manager/v2/fileSystem/StorageManager' import { HTTPDigestAuthentication } from '../../user/v2/authentication/HTTPDigestAuthentication' +import { HTTPBasicAuthentication } from '../../user/v2/authentication/HTTPBasicAuthentication' +import { FileSystemSerializer } from '../../manager/v2/fileSystem/Serialization' import { HTTPAuthentication } from '../../user/v2/authentication/HTTPAuthentication' import { Writable, Readable } from 'stream' -import { PrivilegeManager } from '../../user/v2/privilege/PrivilegeManager' +import { VirtualFileSystem } from '../../manager/v2/instances/VirtualFileSystem' import { SimpleUserManager } from '../../user/v2/simple/SimpleUserManager' +import { PrivilegeManager } from '../../user/v2/privilege/PrivilegeManager' import { RootResource } from '../../resource/std/RootResource' import { IUserManager } from '../../user/v2/IUserManager' -import { VirtualFileSystem } from '../../manager/v2/instances/VirtualFileSystem' import { FileSystem } from '../../manager/v2/fileSystem/FileSystem' -import { FileSystemSerializer } from '../../manager/v2/fileSystem/Serialization' import * as https from 'https' export interface IAutoSave @@ -41,6 +42,7 @@ export class WebDAVServerOptions version ?: string = '1.8.0' autoSave ?: IAutoSave = null autoLoad ?: IAutoLoad = null + storageManager ?: IStorageManager = new NoStorageManager() } export default WebDAVServerOptions; diff --git a/src/server/v2/commands/Propfind.ts b/src/server/v2/commands/Propfind.ts index 9bcc2545..0034d557 100644 --- a/src/server/v2/commands/Propfind.ts +++ b/src/server/v2/commands/Propfind.ts @@ -472,7 +472,6 @@ export default class implements HTTPMethod if(reqBody.mustDisplay(name)) { const tag = prop.ele(name); - console.log(name, tag); if(reqBody.mustDisplayValue(name)) { const property = properties[name]; @@ -481,7 +480,7 @@ export default class implements HTTPMethod tag.attributes[attName] = property.attributes[attName]; else tag.attributes = property.attributes; - + tag.add(property.value); } }