/*---------------------------------------------------------------------------*/
/* webdavservice.cpp                                                         */
/* copyright (c) innovaphone 2016                                            */
/*---------------------------------------------------------------------------*/

#include <memory>

#include <platform.h>
#include "sdk/common/os/iomux.h"
#include "sdk/common/interface/webserver_plugin.h"
#include "sdk/common/interface/time.h"
#include "sdk/common/interface/database.h"
#include "sdk/common/interface/task.h"
#include "sdk/common/interface/dbfiles.h"
#include "sdk/common/interface/random.h"
#include "common/ilib/str.h"
#include "common/ilib/uri.h"
#include "common/ilib/xml.h"
#include "../lib/webdavservice.h"

WebdavNotification::WebdavNotification(class UWebdavNotification * notification, const char * appName)
{
    if (notification) this->webdavNotification = notification;
    else webdavNotification = 0;
    if (appName) this->appName = _strdup(appName);
    else this->appName = 0;
}
WebdavNotification::~WebdavNotification()
{
    if (this->appName) {
        free(this->appName); this->appName = 0;
    }
}

WebdavService::WebdavService(class IIoMux * const iomux, class UWebdavService * service, class IDbFiles * dbFiles, class IDatabase * database, class IInstanceLog * const log, const char * webserverPath, const char * rootFolder) :
log(log)
{
    log->Log(LOG_WEBDAV_SERVICE, "WebdavService(%08x)::WebdavService webserverPath:%s", this, webserverPath);
    this->iomux = iomux;
    this->dbFiles = dbFiles;
    this->database = database;
    this->webserverPath = _strdup(webserverPath);
    this->completed = false;
    this->rootFolder = 0;
    this->service = service;
    this->closing = false;
    if (rootFolder) this->rootFolder = _strdup(rootFolder);
}

WebdavService::~WebdavService()
{
    log->Log(LOG_WEBDAV_SERVICE, "WebdavService(%08x)::~WebdavService", this);
    free(webserverPath);
    if (rootFolder) free(rootFolder);
}

void WebdavService::Start()
{
    this->completed = false;
    this->dbFiles->Start(this, this->database, 0);
}

void WebdavService::Close()
{
    log->Log(LOG_WEBDAV_SERVICE, "WebdavService(%08x)::Close", this);
    closing = true;
    while (!notifications.empty()){
        WebdavNotification * notification = notifications.front();
        delete notification;
    }
    while (!locks.empty()){
        WebdavLock * lock = locks.front();
        delete lock;
    }
    
    if (!tasks.empty()) {
        for (class UWebdavServiceTask* task = tasks.front(); task; task = task->goNext()) {

            
            task->Close();
        }
    }
    else
        this->CloseComplete(0);
}

void WebdavService::CloseComplete(class UWebdavServiceTask * task)
{
    if (task == 0)
        this->iomux->SetExec(this, NULL);
    else {
        task->remove();
        if (tasks.empty() && closing) {
            this->iomux->SetExec(this, NULL);
        }
    }

}

void WebdavService::TaskComplete(class ITask * const task)
{   
    service->WebdavServiceInitComplete();
    this->completed = true;
    delete task;
}
void WebdavService::TaskFailed(class ITask * const task)
{
    delete task;
}

void WebdavService::IoExec(void * execContext)
{
    if (closing) service->WebdavServiceClosed();
}


bool WebdavService::WebRequestWebdavService(IWebserverPlugin * const webserverPlugin, ws_request_type_t requestType, char * resourceName, ulong64 dataSize, bool decoded)
{
    log->Log(LOG_WEBDAV_SERVICE, "WebdavService(%08x)::WebRequestWebdavService requestType:%i resourceName:%s dataSize:%llu", this, requestType, resourceName, dataSize);
    if (!decoded) str::from_url(resourceName);
    if (this->completed){
        char * rootFolderPath = resourceName;
        if (rootFolder)
        {
            unsigned rootFolderlength = (unsigned)strlen(rootFolder);
            unsigned resourceNamelength = (unsigned)strlen(resourceName);
            if (rootFolderlength <= resourceNamelength) {
                if ((resourceName[rootFolderlength + 1] == '/' || resourceName[rootFolderlength + 1] == '\0'))
                {
                    if (strncmp(&resourceName[1], rootFolder, rootFolderlength) == 0)
                    {
                        resourceName = resourceName + rootFolderlength + 1;
                        if (resourceName[rootFolderlength + 1] == '\0') resourceName[rootFolderlength + 1] = '/';
                    }
                    else {
                        webserverPlugin->Cancel(WSP_CANCEL_ACCESS_DENIED);
                        return false;
                    }
                }
                else {
                    webserverPlugin->Cancel(WSP_CANCEL_ACCESS_DENIED);
                    return false;
                }
            }
            else {
                webserverPlugin->Cancel(WSP_CANCEL_ACCESS_DENIED);
                return false;
            }
        }
        switch (requestType)
        {
        case WS_REQUEST_GET:
            if (resourceName[0] == '/') {
                this->tasks.push_back(new WebdavServiceGet(webserverPlugin, this, &resourceName[1]));
                return true;
            }
            webserverPlugin->Cancel(WSP_CANCEL_NOT_FOUND);
            break;
        case WS_REQUEST_PUT:
            if (resourceName[0] == '/') {
                this->tasks.push_back(new WebdavServicePut(webserverPlugin, this, &resourceName[1], dataSize));
                return true;
            }
            webserverPlugin->Cancel(WSP_CANCEL_NOT_FOUND);
            break;
            //case WS_REQUEST_POST:
            //    if (resourceName[0] == '/') {
            //        webserverPlugin->Accept(new WebdavServicePost(this, &resourceName[1], dataSize));
            //        return;
            //    }
            //    webserverPlugin->Cancel(WSP_CANCEL_NOT_FOUND); //
            //    break;
        case WS_REQUEST_PROPFIND:
            if (resourceName[0] == '/') {
                if (rootFolder) new WebdavServicePropfind(webserverPlugin, this, &resourceName[1], dataSize, rootFolderPath);
                else new WebdavServicePropfind(webserverPlugin, this, &resourceName[1], dataSize);
                return true;
            }
            webserverPlugin->Cancel(WSP_CANCEL_NOT_FOUND); //
            break;
        case WS_REQUEST_PROPPATCH:
                if (resourceName[0] == '/') {
                    webserverPlugin->Accept(new WebdavServiceProppatch(this, &resourceName[1], dataSize));
                    return true;
                }
                webserverPlugin->Cancel(WSP_CANCEL_NOT_FOUND); //
                break;
        case WS_REQUEST_MKCOL:
            if (resourceName[0] == '/') {
                new WebdavServiceMkCol(webserverPlugin, this, &resourceName[1], dataSize);
                return true;
            }
            webserverPlugin->Cancel(WSP_CANCEL_NOT_FOUND); //
            break;
        case WS_REQUEST_DELETE:
            if (resourceName[0] == '/') {
                new WebdavServiceDelete(webserverPlugin, this, &resourceName[1], dataSize);
                return true;
            }
            webserverPlugin->Cancel(WSP_CANCEL_NOT_FOUND); //
            break;
        case WS_REQUEST_MOVE:
            if (resourceName[0] == '/') {
                new WebdavServiceMove(webserverPlugin, this, &resourceName[1], dataSize);
                return true;
            }
            webserverPlugin->Cancel(WSP_CANCEL_NOT_FOUND); //
            break;
        //case WS_REQUEST_COPY:
        //    if (resourceName[0] == '/') {
        //        webserverPlugin->Accept(new WebdavServiceCopy(this, &resourceName[1], dataSize));
        //        return true;
        //    }
        //    webserverPlugin->Cancel(WSP_CANCEL_NOT_FOUND); //
        //    break;
        case WS_REQUEST_OPTIONS:
            if (resourceName[0] == '/') {
                webserverPlugin->Accept(new WebdavServiceOptions(this, &resourceName[1], dataSize));
                return true;
            }
            break;
        case WS_REQUEST_LOCK:
            if (resourceName[0] == '/') {
                webserverPlugin->Accept(new WebdavServiceLock(this, &resourceName[1], dataSize));
                return true;
            }
            break;
        case WS_REQUEST_UNLOCK:
            if (resourceName[0] == '/') {
                webserverPlugin->Accept(new WebdavServiceUnLock(this, &resourceName[1], dataSize));
                return true;
            }
            break;
       
        default:
            log->Log(LOG_WEBDAV_SERVICE, "WebdavService::WebRequestWebdavService request type not available");
            webserverPlugin->Cancel(WSP_CANCEL_BAD_REQUEST);
            break;
        }
    }
    else {
        webserverPlugin->Cancel(WSP_CANCEL_UNAVAILABLE);
    }
    return true;
}

void WebdavService::RegisterforNotification(class UWebdavNotification * notification, const char * appName)
{
    if (notification && appName){
        class WebdavNotification * notify = new WebdavNotification(notification, appName);
        this->notifications.push_back(notify);
    }
}

void WebdavService::DeregisterforNotification(class UWebdavNotification * notification)
{
    /*class WebdavNotification * notify = (class WebdavNotification *) notifications.front();
    while (notify){
        if (notify->webdavNotification == notification){
            delete notify;
            break;
        }
    }*/
    for (class WebdavNotification * notify = (class WebdavNotification *) notifications.front(); notify; notify = notify->goNext()){
        if (notify->webdavNotification == notification){
            delete notify;
            break;
        }
    }
}

bool WebdavService::InsertLock(class WebdavLock * lock)
{
    class WebdavLock * listlock = locks.front();
    do {
        if (listlock) {
            if (listlock->resource && lock->resource && str::casecmp(listlock->resource, lock->resource) == 0) {
                break;
            }
            listlock = listlock->goNext();
        }
    } while (listlock);
    if (listlock)
        return false;
    
    this->locks.push_back(lock);
    return true;
}



class WebdavLock * WebdavService::LockLookup(const char * resource)
{
    class WebdavLock * listlock = locks.front();
    do {
        if (listlock) {
            if (listlock->resource && resource && str::casecmp(listlock->resource, resource) == 0) {
                return listlock;
            }
            listlock = listlock->goNext();
        }
    } while (listlock);

    return 0;
}

class WebdavLock * WebdavService::LockLookup(const char * resource, const char * token)
{
    class WebdavLock * listlock = locks.front();
    do {
        if (listlock) {
            if (listlock->resource && resource && listlock->token && token && strstr(token,listlock->token) && str::casecmp(listlock->resource, resource) == 0) {
                return listlock;
            }
            listlock = listlock->goNext();
        }
    } while (listlock);

    return 0;
}




//***************************************************************************************************//
//********************************** WebdavServiceGet ***********************************************//
//***************************************************************************************************//

WebdavServiceGetWebserver::WebdavServiceGetWebserver(class WebdavServiceGet * webdavServiceGet)
{
    this->webdavServiceGet = webdavServiceGet;
    this->webserverget = nullptr;
}

WebdavServiceGetWebserver::~WebdavServiceGetWebserver()
{
    
}

void WebdavServiceGetWebserver::WebserverGetRequestAcceptComplete(IWebserverGet * const webserverGet)
{
    this->webserverget = webserverGet;
    this->webdavServiceGet->AcceptReceived();
}

void WebdavServiceGetWebserver::WebserverGetSendResult(IWebserverGet * const webserverGet)
{
    this->webdavServiceGet->SendResult();
}

void WebdavServiceGetWebserver::WebserverGetCloseComplete(IWebserverGet * const webserverGet)
{
    delete webserverget;
    this->webdavServiceGet->CloseComplete(this);
}

void WebdavServiceGetWebserver::Cancel(wsr_cancel_type_t type)
{
    this->webserverget->Cancel(type);
}

void WebdavServiceGetWebserver::Send(const void * data, size_t len)
{
    this->webserverget->Send(data, len);
}

void WebdavServiceGetWebserver::SetTransferInfo(wsr_type_t resourceType, ulong64 dataSize, const char * filename)
{
    this->webserverget->ForceDownloadResponse(filename);
    this->webserverget->SetTransferInfo(resourceType, dataSize);
}

void WebdavServiceGetWebserver::SetTransferRange(ulong64 start, ulong64 end)
{
    this->webserverget->SetTransferRange(start, end);
}

size_t WebdavServiceGetWebserver::GetRangeCount()
{
    return this->webserverget->GetRangeCount();
}

IWebserverGetRange* WebdavServiceGetWebserver::GetRange(size_t idx)
{
    return this->webserverget->GetRange(idx);
}

void WebdavServiceGetWebserver::Close()
{
    webserverget->Close();
}


WebdavServiceGetDBFiles::WebdavServiceGetDBFiles(class WebdavServiceGet * webdavServiceGet, class IDbFiles * dbFiles)
{
    this->webdavServiceGet = webdavServiceGet;
    this->dbFiles = dbFiles;
    this->filesget = nullptr;
    this->pathinfo = nullptr;
    this->state = 0;
}

WebdavServiceGetDBFiles::~WebdavServiceGetDBFiles()
{
}

void WebdavServiceGetDBFiles::LookPath(char * path)
{
    this->state = GETPATHREQUEST;
    pathinfo = dbFiles->PathInfo(this, path);
}

void WebdavServiceGetDBFiles::GetFile(ulong64 id, ulong64 offset)
{
    this->state = GETREADFILE;
    filesget = dbFiles->Get(this, id, offset, true);
}

void WebdavServiceGetDBFiles::ReadFile()
{
    filesget->Read();
}

void WebdavServiceGetDBFiles::StopReadFile()
{
    this->state = GETSTOPREADFILE;
    filesget->Stop();
}

void WebdavServiceGetDBFiles::TaskComplete(class ITask * const task)
{
    switch (state){
    case GETPATHREQUEST:
    {
        ulong64 id;
        const char *  name;
        ulong64 length;
        bool isFolder;
        ulong64 created; 
        ulong64 modified;
        pathinfo->Get(id, name, length, isFolder, &created, &modified);
        this->webdavServiceGet->PathCompleted(true, id, name, length, isFolder, created, modified);
        break;
    }
    case GETREADFILE:
        this->webdavServiceGet->GetCompleted(true);
        break;
    case GETSTOPREADFILE:
        this->webdavServiceGet->StopReadFileCompleted();
        break;
    case GETCLOSE:
        delete task;
        this->webdavServiceGet->CloseComplete(this);
        return;
    }
    delete task;
}

void WebdavServiceGetDBFiles::TaskFailed(class ITask * const task)
{
    switch (state){
    case GETPATHREQUEST:
        this->webdavServiceGet->PathCompleted(false, 0, 0, 0, 0, 0, 0);
        break;
    case GETREADFILE:
        this->webdavServiceGet->GetCompleted(false);
        break;
    
    case GETCLOSE:
        delete task;
        this->webdavServiceGet->CloseComplete(this);
        return;
    }
    delete task;
}

void WebdavServiceGetDBFiles::TaskProgress(class ITask * const task, dword progress)
{
    switch (state){
    case GETPATHREQUEST:
        break;
    case GETREADFILE:
        const byte * data;
        unsigned len;
        filesget->Get(data, len);
        this->webdavServiceGet->GetProgress(data, len);
        break;
    
    case GETCLOSE:
        task->Stop();
    }
}

void WebdavServiceGetDBFiles::Close()
{
    this->state = GETCLOSE;
}

void WebdavServiceGetDBFiles::ClosebyWebserver()
{
    debug->printf("WebdavServiceGetDBFiles::ClosebyWebserver() state = %u", state);
    if (filesget) delete filesget;
    //this->webdavServiceGet->CloseComplete(0);
}

