/** * \file local.c * Local delivery of mail via a MDA. */ #include #include #include #include #include #include #include "local.h" #include "main.h" #include "xmalloc.h" char *mda = NULL; char *force_mda = NULL; FILE *mda_fp = NULL; int local_address(const char *address) { if (!force_mda) { /* TODO: * * A litle more searching * Does the regex"[^@]*\@(.*)" resolve to 127.0.0.1 or a local ip? * * Maybe it should be delivered locally? */ return !strchr(address, '@'); } return 1; } /** replace ' by _ */ static void sanitize(char *s) { char *cp; for (cp = s; (cp = strchr (cp, '\'')); cp++) *cp = '_'; } /** * Pipe the message to the MDA for local delivery. * * Based on fetchmail's open_mda_sink(). */ void local_init(message_t *message) { int length = 0, fromlen = 0, nameslen = 0; char *names = NULL, *before, *after, *from = NULL; if (!mda) { fprintf(stderr, "Local delivery not possible without a MDA\n"); exit(EX_OSFILE); } length = strlen(mda); before = xstrdup(mda); /* get user addresses for %T */ if (strstr(before, "%T")) { if (!force_mda) { struct list_head *ptr; char *p; /* * We go through this in order to be able to handle very * long lists of users and (re)implement %s. */ nameslen = 0; list_for_each(ptr, &message->local_recipients) { recipient_t *recipient = list_entry(ptr, recipient_t, list); assert(recipient->address); nameslen += (strlen(recipient->address) + 3); /* string + quotes + ' ' */ } names = (char *)xmalloc(nameslen + 1); /* account for '\0' */ p = names; list_for_each(ptr, &message->local_recipients) { recipient_t *recipient = list_entry(ptr, recipient_t, list); int written; sanitize(recipient->address); written = sprintf(p, "'%s' ", recipient->address); if (written < 0) { perror(NULL); exit(EX_OSERR); } p += written; } names[--nameslen] = '\0'; /* chop trailing space */ } else { nameslen = (strlen(force_mda) + 3); // 'force_mda' names = (char *)xmalloc(nameslen + 1); // 'force_mda'\0 // Let's skip sanitization? // Hope the config isn't evil to us 0.o if (sprintf(names, "'%s' ", force_mda) < 0) { perror(NULL); exit(EX_OSERR); } } } /* get From address for %F */ if (strstr(before, "%F")) { from = xstrdup(message->reverse_path ? message->reverse_path : ""); 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; /* subtract %s and add '' */ sp += 2; } sp = before; while ((sp = strstr(sp, "%T"))) { length += nameslen; /* subtract %T and add '' */ sp += 2; } sp = before; while ((sp = strstr(sp, "%F"))) { length += fromlen; /* subtract %F and add '' */ 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] == 'T') { strcpy(dp, names); dp += nameslen; sp++; /* position sp over [sT] */ dp--; /* adjust dp */ } else if (sp[1] == 'F') { *dp++ = '\''; strcpy(dp, from); dp += fromlen; *dp++ = '\''; 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(!(mda_fp = popen(before, "w"))) { fprintf(stderr, "Failed to connect to MDA\n"); exit(EX_OSERR); } if(verbose) fprintf(stdout, "Connected to MDA: %s\n", before); free(before); } void local_flush(message_t *message) { char buffer[BUFSIZ]; size_t n; do { n = message_read(message, buffer, BUFSIZ); if(ferror(mda_fp)) { perror(NULL); exit(EX_OSERR); } } while(n == BUFSIZ); } void local_cleanup(void) { if(mda_fp) { int status; if((status = pclose(mda_fp))) { if(WIFSIGNALED(status)) fprintf(stderr, "MDA died of signal %d\n", WTERMSIG(status)); else if(WIFEXITED(status)) fprintf(stderr, "MDA returned nonzero status %d\n", WEXITSTATUS(status)); else fprintf(stderr, "MDA failed\n"); exit(EX_OSERR); } mda_fp = NULL; if(verbose) fprintf(stdout, "Disconnected to MDA\n"); } if(mda) free(mda); if(force_mda) free(force_mda); }