#include "config.h"

#ifdef HAVE_POP3

#include "pop3client.h"
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#ifdef HAVE_SSL
#include "ssl.h"
#endif


struct pop3_struct
{
        struct sockaddr_in server;
        struct hostent *hp;
        int s;
        int numunread;
        int numtotal;
#ifdef HAVE_SSL
        ssl_info_t *ssl;
#endif
};


#define BUFLEN 1024

static int pop3Send(Pop3 pc, char *msg, int len)
{
#ifdef HAVE_SSL
        if ( pc->ssl != NULL )
                return SSL_write(pc->ssl->ssl, msg, len);
#endif

        return send(pc->s, msg, len, 0);
}

static int pop3Recv(Pop3 pc, char *res, int size)
{
        int len = 0;
        int sel = 1;
        int fd = pc->s;
        fd_set fds;
        struct timeval tv;

#ifdef HAVE_SSL
        if ( pc->ssl != NULL )
                fd = SSL_get_fd(pc->ssl->ssl);
#endif
        do
        {
                int n_read = 0;

                FD_ZERO(&fds);
                FD_SET(fd, &fds);

                tv.tv_sec = 1;
                tv.tv_usec = 0;

                sel = select(fd + 1, &fds, NULL, NULL, &tv);
                if ( sel > 0 )
                {
#ifdef HAVE_SSL
                        if ( pc->ssl != NULL )
                                n_read = SSL_read(pc->ssl->ssl, &res[len], size - len);
                        else
#endif
                                n_read = recv(fd, &res[len], size - len, 0);
                        if ( n_read == 0 )
                        {
                                res[len] = '\0';
                                return len;
                        }
                        if ( n_read < 0 )
                        {
                                fprintf(stderr, "pop3Recv() error (%d)\n", n_read);
                                return -1;
                        }

                        len += n_read;
                        res[len] = '\0';

                        if ( len > 0 && (*res == '+' || *res == '-') )
                                break;
                }
        }
        while ( (len < size) && (sel != 0) );


        return len;
}

Pop3 initPop3(void)
{

        Pop3 pc;

        pc = (Pop3)malloc(sizeof(*pc));
        if (pc == 0) return 0;

        pc->numtotal = 0;
        pc->numunread = 0;

#ifdef HAVE_SSL
        pc->ssl = NULL;
#endif

        return pc;
}

int pop3Connect(Pop3 pc, char *serverName, int port)
{
        if ((pc->s = socket(AF_INET, SOCK_STREAM, 0)) < 0) return -1;

        memset(&(pc->server), 0, sizeof(pc->server));
        pc->server.sin_family = AF_INET;
        if ((pc->hp = gethostbyname(serverName)) <= 0) return -1;

        memcpy( &(pc->server.sin_addr), pc->hp->h_addr, pc->hp->h_length);
        pc->server.sin_port = htons(port);
        if (connect(pc->s, (struct sockaddr *)&(pc->server), sizeof(pc->server)) < 0)
                return -1;

#ifdef HAVE_SSL
        if ( port == 995 )
                init_SSL(&pc->ssl, pc->s);
#endif

        return 0;
}

int pop3Login(Pop3 pc, char *user, char *pass)
{

        int size;
        char in[BUFLEN];
        char temp[BUFLEN];
        char out[BUFLEN];

        if ((size = pop3Recv(pc, in, BUFLEN)) < 1) return -1;

        memset(temp, 0, BUFLEN);
        memcpy(temp, in, size > BUFLEN ? BUFLEN : size);
        if (temp[0] != '+' ) return -1;

        snprintf(out, BUFLEN, "USER %s\r\n", user);
        pop3Send(pc, out, strlen(out));
        if ((size = pop3Recv(pc, in, BUFLEN)) < 1) return -1;

        memset(temp, 0, BUFLEN);
        memcpy(temp, in, size > BUFLEN ? BUFLEN : size);
        if (temp[0] != '+') return -1;

        snprintf(out, BUFLEN, "PASS %s\r\n", pass);
        pop3Send(pc, out, strlen(out));
        if ((size = pop3Recv(pc, in, BUFLEN)) < 1) return -1;
        memset(temp, 0, BUFLEN);
        memcpy(temp, in, size > BUFLEN ? BUFLEN : size);
        if (temp[0] != '+') return -1;
        /*free(in);
        free(temp);
        */ 
        return 0;
}

int pop3CheckMail(Pop3 pc)
{

        int size;
        char in[BUFLEN];
        char temp[BUFLEN];
        char out[BUFLEN];
        char *ptr;

        snprintf(out, BUFLEN, "STAT\r\n");
        pop3Send(pc, out, strlen(out));
        if ((size = pop3Recv(pc, in, BUFLEN)) < 1) return -1;

        ptr = strtok(in, " ");
        ptr = strtok(0, " ");
        memset(temp, 0, BUFLEN);
        memcpy(temp, in, size > BUFLEN ? BUFLEN : size);
        if (temp[0] != '+') return -1;

        pc->numtotal = atoi(ptr);
        snprintf(out, BUFLEN, "LAST\r\n");
        pop3Send(pc, out, strlen(out));
        if ((size = pop3Recv(pc, in, BUFLEN)) < 1) return -1;

        memset(temp, 0, BUFLEN);
        memcpy(temp, in, size > BUFLEN ? BUFLEN : size);
        if ( temp[0] != '+')
        {
                pc->numunread = pc->numtotal;
        }
        else
        {
                ptr = strtok(in, " ");
                ptr = strtok(0, " ");
                pc->numunread = pc->numtotal - atoi(ptr);
        }
        return 1;
}

int pop3GetTotalMess(Pop3 pc)
{
        if (pc != 0) return pc->numtotal;
        return -1;
}

int pop3GetUnreadMess(Pop3 pc)
{
        if (pc != 0) return pc->numunread;
        return -1;
}

int pop3Quit(Pop3 pc)
{

        int size;
        char in[BUFLEN];

        pop3Send(pc, "quit\r\n", 6);
        size = pop3Recv(pc, in, BUFLEN);
#ifdef HAVE_SSL
        if (pc->ssl != NULL)
        {
                SSL_shutdown(pc->ssl->ssl);
                SSL_free(pc->ssl->ssl);
                free(pc->ssl);
                pc->ssl = NULL;
        }
#endif
        if (pc->s != 0) close(pc->s);
        return 0;
}

void free_pop3(Pop3 *pc)
{
        free(*pc);

        *pc = NULL;
}
#endif /* HAVE_POP3 */