WebdavServiceGet::WebdavServiceGet(IWebserverPlugin * const webserverPlugin, class WebdavService * webdavservice, char * path)
{
    webdavservice->log->Log(LOG_WEBDAV_SERVICE, "WebdavServiceGet(%08x)::WebdavServiceGet path:%s", this, path);
    this->webdavServiceGetWebserver = new class WebdavServiceGetWebserver(this);
    this->webdavServiceGetDBFiles = new class WebdavServiceGetDBFiles(this, webdavservice->dbFiles);
    this->pendingRequestWebserver = true;
    this->pendingRequestDBFiles = false;
    this->webdavservice = webdavservice;
    this->path = _strdup(path);
    this->sendedLength = 0;
    this->requestedLength = 0;
    this->sendBuffer = 0;
    webserverPlugin->Accept(webdavServiceGetWebserver);
}
WebdavServiceGet::~WebdavServiceGet()
{
    webdavservice->log->Log(LOG_WEBDAV_SERVICE, "WebdavServiceGet(%08x)::~WebdavServiceGet", this);
    free(this->path);
    if (this->sendBuffer) delete [] this->sendBuffer;
}

void WebdavServiceGet::AcceptReceived()
{
    this->pendingRequestWebserver = false;
    this->pendingRequestDBFiles = true;
    this->webdavServiceGetDBFiles->LookPath(this->path);
}

void WebdavServiceGet::PathCompleted(bool completed, ulong64 id, const char * name, ulong64 length, bool isFolder, ulong64 created, ulong64 modified)
{
    this->pendingRequestDBFiles = false;
    if (completed && id){
        this->webdavServiceGetWebserver->GetRangeCount();
        size_t rangeCount = this->webdavServiceGetWebserver->GetRangeCount();
        class IWebserverGetRange * range = 0;
        if (rangeCount != 0){
            for (size_t i = 0; i < rangeCount; i++){
                range = this->webdavServiceGetWebserver->GetRange(i);
            }
            switch (range->rangeType){
            case IWebserverGetRange::RANGE_NONE:
                break;
            case IWebserverGetRange::RANGE_START_END:
                this->webdavServiceGetWebserver->SetTransferRange(range->start, range->end>length ? length - 1:range->end);
                break;
            case IWebserverGetRange::RANGE_START_ONLY:
                this->webdavServiceGetWebserver->SetTransferRange(range->start, length - 1);
                break;
            case IWebserverGetRange::RANGE_LAST_BYTES:
                break;
            }
            this->requestedLength = (range->end>length ? length : range->end) - range->start + 1;
        }
        else {
            this->requestedLength = length;
        }
        this->pendingRequestDBFiles = true;
        wsr_type_t type = GetResponseTypeForFileName(path);
        if (type != WSP_RESPONSE_NO_TYPE) {
            this->webdavServiceGetWebserver->SetTransferInfo(type, length, name);
            this->webdavServiceGetDBFiles->GetFile(id, range ? range->start : 0);
        }
        else {
            this->webdavServiceGetWebserver->SetTransferInfo(WSP_RESPONSE_BINARY, length, name);
            this->webdavServiceGetDBFiles->GetFile(id, range ? range->start : 0);
        }
    }
    else {
        this->pendingRequestWebserver = true;
        this->webdavServiceGetWebserver->Cancel(WSP_CANCEL_NOT_FOUND);
    }
}

void WebdavServiceGet::GetCompleted(bool completed)
{
    this->pendingRequestDBFiles = false;
    if (completed) {
        Close();
    }
    else {
        delete this->webdavServiceGetDBFiles;
        this->webdavServiceGetDBFiles = 0;
        this->pendingRequestWebserver = true;
        this->webdavServiceGetWebserver->Cancel(WSP_CANCEL_INTERNAL_ERROR);
    }
}

void WebdavServiceGet::StopReadFileCompleted()
{
    this->pendingRequestDBFiles = false;
    this->pendingRequestWebserver = true;
    ulong64 sendLen = requestedLength - sendedLength;
    if (sendLen > WS_MAX_DATA_SIZE) sendLen = WS_MAX_DATA_SIZE;
    this->webdavServiceGetWebserver->Send(this->sendBuffer, size_t(sendLen));
    this->pendingSendRequest = true;
    this->sendedLength += sendLen;
    
}

void WebdavServiceGet::GetProgress(const byte * data, unsigned len)
{
    this->pendingRequestDBFiles = false;
    if (requestedLength){
        if (sendedLength < requestedLength){
            if (sendedLength + len < requestedLength) {
                this->pendingRequestWebserver = true;
                this->pendingSendRequest = true;
                this->sendedLength += len;
                this->webdavServiceGetWebserver->Send(data, len);
            }
            else {
                // TODO Check if the solution will be save to not have a problem of a new call with 4GB.
                this->sendBuffer = new byte[size_t(requestedLength - sendedLength)];
                memcpy(this->sendBuffer, data, size_t(requestedLength - sendedLength));
                this->pendingRequestDBFiles = true;
                webdavServiceGetDBFiles->StopReadFile();
            }
        }
        else {
            this->pendingRequestDBFiles = true;
            this->webdavServiceGetDBFiles->ReadFile();
        }
    }
    else {
        this->pendingSendRequest = true;
        this->pendingRequestWebserver = true;
        this->webdavServiceGetWebserver->Send(data, len);
    }
    
}

void WebdavServiceGet::SendResult()
{
    this->pendingRequestWebserver = false;
    this->pendingSendRequest = false;
    if (sendedLength < requestedLength) {
        this->pendingRequestDBFiles = true;
        this->webdavServiceGetDBFiles->ReadFile();
    }
    else {
        Close();
    }
}

void WebdavServiceGet::Close()
{
    webdavservice->log->Log(LOG_WEBDAV_SERVICE, "WebdavServiceGet(%08x)::Close pendingRequestDBFiles=%u pendingRequestWebserver=%u", this,pendingRequestDBFiles,pendingRequestWebserver);
    if (this->pendingRequestDBFiles)
    {
        this->webdavServiceGetDBFiles->Close();
    }
    else if (this->webdavServiceGetDBFiles)
    {
        this->pendingRequestDBFiles = false;
        delete this->webdavServiceGetDBFiles;
        this->webdavServiceGetDBFiles = 0;
    }
    if (!this->pendingRequestWebserver && this->webdavServiceGetWebserver)
    {
        this->webdavServiceGetWebserver->Close();
    }
    if (!this->webdavServiceGetDBFiles && !this->webdavServiceGetWebserver)
    {
        this->CloseComplete(0);
    }
}

void WebdavServiceGet::CloseComplete(class UWebdavServiceTask * task)
{
    webdavservice->log->Log(LOG_WEBDAV_SERVICE, "WebdavServiceGet(%08x)::CloseComplete pendingRequestDBFiles=%u pendingRequestWebserver=%u task=%x webdavServiceGetWebserver=%x webdavServiceGetDBFiles=%x", this,pendingRequestDBFiles,pendingRequestWebserver,task,webdavServiceGetWebserver,webdavServiceGetDBFiles);
    if (task == this->webdavServiceGetWebserver)
    {
        this->pendingRequestWebserver = false;
        delete this->webdavServiceGetWebserver;
        this->webdavServiceGetWebserver = 0;
    }
    if (task == this->webdavServiceGetDBFiles)
    {
        this->pendingRequestDBFiles = false;
        delete this->webdavServiceGetDBFiles;
        this->webdavServiceGetDBFiles = 0;
    }
    if (this->pendingRequestDBFiles)
    {
        this->webdavServiceGetDBFiles->Close();
    }
    else if (this->pendingSendRequest && (sendedLength < requestedLength))
    {
        this->pendingSendRequest = false;
        this->pendingRequestWebserver = false;
        if (this->webdavServiceGetDBFiles != 0) {
            this->webdavServiceGetDBFiles->ClosebyWebserver();
            this->pendingRequestDBFiles = false;
            delete this->webdavServiceGetDBFiles;
            this->webdavServiceGetDBFiles = 0;
        }
        webdavservice->log->Log(LOG_WEBDAV_SERVICE, "WebdavServiceGet(%08x)::CloseComplete webdavServiceGetWebserver=%x webdavServiceGetDBFiles=%x", this,webdavServiceGetWebserver,webdavServiceGetDBFiles);
    }
    else if (this->webdavServiceGetDBFiles)
    {
        this->pendingRequestDBFiles = false;
        delete this->webdavServiceGetDBFiles;
        this->webdavServiceGetDBFiles = 0;
    }
    if (this->pendingRequestWebserver)
    {
        this->webdavServiceGetWebserver->Close();
    }
    else if (this->webdavServiceGetWebserver)
    {
        this->pendingRequestWebserver = false;
        delete this->webdavServiceGetWebserver;
        this->webdavServiceGetWebserver = 0;
    }
    if (!this->webdavServiceGetDBFiles && !this->webdavServiceGetWebserver)
    {
        this->webdavservice->CloseComplete(this);
        delete this;
    }
}



//***************************************************************************************************//
//********************************** WebdavServicePut ***********************************************//
//***************************************************************************************************//

WebdavServicePutWebserver::WebdavServicePutWebserver(class WebdavServicePut * webdavServicePut)
{
    this->webdavServicePut = webdavServicePut;
    this->webserverPut = nullptr;
}

WebdavServicePutWebserver::~WebdavServicePutWebserver()
{

}

void WebdavServicePutWebserver::WebserverPutRequestAcceptComplete(IWebserverPut * const webserverPut)
{
    this->webserverPut = webserverPut;
    this->webdavServicePut->AcceptReceived();
}

void WebdavServicePutWebserver::WebserverPutSendResult(IWebserverPut * const webserverPut)
{
    this->webdavServicePut->SendResult();
}

void WebdavServicePutWebserver::WebserverPutRecvResult(IWebserverPut * const webserverPut, void * buffer, size_t len)
{
    this->webdavServicePut->WebserverRecv(buffer, len);
}

void * WebdavServicePutWebserver::WebserverPutRecvBuffer(size_t len)
{
    return len ? malloc(len) : NULL;
}

void WebdavServicePutWebserver::WebserverPutRecvCanceled(IWebserverPut * const webserverPut, void * buffer)
{
    free(buffer);
}

void WebdavServicePutWebserver::WebserverPutCloseComplete(IWebserverPut * const webserverPut)
{
    delete webserverPut;
    this->webdavServicePut->CloseComplete(this);
}

void WebdavServicePutWebserver::Cancel(wsr_cancel_type_t type)
{
    this->webserverPut->Cancel(type);
}

void WebdavServicePutWebserver::Recv()
{
    this->webserverPut->Recv(0, 0);
}

bool WebdavServicePutWebserver::DataIsChunkEncoded()
{
    return webserverPut->DataIsChunkEncoded();
}

void WebdavServicePutWebserver::Close()
{
    webserverPut->Close();
}

void WebdavServicePutWebserver::SetResultCode(ws_webdav_result_t result)
{
    webserverPut->SetResultCode(result);
}

void WebdavServicePutWebserver::Send(const void * data, size_t len)
{
    webserverPut->Send(data, len);
}


WebdavServicePutDBFiles::WebdavServicePutDBFiles(class WebdavServicePut * webdavServicePut, class IDbFiles * dbFiles)
{
    this->webdavServicePut = webdavServicePut;
    this->dbFiles = dbFiles;
    this->filesput = nullptr;
    this->pathinfo = nullptr;
    this->state = 0;
}

WebdavServicePutDBFiles::~WebdavServicePutDBFiles()
{
}

void WebdavServicePutDBFiles::LookPath(char * path)
{
    this->state = PUTPATHREQUEST;
    pathinfo = dbFiles->PathInfo(this, path);
}

void WebdavServicePutDBFiles::WriteFile(byte * data, unsigned len, bool last)
{
    this->state = PUTWRITEFILE;
    filesput->Write(data, len, last);
}

void WebdavServicePutDBFiles::PutFile(char * filename, ulong64 folder)
{
    this->state = PUTWRITEFILE;
    filesput = this->dbFiles->Put(this, filename, folder);
}

void WebdavServicePutDBFiles::GetFailed(ulong64 & parent, bool & isFolder, const char * &path)
{
    pathinfo->GetFailed(parent, isFolder, path);
}

ulong64 WebdavServicePutDBFiles::GetFileId()
{
    return filesput->Get();
}

void WebdavServicePutDBFiles::TaskComplete(class ITask * const task)
{
    switch (state){
    case PUTPATHREQUEST:
    {
        ulong64 id;
        const char *  name;
        ulong64 length;
        bool isFolder;
        ulong64 created;
        ulong64 modified;
        pathinfo->Get(id, name, length, isFolder, &created, &modified);
        this->webdavServicePut->PathCompleted(true, id, name, length, isFolder, created, modified);
        break;
    }
    case PUTWRITEFILE:
        
        this->webdavServicePut->WriteCompleted(true);
        break;
    case PUTCLOSE:
        delete task;
        this->webdavServicePut->CloseComplete(this);
        return;
    }
    delete task;
}

void WebdavServicePutDBFiles::TaskFailed(class ITask * const task)
{
    switch (state){
    case PUTPATHREQUEST:
        this->webdavServicePut->PathCompleted(false, 0, 0, 0, 0, 0, 0);
        break;
    case PUTWRITEFILE:
        this->webdavServicePut->WriteCompleted(false);
        break;
    case PUTCLOSE:
        delete task;
        this->webdavServicePut->CloseComplete(this);
        return;
    }
    delete task;
}

void WebdavServicePutDBFiles::TaskProgress(class ITask * const task, dword progress)
{
    switch (state){
    case PUTPATHREQUEST:
        break;
    case PUTWRITEFILE:
        this->webdavServicePut->WriteProgress();
        break;
    case PUTCLOSE:
        delete task;
        this->webdavServicePut->CloseComplete(this);
        return;
    }
}

void WebdavServicePutDBFiles::Close()
{
    this->state = PUTCLOSE;
}

void WebdavServicePutDBFiles::CloseProgress() {
    debug->printf("WebdavServicePutDBFiles::CloseProgress()");
    this->state = PUTCLOSE;
    this->filesput->Stop();
}


WebdavServicePut::WebdavServicePut(IWebserverPlugin * const webserverPlugin, class WebdavService * webdavservice, char * path, ulong64 dataSize)
{
    webdavservice->log->Log(LOG_WEBDAV_SERVICE, "WebdavServicePut(%08x)::WebdavServicePut path:%s dataSize:%llu", this, path, dataSize);
    this->path = _strdup(path);
    this->webdavservice = webdavservice;
    this->dataSize = dataSize;
    this->filename = 0;
    this->chunkEncoded = false;
    this->webdavServicePutWebserver = new class WebdavServicePutWebserver(this);
    this->webdavServicePutDBFiles = new class WebdavServicePutDBFiles(this, webdavservice->dbFiles);
    this->pendingRequestWebserver = true;
    this->pendingRequestDBFiles = false;
    this->waitfordatatowrite = false;
    this->id = 0;
    webserverPlugin->Accept(webdavServicePutWebserver);
}
WebdavServicePut::~WebdavServicePut()
{
    webdavservice->log->Log(LOG_WEBDAV_SERVICE, "WebdavServicePut(%08x)::~WebdavServicePut", this);
    free(this->path);
    if (filename)free(filename);
    filename = 0;
}

