source: trunk/third/sendmail/src/srvrsmtp.c @ 12554

Revision 12554, 33.6 KB checked in by danw, 26 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r12553, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
3 * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
4 * Copyright (c) 1988, 1993
5 *      The Regents of the University of California.  All rights reserved.
6 *
7 * By using this file, you agree to the terms and conditions set
8 * forth in the LICENSE file which can be found at the top level of
9 * the sendmail distribution.
10 *
11 */
12
13# include "sendmail.h"
14
15#ifndef lint
16#if SMTP
17static char sccsid[] = "@(#)srvrsmtp.c  8.187 (Berkeley) 10/23/1998 (with SMTP)";
18#else
19static char sccsid[] = "@(#)srvrsmtp.c  8.187 (Berkeley) 10/23/1998 (without SMTP)";
20#endif
21#endif /* not lint */
22
23# include <errno.h>
24
25# if SMTP
26
27/*
28**  SMTP -- run the SMTP protocol.
29**
30**      Parameters:
31**              nullserver -- if non-NULL, rejection message for
32**                      all SMTP commands.
33**              e -- the envelope.
34**
35**      Returns:
36**              never.
37**
38**      Side Effects:
39**              Reads commands from the input channel and processes
40**                      them.
41*/
42
43struct cmd
44{
45        char    *cmdname;       /* command name */
46        int     cmdcode;        /* internal code, see below */
47};
48
49/* values for cmdcode */
50# define CMDERROR       0       /* bad command */
51# define CMDMAIL        1       /* mail -- designate sender */
52# define CMDRCPT        2       /* rcpt -- designate recipient */
53# define CMDDATA        3       /* data -- send message text */
54# define CMDRSET        4       /* rset -- reset state */
55# define CMDVRFY        5       /* vrfy -- verify address */
56# define CMDEXPN        6       /* expn -- expand address */
57# define CMDNOOP        7       /* noop -- do nothing */
58# define CMDQUIT        8       /* quit -- close connection and die */
59# define CMDHELO        9       /* helo -- be polite */
60# define CMDHELP        10      /* help -- give usage info */
61# define CMDEHLO        11      /* ehlo -- extended helo (RFC 1425) */
62# define CMDETRN        12      /* etrn -- flush queue */
63/* non-standard commands */
64# define CMDONEX        16      /* onex -- sending one transaction only */
65# define CMDVERB        17      /* verb -- go into verbose mode */
66# define CMDXUSR        18      /* xusr -- initial (user) submission */
67/* use this to catch and log "door handle" attempts on your system */
68# define CMDLOGBOGUS    23      /* bogus command that should be logged */
69/* debugging-only commands, only enabled if SMTPDEBUG is defined */
70# define CMDDBGQSHOW    24      /* showq -- show send queue */
71# define CMDDBGDEBUG    25      /* debug -- set debug mode */
72
73static struct cmd       CmdTab[] =
74{
75        { "mail",       CMDMAIL         },
76        { "rcpt",       CMDRCPT         },
77        { "data",       CMDDATA         },
78        { "rset",       CMDRSET         },
79        { "vrfy",       CMDVRFY         },
80        { "expn",       CMDEXPN         },
81        { "help",       CMDHELP         },
82        { "noop",       CMDNOOP         },
83        { "quit",       CMDQUIT         },
84        { "helo",       CMDHELO         },
85        { "ehlo",       CMDEHLO         },
86        { "etrn",       CMDETRN         },
87        { "verb",       CMDVERB         },
88        { "onex",       CMDONEX         },
89        { "xusr",       CMDXUSR         },
90    /* remaining commands are here only to trap and log attempts to use them */
91        { "showq",      CMDDBGQSHOW     },
92        { "debug",      CMDDBGDEBUG     },
93        { "wiz",        CMDLOGBOGUS     },
94
95        { NULL,         CMDERROR        }
96};
97
98bool    OneXact = FALSE;                /* one xaction only this run */
99char    *CurSmtpClient;                 /* who's at the other end of channel */
100
101static char     *skipword __P((char *volatile, char *));
102
103
104#define MAXBADCOMMANDS  25      /* maximum number of bad commands */
105#define MAXNOOPCOMMANDS 20      /* max "noise" commands before slowdown */
106#define MAXHELOCOMMANDS 3       /* max HELO/EHLO commands before slowdown */
107#define MAXVRFYCOMMANDS 6       /* max VRFY/EXPN commands before slowdown */
108#define MAXETRNCOMMANDS 8       /* max ETRN commands before slowdown */
109
110void
111smtp(nullserver, e)
112        char *nullserver;
113        register ENVELOPE *volatile e;
114{
115        register char *volatile p;
116        register struct cmd *c;
117        char *cmd;
118        auto ADDRESS *vrfyqueue;
119        ADDRESS *a;
120        volatile bool gotmail;          /* mail command received */
121        volatile bool gothello;         /* helo command received */
122        bool vrfy;                      /* set if this is a vrfy command */
123        char *volatile protocol;        /* sending protocol */
124        char *volatile sendinghost;     /* sending hostname */
125        char *volatile peerhostname;    /* name of SMTP peer or "localhost" */
126        auto char *delimptr;
127        char *id;
128        volatile int nrcpts = 0;        /* number of RCPT commands */
129        bool doublequeue;
130        volatile bool discard;
131        volatile int badcommands = 0;   /* count of bad commands */
132        volatile int nverifies = 0;     /* count of VRFY/EXPN commands */
133        volatile int n_etrn = 0;        /* count of ETRN commands */
134        volatile int n_noop = 0;        /* count of NOOP/VERB/ONEX etc cmds */
135        volatile int n_helo = 0;        /* count of HELO/EHLO commands */
136        bool ok;
137        volatile int lognullconnection = TRUE;
138        register char *q;
139        QUEUE_CHAR *new;
140        char inp[MAXLINE];
141        char cmdbuf[MAXLINE];
142        extern ENVELOPE BlankEnvelope;
143        extern void help __P((char *));
144        extern void settime __P((ENVELOPE *));
145        extern bool enoughdiskspace __P((long));
146        extern int runinchild __P((char *, ENVELOPE *));
147        extern void checksmtpattack __P((volatile int *, int, char *, ENVELOPE *));
148
149        if (fileno(OutChannel) != fileno(stdout))
150        {
151                /* arrange for debugging output to go to remote host */
152                (void) dup2(fileno(OutChannel), fileno(stdout));
153        }
154        settime(e);
155        peerhostname = RealHostName;
156        if (peerhostname == NULL)
157                peerhostname = "localhost";
158        CurHostName = peerhostname;
159        CurSmtpClient = macvalue('_', e);
160        if (CurSmtpClient == NULL)
161                CurSmtpClient = CurHostName;
162
163        /* check_relay may have set discard bit, save for later */
164        discard = bitset(EF_DISCARD, e->e_flags);
165
166        sm_setproctitle(TRUE, "server %s startup", CurSmtpClient);
167#if DAEMON
168        if (LogLevel > 11)
169        {
170                /* log connection information */
171                sm_syslog(LOG_INFO, NOQID,
172                        "SMTP connect from %.100s (%.100s)",
173                        CurSmtpClient, anynet_ntoa(&RealHostAddr));
174        }
175#endif
176
177        /* output the first line, inserting "ESMTP" as second word */
178        expand(SmtpGreeting, inp, sizeof inp, e);
179        p = strchr(inp, '\n');
180        if (p != NULL)
181                *p++ = '\0';
182        id = strchr(inp, ' ');
183        if (id == NULL)
184                id = &inp[strlen(inp)];
185        cmd = p == NULL ? "220 %.*s ESMTP%s" : "220-%.*s ESMTP%s";
186        message(cmd, id - inp, inp, id);
187
188        /* output remaining lines */
189        while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL)
190        {
191                *p++ = '\0';
192                if (isascii(*id) && isspace(*id))
193                        id++;
194                message("220-%s", id);
195        }
196        if (id != NULL)
197        {
198                if (isascii(*id) && isspace(*id))
199                        id++;
200                message("220 %s", id);
201        }
202
203        protocol = NULL;
204        sendinghost = macvalue('s', e);
205        gothello = FALSE;
206        gotmail = FALSE;
207        for (;;)
208        {
209                /* arrange for backout */
210                (void) setjmp(TopFrame);
211                QuickAbort = FALSE;
212                HoldErrs = FALSE;
213                SuprErrs = FALSE;
214                LogUsrErrs = FALSE;
215                OnlyOneError = TRUE;
216                e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
217
218                /* setup for the read */
219                e->e_to = NULL;
220                Errors = 0;
221                (void) fflush(stdout);
222
223                /* read the input line */
224                SmtpPhase = "server cmd read";
225                sm_setproctitle(TRUE, "server %s cmd read", CurSmtpClient);
226                p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
227                                SmtpPhase);
228
229                /* handle errors */
230                if (p == NULL)
231                {
232                        /* end of file, just die */
233                        disconnect(1, e);
234                        message("421 %s Lost input channel from %s",
235                                MyHostName, CurSmtpClient);
236                        if (LogLevel > (gotmail ? 1 : 19))
237                                sm_syslog(LOG_NOTICE, e->e_id,
238                                        "lost input channel from %.100s",
239                                        CurSmtpClient);
240                        if (lognullconnection && LogLevel > 5)
241                                sm_syslog(LOG_INFO, NULL,
242                                "Null connection from %.100s",
243                                CurSmtpClient);
244
245                        /*
246                        **  If have not accepted mail (DATA), do not bounce
247                        **  bad addresses back to sender.
248                        */
249                        if (bitset(EF_CLRQUEUE, e->e_flags))
250                                e->e_sendqueue = NULL;
251
252                        if (InChild)
253                                ExitStat = EX_QUIT;
254                        finis(TRUE, ExitStat);
255                }
256
257                /* clean up end of line */
258                fixcrlf(inp, TRUE);
259
260                /* echo command to transcript */
261                if (e->e_xfp != NULL)
262                        fprintf(e->e_xfp, "<<< %s\n", inp);
263
264                if (LogLevel >= 15)
265                        sm_syslog(LOG_INFO, e->e_id,
266                                "<-- %s",
267                                inp);
268
269                if (e->e_id == NULL)
270                        sm_setproctitle(TRUE, "%s: %.80s", CurSmtpClient, inp);
271                else
272                        sm_setproctitle(TRUE, "%s %s: %.80s", e->e_id, CurSmtpClient, inp);
273
274                /* break off command */
275                for (p = inp; isascii(*p) && isspace(*p); p++)
276                        continue;
277                cmd = cmdbuf;
278                while (*p != '\0' &&
279                       !(isascii(*p) && isspace(*p)) &&
280                       cmd < &cmdbuf[sizeof cmdbuf - 2])
281                        *cmd++ = *p++;
282                *cmd = '\0';
283
284                /* throw away leading whitespace */
285                while (isascii(*p) && isspace(*p))
286                        p++;
287
288                /* decode command */
289                for (c = CmdTab; c->cmdname != NULL; c++)
290                {
291                        if (!strcasecmp(c->cmdname, cmdbuf))
292                                break;
293                }
294
295                /* reset errors */
296                errno = 0;
297
298                /*
299                **  Process command.
300                **
301                **      If we are running as a null server, return 550
302                **      to everything.
303                */
304
305                if (nullserver != NULL)
306                {
307                        switch (c->cmdcode)
308                        {
309                          case CMDQUIT:
310                          case CMDHELO:
311                          case CMDEHLO:
312                          case CMDNOOP:
313                                /* process normally */
314                                break;
315
316                          default:
317                                if (++badcommands > MAXBADCOMMANDS)
318                                        sleep(1);
319                                usrerr("550 %s", nullserver);
320                                continue;
321                        }
322                }
323
324                /* non-null server */
325                switch (c->cmdcode)
326                {
327                  case CMDMAIL:
328                  case CMDEXPN:
329                  case CMDVRFY:
330                  case CMDETRN:
331                        lognullconnection = FALSE;
332                }
333
334                switch (c->cmdcode)
335                {
336                  case CMDHELO:         /* hello -- introduce yourself */
337                  case CMDEHLO:         /* extended hello */
338                        if (c->cmdcode == CMDEHLO)
339                        {
340                                protocol = "ESMTP";
341                                SmtpPhase = "server EHLO";
342                        }
343                        else
344                        {
345                                protocol = "SMTP";
346                                SmtpPhase = "server HELO";
347                        }
348
349                        /* avoid denial-of-service */
350                        checksmtpattack(&n_helo, MAXHELOCOMMANDS, "HELO/EHLO", e);
351
352                        /* check for duplicate HELO/EHLO per RFC 1651 4.2 */
353                        if (gothello)
354                        {
355                                usrerr("503 %s Duplicate HELO/EHLO",
356                                        MyHostName);
357                                break;
358                        }
359
360                        /* check for valid domain name (re 1123 5.2.5) */
361                        if (*p == '\0' && !AllowBogusHELO)
362                        {
363                                usrerr("501 %s requires domain address",
364                                        cmdbuf);
365                                break;
366                        }
367
368                        /* check for long domain name (hides Received: info) */
369                        if (strlen(p) > MAXNAME)
370                        {
371                                usrerr("501 Invalid domain name");
372                                break;
373                        }
374
375                        for (q = p; *q != '\0'; q++)
376                        {
377                                if (!isascii(*q))
378                                        break;
379                                if (isalnum(*q))
380                                        continue;
381                                if (isspace(*q))
382                                {
383                                        *q = '\0';
384                                        break;
385                                }
386                                if (strchr("[].-_#", *q) == NULL)
387                                        break;
388                        }
389                        if (*q == '\0')
390                        {
391                                q = "pleased to meet you";
392                                sendinghost = newstr(p);
393                        }
394                        else if (!AllowBogusHELO)
395                        {
396                                usrerr("501 Invalid domain name");
397                                break;
398                        }
399                        else
400                        {
401                                q = "accepting invalid domain name";
402                        }
403
404                        gothello = TRUE;
405                       
406                        /* print HELO response message */
407                        if (c->cmdcode != CMDEHLO || nullserver != NULL)
408                        {
409                                message("250 %s Hello %s, %s",
410                                        MyHostName, CurSmtpClient, q);
411                                break;
412                        }
413
414                        message("250-%s Hello %s, %s",
415                                MyHostName, CurSmtpClient, q);
416
417                        /* print EHLO features list */
418                        if (!bitset(PRIV_NOEXPN, PrivacyFlags))
419                        {
420                                message("250-EXPN");
421                                if (!bitset(PRIV_NOVERB, PrivacyFlags))
422                                        message("250-VERB");
423                        }
424#if MIME8TO7
425                        message("250-8BITMIME");
426#endif
427                        if (MaxMessageSize > 0)
428                                message("250-SIZE %ld", MaxMessageSize);
429                        else
430                                message("250-SIZE");
431#if DSN
432                        if (SendMIMEErrors)
433                                message("250-DSN");
434#endif
435                        message("250-ONEX");
436                        if (!bitset(PRIV_NOETRN, PrivacyFlags))
437                                message("250-ETRN");
438                        message("250-XUSR");
439                        message("250 HELP");
440                        break;
441
442                  case CMDMAIL:         /* mail -- designate sender */
443                        SmtpPhase = "server MAIL";
444
445                        /* check for validity of this command */
446                        if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
447                        {
448                                usrerr("503 Polite people say HELO first");
449                                break;
450                        }
451                        if (gotmail)
452                        {
453                                usrerr("503 Sender already specified");
454                                break;
455                        }
456                        if (InChild)
457                        {
458                                errno = 0;
459                                syserr("503 Nested MAIL command: MAIL %s", p);
460                                finis(TRUE, ExitStat);
461                        }
462
463                        /* make sure we know who the sending host is */
464                        if (sendinghost == NULL)
465                                sendinghost = peerhostname;
466
467                        p = skipword(p, "from");
468                        if (p == NULL)
469                                break;
470
471                        /* fork a subprocess to process this command */
472                        if (runinchild("SMTP-MAIL", e) > 0)
473                                break;
474                        if (Errors > 0)
475                                goto undo_subproc_no_pm;
476                        if (!gothello)
477                        {
478                                auth_warning(e,
479                                        "%s didn't use HELO protocol",
480                                        CurSmtpClient);
481                        }
482#ifdef PICKY_HELO_CHECK
483                        if (strcasecmp(sendinghost, peerhostname) != 0 &&
484                            (strcasecmp(peerhostname, "localhost") != 0 ||
485                             strcasecmp(sendinghost, MyHostName) != 0))
486                        {
487                                auth_warning(e, "Host %s claimed to be %s",
488                                        CurSmtpClient, sendinghost);
489                        }
490#endif
491
492                        if (protocol == NULL)
493                                protocol = "SMTP";
494                        define('r', protocol, e);
495                        define('s', sendinghost, e);
496                        initsys(e);
497                        if (Errors > 0)
498                                goto undo_subproc_no_pm;
499                        nrcpts = 0;
500                        e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE;
501                        sm_setproctitle(TRUE, "%s %s: %.80s", e->e_id, CurSmtpClient, inp);
502
503                        /* child -- go do the processing */
504                        if (setjmp(TopFrame) > 0)
505                        {
506                                /* this failed -- undo work */
507 undo_subproc_no_pm:
508                                e->e_flags &= ~EF_PM_NOTIFY;
509 undo_subproc:
510                                if (InChild)
511                                {
512                                        QuickAbort = FALSE;
513                                        SuprErrs = TRUE;
514                                        e->e_flags &= ~EF_FATALERRS;
515                                        finis(TRUE, ExitStat);
516                                }
517                                break;
518                        }
519                        QuickAbort = TRUE;
520
521                        /* must parse sender first */
522                        delimptr = NULL;
523                        setsender(p, e, &delimptr, ' ', FALSE);
524                        if (delimptr != NULL && *delimptr != '\0')
525                                *delimptr++ = '\0';
526                        if (Errors > 0)
527                                goto undo_subproc_no_pm;
528
529                        /* do config file checking of the sender */
530                        if (rscheck("check_mail", p, NULL, e) != EX_OK ||
531                            Errors > 0)
532                                goto undo_subproc_no_pm;
533
534                        /* check for possible spoofing */
535                        if (RealUid != 0 && OpMode == MD_SMTP &&
536                            !wordinclass(RealUserName, 't') &&
537                            !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) &&
538                            strcmp(e->e_from.q_user, RealUserName) != 0)
539                        {
540                                auth_warning(e, "%s owned process doing -bs",
541                                        RealUserName);
542                        }
543
544                        /* now parse ESMTP arguments */
545                        e->e_msgsize = 0;
546                        p = delimptr;
547                        while (p != NULL && *p != '\0')
548                        {
549                                char *kp;
550                                char *vp = NULL;
551                                extern void mail_esmtp_args __P((char *, char *, ENVELOPE *));
552
553                                /* locate the beginning of the keyword */
554                                while (isascii(*p) && isspace(*p))
555                                        p++;
556                                if (*p == '\0')
557                                        break;
558                                kp = p;
559
560                                /* skip to the value portion */
561                                while ((isascii(*p) && isalnum(*p)) || *p == '-')
562                                        p++;
563                                if (*p == '=')
564                                {
565                                        *p++ = '\0';
566                                        vp = p;
567
568                                        /* skip to the end of the value */
569                                        while (*p != '\0' && *p != ' ' &&
570                                               !(isascii(*p) && iscntrl(*p)) &&
571                                               *p != '=')
572                                                p++;
573                                }
574
575                                if (*p != '\0')
576                                        *p++ = '\0';
577
578                                if (tTd(19, 1))
579                                        printf("MAIL: got arg %s=\"%s\"\n", kp,
580                                                vp == NULL ? "<null>" : vp);
581
582                                mail_esmtp_args(kp, vp, e);
583                                if (Errors > 0)
584                                        goto undo_subproc_no_pm;
585                        }
586                        if (Errors > 0)
587                                goto undo_subproc_no_pm;
588
589                        if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
590                        {
591                                usrerr("552 Message size exceeds fixed maximum message size (%ld)",
592                                        MaxMessageSize);
593                                goto undo_subproc_no_pm;
594                        }
595                               
596                        if (!enoughdiskspace(e->e_msgsize))
597                        {
598                                usrerr("452 Insufficient disk space; try again later");
599                                goto undo_subproc_no_pm;
600                        }
601                        if (Errors > 0)
602                                goto undo_subproc_no_pm;
603                        message("250 Sender ok");
604                        gotmail = TRUE;
605                        break;
606
607                  case CMDRCPT:         /* rcpt -- designate recipient */
608                        if (!gotmail)
609                        {
610                                usrerr("503 Need MAIL before RCPT");
611                                break;
612                        }
613                        SmtpPhase = "server RCPT";
614                        if (setjmp(TopFrame) > 0)
615                        {
616                                e->e_flags &= ~EF_FATALERRS;
617                                break;
618                        }
619                        QuickAbort = TRUE;
620                        LogUsrErrs = TRUE;
621
622                        /* limit flooding of our machine */
623                        if (MaxRcptPerMsg > 0 && nrcpts >= MaxRcptPerMsg)
624                        {
625                                usrerr("452 Too many recipients");
626                                break;
627                        }
628
629                        if (e->e_sendmode != SM_DELIVER)
630                                e->e_flags |= EF_VRFYONLY;
631
632                        p = skipword(p, "to");
633                        if (p == NULL)
634                                break;
635                        a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e);
636                        if (Errors > 0)
637                                break;
638                        if (a == NULL)
639                        {
640                                usrerr("501 Missing recipient");
641                                break;
642                        }
643
644                        if (delimptr != NULL && *delimptr != '\0')
645                                *delimptr++ = '\0';
646
647                        /* do config file checking of the recipient */
648                        if (rscheck("check_rcpt", p, NULL, e) != EX_OK ||
649                            Errors > 0)
650                                break;
651
652                        /* now parse ESMTP arguments */
653                        p = delimptr;
654                        while (p != NULL && *p != '\0')
655                        {
656                                char *kp;
657                                char *vp = NULL;
658                                extern void rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *));
659
660                                /* locate the beginning of the keyword */
661                                while (isascii(*p) && isspace(*p))
662                                        p++;
663                                if (*p == '\0')
664                                        break;
665                                kp = p;
666
667                                /* skip to the value portion */
668                                while ((isascii(*p) && isalnum(*p)) || *p == '-')
669                                        p++;
670                                if (*p == '=')
671                                {
672                                        *p++ = '\0';
673                                        vp = p;
674
675                                        /* skip to the end of the value */
676                                        while (*p != '\0' && *p != ' ' &&
677                                               !(isascii(*p) && iscntrl(*p)) &&
678                                               *p != '=')
679                                                p++;
680                                }
681
682                                if (*p != '\0')
683                                        *p++ = '\0';
684
685                                if (tTd(19, 1))
686                                        printf("RCPT: got arg %s=\"%s\"\n", kp,
687                                                vp == NULL ? "<null>" : vp);
688
689                                rcpt_esmtp_args(a, kp, vp, e);
690                                if (Errors > 0)
691                                        break;
692                        }
693                        if (Errors > 0)
694                                break;
695
696                        /* save in recipient list after ESMTP mods */
697                        a = recipient(a, &e->e_sendqueue, 0, e);
698                        if (Errors > 0)
699                                break;
700
701                        /* no errors during parsing, but might be a duplicate */
702                        e->e_to = a->q_paddr;
703                        if (!bitset(QBADADDR, a->q_flags))
704                        {
705                                message("250 Recipient ok%s",
706                                        bitset(QQUEUEUP, a->q_flags) ?
707                                                " (will queue)" : "");
708                                nrcpts++;
709                        }
710                        else
711                        {
712                                /* punt -- should keep message in ADDRESS.... */
713                                usrerr("550 Addressee unknown");
714                        }
715                        break;
716
717                  case CMDDATA:         /* data -- text of mail */
718                        SmtpPhase = "server DATA";
719                        if (!gotmail)
720                        {
721                                usrerr("503 Need MAIL command");
722                                break;
723                        }
724                        else if (nrcpts <= 0)
725                        {
726                                usrerr("503 Need RCPT (recipient)");
727                                break;
728                        }
729
730                        /* put back discard bit */
731                        if (discard)
732                                e->e_flags |= EF_DISCARD;
733
734                        /* check to see if we need to re-expand aliases */
735                        /* also reset QBADADDR on already-diagnosted addrs */
736                        doublequeue = FALSE;
737                        for (a = e->e_sendqueue; a != NULL; a = a->q_next)
738                        {
739                                if (bitset(QVERIFIED, a->q_flags) &&
740                                    !bitset(EF_DISCARD, e->e_flags))
741                                {
742                                        /* need to re-expand aliases */
743                                        doublequeue = TRUE;
744                                }
745                                if (bitset(QBADADDR, a->q_flags))
746                                {
747                                        /* make this "go away" */
748                                        a->q_flags |= QDONTSEND;
749                                        a->q_flags &= ~QBADADDR;
750                                }
751                        }
752
753                        /* collect the text of the message */
754                        SmtpPhase = "collect";
755                        buffer_errors();
756                        collect(InChannel, TRUE, NULL, e);
757                        if (Errors > 0)
758                        {
759                                flush_errors(TRUE);
760                                buffer_errors();
761                                goto abortmessage;
762                        }
763
764                        /* make sure we actually do delivery */
765                        e->e_flags &= ~EF_CLRQUEUE;
766
767                        /* from now on, we have to operate silently */
768                        buffer_errors();
769                        e->e_errormode = EM_MAIL;
770
771                        /*
772                        **  Arrange to send to everyone.
773                        **      If sending to multiple people, mail back
774                        **              errors rather than reporting directly.
775                        **      In any case, don't mail back errors for
776                        **              anything that has happened up to
777                        **              now (the other end will do this).
778                        **      Truncate our transcript -- the mail has gotten
779                        **              to us successfully, and if we have
780                        **              to mail this back, it will be easier
781                        **              on the reader.
782                        **      Then send to everyone.
783                        **      Finally give a reply code.  If an error has
784                        **              already been given, don't mail a
785                        **              message back.
786                        **      We goose error returns by clearing error bit.
787                        */
788
789                        SmtpPhase = "delivery";
790                        e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
791                        id = e->e_id;
792
793                        if (doublequeue)
794                        {
795                                /* make sure it is in the queue */
796                                queueup(e, FALSE);
797                        }
798                        else
799                        {
800                                /* send to all recipients */
801                                sendall(e, SM_DEFAULT);
802                        }
803                        e->e_to = NULL;
804
805                        /* issue success message */
806                        message("250 %s Message accepted for delivery", id);
807
808                        /* if we just queued, poke it */
809                        if (doublequeue &&
810                            e->e_sendmode != SM_QUEUE &&
811                            e->e_sendmode != SM_DEFER)
812                        {
813                                CurrentLA = getla();
814
815                                if (!shouldqueue(e->e_msgpriority, e->e_ctime))
816                                {
817                                        unlockqueue(e);
818                                        (void) dowork(id, TRUE, TRUE, e);
819                                }
820                        }
821
822  abortmessage:
823                        /* if in a child, pop back to our parent */
824                        if (InChild)
825                                finis(TRUE, ExitStat);
826
827                        /* clean up a bit */
828                        gotmail = FALSE;
829                        dropenvelope(e, TRUE);
830                        CurEnv = e = newenvelope(e, CurEnv);
831                        e->e_flags = BlankEnvelope.e_flags;
832                        break;
833
834                  case CMDRSET:         /* rset -- reset state */
835                        if (tTd(94, 100))
836                                message("451 Test failure");
837                        else
838                                message("250 Reset state");
839
840                        /* arrange to ignore any current send list */
841                        e->e_sendqueue = NULL;
842                        e->e_flags |= EF_CLRQUEUE;
843                        if (InChild)
844                                finis(TRUE, ExitStat);
845
846                        /* clean up a bit */
847                        gotmail = FALSE;
848                        SuprErrs = TRUE;
849                        dropenvelope(e, TRUE);
850                        CurEnv = e = newenvelope(e, CurEnv);
851                        break;
852
853                  case CMDVRFY:         /* vrfy -- verify address */
854                  case CMDEXPN:         /* expn -- expand address */
855                        checksmtpattack(&nverifies, MAXVRFYCOMMANDS,
856                                c->cmdcode == CMDVRFY ? "VRFY" : "EXPN", e);
857                        vrfy = c->cmdcode == CMDVRFY;
858                        if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
859                                                PrivacyFlags))
860                        {
861                                if (vrfy)
862                                        message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
863                                else
864                                        message("502 Sorry, we do not allow this operation");
865                                if (LogLevel > 5)
866                                        sm_syslog(LOG_INFO, e->e_id,
867                                                "%.100s: %s [rejected]",
868                                                CurSmtpClient,
869                                                shortenstring(inp, MAXSHORTSTR));
870                                break;
871                        }
872                        else if (!gothello &&
873                                 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
874                                                PrivacyFlags))
875                        {
876                                usrerr("503 I demand that you introduce yourself first");
877                                break;
878                        }
879                        if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
880                                break;
881                        if (Errors > 0)
882                                goto undo_subproc;
883                        if (LogLevel > 5)
884                                sm_syslog(LOG_INFO, e->e_id,
885                                        "%.100s: %s",
886                                        CurSmtpClient,
887                                        shortenstring(inp, MAXSHORTSTR));
888                        if (setjmp(TopFrame) > 0)
889                                goto undo_subproc;
890                        QuickAbort = TRUE;
891                        vrfyqueue = NULL;
892                        if (vrfy)
893                                e->e_flags |= EF_VRFYONLY;
894                        while (*p != '\0' && isascii(*p) && isspace(*p))
895                                p++;
896                        if (*p == '\0')
897                        {
898                                usrerr("501 Argument required");
899                        }
900                        else
901                        {
902                                (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
903                        }
904                        if (Errors > 0)
905                                goto undo_subproc;
906                        if (vrfyqueue == NULL)
907                        {
908                                usrerr("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
909                        }
910                        while (vrfyqueue != NULL)
911                        {
912                                extern void printvrfyaddr __P((ADDRESS *, bool, bool));
913
914                                a = vrfyqueue;
915                                while ((a = a->q_next) != NULL &&
916                                       bitset(QDONTSEND|QBADADDR, a->q_flags))
917                                        continue;
918                                if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
919                                        printvrfyaddr(vrfyqueue, a == NULL, vrfy);
920                                vrfyqueue = vrfyqueue->q_next;
921                        }
922                        if (InChild)
923                                finis(TRUE, ExitStat);
924                        break;
925
926                  case CMDETRN:         /* etrn -- force queue flush */
927                        if (bitset(PRIV_NOETRN, PrivacyFlags))
928                        {
929                                message("502 Sorry, we do not allow this operation");
930                                if (LogLevel > 5)
931                                        sm_syslog(LOG_INFO, e->e_id,
932                                                "%.100s: %s [rejected]",
933                                                CurSmtpClient,
934                                                shortenstring(inp, MAXSHORTSTR));
935                                break;
936                        }
937
938                        if (strlen(p) <= 0)
939                        {
940                                usrerr("500 Parameter required");
941                                break;
942                        }
943
944                        /* crude way to avoid denial-of-service attacks */
945                        checksmtpattack(&n_etrn, MAXETRNCOMMANDS, "ETRN", e);
946
947                        if (LogLevel > 5)
948                                sm_syslog(LOG_INFO, e->e_id,
949                                        "%.100s: ETRN %s",
950                                        CurSmtpClient,
951                                        shortenstring(p, MAXSHORTSTR));
952
953                        id = p;
954                        if (*id == '@')
955                                id++;
956                        else
957                                *--id = '@';
958                                 
959                        if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
960                        {
961                                syserr("500 ETRN out of memory");
962                                break;
963                        }
964                        new->queue_match = id;
965                        new->queue_next = NULL;
966                        QueueLimitRecipient = new;
967                        ok = runqueue(TRUE, TRUE);
968                        free(QueueLimitRecipient);
969                        QueueLimitRecipient = NULL;
970                        if (ok && Errors == 0)
971                                message("250 Queuing for node %s started", p);
972                        break;
973
974                  case CMDHELP:         /* help -- give user info */
975                        help(p);
976                        break;
977
978                  case CMDNOOP:         /* noop -- do nothing */
979                        checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "NOOP", e);
980                        message("250 OK");
981                        break;
982
983                  case CMDQUIT:         /* quit -- leave mail */
984                        message("221 %s closing connection", MyHostName);
985
986doquit:
987                        /* arrange to ignore any current send list */
988                        e->e_sendqueue = NULL;
989
990                        /* avoid future 050 messages */
991                        disconnect(1, e);
992
993                        if (InChild)
994                                ExitStat = EX_QUIT;
995                        if (lognullconnection && LogLevel > 5)
996                                sm_syslog(LOG_INFO, NULL,
997                                        "Null connection from %.100s",
998                                        CurSmtpClient);
999                        finis(TRUE, ExitStat);
1000
1001                  case CMDVERB:         /* set verbose mode */
1002                        if (bitset(PRIV_NOEXPN, PrivacyFlags) ||
1003                            bitset(PRIV_NOVERB, PrivacyFlags))
1004                        {
1005                                /* this would give out the same info */
1006                                message("502 Verbose unavailable");
1007                                break;
1008                        }
1009                        checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "VERB", e);
1010                        Verbose = 1;
1011                        e->e_sendmode = SM_DELIVER;
1012                        message("250 Verbose mode");
1013                        break;
1014
1015                  case CMDONEX:         /* doing one transaction only */
1016                        checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "ONEX", e);
1017                        OneXact = TRUE;
1018                        message("250 Only one transaction");
1019                        break;
1020
1021                  case CMDXUSR:         /* initial (user) submission */
1022                        checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "XUSR", e);
1023                        UserSubmission = TRUE;
1024                        message("250 Initial submission");
1025                        break;
1026
1027# if SMTPDEBUG
1028                  case CMDDBGQSHOW:     /* show queues */
1029                        printf("Send Queue=");
1030                        printaddr(e->e_sendqueue, TRUE);
1031                        break;
1032
1033                  case CMDDBGDEBUG:     /* set debug mode */
1034                        tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
1035                        tTflag(p);
1036                        message("200 Debug set");
1037                        break;
1038
1039# else /* not SMTPDEBUG */
1040                  case CMDDBGQSHOW:     /* show queues */
1041                  case CMDDBGDEBUG:     /* set debug mode */
1042# endif /* SMTPDEBUG */
1043                  case CMDLOGBOGUS:     /* bogus command */
1044                        if (LogLevel > 0)
1045                                sm_syslog(LOG_CRIT, e->e_id,
1046                                    "\"%s\" command from %.100s (%.100s)",
1047                                    c->cmdname, CurSmtpClient,
1048                                    anynet_ntoa(&RealHostAddr));
1049                        /* FALL THROUGH */
1050
1051                  case CMDERROR:        /* unknown command */
1052                        if (++badcommands > MAXBADCOMMANDS)
1053                        {
1054                                message("421 %s Too many bad commands; closing connection",
1055                                        MyHostName);
1056                                goto doquit;
1057                        }
1058
1059                        usrerr("500 Command unrecognized: \"%s\"",
1060                                shortenstring(inp, MAXSHORTSTR));
1061                        break;
1062
1063                  default:
1064                        errno = 0;
1065                        syserr("500 smtp: unknown code %d", c->cmdcode);
1066                        break;
1067                }
1068        }
1069}
1070/*
1071**  CHECKSMTPATTACK -- check for denial-of-service attack by repetition
1072**
1073**      Parameters:
1074**              pcounter -- pointer to a counter for this command.
1075**              maxcount -- maximum value for this counter before we
1076**                      slow down.
1077**              cname -- command name for logging.
1078**              e -- the current envelope.
1079**
1080**      Returns:
1081**              none.
1082**
1083**      Side Effects:
1084**              Slows down if we seem to be under attack.
1085*/
1086
1087void
1088checksmtpattack(pcounter, maxcount, cname, e)
1089        volatile int *pcounter;
1090        int maxcount;
1091        char *cname;
1092        ENVELOPE *e;
1093{
1094        if (++(*pcounter) >= maxcount)
1095        {
1096                if (*pcounter == maxcount && LogLevel > 5)
1097                {
1098                        sm_syslog(LOG_INFO, e->e_id,
1099                                "%.100s: %.40s attack?",
1100                               CurSmtpClient, cname);
1101                }
1102                sleep(*pcounter / maxcount);
1103        }
1104}
1105/*
1106**  SKIPWORD -- skip a fixed word.
1107**
1108**      Parameters:
1109**              p -- place to start looking.
1110**              w -- word to skip.
1111**
1112**      Returns:
1113**              p following w.
1114**              NULL on error.
1115**
1116**      Side Effects:
1117**              clobbers the p data area.
1118*/
1119
1120static char *
1121skipword(p, w)
1122        register char *volatile p;
1123        char *w;
1124{
1125        register char *q;
1126        char *firstp = p;
1127
1128        /* find beginning of word */
1129        while (isascii(*p) && isspace(*p))
1130                p++;
1131        q = p;
1132
1133        /* find end of word */
1134        while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
1135                p++;
1136        while (isascii(*p) && isspace(*p))
1137                *p++ = '\0';
1138        if (*p != ':')
1139        {
1140          syntax:
1141                usrerr("501 Syntax error in parameters scanning \"%s\"",
1142                        shortenstring(firstp, MAXSHORTSTR));
1143                return (NULL);
1144        }
1145        *p++ = '\0';
1146        while (isascii(*p) && isspace(*p))
1147                p++;
1148
1149        if (*p == '\0')
1150                goto syntax;
1151
1152        /* see if the input word matches desired word */
1153        if (strcasecmp(q, w))
1154                goto syntax;
1155
1156        return (p);
1157}
1158/*
1159**  MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
1160**
1161**      Parameters:
1162**              kp -- the parameter key.
1163**              vp -- the value of that parameter.
1164**              e -- the envelope.
1165**
1166**      Returns:
1167**              none.
1168*/
1169
1170void
1171mail_esmtp_args(kp, vp, e)
1172        char *kp;
1173        char *vp;
1174        ENVELOPE *e;
1175{
1176        if (strcasecmp(kp, "size") == 0)
1177        {
1178                if (vp == NULL)
1179                {
1180                        usrerr("501 SIZE requires a value");
1181                        /* NOTREACHED */
1182                }
1183# if defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY)
1184                e->e_msgsize = strtoul(vp, (char **) NULL, 10);
1185# else
1186                e->e_msgsize = strtol(vp, (char **) NULL, 10);
1187# endif
1188        }
1189        else if (strcasecmp(kp, "body") == 0)
1190        {
1191                if (vp == NULL)
1192                {
1193                        usrerr("501 BODY requires a value");
1194                        /* NOTREACHED */
1195                }
1196                else if (strcasecmp(vp, "8bitmime") == 0)
1197                {
1198                        SevenBitInput = FALSE;
1199                }
1200                else if (strcasecmp(vp, "7bit") == 0)
1201                {
1202                        SevenBitInput = TRUE;
1203                }
1204                else
1205                {
1206                        usrerr("501 Unknown BODY type %s",
1207                                vp);
1208                        /* NOTREACHED */
1209                }
1210                e->e_bodytype = newstr(vp);
1211        }
1212        else if (strcasecmp(kp, "envid") == 0)
1213        {
1214                if (vp == NULL)
1215                {
1216                        usrerr("501 ENVID requires a value");
1217                        /* NOTREACHED */
1218                }
1219                if (!xtextok(vp))
1220                {
1221                        usrerr("501 Syntax error in ENVID parameter value");
1222                        /* NOTREACHED */
1223                }
1224                if (e->e_envid != NULL)
1225                {
1226                        usrerr("501 Duplicate ENVID parameter");
1227                        /* NOTREACHED */
1228                }
1229                e->e_envid = newstr(vp);
1230        }
1231        else if (strcasecmp(kp, "ret") == 0)
1232        {
1233                if (vp == NULL)
1234                {
1235                        usrerr("501 RET requires a value");
1236                        /* NOTREACHED */
1237                }
1238                if (bitset(EF_RET_PARAM, e->e_flags))
1239                {
1240                        usrerr("501 Duplicate RET parameter");
1241                        /* NOTREACHED */
1242                }
1243                e->e_flags |= EF_RET_PARAM;
1244                if (strcasecmp(vp, "hdrs") == 0)
1245                        e->e_flags |= EF_NO_BODY_RETN;
1246                else if (strcasecmp(vp, "full") != 0)
1247                {
1248                        usrerr("501 Bad argument \"%s\" to RET", vp);
1249                        /* NOTREACHED */
1250                }
1251        }
1252        else
1253        {
1254                usrerr("501 %s parameter unrecognized", kp);
1255                /* NOTREACHED */
1256        }
1257}
1258/*
1259**  RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
1260**
1261**      Parameters:
1262**              a -- the address corresponding to the To: parameter.
1263**              kp -- the parameter key.
1264**              vp -- the value of that parameter.
1265**              e -- the envelope.
1266**
1267**      Returns:
1268**              none.
1269*/
1270
1271void
1272rcpt_esmtp_args(a, kp, vp, e)
1273        ADDRESS *a;
1274        char *kp;
1275        char *vp;
1276        ENVELOPE *e;
1277{
1278        if (strcasecmp(kp, "notify") == 0)
1279        {
1280                char *p;
1281
1282                if (vp == NULL)
1283                {
1284                        usrerr("501 NOTIFY requires a value");
1285                        /* NOTREACHED */
1286                }
1287                a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
1288                a->q_flags |= QHASNOTIFY;
1289                if (strcasecmp(vp, "never") == 0)
1290                        return;
1291                for (p = vp; p != NULL; vp = p)
1292                {
1293                        p = strchr(p, ',');
1294                        if (p != NULL)
1295                                *p++ = '\0';
1296                        if (strcasecmp(vp, "success") == 0)
1297                                a->q_flags |= QPINGONSUCCESS;
1298                        else if (strcasecmp(vp, "failure") == 0)
1299                                a->q_flags |= QPINGONFAILURE;
1300                        else if (strcasecmp(vp, "delay") == 0)
1301                                a->q_flags |= QPINGONDELAY;
1302                        else
1303                        {
1304                                usrerr("501 Bad argument \"%s\"  to NOTIFY",
1305                                        vp);
1306                                /* NOTREACHED */
1307                        }
1308                }
1309        }
1310        else if (strcasecmp(kp, "orcpt") == 0)
1311        {
1312                if (vp == NULL)
1313                {
1314                        usrerr("501 ORCPT requires a value");
1315                        /* NOTREACHED */
1316                }
1317                if (strchr(vp, ';') == NULL || !xtextok(vp))
1318                {
1319                        usrerr("501 Syntax error in ORCPT parameter value");
1320                        /* NOTREACHED */
1321                }
1322                if (a->q_orcpt != NULL)
1323                {
1324                        usrerr("501 Duplicate ORCPT parameter");
1325                        /* NOTREACHED */
1326                }
1327                a->q_orcpt = newstr(vp);
1328        }
1329        else
1330        {
1331                usrerr("501 %s parameter unrecognized", kp);
1332                /* NOTREACHED */
1333        }
1334}
1335/*
1336**  PRINTVRFYADDR -- print an entry in the verify queue
1337**
1338**      Parameters:
1339**              a -- the address to print
1340**              last -- set if this is the last one.
1341**              vrfy -- set if this is a VRFY command.
1342**
1343**      Returns:
1344**              none.
1345**
1346**      Side Effects:
1347**              Prints the appropriate 250 codes.
1348*/
1349
1350void
1351printvrfyaddr(a, last, vrfy)
1352        register ADDRESS *a;
1353        bool last;
1354        bool vrfy;
1355{
1356        char fmtbuf[20];
1357
1358        if (vrfy && a->q_mailer != NULL &&
1359            !bitnset(M_VRFY250, a->q_mailer->m_flags))
1360                strcpy(fmtbuf, "252");
1361        else
1362                strcpy(fmtbuf, "250");
1363        fmtbuf[3] = last ? ' ' : '-';
1364
1365        if (a->q_fullname == NULL)
1366        {
1367                if (strchr(a->q_user, '@') == NULL)
1368                        strcpy(&fmtbuf[4], "<%s@%s>");
1369                else
1370                        strcpy(&fmtbuf[4], "<%s>");
1371                message(fmtbuf, a->q_user, MyHostName);
1372        }
1373        else
1374        {
1375                if (strchr(a->q_user, '@') == NULL)
1376                        strcpy(&fmtbuf[4], "%s <%s@%s>");
1377                else
1378                        strcpy(&fmtbuf[4], "%s <%s>");
1379                message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
1380        }
1381}
1382/*
1383**  RUNINCHILD -- return twice -- once in the child, then in the parent again
1384**
1385**      Parameters:
1386**              label -- a string used in error messages
1387**
1388**      Returns:
1389**              zero in the child
1390**              one in the parent
1391**
1392**      Side Effects:
1393**              none.
1394*/
1395
1396int
1397runinchild(label, e)
1398        char *label;
1399        register ENVELOPE *e;
1400{
1401        pid_t childpid;
1402
1403        if (!OneXact)
1404        {
1405                /*
1406                **  Disable child process reaping, in case ETRN has preceeded
1407                **  MAIL command, and then fork.
1408                */
1409
1410                (void) blocksignal(SIGCHLD);
1411
1412                childpid = dofork();
1413                if (childpid < 0)
1414                {
1415                        syserr("451 %s: cannot fork", label);
1416                        (void) releasesignal(SIGCHLD);
1417                        return (1);
1418                }
1419                if (childpid > 0)
1420                {
1421                        auto int st;
1422
1423                        /* parent -- wait for child to complete */
1424                        sm_setproctitle(TRUE, "server %s child wait", CurSmtpClient);
1425                        st = waitfor(childpid);
1426                        if (st == -1)
1427                                syserr("451 %s: lost child", label);
1428                        else if (!WIFEXITED(st))
1429                                syserr("451 %s: died on signal %d",
1430                                        label, st & 0177);
1431
1432                        /* if we exited on a QUIT command, complete the process */
1433                        if (WEXITSTATUS(st) == EX_QUIT)
1434                        {
1435                                disconnect(1, e);
1436                                finis(TRUE, ExitStat);
1437                        }
1438
1439                        /* restore the child signal */
1440                        (void) releasesignal(SIGCHLD);
1441
1442                        return (1);
1443                }
1444                else
1445                {
1446                        /* child */
1447                        InChild = TRUE;
1448                        QuickAbort = FALSE;
1449                        clearenvelope(e, FALSE);
1450                        (void) setsignal(SIGCHLD, SIG_DFL);
1451                        (void) releasesignal(SIGCHLD);
1452                }
1453        }
1454        return (0);
1455}
1456
1457# endif /* SMTP */
1458/*
1459**  HELP -- implement the HELP command.
1460**
1461**      Parameters:
1462**              topic -- the topic we want help for.
1463**
1464**      Returns:
1465**              none.
1466**
1467**      Side Effects:
1468**              outputs the help file to message output.
1469*/
1470
1471void
1472help(topic)
1473        char *topic;
1474{
1475        register FILE *hf;
1476        int len;
1477        bool noinfo;
1478        int sff = SFF_OPENASROOT|SFF_REGONLY;
1479        char buf[MAXLINE];
1480        extern char Version[];
1481
1482        if (DontLockReadFiles)
1483                sff |= SFF_NOLOCK;
1484        if (!bitset(DBS_HELPFILEINUNSAFEDIRPATH, DontBlameSendmail))   
1485                sff |= SFF_SAFEDIRPATH;
1486
1487        if (HelpFile == NULL ||
1488            (hf = safefopen(HelpFile, O_RDONLY, 0444, sff)) == NULL)
1489        {
1490                /* no help */
1491                errno = 0;
1492                message("502 Sendmail %s -- HELP not implemented", Version);
1493                return;
1494        }
1495
1496        if (topic == NULL || *topic == '\0')
1497        {
1498                topic = "smtp";
1499                message("214-This is Sendmail version %s", Version);
1500                noinfo = FALSE;
1501        }
1502        else
1503        {
1504                makelower(topic);
1505                noinfo = TRUE;
1506        }
1507
1508        len = strlen(topic);
1509
1510        while (fgets(buf, sizeof buf, hf) != NULL)
1511        {
1512                if (strncmp(buf, topic, len) == 0)
1513                {
1514                        register char *p;
1515
1516                        p = strchr(buf, '\t');
1517                        if (p == NULL)
1518                                p = buf;
1519                        else
1520                                p++;
1521                        fixcrlf(p, TRUE);
1522                        message("214-%s", p);
1523                        noinfo = FALSE;
1524                }
1525        }
1526
1527        if (noinfo)
1528                message("504 HELP topic \"%.10s\" unknown", topic);
1529        else
1530                message("214 End of HELP info");
1531        (void) fclose(hf);
1532}
Note: See TracBrowser for help on using the repository browser.