/*
 *	pop3d		- IP/TCP/POP3 server for UNIX 4.3BSD
 *			  Post Office Protocol - Version 3 (RFC1225)
 *
 *      (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.
 *
 *	Katie Stevens
 *	dkstevens@ucdavis.edu
 * 	Information Technology -- Technology Support Program
 *	University of California, Davis
 *
 **************************************
 *
 *	main.c
 *
 *	REVISIONS:
 *		02-27-90 [ks]	original implementation
 *	1.000	03-04-90 [ks]
 *	1.001	06-24-90 [ks]	implement optional TOP command
 *	1.002	07-22-91 [ks]	-- reset index counter after folder rewind
 *				   in fld_release (Thanks to John Briggs,
 *				   vaxs09@vitro.com, Vitro Corporation,
 *				   Silver Spring, MD for finding this bug!)
 *				-- set umask() value explicitly (Thanks to
 *				   Vikas Aggarwal, JvNCnet, Princeton, NJ
 *				   for suggesting this)
 *	1.003	03-92    [ks]   close mailbox before return from main()
 *	1.004   11-13-91 [ks]	leave original mailbox intact during POP
 *				session (Thanks to Dave Cooley,
 *				dwcooley@colby.edu for suggesting this)
 *		08-93	 [ks]	Thanks to Kurt Jordan, Purdue Calumet
 *				for the port to SVR3.2
 *	1.005	06-17-94 [ks]	port to SVR4/ANSIC; fold BSD, SVR3.2, SVR4
 *				into one source ( Thanks to Mark Horstman,
 *				Southwester Bell Telephone for this work )
 *	1.006	07-20-94 [ks]   add hooks for syslog tracking of POP
 *                              sessions; conditional compilation ( Thanks
 *				to David Wong, UC Davis for this work )
 *	1.007	07-29-94 [ks]	add '\r' to POP server greeting string
 *	1.008	03-23-95 [ks]	check for occupied lock on mailbox
 *      1.009   10-26-95 [ks]   remove fld_write_ok flag; thanks to
 *                              Craig D. von Land for pointing out a bug
 *      1.010   06-07-96 [ks]   -- added AIX define conditional compilation
 *                              flag to open in r+ mode before file lock.
 *                              Thanks to Danny Cohen for this suggestion.
 *                              -- moved HOST and MBOX extension commands
 *                              and routines to conditional compilation
 *                              status HOST_EXT; no longer used at UC Davis
 *                              -- corrected response to TOP n 0 command. 
 *                              Thanks to Stephen R. van den Berg.
 *                              -- add UIDL command, under conditional
 *                              compilation flag UIDL_OKAY ( FromSP only ).
 *                              Thanks to Henry Holtzman for his patches.
 *	1.012  03-28-97 [ks]    -- add NO_MULTIPLE conditional compilation
 *				code to disallow multiple simultaneous
 *				POP3 sessions on a single mailbox.
 *      1.013  07-17-97 [ks]    -- correct #ifdef condition for shadow.h
 *                                 #include in util.c; thanks to Daniel Roche
 *                              -- add LOCK_EX flag to flock() call in
 *                                 folder.c; thanks to Shao Hua for this fix
 *      1.014  02-11-98 [ks]    -- added patches provided by Blaise Camp.
 *                              Create lockfile only if mailbox is non-zero
 *                              size.
 *      1.015  02-19-98 [ks]    -- added patches provided by Blaise Camp.
 *                              Add an expiration time on the lockfiles.
 *	1.016  02-23-98 [ks]	-- added signal handler for SIGPIPE.
 *				Thanks to Blaise Camp for his suggestion.
 */

#include <stdio.h>
#include <signal.h>
#include <ctype.h>
#include <string.h>	/* [1.015] */
#include <unistd.h>	/* [1.015] */
#ifdef SYSV
#include <stropts.h>
#endif /* SYSV */
#include <sys/param.h>
#ifdef TLI
#include <sys/ioctl.h>
#endif /* TLI */
#ifdef SVR4
#include <netdb.h>
#include <stdlib.h>
#else
/* Added this #else because I found that an old SunOS box needed it, and it's
   not SVR4.  I'll clean this up later, hopefully.
*/
#include <netdb.h>
#endif /* SVR4 */
#include "pop3.h"

#ifdef STANDALONE
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <strings.h>
#endif /* STANDALONE */

#ifdef LOG_SESSIONS
#include <syslog.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define FACILITY	LOG_LOCAL3
int retr_count = 0;     /* Keep track of # of messages & bytes retrieved. */
long byte_count = 0;
#endif

#define VERSION		"1.020i"
#define REVDATE		"October, 1998"

char *svr_hostname;				/* Hostname of POP3 server */
char svr_buf[SVR_BUFSIZ+2];			/* Buffer for server I/O */
char cli_user[CLI_BUFSIZ] = "0";		/* Save client username */

static char *svr_invalid = "-ERR Invalid command; valid commands:";