void WebdavServicePut::AcceptReceived()
{
    this->pendingRequestWebserver = false;
    
    char * pch = this->path + strlen(this->path) - 1;
    this->chunkEncoded = webdavServicePutWebserver->DataIsChunkEncoded();
    if (*pch == '/') {
        this->pendingRequestWebserver = true;
        webdavServicePutWebserver->Cancel(WSP_CANCEL_NOT_FOUND);
    }
    else {
        this->pendingRequestDBFiles = true;
        this->webdavServicePutDBFiles->LookPath(this->path);
    }
        
}

void WebdavServicePut::PathCompleted(bool completed, ulong64 id, const char * name, ulong64 length, bool isFolder, ulong64 created, ulong64 modified)
{
    this->pendingRequestDBFiles = false;
    if (completed){
        char * pathcopy = _strdup(this->path);
        char * pch = pathcopy + strlen(pathcopy) - 1;
        pch = strrchr(pathcopy, '/');
        if (pch) filename = _strdup(pch + 1);
        else filename = _strdup(path);
        if (id) {
            if (isFolder){
                this->pendingRequestWebserver = true;
                webdavServicePutWebserver->SetResultCode(WEBDAV_RESULT_METHOD_NOT_ALLOWED);
                webdavServicePutWebserver->Send(0, 0);
            }
            else {
                const char * path;
                ulong64 parent;
                webdavServicePutDBFiles->GetFailed(parent, isFolder, path);
                if (isFolder && parent)
                {
                    this->pendingRequestDBFiles = true;
                    webdavServicePutDBFiles->PutFile(filename, parent);
                }
                else
                {
                    this->pendingRequestWebserver = true;
                    webdavServicePutWebserver->Cancel(WSP_CANCEL_NOT_FOUND);
                }
            }
        }
        else {
            const char * path;
            ulong64 parent;
            webdavServicePutDBFiles->GetFailed(parent, isFolder, path);
            if (isFolder && parent)
            {
                this->pendingRequestDBFiles = true;
                webdavServicePutDBFiles->PutFile(filename, parent);
            }
            else
            {
                this->pendingRequestWebserver = true;
                webdavServicePutWebserver->Cancel(WSP_CANCEL_NOT_FOUND);
            }
        }
        free(pathcopy);
        //this->webdavServicePutDBFiles->GetFile(id);
    }
    else {
        this->pendingRequestWebserver = true;
        this->webdavServicePutWebserver->Cancel(WSP_CANCEL_NOT_FOUND);
    }
}

void WebdavServicePut::WriteCompleted(bool completed)
{
    this->pendingRequestDBFiles = false;
    if (completed){
        this->id = this->webdavServicePutDBFiles->GetFileId();
        if (webdavservice && !webdavservice->notifications.empty()) {
            WebdavNotification* notification = webdavservice->notifications.front();
            while (notification) {
                if (notification->webdavNotification) {
                    notification->webdavNotification->PutCompleted(notification->appName, this->path, this->id);
                }
                notification = notification->goNext();
            }
        }
        Close();
    }
    else
    {
        this->pendingRequestWebserver = true;
        this->webdavServicePutWebserver->Cancel(WSP_CANCEL_INTERNAL_ERROR);
    }
}

void WebdavServicePut::Close()
{
    if (this->webdavServicePutDBFiles) this->webdavServicePutDBFiles->Close();
    if (this->webdavServicePutWebserver) this->webdavServicePutWebserver->Close();
}

void WebdavServicePut::CloseComplete(class UWebdavServiceTask * task)
{
    if (task == this->webdavServicePutWebserver)
    {
        this->pendingRequestWebserver = false;
        delete this->webdavServicePutWebserver;
        this->webdavServicePutWebserver = 0;
    }
    if (task == this->webdavServicePutDBFiles)
    {
        this->pendingRequestDBFiles = false;
        delete this->webdavServicePutDBFiles;
        this->webdavServicePutDBFiles = 0;
    }
    if (this->pendingRequestDBFiles)
    {
        this->webdavServicePutDBFiles->Close();
    }
    else if (this->waitfordatatowrite) {
        this->waitfordatatowrite = false;
        this->webdavServicePutDBFiles->CloseProgress();

    }
    else if (this->webdavServicePutDBFiles)
    {
        this->pendingRequestDBFiles = false;
        delete this->webdavServicePutDBFiles;
        this->webdavServicePutDBFiles = 0;
    }
    if (this->pendingRequestWebserver)
    {
        this->webdavServicePutWebserver->Close();
    }
    else if (this->webdavServicePutWebserver)
    {
        this->pendingRequestWebserver = false;
        delete this->webdavServicePutWebserver;
        this->webdavServicePutWebserver = 0;
    }
    if (!this->webdavServicePutDBFiles && !this->webdavServicePutWebserver)
    {
        this->webdavservice->CloseComplete(this);
        delete this;
    }
}

void WebdavServicePut::WebserverRecv(void * buffer, size_t len)
{
    this->pendingRequestDBFiles = true;
    this->waitfordatatowrite = false;
    if (chunkEncoded){
        if (len){
            webdavServicePutDBFiles->WriteFile((byte*)buffer, (unsigned int)len, false);
        }
        else {
            webdavServicePutDBFiles->WriteFile(0, (unsigned int)len, true);
        }
    }
    else {
        dataSize -= len;
        if (dataSize)
            webdavServicePutDBFiles->WriteFile((byte*)buffer, (unsigned int)len, false);
        else {
            webdavServicePutDBFiles->WriteFile((byte*)buffer, (unsigned int)len, true);
        }
    }
    free(buffer);
}

void WebdavServicePut::SendResult()
{
    Close();
}

void WebdavServicePut::WriteProgress()
{
    this->pendingRequestDBFiles = false;
    if (chunkEncoded){
        this->pendingRequestWebserver = true;
        this->waitfordatatowrite = true;
        this->webdavServicePutWebserver->Recv();
    }
    else {
        if (dataSize)
        {
            this->waitfordatatowrite = true;
            this->pendingRequestWebserver = true;
            this->webdavServicePutWebserver->Recv();
        }
        else {
            this->pendingRequestDBFiles = true;
            this->webdavServicePutDBFiles->WriteFile(0,0,true);
        }
    }
}


//***************************************************************************************************//
//********************************** WebdavServicePropfind ******************************************//
//***************************************************************************************************//

static char * XmlOpen(unsigned & length)
{   // Note: Writing XML content with default namespace definition (xmlns="DAV:")
    //       does not work with Microsoft-WebDAV-MiniRedir as user agent.
    static char str [] =
        "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
        "<DAV:multistatus xmlns:DAV=\"DAV:\">";
    length = sizeof(str)-1;
    return str;
}

static char * XmlClose(unsigned & length)
{
    static char str [] = "</DAV:multistatus>";
    length = sizeof(str)-1;
    return str;
}

static unsigned url_encode(char * dst, const char * src) {
    static const char xdigits [] = "0123456789ABCDEF";
    unsigned len = 0;
    for (; *src; src++) {
        if (strchr("/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-.", *src)) {
            dst[len++] = *src;
        }
        else {
            dst[len++] = '%';
            dst[len++] = xdigits[(*src >> 4) & 0x0f];
            dst[len++] = xdigits[(*src) & 0x0f];
        }
    }
    dst[len] = '\0';
    return len;
}

WebdavServicePropfindWebserver::WebdavServicePropfindWebserver(class WebdavServicePropfind * webdavServicePropfind)
{
    this->webdavServicePropfind = webdavServicePropfind;
    this->webserverPropfind = nullptr;
}

WebdavServicePropfindWebserver::~WebdavServicePropfindWebserver()
{

}

void WebdavServicePropfindWebserver::WebserverPropfindRequestAcceptComplete(IWebserverPropfind * const webserverPropfind)
{
    this->webserverPropfind = webserverPropfind;
    this->webdavServicePropfind->AcceptReceived();
}

void WebdavServicePropfindWebserver::WebserverPropfindSendResult(IWebserverPropfind * const webserverPropfind)
{
    this->webdavServicePropfind->SendResult();
}

void WebdavServicePropfindWebserver::WebserverPropfindRecvResult(IWebserverPropfind * const webserverPropfind, void * buffer, size_t len)
{
    this->webdavServicePropfind->WebserverRecv(buffer, len);
}

void * WebdavServicePropfindWebserver::WebserverPropfindRecvBuffer(size_t len)
{
    return len ? malloc(len) : NULL; 
}

void WebdavServicePropfindWebserver::WebserverPropfindRecvCanceled(IWebserverPropfind * const webserverPropfind, void * buffer)
{
    free(buffer);
}

void WebdavServicePropfindWebserver::WebserverPropfindCloseComplete(IWebserverPropfind * const webserverPropfind)
{
    delete webserverPropfind;
    this->webdavServicePropfind->CloseComplete(this);
}

void WebdavServicePropfindWebserver::Cancel(wsr_cancel_type_t type)
{
    this->webserverPropfind->Cancel(type);
}

void WebdavServicePropfindWebserver::Recv()
{
    this->webserverPropfind->Recv(0, 0);
}

bool WebdavServicePropfindWebserver::DataIsChunkEncoded()
{
    return webserverPropfind->DataIsChunkEncoded();
}

void WebdavServicePropfindWebserver::Close()
{
    webserverPropfind->Close();
}

void WebdavServicePropfindWebserver::SetResultCode(ws_webdav_result_t result, ulong64 datasize)
{
    webserverPropfind->SetResultCode(result, datasize);
}

void WebdavServicePropfindWebserver::Send(const void * data, size_t len)
{
    webserverPropfind->Send(data, len);
}

const char * WebdavServicePropfindWebserver::GetHeaderFieldValue(const char * field)
{
    return webserverPropfind->GetHeaderFieldValue(field);
}

WebdavServicePropfindDBFiles::WebdavServicePropfindDBFiles(class WebdavServicePropfind * webdavServicePropfind, class IDbFiles * dbFiles)
{
    this->webdavServicePropfind = webdavServicePropfind;
    this->dbFiles = dbFiles;
    this->fileslist = nullptr;
    this->pathinfo = nullptr;
    this->state = 0;
}

WebdavServicePropfindDBFiles::~WebdavServicePropfindDBFiles()
{
}

void WebdavServicePropfindDBFiles::LookPath(char * path)
{
    this->state = PROPFINDPATHREQUEST;
    pathinfo = dbFiles->PathInfo(this, path);
}

void WebdavServicePropfindDBFiles::List(ulong64 folderId)
{
    this->state = PROPFINDLIST;
    this->fileslist = dbFiles->List(this, folderId, 0);
}

void WebdavServicePropfindDBFiles::Next()
{
    fileslist->Next();
}

void WebdavServicePropfindDBFiles::TaskComplete(class ITask * const task)
{
    switch (state) {
    case PROPFINDPATHREQUEST:
        {
            ulong64 id = 0;
            const char * name = 0;
            ulong64 length = 0;
            bool isFolder = false;
            ulong64 created = 0;
            ulong64 modified = 0;
            pathinfo->Get(id, name, length, isFolder, &created, &modified);
            this->webdavServicePropfind->PathCompleted(true, id, name, length, isFolder, created, modified);
        }
        break;

    case PROPFINDLIST:
    {
        this->webdavServicePropfind->ListCompleted(true);
        break;
    }       
    case PROPFINDCLOSE:
        delete task;
        this->webdavServicePropfind->CloseComplete(this);
        return;
    default:
        break;
    }
    delete task;
}

void WebdavServicePropfindDBFiles::TaskFailed(class ITask * const task)
{
    switch (state){
    case PROPFINDPATHREQUEST:
        this->webdavServicePropfind->PathCompleted(false, 0, 0, 0, 0, 0, 0);
        break;
    case PROPFINDLIST:
        this->webdavServicePropfind->ListCompleted(false);
        break;
    case PROPFINDCLOSE:
        delete task;
        this->webdavServicePropfind->CloseComplete(this);
        return;
    }
    delete task;
}

void WebdavServicePropfindDBFiles::TaskProgress(class ITask * const task, dword progress)
{
    switch (state){
    case PROPFINDPATHREQUEST:
        break;
    case PROPFINDLIST:
    {
        ulong64 id;
        const char * name;
        ulong64 length;
        bool is_folder;
        ulong64 created;
        ulong64 modified;
        fileslist->Get(id, name, length, is_folder, &created, &modified);
        webdavServicePropfind->ListProgress(id, name, length, is_folder, created, modified);
        break;
    }
    case PROPFINDCLOSE:
        task->Stop();
        break;
    }
}

void WebdavServicePropfindDBFiles::Close()
{
    this->state = PROPFINDCLOSE;
}


WebdavServicePropfind::WebdavServicePropfind(IWebserverPlugin * const webserverPlugin, class WebdavService * webdavservice, char * path, ulong64 dataSize, char * rootFolderPath)
{
    webdavservice->log->Log(LOG_WEBDAV_SERVICE, "WebdavServicePropfind(%08x)::WebdavServicePropfind path:%s dataSize:%llu", this, path, dataSize);
    this->path = _strdup(path);
    this->webdavservice = webdavservice;
    this->dataSize = dataSize;
    this->rootFolderPath = 0;
    this->propfindComplete = false;
    this->webdavServicePropfindWebserver = new class WebdavServicePropfindWebserver(this);
    this->webdavServicePropfindDBFiles = new class WebdavServicePropfindDBFiles(this, webdavservice->dbFiles);
    this->pendingRequestWebserver = true;
    this->pendingRequestDBFiles = false;
    this->depth = 0;
    this->host = nullptr;
    this->id = 0;
    this->properties = 0;
    if (rootFolderPath) this->rootFolderPath = _strdup(rootFolderPath);
    webserverPlugin->Accept(webdavServicePropfindWebserver);
}
WebdavServicePropfind::~WebdavServicePropfind()
{
    webdavservice->log->Log(LOG_WEBDAV_SERVICE, "WebdavServicePropfind(%08x)::~WebdavServicePropfind", this);
    free(this->path);
    if (rootFolderPath) free(rootFolderPath);
}

void WebdavServicePropfind::AcceptReceived()
{
    this->pendingRequestWebserver = false;
    this->depth = GetDepthHeader();
    this->host = GetHostHeader();
    this->pendingRequestWebserver = true;
    this->webdavServicePropfindWebserver->Recv();
}

void WebdavServicePropfind::SendResult()
{
    this->pendingRequestWebserver = false;
    if (this->propfindComplete)
        this->webdavServicePropfindWebserver->Close();
}
void WebdavServicePropfind::WebserverRecv(void * buffer, size_t len)
{
    dataSize -= len;
    if (dataSize == 0) {
        this->properties = GetRequestedProperties(buffer);
        size_t strLength = strlen(this->path);
        char * pch = this->path + strLength - 1;
        if (strLength && *pch == '/') {
            *pch = '\0';
        }
        this->pendingRequestDBFiles = true;
        this->webdavServicePropfindDBFiles->LookPath(this->path);
    }
    else {
        this->pendingRequestWebserver = true;
        this->webdavServicePropfindWebserver->Recv();
    }
    free(buffer);
}

