/*
 *	SERVER.C  --  this C file contains the routines to run
 *		      a POP2 protocol server.
 *
 *      (C) Copyright 1991 Regents of the University of California
 *
 *      Permission to use, copy, modify, and distribute this program
 *      for any purpose and without fee is hereby granted, provided
 *      that this copyright and permission notice appear on all copies
 *      and supporting documentation, the name of University of California
 *      not be used in advertising or publicity pertaining to distribution
 *      of the program without specific prior permission, and notice be
 *      given in supporting documentation that copying and distribution is
 *      by permission of the University of California.
 *      The University of California makes no representations about
 *      the suitability of this software for any purpose.  It is provided
 *      "as is" without express or implied warranty.
 *
 *	REVISIONS:
 *			12-87	[ks]	original implementation
 *		1.000	 5-88	[ks]
 *		1.001	 2-89	[ks]
 *
 */

#include "globdefs.h"
#include "faildefs.h"
#include "svrdefs.h"
#include "svrvars.h"

/************************** DEBUGGING ***************************************/

trace(side,state,str)
    char *side;					/* SVR(server) or CLI(client) */
    int   state;				/* Current state */
    char *str;					/* Reply for client */
	/* This routine reports on actions taken by the server. The  */
	/*   routine is only called if program debugging is enabled. */
{
    fprintf(log_fp,"%s (%d): %s",side,state,str);
    fflush(log_fp);
}

/******************* SERVER UTILITY ROUTINES ********************************/

char *user_ok(pop2_str)
    char *pop2_str;			/* Ptr to usercode/password strings */
	/* This routine performs usercode/password verification on */
	/*   the tokens given with the client's HELO command. If   */
	/*   the usercode/password are valid, the routine returns  */
	/*   a pointer to alloced memory containing the usercode;  */
	/*   if the usercode/password are invalid, the routine     */
	/*   returns NULL.					   */
{
    int r;				/* Temp integer storage */
    char *pt,*pt2;			/* Temp pointers */
    char *c_pt;
    char *user;				/* Temp ptr for return value */
    struct passwd *pwd;			/* Struct for password entry */
#ifdef SHADOWPWD
    struct spwd *spwd;
#endif

    if (pop2_str == NULL) return(NULL);	/* Shouldnt happen, check happen */
					/* Skip LWSP before usercode string */
    while (  isspace(*pop2_str)  && (*pop2_str != NULL_CHAR)) { ++pop2_str; }
    if (*pop2_str == NULL_CHAR) {	/* Anything left? */
	return(NULL);			/* Ooops, no usercode given */
    }
    pt = pop2_str;			/* Save ptr to start of usercode */

					/* Skip to next LWSP after usercode */
    while ((!isspace(*pop2_str)) && (*pop2_str != NULL_CHAR)) {
	if (*pop2_str == BACKSL_CHAR) {	/* Quoted special character? */
					/* Yes, interpret the quoting */
	    pt2 = pop2_str;		/* Use a temp pointer to un-quote */
	    while (*pt2 != NULL_CHAR) {	/* Shift rest of string one slot */
		*pt2++ = *(pt2+1);
	    }
	}
	++pop2_str;			/* Incr to next slot */
    }
    if (*pop2_str == NULL_CHAR) {	/* Anything left after usercode? */
	return(NULL);			/* Ooops, no password given */
    }
    *pop2_str++ = NULL_CHAR;		/* NULL terminate string for save */

    user = malloc(strlen(pt) + 1);	/* Save username in alloced memory */
    if (user == NULL) fail(FAIL_OUT_OF_MEMORY);
    strcpy(user,pt);
					/* Skip LWSP before password */
    while (  isspace(*pop2_str)  && (*pop2_str != NULL_CHAR)) { ++pop2_str; }
    if (*pop2_str == NULL_CHAR) {	/* Anything left? */
	return(NULL);			/* Ooops, no password given */
    }

        /* Verify usercode and password */
    pwd = getpwnam(user);		/* Check for a passwd entry this user */
    if (pwd == NULL) return(NULL);	/* Ooops, invalid usercode */

    pt = pop2_str;			/* Save ptr to start of password */
					/* Skip to next LWSP after password */
    while ((!isspace(*pop2_str)) && (*pop2_str != NULL_CHAR)) {
	if (*pop2_str == BACKSL_CHAR) {	/* Quoted special character? */
					/* Yes, interpret the quoting */
	    pt2 = pop2_str;		/* Use a temp pointer to un-quote */
	    while (*pt2 != NULL_CHAR) {	/* Shift rest of string one slot */
		*pt2++ = *(pt2+1);
	    }
	}
	++pop2_str;			/* Incr to next slot */
    }
    *pop2_str = NULL_CHAR;		/* NULL terminate string for save */
    pop2_str = pt;			/* Restore ptr to start of password */

#ifdef SHADOWPWD
    spwd = getspnam(user);
    if (spwd == NULL) return(NULL);

    pwd->pw_passwd = spwd->sp_pwdp;
    if (pwd->pw_name && pwd->pw_passwd[0] == '@') {
	if (pw_auth(pwd->pw_passwd+1, user, PW_LOGIN)) return(NULL);
	r = setuid(pwd->pw_uid);	/* Run under user's uid */
	if (r == UNDEFINED) return(NULL);
	return(user);			/* Return ptr to username */
    }
    if (!valid(pop2_str, pwd)) return(NULL);
    r = setuid(pwd->pw_uid);		/* Run under user's uid */
    if (r == UNDEFINED) return(NULL);
    return(user);			/* Return ptr to username */
#else
					/* Encrypt the given password string */
    pt = crypt(pop2_str,pwd->pw_passwd);
    if (strcmp(pt,pwd->pw_passwd)) {	/* Encrypted strings match? */
	return(NULL);			/* No, bad password given */
    } else {
					/* Yes, user and passwd both valid */
	r = setuid(pwd->pw_uid);	/* Run under user's uid */
	if (r == UNDEFINED) return(NULL);
    }
#endif

    return(user);			/* Return ptr to username */
}

