DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T p

⟦8b3688795⟧ TextFile

    Length: 17358 (0x43ce)
    Types: TextFile
    Names: »popd.c«

Derivation

└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit
    └─⟦e84e043a4⟧ »EurOpenD3/mail/pop/trout-popd.tar.Z« 
        └─⟦632bfcc3f⟧ 
            └─⟦this⟧ »popd.c« 

TextFile

/*
 * popd - Post Office Protocol server
 */
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <signal.h>
#include <errno.h>
#include <stdio.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sgtty.h>
#include <sys/socket.h>
#include <pwd.h>
#include <sys/time.h>
#include <strings.h>

static char *rcsid = "$Header: popd.c,v 1.17 89/12/13 23:43:21 ron Exp $ $Source: /usr/src/local/popd/RCS/popd.c,v $";

#define null 0
#define helo 1
#define fold 2
#define raed 3
#define retr 4
#define acks 5
#define ackd 6
#define nack 7
#define quit 8
#define	save 9
#define NKEYS 9

#define NACK 0
#define ACK 1

struct key_word {
	char	*key;
	int	val;
	int	args;
	} key, keytab[] = {
		{ "null", null, 0 } ,
		{ "helo", helo, 2 } ,
		{ "fold", fold, 1 } ,
		{ "read", raed, 1 } ,
		{ "retr", retr, 0 } ,
		{ "acks", acks, 0 } ,
		{ "ackd", ackd, 0 } ,
		{ "nack", nack, 0 } ,
		{ "quit", quit, 0 } ,
		{ "save", save, 1 } ,		/* Non standard */
	};

int state_tab [4][NKEYS] = {
	{1,5,5,5,5,5,5,4,5},
	{5,1,2,5,5,5,5,4,1},
	{5,1,2,3,5,5,5,4,1},
	{5,5,5,5,2,2,2,4,5}
	};

/******************************************************************************
                State Diagram corresponding to the state transition table
                represented by the variable state_tab[][]

                             |
                             | helo 
                             |
                            \|/
                       --------------      quit           -----------
              ------->|      1       |-----------------> |     4     |
       fold  |         --------------                     -----------
    or save  |         |   |      /|\                         /|\
              ---------    |       |                           |
                           |       |                           |
                      read |       | fold or save              |
                          \|/      |                           |
                       --------------      quit                |
              ------->|      2       |-------------------------
       read  |         --------------                          |
             |         |   |      /|\                          |
              ---------    |       | ack(s/d)                  |
                      retr |       |                           |
                          \|/      |                           |
                       --------------      quit                |
                      |      3       |-------------------------
                       --------------           
                                                 ---------
                                                |    5    |
                                                |  error  |
                                                 ---------
*******************************************************************************/ 
        
int	myargc, cur_msg, msg_cnt, cur_msg_len;
char	myargv[4][30];
int	cur_state = 0;
int	debug = 0;
struct	passwd *pwd;
#define	MAXMSGS	1000
char	mailbox[128];
#define	MAILDIR "/usr/spool/mail/"
char	savebox[128];
#define	SAVEFILE ".bak"
int	savefd = -1;
char	tmplate[] = "/tmp/popXXXXX";
char	mailtmp[16];
char    user[128];
char 	*pidfile = "/usr/local/etc/popd.pid";
int	pid;
#define MAXNAME 30
char    logfile[MAXNAME] = "/usr/adm/popd.log";
FILE    *log;
FILE	*ftmp, *fmail;
int	nmsg, nlines;
int	opened;
int     dolog;
int     num_msg = 0;
int     tbytes = 0;
int     closelog();
void    doexit();
int	timeout();
#define MIN 60
#define TIMEOUT	(5*MIN)

struct	sockaddr_in server_name;

int fd_stdin;

extern int errno;

struct message {
	long	headp;
	int	deleted;
	int	lines;
};

struct message msg[MAXMSGS+2];
	
int result, flags;
int oursock, hissock;
char	line[BUFSIZ];