dword WebdavServicePropfind::GetRequestedProperties(void * buffer)
{
    dword properties = 0;
    if (!buffer) return DAV_PROP_ALLPROPS;
    class xml_io xml((char*) buffer);
    byte error = xml.read();
    if (error == XML_ERROR_FATAL) {
        debug->printf("Xml error, XML decode error");
        return DAV_PROP_ALLPROPS;
    }
    word e = xml.get_first(XML_TYPE_TAG, XML_ROOT_TAG);
    const char * s = (e != XML_NO_TAG) ? xml.info(e) : 0;

    if (s && strstr(s, "propfind")) {

        e = xml.get_first(XML_TYPE_TAG, e);
        s = (e != XML_NO_TAG) ? xml.info(e) : 0;

        if (s && strstr(s, "allprop")) {

            return DAV_PROP_ALLPROPS;
        }
        else if (s && strstr(s, "prop")) {

            word p = xml.get_first(XML_TYPE_TAG, e);
            s = (p != XML_NO_TAG) ? xml.info(p) : 0;

            while (s) {
                if (strstr(s, "creationdate"))      properties |= DAV_PROP_CREATIONDATE;
                else if (strstr(s, "displayname"))       properties |= DAV_PROP_DISPLAYNAME;
                else if (strstr(s, "getcontentlanguage"))properties |= DAV_PROP_GETCONTENTLANGUAGE;
                else if (strstr(s, "getcontentlength"))  properties |= DAV_PROP_GETCONTENTLENGTH;
                else if (strstr(s, "getcontenttype"))    properties |= DAV_PROP_GETCONTENTTYPE;
                else if (strstr(s, "getetag"))           properties |= DAV_PROP_GETETAG;
                else if (strstr(s, "getlastmodified"))   properties |= DAV_PROP_GETLASTMODIFIED;
                else if (strstr(s, "lockdiscovery"))     properties |= DAV_PROP_LOCKDISCOVERY;
                else if (strstr(s, "resourcetype"))      properties |= DAV_PROP_RESOURCETYPE;
                else if (strstr(s, "source"))            properties |= DAV_PROP_SOURCE;
                else if (strstr(s, "supportedlock"))     properties |= DAV_PROP_SUPPORTEDLOCK;
                p = xml.get_next(XML_TYPE_TAG, e, p);
                s = (p != XML_NO_TAG) ? xml.info(p) : 0;
            }
            return properties;
        }
    }
    return DAV_PROP_ALLPROPS;
}

dword WebdavServicePropfind::GetDepthHeader()
{
    const char * depth_char = this->webdavServicePropfindWebserver->GetHeaderFieldValue("Depth");
    if (depth_char)
    {
        if (depth_char[0] == '0')
            return 0;
        if (depth_char[0] == '1')
            return 1;
    }
    return DAV_DEPTH_INFINITY;
}

const char * WebdavServicePropfind::GetHostHeader()
{
    return this->webdavServicePropfindWebserver->GetHeaderFieldValue("Host");
}

void WebdavServicePropfind::SendItem(const char * name, ulong64 contentlength, bool is_folder, ulong64 created, ulong64 modified)
{
    char * buffer = new char[WS_MAX_DATA_SIZE];
    char * tmp = buffer;
    unsigned len = 0;
    class xml_io xml(tmp);
    char b[WS_MAX_DATA_SIZE];
    tmp = b;
    unsigned isotimelength = 64; //xml only saves the pointer, so it is necessary to declare the buffer outside of the do/while loop
    char isotimebuf[64];
    unsigned rfctimelength = 64; //xml only saves the pointer, so it is necessary to declare the buffer outside of the do/while loop
    char rfctimebuf[64];
    char tmpWebserverPath[1024];
    char tmpRootFolderPath[1024];
    char tmpPath[1024];
    char tmpName[1024];
    char timeout_val[64];
    word response = xml.add_tag(XML_ROOT_TAG, "DAV:response");
    {
        word href = xml.add_tag(response, "DAV:href");
        url_encode(tmpWebserverPath, webdavservice->webserverPath);
        xml.add_content(href, tmpWebserverPath);
        if (rootFolderPath && rootFolderPath[0]) {
            xml.add_content(href, "/");
            url_encode(tmpRootFolderPath, rootFolderPath);
            xml.add_content(href, tmpRootFolderPath);
        }
        else if (path && path[0]){
            xml.add_content(href, "/");
            url_encode(tmpPath, path);
            xml.add_content(href, tmpPath);
        }
        if (name && name[0]) {
            xml.add_content(href, "/");
            url_encode(tmpName, const_cast<char*>(name));
            xml.add_content(href, tmpName);
        }
        
        // if (is_folder) xml.add_content(href, "/");
            word propstat = xml.add_tag(response, "DAV:propstat");
            {
                word prop = xml.add_tag(propstat, "DAV:prop");
                {

                    if (properties & DAV_PROP_RESOURCETYPE) {
                        word resourcetype = xml.add_tag(prop, "DAV:resourcetype");
                        if (is_folder) xml.add_tag(resourcetype, "DAV:collection");
                    }
                    if (properties & DAV_PROP_GETCONTENTLENGTH) {
                        word getcontentlength = xml.add_tag(prop, "DAV:getcontentlength");
                        word l = sprintf(tmp, "%llu", (unsigned long long)contentlength);
                        xml.add_content(getcontentlength, tmp, l);
                        tmp += l;
                    }
                    if (properties & DAV_PROP_CREATIONDATE) {
                        ITime::FormatTimeStampISO(isotimebuf, isotimelength, created);
                        word creationdate = xml.add_tag(prop, "DAV:creationdate");
                        //    word l = sprintf(tmp, "%s", timebuf);
                        xml.add_content(creationdate, isotimebuf);
                        //    tmp += l;
                    }
                    if (properties & DAV_PROP_GETLASTMODIFIED) {
                        ITime::FormatTimeStampRFC1123(rfctimebuf, rfctimelength, modified);
                        word modifieddate = xml.add_tag(prop, "DAV:getlastmodified");
                        //    word l = sprintf(tmp, "%s", timebuf);
                        xml.add_content(modifieddate, rfctimebuf);
                        //    tmp += l;
                    }
                    if (properties & DAV_PROP_SUPPORTEDLOCK) {
                        // supportedlock: return supported lock types
                        word supportedlock = xml.add_tag(prop, "DAV:supportedlock");
                        {   // exclusive/write
                            word lockentry = xml.add_tag(supportedlock, "DAV:lockentry");
                            xml.add_tag(xml.add_tag(lockentry, "DAV:lockscope"), "DAV:exclusive");
                            xml.add_tag(xml.add_tag(lockentry, "DAV:locktype"), "DAV:write");
                        }
                        {   // shared/write
                            word lockentry = xml.add_tag(supportedlock, "DAV:lockentry");
                            xml.add_tag(xml.add_tag(lockentry, "DAV:lockscope"), "DAV:shared");
                            xml.add_tag(xml.add_tag(lockentry, "DAV:locktype"), "DAV:write");
                        }
                    }
                    if (properties &DAV_PROP_LOCKDISCOVERY) {
                        // lockdiscovery: current lock status of the resource
                        
                        class WebdavLock * lock = this->webdavservice->LockLookup(path);
                        word lockdiscovery = xml.add_tag(prop, "DAV:lockdiscovery");
                        if (lock) {
                            word activelock = xml.add_tag(lockdiscovery, "DAV:activelock");
                            if (lock->write) {
                                word type = xml.add_tag(activelock, "DAV:locktype");
                                xml.add_tag(type, "DAV:write");
                            }
                            if (true) {
                                word scope = xml.add_tag(activelock, "DAV:lockscope");
                                xml.add_tag(scope, lock->exclusive ? "DAV:exclusive" : "DAV:shared");
                            }
                            if (false) {
                                word depth = xml.add_tag(activelock, "DAV:depth");
                                xml.add_content(depth, "Infinity");
                            }
                            if (lock->owner || lock->owner_href) {
                                word owner = xml.add_tag(activelock, "DAV:owner");
                                if (lock->owner_href) {
                                    word href = xml.add_tag(owner, "DAV:href");
                                    xml.add_content(href, lock->owner_href);
                                }
                                else {
                                    xml.add_content(owner, lock->owner);
                                }
                            }
                            if (true) {
                                word timeout = xml.add_tag(activelock, "DAV:timeout");
                                if (lock->timeout) {
                                    unsigned len = _sprintf(timeout_val, "Second-%u", (unsigned)lock->timeout);
                                    xml.add_content(timeout, timeout_val, len);
                                }
                                else {
                                    xml.add_content(timeout, "Infinite", 8);
                                }
                            }
                            if (lock->token) {
                                word token = xml.add_tag(activelock, "DAV:locktoken");
                                word href = xml.add_tag(token, "DAV:href");
                                xml.add_content(href, lock->token);
                            }
                        }
                    }
                }
            }
            word status = xml.add_tag(propstat, "DAV:status");
            xml.add_content(status, "HTTP/1.1 200 OK");
    }       
    word length = xml.encode() + len;
    //this->webserverpropfind->SetResultCode(WEBDAV_RESULT_MULTIPLE_STATUS,length);
    this->webdavServicePropfindWebserver->Send(buffer, length);
    delete [] buffer;
}


void WebdavServicePropfind::PathCompleted(bool completed, ulong64 id, const char * name, ulong64 length, bool isFolder, ulong64 created, ulong64 modified)
{
    this->pendingRequestDBFiles = false;
    if (id) {
        if ((rootFolderPath || !(path[0])) && id == DBFILES_ROOT) {
            
            isFolder = true;
        } //root Folder

        this->webdavServicePropfindWebserver->SetResultCode(WEBDAV_RESULT_MULTIPLE_STATUS, WS_RESPONSE_CHUNKED);
        unsigned xmlopenlength;
        char * xmlopen = XmlOpen(xmlopenlength);
        this->pendingRequestWebserver = true;
        this->webdavServicePropfindWebserver->Send(xmlopen, xmlopenlength);
        SendItem(0, length, isFolder, created, modified); // First Element is written with the path
        if (depth) {
            this->pendingRequestDBFiles = true;
            this->webdavServicePropfindDBFiles->List(id);
        }

        else {
            this->propfindComplete = true;
            unsigned xmlcloselength;
            char * xmlclose = XmlClose(xmlcloselength);
            this->pendingRequestWebserver = true;
            this->webdavServicePropfindWebserver->Send(xmlclose, xmlcloselength);
        }
    }
    else {
        this->pendingRequestWebserver = true;
        webdavServicePropfindWebserver->Cancel(WSP_CANCEL_NOT_FOUND);
    }
}

void WebdavServicePropfind::ListCompleted(bool completed)
{
    this->pendingRequestDBFiles = false;
    this->propfindComplete = true;
    unsigned xmlcloselength;
    char * xmlclose = XmlClose(xmlcloselength);
    this->pendingRequestWebserver = true;
    this->webdavServicePropfindWebserver->Send(xmlclose, xmlcloselength);
}

void WebdavServicePropfind::ListProgress(ulong64 id, const char * name, ulong64 length, bool isFolder, ulong64 created, ulong64 modified)
{
    this->pendingRequestDBFiles = false;
    if (id) {
        unsigned len = (unsigned)strlen(name) + (unsigned)strlen(path) + 1 + 1;
        char * buf = new char[len];
        sprintf(buf, "%s/%s", path, name);
        buf[len - 1] = '\0';
        this->pendingRequestWebserver = true;
        SendItem(name, length, isFolder, created, modified);
        delete [] buf;
        this->pendingRequestDBFiles = true;
        this->webdavServicePropfindDBFiles->Next();
    }
    else {
        this->pendingRequestWebserver = true;
        webdavServicePropfindWebserver->Cancel(WSP_CANCEL_INTERNAL_ERROR);
    }
}

void WebdavServicePropfind::Close()
{
    if (this->webdavServicePropfindDBFiles) this->webdavServicePropfindDBFiles->Close();
    if (this->webdavServicePropfindWebserver)this->webdavServicePropfindWebserver->Close();
}

void WebdavServicePropfind::CloseComplete(class UWebdavServiceTask * task)
{
    if (task == this->webdavServicePropfindWebserver)
    {
        this->pendingRequestWebserver = false;
        delete this->webdavServicePropfindWebserver;
        this->webdavServicePropfindWebserver = 0;
    }
    if (task == this->webdavServicePropfindDBFiles)
    {
        this->pendingRequestDBFiles = false;
        delete this->webdavServicePropfindDBFiles;
        this->webdavServicePropfindDBFiles = 0;
    }
    if (this->pendingRequestDBFiles)
    {
        this->webdavServicePropfindDBFiles->Close();
    }
    else if (this->webdavServicePropfindDBFiles)
    {
        this->pendingRequestDBFiles = false;
        delete this->webdavServicePropfindDBFiles;
        this->webdavServicePropfindDBFiles = 0;
    }
    if (this->pendingRequestWebserver)
    {
        this->webdavServicePropfindWebserver->Close();
    }
    else if (this->webdavServicePropfindWebserver)
    {
        this->pendingRequestWebserver = false;
        delete this->webdavServicePropfindWebserver;
        this->webdavServicePropfindWebserver = 0;
    }
    if (!this->webdavServicePropfindDBFiles && !this->webdavServicePropfindWebserver)
    {
        this->webdavservice->CloseComplete(this);
        delete this;
    }
}

//***************************************************************************************************//
//********************************** WebdavServiceMove **********************************************//
//***************************************************************************************************//

WebdavServiceMoveWebserver::WebdavServiceMoveWebserver(class WebdavServiceMove * webdavServiceMove)
{
    this->webdavServiceMove = webdavServiceMove;
    this->webserverMove = nullptr;
}

WebdavServiceMoveWebserver::~WebdavServiceMoveWebserver()
{

}

void WebdavServiceMoveWebserver::WebserverMoveRequestAcceptComplete(IWebserverMove * const webserverMove)
{
    this->webserverMove = webserverMove;
    this->webdavServiceMove->AcceptReceived();
}

void WebdavServiceMoveWebserver::WebserverMoveSendResult(IWebserverMove * const webserverMove)
{
    this->webdavServiceMove->SendResult();
}

void WebdavServiceMoveWebserver::WebserverMoveCloseComplete(IWebserverMove * const webserverMove)
{
    delete webserverMove;
    this->webdavServiceMove->CloseComplete(this);
}

void WebdavServiceMoveWebserver::Cancel(wsr_cancel_type_t type)
{
    this->webserverMove->Cancel(type);
}

void WebdavServiceMoveWebserver::Close()
{
    webserverMove->Close();
}

void WebdavServiceMoveWebserver::SetResultCode(ws_webdav_result_t result, ulong64 datasize)
{
    webserverMove->SetResultCode(result, datasize);
}

void WebdavServiceMoveWebserver::Send(const void * data, size_t len)
{
    webserverMove->Send(data, len);
}

const char * WebdavServiceMoveWebserver::GetHeaderFieldValue(const char * field)
{
    return webserverMove->GetHeaderFieldValue(field);
}


WebdavServiceMoveDBFiles::WebdavServiceMoveDBFiles(class WebdavServiceMove * webdavServiceMove, class IDbFiles * dbFiles)
{
    this->webdavServiceMove = webdavServiceMove;
    this->dbFiles = dbFiles;
    this->dstpathinfo = nullptr;
    this->filesDelete = nullptr;
    this->filesMove = nullptr;
    this->srcpathinfo = nullptr;
    this->state = 0;
}

WebdavServiceMoveDBFiles::~WebdavServiceMoveDBFiles()
{
}

void WebdavServiceMoveDBFiles::LookSrcPath(char * path)
{
    this->state = MOVEPATHREQUESTSOURCE;
    srcpathinfo = dbFiles->PathInfo(this, path);
}

void WebdavServiceMoveDBFiles::LookDstPath(char * path)
{
    this->state = MOVEPATHREQUESTDESTINATION;
    dstpathinfo = dbFiles->PathInfo(this, path);
}

