]> diplodocus.org Git - mdeliver/blob - processor.c
66652d77865fc2a1c63021d9bf3bf2c104fbcd96
[mdeliver] / processor.c
1 static char ident[] = "$Id$";
2
3 // Configuration
4 #define T_TYPE TRANSPORT_LOCALHOST
5 #define T_SOCK NULL /* for TRANSPORT_UNIX */
6 #define T_HOST NULL /* for TRANSPORT_TCP */
7 #define T_PORT 7783 /* for TRANSPORT_{LOCALHOST,TCP} */
8 #define M_TIME 30 /* call it ham if no answer in 30 seconds */
9
10 #include <sys/types.h>
11
12 #include <errno.h>
13 #include <fcntl.h>
14 #include <pwd.h>
15 #include <stdarg.h>
16 #include <stdbool.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <sysexits.h>
21 #include <unistd.h>
22
23 #include "libspamc.h"
24
25 static char *errfn;
26
27 static int flags = SPAMC_CHECK_ONLY | SPAMC_RAW_MODE;
28
29 #define err(n, ...) _err(n, true, __FILE__, __LINE__, __VA_ARGS__)
30 #define errx(n, ...) _err(n, false, __FILE__, __LINE__, __VA_ARGS__)
31 #define warn(...) _err(-1, true, __FILE__, __LINE__, __VA_ARGS__)
32 #define warnx(...) _err(-1, false, __FILE__, __LINE__, __VA_ARGS__)
33
34 void
35 _err(int exitcode, bool want_strerror, char *fn, int lineno, char *format, ...)
36 {
37 FILE *fp;
38 va_list ap;
39
40 /* If anything fails in here, we're fucked, so just exit. If
41 * exitcode is greather than 0 (i.e. the caller is trying to exit
42 * with a failure code), use that, else use EX_IOERR. */
43
44 if ((fp = fopen(errfn, "a")) == NULL) {
45 exit(exitcode > 0 ? exitcode : EX_IOERR);
46 }
47
48 if (fprintf(fp, ".mdeliver-processor:%s:%d:", fn, lineno) < 0) {
49 exit(exitcode > 0 ? exitcode : EX_IOERR);
50 }
51
52 va_start(ap, format);
53 if (vfprintf(fp, format, ap) < 0) {
54 exit(exitcode > 0 ? exitcode : EX_IOERR);
55 }
56 va_end(ap);
57
58 if (want_strerror) {
59 if (fputs(": ", fp) == EOF) {
60 exit(exitcode > 0 ? exitcode : EX_IOERR);
61 }
62 if (fputs(strerror(errno), fp) == EOF) {
63 exit(exitcode > 0 ? exitcode : EX_IOERR);
64 }
65 }
66 if (fputs("\n", fp) == EOF) {
67 exit(exitcode > 0 ? exitcode : EX_IOERR);
68 }
69
70 if (fclose(fp) == EOF) {
71 exit(exitcode > 0 ? exitcode : EX_IOERR);
72 }
73
74 if (exitcode >= 0) {
75 exit(exitcode);
76 }
77 }
78
79 static char *
80 logname()
81 {
82 char *logname;
83 struct passwd *p;
84
85 if ((logname = getenv("LOGNAME")) != NULL) {
86 return logname;
87 }
88
89 errno = 0;
90 if ((p = getpwuid(getuid())) == NULL) {
91 if (errno != 0) {
92 err(EX_OSERR, "getpwuid(getuid())");
93 }
94 errx(EX_NOUSER, "Who are you? What do you want? Why are you here? Where are you going?");
95 }
96
97 return p->pw_name;
98 }
99
100 static bool
101 get_transport(struct transport *t)
102 {
103 int status;
104
105 transport_init(t);
106 t->type = T_TYPE;
107 t->socketpath = T_SOCK;
108 t->hostname = T_HOST;
109 t->port = T_PORT;
110 /* In 3.1.3, these were the hard-coded settings. */
111 t->connect_retries = 3;
112 t->retry_sleep = 1;
113
114 if ((status = transport_setup(t, 0)) != EX_OK) {
115 warnx("non-fatal: transport_setup");
116 return false;
117 }
118
119 return true;
120 }
121
122 static bool
123 get_message(char *fn, struct message *m)
124 {
125 int fd;
126 int status;
127
128 m->timeout = M_TIME;
129 /* message_filter stupidly allocates a buffer a little bigger than
130 * max_len for reading the processed message back from spamd even
131 * when told not to. It never uses the buffer. */
132 m->max_len = 0;
133
134 if ((fd = open(fn, O_RDONLY)) == -1) {
135 err(EX_IOERR, "open(%s)", fn);
136 }
137
138 if ((status = message_read(fd, flags, m)) != EX_OK) {
139 warnx("non-fatal: message_read");
140 return false;
141 }
142
143 return true;
144 }
145
146 static bool
147 is_spam(char *message)
148 {
149 struct transport t;
150 struct message m;
151 int status;
152
153 if (!get_transport(&t)) {
154 return false;
155 }
156 if (!get_message(message, &m)) {
157 return false;
158 }
159
160 if ((status = message_filter(&t, logname(), flags, &m)) != EX_OK) {
161 warnx("non-fatal: message_filter");
162 return false;
163 }
164
165 if (m.is_spam == EX_ISSPAM) {
166 return true;
167 } else if (m.is_spam == EX_NOTSPAM) {
168 return false;
169 }
170
171 warnx("non-fatal: message_filter returned %d", m.is_spam);
172 return false;
173 }
174
175 static int
176 process(char *tmp)
177 {
178 size_t len = strlen(tmp);
179 errfn = malloc(len + 1);
180 /* 5 for spam/ and 1 for terminating null */
181 char *new = malloc(len + 5 + 1);
182
183 /* Nothing to do about a malloc failure but dump core. */
184 strcpy(errfn, "err");
185 strcat(errfn, tmp + 3);
186
187 new[0] = '\0';
188 if (is_spam(tmp)) {
189 strcpy(new, "spam/");
190 }
191 strcat(new, "new");
192 strcat(new, tmp + 3);
193
194 if (rename(tmp, new) == -1) {
195 err(EX_OSERR, "rename(%s, %s)", tmp, new);
196 }
197
198 return 0;
199 }
200
201
202 int
203 main(int argc, char *argv[])
204 {
205 if (argc < 2) {
206 exit(EX_USAGE);
207 }
208
209 if (argv[1][0] == '-' && argv[1][1] == 'v') {
210 puts(ident);
211 return 0;
212 }
213
214 return process(argv[1]);
215 }