1 /* popser.c - the POP service */
3 static char ident[]="@(#)$Id: popser.c,v 1.34 1995/12/07 18:54:35 jromine Exp $";
7 #include "../h/dropsbr.h"
10 #include "../h/formatsbr.h"
11 #include "../h/scansbr.h"
14 #include "../zotnet/bboards.h"
16 #include "../zotnet/mts.h"
22 #include <sys/types.h>
44 #define POPSERVICE "pop"
57 auth1, auth2, trans, update, halt, error
61 auth1, auth2, trans, mbox, item, ack, update, halt, error
66 static int user (), pass ();
68 static isguest(), getbbmax();
70 static int xtnd1(), xtnd2();
72 static int xtnd1(), xtnd2(), xtnd3 ();
81 static int status (), list (), retrieve (), delete (), reset ();
82 static int top (), last ();
88 static int helo (), rdp2 (), acks (), ack2 (), fold (), nack ();
91 static struct vector {
96 enum state v_win, v_lose;
98 "user", 1, 1, user, auth1, auth2, auth1,
99 "pass", 1, 1, pass, auth2, trans, auth1,
101 "rpop", 1, 1, rpop, auth2, trans, auth1,
104 "apop", 2, 2, apop, auth1, trans, auth1,
106 "quit", 0, 0, NULL, auth1, halt, halt,
107 "quit", 0, 0, NULL, auth2, halt, halt,
109 "stat", 0, 0, status, trans, trans, trans,
110 "list", 0, 1, list, trans, trans, trans,
111 "retr", 1, 1, retrieve, trans, trans, trans,
112 "dele", 1, 1, delete, trans, trans, trans,
113 "noop", 0, 0, NULL, trans, trans, trans,
114 "rset", 0, 0, reset, trans, trans, trans,
116 "top", 2, 2, top, trans, trans, trans,
117 "last", 0, 0, last, trans, trans, trans,
120 "xtnd", 1, 2, xtnd, trans, trans, trans,
122 "xtnd", 1, 3, xtnd, trans, trans, trans,
125 "quit", 0, 0, quit, trans, halt, halt,
128 "helo", 2, 2, helo, auth1, mbox, auth1,
130 "fold", 1, 1, fold, mbox, mbox, mbox,
131 "quit", 0, 0, quit, mbox, halt, halt,
132 "read", 0, 1, rdp2, mbox, item, error,
134 "fold", 1, 1, fold, item, mbox, mbox,
135 "read", 0, 1, rdp2, item, item, error,
136 "quit", 0, 0, quit, item, halt, halt,
137 "retr", 0, 0, retrieve, item, ack, error,
139 "acks", 0, 0, ack2, ack, item, error,
140 "ackd", 0, 0, ack2, ack, item, error,
141 "nack", 0, 0, rdp2, ack, item, error,
142 "quit", 0, 0, NULL, ack, halt, halt,
148 static struct vector *getvector ();
153 static int pop2 = NOTOK; /* current pop2 msg, or NOTOK if pop3 */
161 static char *hostname;
162 static char server[BUFSIZ];
163 static char timestamp[BUFSIZ];
165 static char username[BUFSIZ];
167 static char maildrop[BUFSIZ];
172 static long lastseen;
178 static int guest_uid;
179 static int guest_gid;
181 static struct bboard *BBhead = NULL;
182 static struct bboard *BBtail = NULL;
184 static long BBtime = 0L;
186 static struct bboard *getbbaux ();
190 struct Msg { /* Msgs[0] contains info for entire maildrop */
192 #define m_id m_drop.d_id
193 #define m_size m_drop.d_size
194 #define m_last m_drop.d_start /* Msgs[i = 0] */
195 #define m_start m_drop.d_start /* Msgs[i > 0] */
196 #define m_stop m_drop.d_stop
204 static int nMsgs = 0;
205 static struct Msg *Msgs = NULL;
211 static int _sc_width = 0;
212 static char *nfs = NULL;
218 #define TRMLEN (sizeof TRM - 1)
221 static TYPESIG pipeser ();
228 struct passwd *getpwnam();
232 void padvise (), padios ();
236 #define MBX_READ pmbx_read
237 static int pmbx_read ();
238 static char *p_copy(), *p_copyin(), *p_nextword();
239 static p_cmatch(), p_isdate(), p_ishead(), p_parse(), any();
241 #define MBX_READ mbx_read
243 extern int mbx_read ();
245 static int setup(), setupaux(), read_map(), read_file(), pmbx_size();
246 static int quitaux(), quitfile(), respond(), getline();
247 static m_gMsgs(), multiline(), multiend(), putline();
252 padvise (NULLCP, LOG_INFO, "initialize list of BBoards");
254 BBhead = BBtail = NULL;
255 while (getbbaux (NULLCP))
263 register struct bboard *bb,
266 if (BBtime == getbbtime ())
269 padvise (NULLCP, LOG_INFO, "list of BBoards has changed");
271 for (bb = BBhead; bb; bb = bp) {
275 free (bb -> bb_name);
277 free (bb -> bb_file);
278 if (bb -> bb_archive)
279 free (bb -> bb_archive);
281 free (bb -> bb_info);
285 free (bb -> bb_passwd);
287 free (bb -> bb_date);
289 free (bb -> bb_addr);
290 if (bb -> bb_request)
291 free (bb -> bb_request);
293 free (bb -> bb_relay);
295 for (p = bb -> bb_aka; *p; p++)
297 free ((char *) bb -> bb_aka);
299 for (p = bb -> bb_leader; *p; p++)
301 free ((char *) bb -> bb_leader);
303 for (p = bb -> bb_dist; *p; p++)
305 free ((char *) bb -> bb_dist);
310 BBhead = BBtail = NULL;
311 while (getbbaux (NULLCP))
319 static char *kusername;
321 kpop (in, out, principal, rhost, auth)
324 char *principal, *rhost;
327 pop (in, out, priv, rhost)
336 #if defined (DPOP) || defined (BPOP)
337 register struct passwd *pw;
338 #endif /* defined (DPOP) || defined (BPOP) */
339 register struct vector *v;
347 (void) sprintf (server, "%s KPOP server", myhost);
350 (void) sprintf (server, "%s server", priv ? "RPOP" : "POP");
353 if ((input = fdopen (in, "r")) == NULL
354 || (output = fdopen (out, "w")) == NULL) {/* you lose big */
355 (void) respond (NOTOK, "%s loses on initialization", server);
358 (void) signal (SIGPIPE, pipeser);
360 if (principal == NULLCP) {
362 strcpy(buf, "Authentication failed: ");
363 strcat(buf, krb_err_txt[auth]);
364 (void) respond (NOTOK, buf);
367 kusername = principal;
371 if ((pw = getpwnam (POPUID)) == NULL || !setpwinfo (pw, POPDB, 1)) {
372 (void) respond (NOTOK, "%s loses on DB initialization -- %s",
373 server, pw ? getbberr () : "POP user-id unknown");
376 pop_uid = pw -> pw_uid;
377 pop_gid = pw -> pw_gid;
380 if ((pw = getpwnam (popbbuser)) && pw -> pw_uid) {
381 guest_uid = pw -> pw_uid;
382 guest_gid = pw -> pw_gid;
385 guest_uid = guest_gid = 0;
391 (void) time (&clock);
392 (void) sprintf (timestamp, "<%d.%ld@%s>", getpid (), clock, myhost);
394 (void) respond (OK, "%s ready %s", server, timestamp);
396 for (mystate = auth1; mystate != halt && mystate != error;)
397 switch (getline (buffer, sizeof buffer, input)) {
399 if ((v = getvector (buffer, vec)) == NULL)
401 mystate = (v -> v_vec ? (v -> v_vec) (vec)
402 : respond (OK, NULLCP)) == OK
410 (void) respond (NOTOK, "%s signing off", server);
417 static int helo (vec) /* sort of "user" and "pass" */
420 pop2 = 0; /* now we're talkin' pop2! */
421 make_lower (username, vec[1]); /* helo user pass */
422 return pass (++vec); /* user pass */
426 static int user (vec)
429 make_lower (username, vec[1]);
431 if (!strcmp(username, kusername))
432 return respond (OK, "Kerberos authentication succeeded. Send username as password (%s)", username);
434 respond (NOTOK, "Wrong username supplied (%s vs. %s)",
435 kusername, username);
439 return respond (OK, "password required for %s", username);
445 static int pass (vec)
450 register struct passwd *pw;
452 register struct spwd *shpw;
455 register struct bboard *pw;
460 if ((pw = getpwnam (username)) != NULL)
461 return setup(pw, FALSE);
463 return respond (NOTOK, "no local password entry");
466 static struct bboard entry;
467 static char entry_file[BUFSIZ] = "/usr/spool/pop";
470 pw->bb_name = username;
471 strcat(entry_file, username);
472 pw->bb_file = entry_file;
473 return setup(pw, FALSE);
484 gw.pw_name = popbbuser;
485 gw.pw_uid = guest_uid;
492 if ((pw = getpwnam (username)) == NULL
494 || *pw -> pw_passwd == NULL
495 || strcmp (crypt (vec[1], pw -> pw_passwd), pw -> pw_passwd)) {
497 || (shpw = getspnam (username)) == NULL
498 || *shpw -> sp_pwdp == NULL
499 || strcmp (crypt (vec[1], shpw -> sp_pwdp), shpw -> sp_pwdp)) {
502 trusted (0, hostname, NULLCP, 0, pw ? pw -> pw_name : username,
503 pw && pw -> pw_uid == 0, POPSERVICE, "tcp", NULL);
505 return respond (NOTOK, "login incorrect");
510 static struct bboard gw;
512 gw.bb_name = popbbuser;
518 if (((pw = getbbnam (username)) == NULL
519 && (pw = getbbaka (username)) == NULL)
520 || *pw -> bb_passwd == NULL
521 || strcmp (crypt (vec[1], pw -> bb_passwd), pw -> bb_passwd)) {
523 trusted (0, hostname, NULLCP, 0, pw ? pw -> bb_name : username,
524 0, POPSERVICE, "tcp", NULL);
526 return respond (NOTOK, "login incorrect");
534 if (trusted (1, hostname, NULLCP, 0, myhost,
536 pw -> pw_name, pw -> pw_uid == 0,
540 POPSERVICE, "tcp", NULL)
542 return respond (NOTOK, "permission denied");
544 return setup (pw, guest);
557 if (strcmp (username, popbbuser) || !guest_uid)
559 if (popbblist == NULL || (fp = fopen (popbblist, "r")) == NULL)
564 while (fgets (buffer, sizeof buffer, fp)) {
565 if (cp = index (buffer, '\n'))
567 if (strcmp (buffer, hostname) == 0) {
582 static int rpop (vec)
586 register struct passwd *pw;
588 register int hostok = 0;
592 register struct bboard *pw;
596 if (!rproto || (pw = getpwnam (username)) == NULL) {
598 trusted (0, hostname, vec[1], 0, username, 0, "rpop", "tcp",
601 return respond (NOTOK, "login incorrect");
603 if (chdir (pw -> pw_dir) == NOTOK && chdir ("/") == NOTOK)
604 return respond (NOTOK, "no remote directory");
605 if (ruserok (hostname, pw -> pw_uid == 0, vec[1], username) == NOTOK) {
607 trusted (0, hostname, vec[1], 0, pw -> pw_name,
608 pw -> pw_uid == 0, "rpop", "tcp", NULL);
610 return respond (NOTOK, "permission denied");
614 || ((pw = getbbnam (username)) == NULL
615 && (pw = getbbaka (username)) == NULL)) {
617 trusted (0, hostname, vec[1], 0, username, 0, "rpop", "tcp",
620 return respond (NOTOK, "login incorrect");
623 * hacked by Dave Cohrs Tue Feb 4 14:12:15 CST 1986
624 * to allow the hostname to be a list: user@host1,user@host2
625 * NOTE: the separator must be a comma -- no spaces are allowed
627 (void) sprintf (buffer, "%s@%s", vec[1], hostname);
628 for (bp = pw -> bb_addr; bp; bp = cp) {
629 if ((cp = index (bp, ',')))
631 hostok = uleq (bp, buffer);
639 trusted (0, hostname, vec[1], 0, pw -> bb_name, 0, "rpop",
642 return respond (NOTOK, "permission denied");
647 if (trusted (1, hostname, vec[1], 0, username,
655 return respond (NOTOK, "permission denied");
657 return setup (pw, FALSE);
665 #include "../../uip/md5.c"
667 #include <sys/file.h>
673 static int apop (vec)
678 register unsigned char *dp;
682 register struct passwd *pw;
684 register struct bboard *pw;
691 struct authinfo auth;
693 (void) strcpy (username, vec[1]);
696 if ((pw = getpwnam (username)) == NULL
697 || *pw -> pw_passwd == NULL) {
698 return respond (NOTOK, "user invalid");
701 if (((pw = getbbnam (username)) == NULL
702 && (pw = getbbaka (username)) == NULL)
703 || *pw -> bb_passwd == NULL) {
704 return respond (NOTOK, "subscriber invalid");
708 if ((db = dbm_open (APOP, O_RDONLY, 0)) == NULL)
709 return respond (NOTOK, "POP authorization DB not available (%d)",
711 if (fstat (dbm_pagfno (db), &st) != NOTOK
712 && (st.st_mode & 0777) != 0600) {
714 return respond (NOTOK, "POP authorization DB has wrong mode (0%o)",
717 if (flock (dbm_pagfno (db), LOCK_SH) == NOTOK) {
719 return respond (NOTOK, "unable to lock POP authorization DB (%d)",
722 key.dsize = strlen (key.dptr = vec[1]) + 1;
723 value = dbm_fetch (db, key);
724 if (value.dptr == NULL) {
726 return respond (NOTOK, "not authorized");
728 bcopy (value.dptr, (char *) &auth, sizeof auth);
729 (void) sprintf (cp = buffer, "%s%*.*s", timestamp, auth.auth_secretlen,
730 auth.auth_secretlen, auth.auth_secret);
734 MD5Init (&mdContext);
735 MD5Update (&mdContext, (unsigned char *) buffer,
736 (unsigned int) (strlen (timestamp) + auth.auth_secretlen));
737 MD5Final (digest, &mdContext);
740 for (ep = (dp = digest) + sizeof digest / sizeof digest[0];
743 (void) sprintf (cp, "%02x", *dp++ & 0xff);
746 if (strcmp (vec[2], buffer))
747 return respond (NOTOK, "authentication failure");
749 return setup (pw, 0);
755 static int setup (pw, guest)
757 register struct passwd *pw;
759 register struct bboard *pw;
765 (void) setgid (guest_gid);
767 (void) initgroups (popbbuser, guest_gid);
769 (void) setuid (guest_uid);
774 (void) setgid (pw -> pw_gid);
776 (void) initgroups (pw -> pw_name, pw -> pw_gid);
778 (void) setuid (pw -> pw_uid);
780 (void) setgid (pop_gid);
782 (void) initgroups (POPUID, pop_gid);
784 (void) setuid (pop_uid);
791 (void) sprintf (maildrop, "%s/%s",
792 mmdfldir && *mmdfldir ? mmdfldir : pw -> pw_dir,
793 mmdflfil && *mmdflfil ? mmdflfil : pw -> pw_name);
795 (void) strcpy (maildrop, pw -> bb_file);
798 if (setupaux (guest) == NOTOK)
802 if (pop2 != NOTOK) { /* in response to pop2 "helo" */
803 pop2 = nmsgs > 0 ? 1 : 0;
804 return respond ('#', "%d message%s (%d octets)",
805 nmsgs, nmsgs != 1 ? "s" : "", Msgs[0].m_size);
810 nmsgs ? "maildrop has %d message%s (%d octets)" : "maildrop empty",
811 nmsgs, nmsgs != 1 ? "s" : "", Msgs[0].m_size);
816 static int setupaux (readonly)
826 if ((dp = readonly ? fopen (maildrop, "r") : lkfopen (maildrop, "r"))
835 return respond (NOTOK, "unable to %s maildrop: \"%s\"",
836 readonly ? "read" : "lock", maildrop);
839 if (fstat (fileno (dp), &st) != NOTOK) {
840 mode = (int) (st.st_mode & 0777), mtime = st.st_mtime;
841 msgp = read_map (maildrop, (long) st.st_size);
844 mode = 0600, mtime = 0;
848 if ((msgp = read_file (msgp ? Msgs[msgp].m_stop : 0L, msgp + 1)) < 1)
852 lastseen = Msgs[0].m_last;
853 if(debug)padvise(NULLCP,LOG_DEBUG,"XXX: lastseen=%d",lastseen);
857 Msgs[0].m_flags = readonly ? MREAD : MNULL;
859 for (i = 1; i <= nmsgs; i++) {
860 if (Msgs[i].m_size == 0)
861 Msgs[i].m_size = pmbx_size (i);
862 Msgs[0].m_size += Msgs[i].m_size;
863 Msgs[i].m_flags = MNULL;
871 static int read_map (file, pos)
877 register struct drop *pp,
882 padvise (NULLCP, LOG_DEBUG, "read_map (%s, %ld)", file, pos);
884 if ((i = map_read (file, pos, &rp, debug)) == 0)
889 Msgs[0].m_last = rp -> d_start;
892 for (pp = rp + 1; i-- > 0; msgp++, pp++) {
893 mp = &Msgs[msgp].m_drop;
894 mp -> d_id = pp -> d_id;
895 mp -> d_size = pp -> d_size;
896 mp -> d_start = pp -> d_start;
897 mp -> d_stop = pp -> d_stop;
901 if (Msgs[0].m_last > msgp) {
903 padvise (NULLCP, LOG_DEBUG, "lastseen adjusted from %d to %d",
904 Msgs[0].m_last, msgp);
905 Msgs[0].m_last = msgp;
913 static int read_file (pos, msgp)
918 register struct drop *pp,
923 padvise (NULLCP, LOG_DEBUG, "read_file (%ld, %d)",
926 if ((i = MBX_READ (dp, pos, &rp, debug)) <= 0)
929 m_gMsgs ((msgp - 1) + i);
931 for (pp = rp; i-- > 0; msgp++, pp++) {
932 mp = &Msgs[msgp].m_drop;
934 mp -> d_size = pp -> d_size;
935 mp -> d_start = pp -> d_start;
936 mp -> d_stop = pp -> d_stop;
949 padvise (NULLCP, LOG_DEBUG, "m_gMsgs (%d) 0x%x %d",
953 nMsgs = n + MAXFOLDER / 2;
954 Msgs = (struct Msg *) calloc ((unsigned) (nMsgs + 2), sizeof *Msgs);
956 padios (NULLCP, "unable to allocate Msgs structure");
963 nMsgs = n + MAXFOLDER / 2;
964 Msgs = (struct Msg *) realloc ((char *) Msgs,
965 (unsigned) (nMsgs + 2) * sizeof *Msgs);
967 padios (NULLCP, "unable to reallocate Msgs structure");
972 static int pmbx_size (m)
978 (void) fseek (dp, Msgs[m].m_start, 0);
979 for (i = 0, pos = Msgs[m].m_stop - Msgs[m].m_start; pos > 0; i++, pos--)
980 if (fgetc (dp) == '\n')
990 static int status (vec)
993 return respond (OK, "%d %d", nmsgs - dmsgs, Msgs[0].m_size);
998 static int rdp2 (vec) /* always returns OK */
1002 if ((pop2 = atoi (vec[1])) <= 0)
1006 return NOTOK; /* close 'em down */
1008 if (pop2 <= 0 || pop2 > nmsgs) {
1010 return respond ('=', "0 no message");
1012 if (Msgs[pop2].m_flags & MDELE) {
1014 return respond ('=', "0 message %d is deleted", pop2);
1017 return respond ('=', "%d (message %d)", Msgs[pop2].m_size, pop2);
1020 static int ack2 (vec)
1023 if (strcmp (vec[0], "ackd") == 0) {
1024 Msgs[pop2].m_flags |= MDELE; /* ignored later if MREAD */
1025 Msgs[0].m_size -= Msgs[pop2].m_size;
1029 if (pop2) { /* a current msg */
1030 rmsgs++; /* mark this one as read */
1032 pop2 = -1; /* let rdp2 reset */
1033 else if (Msgs[pop2].m_flags & MDELE)
1034 pop2 = -1; /* let rdp2 reset */
1035 if (pop2 > Msgs[0].m_last)
1036 Msgs[0].m_last = pop2;
1038 return rdp2 (vec); /* vec = { "acks", 0 } */
1041 static int fold (vec)
1042 register char **vec;
1048 /* This might work, or it might be a huge security hole. For my purpose,
1049 * it doesn't need to work, so I'm not going to make sure it's OK.
1053 if (quitaux (NULLVP) == NOTOK)
1054 return respond ('#', "0 unable to close folder");
1056 (void) sprintf (maildrop, vec[1]);
1057 if (setupaux (access (maildrop, 2) ? 1 : 0) == NOTOK)
1058 return respond ('#', "0 unable to read %s", maildrop);
1060 pop2 = nmsgs > 0 ? 1 : 0;
1061 return respond ('#', "%d message%s in %s (%d octets)",
1062 nmsgs, nmsgs != 1 ? "s" : "", maildrop, Msgs[0].m_size);
1066 respond ('#', "0 unable to change folders");
1071 static int list (vec)
1072 register char **vec;
1077 if ((i = atoi (vec[1])) <= 0 || i > nmsgs)
1078 return respond (NOTOK, "no such message: \"%s\"", vec[1]);
1079 if (Msgs[i].m_flags & MDELE)
1080 return respond (NOTOK, "message %d is deleted", i);
1083 return respond (OK, "%d %d", i, Msgs[i].m_size);
1086 if (nfs && !xtnded) {
1089 (void) fseek (dp, Msgs[i].m_start, 0);
1091 switch (scan (dp, i, 0, nfs, 0, 0, 0,
1092 0, NULLCP, (long) Msgs[i].m_size, 0)) {
1096 if (cp = index (scanl, '\n'))
1098 return respond (OK, "%d %d #%s",
1099 i, Msgs[i].m_size, scanl);
1102 return respond (OK, "%d %d #%*d empty",
1103 i, Msgs[i].m_size, DMAXFOLDER, i);
1110 return respond (OK, xtnded ? "%d %d %d" : "%d %d",
1111 i, Msgs[i].m_size, Msgs[i].m_id);
1115 (void) respond (OK, "%d message%s (%d octets)",
1116 nmsgs - dmsgs, nmsgs - dmsgs != 1 ? "s" : "",
1118 for (i = 1; i <= nmsgs; i++)
1119 if (!(Msgs[i].m_flags & MDELE)) {
1121 multiline ("%d %d", i, Msgs[i].m_size);
1124 if (nfs && !xtnded) {
1127 (void) fseek (dp, Msgs[i].m_start, 0);
1129 switch (scan (dp, i, 0, nfs, 0, 0, 0,
1130 0, NULLCP, (long) Msgs[i].m_size, 0)) {
1134 if (cp = index (scanl, '\n'))
1136 multiline ("%d %d #%s",
1137 i, Msgs[i].m_size, scanl);
1141 multiline ("%d %d #%*d empty",
1142 i, Msgs[i].m_size, DMAXFOLDER, i);
1150 multiline (xtnded ? "%d %d %d" : "%d %d",
1151 i, Msgs[i].m_size, Msgs[i].m_id);
1161 static int retrieve (vec)
1162 register char **vec;
1167 char buffer[BUFSIZ];
1172 else if (pop2 == NOTOK) {
1174 if ((i = atoi (vec[1])) <= 0 || i > nmsgs)
1175 return respond (NOTOK, "no such message: \"%s\"", vec[1]);
1176 if (Msgs[i].m_flags & MDELE)
1177 return respond (NOTOK, "message %d is deleted", i);
1179 (void) respond (OK, "%d octets", Msgs[i].m_size);
1182 else /* if called by pop2, vec = { "retr", 0 } */
1186 for ((void) fseek (dp, pos = Msgs[i].m_start, 0);
1187 fgets (buffer, sizeof buffer, dp) != NULL && pos < Msgs[i].m_stop;
1188 pos += (long) (cp - buffer + 1)) {
1189 if (*(cp = buffer + strlen (buffer) - 1) == '\n')
1191 multiline ("%s", buffer);
1194 if (pop2 == NOTOK) { /* then multiend */
1198 if (i > Msgs[0].m_last) {
1211 static int delete (vec)
1212 register char **vec;
1216 if (Msgs[0].m_flags & MREAD)
1217 return respond (NOTOK, "maildrop is read-only");
1219 if ((i = atoi (vec[1])) <= 0 || i > nmsgs)
1220 return respond (NOTOK, "no such message: \"%s\"", vec[1]);
1221 if (Msgs[i].m_flags & MDELE)
1222 return respond (NOTOK, "message %d is deleted", i);
1224 Msgs[i].m_flags |= MDELE;
1225 Msgs[0].m_size -= Msgs[i].m_size;
1228 if (i > Msgs[0].m_last)
1231 return respond (OK, "message %d deleted (%d octets)", i, Msgs[i].m_size);
1235 static int reset (vec)
1240 for (i = 1; i <= nmsgs; i++)
1241 if (Msgs[i].m_flags & MDELE) {
1242 Msgs[i].m_flags &= ~MDELE;
1243 Msgs[0].m_size += Msgs[i].m_size;
1247 Msgs[0].m_last = lastseen;
1253 free (scanl), scanl = NULL;
1254 free (nfs), nfs = NULL;
1259 return status (vec);
1264 static int top (vec)
1265 register char **vec;
1273 char buffer[BUFSIZ];
1275 if ((i = atoi (vec[1])) <= 0 || i > nmsgs)
1276 return respond (NOTOK, "no such message: \"%s\"", vec[1]);
1277 if (Msgs[i].m_flags & MDELE)
1278 return respond (NOTOK, "message %d is deleted", i);
1279 if ((j = atoi (vec[2])) < 0)
1280 return respond (NOTOK, "bad number: \"%s\"", vec[2]);
1282 (void) respond (OK, vec[0]);
1285 for ((void) fseek (dp, pos = Msgs[i].m_start, 0);
1286 fgets (buffer, sizeof buffer, dp) != NULL && pos < Msgs[i].m_stop;
1287 pos += (long) (cp - buffer + 1)) {
1288 if (*(cp = buffer + strlen (buffer) - 1) == '\n')
1297 multiline ("%s", buffer);
1308 static int last (vec)
1311 return respond (OK, "%d is the last msg seen", Msgs[0].m_last);
1317 static int xtnd (vec)
1318 register char **vec;
1320 make_lower (vec[1], vec[1]);
1322 if (strcmp (vec[1], "bboards") == 0 || strcmp (vec[1], "archive") == 0)
1324 if (strcmp (vec[1], "x-bboards") == 0)
1327 if (strcmp (vec[1], "scan") == 0)
1331 return respond (NOTOK, "unknown XTND command: \"%s\"", vec[1]);
1335 static int xtnd1 (vec)
1336 register char **vec;
1338 register struct bboard *bb;
1341 make_lower (vec[2], vec[2]);
1342 if ((bb = getbbaux (vec[2])) == NULL)
1343 return respond (NOTOK, "unknown BBoard: \"%s\"", vec[2]);
1345 if (quitaux (NULLVP) == NOTOK)
1347 (void) strcpy (maildrop,
1348 strcmp (vec[1], "bboards") ? bb -> bb_archive : bb -> bb_file);
1349 if (setupaux (TRUE) == NOTOK)
1352 (void) respond (OK, "%s", vec[1]);
1353 multiline ("%s %d", bb -> bb_name, bb -> bb_maxima);
1356 if (strcmp (vec[1], "bboards"))
1357 return respond (NOTOK, "too few arguments to XTND \"%s\"", vec[1]);
1359 (void) respond (OK, "%s", vec[1]);
1360 for (bb = BBhead; bb; bb = bb -> bb_next) {
1362 if (!(bb -> bb_flags & BB_INVIS))
1363 multiline ("%s %d", bb -> bb_name, bb -> bb_maxima);
1365 while (bb = getbbaux (NULLCP))
1366 if (!(bb -> bb_flags & BB_INVIS))
1367 multiline ("%s %d", bb -> bb_name, bb -> bb_maxima);
1376 static int xtnd2 (vec)
1377 register char **vec;
1381 char buffer[BUFSIZ];
1382 register struct bboard *bb;
1385 return respond (NOTOK, "too few arguments to XTND \"%s\"", vec[1]);
1387 make_lower (vec[2], vec[2]);
1388 if ((bb = getbbaux (vec[2])) == NULL)
1389 return respond (NOTOK, "unknown BBoard: \"%s\"", vec[2]);
1391 (void) respond (OK, "%s", vec[1]);
1392 multiline ("%s", bb -> bb_name);
1395 for (ap = bb -> bb_aka; *ap; ap++) {
1396 (void) sprintf (cp, cp != buffer ? " %s" : "%s", *ap);
1399 multiline ("%s", buffer);
1401 multiline ("%s", bb -> bb_file);
1402 multiline ("%s", bb -> bb_archive);
1403 multiline ("%s", bb -> bb_info);
1404 multiline ("%s", bb -> bb_map);
1405 multiline ("%s", bb -> bb_passwd);
1408 for (ap = bb -> bb_leader; *ap; ap++) {
1409 (void) sprintf (cp, cp != buffer ? " %s" : "%s", *ap);
1412 multiline ("%s", buffer);
1414 multiline ("%s", bb -> bb_addr);
1415 multiline ("%s", bb -> bb_request);
1416 multiline ("%s", bb -> bb_relay);
1419 for (ap = bb -> bb_dist; *ap; ap++) {
1420 (void) sprintf (cp, cp != buffer ? " %s" : "%s", *ap);
1423 multiline ("%s", buffer);
1426 multiline ("0%o %d", bb -> bb_flags, bb -> bb_maxima);
1427 multiline ("%s", bb -> bb_date);
1436 static struct bboard *getbbaux (s)
1439 register struct bboard *bb;
1443 if (setbbinfo (BBOARDS, BBDB, 1))
1444 BBtime = getbbtime ();
1449 for (bb = BBhead; bb; bb = bb -> bb_next)
1450 if (strcmp (bb -> bb_name, s) == 0) {
1452 padvise (NULLCP, LOG_DEBUG, "getbbaux: \"%s\" from cache",
1458 while (bb = getbbent ()) {
1459 if ((bb = getbbcpy (bb)) == NULL)
1462 if (access (bb -> bb_file, 04) == NOTOK && errno == EACCES)
1463 bb -> bb_flags |= BB_INVIS;
1464 bb -> bb_mtime = stat (bb -> bb_info, &st) != NOTOK ? st.st_mtime : 0L;
1467 BBtail -> bb_next = bb;
1472 if (s == NULL || strcmp (bb -> bb_name, s) == 0) {
1474 padvise (NULLCP, LOG_DEBUG, "getbbaux: \"%s\" from scratch",
1485 static getbbmax (bb)
1486 register struct bboard *bb;
1490 char buffer[BUFSIZ];
1495 padvise (NULLCP, LOG_DEBUG, "getbbmax: \"%s\", 0%o, %d, %s",
1496 bb -> bb_name, bb -> bb_flags, bb -> bb_maxima, bb -> bb_date);
1498 if (!(bb -> bb_flags & BB_INVIS)
1499 && access (bb -> bb_file, 04) == NOTOK && errno == EACCES)
1500 bb -> bb_flags |= BB_INVIS;
1502 if (stat (bb -> bb_info, &st) == NOTOK
1503 || bb -> bb_mtime == st.st_mtime
1504 || (fp = fopen (bb -> bb_info, "r")) == NULL)
1506 bb -> bb_mtime = st.st_mtime;
1508 if (fgets (buffer, sizeof buffer, fp) && (i = atoi (buffer)) > 0)
1509 bb -> bb_maxima = i;
1510 if (!feof (fp) && fgets (buffer, sizeof buffer, fp)) {
1512 free (bb -> bb_date);
1513 if (cp = index (buffer, '\n'))
1515 bb -> bb_date = getcpy (buffer);
1521 padvise (NULLCP, LOG_DEBUG, "updated: \"%s\", 0%o, %d, %s",
1522 bb -> bb_name, bb -> bb_flags, bb -> bb_maxima, bb -> bb_date);
1528 static int xtnd3 (vec)
1529 register char **vec;
1532 return respond (NOTOK, "too few arguments to XTND \"%s\"", vec[1]);
1533 if ((_sc_width = atoi (vec[2])) < WIDTH / 2)
1534 _sc_width = WIDTH / 2;
1535 nfs = new_fs (NULLCP, vec[3], FORMAT);
1537 (void) free (scanl), scanl = NULL;
1539 return respond (OK, vec[1]);
1542 int sc_width () { return _sc_width; }
1548 static int quit (vec)
1554 d = dmsgs, n = nmsgs;
1556 if (quitaux (vec) == NOTOK)
1561 return respond (OK, "%s signing off", server);
1565 return respond (OK, "%s signing off (maildrop empty)", server);
1568 n ? "%s signing off (%d message%s, %d octets left)"
1569 : "%s signing off (maildrop empty)",
1570 server, n - d, n - d != 1 ? "s" : "", Msgs[0].m_size);
1574 static int quitaux (vec)
1584 nmsgs = dmsgs = rmsgs = 0;
1585 (void) lkfclose (dp, maildrop);
1595 static int quitfile (vec)
1602 char tmpfil[BUFSIZ],
1607 if(debug)padvise(NULLCP,LOG_DEBUG,"XXX: dmsgs=%d rmsgs=%d readonly=%d",
1608 dmsgs, rmsgs, Msgs[0].m_flags & MREAD);
1610 if (dmsgs == 0 || (Msgs[0].m_flags & MREAD))
1613 if (fstat (fileno (dp), &st) == NOTOK)
1614 return respond (NOTOK, "unable to stat file");
1615 if (mtime != st.st_mtime)
1616 return respond (NOTOK, "new messages have arrived, no update");
1617 mode = (int) (st.st_mode & 0777);
1619 if (nmsgs == dmsgs) {
1621 i = truncate (maildrop, 0);
1623 i = open (maildrop, O_WRONLY | O_TRUNC);
1624 if (i != NOTOK) (void) close (i);
1626 (void) unlink (map_name (maildrop));/* XXX */
1628 return respond (NOTOK, "unable to zero %s", maildrop);
1632 (void) strcpy (tmpfil, m_backup (maildrop));
1633 if ((md = mbx_open (tmpfil, st.st_uid, st.st_gid, mode)) == NOTOK)
1634 { char msgbuf0[256];
1635 sprintf(msgbuf0,"unable to create temporary file (%s)",tmpfil);
1636 return respond (NOTOK, msgbuf0);
1639 j = 0, tmpDR = Msgs[0].m_last;
1640 if(debug)padvise(NULLCP,LOG_DEBUG,"XXX: last=%d",Msgs[0].m_last);
1641 for (i = 1; i <= nmsgs; i++) {
1642 if (!(Msgs[i].m_flags & MDELE))
1647 if(debug)padvise(NULLCP,LOG_DEBUG,"XXX: last=%d",Msgs[0].m_last);
1649 for (i = 1; i <= nmsgs; i++)
1650 if (!(Msgs[i].m_flags & MDELE)
1651 && mbx_write (tmpfil, md, dp, Msgs[i].m_id, Msgs[0].m_last,
1652 Msgs[i].m_start, Msgs[i].m_stop, TRUE, debug)
1654 (void) mbx_close (tmpfil, md);
1655 (void) unlink (tmpfil);
1656 return respond (NOTOK, "error writing temporary file");
1658 (void) mbx_close (tmpfil, md);
1660 if ((i = rename (tmpfil, maildrop)) == OK) {
1661 (void) strcpy (map1, map_name (tmpfil));
1662 (void) strcpy (map2, map_name (maildrop));
1663 if (rename (map1, map2) == NOTOK) {
1664 (void) unlink (map1);
1665 (void) unlink (map2);
1670 return respond (NOTOK, "unable to rename maildrop");
1677 static struct vector *getvector (bp, vec)
1682 register struct vector *v;
1684 for (i = 0; i < NVEC; i++) {
1685 while (isspace (*bp))
1693 for (vec[i] = ++bp; *bp != '\0' && *bp != '"'; bp++)
1697 (void) strcpy (bp, bp + 1);
1703 (void) strcpy (bp - 1, bp);
1716 while (!isspace (*bp))
1723 (void) respond (NOTOK, "too many arguments");
1727 (void) respond (NOTOK, "null command");
1730 make_lower (vec[0], vec[0]);
1732 for (v = vectors; v -> v_cmd; v++)
1733 if (strcmp (v -> v_cmd, vec[0]) == 0 && v -> v_valid == mystate) {
1734 if (i < v -> v_min || v -> v_max < i) {
1735 (void) respond (NOTOK, "too %s arguments to \"%s\"",
1736 i < v -> v_min ? "few" : "many", vec[0]);
1743 (void) respond (NOTOK, "unknown command: \"%s\"", vec[0]);
1751 static int respond (code, fmt, a, b, c, d)
1760 char buffer[BUFSIZ];
1764 (void) sprintf (bp, "%s%s", code == OK ? "+OK" : "-ERR", fmt ? " " : "");
1770 (void) sprintf (bp, "%s%s", code == OK ? "+OK" : "-ERR",
1775 default: /* only happens in pop2 */
1781 (void) sprintf (bp, fmt, a, b, c, d);
1784 putline (buffer, output);
1792 static multiline (fmt, a, b, c, d)
1800 char buffer[BUFSIZ + TRMLEN];
1802 (void) strcpy (buffer, TRM);
1803 (void) sprintf (cp = (buffer + TRMLEN), fmt, a, b, c, d);
1804 if (strncmp (cp, TRM, TRMLEN) == 0)
1807 putline (cp, output);
1811 static multiend () {
1812 putline (TRM, output);
1817 static int getline (s, n, iop)
1826 while (--n > 0 && (c = fgetc (iop)) != EOF) {
1831 if ((*p++ = c) == '\n')
1836 if (c == EOF && p == s)
1841 padvise (NULLCP, LOG_DEBUG, "<--- %s", s);
1852 static putline (s, iop)
1856 (void) fprintf (iop, "%s\r\n", s);
1858 padvise (NULLCP, LOG_DEBUG, "---> %s", s);
1860 (void) fflush (iop);
1866 static TYPESIG pipeser (sig, code, sc)
1869 struct sigcontext *sc;
1871 padvise (NULLCP, LOG_WARNING, "lost connection");
1878 /* Some people don't want to use the POP delivery agent with Sendmail
1879 * if they're going to run POP. Sendmail writes maildrops in the old
1880 * UUCP format, and popd doesn't know how to read them. These people
1881 * really should do what the MH manual says -- run the pop delivery
1882 * agent and be done with it. Some things never die.
1884 * A real fix would be to make uip/dropsbr.c should use the same methods
1885 * as sbr/m_getfld.c to determine the format of maildrops and read &
1886 * write them. Unfortunately, it'll take a lot of work to bring it into
1887 * the fold. 20Mar90/JLR
1889 * I really really hate to add this, but this lets stuff popd read
1890 * UUCP style maildrops as well as MMDF (ctrl/A) style maildrops. It was
1891 * contributed by Steve Dempsey <steved@longs.LANCE.ColoState.Edu>.
1893 * Here's what he says:
1895 * Ideally, one should be able to do it with the mmdelim strings, but
1896 * the MH parser is not intelligent enough to do this. You have at
1897 * least a couple of choices:
1899 * - use aliases to deliver mail to POP users (user: user@pop) and
1900 * install the POP delivery agent - should work well with sendmail.
1901 * - fix the POP server!
1903 * We have all mail sent to one machine and users are given two options:
1905 * - MH on any machine.
1906 * - any user agent on the postoffice machine.
1908 * Most of our workstations run xmh and users find that to be sufficient.
1909 * New users are only taught to use MH, and a very few old timers stay
1910 * with BSD mail. In any case, several agents are available at the cost
1911 * of a telnet/rlogin if a user does not like MH.
1913 * I have made the changes to the POP server (MH-6.6/support/pop/popser.c)
1914 * to look for the `\n\nFrom ' delimiter instead of the ^A's, using some
1915 * code from the BSD agent. Context diff is included below. When this
1916 * is installed, you just go back to the normal localmail and get rid of
1917 * slocal completely.
1919 * I have not tried this modification with anything but the MH client,
1920 * but it should work. Nothing in the POP protocol changes; the server
1921 * just has different criteria for delimiting messages in the mailbox.
1922 * If you decide to use this, I'd like to know what happens.
1924 * Steve Dempsey, Center for Computer Assisted Engineering
1925 * Colorado State University, Fort Collins, CO 80523 +1 303 491 0630
1926 * INET: steved@longs.LANCE.ColoState.Edu, dempsey@handel.CS.ColoState.Edu
1927 * boulder!ccncsu!longs.LANCE.ColoState.Edu!steved, ...!ncar!handel!dempsey
1929 /* From: Jim Reid <jim@computer-science.strathclyde.ac.UK>
1931 * MH-6.7 does not support MMDF-style mailboxes with POP as claimed. It
1932 * appears that when code was added to popser.c to support UNIX-style
1933 * mailboxes, the old behaviour was lost. i.e. The new popd worked with
1934 * UNIX-style mailboxes, but not MMDF ones. Users would get "format error"
1935 * error messages if they tried to inc a remote MMDF-style mailbox because
1936 * the pop daemon didn't want to know or like the MMDF message delimiters.
1939 /* So... Now there's an incredible hack in mhconfig.c to define POPUUMBOX
1940 * in support/pop/Makefile if we're using Sendmail. This causes this
1941 * UUCP-mbox reading code to be used here. Ugh. 05Nov90/JLR
1946 /* from dropsbr.c - read from a mailbox - pop server version */
1948 /* ALMOST IDENTICAL to mbx_read */
1950 static int pmbx_read (fp, pos, drops, noisy)
1953 struct drop **drops;
1959 char buffer[BUFSIZ];
1960 register struct drop *cp,
1965 /* MTR: tsk, tsk, tsk... */
1966 (void) fseek (fp, pos, 0);
1967 if (fgets (buffer, sizeof buffer, fp)
1968 && strcmp (buffer, mmdlm1) == 0)
1969 return mbx_read (fp, pos, drops, noisy);
1971 /* get drop storage */
1972 pp = (struct drop *) calloc ((unsigned) (len = MAXFOLDER), sizeof *dp);
1975 padvise (NULLCP, LOG_DEBUG, "pmbx_read (%d, %ld, %d, %d)",
1976 fp, pos,drops,noisy);
1980 admonish (NULLCP, "unable to allocate drop storage");
1984 /* rewind drop file */
1985 (void) fseek (fp, pos, 0);
1988 padvise (NULLCP, LOG_DEBUG, "rewind maildrop");
1991 for (ep = (dp = pp) + len - 1; fgets (buffer, sizeof buffer, fp);) {
1994 /* if beginning of msg then mark it */
1996 if (p_ishead(buffer)) /*(strcmp (buffer, mmdlm1) == 0)*/ {
1997 /* (don't) inc pos to msg start, mark it */
1999 dp -> d_start = pos;
2000 pos += strlen(buffer); /* inc pos after marking head */
2003 /* didn't find it; mark it anyway */
2004 dp -> d_start = pos, pos += (long) strlen (buffer);
2006 /* count newlines and inc size if any found */
2007 for (bp = buffer; *bp; bp++, size++)
2012 /* read more lines... */
2013 while (fgets (buffer, sizeof buffer, fp) != NULL)
2016 if (p_ishead(buffer)) /*(strcmp (buffer, mmdlm2) == 0)*/ {
2019 (void) fseek (fp, pos, 0);
2024 /* add buffer size to pos */
2025 pos += (long) strlen (buffer);
2027 /* count newlines.... */
2028 for (bp = buffer; *bp; bp++, size++)
2033 if (dp -> d_start != pos) {
2034 /* do this if pos was actually incremented; got some text */
2036 dp -> d_size = size; /* save the stuff we got */
2041 /* (don't) advance pos */
2044 /* need more storage.... */
2046 register int curlen = dp - pp;
2048 cp = (struct drop *) realloc ((char *) pp,
2049 (unsigned) (len += MAXFOLDER) * sizeof *pp);
2052 admonish (NULLCP, "unable to allocate drop storage");
2056 dp = cp + curlen, ep = (pp = cp) + len - 1;
2060 /* return unused stuff */
2069 * The remainder of this file adapted from:
2071 * head.c 5.2 (Berkeley) 6/21/85
2075 char *l_from; /* The name of the sender */
2076 char *l_tty; /* His tty string (if any) */
2077 char *l_date; /* The entire date string */
2082 * See if position in a file is a mail header.
2083 * Return true if yes. Note the extreme pains to
2084 * accomodate all funny formats.
2087 #define NOSTR ((char *) 0) /* Null string pointer */
2088 static char *p_copyin();
2089 static char *p_copy();
2092 static p_ishead(buffer)
2097 char linebuf[BUFSIZ];
2098 char parbuf[BUFSIZ];
2100 strcpy(linebuf,buffer);
2103 if (linebuf[0]=='F')
2104 padvise (NULLCP, LOG_DEBUG, "ishead: '%s'",linebuf);
2106 if (strncmp("From ", cp, 5) != 0)
2109 padvise (NULLCP, LOG_DEBUG, "Fromline...");
2111 /* get full header */
2112 p_parse(cp, &hl, parbuf);
2114 if (hl.l_from == NOSTR || hl.l_date == NOSTR) {
2115 padvise (NULLCP, LOG_DEBUG, "Fromline...NODATE");
2119 if (!p_isdate(hl.l_date)) {
2120 padvise (NULLCP, LOG_DEBUG, "Fromline...BADDATE %s",
2125 /* I guess we got it! */
2126 padvise (NULLCP, LOG_DEBUG, "got a head.. ");
2132 * Split a headline into its useful components.
2133 * Copy the line into dynamic string space, then set
2134 * pointers into the copied line in the passed headline
2135 * structure. Actually, it scans.
2138 static p_parse(line, hl, pbuf)
2139 char line[], pbuf[];
2140 struct p_hdline *hl;
2142 register char *cp, *dp;
2145 char * p_nextword();
2154 * Skip the first "word" of the line, which should be "From"
2157 cp = p_nextword(cp, word);
2158 dp = p_nextword(cp, word);
2159 if (!(strcmp(word, "")==0))
2160 hl->l_from = p_copyin(word, &sp);
2163 if (strncmp(dp, "tty", 3) == 0) {
2164 cp = p_nextword(dp, word);
2165 hl->l_tty = p_copyin(word, &sp);
2167 hl->l_date = p_copyin(cp, &sp);
2173 hl->l_date = p_copyin(dp, &sp);
2177 * Copy the string on the left into the string on the right
2178 * and bump the right (reference) string pointer by the length.
2179 * Thus, dynamically allocate space in the right string, copying
2180 * the left string into it.
2184 p_copyin(src, space)
2188 register char *cp, *top;
2201 * Collect a liberal (space, tab delimited) word into the word buffer
2202 * passed. Also, return a pointer to the next word following that,
2203 * or (empty) if none follow.
2207 p_nextword(wp, wbuf)
2210 register char *cp, *cp2;
2212 if ((cp = wp) == NOSTR) {
2217 while (!any(*cp, " \t") && *cp != '\0')
2220 while (*cp != '\0' && *cp != '"')
2227 while (any(*cp, " \t"))
2235 * Copy str1 to str2, return pointer to null in str2.
2242 register char *s1, *s2;
2252 #define L 1 /* A lower case char */
2253 #define S 2 /* A space */
2254 #define D 3 /* A digit */
2255 #define O 4 /* An optional digit or space */
2256 #define C 5 /* A colon */
2257 #define N 6 /* A new line */
2258 #define U 7 /* An upper case char */
2260 static char p_ctypes[] =
2261 {U,L,L,S,U,L,L,S,O,D,S,D,D,C,D,D,C,D,D,S,D,D,D,D,0};
2262 /* T h u S e p 2 9 1 5 : 2 0 : 1 9 1 9 8 8 */
2264 static char p_tmztyp[] =
2265 {U,L,L,S,U,L,L,S,O,D,S,D,D,C,D,D,C,D,D,S,U,U,U,S,D,D,D,D,0};
2266 /* T h u S e p 2 9 1 5 : 2 0 : 1 9 M S T 1 9 8 8 */
2268 static p_isdate(date)
2274 if (p_cmatch(cp, p_ctypes))
2277 return(p_cmatch(cp, p_tmztyp));
2281 * Match the given string against the given template.
2282 * Return 1 if they match, 0 if they don't
2285 static p_cmatch(str, temp)
2288 register char *cp, *tp;
2293 while (*cp != '\0' && *tp != 0) {
2297 if (c < 'a' || c > 'z')
2302 if (c < 'A' || c > 'Z')
2317 if (c != ' ' && !isdigit(c))
2332 if ((*cp != '\0' && *cp != '\n') || *tp != 0)