main ()
{
	struct servent *sp;
	int s;
	FILE *f;

	strcpy (line, "+ POP2 Unix Server on ");
	gethostname(&line[strlen(line)], 1024-strlen(line));
	strcat (line, "\r\n");


	if (debug) setbuf (stdout, NULL);

	/* find pop's socket number */
	if ((sp = getservbyname("pop", "tcp")) == 0) {
		fprintf(stderr, "popd: tcp/pop: unknown service\n");
		exit(1);
	}
	server_name.sin_port = sp->s_port;
	
	if (debug) server_name.sin_port = htons(1025);
	else {
		if (fork())
			exit(0);
		if ((s = open("/dev/tty", 2)) > 0) {
			ioctl(s, TIOCNOTTY, 0);
			close(s);
		}
	}
	
	/* now create socket for receives */
again:
	oursock = socket(AF_INET, SOCK_STREAM, 0, 0);
	if (oursock < 0) {
		perror("popd: socket");;
		sleep(5);
		goto again;
	}
	
	/* name us as the pop server */
	while (bind(oursock,
		(caddr_t)&server_name, sizeof (server_name), 0) < 0) {
		perror("pop: bind");
		sleep(5);
	}
	

	/* cycle the log on a HUP signal */
	signal (SIGHUP, closelog);

	/* save popd pid for later */
	f = fopen(pidfile, "w");
	fprintf(f, "%d\n", pid = getpid());
	fclose(f);

	/* start logfile */
        if(strlen(logfile))
	      if ((log = fopen(logfile,"a")) != NULL) dolog =1;
	      else {
		       fprintf(stderr, "Can't open %s\n", logfile);
		       dolog = 0;
                    }
         logit("Pop2 server started.\n");


	/* allow connections... */
	listen(oursock, 10);
	for (;;) {
	    hissock = accept (oursock, 0, 0);
	    if (hissock < 0) continue;
	    if (!fork()) {
		num_msg = 0;
		tbytes = 0;
		if (fork() == 0) {
			pid = getpid();
			signal(SIGHUP, SIG_DFL);
			exit(do_pop());
		}
		else exit(0);
	    }
	    else {
		wait(0); /* collect zombie */
		close(hissock);
	    }
       }
}

/* here to do all the real protocol work... */
/* assumes that socket is already open and we're about to prompt */
do_pop ()
{
    if (debug) printf ("(%d) starting fork\n", hissock);
    /* provide initial prompt */
    net_out(line);
    
    /* now get right down to business */
    signal(SIGALRM, timeout);
    while ((result = parse()) > 0) ;
    close (hissock);
    if (debug) printf("(%d) closing...\n", hissock);
    return (result);
}

