Blame view

examples/mta.c 18.1 KB
1
/* GNU Mailutils -- a suite of utilities for electronic mail
2 3
   Copyright (C) 1999, 2000, 2001, 2005, 2007, 2009, 2010 Free Software
   Foundation, Inc.
4

5
   GNU Mailutils is free software; you can redistribute it and/or modify
6
   it under the terms of the GNU General Public License as published by
7
   the Free Software Foundation; either version 3, or (at your option)
8 9
   any later version.

10
   GNU Mailutils is distributed in the hope that it will be useful,
11 12 13 14 15
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   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
16
   along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */
17 18 19

/* This is a "fake" mta designed for testing purposes. It imitates
   sendmail sending and daemon modes. It does not actually send anything,
20
   instead it just outputs a transcript of what would have been done.
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42

   Invocation:
   
   1. mta [-t][-oi][-f from-person] [recipients]

   Reads the message from the standard input and delivers it to each of the
   recipients.

   2. mta -bd [-p port]

   Operates as daemon. If port is given, mta will listen on that port.
   Otherwise, it will use the first free port in the range 1024-65535.
   In this case, mta prints the port number on the stdout, prior to
   starting operation. Notice, that in this mode mta does not disconnect
   itself from the controlling terminal, it always stays on the foreground.

   Environment variables:

   MTA_DOMAIN   Sets the default domain name for email addresses. Default
                is "localhost".
   MTA_DIAG     Sets the name of the output diagnostic file. By default,
                the diagnostics goes to stderr.
43 44
   MTA_APPEND   When set to any non-empty value, directs mta to append
                to the diagnostics file, not to overwrite it. 
45 46 47 48 49 50 51 52

*/

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <unistd.h>
53 54 55
#ifdef HAVE_GETOPT_H
# include <getopt.h>
#endif
56 57
#include <string.h>
#include <pwd.h>
58
#include <sysexits.h>
59 60 61 62 63 64 65 66 67 68 69 70 71 72
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <mailutils/argcv.h>
#include <mailutils/mailutils.h>

FILE *diag = NULL;       /* diagnostic output */
int smtp_mode;           /* Operate in smtp mode */
int port = 0;            /* Port number (for smtp mode) */
char *from_person = NULL; /* Set the name of the `from' person */
int read_recipients = 0; /* Read the message for recipients */
int dot = 1;             /* Message is terminated by a lone dot on a line */

73
mu_address_t recipients = NULL;
74
char *progname;
75 76 77 78 79 80 81 82 83 84 85 86
/* FIXME: If finalize_option is set, mta should try to finalize
   received messages the way sendmail does, i.e. to add To: or
   Cc: headers, if they are missing, etc. The code to do so is
   already included, but the modified message is not reproduced
   on diagnostic output because mta_send reads it from the stream,
   which does not reflect modifications to the header.

   Ideally, the mu_message_get_streamref function should notice the
   change in the header (and/or the body) and return a temporary
   stream, which will read the modified values.  This is left as
   as TODO for a later time.                  2010-09-29, Sergey */
int finalize_option = 0; 
87

88 89 90
int mta_stdin (int argc, char **argv);
int mta_smtp (int argc, char **argv);
void register_handlers (void);
91 92 93 94 95 96 97

int
main (int argc, char **argv)
{
  int c, status;
  char *domain;

98 99 100 101
  progname = strrchr (argv[0], '/');
  if (!progname)
    progname = argv[0];
  
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
  while ((c = getopt (argc, argv, "b:f:p:to:")) != EOF)
    {
      switch (c)
	{
	case 'b':
	  switch (optarg[0])
	    {
	    case 'd':
	      smtp_mode = 1;
	      break;

	    default:
	      /*FIXME*/;
	    }
	  break;
	  
	case 'f':
	  from_person = optarg;
	  break;
	  
	case 'p':
	  port = strtoul (optarg, NULL, 0);
	  break;
	  
	case 't':
	  read_recipients = 1;
	  break;

	case 'o':
	  switch (optarg[0])
	    {
	    case 'i':
	      dot = 0;
	      break;

	    default:
	      /* FIXME */ ;
	    }
	  break;

	default:
143
	  exit (EX_USAGE);
144 145 146 147 148 149 150 151
	}
    }

  if (!diag)
    {
      char *name = getenv ("MTA_DIAG");
      if (name)
	{
152 153
	  char *mode = getenv ("MTA_APPEND") ? "a" : "w";
	  diag = fopen (name, mode);
154 155 156
	  if (!diag)
	    {
	      mu_error ("%s: can't open diagnostic output: %s",
157
			progname, name);
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
	      return 1;
	    }
	}
      else
	diag = stdout;
    }
  
  register_handlers ();

  domain = getenv ("MTA_DOMAIN");
  mu_set_user_email_domain (domain ? domain : "localhost");
  
  argc -= optind;
  argv += optind;
  if (smtp_mode)
    status = mta_smtp (argc, argv);
  else
    status = mta_stdin (argc, argv);
  fclose (diag);
  return status;
}

