/* 
   HTTP authentication routines
   Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>

   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

*/

#ifndef HTTPAUTH_H
#define HTTPAUTH_H

#include <sys/types.h>

#include "neon_md5.h"

/* HTTP Authentication - a pretty complete client implementation of RFC2617.
 */

/*
  To use:
 
 1. A session state variable (http_auth_session) is needed for each of
 server state and proxy state. These may be statically declared (use
 _init/_finish), or dynamically (use _create/_destroy).
 
 2. To begin a new session, call http_auth_init() or http_auth_create().
 Set up a callback function with http_auth_set_creds_cb() for
 supplying the username and password on demand. See below for details.

 3. Before sending a request, pass http_auth_new_request its details,
 on BOTH auth session variables if you are using a proxy server too.
 
 4. Call http_auth_request_header() and add your 'Authentication:'
 header to the request if returns non-NULL. Similarly for
 Proxy-Authentication.
 
 5. Send the request.

 6. Read the response:
  - Pass the value of the '(Proxy|WWW)-Authenticate' header to 
    http_auth_challenge.
  - If there is 'Authentication-Info', save its value for later.
  - Pass each block of the response entity-body to http_auth_response_body.

 7. After reading the complete response, if an Auth-Info header was
 received, pass its value to http_auth_verify_response to check
 whether the SERVER was authenticated okay, passing the saved value.

 8. If a 401 or a 407 response is received, retry once for each, by
 going back to step 3. Note that http_auth_new_request MUST be called
 again if the SAME request is being retried.

*/

/* The authentication scheme we are using */
typedef enum {
    http_auth_scheme_basic,
    http_auth_scheme_digest
} http_auth_scheme;

typedef enum { 
    http_auth_alg_md5,
    http_auth_alg_md5_sess,
    http_auth_alg_unknown
} http_auth_algorithm;

/* Selected method of qop which the client is using */
typedef enum {
    http_auth_qop_none,
    http_auth_qop_auth,
    http_auth_qop_auth_int
} http_auth_qop;

/* The callback used to request the username and password in the given
 * realm. The username and password must be placed in malloc()-allocate
 * memory.
 * Must return:
 *   0 on success, 
 *  -1 to cancel.
 */
typedef int (*http_auth_request_creds)(
    void *userdata, const char *realm,
    char **username, char **password);

/* Authentication session state. */
typedef struct {
    /* The scheme used for this authentication session */
    http_auth_scheme scheme;
    /* The callback used to request new username+password */
    http_auth_request_creds reqcreds;
    void *reqcreds_udata;

    /*** Session details ***/

    /* The username and password we are using to authenticate with */
    char *username;
    /* Whether we CAN supply authentication at the moment */
    unsigned int can_handle:1;
    /* This used for Basic auth */
    char *basic; 
    /* These all used for Digest auth */
    char *unq_realm;
    char *unq_nonce;
    char *unq_cnonce;
    char *opaque;
    /* A list of domain strings */
    unsigned int domain_count;
    char **domain;
    http_auth_qop qop;
    http_auth_algorithm alg;
    int nonce_count;
    /* The ASCII representation of the session's H(A1) value */
    char h_a1[33];
    /* Used for calculation of H(entity-body) of the response */
    struct md5_ctx response_body;
    /* Temporary store for half of the Request-Digest
     * (an optimisation - used in the response-digest calculation) */
    struct md5_ctx stored_rdig;

    /* Details of server... needed to reconstruct absoluteURI's when
     * necessary */
    const char *host;
    const char *uri_scheme;
    unsigned int port;

    /*** Details of current request ***/

    /* The method and URI we are using for the current request */
    const char *uri;
    const char *method;
    /* Whether we WILL supply authentication for this request or not */
    unsigned int will_handle:1;
    /* Whether we have a request body for the current request */
    unsigned int got_body:1;
    /* And what the body is - stream or buffer */
    FILE *body_stream;
    const char *body_buffer;

} http_auth_session;

/* Initializes the authentication state for the given session,
 * which will use the given username and password. */
void http_auth_init(http_auth_session *sess);

void http_auth_set_creds_cb(http_auth_session *sess,
			    http_auth_request_creds callback, void *userdata);

/* Finishes off the given authentication session, freeing
 * any memory used. */
void http_auth_finish(http_auth_session *sess);

/* Creates a new authentication session.
 * Returns non-NULL on success */
http_auth_session * http_auth_create(void);

/* Destroys an authentication session, freeing the session state
 * itself too. */
void http_auth_destroy(http_auth_session *sess); 

/* Call this before sending a request.  Pass ONE OF body_buffer or
 * body_stream as non-NULL if the request will include an
 * entity-body. If body_buffer is non-NULL, it MUST be
 * \0-terminated. If body_stream is non-NULL, it may be read once
 * during http_auth_challenge, then rewound.  uri must identical to
 * Request-URI, EXCEPT for server auth state, where if the request is
 * passing through a proxy, then uri should be the same as abs_path.  */
void http_auth_new_request(http_auth_session *sess,
			   const char *method, const char *uri,
			   const char *body_buffer, FILE *body_stream);

/* Returns the value of the authentication field if one is to be sent,
 * else NULL. The return value will be taken from malloc()'ed memory,
 * so should be free()'ed after use. */
char *http_auth_request_header(http_auth_session *sess);

/* Pass this the value of the "(Proxy,WWW)-Authenticate: " header field.
 * Returns:
 *   0 if we can now authenticate ourselves with the server.
 *   non-zero if we can't
 */
int http_auth_challenge(http_auth_session *sess, const char *value);

/* As you receive sections of the response entity-body, pass them to 
 * this function. */
void http_auth_response_body(http_auth_session *sess, 
			     const char *buffer, size_t buffer_len);

/* If you receive a "(Proxy-)Authentication-Info:" header, pass its value to
 * this function. Returns zero if this successfully authenticates
 * the response as coming from the server, and false if it hasn't. */
int http_auth_verify_response(http_auth_session *sess, const char *value);

#endif /* HTTPAUTH_H */
