/* $Id: imap.c,v 1.9 2000/06/06 05:26:10 jared Exp $ */
#include "config.h"

/* see RFC 2060 for protocol details 
 Excerpt included here:

6.2.2.  LOGIN Command

   Arguments:  user name
               password

   Responses:  no specific responses for this command

   Result:     OK - login completed, now in authenticated state
               NO - login failure: user name or password rejected
               BAD - command unknown or arguments invalid

      The LOGIN command identifies the client to the server and carries
      the plaintext password authenticating this user.

   Example:    C: a001 LOGIN SMITH SESAME
               S: a001 OK LOGIN completed

6.1.3.  LOGOUT Command

   Arguments:  none

   Responses:  REQUIRED untagged response: BYE

   Result:     OK - logout completed
               BAD - command unknown or arguments invalid

      The LOGOUT command informs the server that the client is done with
      the connection.  The server MUST send a BYE untagged response
      before the (tagged) OK response, and then close the network
      connection.

   Example:    C: A023 LOGOUT
               S: * BYE IMAP4rev1 Server logging out
               S: A023 OK LOGOUT completed
               (Server and client then close the connection)

 */

struct imapdata {
        time_t start; /* test start time */
        time_t lastact; /* last activity */
        int state;
        };

#define IMAP_CONNECT    1 /* doing a connect() */
#define IMAP_WAITBAN    2 /* Waiting for the banner */
#define IMAP_SENT_AUTH	3 /* sent our auth data */
#define IMAP_SENT_QUIT  4 /* sent logout */
#define IMAP_GOT_BYE    5 /* got a bye from the remote server */

void	start_test_imap(struct monitorent *here)
{
        struct imapdata *localstruct = NULL;
        struct my_hostent *hp = NULL;
        struct sockaddr_in name;
        int serrno = -1, errcode = 0;

        /* Allocate our memory */
        here->monitordata = MALLOC(sizeof(struct imapdata), "imap localstruct");

        localstruct = here->monitordata;

        time(&localstruct->start);

        localstruct->lastact = localstruct->start;

        here->filedes = open_sock();

        if (here->filedes == -1)
	{
		here->retval = here->checkent->lastcheck;
		FREE(here->monitordata);
		here->monitordata = NULL;
		return;
	}


        hp = my_gethostbyname(here->checkent->hostname);
	
	if (hp == NULL)
	{
		here->retval = SYSM_NODNS;
		FREE(here->monitordata);
		here->monitordata = NULL;
		return;
	}

        /* zero out the space */
        memset ( &name, 0, sizeof ( name ) );

        /* copy data */
        memcpy((char*)&name.sin_addr, (char*)hp->my_h_addr, hp->h_length);

        /* set family type */
        name.sin_family = AF_INET;

        /* set the port we're connecting to */
        name.sin_port = htons(IMAP_PORTNUM);

        if (debug)
	{
		print_err(0, "start_test_imap() doing connect() to %s:%d\n",
			here->checkent->hostname, IMAP_PORTNUM);
	}

        errcode = connect(here->filedes, (struct sockaddr*)&name,
                sizeof(struct sockaddr_in));
        serrno = errno;
        if (debug)
	{
                perror("connect() in start_imap");
	}

        if ((errcode < 0) && (serrno != EINPROGRESS))
        {
                close(here->filedes);

		switch(serrno)
		{
			case ECONNREFUSED:
			case EINTR:
				here->retval = SYSM_CONNREF;
				break;
			case ENETUNREACH:
				here->retval = SYSM_NETUNRCH;
				break;
			case EHOSTDOWN:
			case EHOSTUNREACH:
                        	here->retval = SYSM_HOSTDOWN;
				break;
			case ETIMEDOUT:
                        	here->retval = SYSM_TIMEDOUT;
				break;
                }

                /* Free memory we'd normally leak */
                FREE(localstruct);
                here->monitordata = NULL;

		if (debug)
		{
			print_err(0, "imap.c: retval = %d\n", here->retval);
		}
                return;
        }

        /* doing a connect() */
        localstruct->state = IMAP_CONNECT;

        /* poll it again later */
        return;
}

