#ifndef HTTP_H /**************************************************************************************************/
#define HTTP_H

#include <sys/types.h>
#include <sys/stat.h>
#include <stdarg.h>

#include <mbedtls/x509.h>
#include <mbedtls/ssl.h>

/*****************************************************************************************************************/

struct http_server;

struct http_kvitem {
  const char *key;
  const char *filename;
  const char *content_type;
  const char *content;
  unsigned int content_length;
};

struct http_kvlist {
  unsigned int count;
  struct http_kvitem *items;
};

/*****************************************************************************************************************/

#define HTTP_RFC1123FMT "%a, %d %b %Y %H:%M:%S GMT"

#define HTTP_MAXCLIENTS		16

struct http_server;
struct http_request;
struct pttq;

#define HTTP_RESPONSE_STARTED	1	// Set as soon as the first byte goes back to the client
#define HTTP_HEADER_SENT	2	// Set when the HTTP header has been sent
#define HTTP_RESPONSE_FINISHED	4	// Set when the response has been completely sent

enum HttpRequestMethod {
  httpGet,
  httpPost,
  httpPut,
  httpPatch,
  httpDelete,
};

enum HttpScheme {
  httpHTTP,
  httpHTTPS,
};

struct http_pathinfo {
  const char *urn;			// Path of document exactly as requested by client

  const char *document_file;		// File name of document, without directory

  const char *document_path;		// Complete path of document in filesystem, including filename
  const char *document_dir;		// Directory of document in filesystem, without filename

  const char *document_vpath;		// Path of documnet relative to server root, including filename
  const char *document_vdir;		// Directory of document relative to server root, without filename
};

struct http_operations {
  // Callback type for handling a request
  void (*handler)(struct http_request *req);

  // Callback type for finding a user's password hash
  const char *(*find_user)(struct http_request *req, const char *realm, const char *user,
			   char *buffer, size_t bufsize);
};

struct http_userdata {
  void *data;				// Opaque data registered with callback
  int ref;				// Complimentary variable for use by application
  int state;				// Complimentary variable for use by application
};

struct http_request {
  struct http_server *server;		// Server data structure

  struct http_userdata userdata;	// Data for use by callback

  int depth;				// Nesting level, when calling another file from within a script

  unsigned int flags;			// Flags about request

  const char *peer_address;		// Address of peer
  unsigned int peer_port;		// Port number

  enum HttpScheme scheme;               // HTTP or HTTPS

  enum HttpRequestMethod method;	// GET or POST

  struct http_pathinfo *document;	// Document path, filename, etc...

  struct http_kvlist *header;		// Contents of header
  struct http_kvlist *query;		// GET parameter list
  struct http_kvlist *post;		// POST parameter list
  struct http_kvlist *cookies;		// Cookie parameter list

  time_t if_modified_since;		// Value of If-Modified-Since header, or 0 if none was sent

  const char *client_authorization;	// Value of "Authorization" header

  const char *client_content_type;	// Content type reported by client
  size_t client_content_length;		// Content length as reported by client
  char *client_content;			// Buffered content
};


// Construction
struct http_server *http_init(struct pttq *pttq);
void http_release(struct http_server *hs);

// Configuration
void http_set_path(struct http_server *hs, const char *path);
void http_set_hostname(struct http_server *hs, const char *hostname);
void http_set_handler(struct http_server *hs, const struct http_operations *hh, void *data);
void http_set_maxmem(struct http_server *hs, size_t maxmem);
void http_set_trace(struct http_server *hs, int flag);

// SSL support
mbedtls_x509_crt *http_ca_chain(struct http_server *hs);
mbedtls_x509_crt *http_cert_store(struct http_server *hs);
mbedtls_pk_context *http_server_key(struct http_server *hs);
int http_set_ciphersuites(struct http_server *hs, const int *list);
const int *http_get_ciphersuites(struct http_server *hs);
mbedtls_ssl_context *http_client_ssl_context(struct http_request *req);

// Network
int http_listen_at(struct http_server *hs, const char *bind_addr, int port, int secure);
int http_listen(struct http_server *hs, int port, int secure);
int http_unlisten(struct http_server *hs, int secure);

// Function that send a full response from start to finish
void http_serve(struct http_request *req); // Default request processing for static files
void http_serve_html(struct http_request *req, const char *path);
void http_send_error(struct http_request *req, int status, const char *title,
		     const char *extra_header, const char *text);
void http_send_errno_error(struct http_request *req, int err);

// Do an authentication check
// Returns true to proceed and false if a 401 header has been sent.
int http_authenticate(struct http_request *req, const char *realm);

// Send a directory index
#define HTTP_INDEX_HEADER		1	// Include a table header
#define HTTP_INDEX_CLICKABLE_HEADER	2	// Include a clickable table header
void http_send_index(struct http_request *req, const char *urn, unsigned int flags);

// Serve a http document with support for inline scripting
struct http_script;
typedef void http_script_handler_t(struct http_script *scp);

struct http_script {
  struct http_request *req;
  void *data;
  http_script_handler_t *handler;
  const char *language;
  const char *script;
  unsigned int len;
};

void http_serve_script(struct http_script *scp, const char *filename);

// Run another file from within a file
void http_run_file(struct http_request *req, const char *urn);
void http_run_file3(struct http_request *req, const char *urn, size_t urnlen);

// Send data to host without any fancy header/body handling.
ssize_t http_writen(int fd, const void *ptr, size_t bytes);
ssize_t http_raw_write(struct http_request *req, const void *ptr, size_t bytes);
void http_raw_puts(const char *str, struct http_request *req);
void http_raw_vprintf(struct http_request *req, const char *fmt, va_list va);
void http_raw_printf(struct http_request *req, const char *fmt, ...);

// Begin sending header. The header is kept open so more lines can be added.
void http_start_header(struct http_request *req, unsigned int status, const char *title);

// Send a Content-Type: header. Implies a http_start_header() with defaults if not yet called.
void http_send_contentheader(struct http_request *req, const char *type, const char *subtype);

// Add more header lines. Implies a http_start_header() with defaults if not yet called.
void http_send_header(struct http_request *req, const char *header, const char *value);

// The header has been sent.
void http_done_header(struct http_request *hdr);

// Determine state of response
int http_response_started(struct http_request *hdr);
int http_response_finished(struct http_request *hdr);

// Send data to client. Will make sure the header is closed.
ssize_t http_write(struct http_request *req, const void *ptr, size_t bytes);
void http_puts(const char *str, struct http_request *req);
void http_printf(struct http_request *req, const char *fmt, ...);

void http_send_file_content(struct http_request *req, const char *file);

// The full response has been sent.
void http_done(struct http_request *req);

// Percent encode. If plusmode is true, all spaces are encoded as '+'
size_t http_urlencode(char *dst, const char *src, size_t dst_size, int plusmode);

// Percent decode. Result will never be longer than source string.
size_t http_urldecode(char *dst, const char *src, int plusmode);

// Determine mime-type from filename extension.
const char *http_get_mime_type(const char *name);

/*****************************************************************************************************************/
#endif