char *get_host_mbox(host)
    char *host;				/* Ptr to name of host */
	/* This routine constructs a string defining the filename */
	/*   of the system mailbox for a given host machine. A    */
	/*   ptr to the host name is passed in as host.		  */
{
    char *mbox;				/* Ptr to save constructed filename */

					/* Get memory for mailbox filename */
    mbox = malloc(strlen(DEF_OS_POP2DIR) + strlen(host) + 2);
    if (mbox == NULL) fail(FAIL_OUT_OF_MEMORY);

    strcpy(mbox, DEF_OS_POP2DIR);	/* All mailboxes in same directory */
    strcat(mbox, DEF_DIR_DELIM);	/* Directory delimiter char */
    strcat(mbox, host);			/* Host name is name of mailbox file */

    return(mbox);			/* Return ptr to constructed name */
}

char *get_user_mbox(user)
    char *user;				/* Ptr to name of user */
	/* This routine constructs a string defining the filename */
	/*   of the system mailbox for a given usercode. A ptr to */
	/*   the usercode is passed in as user.			  */
{
    char *mbox;				/* Ptr to save constructed filename */

					/* Get memory for mailbox filename */
    mbox = malloc(strlen(DEF_OS_MAILDIR) + strlen(user) + 2);
    if (mbox == NULL) fail(FAIL_OUT_OF_MEMORY);

    strcpy(mbox, DEF_OS_MAILDIR);	/* All mailboxes in same directory */
    strcat(mbox, DEF_DIR_DELIM);	/* Directory delimiter char */
    strcat(mbox, user);			/* Username is name of mailbox file */

    return(mbox);			/* Return ptr to constructed name */
}

/******************** SERVER ACTION ROUTINES ********************************/