#ifdef DEBUG
FILE *logfp = NULL;				/* File for recording session */
#include <sys/types.h>
#include <dirent.h>
#endif


#ifdef UCD  	/* NIS lookups to help sort out POP users */
#include <rpcsvc/ypclnt.h>
#include <rpcsvc/yp_prot.h>
#include <errno.h>
char new_mbox[64];
int nis_look(char *, char *);  /* Do an NIS lookup to see if the user
				  is on the right pop server. */
char *create_msg(char *, char *);  /* Construct a message to tell a user
				      that they're on the wrong server. */
#endif 		/* UCD */


/* [1.012] Disallow multiple simultaneous POP3 sessions on single mailbox */
#ifdef NO_MULTIPLE
char lockfile[128];
int remove_lockfile = 0;
int test_lock();	/* [1.015] */
#include <sys/types.h>	/* [1.015] */
#include <sys/stat.h>	/* [1.015] */
#include <time.h>	/* [1.015] To see if lockfile has expired */
#endif

#ifdef QMAIL
char cli_dir[CLI_DIRSIZ];     /* Save client home dir */
#endif /* QMAIL */


/* In util.c */
extern char flash_buf[];

/* Routines in this file */
#ifdef ANSIC
static void initialize(void);
static void svr_timeout(int);
static void int_hangup(int);
static void int_progerr(int);
void print_commands(void);

static int svr_auth(int, char *);
static int svr_pass(int, char *);
static int svr_trans(int, char *);
static int svr_fold(int, char *);
static int svr_shutdown(void);
#else /* !ANSIC */
static void initialize();
static void svr_timeout();
static void int_hangup();
static void int_progerr();

static int svr_auth();
static int svr_pass();
static int svr_trans();
static int svr_fold();
static int svr_shutdown();
#endif /* ANSIC */

/**************************************************************************/
/**************************************************************************/

/* Initialize POP3 server */
static void
#ifdef ANSIC
initialize(void)
#else /* !ANSIC */
initialize()
#endif /* ANSIC */
{
    char buf[MAXHOSTNAMELEN+1];
    /* File permissions for owner only */
    umask(077);		/* [1.002] */

#ifdef DEBUG
    /* Prepare log file */
    logfp = fopen(LOGFILE,"a");
    if (!logfp) {
        fprintf (stderr, "Couldn't open logfile\n");
        exit (-1);
    }
    fprintf(logfp,"POP3 server startup; version %s (%s)\n",
	    VERSION,REVDATE);
#endif
#ifdef TLI
    if (ioctl(fileno(stdin), I_PUSH, "tirdwr") == -1 ) {
        perror("ioctl");
        exit(7);
    }
#endif /* TLI */
 
    /* Get our hostname */
    gethostname(buf,MAXHOSTNAMELEN);
    svr_hostname = (char *)malloc(strlen(buf) + 1);
    if (svr_hostname == NULL)
    	    fail(FAIL_OUT_OF_MEMORY, 0);
    strcpy(svr_hostname,buf);

    /* Handle process signals ourself */
    signal(SIGALRM, svr_timeout);		/* timer expiration */

    signal(SIGHUP, int_hangup);		/* socket signals */ 
    signal(SIGURG, int_hangup); 
    signal(SIGTERM, int_hangup); 
    signal(SIGPIPE, int_hangup);		/* [1.016] */ 
    signal(SIGXCPU, int_hangup); 
    signal(SIGINT, int_hangup); 
#ifdef DEBUG
    signal(SIGABRT, int_hangup); 
    signal(SIGFPE, int_hangup); 
#ifdef undef
    signal(SIGSYS, int_hangup); 
    signal(SIGEMT, int_hangup); 
#endif /* undef */
    signal(SIGQUIT, int_hangup); 
    signal(SIGXFSZ, int_hangup); 
    signal(SIGTRAP, int_hangup); 
    signal(SIGSEGV, int_progerr); 
#endif /* DEBUG */
    signal(SIGBUS, int_progerr);		/* fatal program errors */ 
    signal(SIGILL, int_progerr); 
    signal(SIGIOT, int_progerr); 
}

#define ignore_sigs() \
{ \
    signal(SIGHUP, (void *)SIG_IGN);		/* socket signals */ \
    signal(SIGURG, (void *)SIG_IGN); \
    signal(SIGTERM, (void *)SIG_IGN); \
    signal(SIGPIPE, (void *)SIG_IGN); \
    signal(SIGXCPU, (void *)SIG_IGN); \
    signal(SIGINT, (void *)SIG_IGN); \
}

#ifdef STANDALONE

void standalone_hangup(int sig)
{
    fprintf (stderr, "pop3d standalone: FATAL ERROR: exited with signal %d\n", 
		    sig);
    exit(1);
}

static void
catch_sigchld(int sig) 
{
    while (waitpid( -1, NULL, WNOHANG ) > 0 ) ;
    signal(SIGCHLD,catch_sigchld);      /* Reset to get it again. */
}

