|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T p
Length: 17358 (0x43ce)
Types: TextFile
Names: »popd.c«
└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit
└─⟦e84e043a4⟧ »EurOpenD3/mail/pop/trout-popd.tar.Z«
└─⟦632bfcc3f⟧
└─⟦this⟧ »popd.c«
/*
* 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();
}