Initial revision
[emacs.git] / lib-src / fakemail.c
blob19b053abaa32adb0aafd5c325fbd6756f3f68254
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. */
22 #define NO_SHORTNAMES
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. */
27 main ()
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. */
35 #ifdef static
36 #undef static
37 #endif
39 #ifdef read
40 #undef read
41 #undef write
42 #undef open
43 #undef close
44 #endif
46 #include <stdio.h>
47 #include <string.h>
48 #include <ctype.h>
49 #include <time.h>
50 #include <pwd.h>
52 /* Type definitions */
54 #define boolean int
55 #define true 1
56 #define false 0
58 /* Various lists */
60 struct line_record
62 char *string;
63 struct line_record *continuation;
65 typedef struct line_record *line_list;
67 struct header_record
69 line_list text;
70 struct header_record *next;
71 struct header_record *previous;
73 typedef struct header_record *header;
75 struct stream_record
77 FILE *handle;
78 int (*action)();
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.
88 struct linebuffer
90 long size;
91 char *buffer;
94 struct linebuffer lb;
96 #define new_list() \
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 */
107 #define BUFLEN 1024
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"
116 #endif
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 ();
129 #ifdef CURRENT_USER
130 extern struct passwd *getpwuid ();
131 extern unsigned short geteuid ();
132 static struct passwd *my_entry;
133 #define cuserid(s) \
134 (my_entry = getpwuid (((int) geteuid ())), \
135 my_entry->pw_name)
136 #endif
138 /* Utilities */
140 /* Print error message. `s1' is printf control string, `s2' is arg for it. */
142 static void
143 error (s1, s2)
144 char *s1, *s2;
146 printf ("%s: ", my_name);
147 printf (s1, s2);
148 printf ("\n");
149 no_problems = false;
152 /* Print error message and exit. */
154 static void
155 fatal (s1, s2)
156 char *s1, *s2;
158 error (s1, s2);
159 exit (1);
162 /* Like malloc but get fatal error if memory is exhausted. */
164 static char *
165 xmalloc (size)
166 int size;
168 char *result = malloc (((unsigned) size));
169 if (result == ((char *) NULL))
170 fatal ("virtual memory exhausted", 0);
171 return result;
174 static char *
175 xrealloc (ptr, size)
176 char *ptr;
177 int size;
179 char *result = realloc (ptr, ((unsigned) size));
180 if (result == ((char *) NULL))
181 fatal ("virtual memory exhausted");
182 return result;
185 /* Initialize a linebuffer for use */
187 void
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.
199 long
200 readline (linebuffer, stream)
201 struct linebuffer *linebuffer;
202 FILE *stream;
204 char *buffer = linebuffer->buffer;
205 char *p = linebuffer->buffer;
206 char *end = p + linebuffer->size;
208 while (true)
210 int c = getc (stream);
211 if (p == end)
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')
221 *p = 0;
222 break;
224 *p++ = c;
227 return p - buffer;
230 char *
231 get_keyword (field, rest)
232 register char *field;
233 char **rest;
235 static char keyword[KEYWORD_SIZE];
236 register char *ptr;
237 register char c;
239 ptr = &keyword[0];
240 c = *field++;
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);
246 *ptr++ = '\0';
247 while (isspace (c)) c = *field++;
248 if (c != ':') return ((char *) NULL);
249 *rest = field;
250 return &keyword[0];
253 boolean
254 has_keyword (field)
255 char *field;
257 char *ignored;
258 return (get_keyword (field, &ignored) != ((char *) NULL));
261 char *
262 add_field (the_list, field, where)
263 line_list the_list;
264 register char *field, *where;
266 register char c;
267 while (true)
269 *where++ = ' ';
270 while ((c = *field++) != '\0')
272 if (c == '(')
274 while (*field && *field != ')') ++field;
275 if (! (*field++)) break; /* no closer */
276 if (! (*field)) break; /* closerNULL */
277 c = *field;
279 *where++ = ((c == ','||c=='>'||c=='<') ? ' ' : c);
281 if (the_list == NIL) break;
282 field = the_list->string;
283 the_list = the_list->continuation;
285 return where;
288 line_list
289 make_file_preface ()
291 char *the_string, *temp;
292 long idiotic_interface;
293 long prefix_length;
294 long user_length;
295 long date_length;
296 line_list result;
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 +
309 user_length +
310 date_length);
311 temp = the_string;
312 strcpy (temp, FROM_PREFIX);
313 temp = &temp[prefix_length];
314 *temp++ = ' ';
315 strcpy (temp, the_user);
316 temp = &temp[user_length];
317 *temp++ = ' ';
318 strcpy (temp, the_date);
319 result = new_list ();
320 result->string = the_string;
321 result->continuation = ((line_list) NULL);
322 return result;
325 void
326 write_line_list (the_list, the_stream)
327 register line_list the_list;
328 FILE *the_stream;
330 for ( ;
331 the_list != ((line_list) NULL) ;
332 the_list = the_list->continuation)
334 fputs (the_list->string, the_stream);
335 putc ('\n', the_stream);
337 return;
341 close_the_streams ()
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);
353 void
354 add_a_stream (the_stream, closing_action)
355 FILE *the_stream;
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;
363 return;
367 my_fclose (the_file)
368 FILE *the_file;
370 putc ('\n', the_file);
371 fflush (the_file);
372 return fclose (the_file);
375 boolean
376 open_a_file (name)
377 char *name;
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);
386 return true;
388 return false;
391 void
392 put_string (s)
393 char *s;
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);
400 return;
403 void
404 put_line (string)
405 char *string;
407 register stream_list rem;
408 for (rem = the_streams;
409 rem != ((stream_list) NULL);
410 rem = rem->rest_streams)
412 char *s = string;
413 int column = 0;
415 /* Divide STRING into lines. */
416 while (*s != 0)
418 char *breakpos;
420 /* Find the last char that fits. */
421 for (breakpos = s; *breakpos && column < 78; ++breakpos)
423 if (*breakpos == '\t')
424 column += 8;
425 else
426 column++;
428 /* Back up to just after the last comma that fits. */
429 while (breakpos != s && breakpos[-1] != ',') --breakpos;
430 if (breakpos == s)
432 /* If no comma fits, move past the first address anyway. */
433 while (*breakpos != 0 && *breakpos != ',') ++breakpos;
434 if (*breakpos != 0)
435 /* Include the comma after it. */
436 ++breakpos;
438 /* Output that much, then break the line. */
439 fwrite (s, 1, breakpos - s, rem->handle);
440 fputs ("\n\t", rem->handle);
441 column = 8;
443 /* Skip whitespace and prepare to print more addresses. */
444 s = breakpos;
445 while (*s == ' ' || *s == '\t') ++s;
447 putc ('\n', rem->handle);
449 return;
452 #define mail_error error
454 void
455 setup_files (the_list, field)
456 register line_list the_list;
457 register char *field;
459 register char *start;
460 register char c;
461 while (true)
463 while (((c = *field) != '\0') &&
464 ((c == ' ') ||
465 (c == '\t') ||
466 (c == ',')))
467 field += 1;
468 if (c != '\0')
470 start = field;
471 while (((c = *field) != '\0') &&
472 (c != ' ') &&
473 (c != '\t') &&
474 (c != ','))
475 field += 1;
476 *field = '\0';
477 if (!open_a_file (start))
478 mail_error ("Could not open file %s", start);
479 *field = c;
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)
490 header the_header;
492 register header old = the_header;
493 register line_list rem;
494 register int size = 0;
497 char *field;
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;
505 rem != NIL;
506 rem = rem->continuation)
507 size += 1 + strlen (rem->string);
509 the_header = the_header->next;
510 } while (the_header != old);
511 return size;
514 parse_header (the_header, where)
515 header the_header;
516 register char *where;
518 register header old = the_header;
521 char *field;
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);
537 *where = '\0';
538 return;
541 header
542 read_header ()
544 register header the_header = ((header) NULL);
545 register line_list *next_line = ((line_list *) NULL);
547 init_linebuffer (&lb);
551 long length;
552 register char *line;
554 readline (&lb, stdin);
555 line = lb.buffer;
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;
568 else
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 */
580 exit (1);
582 *next_line = new_list ();
583 (*next_line)->string = alloc_string (length);
584 strcpy (((*next_line)->string), line);
585 next_line = &((*next_line)->continuation);
586 *next_line = NIL;
588 } while (true);
590 return the_header->next;
593 void
594 write_header (the_header)
595 header the_header;
597 register header old = the_header;
600 register line_list the_list;
601 for (the_list = the_header->text;
602 the_list != NIL;
603 the_list = the_list->continuation)
604 put_line (the_list->string);
605 the_header = the_header->next;
606 } while (the_header != old);
607 put_line ("");
608 return;
611 void
612 main (argc, argv)
613 int argc;
614 char **argv;
616 char *command_line;
617 header the_header;
618 long name_length;
619 char *mail_program_name;
620 char buf[BUFLEN + 1];
621 register int size;
622 FILE *the_pipe;
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);
631 my_name = MY_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);
654 buf[size] = '\0';
655 put_string (buf);
658 exit (close_the_streams ());
661 #endif /* not BSD 4.2 (or newer) */