void WebdavServiceMoveDBFiles::SrcGetFailed(ulong64 & parent, bool & isFolder, const char * &path)
{
    srcpathinfo->GetFailed(parent, isFolder, path);
}

void WebdavServiceMoveDBFiles::DstGetFailed(ulong64 & parent, bool & isFolder, const char * &path)
{
    dstpathinfo->GetFailed(parent, isFolder, path);
}

void WebdavServiceMoveDBFiles::Delete(ulong64 id)
{
    this->state = MOVEDELETEDESTINATION;
    filesDelete = this->dbFiles->Del(this, id);
}

void WebdavServiceMoveDBFiles::Move(ulong64 id, char * name, ulong64 folder)
{
    this->state = MOVEWRITETODESTINATION;
    filesMove = this->dbFiles->Move(this, id, name, folder);
}

void WebdavServiceMoveDBFiles::TaskComplete(class ITask * const task)
{
    switch (state){
    case MOVEPATHREQUESTSOURCE:
    {
        ulong64 id;
        const char *  name;
        ulong64 length;
        bool isFolder;
        ulong64 created;
        ulong64 modified;
        srcpathinfo->Get(id, name, length, isFolder, &created, &modified);
        this->webdavServiceMove->SrcPathCompleted(true, id, name, length, isFolder, created, modified);
        
        break;
    }
    case MOVEPATHREQUESTDESTINATION:
    {
        ulong64 id;
        const char *  name;
        ulong64 length;
        bool isFolder;
        ulong64 created;
        ulong64 modified;
        dstpathinfo->Get(id, name, length, isFolder, &created, &modified);
        this->webdavServiceMove->DstPathCompleted(true, id, name, length, isFolder, created, modified);
        
        break;
    }
    
    case MOVEDELETEDESTINATION:
        this->webdavServiceMove->DeleteCompleted(true);
        break;
    case MOVEWRITETODESTINATION:
    {
        this->webdavServiceMove->MoveCompleted(true);
        break;
    }
    case MOVECLOSE:
        delete task;
        this->webdavServiceMove->CloseComplete(this);
        return;
    }
    delete task;
}

void WebdavServiceMoveDBFiles::TaskFailed(class ITask * const task)
{
    switch (state)
    {
    case MOVEPATHREQUESTSOURCE:
        this->webdavServiceMove->SrcPathCompleted(false, 0, 0, 0, 0, 0, 0);
        break;
    case MOVEPATHREQUESTDESTINATION:
    {
        this->webdavServiceMove->DstPathCompleted(false, 0, 0, 0, 0, 0, 0);
        break;
    }
    case MOVEDELETEDESTINATION:
    {
        this->webdavServiceMove->DeleteCompleted(false);
        
        break;
    }
    case MOVEWRITETODESTINATION:
        this->webdavServiceMove->MoveCompleted(false);
        break;
    case MOVECLOSE:
        delete task;
        this->webdavServiceMove->CloseComplete(this);
        return;
    default:
        break;
    }
    delete task;
}

void WebdavServiceMoveDBFiles::TaskProgress(class ITask * const task, dword progress)
{
    delete task;
}

void WebdavServiceMoveDBFiles::Close()
{
    this->state = MOVECLOSE;
}

WebdavServiceMove::WebdavServiceMove(IWebserverPlugin * const webserverPlugin, class WebdavService * webdavservice, char * path, ulong64 dataSize)
{
    webdavservice->log->Log(LOG_WEBDAV_SERVICE, "WebdavServiceMove(%08x)::WebdavServiceMove path:%s dataSize:%llu", this, path, dataSize);
    this->path = _strdup(path);
    this->webdavservice = webdavservice;
    this->dataSize = dataSize;
    this->iscollection = false;
    this->destination = NULL;
    this->webdavServiceMoveWebserver = new class WebdavServiceMoveWebserver(this);
    this->webdavServiceMoveDBFiles = new class WebdavServiceMoveDBFiles(this, webdavservice->dbFiles);
    this->pendingRequestWebserver = true;
    this->pendingRequestDBFiles = false;
    this->filename = 0;
    this->depth = 0;
    this->host = nullptr;
    this->overwrite = false;
    this->parentId = 0;
    this->srcId = 0;
    webserverPlugin->Accept(webdavServiceMoveWebserver);
}
WebdavServiceMove::~WebdavServiceMove()
{
    webdavservice->log->Log(LOG_WEBDAV_SERVICE, "WebdavServiceMove(%08x)::~WebdavServiceMove", this);
    free(this->path);
    if (destination) free(destination);
    if (filename) free(filename);
}

void WebdavServiceMove::AcceptReceived()
{
    this->pendingRequestWebserver = false;
    this->depth = GetDepthHeader();
    this->host = GetHostHeader();
    char * dstPath = GetDestinationHeader();
    str::from_url(dstPath);
    this->destination = _strdup(dstPath);
    this->overwrite = GetOverwriteHeader();
    char * pch = this->path + strlen(this->path) - 1;
    if (*pch == '/') {
        *pch = '\0';
        this->iscollection = true;
    }
    this->pendingRequestDBFiles = true;
    this->webdavServiceMoveDBFiles->LookSrcPath(this->path);
}

void WebdavServiceMove::SendResult()
{
    this->pendingRequestWebserver = false;
    Close();
}

void WebdavServiceMove::Close()
{
    if (this->webdavServiceMoveDBFiles) this->webdavServiceMoveDBFiles->Close();
    if(this->webdavServiceMoveWebserver) this->webdavServiceMoveWebserver->Close();
}

void WebdavServiceMove::CloseComplete(class UWebdavServiceTask * task)
{
    if (task == this->webdavServiceMoveWebserver)
    {
        this->pendingRequestWebserver = false;
        delete this->webdavServiceMoveWebserver;
        this->webdavServiceMoveWebserver = 0;
    }
    if (task == this->webdavServiceMoveDBFiles)
    {
        this->pendingRequestDBFiles = false;
        delete this->webdavServiceMoveDBFiles;
        this->webdavServiceMoveDBFiles = 0;
    }
    if (this->pendingRequestDBFiles)
    {
        this->webdavServiceMoveDBFiles->Close();
    }
    else if (this->webdavServiceMoveDBFiles)
    {
        this->pendingRequestDBFiles = false;
        delete this->webdavServiceMoveDBFiles;
        this->webdavServiceMoveDBFiles = 0;
    }
    if (this->pendingRequestWebserver)
    {
        this->webdavServiceMoveWebserver->Close();
    }
    else if (this->webdavServiceMoveWebserver)
    {
        this->pendingRequestWebserver = false;
        delete this->webdavServiceMoveWebserver;
        this->webdavServiceMoveWebserver = 0;
    }
    if (!this->webdavServiceMoveWebserver) // && !this->webdavServiceMoveWebserver)
    {
        this->webdavservice->CloseComplete(this);
        delete this;
    }
}

void WebdavServiceMove::SrcPathCompleted(bool completed, ulong64 id, const char * name, ulong64 length, bool isFolder, ulong64 created, ulong64 modified)
{
    this->pendingRequestDBFiles = false;
    if (completed && id) {
        this->srcId = id;
        char * pch = this->destination + strlen(this->destination) - 1;
        if (*pch == '/') {
            *pch = '\0';
        }
        this->pendingRequestDBFiles = true;
        this->webdavServiceMoveDBFiles->LookDstPath(this->destination);
    }
    else {
        this->pendingRequestWebserver = true;
        this->webdavServiceMoveWebserver->Cancel(WSP_CANCEL_NOT_FOUND);
    }
}

void WebdavServiceMove::DstPathCompleted(bool completed, ulong64 id, const char * name, ulong64 length, bool isFolder, ulong64 created, ulong64 modified)
{
    this->pendingRequestDBFiles = false;
    if (completed) {
        char * pathcopy = _strdup(this->destination);
        char * pch = pathcopy + strlen(pathcopy) - 1;
        pch = strrchr(pathcopy, '/');
        if (pch) filename = _strdup(pch + 1);
        else filename = _strdup(pathcopy);
        free(pathcopy);
        if (id) {
            if (overwrite)
            {
                const char * path;
                this->webdavServiceMoveDBFiles->DstGetFailed(parentId, isFolder, path);
                this->pendingRequestDBFiles = true;
                this->webdavServiceMoveDBFiles->Delete(id);
            }
            else {
                webdavServiceMoveWebserver->SetResultCode(WEBDAV_RESULT_PRECONDITION_FAILED);
                this->pendingRequestWebserver = true;
                webdavServiceMoveWebserver->Send(0, 0);
            }

        }
        else {
            const char * path;
            ulong64 parent;
            this->webdavServiceMoveDBFiles->DstGetFailed(parent, isFolder, path);
            if (parent)
            {
                this->pendingRequestDBFiles = true;
                this->webdavServiceMoveDBFiles->Move(srcId, filename, parent);
                /*webdavServiceMoveWebserver->SetResultCode(WEBDAV_RESULT_CREATED);
                webdavServiceMoveWebserver->Send(0, 0);*/
            }
            else {
                webdavServiceMoveWebserver->SetResultCode(WEBDAV_RESULT_CONFLICT);
                this->pendingRequestWebserver = true;
                webdavServiceMoveWebserver->Send(0, 0);
            }
        }
    }
    else {
        webdavServiceMoveWebserver->SetResultCode(WEBDAV_RESULT_CONFLICT);
        this->pendingRequestWebserver = true;
        webdavServiceMoveWebserver->Send(0, 0);
    }
}

void WebdavServiceMove::MoveCompleted(bool completed)
{
    this->pendingRequestDBFiles = false;
    if (completed){
        if (webdavservice && !webdavservice->notifications.empty()) {
            WebdavNotification* notification = webdavservice->notifications.front();
            while (notification) {
                if (notification->webdavNotification) {
                    notification->webdavNotification->MoveCompleted(notification->appName, this->path);
                }
                notification = notification->goNext();
            }
        }
        this->pendingRequestWebserver = true;
        webdavServiceMoveWebserver->SetResultCode(WEBDAV_RESULT_CREATED);
        webdavServiceMoveWebserver->Send(0, 0);
    }
    else {
        this->pendingRequestWebserver = true;
        webdavServiceMoveWebserver->SetResultCode(WEBDAV_RESULT_CONFLICT);
        webdavServiceMoveWebserver->Send(0, 0);
    }
}

void WebdavServiceMove::DeleteCompleted(bool completed)
{
    this->pendingRequestDBFiles = false;
    if (completed) {
        this->pendingRequestDBFiles = true;
        this->webdavServiceMoveDBFiles->Move(srcId, filename, parentId);
        /*webdavServiceMoveWebserver->SetResultCode(WEBDAV_RESULT_NO_CONTENT);
        webdavServiceMoveWebserver->Send(0, 0);*/
    }
    else {
        this->pendingRequestWebserver = true;
        webdavServiceMoveWebserver->SetResultCode(WEBDAV_RESULT_CONFLICT);
        webdavServiceMoveWebserver->Send(0, 0);
    }
}

dword WebdavServiceMove::GetDepthHeader()
{
    const char * depthchar = this->webdavServiceMoveWebserver->GetHeaderFieldValue("Depth");
    if (depthchar)
    {
        if (depthchar[0] == '0')
            return 0;
    }
    return DAV_DEPTH_INFINITY;
}

const char * WebdavServiceMove::GetHostHeader()
{
    return this->webdavServiceMoveWebserver->GetHeaderFieldValue("Host");
}

char * WebdavServiceMove::GetDestinationHeader()
{
    class uri_dissector uri;
    char * dst = const_cast<char*>(this->webdavServiceMoveWebserver->GetHeaderFieldValue("Destination"));
    if (uri.dissect_uri(dst)){
        char * path;

        if (webdavservice->webserverPath && strstr(uri.path, webdavservice->webserverPath)){
            path = &uri.path[strlen(webdavservice->webserverPath) + 1];
        }
        else {
            path = &uri.path[1];
        }        
        return path;
    }
    return NULL;
}

bool WebdavServiceMove::GetOverwriteHeader()
{
    const char * overwritechar = this->webdavServiceMoveWebserver->GetHeaderFieldValue("Overwrite");
    if (overwritechar)
    {
        if (overwritechar[0] == 'F')
            return false;
    }
    return true;
}

