// $Id: HTTP.h,v 1.27 2003/06/15 19:16:15 vern Exp $
//
// Copyright (c) 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.

#ifndef http_h
#define http_h

#include "TCP.h"
#include "MIME.h"

enum CHUNKED_TRANSFER_STATE {
	NON_CHUNKED_TRANSFER,
	BEFORE_CHUNK,
	EXPECT_CHUNK_SIZE,
	EXPECT_CHUNK_DATA,
	EXPECT_CHUNK_DATA_CRLF,
	EXPECT_CHUNK_TRAILER,
	EXPECT_NOTHING,
};

class HTTP_Entity;
class HTTP_Message;
class HTTP_Endpoint;
class HTTP_Conn;

class HTTP_Entity : public MIME_Entity {
public:
	HTTP_Entity(HTTP_Message *msg, MIME_Entity* parent_entity, int expect_body);

	void EndOfData();
	void Deliver(int len, const char* data, int trailing_CRLF);
	int Undelivered(int len);
	int BodyLength() const { return body_length; }
	void SkipBody() { deliver_body = 0; }

protected:
	HTTP_Message* http_message;
	int chunked_transfer_state;
	int content_length;
	int expect_data_length;
	int expect_body;
	int body_length;
	int deliver_body;

	MIME_Entity* NewChildEntity() { return new HTTP_Entity(http_message, this, 1); }

	void DeliverBody(int len, const char* data, int trailing_CRLF);

	void SubmitData(int len, const char* buf);

	void SetPlainDelivery(int length);

	void SubmitHeader(MIME_Header* h);
	void SubmitAllHeaders();
};

enum {
	HTTP_BODY_NOT_EXPECTED,
	HTTP_BODY_EXPECTED,
	HTTP_BODY_MAYBE,
};

// Finishing HTTP Messages:
//
// HTTP_Entity::SubmitAllHeaders	-> EndOfData (no body)
// HTTP_Entity::Deliver	-> EndOfData (end of body)
// HTTP_Conn::Done	-> {Request,Reply}Made (connection terminated)
// {Request,Reply}Made	-> HTTP_Message::Done
// HTTP_Message::Done	-> MIME_Message::Done, EndOfData, HTTP_MessageDone
// MIME_Entity::EndOfData	-> Message::EndEntity
// HTTP_Message::EndEntity	-> Message::Done
// HTTP_MessageDone	-> {Request,Reply}Made

class HTTP_Message : public MIME_Message {
public:
	HTTP_Message(HTTP_Conn* conn, HTTP_Endpoint* endp, int expect_body);
	~HTTP_Message();
	void Done(const int interrupted, const char* msg);
	void Done() { Done(0, "message ends normally"); }

	int Undelivered(int len);

	void BeginEntity(MIME_Entity* /* entity */);
	void EndEntity(MIME_Entity* entity);
	void SubmitHeader(MIME_Header* h);
	void SubmitAllHeaders(MIME_HeaderList& /* hlist */);
	void SubmitData(int len, const char* buf);
	int RequestBuffer(int* plen, char** pbuf);
	void SubmitAllData();
	void SubmitEvent(int event_type, const char* detail);

	void SubmitTrailingHeaders(MIME_HeaderList& /* hlist */);
	void SetPlainDelivery(int length);
	void SkipEntityData();

	void Weird(const char* msg);

protected:
	HTTP_Conn* conn;
	HTTP_Endpoint* endp;
	int is_orig;

	vector<BroString*> buffers;

	// Controls the total buffer size within http_entity_data_delivery_size.
	int total_buffer_size;

	int buffer_offset, buffer_size;
	BroString* data_buffer;

	int body_length;	// total length of entity bodies

	// Total length of content gaps that are "successfully" skipped.
	// Note: this might NOT include all content gaps!
	int content_gap_length;

	HTTP_Entity* current_entity;

	int InitBuffer(int length);
	void DeliverEntityData();

	Val* BuildMessageStat(const int interrupted, const char* msg);
};

class HTTP_Endpoint : public TCP_EndpointLine {
public:
	HTTP_Endpoint(HTTP_Conn* conn, int is_orig);

	// Ignore CR/LF if length != 0
	// If length = n > 0, keep plain delivery for next n bytes
	// If length < 0, keep plain delivery till SwitchToPlainDelivery(0)
	void SetPlainDelivery(int length);
	int GetPlainDeliveryLength() const { return plain_delivery_length; }

protected:
	int DoDeliverOnce(int len, u_char* data);
	int plain_delivery_length;
};

class HTTP_Conn : public TCP_Connection {
public:
	HTTP_Conn(NetSessions* s, HashKey* k, double t, const ConnID* id,
			const struct tcphdr* tp);
	~HTTP_Conn();
	void Done();
	void EndpointEOF(TCP_EndpointContents* endp);

	int RewritingTrace()	{ return rewriting_http_trace; }

	void NewLine(TCP_Endpoint* sender, int length, const char* line, int is_const);
	void DeliverPlainSegment(TCP_Endpoint* sender, int length, const char* data);
	void Undelivered(TCP_Endpoint* sender, int seq, int len);

	void HTTP_Header(TCP_Endpoint* sender, MIME_Header* h);
	void HTTP_EntityData(TCP_Endpoint* sender, BroString* entity_data);
	void HTTP_MessageDone(TCP_Endpoint* sender, HTTP_Message* message);
	void HTTP_Event(const char* category, const char* detail);
	void HTTP_Event(const char* category, StringVal *detail);

	void SkipEntityData(int is_orig);

protected:
	void BuildEndpoints();

	void ConnectionFinished(int half_finished);
	void ConnectionReset();
	void PacketWithRST();

	void GenStats();

	int HTTP_RequestLine(const char* line, const char* end_of_line);
	int HTTP_ReplyLine(const char* line, const char* end_of_line);

	void InitHTTPMessage(HTTP_Endpoint* endp, HTTP_Message*& message, int expect_body);

	const char* PrefixMatch(const char* line, const char* end_of_line,
				const char* prefix);
	const char* PrefixWordMatch(const char* line, const char* end_of_line,
				const char* prefix);

	int ParseRequest(const char *line, const char *end_of_line);
	double HTTP_Version(int len, const char* data);

	void SetVersion(double& version, double new_version);

	int RequestExpected() const { return num_requests == 0 || keep_alive; }

	void HTTP_Request();
	void HTTP_Reply();

	void RequestMade(const int interrupted, const char* msg);
	void ReplyMade(const int interrupted, const char* msg);
	void RequestClash(Val* clash_val);

	BroString* UnansweredRequestMethod();

	void ParseServerVersion(data_chunk_t ver);
	int HTTP_ReplyCode(const char* code_str);
	int ExpectReplyMessageBody();

	int request_state, reply_state;
	int num_requests, num_replies;
	int num_request_lines, num_reply_lines;
	double request_version, reply_version;
	int keep_alive;
	int request_ongoing, reply_ongoing;

	Val* request_method;

	// request_URI is in the original form (may contain '%<hex><hex>'
	// sequences).
	Val* request_URI;

	// unescaped_URI does not contain escaped sequences.
	Val* unescaped_URI;

	std::queue<Val*> unanswered_requests;

	int reply_code;
	Val* reply_reason_phrase;

	HTTP_Message* request_message;
	HTTP_Message* reply_message;
};

extern int is_reserved_URI_char(unsigned char ch);
extern int is_unreserved_URI_char(unsigned char ch);
extern void escape_URI_char(unsigned char ch, unsigned char*& p);
extern BroString* unescape_URI(const u_char* line, const u_char* line_end,
				Connection* conn);

#endif
