160240Shibler /* movemail foo bar -- move file foo to file bar,
260240Shibler    locking file foo the way /bin/mail respects.
360240Shibler    Copyright (C) 1986 Free Software Foundation, Inc.
460240Shibler 
560240Shibler This file is part of GNU Emacs.
660240Shibler 
760240Shibler GNU Emacs is free software; you can redistribute it and/or modify
860240Shibler it under the terms of the GNU General Public License as published by
960240Shibler the Free Software Foundation; either version 1, or (at your option)
1060240Shibler any later version.
1160240Shibler 
1260240Shibler GNU Emacs is distributed in the hope that it will be useful,
1360240Shibler but WITHOUT ANY WARRANTY; without even the implied warranty of
1460240Shibler MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1560240Shibler GNU General Public License for more details.
1660240Shibler 
1760240Shibler You should have received a copy of the GNU General Public License
1860240Shibler along with GNU Emacs; see the file COPYING.  If not, write to
1960240Shibler the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
2060240Shibler 
2160240Shibler /*
2260240Shibler  * Modified January, 1986 by Michael R. Gretzinger (Project Athena)
2360240Shibler  *
2460240Shibler  * Added POP (Post Office Protocol) service.  When compiled -DPOP
2560240Shibler  * movemail will accept input filename arguments of the form
2660240Shibler  * "po:username".  This will cause movemail to open a connection to
2760240Shibler  * a pop server running on $MAILHOST (environment variable).  Movemail
2860240Shibler  * must be setuid to root in order to work with POP.
2960240Shibler  *
3060240Shibler  * New module: popmail.c
3160240Shibler  * Modified routines:
3260240Shibler  *	main - added code within #ifdef MAIL_USE_POP; added setuid(getuid())
3360240Shibler  *		after POP code.
3460240Shibler  * New routines in movemail.c:
3560240Shibler  *	get_errmsg - return pointer to system error message
3660240Shibler  *
3760240Shibler  */
3860240Shibler 
3960240Shibler #include <sys/types.h>
4060240Shibler #include <sys/stat.h>
4160240Shibler #include <sys/file.h>
4260240Shibler #include <errno.h>
43*60587Sbostic #include <unistd.h>
4460240Shibler #define NO_SHORTNAMES   /* Tell config not to load remap.h */
4560240Shibler #include "../src/config.h"
4660240Shibler 
4760240Shibler #ifdef USG
4860240Shibler #include <fcntl.h>
4960240Shibler #include <unistd.h>
5060240Shibler #ifndef F_OK
5160240Shibler #define F_OK 0
5260240Shibler #define X_OK 1
5360240Shibler #define W_OK 2
5460240Shibler #define R_OK 4
5560240Shibler #endif
5660240Shibler #endif /* USG */
5760240Shibler 
5860240Shibler #ifdef XENIX
5960240Shibler #include <sys/locking.h>
6060240Shibler #endif
6160240Shibler 
6260240Shibler /* Cancel substitutions made by config.h for Emacs.  */
6360240Shibler #undef open
6460240Shibler #undef read
6560240Shibler #undef write
6660240Shibler #undef close
6760240Shibler 
6860240Shibler char *concat ();
6960240Shibler extern int errno;
7060240Shibler 
7160240Shibler /* Nonzero means this is name of a lock file to delete on fatal error.  */
7260240Shibler char *delete_lockname;
7360240Shibler 
main(argc,argv)7460240Shibler main (argc, argv)
7560240Shibler      int argc;
7660240Shibler      char **argv;
7760240Shibler {
7860240Shibler   char *inname, *outname;
7960240Shibler   int indesc, outdesc;
8060240Shibler   char buf[1024];
8160240Shibler   int nread;
8260240Shibler 
8360240Shibler #ifndef MAIL_USE_FLOCK
8460240Shibler   struct stat st;
8560240Shibler   long now;
8660240Shibler   int tem;
8760240Shibler   char *lockname, *p;
8860240Shibler   char tempname[40];
8960240Shibler   int desc;
9060240Shibler #endif /* not MAIL_USE_FLOCK */
9160240Shibler 
9260240Shibler   delete_lockname = 0;
9360240Shibler 
9460240Shibler   if (argc < 3)
9560240Shibler     fatal ("two arguments required");
9660240Shibler 
9760240Shibler   inname = argv[1];
9860240Shibler   outname = argv[2];
9960240Shibler 
10060240Shibler   /* Check access to output file.  */
10160240Shibler   if (access (outname, F_OK) == 0 && access (outname, W_OK) != 0)
10260240Shibler     pfatal_with_name (outname);
10360240Shibler 
10460240Shibler   /* Also check that outname's directory is writeable to the real uid.  */
10560240Shibler   {
10660240Shibler     char *buf = (char *) malloc (strlen (outname) + 1);
10760240Shibler     char *p, q;
10860240Shibler     strcpy (buf, outname);
10960240Shibler     p = buf + strlen (buf);
11060240Shibler     while (p > buf && p[-1] != '/')
11160240Shibler       *--p = 0;
11260240Shibler     if (p == buf)
11360240Shibler       *p++ = '.';
11460240Shibler     if (access (buf, W_OK) != 0)
11560240Shibler       pfatal_with_name (buf);
11660240Shibler     free (buf);
11760240Shibler   }
11860240Shibler 
11960240Shibler #ifdef MAIL_USE_POP
12060240Shibler   if (!bcmp (inname, "po:", 3))
12160240Shibler     {
12260240Shibler       int status; char *user;
12360240Shibler 
12460240Shibler       user = (char *) rindex (inname, ':') + 1;
12560240Shibler       status = popmail (user, outname);
12660240Shibler       exit (status);
12760240Shibler     }
12860240Shibler 
12960240Shibler   setuid (getuid());
13060240Shibler #endif /* MAIL_USE_POP */
13160240Shibler 
13260240Shibler   /* Check access to input file.  */
13360240Shibler   if (access (inname, R_OK | W_OK) != 0)
13460240Shibler     pfatal_with_name (inname);
13560240Shibler 
13660240Shibler #ifndef MAIL_USE_FLOCK
13760240Shibler   /* Use a lock file named /usr/spool/mail/$USER.lock:
13860240Shibler      If it exists, the mail file is locked.  */
13960240Shibler   lockname = concat (inname, ".lock", "");
14060240Shibler   strcpy (tempname, inname);
14160240Shibler   p = tempname + strlen (tempname);
14260240Shibler   while (p != tempname && p[-1] != '/')
14360240Shibler     p--;
14460240Shibler   *p = 0;
14560240Shibler   strcpy (p, "EXXXXXX");
14660240Shibler   mktemp (tempname);
14760240Shibler   (void) unlink (tempname);
14860240Shibler 
14960240Shibler   while (1)
15060240Shibler     {
15160240Shibler       /* Create the lock file, but not under the lock file name.  */
15260240Shibler       /* Give up if cannot do that.  */
15360240Shibler       desc = open (tempname, O_WRONLY | O_CREAT, 0666);
15460240Shibler       if (desc < 0)
15560240Shibler         pfatal_with_name (concat ("temporary file \"", tempname, "\""));
15660240Shibler       close (desc);
15760240Shibler 
15860240Shibler       tem = link (tempname, lockname);
15960240Shibler       (void) unlink (tempname);
16060240Shibler       if (tem >= 0)
16160240Shibler 	break;
16260240Shibler       sleep (1);
16360240Shibler 
16460240Shibler       /* If lock file is a minute old, unlock it.  */
16560240Shibler       if (stat (lockname, &st) >= 0)
16660240Shibler 	{
16760240Shibler 	  now = time (0);
16860240Shibler 	  if (st.st_ctime < now - 60)
16960240Shibler 	    (void) unlink (lockname);
17060240Shibler 	}
17160240Shibler     }
17260240Shibler 
17360240Shibler   delete_lockname = lockname;
17460240Shibler #endif /* not MAIL_USE_FLOCK */
17560240Shibler 
17660240Shibler #ifdef MAIL_USE_FLOCK
17760240Shibler   indesc = open (inname, O_RDWR);
17860240Shibler #else /* if not MAIL_USE_FLOCK */
17960240Shibler   indesc = open (inname, O_RDONLY);
18060240Shibler #endif /* not MAIL_USE_FLOCK */
18160240Shibler   if (indesc < 0)
18260240Shibler     pfatal_with_name (inname);
18360240Shibler 
18460240Shibler #if defined(BSD) || defined(XENIX)
18560240Shibler   /* In case movemail is setuid to root, make sure the user can
18660240Shibler      read the output file.  */
18760240Shibler   /* This is desirable for all systems
18860240Shibler      but I don't want to assume all have the umask system call */
18960240Shibler   umask (umask (0) & 0333);
19060240Shibler #endif /* BSD or Xenix */
19160240Shibler   outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
19260240Shibler   if (outdesc < 0)
19360240Shibler     pfatal_with_name (outname);
19460240Shibler #ifdef MAIL_USE_FLOCK
19560240Shibler #ifdef XENIX
19660240Shibler   if (locking (indesc, LK_RLCK, 0L) < 0) pfatal_with_name (inname);
19760240Shibler #else
19860240Shibler   flock (indesc, LOCK_EX);
19960240Shibler #endif
20060240Shibler #endif /* MAIL_USE_FLOCK */
20160240Shibler 
20260240Shibler   while (1)
20360240Shibler     {
20460240Shibler       nread = read (indesc, buf, sizeof buf);
20560240Shibler       if (nread != write (outdesc, buf, nread))
20660240Shibler 	{
20760240Shibler 	  int saved_errno = errno;
20860240Shibler 	  (void) unlink (outname);
20960240Shibler 	  errno = saved_errno;
21060240Shibler 	  pfatal_with_name (outname);
21160240Shibler 	}
21260240Shibler       if (nread < sizeof buf)
21360240Shibler 	break;
21460240Shibler     }
21560240Shibler 
21660240Shibler #ifdef BSD
21760240Shibler   fsync (outdesc);
21860240Shibler #endif
21960240Shibler 
22060240Shibler   /* Check to make sure no errors before we zap the inbox.  */
22160240Shibler   if (close (outdesc) != 0)
22260240Shibler     {
22360240Shibler       int saved_errno = errno;
22460240Shibler       (void) unlink (outname);
22560240Shibler       errno = saved_errno;
22660240Shibler       pfatal_with_name (outname);
22760240Shibler   }
22860240Shibler 
22960240Shibler #ifdef MAIL_USE_FLOCK
23060240Shibler #if defined(STRIDE) || defined(XENIX)
23160240Shibler   /* Stride, xenix have file locking, but no ftruncate.  This mess will do. */
23260240Shibler   (void) close (open (inname, O_CREAT | O_TRUNC | O_RDWR, 0666));
23360240Shibler #else
23460290Shibler   (void) ftruncate (indesc, (off_t)0);
23560240Shibler #endif /* STRIDE or XENIX */
23660240Shibler #endif /* MAIL_USE_FLOCK */
23760240Shibler   close (indesc);
23860240Shibler 
23960240Shibler #ifndef MAIL_USE_FLOCK
24060240Shibler   /* Delete the input file; if we can't, at least get rid of its contents.  */
24160240Shibler   if (unlink (inname) < 0)
24260240Shibler     if (errno != ENOENT)
24360240Shibler       creat (inname, 0666);
24460240Shibler   (void) unlink (lockname);
24560240Shibler #endif /* not MAIL_USE_FLOCK */
24660240Shibler   exit (0);
24760240Shibler }
24860240Shibler 
24960240Shibler /* Print error message and exit.  */
25060240Shibler 
fatal(s1,s2)25160240Shibler fatal (s1, s2)
25260240Shibler      char *s1, *s2;
25360240Shibler {
25460240Shibler   if (delete_lockname)
25560240Shibler     unlink (delete_lockname);
25660240Shibler   error (s1, s2);
25760240Shibler   exit (1);
25860240Shibler }
25960240Shibler 
26060240Shibler /* Print error message.  `s1' is printf control string, `s2' is arg for it. */
26160240Shibler 
error(s1,s2,s3)26260240Shibler error (s1, s2, s3)
26360240Shibler      char *s1, *s2, *s3;
26460240Shibler {
26560240Shibler   printf ("movemail: ");
26660240Shibler   printf (s1, s2, s3);
26760240Shibler   printf ("\n");
26860240Shibler }
26960240Shibler 
pfatal_with_name(name)27060240Shibler pfatal_with_name (name)
27160240Shibler      char *name;
27260240Shibler {
27360240Shibler   extern int errno, sys_nerr;
27460240Shibler   extern char *sys_errlist[];
27560240Shibler   char *s;
27660240Shibler 
27760240Shibler   if (errno < sys_nerr)
27860240Shibler     s = concat ("", sys_errlist[errno], " for %s");
27960240Shibler   else
28060240Shibler     s = "cannot open %s";
28160240Shibler   fatal (s, name);
28260240Shibler }
28360240Shibler 
28460240Shibler /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3.  */
28560240Shibler 
28660240Shibler char *
concat(s1,s2,s3)28760240Shibler concat (s1, s2, s3)
28860240Shibler      char *s1, *s2, *s3;
28960240Shibler {
29060240Shibler   int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
29160240Shibler   char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
29260240Shibler 
29360240Shibler   strcpy (result, s1);
29460240Shibler   strcpy (result + len1, s2);
29560240Shibler   strcpy (result + len1 + len2, s3);
29660240Shibler   *(result + len1 + len2 + len3) = 0;
29760240Shibler 
29860240Shibler   return result;
29960240Shibler }
30060240Shibler 
30160240Shibler /* Like malloc but get fatal error if memory is exhausted.  */
30260240Shibler 
30360240Shibler int
xmalloc(size)30460240Shibler xmalloc (size)
30560240Shibler      int size;
30660240Shibler {
30760240Shibler   int result = malloc (size);
30860240Shibler   if (!result)
30960240Shibler     fatal ("virtual memory exhausted", 0);
31060240Shibler   return result;
31160240Shibler }
31260240Shibler 
31360240Shibler /* This is the guts of the interface to the Post Office Protocol.  */
31460240Shibler 
31560240Shibler #ifdef MAIL_USE_POP
31660240Shibler 
31760240Shibler #include <sys/socket.h>
31860240Shibler #include <netinet/in.h>
31960240Shibler #include <netdb.h>
32060240Shibler #include <stdio.h>
32160240Shibler 
32260240Shibler #ifdef USG
32360240Shibler #include <fcntl.h>
32460240Shibler /* Cancel substitutions made by config.h for Emacs.  */
32560240Shibler #undef open
32660240Shibler #undef read
32760240Shibler #undef write
32860240Shibler #undef close
32960240Shibler #endif /* USG */
33060240Shibler 
33160240Shibler #define NOTOK (-1)
33260240Shibler #define OK 0
33360240Shibler #define DONE 1
33460240Shibler 
33560240Shibler char *progname;
33660240Shibler FILE *sfi;
33760240Shibler FILE *sfo;
33860240Shibler char Errmsg[80];
33960240Shibler 
34060240Shibler static int debug = 0;
34160240Shibler 
popmail(user,outfile)34260240Shibler popmail(user, outfile)
34360240Shibler char *user;
34460240Shibler char *outfile;
34560240Shibler {
34660240Shibler     char *host;
34760240Shibler     int nmsgs, nbytes;
34860240Shibler     char response[128];
34960240Shibler     register int i;
35060240Shibler     int mbfi;
35160240Shibler     FILE *mbf;
35260240Shibler     char *getenv();
35360240Shibler     int mbx_write();
35460240Shibler     char *get_errmsg();
35560240Shibler 
35660240Shibler     host = getenv("MAILHOST");
35760240Shibler     if (host == NULL) {
35860240Shibler 	fatal("no MAILHOST defined");
35960240Shibler     }
36060240Shibler 
36160240Shibler     if (pop_init(host) == NOTOK) {
36260240Shibler 	error(Errmsg);
36360240Shibler 	return(1);
36460240Shibler     }
36560240Shibler 
36660240Shibler     if (getline(response, sizeof response, sfi) != OK) {
36760240Shibler 	error(response);
36860240Shibler 	return(1);
36960240Shibler     }
37060240Shibler 
37160240Shibler     if (pop_command("USER %s", user) == NOTOK ||
37260240Shibler 	pop_command("RPOP %s", user) == NOTOK) {
37360240Shibler 	error(Errmsg);
37460240Shibler 	pop_command("QUIT");
37560240Shibler 	return(1);
37660240Shibler     }
37760240Shibler 
37860240Shibler     if (pop_stat(&nmsgs, &nbytes) == NOTOK) {
37960240Shibler 	error(Errmsg);
38060240Shibler 	pop_command("QUIT");
38160240Shibler 	return(1);
38260240Shibler     }
38360240Shibler 
38460240Shibler     if (!nmsgs)
38560240Shibler       {
38660240Shibler 	pop_command("QUIT");
38760240Shibler 	return(0);
38860240Shibler       }
38960240Shibler 
39060240Shibler     mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
39160240Shibler     if (mbfi < 0)
39260240Shibler       {
39360240Shibler 	pop_command("QUIT");
39460240Shibler 	error("Error in open: %s, %s", get_errmsg(), outfile);
39560240Shibler 	return(1);
39660240Shibler       }
39760240Shibler     fchown(mbfi, getuid(), -1);
39860240Shibler 
39960240Shibler     if ((mbf = fdopen(mbfi, "w")) == NULL)
40060240Shibler       {
40160240Shibler 	pop_command("QUIT");
40260240Shibler 	error("Error in fdopen: %s", get_errmsg());
40360240Shibler 	close(mbfi);
40460240Shibler 	unlink(outfile);
40560240Shibler 	return(1);
40660240Shibler       }
40760240Shibler 
40860240Shibler     for (i = 1; i <= nmsgs; i++) {
40960240Shibler 	mbx_delimit_begin(mbf);
41060240Shibler 	if (pop_retr(i, mbx_write, mbf) != OK) {
41160240Shibler 	    error(Errmsg);
41260240Shibler 	    pop_command("QUIT");
41360240Shibler 	    close(mbfi);
41460240Shibler 	    return(1);
41560240Shibler 	}
41660240Shibler 	mbx_delimit_end(mbf);
41760240Shibler 	fflush(mbf);
41860240Shibler     }
41960240Shibler 
42060240Shibler     for (i = 1; i <= nmsgs; i++) {
42160240Shibler 	if (pop_command("DELE %d", i) == NOTOK) {
42260240Shibler 	    error(Errmsg);
42360240Shibler 	    pop_command("QUIT");
42460240Shibler 	    close(mbfi);
42560240Shibler 	    return(1);
42660240Shibler 	}
42760240Shibler     }
42860240Shibler 
42960240Shibler     pop_command("QUIT");
43060240Shibler     close(mbfi);
43160240Shibler     return(0);
43260240Shibler }
43360240Shibler 
pop_init(host)43460240Shibler pop_init(host)
43560240Shibler char *host;
43660240Shibler {
43760240Shibler     register struct hostent *hp;
43860240Shibler     register struct servent *sp;
43960240Shibler     int lport = IPPORT_RESERVED - 1;
44060240Shibler     struct sockaddr_in sin;
44160240Shibler     register int s;
44260240Shibler     char *get_errmsg();
44360240Shibler 
44460240Shibler     hp = gethostbyname(host);
44560240Shibler     if (hp == NULL) {
44660240Shibler 	sprintf(Errmsg, "MAILHOST unknown: %s", host);
44760240Shibler 	return(NOTOK);
44860240Shibler     }
44960240Shibler 
45060240Shibler     sp = getservbyname("pop", "tcp");
45160240Shibler     if (sp == 0) {
45260240Shibler 	strcpy(Errmsg, "tcp/pop: unknown service");
45360240Shibler 	return(NOTOK);
45460240Shibler     }
45560240Shibler 
45660240Shibler     sin.sin_family = hp->h_addrtype;
45760240Shibler     bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
45860240Shibler     sin.sin_port = sp->s_port;
45960240Shibler     s = rresvport(&lport);
46060240Shibler     if (s < 0) {
46160240Shibler 	sprintf(Errmsg, "error creating socket: %s", get_errmsg());
46260240Shibler 	return(NOTOK);
46360240Shibler     }
46460240Shibler 
46560240Shibler     if (connect(s, (char *)&sin, sizeof sin) < 0) {
46660240Shibler 	sprintf(Errmsg, "error during connect: %s", get_errmsg());
46760240Shibler 	close(s);
46860240Shibler 	return(NOTOK);
46960240Shibler     }
47060240Shibler 
47160240Shibler     sfi = fdopen(s, "r");
47260240Shibler     sfo = fdopen(s, "w");
47360240Shibler     if (sfi == NULL || sfo == NULL) {
47460240Shibler 	sprintf(Errmsg, "error in fdopen: %s", get_errmsg());
47560240Shibler 	close(s);
47660240Shibler 	return(NOTOK);
47760240Shibler     }
47860240Shibler 
47960240Shibler     return(OK);
48060240Shibler }
48160240Shibler 
pop_command(fmt,a,b,c,d)48260240Shibler pop_command(fmt, a, b, c, d)
48360240Shibler char *fmt;
48460240Shibler {
48560240Shibler     char buf[128];
48660240Shibler     char errmsg[64];
48760240Shibler 
48860240Shibler     sprintf(buf, fmt, a, b, c, d);
48960240Shibler 
49060240Shibler     if (debug) fprintf(stderr, "---> %s\n", buf);
49160240Shibler     if (putline(buf, Errmsg, sfo) == NOTOK) return(NOTOK);
49260240Shibler 
49360240Shibler     if (getline(buf, sizeof buf, sfi) != OK) {
49460240Shibler 	strcpy(Errmsg, buf);
49560240Shibler 	return(NOTOK);
49660240Shibler     }
49760240Shibler 
49860240Shibler     if (debug) fprintf(stderr, "<--- %s\n", buf);
49960240Shibler     if (*buf != '+') {
50060240Shibler 	strcpy(Errmsg, buf);
50160240Shibler 	return(NOTOK);
50260240Shibler     } else {
50360240Shibler 	return(OK);
50460240Shibler     }
50560240Shibler }
50660240Shibler 
50760240Shibler 
pop_stat(nmsgs,nbytes)50860240Shibler pop_stat(nmsgs, nbytes)
50960240Shibler int *nmsgs, *nbytes;
51060240Shibler {
51160240Shibler     char buf[128];
51260240Shibler 
51360240Shibler     if (debug) fprintf(stderr, "---> STAT\n");
51460240Shibler     if (putline("STAT", Errmsg, sfo) == NOTOK) return(NOTOK);
51560240Shibler 
51660240Shibler     if (getline(buf, sizeof buf, sfi) != OK) {
51760240Shibler 	strcpy(Errmsg, buf);
51860240Shibler 	return(NOTOK);
51960240Shibler     }
52060240Shibler 
52160240Shibler     if (debug) fprintf(stderr, "<--- %s\n", buf);
52260240Shibler     if (*buf != '+') {
52360240Shibler 	strcpy(Errmsg, buf);
52460240Shibler 	return(NOTOK);
52560240Shibler     } else {
52660240Shibler 	sscanf(buf, "+OK %d %d", nmsgs, nbytes);
52760240Shibler 	return(OK);
52860240Shibler     }
52960240Shibler }
53060240Shibler 
53160240Shibler pop_retr(msgno, action, arg)
53260240Shibler int (*action)();
53360240Shibler {
53460240Shibler     char buf[128];
53560240Shibler 
53660240Shibler     sprintf(buf, "RETR %d", msgno);
53760240Shibler     if (debug) fprintf(stderr, "%s\n", buf);
53860240Shibler     if (putline(buf, Errmsg, sfo) == NOTOK) return(NOTOK);
53960240Shibler 
54060240Shibler     if (getline(buf, sizeof buf, sfi) != OK) {
54160240Shibler 	strcpy(Errmsg, buf);
54260240Shibler 	return(NOTOK);
54360240Shibler     }
54460240Shibler 
54560240Shibler     while (1) {
54660240Shibler 	switch (multiline(buf, sizeof buf, sfi)) {
54760240Shibler 	case OK:
54860240Shibler 	    (*action)(buf, arg);
54960240Shibler 	    break;
55060240Shibler 	case DONE:
55160240Shibler 	    return (OK);
55260240Shibler 	case NOTOK:
55360240Shibler 	    strcpy(Errmsg, buf);
55460240Shibler 	    return (NOTOK);
55560240Shibler 	}
55660240Shibler     }
55760240Shibler }
55860240Shibler 
getline(buf,n,f)55960240Shibler getline(buf, n, f)
56060240Shibler char *buf;
56160240Shibler register int n;
56260240Shibler FILE *f;
56360240Shibler {
56460240Shibler     register char *p;
56560240Shibler     int c;
56660240Shibler 
56760240Shibler     p = buf;
56860240Shibler     while (--n > 0 && (c = fgetc(f)) != EOF)
56960240Shibler       if ((*p++ = c) == '\n') break;
57060240Shibler 
57160240Shibler     if (ferror(f)) {
57260240Shibler 	strcpy(buf, "error on connection");
57360240Shibler 	return (NOTOK);
57460240Shibler     }
57560240Shibler 
57660240Shibler     if (c == EOF && p == buf) {
57760240Shibler 	strcpy(buf, "connection closed by foreign host");
57860240Shibler 	return (DONE);
57960240Shibler     }
58060240Shibler 
58160240Shibler     *p = NULL;
58260240Shibler     if (*--p == '\n') *p = NULL;
58360240Shibler     if (*--p == '\r') *p = NULL;
58460240Shibler     return(OK);
58560240Shibler }
58660240Shibler 
multiline(buf,n,f)58760240Shibler multiline(buf, n, f)
58860240Shibler char *buf;
58960240Shibler register int n;
59060240Shibler FILE *f;
59160240Shibler {
59260240Shibler     if (getline(buf, n, f) != OK) return (NOTOK);
59360240Shibler     if (*buf == '.') {
59460240Shibler 	if (*(buf+1) == NULL) {
59560240Shibler 	    return (DONE);
59660240Shibler 	} else {
59760240Shibler 	    strcpy(buf, buf+1);
59860240Shibler 	}
59960240Shibler     }
60060240Shibler     return(OK);
60160240Shibler }
60260240Shibler 
60360240Shibler char *
get_errmsg()60460240Shibler get_errmsg()
60560240Shibler {
60660240Shibler     extern int errno, sys_nerr;
60760240Shibler     extern char *sys_errlist[];
60860240Shibler     char *s;
60960240Shibler 
61060240Shibler     if (errno < sys_nerr)
61160240Shibler       s = sys_errlist[errno];
61260240Shibler     else
61360240Shibler       s = "unknown error";
61460240Shibler     return(s);
61560240Shibler }
61660240Shibler 
putline(buf,err,f)61760240Shibler putline(buf, err, f)
61860240Shibler char *buf;
61960240Shibler char *err;
62060240Shibler FILE *f;
62160240Shibler {
62260240Shibler     fprintf(f, "%s\r\n", buf);
62360240Shibler     fflush(f);
62460240Shibler     if (ferror(f)) {
62560240Shibler 	strcpy(err, "lost connection");
62660240Shibler 	return(NOTOK);
62760240Shibler     }
62860240Shibler     return(OK);
62960240Shibler }
63060240Shibler 
mbx_write(line,mbf)63160240Shibler mbx_write(line, mbf)
63260240Shibler char *line;
63360240Shibler FILE *mbf;
63460240Shibler {
63560240Shibler     fputs(line, mbf);
63660240Shibler     fputc(0x0a, mbf);
63760240Shibler }
63860240Shibler 
mbx_delimit_begin(mbf)63960240Shibler mbx_delimit_begin(mbf)
64060240Shibler FILE *mbf;
64160240Shibler {
64260240Shibler     fputs("\f\n0, unseen,,\n", mbf);
64360240Shibler }
64460240Shibler 
mbx_delimit_end(mbf)64560240Shibler mbx_delimit_end(mbf)
64660240Shibler FILE *mbf;
64760240Shibler {
64860240Shibler     putc('\037', mbf);
64960240Shibler }
65060240Shibler 
65160240Shibler #endif /* MAIL_USE_POP */
652