void standalone_initialize()
{
    ignore_sigs();
    
#ifdef DEBUG
    signal(SIGABRT, standalone_hangup);
    signal(SIGFPE, standalone_hangup);
    signal(SIGQUIT, standalone_hangup);
    signal(SIGXFSZ, standalone_hangup);
    signal(SIGTRAP, standalone_hangup);
    signal(SIGSEGV, standalone_hangup);
#endif

    signal(SIGBUS, standalone_hangup);		/* fatal program errors */
    signal(SIGILL, standalone_hangup);
    signal(SIGIOT, standalone_hangup);
}


#endif /* STANDALONE */


/* Timeout while waiting for next client command */
static void
#ifdef ANSIC
svr_timeout(int sig)
#else /* !ANSIC */
svr_timeout(sig)
int sig;
#endif /* ANSIC */
{
    ignore_sigs();
    fld_release();				/* Release mailbox folder */
    fail(FAIL_LOST_CLIENT, sig);		/* Exit POP3 server */
}
/* Timeout while waiting for next client command */
static void
#ifdef ANSIC
int_hangup(int sig)
#else /* !ANSIC */
int_hangup(sig)
int sig;
#endif /* ANSIC */
{
#ifdef DEBUG
	fprintf(logfp, "%s: int_hangup, signal: %d\n", cli_user, sig);
#endif
    ignore_sigs();
	fld_release();				/* Release mailbox folder */
	fail(FAIL_HANGUP, sig); 		/* Exit POP3 server */
}
/* Timeout while waiting for next client command */
static void
#ifdef ANSIC
int_progerr(int sig)
#else /* !ANSIC */
int_progerr(sig)
int sig;
#endif /* ANSIC */
{
#ifdef DEBUG
	fprintf(logfp, "%s: int_progerr, signal: %d\n", cli_user,sig);
#endif
    ignore_sigs();
    fld_release();				/* Release mailbox folder */
    fail(FAIL_PROGERR, sig);		/* Exit POP3 server */
}

/**************************************************************************/

/* Server Authentification State; process client USER command */
static int
#ifdef ANSIC
svr_auth(int state, char *inbuf)
#else /* !ANSIC */
svr_auth(state,inbuf)
int state;
char *inbuf;
#endif /* ANSIC */
{
	if (strncmp(inbuf,"quit",4) == 0)
		return(svr_shutdown());
	/* Expecting USER command */
	if (strncmp(inbuf,"user",4) == 0) {
		inbuf += 4;
		EATSPACE(inbuf);
		strcpy(cli_user,inbuf);
		strcpy(svr_buf,"+OK please send PASS command\r\n");
		state = SVR_PASS_STATE;
	} else {
		sprintf(svr_buf,"%s  USER,  QUIT\r\n",svr_invalid);
	}
	return(state);
}

