tramp-file-name-handler-alist): Delete expand-file-name and other
[bpt/emacs.git] / lib-src / fakemail.c
CommitLineData
6b3ee98a 1/* sendmail-like interface to /bin/mail for system V,
92b47a4a 2 Copyright (C) 1985, 1994, 1999, 2002, 2003, 2004,
273dc16a 3 2005, 2006 Free Software Foundation, Inc.
6b3ee98a
RS
4
5This file is part of GNU Emacs.
6
518dd722
JB
7GNU Emacs is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
530715fe 9the Free Software Foundation; either version 2, or (at your option)
518dd722
JB
10any later version.
11
6b3ee98a 12GNU Emacs is distributed in the hope that it will be useful,
518dd722
JB
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Emacs; see the file COPYING. If not, write to
364c38d3
LK
19the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20Boston, MA 02110-1301, USA. */
6b3ee98a 21
6b3ee98a 22#define NO_SHORTNAMES
a33c19b1 23#define _XOPEN_SOURCE 500 /* for cuserid */
594aa066
PJ
24
25#ifdef HAVE_CONFIG_H
4838e624 26#include <config.h>
594aa066 27#endif
6b3ee98a 28
e397a017 29#if defined (BSD_SYSTEM) && !defined (BSD4_1) && !defined (USE_FAKEMAIL)
6b3ee98a 30/* This program isnot used in BSD, so just avoid loader complaints. */
d0dff6e5 31int
6b3ee98a
RS
32main ()
33{
0d1841a6 34 return 0;
6b3ee98a
RS
35}
36#else /* not BSD 4.2 (or newer) */
26528bbc 37#ifdef MSDOS
d0dff6e5 38int
26528bbc
RS
39main ()
40{
0d1841a6 41 return 0;
26528bbc
RS
42}
43#else /* not MSDOS */
6b3ee98a
RS
44/* This conditional contains all the rest of the file. */
45
46/* These are defined in config in some versions. */
47
48#ifdef static
49#undef static
50#endif
51
7c544420
RS
52#ifdef WINDOWSNT
53#include "ntlib.h"
54#endif
55
6b3ee98a
RS
56#include <stdio.h>
57#include <string.h>
58#include <ctype.h>
59#include <time.h>
60#include <pwd.h>
7543028e
RS
61
62/* This is to declare cuserid. */
63#ifdef HAVE_UNISTD_H
64#include <unistd.h>
65#endif
6b3ee98a
RS
66\f
67/* Type definitions */
68
69#define boolean int
70#define true 1
71#define false 0
72
d65b4235
PE
73#define TM_YEAR_BASE 1900
74
75/* Nonzero if TM_YEAR is a struct tm's tm_year value that causes
76 asctime to have well-defined behavior. */
77#ifndef TM_YEAR_IN_ASCTIME_RANGE
f5565804 78# define TM_YEAR_IN_ASCTIME_RANGE(tm_year) \
d65b4235 79 (1000 - TM_YEAR_BASE <= (tm_year) && (tm_year) <= 9999 - TM_YEAR_BASE)
f5565804
PE
80#endif
81
6b3ee98a
RS
82/* Various lists */
83
84struct line_record
85{
86 char *string;
87 struct line_record *continuation;
88};
89typedef struct line_record *line_list;
90
91struct header_record
92{
93 line_list text;
94 struct header_record *next;
95 struct header_record *previous;
96};
97typedef struct header_record *header;
594aa066 98
6b3ee98a
RS
99struct stream_record
100{
101 FILE *handle;
102 int (*action)();
103 struct stream_record *rest_streams;
104};
105typedef struct stream_record *stream_list;
106
107/* A `struct linebuffer' is a structure which holds a line of text.
108 * `readline' reads a line from a stream into a linebuffer
109 * and works regardless of the length of the line.
110 */
111
112struct linebuffer
113{
114 long size;
115 char *buffer;
116};
117
118struct linebuffer lb;
119
120#define new_list() \
121 ((line_list) xmalloc (sizeof (struct line_record)))
122#define new_header() \
123 ((header) xmalloc (sizeof (struct header_record)))
124#define new_stream() \
125 ((stream_list) xmalloc (sizeof (struct stream_record)))
126#define alloc_string(nchars) \
127 ((char *) xmalloc ((nchars) + 1))
128\f
129/* Global declarations */
130
131#define BUFLEN 1024
132#define KEYWORD_SIZE 256
133#define FROM_PREFIX "From"
134#define MY_NAME "fakemail"
135#define NIL ((line_list) NULL)
136#define INITIAL_LINE_SIZE 200
137
138#ifndef MAIL_PROGRAM_NAME
139#define MAIL_PROGRAM_NAME "/bin/mail"
140#endif
141
142static char *my_name;
143static char *the_date;
144static char *the_user;
145static line_list file_preface;
146static stream_list the_streams;
147static boolean no_problems = true;
148
149extern FILE *popen ();
150extern int fclose (), pclose ();
6b3ee98a
RS
151
152#ifdef CURRENT_USER
153extern struct passwd *getpwuid ();
154extern unsigned short geteuid ();
155static struct passwd *my_entry;
156#define cuserid(s) \
157(my_entry = getpwuid (((int) geteuid ())), \
158 my_entry->pw_name)
159#endif
160\f
161/* Utilities */
162
163/* Print error message. `s1' is printf control string, `s2' is arg for it. */
164
165static void
166error (s1, s2)
167 char *s1, *s2;
168{
169 printf ("%s: ", my_name);
170 printf (s1, s2);
171 printf ("\n");
172 no_problems = false;
173}
174
175/* Print error message and exit. */
176
177static void
178fatal (s1, s2)
179 char *s1, *s2;
180{
181 error (s1, s2);
65396510 182 exit (EXIT_FAILURE);
6b3ee98a
RS
183}
184
185/* Like malloc but get fatal error if memory is exhausted. */
186
65119039 187static long *
6b3ee98a
RS
188xmalloc (size)
189 int size;
190{
65119039
RS
191 long *result = (long *) malloc (((unsigned) size));
192 if (result == ((long *) NULL))
6b3ee98a
RS
193 fatal ("virtual memory exhausted", 0);
194 return result;
195}
196
65119039 197static long *
6b3ee98a 198xrealloc (ptr, size)
65119039 199 long *ptr;
6b3ee98a
RS
200 int size;
201{
65119039 202 long *result = (long *) realloc (ptr, ((unsigned) size));
5391a863 203 if (result == ((long *) NULL))
6b3ee98a
RS
204 fatal ("virtual memory exhausted");
205 return result;
206}
207\f
208/* Initialize a linebuffer for use */
209
210void
211init_linebuffer (linebuffer)
212 struct linebuffer *linebuffer;
213{
214 linebuffer->size = INITIAL_LINE_SIZE;
215 linebuffer->buffer = ((char *) xmalloc (INITIAL_LINE_SIZE));
216}
217
218/* Read a line of text from `stream' into `linebuffer'.
594aa066 219 Return the length of the line. */
6b3ee98a
RS
220
221long
222readline (linebuffer, stream)
223 struct linebuffer *linebuffer;
224 FILE *stream;
225{
226 char *buffer = linebuffer->buffer;
227 char *p = linebuffer->buffer;
228 char *end = p + linebuffer->size;
229
230 while (true)
231 {
232 int c = getc (stream);
233 if (p == end)
234 {
235 linebuffer->size *= 2;
26df3bb0 236 buffer = ((char *) xrealloc ((long *)buffer, linebuffer->size));
f3309b68 237 p = buffer + (p - linebuffer->buffer);
1ddb6978 238 end = buffer + linebuffer->size;
6b3ee98a
RS
239 linebuffer->buffer = buffer;
240 }
241 if (c < 0 || c == '\n')
242 {
243 *p = 0;
244 break;
245 }
246 *p++ = c;
247 }
248
249 return p - buffer;
250}
251\f
3d23b985
RS
252/* Extract a colon-terminated keyword from the string FIELD.
253 Return that keyword as a string stored in a static buffer.
254 Store the address of the rest of the string into *REST.
255
256 If there is no keyword, return NULL and don't alter *REST. */
257
6b3ee98a
RS
258char *
259get_keyword (field, rest)
260 register char *field;
261 char **rest;
262{
263 static char keyword[KEYWORD_SIZE];
264 register char *ptr;
859cbb40 265 register int c;
6b3ee98a
RS
266
267 ptr = &keyword[0];
859cbb40 268 c = (unsigned char) *field++;
3d23b985 269 if (isspace (c) || c == ':')
6b3ee98a 270 return ((char *) NULL);
3d23b985 271 *ptr++ = (islower (c) ? toupper (c) : c);
859cbb40 272 while (((c = (unsigned char) *field++) != ':') && ! isspace (c))
3d23b985 273 *ptr++ = (islower (c) ? toupper (c) : c);
6b3ee98a 274 *ptr++ = '\0';
3d23b985 275 while (isspace (c))
859cbb40 276 c = (unsigned char) *field++;
3d23b985
RS
277 if (c != ':')
278 return ((char *) NULL);
6b3ee98a
RS
279 *rest = field;
280 return &keyword[0];
281}
282
3d23b985
RS
283/* Nonzero if the string FIELD starts with a colon-terminated keyword. */
284
6b3ee98a
RS
285boolean
286has_keyword (field)
287 char *field;
288{
289 char *ignored;
290 return (get_keyword (field, &ignored) != ((char *) NULL));
291}
292
3d23b985
RS
293/* Store the string FIELD, followed by any lines in THE_LIST,
294 into the buffer WHERE.
295 Concatenate lines, putting just a space between them.
296 Delete everything contained in parentheses.
297 When a recipient name contains <...>, we discard
298 everything except what is inside the <...>.
299
300 We don't pay attention to overflowing WHERE;
301 the caller has to make it big enough. */
302
6b3ee98a
RS
303char *
304add_field (the_list, field, where)
305 line_list the_list;
306 register char *field, *where;
307{
308 register char c;
309 while (true)
310 {
3d23b985
RS
311 char *this_recipient_where;
312 int in_quotes = 0;
313
6b3ee98a 314 *where++ = ' ';
3d23b985
RS
315 this_recipient_where = where;
316
6b3ee98a
RS
317 while ((c = *field++) != '\0')
318 {
3d23b985
RS
319 if (c == '\\')
320 *where++ = c;
321 else if (c == '"')
322 {
323 in_quotes = ! in_quotes;
324 *where++ = c;
325 }
326 else if (in_quotes)
327 *where++ = c;
328 else if (c == '(')
6b3ee98a
RS
329 {
330 while (*field && *field != ')') ++field;
3d23b985
RS
331 if (! (*field++)) break; /* no close */
332 continue;
6b3ee98a 333 }
3d23b985
RS
334 else if (c == ',')
335 {
336 *where++ = ' ';
337 /* When we get to the end of one recipient,
338 don't discard it if the next one has <...>. */
339 this_recipient_where = where;
340 }
341 else if (c == '<')
342 /* Discard everything we got before the `<'. */
343 where = this_recipient_where;
344 else if (c == '>')
345 /* Discard the rest of this name that follows the `>'. */
346 {
347 while (*field && *field != ',') ++field;
348 if (! (*field++)) break; /* no comma */
349 continue;
350 }
351 else
352 *where++ = c;
6b3ee98a
RS
353 }
354 if (the_list == NIL) break;
355 field = the_list->string;
356 the_list = the_list->continuation;
357 }
358 return where;
359}
360\f
361line_list
362make_file_preface ()
363{
364 char *the_string, *temp;
365 long idiotic_interface;
f5565804 366 struct tm *tm;
6b3ee98a
RS
367 long prefix_length;
368 long user_length;
369 long date_length;
370 line_list result;
371
372 prefix_length = strlen (FROM_PREFIX);
373 time (&idiotic_interface);
f5565804
PE
374 /* Convert to a string, checking for out-of-range time stamps.
375 Don't use 'ctime', as that might dump core if the hardware clock
376 is set to a bizarre value. */
377 tm = localtime (&idiotic_interface);
d65b4235
PE
378 if (! (tm && TM_YEAR_IN_ASCTIME_RANGE (tm->tm_year)
379 && (the_date = asctime (tm))))
f5565804 380 fatal ("current time is out of range", 0);
6b3ee98a
RS
381 /* the_date has an unwanted newline at the end */
382 date_length = strlen (the_date) - 1;
383 the_date[date_length] = '\0';
384 temp = cuserid ((char *) NULL);
385 user_length = strlen (temp);
386 the_user = alloc_string (user_length + 1);
387 strcpy (the_user, temp);
7543028e
RS
388 the_string = alloc_string (3 + prefix_length
389 + user_length
390 + date_length);
6b3ee98a
RS
391 temp = the_string;
392 strcpy (temp, FROM_PREFIX);
393 temp = &temp[prefix_length];
394 *temp++ = ' ';
395 strcpy (temp, the_user);
396 temp = &temp[user_length];
397 *temp++ = ' ';
398 strcpy (temp, the_date);
399 result = new_list ();
400 result->string = the_string;
401 result->continuation = ((line_list) NULL);
402 return result;
403}
404
405void
406write_line_list (the_list, the_stream)
407 register line_list the_list;
408 FILE *the_stream;
409{
410 for ( ;
411 the_list != ((line_list) NULL) ;
412 the_list = the_list->continuation)
413 {
414 fputs (the_list->string, the_stream);
415 putc ('\n', the_stream);
416 }
417 return;
418}
419\f
420int
421close_the_streams ()
422{
423 register stream_list rem;
424 for (rem = the_streams;
425 rem != ((stream_list) NULL);
426 rem = rem->rest_streams)
427 no_problems = (no_problems &&
428 ((*rem->action) (rem->handle) == 0));
429 the_streams = ((stream_list) NULL);
65396510 430 return (no_problems ? EXIT_SUCCESS : EXIT_FAILURE);
6b3ee98a
RS
431}
432
433void
434add_a_stream (the_stream, closing_action)
435 FILE *the_stream;
436 int (*closing_action)();
437{
438 stream_list old = the_streams;
439 the_streams = new_stream ();
440 the_streams->handle = the_stream;
441 the_streams->action = closing_action;
442 the_streams->rest_streams = old;
443 return;
444}
445
446int
447my_fclose (the_file)
448 FILE *the_file;
449{
450 putc ('\n', the_file);
451 fflush (the_file);
452 return fclose (the_file);
453}
454
455boolean
456open_a_file (name)
457 char *name;
458{
459 FILE *the_stream = fopen (name, "a");
460 if (the_stream != ((FILE *) NULL))
461 {
462 add_a_stream (the_stream, my_fclose);
463 if (the_user == ((char *) NULL))
464 file_preface = make_file_preface ();
465 write_line_list (file_preface, the_stream);
466 return true;
467 }
468 return false;
469}
470
471void
472put_string (s)
473 char *s;
474{
475 register stream_list rem;
476 for (rem = the_streams;
477 rem != ((stream_list) NULL);
478 rem = rem->rest_streams)
479 fputs (s, rem->handle);
480 return;
481}
482
483void
a492a5b9
RS
484put_line (string)
485 char *string;
6b3ee98a
RS
486{
487 register stream_list rem;
488 for (rem = the_streams;
489 rem != ((stream_list) NULL);
490 rem = rem->rest_streams)
491 {
a492a5b9
RS
492 char *s = string;
493 int column = 0;
494
495 /* Divide STRING into lines. */
496 while (*s != 0)
497 {
498 char *breakpos;
499
fbffe7d9 500 /* Find the last char that fits. */
a492a5b9
RS
501 for (breakpos = s; *breakpos && column < 78; ++breakpos)
502 {
503 if (*breakpos == '\t')
504 column += 8;
505 else
506 column++;
507 }
fbffe7d9
RS
508 /* If we didn't reach end of line, break the line. */
509 if (*breakpos)
a492a5b9 510 {
fbffe7d9
RS
511 /* Back up to just after the last comma that fits. */
512 while (breakpos != s && breakpos[-1] != ',') --breakpos;
513
514 if (breakpos == s)
515 {
516 /* If no comma fits, move past the first address anyway. */
517 while (*breakpos != 0 && *breakpos != ',') ++breakpos;
518 if (*breakpos != 0)
519 /* Include the comma after it. */
520 ++breakpos;
521 }
a492a5b9
RS
522 }
523 /* Output that much, then break the line. */
524 fwrite (s, 1, breakpos - s, rem->handle);
a492a5b9
RS
525 column = 8;
526
527 /* Skip whitespace and prepare to print more addresses. */
528 s = breakpos;
529 while (*s == ' ' || *s == '\t') ++s;
c1380f31
RS
530 if (*s != 0)
531 fputs ("\n\t", rem->handle);
a492a5b9 532 }
6b3ee98a
RS
533 putc ('\n', rem->handle);
534 }
535 return;
536}
537\f
538#define mail_error error
539
3d23b985
RS
540/* Handle an FCC field. FIELD is the text of the first line (after
541 the header name), and THE_LIST holds the continuation lines if any.
542 Call open_a_file for each file. */
543
6b3ee98a
RS
544void
545setup_files (the_list, field)
546 register line_list the_list;
547 register char *field;
548{
549 register char *start;
550 register char c;
551 while (true)
552 {
3d23b985
RS
553 while (((c = *field) != '\0')
554 && (c == ' '
555 || c == '\t'
556 || c == ','))
6b3ee98a
RS
557 field += 1;
558 if (c != '\0')
559 {
560 start = field;
3d23b985
RS
561 while (((c = *field) != '\0')
562 && c != ' '
563 && c != '\t'
564 && c != ',')
6b3ee98a
RS
565 field += 1;
566 *field = '\0';
567 if (!open_a_file (start))
568 mail_error ("Could not open file %s", start);
569 *field = c;
570 if (c != '\0') continue;
571 }
3d23b985
RS
572 if (the_list == ((line_list) NULL))
573 return;
6b3ee98a
RS
574 field = the_list->string;
575 the_list = the_list->continuation;
576 }
577}
578\f
3d23b985
RS
579/* Compute the total size of all recipient names stored in THE_HEADER.
580 The result says how big to make the buffer to pass to parse_header. */
581
6b3ee98a
RS
582int
583args_size (the_header)
584 header the_header;
585{
586 register header old = the_header;
587 register line_list rem;
588 register int size = 0;
589 do
590 {
591 char *field;
592 register char *keyword = get_keyword (the_header->text->string, &field);
3d23b985
RS
593 if ((strcmp (keyword, "TO") == 0)
594 || (strcmp (keyword, "CC") == 0)
595 || (strcmp (keyword, "BCC") == 0))
6b3ee98a
RS
596 {
597 size += 1 + strlen (field);
598 for (rem = the_header->text->continuation;
599 rem != NIL;
600 rem = rem->continuation)
601 size += 1 + strlen (rem->string);
602 }
603 the_header = the_header->next;
604 } while (the_header != old);
605 return size;
606}
607
3d23b985
RS
608/* Scan the header described by the lists THE_HEADER,
609 and put all recipient names into the buffer WHERE.
610 Precede each recipient name with a space.
611
612 Also, if the header has any FCC fields, call setup_files for each one. */
613
cb58ebb0 614void
6b3ee98a
RS
615parse_header (the_header, where)
616 header the_header;
617 register char *where;
618{
619 register header old = the_header;
620 do
621 {
622 char *field;
623 register char *keyword = get_keyword (the_header->text->string, &field);
624 if (strcmp (keyword, "TO") == 0)
625 where = add_field (the_header->text->continuation, field, where);
626 else if (strcmp (keyword, "CC") == 0)
627 where = add_field (the_header->text->continuation, field, where);
628 else if (strcmp (keyword, "BCC") == 0)
629 {
630 where = add_field (the_header->text->continuation, field, where);
631 the_header->previous->next = the_header->next;
632 the_header->next->previous = the_header->previous;
633 }
634 else if (strcmp (keyword, "FCC") == 0)
635 setup_files (the_header->text->continuation, field);
636 the_header = the_header->next;
637 } while (the_header != old);
638 *where = '\0';
639 return;
640}
594aa066 641\f
3d23b985
RS
642/* Read lines from the input until we get a blank line.
643 Create a list of `header' objects, one for each header field,
644 each of which points to a list of `line_list' objects,
645 one for each line in that field.
646 Continuation lines are grouped in the headers they continue. */
594aa066 647
6b3ee98a
RS
648header
649read_header ()
650{
651 register header the_header = ((header) NULL);
652 register line_list *next_line = ((line_list *) NULL);
653
654 init_linebuffer (&lb);
655
656 do
657 {
658 long length;
659 register char *line;
660
661 readline (&lb, stdin);
662 line = lb.buffer;
663 length = strlen (line);
664 if (length == 0) break;
665
666 if (has_keyword (line))
667 {
668 register header old = the_header;
669 the_header = new_header ();
670 if (old == ((header) NULL))
671 {
672 the_header->next = the_header;
673 the_header->previous = the_header;
674 }
675 else
676 {
677 the_header->previous = old;
678 the_header->next = old->next;
679 old->next = the_header;
680 }
681 next_line = &(the_header->text);
682 }
683
684 if (next_line == ((line_list *) NULL))
685 {
686 /* Not a valid header */
65396510 687 exit (EXIT_FAILURE);
6b3ee98a
RS
688 }
689 *next_line = new_list ();
690 (*next_line)->string = alloc_string (length);
691 strcpy (((*next_line)->string), line);
692 next_line = &((*next_line)->continuation);
693 *next_line = NIL;
694
695 } while (true);
696
697 return the_header->next;
698}
699\f
700void
701write_header (the_header)
702 header the_header;
703{
704 register header old = the_header;
705 do
706 {
707 register line_list the_list;
708 for (the_list = the_header->text;
709 the_list != NIL;
710 the_list = the_list->continuation)
711 put_line (the_list->string);
712 the_header = the_header->next;
713 } while (the_header != old);
714 put_line ("");
715 return;
716}
717\f
d0dff6e5 718int
6b3ee98a
RS
719main (argc, argv)
720 int argc;
721 char **argv;
722{
723 char *command_line;
724 header the_header;
725 long name_length;
726 char *mail_program_name;
727 char buf[BUFLEN + 1];
728 register int size;
729 FILE *the_pipe;
730
731 extern char *getenv ();
732
733 mail_program_name = getenv ("FAKEMAILER");
734 if (!(mail_program_name && *mail_program_name))
735 mail_program_name = MAIL_PROGRAM_NAME;
736 name_length = strlen (mail_program_name);
737
738 my_name = MY_NAME;
739 the_streams = ((stream_list) NULL);
740 the_date = ((char *) NULL);
741 the_user = ((char *) NULL);
742
743 the_header = read_header ();
744 command_line = alloc_string (name_length + args_size (the_header));
745 strcpy (command_line, mail_program_name);
746 parse_header (the_header, &command_line[name_length]);
594aa066 747
6b3ee98a
RS
748 the_pipe = popen (command_line, "w");
749 if (the_pipe == ((FILE *) NULL))
750 fatal ("cannot open pipe to real mailer");
751
752 add_a_stream (the_pipe, pclose);
753
754 write_header (the_header);
755
756 /* Dump the message itself */
757
758 while (!feof (stdin))
759 {
760 size = fread (buf, 1, BUFLEN, stdin);
761 buf[size] = '\0';
762 put_string (buf);
763 }
764
765 exit (close_the_streams ());
766}
767
26528bbc 768#endif /* not MSDOS */
6b3ee98a 769#endif /* not BSD 4.2 (or newer) */
ab5796a9
MB
770
771/* arch-tag: acb0afa6-315a-4c5b-b9e3-def5725c8783
772 (do not change this comment) */
65396510
TTN
773
774/* fakemail.c ends here */