/*
 * asmail is the AfterStep mailbox monitor
 * Copyright (c) 2002 Albert Dorofeev <albert@tigr.net>
 * For the updates see http://www.tigr.net/
 *
 * This software is distributed under GPL. For details see LICENSE file.
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>

#include "globals.h"
#include "imap.h"
#include "socklib.h"

#define BYE(RET_STATUS) { \
	Sclose(s); \
	return(RET_STATUS); \
}

#define WAITOK {					\
    switch (Sread(s->sd, input, MAX_INPUT_LENGTH, mb->timeout)) {	\
        case -1:	/* Error */			\
	   BYE(STAT_CONN);				\
        case 0:		/* Timeout */			\
           BYE(STAT_TIMEOUT);				\
    }							\
}

int imap_login( struct mbox_struct * mb, SOCKET ** sock ) {
	char input[MAX_INPUT_LENGTH+1];
	char output[MAX_INPUT_LENGTH+1];
	char key[MAX_INPUT_LENGTH+1];
	char * tmp_str;
	int tmp_int;
	SOCKET * s;

	if ( !( *sock = Sopen() ) ) {
		return STAT_FAIL;
	}
	s = *sock;
	if ( Sclient(s, mb->server, mb->port) == -1 ) {
		BYE(STAT_CONN);
	}
	/* Once we connect, we should receive the greeting */
	WAITOK;
	if ( strncmp(input, "* OK", 4) ) {
		BYE(STAT_CONN);
	}
	/* Login into the server */
	sprintf(output, "A000 LOGIN %s %s\r\n", mb->user, mb->pass);
	if ( Swrite(s->sd, output) == -1 ) {
		BYE(STAT_CONN);
	}
	WAITOK;
	if ( strncmp(input, "A000 OK", 7) ) {
		BYE(STAT_LOGIN);
	}
	return(STAT_IDLE);
}

int imap_checkmbox ( struct mbox_struct * mb, SOCKET ** sock ) {
	int ctotal = 0;
	int cnew = 0;
	char input[MAX_INPUT_LENGTH+1];
	char output[MAX_INPUT_LENGTH+1];
	char * tmp_str;
	SOCKET * s;
	s = *sock;

	/* seems we are logged in, get statistics */
	sprintf(output, "A001 STATUS %s (MESSAGES)\r\n", mb->mbox);
	if ( Swrite(s->sd, output) == -1 ) {
		BYE(STAT_CONN);
	}
	WAITOK;
	if ( strncmp(input, "* STATUS", 8) ) {
		/* Notes 6 sends a different (non-standard?) response */
		/* According to Jason Day <jasonday@worldnet.att.net> :
		 * * OK Domino IMAP4 Server Release 6.0.1 ready
		 * A000 LOGIN xxx xxx
		 * A000 OK LOGIN completed
		 * A001 STATUS INBOX (MESSAGES)
		 * * 10 EXISTS
		 * * 0 RECENT
		 * * STATUS INBOX (MESSAGES 10)
		 * A001 OK STATUS completed
		 */
		if (strncmp (input, "* ", 2)) {
		BYE(STAT_CONN);
		}
		WAITOK;

		if (strncmp (input, "* ", 2)) {
			BYE(STAT_CONN);
		}
		WAITOK;

		if ( strncmp(input, "* STATUS", 8) ) {
			BYE(STAT_CONN);
		}
	}
	if ( tmp_str = strstr(input, "MESSAGES") ) {
		sscanf(tmp_str, "MESSAGES %d", &ctotal);
	} else {
		printf("asmail: imap_mailcheck: did not get MESSAGES in response to the STATUS.\n");
		BYE(STAT_FAIL);
	}
	WAITOK;
	if ( strncmp(input, "A001 OK", 7) ) {
		BYE(STAT_CONN);
	}
	sprintf(output, "A002 STATUS %s (UNSEEN)\r\n", mb->mbox);
	if ( Swrite(s->sd, output) == -1 ) {
		BYE(STAT_CONN);
	}
	WAITOK;
	if ( strncmp(input, "* STATUS", 8) ) {
		BYE(STAT_CONN);
	}
	if ( tmp_str = strstr(input, "UNSEEN") ) {
		sscanf(tmp_str, "UNSEEN %d", &cnew);
	} else {
		printf("asmail: imap_mailcheck: did not get UNSEEN in response to the STATUS.\n");
		BYE(STAT_FAIL);
	}
	WAITOK;
	if ( strncmp(input, "A002 OK", 7) ) {
		BYE(STAT_CONN);
	}
	if ( (mb->cnew != cnew) && (cnew != 0) ) {
		pthread_mutex_lock(&mb->mutex);
		mb->flags |= FLAG_ARRIVED;
		pthread_mutex_unlock(&mb->mutex);
	}
	mb->cnew = cnew;
	mb->ctotal = ctotal;
	return(STAT_IDLE);
}

int imap_goodbye ( struct mbox_struct * mb, SOCKET ** sock ) {
	char input[MAX_INPUT_LENGTH+1];
	char output[MAX_INPUT_LENGTH+1];
	SOCKET * s;
	s = *sock;

	/* and good-bye */
	sprintf(output, "A004 LOGOUT\r\n");
	if ( Swrite(s->sd, output) == -1 ) {
		BYE(STAT_CONN);
	}
	WAITOK;
	if ( strncmp(input, "* BYE", 5) ) {
		BYE(STAT_CONN);
	}
	Sclose(*sock);
	return(STAT_IDLE);
}

void imap_handle( struct mbox_struct * mb ) {
	SOCKET * s;
	/* Sanity check */
	if ( mb->type != MBOX_IMAP ) {
		printf("asmail: imap_handle: Cowardly refusing to work with a non-IMAP server.\n");
		mb->status = STAT_FAIL;
		pthread_exit(NULL);
	}
	if ( ! strlen(mb->server) ) {
		printf("asmail: imap_handle: no server name specified!\n");
		mb->status = STAT_FAIL;
		pthread_exit(NULL);
	}
	if ( ! strlen(mb->user) ) {
		printf("asmail: imap_handle: no user name specified!\n");
		mb->status = STAT_FAIL;
		pthread_exit(NULL);
	}
	if ( mb->port == 0 ) 
		mb->port = PORT_IMAP;
	if ( ! strlen(mb->mbox) )
		strcpy(mb->mbox, IMAP_MAILBOX_DEFAULT);
	if ( mb->flags & FLAG_PERSISTENT_CONN )
		mb->status = imap_login(mb, &s);
	while (1) {
		mb->status |= STAT_RUN;
		signal_update();

		if ( mb->flags & FLAG_PERSISTENT_CONN ) {
			if ( mb->status = imap_checkmbox(mb, &s) ) {
				/* something failed - retry once */
				mb->status = imap_goodbye(mb, &s);
				mb->status = imap_login(mb, &s) | 
					imap_checkmbox(mb, &s);
			}
		} else {
			mb->status |= imap_login(mb, &s);
			mb->status |= imap_checkmbox(mb, &s);
			mb->status |= imap_goodbye(mb, &s);
		}
		mb->status &= ~STAT_RUN;
		if ( ! mb->status ) {
			mb->status = STAT_IDLE;
			if ( mb->cnew > 0)
				mb->mail = MAIL_NEW;
			else if ( mb->ctotal > 0 )
				mb->mail = MAIL_OLD;
			else
				mb->mail = MAIL_NONE;
		}
		signal_update();
		sleep_check(mb->update);
	}
}