char *
from_address ()
{
  if (!from_person)
    {
      struct passwd *pwd = getpwuid (getuid ());
      if (pwd)
	return mu_get_user_email (pwd->pw_name);
    }
  return strdup (from_person);
}
     
192 193
static mu_message_t
make_tmp (mu_stream_t in)
194
{
195 196
  int rc;
  mu_stream_t out;
197
  char *buf = NULL;
198 199
  size_t size = 0, n;
  mu_message_t mesg;
200
  
201
  rc = mu_temp_file_stream_create (&out, NULL, 0);
202
  if (rc)
203
    {
204 205
      mu_error (_("unable to open temporary file: %s"), mu_strerror (rc));
      exit (EX_UNAVAILABLE);
206 207
    }

208 209
  rc = mu_stream_getline (in, &buf, &size, &n);
  if (rc)
210
    {
211 212 213 214 215 216 217 218 219 220
      mu_error (_("read error: %s"), mu_strerror (rc));
      mu_stream_destroy (&out);
      exit (EX_UNAVAILABLE);
    }
  if (n == 0)
    {
      mu_error (_("unexpected EOF on input"));
      mu_stream_destroy (&out);
      exit (EX_DATAERR);
    } 
221

222 223 224 225 226
  if (n >= 5 && memcmp (buf, "From ", 5))
    {
      time_t t;
      const char *from = from_address ();
      if (!from)
227
	{
228 229
	  mu_error ("%s: can't determine sender address", progname);
	  exit (EX_NOUSER);
230
	}
231 232 233
	  
      time (&t);
      mu_stream_printf (out, "From %s %s", from, ctime (&t));
234 235
    }

236
  mu_stream_write (out, buf, n, NULL);
237
  free (buf);
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
  
  rc = mu_stream_copy (out, in, 0, NULL);
  if (rc)
    {
      mu_error (_("copy error: %s"), mu_strerror (rc));
      mu_stream_destroy (&out);
      exit (EX_UNAVAILABLE);
    }

  rc = mu_stream_to_message (out, &mesg);
  mu_stream_destroy (&out);
  if (rc)
    {
      mu_error (_("error creating temporary message: %s"),
		mu_strerror (rc));
      exit (EX_UNAVAILABLE);
    }
255

256
  return mesg;
257 258 259 260 261
}

void
register_handlers ()
{
262
  mu_registrar_record (mu_mbox_record);
263 264
  mu_registrar_record (mu_sendmail_record);
  mu_registrar_record (mu_smtp_record);
265
  mu_registrar_set_default_record (mu_mbox_record);
266 267 268 269 270
}

int
add_recipient (const char *name)
{
271
  mu_address_t addr;
272 273
  int status;
    
274
  status = mu_address_create (&addr, name);
275
  if (status == 0)
276 277
    status = mu_address_union (&recipients, addr);
  mu_address_destroy (&addr);
278 279 280
  return status;
}

281 282 283
/* Convert addr to a comma-separated list of email addresses,
   suitable as an argument to RCPT TO command */
