diff options
-rw-r--r-- | sink.c | 354 |
1 files changed, 192 insertions, 162 deletions
@@ -562,160 +562,13 @@ int open_sink(struct query *ctl, struct msgblk *msg, return(PS_BSMTP); } } - else if (ctl->mda) /* we have a declared MDA */ - { - int length = 0, fromlen = 0, nameslen = 0; - char *names = NULL, *before, *after, *from = NULL; - - ctl->destaddr = "localhost"; - - for (idp = msg->recipients; idp; idp = idp->next) - if (idp->val.status.mark == XMIT_ACCEPT) - (*good_addresses)++; - - length = strlen(ctl->mda); - before = xstrdup(ctl->mda); - - /* get user addresses for %T (or %s for backward compatibility) */ - if (strstr(before, "%s") || strstr(before, "%T")) - { - /* - * We go through this in order to be able to handle very - * long lists of users and (re)implement %s. - */ - nameslen = 0; - for (idp = msg->recipients; idp; idp = idp->next) - if ((idp->val.status.mark == XMIT_ACCEPT)) - nameslen += (strlen(idp->id) + 1); /* string + ' ' */ - if ((*good_addresses == 0)) - nameslen = strlen(run.postmaster); - - names = (char *)xmalloc(nameslen + 1); /* account for '\0' */ - if (*good_addresses == 0) - strcpy(names, run.postmaster); - else - { - names[0] = '\0'; - for (idp = msg->recipients; idp; idp = idp->next) - if (idp->val.status.mark == XMIT_ACCEPT) - { - strcat(names, idp->id); - strcat(names, " "); - } - names[--nameslen] = '\0'; /* chop trailing space */ - } - - /* sanitize names in order to contain only harmless shell chars */ - sanitize(names); - } - - /* get From address for %F */ - if (strstr(before, "%F")) - { - from = xstrdup(msg->return_path); - - /* sanitize from in order to contain *only* harmless shell chars */ - sanitize(from); - - fromlen = strlen(from); - } - - /* do we have to build an mda string? */ - if (names || from) - { - char *sp, *dp; - - /* find length of resulting mda string */ - sp = before; - while ((sp = strstr(sp, "%s"))) { - length += nameslen - 2; /* subtract %s */ - sp += 2; - } - sp = before; - while ((sp = strstr(sp, "%T"))) { - length += nameslen - 2; /* subtract %T */ - sp += 2; - } - sp = before; - while ((sp = strstr(sp, "%F"))) { - length += fromlen - 2; /* subtract %F */ - sp += 2; - } - - after = xmalloc(length + 1); - - /* copy mda source string to after, while expanding %[sTF] */ - for (dp = after, sp = before; (*dp = *sp); dp++, sp++) { - if (sp[0] != '%') continue; - /* need to expand? BTW, no here overflow, because in - ** the worst case (end of string) sp[1] == '\0' */ - if (sp[1] == 's' || sp[1] == 'T') { - strcpy(dp, names); - dp += nameslen; - sp++; /* position sp over [sT] */ - dp--; /* adjust dp */ - } else if (sp[1] == 'F') { - strcpy(dp, from); - dp += fromlen; - sp++; /* position sp over F */ - dp--; /* adjust dp */ - } - } - - if (names) { - free(names); - names = NULL; - } - if (from) { - free(from); - from = NULL; - } - - free(before); - - before = after; - } - - - if (outlevel >= O_DEBUG) - report(stdout, _("about to deliver with: %s\n"), before); - -#ifdef HAVE_SETEUID - /* - * Arrange to run with user's permissions if we're root. - * This will initialize the ownership of any files the - * MDA creates properly. (The seteuid call is available - * under all BSDs and Linux) - */ - seteuid(ctl->uid); -#endif /* HAVE_SETEUID */ - - sinkfp = popen(before, "w"); - free(before); - before = NULL; - -#ifdef HAVE_SETEUID - /* this will fail quietly if we didn't start as root */ - seteuid(0); -#endif /* HAVE_SETEUID */ - - if (!sinkfp) - { - report(stderr, _("MDA open failed\n")); - return(PS_IOERR); - } - -#ifndef HAVE_SIGACTION - sigchld = signal(SIGCHLD, SIG_DFL); -#else - memset (&sa_new, 0, sizeof sa_new); - sigemptyset (&sa_new.sa_mask); - sa_new.sa_handler = SIG_DFL; - sigaction (SIGCHLD, &sa_new, &sa_old); -#endif /* HAVE_SIGACTION */ - } - else /* forward to an SMTP or LMTP listener */ + /* + * Try to forward to an SMTP or LMTP listener. If the attempt to + * open a socket fails, fall through to attempt delivery via + * local MDA. + */ + else if (!ctl->mda && smtp_open(ctl) != -1) { const char *ap; char options[MSGBUFSIZE]; @@ -723,15 +576,6 @@ int open_sink(struct query *ctl, struct msgblk *msg, char **from_responses; int total_addresses; - /* build a connection to the SMTP listener */ - if ((smtp_open(ctl) == -1)) - { - report(stderr, _("%cMTP connect to %s failed\n"), - ctl->listener, - ctl->smtphost ? ctl->smtphost : "localhost"); - return(PS_SMTP); - } - /* * Compute ESMTP options. */ @@ -882,6 +726,192 @@ int open_sink(struct query *ctl, struct msgblk *msg, } /* + * Awkward case. User didn't specify an MDA. Our attempt to get a + * listener socket failed. Try to cope anyway -- initial configuration + * may have found procmail. + */ + else if (!ctl->mda) + { + report(stderr, _("%cMTP connect to %s failed\n"), + ctl->listener, + ctl->smtphost ? ctl->smtphost : "localhost"); + +#ifndef FALLBACK_MDA + /* No fallback MDA declared. Bail out. */ + return(PS_SMTP); +#else + /* + * If user had things set up to forward offsite, no way + * we want to deliver locally! + */ + if (ctl->smtphost && strcmp(ctl->smtphost, "localhost")) + return(PS_SMTP); + + /* + * User was delivering locally. We have a fallback MDA. + * Latch it in place, logging the error, and fall through. + */ + ctl->mda = FALLBACK_MDA; + + report(stderr, _("can't raise the listener; falling back to " FALLBACK_MDA)); +#endif + } + + if (ctl->mda) /* must deliver through an MDA */ + { + int length = 0, fromlen = 0, nameslen = 0; + char *names = NULL, *before, *after, *from = NULL; + + ctl->destaddr = "localhost"; + + for (idp = msg->recipients; idp; idp = idp->next) + if (idp->val.status.mark == XMIT_ACCEPT) + (*good_addresses)++; + + length = strlen(ctl->mda); + before = xstrdup(ctl->mda); + + /* get user addresses for %T (or %s for backward compatibility) */ + if (strstr(before, "%s") || strstr(before, "%T")) + { + /* + * We go through this in order to be able to handle very + * long lists of users and (re)implement %s. + */ + nameslen = 0; + for (idp = msg->recipients; idp; idp = idp->next) + if ((idp->val.status.mark == XMIT_ACCEPT)) + nameslen += (strlen(idp->id) + 1); /* string + ' ' */ + if ((*good_addresses == 0)) + nameslen = strlen(run.postmaster); + + names = (char *)xmalloc(nameslen + 1); /* account for '\0' */ + if (*good_addresses == 0) + strcpy(names, run.postmaster); + else + { + names[0] = '\0'; + for (idp = msg->recipients; idp; idp = idp->next) + if (idp->val.status.mark == XMIT_ACCEPT) + { + strcat(names, idp->id); + strcat(names, " "); + } + names[--nameslen] = '\0'; /* chop trailing space */ + } + + /* sanitize names in order to contain only harmless shell chars */ + sanitize(names); + } + + /* get From address for %F */ + if (strstr(before, "%F")) + { + from = xstrdup(msg->return_path); + + /* sanitize from in order to contain *only* harmless shell chars */ + sanitize(from); + + fromlen = strlen(from); + } + + /* do we have to build an mda string? */ + if (names || from) + { + char *sp, *dp; + + /* find length of resulting mda string */ + sp = before; + while ((sp = strstr(sp, "%s"))) { + length += nameslen - 2; /* subtract %s */ + sp += 2; + } + sp = before; + while ((sp = strstr(sp, "%T"))) { + length += nameslen - 2; /* subtract %T */ + sp += 2; + } + sp = before; + while ((sp = strstr(sp, "%F"))) { + length += fromlen - 2; /* subtract %F */ + sp += 2; + } + + after = xmalloc(length + 1); + + /* copy mda source string to after, while expanding %[sTF] */ + for (dp = after, sp = before; (*dp = *sp); dp++, sp++) { + if (sp[0] != '%') continue; + + /* need to expand? BTW, no here overflow, because in + ** the worst case (end of string) sp[1] == '\0' */ + if (sp[1] == 's' || sp[1] == 'T') { + strcpy(dp, names); + dp += nameslen; + sp++; /* position sp over [sT] */ + dp--; /* adjust dp */ + } else if (sp[1] == 'F') { + strcpy(dp, from); + dp += fromlen; + sp++; /* position sp over F */ + dp--; /* adjust dp */ + } + } + + if (names) { + free(names); + names = NULL; + } + if (from) { + free(from); + from = NULL; + } + + free(before); + + before = after; + } + + + if (outlevel >= O_DEBUG) + report(stdout, _("about to deliver with: %s\n"), before); + +#ifdef HAVE_SETEUID + /* + * Arrange to run with user's permissions if we're root. + * This will initialize the ownership of any files the + * MDA creates properly. (The seteuid call is available + * under all BSDs and Linux) + */ + seteuid(ctl->uid); +#endif /* HAVE_SETEUID */ + + sinkfp = popen(before, "w"); + free(before); + before = NULL; + +#ifdef HAVE_SETEUID + /* this will fail quietly if we didn't start as root */ + seteuid(0); +#endif /* HAVE_SETEUID */ + + if (!sinkfp) + { + report(stderr, _("MDA open failed\n")); + return(PS_IOERR); + } + +#ifndef HAVE_SIGACTION + sigchld = signal(SIGCHLD, SIG_DFL); +#else + memset (&sa_new, 0, sizeof sa_new); + sigemptyset (&sa_new.sa_mask); + sa_new.sa_handler = SIG_DFL; + sigaction (SIGCHLD, &sa_new, &sa_old); +#endif /* HAVE_SIGACTION */ + } + + /* * We need to stash this away in order to know how many * response lines to expect after the LMTP end-of-message. */ |