// $Id: NVT.h,v 1.6 2003/03/26 03:45:54 leres Exp $
//
// Copyright (c) 1999, 2001, 2002, 2003
//      The Regents of the University of California.  All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that: (1) source code distributions
// retain the above copyright notice and this paragraph in its entirety, (2)
// distributions including binary code include the above copyright notice and
// this paragraph in its entirety in the documentation or other materials
// provided with the distribution, and (3) all advertising materials mentioning
// features or use of this software display the following acknowledgement:
// ``This product includes software developed by the University of California,
// Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
// the University nor the names of its contributors may be used to endorse
// or promote products derived from this software without specific prior
// written permission.
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

#ifndef nvt_h
#define nvt_h

#include "TCP.h"


#define TELNET_OPTION_BINARY 0
#define TELNET_OPTION_TERMINAL 24
#define TELNET_OPTION_AUTHENTICATE 37
#define TELNET_OPTION_ENCRYPT 38
#define TELNET_OPTION_ENVIRON 39
#define NUM_TELNET_OPTIONS 5

class TelnetOption {
public:
	TelnetOption(TCP_NVT* endp, unsigned int code);

// Whether we told the other side WILL/WONT/DO/DONT.
#define OPT_SAID_WILL 0x1
#define OPT_SAID_WONT 0x2
#define OPT_SAID_DO 0x4
#define OPT_SAID_DONT 0x8

	unsigned int Code() const	{ return code; }

	int IsActive() const		{ return active; }

	int SaidWill() const	{ return flags & OPT_SAID_WILL; }
	int SaidWont() const	{ return flags & OPT_SAID_WONT; }
	int SaidDo() const	{ return flags & OPT_SAID_DO; }
	int SaidDont() const	{ return flags & OPT_SAID_DONT; }

	void SetWill()	{ flags |= OPT_SAID_WILL; }
	void SetWont()	{ flags |= OPT_SAID_WONT; }
	void SetDo()	{ flags |= OPT_SAID_DO; }
	void SetDont()	{ flags |= OPT_SAID_DONT; }

	void RecvOption(unsigned int type);
	virtual void RecvSubOption(u_char* data, int len);

	virtual void SetActive(int is_active);

	const TCP_NVT* Endpoint() const	{ return endp; }

protected:
	virtual void InconsistentOption(unsigned int type);
	virtual void BadOption();

	TCP_NVT* endp;
	unsigned int code;
	int flags;
	int active;
};

class TelnetTerminalOption : public TelnetOption {
public:
	TelnetTerminalOption(TCP_NVT* arg_endp)
		: TelnetOption(arg_endp, TELNET_OPTION_TERMINAL)	{ }

	void RecvSubOption(u_char* data, int len);
};

class TelnetEncryptOption : public TelnetOption {
public:
	TelnetEncryptOption(TCP_NVT* arg_endp)
		: TelnetOption(arg_endp, TELNET_OPTION_ENCRYPT)
			{ did_encrypt_request = doing_encryption = 0; }

	void RecvSubOption(u_char* data, int len);

	int DidRequest() const		{ return did_encrypt_request; }
	int DoingEncryption() const	{ return doing_encryption; }

protected:
	int did_encrypt_request, doing_encryption;
};

class TelnetAuthenticateOption : public TelnetOption {
public:
	TelnetAuthenticateOption(TCP_NVT* arg_endp)
		: TelnetOption(arg_endp, TELNET_OPTION_AUTHENTICATE)
			{ authentication_requested = 0; }

	void RecvSubOption(u_char* data, int len);

	int DidRequestAuthentication() const
		{ return authentication_requested; }

protected:
	int authentication_requested;
};

class TelnetEnvironmentOption : public TelnetOption {
public:
	TelnetEnvironmentOption(TCP_NVT* arg_endp)
		: TelnetOption(arg_endp, TELNET_OPTION_ENVIRON)
			{ }

	void RecvSubOption(u_char* data, int len);

protected:
	char* ExtractEnv(u_char*& data, int& len, int& code);
};

class TelnetBinaryOption : public TelnetOption {
public:
	TelnetBinaryOption(TCP_NVT* arg_endp)
		: TelnetOption(arg_endp, TELNET_OPTION_BINARY)
			{ }

	void SetActive(int is_active);

protected:
	void InconsistentOption(unsigned int type);
};

class TCP_NVT : public TCP_EndpointLine {
public:
	TCP_NVT(TCP_Connection* conn, int is_orig,
			int is_NUL_sensitive, int skip_partial,
		        int CR_LF_as_EOL = LF_as_EOL);
	~TCP_NVT();

	TelnetOption* FindOption(unsigned int code);
	TelnetOption* FindPeerOption(unsigned int code);

	void AuthenticationAccepted();
	void AuthenticationRejected();

	void SetTerminal(const u_char* terminal, int len);
	void SetBinaryMode(int mode)	{ binary_mode = mode; }
	void SetEncrypting(int mode);
	void SetAuthName(char* arg_auth_name)	{ auth_name = arg_auth_name; }

	const char* AuthName() const	{ return auth_name; }
	int AuthenticationHasBeenAccepted() const
		{ return authentication_has_been_accepted; }

protected:
	void DoDeliver(double t, int seq, int len, u_char* data);

	void ScanOption(double t, int seq, int len, u_char* data);
	virtual void SawOption(unsigned int code);
	virtual void SawOption(unsigned int code, unsigned int subcode);
	virtual void SawSubOption(const char* opt, int len);
	virtual void BadOptionTermination(unsigned int code);
	const char* PeerAuthName() const;

	int pending_IAC;	// true if we're working on an option/IAC
	int IAC_pos;		// where the IAC was seen
	int is_suboption;	// true if current option is suboption
	int last_was_IAC;	// for scanning suboptions

	int binary_mode, encrypting_mode;
	int authentication_has_been_accepted;	// if true, we accepted peer's authentication
	char* auth_name;

	TelnetOption* options[NUM_TELNET_OPTIONS];
	int num_options;
};

#endif