////***************************************************************************************************//
////********************************** WebdavServiceCopy **********************************************//
////***************************************************************************************************//
//
//
//
//WebdavServiceCopyWebserver::WebdavServiceCopyWebserver(class WebdavServiceCopy * webdavServiceCopy)
//{
//    this->webdavServiceCopy = webdavServiceCopy;
//}
//
//WebdavServiceCopyWebserver::~WebdavServiceCopyWebserver()
//{
//
//}
//
//void WebdavServiceCopyWebserver::WebserverCopyRequestAcceptComplete(IWebserverCopy * const webserverCopy)
//{
//    this->webserverCopy = webserverCopy;
//    this->webdavServiceCopy->AcceptReceived();
//}
//
//void WebdavServiceCopyWebserver::WebserverMoveSendResult(IWebserverCopy * const webserverCopy)
//{
//    this->webdavServiceCopy->SendResult();
//}
//
//void WebdavServiceCopyWebserver::WebserverCopyCloseComplete(IWebserverCopy * const webserverCopy)
//{
//    delete webserverCopy;
//    this->webdavServiceCopy->CloseComplete(this);
//}
//
//void WebdavServiceCopyWebserver::Cancel(wsr_cancel_type_t type)
//{
//    this->webserverCopy->Cancel(type);
//}
//
//void WebdavServiceCopyWebserver::Close()
//{
//    webserverCopy->Close();
//}
//
//void WebdavServiceCopyWebserver::SetResultCode(ws_webdav_result_t result, size_t datasize)
//{
//    webserverCopy->SetResultCode(result, datasize);
//}
//
//void WebdavServiceCopyWebserver::Send(const void * data, size_t len)
//{
//    webserverCopy->Send(data, len);
//}
//
//const char * WebdavServiceCopyWebserver::GetHeaderFieldValue(const char * field)
//{
//    return webserverCopy->GetHeaderFieldValue(field);
//}
//
//
//WebdavServiceCopyDBFiles::WebdavServiceCopyDBFiles(class WebdavServiceMove * webdavServiceMove, class IDbFiles * dbFiles)
//{
//    this->webdavServiceMove = webdavServiceMove;
//    this->dbFiles = dbFiles;
//}
//
//WebdavServiceCopyDBFiles::~WebdavServiceCopyDBFiles()
//{
//}
//
//void WebdavServiceCopyDBFiles::LookSrcPath(char * path)
//{
//    this->state = COPYPATHREQUESTSOURCE;
//    srcpathinfo = dbFiles->PathInfo(this, path);
//}
//
//void WebdavServiceCopyDBFiles::LookDstPath(char * path)
//{
//    this->state = COPYPATHREQUESTDESTINATION;
//    dstpathinfo = dbFiles->PathInfo(this, path);
//}
//
//void WebdavServiceCopyDBFiles::SrcGetFailed(ulong64 & parent, bool & isFolder, const char * &path)
//{
//    srcpathinfo->GetFailed(parent, isFolder, path);
//}
//
//void WebdavServiceCopyDBFiles::DstGetFailed(ulong64 & parent, bool & isFolder, const char * &path)
//{
//    dstpathinfo->GetFailed(parent, isFolder, path);
//}
//
//void WebdavServiceCopyDBFiles::Delete(ulong64 id)
//{
//    this->state = MOVEDELETEDESTINATION;
//    filesDelete = this->dbFiles->Del(this, id);
//}
//
//void WebdavServiceCopyDBFiles::Move(ulong64 id, char * name, ulong64 folder)
//{
//    this->state = MOVEWRITETODESTINATION;
//    filesMove = this->dbFiles->Move(this, id, name, folder);
//}
//
//void WebdavServiceCopyDBFiles::TaskComplete(class ITask * const task)
//{
//    switch (state){
//    case COPYPATHREQUESTSOURCE:
//    {
//        ulong64 id;
//        const char *  name;
//        unsigned length;
//        bool isFolder;
//        ulong64 created;
//        ulong64 modified;
//        srcpathinfo->Get(id, name, length, isFolder, &created, &modified);
//        this->webdavServiceCopy->SrcPathCompleted(true, id, name, length, isFolder, created, modified);
//        
//        break;
//    }
//    case MOVEPATHREQUESTDESTINATION:
//    {
//        ulong64 id;
//        const char *  name;
//        unsigned length;
//        bool isFolder;
//        ulong64 created;
//        ulong64 modified;
//        dstpathinfo->Get(id, name, length, isFolder, &created, &modified);
//        this->webdavServiceMove->DstPathCompleted(true, id, name, length, isFolder, created, modified);
//        
//        break;
//    }
//    
//    case MOVEDELETEDESTINATION:
//        this->webdavServiceMove->DeleteCompleted(true);
//        break;
//    case MOVEWRITETODESTINATION:
//    {
//        this->webdavServiceMove->MoveCompleted(true);
//        break;
//    }
//    case COPYCLOSE:
//        delete task;
//        this->webdavServiceMove->CloseComplete(this);
//        return;
//    }
//    delete task;
//}
//
//void WebdavServiceCopyDBFiles::TaskFailed(class ITask * const task)
//{
//    switch (state)
//    {
//    case MOVEPATHREQUESTSOURCE:
//        this->webdavServiceMove->SrcPathCompleted(false, 0, 0, 0, 0, 0, 0);
//        break;
//    case MOVEPATHREQUESTDESTINATION:
//    {
//        this->webdavServiceMove->DstPathCompleted(false, 0, 0, 0, 0, 0, 0);
//        break;
//    }
//    case MOVEDELETEDESTINATION:
//    {
//        this->webdavServiceMove->DeleteCompleted(false);
//        
//        break;
//    }
//    case MOVEWRITETODESTINATION:
//        this->webdavServiceMove->MoveCompleted(false);
//        break;
//    case COPYCLOSE:
//        delete task;
//        this->webdavServiceMove->CloseComplete(this);
//        return;
//    default:
//        break;
//    }
//    delete task;
//}
//
//void WebdavServiceCopyDBFiles::TaskProgress(class ITask * const task, dword progress)
//{
//    delete task;
//}
//
//void WebdavServiceCopyDBFiles::Close()
//{
//    this->state = COPYCLOSE;
//}
//
//
//WebdavServiceCopy::WebdavServiceCopy(class WebdavService * webdavservice, char * path, size_t dataSize)
//{
//    webdavservice->log->Log(LOG_WEBDAV_SERVICE, "WebdavServiceCopy(%08x)::WebdavServiceCopy path:%s dataSize:%i", this, path, dataSize);
//    this->path = _strdup(path);
//    this->webdavservice = webdavservice;
//    this->dataSize = dataSize;
//    this->state = COPYPATHREQUESTSOURCE;
//    this->iscollection = false;
//    this->destination = NULL;
//}
//WebdavServiceCopy::~WebdavServiceCopy()
//{
//    webdavservice->log->Log(LOG_WEBDAV_SERVICE, "WebdavServiceCopy(%08x)::~WebdavServiceCopy", this);
//    free(this->path);
//    if (destination) free(destination);
//}
//
//void WebdavServiceCopy::AcceptReceived()
//{
//    this->pendingRequestWebserver = false;
//    this->depth = GetDepthHeader();
//    this->host = GetHostHeader();
//    this->destination = _strdup(GetDestinationHeader());
//    this->overwrite = GetOverwriteHeader();
//    char * pch = this->path + strlen(this->path) - 1;
//    if (*pch == '/') {
//        *pch = '\0';
//        this->iscollection = true;
//    }
//    this->pendingRequestDBFiles = true;
//    this->webdavServiceCopyDBFiles->LookSrcPath(this->path);
//}
//
//void WebdavServiceMove::SrcPathCompleted(bool completed, ulong64 id, const char * name, unsigned length, bool isFolder, ulong64 created, ulong64 modified)
//{
//    this->pendingRequestDBFiles = false;
//    if (completed && id) {
//        this->srcId = id;
//        char * pch = this->destination + strlen(this->destination) - 1;
//        if (*pch == '/') {
//            *pch = '\0';
//        }
//        this->pendingRequestDBFiles = true;
//        this->webdavServiceCopyDBFiles->LookDstPath(this->destination);
//    }
//    else {
//        this->pendingRequestWebserver = true;
//        this->webdavServiceCopyWebserver->Cancel(WSP_CANCEL_NOT_FOUND);
//    }
//}
//
//v/*oid WebdavServiceCopy::WebserverCopySendResult(IWebserverCopy * const webserverCopy)
//{
//    webservercopy->Close();
//}
//
//void WebdavServiceCopy::WebserverCopyCloseComplete(IWebserverCopy * const webserverCopy)
//{
//
//}*/
//
//void WebdavServiceCopy::TaskComplete(class ITask * const task)
//{
//    switch (state)
//    {
//    case COPYPATHREQUESTSOURCE:
//    {
//        ulong64 id;
//        const char *  name;
//        unsigned length;
//        bool is_folder;
//        srcpathinfo->Get(id, name, length, is_folder);
//        if (id) {
//            state = COPYPATHREQUESTDESTINATION;
//            char * pch = this->destination + strlen(this->destination) - 1;
//            if (*pch == '/') {
//                *pch = '\0';
//            }
//            dstpathinfo = this->webdavservice->dbFiles->PathInfo(this, this->destination);
//        }
//        else {
//            webservercopy->Cancel(WSP_CANCEL_NOT_FOUND);
//        }
//        break;
//    }
//    case COPYPATHREQUESTDESTINATION:
//    {
//        ulong64 id;
//        const char *  name;
//        unsigned length;
//        bool is_folder;
//        dstpathinfo->Get(id, name, length, is_folder);
//        if (id) {
//            if (overwrite)
//            {
//                if (is_folder) {
//                    // Overwrite Existing Folder
//                    webservercopy->Cancel(WSP_CANCEL_UNAVAILABLE);
//                }
//                else {
//                    // Overwrite Existing File
//                    webservercopy->Cancel(WSP_CANCEL_UNAVAILABLE);
//                }
//            }
//            else {
//                webservercopy->SetResultCode(WEBDAV_RESULT_PRECONDITION_FAILED);
//                webservercopy->Send(0, 0);
//            }
//        }
//        else {
//            if (is_folder) {
//                // Copy Folder
//                webservercopy->Cancel(WSP_CANCEL_UNAVAILABLE);
//            }
//            else
//            {
//                // Copy File
//                webservercopy->Cancel(WSP_CANCEL_UNAVAILABLE);
//            }
//        }
//        break;
//    }
//    case COPYWRITETODESTINATION:
//        break;
//    default:
//        break;
//    }
//    delete task;
//}
//
//void WebdavServiceCopy::TaskFailed(class ITask * const task)
//{
//    switch (state)
//    {
//    case COPYPATHREQUESTSOURCE:
//        webservercopy->Cancel(WSP_CANCEL_NOT_FOUND);
//        break;
//    case COPYPATHREQUESTDESTINATION:
//        webservercopy->SetResultCode(WEBDAV_RESULT_CONFLICT);
//        webservercopy->Send(0, 0);
//    case COPYWRITETODESTINATION:
//        break;
//    default:
//        break;
//    }
//    delete task;
//}
//
//void WebdavServiceCopy::TaskProgress(class ITask * const task, dword progress)
//{
//    switch (state)
//    {
//    case COPYPATHREQUESTSOURCE:
//        //webservercopy->Cancel(WSP_CANCEL_NOT_FOUND);
//        break;
//    case COPYPATHREQUESTDESTINATION:
//        break;
//    case COPYWRITETODESTINATION:
//        break;
//    default:
//        break;
//    }
//    delete task;
//}
//
//dword WebdavServiceCopy::GetDepthHeader()
//{
//    const char * depthchar = this->webservercopy->GetHeaderFieldValue("Depth");
//    if (depthchar)
//    {
//        if (depthchar[0] == '0')
//            return 0;
//    }
//    return DAV_DEPTH_INFINITY;
//}
//
//const char * WebdavServiceCopy::GetHostHeader()
//{
//    return this->webservercopy->GetHeaderFieldValue("Host");
//}
//
//const char * WebdavServiceCopy::GetDestinationHeader()
//{
//    class uri_dissector uri;
//    char * dst = const_cast<char*>(this->webservercopy->GetHeaderFieldValue("Destination"));
//    if (uri.dissect_uri(dst)){
//        char * path;
//
//        if (webdavservice->webserverPath && strstr(uri.path, webdavservice->webserverPath)){
//            path = &uri.path[strlen(webdavservice->webserverPath) + 1];
//        }
//        else {
//            path = &uri.path[1];
//        }
//        return path;
//    }
//    return NULL;
//}
//
//bool WebdavServiceCopy::GetOverwriteHeader()
//{
//    const char * overwritechar = this->webservercopy->GetHeaderFieldValue("Overwrite");
//    if (overwritechar)
//    {
//        if (overwritechar[0] == 'F')
//            return false;
//    }
//    return true;
//}

//***************************************************************************************************//
//********************************** WebdavServiceDelete ********************************************//
//***************************************************************************************************//

WebdavServiceDeleteWebserver::WebdavServiceDeleteWebserver(class WebdavServiceDelete * webdavServiceDelete)
{
    this->webdavServiceDelete = webdavServiceDelete;
    this->webserverDelete = nullptr;
}

WebdavServiceDeleteWebserver::~WebdavServiceDeleteWebserver()
{

}

void WebdavServiceDeleteWebserver::WebserverDeleteRequestAcceptComplete(IWebserverDelete * const webserverDelete)
{
    this->webserverDelete = webserverDelete;
    this->webdavServiceDelete->AcceptReceived();
}

void WebdavServiceDeleteWebserver::WebserverDeleteSendResult(IWebserverDelete * const webserverDelete)
{
    Close();
}

void WebdavServiceDeleteWebserver::WebserverDeleteCloseComplete(IWebserverDelete * const webserverDelete)
{
    delete webserverDelete;
    this->webdavServiceDelete->CloseComplete(this);
}

void WebdavServiceDeleteWebserver::Cancel(wsr_cancel_type_t type)
{
    this->webserverDelete->Cancel(type);
}

void WebdavServiceDeleteWebserver::Send(const void * data, size_t len)
{
    this->webserverDelete->Send(data, len);
}

void WebdavServiceDeleteWebserver::Close()
{
    webserverDelete->Close();
}

void WebdavServiceDeleteWebserver::SetResultCode(ws_webdav_result_t result, ulong64 datasize)
{
    webserverDelete->SetResultCode(result, datasize);
}


WebdavServiceDeleteDBFiles::WebdavServiceDeleteDBFiles(class WebdavServiceDelete * webdavServiceDelete, class IDbFiles * dbFiles)
{
    this->webdavServiceDelete = webdavServiceDelete;
    this->dbFiles = dbFiles;
    this->filesDelete = nullptr;
    this->pathinfo = nullptr;
    this->state = 0;
}

WebdavServiceDeleteDBFiles::~WebdavServiceDeleteDBFiles()
{
}

void WebdavServiceDeleteDBFiles::LookPath(char * path)
{
    this->state = DELETEPATHREQUEST;
    pathinfo = dbFiles->PathInfo(this, path);
}

void WebdavServiceDeleteDBFiles::Delete(ulong64 id)
{
    this->state = DELETEREQUEST;
    filesDelete = dbFiles->Del(this, id);
}

void WebdavServiceDeleteDBFiles::TaskComplete(class ITask * const task)
{
    switch (state){
    case DELETEPATHREQUEST:
    {
        ulong64 id;
        const char *  name;
        ulong64 length;
        bool isFolder;
        ulong64 created;
        ulong64 modified;
        pathinfo->Get(id, name, length, isFolder, &created, &modified);
        this->webdavServiceDelete->PathCompleted(true, id, name, length, isFolder, created, modified);
        break;
    }
    case DELETEREQUEST:
        this->webdavServiceDelete->DeleteCompleted(true);
        break;
    case DELETECLOSE:
        delete task;
        this->webdavServiceDelete->CloseComplete(this);
        return;
    }
    delete task;
}

void WebdavServiceDeleteDBFiles::TaskFailed(class ITask * const task)
{
    switch (state){
    case DELETEPATHREQUEST:
        this->webdavServiceDelete->PathCompleted(false, 0, 0, 0, 0, 0, 0);
        break;
    case DELETEREQUEST:
        this->webdavServiceDelete->DeleteCompleted(false);
        break;
    case DELETECLOSE:
        delete task;
        this->webdavServiceDelete->CloseComplete(this);
        return;
    }
    delete task;
}

void WebdavServiceDeleteDBFiles::TaskProgress(class ITask * const task, dword progress)
{
    delete task;
}

void WebdavServiceDeleteDBFiles::Close()
{
    this->state = DELETECLOSE;
}


WebdavServiceDelete::WebdavServiceDelete(IWebserverPlugin * const webserverPlugin, class WebdavService * webdavservice, char * path, ulong64 dataSize)
{
    webdavservice->log->Log(LOG_WEBDAV_SERVICE, "WebdavServiceDelete(%08x)::WebdavServiceDelete path:%s dataSize:%llu", this, path, dataSize);
    this->webdavServiceDeleteWebserver = new class WebdavServiceDeleteWebserver(this);
    this->webdavServiceDeleteDBFiles = new class WebdavServiceDeleteDBFiles(this, webdavservice->dbFiles);
    this->pendingRequestWebserver = true;
    this->pendingRequestDBFiles = false;
    this->webdavservice = webdavservice;
    this->path = _strdup(path);
    this->dataSize = 0;
    this->id = 0;
    this->pathinfocalled = false;
    webserverPlugin->Accept(webdavServiceDeleteWebserver);
}


WebdavServiceDelete::~WebdavServiceDelete()
{
    webdavservice->log->Log(LOG_WEBDAV_SERVICE, "WebdavServiceDelete(%08x)::~WebdavServiceDelete", this);
    free(this->path);
}

void WebdavServiceDelete::AcceptReceived()
{
    this->pendingRequestWebserver = false;
    this->pendingRequestDBFiles = true;
    char * pch = this->path + strlen(this->path) - 1;
    if (*pch == '/') *pch = '\0';
    this->webdavServiceDeleteDBFiles->LookPath(this->path);
}

void WebdavServiceDelete::PathCompleted(bool completed, ulong64 id, const char * name, ulong64 length, bool isFolder, ulong64 created, ulong64 modified)
{
    this->pendingRequestDBFiles = false;
    if (completed && id){
        this->pendingRequestDBFiles = true;
        this->webdavServiceDeleteDBFiles->Delete(id);
    }
    else {
        this->pendingRequestWebserver = true;
        this->webdavServiceDeleteWebserver->Cancel(WSP_CANCEL_NOT_FOUND);
    }
}

