#ifndef __TLSSOCKET_H__
#define __TLSSOCKET_H__

#define ssize_t long
#include <gnutls/gnutls.h>
#include "backend.h"

class CControlSocket;
class CTlsSocket : protected wxEvtHandler, public CBackend
{
public:
	enum TlsState
	{
		noconn,
		handshake,
		verifycert,
		conn,
		closing,
		closed
	};

	CTlsSocket(wxEvtHandler* pEvtHandler, wxSocketBase* pSocket, CControlSocket* pOwner);
	~CTlsSocket();

	bool Init();
	void Uninit();

	int Handshake(const CTlsSocket* pPrimarySocket = 0);

	virtual void Read(void *buffer, unsigned int len);
	virtual void Write(const void *buffer, unsigned int len);
	virtual bool Error() const { return !m_lastSuccessful; }
	virtual unsigned int LastCount() const { return m_lastCount; }
	virtual int LastError() const { return m_lastError; }
	virtual void Peek(void *buffer, unsigned int len);

	void Shutdown();

	void TrustCurrentCert(bool trusted);

	enum TlsState GetState() const { return m_tlsState; }
	
protected:

	virtual void OnRateAvailable(enum CRateLimiter::rate_direction direction);

	void ContinueShutdown();
	
	int VerifyCertificate();

	enum TlsState m_tlsState;

	CControlSocket* m_pOwner;

	bool m_initialized;
	gnutls_session_t m_session;

	gnutls_certificate_credentials_t m_certCredentials;

	void LogError(int code);
	void PrintAlert();
	
	// Failure logs the error, uninits the session and sends a close event
	void Failure(int code);

	static ssize_t PushFunction(gnutls_transport_ptr_t ptr, const void* data, size_t len);
	static ssize_t PullFunction(gnutls_transport_ptr_t ptr, void* data, size_t len);
	ssize_t PushFunction(const void* data, size_t len);
	ssize_t PullFunction(void* data, size_t len);

	void TriggerEvents();

	DECLARE_EVENT_TABLE();
	void OnSocketEvent(wxSocketEvent& event);
	void OnRead();
	void OnSend();

	bool m_canReadFromSocket;
	bool m_canWriteToSocket;
	bool m_canCheckCloseSocket;

	bool m_canTriggerRead;
	bool m_canTriggerWrite;

	bool m_socketClosed;

	CSocketBackend* m_pSocketBackend;
	wxSocketBase* m_pSocket;

	// Used by LastError() and LastCount()
	int m_lastError;
	unsigned int m_lastCount;
	bool m_lastSuccessful;
	bool m_shutdown_requested;

	// Due to the strange gnutls_record_send semantics, call it again
	// with 0 data and 0 length after GNUTLS_E_AGAIN and store the number
	// of bytes written. These bytes get skipped on next write from the
	// application.
	// This avoids the rule to call it again with the -same- data after
	// GNUTLS_E_AGAIN.
	void CheckResumeFailedWrite();
	bool m_lastWriteFailed;
	unsigned int m_writeSkip;

	// Peek data
	char* m_peekData;
	unsigned int m_peekDataLen;

	gnutls_datum_t m_implicitTrustedCert;
};

#endif //__TLSSOCKET_H__


syntax highlighted by Code2HTML, v. 0.9.1