install correct aliases file
[unix-history] / usr / src / usr.sbin / sendmail / src / srvrsmtp.c
CommitLineData
b2a81223 1/*
dc45ba8c 2 * Copyright (c) 1983 Eric P. Allman
bee79b64
KB
3 * Copyright (c) 1988 Regents of the University of California.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms are permitted
dc45ba8c
KB
7 * provided that the above copyright notice and this paragraph are
8 * duplicated in all such forms and that any documentation,
9 * advertising materials, and other materials related to such
10 * distribution and use acknowledge that the software was developed
11 * by the University of California, Berkeley. The name of the
12 * University may not be used to endorse or promote products derived
13 * from this software without specific prior written permission.
14 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
16 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
bee79b64
KB
17 */
18
19# include "sendmail.h"
b2a81223 20
bee79b64
KB
21#ifndef lint
22#ifdef SMTP
2e15a2d8 23static char sccsid[] = "@(#)srvrsmtp.c 5.25 (Berkeley) %G% (with SMTP)";
bee79b64 24#else
2e15a2d8 25static char sccsid[] = "@(#)srvrsmtp.c 5.25 (Berkeley) %G% (without SMTP)";
bee79b64
KB
26#endif
27#endif /* not lint */
b2a81223 28
e6f08ab1 29# include <errno.h>
671d4b5c 30# include <signal.h>
6b861048 31
bee79b64 32# ifdef SMTP
d727056e 33
6b861048
EA
34/*
35** SMTP -- run the SMTP protocol.
36**
37** Parameters:
38** none.
39**
40** Returns:
41** never.
42**
43** Side Effects:
44** Reads commands from the input channel and processes
45** them.
46*/
47
48struct cmd
49{
50 char *cmdname; /* command name */
51 int cmdcode; /* internal code, see below */
52};
53
54/* values for cmdcode */
55# define CMDERROR 0 /* bad command */
56# define CMDMAIL 1 /* mail -- designate sender */
4a4ebe09 57# define CMDRCPT 2 /* rcpt -- designate recipient */
6b861048 58# define CMDDATA 3 /* data -- send message text */
7d7fdf93 59# define CMDHOPS 4 /* hops -- specify hop count */
e6f08ab1
EA
60# define CMDRSET 4 /* rset -- reset state */
61# define CMDVRFY 5 /* vrfy -- verify address */
62# define CMDHELP 6 /* help -- give usage info */
63# define CMDNOOP 7 /* noop -- do nothing */
64# define CMDQUIT 8 /* quit -- close connection and die */
65# define CMDHELO 9 /* helo -- be polite */
ab4889ea
MK
66# define CMDONEX 10 /* onex -- sending one transaction only */
67# define CMDVERB 11 /* verb -- go into verbose mode */
68/* debugging-only commands, only enabled if SMTPDEBUG is defined */
69# define CMDDBGQSHOW 12 /* showq -- show send queue */
70# define CMDDBGDEBUG 13 /* debug -- set debug mode */
71# define CMDDBGKILL 14 /* kill -- kill sendmail */
72# define CMDDBGWIZ 15 /* wiz -- become a wizard */
6b861048
EA
73
74static struct cmd CmdTab[] =
75{
76 "mail", CMDMAIL,
4a4ebe09 77 "rcpt", CMDRCPT,
6b861048 78 "data", CMDDATA,
6b861048
EA
79 "rset", CMDRSET,
80 "vrfy", CMDVRFY,
abae7b2d 81 "expn", CMDVRFY,
633a2e02 82 "expn", CMDVRFY,
6b861048
EA
83 "help", CMDHELP,
84 "noop", CMDNOOP,
85 "quit", CMDQUIT,
4a4ebe09 86 "helo", CMDHELO,
e8ad767d 87 "verb", CMDVERB,
8fe4fb9b 88 "onex", CMDONEX,
7d7fdf93 89 "hops", CMDHOPS,
ab4889ea
MK
90 /*
91 * remaining commands are here only
92 * to trap and log attempts to use them
93 */
e6f08ab1 94 "showq", CMDDBGQSHOW,
ad20d67b 95 "debug", CMDDBGDEBUG,
e8ad767d
EA
96 "kill", CMDDBGKILL,
97 "wiz", CMDDBGWIZ,
6b861048
EA
98 NULL, CMDERROR,
99};
100
8ff78b51 101# ifdef WIZ
e8ad767d 102bool IsWiz = FALSE; /* set if we are a wizard */
c50c55e6 103char *WizWord; /* the wizard word to compare against */
ab4889ea 104# endif WIZ
e6f08ab1 105bool InChild = FALSE; /* true if running in a subprocess */
7338e3d4 106bool OneXact = FALSE; /* one xaction only this run */
1972fb40 107
e6f08ab1 108#define EX_QUIT 22 /* special code for QUIT command */
e8ad767d 109
6b861048
EA
110smtp()
111{
6b861048 112 register char *p;
e8ad767d 113 register struct cmd *c;
6b861048
EA
114 char *cmd;
115 extern char *skipword();
6b861048 116 bool hasmail; /* mail command received */
abae7b2d
EA
117 extern ADDRESS *sendto();
118 ADDRESS *a;
6b861048 119
abae7b2d 120 hasmail = FALSE;
1f1cc003
EA
121 if (OutChannel != stdout)
122 {
123 /* arrange for debugging output to go to remote host */
124 (void) close(1);
125 (void) dup(fileno(OutChannel));
126 }
1b932f66 127 settime();
356262e6 128 if (RealHostName != NULL)
57c97d4a
EA
129 {
130 CurHostName = RealHostName;
131 setproctitle("srvrsmtp %s", CurHostName);
132 }
133 else
134 {
135 /* this must be us!! */
136 CurHostName = MyHostName;
137 }
a73ae8ac 138 expand("\001e", inp, &inp[sizeof inp], CurEnv);
378e8da7 139 message("220", inp);
2e3062fe 140 SmtpPhase = "startup";
7564b51f 141 sendinghost = NULL;
6b861048
EA
142 for (;;)
143 {
d344c0b7
EA
144 /* arrange for backout */
145 if (setjmp(TopFrame) > 0 && InChild)
146 finis();
147 QuickAbort = FALSE;
148 HoldErrs = FALSE;
149
37eaaadb 150 /* setup for the read */
2654b031 151 CurEnv->e_to = NULL;
34d37b7d 152 Errors = 0;
09eb49d8 153 (void) fflush(stdout);
37eaaadb 154
37eaaadb 155 /* read the input line */
2439b900 156 p = sfgets(inp, sizeof inp, InChannel);
37eaaadb 157
2439b900 158 /* handle errors */
37eaaadb 159 if (p == NULL)
6b861048
EA
160 {
161 /* end of file, just die */
ab4889ea 162 message("421", "%s Lost input channel from %s",
57c97d4a 163 MyHostName, CurHostName);
6b861048
EA
164 finis();
165 }
166
167 /* clean up end of line */
2768afe3 168 fixcrlf(inp, TRUE);
6b861048 169
49086753 170 /* echo command to transcript */
912acb74
EA
171 if (CurEnv->e_xfp != NULL)
172 fprintf(CurEnv->e_xfp, "<<< %s\n", inp);
49086753 173
6b861048
EA
174 /* break off command */
175 for (p = inp; isspace(*p); p++)
176 continue;
177 cmd = p;
a0225d08
EA
178 for (cmd = cmdbuf; *p != '\0' && !isspace(*p); )
179 *cmd++ = *p++;
180 *cmd = '\0';
6b861048 181
a1a07282
EA
182 /* throw away leading whitespace */
183 while (isspace(*p))
184 p++;
185
6b861048
EA
186 /* decode command */
187 for (c = CmdTab; c->cmdname != NULL; c++)
188 {
ed73ef1d 189 if (!strcasecmp(c->cmdname, cmdbuf))
6b861048
EA
190 break;
191 }
192
193 /* process command */
194 switch (c->cmdcode)
195 {
4a4ebe09 196 case CMDHELO: /* hello -- introduce yourself */
2e3062fe 197 SmtpPhase = "HELO";
57c97d4a 198 setproctitle("%s: %s", CurHostName, inp);
ed73ef1d 199 if (!strcasecmp(p, MyHostName))
f7e74083 200 {
ab4889ea
MK
201 /*
202 * didn't know about alias,
203 * or connected to an echo server
204 */
205 message("553", "Local configuration error, hostname not recognized as local");
f7e74083
EA
206 break;
207 }
ed73ef1d 208 if (RealHostName != NULL && strcasecmp(p, RealHostName))
1972fb40 209 {
a0225d08 210 char hostbuf[MAXNAME];
1972fb40 211
a0225d08 212 (void) sprintf(hostbuf, "%s (%s)", p, RealHostName);
7564b51f 213 sendinghost = newstr(hostbuf);
1972fb40
EA
214 }
215 else
7564b51f 216 sendinghost = newstr(p);
abae7b2d 217 message("250", "%s Hello %s, pleased to meet you", HostName, p);
4a4ebe09
EA
218 break;
219
6b861048 220 case CMDMAIL: /* mail -- designate sender */
2e3062fe
EA
221 SmtpPhase = "MAIL";
222
0c6a0070
EA
223 /* force a sending host even if no HELO given */
224 if (RealHostName != NULL && macvalue('s', CurEnv) == NULL)
7564b51f 225 sendinghost = RealHostName;
0c6a0070 226
8fe4fb9b 227 /* check for validity of this command */
2768afe3
EA
228 if (hasmail)
229 {
230 message("503", "Sender already specified");
231 break;
232 }
e6f08ab1
EA
233 if (InChild)
234 {
ab4889ea 235 errno = 0;
e6f08ab1
EA
236 syserr("Nested MAIL command");
237 exit(0);
238 }
239
240 /* fork a subprocess to process this command */
241 if (runinchild("SMTP-MAIL") > 0)
242 break;
7564b51f 243 define('s', sendinghost, CurEnv);
e6f08ab1 244 initsys();
4d344ce6 245 setproctitle("%s %s: %s", CurEnv->e_id,
57c97d4a 246 CurHostName, inp);
e6f08ab1
EA
247
248 /* child -- go do the processing */
6b861048
EA
249 p = skipword(p, "from");
250 if (p == NULL)
251 break;
6b861048 252 setsender(p);
34d37b7d 253 if (Errors == 0)
6b861048
EA
254 {
255 message("250", "Sender ok");
256 hasmail = TRUE;
257 }
e6f08ab1
EA
258 else if (InChild)
259 finis();
6b861048
EA
260 break;
261
4a4ebe09 262 case CMDRCPT: /* rcpt -- designate recipient */
2e3062fe 263 SmtpPhase = "RCPT";
4d344ce6 264 setproctitle("%s %s: %s", CurEnv->e_id,
57c97d4a 265 CurHostName, inp);
d344c0b7 266 if (setjmp(TopFrame) > 0)
9bfb75c1
EA
267 {
268 CurEnv->e_flags &= ~EF_FATALERRS;
d344c0b7 269 break;
9bfb75c1 270 }
d344c0b7 271 QuickAbort = TRUE;
6b861048
EA
272 p = skipword(p, "to");
273 if (p == NULL)
274 break;
abae7b2d
EA
275 a = sendto(p, 1, (ADDRESS *) NULL, 0);
276# ifdef DEBUG
277 if (Debug > 1)
278 printaddr(a, TRUE);
279# endif DEBUG
d344c0b7
EA
280 if (Errors != 0)
281 break;
282
283 /* no errors during parsing, but might be a duplicate */
284 CurEnv->e_to = p;
285 if (!bitset(QBADADDR, a->q_flags))
286 message("250", "Recipient ok");
287 else
6b861048 288 {
d344c0b7
EA
289 /* punt -- should keep message in ADDRESS.... */
290 message("550", "Addressee unknown");
6b861048 291 }
d344c0b7 292 CurEnv->e_to = NULL;
6b861048
EA
293 break;
294
295 case CMDDATA: /* data -- text of mail */
2e3062fe 296 SmtpPhase = "DATA";
6b861048 297 if (!hasmail)
4a4ebe09 298 {
6b861048 299 message("503", "Need MAIL command");
4a4ebe09
EA
300 break;
301 }
2e3062fe 302 else if (CurEnv->e_nrcpts <= 0)
6b861048 303 {
4a4ebe09
EA
304 message("503", "Need RCPT (recipient)");
305 break;
6b861048 306 }
4a4ebe09
EA
307
308 /* collect the text of the message */
2e3062fe 309 SmtpPhase = "collect";
4d344ce6 310 setproctitle("%s %s: %s", CurEnv->e_id,
57c97d4a 311 CurHostName, inp);
4a4ebe09
EA
312 collect(TRUE);
313 if (Errors != 0)
314 break;
315
8eedb496
EA
316 /*
317 ** Arrange to send to everyone.
318 ** If sending to multiple people, mail back
319 ** errors rather than reporting directly.
320 ** In any case, don't mail back errors for
321 ** anything that has happened up to
322 ** now (the other end will do this).
bcf74f25
EA
323 ** Truncate our transcript -- the mail has gotten
324 ** to us successfully, and if we have
325 ** to mail this back, it will be easier
326 ** on the reader.
8eedb496
EA
327 ** Then send to everyone.
328 ** Finally give a reply code. If an error has
329 ** already been given, don't mail a
330 ** message back.
e6f08ab1 331 ** We goose error returns by clearing error bit.
8eedb496
EA
332 */
333
2e3062fe
EA
334 SmtpPhase = "delivery";
335 if (CurEnv->e_nrcpts != 1)
7338e3d4
EA
336 {
337 HoldErrs = TRUE;
83f674d8 338 ErrorMode = EM_MAIL;
7338e3d4 339 }
e6f08ab1 340 CurEnv->e_flags &= ~EF_FATALERRS;
bcf74f25 341 CurEnv->e_xfp = freopen(queuename(CurEnv, 'x'), "w", CurEnv->e_xfp);
4a4ebe09
EA
342
343 /* send to all recipients */
f7e74083 344 sendall(CurEnv, SM_DEFAULT);
2654b031 345 CurEnv->e_to = NULL;
4a4ebe09 346
666b39ad
EA
347 /* save statistics */
348 markstats(CurEnv, (ADDRESS *) NULL);
349
8eedb496
EA
350 /* issue success if appropriate and reset */
351 if (Errors == 0 || HoldErrs)
75f95954 352 message("250", "Ok");
8eedb496 353 else
e6f08ab1
EA
354 CurEnv->e_flags &= ~EF_FATALERRS;
355
356 /* if in a child, pop back to our parent */
357 if (InChild)
358 finis();
2e3062fe
EA
359
360 /* clean up a bit */
361 hasmail = 0;
2e3062fe
EA
362 dropenvelope(CurEnv);
363 CurEnv = newenvelope(CurEnv);
364 CurEnv->e_flags = BlankEnvelope.e_flags;
6b861048
EA
365 break;
366
367 case CMDRSET: /* rset -- reset state */
368 message("250", "Reset state");
e6f08ab1
EA
369 if (InChild)
370 finis();
371 break;
6b861048
EA
372
373 case CMDVRFY: /* vrfy -- verify address */
e6f08ab1
EA
374 if (runinchild("SMTP-VRFY") > 0)
375 break;
57c97d4a 376 setproctitle("%s: %s", CurHostName, inp);
abae7b2d 377 paddrtree(a);
6b861048
EA
378 break;
379
380 case CMDHELP: /* help -- give user info */
34d37b7d
EA
381 if (*p == '\0')
382 p = "SMTP";
383 help(p);
6b861048
EA
384 break;
385
386 case CMDNOOP: /* noop -- do nothing */
387 message("200", "OK");
388 break;
389
390 case CMDQUIT: /* quit -- leave mail */
57c97d4a 391 message("221", "%s closing connection", MyHostName);
e6f08ab1
EA
392 if (InChild)
393 ExitStat = EX_QUIT;
6b861048
EA
394 finis();
395
e8ad767d
EA
396 case CMDVERB: /* set verbose mode */
397 Verbose = TRUE;
28852808 398 SendMode = SM_DELIVER;
e8ad767d
EA
399 message("200", "Verbose mode");
400 break;
401
8fe4fb9b 402 case CMDONEX: /* doing one transaction only */
7338e3d4 403 OneXact = TRUE;
8fe4fb9b
EA
404 message("200", "Only one transaction");
405 break;
406
ab4889ea 407# ifdef SMTPDEBUG
e6f08ab1 408 case CMDDBGQSHOW: /* show queues */
2654b031
EA
409 printf("Send Queue=");
410 printaddr(CurEnv->e_sendqueue, TRUE);
d4f42161 411 break;
09eb49d8
EA
412
413 case CMDDBGDEBUG: /* set debug mode */
9678c96d
EA
414 tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
415 tTflag(p);
416 message("200", "Debug set");
09eb49d8
EA
417 break;
418
f87fbdc2 419# ifdef WIZ
e673aad7 420 case CMDDBGKILL: /* kill the parent */
e8ad767d
EA
421 if (!iswiz())
422 break;
e673aad7
EA
423 if (kill(MotherPid, SIGTERM) >= 0)
424 message("200", "Mother is dead");
425 else
426 message("500", "Can't kill Mom");
427 break;
e8ad767d
EA
428
429 case CMDDBGWIZ: /* become a wizard */
430 if (WizWord != NULL)
431 {
432 char seed[3];
433 extern char *crypt();
434
03388044 435 (void) strncpy(seed, WizWord, 2);
c50c55e6 436 if (strcmp(WizWord, crypt(p, seed)) == 0)
e8ad767d 437 {
c50c55e6
EA
438 IsWiz = TRUE;
439 message("200", "Please pass, oh mighty wizard");
e8ad767d
EA
440 break;
441 }
442 }
c50c55e6 443 message("500", "You are no wizard!");
e8ad767d 444 break;
f87fbdc2
EA
445
446# else WIZ
447 case CMDDBGWIZ: /* try to become a wizard */
448 message("500", "You wascal wabbit! Wandering wizards won't win!");
449 break;
450# endif WIZ
ab4889ea
MK
451# else /* not SMTPDEBUG */
452
453 case CMDDBGQSHOW: /* show queues */
454 case CMDDBGDEBUG: /* set debug mode */
455 case CMDDBGKILL: /* kill the parent */
456 case CMDDBGWIZ: /* become a wizard */
2e15a2d8
MK
457# ifdef LOG
458 if (RealHostName != NULL && LogLevel > 0)
ab4889ea
MK
459 syslog(LOG_NOTICE,
460 "\"%s\" command from %s (%s)\n",
461 c->cmdname, RealHostName,
462 inet_ntoa(RealHostAddr.sin_addr));
2e15a2d8 463# endif
ab4889ea
MK
464 /* FALL THROUGH */
465# endif /* SMTPDEBUG */
d4f42161 466
6b861048
EA
467 case CMDERROR: /* unknown command */
468 message("500", "Command unrecognized");
469 break;
470
471 default:
ab4889ea 472 errno = 0;
6b861048
EA
473 syserr("smtp: unknown code %d", c->cmdcode);
474 break;
475 }
476 }
477}
478\f/*
479** SKIPWORD -- skip a fixed word.
480**
481** Parameters:
482** p -- place to start looking.
483** w -- word to skip.
484**
485** Returns:
486** p following w.
487** NULL on error.
488**
489** Side Effects:
490** clobbers the p data area.
491*/
492
493static char *
494skipword(p, w)
495 register char *p;
496 char *w;
497{
498 register char *q;
6b861048
EA
499
500 /* find beginning of word */
501 while (isspace(*p))
502 p++;
503 q = p;
504
505 /* find end of word */
506 while (*p != '\0' && *p != ':' && !isspace(*p))
507 p++;
508 while (isspace(*p))
509 *p++ = '\0';
510 if (*p != ':')
511 {
512 syntax:
513 message("501", "Syntax error");
514 Errors++;
515 return (NULL);
516 }
517 *p++ = '\0';
518 while (isspace(*p))
519 p++;
520
521 /* see if the input word matches desired word */
ed73ef1d 522 if (strcasecmp(q, w))
6b861048
EA
523 goto syntax;
524
525 return (p);
526}
34d37b7d
EA
527\f/*
528** HELP -- implement the HELP command.
529**
530** Parameters:
531** topic -- the topic we want help for.
532**
533** Returns:
534** none.
535**
536** Side Effects:
537** outputs the help file to message output.
538*/
539
540help(topic)
541 char *topic;
542{
543 register FILE *hf;
544 int len;
545 char buf[MAXLINE];
546 bool noinfo;
547
c1e24818 548 if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
34d37b7d
EA
549 {
550 /* no help */
1b932f66 551 errno = 0;
34d37b7d
EA
552 message("502", "HELP not implemented");
553 return;
554 }
555
556 len = strlen(topic);
557 makelower(topic);
558 noinfo = TRUE;
559
560 while (fgets(buf, sizeof buf, hf) != NULL)
561 {
562 if (strncmp(buf, topic, len) == 0)
563 {
564 register char *p;
565
566 p = index(buf, '\t');
567 if (p == NULL)
568 p = buf;
569 else
570 p++;
571 fixcrlf(p, TRUE);
572 message("214-", p);
573 noinfo = FALSE;
574 }
575 }
576
577 if (noinfo)
578 message("504", "HELP topic unknown");
579 else
580 message("214", "End of HELP info");
ed45aae1 581 (void) fclose(hf);
34d37b7d 582}
abae7b2d 583\f/*
e8ad767d
EA
584** ISWIZ -- tell us if we are a wizard
585**
586** If not, print a nasty message.
587**
588** Parameters:
589** none.
590**
591** Returns:
592** TRUE if we are a wizard.
593** FALSE if we are not a wizard.
594**
595** Side Effects:
596** Prints a 500 exit stat if we are not a wizard.
597*/
598
f87fbdc2 599#ifdef WIZ
d6de42cf 600
e8ad767d
EA
601bool
602iswiz()
603{
604 if (!IsWiz)
605 message("500", "Mere mortals musn't mutter that mantra");
606 return (IsWiz);
607}
d6de42cf 608
f87fbdc2 609#endif WIZ
e8ad767d 610\f/*
e6f08ab1
EA
611** RUNINCHILD -- return twice -- once in the child, then in the parent again
612**
613** Parameters:
614** label -- a string used in error messages
615**
616** Returns:
617** zero in the child
618** one in the parent
619**
620** Side Effects:
621** none.
622*/
623
624runinchild(label)
625 char *label;
626{
627 int childpid;
628
c1a66acf 629 if (!OneXact)
e6f08ab1 630 {
c1a66acf
EA
631 childpid = dofork();
632 if (childpid < 0)
633 {
634 syserr("%s: cannot fork", label);
635 return (1);
636 }
637 if (childpid > 0)
638 {
639 auto int st;
e6f08ab1 640
c1a66acf
EA
641 /* parent -- wait for child to complete */
642 st = waitfor(childpid);
643 if (st == -1)
644 syserr("%s: lost child", label);
e6f08ab1 645
c1a66acf
EA
646 /* if we exited on a QUIT command, complete the process */
647 if (st == (EX_QUIT << 8))
648 finis();
e6f08ab1 649
c1a66acf
EA
650 return (1);
651 }
652 else
653 {
654 /* child */
655 InChild = TRUE;
57c97d4a 656 QuickAbort = FALSE;
66ba9834 657 clearenvelope(CurEnv, FALSE);
c1a66acf 658 }
e6f08ab1 659 }
55f0da62 660
c1a66acf
EA
661 /* open alias database */
662 initaliases(AliasFile, FALSE);
663
664 return (0);
e6f08ab1
EA
665}
666\f/*
abae7b2d
EA
667** PADDRTREE -- print address tree
668**
669** Used by VRFY and EXPD to dump the tree of addresses produced.
670**
671** Parameters:
672** a -- address of root.
673**
674** Returns:
675** none.
676**
677** Side Effects:
678** prints the tree in a nice order.
679*/
680
681paddrtree(a)
682 register ADDRESS *a;
683{
684 static ADDRESS *prev;
685 static int lev;
686
687 if (a == NULL)
688 return;
689 lev++;
690 if (!bitset(QDONTSEND, a->q_flags))
691 {
692 if (prev != NULL)
693 {
694 if (prev->q_fullname != NULL)
695 message("250-", "%s <%s>", prev->q_fullname, prev->q_paddr);
696 else
697 message("250-", "<%s>", prev->q_paddr);
698 }
699 prev = a;
700 }
701 paddrtree(a->q_child);
702 paddrtree(a->q_sibling);
703 if (--lev <= 0)
704 {
705 if (prev != NULL)
706 {
707 /* last one */
708 if (prev->q_fullname != NULL)
709 message("250", "%s <%s>", prev->q_fullname, prev->q_paddr);
710 else
711 message("250", "<%s>", prev->q_paddr);
712 prev = NULL;
713 }
714 else
715 message("550", "User unknown");
716 }
717}
884a20cb
EA
718
719# endif SMTP