void WebdavServiceDelete::DeleteCompleted(bool completed)
{
    this->pendingRequestDBFiles = false;
    if (completed){
        this->pendingRequestWebserver = true;
        webdavServiceDeleteWebserver->SetResultCode(WEBDAV_RESULT_NO_CONTENT);
        webdavServiceDeleteWebserver->Send(0, 0);
        if (webdavservice && !webdavservice->notifications.empty()) {
            WebdavNotification* notification = webdavservice->notifications.front();
            while (notification) {
                if (notification->webdavNotification) {
                    notification->webdavNotification->DeleteCompleted(notification->appName, this->path);
                }
                notification = notification->goNext();
            }
        }
        
    }
    else
    {
        this->pendingRequestWebserver = true;
        this->webdavServiceDeleteWebserver->Cancel(WSP_CANCEL_NOT_FOUND);
    }
    
}

void WebdavServiceDelete::Close()
{
    if (this->webdavServiceDeleteDBFiles) this->webdavServiceDeleteDBFiles->Close();
    if (this->webdavServiceDeleteWebserver) this->webdavServiceDeleteWebserver->Close();
}

void WebdavServiceDelete::CloseComplete(class UWebdavServiceTask * task)
{
    if (task == this->webdavServiceDeleteWebserver)
    {
        this->pendingRequestWebserver = false;
        delete this->webdavServiceDeleteWebserver;
        this->webdavServiceDeleteWebserver = 0;
    }
    if (task == this->webdavServiceDeleteDBFiles)
    {
        this->pendingRequestDBFiles = false;
        delete this->webdavServiceDeleteDBFiles;
        this->webdavServiceDeleteDBFiles = 0;
    }
    if (this->pendingRequestDBFiles)
    {
        this->webdavServiceDeleteDBFiles->Close();
    }
    else if (this->webdavServiceDeleteDBFiles)
    {
        this->pendingRequestDBFiles = false;
        delete this->webdavServiceDeleteDBFiles;
        this->webdavServiceDeleteDBFiles = 0;
    }
    if (this->pendingRequestWebserver)
    {
        this->webdavServiceDeleteWebserver->Close();
    }
    else if (this->webdavServiceDeleteWebserver)
    {
        this->pendingRequestWebserver = false;
        delete this->webdavServiceDeleteWebserver;
        this->webdavServiceDeleteWebserver = 0;
    }
    if (!this->webdavServiceDeleteDBFiles && !this->webdavServiceDeleteWebserver)
    {
        this->webdavservice->CloseComplete(this);
        delete this;
    }
}

void WebdavServiceDelete::SendResult()
{
    Close();
}

//***************************************************************************************************//
//********************************** WebdavServiceMkCol *********************************************//
//***************************************************************************************************//

WebdavServiceMkColWebserver::WebdavServiceMkColWebserver(class WebdavServiceMkCol * webdavServiceMkCol)
{
    this->webdavServiceMkCol = webdavServiceMkCol;
    this->webserverMkCol = nullptr;
}

WebdavServiceMkColWebserver::~WebdavServiceMkColWebserver()
{

}

void WebdavServiceMkColWebserver::WebserverMkColRequestAcceptComplete(IWebserverMkCol * const webserverMkCol)
{
    this->webserverMkCol = webserverMkCol;
    this->webdavServiceMkCol->AcceptReceived();
}

void WebdavServiceMkColWebserver::WebserverMkColSendResult(IWebserverMkCol * const webserverMkCol)
{
    this->webdavServiceMkCol->SendResult();
}

void WebdavServiceMkColWebserver::WebserverMkColCloseComplete(IWebserverMkCol * const webserverMkCol)
{
    delete webserverMkCol;
    this->webdavServiceMkCol->CloseComplete(this);
}

void WebdavServiceMkColWebserver::Cancel(wsr_cancel_type_t type)
{
    this->webserverMkCol->Cancel(type);
}

void WebdavServiceMkColWebserver::Close()
{
    webserverMkCol->Close();
}

void WebdavServiceMkColWebserver::SetResultCode(ws_webdav_result_t result)
{
    webserverMkCol->SetResultCode(result);
}

void WebdavServiceMkColWebserver::Send(const void * data, size_t len)
{
    webserverMkCol->Send(data, len);
}

const char * WebdavServiceMkColWebserver::GetHeaderFieldValue(const char * field)
{
    return webserverMkCol->GetHeaderFieldValue(field);
}


WebdavServiceMkColDBFiles::WebdavServiceMkColDBFiles(class WebdavServiceMkCol * webdavServiceMkCol, class IDbFiles * dbFiles)
{
    this->webdavServiceMkCol = webdavServiceMkCol;
    this->dbFiles = dbFiles;
    this->filesmkcol = nullptr;
    this->pathinfo = nullptr;
    this->state = 0;
}

WebdavServiceMkColDBFiles::~WebdavServiceMkColDBFiles()
{
}

void WebdavServiceMkColDBFiles::LookPath(char * path)
{
    this->state = MKCOLPATHREQUEST;
    pathinfo = dbFiles->PathInfo(this, path);
}

void WebdavServiceMkColDBFiles::WriteFolder(char * foldername, ulong64 folder)
{
    this->state = MKCOLNOCOLLECTIONFOUND;
    filesmkcol = this->dbFiles->Put(this, foldername, folder, true);
}

void WebdavServiceMkColDBFiles::GetFailed(ulong64 & parent, bool & isFolder, const char * &path)
{
    pathinfo->GetFailed(parent, isFolder, path);
}

void WebdavServiceMkColDBFiles::TaskComplete(class ITask * const task)
{
    switch (state)
    {
    case MKCOLPATHREQUEST:
    {
        ulong64 id;
        const char *  name;
        ulong64 length;
        bool isFolder;
        ulong64 created;
        ulong64 modified;
        pathinfo->Get(id, name, length, isFolder, &created, &modified);
        this->webdavServiceMkCol->PathCompleted(true, id, name, length, isFolder, created, modified);
        break;
    }
    case MKCOLNOCOLLECTIONFOUND:
        this->webdavServiceMkCol->WriteCompleted(true);
        break;
    case MKCOLCLOSE:
        delete task;
        this->webdavServiceMkCol->CloseComplete(this);
        return;
    default:
        break;
    }
    delete task;
}

void WebdavServiceMkColDBFiles::TaskFailed(class ITask * const task)
{
    switch (state){
    case MKCOLPATHREQUEST:
        this->webdavServiceMkCol->PathCompleted(false, 0, 0, 0, 0, 0, 0);
        break;
    case MKCOLNOCOLLECTIONFOUND:
        this->webdavServiceMkCol->WriteCompleted(false);
        break;
    case MKCOLCLOSE:
        delete task;
        this->webdavServiceMkCol->CloseComplete(this);
        return;
    }
    delete task;
}

void WebdavServiceMkColDBFiles::TaskProgress(class ITask * const task, dword progress)
{
    delete task;
}

void WebdavServiceMkColDBFiles::Close()
{
    this->state = MKCOLCLOSE;
}


WebdavServiceMkCol::WebdavServiceMkCol(IWebserverPlugin * const webserverPlugin, class WebdavService * webdavservice, char * path, ulong64 dataSize)
{
    webdavservice->log->Log(LOG_WEBDAV_SERVICE, "WebdavServiceMkCol(%08x)::WebdavServiceMkCol path:%s dataSize:%llu", this, path, dataSize);
    this->path = _strdup(path);
    this->webdavservice = webdavservice;
    this->dataSize = dataSize;
    this->webdavServiceMkColWebserver = new class WebdavServiceMkColWebserver(this);
    this->webdavServiceMkColDBFiles = new class WebdavServiceMkColDBFiles(this, webdavservice->dbFiles);
    this->pendingRequestWebserver = true;
    this->pendingRequestDBFiles = false;
    this->foldername = 0;
    this->host = nullptr;
    this->id = 0;
    webserverPlugin->Accept(webdavServiceMkColWebserver);
}
WebdavServiceMkCol::~WebdavServiceMkCol()
{
    webdavservice->log->Log(LOG_WEBDAV_SERVICE, "WebdavServiceMkCol(%08x)::~WebdavServiceMkCol", this);
    free(this->path);
    if (foldername) free(foldername);
}

void WebdavServiceMkCol::AcceptReceived()
{
    this->pendingRequestWebserver = false;
    if (path && path[0]) {
        char * pch = this->path + strlen(this->path) - 1; // For Parent - ID it has to be changed...
        if (*pch == '/') *pch = '\0';
        this->pendingRequestDBFiles = true;
        this->webdavServiceMkColDBFiles->LookPath(this->path);
        this->host = GetHostHeader();
    }
    else {
        this->pendingRequestWebserver = true;
        this->webdavServiceMkColWebserver->Cancel(WSP_CANCEL_NOT_FOUND);
    }
}

void WebdavServiceMkCol::PathCompleted(bool completed, ulong64 id, const char * name, ulong64 length, bool isFolder, ulong64 created, ulong64 modified)
{
    webdavservice->log->Log(LOG_WEBDAV_SERVICE, "WebdavServiceMkCol(%08x)::PathCompleted completed=%u id=%x", this,completed, id);
    this->pendingRequestDBFiles = false;
    if (completed){
        char * pathcopy = _strdup(this->path);
        char * pch = pathcopy + strlen(pathcopy) - 1;
        pch = strrchr(pathcopy, '/');
        if (pch) foldername = _strdup(pch + 1);
        else foldername = _strdup(path);
        free(pathcopy);
        if (id) {
            this->pendingRequestWebserver = true;
            this->webdavServiceMkColWebserver->SetResultCode(WEBDAV_RESULT_METHOD_NOT_ALLOWED);
            this->webdavServiceMkColWebserver->Send(0, 0);
        }
        else {
            const char * path;
            ulong64 parent;
            bool parentFolder;
            webdavServiceMkColDBFiles->GetFailed(parent, parentFolder, path);
            if (parentFolder && parent)
            {
                this->pendingRequestDBFiles = true;
                webdavServiceMkColDBFiles->WriteFolder(foldername, parent);
            }
            else
            {
                this->pendingRequestWebserver = true;
                this->webdavServiceMkColWebserver->SetResultCode(WEBDAV_RESULT_CONFLICT);
                this->webdavServiceMkColWebserver->Send(0, 0);
            }
        }
        //this->webdavServicePutDBFiles->GetFile(id);
    }
    else {
        this->pendingRequestWebserver = true;
        this->webdavServiceMkColWebserver->Cancel(WSP_CANCEL_NOT_FOUND);
    }
}

void WebdavServiceMkCol::WriteCompleted(bool completed)
{
    this->pendingRequestDBFiles = false;
    if (completed){
        this->pendingRequestWebserver = true;
        webdavServiceMkColWebserver->SetResultCode(WEBDAV_RESULT_CREATED);
        webdavServiceMkColWebserver->Send(0, 0);
    }
    else
    {
        this->pendingRequestWebserver = true;
        this->webdavServiceMkColWebserver->Cancel(WSP_CANCEL_INTERNAL_ERROR);
    }
}

void WebdavServiceMkCol::Close()
{
    if (this->webdavServiceMkColDBFiles)this->webdavServiceMkColDBFiles->Close();
    if (this->webdavServiceMkColWebserver) this->webdavServiceMkColWebserver->Close();
}

void WebdavServiceMkCol::CloseComplete(class UWebdavServiceTask * task)
{
    if (task == this->webdavServiceMkColWebserver)
    {
        this->pendingRequestWebserver = false;
        delete this->webdavServiceMkColWebserver;
        this->webdavServiceMkColWebserver = 0;
    }
    if (task == this->webdavServiceMkColDBFiles)
    {
        this->pendingRequestDBFiles = false;
        delete this->webdavServiceMkColDBFiles;
        this->webdavServiceMkColDBFiles = 0;
    }
    if (this->pendingRequestDBFiles)
    {
        this->webdavServiceMkColDBFiles->Close();
    }
    else if (this->webdavServiceMkColDBFiles)
    {
        this->pendingRequestDBFiles = false;
        delete this->webdavServiceMkColDBFiles;
        this->webdavServiceMkColDBFiles = 0;
    }
    if (this->pendingRequestWebserver)
    {
        this->webdavServiceMkColWebserver->Close();
    }
    else if (this->webdavServiceMkColWebserver)
    {
        this->pendingRequestWebserver = false;
        delete this->webdavServiceMkColWebserver;
        this->webdavServiceMkColWebserver = 0;
    }
    if (!this->webdavServiceMkColDBFiles && !this->webdavServiceMkColWebserver)
    {
        this->webdavservice->CloseComplete(this);
        delete this;
    }
}

void WebdavServiceMkCol::SendResult()
{
    Close();
}

const char * WebdavServiceMkCol::GetHostHeader()
{
    return this->webdavServiceMkColWebserver->GetHeaderFieldValue("Host");
}


//***************************************************************************************************//
//********************************** WebdavServiceOptions *******************************************//
//***************************************************************************************************//


WebdavServiceOptions::WebdavServiceOptions(class WebdavService * webdavservice, char * path, ulong64 dataSize)
{
    webdavservice->log->Log(LOG_WEBDAV_SERVICE, "WebdavServiceOptions(%08x)::WebdavServiceOptions path:%s dataSize:%llu", this, path, dataSize);
    this->webdavservice = webdavservice;
    this->webserveroptions = nullptr;
}
WebdavServiceOptions::~WebdavServiceOptions()
{
    webdavservice->log->Log(LOG_WEBDAV_SERVICE, "WebdavServiceOptions(%08x)::~WebdavServiceOptions");
}

void WebdavServiceOptions::WebserverOptionsRequestAcceptComplete(IWebserverOptions * const webserverOptions)
{
    webserverOptions->SetSupportedRequests( WS_REQUEST_GET | 
                                            WS_REQUEST_PUT |
                                            WS_REQUEST_PROPFIND | 
                                            WS_REQUEST_MOVE | 
                                            WS_REQUEST_MKCOL | 
                                            WS_REQUEST_DELETE | 
                                            WS_REQUEST_OPTIONS);
    webserverOptions->Close();
}

void WebdavServiceOptions::WebserverOptionsCloseComplete(IWebserverOptions * const webserverOptions)
{
    delete webserverOptions;
    delete this;
}


//***************************************************************************************************//
//********************************** WebdavServiceLock **********************************************//
//***************************************************************************************************//

WebdavLock::WebdavLock()
{
    this->exclusive = false;
    this->expires = false;
    this->owner = nullptr;
    this->owner_href = nullptr;
    this->resource = nullptr;
    this->timeout = 0;
    this->token = nullptr;
    this->write = false;
}

WebdavLock::~WebdavLock() {
    if (owner) delete owner;
    owner = 0;
    if (owner_href) delete owner_href;
    owner_href = 0;
    if (token) delete token;
    token = 0;
    if (resource) delete resource;
    resource = 0;
}

char * LockXmlOpen(unsigned & length)
{   // Note: Writing XML content with default namespace definition (xmlns="DAV:")
    //       does not work with Microsoft-WebDAV-MiniRedir as user agent.
    static char str [] =
        "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
    length = sizeof(str) - 1;
    return str;
}

WebdavServiceLock::WebdavServiceLock(class WebdavService * webdavservice, char * path, ulong64 dataSize)
{
    webdavservice->log->Log(LOG_WEBDAV_SERVICE, "WebdavServiceLock(%08x)::WebdavServiceLock path: %s dataSize: %llu", this, path, dataSize);
    this->webdavservice = webdavservice;
    this->dataSize = dataSize;
    this->path = _strdup(path);
    this->webserverLock = nullptr;
}

