Mercurial > emacs
view lib-src/movemail.c @ 333:885bf0750dbb
Initial revision
author | Roland McGrath <roland@gnu.org> |
---|---|
date | Mon, 15 Jul 1991 21:27:11 +0000 |
parents | 762710f7381a |
children | 5729b1cc3942 |
line wrap: on
line source
/* movemail foo bar -- move file foo to file bar, locking file foo the way /bin/mail respects. Copyright (C) 1986 Free Software Foundation, Inc. This file is part of GNU Emacs. GNU Emacs is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. GNU Emacs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Emacs; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Modified January, 1986 by Michael R. Gretzinger (Project Athena) * * Added POP (Post Office Protocol) service. When compiled -DPOP * movemail will accept input filename arguments of the form * "po:username". This will cause movemail to open a connection to * a pop server running on $MAILHOST (environment variable). Movemail * must be setuid to root in order to work with POP. * * New module: popmail.c * Modified routines: * main - added code within #ifdef MAIL_USE_POP; added setuid(getuid()) * after POP code. * New routines in movemail.c: * get_errmsg - return pointer to system error message * */ #include <sys/types.h> #include <sys/stat.h> #include <sys/file.h> #include <errno.h> #define NO_SHORTNAMES /* Tell config not to load remap.h */ #include "../src/config.h" #ifdef USG #include <fcntl.h> #include <unistd.h> #ifndef F_OK #define F_OK 0 #define X_OK 1 #define W_OK 2 #define R_OK 4 #endif #endif /* USG */ #ifdef XENIX #include <sys/locking.h> #endif #ifdef MAIL_USE_MMDF extern int lk_open (), lk_close (); #endif /* Cancel substitutions made by config.h for Emacs. */ #undef open #undef read #undef write #undef close char *concat (); extern int errno; /* Nonzero means this is name of a lock file to delete on fatal error. */ char *delete_lockname; main (argc, argv) int argc; char **argv; { char *inname, *outname; int indesc, outdesc; char buf[1024]; int nread; #ifndef MAIL_USE_FLOCK struct stat st; long now; int tem; char *lockname, *p; char tempname[40]; int desc; #endif /* not MAIL_USE_FLOCK */ delete_lockname = 0; if (argc < 3) fatal ("two arguments required"); inname = argv[1]; outname = argv[2]; #ifdef MAIL_USE_MMDF mmdf_init (argv[0]); #endif /* Check access to output file. */ if (access (outname, F_OK) == 0 && access (outname, W_OK) != 0) pfatal_with_name (outname); /* Also check that outname's directory is writeable to the real uid. */ { char *buf = (char *) malloc (strlen (outname) + 1); char *p, q; strcpy (buf, outname); p = buf + strlen (buf); while (p > buf && p[-1] != '/') *--p = 0; if (p == buf) *p++ = '.'; if (access (buf, W_OK) != 0) pfatal_with_name (buf); free (buf); } #ifdef MAIL_USE_POP if (!bcmp (inname, "po:", 3)) { int status; char *user; user = (char *) rindex (inname, ':') + 1; status = popmail (user, outname); exit (status); } setuid (getuid()); #endif /* MAIL_USE_POP */ /* Check access to input file. */ if (access (inname, R_OK | W_OK) != 0) pfatal_with_name (inname); #ifndef MAIL_USE_MMDF #ifndef MAIL_USE_FLOCK /* Use a lock file named /usr/spool/mail/$USER.lock: If it exists, the mail file is locked. */ lockname = concat (inname, ".lock", ""); strcpy (tempname, inname); p = tempname + strlen (tempname); while (p != tempname && p[-1] != '/') p--; *p = 0; strcpy (p, "EXXXXXX"); mktemp (tempname); (void) unlink (tempname); while (1) { /* Create the lock file, but not under the lock file name. */ /* Give up if cannot do that. */ desc = open (tempname, O_WRONLY | O_CREAT, 0666); if (desc < 0) pfatal_with_name (concat ("temporary file \"", tempname, "\"")); close (desc); tem = link (tempname, lockname); (void) unlink (tempname); if (tem >= 0) break; sleep (1); /* If lock file is a minute old, unlock it. */ if (stat (lockname, &st) >= 0) { now = time (0); if (st.st_ctime < now - 60) (void) unlink (lockname); } } delete_lockname = lockname; #endif /* not MAIL_USE_FLOCK */ #ifdef MAIL_USE_FLOCK indesc = open (inname, O_RDWR); #else /* if not MAIL_USE_FLOCK */ indesc = open (inname, O_RDONLY); #endif /* not MAIL_USE_FLOCK */ #else /* MAIL_USE_MMDF */ indesc = lk_open (inname, O_RDONLY, 0, 0, 10); #endif /* MAIL_USE_MMDF */ if (indesc < 0) pfatal_with_name (inname); #if defined(BSD) || defined(XENIX) /* In case movemail is setuid to root, make sure the user can read the output file. */ /* This is desirable for all systems but I don't want to assume all have the umask system call */ umask (umask (0) & 0333); #endif /* BSD or Xenix */ outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666); if (outdesc < 0) pfatal_with_name (outname); #ifdef MAIL_USE_FLOCK #ifdef XENIX if (locking (indesc, LK_RLCK, 0L) < 0) pfatal_with_name (inname); #else if (flock (indesc, LOCK_EX) < 0) pfatal_with_name (inname); #endif #endif /* MAIL_USE_FLOCK */ while (1) { nread = read (indesc, buf, sizeof buf); if (nread != write (outdesc, buf, nread)) { int saved_errno = errno; (void) unlink (outname); errno = saved_errno; pfatal_with_name (outname); } if (nread < sizeof buf) break; } #ifdef BSD fsync (outdesc); #endif /* Check to make sure no errors before we zap the inbox. */ if (close (outdesc) != 0) { int saved_errno = errno; (void) unlink (outname); errno = saved_errno; pfatal_with_name (outname); } #ifdef MAIL_USE_FLOCK #if defined(STRIDE) || defined(XENIX) /* Stride, xenix have file locking, but no ftruncate. This mess will do. */ (void) close (open (inname, O_CREAT | O_TRUNC | O_RDWR, 0666)); #else (void) ftruncate (indesc, 0L); #endif /* STRIDE or XENIX */ #endif /* MAIL_USE_FLOCK */ #ifdef MAIL_USE_MMDF lk_close (indesc, 0, 0, 0); #else close (indesc); #endif #ifndef MAIL_USE_FLOCK /* Delete the input file; if we can't, at least get rid of its contents. */ if (unlink (inname) < 0) if (errno != ENOENT) creat (inname, 0666); #ifndef MAIL_USE_MMDF unlink (lockname); #endif /* not MAIL_USE_MMDF */ #endif /* not MAIL_USE_FLOCK */ exit (0); } /* Print error message and exit. */ fatal (s1, s2) char *s1, *s2; { if (delete_lockname) unlink (delete_lockname); error (s1, s2); exit (1); } /* Print error message. `s1' is printf control string, `s2' is arg for it. */ error (s1, s2, s3) char *s1, *s2, *s3; { printf ("movemail: "); printf (s1, s2, s3); printf ("\n"); } pfatal_with_name (name) char *name; { extern int errno, sys_nerr; extern char *sys_errlist[]; char *s; if (errno < sys_nerr) s = concat ("", sys_errlist[errno], " for %s"); else s = "cannot open %s"; fatal (s, name); } /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */ char * concat (s1, s2, s3) char *s1, *s2, *s3; { int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3); char *result = (char *) xmalloc (len1 + len2 + len3 + 1); strcpy (result, s1); strcpy (result + len1, s2); strcpy (result + len1 + len2, s3); *(result + len1 + len2 + len3) = 0; return result; } /* Like malloc but get fatal error if memory is exhausted. */ int xmalloc (size) int size; { int result = malloc (size); if (!result) fatal ("virtual memory exhausted", 0); return result; } /* This is the guts of the interface to the Post Office Protocol. */ #ifdef MAIL_USE_POP #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <stdio.h> #ifdef USG #include <fcntl.h> /* Cancel substitutions made by config.h for Emacs. */ #undef open #undef read #undef write #undef close #endif /* USG */ #define NOTOK (-1) #define OK 0 #define DONE 1 char *progname; FILE *sfi; FILE *sfo; char Errmsg[80]; static int debug = 0; char *get_errmsg (); char *getenv (); int mbx_write (); popmail (user, outfile) char *user; char *outfile; { char *host; int nmsgs, nbytes; char response[128]; register int i; int mbfi; FILE *mbf; host = getenv ("MAILHOST"); if (host == NULL) { fatal ("no MAILHOST defined"); } if (pop_init (host) == NOTOK) { error (Errmsg); return 1; } if (getline (response, sizeof response, sfi) != OK) { error (response); return 1; } if (pop_command ("USER %s", user) == NOTOK || pop_command ("RPOP %s", user) == NOTOK) { error (Errmsg); pop_command ("QUIT"); return 1; } if (pop_stat (&nmsgs, &nbytes) == NOTOK) { error (Errmsg); pop_command ("QUIT"); return 1; } if (!nmsgs) { pop_command ("QUIT"); return 0; } mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666); if (mbfi < 0) { pop_command ("QUIT"); error ("Error in open: %s, %s", get_errmsg (), outfile); return 1; } fchown (mbfi, getuid (), -1); if ((mbf = fdopen (mbfi, "w")) == NULL) { pop_command ("QUIT"); error ("Error in fdopen: %s", get_errmsg ()); close (mbfi); unlink (outfile); return 1; } for (i = 1; i <= nmsgs; i++) { mbx_delimit_begin (mbf); if (pop_retr (i, mbx_write, mbf) != OK) { error (Errmsg); pop_command ("QUIT"); close (mbfi); return 1; } mbx_delimit_end (mbf); fflush (mbf); } for (i = 1; i <= nmsgs; i++) { if (pop_command ("DELE %d", i) == NOTOK) { error (Errmsg); pop_command ("QUIT"); close (mbfi); return 1; } } pop_command ("QUIT"); close (mbfi); return 0; } pop_init (host) char *host; { register struct hostent *hp; register struct servent *sp; int lport = IPPORT_RESERVED - 1; struct sockaddr_in sin; register int s; hp = gethostbyname (host); if (hp == NULL) { sprintf (Errmsg, "MAILHOST unknown: %s", host); return NOTOK; } sp = getservbyname ("pop", "tcp"); if (sp == 0) { strcpy (Errmsg, "tcp/pop: unknown service"); return NOTOK; } sin.sin_family = hp->h_addrtype; bcopy (hp->h_addr, (char *)&sin.sin_addr, hp->h_length); sin.sin_port = sp->s_port; s = rresvport (&lport); if (s < 0) { sprintf (Errmsg, "error creating socket: %s", get_errmsg ()); return NOTOK; } if (connect (s, (char *)&sin, sizeof sin) < 0) { sprintf (Errmsg, "error during connect: %s", get_errmsg ()); close (s); return NOTOK; } sfi = fdopen (s, "r"); sfo = fdopen (s, "w"); if (sfi == NULL || sfo == NULL) { sprintf (Errmsg, "error in fdopen: %s", get_errmsg ()); close (s); return NOTOK; } return OK; } pop_command (fmt, a, b, c, d) char *fmt; { char buf[128]; char errmsg[64]; sprintf (buf, fmt, a, b, c, d); if (debug) fprintf (stderr, "---> %s\n", buf); if (putline (buf, Errmsg, sfo) == NOTOK) return NOTOK; if (getline (buf, sizeof buf, sfi) != OK) { strcpy (Errmsg, buf); return NOTOK; } if (debug) fprintf (stderr, "<--- %s\n", buf); if (*buf != '+') { strcpy (Errmsg, buf); return NOTOK; } else { return OK; } } pop_stat (nmsgs, nbytes) int *nmsgs, *nbytes; { char buf[128]; if (debug) fprintf (stderr, "---> STAT\n"); if (putline ("STAT", Errmsg, sfo) == NOTOK) return NOTOK; if (getline (buf, sizeof buf, sfi) != OK) { strcpy (Errmsg, buf); return NOTOK; } if (debug) fprintf (stderr, "<--- %s\n", buf); if (*buf != '+') { strcpy (Errmsg, buf); return NOTOK; } else { sscanf (buf, "+OK %d %d", nmsgs, nbytes); return OK; } } pop_retr (msgno, action, arg) int (*action)(); { char buf[128]; sprintf (buf, "RETR %d", msgno); if (debug) fprintf (stderr, "%s\n", buf); if (putline (buf, Errmsg, sfo) == NOTOK) return NOTOK; if (getline (buf, sizeof buf, sfi) != OK) { strcpy (Errmsg, buf); return NOTOK; } while (1) { switch (multiline (buf, sizeof buf, sfi)) { case OK: (*action)(buf, arg); break; case DONE: return OK; case NOTOK: strcpy (Errmsg, buf); return NOTOK; } } } getline (buf, n, f) char *buf; register int n; FILE *f; { register char *p; int c; p = buf; while (--n > 0 && (c = fgetc (f)) != EOF) if ((*p++ = c) == '\n') break; if (ferror (f)) { strcpy (buf, "error on connection"); return NOTOK; } if (c == EOF && p == buf) { strcpy (buf, "connection closed by foreign host"); return DONE; } *p = NULL; if (*--p == '\n') *p = NULL; if (*--p == '\r') *p = NULL; return OK; } multiline (buf, n, f) char *buf; register int n; FILE *f; { if (getline (buf, n, f) != OK) return NOTOK; if (*buf == '.') { if (*(buf+1) == NULL) { return DONE; } else { strcpy (buf, buf+1); } } return OK; } char * get_errmsg () { extern int errno, sys_nerr; extern char *sys_errlist[]; char *s; if (errno < sys_nerr) s = sys_errlist[errno]; else s = "unknown error"; return (s); } putline (buf, err, f) char *buf; char *err; FILE *f; { fprintf (f, "%s\r\n", buf); fflush (f); if (ferror (f)) { strcpy (err, "lost connection"); return NOTOK; } return OK; } mbx_write (line, mbf) char *line; FILE *mbf; { fputs (line, mbf); fputc (0x0a, mbf); } mbx_delimit_begin (mbf) FILE *mbf; { fputs ("\f\n0, unseen,,\n", mbf); } mbx_delimit_end (mbf) FILE *mbf; { putc ('\037', mbf); } #endif /* MAIL_USE_POP */