// $Id: FTP.cc,v 1.36 2003/08/31 00:38:11 vern Exp $
//
// Copyright (c) 1996, 1997, 1998, 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.

#include "config.h"

#include <stdlib.h>

#include "NetVar.h"
#include "FTP.h"
#include "NVT.h"
#include "Event.h"
#include "TCP_Rewriter.h"

FTP_Conn::FTP_Conn(NetSessions* s, HashKey* k, double t, const ConnID* id,
		const struct tcphdr* tp)
: TCP_Connection(s, k, t, id, tp)
	{
	pending_reply = 0;
	}

void FTP_Conn::BuildEndpoints()
	{
	// Only LF/CRLF are to be considered as end-of-line.
	orig = new TCP_NVT(this, 1, 1, 0, LF_as_EOL);
	resp = new TCP_NVT(this, 0, 1, 0, LF_as_EOL);
	}

void FTP_Conn::Done()
	{
	if ( orig->AsNVT()->HasPartialLine() &&
	     (orig->state == TCP_CLOSED || orig->prev_state == TCP_CLOSED) )
		// ### should include the partial text
		Weird("partial_ftp_request");

	TCP_Connection::Done();
	}

void FTP_Conn::NewLine(TCP_Endpoint* s, int length, const char* line, int /* is_const */)
	{
	if ( (s == orig && ! ftp_request) || (s == resp && ! ftp_reply) )
		return;

	// const char* orig_line = line;
	const char* end_of_line = line + length;

	val_list* vl = new val_list;
	vl->append(BuildConnVal());

	EventHandlerPtr f;
	if ( s == orig )
		{
		int cmd_len;
		const char* cmd;
		StringVal* cmd_str;

		line = skip_whitespace(line, end_of_line);
		get_word(length, line, cmd_len, cmd);
		line = skip_whitespace(line + cmd_len, end_of_line);

		if ( cmd_len == 0 )
			{
			// Weird("FTP command missing", end_of_line - orig_line, orig_line);
			cmd_str = new StringVal("<missing>");
			}
		else
			cmd_str = (new StringVal(cmd_len, cmd))->ToUpper();

		vl->append(cmd_str);
		vl->append(new StringVal(end_of_line - line, line));

		f = ftp_request;

  		if ( rule_matcher )
			Match(Rule::FTP, (const u_char *) cmd,
				end_of_line - cmd, true, true, 1);
		}
	else
		{
		uint32 reply_code;
		if ( length >= 3 &&
		     isdigit(line[0]) && isdigit(line[1]) && isdigit(line[2]) )
			{
			reply_code = (line[0] - '0') * 100 +
					(line[1] - '0') * 10 +
					(line[2] - '0');
			}
		else
			reply_code = 0;

		int cont_resp;

		if ( pending_reply )
			{
			if ( reply_code == pending_reply &&
			     length > 3 && line[3] == ' ' )
				{
				// This is the end of the reply.
				line = skip_whitespace(line + 3, end_of_line);
				pending_reply = 0;
				cont_resp = 0;
				}
			else
				{
				cont_resp = 1;	// not the end
				reply_code = 0;	// flag as intermediary
				}
			}
		else
			{ // a new reply
			if ( reply_code > 0 && length > 3 && line[3] == '-' )
				{ // a continued reply
				pending_reply = reply_code;
				line = skip_whitespace(line + 4, end_of_line);
				cont_resp = 1;
				}
			else
				{ // a self-contained reply
				if ( reply_code > 0 )
					line += 3;
				if ( line < end_of_line )
					line = skip_whitespace(line, end_of_line);
				else
					line = end_of_line;

				cont_resp = 0;
				}
			}

		vl->append(new Val(reply_code, TYPE_COUNT));
		vl->append(new StringVal(end_of_line - line, line));
		vl->append(new Val(cont_resp, TYPE_BOOL));

		f = ftp_reply;
		}

	ConnectionEvent(f, vl);
	}

#include "ftp-rw.bif.func_def"
