static char ident[] = "$Id$"; // Configuration #define T_TYPE TRANSPORT_LOCALHOST #define T_SOCK NULL /* for TRANSPORT_UNIX */ #define T_HOST NULL /* for TRANSPORT_TCP */ #define T_PORT 7783 /* for TRANSPORT_{LOCALHOST,TCP} */ #define M_TIME 30 /* call it ham if no answer in 30 seconds */ #include #include #include #include #include #include #include #include #include #include #include #include "libspamc.h" static char *errfn; static int flags = SPAMC_CHECK_ONLY | SPAMC_RAW_MODE; #define err(n, ...) _err(n, true, __FILE__, __LINE__, __VA_ARGS__) #define errx(n, ...) _err(n, false, __FILE__, __LINE__, __VA_ARGS__) #define warn(...) _err(-1, true, __FILE__, __LINE__, __VA_ARGS__) #define warnx(...) _err(-1, false, __FILE__, __LINE__, __VA_ARGS__) void _err(int exitcode, bool want_strerror, char *fn, int lineno, char *format, ...) { FILE *fp; va_list ap; /* If anything fails in here, we're fucked, so just exit. If * exitcode is greather than 0 (i.e. the caller is trying to exit * with a failure code), use that, else use EX_IOERR. */ if ((fp = fopen(errfn, "a")) == NULL) { exit(exitcode > 0 ? exitcode : EX_IOERR); } if (fprintf(fp, ".mdeliver-processor:%s:%d:", fn, lineno) < 0) { exit(exitcode > 0 ? exitcode : EX_IOERR); } va_start(ap, format); if (vfprintf(fp, format, ap) < 0) { exit(exitcode > 0 ? exitcode : EX_IOERR); } va_end(ap); if (want_strerror) { if (fputs(": ", fp) == EOF) { exit(exitcode > 0 ? exitcode : EX_IOERR); } if (fputs(strerror(errno), fp) == EOF) { exit(exitcode > 0 ? exitcode : EX_IOERR); } } if (fputs("\n", fp) == EOF) { exit(exitcode > 0 ? exitcode : EX_IOERR); } if (fclose(fp) == EOF) { exit(exitcode > 0 ? exitcode : EX_IOERR); } if (exitcode >= 0) { exit(exitcode); } } static char * logname() { char *logname; struct passwd *p; if ((logname = getenv("LOGNAME")) != NULL) { return logname; } errno = 0; if ((p = getpwuid(getuid())) == NULL) { if (errno != 0) { err(EX_OSERR, "getpwuid(getuid())"); } errx(EX_NOUSER, "Who are you? What do you want? Why are you here? Where are you going?"); } return p->pw_name; } static bool get_transport(struct transport *t) { int status; transport_init(t); t->type = T_TYPE; t->socketpath = T_SOCK; t->hostname = T_HOST; t->port = T_PORT; /* In 3.1.3, these were the hard-coded settings. */ t->connect_retries = 3; t->retry_sleep = 1; if ((status = transport_setup(t, 0)) != EX_OK) { warnx("non-fatal: transport_setup"); return false; } return true; } static bool get_message(char *fn, struct message *m) { int fd; int status; m->timeout = M_TIME; /* message_filter stupidly allocates a buffer a little bigger than * max_len for reading the processed message back from spamd even * when told not to. It never uses the buffer. */ m->max_len = 0; if ((fd = open(fn, O_RDONLY)) == -1) { err(EX_IOERR, "open(%s)", fn); } if ((status = message_read(fd, flags, m)) != EX_OK) { warnx("non-fatal: message_read"); return false; } return true; } static int spam_level(char *message) { struct transport t; struct message m; int status; if (!get_transport(&t)) { return 0; } if (!get_message(message, &m)) { return 0; } if ((status = message_filter(&t, logname(), flags, &m)) != EX_OK) { warnx("non-fatal: message_filter"); return 0; } if (m.is_spam == EX_ISSPAM) { if (m.score > m.threshold * 3) { return 3; } else if (m.score > m.threshold * 2) { return 2; } return 1; } else if (m.is_spam == EX_NOTSPAM) { return 0; } warnx("non-fatal: message_filter returned %d", m.is_spam); return 0; } static int process(char *tmp) { size_t len = strlen(tmp); /* 6 for spamN/ and 1 for terminating null */ char *new = malloc(len + 6 + 1); int level = spam_level(tmp); /* Nothing to do about a malloc failure but dump core. */ errfn = malloc(len + 1); strcpy(errfn, "err"); strcat(errfn, tmp + 3); if (level > 0) { snprintf(new, 7, "spam%d/", level); } else { new[0] = '\0'; } strcat(new, "new"); strcat(new, tmp + 3); if (rename(tmp, new) == -1) { err(EX_OSERR, "rename(%s, %s)", tmp, new); } return 0; } int main(int argc, char *argv[]) { if (argc < 2) { exit(EX_USAGE); } if (argv[1][0] == '-' && argv[1][1] == 'v') { puts(ident); return 0; } return process(argv[1]); }