/* Main parser - guides us through states and checks protocol compliance */
parse ()
{
	int token, next_state, i, slen;

	token = lexical();
	if (debug) printf ("(%d)token: %d\n", hissock, token);
	if (token < 0) return (-1);
	next_state = state_tab [cur_state] [token-1];
	if (debug) printf ("(%d)next state: %d\n", hissock, next_state);
	
	/* things to do when we enter a state */
	switch (next_state) {
		/* about to open folder */
		case 1:
			switch (token) {
			case helo:
				strcpy(user,myargv[1]);
				strcpy(mailbox, MAILDIR);
				strcat(mailbox, myargv[1]);
				if (myargc == 2 && strlen(myargv[2])) {
				    if (check_user(myargv[1],myargv[2])) 
					return (-1);
				    /* include msgs messages */
				    msgs(mailbox);
				    msg_cnt = openit(mailbox);
				} else {
				    if (geteuid() == 0 &&
					(pwd = getpwnam(user))) {
					setreuid(pwd->pw_uid, 0);
					msg_cnt = msgs(NULL);
					setreuid(0, 0);
				    } else msg_cnt = 0;
				    msg_cnt += checkit(mailbox);
				}
				logit("%s logged on, %d messages in %s.\n",
				      user, msg_cnt, mailbox);
				sprintf (line, "#%d\r\n",msg_cnt);
				net_out (line);
				break;
			case fold:
				strcpy (mailbox, myargv[1]);
				msg_cnt = openit(mailbox);
         		        logit("%s fold %s has %d messages.\n",user,
				       mailbox, msg_cnt);
				sprintf (line, "#%d\r\n",msg_cnt);
				net_out (line);
				break;
			case save:
				if (myargc == 1 && strlen(myargv[1]))
					strcpy (savebox, myargv[1]);
				else {
					strcpy (savebox, mailbox);
					strcat (savebox, SAVEFILE);
				}
				if (openbk(savebox)) {
					sprintf (line,"+ OK, saving to %s\r\n",
					savebox);
                                    logit("%s saving to %s.\n",user,savebox);
                                }
				else {
				    sprintf (line,"- Can't access file %s\r\n",
				    savebox);
	                               logit("%s can't access file %s.\n",
					     user,savebox);
				 }
				net_out (line);
				break;
			default:
				net_out ("- unexpected...\r\n");
                                logit ("%s unexpected token.\n",user);
				break;
			}

			if (debug) printf("(%d) mailbox '%s'\n", hissock,
						mailbox);
			break;
		case 2:	if (!opened) {
                             logit ("%s mailbox %s not open.\n",user,mailbox);
			       net_out ("- Error...no open mailbox\r\n");
			       doexit();
			       return(-1);
			}
			switch (token) {
				case raed: 	if (myargc != 0) {
						    cur_msg = atoi (myargv[1]);
						}
						if ( cur_msg < 1 ) {
					        net_out ("- ill msg num\r\n");
		     logit ("%s tried to read illegal message, msg num %d.\n",
						       user, cur_msg);
						    doexit();
						    return (-1);
						}
						break;
				case ackd:	msg[cur_msg].deleted++;
				case acks:	cur_msg++;
						break;
				default:	null;
			}
			
			cur_msg_len = msglen (cur_msg);
			sprintf (line, "=%d\r\n",cur_msg_len);
			net_out (line);
			break;
		case 3: if (!cur_msg_len) {
				net_out ("- zero length message\r\n");
				logit ("%s message %d is zero in length.\n",
					user, cur_msg);
				doexit();
				return (-1);
			}
			tbytes = tbytes + cur_msg_len;
			num_msg = num_msg +1;
			outmsg (cur_msg);
			break;
		case 4:	net_out ("+ OK Exiting...\r\n");
			logit ("%s exiting, %d bytes read %d messages.\n",
			       user, tbytes, num_msg);
			closeit ();
			return (0);
		case 5:	net_out ("- Syntax Error...\r\n");
			logit ("%s  syntax error.\n",user);

			doexit();
			return (-1);
		/* nothing else ever expected... */
		default: break;
	}
	cur_state = next_state;
	return (1);
    }

/*
 * Check userid and password
 */
check_user (name,passwd)
char  *name,*passwd;
{
	char *namep, *crypt();
	struct passwd *getpwnam();

	pwd = getpwnam(name);
	if (debug) printf ("(%d): checking user info\n", hissock);
	if (!pwd) {
		logit ("%s not a registered user.\n",name);
		net_out ("- logon incorrect\r\n");
		return (-1);
	}
	namep = crypt(passwd, pwd->pw_passwd);
	if (strcmp(namep, pwd->pw_passwd)) {
		net_out ("- logon incorrect\r\n");
		logit ("%s authorization failure.\n",name);
		return (-1);
	}

	/* got valid user here, become him... */
	if (debug) printf ("(%d): logon ok, setting UID\n", hissock);
	setgid (pwd->pw_gid);
	initgroups(user, pwd->pw_gid);
	setuid (pwd->pw_uid);
	if (debug) printf ("(%d): logged on and ready\n", hissock);
	/* make filenames relative to home directory */
	if (chdir (pwd->pw_dir) < 0) {
		if (debug) printf("Can't chdir to %s.\n", pwd->pw_dir);
		logit ("chdir(%s) failed.\n", pwd->pw_dir);
		chdir ("/usr/tmp");
	}

}


/* perform lexical analysis on incomming stuff */
lexical ()
{
	int result = 0;
	char inline[100];	/* main input comes here */
	int i;

	for (i = 0; i < 3; i++) myargv[i][0] = '\0';
	myargc = -1;
	while (myargc < 0) {
		if (my_gets (inline) < 0) return (-1);
		if (debug) printf ("(%d)saw: %s\n",hissock, inline );
		myargc = sscanf (inline, "%s%s%s",myargv[0],myargv[1],
						    myargv[2]);
		if (debug) printf ("(%d)scan: %d args\n", hissock, myargc);
		myargc--;
	}
	key = keytab[whatkey(myargv[0])];
	if (debug) printf ("(%d)key: %d\n", hissock, key.val);
	if (key.val == null) {
			sprintf (line, "- Invalid command '%s'\r\n", myargv[0]);
		logit ("%s  - Invalid command '%s'.\n",user, myargv[0]);
			net_out(line);
			doexit();
			return (-1);
		}
	if (key.args && ((key.args - myargc) > 1)) {
		sprintf (line, "- wna for '%s'\r\n", myargv[0]);
		net_out(line);
		return (-1);
	}
	return (key.val);
}


