
/*---------------------------------------------------------------------------*/
/* webserverplugin.h                                                         */
/* copyright (c) innovaphone 2015                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

class WebserverPluginProvider : public IWebserverPluginProvider {
public:
    WebserverPluginProvider() {}
    virtual ~WebserverPluginProvider() {}
    virtual class IWebserverPlugin * CreateWebserverPlugin(class IIoMux * const iomux,
                                                           class ISocketProvider * localSocketProvider,
                                                           class UWebserverPlugin * const user,
                                                           const char * webserverAddress,
                                                           const char * appWebRoot,
                                                           class IInstanceLog * const log);
};


class WSRegisterPathItem : public istd::listElement<WSRegisterPathItem> {
private:
    class WSRegisteredPath * const rp;

public:
    WSRegisterPathItem(WSRegisteredPath * rp) : rp(rp) {}
    ~WSRegisterPathItem() {}
    WSRegisteredPath * RegisteredPath() { return this->rp; }
};

class DigestAuthenticatorLifeTime {
private:
	class HTTPAuthDigest * authDigest;
	dword lastAccess = 0;
	bool assignedToHandler = false;
	
public:
	DigestAuthenticatorLifeTime(class HTTPAuthDigest * authDigest) 
		: authDigest(authDigest)
	{
		this->UpdateLastAccess();
	}
	
	DigestAuthenticatorLifeTime(const DigestAuthenticatorLifeTime &) = delete;
	DigestAuthenticatorLifeTime(DigestAuthenticatorLifeTime && other)
	{
		this->authDigest = other.authDigest;
		this->lastAccess= other.lastAccess;
		this->assignedToHandler = other.assignedToHandler;
		other.authDigest = nullptr;
		other.assignedToHandler = false;
		other.lastAccess = 0;
	}
	~DigestAuthenticatorLifeTime() { delete this->authDigest; }
	dword LastAccess() { return this->lastAccess; }
	void UpdateLastAccess() {
		this->lastAccess = static_cast<dword>(ITime::TimeStampMilliseconds() / 1000);
	}
	void AssignedToHandler(bool flag) { this->assignedToHandler = flag; }
	bool AssignedToHandler() { return this->assignedToHandler; }
	class HTTPAuthDigest * Authenticator() { return this->authDigest; }
};

class WebserverPlugin : public IWebserverPlugin, public UWebserverMessageHandler, public USocket, public UTimer, public UIoExec {
private:
    IIoMux * const iomux;
    ISocketProvider * const localSocketProvider;
    UWebserverPlugin * const user;
    IInstanceLog * const log;
    ISocket * localSocket;
    IWebserverMessage * webserverMessageRecv;
    IWebserverMessage * webserverMessageSend;
    WebserverMessageHandler * pendingMessageHandler;
    btree * messageHandlers;

    char * appWebRoot;
    size_t appWebRootLen;
    char * webserverAddress;
    word protocolVersion;

    istd::list<class WSRegisteredPath> httpListenList;
    istd::list<class WSRegisteredPath> passthroughListenList;
    istd::list<class WSRegisteredPath> websocketListenList;
    istd::list<class WSRegisterPathItem> registerPathStack;
	class HTTPDigestHostManager * digestHostManager;
	std::map<dword, DigestAuthenticatorLifeTime> digestAuthenticators;
	std::string digestAuthRealm;

    ITimer * reconnectTimer;
	ITimer * cleanUpDigestDataTimer;
    int reconnectInterval;

    bool closing;
    int state;
    bool needFreeBuffer;
    wsp_close_reason_t closeReason;

    int registerResultCnt;
    bool pluginConnected;

    void * curRecvBuffer;
    void * userRecvBuffer;

    void Connect();
    void HandleListen(ws_message_t msg, const char * path, const char * staticFilesPfx, UWebserverPlugin * user, const char * authUsr, const char * authPwd);
    WSRegisteredPath * GetRegisterdPathForRequest(int pathType, const char * path);
    void HandleRegisterResult(class ExtendedHeader & extHeader);
    template<class T>void HandleRequest(class ExtendedHeader & extHeader, byte * extHeaderBuffer, ws_request_type_t requestType);
    void HandleGetRequest(class ExtendedHeader & extHeader, byte * extHeaderBuffer);

    bool PathHasStaticFiles(const char * thePath);
    void DoCancel(wsr_cancel_type_t reason, dword connectionID = 0);
    void CloseWebserverPluginUsers();

public:
    WebserverPlugin(IIoMux * const iomux,
                    ISocketProvider * const localSocketProvider,
                    UWebserverPlugin * const user,
                    const char * webserverAddress,
                    const char * appWebRoot,
                    IInstanceLog * const log);
    ~WebserverPlugin();

    virtual bool Connected() override { return this->pluginConnected; }
    virtual word ProtocolVersion() override { return this->protocolVersion; }

    // UWebserverMessageHandler
    virtual void RegisterHandler(class WebserverMessageHandler * handler) override;
    virtual void UnregisterHandler(class WebserverMessageHandler * handler) override;

    // IWebserverPlugin
    virtual void HttpListen(const char * path = nullptr, UWebserverPlugin * user = nullptr, const char * authUser = nullptr, const char * authUserPwd = nullptr, const char * staticFilePrefix = nullptr) override;
    virtual void HttpListen(const char * path, UWebserverPlugin * user, bool dynamicAuth, const char * staticFilePrefix = nullptr) override;
    virtual void WebsocketListen(const char * path = nullptr, UWebserverPlugin * user = nullptr) override;
    virtual void PassthroughListen(const char * path = nullptr, UWebserverPlugin * user = nullptr) override;
    
    virtual void SetAuthConfig(const char * path, const char * authUser = nullptr, const char * authPwd = nullptr) override;
    virtual void SetAuthConfig(const char * path, bool enableDynAuth) override;

    virtual void SendCertificate(const char * password, const byte * certBuf, size_t certLen, const char * hostName = nullptr, UWebserverPlugin * user = nullptr) override;

    virtual void Cancel(wsr_cancel_type_t reason) override;
    virtual void Redirect(const char * newDestination) override;
    virtual bool BuildRedirect(const char * resourceName, const char * build, unsigned buildLen) override;
    virtual bool BuildRedirectIP(const char * resourceName, const char * build, unsigned buildLen) override;

    virtual void Accept(class UWebserverGet * const user) override;
    virtual void Accept(class UWebserverPost * const user) override;
    virtual void Accept(class UWebserverPut * const user) override;
    virtual void Accept(class UWebserverPassthrough * const user) override;
    virtual void Accept(class UWebserverPropfind * const user) override;
    virtual void Accept(class UWebserverMove * const user) override;
    virtual void Accept(class UWebserverMkCol * const user) override;
    virtual void Accept(class UWebserverCopy * const user) override;
    virtual void Accept(class UWebserverDelete * const user) override;
    virtual void Accept(class UWebserverLock * const user) override;
    virtual void Accept(class UWebserverUnlock * const user) override;
    virtual void Accept(class UWebserverProppatch * const user) override;
    virtual void Accept(class UWebserverOptions * const  user) override;

    virtual void WebsocketAccept(class UWebsocket * user) override;
    virtual void RequestUserPasswordResult(dword connectionID, const char * user, const char * password) override;

    virtual void Close() override;

    // USocket
    virtual void SocketConnectComplete(ISocket * const socket) override;
    virtual void SocketShutdown(ISocket * const socket, shutdownreason_t reason) override;
    virtual void SocketSendResult(ISocket * const socket) override;
    virtual void SocketRecvResult(ISocket * const socket, void * buf, size_t len) override;
    virtual void SocketRecvCanceled(ISocket * const socket, void * buf) override;

    virtual void TimerOnTimeout(ITimer * timer) override;

    virtual void IoExec(void * execContext) override;
	
	virtual class WebserverDigestAuthenticator * GetDigestAuthenticator(dword connectionID, const char * host) override;
	virtual void SetDigestAuthenticationRealm(const char * realm) override { this->digestAuthRealm = realm; };
	virtual const char * GetDigestAuthenticationRealm() override { return this->digestAuthRealm.c_str(); }
	virtual const std::string & GetDigestAuthenticationRealmAsStr() override { return this->digestAuthRealm; }
	virtual const char * GetAppWebRoot() override { return this->appWebRoot; }
};