svr_garbage(err_msg)
    char *err_msg;				/* Ptr to error message */
	/* This routine is called when the POP2 server state machine */
	/*   receives an unexpected command from the client. The     */
	/*   calling routine supplies a detailed error message. A    */
	/*   pointer to the error message is passed as err_msg. This */
	/*   routine releases the current mail folder (if any). It   */
	/*   then constructs an error reply for the client. This     */
	/*   routine always returns EXIT state.			     */
{
    fld_release();				/* Release open folder */

    strcpy(from_svr_buf,"- error: ");		/* Construct an error reply */
    strcat(from_svr_buf,err_msg);
    strcat(from_svr_buf,"\r\n");

    return(SVR_DONE_STATE);			/* Will quit after err send */
}

svr_greeting()
	/* This routine constructs an POP2 server greeting message. */
	/*   The routine is called in response to an new connection */
	/*   openned by a client. The routine returns AUTH state to */
	/*   indicate the next command expected from the client is  */
	/*   the HELO command.					    */
{
    strcpy(from_svr_buf,"+ POP2 ");		/* Construct greeting msg */
    strcat(from_svr_buf,host_i_am);
    strcat(from_svr_buf," Server (Version ");
    strcat(from_svr_buf,VERSION);
    strcat(from_svr_buf,"   ");
    strcat(from_svr_buf,REVDATE);
    strcat(from_svr_buf,")\r\n");

    return(SVR_AUTH_STATE);			/* Expect HELO command next */
}

svr_authenticate(pop2_str)
    char *pop2_str;				/* Ptr to HELO command */
	/* This routine is called in response to a HELO command. The */
	/*   routine peforms usercode/password verification. If the  */
	/*   usercode/password are invalid, the routine returns EXIT */
	/*   state. If the usercode/password are valid, the routine  */
	/*   loads the default system mailbox for the user and       */
	/*   returns MBOX state to indicate the user can now ask for */
	/*   message byte counts or another folder.		     */
{
    char buf[16];				/* Temp char storage */

    pop2_str += 4;				/* Skip past HELO verb */

	/* Ask for system verification of usercode/password */
    user_name = user_ok(pop2_str);
    if (user_name == NULL) {			/* Usercode valid? */
						/* No, tell client NO */
	return(svr_garbage("invalid usercode or password in HELO command"));

    } else {					/* Yes, usercode is valid */
	fld_orig = get_user_mbox(user_name);	/* Get name of system mailbox */

	fld_cnt = fld_select(fld_orig);		/* Load system mailbox */
	sprintf(buf,"%d",fld_cnt);		/* Convert count to string */

	    /* Construct a message to tell client how many messages */
	    /*   are in the system mailbox.			    */
	strcpy(from_svr_buf,"#");
	strcat(from_svr_buf,buf);
	strcat(from_svr_buf," messages ready for ");
	strcat(from_svr_buf,user_name);
	strcat(from_svr_buf,"\r\n");

	return(SVR_MBOX_STATE);			/* Return, user can now READ */
    }
}

svr_shutdown()
	/* This routine provides for a normal shutdown of the POP2 server */
	/*   It is called in response to a QUIT command from the client.  */
{
    fld_release();				/* Release open folder */

    strcpy(from_svr_buf,"+ Bye POP2 ");		/* Construct signoff msg */
    strcat(from_svr_buf,host_i_am);
    strcat(from_svr_buf," Server (Version ");
    strcat(from_svr_buf,VERSION);
    strcat(from_svr_buf,") shutdown\r\n");

    return(SVR_DONE_STATE);			/* Return EXIT state */
}