char *
284
address_email_string (mu_address_t addr)
285 286 287 288
{
  size_t count, i, n, length;
  char *value, *p;
  
289
  mu_address_get_email_count (addr, &count);
290 291 292
  length = 0;
  for (i = 1; i <= count; i++)
    {
293 294
      const char *str;
      mu_address_sget_email (recipients, i, &str);
295 296 297 298 299 300 301 302 303 304 305 306 307
      length += strlen (str) + 3;
    }

  value = malloc (length + 1);
  if (!value)
    {
      mu_error ("%s: not enough memory", progname);
      return NULL;
    }
  p = value;
  for (i = 1; i <= count; i++)
    {
      *p++ = '<';
308
      mu_address_get_email (recipients, i, p, length - (p - value), &n);
309 310 311 312 313 314 315 316 317
      p += n;
      *p++ = '>';
      if (i + 1 <= count)
	*p++ = ',';
    }
  *p = 0;
  return value;
}

318
int
319
mta_send (mu_message_t msg)
320 321 322
{
  size_t n;
  char buffer[512];    
323
  mu_stream_t stream = NULL;
324
  size_t line;
325
  char *value;
326 327
  int newline = 0;
  
328 329 330 331 332 333 334
  value = from_address ();
  if (value)
    {
      fprintf (diag, "ENVELOPE FROM: %s\n", value);
      free (value);
    }
  
335
  value = address_email_string (recipients);
336 337 338
  fprintf (diag, "ENVELOPE TO: %s\n", value);
  free (value);

339
  mu_message_get_streamref (msg, &stream);
340
  line = 0;
341
  newline = 1;
342
  while (mu_stream_read (stream, buffer, sizeof buffer - 1, &n) == 0
343 344 345 346 347 348
	 && n != 0)
    {
      size_t i;

      for (i = 0; i < n; i++)
	{
349 350
	  if (newline)
	    {
351 352 353
	      fprintf (diag, "%4lu:", (unsigned long) line);
	      if (buffer[i] != '\n')
		fputc (' ', diag);
354 355
	      newline = 0;
	    }
356 357 358 359
	  fputc (buffer[i], diag);
	  if (buffer[i] == '\n')
	    {
	      line++;
360
	      newline = 1;
361 362 363
	    }
	}
    }
364
  mu_stream_destroy (&stream);
365 366 367
  if (!newline)
    fprintf (diag, "\n(no newline at EOF)\n");
  fprintf (diag, "END OF MESSAGE\n");
368 369 370 371
  fflush (diag);
  return 0;
}

372 373
#define SENDER_WARNING "set sender using -f flag"

374
int
375
message_finalize (mu_message_t msg, int warn)
376
{
377
  mu_header_t header = NULL;
378 379 380
  int have_to;
  char *value = NULL;
  
381
  mu_message_get_header (msg, &header);
382

383
  if (finalize_option && warn && from_person)
384 385
    {
      struct passwd *pwd = getpwuid (getuid ());
386 387 388 389 390 391 392 393
      char *warn = malloc (strlen (pwd->pw_name) + 1 +
			   sizeof (SENDER_WARNING));
      if (warn == NULL)
	{
	  mu_error ("%s: not enough memory", progname);
	  return 1;
	}
      sprintf (warn, "%s %s", pwd->pw_name, SENDER_WARNING);
394
      mu_header_set_value (header, "X-Authentication-Warning", warn, 0);
395 396 397
      free (warn);
    }
  
398
  have_to = mu_header_aget_value (header, MU_HEADER_TO, &value) == 0;
399 400 401 402 403 404 405
  
  if (read_recipients)
    {
      if (value)
	{
	  if (add_recipient (value))
	    {
406
	      mu_error ("%s: bad address %s", progname, value);
407 408 409 410 411
	      return 1;
	    }
	  free (value);
	}
	  
412
      if (mu_header_aget_value (header, MU_HEADER_CC, &value) == 0)
413 414 415
	{
	  if (add_recipient (value))
	    {
416
	      mu_error ("%s: bad address %s", progname, value);
417 418 419 420 421
	      return 1;
	    }
	  free (value);
	}  
	  
422 423
      if (finalize_option &&
	  mu_header_aget_value (header, MU_HEADER_BCC, &value) == 0)
424 425 426
	{
	  if (add_recipient (value))
	    {
Sergey Poznyakoff authored
427
	      mu_error ("%s: bad address %s", progname, value);
428 429 430
	      return 1;
	    }
	  free (value);
431
	  mu_header_set_value (header, MU_HEADER_BCC, NULL, 1);
432 433 434
	}  
    }

435
  if (finalize_option && !have_to)
436 437 438 439
    {
      size_t n;
      int c;
      
440
      c = mu_address_to_string (recipients, NULL, 0, &n);
441 442
      if (c)
	{
443
	  mu_error ("%s: mu_address_to_string failure: %s",
444
		    progname, mu_strerror (c));
445 446 447 448 449
	  return 1;
	}
      value = malloc (n + 1);
      if (!value)
	{
450
	  mu_error ("%s: not enough memory", progname);
451 452 453
	  return 1;
	}

454 455
      mu_address_to_string (recipients, value, n + 1, &n);
      mu_header_set_value (header, MU_HEADER_TO, value, 1);
456 457 458 459 460 461 462 463
      free (value);
    }
  return 0;
}

