/*
 * http.h - dnssec-trigger HTTP client code to GET a simple URL
 *
 * Copyright (c) 2012, NLnet Labs. All rights reserved.
 *
 * This software is open source.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * 
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * 
 * Neither the name of the NLNET LABS 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * \file
 *
 * This file contains an implementation of HTTP fetch for a simple URL file.
 */

#ifndef HTTP_H
#define HTTP_H
struct comm_base;
struct comm_point;
#include <ldns/buffer.h>
#include <ldns/packet.h>
struct svr;
struct http_probe;
struct probe_ip;
struct comm_reply;

/**
 * overall HTTP probe structure
 */
struct http_general {
	struct svr* svr;

	/* array of urls to attempt to transfer from */
	char** urls;
	/* array of checkcodes to check - the content of those urls */
	char** codes;
	/* number of urls in array */
	size_t url_num;

	/* the ipv4 http probe */
	struct http_probe* v4;
	/* the ipv6 http probe */
	struct http_probe* v6;

	/* http works */
	int saw_http_work;
};

/**
 * HTTP probe.  The url split up and results.
 */
struct http_probe {
	/* the current url (const reference) */
	char* url;
	/* hostname */
	char* hostname;
	/* filename (not prefixed with a / ) */
	char* filename;
	/* url index in urls array */
	size_t url_idx;

	/* are we fetching the address record or doing http_get? */
	int do_addr;
	/* is this ipv6? */
	int ip6;
	
	/* num addr queries */
	int num_addr_qs;
	/* num addr queries that failed */
	int num_failed_addr_qs;
	/* list of addresses (RR records) */
	ldns_rr_list* addr;
	/* port number */
	int port;

	/* number of redirects we followed */
	int redirects;
	/* result: did we get addresses? */
	int got_addrs;
	/* result: does it connect? */
	int connects;
	/* result: does it work (correct page) */
	int works;
	/* is the probe finished? */
	int finished;
};

/** the number of urls to try to probe; in case one fails. */
#define HTTP_NUM_URLS_MAX_PROBE 3

/** max number of address queries for one name (all to different caches,
 * once for A then for AAAA, so double that number in sockets is needed). */
#define HTTP_MAX_ADDR_QUERIES 5

/** max number of redirects in sequence */
#define HTTP_MAX_REDIRECT 8

/**
 * create and randomise http general structure
 * @param svr: with config and create and register probes here.
 * @return: new http lookup administration
 */
struct http_general* http_general_start(struct svr* svr);

/**
 * delete the http_general structure.
 * @param hg: http lookup administration
 */
void http_general_delete(struct http_general* hg);

/**
 * The http lookup is completely done, either success (NULL) or fail reason
 */
void http_general_done(const char* reason);

/** a host addr lookup is done (with an error or NULL) */
void http_host_outq_done(struct probe_ip* p, const char* reason);
/** a host addr lookup is done, here is the packet (QR, rcode NOERROR).
 * The pkt is freeed by this routine. */
void http_host_outq_result(struct probe_ip* p, ldns_pkt* pkt);

/**
 * Structure that represents an open TCP activity for a HTTP (no -s) GET.
 */
struct http_get {
	/* The url of the target */
	char* url;
	/* hostname : name of host part of the URL */
	char* hostname;
	/* filename : name of file part of URL (*not* prefixed with a / ) */
	char* filename;

	/* state of the HTTP transaction */
	enum http_get_state {
		/* we are not sending or reading any things */
		http_state_none,
		/* we are sending the request (initial headers) */
		http_state_request,
		/* we are reading the reply headers */
		http_state_reply_header,
		/* we are reading the reply (HTTP/1.0) */
		http_state_reply_data,
		/* we are reading chunked reply headers (HTTP/1.1) */
		http_state_chunk_header,
		/* we are reading chunked reply data (HTTP/1.1) */
		http_state_chunk_data,
	} state;
	/* data length (of replydata or chunkdata) */
	size_t datalen;

	/* max data we want (0 is no max) */
	size_t data_limit;
	/* this is a redirect response */
	int redirect_now;

	/* the buffer with contents sent/received */
	ldns_buffer* buf;
	/* the buffer with the result data */
	ldns_buffer* data;

	/* my comm_base */
	struct comm_base* base;
	/* my comm_point (a comm_raw) */
	struct comm_point* cp;
	/* the timer */
	struct comm_timer* timer;

	/* destination IP as a string */
	char* dest;
	/* port number */
	int port;
	/* the probe that this is part of */
	struct probe_ip* probe;
};

/* define max length that the buffer is created for */
#define MAX_HTTP_LENGTH 16384
/* the timeout for the HTTP part of the httpprobe operation, in msec */
#define HTTP_TIMEOUT 3000
/* HTTP port */
#define HTTP_PORT 80

/**
 * Create a new HTTP GET for the given url (http://example.com/bla.txt) 
 * It does a plain (noSSL) http GET.
 * @param url: The url to fetch.
 * @param base: comm_base to register connect-ed TCP socket's comm_point.
 * @param probe: probe this is part of.
 * @return new get or NULL on failure (malloc failure).
 */
struct http_get* http_get_create(const char* url, struct comm_base* base,
	struct probe_ip* probe);

/**
 * Delete a HTTP GET fetch.
 * @param hg: http_get structure to delete.
 */
void http_get_delete(struct http_get* hg);

/**
 * Perform the fetch that was initialised.
 * Parses, connects, and so on.
 * @param hg: http_get structure.
 * @param dest: destination IP address.
 * @param port: port number (HTTP_PORT is the default 80).
 * @param err: the detailed error on failure (set to constant string).
 * @return false if failed.
 */
int http_get_fetch(struct http_get* hg, const char* dest, int port, char** err);

/** handle socket events on http_get */
int http_get_callback(struct comm_point* cp, void* arg, int err,
	struct comm_reply* reply);
/** handle timeout for the http_get operation */
void http_get_timeout_handler(void* arg);

/** pick random RR from rr list, removes it from the list. list not empty*/
ldns_rr* http_pick_random_addr(ldns_rr_list* list);

#endif /* HTTP_H */