svr_folder(pop2_str)
    char *pop2_str;				/* Ptr to FOLD command */
	/* This routine is called in response to a client FOLD command. */
	/*   The procedure attempts to parse a folder name from the     */
	/*   command line. If the parse is successfull, the routine     */
	/*   attempts to load the folder. After a successfull FOLD      */
	/*   command, the user may issue READ or FOLD commands.		*/
{
    char buf[80];				/* Temp char storage */
    char *pt,*pt2;				/* Temp pointers */

    fld_release();				/* Release open folder */

    pop2_str += 4;				/* Skip past FOLD verb */
						/* Skip LWSP before fold name */
    while (isspace(*pop2_str) && (*pop2_str != NULL_CHAR)) { ++pop2_str; }
    if (*pop2_str == NULL_CHAR) {		/* Anything left? */
						/* Ooops, no foldername given */
	return(svr_garbage("mailbox name missing from FOLD command"));
    }

    pt = pop2_str;				/* Save ptr to start of name */
						/* Skip to end of folder name */
    while ((!isspace(*pop2_str)) && (*pop2_str != NULL_CHAR)) {
	if (*pop2_str == BACKSL_CHAR) {		/* Quoting in folder name? */
						/* Yes, decode quote */
	    pt2 = pop2_str;			/* Use a temp pointer here */
	    while (*pt2 != NULL_CHAR) {		/* Move all chars over 1 slot */
		*pt2++ = *(pt2+1);
	    }
	}
	++pop2_str;				/* Incr to next slot */
    }
    *pop2_str = NULL_CHAR;			/* Discard LWSP after name */

						/* Get memory to save name */
    fld_orig = malloc(strlen(pt) + 1);
    if (fld_orig == NULL) fail(FAIL_OUT_OF_MEMORY);
    strcpy(fld_orig,pt);			/* Save original mailbox name */

    fld_cnt = fld_select(fld_orig);		/* Attempt to load folder */
    sprintf(buf,"%d",fld_cnt);			/* Convert integer to string */

	/* Construct a message to tell client how many messages */
	/*   are in the requested mailbox file.			*/
    strcpy(from_svr_buf,"#");
    strcat(from_svr_buf,buf);
    strcat(from_svr_buf," messages ready in ");
    strcat(from_svr_buf,fld_orig);
    strcat(from_svr_buf,"\r\n");

    return(SVR_MBOX_STATE);			/* Return, user can now READ */
}

svr_host_folder(pop2_str)
    char *pop2_str;				/* Ptr to HOST command */
	/* This routine is called in response to a client HOST command. */
	/*   The procedure attempts to parse a host name from the       */
	/*   command line. If the parse is successfull, the routine     */
	/*   attempts to load the messages currently avaliable in the   */
	/*   system /usr/spool directory for POP2 hosts. After a HOST   */
	/*   folder is loaded, the user may use READ or FOLD commands.	*/
{
    char buf[80];				/* Temp char storage */
    char *pt,*pt2;				/* Temp pointers */

    fld_release();				/* Release open folder */

    pop2_str += 4;				/* Skip past HOST verb */
						/* Skip LWSP before fold name */
    while (isspace(*pop2_str) && (*pop2_str != NULL_CHAR)) { ++pop2_str; }
    if (*pop2_str == NULL_CHAR) {		/* Anything left? */
						/* Ooops, no hostname given */
	return(svr_garbage("host name missing from HOST command"));
    }

    pt = pop2_str;				/* Save ptr to start of name */
						/* Skip to end of host name */
    while ((!isspace(*pop2_str)) && (*pop2_str != NULL_CHAR)) {
	++pop2_str;				/* Incr to next slot */
    }
    *pop2_str = NULL_CHAR;			/* Discard LWSP after name */

    host_client = malloc(strlen(pt) + 1);	/* Save name for HOST POP2 */
    if (host_client == NULL) fail(FAIL_OUT_OF_MEMORY);
    strcpy(host_client,pt);

    fld_orig = get_host_mbox(host_client);	/* Get name of host mailbox */

    fld_cnt = fld_select(fld_orig);		/* Attempt to load folder */
    sprintf(buf,"%d",fld_cnt);			/* Convert integer to string */

	/* Construct a message to tell client how many messages */
	/*   are in the requested mailbox file.			*/
    strcpy(from_svr_buf,"#");
    strcat(from_svr_buf,buf);
    strcat(from_svr_buf," messages ready for ");
    strcat(from_svr_buf,host_client);
    strcat(from_svr_buf,"\r\n");

    return(SVR_MBOX_STATE);			/* Return, user can now READ */
}

