/*
 * socket.h
 */

#ifndef __SOCKET_H
#   define __SOCKET_H

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

#ifdef HAVE_POLL_H
#   include <poll.h>
#elif HAVE_SYS_POLL_H
#   include <sys/poll.h>
#else

    #ifndef USE_SELECT
        #define USE_SELECT
    #endif

    #ifdef HAVE_SYS_SELECT_H
        #include <sys/select.h>
    #endif

    struct pollfd
    {
        int fd;                     /* File descriptor to poll.  */
        short int events;           /* Types of events poller cares about.  */
        short int revents;          /* Types of events that actually occurred.  */
    };

    #define POLLIN          0x001           /* There is data to read.  */
    #define POLLPRI         0x002           /* There is urgent data to read.  */
    #define POLLOUT         0x004           /* Writing now will not block.  */
    #define POLLERR         0x008           /* Error condition.  */
    #define POLLHUP         0x010           /* Hung up.  */
    #define POLLNVAL        0x020           /* Invalid polling request.  */
#endif

#ifdef _USE_SSL
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#endif


#include "dynbuff.h"

class pollsocket
{
protected:
    int fd;

    static int num_socks;
    static pollsocket **table;
    static int MAX_SOCKETS;

    dynbuff * ibuff;
    dynbuff * obuff;

#ifdef _USE_SSL
	static SSL_CTX *ssl_ctx;
	static char *tls_rand_file;
	static int seed_PRNG(void);
	SSL *ssl;
#endif

#ifdef USE_SELECT
    static fd_set fds_read, fds_write;
    static fd_set res_read, res_write;
    static int highest_fd;
#else
    static int num_pfds;
    static pollfd * pfds;
    static struct pollfd * find_empty_slot();
    int idx;
#endif

public:
    pollsocket(int f, bool * success, int events = POLLIN | POLLOUT, int = 512, int = 0);
    virtual ~pollsocket();
    virtual int event_handler(const struct pollfd *) = 0;

    static  int create_table(int );
    static  int poll_all(int );
    static  int compress(void);
    int close(void);
    int set_events(int);
    int set_revents(int);

    /** (buffered) reads and writes
    int read(int = 0);
    int queue(const void *,  int);
    int write(const void * , int);

    *** Flush input buffer ... ***
    int flush(pollsocket *);             ** ... to another socket (output buff) **
    int flush(int, int);                 ** ... to a raw file descriptor **

    *** Flush output buffer ... ***
    int flushO(pollsocket *);            ** ... to another socket (input buff) **
    int flushO(int = 0);                 ** ... out our own fd

    *** To clear buffers ***
    void clear();
    void clearO();
    **/

    void optimize_buffers(void);

    int printf(const char *, ...);
    int printf_raw(const char *, va_list *);

#ifdef _USE_SSL
    bool switch_to_ssl(void);
    bool accept_to_ssl(void);
    static int init_ssl(const char *);
    static int shutdown_ssl(void);
#endif


#ifdef USE_SELECT
    int events() const;
    int revents() const;
    int revents(int) const;
#else
	/* Get the events flag */
	int events() const
	{
		if (fd > -1)
			return pfds[idx].events;
		return 0;
	}
    /* Returns the result flags */
    int revents() const
    {
        if (fd > -1)
            return pfds[idx].revents;
        return 0;
    }

    /* Tries to match a result flag */
    int revents(int r) const
    {
        if (fd > -1)
            return (pfds[idx].revents & r);
        return 0;
    }
#endif


    /*
     * read() from file descriptor & add to buffer
     * return:  > 0 = success (bytes added)
     *            0 = nothing to add
     *          < 0 = error condition
     *     [... error codes same as dynbuff error codes ...]
     */
    int read(int b = 0)
    {
    #ifdef _USE_SSL
        if (ssl)
            return ibuff->add(ssl, b);
    #endif
        return ibuff->add(fd, b);
    }

    /* buffer size bytes, and attempt to send as much as possible
     * return # bytes sent */
    int write(const void * data , int size)
    {
        int r = queue(data, size);
        if (r < 0)
            return r;
        return obuff->flush(fd);
    }

    /* buffer 'size' bytes, but do not send
     * return # bytes queued */
    int queue(const void * data, int size)
    {
        return obuff->add((const char *) data, size);
    }

    /* flush input buffer to output buffer of
     * socket */
    int flush(pollsocket * target)
    {
        return ibuff->transfer(target->obuff);
    }

    #if 0
    /* flush input buffer to a file descriptor */
    int flush(int fd, int size)
    {
        return ibuff->flush(fd, size);
    }
    #endif

    /* flush output buffer to input buffer of
     * another socket  */
    int flushO(pollsocket * target)
    {
        return obuff->transfer(target->ibuff);
    }

    /* flush output buffer out our own fd */
    int flushO(int size = 0)
    {
    #ifdef _USE_SSL
        if (ssl)
            return obuff->flush(ssl, size);
    #endif
        return obuff->flush(this->fd, size);
    }

    /* clear input buffer */
    void clear()
    {
        ibuff->reset();
    }

    void clearO()
    {
        obuff->reset();
    }

};

enum {
    SOCK_ERR_READ  = DYNBUFF_ERR_READ,
    SOCK_ERR_WRITE = DYNBUFF_ERR_WRITE,
    SOCK_ERR_MEM   = DYNBUFF_ERR_MEM,
    SOCK_ERR_FULL  = DYNBUFF_ERR_FULL
};

#endif


syntax highlighted by Code2HTML, v. 0.9.1