/* Server Password State; process client PASS command */
static int
#ifdef ANSIC
svr_pass(int state, char *inbuf)
#else /* !ANSIC */
svr_pass(state,inbuf)
int state;
char *inbuf;
#endif /* ANSIC */
{
#ifdef notdef
	int cnt;	/* [1.015] No longer needed */
#endif

#ifdef LOG_SESSIONS
	struct sockaddr_in cs;		/*  Communication parameters */
	int len;
	int sp = 0;
	char *peername = NULL;
#ifdef RESOLVE_IPS
	struct hostent *ch;
#endif /* RESOLVE_IPS */
#endif /* LOG_SESSIONS */
#ifdef UCD
	struct stat mbox_stat;
	char right_server[90],
	     diff_server;
#endif /* UCD */

	if (strncmp(inbuf,"quit",4) == 0)
		return(svr_shutdown());
	/* Expecting PASS command */
	if (strncmp(inbuf,"pass",4) != 0) {
		sprintf(svr_buf,"%s  PASS,  QUIT\r\n",svr_invalid);
		return(state);
	}
	/* Verify usercode/password pair */
	inbuf += 4;
	EATSPACE(inbuf);
	if (verify_user(cli_user,inbuf) == -1) {
#ifdef LOG_SESSIONS
            openlog( "pop3d", LOG_PID, FACILITY );
            syslog( LOG_INFO | FACILITY, "user %s: PASSWD FAILED", cli_user );
	    closelog();
#endif /* LOG_SESSIONS */
	    strcpy(svr_buf,"-ERR invalid usercode or password, please try again\r\n");
	    return(SVR_AUTH_STATE);
	}

/* [1.012] Disallow multiple simultaneous POP3 sessions on single mailbox */
#ifdef NO_MULTIPLE
        if (test_lock() == SVR_AUTH_STATE) {	/* [1.015] Lockfile test */
            return(SVR_AUTH_STATE);
        }
	/* [1.014] Lock creation moved to folder.c */
#endif /* NO_MULTIPLE */

#ifdef UCD
	/* Check here to see if the person is trying to pop off the wrong
	   server.  diff_server will be 1 if it's the wrong server.
	*/
	new_mbox[0] = '\0';
	if ((diff_server = nis_look(cli_user, right_server)) == 1) {
	    /* Got the wrong server, send a message telling
	       so.
	       new_mbox will contain the name of the new (temp) mailbox to
	       use to send the person the message.
	    */
	    strcpy(new_mbox, create_msg(cli_user, right_server));
	}
#endif

#ifdef LOG_SESSIONS
	len = sizeof(cs);
	if (getpeername(sp,(struct sockaddr *)&cs,&len) < 0) {
	    peername = "unresolved";
	}
	else {
#ifdef RESOLVE_IPS
	    ch = gethostbyaddr((char *) &cs.sin_addr, 
		       sizeof(cs.sin_addr), AF_INET);
	    if(ch == NULL) {
		peername = (char *)inet_ntoa(cs.sin_addr);
	    }
	    else {
		peername = ch->h_name;
	    }
	    if (peername == NULL) {
		peername = "unresolved";
	    }
#else /* Don't RESOLVE_IPS */
	    peername = (char *)inet_ntoa(cs.sin_addr);
	    if (peername == NULL) {
	        printf("inet_ntoa failed\n");
		peername = "unresolved";
	    }
#endif /* RESOLVE_IPS */
	}
	openlog( "pop3", LOG_PID, FACILITY );
	/* name of the user, and the calling client name logged. */
	syslog( LOG_INFO | FACILITY, "%s: client %s", cli_user, peername);
	closelog();
#endif
#ifdef QMAIL
	strcpy(svr_buf,cli_dir);
	strcat(svr_buf,DEF_MBOX_NAME);
#else /* !QMAIL */
	strcpy(svr_buf,DEF_MAIL_DIR);
	strcat(svr_buf,cli_user);
#endif /* QMAIL */
#ifdef UCD	/* UCD specific NIS lookups */
	if (diff_server == 1) {	/* If wrong server */
	    if (((stat(svr_buf, &mbox_stat) == -1) && (errno == ENOENT)) ||
			(mbox_stat.st_size == 0)) {
	    	if(new_mbox != NULL) {
			strcpy(svr_buf, new_mbox);
	    	}
	    }
	}
#endif /* UCD */
	return(fld_fromsp(svr_buf));
}

#ifdef NO_MULTIPLE	/* [1.015] Lockfile integrity test */
int test_lock()
{
    struct stat lock_stat; /* To determine if lockfile is expired */
    time_t curr_time;


    sprintf(lockfile, "%s%s", POP3_NOMULTIPLE_STUB, cli_user);
    if(stat(lockfile, &lock_stat) == 0) { /* The file exists */
#ifdef DEBUG
	DIR *dirp;
	struct dirent *dir_info;
	dirp = opendir("/var/tmp");
	if (dirp) {
	    while ((dir_info = readdir(dirp))) {
		if (strncmp(dir_info.d_name, "pop_", 4) == 0) {
		    fprintf(logfp,"%s: u - file %s\n",cli_user,dir_info->d_name);
		}
	    }
	}
#endif
	if (lock_stat.st_uid != getuid()) {
	    return(0);
	}
        time(&curr_time);
        if(curr_time <= lock_stat.st_ctime + LOCK_EXPIRE_TIME) {
            strcpy(svr_buf, "-ERR You already have an active pop session.\r\n");
            remove_lockfile = 0;
#ifdef DEBUG
            fprintf(logfp, 
		"%s: POP3 already active for mailbox; lockfile not created\n",
		cli_user);
#endif /* DEBUG */
            return(SVR_AUTH_STATE);  /* Don't allow validation, since
                                        there may already be another 
                                        session going. */
        }
    }
#ifdef DEBUG
    fprintf(logfp, 
	"%s: No other POP3 sessions active on mailbox; will create lockfile\n",
	cli_user);
#endif /* DEBUG */
    remove_lockfile = 1;
    return(0);
}
#endif