svr_read(pop2_str)
    char *pop2_str;				/* Ptr to READ command */
	/* This routine is called in response to a READ command */
	/*    The routine reports to the client how many bytes  */
	/*    are in the current message. If a number follows   */
	/*    the READ verb on the command line, the current    */
	/*    message pointer is set to that message number.    */
	/*    After a READ command, the user may READ, RETR or  */
	/*    FOLD; return ITEM state.				*/
{
    int i;					/* Temp integer storage */

    pop2_str += 4;				/* Skip past READ verb */
						/* Skip LWSP after verb */
    while (isspace(*pop2_str) && (*pop2_str != NULL_CHAR)) { ++pop2_str; }

    if (*pop2_str == NULL_CHAR) {		/* Anything left? */
						/* No, msg# not given */
						/* Leave current ptr as is */
	if (fld_current == UNDEFINED) {		/* Msg pointer off scale? */
	    if (fld_cnt > 0) {			/* Yes, any msgs in folder? */
	        fld_current = 0;		/* Yes, reset msg pointer */
	    }
	}

    } else {					/* Yes, msg# on command line */
	i = 0;					/* Init integer storage */
	if (sscanf(pop2_str,"%d",&i)) {		/* Can scan an integer? */
						/* Yes, set current msg ptr */
	    --i;				/* Make index 0-based */
	    if ((i < 0) || (i >= fld_cnt)) {	/* Is given index in range */
		fld_current = UNDEFINED;	/* No, message does not exist */
	    } else {
	        fld_current = i;		/* Yes, set current msg ptr */
	    }
	} else {				/* No, couldn't scan int */
	    fld_current = UNDEFINED;		/* Indicate msg doesn't exist */
	}
    }

	/* Call a routine to construct a msg reporting to the client */
	/*   the number of bytes in the current message. Return the  */
	/*   state returned by svr_curr_bcount().		     */
    return(svr_curr_bcount());
}

svr_curr_bcount()
	/* This routine constructs a message reporting to the client */
	/*   the number of bytes in the current message. The info    */
	/*   is drawn from the message structure array. If the       */
	/*   current msg index is off the array, indicate 0 bytes    */
	/*   available in the message. A user may not RETR a message */
	/*   with 0 bytes. Return ITEM state.			     */
{
    char buf[80];				/* Temp char storage */

	/* Construct a reply reporting the # of bytes in the current message */
    strcpy(from_svr_buf,"=");			/* Byte count reply char */

    if (fld_current == UNDEFINED) {		/* Current msg exists? */
	strcat(from_svr_buf,"0");		/* No, report 0 bytes */
	strcat(from_svr_buf,"\r\n");
	return(SVR_ITEM_STATE);			/* Return, but user cant RETR */
    }
						/* Current msg deleted? */
    if (fld_msg[fld_current].status & STAT_DELETED) {
	strcat(from_svr_buf,"0");		/* Yes, report 0 bytes */
						/* User cant RETR */

    } else {					/* No, msg not deleted yet */
						/* Report actual byte count */
        sprintf(buf,"%ld",fld_msg[fld_current].bcount);
        strcat(from_svr_buf,buf);
    }

    sprintf(buf,"%d",(fld_current+1));		/* Indicate msg count is for */
    strcat(from_svr_buf," bytes in message ");
    strcat(from_svr_buf,buf);
    strcat(from_svr_buf,"\r\n");

    return(SVR_ITEM_STATE);			/* Return ITEM state */
}