void	service_test_imap(struct monitorent *here)
{
        struct imapdata *localstruct = NULL;
        char buffer[256];
        int isopenretval = -1;

        /* do some variable shufflign */
        localstruct = here->monitordata;
	
	if (localstruct == NULL)
	{
		print_err(1, "bug - localstruct == NULL in imap.c:service_test_imap\n");
		return;
	}

        if (debug)
	{
                print_err(0, "service_test_imap: state = %d\n", localstruct->state);
	}
        /* do different things based on the state */
        switch (localstruct->state)
        {
                case IMAP_CONNECT:
                {
                        isopenretval = is_open(here->filedes);
                        if (debug)
			{
				print_err(0, "is_open is returning %d\n", isopenretval);
			}
                        if ((isopenretval != -1) &&
                                (isopenretval != SYSM_INPROG))
                        {
                                time(&localstruct->lastact);
                                if (debug)
				{
					print_err(0, "is_open() in service_test_imap()\n");
				}
                                if (isopenretval != SYSM_OK)
                                {
                                        here->retval = isopenretval;
                                        close(here->filedes);
                                        FREE(localstruct);
                                        here->monitordata = NULL;
                                        if (debug)
					{
                                                print_err(0, "ending service_test_imap() with retval = %s\n", errtostr(here->retval));
					}
                                        return;
                                }
                                localstruct->state = IMAP_WAITBAN;
                                if (debug)
				{
					print_err(0, "connected() to the imap port of %s\n", here->checkent->hostname);
				}
                        }
                        break;
                }

                case IMAP_WAITBAN:
                {
                        if (data_waiting_read(here->filedes, 0))
                        {
                                memset(buffer, 0, 256);
                                read(here->filedes, buffer, 254);
                                if (debug)
				{
					print_err(0, "imap.c:Got :%s:\n", buffer);
				}
                                if (strncmp(buffer, "* OK", 4) == 0)
                                {
                                        /* prepare the buffer */
                                        snprintf(buffer, 256, "A100 LOGIN %s %s",
                                                here->checkent->username,
						here->checkent->password);

                                        /* send the buffer out the socket */
                                        sendline(here->filedes, buffer);
                                        localstruct->state = IMAP_SENT_AUTH;
                                } else {
                                        here->retval = SYSM_BAD_RESP;
				}
                        }
                        break;
                }
		case IMAP_SENT_AUTH:
		{
                        if (data_waiting_read(here->filedes, 0))
                        {
                                memset(buffer, 0, 256);
                                read(here->filedes, buffer, 254);
                                if (debug)
				{
					print_err(0, "imap.c:Got :%s:\n", buffer);
				}
                                if (strncmp(buffer, "A100 OK", 7) == 0)
                                {
					here->retval = SYSM_OK;
				} else if (strncmp(buffer, "A100 NO", 7) == 0)
				{
					here->retval = SYSM_BAD_AUTH;
                                } else {
                                        here->retval = SYSM_BAD_RESP;
                                }

				sendline(here->filedes, "A102 LOGOUT");

                                /* send the buffer out the socket */
                                localstruct->state = IMAP_SENT_QUIT;
                        }
                        break;
		}
		case IMAP_SENT_QUIT:
		{
                        if (data_waiting_read(here->filedes, 0))
                        {
                                memset(buffer, 0, 256);
                                read(here->filedes, buffer, 254);
                                if (debug)
				{
					print_err(0, "imap.c:Got :%s:\n", buffer);
				}
                                if (strncmp(buffer, "A102 OK", 7) == 0)
                                {
                                        here->retval = SYSM_OK;
					localstruct->state = IMAP_GOT_BYE;
                                } else {
                                        here->retval = SYSM_BAD_RESP;
                                }
                        }
                        break;
		}


	} /* end of switch */
        if (here->retval != -1)
        {
                /* insert cleanup code here */
                close (here->filedes);
                FREE(localstruct);
                here->monitordata = NULL;
        }
        return;

}

void    stop_test_imap(struct monitorent *here)
{
        struct imapdata *localstruct = NULL;

        localstruct = here->monitordata;
	if (localstruct == NULL)
		return;

        close(here->filedes);
        FREE(localstruct);
        here->monitordata = NULL;
        return;

}