/* Now determine which keyword we've got... */
whatkey (s)
  char s[4];
  {
	int i;
  /* First convert input to lower case... */
	for (i = 0; i < 4; i++) {
		s[i] = (('A' <= s[i]) && ('Z' >= s[i])) ?
			    s[i] + 040 : s[i];
		}

	for (i = NKEYS; i > 0; i--) {
		if (!strncmp (keytab[i].key, s, 4)) return (keytab[i].val);
		}
		
	return (null);
}		

/*
 * check the mailbox for existence of mail.
 */
checkit(mailbox)
char *mailbox;
{
	struct stat statb;

	if (stat(mailbox, &statb) == 0 && statb.st_size > 0) return(1);
	return(0);
}

/*
 * open the specified mailbox
 */
openit(mailbox)
char *mailbox;
{
	struct stat statb;
	int tfd;

	if (opened) closeit();
	nmsg = 0; nlines = 0;
	umask(077);	/* readable only by user */
	strcpy(mailtmp, tmplate);
	tfd = mkstemp(mailtmp);
	if ((ftmp = fdopen(tfd, "w")) == NULL) {
		if (debug) printf("Can't open temp file %s\n", mailtmp);
		return(0);
	}

	if ((fmail = fopen(mailbox, "r")) == NULL) {
		if (debug) printf("Can't open mailbox %s\n", mailbox);
		fclose(ftmp); unlink(mailtmp);
		return(0);
	}
	if (fstat(fileno(fmail), &statb) != 0 || statb.st_size == 0) {
		if (debug) printf("mail file empty\n");
		fclose(ftmp); unlink(mailtmp);
		fclose(fmail);
		return(0);
	}
	
	flock(fileno(fmail), LOCK_EX);
	while (fgets(line, BUFSIZ, fmail) != NULL) {
		if (strncmp("From ", line, 5) == 0) {
			msg[nmsg++].lines = nlines;
if (debug) printf("msg[%d].lines = %d\n", nmsg-1,msg[nmsg-1].lines);
			nlines = 0;
			msg[nmsg].headp = ftell(fmail) - strlen(line);
if (debug) printf("msg[%d].headp = %d\n", nmsg, msg[nmsg].headp);
		} else nlines++;
		fputs(line, ftmp);
	}
	msg[nmsg].lines = nlines;
if (debug) printf("msg[%d].lines = %d\n", nmsg,msg[nmsg].lines);
	msg[nmsg+1].headp = ftell(fmail);
if (debug) printf("msg[%d].headp = %d\n", nmsg+1, msg[nmsg+1].headp);
	flock(fileno(fmail), LOCK_UN);
	fclose(fmail);

	fclose(ftmp);
	ftmp = fopen(mailtmp, "r");
	cur_msg = 1;

	opened++;
	return (nmsg);
}

closeit()
{

	struct stat statb;
	int changed = 0;
	int nlines;
	int f, i, j;

	if (!opened) return;
	for (i = 1; i <= nmsg; i++) 
		if (msg[i].deleted) {
			changed++;
			break;
		}
	if (!changed) {
		fclose(fmail);
		fclose(ftmp);
		unlink(mailtmp);
		opened = 0;
		return;
	}

	signal(SIGINT, SIG_IGN);
	signal(SIGHUP, SIG_IGN);
	signal(SIGQUIT, SIG_IGN);
	f = open(mailbox, 0);
	flock(f, LOCK_EX);
	fstat(f, &statb);
	if (statb.st_size != msg[nmsg+1].headp) {
		/*
		 * there is new mail
		 */
		if ((fmail = fopen(mailbox, "r")) == NULL) return;
		fseek(fmail, msg[nmsg+1].headp, 0);
		fclose(ftmp);
		ftmp = fopen(mailtmp, "a");
		fseek(ftmp, msg[nmsg+1].headp, 0);
		nlines = 0;
		while(fgets(line, BUFSIZ, fmail) != NULL) {
			nlines++;
			fputs(line, ftmp);
		}
		nmsg++;
		msg[nmsg].lines = nlines - 1;
		msg[nmsg+1].headp = ftell(ftmp);
		fclose(fmail);
		fclose(ftmp);
		ftmp = fopen(mailtmp, "r");
	}
	if ((fmail = fopen(mailbox, "w")) != NULL) {
		if (savefd >= 0) flock(savefd, LOCK_EX);
		for (i = 1; i <= nmsg; i++) {
			if (!msg[i].deleted || savefd >= 0) {
				fseek(ftmp, msg[i].headp, 0);
				for (j = 0; j <= msg[i].lines; j++) {
					fgets(line, BUFSIZ, ftmp);
					if (msg[i].deleted) 
					    write(savefd, line, strlen(line));
					else fputs(line, fmail);
				}
			}
		}
		if (savefd >= 0) flock(savefd, LOCK_SH);
		fclose(fmail);
	}
	flock(f, LOCK_UN);
	close(f);
	fclose(ftmp);
	unlink(mailtmp);
	opened = 0;

	signal(SIGHUP, SIG_DFL);
}


