home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / gnu / emacs-18.59-bin.lha / lib / emacs / 18.59 / etc / fakemail.c < prev    next >
C/C++ Source or Header  |  1991-01-08  |  13KB  |  610 lines

  1. /* sendmail-like interface to /bin/mail for system V,
  2.    Copyright (C) 1985 Free Software Foundation, Inc.
  3.  
  4. This file is part of GNU Emacs.
  5.  
  6. GNU Emacs is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 1, or (at your option)
  9. any later version.
  10.  
  11. GNU Emacs is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with GNU Emacs; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20.  
  21. #define NO_SHORTNAMES
  22. #include "../src/config.h"
  23.  
  24. #if defined (BSD) && !defined (BSD4_1)
  25. /* This program isnot used in BSD, so just avoid loader complaints.  */
  26. main ()
  27. {
  28. }
  29. #else /* not BSD 4.2 (or newer) */
  30. /* This conditional contains all the rest of the file.  */
  31.  
  32. /* These are defined in config in some versions. */
  33.  
  34. #ifdef static
  35. #undef static
  36. #endif
  37.  
  38. #ifdef read
  39. #undef read
  40. #undef write
  41. #undef open
  42. #undef close
  43. #endif
  44.  
  45. #include <stdio.h>
  46. #include <string.h>
  47. #include <ctype.h>
  48. #include <time.h>
  49. #include <pwd.h>
  50.  
  51. /* Type definitions */
  52.  
  53. #define boolean int
  54. #define true 1
  55. #define false 0
  56.  
  57. /* Various lists */
  58.  
  59. struct line_record
  60. {
  61.   char *string;
  62.   struct line_record *continuation;
  63. };
  64. typedef struct line_record *line_list;
  65.  
  66. struct header_record
  67. {
  68.   line_list text;
  69.   struct header_record *next;
  70.   struct header_record *previous;
  71. };
  72. typedef struct header_record *header;
  73.             
  74. struct stream_record
  75. {
  76.   FILE *handle;
  77.   int (*action)();
  78.   struct stream_record *rest_streams;
  79. };
  80. typedef struct stream_record *stream_list;
  81.  
  82. /* A `struct linebuffer' is a structure which holds a line of text.
  83.  * `readline' reads a line from a stream into a linebuffer
  84.  * and works regardless of the length of the line.
  85.  */
  86.  
  87. struct linebuffer
  88. {
  89.   long size;
  90.   char *buffer;
  91. };
  92.  
  93. struct linebuffer lb;
  94.  
  95. #define new_list()                    \
  96.   ((line_list) xmalloc (sizeof (struct line_record)))
  97. #define new_header()                \
  98.   ((header) xmalloc (sizeof (struct header_record)))
  99. #define new_stream()                \
  100.   ((stream_list) xmalloc (sizeof (struct stream_record)))
  101. #define alloc_string(nchars)                \
  102.   ((char *) xmalloc ((nchars) + 1))
  103.  
  104. /* Global declarations */
  105.  
  106. #define BUFLEN 1024
  107. #define KEYWORD_SIZE 256
  108. #define FROM_PREFIX "From"
  109. #define MY_NAME "fakemail"
  110. #define NIL ((line_list) NULL)
  111. #define INITIAL_LINE_SIZE 200
  112.  
  113. #ifndef MAIL_PROGRAM_NAME
  114. #define MAIL_PROGRAM_NAME "/bin/mail"
  115. #endif
  116.  
  117. static char *my_name;
  118. static char *the_date;
  119. static char *the_user;
  120. static line_list file_preface;
  121. static stream_list the_streams;
  122. static boolean no_problems = true;
  123.  
  124. extern FILE *popen ();
  125. extern int fclose (), pclose ();
  126. extern char *malloc (), *realloc ();
  127.  
  128. #ifdef CURRENT_USER
  129. extern struct passwd *getpwuid ();
  130. extern unsigned short geteuid ();
  131. static struct passwd *my_entry;
  132. #define cuserid(s)                \
  133. (my_entry = getpwuid (((int) geteuid ())),    \
  134.  my_entry->pw_name)
  135. #endif
  136.  
  137. /* Utilities */
  138.  
  139. /* Print error message.  `s1' is printf control string, `s2' is arg for it. */
  140.  
  141. static void
  142. error (s1, s2)
  143.      char *s1, *s2;
  144. {
  145.   printf ("%s: ", my_name);
  146.   printf (s1, s2);
  147.   printf ("\n");
  148.   no_problems = false;
  149. }
  150.  
  151. /* Print error message and exit.  */
  152.  
  153. static void
  154. fatal (s1, s2)
  155.      char *s1, *s2;
  156. {
  157.   error (s1, s2);
  158.   exit (1);
  159. }
  160.  
  161. /* Like malloc but get fatal error if memory is exhausted.  */
  162.  
  163. static char *
  164. xmalloc (size)
  165.      int size;
  166. {
  167.   char *result = malloc (((unsigned) size));
  168.   if (result == ((char *) NULL))
  169.     fatal ("virtual memory exhausted", 0);
  170.   return result;
  171. }
  172.  
  173. static char *
  174. xrealloc (ptr, size)
  175.      char *ptr;
  176.      int size;
  177. {
  178.   char *result = realloc (ptr, ((unsigned) size));
  179.   if (result == ((char *) NULL))
  180.     fatal ("virtual memory exhausted");
  181.   return result;
  182. }
  183.  
  184. /* Initialize a linebuffer for use */
  185.  
  186. void
  187. init_linebuffer (linebuffer)
  188.      struct linebuffer *linebuffer;
  189. {
  190.   linebuffer->size = INITIAL_LINE_SIZE;
  191.   linebuffer->buffer = ((char *) xmalloc (INITIAL_LINE_SIZE));
  192. }
  193.  
  194. /* Read a line of text from `stream' into `linebuffer'.
  195.  * Return the length of the line.  
  196.  */
  197.  
  198. long
  199. readline (linebuffer, stream)
  200.      struct linebuffer *linebuffer;
  201.      FILE *stream;
  202. {
  203.   char *buffer = linebuffer->buffer;
  204.   char *p = linebuffer->buffer;
  205.   char *end = p + linebuffer->size;
  206.  
  207.   while (true)
  208.     {
  209.       int c = getc (stream);
  210.       if (p == end)
  211.     {
  212.       linebuffer->size *= 2;
  213.       buffer = ((char *) xrealloc (buffer, linebuffer->size));
  214.       p += buffer - linebuffer->buffer;
  215.       end += buffer - linebuffer->buffer;
  216.       linebuffer->buffer = buffer;
  217.     }
  218.       if (c < 0 || c == '\n')
  219.     {
  220.       *p = 0;
  221.       break;
  222.     }
  223.       *p++ = c;
  224.     }
  225.  
  226.   return p - buffer;
  227. }
  228.  
  229. char *
  230. get_keyword (field, rest)
  231.      register char *field;
  232.      char **rest;
  233. {
  234.   static char keyword[KEYWORD_SIZE];
  235.   register char *ptr;
  236.   register char c;
  237.  
  238.   ptr = &keyword[0];
  239.   c = *field++;
  240.   if ((isspace (c)) || (c == ':'))
  241.     return ((char *) NULL);
  242.   *ptr++ = ((islower (c)) ? (toupper (c)) : c);
  243.   while (((c = *field++) != ':') && (!(isspace (c))))
  244.     *ptr++ = ((islower (c)) ? (toupper (c)) : c);
  245.   *ptr++ = '\0';
  246.   while (isspace (c)) c = *field++;
  247.   if (c != ':') return ((char *) NULL);
  248.   *rest = field;
  249.   return &keyword[0];
  250. }
  251.  
  252. boolean
  253. has_keyword (field)
  254.      char *field;
  255. {
  256.   char *ignored;
  257.   return (get_keyword (field, &ignored) != ((char *) NULL));
  258. }
  259.  
  260. char *
  261. add_field (the_list, field, where)
  262.      line_list the_list;
  263.      register char *field, *where;
  264. {
  265.   register char c;
  266.   while (true)
  267.     {
  268.       *where++ = ' ';
  269.       while ((c = *field++) != '\0')
  270.     *where++ = ((c == ',') ? ' ' : c);
  271.       if (the_list == NIL) break;
  272.       field = the_list->string;
  273.       the_list = the_list->continuation;
  274.     }
  275.   return where;
  276. }
  277.  
  278. line_list
  279. make_file_preface ()
  280. {
  281.   char *the_string, *temp;
  282.   long idiotic_interface;
  283.   long prefix_length;
  284.   long user_length;
  285.   long date_length;
  286.   line_list result;
  287.  
  288.   prefix_length = strlen (FROM_PREFIX);
  289.   time (&idiotic_interface);
  290.   the_date = ctime (&idiotic_interface);
  291.   /* the_date has an unwanted newline at the end */
  292.   date_length = strlen (the_date) - 1;
  293.   the_date[date_length] = '\0';
  294.   temp = cuserid ((char *) NULL);
  295.   user_length = strlen (temp);
  296.   the_user = alloc_string (user_length + 1);
  297.   strcpy (the_user, temp);
  298.   the_string = alloc_string (3 + prefix_length +
  299.                  user_length +
  300.                  date_length);
  301.   temp = the_string;
  302.   strcpy (temp, FROM_PREFIX);
  303.   temp = &temp[prefix_length];
  304.   *temp++ = ' ';
  305.   strcpy (temp, the_user);
  306.   temp = &temp[user_length];
  307.   *temp++ = ' ';
  308.   strcpy (temp, the_date);
  309.   result = new_list ();
  310.   result->string = the_string;
  311.   result->continuation = ((line_list) NULL);
  312.   return result;
  313. }
  314.  
  315. void
  316. write_line_list (the_list, the_stream)
  317.      register line_list the_list;
  318.      FILE *the_stream;
  319. {
  320.   for ( ;
  321.       the_list != ((line_list) NULL) ;
  322.       the_list = the_list->continuation)
  323.     {
  324.       fputs (the_list->string, the_stream);
  325.       putc ('\n', the_stream);
  326.     }
  327.   return;
  328. }
  329.  
  330. int
  331. close_the_streams ()
  332. {
  333.   register stream_list rem;
  334.   for (rem = the_streams;
  335.        rem != ((stream_list) NULL);
  336.        rem = rem->rest_streams)
  337.     no_problems = (no_problems &&
  338.            ((*rem->action) (rem->handle) == 0));
  339.   the_streams = ((stream_list) NULL);
  340.   return (no_problems ? 0 : 1);
  341. }
  342.  
  343. void
  344. add_a_stream (the_stream, closing_action)
  345.      FILE *the_stream;
  346.      int (*closing_action)();
  347. {
  348.   stream_list old = the_streams;
  349.   the_streams = new_stream ();
  350.   the_streams->handle = the_stream;
  351.   the_streams->action = closing_action;
  352.   the_streams->rest_streams = old;
  353.   return;
  354. }
  355.  
  356. int
  357. my_fclose (the_file)
  358.      FILE *the_file;
  359. {
  360.   putc ('\n', the_file);
  361.   fflush (the_file);
  362.   return fclose (the_file);
  363. }
  364.  
  365. boolean
  366. open_a_file (name)
  367.      char *name;
  368. {
  369.   FILE *the_stream = fopen (name, "a");
  370.   if (the_stream != ((FILE *) NULL))
  371.     {
  372.       add_a_stream (the_stream, my_fclose);
  373.       if (the_user == ((char *) NULL))
  374.     file_preface = make_file_preface ();
  375.       write_line_list (file_preface, the_stream);
  376.       return true;
  377.     }
  378.   return false;
  379. }
  380.  
  381. void
  382. put_string (s)
  383.      char *s;
  384. {
  385.   register stream_list rem;
  386.   for (rem = the_streams;
  387.        rem != ((stream_list) NULL);
  388.        rem = rem->rest_streams)
  389.     fputs (s, rem->handle);
  390.   return;
  391. }
  392.  
  393. void
  394. put_line (s)
  395.      char *s;
  396. {
  397.   register stream_list rem;
  398.   for (rem = the_streams;
  399.        rem != ((stream_list) NULL);
  400.        rem = rem->rest_streams)
  401.     {
  402.       fputs (s, rem->handle);
  403.       putc ('\n', rem->handle);
  404.     }
  405.   return;
  406. }
  407.  
  408. #define mail_error error
  409.  
  410. void
  411. setup_files (the_list, field)
  412.      register line_list the_list;
  413.      register char *field;
  414. {
  415.   register char *start;
  416.   register char c;
  417.   while (true)
  418.     {
  419.       while (((c = *field) != '\0') &&
  420.          ((c == ' ') ||
  421.           (c == '\t') ||
  422.           (c == ',')))
  423.     field += 1;
  424.       if (c != '\0')
  425.     {
  426.       start = field;
  427.       while (((c = *field) != '\0') &&
  428.          (c != ' ') &&
  429.          (c != '\t') &&
  430.          (c != ','))
  431.         field += 1;
  432.       *field = '\0';
  433.       if (!open_a_file (start))
  434.         mail_error ("Could not open file %s", start);
  435.       *field = c;
  436.       if (c != '\0') continue;
  437.     }
  438.       if (the_list == ((line_list) NULL)) return;
  439.       field = the_list->string;
  440.       the_list = the_list->continuation;
  441.     }
  442. }
  443.  
  444. int
  445. args_size (the_header)
  446.      header the_header;
  447. {
  448.   register header old = the_header;
  449.   register line_list rem;
  450.   register int size = 0;
  451.   do
  452.     {
  453.       char *field;
  454.       register char *keyword = get_keyword (the_header->text->string, &field);
  455.       if ((strcmp (keyword, "TO") == 0) ||
  456.       (strcmp (keyword, "CC") == 0) ||
  457.       (strcmp (keyword, "BCC") == 0))
  458.     {
  459.       size += 1 + strlen (field);
  460.       for (rem = the_header->text->continuation;
  461.            rem != NIL;
  462.            rem = rem->continuation)
  463.         size += 1 + strlen (rem->string);
  464.     }
  465.       the_header = the_header->next;
  466.     } while (the_header != old);
  467.   return size;
  468. }
  469.  
  470. parse_header (the_header, where)
  471.      header the_header;
  472.      register char *where;
  473. {
  474.   register header old = the_header;
  475.   do
  476.     {
  477.       char *field;
  478.       register char *keyword = get_keyword (the_header->text->string, &field);
  479.       if (strcmp (keyword, "TO") == 0)
  480.     where = add_field (the_header->text->continuation, field, where);
  481.       else if (strcmp (keyword, "CC") == 0)
  482.     where = add_field (the_header->text->continuation, field, where);
  483.       else if (strcmp (keyword, "BCC") == 0)
  484.     {
  485.       where = add_field (the_header->text->continuation, field, where);
  486.       the_header->previous->next = the_header->next;
  487.       the_header->next->previous = the_header->previous;
  488.     }
  489.       else if (strcmp (keyword, "FCC") == 0)
  490.     setup_files (the_header->text->continuation, field);
  491.       the_header = the_header->next;
  492.     } while (the_header != old);
  493.   *where = '\0';
  494.   return;
  495. }
  496.     
  497. header
  498. read_header ()
  499. {
  500.   register header the_header = ((header) NULL);
  501.   register line_list *next_line = ((line_list *) NULL);
  502.  
  503.   init_linebuffer (&lb);
  504.  
  505.   do
  506.     {
  507.       long length;
  508.       register char *line;
  509.  
  510.       readline (&lb, stdin);
  511.       line = lb.buffer;
  512.       length = strlen (line);
  513.       if (length == 0) break;
  514.  
  515.       if (has_keyword (line))
  516.     {
  517.       register header old = the_header;
  518.       the_header = new_header ();
  519.       if (old == ((header) NULL))
  520.         {
  521.           the_header->next = the_header;
  522.           the_header->previous = the_header;
  523.         }
  524.       else
  525.         {
  526.           the_header->previous = old;
  527.           the_header->next = old->next;
  528.           old->next = the_header;
  529.         }
  530.       next_line = &(the_header->text);
  531.     }
  532.  
  533.       if (next_line == ((line_list *) NULL))
  534.     {
  535.       /* Not a valid header */
  536.       exit (1);
  537.     }
  538.       *next_line = new_list ();
  539.       (*next_line)->string = alloc_string (length);
  540.       strcpy (((*next_line)->string), line);
  541.       next_line = &((*next_line)->continuation);
  542.       *next_line = NIL;
  543.  
  544.     } while (true);
  545.  
  546.   return the_header->next;
  547. }
  548.  
  549. void
  550. write_header (the_header)
  551.      header the_header;
  552. {
  553.   register header old = the_header;
  554.   do
  555.     {
  556.       register line_list the_list;
  557.       for (the_list = the_header->text;
  558.        the_list != NIL;
  559.        the_list = the_list->continuation)
  560.     put_line (the_list->string);
  561.       the_header = the_header->next;
  562.     } while (the_header != old);
  563.   put_line ("");
  564.   return;
  565. }
  566.  
  567. void
  568. main (argc, argv)
  569.      int argc;
  570.      char **argv;
  571. {
  572.   char *command_line;
  573.   header the_header;
  574.   long name_length = strlen (MAIL_PROGRAM_NAME);
  575.   char buf[BUFLEN + 1];
  576.   register int size;
  577.   FILE *the_pipe;
  578.  
  579.   my_name = MY_NAME;
  580.   the_streams = ((stream_list) NULL);
  581.   the_date = ((char *) NULL);
  582.   the_user = ((char *) NULL);
  583.  
  584.   the_header = read_header ();
  585.   command_line = alloc_string (name_length + args_size (the_header));
  586.   strcpy (command_line, MAIL_PROGRAM_NAME);
  587.   parse_header (the_header, &command_line[name_length]);
  588.   
  589.   the_pipe = popen (command_line, "w");
  590.   if (the_pipe == ((FILE *) NULL))
  591.     fatal ("cannot open pipe to real mailer");
  592.  
  593.   add_a_stream (the_pipe, pclose);
  594.  
  595.   write_header (the_header);
  596.  
  597.   /* Dump the message itself */
  598.  
  599.   while (!feof (stdin))
  600.     {
  601.       size = fread (buf, 1, BUFLEN, stdin);
  602.       buf[size] = '\0';
  603.       put_string (buf);
  604.     }
  605.  
  606.   exit (close_the_streams ());
  607. }
  608.  
  609. #endif /* not BSD 4.2 (or newer) */
  610.