Added all of the MH sources, including RCS files, in
[mmh] / docs / historical / mh-6.8.5 / support / pop / popser.c
1 /* popser.c - the POP service */
2 #ifndef lint
3 static char ident[]="@(#)$Id: popser.c,v 1.34 1995/12/07 18:54:35 jromine Exp $";
4 #endif
5
6 #include "../h/mh.h"
7 #include "../h/dropsbr.h"
8 #ifdef  MPOP
9 #ifdef  BPOP
10 #include "../h/formatsbr.h"
11 #include "../h/scansbr.h"
12 #endif
13 #endif /* MPOP */
14 #include "../zotnet/bboards.h"
15 #include <stdio.h>
16 #include "../zotnet/mts.h"
17 #include <ctype.h>
18 #include <errno.h>
19 #include <pwd.h>
20 #include <signal.h>
21 #include "syslog.h"
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #ifdef UNISTD
25 #include <unistd.h>
26 #endif
27 #ifdef KPOP
28 #include <krb.h>
29 #endif  /* KPOP */
30 #ifdef  SYS5
31 #include <fcntl.h>
32 #endif  /* SYS5 */
33 #ifdef  SHADOW
34 #include <shadow.h>
35 #endif  /* SHADOW */
36
37
38 #define TRUE    1
39 #define FALSE   0
40
41 #define NVEC    5
42
43 #ifndef POPSERVICE
44 #define POPSERVICE      "pop"
45 #endif
46
47 /* \f */
48
49 extern int  errno;
50
51 extern int  debug;
52 extern char myhost[];
53 extern char *myname;
54
55 #ifndef POP2
56 static enum state {
57     auth1, auth2, trans, update, halt, error
58 } mystate;
59 #else
60 static enum state {
61     auth1, auth2, trans, mbox, item, ack, update, halt, error
62 } mystate;
63 #endif
64
65
66 static int     user (), pass ();
67 #ifdef  BPOP
68 static          isguest(), getbbmax();
69 #ifndef MPOP
70 static  int     xtnd1(), xtnd2();
71 #else
72 static  int     xtnd1(), xtnd2(), xtnd3 ();
73 #endif /* MPOP */
74 #endif  /* BPOP */
75 #ifdef  RPOP
76 static int      rpop ();
77 #endif  /* RPOP */
78 #ifdef  APOP
79 static  int     apop ();
80 #endif
81 static int     status (), list (), retrieve (), delete (), reset ();
82 static int      top (), last ();
83 #ifdef  BPOP
84 static int      xtnd ();
85 #endif  /* BPOP */
86 static int     quit ();
87 #ifdef  POP2
88 static int      helo (), rdp2 (), acks (), ack2 (), fold (), nack ();
89 #endif  /* POP2 */
90
91 static struct vector {
92     char   *v_cmd;
93     int     v_min, v_max;
94     int     (*v_vec) ();
95     enum state v_valid;
96     enum state v_win, v_lose;
97 }               vectors[] = {
98     "user", 1, 1, user, auth1, auth2, auth1,
99     "pass", 1, 1, pass, auth2, trans, auth1,
100 #ifdef  RPOP
101     "rpop", 1, 1, rpop, auth2, trans, auth1,
102 #endif  /* RPOP */
103 #ifdef  APOP
104     "apop", 2, 2, apop, auth1, trans, auth1,
105 #endif
106     "quit", 0, 0, NULL, auth1, halt, halt,
107     "quit", 0, 0, NULL, auth2, halt, halt,
108
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,
115
116     "top",  2, 2, top,  trans, trans, trans,
117     "last", 0, 0, last, trans, trans, trans,
118 #ifdef  BPOP
119 #ifndef MPOP
120     "xtnd", 1, 2, xtnd, trans, trans, trans,
121 #else
122     "xtnd", 1, 3, xtnd, trans, trans, trans,
123 #endif /* MPOP */
124 #endif  /* BPOP */
125     "quit", 0, 0, quit, trans, halt, halt,
126
127 #ifdef  POP2
128     "helo", 2, 2, helo, auth1, mbox, auth1,
129
130     "fold", 1, 1, fold, mbox, mbox, mbox,
131     "quit", 0, 0, quit, mbox, halt, halt,
132     "read", 0, 1, rdp2, mbox, item, error,
133
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,
138
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,
143
144 #endif  /* POP2 */
145     NULL
146 };
147
148 static struct vector  *getvector ();
149
150 /* \f */
151
152 #ifdef  POP2
153 static int pop2 = NOTOK;        /* current pop2 msg, or NOTOK if pop3 */
154 #endif  /* POP2 */
155 #ifdef  DPOP
156 static int pop_uid;
157 static int pop_gid;
158 #endif  /* DPOP */
159
160 static int  rproto;
161 static char *hostname;
162 static char server[BUFSIZ];
163 static char timestamp[BUFSIZ];
164
165 static char username[BUFSIZ];
166
167 static char maildrop[BUFSIZ];
168 static int  mode;
169 static time_t mtime;
170 static FILE *dp;
171
172 static long lastseen;
173 static int  rmsgs;
174
175 #ifdef  BPOP
176 static int xtnded;
177
178 static int guest_uid;
179 static int guest_gid;
180
181 static struct bboard *BBhead = NULL;
182 static struct bboard *BBtail = NULL;
183
184 static long BBtime = 0L;
185
186 static struct bboard *getbbaux ();
187 #endif  /* BPOP */
188
189
190 struct Msg {                    /* Msgs[0] contains info for entire maildrop */
191     struct drop m_drop;
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
197
198     unsigned    m_flags;
199 #define MNULL   0x00
200 #define MDELE   0x01
201 #define MREAD   0x02
202 };
203
204 static int nMsgs = 0;
205 static struct Msg *Msgs = NULL;
206
207 static int  nmsgs;
208 static int  dmsgs;
209 #ifdef  MPOP
210 #ifdef  BPOP
211 static int   _sc_width = 0;
212 static char *nfs = NULL;
213 #endif
214 #endif /* MPOP */
215
216
217 #define TRM     "."
218 #define TRMLEN  (sizeof TRM - 1)
219 #define IAC     255
220
221 static TYPESIG    pipeser ();
222
223 FILE   *input;
224 FILE   *output;
225
226 #ifndef __STDC__
227 #ifdef  SYS5
228 struct passwd *getpwnam();
229 #endif
230 #endif
231
232 void    padvise (), padios ();
233 char   *crypt ();
234
235 #ifdef  POPUUMBOX
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();
240 #else
241 #define MBX_READ        mbx_read
242 #endif
243 extern  int     mbx_read ();
244
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();
248 /* \f */
249
250 popinit () {
251 #ifdef  BPOP
252     padvise (NULLCP, LOG_INFO, "initialize list of BBoards");
253
254     BBhead = BBtail = NULL;
255     while (getbbaux (NULLCP))
256         continue;
257 #endif  /* BPOP */
258 }
259
260 popassert () {
261 #ifdef  BPOP
262     register char **p;
263     register struct bboard *bb,
264                            *bp;
265
266     if (BBtime == getbbtime ())
267         return;
268
269     padvise (NULLCP, LOG_INFO, "list of BBoards has changed");
270
271     for (bb = BBhead; bb; bb = bp) {
272         bp = bb -> bb_next;
273
274         if (bb -> bb_name)
275             free (bb -> bb_name);
276         if (bb -> bb_file)
277             free (bb -> bb_file);
278         if (bb -> bb_archive)
279             free (bb -> bb_archive);
280         if (bb -> bb_info)
281             free (bb -> bb_info);
282         if (bb -> bb_map)
283             free (bb -> bb_map);
284         if (bb -> bb_passwd)
285             free (bb -> bb_passwd);
286         if (bb -> bb_date)
287             free (bb -> bb_date);
288         if (bb -> bb_addr)
289             free (bb -> bb_addr);
290         if (bb -> bb_request)
291             free (bb -> bb_request);
292         if (bb -> bb_relay)
293             free (bb -> bb_relay);
294
295         for (p = bb -> bb_aka; *p; p++)
296             free (*p);
297         free ((char *) bb -> bb_aka);
298
299         for (p = bb -> bb_leader; *p; p++)
300             free (*p);
301         free ((char *) bb -> bb_leader);
302
303         for (p = bb -> bb_dist; *p; p++)
304             free (*p);
305         free ((char *) bb -> bb_dist);
306
307         free ((char *) bb);
308     }
309
310     BBhead = BBtail = NULL;
311     while (getbbaux (NULLCP))
312         continue;
313 #endif  /* BPOP */
314 }
315
316 /* \f */
317
318 #ifdef KPOP
319 static char *kusername;
320
321 kpop (in, out, principal, rhost, auth)
322 int   in,
323       out;
324 char  *principal, *rhost;
325 int auth;
326 #else   /* KPOP */
327 pop (in, out, priv, rhost)
328 int     in,
329         out,
330         priv;
331 char   *rhost;
332 #endif  /* KPOP */
333 {
334     char    buffer[BUFSIZ],
335            *vec[NVEC + 1];
336 #if     defined (DPOP) || defined (BPOP)
337     register struct passwd *pw;
338 #endif  /* defined (DPOP) || defined (BPOP) */
339     register struct vector *v;
340
341     m_foil (NULLCP);
342     mts_init (myname);
343
344     hostname = rhost;
345 #ifdef KPOP
346     rproto = 1;
347     (void) sprintf (server, "%s KPOP server", myhost);
348 #else
349     rproto = priv;
350     (void) sprintf (server, "%s server", priv ? "RPOP" : "POP");
351 #endif  /* KPOP */
352
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);
356         return;
357     }
358     (void) signal (SIGPIPE, pipeser);
359 #ifdef KPOP
360     if (principal == NULLCP) {
361         char buf[512];
362         strcpy(buf,  "Authentication failed: ");
363         strcat(buf, krb_err_txt[auth]);
364         (void) respond (NOTOK, buf);
365         return;
366     }
367     kusername = principal;
368 #endif  /* KPOP */
369
370 #ifdef  DPOP
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");
374         return;
375     }
376     pop_uid = pw -> pw_uid;
377     pop_gid = pw -> pw_gid;
378 #endif  /* DPOP */
379 #ifdef  BPOP
380     if ((pw = getpwnam (popbbuser)) && pw -> pw_uid) {
381         guest_uid = pw -> pw_uid;
382         guest_gid = pw -> pw_gid;
383     }
384     else
385         guest_uid = guest_gid = 0;
386 #endif  /* BPOP */
387
388     {
389         long    clock;
390
391         (void) time (&clock);
392         (void) sprintf (timestamp, "<%d.%ld@%s>", getpid (), clock, myhost);
393     }
394     (void) respond (OK, "%s ready %s", server, timestamp);
395
396     for (mystate = auth1; mystate != halt && mystate != error;)
397         switch (getline (buffer, sizeof buffer, input)) {
398             case OK: 
399                 if ((v = getvector (buffer, vec)) == NULL)
400                     continue;
401                 mystate = (v -> v_vec ? (v -> v_vec) (vec)
402                         : respond (OK, NULLCP)) == OK
403                     ? v -> v_win
404                     : v -> v_lose;
405                 break;
406
407             case NOTOK: 
408             case DONE: 
409                 mystate = error;
410                 (void) respond (NOTOK, "%s signing off", server);
411                 break;
412         }
413 }
414
415 /* \f */
416 #ifdef  POP2
417 static int  helo (vec)          /* sort of "user" and "pass" */
418 register char  **vec;
419 {
420     pop2 = 0;                           /* now we're talkin' pop2! */
421     make_lower (username, vec[1]);      /* helo user pass */
422     return pass (++vec);                /* user pass */
423 }
424 #endif
425
426 static int  user (vec)
427 register char  **vec;
428 {
429     make_lower (username, vec[1]);
430 #ifdef KPOP
431     if (!strcmp(username, kusername))
432       return respond (OK, "Kerberos authentication succeeded. Send username as password (%s)", username);
433     else {
434         respond (NOTOK, "Wrong username supplied (%s vs. %s)",
435                  kusername, username);
436         return (NOTOK);
437     }
438 #else
439     return respond (OK, "password required for %s", username);
440 #endif
441 }
442
443 /* \f */
444
445 static int  pass (vec)
446 register char  **vec;
447 {
448     int guest = 0;
449 #ifndef DPOP
450     register struct passwd *pw;
451 #ifdef  SHADOW
452     register struct spwd *shpw;
453 #endif  /* SHADOW */
454 #else   /* DPOP */
455     register struct bboard *pw;
456 #endif  /* DPOP */
457
458 #ifdef KPOP
459 #ifndef DPOP
460     if ((pw = getpwnam (username)) != NULL)
461       return setup(pw, FALSE);
462     else
463       return respond (NOTOK, "no local password entry");
464 #else
465     {
466       static struct bboard entry;
467       static char entry_file[BUFSIZ] = "/usr/spool/pop";
468       
469       pw = &entry;
470       pw->bb_name = username;
471       strcat(entry_file, username);
472       pw->bb_file = entry_file;
473       return setup(pw, FALSE);
474     }
475 #endif
476 #else   /* KPOP */
477
478 #ifndef DPOP
479 #ifdef  BPOP
480     if (isguest ()) {
481 #ifdef  TRUSTED
482         static passwd gw;
483
484         gw.pw_name = popbbuser;
485         gw.pw_uid = guest_uid;
486         pw = &gw;
487 #endif  /* TRUSTED */
488         guest = 1;
489         goto anonymous;
490     }
491 #endif  /* BPOP */
492     if ((pw = getpwnam (username)) == NULL
493 #ifndef SHADOW
494             || *pw -> pw_passwd == NULL
495             || strcmp (crypt (vec[1], pw -> pw_passwd), pw -> pw_passwd)) {
496 #else   /* SHADOW */
497             || (shpw = getspnam (username)) == NULL
498             || *shpw -> sp_pwdp == NULL
499             || strcmp (crypt (vec[1], shpw -> sp_pwdp), shpw -> sp_pwdp)) {
500 #endif  /* SHADOW */
501 #ifdef  TRUSTED
502         trusted (0, hostname, NULLCP, 0, pw ? pw -> pw_name : username,
503             pw && pw -> pw_uid == 0, POPSERVICE, "tcp", NULL);
504 #endif  /* TRUSTED */
505         return respond (NOTOK, "login incorrect");
506     }
507 #else   /* DPOP */
508 #ifdef  BPOP
509     if (isguest ()) {
510         static struct bboard gw;
511
512         gw.bb_name = popbbuser;
513         pw = &gw;
514         guest = 1;
515         goto anonymous;
516     }
517 #endif  /* BPOP */
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)) {
522 #ifdef  TRUSTED
523         trusted (0, hostname, NULLCP, 0, pw ? pw -> bb_name : username,
524             0, POPSERVICE, "tcp", NULL);
525 #endif  /* TRUSTED */
526         return respond (NOTOK, "login incorrect");
527     }
528 #endif  /* DPOP */
529
530 #ifdef  BPOP
531 anonymous: ;
532 #endif  /* BPOP */
533 #ifdef  TRUSTED
534     if (trusted (1, hostname, NULLCP, 0, myhost,
535 #ifndef DPOP
536                 pw -> pw_name, pw -> pw_uid == 0,
537 #else   /* DPOP */
538                 pw -> bb_name, 0,
539 #endif  /* DPOP */
540                 POPSERVICE, "tcp", NULL)
541             == 0)
542         return respond (NOTOK, "permission denied");
543 #endif  /* TRUSTED */
544     return setup (pw, guest);
545 #endif /* KPOP */
546 }
547
548 /* \f */
549
550 #ifdef  BPOP
551 static  isguest () {
552     int     i;
553     register char  *cp;
554     char    buffer[BUFSIZ];
555     register FILE  *fp;
556
557     if (strcmp (username, popbbuser) || !guest_uid)
558         return FALSE;
559     if (popbblist == NULL || (fp = fopen (popbblist, "r")) == NULL)
560         return TRUE;
561
562     i = FALSE;
563     if (hostname)
564         while (fgets (buffer, sizeof buffer, fp)) {
565             if (cp = index (buffer, '\n'))
566                 *cp = NULL;
567             if (strcmp (buffer, hostname) == 0) {
568                 i = TRUE;
569                 break;
570             }
571         }
572
573     (void) fclose (fp);
574
575     return i;
576 }
577 #endif  /* BPOP */
578
579 /* \f */
580
581 #ifdef  RPOP
582 static int rpop (vec)
583 register char  **vec;
584 {
585 #ifndef DPOP
586     register struct passwd *pw;
587 #else   /* DPOP */
588     register int hostok = 0;
589     register char  *bp,
590                    *cp;
591     char    buffer[BUFSIZ];
592     register struct bboard *pw;
593 #endif  /* DPOP */
594
595 #ifndef DPOP
596     if (!rproto || (pw = getpwnam (username)) == NULL) {
597 #ifdef  TRUSTED
598         trusted (0, hostname, vec[1], 0, username, 0, "rpop", "tcp",
599             NULL);
600 #endif  /* TRUSTED */
601         return respond (NOTOK, "login incorrect");
602     }
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) {
606 #ifdef  TRUSTED
607         trusted (0, hostname, vec[1], 0, pw -> pw_name,
608            pw -> pw_uid == 0, "rpop", "tcp", NULL);
609 #endif  /* TRUSTED */
610         return respond (NOTOK, "permission denied");
611     }
612 #else   /* DPOP */
613     if (!rproto
614             || ((pw = getbbnam (username)) == NULL
615                 && (pw = getbbaka (username)) == NULL)) {
616 #ifdef  TRUSTED
617         trusted (0, hostname, vec[1], 0, username, 0, "rpop", "tcp",
618             NULL);
619 #endif  /* TRUSTED */
620         return respond (NOTOK, "login incorrect");
621     }
622 /*
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
626  */
627     (void) sprintf (buffer, "%s@%s", vec[1], hostname);
628     for (bp = pw -> bb_addr; bp; bp = cp) {
629         if ((cp = index (bp, ',')))
630             *cp = 0;
631         hostok = uleq (bp, buffer);
632         if (cp)
633             *cp++ = ',';
634         if (hostok)
635             break;
636     }
637     if (!hostok) {
638 #ifdef  TRUSTED
639         trusted (0, hostname, vec[1], 0, pw -> bb_name, 0, "rpop",
640             "tcp", NULL);
641 #endif  /* TRUSTED */
642         return respond (NOTOK, "permission denied");
643     }
644 #endif  /* DPOP */
645
646 #ifdef  TRUSTED
647     if (trusted (1, hostname, vec[1], 0, username,
648 #ifndef DPOP
649                 pw -> pw_uid == 0,
650 #else   /* DPOP */
651                 0,
652 #endif  /* DPOP */
653                 "rpop", "tcp", NULL)
654             == 0)
655         return respond (NOTOK, "permission denied");
656 #endif  /* TRUSTED */
657     return setup (pw, FALSE);
658 }
659 #endif  /* RPOP */
660
661 /* \f */
662
663 #ifdef  APOP
664 #include "popauth.h"
665 #include "../../uip/md5.c"
666 #include <ndbm.h>
667 #include <sys/file.h>
668 #ifdef  SYS5
669 #include <fcntl.h>
670 #endif
671
672
673 static int apop (vec)
674 register char  **vec;
675 {
676     register char *cp;
677     char    buffer[BUFSIZ];
678     register unsigned char *dp;
679     unsigned char *ep,
680                    digest[16];
681 #ifndef DPOP
682     register struct passwd *pw;
683 #else
684     register struct bboard *pw;
685 #endif
686     struct stat st;
687     datum   key,
688             value;
689     DBM    *db;
690     MD5_CTX mdContext;
691     struct authinfo auth;
692
693     (void) strcpy (username, vec[1]);
694
695 #ifndef DPOP
696     if ((pw = getpwnam (username)) == NULL
697             || *pw -> pw_passwd == NULL) {
698         return respond (NOTOK, "user invalid");
699     }
700 #else
701     if (((pw = getbbnam (username)) == NULL
702                 && (pw = getbbaka (username)) == NULL)
703             || *pw -> bb_passwd == NULL) {
704         return respond (NOTOK, "subscriber invalid");
705     }
706 #endif
707
708     if ((db = dbm_open (APOP, O_RDONLY, 0)) == NULL)
709         return respond (NOTOK, "POP authorization DB not available (%d)",
710                         errno);
711     if (fstat (dbm_pagfno (db), &st) != NOTOK
712             && (st.st_mode & 0777) != 0600) {
713         dbm_close (db);
714         return respond (NOTOK, "POP authorization DB has wrong mode (0%o)",
715                         st.st_mode & 0777);
716     }
717     if (flock (dbm_pagfno (db), LOCK_SH) == NOTOK) {
718         dbm_close (db);
719         return respond (NOTOK, "unable to lock POP authorization DB (%d)",
720                         errno);
721     }
722     key.dsize = strlen (key.dptr = vec[1]) + 1;
723     value = dbm_fetch (db, key);
724     if (value.dptr == NULL) {
725         dbm_close (db);
726         return respond (NOTOK, "not authorized");
727     }
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);
731
732     dbm_close (db);
733
734     MD5Init (&mdContext);
735     MD5Update (&mdContext, (unsigned char *) buffer,
736                (unsigned int) (strlen (timestamp) + auth.auth_secretlen));
737     MD5Final (digest, &mdContext);
738
739     cp = buffer;
740     for (ep = (dp = digest) + sizeof digest / sizeof digest[0];
741              dp < ep;
742              cp += 2)
743         (void) sprintf (cp, "%02x", *dp++ & 0xff);
744     *cp = NULL;
745
746     if (strcmp (vec[2], buffer))
747         return respond (NOTOK, "authentication failure");
748
749     return setup (pw, 0);
750 }
751 #endif
752
753 /* \f */
754
755 static int setup (pw, guest)
756 #ifndef DPOP
757 register struct passwd *pw;
758 #else   /* DPOP */
759 register struct bboard *pw;
760 #endif  /* DPOP */
761 int     guest;
762 {
763 #ifdef  BPOP
764     if (guest) {
765         (void) setgid (guest_gid);
766 #ifndef SYS5
767         (void) initgroups (popbbuser, guest_gid);
768 #endif  /* SYS5 */
769         (void) setuid (guest_uid);
770     }
771     else {
772 #endif  /* BPOP */
773 #ifndef DPOP
774         (void) setgid (pw -> pw_gid);
775 #ifndef SYS5
776         (void) initgroups (pw -> pw_name, pw -> pw_gid);
777 #endif  /* SYS5 */
778         (void) setuid (pw -> pw_uid);
779 #else   /* DPOP */
780         (void) setgid (pop_gid);
781 #ifndef SYS5
782         (void) initgroups (POPUID, pop_gid);
783 #endif  /* SYS5 */
784         (void) setuid (pop_uid);
785 #endif  /* DPOP */
786 #ifdef  BPOP
787     }
788 #endif  /* BPOP */
789
790 #ifndef DPOP
791     (void) sprintf (maildrop, "%s/%s",
792             mmdfldir && *mmdfldir ? mmdfldir : pw -> pw_dir,
793             mmdflfil && *mmdflfil ? mmdflfil : pw -> pw_name);
794 #else   /* DPOP */
795     (void) strcpy (maildrop, pw -> bb_file);
796 #endif  /* DPOP */
797
798     if (setupaux (guest) == NOTOK)
799         return NOTOK;
800
801 #ifdef  POP2
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);
806     }
807     else
808 #endif  /* POP2 */
809     return respond (OK,
810             nmsgs ? "maildrop has %d message%s (%d octets)" : "maildrop empty",
811             nmsgs, nmsgs != 1 ? "s" : "", Msgs[0].m_size);
812 }
813
814 /* \f */
815
816 static int  setupaux (readonly)
817 int     readonly;
818 {
819     register int    i,
820                     msgp;
821     struct stat st;
822
823 #ifdef  BPOP
824     xtnded = 0;
825 #endif  /* BPOP */
826     if ((dp = readonly ? fopen (maildrop, "r") : lkfopen (maildrop, "r"))
827             == NULL)
828         switch (errno) {
829             case ENOENT: 
830                 m_gMsgs (msgp = 0);
831                 goto no_mail;
832
833             default: 
834                 nmsgs = dmsgs = 0;
835                 return respond (NOTOK, "unable to %s maildrop: \"%s\"",
836                         readonly ? "read" : "lock", maildrop);
837         }
838
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);
842     }
843     else {
844         mode = 0600, mtime = 0;
845         msgp = 0;
846     }
847
848     if ((msgp = read_file (msgp ? Msgs[msgp].m_stop : 0L, msgp + 1)) < 1)
849         m_gMsgs (0);
850
851 no_mail: ;
852     lastseen = Msgs[0].m_last;
853 if(debug)padvise(NULLCP,LOG_DEBUG,"XXX: lastseen=%d",lastseen);
854     dmsgs = rmsgs = 0;
855     nmsgs = msgp;
856
857     Msgs[0].m_flags = readonly ? MREAD : MNULL;
858     Msgs[0].m_size = 0;
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;
864     }
865
866     return OK;
867 }
868
869 /* \f */
870
871 static int  read_map (file, pos)
872 char   *file;
873 long    pos;
874 {
875     register int    i,
876                     msgp;
877     register struct drop   *pp,
878                            *mp;
879     struct drop *rp;
880
881     if (debug)
882         padvise (NULLCP, LOG_DEBUG, "read_map (%s, %ld)", file, pos);
883
884     if ((i = map_read (file, pos, &rp, debug)) == 0)
885         return 0;
886
887     m_gMsgs (i);
888
889     Msgs[0].m_last = rp -> d_start;
890
891     msgp = 1;
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;
898     }
899     free ((char *) rp);
900
901     if (Msgs[0].m_last > msgp) {
902         if (debug)
903             padvise (NULLCP, LOG_DEBUG, "lastseen adjusted from %d to %d",
904                 Msgs[0].m_last, msgp);
905         Msgs[0].m_last = msgp;
906     }
907
908     return (msgp - 1);
909 }
910
911 /* \f */
912
913 static int  read_file (pos, msgp)
914 register long   pos;
915 register int    msgp;
916 {
917     register int    i;
918     register struct drop   *pp,
919                            *mp;
920     struct drop *rp;
921
922     if (debug)
923         padvise (NULLCP, LOG_DEBUG, "read_file (%ld, %d)",
924                 pos, msgp);
925
926     if ((i = MBX_READ (dp, pos, &rp, debug)) <= 0)
927         return (msgp - 1);
928
929     m_gMsgs ((msgp - 1) + i);
930
931     for (pp = rp; i-- > 0; msgp++, pp++) {
932         mp = &Msgs[msgp].m_drop;
933         mp -> d_id = 0;
934         mp -> d_size = pp -> d_size;
935         mp -> d_start = pp -> d_start;
936         mp -> d_stop = pp -> d_stop;
937     }
938     free ((char *) rp);
939
940     return (msgp - 1);
941 }
942
943 /* \f */
944
945 static m_gMsgs (n)
946 int     n;
947 {
948     if (debug)
949         padvise (NULLCP, LOG_DEBUG, "m_gMsgs (%d) 0x%x %d",
950                 n, Msgs, nMsgs);
951
952     if (Msgs == NULL) {
953         nMsgs = n + MAXFOLDER / 2;
954         Msgs = (struct Msg *) calloc ((unsigned) (nMsgs + 2), sizeof *Msgs);
955         if (Msgs == NULL)
956             padios (NULLCP, "unable to allocate Msgs structure");
957         return;
958     }
959
960     if (nMsgs >= n)
961         return;
962
963     nMsgs = n + MAXFOLDER / 2;
964     Msgs = (struct Msg *) realloc ((char *) Msgs,
965                                 (unsigned) (nMsgs + 2) * sizeof *Msgs);
966     if (Msgs == NULL)
967         padios (NULLCP, "unable to reallocate Msgs structure");
968 }
969
970 /* \f */
971
972 static int  pmbx_size (m)
973 register int     m;
974 {
975     register int    i;
976     register long   pos;
977
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')
981             i++;
982
983     return i;
984 }
985
986 /* \f */
987
988 /* ARGSUSED */
989
990 static int  status (vec)
991 char  **vec;
992 {
993     return respond (OK, "%d %d", nmsgs - dmsgs, Msgs[0].m_size);
994 }
995
996
997 #ifdef  POP2
998 static int  rdp2 (vec)          /* always returns OK */
999 char  **vec;
1000 {
1001     if (vec[1]) {
1002         if ((pop2 = atoi (vec[1])) <= 0)
1003             pop2 = 0;
1004     }
1005     else if (pop2 == 0)
1006         return NOTOK;           /* close 'em down */
1007
1008     if (pop2 <= 0 || pop2 > nmsgs) {
1009         pop2 = 0;
1010         return respond ('=', "0 no message"); 
1011     }
1012     if (Msgs[pop2].m_flags & MDELE) {
1013         pop2 = 0;
1014         return respond ('=', "0 message %d is deleted", pop2);
1015     }
1016
1017     return respond ('=', "%d (message %d)", Msgs[pop2].m_size, pop2);
1018 }
1019
1020 static int  ack2 (vec)
1021 char   **vec;
1022 {
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;
1026         dmsgs++;
1027     }
1028
1029     if (pop2) {         /* a current msg */
1030         rmsgs++;                        /* mark this one as read */
1031         if (++pop2 > nmsgs)
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;
1037     }
1038     return rdp2 (vec);          /* vec = { "acks", 0 } */
1039 }
1040
1041 static int  fold (vec)
1042 register char  **vec;
1043 {
1044     pop2 = 0;
1045
1046 #ifdef  notdef          
1047
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.
1050  * 16Nov90/JLR
1051  */
1052    
1053     if (quitaux (NULLVP) == NOTOK)
1054         return respond ('#', "0 unable to close folder");
1055     
1056     (void) sprintf (maildrop, vec[1]);
1057     if (setupaux (access (maildrop, 2) ? 1 : 0) == NOTOK)
1058         return respond ('#', "0 unable to read %s", maildrop);
1059
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);
1063     
1064 #endif
1065
1066     respond ('#', "0 unable to change folders");
1067     return NOTOK;
1068 }
1069 #endif  /* POP2 */
1070
1071 static int  list (vec)
1072 register char  **vec;
1073 {
1074     register int    i;
1075
1076     if (vec[1]) {
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);
1081
1082 #ifndef BPOP
1083         return respond (OK, "%d %d", i, Msgs[i].m_size);
1084 #else   /* BPOP */
1085 #ifdef  MPOP
1086         if (nfs && !xtnded) {
1087             char   *cp;
1088
1089             (void) fseek (dp, Msgs[i].m_start, 0);
1090
1091             switch (scan (dp, i, 0, nfs, 0, 0, 0,
1092                     0, NULLCP, (long) Msgs[i].m_size, 0)) {
1093                 case SCNMSG:
1094                 case SCNENC:
1095                 case SCNERR:
1096                     if (cp = index (scanl, '\n'))
1097                         *cp = NULL;
1098                     return respond (OK, "%d %d #%s",
1099                                     i, Msgs[i].m_size, scanl);
1100
1101                 case SCNEOF:
1102                     return respond (OK, "%d %d #%*d  empty",
1103                                     i, Msgs[i].m_size, DMAXFOLDER, i);
1104
1105                 default:
1106                     break;
1107             }
1108         }
1109 #endif /* MPOP */
1110         return respond (OK, xtnded ? "%d %d %d" : "%d %d",
1111                         i, Msgs[i].m_size, Msgs[i].m_id);
1112 #endif  /* BPOP */
1113     }
1114
1115     (void) respond (OK, "%d message%s (%d octets)",
1116             nmsgs - dmsgs, nmsgs - dmsgs != 1 ? "s" : "",
1117             Msgs[0].m_size);
1118     for (i = 1; i <= nmsgs; i++)
1119         if (!(Msgs[i].m_flags & MDELE)) {
1120 #ifndef BPOP
1121             multiline ("%d %d", i, Msgs[i].m_size);
1122 #else   /* BPOP */
1123 #ifdef  MPOP
1124             if (nfs && !xtnded) {
1125                 char   *cp;
1126
1127                 (void) fseek (dp, Msgs[i].m_start, 0);
1128
1129                 switch (scan (dp, i, 0, nfs, 0, 0, 0, 
1130                         0, NULLCP, (long) Msgs[i].m_size, 0)) {
1131                     case SCNMSG:
1132                     case SCNENC:
1133                     case SCNERR:
1134                         if (cp = index (scanl, '\n'))
1135                             *cp = NULL;
1136                         multiline ("%d %d #%s",
1137                                    i, Msgs[i].m_size, scanl);
1138                         continue;
1139
1140                     case SCNEOF:
1141                         multiline ("%d %d #%*d  empty",
1142                                    i, Msgs[i].m_size, DMAXFOLDER, i);
1143                         continue;
1144
1145                     default:
1146                         break;
1147                 }
1148             }
1149 #endif /* MPOP */
1150             multiline (xtnded ? "%d %d %d" : "%d %d",
1151                        i, Msgs[i].m_size, Msgs[i].m_id);
1152 #endif  /* BPOP */
1153         }
1154     multiend ();
1155
1156     return OK;
1157 }
1158
1159 /* \f */
1160
1161 static int  retrieve (vec)
1162 register char  **vec;
1163 {
1164     register int    i;
1165     register long   pos;
1166     register char  *cp;
1167     char    buffer[BUFSIZ];
1168
1169 #ifdef  POP2
1170   if (pop2 == 0)
1171     return NOTOK;
1172   else if (pop2 == NOTOK) {
1173 #endif
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);
1178
1179     (void) respond (OK, "%d octets", Msgs[i].m_size);
1180 #ifdef  POP2
1181   }
1182   else          /* if called by pop2, vec = { "retr", 0 } */
1183     i = pop2;
1184 #endif
1185
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')
1190             *cp = 0;
1191         multiline ("%s", buffer);
1192     }
1193 #ifdef  POP2
1194   if (pop2 == NOTOK) {          /* then multiend */
1195 #endif
1196     multiend ();
1197
1198     if (i > Msgs[0].m_last) {
1199         Msgs[0].m_last = i; 
1200         rmsgs++;
1201     }
1202 #ifdef  POP2
1203   }
1204 #endif
1205
1206     return OK;
1207 }
1208
1209 /* \f */
1210
1211 static int  delete (vec)
1212 register char   **vec;
1213 {
1214     register int    i;
1215
1216     if (Msgs[0].m_flags & MREAD)
1217         return respond (NOTOK, "maildrop is read-only");
1218
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);
1223
1224     Msgs[i].m_flags |= MDELE;
1225     Msgs[0].m_size -= Msgs[i].m_size;
1226     dmsgs++;
1227
1228     if (i > Msgs[0].m_last)
1229         Msgs[0].m_last = i;
1230
1231     return respond (OK, "message %d deleted (%d octets)", i, Msgs[i].m_size);
1232 }
1233
1234
1235 static int  reset (vec)
1236 char   **vec;
1237 {
1238     register int    i;
1239
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;
1244             dmsgs--;
1245         }
1246
1247     Msgs[0].m_last = lastseen;
1248
1249 #ifdef  MPOP
1250 #ifdef  BPOP
1251     if (nfs) {
1252         if (scanl)
1253             free (scanl), scanl = NULL;
1254         free (nfs), nfs = NULL;
1255     }
1256 #endif
1257 #endif /* MPOP */
1258
1259     return status (vec);
1260 }
1261
1262 /* \f */
1263
1264 static int  top (vec)
1265 register char  **vec;
1266 {
1267     register int    i,
1268                     j,
1269                     body,
1270                     lines;
1271     register long   pos;
1272     register char  *cp;
1273     char    buffer[BUFSIZ];
1274
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]);
1281
1282     (void) respond (OK, vec[0]);
1283
1284     body = lines = 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')
1289             *cp = 0;
1290         if (body) {
1291             if (lines++ >= j)
1292                 break;
1293         }
1294         else
1295             if (*buffer == 0)
1296                 body++;
1297         multiline ("%s", buffer);
1298     }
1299     multiend ();
1300
1301     return OK;
1302 }
1303
1304 /* \f */
1305
1306 /* ARGSUSED */
1307
1308 static int  last (vec)  
1309 char  **vec;
1310 {
1311     return respond (OK, "%d is the last msg seen", Msgs[0].m_last);
1312 }
1313
1314 /* \f */
1315
1316 #ifdef  BPOP
1317 static int  xtnd (vec)
1318 register char    **vec;
1319 {
1320     make_lower (vec[1], vec[1]);
1321
1322     if (strcmp (vec[1], "bboards") == 0 || strcmp (vec[1], "archive") == 0)
1323         return xtnd1 (vec);
1324     if (strcmp (vec[1], "x-bboards") == 0)
1325         return xtnd2 (vec);
1326 #ifdef  MPOP
1327     if (strcmp (vec[1], "scan") == 0)
1328         return xtnd3 (vec);
1329 #endif /* MPOP */
1330
1331     return respond (NOTOK, "unknown XTND command: \"%s\"", vec[1]);
1332 }
1333
1334
1335 static int  xtnd1 (vec)
1336 register char    **vec;
1337 {
1338     register struct bboard *bb;
1339
1340     if (vec[2]) {
1341         make_lower (vec[2], vec[2]);
1342         if ((bb = getbbaux (vec[2])) == NULL)
1343             return respond (NOTOK, "unknown BBoard: \"%s\"", vec[2]);
1344
1345         if (quitaux (NULLVP) == NOTOK)
1346             return NOTOK;
1347         (void) strcpy (maildrop,
1348                 strcmp (vec[1], "bboards") ? bb -> bb_archive : bb -> bb_file);
1349         if (setupaux (TRUE) == NOTOK)
1350             return NOTOK;
1351         xtnded++;
1352         (void) respond (OK, "%s", vec[1]);
1353         multiline ("%s %d", bb -> bb_name, bb -> bb_maxima);
1354     }
1355     else {
1356         if (strcmp (vec[1], "bboards"))
1357             return respond (NOTOK, "too few arguments to XTND \"%s\"", vec[1]);
1358
1359         (void) respond (OK, "%s", vec[1]);
1360         for (bb = BBhead; bb; bb = bb -> bb_next) {
1361             getbbmax (bb);
1362             if (!(bb -> bb_flags & BB_INVIS))
1363                 multiline ("%s %d", bb -> bb_name, bb -> bb_maxima);
1364         }
1365         while (bb = getbbaux (NULLCP))
1366             if (!(bb -> bb_flags & BB_INVIS))
1367                 multiline ("%s %d", bb -> bb_name, bb -> bb_maxima);
1368     }
1369     multiend ();
1370
1371     return OK;
1372 }
1373
1374 /* \f */
1375
1376 static int  xtnd2 (vec)
1377 register char     **vec;
1378 {
1379     register char  *cp,
1380                   **ap;
1381     char    buffer[BUFSIZ];
1382     register struct bboard *bb;
1383
1384     if (vec[2] == NULL)
1385         return respond (NOTOK, "too few arguments to XTND \"%s\"", vec[1]);
1386
1387     make_lower (vec[2], vec[2]);
1388     if ((bb = getbbaux (vec[2])) == NULL)
1389         return respond (NOTOK, "unknown BBoard: \"%s\"", vec[2]);
1390
1391     (void) respond (OK, "%s", vec[1]);
1392     multiline ("%s", bb -> bb_name);
1393
1394     cp = buffer;
1395     for (ap = bb -> bb_aka; *ap; ap++) {
1396         (void) sprintf (cp, cp != buffer ? " %s" : "%s", *ap);
1397         cp += strlen (cp);
1398     }
1399     multiline ("%s", buffer);
1400
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);
1406
1407     cp = buffer;
1408     for (ap = bb -> bb_leader; *ap; ap++) {
1409         (void) sprintf (cp, cp != buffer ? " %s" : "%s", *ap);
1410         cp += strlen (cp);
1411     }
1412     multiline ("%s", buffer);
1413
1414     multiline ("%s", bb -> bb_addr);
1415     multiline ("%s", bb -> bb_request);
1416     multiline ("%s", bb -> bb_relay);
1417
1418     cp = buffer;
1419     for (ap = bb -> bb_dist; *ap; ap++) {
1420         (void) sprintf (cp, cp != buffer ? " %s" : "%s", *ap);
1421         cp += strlen (cp);
1422     }
1423     multiline ("%s", buffer);
1424
1425     getbbmax (bb);
1426     multiline ("0%o %d", bb -> bb_flags, bb -> bb_maxima);
1427     multiline ("%s", bb -> bb_date);
1428
1429     multiend ();
1430
1431     return OK;
1432 }
1433
1434 /* \f */
1435
1436 static struct bboard *getbbaux (s)
1437 register char   *s;
1438 {
1439     register struct bboard *bb;
1440     struct stat st;
1441
1442     if (BBhead == NULL)
1443         if (setbbinfo (BBOARDS, BBDB, 1))
1444             BBtime = getbbtime ();
1445         else
1446             return NULL;
1447
1448     if (s != NULLCP)
1449         for (bb = BBhead; bb; bb = bb -> bb_next)
1450             if (strcmp (bb -> bb_name, s) == 0) {
1451                 if (debug)
1452                     padvise (NULLCP, LOG_DEBUG, "getbbaux: \"%s\" from cache",
1453                             bb -> bb_name);
1454                 getbbmax (bb);
1455                 return bb;
1456             }
1457
1458     while (bb = getbbent ()) {
1459         if ((bb = getbbcpy (bb)) == NULL)
1460             return NULL;
1461
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;
1465
1466         if (BBtail != NULL)
1467             BBtail -> bb_next = bb;
1468         if (BBhead == NULL)
1469             BBhead = bb;
1470         BBtail = bb;
1471
1472         if (s == NULL || strcmp (bb -> bb_name, s) == 0) {
1473             if (s && debug)
1474                 padvise (NULLCP, LOG_DEBUG, "getbbaux: \"%s\" from scratch",
1475                         bb -> bb_name);
1476             return bb;
1477         }
1478     }
1479
1480     return NULL;
1481 }
1482
1483 /* \f */
1484
1485 static  getbbmax (bb)
1486 register struct bboard *bb;
1487 {
1488     int     i;
1489     register char  *cp;
1490     char    buffer[BUFSIZ];
1491     struct stat st;
1492     register    FILE * fp;
1493
1494     if (debug)
1495         padvise (NULLCP, LOG_DEBUG, "getbbmax: \"%s\", 0%o, %d, %s",
1496                 bb -> bb_name, bb -> bb_flags, bb -> bb_maxima, bb -> bb_date);
1497
1498     if (!(bb -> bb_flags & BB_INVIS)
1499             && access (bb -> bb_file, 04) == NOTOK && errno == EACCES)
1500         bb -> bb_flags |= BB_INVIS;
1501
1502     if (stat (bb -> bb_info, &st) == NOTOK
1503             || bb -> bb_mtime == st.st_mtime
1504             || (fp = fopen (bb -> bb_info, "r")) == NULL)
1505         return;
1506     bb -> bb_mtime = st.st_mtime;
1507
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)) {
1511         if (bb -> bb_date)
1512             free (bb -> bb_date);
1513         if (cp = index (buffer, '\n'))
1514             *cp = NULL;
1515         bb -> bb_date = getcpy (buffer);
1516     }
1517
1518     (void) fclose (fp);
1519
1520     if (debug)
1521         padvise (NULLCP, LOG_DEBUG, "updated: \"%s\", 0%o, %d, %s",
1522                 bb -> bb_name, bb -> bb_flags, bb -> bb_maxima, bb -> bb_date);
1523 }
1524
1525 /* \f */
1526
1527 #ifdef  MPOP
1528 static int xtnd3 (vec)
1529 register char **vec;
1530 {
1531     if (vec[2] == NULL)
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);
1536     if (scanl)
1537         (void) free (scanl), scanl = NULL;
1538
1539     return respond (OK, vec[1]);
1540 }
1541
1542 int     sc_width () { return _sc_width; }
1543 #endif /* MPOP */
1544 #endif  /* BPOP */
1545
1546 /* \f */
1547
1548 static int  quit (vec)
1549 char   **vec;
1550 {
1551     int     d,
1552             n;
1553
1554     d = dmsgs, n = nmsgs;
1555
1556     if (quitaux (vec) == NOTOK)
1557         return NOTOK;
1558
1559 #ifdef  BPOP
1560     if (xtnded)
1561         return respond (OK, "%s signing off", server);
1562 #endif  /* BPOP */
1563
1564     if (n == d)
1565         return respond (OK, "%s signing off (maildrop empty)", server);
1566
1567     return respond (OK,
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);
1571 }
1572
1573
1574 static int  quitaux (vec)
1575 char   **vec;
1576 {
1577     int     i;
1578
1579     if (dp == NULL)
1580         return OK;
1581
1582     i = quitfile (vec);
1583
1584     nmsgs = dmsgs = rmsgs = 0;
1585     (void) lkfclose (dp, maildrop);
1586     dp = NULL;
1587
1588     return i;
1589 }
1590
1591 /* \f */
1592
1593 /* ARGSUSED */
1594
1595 static int  quitfile (vec)
1596 char   **vec;
1597 {
1598     register int    i,
1599                     j,
1600                     tmpDR,
1601                     md;
1602     char    tmpfil[BUFSIZ],
1603             map1[BUFSIZ],
1604             map2[BUFSIZ];
1605     struct stat st;
1606
1607 if(debug)padvise(NULLCP,LOG_DEBUG,"XXX: dmsgs=%d rmsgs=%d readonly=%d",
1608                  dmsgs, rmsgs, Msgs[0].m_flags & MREAD);
1609
1610     if (dmsgs == 0 || (Msgs[0].m_flags & MREAD))
1611         return OK;
1612
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);
1618
1619     if (nmsgs == dmsgs) {
1620 #ifndef SYS5
1621         i = truncate (maildrop, 0);
1622 #else   /* SYS5 */
1623         i = open (maildrop, O_WRONLY | O_TRUNC);
1624         if (i != NOTOK) (void) close (i);
1625 #endif  /* SYS5 */
1626         (void) unlink (map_name (maildrop));/* XXX */
1627         if (i == NOTOK)
1628             return respond (NOTOK, "unable to zero %s", maildrop);
1629         return OK;
1630     }
1631
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);
1637           }
1638
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))
1643             j++;
1644         if (i == tmpDR)
1645             Msgs[0].m_last = j;
1646     }
1647 if(debug)padvise(NULLCP,LOG_DEBUG,"XXX: last=%d",Msgs[0].m_last);
1648
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)
1653                                 == NOTOK) {
1654             (void) mbx_close (tmpfil, md);
1655             (void) unlink (tmpfil);
1656             return respond (NOTOK, "error writing temporary file");
1657         }
1658     (void) mbx_close (tmpfil, md);
1659
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);
1666         }
1667     }
1668
1669     if (i == NOTOK)
1670         return respond (NOTOK, "unable to rename maildrop");
1671
1672     return OK;
1673 }
1674
1675 /* \f */
1676
1677 static struct vector   *getvector (bp, vec)
1678 register char   *bp,
1679               **vec;
1680 {
1681     register int    i;
1682     register struct vector *v;
1683
1684     for (i = 0; i < NVEC; i++) {
1685         while (isspace (*bp))
1686             *bp++ = 0;
1687         if (*bp == 0) {
1688             vec[i] = NULL;
1689             break;
1690         }
1691
1692         if (*bp == '"') {
1693             for (vec[i] = ++bp; *bp != '\0' && *bp != '"'; bp++)
1694                 if (*bp == '\\') {
1695                     switch (*++bp) {
1696                         case 'n':
1697                             (void) strcpy (bp, bp + 1);
1698                             *--bp = '\n';
1699                             break;
1700
1701                         case '\\':
1702                         case '"':
1703                             (void) strcpy (bp - 1, bp);
1704                             /* and fall... */
1705                         default:
1706                             bp--;
1707                             break;
1708                     }
1709                 }
1710             if (*bp == '"')
1711                 *bp++ = '\0';
1712             continue;
1713         }
1714         
1715         vec[i] = bp;
1716         while (!isspace (*bp))
1717             bp++;
1718     }
1719     i--;
1720     vec[NVEC] = NULL;
1721
1722     if (*bp != 0) {
1723         (void) respond (NOTOK, "too many arguments");
1724         return NULL;
1725     }
1726     if (*vec[0] == 0) {
1727         (void) respond (NOTOK, "null command");
1728         return NULL;
1729     }
1730     make_lower (vec[0], vec[0]);
1731
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]);
1737                 return NULL;
1738             }
1739             else
1740                 return v;
1741         }
1742
1743     (void) respond (NOTOK, "unknown command: \"%s\"", vec[0]);
1744     return NULL;
1745 }
1746
1747 /* \f */
1748
1749 /* VARARGS2 */
1750
1751 static int  respond (code, fmt, a, b, c, d)
1752 char   *fmt,
1753        *a,
1754        *b,
1755        *c,
1756        *d;
1757 int     code;
1758 {
1759     register char  *bp;
1760     char    buffer[BUFSIZ];
1761
1762     bp = buffer;
1763 #ifndef POP2
1764     (void) sprintf (bp, "%s%s", code == OK ? "+OK" : "-ERR", fmt ? " " : "");
1765     bp += strlen (bp);
1766 #else
1767     switch (code) {
1768         case OK:
1769         case NOTOK:
1770             (void) sprintf (bp, "%s%s", code == OK ? "+OK" : "-ERR",
1771                     fmt ? " " : "");
1772             bp += strlen (bp);
1773             break;
1774
1775         default:                /* only happens in pop2 */
1776             *bp++ = code;
1777             code = OK;
1778     }
1779 #endif
1780     if (fmt) {
1781         (void) sprintf (bp, fmt, a, b, c, d);
1782         bp += strlen (bp);
1783     }
1784     putline (buffer, output);
1785
1786     return code;
1787 }
1788
1789
1790 /* VARARGS1 */
1791
1792 static  multiline (fmt, a, b, c, d)
1793 char   *fmt,
1794        *a,
1795        *b,
1796        *c,
1797        *d;
1798 {
1799     register char  *cp;
1800     char    buffer[BUFSIZ + TRMLEN];
1801
1802     (void) strcpy (buffer, TRM);
1803     (void) sprintf (cp = (buffer + TRMLEN), fmt, a, b, c, d);
1804     if (strncmp (cp, TRM, TRMLEN) == 0)
1805         cp = buffer;
1806
1807     putline (cp, output);
1808 }
1809
1810
1811 static  multiend () {
1812     putline (TRM, output);
1813 }
1814
1815 /* \f */
1816
1817 static int  getline (s, n, iop)
1818 register char  *s;
1819 register int    n;
1820 register FILE  *iop;
1821 {
1822     register int    c;
1823     register char  *p;
1824
1825     p = s;
1826     while (--n > 0 && (c = fgetc (iop)) != EOF) {
1827         while (c == IAC) {
1828             (void) fgetc (iop);
1829             c = fgetc (iop);
1830         }
1831         if ((*p++ = c) == '\n')
1832             break;
1833     }
1834     if (ferror (iop))
1835         return NOTOK;
1836     if (c == EOF && p == s)
1837         return DONE;
1838     if (debug) {
1839         if (*--p == '\n')
1840             *p = 0;
1841         padvise (NULLCP, LOG_DEBUG, "<--- %s", s);
1842         if (*p == 0)
1843             *p = '\n';
1844         p++;
1845     }
1846     *p++ = 0;
1847
1848     return OK;
1849 }
1850
1851
1852 static  putline (s, iop)
1853 register char   *s;
1854 register FILE   *iop;
1855 {
1856     (void) fprintf (iop, "%s\r\n", s);
1857     if (debug)
1858         padvise (NULLCP, LOG_DEBUG, "---> %s", s);
1859
1860     (void) fflush (iop);
1861 }
1862
1863
1864 /* ARGSUSED */
1865
1866 static TYPESIG pipeser (sig, code, sc)
1867 int     sig;
1868 long    code;
1869 struct sigcontext *sc;
1870 {
1871     padvise (NULLCP, LOG_WARNING, "lost connection");
1872
1873     _exit (NOTOK);
1874 }
1875
1876 /* \f */
1877
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.
1883  *
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
1888  * 
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>.
1892  *
1893  * Here's what he says:
1894  * 
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:
1898  * 
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!
1902  * 
1903  * We have all mail sent to one machine and users are given two options:
1904  * 
1905  *   - MH on any machine.
1906  *   - any user agent on the postoffice machine.
1907  * 
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.
1912  * 
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.
1918  * 
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.
1923  * 
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
1928  */
1929 /* From:    Jim Reid <jim@computer-science.strathclyde.ac.UK>
1930  * 
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.
1937  */
1938
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
1942  */
1943
1944 /* \f */
1945 #ifdef  POPUUMBOX
1946 /* from dropsbr.c - read from a mailbox - pop server version */
1947
1948 /* ALMOST IDENTICAL to mbx_read */
1949
1950 static  int     pmbx_read (fp, pos, drops, noisy)
1951 register FILE  *fp;
1952 register long   pos;
1953 struct drop **drops;
1954 int     noisy;
1955 {
1956     register int    len,
1957                     size;
1958     register char  *bp;
1959     char    buffer[BUFSIZ];
1960     register struct drop   *cp,
1961                            *dp,
1962                            *ep,
1963                            *pp;
1964
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);
1970
1971     /* get drop storage */
1972     pp = (struct drop  *) calloc ((unsigned) (len = MAXFOLDER), sizeof *dp);
1973
1974     if (debug)
1975         padvise (NULLCP, LOG_DEBUG, "pmbx_read (%d, %ld, %d, %d)",
1976                 fp, pos,drops,noisy);
1977
1978     if (pp == NULL) {
1979         if (noisy)
1980             admonish (NULLCP, "unable to allocate drop storage");
1981         return NOTOK;
1982     }
1983
1984     /* rewind drop file */
1985     (void) fseek (fp, pos, 0);
1986
1987     if (debug)
1988         padvise (NULLCP, LOG_DEBUG, "rewind maildrop");
1989
1990     /* read a buffer */
1991     for (ep = (dp = pp) + len - 1; fgets (buffer, sizeof buffer, fp);) {
1992         size = 0;
1993
1994         /* if beginning of msg then mark it */
1995
1996         if (p_ishead(buffer)) /*(strcmp (buffer, mmdlm1) == 0)*/ {
1997             /* (don't) inc pos to msg start, mark it */
1998             /*pos += ld1;*/
1999             dp -> d_start = pos;
2000             pos += strlen(buffer);  /* inc pos after marking head */
2001         }
2002         else {
2003             /* didn't find it; mark it anyway */
2004             dp -> d_start = pos, pos += (long) strlen (buffer);
2005
2006             /* count newlines and inc size if any found */
2007             for (bp = buffer; *bp; bp++, size++)
2008                 if (*bp == '\n')
2009                     size++;
2010         }
2011
2012         /* read more lines... */
2013         while (fgets (buffer, sizeof buffer, fp) != NULL)
2014
2015             /* found end? */
2016             if (p_ishead(buffer)) /*(strcmp (buffer, mmdlm2) == 0)*/ {
2017
2018                 /* out of loop */
2019                 (void) fseek (fp, pos, 0);
2020                 break;
2021
2022             }
2023             else {
2024                 /* add buffer size to pos */
2025                 pos += (long) strlen (buffer);
2026
2027                 /* count newlines.... */
2028                 for (bp = buffer; *bp; bp++, size++)
2029                     if (*bp == '\n')
2030                         size++;
2031             }
2032
2033         if (dp -> d_start != pos) {
2034             /* do this if pos was actually incremented; got some text */
2035             dp -> d_id = 0;
2036             dp -> d_size = size;  /* save the stuff we got */
2037             dp -> d_stop = pos;
2038             dp++;
2039         }
2040
2041         /* (don't) advance pos */
2042         /* pos += ld2; */
2043
2044         /* need more storage.... */
2045         if (dp >= ep) {
2046             register int    curlen = dp - pp;
2047
2048             cp = (struct drop  *) realloc ((char *) pp,
2049                                     (unsigned) (len += MAXFOLDER) * sizeof *pp);
2050             if (cp == NULL) {
2051                 if (noisy)
2052                     admonish (NULLCP, "unable to allocate drop storage");
2053                 free ((char *) pp);
2054                 return 0;
2055             }
2056             dp = cp + curlen, ep = (pp = cp) + len - 1;
2057         }
2058     }
2059
2060     /* return unused stuff */
2061     if (dp == pp)
2062         free ((char *) pp);
2063     else
2064         *drops = pp;
2065     return (dp - pp);
2066 }
2067
2068 /*
2069  * The remainder of this file adapted from:
2070  *
2071  *      head.c  5.2 (Berkeley) 6/21/85
2072  */
2073
2074 struct p_hdline {
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 */
2078 };
2079
2080 /*
2081  *
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.
2085  */
2086
2087 #define NOSTR           ((char *) 0)    /* Null string pointer */
2088 static  char *p_copyin();
2089 static  char *p_copy();
2090
2091
2092 static  p_ishead(buffer)
2093 char buffer[];
2094 {
2095 register char *cp;
2096 struct p_hdline hl;
2097 char linebuf[BUFSIZ];
2098 char parbuf[BUFSIZ];
2099
2100         strcpy(linebuf,buffer);
2101         cp = linebuf;
2102
2103         if (linebuf[0]=='F')
2104                   padvise (NULLCP, LOG_DEBUG, "ishead: '%s'",linebuf);
2105
2106         if (strncmp("From ", cp, 5) != 0)
2107                 return(0);
2108
2109         padvise (NULLCP, LOG_DEBUG, "Fromline...");
2110
2111         /* get full header */
2112         p_parse(cp, &hl, parbuf);
2113
2114         if (hl.l_from == NOSTR || hl.l_date ==  NOSTR) {
2115                   padvise (NULLCP, LOG_DEBUG, "Fromline...NODATE");
2116                 return(0);
2117                 }
2118
2119         if (!p_isdate(hl.l_date)) {
2120                   padvise (NULLCP, LOG_DEBUG, "Fromline...BADDATE %s",
2121                         hl.l_date);
2122                 return(0);
2123                 }
2124
2125         /* I guess we got it! */
2126         padvise (NULLCP, LOG_DEBUG, "got a head.. ");
2127
2128         return(1);
2129 }
2130
2131 /*
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.
2136  */
2137
2138 static  p_parse(line, hl, pbuf)
2139         char line[], pbuf[];
2140         struct p_hdline *hl;
2141 {
2142         register char *cp, *dp;
2143         char *sp;
2144         char word[BUFSIZ];
2145         char * p_nextword();
2146
2147         hl->l_from = NOSTR;
2148         hl->l_tty = NOSTR;
2149         hl->l_date = NOSTR;
2150         cp = line;
2151         sp = pbuf;
2152
2153         /*
2154          * Skip the first "word" of the line, which should be "From"
2155          * anyway.
2156          */
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);
2161
2162         /* UNLIKELY */
2163         if (strncmp(dp, "tty", 3) == 0) {
2164                 cp = p_nextword(dp, word);
2165                 hl->l_tty = p_copyin(word, &sp);
2166                 if (cp != NOSTR)
2167                         hl->l_date = p_copyin(cp, &sp);
2168         }
2169
2170         /* USUAL */
2171         else
2172                 if (dp != NOSTR)
2173                         hl->l_date = p_copyin(dp, &sp);
2174 }
2175
2176 /*
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.
2181  */
2182
2183 static  char *
2184 p_copyin(src, space)
2185         char src[];
2186         char **space;
2187 {
2188         register char *cp, *top;
2189         register int s;
2190
2191         s = strlen(src);
2192         cp = *space;
2193         top = cp;
2194         strcpy(cp, src);
2195         cp += s + 1;
2196         *space = cp;
2197         return(top);
2198 }
2199
2200 /*
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.
2204  */
2205
2206 static  char *
2207 p_nextword(wp, wbuf)
2208         char wp[], wbuf[];
2209 {
2210         register char *cp, *cp2;
2211
2212         if ((cp = wp) == NOSTR) {
2213                 p_copy("", wbuf);
2214                 return(NOSTR);
2215         }
2216         cp2 = wbuf;
2217         while (!any(*cp, " \t") && *cp != '\0')
2218                 if (*cp == '"') {
2219                         *cp2++ = *cp++;
2220                         while (*cp != '\0' && *cp != '"')
2221                                 *cp2++ = *cp++;
2222                         if (*cp == '"')
2223                                 *cp2++ = *cp++;
2224                 } else
2225                         *cp2++ = *cp++;
2226         *cp2 = '\0';
2227         while (any(*cp, " \t"))
2228                 cp++;
2229         if (*cp == '\0')
2230                 return(NOSTR);
2231         return(cp);
2232 }
2233
2234 /*
2235  * Copy str1 to str2, return pointer to null in str2.
2236  */
2237
2238 static  char *
2239 p_copy(str1, str2)
2240         char *str1, *str2;
2241 {
2242         register char *s1, *s2;
2243
2244         s1 = str1;
2245         s2 = str2;
2246         while (*s1)
2247                 *s2++ = *s1++;
2248         *s2 = 0;
2249         return(s2);
2250 }
2251
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 */
2259
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 */
2263
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 */
2267
2268 static  p_isdate(date)
2269         char date[];
2270 {
2271         register char *cp;
2272
2273         cp = date;
2274         if (p_cmatch(cp, p_ctypes))
2275                 return(1);
2276
2277         return(p_cmatch(cp, p_tmztyp));
2278 }
2279
2280 /*
2281  * Match the given string against the given template.
2282  * Return 1 if they match, 0 if they don't
2283  */
2284
2285 static  p_cmatch(str, temp)
2286         char str[], temp[];
2287 {
2288         register char *cp, *tp;
2289         register int c;
2290
2291         cp = str;
2292         tp = temp;
2293         while (*cp != '\0' && *tp != 0) {
2294                 c = *cp++;
2295                 switch (*tp++) {
2296                 case L:
2297                         if (c < 'a' || c > 'z')
2298                                 return(0);
2299                         break;
2300
2301                 case U:
2302                         if (c < 'A' || c > 'Z')
2303                                 return(0);
2304                         break;
2305
2306                 case S:
2307                         if (c != ' ')
2308                                 return(0);
2309                         break;
2310
2311                 case D:
2312                         if (!isdigit(c))
2313                                 return(0);
2314                         break;
2315
2316                 case O:
2317                         if (c != ' ' && !isdigit(c))
2318                                 return(0);
2319                         break;
2320
2321                 case C:
2322                         if (c != ':')
2323                                 return(0);
2324                         break;
2325
2326                 case N:
2327                         if (c != '\n')
2328                                 return(0);
2329                         break;
2330                 }
2331         }
2332         if ((*cp != '\0' && *cp != '\n') || *tp != 0)
2333                 return(0);
2334         return(1);
2335 }
2336
2337 static  any(ch, str)
2338         char *str;
2339 {
2340         register char *f;
2341         register c;
2342
2343         f = str;
2344         c = ch;
2345         while (*f)
2346                 if (c == *f++)
2347                         return(1);
2348         return(0);
2349 }
2350 #endif