svr_sendfile()
	/* This routine is called in response to a RETR command. */
	/*   If the current message does not exist, has zero     */
	/*   length, or has been deleted, send an error message  */
	/*   and shutdown the server; otherwise, send the msg    */
	/*   data to the client. After the msg data is sent, the */
	/*   client must ACKS, ACKD or NACK.			 */
{
    int i;					/* Temp index counter */
    int r;					/* Temp integer counter */
    long bcount;				/* Temp counter */
    char *pt;					/* Temp pointer */

						/* Current msg exists? */
    if ((fld_current < 0) && (fld_current >= fld_cnt)) {
						/* No, shutdown server */
	return(svr_garbage("attempt to RETR non-existant message"));
    }

						/* 0 byte count or deleted? */
    if ((fld_msg[fld_current].bcount == 0)
	    || (fld_msg[fld_current].status & STAT_DELETED)) {
						/* Yes, shutdown server */
	return(svr_garbage("attempt to RETR message with zero length"));
    }


	/* Message exists, has non-zero byte count and is not deleted. */
	/*   Sent message data to the client.			       */
						/* Locate start of message */
    r = fseek(fld_fp, fld_msg[fld_current].fmsg_entry, FSEEK_BEGIN);
    if (r == UNDEFINED) fail(FAIL_FILE_IO_ERROR);

    bcount = 0;					/* Zero check counter */
    from_svr_buf[0] = NULL_CHAR;		/* Prepare for strcat() */

						/* Send RCPT header to client */
    strcat(from_svr_buf,fld_msg[fld_current].pop_hdr);
    bcount += strlen(from_svr_buf);

	/* Send each line of msg text to the client. Limit each text */
	/*   line to 1000 chars including CRLF terminator.	     */
    while (fgetl(rfc_buf,POP2_BUFSIZ,fld_fp) != NULL) {
	if (host_client == NULL) {		/* Reading a HOST folder? */
						/* No, it is a FOLD folder */
		/* FOLD folders are delimited by UNIX-style FromSPACE */
		/*   FromSPACE within the message has already been    */
		/*   escaped, we won't unescape it though.	      */
	    if (ismsg_start(rfc_buf)) break;	/* Break at start of next msg */
	    pt = rfc_buf;			/* Always send entire buffer */
	} else {
						/* Yes, it it a HOST folder */
		/* HOST folders are delimited by BSMTP commands. The */
		/*   BSMTP end-of-message signal is <CRLF>.<CRLF>;   */
		/*   DOT lines within the message have been escaped, */
		/*   and should be unescaped before sending the msg. */
	    if (isdot(rfc_buf)) break;		/* Break at end of message */
	    if (rfc_buf[0] == DOT_CHAR) {	/* Does line begin with DOT? */
		pt = (rfc_buf+1);		/* Yes, strip escape-DOT */
	    } else {
		pt =  rfc_buf;			/* No, not prefixed with DOT */
	    }
	}

	i = strlen(pt) - 1;			/* Change LF terminator to */
	pt[i++] = CR_CHAR;			/*   CRLF to conform with  */
	pt[i++] = LF_CHAR;			/*   POP2 convention       */
	pt[i  ] = NULL_CHAR;

	bcount += strlen(pt);			/* Incr check counter */

	    /* Buffer up 1K of data before transmitting to the client */
						/* Buf full enough to send? */
	if ((strlen(from_svr_buf) + strlen(rfc_buf)) < POP2_BUFSIZ) {
	     strcat(from_svr_buf,pt);		/* No, add text line to buf */

	} else {				/* Yes, can't add to buffer */
	    svr_data_out(from_svr_buf);		/* Send buffer to client */
	    strcpy(from_svr_buf,pt);		/* Start buffer again */
	}
    }
    if (ferror(fld_fp)) fail(FAIL_FILE_IO_ERROR);

	/* Make sure # of bytes loaded from message still matches the */
	/*   count determined at load time.			      */
						/* Count matches previous? */
    if (bcount != fld_msg[fld_current].bcount) {
	fail(FAIL_SERVER_LOST);			/* No, something is wrong */
    }

	/* The from_svr_buf still contains the last buffer of data. */
	/*   return to caller and send this last buffer.            */
    return(SVR_NEXT_STATE);			/* Return NEXT msg state */
}