int
mta_stdin (int argc, char **argv)
{
464 465
  int c, rc;
  mu_stream_t in;
466
  mu_message_t msg = NULL;
467 468 469 470 471
  
  for (c = 0; c < argc; c++)
    {
      if (add_recipient (argv[c]))
	{
472
	  mu_error ("%s: bad address %s", progname, argv[c]);
473 474 475 476
	  return 1;
	}
    }

477 478
  rc = mu_stdio_stream_create (&in, MU_STDIN_FD, MU_STREAM_READ);
  if (rc)
479
    {
480 481 482 483
      mu_diag_funcall (MU_DIAG_ERROR, "mu_stdio_stream_create",
		       "MU_STDIN_FD", rc);
      exit (EX_UNAVAILABLE);
    } 
484

485 486
  msg = make_tmp (in);
  mu_stream_destroy (&in);
487 488
  if (message_finalize (msg, 1))
    return 1;
489
  
490 491
  if (!recipients)
    {
492
      mu_error ("%s: Recipient names must be specified", progname);
493 494 495 496
      return 1;
    }

  mta_send (msg);
497
  mu_message_destroy (&msg, mu_message_get_owner (msg));
498 499 500 501
  return 0;
}

void
502
smtp_reply (mu_stream_t str, int code, char *fmt, ...)
503 504 505
{
  va_list ap;

506
  mu_stream_printf (str, "%d ", code);
507
  va_start (ap, fmt);
508
  mu_stream_vprintf (str, fmt, ap);
509
  va_end (ap);
510
  mu_stream_printf (str, "\r\n");
511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547
}

#define STATE_INIT   0
#define STATE_EHLO   1
#define STATE_MAIL   2
#define STATE_RCPT   3
#define STATE_DATA   4  
#define STATE_QUIT   5 
#define STATE_DOT    6

#define KW_EHLO      0
#define KW_HELO      1
#define KW_MAIL      2
#define KW_RCPT      3
#define KW_DATA      4   
#define KW_HELP      5
#define KW_QUIT      6

int
smtp_kw (const char *name)
{
  static struct kw {
    char *name;
    int code;
  } kw[] = {
    { "ehlo", KW_EHLO },      
    { "helo", KW_HELO },       
    { "mail", KW_MAIL },
    { "rcpt", KW_RCPT },
    { "data", KW_DATA },
    { "help", KW_HELP },
    { "quit", KW_QUIT },
    { NULL },
  };
  int i;

  for (i = 0; kw[i].name != NULL; i++)
548
    if (mu_c_strcasecmp (name, kw[i].name) == 0)
549 550 551 552
      return kw[i].code;
  return -1;
}

553 554 555 556
static char *
check_prefix (char *str, const char *prefix)
{
  int pflen = strlen (prefix);
557
  if (strlen (str) > pflen && mu_c_strncasecmp (str, prefix, pflen) == 0)
558 559 560 561 562
    return str + pflen;
  else
    return NULL;
}