/* Server Transaction State; process client mailbox command */
static int
#ifdef ANSIC
svr_trans(int state, char *inbuf)
#else /* !ANSIC */
svr_trans(state,inbuf)
int state;
char *inbuf;
#endif /* ANSIC */
{
	int msgnum;

	if (strncmp(inbuf,"quit",4) == 0)
		return(svr_shutdown());
	/* Expecting mailbox command */
	if (strncmp(inbuf,"dele",4) == 0) {
		inbuf += 4;
		EATSPACE(inbuf);
		if (*inbuf == NULL_CHAR)
			sprintf(svr_buf,"-ERR message number required (e.g.  DELE 1)\r\n");
		else
			fld_delete(atoi(inbuf));
#ifdef HOST_EXT
	} else if (strncmp(inbuf,"host",4) == 0) {
		inbuf += 4;
		EATSPACE(inbuf);
		if (*inbuf == NULL_CHAR)
			sprintf(svr_buf,"-ERR must specify hostname\r\n");
		else
			state = fld_bsmtp(inbuf);
#endif
	} else if (strncmp(inbuf,"last",4) == 0) {
		fld_last();
	} else if (strncmp(inbuf,"list",4) == 0) {
		inbuf += 4;
		if (isalpha((int)*inbuf)) {
		    *inbuf = ' ';
		}
		EATSPACE(inbuf);
		if (*inbuf == NULL_CHAR)
			fld_list(-1);
		else
			fld_list(atoi(inbuf));
#ifdef HOST_EXT
	} else if (strncmp(inbuf,"mbox",4) == 0) {
		inbuf += 4;
		EATSPACE(inbuf);
		if (*inbuf == NULL_CHAR)
			sprintf(svr_buf,"-ERR must specify mailbox filename\r\n");
		else
			state = fld_fromsp(inbuf);
#endif
	} else if (strncmp(inbuf,"noop",4) == 0) {
		strcpy(svr_buf,"+OK\r\n");
	} else if (strncmp(inbuf,"retr",4) == 0) {
		inbuf += 4;
		EATSPACE(inbuf);
		if (*inbuf == NULL_CHAR) {
			sprintf(svr_buf,"-ERR message number required (e.g.  RETR 1)\r\n");
		} else
#ifdef LOG_SESSIONS
			retr_count += fld_retr(atoi(inbuf),-1);
#else
			fld_retr(atoi(inbuf),-1);
#endif
	} else if (strncmp(inbuf,"rset",4) == 0) {
		fld_reset();
	} else if (strncmp(inbuf,"stat",4) == 0) {
		fld_stat();
	} else if (strncmp(inbuf,"top",3) == 0) {
		inbuf += 3;
		EATSPACE(inbuf);
		if (*inbuf == NULL_CHAR) {
			sprintf(svr_buf,"-ERR message number and line count required (e.g.  TOP 1 7)\r\n");
		} else {
			msgnum = atoi(inbuf);
			while (!isspace((int)*inbuf)) ++inbuf;
			EATSPACE(inbuf);
			if (*inbuf == NULL_CHAR)
				sprintf(svr_buf,"-ERR line count required (e.g.  TOP 1 7)\r\n");
			else
#ifdef LOG_SESSIONS
				retr_count += fld_retr(msgnum,atoi(inbuf));
#else
				fld_retr(msgnum,atoi(inbuf));
#endif
		}
#ifdef UIDL_OKAY
	} else if (strncmp(inbuf,"uidl",4) == 0) {
		inbuf += 4;
		EATSPACE(inbuf);
		if (*inbuf == NULL_CHAR) {
		    fld_uidl( -1 );
		} else {
		    fld_uidl(atoi(inbuf));
		}
#endif
	} else {
		print_commands();
	}
	return(state);
}

void print_commands () 
{
    sprintf( flash_buf, "%s  DELE, ", svr_invalid );
#ifdef HOST_EXT /* Typo? Was: "MBOX_EXT" */
    strcat( flash_buf, "HOST, " );
#endif
    strcat( flash_buf, "LAST, LIST, " );
#ifdef MBOX_EXT
    strcat( flash_buf, "MBOX, " );
#endif
    strcat( flash_buf, "NOOP, RETR, RSET, STAT, TOP, " );
#ifdef UIDL_OKAY
    strcat( flash_buf, "UIDL " );
#endif
    strcat( flash_buf, "or QUIT\r\n" );
    strcpy( svr_buf, flash_buf );   
}


/* Server Folder State; need to open another folder */
static int
#ifdef ANSIC
svr_fold(int state, char *inbuf)
#else /* !ANSIC */
svr_fold(state,inbuf)
int state;
char *inbuf;
#endif /* ANSIC */
{
	if (strncmp(inbuf,"quit",4) == 0)
		return(svr_shutdown());
	if (strncmp(inbuf,"noop",4) == 0) {
		strcpy(svr_buf,"+OK\r\n");
#ifdef HOST_EXT
	} else if (strncmp(inbuf,"host",4) == 0) {
		inbuf += 4;
		EATSPACE(inbuf);
		state = fld_bsmtp(inbuf);
	} else if (strncmp(inbuf,"mbox",4) == 0) {
		inbuf += 4;
		EATSPACE(inbuf);
		state = fld_fromsp(inbuf);
#endif
	} else {
#ifdef HOST_EXT
		sprintf(svr_buf,"%s  HOST,  MBOX,  NOOP  or  QUIT\r\n",svr_invalid);
#else /* !HOST_EXT */
		sprintf(svr_buf,"%s  NOOP  or  QUIT\r\n",svr_invalid);
#endif
	}
	return(state);
}

/* Prepare to shutdown POP3 server */
static int
#ifdef ANSIC
svr_shutdown(void)
#else /* !ANSIC */
svr_shutdown()
#endif /* ANSIC */
{
    ignore_sigs();
    fld_release();
    sprintf(svr_buf,"+OK %s POP3 Server (Version %s) shutdown.\r\n",
		svr_hostname,VERSION);
    return(SVR_DONE_STATE);
}

