/* 
   HTTP Request Handling
   Copyright (C) 1999-2002, Joe Orton <joe@manyfish.co.uk>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.
   
   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
   MA 02111-1307, USA

*/

/* THIS IS NOT A PUBLIC INTERFACE. You CANNOT include this header file
 * from an application.  */
 
#ifndef NE_PRIVATE_H
#define NE_PRIVATE_H

#ifdef NEON_SSL
#include <openssl/ssl.h>
#endif

#include "ne_request.h"

struct host_info {
    char *hostname;
    int port;
    int resolved; /* non-zero if we've done a DNS lookup on this
                     address. */
    struct in_addr addr;
    char *hostport; /* URI hostport segment */
};

/* This is called with each of the headers in the response */
struct header_handler {
    char *name;
    ne_header_handler handler;
    void *userdata;
    struct header_handler *next;
};

/* TODO: could unify these all into a generic callback list */

struct body_reader {
    ne_block_reader handler;
    ne_accept_response accept_response;
    unsigned int use:1;
    void *userdata;
    struct body_reader *next;
};

/* Store every registered callback in a generic container, and cast
 * the function pointer when calling it.  */
struct hook {
    void (*fn)(void);
    void *userdata;
    const char *id; /* non-NULL for accessors. */
    struct hook *next;
};

#define HAVE_HOOK(st,func) (st->hook->hooks->func != NULL)
#define HOOK_FUNC(st, func) (*st->hook->hooks->func)

#define VERSION_PRE11(s) ((s)->version_major<1 || ((s)->version_major==1 && (s)->version_minor<1))

/* Session support. */
struct ne_session_s {
    /* Connection information */
    nsocket *socket;
    
    /* Number of requests made on this connection. */
    int reqcount;

    char *scheme;
    struct host_info server, proxy;

    /* Connection states:
     *   0:  Not connected at all.
     *   1:  We have a TCP connection to the next-hop server.
     *   2:  We have a negotiated an SSL connection over the proxy's 
     *       TCP tunnel.
     *
     * Note, 1 is all we need if we don't have a proxy server, or
     * if we do have a proxy server and we're not using SSL.
     */
    unsigned int connected:2;

    /* Settings */
    unsigned int use_proxy:1; /* do we have a proxy server? */
    unsigned int no_persist:1; /* set to disable persistent connections */
    unsigned int use_ssl:1; /* whether a secure connection is required */
    unsigned int in_connect:1; /* doing a proxy CONNECT */

    int expect100_works; /* known state of 100-continue support */

    sock_progress progress_cb;
    void *progress_ud;

    ne_notify_status notify_cb;
    void *notify_ud;

    int rdtimeout; /* read timeout. */

    struct hook *create_req_hooks, *pre_send_hooks, *post_send_hooks,
	*destroy_req_hooks, *destroy_sess_hooks, *accessor_hooks;

    char *user_agent; /* full User-Agent string */

    /* The last HTTP-Version returned by the server */
    int version_major;
    int version_minor;

#ifdef NEON_SSL
    SSL_CTX *ssl_context;
    X509 *server_cert;
    SSL_SESSION *ssl_sess;
    /* client cert */
    EVP_PKEY *client_key;
    X509 *client_cert;
#endif

    /* Server cert verification callback: */
    ne_ssl_verify_fn ssl_verify_fn;
    void *ssl_verify_ud;
    /* Client cert provider callback: */
    ne_ssl_provide_fn ssl_provide_fn;
    void *ssl_provide_ud;
    /* Client cert key password private key provider callback: */
    ne_ssl_keypw_fn ssl_keypw_fn;
    void *ssl_keypw_ud;

    /* Error string */
    char error[BUFSIZ];
};

struct ne_request_s {
    const char *method;
    char *uri, *abs_path;
    
    /*** Request ***/

    ne_buffer *headers;

    /* Request body. */
    ne_provide_body body_cb;
    void *body_ud;

    /* Comes from either an fd or a buffer. */
    union {
	int fd;
	struct {
	    const char *buffer, *pnt;
	    size_t left;
	} buf;
    } body;
	    
    size_t body_size, body_progress;

    /* temporary store for request. */
    ne_buffer *reqbuf;

    /* temporary store for response lines. */
    ne_buffer *respbuf;

    /**** Response ***/

    /* The transfer encoding types */
    struct ne_response {
	int length;            /* Response entity-body content-length */
	size_t left;              /* Bytes left to read */
	size_t chunk_left;        /* Bytes of chunk left to read */
	size_t total;             /* total bytes read so far. */
	unsigned int is_chunked; /* Are we using chunked TE? */
    } resp;

    /* List of callbacks which are passed response headers */
    struct header_handler *header_catchers;
    
    struct hook *accessor_hooks;

    /* We store response header handlers in a hash table.  The hash is
     * derived from the header name in lower case. */

    /* FIXME: this comment is WAY out of date, and no longer in any
     * way true. */
	    
    /* 53 is magic, of course.  For a standard ne_get() (with
     * redirects), 9 header handlers are defined.  Two of these are
     * for Content-Length (which is a bug, and should be fixed
     * really).  Ignoring that hash clash, the 8 *different* handlers
     * all hash uniquely into the hash table of size 53.  */
#define HH_HASHSIZE 53
    
    struct header_handler *header_handlers[HH_HASHSIZE];
    /* List of callbacks which are passed response body blocks */
    struct body_reader *body_readers;

    /*** Miscellaneous ***/
    unsigned int method_is_head:1;
    unsigned int use_expect100:1;
    unsigned int can_persist:1;
    unsigned int forced_close:1;

    ne_session *session;
    ne_status status;
};

/* Set a new URI for the given request. */
void ne_set_request_uri(ne_request *req, const char *uri);

typedef int (*ne_push_fn)(void *userdata, const char *buf, size_t count);

/* Pulls the request body for the given request, passing blocks to the
 * given callback.
 */
int ne_pull_request_body(ne_request *req, ne_push_fn fn, void *ud);

/* Do the SSL negotiation. */
int ne_negotiate_ssl(ne_request *req);

#endif /* HTTP_PRIVATE_H */
