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)
  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: #endif
  44: 
  45: #include <stdio.h>
  46: #include <string.h>
  47: #include <ctype.h>
  48: #include <time.h>
  49: #include <pwd.h>
  50: 
  51: /* Type definitions */
  52: 
  53: #define boolean int
  54: #define true 1
  55: #define false 0
  56: 
  57: /* Various lists */
  58: 
  59: struct line_record
  60: {
  61:   char *string;
  62:   struct line_record *continuation;
  63: };
  64: typedef struct line_record *line_list;
  65: 
  66: struct header_record
  67: {
  68:   line_list text;
  69:   struct header_record *next;
  70:   struct header_record *previous;
  71: };
  72: typedef struct header_record *header;
  73: 
  74: struct stream_record
  75: {
  76:   FILE *handle;
  77:   int (*action)();
  78:   struct stream_record *rest_streams;
  79: };
  80: typedef struct stream_record *stream_list;
  81: 
  82: /* A `struct linebuffer' is a structure which holds a line of text.
  83:  * `readline' reads a line from a stream into a linebuffer
  84:  * and works regardless of the length of the line.
  85:  */
  86: 
  87: struct linebuffer
  88: {
  89:   long size;
  90:   char *buffer;
  91: };
  92: 
  93: struct linebuffer lb;
  94: 
  95: #define new_list()                  \
  96:   ((line_list) xmalloc (sizeof (struct line_record)))
  97: #define new_header()                \
  98:   ((header) xmalloc (sizeof (struct header_record)))
  99: #define new_stream()                \
 100:   ((stream_list) xmalloc (sizeof (struct stream_record)))
 101: #define alloc_string(nchars)                \
 102:   ((char *) xmalloc ((nchars) + 1))
 103: 
 104: /* Global declarations */
 105: 
 106: #define BUFLEN 1024
 107: #define KEYWORD_SIZE 256
 108: #define PROGRAM_NAME "/bin/mail"
 109: #define FROM_PREFIX "From"
 110: #define MY_NAME "fakemail"
 111: #define NIL ((line_list) NULL)
 112: #define INITIAL_LINE_SIZE 200
 113: 
 114: static char *my_name;
 115: static char *the_date;
 116: static char *the_user;
 117: static line_list file_preface;
 118: static stream_list the_streams;
 119: static boolean no_problems = true;
 120: 
 121: extern FILE *popen ();
 122: extern int fclose (), pclose ();
 123: extern char *malloc (), *realloc ();
 124: 
 125: #ifdef CURRENT_USER
 126: extern struct passwd *getpwuid ();
 127: extern unsigned short geteuid ();
 128: static struct passwd *my_entry;
 129: #define cuserid(s)              \
 130: (my_entry = getpwuid (((int) geteuid ())),  \
 131:  my_entry->pw_name)
 132: #endif
 133: 
 134: /* Utilities */
 135: 
 136: /* Print error message.  `s1' is printf control string, `s2' is arg for it. */
 137: 
 138: static void
 139: error (s1, s2)
 140:      char *s1, *s2;
 141: {
 142:   printf ("%s: ", my_name);
 143:   printf (s1, s2);
 144:   printf ("\n");
 145:   no_problems = false;
 146: }
 147: 
 148: /* Print error message and exit.  */
 149: 
 150: static void
 151: fatal (s1, s2)
 152:      char *s1, *s2;
 153: {
 154:   error (s1, s2);
 155:   exit (1);
 156: }
 157: 
 158: /* Like malloc but get fatal error if memory is exhausted.  */
 159: 
 160: static char *
 161: xmalloc (size)
 162:      int size;
 163: {
 164:   char *result = malloc (((unsigned) size));
 165:   if (result == ((char *) NULL))
 166:     fatal ("virtual memory exhausted", 0);
 167:   return result;
 168: }
 169: 
 170: static char *
 171: xrealloc (ptr, size)
 172:      char *ptr;
 173:      int size;
 174: {
 175:   char *result = realloc (ptr, ((unsigned) size));
 176:   if (result == ((char *) NULL))
 177:     fatal ("virtual memory exhausted");
 178:   return result;
 179: }
 180: 
 181: /* Initialize a linebuffer for use */
 182: 
 183: void
 184: init_linebuffer (linebuffer)
 185:      struct linebuffer *linebuffer;
 186: {
 187:   linebuffer->size = INITIAL_LINE_SIZE;
 188:   linebuffer->buffer = ((char *) xmalloc (INITIAL_LINE_SIZE));
 189: }
 190: 
 191: /* Read a line of text from `stream' into `linebuffer'.
 192:  * Return the length of the line.
 193:  */
 194: 
 195: long
 196: readline (linebuffer, stream)
 197:      struct linebuffer *linebuffer;
 198:      FILE *stream;
 199: {
 200:   char *buffer = linebuffer->buffer;
 201:   char *p = linebuffer->buffer;
 202:   char *end = p + linebuffer->size;
 203: 
 204:   while (true)
 205:     {
 206:       int c = getc (stream);
 207:       if (p == end)
 208:     {
 209:       linebuffer->size *= 2;
 210:       buffer = ((char *) xrealloc (buffer, linebuffer->size));
 211:       p += buffer - linebuffer->buffer;
 212:       end += buffer - linebuffer->buffer;
 213:       linebuffer->buffer = buffer;
 214:     }
 215:       if (c < 0 || c == '\n')
 216:     {
 217:       *p = 0;
 218:       break;
 219:     }
 220:       *p++ = c;
 221:     }
 222: 
 223:   return p - buffer;
 224: }
 225: 
 226: char *
 227: get_keyword (field, rest)
 228:      register char *field;
 229:      char **rest;
 230: {
 231:   static char keyword[KEYWORD_SIZE];
 232:   register char *ptr;
 233:   register char c;
 234: 
 235:   ptr = &keyword[0];
 236:   c = *field++;
 237:   if ((isspace (c)) || (c == ':'))
 238:     return ((char *) NULL);
 239:   *ptr++ = ((islower (c)) ? (toupper (c)) : c);
 240:   while (((c = *field++) != ':') && (!(isspace (c))))
 241:     *ptr++ = ((islower (c)) ? (toupper (c)) : c);
 242:   *ptr++ = '\0';
 243:   while (isspace (c)) c = *field++;
 244:   if (c != ':') return ((char *) NULL);
 245:   *rest = field;
 246:   return &keyword[0];
 247: }
 248: 
 249: boolean
 250: has_keyword (field)
 251:      char *field;
 252: {
 253:   char *ignored;
 254:   return (get_keyword (field, &ignored) != ((char *) NULL));
 255: }
 256: 
 257: char *
 258: add_field (the_list, field, where)
 259:      line_list the_list;
 260:      register char *field, *where;
 261: {
 262:   register char c;
 263:   while (true)
 264:     {
 265:       *where++ = ' ';
 266:       while ((c = *field++) != '\0')
 267:     *where++ = ((c == ',') ? ' ' : c);
 268:       if (the_list == NIL) break;
 269:       field = the_list->string;
 270:       the_list = the_list->continuation;
 271:     }
 272:   return where;
 273: }
 274: 
 275: line_list
 276: make_file_preface ()
 277: {
 278:   char *the_string, *temp;
 279:   long idiotic_interface;
 280:   long prefix_length;
 281:   long user_length;
 282:   long date_length;
 283:   line_list result;
 284: 
 285:   prefix_length = strlen (FROM_PREFIX);
 286:   time (&idiotic_interface);
 287:   the_date = ctime (&idiotic_interface);
 288:   /* the_date has an unwanted newline at the end */
 289:   date_length = strlen (the_date) - 1;
 290:   the_date[date_length] = '\0';
 291:   temp = cuserid ((char *) NULL);
 292:   user_length = strlen (temp);
 293:   the_user = alloc_string (user_length + 1);
 294:   strcpy (the_user, temp);
 295:   the_string = alloc_string (3 + prefix_length +
 296:                  user_length +
 297:                  date_length);
 298:   temp = the_string;
 299:   strcpy (temp, FROM_PREFIX);
 300:   temp = &temp[prefix_length];
 301:   *temp++ = ' ';
 302:   strcpy (temp, the_user);
 303:   temp = &temp[user_length];
 304:   *temp++ = ' ';
 305:   strcpy (temp, the_date);
 306:   result = new_list ();
 307:   result->string = the_string;
 308:   result->continuation = ((line_list) NULL);
 309:   return result;
 310: }
 311: 
 312: void
 313: write_line_list (the_list, the_stream)
 314:      register line_list the_list;
 315:      FILE *the_stream;
 316: {
 317:   for ( ;
 318:       the_list != ((line_list) NULL) ;
 319:       the_list = the_list->continuation)
 320:     {
 321:       fputs (the_list->string, the_stream);
 322:       putc ('\n', the_stream);
 323:     }
 324:   return;
 325: }
 326: 
 327: int
 328: close_the_streams ()
 329: {
 330:   register stream_list rem;
 331:   for (rem = the_streams;
 332:        rem != ((stream_list) NULL);
 333:        rem = rem->rest_streams)
 334:     no_problems = (no_problems &&
 335:            (rem->action (rem->handle) == 0));
 336:   the_streams = ((stream_list) NULL);
 337:   return (no_problems ? 0 : 1);
 338: }
 339: 
 340: void
 341: add_a_stream (the_stream, closing_action)
 342:      FILE *the_stream;
 343:      int (*closing_action)();
 344: {
 345:   stream_list old = the_streams;
 346:   the_streams = new_stream ();
 347:   the_streams->handle = the_stream;
 348:   the_streams->action = closing_action;
 349:   the_streams->rest_streams = old;
 350:   return;
 351: }
 352: 
 353: int
 354: my_fclose (the_file)
 355:      FILE *the_file;
 356: {
 357:   putc ('\n', the_file);
 358:   fflush (the_file);
 359:   return fclose (the_file);
 360: }
 361: 
 362: boolean
 363: open_a_file (name)
 364:      char *name;
 365: {
 366:   FILE *the_stream = fopen (name, "a");
 367:   if (the_stream != ((FILE *) NULL))
 368:     {
 369:       add_a_stream (the_stream, my_fclose);
 370:       if (the_user == ((char *) NULL))
 371:     file_preface = make_file_preface ();
 372:       write_line_list (file_preface, the_stream);
 373:       return true;
 374:     }
 375:   return false;
 376: }
 377: 
 378: void
 379: put_string (s)
 380:      char *s;
 381: {
 382:   register stream_list rem;
 383:   for (rem = the_streams;
 384:        rem != ((stream_list) NULL);
 385:        rem = rem->rest_streams)
 386:     fputs (s, rem->handle);
 387:   return;
 388: }
 389: 
 390: void
 391: put_line (s)
 392:      char *s;
 393: {
 394:   register stream_list rem;
 395:   for (rem = the_streams;
 396:        rem != ((stream_list) NULL);
 397:        rem = rem->rest_streams)
 398:     {
 399:       fputs (s, rem->handle);
 400:       putc ('\n', rem->handle);
 401:     }
 402:   return;
 403: }
 404: 
 405: #define mail_error error
 406: 
 407: void
 408: setup_files (the_list, field)
 409:      register line_list the_list;
 410:      register char *field;
 411: {
 412:   register char *start;
 413:   register char c;
 414:   while (true)
 415:     {
 416:       while (((c = *field) != '\0') &&
 417:          ((c == ' ') ||
 418:           (c == '\t') ||
 419:           (c == ',')))
 420:     field += 1;
 421:       if (c != '\0')
 422:     {
 423:       start = field;
 424:       while (((c = *field) != '\0') &&
 425:          (c != ' ') &&
 426:          (c != '\t') &&
 427:          (c != ','))
 428:         field += 1;
 429:       *field = '\0';
 430:       if (!open_a_file (start))
 431:         mail_error ("Could not open file %s", start);
 432:       *field = c;
 433:       if (c != '\0') continue;
 434:     }
 435:       if (the_list == ((line_list) NULL)) return;
 436:       field = the_list->string;
 437:       the_list = the_list->continuation;
 438:     }
 439: }
 440: 
 441: int
 442: args_size (the_header)
 443:      header the_header;
 444: {
 445:   register header old = the_header;
 446:   register line_list rem;
 447:   register int size = 0;
 448:   do
 449:     {
 450:       char *field;
 451:       register char *keyword = get_keyword (the_header->text->string, &field);
 452:       if ((strcmp (keyword, "TO") == 0) ||
 453:       (strcmp (keyword, "CC") == 0) ||
 454:       (strcmp (keyword, "BCC") == 0))
 455:     {
 456:       size += 1 + strlen (field);
 457:       for (rem = the_header->text->continuation;
 458:            rem != NIL;
 459:            rem = rem->continuation)
 460:         size += 1 + strlen (rem->string);
 461:     }
 462:       the_header = the_header->next;
 463:     } while (the_header != old);
 464:   return size;
 465: }
 466: 
 467: parse_header (the_header, where)
 468:      header the_header;
 469:      register char *where;
 470: {
 471:   register header old = the_header;
 472:   do
 473:     {
 474:       char *field;
 475:       register char *keyword = get_keyword (the_header->text->string, &field);
 476:       if (strcmp (keyword, "TO") == 0)
 477:     where = add_field (the_header->text->continuation, field, where);
 478:       else if (strcmp (keyword, "CC") == 0)
 479:     where = add_field (the_header->text->continuation, field, where);
 480:       else if (strcmp (keyword, "BCC") == 0)
 481:     {
 482:       where = add_field (the_header->text->continuation, field, where);
 483:       the_header->previous->next = the_header->next;
 484:       the_header->next->previous = the_header->previous;
 485:     }
 486:       else if (strcmp (keyword, "FCC") == 0)
 487:     setup_files (the_header->text->continuation, field);
 488:       the_header = the_header->next;
 489:     } while (the_header != old);
 490:   *where = '\0';
 491:   return;
 492: }
 493: 
 494: header
 495: read_header ()
 496: {
 497:   register header the_header = ((header) NULL);
 498:   register line_list *next_line = ((line_list *) NULL);
 499: 
 500:   init_linebuffer (&lb);
 501: 
 502:   do
 503:     {
 504:       long length;
 505:       register char *line;
 506: 
 507:       readline (&lb, stdin);
 508:       line = lb.buffer;
 509:       length = strlen (line);
 510:       if (length == 0) break;
 511: 
 512:       if (has_keyword (line))
 513:     {
 514:       register header old = the_header;
 515:       the_header = new_header ();
 516:       if (old == ((header) NULL))
 517:         {
 518:           the_header->next = the_header;
 519:           the_header->previous = the_header;
 520:         }
 521:       else
 522:         {
 523:           the_header->previous = old;
 524:           the_header->next = old->next;
 525:           old->next = the_header;
 526:         }
 527:       next_line = &(the_header->text);
 528:     }
 529: 
 530:       if (next_line == ((line_list *) NULL))
 531:     {
 532:       /* Not a valid header */
 533:       exit (1);
 534:     }
 535:       *next_line = new_list ();
 536:       (*next_line)->string = alloc_string (length);
 537:       strcpy (((*next_line)->string), line);
 538:       next_line = &((*next_line)->continuation);
 539:       *next_line = NIL;
 540: 
 541:     } while (true);
 542: 
 543:   return the_header->next;
 544: }
 545: 
 546: void
 547: write_header (the_header)
 548:      header the_header;
 549: {
 550:   register header old = the_header;
 551:   do
 552:     {
 553:       register line_list the_list;
 554:       for (the_list = the_header->text;
 555:        the_list != NIL;
 556:        the_list = the_list->continuation)
 557:     put_line (the_list->string);
 558:       the_header = the_header->next;
 559:     } while (the_header != old);
 560:   put_line ("");
 561:   return;
 562: }
 563: 
 564: void
 565: main (argc, argv)
 566:      int argc;
 567:      char **argv;
 568: {
 569:   char *command_line;
 570:   header the_header;
 571:   long name_length = strlen (PROGRAM_NAME);
 572:   char buf[BUFLEN + 1];
 573:   register int size;
 574:   FILE *the_pipe;
 575: 
 576:   my_name = MY_NAME;
 577:   the_streams = ((stream_list) NULL);
 578:   the_date = ((char *) NULL);
 579:   the_user = ((char *) NULL);
 580: 
 581:   the_header = read_header ();
 582:   command_line = alloc_string (name_length + args_size (the_header));
 583:   strcpy (command_line, PROGRAM_NAME);
 584:   parse_header (the_header, &command_line[name_length]);
 585: 
 586:   the_pipe = popen (command_line, "w");
 587:   if (the_pipe == ((FILE *) NULL))
 588:     fatal ("cannot open pipe to real mailer");
 589: 
 590:   add_a_stream (the_pipe, pclose);
 591: 
 592:   write_header (the_header);
 593: 
 594:   /* Dump the message itself */
 595: 
 596:   while (!feof (stdin))
 597:     {
 598:       size = fread (buf, 1, BUFLEN, stdin);
 599:       buf[size] = '\0';
 600:       put_string (buf);
 601:     }
 602: 
 603:   exit (close_the_streams ());
 604: }
 605: 
 606: #endif /* not BSD 4.2 (or newer) */

