1 /* sendmail-like interface to /bin/mail for system V,
2 Copyright (C) 1985 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
6 GNU Emacs is distributed in the hope that it will be useful,
7 but WITHOUT ANY WARRANTY. No author or distributor
8 accepts responsibility to anyone for the consequences of using it
9 or for whether it serves any particular purpose or works at all,
10 unless he says so in writing. Refer to the GNU Emacs General Public
11 License for full details.
13 Everyone is granted permission to copy, modify and redistribute
14 GNU Emacs, but only under the conditions described in the
15 GNU Emacs General Public License. A copy of this license is
16 supposed to have been given to you along with GNU Emacs so you
17 can know your rights and responsibilities. It should be in a
18 file named COPYING. Among other things, the copyright notice
19 and this notice must be preserved on all copies. */
23 #include "../src/config.h"
25 #if defined (BSD) && !defined (BSD4_1) && !defined (USE_FAKEMAIL)
26 /* This program isnot used in BSD, so just avoid loader complaints. */
30 #else /* not BSD 4.2 (or newer) */
31 /* This conditional contains all the rest of the file. */
33 /* These are defined in config in some versions. */
52 /* Type definitions */
63 struct line_record
*continuation
;
65 typedef struct line_record
*line_list
;
70 struct header_record
*next
;
71 struct header_record
*previous
;
73 typedef struct header_record
*header
;
79 struct stream_record
*rest_streams
;
81 typedef struct stream_record
*stream_list
;
83 /* A `struct linebuffer' is a structure which holds a line of text.
84 * `readline' reads a line from a stream into a linebuffer
85 * and works regardless of the length of the line.
97 ((line_list) xmalloc (sizeof (struct line_record)))
98 #define new_header() \
99 ((header) xmalloc (sizeof (struct header_record)))
100 #define new_stream() \
101 ((stream_list) xmalloc (sizeof (struct stream_record)))
102 #define alloc_string(nchars) \
103 ((char *) xmalloc ((nchars) + 1))
105 /* Global declarations */
108 #define KEYWORD_SIZE 256
109 #define FROM_PREFIX "From"
110 #define MY_NAME "fakemail"
111 #define NIL ((line_list) NULL)
112 #define INITIAL_LINE_SIZE 200
114 #ifndef MAIL_PROGRAM_NAME
115 #define MAIL_PROGRAM_NAME "/bin/mail"
118 static char *my_name
;
119 static char *the_date
;
120 static char *the_user
;
121 static line_list file_preface
;
122 static stream_list the_streams
;
123 static boolean no_problems
= true;
125 extern FILE *popen ();
126 extern int fclose (), pclose ();
127 extern char *malloc (), *realloc ();
130 extern struct passwd
*getpwuid ();
131 extern unsigned short geteuid ();
132 static struct passwd
*my_entry
;
134 (my_entry = getpwuid (((int) geteuid ())), \
140 /* Print error message. `s1' is printf control string, `s2' is arg for it. */
146 printf ("%s: ", my_name
);
152 /* Print error message and exit. */
162 /* Like malloc but get fatal error if memory is exhausted. */
168 char *result
= malloc (((unsigned) size
));
169 if (result
== ((char *) NULL
))
170 fatal ("virtual memory exhausted", 0);
179 char *result
= realloc (ptr
, ((unsigned) size
));
180 if (result
== ((char *) NULL
))
181 fatal ("virtual memory exhausted");
185 /* Initialize a linebuffer for use */
188 init_linebuffer (linebuffer
)
189 struct linebuffer
*linebuffer
;
191 linebuffer
->size
= INITIAL_LINE_SIZE
;
192 linebuffer
->buffer
= ((char *) xmalloc (INITIAL_LINE_SIZE
));
195 /* Read a line of text from `stream' into `linebuffer'.
196 * Return the length of the line.
200 readline (linebuffer
, stream
)
201 struct linebuffer
*linebuffer
;
204 char *buffer
= linebuffer
->buffer
;
205 char *p
= linebuffer
->buffer
;
206 char *end
= p
+ linebuffer
->size
;
210 int c
= getc (stream
);
213 linebuffer
->size
*= 2;
214 buffer
= ((char *) xrealloc (buffer
, linebuffer
->size
));
215 p
+= buffer
- linebuffer
->buffer
;
216 end
+= buffer
- linebuffer
->buffer
;
217 linebuffer
->buffer
= buffer
;
219 if (c
< 0 || c
== '\n')
231 get_keyword (field
, rest
)
232 register char *field
;
235 static char keyword
[KEYWORD_SIZE
];
241 if ((isspace (c
)) || (c
== ':'))
242 return ((char *) NULL
);
243 *ptr
++ = ((islower (c
)) ? (toupper (c
)) : c
);
244 while (((c
= *field
++) != ':') && (!(isspace (c
))))
245 *ptr
++ = ((islower (c
)) ? (toupper (c
)) : c
);
247 while (isspace (c
)) c
= *field
++;
248 if (c
!= ':') return ((char *) NULL
);
258 return (get_keyword (field
, &ignored
) != ((char *) NULL
));
262 add_field (the_list
, field
, where
)
264 register char *field
, *where
;
270 while ((c
= *field
++) != '\0')
274 while (*field
&& *field
!= ')') ++field
;
275 if (! (*field
++)) break; /* no closer */
276 if (! (*field
)) break; /* closerNULL */
279 *where
++ = ((c
== ','||c
=='>'||c
=='<') ? ' ' : c
);
281 if (the_list
== NIL
) break;
282 field
= the_list
->string
;
283 the_list
= the_list
->continuation
;
291 char *the_string
, *temp
;
292 long idiotic_interface
;
298 prefix_length
= strlen (FROM_PREFIX
);
299 time (&idiotic_interface
);
300 the_date
= ctime (&idiotic_interface
);
301 /* the_date has an unwanted newline at the end */
302 date_length
= strlen (the_date
) - 1;
303 the_date
[date_length
] = '\0';
304 temp
= cuserid ((char *) NULL
);
305 user_length
= strlen (temp
);
306 the_user
= alloc_string (user_length
+ 1);
307 strcpy (the_user
, temp
);
308 the_string
= alloc_string (3 + prefix_length
+
312 strcpy (temp
, FROM_PREFIX
);
313 temp
= &temp
[prefix_length
];
315 strcpy (temp
, the_user
);
316 temp
= &temp
[user_length
];
318 strcpy (temp
, the_date
);
319 result
= new_list ();
320 result
->string
= the_string
;
321 result
->continuation
= ((line_list
) NULL
);
326 write_line_list (the_list
, the_stream
)
327 register line_list the_list
;
331 the_list
!= ((line_list
) NULL
) ;
332 the_list
= the_list
->continuation
)
334 fputs (the_list
->string
, the_stream
);
335 putc ('\n', the_stream
);
343 register stream_list rem
;
344 for (rem
= the_streams
;
345 rem
!= ((stream_list
) NULL
);
346 rem
= rem
->rest_streams
)
347 no_problems
= (no_problems
&&
348 ((*rem
->action
) (rem
->handle
) == 0));
349 the_streams
= ((stream_list
) NULL
);
350 return (no_problems
? 0 : 1);
354 add_a_stream (the_stream
, closing_action
)
356 int (*closing_action
)();
358 stream_list old
= the_streams
;
359 the_streams
= new_stream ();
360 the_streams
->handle
= the_stream
;
361 the_streams
->action
= closing_action
;
362 the_streams
->rest_streams
= old
;
370 putc ('\n', the_file
);
372 return fclose (the_file
);
379 FILE *the_stream
= fopen (name
, "a");
380 if (the_stream
!= ((FILE *) NULL
))
382 add_a_stream (the_stream
, my_fclose
);
383 if (the_user
== ((char *) NULL
))
384 file_preface
= make_file_preface ();
385 write_line_list (file_preface
, the_stream
);
395 register stream_list rem
;
396 for (rem
= the_streams
;
397 rem
!= ((stream_list
) NULL
);
398 rem
= rem
->rest_streams
)
399 fputs (s
, rem
->handle
);
407 register stream_list rem
;
408 for (rem
= the_streams
;
409 rem
!= ((stream_list
) NULL
);
410 rem
= rem
->rest_streams
)
415 /* Divide STRING into lines. */
420 /* Find the last char that fits. */
421 for (breakpos
= s
; *breakpos
&& column
< 78; ++breakpos
)
423 if (*breakpos
== '\t')
428 /* Back up to just after the last comma that fits. */
429 while (breakpos
!= s
&& breakpos
[-1] != ',') --breakpos
;
432 /* If no comma fits, move past the first address anyway. */
433 while (*breakpos
!= 0 && *breakpos
!= ',') ++breakpos
;
435 /* Include the comma after it. */
438 /* Output that much, then break the line. */
439 fwrite (s
, 1, breakpos
- s
, rem
->handle
);
440 fputs ("\n\t", rem
->handle
);
443 /* Skip whitespace and prepare to print more addresses. */
445 while (*s
== ' ' || *s
== '\t') ++s
;
447 putc ('\n', rem
->handle
);
452 #define mail_error error
455 setup_files (the_list
, field
)
456 register line_list the_list
;
457 register char *field
;
459 register char *start
;
463 while (((c
= *field
) != '\0') &&
471 while (((c
= *field
) != '\0') &&
477 if (!open_a_file (start
))
478 mail_error ("Could not open file %s", start
);
480 if (c
!= '\0') continue;
482 if (the_list
== ((line_list
) NULL
)) return;
483 field
= the_list
->string
;
484 the_list
= the_list
->continuation
;
489 args_size (the_header
)
492 register header old
= the_header
;
493 register line_list rem
;
494 register int size
= 0;
498 register char *keyword
= get_keyword (the_header
->text
->string
, &field
);
499 if ((strcmp (keyword
, "TO") == 0) ||
500 (strcmp (keyword
, "CC") == 0) ||
501 (strcmp (keyword
, "BCC") == 0))
503 size
+= 1 + strlen (field
);
504 for (rem
= the_header
->text
->continuation
;
506 rem
= rem
->continuation
)
507 size
+= 1 + strlen (rem
->string
);
509 the_header
= the_header
->next
;
510 } while (the_header
!= old
);
514 parse_header (the_header
, where
)
516 register char *where
;
518 register header old
= the_header
;
522 register char *keyword
= get_keyword (the_header
->text
->string
, &field
);
523 if (strcmp (keyword
, "TO") == 0)
524 where
= add_field (the_header
->text
->continuation
, field
, where
);
525 else if (strcmp (keyword
, "CC") == 0)
526 where
= add_field (the_header
->text
->continuation
, field
, where
);
527 else if (strcmp (keyword
, "BCC") == 0)
529 where
= add_field (the_header
->text
->continuation
, field
, where
);
530 the_header
->previous
->next
= the_header
->next
;
531 the_header
->next
->previous
= the_header
->previous
;
533 else if (strcmp (keyword
, "FCC") == 0)
534 setup_files (the_header
->text
->continuation
, field
);
535 the_header
= the_header
->next
;
536 } while (the_header
!= old
);
544 register header the_header
= ((header
) NULL
);
545 register line_list
*next_line
= ((line_list
*) NULL
);
547 init_linebuffer (&lb
);
554 readline (&lb
, stdin
);
556 length
= strlen (line
);
557 if (length
== 0) break;
559 if (has_keyword (line
))
561 register header old
= the_header
;
562 the_header
= new_header ();
563 if (old
== ((header
) NULL
))
565 the_header
->next
= the_header
;
566 the_header
->previous
= the_header
;
570 the_header
->previous
= old
;
571 the_header
->next
= old
->next
;
572 old
->next
= the_header
;
574 next_line
= &(the_header
->text
);
577 if (next_line
== ((line_list
*) NULL
))
579 /* Not a valid header */
582 *next_line
= new_list ();
583 (*next_line
)->string
= alloc_string (length
);
584 strcpy (((*next_line
)->string
), line
);
585 next_line
= &((*next_line
)->continuation
);
590 return the_header
->next
;
594 write_header (the_header
)
597 register header old
= the_header
;
600 register line_list the_list
;
601 for (the_list
= the_header
->text
;
603 the_list
= the_list
->continuation
)
604 put_line (the_list
->string
);
605 the_header
= the_header
->next
;
606 } while (the_header
!= old
);
619 char *mail_program_name
;
620 char buf
[BUFLEN
+ 1];
624 extern char *getenv ();
626 mail_program_name
= getenv ("FAKEMAILER");
627 if (!(mail_program_name
&& *mail_program_name
))
628 mail_program_name
= MAIL_PROGRAM_NAME
;
629 name_length
= strlen (mail_program_name
);
632 the_streams
= ((stream_list
) NULL
);
633 the_date
= ((char *) NULL
);
634 the_user
= ((char *) NULL
);
636 the_header
= read_header ();
637 command_line
= alloc_string (name_length
+ args_size (the_header
));
638 strcpy (command_line
, mail_program_name
);
639 parse_header (the_header
, &command_line
[name_length
]);
641 the_pipe
= popen (command_line
, "w");
642 if (the_pipe
== ((FILE *) NULL
))
643 fatal ("cannot open pipe to real mailer");
645 add_a_stream (the_pipe
, pclose
);
647 write_header (the_header
);
649 /* Dump the message itself */
651 while (!feof (stdin
))
653 size
= fread (buf
, 1, BUFLEN
, stdin
);
658 exit (close_the_streams ());
661 #endif /* not BSD 4.2 (or newer) */