/**************************************/

void
#ifdef ANSIC
svr_data_out( char *buf)
#else /* !ANSIC */
svr_data_out(buf)
char *buf;
#endif /* ANSIC */
{
	/* Send out response to client */
	alarm(SVR_TIMEOUT_SEND);
	fputs(buf,stdout);
#ifdef DEBUG
	/* 
	fprintf (logfp, "%s: %s", cli_user, buf);
	Wouldn't use this, except in a case where you *really* can't figure
	out what's wrong, since it pretty much logs *everything*.  It did
	help me figure out one obscure bug, but in general using this is
	a bad idea.
	*/

#endif
	fflush(stdout);
	alarm(0);
}

/**************************************************************************/


#ifdef UCD	/* UCD specific NIS lookups */

/* 
   This function takes in the name of the current user, and the name of the
   server that the user *should* be popping off of (we only get to this
   function if the user is popping off the wrong server).
   It creates a temp file which contains a mail message to the user, telling
   them that they're on the wrong pop server, and which one they should use
   instead.
*/
char * create_msg(char *pw_name, char *right_server) {
    FILE *one_msg;
    char tmp_name[50], *tmp;
    char *time_string;
    char *string = "wrong_server.XXXXXX";
    time_t gm_time;

    sprintf(tmp_name, "%s%s%s", TMP_DIR, pw_name, string);
    tmp = mktemp(tmp_name);
    if (tmp == NULL) {
	return(NULL);
    }
    one_msg = fopen(tmp, "w");
    if(!one_msg) {
	return(NULL);
    }


    gm_time = time((time_t) 0);
    time_string = ctime(&gm_time);
    /* Put some error checking here later... */

    fprintf(one_msg, "From ithelp@ucdavis.edu %s", time_string);

    fprintf(one_msg, "From: IT Help <ithelp@ucdavis.edu>\n");

    fprintf(one_msg, "Date: %s", time_string);

    fprintf(one_msg, "Message-Id: <wrong_server-%d@ucdavis.edu>\n", 
	(int)gm_time);

    fprintf(one_msg, "To: %s <%s@ucdavis.edu>\n", pw_name, pw_name);

    fprintf(one_msg, "Subject: Incorrect POP server settings\n\n");

    fprintf(one_msg, "Greetings,\n\nyou are seeing this message because ");
    fprintf(one_msg, "you tried retrieving your mail\noff the incorrect POP ");
    fprintf(one_msg, "server.  The POP server is the remote computer\nto ");
    fprintf(one_msg, 
	"which you must connect in order to download your messages.\n\n");
    fprintf(one_msg, 
	"You need to change your POP server settings to use\n\n\t%s\n\n",
	right_server);
    fprintf(one_msg, 
	"instead.  This can be accomplished in your preferences or setup ");
    fprintf(one_msg, 
	"menus.\nA more complete listing of your settings can be viewed at\n");
    fprintf(one_msg,
	"http://ir.ucdavis.edu/info/email/mailid.html\n\n\n");
    fprintf(one_msg,
	"\tYour Support Staff\n\n");
    fclose(one_msg);

    return(tmp);
}

/*
   Takes in the user's loginid, and a char pointer, which is assigned the 
   name of the server the user should be popping off of, if they are in fact
   using the wrong server.
   Returns an int, either 0 or 1, indicating whether the user is on the wrong
   server (1), or the right server (0).
*/
int nis_look(char *name, char *right_server) {
    char *domainname = NULL,
	 hostname[64],
	 name2[64],
	 *dot_ptr;
    char **outname,	/* Return value from yp_match. */ 
	 *amp_ptr;	/* Pointer to the "@" in the string ret by yp_match */
    int outvallen, keylen, result;


    result = yp_get_default_domain ((char **)&domainname);
    if (result != 0) {  /* Failure */
#ifdef DEBUG
/*	printf("Domain lookup failure\n"); */
#endif
	return(0);
    }

    keylen = strlen(name);
    name[keylen] = 0;
    	/* strlen(name) + 1 is used because we need the extra NULL tacked
	   on the end of _name_.  Some funky sendmail thing.
	*/
    result = yp_match(domainname, "mailids.byloginid",name,strlen(name) + 1, 
	(char **)&outname, &outvallen);

    if (result != 0) { /* Failure */
#ifdef DEBUG
/*	fprintf(stderr, "yp_match failure #: %d\n", result);
	perror("nis lookup failure");
*/
#endif
	return (0);
    }

    /* If we're here, NIS lookup succeeded. */
    if((gethostname(hostname, sizeof(hostname))) != 0) {
#ifdef DEBUG
/*	printf("gethostname lookup failure\n"); */
#endif
	return (0);
    }
    if (strstr(hostname, "pop1")) {
	strcpy(hostname, "blue");
    }
    else if (strstr(hostname, "pop2")) {
	strcpy(hostname, "green");
    }
    else if (strstr(hostname, "pop3")) {
	strcpy(hostname, "scarlet");
    }
    else if (strstr(hostname, "sol")) {
	strcpy(hostname, "mailbox");
    }
    amp_ptr = strchr((char *)outname, '@');

    if (amp_ptr == NULL) {
	return(0);
    }

    if((dot_ptr = strchr(amp_ptr, '.')) == NULL) {
	return(0);
    }

    /* Name2 will be the short name of the person's pop host.  I.e., blue, if
	the person's pop host is blue.ucdavis.edu.
       hostname will be the short name of the host that the person is connected
	to, hopefully the same as their assigned pop host.
    */
    strncpy(name2, amp_ptr + 1, dot_ptr - (amp_ptr + 1));
    name2[dot_ptr - (amp_ptr + 1)] = 0;

    /* If you're on the same host as mailid points to, then you're ok.  This
	covers people who pop off of scarlet, blue, green, as well as Dave,
	who gets his mail on pandora.
    */
    if(strcmp(name2, hostname) == 0) {
	return(0);
    }

    /* This will let people who are popping on login hosts through, even if
	their mailid points to mailbox.  LOGINPOP is defined in pop3.h.
    */
    if(strstr(LOGINPOP, name2) && strstr(LOGINPOP, hostname)) {
	return(0);
    }
    strcpy(right_server, amp_ptr + 1);
/*    return (1); */
#ifdef DEBUG
    fprintf(logfp, "%s: name2: %s\nhostname: %s\nright_server: %s\n",cli_user, name2, hostname,right_server);
#endif
    return (1);
}

