]> diplodocus.org Git - mdeliver/blob - processor.c
2ebca536c55d825cca02c8522b8fd5f42c4911ce
[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(0, true, __FILE__, __LINE__, __VA_ARGS__)
32 #define warnx(...) _err(0, 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 t->type = T_TYPE;
106 t->socketpath = T_SOCK;
107 t->hostname = T_HOST;
108 t->port = T_PORT;
109
110 if ((status = transport_setup(t, 0)) != EX_OK) {
111 warnx("non-fatal: transport_setup");
112 return false;
113 }
114
115 return true;
116 }
117
118 static bool
119 get_message(char *fn, struct message *m)
120 {
121 int fd;
122 int status;
123
124 m->timeout = M_TIME;
125 /* message_filter stupidly allocates a buffer a little bigger than
126 * max_len for reading the processed message back from spamd even
127 * when told not to. It never uses the buffer. */
128 m->max_len = 0;
129
130 if ((fd = open(fn, O_RDONLY)) == -1) {
131 err(EX_IOERR, "open(%s)", fn);
132 }
133
134 if ((status = message_read(fd, flags, m)) != EX_OK) {
135 warnx("non-fatal: message_read");
136 return false;
137 }
138
139 return true;
140 }
141
142 static bool
143 is_spam(char *message)
144 {
145 struct transport t;
146 struct message m;
147 int status;
148
149 if (!get_transport(&t)) {
150 return false;
151 }
152 if (!get_message(message, &m)) {
153 return false;
154 }
155
156 if ((status = message_filter(&t, logname(), flags, &m)) != EX_OK) {
157 warnx("non-fatal: message_filter");
158 return false;
159 }
160
161 if (m.is_spam == EX_ISSPAM) {
162 return true;
163 } else if (m.is_spam == EX_NOTSPAM) {
164 return false;
165 }
166
167 warnx("non-fatal: message_filter returned %d", m.is_spam);
168 return false;
169 }
170
171 static int
172 process(char *tmp)
173 {
174 size_t len = strlen(tmp);
175 errfn = malloc(len + 1);
176 /* 5 for spam/ and 1 for terminating null */
177 char *new = malloc(len + 5 + 1);
178
179 /* Nothing to do about a malloc failure but dump core. */
180 strcpy(errfn, "err");
181 strcat(errfn, tmp + 3);
182
183 new[0] = '\0';
184 if (is_spam(tmp)) {
185 strcpy(new, "spam/");
186 }
187 strcat(new, "new");
188 strcat(new, tmp + 3);
189
190 if (rename(tmp, new) == -1) {
191 err(EX_OSERR, "rename(%s, %s)", tmp, new);
192 }
193
194 return 0;
195 }
196
197
198 int
199 main(int argc, char *argv[])
200 {
201 if (argc < 2) {
202 exit(EX_USAGE);
203 }
204
205 if (argv[1][0] == '-' && argv[1][1] == 'v') {
206 puts(ident);
207 return 0;
208 }
209
210 return process(argv[1]);
211 }