Expansions: new ${lheader:<name>}. Bug 2272
[exim.git] / src / src / bmi_spam.c
1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
4
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
14 uschar *bmi_current_optin = NULL;
15
16 uschar *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;
29
30 err = bmiInitSystem(BMI_VERSION, CS bmi_config_file, &system);
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;
54 err = bmiProcessConnection(CS host_address, message);
55 if (bmiErrorIsFatal(err) == BMI_TRUE) {
56 err_loc = bmiErrorGetLocation(err);
57 err_type = bmiErrorGetType(err);
58 log_write(0, LOG_PANIC,
59 "bmi error [loc %d type %d]: bmiProcessConnection() failed (IP %s).", (int)err_loc, (int)err_type, CS host_address);
60 bmiFreeMessage(message);
61 bmiFreeSystem(system);
62 return NULL;
63 };
64
65 /* Send envelope sender address */
66 err = bmiProcessFROM(CS sender_address, message);
67 if (bmiErrorIsFatal(err) == BMI_TRUE) {
68 err_loc = bmiErrorGetLocation(err);
69 err_type = bmiErrorGetType(err);
70 log_write(0, LOG_PANIC,
71 "bmi error [loc %d type %d]: bmiProcessFROM() failed (address %s).", (int)err_loc, (int)err_type, CS sender_address);
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;
81
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,
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);
90 if (optin != NULL)
91 bmiOptinFree(optin);
92 optin = NULL;
93 };
94 };
95
96 err = bmiAccumulateTO(CS r->address, optin, message);
97
98 if (optin != NULL)
99 bmiOptinFree(optin);
100
101 if (bmiErrorIsFatal(err) == BMI_TRUE) {
102 err_loc = bmiErrorGetLocation(err);
103 err_type = bmiErrorGetType(err);
104 log_write(0, LOG_PANIC,
105 "bmi error [loc %d type %d]: bmiAccumulateTO() failed (address %s).", (int)err_loc, (int)err_type, CS r->address);
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 };
121
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 };
129 err = bmiAccumulateHeaders(CCS header_list->text, header_list->slen, message);
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 };
151
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) {
157 err = bmiAccumulateBody(CCS data_buffer, j, message);
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 };
179
180
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 };
192
193 /* get store for the verdict string */
194 verdicts = store_get(1);
195 *verdicts = '\0';
196
197 for ( err = bmiAccessFirstVerdict(message, &verdict);
198 verdict != NULL;
199 err = bmiAccessNextVerdict(message, verdict, &verdict) ) {
200 char *verdict_str;
201
202 err = bmiCreateStrFromVerdict(verdict,&verdict_str);
203 if (!store_extend(verdicts, Ustrlen(verdicts)+1, Ustrlen(verdicts)+1+strlen(verdict_str)+1)) {
204 /* can't allocate more store */
205 return NULL;
206 };
207 if (*verdicts != '\0')
208 Ustrcat(verdicts, US ":");
209 Ustrcat(verdicts, US verdict_str);
210 bmiFreeStr(verdict_str);
211 };
212
213 DEBUG(D_receive) debug_printf("bmi verdicts: %s\n", verdicts);
214
215 if (Ustrlen(verdicts) == 0)
216 return NULL;
217 else
218 return verdicts;
219 }
220
221
222 int bmi_get_delivery_status(uschar *base64_verdict) {
223 BmiError err;
224 BmiErrorLocation err_loc;
225 BmiErrorType err_type;
226 BmiVerdict *verdict = NULL;
227 int rc = 1; /* deliver by default */
228
229 /* always deliver when there is no verdict */
230 if (base64_verdict == NULL)
231 return 1;
232
233 /* create verdict from base64 string */
234 err = bmiCreateVerdictFromStr(CS base64_verdict, &verdict);
235 if (bmiErrorIsFatal(err) == BMI_TRUE) {
236 err_loc = bmiErrorGetLocation(err);
237 err_type = bmiErrorGetType(err);
238 log_write(0, LOG_PANIC,
239 "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict);
240 return 1;
241 };
242
243 err = bmiVerdictError(verdict);
244 if (bmiErrorIsFatal(err) == BMI_TRUE) {
245 /* deliver normally due to error */
246 rc = 1;
247 }
248 else if (bmiVerdictDestinationIsDefault(verdict) == BMI_TRUE) {
249 /* deliver normally */
250 rc = 1;
251 }
252 else if (bmiVerdictAccessDestination(verdict) == NULL) {
253 /* do not deliver */
254 rc = 0;
255 }
256 else {
257 /* deliver to alternate location */
258 rc = 1;
259 };
260
261 bmiFreeVerdict(verdict);
262 return rc;
263 }
264
265
266 uschar *bmi_get_alt_location(uschar *base64_verdict) {
267 BmiError err;
268 BmiErrorLocation err_loc;
269 BmiErrorType err_type;
270 BmiVerdict *verdict = NULL;
271 uschar *rc = NULL;
272
273 /* always deliver when there is no verdict */
274 if (base64_verdict == NULL)
275 return NULL;
276
277 /* create verdict from base64 string */
278 err = bmiCreateVerdictFromStr(CS base64_verdict, &verdict);
279 if (bmiErrorIsFatal(err) == BMI_TRUE) {
280 err_loc = bmiErrorGetLocation(err);
281 err_type = bmiErrorGetType(err);
282 log_write(0, LOG_PANIC,
283 "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict);
284 return NULL;
285 };
286
287 err = bmiVerdictError(verdict);
288 if (bmiErrorIsFatal(err) == BMI_TRUE) {
289 /* deliver normally due to error */
290 rc = NULL;
291 }
292 else if (bmiVerdictDestinationIsDefault(verdict) == BMI_TRUE) {
293 /* deliver normally */
294 rc = NULL;
295 }
296 else if (bmiVerdictAccessDestination(verdict) == NULL) {
297 /* do not deliver */
298 rc = NULL;
299 }
300 else {
301 /* deliver to alternate location */
302 rc = store_get(strlen(bmiVerdictAccessDestination(verdict))+1);
303 Ustrcpy(rc, bmiVerdictAccessDestination(verdict));
304 rc[strlen(bmiVerdictAccessDestination(verdict))] = '\0';
305 };
306
307 bmiFreeVerdict(verdict);
308 return rc;
309 }
310
311 uschar *bmi_get_base64_verdict(uschar *bmi_local_part, uschar *bmi_domain) {
312 BmiError err;
313 BmiErrorLocation err_loc;
314 BmiErrorType err_type;
315 BmiVerdict *verdict = NULL;
316 const BmiRecipient *recipient = NULL;
317 const char *verdict_str = NULL;
318 uschar *verdict_ptr;
319 uschar *verdict_buffer = NULL;
320 int sep = 0;
321
322 /* return nothing if there are no verdicts available */
323 if (bmi_verdicts == NULL)
324 return NULL;
325
326 /* allocate room for the b64 verdict string */
327 verdict_buffer = store_get(Ustrlen(bmi_verdicts)+1);
328
329 /* loop through verdicts */
330 verdict_ptr = bmi_verdicts;
331 while ((verdict_str = CCS string_nextinlist(&verdict_ptr, &sep,
332 verdict_buffer,
333 Ustrlen(bmi_verdicts)+1)) != NULL) {
334
335 /* create verdict from base64 string */
336 err = bmiCreateVerdictFromStr(verdict_str, &verdict);
337 if (bmiErrorIsFatal(err) == BMI_TRUE) {
338 err_loc = bmiErrorGetLocation(err);
339 err_type = bmiErrorGetType(err);
340 log_write(0, LOG_PANIC,
341 "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, verdict_str);
342 return NULL;
343 };
344
345 /* loop through rcpts for this verdict */
346 for ( recipient = bmiVerdictAccessFirstRecipient(verdict);
347 recipient != NULL;
348 recipient = bmiVerdictAccessNextRecipient(verdict, recipient)) {
349 uschar *rcpt_local_part;
350 uschar *rcpt_domain;
351
352 /* compare address against our subject */
353 rcpt_local_part = US bmiRecipientAccessAddress(recipient);
354 rcpt_domain = Ustrchr(rcpt_local_part,'@');
355 if (rcpt_domain == NULL) {
356 rcpt_domain = US"";
357 }
358 else {
359 *rcpt_domain = '\0';
360 rcpt_domain++;
361 };
362
363 if ( (strcmpic(rcpt_local_part, bmi_local_part) == 0) &&
364 (strcmpic(rcpt_domain, bmi_domain) == 0) ) {
365 /* found verdict */
366 bmiFreeVerdict(verdict);
367 return US verdict_str;
368 };
369 };
370
371 bmiFreeVerdict(verdict);
372 };
373
374 return NULL;
375 }
376
377
378 uschar *bmi_get_base64_tracker_verdict(uschar *base64_verdict) {
379 BmiError err;
380 BmiErrorLocation err_loc;
381 BmiErrorType err_type;
382 BmiVerdict *verdict = NULL;
383 uschar *rc = NULL;
384
385 /* always deliver when there is no verdict */
386 if (base64_verdict == NULL)
387 return NULL;
388
389 /* create verdict from base64 string */
390 err = bmiCreateVerdictFromStr(CS base64_verdict, &verdict);
391 if (bmiErrorIsFatal(err) == BMI_TRUE) {
392 err_loc = bmiErrorGetLocation(err);
393 err_type = bmiErrorGetType(err);
394 log_write(0, LOG_PANIC,
395 "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict);
396 return NULL;
397 };
398
399 /* create old tracker string from verdict */
400 err = bmiCreateOldStrFromVerdict(verdict, &rc);
401 if (bmiErrorIsFatal(err) == BMI_TRUE) {
402 err_loc = bmiErrorGetLocation(err);
403 err_type = bmiErrorGetType(err);
404 log_write(0, LOG_PANIC,
405 "bmi error [loc %d type %d]: bmiCreateOldStrFromVerdict() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict);
406 return NULL;
407 };
408
409 bmiFreeVerdict(verdict);
410 return rc;
411 }
412
413
414 int bmi_check_rule(uschar *base64_verdict, uschar *option_list) {
415 BmiError err;
416 BmiErrorLocation err_loc;
417 BmiErrorType err_type;
418 BmiVerdict *verdict = NULL;
419 int rc = 0;
420 uschar *rule_num;
421 uschar *rule_ptr;
422 uschar rule_buffer[32];
423 int sep = 0;
424
425
426 /* no verdict -> no rule fired */
427 if (base64_verdict == NULL)
428 return 0;
429
430 /* create verdict from base64 string */
431 err = bmiCreateVerdictFromStr(CS base64_verdict, &verdict);
432 if (bmiErrorIsFatal(err) == BMI_TRUE) {
433 err_loc = bmiErrorGetLocation(err);
434 err_type = bmiErrorGetType(err);
435 log_write(0, LOG_PANIC,
436 "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict);
437 return 0;
438 };
439
440 err = bmiVerdictError(verdict);
441 if (bmiErrorIsFatal(err) == BMI_TRUE) {
442 /* error -> no rule fired */
443 bmiFreeVerdict(verdict);
444 return 0;
445 }
446
447 /* loop through numbers */
448 rule_ptr = option_list;
449 while ((rule_num = string_nextinlist(&rule_ptr, &sep,
450 rule_buffer, 32)) != NULL) {
451 int rule_int = -1;
452
453 /* try to translate to int */
454 (void)sscanf(rule_num, "%d", &rule_int);
455 if (rule_int > 0) {
456 debug_printf("checking rule #%d\n", rule_int);
457 /* check if rule fired on the message */
458 if (bmiVerdictRuleFired(verdict, rule_int) == BMI_TRUE) {
459 debug_printf("rule #%d fired\n", rule_int);
460 rc = 1;
461 break;
462 };
463 };
464 };
465
466 bmiFreeVerdict(verdict);
467 return rc;
468 };
469
470 #endif