#endif


#ifdef STANDALONE
void sock_init(int *sockfd)
{
    int on = 1;
    struct linger linger;		/* To set socket options */
    struct sockaddr_in my_addr;


    signal (SIGCHLD, catch_sigchld);
    *sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (*sockfd == -1) {
    	perror ("pop3d standalone: fatal error creating socket");
    	exit(1);
    }
    setsockopt(*sockfd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
    linger.l_onoff = on;
    linger.l_linger = 15;
    setsockopt(*sockfd, SOL_SOCKET, SO_LINGER, (void *)&linger,
		    sizeof(linger));
    
    bzero(&my_addr, sizeof(struct sockaddr_in));
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(MYPORT);
    my_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    if (bind(*sockfd, (struct sockaddr *)&my_addr, 
		sizeof (struct sockaddr)) == -1) {
    	perror("pop3d standalone: fatal error binding to socket");
    	exit(1);
    }

    if (listen(*sockfd, QUEUE_LIMIT) == -1) {
    	perror("pop3d standalone: fatal error listening to socket");
    	exit(1);
    }
}
#endif /* STANDALONE */



int
#ifdef ANSIC
main(int argc, char *argv[])
#else /* !ANSIC */
main(argc, argv)
int argc;
char *argv[];
#endif /* ANSIC */
{
	int svr_state = SVR_LISTEN_STATE;	/* State of POP3 server */
	char cli_buf[CLI_BUFSIZ];		/* Buffer for client cmds */
#ifdef LOG_SESSIONS
        int pass_complete = 0;
#endif
#ifdef NO_MULTIPLE
	int unlink_result = 0;
#ifdef DEBUG
	DIR *dirp;
	struct dirent *dir_info;
#endif
#endif
#ifdef STANDALONE
	int sockfd; 	/* Initial socket to bind to */
	int new_fd;	/* New connections go here   */
	int sin_size;
	int fork_pid;
	int fork_result;
	int sock_in, sock_out; 		/* For stdin and stdout */
	struct sockaddr_in remote_addr;


	if ((fork_pid = fork()) > 0) { /* Parent */
	    exit(0);
	} else if (fork_pid < 0) { /* Error in forking */
	    perror("pop3d standalone: FATAL ERROR in initial fork");
	    exit(1);
	}

	/* Only the child process of the initial fork will get to here. */
	standalone_initialize();
	sock_init (&sockfd);
	sin_size = sizeof (struct sockaddr_in);
	while (1) {
	    new_fd = accept(sockfd, (struct sockaddr*)&remote_addr, &sin_size);
	    if (new_fd < 0) {
		if (errno != EINTR) {
	    	    perror("pop3d standalone: WARNING: bad accepted socket");
		}
		close(new_fd);
	    	continue;
	    }

	    fork_result = fork();

	    if (fork_result > 0) { /* This is the parent */
	        close (new_fd);
		/* Make sure all exited children aren't hanging around */
	    	while (waitpid (-1, NULL, WNOHANG) > 0);
		continue;
	    }
	    else if (fork_result == 0) { /* This is the child */
		close (sockfd);
		sock_in = new_fd;
		sock_out = new_fd;
		dup2(sock_in, 0);
		dup2(sock_out, 1);

	    	break;
	    }
	    else { /* fork_result < 0 */
	    	perror("pop3d standalone: WARNING: unable to fork");
		close (new_fd);
	    }
	}
#endif /* STANDALONE */
	    

	initialize();

	fprintf(stdout,"+OK %s POP3 Server (Version %s) ready.\r\n",
		svr_hostname,VERSION);
	fflush(stdout);
	svr_state = SVR_AUTH_STATE;
	for ( ; ; ) {
		/* Wait for command from client */
		alarm(SVR_TIMEOUT_CLI);
		if (fgetl(cli_buf,CLI_BUFSIZ,stdin) == -1)
			break;
		alarm(0);

		/* Take action on client command */
		cmd_prepare(cli_buf);
#ifdef DEBUG
		if ((cli_buf[0] == 'p')||(cli_buf[0] == 'P'))
			fprintf(logfp,"%s: CLI: PASS\n", cli_user);
		else
			fprintf(logfp,"%s: CLI: %s\n",cli_user, cli_buf);
#endif
		switch(svr_state) {
		case SVR_AUTH_STATE:	/* Expecting USER command */
			svr_state = svr_auth(svr_state,cli_buf);
			break;
		case SVR_PASS_STATE:	/* Expecting PASS command */
			svr_state = svr_pass(svr_state,cli_buf);

#ifdef LOG_SESSIONS
			if(svr_state != SVR_TRANS_STATE) {
			    pass_complete = 0;
			}
			else {
			    pass_complete = 1;
			}
#endif
			break;
		case SVR_TRANS_STATE:	/* Expecting mailbox command */
			svr_state = svr_trans(svr_state,cli_buf);
			break;
		case SVR_FOLD_STATE:	/* Need to open another mailbox */
			svr_state = svr_fold(svr_state,cli_buf);
			break;
		default:
			fail(FAIL_CONFUSION, 0);	/* Won't return */
			break;
		}
#ifdef DEBUG
		fprintf(logfp,"%s: SVR: %s",cli_user, svr_buf);
#endif

		/* Send out response to client */
		alarm(SVR_TIMEOUT_SEND);
		fputs(svr_buf,stdout);
		fflush(stdout);
		alarm(0);
		if (ferror(stdout))
			break;

		/* Exit when server has sent goodbye */
		if (svr_state == SVR_DONE_STATE)
			break;
	}
	fld_release();		/* [1.003] Make sure folder is released */
#ifdef DEBUG
	fprintf(logfp, "%s: fld_release outside main complete\n", cli_user);
	fflush(logfp);
#endif

/* [1.012] Disallow multiple simultaneous POP3 sessions on single mailbox */
#ifdef NO_MULTIPLE
        if(remove_lockfile != 0) {
#ifdef DEBUG
    	    struct stat lock_stat; /* To determine if lockfile is expired */
	    fprintf(logfp, "%s: about to stat/unlink lockfile\n", cli_user);
	    fflush(logfp);
	    if(stat(lockfile, &lock_stat) == 0) { /* The file exists */
#endif
                unlink_result = unlink(lockfile);
#ifdef DEBUG
		fprintf(logfp, "%s: after unlink\n", cli_user);
		if (unlink_result == 0) {
	    	    fprintf(logfp, "%s: unlink lockfile %s ok\n", cli_user, lockfile);
		    if(stat(lockfile, &lock_stat) == 0) {    
			fprintf(logfp, "%s: lockfile still exists\n", cli_user);
		    }
		    fflush(logfp);
		} else {
	    	    fprintf(logfp,"%s: unlink lockfile %s FAILED\n",cli_user, lockfile);
		    fflush(logfp);
		}
	    }
	    else {
		fprintf(logfp, "%s: No lockfile to unlink\n", cli_user);
	        fflush(logfp);
	    }
	    dirp = opendir("/var/tmp");
	    if (dirp) {
	        while ((dir_info = readdir(dirp)) != NULL) {
		    if (strncmp(dir_info->d_name, "pop_", 4) == 0) {
		        fprintf(logfp, "%s: file %s\n", cli_user, dir_info->d_name);
		    }
	        }
		closedir(dirp);
	    }
	}
	else {
	    fprintf(logfp, "%s: Don't remove lockfile\n", cli_user);
	}
#else
	} 
#endif /* DEBUG */
#endif /* NO_MULTIPLE */

#ifdef TLI
	if (ioctl(fileno(stdin), I_POP, 0) == -1 ) {
	    perror("ioctl");
	    exit(7);
	}
#endif /* TLI */

#ifdef LOG_SESSIONS
        /* Log the number of messages retrieved, and how many bytes those
           messages comprise.
        */
	if (pass_complete == 1) {
	    openlog( "pop3", LOG_PID, FACILITY );
	    syslog( LOG_INFO | FACILITY, "%s: %d %d",cli_user, retr_count, byte_count );
	    closelog();
	}
#endif
#ifdef DEBUG
	fprintf(logfp, "%s: exit\n", cli_user);
	lockf(fileno(logfp), F_ULOCK, 0);
	fclose(logfp);
#endif
	exit(0);
}
