comparison lib-src/fakemail.c @ 18:71c0ddb55b6d

Initial revision
author Richard M. Stallman <rms@gnu.org>
date Fri, 09 Dec 1988 17:01:19 +0000
parents
children 34d646609216
comparison
equal deleted inserted replaced
17:1e1bc0842996 18:71c0ddb55b6d
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 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.
12
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. */
20
21
22 #define NO_SHORTNAMES
23 #include "../src/config.h"
24
25 #if defined (BSD) && !defined (BSD4_1) && !defined (USE_FAKEMAIL)
26 /* This program isnot used in BSD, so just avoid loader complaints. */
27 main ()
28 {
29 }
30 #else /* not BSD 4.2 (or newer) */
31 /* This conditional contains all the rest of the file. */
32
33 /* These are defined in config in some versions. */
34
35 #ifdef static
36 #undef static
37 #endif
38
39 #ifdef read
40 #undef read
41 #undef write
42 #undef open
43 #undef close
44 #endif
45
46 #include <stdio.h>
47 #include <string.h>
48 #include <ctype.h>
49 #include <time.h>
50 #include <pwd.h>
51
52 /* Type definitions */
53
54 #define boolean int
55 #define true 1
56 #define false 0
57
58 /* Various lists */
59
60 struct line_record
61 {
62 char *string;
63 struct line_record *continuation;
64 };
65 typedef struct line_record *line_list;
66
67 struct header_record
68 {
69 line_list text;
70 struct header_record *next;
71 struct header_record *previous;
72 };
73 typedef struct header_record *header;
74
75 struct stream_record
76 {
77 FILE *handle;
78 int (*action)();
79 struct stream_record *rest_streams;
80 };
81 typedef struct stream_record *stream_list;
82
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.
86 */
87
88 struct linebuffer
89 {
90 long size;
91 char *buffer;
92 };
93
94 struct linebuffer lb;
95
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))
104
105 /* Global declarations */
106
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
113
114 #ifndef MAIL_PROGRAM_NAME
115 #define MAIL_PROGRAM_NAME "/bin/mail"
116 #endif
117
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;
124
125 extern FILE *popen ();
126 extern int fclose (), pclose ();
127 extern char *malloc (), *realloc ();
128
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
137
138 /* Utilities */
139
140 /* Print error message. `s1' is printf control string, `s2' is arg for it. */
141
142 static void
143 error (s1, s2)
144 char *s1, *s2;
145 {
146 printf ("%s: ", my_name);
147 printf (s1, s2);
148 printf ("\n");
149 no_problems = false;
150 }
151
152 /* Print error message and exit. */
153
154 static void
155 fatal (s1, s2)
156 char *s1, *s2;
157 {
158 error (s1, s2);
159 exit (1);
160 }
161
162 /* Like malloc but get fatal error if memory is exhausted. */
163
164 static char *
165 xmalloc (size)
166 int size;
167 {
168 char *result = malloc (((unsigned) size));
169 if (result == ((char *) NULL))
170 fatal ("virtual memory exhausted", 0);
171 return result;
172 }
173
174 static char *
175 xrealloc (ptr, size)
176 char *ptr;
177 int size;
178 {
179 char *result = realloc (ptr, ((unsigned) size));
180 if (result == ((char *) NULL))
181 fatal ("virtual memory exhausted");
182 return result;
183 }
184
185 /* Initialize a linebuffer for use */
186
187 void
188 init_linebuffer (linebuffer)
189 struct linebuffer *linebuffer;
190 {
191 linebuffer->size = INITIAL_LINE_SIZE;
192 linebuffer->buffer = ((char *) xmalloc (INITIAL_LINE_SIZE));
193 }
194
195 /* Read a line of text from `stream' into `linebuffer'.
196 * Return the length of the line.
197 */
198
199 long
200 readline (linebuffer, stream)
201 struct linebuffer *linebuffer;
202 FILE *stream;
203 {
204 char *buffer = linebuffer->buffer;
205 char *p = linebuffer->buffer;
206 char *end = p + linebuffer->size;
207
208 while (true)
209 {
210 int c = getc (stream);
211 if (p == end)
212 {
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;
218 }
219 if (c < 0 || c == '\n')
220 {
221 *p = 0;
222 break;
223 }
224 *p++ = c;
225 }
226
227 return p - buffer;
228 }
229
230 char *
231 get_keyword (field, rest)
232 register char *field;
233 char **rest;
234 {
235 static char keyword[KEYWORD_SIZE];
236 register char *ptr;
237 register char c;
238
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];
251 }
252
253 boolean
254 has_keyword (field)
255 char *field;
256 {
257 char *ignored;
258 return (get_keyword (field, &ignored) != ((char *) NULL));
259 }
260
261 char *
262 add_field (the_list, field, where)
263 line_list the_list;
264 register char *field, *where;
265 {
266 register char c;
267 while (true)
268 {
269 *where++ = ' ';
270 while ((c = *field++) != '\0')
271 {
272 if (c == '(')
273 {
274 while (*field && *field != ')') ++field;
275 if (! (*field++)) break; /* no closer */
276 if (! (*field)) break; /* closerNULL */
277 c = *field;
278 }
279 *where++ = ((c == ','||c=='>'||c=='<') ? ' ' : c);
280 }
281 if (the_list == NIL) break;
282 field = the_list->string;
283 the_list = the_list->continuation;
284 }
285 return where;
286 }
287
288 line_list
289 make_file_preface ()
290 {
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;
297
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;
323 }
324
325 void
326 write_line_list (the_list, the_stream)
327 register line_list the_list;
328 FILE *the_stream;
329 {
330 for ( ;
331 the_list != ((line_list) NULL) ;
332 the_list = the_list->continuation)
333 {
334 fputs (the_list->string, the_stream);
335 putc ('\n', the_stream);
336 }
337 return;
338 }
339
340 int
341 close_the_streams ()
342 {
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);
351 }
352
353 void
354 add_a_stream (the_stream, closing_action)
355 FILE *the_stream;
356 int (*closing_action)();
357 {
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;
364 }
365
366 int
367 my_fclose (the_file)
368 FILE *the_file;
369 {
370 putc ('\n', the_file);
371 fflush (the_file);
372 return fclose (the_file);
373 }
374
375 boolean
376 open_a_file (name)
377 char *name;
378 {
379 FILE *the_stream = fopen (name, "a");
380 if (the_stream != ((FILE *) NULL))
381 {
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;
387 }
388 return false;
389 }
390
391 void
392 put_string (s)
393 char *s;
394 {
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;
401 }
402
403 void
404 put_line (s)
405 char *s;
406 {
407 register stream_list rem;
408 for (rem = the_streams;
409 rem != ((stream_list) NULL);
410 rem = rem->rest_streams)
411 {
412 fputs (s, rem->handle);
413 putc ('\n', rem->handle);
414 }
415 return;
416 }
417
418 #define mail_error error
419
420 void
421 setup_files (the_list, field)
422 register line_list the_list;
423 register char *field;
424 {
425 register char *start;
426 register char c;
427 while (true)
428 {
429 while (((c = *field) != '\0') &&
430 ((c == ' ') ||
431 (c == '\t') ||
432 (c == ',')))
433 field += 1;
434 if (c != '\0')
435 {
436 start = field;
437 while (((c = *field) != '\0') &&
438 (c != ' ') &&
439 (c != '\t') &&
440 (c != ','))
441 field += 1;
442 *field = '\0';
443 if (!open_a_file (start))
444 mail_error ("Could not open file %s", start);
445 *field = c;
446 if (c != '\0') continue;
447 }
448 if (the_list == ((line_list) NULL)) return;
449 field = the_list->string;
450 the_list = the_list->continuation;
451 }
452 }
453
454 int
455 args_size (the_header)
456 header the_header;
457 {
458 register header old = the_header;
459 register line_list rem;
460 register int size = 0;
461 do
462 {
463 char *field;
464 register char *keyword = get_keyword (the_header->text->string, &field);
465 if ((strcmp (keyword, "TO") == 0) ||
466 (strcmp (keyword, "CC") == 0) ||
467 (strcmp (keyword, "BCC") == 0))
468 {
469 size += 1 + strlen (field);
470 for (rem = the_header->text->continuation;
471 rem != NIL;
472 rem = rem->continuation)
473 size += 1 + strlen (rem->string);
474 }
475 the_header = the_header->next;
476 } while (the_header != old);
477 return size;
478 }
479
480 parse_header (the_header, where)
481 header the_header;
482 register char *where;
483 {
484 register header old = the_header;
485 do
486 {
487 char *field;
488 register char *keyword = get_keyword (the_header->text->string, &field);
489 if (strcmp (keyword, "TO") == 0)
490 where = add_field (the_header->text->continuation, field, where);
491 else if (strcmp (keyword, "CC") == 0)
492 where = add_field (the_header->text->continuation, field, where);
493 else if (strcmp (keyword, "BCC") == 0)
494 {
495 where = add_field (the_header->text->continuation, field, where);
496 the_header->previous->next = the_header->next;
497 the_header->next->previous = the_header->previous;
498 }
499 else if (strcmp (keyword, "FCC") == 0)
500 setup_files (the_header->text->continuation, field);
501 the_header = the_header->next;
502 } while (the_header != old);
503 *where = '\0';
504 return;
505 }
506
507 header
508 read_header ()
509 {
510 register header the_header = ((header) NULL);
511 register line_list *next_line = ((line_list *) NULL);
512
513 init_linebuffer (&lb);
514
515 do
516 {
517 long length;
518 register char *line;
519
520 readline (&lb, stdin);
521 line = lb.buffer;
522 length = strlen (line);
523 if (length == 0) break;
524
525 if (has_keyword (line))
526 {
527 register header old = the_header;
528 the_header = new_header ();
529 if (old == ((header) NULL))
530 {
531 the_header->next = the_header;
532 the_header->previous = the_header;
533 }
534 else
535 {
536 the_header->previous = old;
537 the_header->next = old->next;
538 old->next = the_header;
539 }
540 next_line = &(the_header->text);
541 }
542
543 if (next_line == ((line_list *) NULL))
544 {
545 /* Not a valid header */
546 exit (1);
547 }
548 *next_line = new_list ();
549 (*next_line)->string = alloc_string (length);
550 strcpy (((*next_line)->string), line);
551 next_line = &((*next_line)->continuation);
552 *next_line = NIL;
553
554 } while (true);
555
556 return the_header->next;
557 }
558
559 void
560 write_header (the_header)
561 header the_header;
562 {
563 register header old = the_header;
564 do
565 {
566 register line_list the_list;
567 for (the_list = the_header->text;
568 the_list != NIL;
569 the_list = the_list->continuation)
570 put_line (the_list->string);
571 the_header = the_header->next;
572 } while (the_header != old);
573 put_line ("");
574 return;
575 }
576
577 void
578 main (argc, argv)
579 int argc;
580 char **argv;
581 {
582 char *command_line;
583 header the_header;
584 long name_length;
585 char *mail_program_name;
586 char buf[BUFLEN + 1];
587 register int size;
588 FILE *the_pipe;
589
590 extern char *getenv ();
591
592 mail_program_name = getenv ("FAKEMAILER");
593 if (!(mail_program_name && *mail_program_name))
594 mail_program_name = MAIL_PROGRAM_NAME;
595 name_length = strlen (mail_program_name);
596
597 my_name = MY_NAME;
598 the_streams = ((stream_list) NULL);
599 the_date = ((char *) NULL);
600 the_user = ((char *) NULL);
601
602 the_header = read_header ();
603 command_line = alloc_string (name_length + args_size (the_header));
604 strcpy (command_line, mail_program_name);
605 parse_header (the_header, &command_line[name_length]);
606
607 the_pipe = popen (command_line, "w");
608 if (the_pipe == ((FILE *) NULL))
609 fatal ("cannot open pipe to real mailer");
610
611 add_a_stream (the_pipe, pclose);
612
613 write_header (the_header);
614
615 /* Dump the message itself */
616
617 while (!feof (stdin))
618 {
619 size = fread (buf, 1, BUFLEN, stdin);
620 buf[size] = '\0';
621 put_string (buf);
622 }
623
624 exit (close_the_streams ());
625 }
626
627 #endif /* not BSD 4.2 (or newer) */