+/* Log a message about missing milter macros, but only the first time */
+void warnmacro(const char *macro, const char *scope)
+{
+ if (warnedmacro)
+ return;
+ debug(D_ALWAYS, "Could not retrieve sendmail macro \"%s\"!. Please add it to confMILTER_MACROS_%s for better spamassassin results",
+ macro, scope);
+ warnedmacro = true;
+}
+
+/*
+ untrusted-argument-safe popen function - only supports "r" and "w" modes
+ for simplicity, and always reads stdout and stderr in "r" mode. Call
+ fclose to close the FILE, and waitpid to reap the child process (pid).
+*/
+FILE *popenv(char *const argv[], const char *type, pid_t *pid)
+{
+ FILE *iop;
+ int pdes[2];
+ int save_errno;
+
+ if ((*type != 'r' && *type != 'w') || type[1])
+ {
+ errno = EINVAL;
+ return (NULL);
+ }
+ if (pipe(pdes) < 0)
+ return (NULL);
+ switch (*pid = fork()) {
+
+ case -1: /* Error. */
+ save_errno = errno;
+ (void)close(pdes[0]);
+ (void)close(pdes[1]);
+ errno = save_errno;
+ return (NULL);
+ /* NOTREACHED */
+ case 0: /* Child. */
+ if (*type == 'r') {
+ /*
+ * The dup2() to STDIN_FILENO is repeated to avoid
+ * writing to pdes[1], which might corrupt the
+ * parent's copy. This isn't good enough in
+ * general, since the exit() is no return, so
+ * the compiler is free to corrupt all the local
+ * variables.
+ */
+ (void)close(pdes[0]);
+ (void)dup2(pdes[1], STDOUT_FILENO);
+ (void)dup2(pdes[1], STDERR_FILENO);
+ if (pdes[1] != STDOUT_FILENO && pdes[1] != STDERR_FILENO) {
+ (void)close(pdes[1]);
+ }
+ } else {
+ if (pdes[0] != STDIN_FILENO) {
+ (void)dup2(pdes[0], STDIN_FILENO);
+ (void)close(pdes[0]);
+ }
+ (void)close(pdes[1]);
+ }
+ execv(argv[0], argv);
+ exit(127);
+ /* NOTREACHED */
+ }
+
+ /* Parent; assume fdopen can't fail. */
+ if (*type == 'r') {
+ iop = fdopen(pdes[0], type);
+ (void)close(pdes[1]);
+ } else {
+ iop = fdopen(pdes[1], type);
+ (void)close(pdes[0]);
+ }
+
+ return (iop);
+}
+