/* 
 * Compute a message's length. <lf> counts as <cr><lf>
 */
msglen (n)
{
	struct message *m = &msg[n];
	int len;

	if (n > nmsg) return(0);
	len = msg[n+1].headp - m->headp;
	fseek(ftmp, m->headp, 0);
	fgets(line, BUFSIZ, ftmp);
	len -= strlen(line);
	len += m->lines;
	return (len);
}

/* 
 * Output msg to primary, convert to net standard ascii.
 * We are already positioned at the first line.
 */
outmsg (n)
{
	int i;
	char *s;

	for (i = 0; i < msg[n].lines; i++) {
		s = fgets(line, BUFSIZ, ftmp);
		s += (strlen(line) - 1);
		strcpy(s, "\r\n");
		net_out(line);
	}
}	

/* write a string to the net */
net_out (s)
char *s;
{
	alarm(TIMEOUT);
	write (hissock, s, strlen(s));
	alarm(0);
	if (debug) printf ("(%d) sent: %s\n", hissock, s);
}

/* something to read an entire line from the network */
my_gets (s)
char *s;
{
    int i = 0;
    char c;

    while ((c = my_getc()) != '\n') {
	if (c == EOF) return (-1);
    	if (c != '\r') s[i++] = c;
    }
    s[i] = 0;
    if (debug) printf ("(%d) rcvd: %s\n", hissock, s);
    return (0);
}

char	ibuf[1024];
int	inptr, ilen;
/* get one character of input from the network... */
my_getc ()
{
    for (;;) {
	if (inptr < ilen) return (ibuf[inptr++]);
	alarm(TIMEOUT);
	ilen = read(hissock, ibuf, 1024);
	alarm(0);
	inptr = 0;
	if (ilen == 0) return (-1);
    }
}

/*
 * open a backup file for deleted messages
 */
openbk(savefile)
char *savefile;
{
	if (savefd >= 0) close(savefd);
	if ((savefd = open(savefile, O_CREAT|O_WRONLY|O_APPEND, 0600)) < 0) {
		if (debug) printf("Can't access file %s\n", savefile);
		return(0);
	}
	flock(savefd, LOCK_SH);
	return (1);
}


/*
 * write log messages to logfile
 */
logit(format, p1, p2, p3, p4)
char *format, *p1, *p2, *p3, *p4;
{
	long now;
	char *t, *p;

	if (dolog) {
		now = time(0);
		t = ctime(&now);
		p = index(t,'\n');
		*p = '\0';

		flock (fileno(log), LOCK_EX);

		fprintf (log, "%s [%d] ", t, pid);
		fprintf (log, format, p1, p2, p3, p4);
		fflush (log);

		flock (fileno(log), LOCK_UN);
	}
}

int closelog()
{
	signal(SIGHUP, closelog);
	logit ("Closing popd server log.\n");
	if (dolog)  fclose(log);

	/* reopen logfile */
	if(strlen(logfile))
		if ((log = fopen(logfile,"a"))!= NULL) dolog =1;
		else {
			fprintf(stderr, "Can't open %s.\n", logfile);
			dolog = 0;
		}
	logit("Pop2 server log opened.\n");	    
}

void doexit()
{
     logit ("%s exiting on error, %d bytes read in %d messages.\n",user,
	     tbytes, num_msg);

}

timeout()
{
	logit ("Inactivity timeout.\n");
	closeit();
	exit();
}