|
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(); }