X-Git-Url: http://git.hcoop.net/bpt/emacs.git/blobdiff_plain/f3309b68f7ee5b9e44b8996d934c120f883026f2..d5cad867eca6beb34092cee18237cbc55100c946:/lib-src/fakemail.c diff --git a/lib-src/fakemail.c b/lib-src/fakemail.c index c3929e0237..940d621942 100644 --- a/lib-src/fakemail.c +++ b/lib-src/fakemail.c @@ -1,12 +1,15 @@ /* sendmail-like interface to /bin/mail for system V, - Copyright (C) 1985 Free Software Foundation, Inc. + Copyright (C) 1985, 1994, 1999, 2001-2011 Free Software Foundation, Inc. + +Author: Bill Rozas +(according to ack.texi) This file is part of GNU Emacs. -GNU Emacs is free software; you can redistribute it and/or modify +GNU Emacs is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. GNU Emacs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -14,22 +17,28 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with GNU Emacs; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ +along with GNU Emacs. If not, see . */ + +#define _XOPEN_SOURCE 500 /* for cuserid */ -#define NO_SHORTNAMES -#include <../src/config.h> +#ifdef HAVE_CONFIG_H +#include +#endif -#if defined (BSD) && !defined (BSD4_1) && !defined (USE_FAKEMAIL) +#if defined (BSD_SYSTEM) && !defined (USE_FAKEMAIL) /* This program isnot used in BSD, so just avoid loader complaints. */ -main () +int +main (void) { + return 0; } #else /* not BSD 4.2 (or newer) */ #ifdef MSDOS +int main () { + return 0; } #else /* not MSDOS */ /* This conditional contains all the rest of the file. */ @@ -40,11 +49,8 @@ main () #undef static #endif -#ifdef read -#undef read -#undef write -#undef open -#undef close +#ifdef WINDOWSNT +#include "ntlib.h" #endif #include @@ -52,6 +58,10 @@ main () #include #include #include +#include + +/* This is to declare cuserid. */ +#include /* Type definitions */ @@ -59,6 +69,15 @@ main () #define true 1 #define false 0 +#define TM_YEAR_BASE 1900 + +/* Nonzero if TM_YEAR is a struct tm's tm_year value that causes + asctime to have well-defined behavior. */ +#ifndef TM_YEAR_IN_ASCTIME_RANGE +# define TM_YEAR_IN_ASCTIME_RANGE(tm_year) \ + (1000 - TM_YEAR_BASE <= (tm_year) && (tm_year) <= 9999 - TM_YEAR_BASE) +#endif + /* Various lists */ struct line_record @@ -75,11 +94,11 @@ struct header_record struct header_record *previous; }; typedef struct header_record *header; - + struct stream_record { FILE *handle; - int (*action)(); + int (*action)(FILE *); struct stream_record *rest_streams; }; typedef struct stream_record *stream_list; @@ -119,20 +138,16 @@ struct linebuffer lb; #define MAIL_PROGRAM_NAME "/bin/mail" #endif -static char *my_name; +static const char *my_name; static char *the_date; static char *the_user; static line_list file_preface; static stream_list the_streams; static boolean no_problems = true; -extern FILE *popen (); -extern int fclose (), pclose (); -extern char *malloc (), *realloc (); +static void fatal (const char *s1) NO_RETURN; #ifdef CURRENT_USER -extern struct passwd *getpwuid (); -extern unsigned short geteuid (); static struct passwd *my_entry; #define cuserid(s) \ (my_entry = getpwuid (((int) geteuid ())), \ @@ -144,8 +159,7 @@ static struct passwd *my_entry; /* Print error message. `s1' is printf control string, `s2' is arg for it. */ static void -error (s1, s2) - char *s1, *s2; +error (const char *s1, const char *s2) { printf ("%s: ", my_name); printf (s1, s2); @@ -156,54 +170,46 @@ error (s1, s2) /* Print error message and exit. */ static void -fatal (s1, s2) - char *s1, *s2; +fatal (const char *s1) { - error (s1, s2); - exit (1); + error ("%s", s1); + exit (EXIT_FAILURE); } /* Like malloc but get fatal error if memory is exhausted. */ -static char * -xmalloc (size) - int size; +static long * +xmalloc (int size) { - char *result = malloc (((unsigned) size)); - if (result == ((char *) NULL)) - fatal ("virtual memory exhausted", 0); + long *result = (long *) malloc (((unsigned) size)); + if (result == ((long *) NULL)) + fatal ("virtual memory exhausted"); return result; } -static char * -xrealloc (ptr, size) - char *ptr; - int size; +static long * +xrealloc (long int *ptr, int size) { - char *result = realloc (ptr, ((unsigned) size)); - if (result == ((char *) NULL)) + long *result = (long *) realloc (ptr, ((unsigned) size)); + if (result == ((long *) NULL)) fatal ("virtual memory exhausted"); return result; } /* Initialize a linebuffer for use */ -void -init_linebuffer (linebuffer) - struct linebuffer *linebuffer; +static void +init_linebuffer (struct linebuffer *linebuffer) { linebuffer->size = INITIAL_LINE_SIZE; linebuffer->buffer = ((char *) xmalloc (INITIAL_LINE_SIZE)); } /* Read a line of text from `stream' into `linebuffer'. - * Return the length of the line. - */ + Return the length of the line. */ -long -readline (linebuffer, stream) - struct linebuffer *linebuffer; - FILE *stream; +static long +readline (struct linebuffer *linebuffer, FILE *stream) { char *buffer = linebuffer->buffer; char *p = linebuffer->buffer; @@ -215,7 +221,7 @@ readline (linebuffer, stream) if (p == end) { linebuffer->size *= 2; - buffer = ((char *) xrealloc (buffer, linebuffer->size)); + buffer = ((char *) xrealloc ((long *)buffer, linebuffer->size)); p = buffer + (p - linebuffer->buffer); end = buffer + linebuffer->size; linebuffer->buffer = buffer; @@ -231,56 +237,102 @@ readline (linebuffer, stream) return p - buffer; } -char * -get_keyword (field, rest) - register char *field; - char **rest; +/* Extract a colon-terminated keyword from the string FIELD. + Return that keyword as a string stored in a static buffer. + Store the address of the rest of the string into *REST. + + If there is no keyword, return NULL and don't alter *REST. */ + +static char * +get_keyword (register char *field, char **rest) { static char keyword[KEYWORD_SIZE]; register char *ptr; - register char c; + register int c; ptr = &keyword[0]; - c = *field++; - if ((isspace (c)) || (c == ':')) + c = (unsigned char) *field++; + if (isspace (c) || c == ':') return ((char *) NULL); - *ptr++ = ((islower (c)) ? (toupper (c)) : c); - while (((c = *field++) != ':') && (!(isspace (c)))) - *ptr++ = ((islower (c)) ? (toupper (c)) : c); + *ptr++ = (islower (c) ? toupper (c) : c); + while (((c = (unsigned char) *field++) != ':') && ! isspace (c)) + *ptr++ = (islower (c) ? toupper (c) : c); *ptr++ = '\0'; - while (isspace (c)) c = *field++; - if (c != ':') return ((char *) NULL); + while (isspace (c)) + c = (unsigned char) *field++; + if (c != ':') + return ((char *) NULL); *rest = field; return &keyword[0]; } -boolean -has_keyword (field) - char *field; +/* Nonzero if the string FIELD starts with a colon-terminated keyword. */ + +static boolean +has_keyword (char *field) { char *ignored; return (get_keyword (field, &ignored) != ((char *) NULL)); } -char * -add_field (the_list, field, where) - line_list the_list; - register char *field, *where; +/* Store the string FIELD, followed by any lines in THE_LIST, + into the buffer WHERE. + Concatenate lines, putting just a space between them. + Delete everything contained in parentheses. + When a recipient name contains <...>, we discard + everything except what is inside the <...>. + + We don't pay attention to overflowing WHERE; + the caller has to make it big enough. */ + +static char * +add_field (line_list the_list, register char *field, register char *where) { register char c; while (true) { + char *this_recipient_where; + int in_quotes = 0; + *where++ = ' '; + this_recipient_where = where; + while ((c = *field++) != '\0') { - if (c == '(') + if (c == '\\') + *where++ = c; + else if (c == '"') + { + in_quotes = ! in_quotes; + *where++ = c; + } + else if (in_quotes) + *where++ = c; + else if (c == '(') { while (*field && *field != ')') ++field; - if (! (*field++)) break; /* no closer */ - if (! (*field)) break; /* closerNULL */ - c = *field; + if (! (*field++)) break; /* no close */ + continue; + } + else if (c == ',') + { + *where++ = ' '; + /* When we get to the end of one recipient, + don't discard it if the next one has <...>. */ + this_recipient_where = where; } - *where++ = ((c == ','||c=='>'||c=='<') ? ' ' : c); + else if (c == '<') + /* Discard everything we got before the `<'. */ + where = this_recipient_where; + else if (c == '>') + /* Discard the rest of this name that follows the `>'. */ + { + while (*field && *field != ',') ++field; + if (! (*field++)) break; /* no comma */ + continue; + } + else + *where++ = c; } if (the_list == NIL) break; field = the_list->string; @@ -289,11 +341,12 @@ add_field (the_list, field, where) return where; } -line_list -make_file_preface () +static line_list +make_file_preface (void) { char *the_string, *temp; long idiotic_interface; + struct tm *tm; long prefix_length; long user_length; long date_length; @@ -301,7 +354,13 @@ make_file_preface () prefix_length = strlen (FROM_PREFIX); time (&idiotic_interface); - the_date = ctime (&idiotic_interface); + /* Convert to a string, checking for out-of-range time stamps. + Don't use 'ctime', as that might dump core if the hardware clock + is set to a bizarre value. */ + tm = localtime (&idiotic_interface); + if (! (tm && TM_YEAR_IN_ASCTIME_RANGE (tm->tm_year) + && (the_date = asctime (tm)))) + fatal ("current time is out of range"); /* the_date has an unwanted newline at the end */ date_length = strlen (the_date) - 1; the_date[date_length] = '\0'; @@ -309,9 +368,9 @@ make_file_preface () user_length = strlen (temp); the_user = alloc_string (user_length + 1); strcpy (the_user, temp); - the_string = alloc_string (3 + prefix_length + - user_length + - date_length); + the_string = alloc_string (3 + prefix_length + + user_length + + date_length); temp = the_string; strcpy (temp, FROM_PREFIX); temp = &temp[prefix_length]; @@ -326,10 +385,8 @@ make_file_preface () return result; } -void -write_line_list (the_list, the_stream) - register line_list the_list; - FILE *the_stream; +static void +write_line_list (register line_list the_list, FILE *the_stream) { for ( ; the_list != ((line_list) NULL) ; @@ -341,23 +398,21 @@ write_line_list (the_list, the_stream) return; } -int -close_the_streams () +static int +close_the_streams (void) { register stream_list rem; for (rem = the_streams; rem != ((stream_list) NULL); rem = rem->rest_streams) - no_problems = (no_problems && - ((*rem->action) (rem->handle) == 0)); + if (no_problems && (*rem->action) (rem->handle) != 0) + error ("output error", NULL); the_streams = ((stream_list) NULL); - return (no_problems ? 0 : 1); + return (no_problems ? EXIT_SUCCESS : EXIT_FAILURE); } -void -add_a_stream (the_stream, closing_action) - FILE *the_stream; - int (*closing_action)(); +static void +add_a_stream (FILE *the_stream, int (*closing_action) (FILE *)) { stream_list old = the_streams; the_streams = new_stream (); @@ -367,18 +422,18 @@ add_a_stream (the_stream, closing_action) return; } -int -my_fclose (the_file) - FILE *the_file; +static int +my_fclose (FILE *the_file) { putc ('\n', the_file); fflush (the_file); + if (ferror (the_file)) + return EOF; return fclose (the_file); } -boolean -open_a_file (name) - char *name; +static boolean +open_a_file (char *name) { FILE *the_stream = fopen (name, "a"); if (the_stream != ((FILE *) NULL)) @@ -392,9 +447,8 @@ open_a_file (name) return false; } -void -put_string (s) - char *s; +static void +put_string (char *s) { register stream_list rem; for (rem = the_streams; @@ -404,22 +458,21 @@ put_string (s) return; } -void -put_line (string) - char *string; +static void +put_line (const char *string) { register stream_list rem; for (rem = the_streams; rem != ((stream_list) NULL); rem = rem->rest_streams) { - char *s = string; + const char *s = string; int column = 0; /* Divide STRING into lines. */ while (*s != 0) { - char *breakpos; + const char *breakpos; /* Find the last char that fits. */ for (breakpos = s; *breakpos && column < 78; ++breakpos) @@ -461,27 +514,29 @@ put_line (string) #define mail_error error -void -setup_files (the_list, field) - register line_list the_list; - register char *field; +/* Handle an FCC field. FIELD is the text of the first line (after + the header name), and THE_LIST holds the continuation lines if any. + Call open_a_file for each file. */ + +static void +setup_files (register line_list the_list, register char *field) { register char *start; register char c; while (true) { - while (((c = *field) != '\0') && - ((c == ' ') || - (c == '\t') || - (c == ','))) + while (((c = *field) != '\0') + && (c == ' ' + || c == '\t' + || c == ',')) field += 1; if (c != '\0') { start = field; - while (((c = *field) != '\0') && - (c != ' ') && - (c != '\t') && - (c != ',')) + while (((c = *field) != '\0') + && c != ' ' + && c != '\t' + && c != ',') field += 1; *field = '\0'; if (!open_a_file (start)) @@ -489,15 +544,18 @@ setup_files (the_list, field) *field = c; if (c != '\0') continue; } - if (the_list == ((line_list) NULL)) return; + if (the_list == ((line_list) NULL)) + return; field = the_list->string; the_list = the_list->continuation; } } -int -args_size (the_header) - header the_header; +/* Compute the total size of all recipient names stored in THE_HEADER. + The result says how big to make the buffer to pass to parse_header. */ + +static int +args_size (header the_header) { register header old = the_header; register line_list rem; @@ -506,9 +564,9 @@ args_size (the_header) { char *field; register char *keyword = get_keyword (the_header->text->string, &field); - if ((strcmp (keyword, "TO") == 0) || - (strcmp (keyword, "CC") == 0) || - (strcmp (keyword, "BCC") == 0)) + if ((strcmp (keyword, "TO") == 0) + || (strcmp (keyword, "CC") == 0) + || (strcmp (keyword, "BCC") == 0)) { size += 1 + strlen (field); for (rem = the_header->text->continuation; @@ -521,9 +579,14 @@ args_size (the_header) return size; } -parse_header (the_header, where) - header the_header; - register char *where; +/* Scan the header described by the lists THE_HEADER, + and put all recipient names into the buffer WHERE. + Precede each recipient name with a space. + + Also, if the header has any FCC fields, call setup_files for each one. */ + +static void +parse_header (header the_header, register char *where) { register header old = the_header; do @@ -547,9 +610,15 @@ parse_header (the_header, where) *where = '\0'; return; } - -header -read_header () + +/* Read lines from the input until we get a blank line. + Create a list of `header' objects, one for each header field, + each of which points to a list of `line_list' objects, + one for each line in that field. + Continuation lines are grouped in the headers they continue. */ + +static header +read_header (void) { register header the_header = ((header) NULL); register line_list *next_line = ((line_list *) NULL); @@ -587,7 +656,7 @@ read_header () if (next_line == ((line_list *) NULL)) { /* Not a valid header */ - exit (1); + exit (EXIT_FAILURE); } *next_line = new_list (); (*next_line)->string = alloc_string (length); @@ -597,12 +666,13 @@ read_header () } while (true); + if (! the_header) + fatal ("input message has no header"); return the_header->next; } -void -write_header (the_header) - header the_header; +static void +write_header (header the_header) { register header old = the_header; do @@ -618,21 +688,17 @@ write_header (the_header) return; } -void -main (argc, argv) - int argc; - char **argv; +int +main (int argc, char **argv) { char *command_line; header the_header; long name_length; - char *mail_program_name; + const char *mail_program_name; char buf[BUFLEN + 1]; register int size; FILE *the_pipe; - extern char *getenv (); - mail_program_name = getenv ("FAKEMAILER"); if (!(mail_program_name && *mail_program_name)) mail_program_name = MAIL_PROGRAM_NAME; @@ -647,7 +713,7 @@ main (argc, argv) command_line = alloc_string (name_length + args_size (the_header)); strcpy (command_line, mail_program_name); parse_header (the_header, &command_line[name_length]); - + the_pipe = popen (command_line, "w"); if (the_pipe == ((FILE *) NULL)) fatal ("cannot open pipe to real mailer"); @@ -665,8 +731,14 @@ main (argc, argv) put_string (buf); } + if (no_problems && (ferror (stdin) || fclose (stdin) != 0)) + error ("input error", NULL); + exit (close_the_streams ()); } #endif /* not MSDOS */ #endif /* not BSD 4.2 (or newer) */ + + +/* fakemail.c ends here */