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