/* Copyright (C) 2007-2008 The Android Open Source Project
**
** This software is licensed under the terms of the GNU General Public
** License version 2, as published by the Free Software Foundation, and
** may be copied, distributed, and modified under those terms.
**
** This program 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 General Public License for more details.
*/
#ifndef _PROXY_INT_H
#define _PROXY_INT_H

#include "proxy_common.h"
#include "sockets.h"
#include "android_utils.h"

extern int  proxy_log;

extern void
proxy_LOG(const char*  fmt, ...);

#define  PROXY_LOG(...)   \
    do { if (proxy_log) proxy_LOG(__VA_ARGS__); } while (0)


/* ProxySelect is used to handle events */

enum {
    PROXY_SELECT_READ  = (1 << 0),
    PROXY_SELECT_WRITE = (1 << 1),
    PROXY_SELECT_ERROR = (1 << 2)
};

typedef struct {
    int*     pcount;
    fd_set*  reads;
    fd_set*  writes;
    fd_set*  errors;
} ProxySelect;

extern void     proxy_select_set( ProxySelect*  sel,
                                  int           fd,
                                  unsigned      flags );

extern unsigned  proxy_select_poll( ProxySelect*  sel, int  fd );


/* sockets proxy manager internals */

typedef struct ProxyConnection   ProxyConnection;
typedef struct ProxyService      ProxyService;

/* free a given proxified connection */
typedef void              (*ProxyConnectionFreeFunc)   ( ProxyConnection*  conn );

/* modify the ProxySelect to tell which events to listen to */
typedef void              (*ProxyConnectionSelectFunc) ( ProxyConnection*  conn,
                                                         ProxySelect*      sel );

/* action a proxy connection when select() returns certain events for its socket */
typedef void              (*ProxyConnectionPollFunc)   ( ProxyConnection*  conn,
                                                         ProxySelect*      sel );


/* root ProxyConnection object */
struct ProxyConnection {
    int                 socket;
    struct sockaddr_in  address;  /* for debugging */
    ProxyConnection*    next;
    ProxyConnection*    prev;
    ProxyEventFunc      ev_func;
    void*               ev_opaque;
    ProxyService*       service;

    /* the following is useful for all types of services */
    char                name[64];    /* for debugging purposes */

    stralloc_t          str[1];      /* network buffer (dynamic) */
    int                 str_pos;     /* see proxy_connection_send() */
    int                 str_sent;    /* see proxy_connection_send() */
    int                 str_recv;    /* see proxy_connection_receive() */

    /* connection methods */
    ProxyConnectionFreeFunc    conn_free;
    ProxyConnectionSelectFunc  conn_select;
    ProxyConnectionPollFunc    conn_poll;

    /* rest of data depend on exact implementation */
};



extern void
proxy_connection_init( ProxyConnection*           conn,
                       int                        socket,
                       struct sockaddr_in*        address,
                       ProxyService*              service,
                       ProxyConnectionFreeFunc    conn_free,
                       ProxyConnectionSelectFunc  conn_select,
                       ProxyConnectionPollFunc    conn_poll );

extern void
proxy_connection_done( ProxyConnection*  conn );

/* free the proxy connection object. this will also
 * close the corresponding socket unless the 
 * 'keep_alive' flag is set to TRUE.
 */
extern void
proxy_connection_free( ProxyConnection*  conn,
                       int               keep_alive,
                       ProxyEvent        event );

/* status of data transfer operations */
typedef enum {
    DATA_ERROR     = -1,
    DATA_NEED_MORE =  0,
    DATA_COMPLETED =  1
} DataStatus;

/* try to send data from the connection's buffer to a socket.
 * starting from offset conn->str_pos in the buffer
 *
 * returns DATA_COMPLETED if everything could be written
 * returns DATA_ERROR for a socket disconnection or error
 * returns DATA_NEED_MORE if all data could not be sent.
 *
 * on exit, conn->str_sent contains the number of bytes
 * that were really sent. conn->str_pos will be incremented
 * by conn->str_sent as well.
 *
 * note that in case of success (DATA_COMPLETED), this also
 * performs a proxy_connection_rewind which sets conn->str_pos
 * to 0.
 */
extern DataStatus
proxy_connection_send( ProxyConnection*  conn, int  fd );

/* try to read 'wanted' bytes into conn->str from a socket
 *
 * returns DATA_COMPLETED if all bytes could be read
 * returns DATA_NEED_MORE if not all bytes could be read
 * returns DATA_ERROR in case of socket disconnection or error
 *
 * on exit, the amount of data received is in conn->str_recv
 */
extern DataStatus
proxy_connection_receive( ProxyConnection*  conn, int  fd, int  wanted );

/* tries to receive a line of text from the proxy.
 * when an entire line is read, the trailing \r\n is stripped
 * and replaced by a terminating zero. str->n will be the
 * lenght of the line, exclusing the terminating zero.
 * returns 1 when a line has been received
 * returns 0 if there is still some data to receive
 * returns -1 in case of error
 */
extern DataStatus
proxy_connection_receive_line( ProxyConnection*  conn, int  fd );

/* rewind the string buffer for a new operation */
extern void
proxy_connection_rewind( ProxyConnection*  conn );

/* base64 encode a source string, returns size of encoded result,
 * or -1 if there was not enough room in the destination buffer
 */
extern int
proxy_base64_encode( const char*  src, int  srclen,
                     char*        dst, int  dstlen );

extern int
proxy_resolve_server( struct sockaddr_in*  addr,
                      const char*          servername,
                      int                  servernamelen,
                      int                  serverport );

/* a ProxyService is really a proxy server and associated options */

/* destroy a given proxy service */
typedef void              (*ProxyServiceFreeFunc)      ( void*  opaque );

/* tries to create a new proxified connection, returns NULL if the service can't
 * handle this address */
typedef ProxyConnection*  (*ProxyServiceConnectFunc)( void*                opaque,
                                                      int                  socket_type,
                                                      struct sockaddr_in*  address );

struct ProxyService {
    void*                      opaque;
    ProxyServiceFreeFunc       serv_free;
    ProxyServiceConnectFunc    serv_connect;
};

extern int
proxy_manager_add_service( ProxyService*  service );


#endif /* _PROXY_INT_H */
