tidying
[exim.git] / src / src / bmi_spam.c
CommitLineData
8523533c
TK
1/*************************************************
2* Exim - an Internet mail transport agent *
3*************************************************/
8e669ac1 4
8523533c
TK
5/* Code for calling Brightmail AntiSpam.
6 Copyright (c) Tom Kistner <tom@duncanthrax.net> 2004
7 License: GPL */
8
9#include "exim.h"
10#ifdef EXPERIMENTAL_BRIGHTMAIL
11
12#include "bmi_spam.h"
13
14uschar *bmi_current_optin = NULL;
15
16uschar *bmi_process_message(header_line *header_list, int data_fd) {
17 BmiSystem *system = NULL;
18 BmiMessage *message = NULL;
19 BmiError err;
20 BmiErrorLocation err_loc;
21 BmiErrorType err_type;
22 const BmiVerdict *verdict = NULL;
23 FILE *data_file;
24 uschar data_buffer[4096];
25 uschar localhost[] = "127.0.0.1";
26 uschar *host_address;
27 uschar *verdicts = NULL;
28 int i,j;
8e669ac1 29
5903c6ff 30 err = bmiInitSystem(BMI_VERSION, CS bmi_config_file, &system);
8523533c
TK
31 if (bmiErrorIsFatal(err) == BMI_TRUE) {
32 err_loc = bmiErrorGetLocation(err);
33 err_type = bmiErrorGetType(err);
34 log_write(0, LOG_PANIC,
35 "bmi error [loc %d type %d]: could not initialize Brightmail system.", (int)err_loc, (int)err_type);
36 return NULL;
37 }
38
39 err = bmiInitMessage(system, &message);
40 if (bmiErrorIsFatal(err) == BMI_TRUE) {
41 err_loc = bmiErrorGetLocation(err);
42 err_type = bmiErrorGetType(err);
43 log_write(0, LOG_PANIC,
44 "bmi error [loc %d type %d]: could not initialize Brightmail message.", (int)err_loc, (int)err_type);
45 bmiFreeSystem(system);
46 return NULL;
47 }
48
49 /* Send IP address of sending host */
50 if (sender_host_address == NULL)
51 host_address = localhost;
52 else
53 host_address = sender_host_address;
5903c6ff 54 err = bmiProcessConnection(CS host_address, message);
8523533c
TK
55 if (bmiErrorIsFatal(err) == BMI_TRUE) {
56 err_loc = bmiErrorGetLocation(err);
57 err_type = bmiErrorGetType(err);
58 log_write(0, LOG_PANIC,
5903c6ff 59 "bmi error [loc %d type %d]: bmiProcessConnection() failed (IP %s).", (int)err_loc, (int)err_type, CS host_address);
8523533c
TK
60 bmiFreeMessage(message);
61 bmiFreeSystem(system);
62 return NULL;
63 };
64
65 /* Send envelope sender address */
5903c6ff 66 err = bmiProcessFROM(CS sender_address, message);
8523533c
TK
67 if (bmiErrorIsFatal(err) == BMI_TRUE) {
68 err_loc = bmiErrorGetLocation(err);
69 err_type = bmiErrorGetType(err);
70 log_write(0, LOG_PANIC,
5903c6ff 71 "bmi error [loc %d type %d]: bmiProcessFROM() failed (address %s).", (int)err_loc, (int)err_type, CS sender_address);
8523533c
TK
72 bmiFreeMessage(message);
73 bmiFreeSystem(system);
74 return NULL;
75 };
76
77 /* Send envelope recipients */
78 for(i=0;i<recipients_count;i++) {
79 recipient_item *r = recipients_list + i;
80 BmiOptin *optin = NULL;
8e669ac1 81
8523533c
TK
82 /* create optin object if optin string is given */
83 if ((r->bmi_optin != NULL) && (Ustrlen(r->bmi_optin) > 1)) {
84 debug_printf("passing bmiOptin string: %s\n", r->bmi_optin);
85 bmiOptinInit(&optin);
86 err = bmiOptinMset(optin, r->bmi_optin, ':');
87 if (bmiErrorIsFatal(err) == BMI_TRUE) {
88 log_write(0, LOG_PANIC|LOG_MAIN,
5903c6ff 89 "bmi warning: [loc %d type %d]: bmiOptinMSet() failed (address '%s', string '%s').", (int)err_loc, (int)err_type, CS r->address, CS r->bmi_optin);
8523533c
TK
90 if (optin != NULL)
91 bmiOptinFree(optin);
92 optin = NULL;
93 };
94 };
8e669ac1 95
5903c6ff 96 err = bmiAccumulateTO(CS r->address, optin, message);
8e669ac1 97
8523533c
TK
98 if (optin != NULL)
99 bmiOptinFree(optin);
8e669ac1 100
8523533c
TK
101 if (bmiErrorIsFatal(err) == BMI_TRUE) {
102 err_loc = bmiErrorGetLocation(err);
103 err_type = bmiErrorGetType(err);
104 log_write(0, LOG_PANIC,
5903c6ff 105 "bmi error [loc %d type %d]: bmiAccumulateTO() failed (address %s).", (int)err_loc, (int)err_type, CS r->address);
8523533c
TK
106 bmiFreeMessage(message);
107 bmiFreeSystem(system);
108 return NULL;
109 };
110 };
111 err = bmiEndTO(message);
112 if (bmiErrorIsFatal(err) == BMI_TRUE) {
113 err_loc = bmiErrorGetLocation(err);
114 err_type = bmiErrorGetType(err);
115 log_write(0, LOG_PANIC,
116 "bmi error [loc %d type %d]: bmiEndTO() failed.", (int)err_loc, (int)err_type);
117 bmiFreeMessage(message);
118 bmiFreeSystem(system);
119 return NULL;
120 };
8e669ac1 121
8523533c
TK
122 /* Send message headers */
123 while (header_list != NULL) {
124 /* skip deleted headers */
125 if (header_list->type == '*') {
126 header_list = header_list->next;
127 continue;
128 };
5903c6ff 129 err = bmiAccumulateHeaders(CCS header_list->text, header_list->slen, message);
8523533c
TK
130 if (bmiErrorIsFatal(err) == BMI_TRUE) {
131 err_loc = bmiErrorGetLocation(err);
132 err_type = bmiErrorGetType(err);
133 log_write(0, LOG_PANIC,
134 "bmi error [loc %d type %d]: bmiAccumulateHeaders() failed.", (int)err_loc, (int)err_type);
135 bmiFreeMessage(message);
136 bmiFreeSystem(system);
137 return NULL;
138 };
139 header_list = header_list->next;
140 };
141 err = bmiEndHeaders(message);
142 if (bmiErrorIsFatal(err) == BMI_TRUE) {
143 err_loc = bmiErrorGetLocation(err);
144 err_type = bmiErrorGetType(err);
145 log_write(0, LOG_PANIC,
146 "bmi error [loc %d type %d]: bmiEndHeaders() failed.", (int)err_loc, (int)err_type);
147 bmiFreeMessage(message);
148 bmiFreeSystem(system);
149 return NULL;
150 };
8e669ac1 151
8523533c
TK
152 /* Send body */
153 data_file = fdopen(data_fd,"r");
154 do {
155 j = fread(data_buffer, 1, sizeof(data_buffer), data_file);
156 if (j > 0) {
5903c6ff 157 err = bmiAccumulateBody(CCS data_buffer, j, message);
8523533c
TK
158 if (bmiErrorIsFatal(err) == BMI_TRUE) {
159 err_loc = bmiErrorGetLocation(err);
160 err_type = bmiErrorGetType(err);
161 log_write(0, LOG_PANIC,
162 "bmi error [loc %d type %d]: bmiAccumulateBody() failed.", (int)err_loc, (int)err_type);
163 bmiFreeMessage(message);
164 bmiFreeSystem(system);
165 return NULL;
166 };
167 };
168 } while (j > 0);
169 err = bmiEndBody(message);
170 if (bmiErrorIsFatal(err) == BMI_TRUE) {
171 err_loc = bmiErrorGetLocation(err);
172 err_type = bmiErrorGetType(err);
173 log_write(0, LOG_PANIC,
174 "bmi error [loc %d type %d]: bmiEndBody() failed.", (int)err_loc, (int)err_type);
175 bmiFreeMessage(message);
176 bmiFreeSystem(system);
177 return NULL;
178 };
8e669ac1
PH
179
180
8523533c
TK
181 /* End message */
182 err = bmiEndMessage(message);
183 if (bmiErrorIsFatal(err) == BMI_TRUE) {
184 err_loc = bmiErrorGetLocation(err);
185 err_type = bmiErrorGetType(err);
186 log_write(0, LOG_PANIC,
187 "bmi error [loc %d type %d]: bmiEndMessage() failed.", (int)err_loc, (int)err_type);
188 bmiFreeMessage(message);
189 bmiFreeSystem(system);
190 return NULL;
191 };
8e669ac1 192
f3ebb786
JH
193 /* Get store for the verdict string. Since we are processing message data, assume that
194 the verdict is tainted. XXX this should use a growable-string */
195
196 verdicts = store_get(1, TRUE);
8523533c 197 *verdicts = '\0';
8e669ac1 198
8523533c
TK
199 for ( err = bmiAccessFirstVerdict(message, &verdict);
200 verdict != NULL;
201 err = bmiAccessNextVerdict(message, verdict, &verdict) ) {
202 char *verdict_str;
203
204 err = bmiCreateStrFromVerdict(verdict,&verdict_str);
f3ebb786
JH
205 if (!store_extend(verdicts, TRUE,
206 Ustrlen(verdicts)+1, Ustrlen(verdicts)+1+strlen(verdict_str)+1)) {
8523533c
TK
207 /* can't allocate more store */
208 return NULL;
209 };
210 if (*verdicts != '\0')
211 Ustrcat(verdicts, US ":");
212 Ustrcat(verdicts, US verdict_str);
213 bmiFreeStr(verdict_str);
214 };
215
216 DEBUG(D_receive) debug_printf("bmi verdicts: %s\n", verdicts);
217
218 if (Ustrlen(verdicts) == 0)
219 return NULL;
220 else
221 return verdicts;
222}
223
224
225int bmi_get_delivery_status(uschar *base64_verdict) {
226 BmiError err;
227 BmiErrorLocation err_loc;
228 BmiErrorType err_type;
229 BmiVerdict *verdict = NULL;
230 int rc = 1; /* deliver by default */
8e669ac1 231
8523533c
TK
232 /* always deliver when there is no verdict */
233 if (base64_verdict == NULL)
234 return 1;
235
236 /* create verdict from base64 string */
237 err = bmiCreateVerdictFromStr(CS base64_verdict, &verdict);
238 if (bmiErrorIsFatal(err) == BMI_TRUE) {
239 err_loc = bmiErrorGetLocation(err);
240 err_type = bmiErrorGetType(err);
241 log_write(0, LOG_PANIC,
242 "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict);
243 return 1;
244 };
245
246 err = bmiVerdictError(verdict);
247 if (bmiErrorIsFatal(err) == BMI_TRUE) {
248 /* deliver normally due to error */
249 rc = 1;
250 }
251 else if (bmiVerdictDestinationIsDefault(verdict) == BMI_TRUE) {
252 /* deliver normally */
8e669ac1 253 rc = 1;
8523533c
TK
254 }
255 else if (bmiVerdictAccessDestination(verdict) == NULL) {
256 /* do not deliver */
257 rc = 0;
258 }
259 else {
260 /* deliver to alternate location */
261 rc = 1;
262 };
8e669ac1 263
8523533c
TK
264 bmiFreeVerdict(verdict);
265 return rc;
266}
267
268
269uschar *bmi_get_alt_location(uschar *base64_verdict) {
270 BmiError err;
271 BmiErrorLocation err_loc;
272 BmiErrorType err_type;
273 BmiVerdict *verdict = NULL;
274 uschar *rc = NULL;
8e669ac1 275
8523533c
TK
276 /* always deliver when there is no verdict */
277 if (base64_verdict == NULL)
278 return NULL;
279
280 /* create verdict from base64 string */
281 err = bmiCreateVerdictFromStr(CS base64_verdict, &verdict);
282 if (bmiErrorIsFatal(err) == BMI_TRUE) {
283 err_loc = bmiErrorGetLocation(err);
284 err_type = bmiErrorGetType(err);
285 log_write(0, LOG_PANIC,
286 "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict);
287 return NULL;
288 };
8e669ac1 289
8523533c
TK
290 err = bmiVerdictError(verdict);
291 if (bmiErrorIsFatal(err) == BMI_TRUE) {
292 /* deliver normally due to error */
293 rc = NULL;
294 }
295 else if (bmiVerdictDestinationIsDefault(verdict) == BMI_TRUE) {
296 /* deliver normally */
8e669ac1 297 rc = NULL;
8523533c
TK
298 }
299 else if (bmiVerdictAccessDestination(verdict) == NULL) {
300 /* do not deliver */
301 rc = NULL;
302 }
303 else {
304 /* deliver to alternate location */
f3ebb786 305 rc = store_get(strlen(bmiVerdictAccessDestination(verdict))+1, TRUE);
8523533c
TK
306 Ustrcpy(rc, bmiVerdictAccessDestination(verdict));
307 rc[strlen(bmiVerdictAccessDestination(verdict))] = '\0';
308 };
8e669ac1 309
8523533c
TK
310 bmiFreeVerdict(verdict);
311 return rc;
312}
313
314uschar *bmi_get_base64_verdict(uschar *bmi_local_part, uschar *bmi_domain) {
315 BmiError err;
316 BmiErrorLocation err_loc;
317 BmiErrorType err_type;
318 BmiVerdict *verdict = NULL;
319 const BmiRecipient *recipient = NULL;
320 const char *verdict_str = NULL;
321 uschar *verdict_ptr;
322 uschar *verdict_buffer = NULL;
323 int sep = 0;
8e669ac1 324
8523533c
TK
325 /* return nothing if there are no verdicts available */
326 if (bmi_verdicts == NULL)
327 return NULL;
8e669ac1 328
8523533c 329 /* allocate room for the b64 verdict string */
f3ebb786 330 verdict_buffer = store_get(Ustrlen(bmi_verdicts)+1, TRUE);
8e669ac1 331
8523533c
TK
332 /* loop through verdicts */
333 verdict_ptr = bmi_verdicts;
5903c6ff 334 while ((verdict_str = CCS string_nextinlist(&verdict_ptr, &sep,
8523533c
TK
335 verdict_buffer,
336 Ustrlen(bmi_verdicts)+1)) != NULL) {
8e669ac1 337
8523533c
TK
338 /* create verdict from base64 string */
339 err = bmiCreateVerdictFromStr(verdict_str, &verdict);
340 if (bmiErrorIsFatal(err) == BMI_TRUE) {
341 err_loc = bmiErrorGetLocation(err);
342 err_type = bmiErrorGetType(err);
343 log_write(0, LOG_PANIC,
344 "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, verdict_str);
345 return NULL;
346 };
8e669ac1 347
8523533c
TK
348 /* loop through rcpts for this verdict */
349 for ( recipient = bmiVerdictAccessFirstRecipient(verdict);
350 recipient != NULL;
351 recipient = bmiVerdictAccessNextRecipient(verdict, recipient)) {
352 uschar *rcpt_local_part;
353 uschar *rcpt_domain;
8e669ac1 354
8523533c 355 /* compare address against our subject */
5903c6ff 356 rcpt_local_part = US bmiRecipientAccessAddress(recipient);
8523533c
TK
357 rcpt_domain = Ustrchr(rcpt_local_part,'@');
358 if (rcpt_domain == NULL) {
359 rcpt_domain = US"";
360 }
361 else {
362 *rcpt_domain = '\0';
363 rcpt_domain++;
364 };
365
366 if ( (strcmpic(rcpt_local_part, bmi_local_part) == 0) &&
367 (strcmpic(rcpt_domain, bmi_domain) == 0) ) {
368 /* found verdict */
369 bmiFreeVerdict(verdict);
5903c6ff 370 return US verdict_str;
8e669ac1 371 };
8523533c 372 };
8e669ac1 373
8523533c
TK
374 bmiFreeVerdict(verdict);
375 };
8e669ac1 376
8523533c
TK
377 return NULL;
378}
379
380
381uschar *bmi_get_base64_tracker_verdict(uschar *base64_verdict) {
382 BmiError err;
383 BmiErrorLocation err_loc;
384 BmiErrorType err_type;
385 BmiVerdict *verdict = NULL;
386 uschar *rc = NULL;
8e669ac1 387
8523533c
TK
388 /* always deliver when there is no verdict */
389 if (base64_verdict == NULL)
390 return NULL;
391
392 /* create verdict from base64 string */
393 err = bmiCreateVerdictFromStr(CS base64_verdict, &verdict);
394 if (bmiErrorIsFatal(err) == BMI_TRUE) {
395 err_loc = bmiErrorGetLocation(err);
396 err_type = bmiErrorGetType(err);
397 log_write(0, LOG_PANIC,
398 "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict);
399 return NULL;
400 };
8e669ac1 401
8523533c
TK
402 /* create old tracker string from verdict */
403 err = bmiCreateOldStrFromVerdict(verdict, &rc);
404 if (bmiErrorIsFatal(err) == BMI_TRUE) {
405 err_loc = bmiErrorGetLocation(err);
406 err_type = bmiErrorGetType(err);
407 log_write(0, LOG_PANIC,
408 "bmi error [loc %d type %d]: bmiCreateOldStrFromVerdict() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict);
409 return NULL;
410 };
8e669ac1 411
8523533c
TK
412 bmiFreeVerdict(verdict);
413 return rc;
414}
415
416
417int bmi_check_rule(uschar *base64_verdict, uschar *option_list) {
418 BmiError err;
419 BmiErrorLocation err_loc;
420 BmiErrorType err_type;
421 BmiVerdict *verdict = NULL;
422 int rc = 0;
423 uschar *rule_num;
424 uschar *rule_ptr;
425 uschar rule_buffer[32];
426 int sep = 0;
8e669ac1
PH
427
428
8523533c
TK
429 /* no verdict -> no rule fired */
430 if (base64_verdict == NULL)
431 return 0;
8e669ac1 432
8523533c
TK
433 /* create verdict from base64 string */
434 err = bmiCreateVerdictFromStr(CS base64_verdict, &verdict);
435 if (bmiErrorIsFatal(err) == BMI_TRUE) {
436 err_loc = bmiErrorGetLocation(err);
437 err_type = bmiErrorGetType(err);
438 log_write(0, LOG_PANIC,
439 "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict);
440 return 0;
441 };
8e669ac1 442
8523533c
TK
443 err = bmiVerdictError(verdict);
444 if (bmiErrorIsFatal(err) == BMI_TRUE) {
445 /* error -> no rule fired */
446 bmiFreeVerdict(verdict);
447 return 0;
448 }
8e669ac1 449
8523533c
TK
450 /* loop through numbers */
451 rule_ptr = option_list;
452 while ((rule_num = string_nextinlist(&rule_ptr, &sep,
453 rule_buffer, 32)) != NULL) {
454 int rule_int = -1;
8e669ac1 455
8523533c 456 /* try to translate to int */
ff790e47 457 (void)sscanf(rule_num, "%d", &rule_int);
8523533c
TK
458 if (rule_int > 0) {
459 debug_printf("checking rule #%d\n", rule_int);
460 /* check if rule fired on the message */
461 if (bmiVerdictRuleFired(verdict, rule_int) == BMI_TRUE) {
462 debug_printf("rule #%d fired\n", rule_int);
463 rc = 1;
464 break;
465 };
466 };
467 };
468
469 bmiFreeVerdict(verdict);
470 return rc;
471};
472
473#endif