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

#include "platform/platform.h"

#include "common/os/iomux.h"

#include "common/lib/httplib.h"

#include "common/interface/socket.h"
#include "common/interface/websocket_client.h"
#include "common/interface/webserver_plugin.h"
#include "common/lib/appservice.h"

#include "common/service/webserver_shared/webserver_internal.h"

#include "types.h"
#include "apparguments.h"
#include "webserver.h"
#include "http_method_handler.h"
#include "http_connection.h"
#include "http_datasource.h"
#include "http_webdav_handler.h"


HTTPWebdavHandler::HTTPWebdavHandler(UHTTPMethodHandler * const user, class HTTPRegisteredPath * reqPathObj, class HTTPDataSource * dataSource, ISocket * const remoteSocket, IInstanceLog * const log, ws_message_t message, connection_flags_t connFlags)
    : HTTPMethodHandler("HTTPWebdavHandler", user, reqPathObj, dataSource, remoteSocket, log, connFlags)
{
    this->message = message;

    this->needDataForHeader = false;
    this->dataSize = 0;
    this->transferInfo = { WSP_RESPONSE_NO_TYPE, WSP_FLAG_NONE, 0 };
    this->needSendHeader = false;
    this->debugResultChanged = false;
}


HTTPWebdavHandler::~HTTPWebdavHandler()
{
}


http_method_init_t HTTPWebdavHandler::OnHandlerInit(const HTTPParser * parser, byte * buffer, size_t size)
{
    dword clen = 0;

    ExtendedHeader extHeader(this->log, this->DataSource()->ProtocolVersion());
    extHeader.SetRequestPath(this->RequestPath(), this->RequestPathLen());
    extHeader.SetRegisteredPathForRequest(this->RegisteredPathForRequest(), this->RegisteredPathForRequestLen());
    extHeader.SetConnectionFlags(this->connFlags);
    extHeader.SetRemoteAddress(this->Socket());
    extHeader.SetHeaderFields(parser);

    // We must be backward compatible in sending the data.
    if (this->DataSource()->ProtocolVersion() == WEBSERVER_PROTOCOL_VERSION_0) {
        clen = (this->RequestIsChunkEncoded() ? WS_RESPONSE_CHUNKED_V0 : dword(parser->GetContentLength()));
    }
    else {
        if (this->RequestIsChunkEncoded()) extHeader.SetChunkEncodedTransfer(true);
        else extHeader.SetDataSize(parser->GetContentLength());
    }

    extHeader.Encode(this->User()->GetConnectionID());
    this->DataSource()->SendMessage("HTTPWebdavHandler", User()->GetConnectionID(), message, clen, extHeader.Buffer(), extHeader.BufferSize());

    return HTTP_INIT_DONE;
}


void HTTPWebdavHandler::PreparHeader()
{
    ws_webdav_result_t webdavResult = ws_webdav_result_t(this->transferInfo.flags);
    this->ResultHeader()->SetHTTPResult(WebDAVResult2HTTPResult(webdavResult));

    if (webdavResult == WEBDAV_RESULT_MULTIPLE_STATUS || webdavResult == WEBDAV_RESULT_OK || webdavResult == WEBDAV_RESULT_CREATED) {
        bool setContentType = this->dataSize > 0;
        if (this->SendingChunckEncodedEnabled()) {
            this->ResultHeader()->SetCustomField("Transfer-Encoding: chunked");
            setContentType = true;
        }
        else this->ResultHeader()->SetContentSize(this->dataSize);

        if (setContentType) this->ResultHeader()->SetContentType("application/xml; charset=utf-8");
    }
    else this->ResultHeader()->SetContentSize(0);
}


void HTTPWebdavHandler::PreparHeaderV2()
{
    std::string sc = this->GetStatusCodeStr(HTTPLib::HTTP_200);
    this->ResultHeader()->SetHTTPResult(sc.c_str());

    if (this->dataSize == 0 && !this->SendingChunckEncodedEnabled()) {
        this->ResultHeader()->SetContentSize(0);
    }
    else {
        if (this->SendingChunckEncodedEnabled()) this->ResultHeader()->SetCustomField("Transfer-Encoding: chunked");
        else this->ResultHeader()->SetContentSize(this->dataSize);
        this->SetResultMIMEType(this->transferInfo.resourceType);
    }
}


void HTTPWebdavHandler::OnPrepareHeader()
{
    if (this->DataSource()->ProtocolVersion() >= WEBSERVER_PROTOCOL_VERSION_2) this->PreparHeaderV2();
    else this->PreparHeader();

    char buff[512];
    sprintf(buff, "inno-debug-webdav: resultChanged %s", this->debugResultChanged ? "TRUE" : "FALSE");
    this->ResultHeader()->SetCustomField(buff);
}


void HTTPWebdavHandler::OnSendMetaData(dword info, ExtendedHeader & extHeader)
{
    this->needSendHeader = true;
    this->transferInfo = extHeader.TransferInfo();
    this->debugResultChanged = true;

    if (this->DataSource()->ProtocolVersion() == WEBSERVER_PROTOCOL_VERSION_0) {
        this->dataSize = extHeader.TransferInfo().dataSize;
        if (this->dataSize == WS_RESPONSE_CHUNKED) this->EnableChunkEncodedSend();
    }
    else {
        this->dataSize = extHeader.DataSize();
        if (extHeader.ChunkEncodedTransfer()) this->EnableChunkEncodedSend();
    }

    if (info != WSM_NONE) {
        bool closeSocket = this->User()->HandleControlMessage(ResultHeader(), info, &extHeader);
        this->User()->HTTPHandlerShutdown(this->DataSource(), closeSocket);
    }
}