Defined functions

add_a_stream defined in line 340; used 2 times
add_field defined in line 257; used 3 times
args_size defined in line 441; used 1 times
close_the_streams defined in line 327; used 1 times
error defined in line 138; used 2 times
fatal defined in line 150; used 3 times
get_keyword defined in line 226; used 3 times
has_keyword defined in line 249; used 1 times
init_linebuffer defined in line 183; used 1 times
main defined in line 564; never used
make_file_preface defined in line 275; used 1 times
my_fclose defined in line 353; used 1 times
open_a_file defined in line 362; used 1 times
parse_header defined in line 467; used 1 times
put_line defined in line 390; used 2 times
put_string defined in line 378; used 1 times
read_header defined in line 494; used 1 times
readline defined in line 195; used 1 times
setup_files defined in line 407; used 1 times
write_header defined in line 546; used 1 times
write_line_list defined in line 312; used 1 times
xmalloc defined in line 160; used 5 times
xrealloc defined in line 170; used 1 times

Defined variables

file_preface defined in line 117; used 2 times
lb defined in line 93; used 3 times
my_entry defined in line 128; used 2 times
my_name defined in line 114; used 2 times
the_date defined in line 115; used 5 times
the_streams defined in line 118; used 10 times
the_user defined in line 116; used 5 times

Defined struct's

header_record defined in line 66; used 6 times
line_record defined in line 59; used 4 times
linebuffer defined in line 87; used 6 times
stream_record defined in line 74; used 4 times

Defined typedef's

header defined in line 72; used 13 times
line_list defined in line 64; used 17 times
stream_list defined in line 80; used 11 times

Defined macros

BUFLEN defined in line 106; used 2 times
FROM_PREFIX defined in line 109; used 2 times
INITIAL_LINE_SIZE defined in line 112; used 2 times
KEYWORD_SIZE defined in line 107; used 1 times
MY_NAME defined in line 110; used 1 times
NIL defined in line 111; used 4 times
NO_SHORTNAMES defined in line 22; never used
PROGRAM_NAME defined in line 108; used 2 times
alloc_string defined in line 101; used 4 times
boolean defined in line 53; used 3 times
cuserid defined in line 129; used 1 times
false defined in line 55; used 2 times
mail_error defined in line 405; used 1 times
new_header defined in line 97; used 1 times
new_list defined in line 95; used 2 times
new_stream defined in line 99; used 1 times
true defined in line 54; used 6 times
Last modified: 1986-03-28
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 2967
Valid CSS Valid XHTML 1.0 Strict