WebdavServiceLock::~WebdavServiceLock()
{
    webdavservice->log->Log(LOG_WEBDAV_SERVICE, "WebdavServiceLock(%08x)::~WebdavServiceLock");
    free(this->path);
}

void WebdavServiceLock::WebserverLockRequestAcceptComplete(IWebserverLock * const webserverLock)
{
    this->webserverLock = webserverLock;
    this->webserverLock->Recv(0, 0);
}

void WebdavServiceLock::WebserverLockSendResult(IWebserverLock * const webserverLock)
{
    this->webserverLock->Close();
}

void WebdavServiceLock::WebserverLockCloseComplete(IWebserverLock * const webserverLock)
{
    delete webserverLock;
    delete this;
}

void WebdavServiceLock::WebserverLockRecvResult(IWebserverLock * const webserverLock, void * buffer, size_t len)
{
    dataSize -= len;
    if (dataSize == 0) {
        class WebdavLock * lock = GetLockProperties(buffer);
        if (lock){
            if (this->webdavservice->InsertLock(lock)){
                
                SendLockResponse(lock);
            }
            else {
                delete lock;
                this->webserverLock->SetResultCode(WEBDAV_RESULT_LOCKED);
                this->webserverLock->Send(0, 0);
            }
        }
        else {
            this->webserverLock->Cancel(WSP_CANCEL_INTERNAL_ERROR);
        }
    }
    else {
        this->webserverLock->Recv(0, 0);
    }
    free(buffer);
}

void * WebdavServiceLock::WebserverLockRecvBuffer(size_t len)
{
    return len ? malloc(len) : NULL;
}

void WebdavServiceLock::WebserverLockRecvCanceled(IWebserverLock * const webserverLock, void * buffer)
{
    free(buffer);
}

class WebdavLock * WebdavServiceLock::GetLockProperties(void * buffer)
{
    class WebdavLock * lock = 0;
    if (!buffer) return 0;
    class xml_io xml((char*) buffer);
    byte error = xml.read();
    if (error == 0) {
        word lockinfo = xml.get_first(XML_TYPE_TAG, XML_ROOT_TAG);
        const char * s = (lockinfo != XML_NO_TAG) ? xml.info(lockinfo) : 0;
        if (s && strstr(s, "lockinfo")) {

            // generate OpaqueLockToken-URI:
            char locktoken_uri[100];
            _sprintf(locktoken_uri, "opaquelocktoken:%8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x",
                IRandom::GetRandom(), 0xffff & IRandom::GetRandom(), 0xffff & IRandom::GetRandom(), 0xffff & IRandom::GetRandom(), 0xffff & IRandom::GetRandom(), IRandom::GetRandom());

            lock = new class WebdavLock();
            lock->token = _strdup(locktoken_uri);
            lock->resource = _strdup(path);
            lock->write = true; // default
            lock->exclusive = true; // default
            lock->timeout = 0; // 0 for infinite

            word tag = xml.get_first(XML_TYPE_TAG, lockinfo);
            while (tag != XML_NO_TAG) {

                if ((s = xml.info(tag))) {

                    if (strstr(s, "lockscope")) {
                        word sub = xml.get_first(XML_TYPE_TAG, tag);
                        s = (sub != XML_NO_TAG) ? xml.info(sub) : 0;
                        lock->exclusive = (s && strstr(s, "exclusive")) ? true : false;
                    }
                    else if (strstr(s, "locktype")) {
                        word sub = xml.get_first(XML_TYPE_TAG, tag);
                        s = (sub != XML_NO_TAG) ? xml.info(sub) : 0;
                        lock->write = (s && strstr(s, "write")) ? true : false;
                    }
                    else if (strstr(s, "owner")) {
                        word href = xml.get_first(XML_TYPE_TAG, tag);
                        while (href != XML_NO_TAG) {
                            if (strstr(xml.info(href), "href")) break;
                            href = xml.get_next(XML_TYPE_TAG, tag, href);
                        }
                        if (href != XML_NO_TAG) {
                            word content = xml.get_first(XML_TYPE_CONTENT, href);
                            s = (content != XML_NO_TAG) ? xml.info(content) : 0;
                            lock->owner_href = _strdup(s);
                        }
                        else {
                            word content = xml.get_first(XML_TYPE_CONTENT, tag);
                            s = (content != XML_NO_TAG) ? xml.info(content) : 0;
                            lock->owner = _strdup(s);
                        }
                    }
                    else if (strstr(s, "timeout")) {
                        word content = xml.get_first(XML_TYPE_CONTENT, tag);
                        s = (content != XML_NO_TAG) ? xml.info(content) : 0;
                        const char * p = s ? strstr(s, "Second-") : 0;
                        if (p) lock->timeout = strtol(p + 7, 0, 10);
                    }
                }
                tag = xml.get_next(XML_TYPE_TAG, lockinfo, tag);
            }
        }
    }

    if (!lock) {
        webdavservice->log->Log(LOG_WEBDAV_SERVICE, "GetLockProperties() decode error!");
    }

    return lock;
}

void WebdavServiceLock::SendLockResponse(class WebdavLock * lock)
{
    char * buffer = new char[WS_MAX_DATA_SIZE];
    char * tmp = buffer;

    class xml_io xml(tmp);

    word prop = xml.add_tag(XML_ROOT_TAG, "DAV:prop");
    xml.add_attrib(prop, "xmlns:DAV", "DAV:");
    {
        word lockdiscovery = xml.add_tag(prop, "DAV:lockdiscovery");
        {
            word activelock = xml.add_tag(lockdiscovery, "DAV:activelock");
            {
                if (lock->write) {
                    word locktype = xml.add_tag(activelock, "DAV:locktype");
                    xml.add_tag(locktype, "DAV:write");
                }
                if (true) {
                    word lockscope = xml.add_tag(activelock, "DAV:lockscope");
                    xml.add_tag(lockscope, lock->exclusive ? "DAV:exclusive" : "DAV:shared");
                }
                if (false) {
                    word depth = xml.add_tag(activelock, "DAV:depth");
                    xml.add_content(depth, "Infinity");
                }
                if (lock->owner || lock->owner_href) {
                    word owner = xml.add_tag(activelock, "DAV:owner");
                    if (lock->owner_href) {
                        word href = xml.add_tag(owner, "DAV:href");
                        xml.add_content(href, lock->owner_href);
                    }
                    else {
                        xml.add_content(owner, lock->owner);
                    }
                }
                if (true) {
                    word timeout = xml.add_tag(activelock, "DAV:timeout");
                    if (lock->timeout) {
                        static char b[64];
                        xml.add_content(timeout, b, _sprintf(b, "Second-%u", (unsigned)lock->timeout));
                    }
                    else {
                        xml.add_content(timeout, "Infinite", 8);
                    }
                }
                if (lock->token) {
                    word tag = xml.add_tag(activelock, "DAV:locktoken");
                    tag = xml.add_tag(tag, "DAV:href");
                    xml.add_content(tag, lock->token);
                }
            }
        }
    }

    word length = xml.encode();
    unsigned xmlopenlength;
    char * xmlopen = LockXmlOpen(xmlopenlength);
    this->webserverLock->SetResultCode(WEBDAV_RESULT_OK,xmlopenlength+length);
    this->webserverLock->Send(xmlopen, xmlopenlength);
    this->webserverLock->Send(buffer, length);
    delete [] buffer;
}

//***************************************************************************************************//
//********************************** WebdavServiceUnLock ********************************************//
//***************************************************************************************************//


WebdavServiceUnLock::WebdavServiceUnLock(class WebdavService * webdavservice, char * path, ulong64 dataSize)
{
    webdavservice->log->Log(LOG_WEBDAV_SERVICE, "WebdavServiceUnLock(%08x)::WebdavServiceUnLock path: %s dataSize: %llu", this, path, dataSize);
    this->webdavservice = webdavservice;
    this->path = _strdup(path);
    this->webserverUnlock = nullptr;
}
WebdavServiceUnLock::~WebdavServiceUnLock()
{
    webdavservice->log->Log(LOG_WEBDAV_SERVICE, "WebdavServiceUnLock(%08x)::~WebdavServiceUnLock");
    free(this->path);
}

void WebdavServiceUnLock::WebserverUnlockRequestAcceptComplete(IWebserverUnlock * const webserverUnlock)
{
    this->webserverUnlock = webserverUnlock;
    const char * token = this->webserverUnlock->GetHeaderFieldValue("Lock-Token");
    class WebdavLock * lock = this->webdavservice->LockLookup(path, token);
    if (lock) {
        delete lock;
        this->webserverUnlock->SetResultCode(WEBDAV_RESULT_NO_CONTENT);
        this->webserverUnlock->Send(0, 0);
    }
    else
    {
        this->webserverUnlock->Cancel(WSP_CANCEL_BAD_REQUEST);
    }
}

void WebdavServiceUnLock::WebserverUnlockSendResult(IWebserverUnlock * const webserverUnlock)
{
    this->webserverUnlock->Close();
}

void WebdavServiceUnLock::WebserverUnlockCloseComplete(IWebserverUnlock * const webserverUnlock)
{
    delete webserverUnlock;
    delete this;
}


//***************************************************************************************************//
//********************************** WebdavServiceProppatch *****************************************//
//***************************************************************************************************//

WebdavServiceProppatch::WebdavServiceProppatch(class WebdavService * webdavservice, char * path, ulong64 dataSize)
{
    webdavservice->log->Log(LOG_WEBDAV_SERVICE, "WebdavServiceProppatch(%08x)::WebdavServiceProppatch path: %s dataSize: %llu", this, path, dataSize);
    this->webdavservice = webdavservice;
    this->dataSize = dataSize;
    this->path = _strdup(path);
    this->webserverProppatch = nullptr;
}
WebdavServiceProppatch::~WebdavServiceProppatch()
{
    webdavservice->log->Log(LOG_WEBDAV_SERVICE, "WebdavServiceProppatch(%08x)::~WebdavServiceProppatch");
    free(this->path);
    
    while (!setProperties.empty())
    {
        class SetProperty* setProperty = this->setProperties.front();
        setProperty->remove();
        delete setProperty;
    }
    while (!remProperties.empty())
    {
        class RemProperty* remProperty = this->remProperties.front();
        remProperty->remove();
        delete remProperty;
    }
}

void WebdavServiceProppatch::WebserverProppatchRequestAcceptComplete(IWebserverProppatch * const webserverProppatch)
{
    this->webserverProppatch = webserverProppatch;
    this->webserverProppatch->Recv(0, 0);
}

void WebdavServiceProppatch::WebserverProppatchSendResult(IWebserverProppatch * const webserverProppatch)
{
    this->webserverProppatch->Close();
}

void WebdavServiceProppatch::WebserverProppatchCloseComplete(IWebserverProppatch * const webserverProppatch)
{
    delete webserverProppatch;
    delete this;
}

void WebdavServiceProppatch::WebserverProppatchRecvResult(IWebserverProppatch * const webserverProppatch, void * buffer, size_t len)
{
    dataSize -= len;
    if (dataSize == 0) {
        GetProppatchProperties(buffer);
        SendProppatchResponse();
    }
    else {
        this->webserverProppatch->Recv(0, 0);
    }
}

void * WebdavServiceProppatch::WebserverProppatchRecvBuffer(size_t len)
{
    return len ? malloc(len) : NULL;
}

void WebdavServiceProppatch::WebserverProppatchRecvCanceled(IWebserverProppatch * const webserverProppatch, void * buffer)
{
    free(buffer);
}

void WebdavServiceProppatch::GetProppatchProperties(void * buffer)
{
    class xml_io xml((char*) buffer);
    byte error = xml.read();
    if (error) return;

    word propertyupdate = xml.get_first(XML_TYPE_TAG, XML_ROOT_TAG);
    if (propertyupdate == XML_NO_TAG || !strstr(xml.info(propertyupdate), "propertyupdate")) return;

    // action can be "set" or "remove"
    word action = xml.get_first(XML_TYPE_TAG, propertyupdate);
    while (action != XML_NO_TAG) {

        bool set = !!strstr(xml.info(action), "set");
        bool rem = !!strstr(xml.info(action), "remove");

        word prop = xml.get_first(XML_TYPE_TAG, action);
        if (prop == XML_NO_TAG || !strstr(xml.info(prop), "prop")) continue;

        word sub = xml.get_first(XML_TYPE_TAG, prop);
        while (sub != XML_NO_TAG) {
            const char * propName = xml.info(sub);
            if (set) {
                this->setProperties.push_back(new SetProperty(propName));
                /*word response_propstat = result_xml.add_tag(response, "DAV:propstat");
                word response_propstat_prop = result_xml.add_tag(response_propstat, "DAV:prop");
                result_xml.add_tag(response_propstat_prop, prop_name);
                word response_propstat_status = result_xml.add_tag(response_propstat, "DAV:status");
                result_xml.add_content(response_propstat_status, "HTTP/1.1 200 OK");*/
            }
            if (rem) {
                this->remProperties.push_back(new RemProperty(propName));
            }
            sub = xml.get_next(XML_TYPE_TAG, prop, sub);
        }

        action = xml.get_next(XML_TYPE_TAG, propertyupdate, action);
    }
    free(buffer);
}

void WebdavServiceProppatch::SendProppatchResponse()
{
    char * buffer = new char[WS_MAX_DATA_SIZE];
    char * tmp = buffer;
    unsigned len = 0;
    class xml_io xml(tmp);
    char b[WS_MAX_DATA_SIZE];
    tmp = b;
    
    char tmpWebserverPath[1024];
    char tmpPath[1024];
    word response = xml.add_tag(XML_ROOT_TAG, "DAV:response");
    
    word href = xml.add_tag(response, "DAV:href");
    url_encode(tmpWebserverPath, webdavservice->webserverPath);
    xml.add_content(href, tmpWebserverPath);
    if (path && path[0]){
        xml.add_content(href, "/");
        url_encode(tmpPath, path);
        xml.add_content(href, tmpPath);
    }
        

    class SetProperty* setProperty = this->setProperties.front();
    do {
        if (setProperty) {
            const char * name = setProperty->propname;
            word propstat = xml.add_tag(response, "DAV:propstat");
            {
                word prop = xml.add_tag(propstat, "DAV:prop");
                {
                    xml.add_tag(prop, name);
                }
            }
            word status = xml.add_tag(propstat, "DAV:status");
            xml.add_content(status, "HTTP/1.1 200 OK");
            setProperty = setProperty->goNext();
        }
    } while (setProperty);
    class RemProperty* remProperty = this->remProperties.front();
    do {
        if (remProperty) {
            const char * name = remProperty->propname;
            word propstat = xml.add_tag(response, "DAV:propstat");
            {
                word prop = xml.add_tag(propstat, "DAV:prop");
                {
                    xml.add_tag(prop, name);
                }
            }
            word status = xml.add_tag(propstat, "DAV:status");
            xml.add_content(status, "HTTP/1.1 200 OK");
            remProperty = remProperty->goNext();
        }
    } while (remProperty);
    
    unsigned xmlopenlength;
    char * xmlopen = XmlOpen(xmlopenlength);
    word length = xml.encode() + len;
    unsigned xmlcloselength;
    char * xmlclose = XmlClose(xmlcloselength);
    this->webserverProppatch->SetResultCode(WEBDAV_RESULT_MULTIPLE_STATUS, ulong64(xmlopenlength) + length + xmlcloselength);
    this->webserverProppatch->Send(xmlopen, xmlopenlength);
    this->webserverProppatch->Send(buffer, length);
    this->webserverProppatch->Send(xmlclose, xmlcloselength);
    delete [] buffer;
}




