home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / mint / editors / mntemacs.zoo / etc / movemail.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-04-29  |  13.2 KB  |  655 lines

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