1*60240Shibler /* movemail foo bar -- move file foo to file bar,
2*60240Shibler    locking file foo the way /bin/mail respects.
3*60240Shibler    Copyright (C) 1986 Free Software Foundation, Inc.
4*60240Shibler 
5*60240Shibler This file is part of GNU Emacs.
6*60240Shibler 
7*60240Shibler GNU Emacs is free software; you can redistribute it and/or modify
8*60240Shibler it under the terms of the GNU General Public License as published by
9*60240Shibler the Free Software Foundation; either version 1, or (at your option)
10*60240Shibler any later version.
11*60240Shibler 
12*60240Shibler GNU Emacs is distributed in the hope that it will be useful,
13*60240Shibler but WITHOUT ANY WARRANTY; without even the implied warranty of
14*60240Shibler MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15*60240Shibler GNU General Public License for more details.
16*60240Shibler 
17*60240Shibler You should have received a copy of the GNU General Public License
18*60240Shibler along with GNU Emacs; see the file COPYING.  If not, write to
19*60240Shibler the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
20*60240Shibler 
21*60240Shibler /*
22*60240Shibler  * Modified January, 1986 by Michael R. Gretzinger (Project Athena)
23*60240Shibler  *
24*60240Shibler  * Added POP (Post Office Protocol) service.  When compiled -DPOP
25*60240Shibler  * movemail will accept input filename arguments of the form
26*60240Shibler  * "po:username".  This will cause movemail to open a connection to
27*60240Shibler  * a pop server running on $MAILHOST (environment variable).  Movemail
28*60240Shibler  * must be setuid to root in order to work with POP.
29*60240Shibler  *
30*60240Shibler  * New module: popmail.c
31*60240Shibler  * Modified routines:
32*60240Shibler  *	main - added code within #ifdef MAIL_USE_POP; added setuid(getuid())
33*60240Shibler  *		after POP code.
34*60240Shibler  * New routines in movemail.c:
35*60240Shibler  *	get_errmsg - return pointer to system error message
36*60240Shibler  *
37*60240Shibler  */
38*60240Shibler 
39*60240Shibler #include <sys/types.h>
40*60240Shibler #include <sys/stat.h>
41*60240Shibler #include <sys/file.h>
42*60240Shibler #include <errno.h>
43*60240Shibler #define NO_SHORTNAMES   /* Tell config not to load remap.h */
44*60240Shibler #include "../src/config.h"
45*60240Shibler 
46*60240Shibler #ifdef USG
47*60240Shibler #include <fcntl.h>
48*60240Shibler #include <unistd.h>
49*60240Shibler #ifndef F_OK
50*60240Shibler #define F_OK 0
51*60240Shibler #define X_OK 1
52*60240Shibler #define W_OK 2
53*60240Shibler #define R_OK 4
54*60240Shibler #endif
55*60240Shibler #endif /* USG */
56*60240Shibler 
57*60240Shibler #ifdef XENIX
58*60240Shibler #include <sys/locking.h>
59*60240Shibler #endif
60*60240Shibler 
61*60240Shibler /* Cancel substitutions made by config.h for Emacs.  */
62*60240Shibler #undef open
63*60240Shibler #undef read
64*60240Shibler #undef write
65*60240Shibler #undef close
66*60240Shibler 
67*60240Shibler char *concat ();
68*60240Shibler extern int errno;
69*60240Shibler 
70*60240Shibler /* Nonzero means this is name of a lock file to delete on fatal error.  */
71*60240Shibler char *delete_lockname;
72*60240Shibler 
73*60240Shibler main (argc, argv)
74*60240Shibler      int argc;
75*60240Shibler      char **argv;
76*60240Shibler {
77*60240Shibler   char *inname, *outname;
78*60240Shibler   int indesc, outdesc;
79*60240Shibler   char buf[1024];
80*60240Shibler   int nread;
81*60240Shibler 
82*60240Shibler #ifndef MAIL_USE_FLOCK
83*60240Shibler   struct stat st;
84*60240Shibler   long now;
85*60240Shibler   int tem;
86*60240Shibler   char *lockname, *p;
87*60240Shibler   char tempname[40];
88*60240Shibler   int desc;
89*60240Shibler #endif /* not MAIL_USE_FLOCK */
90*60240Shibler 
91*60240Shibler   delete_lockname = 0;
92*60240Shibler 
93*60240Shibler   if (argc < 3)
94*60240Shibler     fatal ("two arguments required");
95*60240Shibler 
96*60240Shibler   inname = argv[1];
97*60240Shibler   outname = argv[2];
98*60240Shibler 
99*60240Shibler   /* Check access to output file.  */
100*60240Shibler   if (access (outname, F_OK) == 0 && access (outname, W_OK) != 0)
101*60240Shibler     pfatal_with_name (outname);
102*60240Shibler 
103*60240Shibler   /* Also check that outname's directory is writeable to the real uid.  */
104*60240Shibler   {
105*60240Shibler     char *buf = (char *) malloc (strlen (outname) + 1);
106*60240Shibler     char *p, q;
107*60240Shibler     strcpy (buf, outname);
108*60240Shibler     p = buf + strlen (buf);
109*60240Shibler     while (p > buf && p[-1] != '/')
110*60240Shibler       *--p = 0;
111*60240Shibler     if (p == buf)
112*60240Shibler       *p++ = '.';
113*60240Shibler     if (access (buf, W_OK) != 0)
114*60240Shibler       pfatal_with_name (buf);
115*60240Shibler     free (buf);
116*60240Shibler   }
117*60240Shibler 
118*60240Shibler #ifdef MAIL_USE_POP
119*60240Shibler   if (!bcmp (inname, "po:", 3))
120*60240Shibler     {
121*60240Shibler       int status; char *user;
122*60240Shibler 
123*60240Shibler       user = (char *) rindex (inname, ':') + 1;
124*60240Shibler       status = popmail (user, outname);
125*60240Shibler       exit (status);
126*60240Shibler     }
127*60240Shibler 
128*60240Shibler   setuid (getuid());
129*60240Shibler #endif /* MAIL_USE_POP */
130*60240Shibler 
131*60240Shibler   /* Check access to input file.  */
132*60240Shibler   if (access (inname, R_OK | W_OK) != 0)
133*60240Shibler     pfatal_with_name (inname);
134*60240Shibler 
135*60240Shibler #ifndef MAIL_USE_FLOCK
136*60240Shibler   /* Use a lock file named /usr/spool/mail/$USER.lock:
137*60240Shibler      If it exists, the mail file is locked.  */
138*60240Shibler   lockname = concat (inname, ".lock", "");
139*60240Shibler   strcpy (tempname, inname);
140*60240Shibler   p = tempname + strlen (tempname);
141*60240Shibler   while (p != tempname && p[-1] != '/')
142*60240Shibler     p--;
143*60240Shibler   *p = 0;
144*60240Shibler   strcpy (p, "EXXXXXX");
145*60240Shibler   mktemp (tempname);
146*60240Shibler   (void) unlink (tempname);
147*60240Shibler 
148*60240Shibler   while (1)
149*60240Shibler     {
150*60240Shibler       /* Create the lock file, but not under the lock file name.  */
151*60240Shibler       /* Give up if cannot do that.  */
152*60240Shibler       desc = open (tempname, O_WRONLY | O_CREAT, 0666);
153*60240Shibler       if (desc < 0)
154*60240Shibler         pfatal_with_name (concat ("temporary file \"", tempname, "\""));
155*60240Shibler       close (desc);
156*60240Shibler 
157*60240Shibler       tem = link (tempname, lockname);
158*60240Shibler       (void) unlink (tempname);
159*60240Shibler       if (tem >= 0)
160*60240Shibler 	break;
161*60240Shibler       sleep (1);
162*60240Shibler 
163*60240Shibler       /* If lock file is a minute old, unlock it.  */
164*60240Shibler       if (stat (lockname, &st) >= 0)
165*60240Shibler 	{
166*60240Shibler 	  now = time (0);
167*60240Shibler 	  if (st.st_ctime < now - 60)
168*60240Shibler 	    (void) unlink (lockname);
169*60240Shibler 	}
170*60240Shibler     }
171*60240Shibler 
172*60240Shibler   delete_lockname = lockname;
173*60240Shibler #endif /* not MAIL_USE_FLOCK */
174*60240Shibler 
175*60240Shibler #ifdef MAIL_USE_FLOCK
176*60240Shibler   indesc = open (inname, O_RDWR);
177*60240Shibler #else /* if not MAIL_USE_FLOCK */
178*60240Shibler   indesc = open (inname, O_RDONLY);
179*60240Shibler #endif /* not MAIL_USE_FLOCK */
180*60240Shibler   if (indesc < 0)
181*60240Shibler     pfatal_with_name (inname);
182*60240Shibler 
183*60240Shibler #if defined(BSD) || defined(XENIX)
184*60240Shibler   /* In case movemail is setuid to root, make sure the user can
185*60240Shibler      read the output file.  */
186*60240Shibler   /* This is desirable for all systems
187*60240Shibler      but I don't want to assume all have the umask system call */
188*60240Shibler   umask (umask (0) & 0333);
189*60240Shibler #endif /* BSD or Xenix */
190*60240Shibler   outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
191*60240Shibler   if (outdesc < 0)
192*60240Shibler     pfatal_with_name (outname);
193*60240Shibler #ifdef MAIL_USE_FLOCK
194*60240Shibler #ifdef XENIX
195*60240Shibler   if (locking (indesc, LK_RLCK, 0L) < 0) pfatal_with_name (inname);
196*60240Shibler #else
197*60240Shibler   flock (indesc, LOCK_EX);
198*60240Shibler #endif
199*60240Shibler #endif /* MAIL_USE_FLOCK */
200*60240Shibler 
201*60240Shibler   while (1)
202*60240Shibler     {
203*60240Shibler       nread = read (indesc, buf, sizeof buf);
204*60240Shibler       if (nread != write (outdesc, buf, nread))
205*60240Shibler 	{
206*60240Shibler 	  int saved_errno = errno;
207*60240Shibler 	  (void) unlink (outname);
208*60240Shibler 	  errno = saved_errno;
209*60240Shibler 	  pfatal_with_name (outname);
210*60240Shibler 	}
211*60240Shibler       if (nread < sizeof buf)
212*60240Shibler 	break;
213*60240Shibler     }
214*60240Shibler 
215*60240Shibler #ifdef BSD
216*60240Shibler   fsync (outdesc);
217*60240Shibler #endif
218*60240Shibler 
219*60240Shibler   /* Check to make sure no errors before we zap the inbox.  */
220*60240Shibler   if (close (outdesc) != 0)
221*60240Shibler     {
222*60240Shibler       int saved_errno = errno;
223*60240Shibler       (void) unlink (outname);
224*60240Shibler       errno = saved_errno;
225*60240Shibler       pfatal_with_name (outname);
226*60240Shibler   }
227*60240Shibler 
228*60240Shibler #ifdef MAIL_USE_FLOCK
229*60240Shibler #if defined(STRIDE) || defined(XENIX)
230*60240Shibler   /* Stride, xenix have file locking, but no ftruncate.  This mess will do. */
231*60240Shibler   (void) close (open (inname, O_CREAT | O_TRUNC | O_RDWR, 0666));
232*60240Shibler #else
233*60240Shibler   (void) ftruncate (indesc, 0L);
234*60240Shibler #endif /* STRIDE or XENIX */
235*60240Shibler #endif /* MAIL_USE_FLOCK */
236*60240Shibler   close (indesc);
237*60240Shibler 
238*60240Shibler #ifndef MAIL_USE_FLOCK
239*60240Shibler   /* Delete the input file; if we can't, at least get rid of its contents.  */
240*60240Shibler   if (unlink (inname) < 0)
241*60240Shibler     if (errno != ENOENT)
242*60240Shibler       creat (inname, 0666);
243*60240Shibler   (void) unlink (lockname);
244*60240Shibler #endif /* not MAIL_USE_FLOCK */
245*60240Shibler   exit (0);
246*60240Shibler }
247*60240Shibler 
248*60240Shibler /* Print error message and exit.  */
249*60240Shibler 
250*60240Shibler fatal (s1, s2)
251*60240Shibler      char *s1, *s2;
252*60240Shibler {
253*60240Shibler   if (delete_lockname)
254*60240Shibler     unlink (delete_lockname);
255*60240Shibler   error (s1, s2);
256*60240Shibler   exit (1);
257*60240Shibler }
258*60240Shibler 
259*60240Shibler /* Print error message.  `s1' is printf control string, `s2' is arg for it. */
260*60240Shibler 
261*60240Shibler error (s1, s2, s3)
262*60240Shibler      char *s1, *s2, *s3;
263*60240Shibler {
264*60240Shibler   printf ("movemail: ");
265*60240Shibler   printf (s1, s2, s3);
266*60240Shibler   printf ("\n");
267*60240Shibler }
268*60240Shibler 
269*60240Shibler pfatal_with_name (name)
270*60240Shibler      char *name;
271*60240Shibler {
272*60240Shibler   extern int errno, sys_nerr;
273*60240Shibler   extern char *sys_errlist[];
274*60240Shibler   char *s;
275*60240Shibler 
276*60240Shibler   if (errno < sys_nerr)
277*60240Shibler     s = concat ("", sys_errlist[errno], " for %s");
278*60240Shibler   else
279*60240Shibler     s = "cannot open %s";
280*60240Shibler   fatal (s, name);
281*60240Shibler }
282*60240Shibler 
283*60240Shibler /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3.  */
284*60240Shibler 
285*60240Shibler char *
286*60240Shibler concat (s1, s2, s3)
287*60240Shibler      char *s1, *s2, *s3;
288*60240Shibler {
289*60240Shibler   int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
290*60240Shibler   char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
291*60240Shibler 
292*60240Shibler   strcpy (result, s1);
293*60240Shibler   strcpy (result + len1, s2);
294*60240Shibler   strcpy (result + len1 + len2, s3);
295*60240Shibler   *(result + len1 + len2 + len3) = 0;
296*60240Shibler 
297*60240Shibler   return result;
298*60240Shibler }
299*60240Shibler 
300*60240Shibler /* Like malloc but get fatal error if memory is exhausted.  */
301*60240Shibler 
302*60240Shibler int
303*60240Shibler xmalloc (size)
304*60240Shibler      int size;
305*60240Shibler {
306*60240Shibler   int result = malloc (size);
307*60240Shibler   if (!result)
308*60240Shibler     fatal ("virtual memory exhausted", 0);
309*60240Shibler   return result;
310*60240Shibler }
311*60240Shibler 
312*60240Shibler /* This is the guts of the interface to the Post Office Protocol.  */
313*60240Shibler 
314*60240Shibler #ifdef MAIL_USE_POP
315*60240Shibler 
316*60240Shibler #include <sys/socket.h>
317*60240Shibler #include <netinet/in.h>
318*60240Shibler #include <netdb.h>
319*60240Shibler #include <stdio.h>
320*60240Shibler 
321*60240Shibler #ifdef USG
322*60240Shibler #include <fcntl.h>
323*60240Shibler /* Cancel substitutions made by config.h for Emacs.  */
324*60240Shibler #undef open
325*60240Shibler #undef read
326*60240Shibler #undef write
327*60240Shibler #undef close
328*60240Shibler #endif /* USG */
329*60240Shibler 
330*60240Shibler #define NOTOK (-1)
331*60240Shibler #define OK 0
332*60240Shibler #define DONE 1
333*60240Shibler 
334*60240Shibler char *progname;
335*60240Shibler FILE *sfi;
336*60240Shibler FILE *sfo;
337*60240Shibler char Errmsg[80];
338*60240Shibler 
339*60240Shibler static int debug = 0;
340*60240Shibler 
341*60240Shibler popmail(user, outfile)
342*60240Shibler char *user;
343*60240Shibler char *outfile;
344*60240Shibler {
345*60240Shibler     char *host;
346*60240Shibler     int nmsgs, nbytes;
347*60240Shibler     char response[128];
348*60240Shibler     register int i;
349*60240Shibler     int mbfi;
350*60240Shibler     FILE *mbf;
351*60240Shibler     char *getenv();
352*60240Shibler     int mbx_write();
353*60240Shibler     char *get_errmsg();
354*60240Shibler 
355*60240Shibler     host = getenv("MAILHOST");
356*60240Shibler     if (host == NULL) {
357*60240Shibler 	fatal("no MAILHOST defined");
358*60240Shibler     }
359*60240Shibler 
360*60240Shibler     if (pop_init(host) == NOTOK) {
361*60240Shibler 	error(Errmsg);
362*60240Shibler 	return(1);
363*60240Shibler     }
364*60240Shibler 
365*60240Shibler     if (getline(response, sizeof response, sfi) != OK) {
366*60240Shibler 	error(response);
367*60240Shibler 	return(1);
368*60240Shibler     }
369*60240Shibler 
370*60240Shibler     if (pop_command("USER %s", user) == NOTOK ||
371*60240Shibler 	pop_command("RPOP %s", user) == NOTOK) {
372*60240Shibler 	error(Errmsg);
373*60240Shibler 	pop_command("QUIT");
374*60240Shibler 	return(1);
375*60240Shibler     }
376*60240Shibler 
377*60240Shibler     if (pop_stat(&nmsgs, &nbytes) == NOTOK) {
378*60240Shibler 	error(Errmsg);
379*60240Shibler 	pop_command("QUIT");
380*60240Shibler 	return(1);
381*60240Shibler     }
382*60240Shibler 
383*60240Shibler     if (!nmsgs)
384*60240Shibler       {
385*60240Shibler 	pop_command("QUIT");
386*60240Shibler 	return(0);
387*60240Shibler       }
388*60240Shibler 
389*60240Shibler     mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
390*60240Shibler     if (mbfi < 0)
391*60240Shibler       {
392*60240Shibler 	pop_command("QUIT");
393*60240Shibler 	error("Error in open: %s, %s", get_errmsg(), outfile);
394*60240Shibler 	return(1);
395*60240Shibler       }
396*60240Shibler     fchown(mbfi, getuid(), -1);
397*60240Shibler 
398*60240Shibler     if ((mbf = fdopen(mbfi, "w")) == NULL)
399*60240Shibler       {
400*60240Shibler 	pop_command("QUIT");
401*60240Shibler 	error("Error in fdopen: %s", get_errmsg());
402*60240Shibler 	close(mbfi);
403*60240Shibler 	unlink(outfile);
404*60240Shibler 	return(1);
405*60240Shibler       }
406*60240Shibler 
407*60240Shibler     for (i = 1; i <= nmsgs; i++) {
408*60240Shibler 	mbx_delimit_begin(mbf);
409*60240Shibler 	if (pop_retr(i, mbx_write, mbf) != OK) {
410*60240Shibler 	    error(Errmsg);
411*60240Shibler 	    pop_command("QUIT");
412*60240Shibler 	    close(mbfi);
413*60240Shibler 	    return(1);
414*60240Shibler 	}
415*60240Shibler 	mbx_delimit_end(mbf);
416*60240Shibler 	fflush(mbf);
417*60240Shibler     }
418*60240Shibler 
419*60240Shibler     for (i = 1; i <= nmsgs; i++) {
420*60240Shibler 	if (pop_command("DELE %d", i) == NOTOK) {
421*60240Shibler 	    error(Errmsg);
422*60240Shibler 	    pop_command("QUIT");
423*60240Shibler 	    close(mbfi);
424*60240Shibler 	    return(1);
425*60240Shibler 	}
426*60240Shibler     }
427*60240Shibler 
428*60240Shibler     pop_command("QUIT");
429*60240Shibler     close(mbfi);
430*60240Shibler     return(0);
431*60240Shibler }
432*60240Shibler 
433*60240Shibler pop_init(host)
434*60240Shibler char *host;
435*60240Shibler {
436*60240Shibler     register struct hostent *hp;
437*60240Shibler     register struct servent *sp;
438*60240Shibler     int lport = IPPORT_RESERVED - 1;
439*60240Shibler     struct sockaddr_in sin;
440*60240Shibler     register int s;
441*60240Shibler     char *get_errmsg();
442*60240Shibler 
443*60240Shibler     hp = gethostbyname(host);
444*60240Shibler     if (hp == NULL) {
445*60240Shibler 	sprintf(Errmsg, "MAILHOST unknown: %s", host);
446*60240Shibler 	return(NOTOK);
447*60240Shibler     }
448*60240Shibler 
449*60240Shibler     sp = getservbyname("pop", "tcp");
450*60240Shibler     if (sp == 0) {
451*60240Shibler 	strcpy(Errmsg, "tcp/pop: unknown service");
452*60240Shibler 	return(NOTOK);
453*60240Shibler     }
454*60240Shibler 
455*60240Shibler     sin.sin_family = hp->h_addrtype;
456*60240Shibler     bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
457*60240Shibler     sin.sin_port = sp->s_port;
458*60240Shibler     s = rresvport(&lport);
459*60240Shibler     if (s < 0) {
460*60240Shibler 	sprintf(Errmsg, "error creating socket: %s", get_errmsg());
461*60240Shibler 	return(NOTOK);
462*60240Shibler     }
463*60240Shibler 
464*60240Shibler     if (connect(s, (char *)&sin, sizeof sin) < 0) {
465*60240Shibler 	sprintf(Errmsg, "error during connect: %s", get_errmsg());
466*60240Shibler 	close(s);
467*60240Shibler 	return(NOTOK);
468*60240Shibler     }
469*60240Shibler 
470*60240Shibler     sfi = fdopen(s, "r");
471*60240Shibler     sfo = fdopen(s, "w");
472*60240Shibler     if (sfi == NULL || sfo == NULL) {
473*60240Shibler 	sprintf(Errmsg, "error in fdopen: %s", get_errmsg());
474*60240Shibler 	close(s);
475*60240Shibler 	return(NOTOK);
476*60240Shibler     }
477*60240Shibler 
478*60240Shibler     return(OK);
479*60240Shibler }
480*60240Shibler 
481*60240Shibler pop_command(fmt, a, b, c, d)
482*60240Shibler char *fmt;
483*60240Shibler {
484*60240Shibler     char buf[128];
485*60240Shibler     char errmsg[64];
486*60240Shibler 
487*60240Shibler     sprintf(buf, fmt, a, b, c, d);
488*60240Shibler 
489*60240Shibler     if (debug) fprintf(stderr, "---> %s\n", buf);
490*60240Shibler     if (putline(buf, Errmsg, sfo) == NOTOK) return(NOTOK);
491*60240Shibler 
492*60240Shibler     if (getline(buf, sizeof buf, sfi) != OK) {
493*60240Shibler 	strcpy(Errmsg, buf);
494*60240Shibler 	return(NOTOK);
495*60240Shibler     }
496*60240Shibler 
497*60240Shibler     if (debug) fprintf(stderr, "<--- %s\n", buf);
498*60240Shibler     if (*buf != '+') {
499*60240Shibler 	strcpy(Errmsg, buf);
500*60240Shibler 	return(NOTOK);
501*60240Shibler     } else {
502*60240Shibler 	return(OK);
503*60240Shibler     }
504*60240Shibler }
505*60240Shibler 
506*60240Shibler 
507*60240Shibler pop_stat(nmsgs, nbytes)
508*60240Shibler int *nmsgs, *nbytes;
509*60240Shibler {
510*60240Shibler     char buf[128];
511*60240Shibler 
512*60240Shibler     if (debug) fprintf(stderr, "---> STAT\n");
513*60240Shibler     if (putline("STAT", Errmsg, sfo) == NOTOK) return(NOTOK);
514*60240Shibler 
515*60240Shibler     if (getline(buf, sizeof buf, sfi) != OK) {
516*60240Shibler 	strcpy(Errmsg, buf);
517*60240Shibler 	return(NOTOK);
518*60240Shibler     }
519*60240Shibler 
520*60240Shibler     if (debug) fprintf(stderr, "<--- %s\n", buf);
521*60240Shibler     if (*buf != '+') {
522*60240Shibler 	strcpy(Errmsg, buf);
523*60240Shibler 	return(NOTOK);
524*60240Shibler     } else {
525*60240Shibler 	sscanf(buf, "+OK %d %d", nmsgs, nbytes);
526*60240Shibler 	return(OK);
527*60240Shibler     }
528*60240Shibler }
529*60240Shibler 
530*60240Shibler pop_retr(msgno, action, arg)
531*60240Shibler int (*action)();
532*60240Shibler {
533*60240Shibler     char buf[128];
534*60240Shibler 
535*60240Shibler     sprintf(buf, "RETR %d", msgno);
536*60240Shibler     if (debug) fprintf(stderr, "%s\n", buf);
537*60240Shibler     if (putline(buf, Errmsg, sfo) == NOTOK) return(NOTOK);
538*60240Shibler 
539*60240Shibler     if (getline(buf, sizeof buf, sfi) != OK) {
540*60240Shibler 	strcpy(Errmsg, buf);
541*60240Shibler 	return(NOTOK);
542*60240Shibler     }
543*60240Shibler 
544*60240Shibler     while (1) {
545*60240Shibler 	switch (multiline(buf, sizeof buf, sfi)) {
546*60240Shibler 	case OK:
547*60240Shibler 	    (*action)(buf, arg);
548*60240Shibler 	    break;
549*60240Shibler 	case DONE:
550*60240Shibler 	    return (OK);
551*60240Shibler 	case NOTOK:
552*60240Shibler 	    strcpy(Errmsg, buf);
553*60240Shibler 	    return (NOTOK);
554*60240Shibler 	}
555*60240Shibler     }
556*60240Shibler }
557*60240Shibler 
558*60240Shibler getline(buf, n, f)
559*60240Shibler char *buf;
560*60240Shibler register int n;
561*60240Shibler FILE *f;
562*60240Shibler {
563*60240Shibler     register char *p;
564*60240Shibler     int c;
565*60240Shibler 
566*60240Shibler     p = buf;
567*60240Shibler     while (--n > 0 && (c = fgetc(f)) != EOF)
568*60240Shibler       if ((*p++ = c) == '\n') break;
569*60240Shibler 
570*60240Shibler     if (ferror(f)) {
571*60240Shibler 	strcpy(buf, "error on connection");
572*60240Shibler 	return (NOTOK);
573*60240Shibler     }
574*60240Shibler 
575*60240Shibler     if (c == EOF && p == buf) {
576*60240Shibler 	strcpy(buf, "connection closed by foreign host");
577*60240Shibler 	return (DONE);
578*60240Shibler     }
579*60240Shibler 
580*60240Shibler     *p = NULL;
581*60240Shibler     if (*--p == '\n') *p = NULL;
582*60240Shibler     if (*--p == '\r') *p = NULL;
583*60240Shibler     return(OK);
584*60240Shibler }
585*60240Shibler 
586*60240Shibler multiline(buf, n, f)
587*60240Shibler char *buf;
588*60240Shibler register int n;
589*60240Shibler FILE *f;
590*60240Shibler {
591*60240Shibler     if (getline(buf, n, f) != OK) return (NOTOK);
592*60240Shibler     if (*buf == '.') {
593*60240Shibler 	if (*(buf+1) == NULL) {
594*60240Shibler 	    return (DONE);
595*60240Shibler 	} else {
596*60240Shibler 	    strcpy(buf, buf+1);
597*60240Shibler 	}
598*60240Shibler     }
599*60240Shibler     return(OK);
600*60240Shibler }
601*60240Shibler 
602*60240Shibler char *
603*60240Shibler get_errmsg()
604*60240Shibler {
605*60240Shibler     extern int errno, sys_nerr;
606*60240Shibler     extern char *sys_errlist[];
607*60240Shibler     char *s;
608*60240Shibler 
609*60240Shibler     if (errno < sys_nerr)
610*60240Shibler       s = sys_errlist[errno];
611*60240Shibler     else
612*60240Shibler       s = "unknown error";
613*60240Shibler     return(s);
614*60240Shibler }
615*60240Shibler 
616*60240Shibler putline(buf, err, f)
617*60240Shibler char *buf;
618*60240Shibler char *err;
619*60240Shibler FILE *f;
620*60240Shibler {
621*60240Shibler     fprintf(f, "%s\r\n", buf);
622*60240Shibler     fflush(f);
623*60240Shibler     if (ferror(f)) {
624*60240Shibler 	strcpy(err, "lost connection");
625*60240Shibler 	return(NOTOK);
626*60240Shibler     }
627*60240Shibler     return(OK);
628*60240Shibler }
629*60240Shibler 
630*60240Shibler mbx_write(line, mbf)
631*60240Shibler char *line;
632*60240Shibler FILE *mbf;
633*60240Shibler {
634*60240Shibler     fputs(line, mbf);
635*60240Shibler     fputc(0x0a, mbf);
636*60240Shibler }
637*60240Shibler 
638*60240Shibler mbx_delimit_begin(mbf)
639*60240Shibler FILE *mbf;
640*60240Shibler {
641*60240Shibler     fputs("\f\n0, unseen,,\n", mbf);
642*60240Shibler }
643*60240Shibler 
644*60240Shibler mbx_delimit_end(mbf)
645*60240Shibler FILE *mbf;
646*60240Shibler {
647*60240Shibler     putc('\037', mbf);
648*60240Shibler }
649*60240Shibler 
650*60240Shibler #endif /* MAIL_USE_POP */
651