// See the file "COPYING" in the main distribution directory for copyright.

#pragma once

#include "zeek/analyzer/protocol/tcp/TCP.h"

namespace zeek::analyzer::login {

enum login_state {
	LOGIN_STATE_AUTHENTICATE,	// trying to authenticate
	LOGIN_STATE_LOGGED_IN,	// successful authentication
	LOGIN_STATE_SKIP,	// skip any further processing
	LOGIN_STATE_CONFUSED,	// we're confused
};

// If no action by this many lines, we're definitely confused.
#define MAX_AUTHENTICATE_LINES 50

// Maximum # lines look after login for failure.
#define MAX_LOGIN_LOOKAHEAD 10

class Login_Analyzer : public analyzer::tcp::TCP_ApplicationAnalyzer {
public:
	Login_Analyzer(const char* name, Connection* conn);
	~Login_Analyzer() override;

	void DeliverStream(int len, const u_char* data, bool orig) override;

	void SetEnv(bool orig, char* name, char* val) override;

	login_state LoginState() const		{ return state; }
	void SetLoginState(login_state s)	{ state = s; }

	void EndpointEOF(bool is_orig) override;

protected:
	void NewLine(bool orig, char* line);
	void AuthenticationDialog(bool orig, char* line);

	void LoginEvent(EventHandlerPtr f, const char* line, bool no_user_okay=false);
	const char* GetUsername(const char* line) const;
	void LineEvent(EventHandlerPtr f, const char* line);
	void Confused(const char* msg, const char* addl);
	void ConfusionText(const char* line);

	bool IsPloy(const char* line);
	bool IsSkipAuthentication(const char* line) const;
	const char* IsLoginPrompt(const char* line) const;	// nil if not
	bool IsDirectLoginPrompt(const char* line) const;
	bool IsFailureMsg(const char* line) const;
	bool IsSuccessMsg(const char* line) const;
	bool IsTimeout(const char* line) const;
	bool IsEmpty(const char* line) const;

	void AddUserText(const char* line);	// complains on overflow
	char* PeekUserText();	// internal warning on underflow
	char* PopUserText();		// internal warning on underflow
	Val* PopUserTextVal();

	bool MatchesTypeahead(const char* line) const;
	bool HaveTypeahead() const	{ return num_user_text > 0; }
	void FlushEmptyTypeahead();

// If we have more user text than this unprocessed, we complain about
// excessive typeahead.
#define MAX_USER_TEXT 12
	char* user_text[MAX_USER_TEXT];
	int user_text_first, user_text_last;	// indices into user_text
	int num_user_text;	// number of entries in user_text

	Val* username;	// last username reported
	Val* client_name;	// rlogin client name (or nil if none)

	login_state state;
	int lines_scanned;
	int num_user_lines_seen;
	int last_failure_num_user_lines;
	int login_prompt_line;
	int failure_line;

	bool is_VMS;
	bool saw_ploy;
};

} // namespace zeek::analyzer::login

namespace analyzer::login {

using login_state [[deprecated("Remove in v4.1. Use zeek::analyzer::login::login_state.")]] = zeek::analyzer::login::login_state;
constexpr auto LOGIN_STATE_AUTHENTICATE [[deprecated("Remove in v4.1. Use zeek::analyzer::login::LOGIN_STATE_AUTHENTICATE.")]] = zeek::analyzer::login::LOGIN_STATE_AUTHENTICATE;
constexpr auto LOGIN_STATE_LOGGED_IN [[deprecated("Remove in v4.1. Use zeek::analyzer::login::LOGIN_STATE_LOGGED_IN.")]] = zeek::analyzer::login::LOGIN_STATE_LOGGED_IN;
constexpr auto LOGIN_STATE_SKIP [[deprecated("Remove in v4.1. Use zeek::analyzer::login::LOGIN_STATE_SKIP.")]] = zeek::analyzer::login::LOGIN_STATE_SKIP;
constexpr auto LOGIN_STATE_CONFUSED [[deprecated("Remove in v4.1. Use zeek::analyzer::login::LOGIN_STATE_CONFUSED.")]] = zeek::analyzer::login::LOGIN_STATE_CONFUSED;

using Login_Analyzer [[deprecated("Remove in v4.1. Use zeek::analyzer::login::Login_Analyzer.")]] = zeek::analyzer::login::Login_Analyzer;

} // namespace analyzer::login
