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