svr_chk_ack(pop2_str)
    char *pop2_str;				/* Ptr to ACKS/ACKD/NACK cmd */
	/* This routine handles the client response to a message data */
	/*   send. If the client responds ACKS, the current message   */
	/*   is saved and the current msg counter is incremented. If  */
	/*   the client responds ACKD, the current msg is deleted and */
	/*   the current msg counter is incremented. If the client    */
	/*   responds NACK, the current msg is saved, but the current */
	/*   msg counter is not incremented. The routine then reports */
	/*   to the client the byte count of the updated current msg  */
	/*   number. After the report is sent, the client may READ,   */
	/*   RETR or FOLD.					      */
{
    if (!strn_nocase(pop2_str,"ACK",3)) {	/* Msg recvd without errors? */
						/* Yes, client gave ACK */
	if (_toupper(pop2_str[3]) == 'D') {	/* Delete current msg */
						/* Yes, set delete status bit */
	    fld_msg[fld_current].status |= STAT_DELETED;
	}
						/* Incr current msg counter */
	if (++fld_current >= fld_cnt) {		/* New index out of range? */
	    fld_current = UNDEFINED;		/* Yes, set index to indicate */
	}

    } else {					/* No, client did not ACK */
	if (strn_nocase(pop2_str,"NACK",4)) {	/* Msg recvd but with errors? */
						/* No, client send garbage */
	    return(svr_garbage("unexpected response to data send"));
	}
						/* Yes, client sent NACK */
						/* Leave all as is */
    }
	    
    return(svr_curr_bcount());			/* Return count of curr msg */
}

/*************************** SERVER STATE DRIVER ***************************/

servers_turn(str)
    char *str;					/* Ptr to client command str */
	/* This procedure is the server state machine driver. The routine */
	/*   processes commands from the client. A ptr to the client      */
	/*   command is passed in as str. The routine uses a two-D array  */
	/*   to determine action on the client's command. Only a subset   */
	/*   of commands are available to the client for each server      */
	/*   state. The QUIT command is available to the client in all    */
	/*   states. The decision array crosses the index of the client   */
	/*   command with the index of the current server state to        */
	/*   determine the proper action routine to call. The procedure   */
	/*   returns the new server state (determined by the action       */
	/*   routine) to its caller.					  */
{
    int comm_ndx,				/* Client command number */
	act_num,				/* Server action routine # */
	new_state;				/* Server state after action */

    comm_ndx = isin_carray(str,svr_input);	/* Parse command verb */
    if (comm_ndx == UNDEFINED) {		/* Valid command verb? */
	comm_ndx = SVR_UNKN_INPUT;		/* No, client sent garbage */
    }
    act_num = svr_table[comm_ndx][svr_state];	/* Get action # from table */

    switch(act_num) {				/* Call proper action routine */

 case SVR_GARB_ACTION:				/* Client sent garbage or inv */
						/*   cmd for server state     */
	new_state = svr_garbage("unexpected input from client");
	break;
 case SVR_GREE_ACTION:				/* Client openned connection */
	new_state = svr_greeting();
	break;
 case SVR_AUTH_ACTION:				/* Client sent HELO command */
	new_state = svr_authenticate(str);
	break;
 case SVR_BBYE_ACTION:				/* Client sent QUIT command */
	new_state = svr_shutdown();
	break;
 case SVR_MCNT_ACTION:				/* Client sent FOLD command */
	new_state = svr_folder(str);
	break;
 case SVR_HOST_ACTION:				/* Client sent HOST command */
	new_state = svr_host_folder(str);
	break;
 case SVR_BCNT_ACTION:				/* Client sent READ command */
	new_state = svr_read(str);
	break;
 case SVR_DATA_ACTION:				/* Client sent RETR command */
	new_state = svr_sendfile();
	break;
 case SVR_AKNK_ACTION:				/* Client ACKS,ACKD or NACK */
	new_state = svr_chk_ack(str);
	break;
 default:
	fail(FAIL_SERVER_LOST);
	break;
    }

#ifdef DEBUG
    trace("SVR",svr_state,from_svr_buf);	/* Trace server replies */
#endif

    return(new_state);				/* Return new server state */
}
