#include "config.h"

#ifdef HAVE_IMAP
#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>
#include <sys/select.h>
#ifdef HAVE_SSL
#include "ssl.h"
#endif

#include "imapclient.h"

struct imap_struct
{
        struct sockaddr_in server;
        struct hostent *hp;
        int s;
        int numunread;
        int numtotal;
#ifdef HAVE_SSL
        ssl_info_t *ssl;
#endif
        char *last;
        int id;
};

static int imapSendMsg(struct imap_struct *ic, char *msg, int size)
{
        char buf[4096];

        snprintf(ic->last, 16, "A%04d ", ic->id++);

        strcpy(buf, ic->last);
        strcat(buf, msg);
#ifdef HAVE_SSL
        if ( ic->ssl != NULL )
                return SSL_write(ic->ssl->ssl, buf, strlen(buf));
#endif 
        return send(ic->s, buf, strlen(buf), 0);
}

static int imapGetAnswer(struct imap_struct *ic, char *res, int size)
{
        int len = 0;
        int x;
        int sel = 1;
        int s = ic->s;
        fd_set fds;
        struct timeval tv;

#ifdef HAVE_SSL
        if ( ic->ssl != NULL )
        {
                s = SSL_get_fd(ic->ssl->ssl);
        }
#endif

        size--;
        do
        {
                FD_ZERO(&fds);
                FD_SET(s, &fds);

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

                sel = select(s + 1, &fds, NULL, NULL, &tv );

                if ( sel > 0 )
                {
#ifdef HAVE_SSL
                        if ( ic->ssl != NULL )
                                x = SSL_read(ic->ssl->ssl, &res[len], size - len);
                        else
#endif
                                x = recv(s, &res[len], size - len, 0);
                        if ( x <= 0 )
                        {
                                fprintf(stderr, "imapGetAnswer() receive error\n");
                                return -1;
                        }
                        len += x;
                        res[len] = '\0';
                }
                if ( len > 0 && strstr(res, ic->last) != NULL )
                        break;

        }
        while ( (len < size) && (sel != 0));

        return len;
}

Imap initImap(void)
{
        Imap ic;

        ic = (Imap)malloc( sizeof(*ic) );
        if ( ic == 0 )
                return 0;
        ic->numtotal = -1;
        ic->numunread = -1;
        ic->s = -1;
#ifdef HAVE_SSL
        ic->ssl = NULL;
#endif

        ic->id = 1;
        ic->last = malloc(16);

        return ic;
}

int imapConnect(Imap ic, char *serverName, int port)
{
        int size;
        char in[1024];

        if ((ic->s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
                return -1;
        memset( &(ic->server), 0, sizeof(ic->server) );
        ic->server.sin_family = AF_INET;
        if ((ic->hp = gethostbyname(serverName)) <= 0)
                return -1;
        memcpy( &(ic->server.sin_addr), ic->hp->h_addr, ic->hp->h_length );
        ic->server.sin_port = htons(port);
        if ( connect( ic->s, (struct sockaddr *)&(ic->server), sizeof(ic->server)) < 0)
                return -1;
#ifdef HAVE_SSL
        if ( port != 143 && port != 220 ) /* !imap & !imap3 */
                init_SSL(&ic->ssl, ic->s);
#endif
        if ( ((size = imapGetAnswer(ic, in, 1024)) < 0) && (in[0] != '*') )
                return -1;

        return 0;
}

int imapLogin(Imap ic, char *user, char *pass)
{
        int size;
        char in[1024];
        char out[1024];

#ifdef HAVE_SSL
        snprintf( out, 1024, "CAPABILITY\r\n" );
        imapSendMsg(ic, out, strlen(out));
        if ( (size = imapGetAnswer(ic, in, 1024)) < 0)
                return -1;
        if ( strstr( in, "STARTTLS" ) != NULL )
        {
                snprintf( out, 1024, "STARTTLS\r\n" );
                imapSendMsg(ic, out, strlen(out));
                imapGetAnswer(ic, in, 1024);
                init_SSL( &ic->ssl, ic->s );
        }
#endif
        snprintf( out, 1024, "LOGIN %s \"%s\"\r\n", user, pass);
        imapSendMsg(ic, out, strlen(out));
        size = imapGetAnswer(ic, in, 1024);
        if ( strstr(in, "OK") != NULL )
                return 0;
        return -1;
}

int imapCheckMail(Imap ic, char *folder)
{
        int size;
        int total, num;
        char in[1024];
        char out[1024];
        char temp[1024];

        memset(out, 0, 1024);
        snprintf(out, 1024, "STATUS \"%s\" (MESSAGES UNSEEN)\r\n",
                 folder);
        imapSendMsg(ic, out, strlen(out));
        size = imapGetAnswer(ic, in, 1024);

        if ( (size < 2) || (strstr(in, "OK") == NULL))
                return -1;

        if ( sscanf(in, "* STATUS %s (MESSAGES %d UNSEEN %d)" ,
                    temp, &total, &num ) == 3 )
        {
                ic->numtotal = total;
                ic->numunread = num;
        }

        return 0;
}

int imapGetTotalMess( Imap ic )
{
        if ( ic != 0 )
                return ic->numtotal;
        return -1;
}

int imapGetUnreadMess( Imap ic )
{
        if ( ic != 0 )
                return ic->numunread;
        return -1;
}

int imapQuit( Imap ic )
{
        imapSendMsg(ic, "LOGOUT\r\n", 8);
#ifdef HAVE_SSL
        if ( ic->ssl != NULL )
        {
                SSL_shutdown(ic->ssl->ssl);
                SSL_CTX_free(ic->ssl->ctx);
                SSL_free(ic->ssl->ssl);
                free(ic->ssl);
        }
#endif
        if (ic->s != 0)
                close(ic->s);
        ic->s = -1;
        free(ic->last);
        return 0;
}
#endif /* HAVE_IMAP */