563
void
564
smtp (mu_stream_t str)
565
{
566
  int state;
567 568
  char *buf = NULL;
  size_t size = 0;
569
  char *rcpt_addr;
570 571
  int wsflags = MU_WRDSF_DEFFLAGS;
  struct mu_wordsplit ws;
572
  
573
  smtp_reply (str, 220, "Ready");
574 575
  for (state = STATE_INIT; state != STATE_QUIT; )
    {
576 577
      int kw;
      size_t len;
578
      
579 580 581
      if (mu_stream_getline (str, &buf, &size, &len) || len == 0)
	exit (EX_PROTOCOL);

582 583 584 585 586 587 588
      if (mu_wordsplit (buf, &ws, wsflags))
	{
	  mu_error ("cannot split line `%s': %s", buf,
		    mu_wordsplit_strerror (&ws));
	  exit (EX_UNAVAILABLE);
	}
      wsflags |= MU_WRDSF_REUSE;
589

590
      kw = smtp_kw (ws.ws_wordv[0]);
591 592
      if (kw == KW_QUIT)
	{
593
	  smtp_reply (str, 221, "Done");
594 595 596 597 598 599 600 601 602 603 604
	  state = STATE_QUIT;
	  continue;
	}
      
      switch (state)
	{
	case STATE_INIT:
	  switch (kw)
	    {
	    case KW_EHLO:
	    case KW_HELO:
605
	      if (ws.ws_wordc == 2)
606
		{
607
		  smtp_reply (str, 250, "pleased to meet you");
608 609 610
		  state = STATE_EHLO;
		}
	      else
611 612
		smtp_reply (str, 501, "%s requires domain address",
			    ws.ws_wordv[0]);
613 614 615
	      break;

	    default:
616
	      smtp_reply (str, 503, "Polite people say HELO first");
617 618 619 620 621 622 623 624
	      break;
	    }
	  break;
	  
	case STATE_EHLO:
	  switch (kw)
	    {
	    case KW_MAIL:
625 626 627 628 629
	      if (ws.ws_wordc == 2)
		from_person = check_prefix (ws.ws_wordv[1], "from:");
	      else if (ws.ws_wordc == 3 &&
		       mu_c_strcasecmp (ws.ws_wordv[1], "from:") == 0)
		from_person = ws.ws_wordv[2];
630 631 632 633
	      else
		from_person = NULL;

	      if (from_person)
634
		{
635
		  from_person = strdup (from_person);
636
		  smtp_reply (str, 250, "Sender OK");
637 638 639
		  state = STATE_MAIL;
		}
	      else
640
		smtp_reply (str, 501, "Syntax error");
641 642 643
	      break;

	    default:
644
	      smtp_reply (str, 503, "Need MAIL command");
645 646 647 648 649 650 651
	    }
	  break;
	  
	case STATE_MAIL:
	  switch (kw)
	    {
	    case KW_RCPT:
652 653 654 655 656
	      if (ws.ws_wordc == 2)
		rcpt_addr = check_prefix (ws.ws_wordv[1], "to:");
	      else if (ws.ws_wordc == 3 &&
		       mu_c_strcasecmp (ws.ws_wordv[1], "to:") == 0)
		rcpt_addr = ws.ws_wordv[2];
657 658 659 660
	      else
		rcpt_addr = NULL;
	      
	      if (rcpt_addr)
661
		{
662
		  if (add_recipient (rcpt_addr))
663
		    smtp_reply (str, 451, "Recipient not accepted");
664 665
		  else
		    {
666
		      smtp_reply (str, 250, "Recipient OK");
667 668 669 670
		      state = STATE_RCPT;
		    }
		}
	      else
671
		smtp_reply (str, 501, "Syntax error");
672 673 674
	      break;
	      
	    default:
675
	      smtp_reply (str, 503, "Need RCPT command");
676 677 678 679 680 681 682
	    }
	  break;
	  
	case STATE_RCPT:
	  switch (kw)
	    {
	    case KW_RCPT:
683 684 685 686 687
	      if (ws.ws_wordc == 2)
		rcpt_addr = check_prefix (ws.ws_wordv[1], "to:");
	      else if (ws.ws_wordc == 3 &&
		       mu_c_strcasecmp (ws.ws_wordv[1], "to:") == 0)
		rcpt_addr = ws.ws_wordv[2];
688 689 690 691
	      else
		rcpt_addr = NULL;
	      
	      if (rcpt_addr)
692
		{
693
		  if (add_recipient (rcpt_addr))
694
		    smtp_reply (str, 451, "Recipient not accepted");
695
		  else
696
		    {
697
		      smtp_reply (str, 250, "Recipient OK");
698 699
		      state = STATE_RCPT;
		    }
700 701
		}
	      else
702
		smtp_reply (str, 501, "Syntax error");
703 704 705
	      break;

	    case KW_DATA:
706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730
	      {
		mu_stream_t flt;
		mu_message_t msg;
		int rc;
		
		rc = mu_filter_create (&flt, str, "CRLFDOT", MU_FILTER_DECODE,
				       MU_STREAM_READ|MU_STREAM_WRTHRU);
		if (rc)
		  {
		    mu_diag_funcall (MU_DIAG_ERROR, "mu_filter_create",
				     "CRLFDOT", rc);
		    exit (EX_UNAVAILABLE);
		  }
		smtp_reply (str, 354,
			    "Enter mail, end with \".\" on a line by itself");
		
		msg = make_tmp (flt);
		mu_stream_destroy (&flt);
		if (message_finalize (msg, 0) == 0)
		  mta_send (msg);
		else
		  smtp_reply (str, 501, "can't send message"); /*FIXME: code?*/
		mu_message_destroy (&msg, mu_message_get_owner (msg));
		mu_address_destroy (&recipients);
		from_person = NULL;
731
	      
732 733 734
		smtp_reply (str, 250, "Message accepted for delivery");
		state = STATE_EHLO;
	      }
735 736 737
	      break;

	    default:
738
	      smtp_reply (str, 503, "Invalid command");
739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756
	      break;
	    }
	  break;

	}
    }
}

int
mta_smtp (int argc, char **argv)
{
  int on = 1;
  struct sockaddr_in address;
  int fd;
  
  fd = socket (PF_INET, SOCK_STREAM, 0);
  if (fd < 0)
    {
757
      mu_error ("%s: socket: %s", progname, strerror (errno));
758 759 760 761 762 763 764 765 766 767 768 769 770 771 772
      return 1;
    }

  setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on));

  memset (&address, 0, sizeof (address));
  address.sin_family = AF_INET;
  address.sin_addr.s_addr = INADDR_ANY;

  if (port)
    {
      address.sin_port = htons (port);
      if (bind (fd, (struct sockaddr *) &address, sizeof (address)) < 0)
	{
	  close (fd);
773
	  mu_error ("%s: bind: %s", progname, strerror(errno));
774 775 776 777 778 779 780 781 782 783 784 785 786
	  return 1;
	}
    }
  else
    {
      int status;
      
      port = 1023;
      do
	{
	  if (++port >= 65535)
	    {
	      mu_error ("%s: can't bind socket: all ports in use?",
787
			progname);
788 789 790 791 792 793 794 795 796
	      return 1;
	    }
	  address.sin_port = htons (port);
	  status = bind (fd, (struct sockaddr *) &address, sizeof (address));
	}
      while (status < 0);
    }

  listen (fd, 5);
797 798
  printf ("%d\n", port);
  fclose (stdout);
799 800 801 802
  while (1)
    {
      fd_set rfds;
      struct sockaddr_in his_addr;
803 804
      int sfd, status;
      socklen_t len;
805 806 807
      int rc;
      mu_stream_t str;
      
808 809 810 811 812 813 814 815
      FD_ZERO (&rfds);
      FD_SET (fd, &rfds);
      
      status = select (fd + 1, &rfds, NULL, NULL, NULL);
      if (status == -1)
	{
	  if (errno == EINTR)
	    continue;
816
	  mu_error ("%s: select: %s", progname, strerror (errno));
817 818 819 820 821 822
	  return 1;
	}

      len = sizeof (his_addr);
      if ((sfd = accept (fd, (struct sockaddr *)&his_addr, &len)) < 0)
	{
823
	  mu_error ("%s: accept: %s", progname, strerror (errno));
824 825 826
	  return 1;
	}

827
      rc = mu_fd_stream_create (&str, NULL, sfd, MU_STREAM_RDWR);
828 829 830 831 832 833 834 835
      if (rc)
	{
	  mu_diag_funcall (MU_DIAG_ERROR, "mu_fd_stream_create", NULL, rc);
	  break;
	}
      mu_stream_set_buffer (str, mu_buffer_line, 0);
      smtp (str);
      mu_stream_destroy (&str);
836 837 838 839 840
      break;
    }
  
  return 0;
}