1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Code for calling Brightmail AntiSpam.
6 Copyright (c) Tom Kistner <tom@duncanthrax.net> 2004
10 #ifdef EXPERIMENTAL_BRIGHTMAIL
14 uschar
*bmi_current_optin
= NULL
;
16 uschar
*bmi_process_message(header_line
*header_list
, int data_fd
) {
17 BmiSystem
*system
= NULL
;
18 BmiMessage
*message
= NULL
;
20 BmiErrorLocation err_loc
;
21 BmiErrorType err_type
;
22 const BmiVerdict
*verdict
= NULL
;
24 uschar data_buffer
[4096];
25 uschar localhost
[] = "127.0.0.1";
27 uschar
*verdicts
= NULL
;
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
);
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
);
49 /* Send IP address of sending host */
50 if (sender_host_address
== NULL
)
51 host_address
= localhost
;
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
);
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
);
77 /* Send envelope recipients */
78 for(i
=0;i
<recipients_count
;i
++) {
79 recipient_item
*r
= recipients_list
+ i
;
80 BmiOptin
*optin
= NULL
;
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
);
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
);
96 err
= bmiAccumulateTO(CS r
->address
, optin
, message
);
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
);
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
);
122 /* Send message headers */
123 while (header_list
!= NULL
) {
124 /* skip deleted headers */
125 if (header_list
->type
== '*') {
126 header_list
= header_list
->next
;
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
);
139 header_list
= header_list
->next
;
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
);
153 data_file
= fdopen(data_fd
,"r");
155 j
= fread(data_buffer
, 1, sizeof(data_buffer
), data_file
);
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
);
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
);
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
);
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 */
196 verdicts
= store_get(1, TRUE
);
199 for ( err
= bmiAccessFirstVerdict(message
, &verdict
);
201 err
= bmiAccessNextVerdict(message
, verdict
, &verdict
) ) {
204 err
= bmiCreateStrFromVerdict(verdict
,&verdict_str
);
205 if (!store_extend(verdicts
, TRUE
,
206 Ustrlen(verdicts
)+1, Ustrlen(verdicts
)+1+strlen(verdict_str
)+1)) {
207 /* can't allocate more store */
210 if (*verdicts
!= '\0')
211 Ustrcat(verdicts
, US
":");
212 Ustrcat(verdicts
, US verdict_str
);
213 bmiFreeStr(verdict_str
);
216 DEBUG(D_receive
) debug_printf("bmi verdicts: %s\n", verdicts
);
218 if (Ustrlen(verdicts
) == 0)
225 int bmi_get_delivery_status(uschar
*base64_verdict
) {
227 BmiErrorLocation err_loc
;
228 BmiErrorType err_type
;
229 BmiVerdict
*verdict
= NULL
;
230 int rc
= 1; /* deliver by default */
232 /* always deliver when there is no verdict */
233 if (base64_verdict
== NULL
)
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
);
246 err
= bmiVerdictError(verdict
);
247 if (bmiErrorIsFatal(err
) == BMI_TRUE
) {
248 /* deliver normally due to error */
251 else if (bmiVerdictDestinationIsDefault(verdict
) == BMI_TRUE
) {
252 /* deliver normally */
255 else if (bmiVerdictAccessDestination(verdict
) == NULL
) {
260 /* deliver to alternate location */
264 bmiFreeVerdict(verdict
);
269 uschar
*bmi_get_alt_location(uschar
*base64_verdict
) {
271 BmiErrorLocation err_loc
;
272 BmiErrorType err_type
;
273 BmiVerdict
*verdict
= NULL
;
276 /* always deliver when there is no verdict */
277 if (base64_verdict
== NULL
)
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
);
290 err
= bmiVerdictError(verdict
);
291 if (bmiErrorIsFatal(err
) == BMI_TRUE
) {
292 /* deliver normally due to error */
295 else if (bmiVerdictDestinationIsDefault(verdict
) == BMI_TRUE
) {
296 /* deliver normally */
299 else if (bmiVerdictAccessDestination(verdict
) == NULL
) {
304 /* deliver to alternate location */
305 rc
= store_get(strlen(bmiVerdictAccessDestination(verdict
))+1, TRUE
);
306 Ustrcpy(rc
, bmiVerdictAccessDestination(verdict
));
307 rc
[strlen(bmiVerdictAccessDestination(verdict
))] = '\0';
310 bmiFreeVerdict(verdict
);
314 uschar
*bmi_get_base64_verdict(uschar
*bmi_local_part
, uschar
*bmi_domain
) {
316 BmiErrorLocation err_loc
;
317 BmiErrorType err_type
;
318 BmiVerdict
*verdict
= NULL
;
319 const BmiRecipient
*recipient
= NULL
;
320 const char *verdict_str
= NULL
;
322 uschar
*verdict_buffer
= NULL
;
325 /* return nothing if there are no verdicts available */
326 if (bmi_verdicts
== NULL
)
329 /* allocate room for the b64 verdict string */
330 verdict_buffer
= store_get(Ustrlen(bmi_verdicts
)+1, TRUE
);
332 /* loop through verdicts */
333 verdict_ptr
= bmi_verdicts
;
334 while ((verdict_str
= CCS
string_nextinlist(&verdict_ptr
, &sep
,
336 Ustrlen(bmi_verdicts
)+1)) != NULL
) {
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
);
348 /* loop through rcpts for this verdict */
349 for ( recipient
= bmiVerdictAccessFirstRecipient(verdict
);
351 recipient
= bmiVerdictAccessNextRecipient(verdict
, recipient
)) {
352 uschar
*rcpt_local_part
;
355 /* compare address against our subject */
356 rcpt_local_part
= US
bmiRecipientAccessAddress(recipient
);
357 rcpt_domain
= Ustrchr(rcpt_local_part
,'@');
358 if (rcpt_domain
== NULL
) {
366 if ( (strcmpic(rcpt_local_part
, bmi_local_part
) == 0) &&
367 (strcmpic(rcpt_domain
, bmi_domain
) == 0) ) {
369 bmiFreeVerdict(verdict
);
370 return US verdict_str
;
374 bmiFreeVerdict(verdict
);
381 uschar
*bmi_get_base64_tracker_verdict(uschar
*base64_verdict
) {
383 BmiErrorLocation err_loc
;
384 BmiErrorType err_type
;
385 BmiVerdict
*verdict
= NULL
;
388 /* always deliver when there is no verdict */
389 if (base64_verdict
== NULL
)
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
);
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
);
412 bmiFreeVerdict(verdict
);
417 int bmi_check_rule(uschar
*base64_verdict
, uschar
*option_list
) {
419 BmiErrorLocation err_loc
;
420 BmiErrorType err_type
;
421 BmiVerdict
*verdict
= NULL
;
425 uschar rule_buffer
[32];
429 /* no verdict -> no rule fired */
430 if (base64_verdict
== NULL
)
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
);
443 err
= bmiVerdictError(verdict
);
444 if (bmiErrorIsFatal(err
) == BMI_TRUE
) {
445 /* error -> no rule fired */
446 bmiFreeVerdict(verdict
);
450 /* loop through numbers */
451 rule_ptr
= option_list
;
452 while ((rule_num
= string_nextinlist(&rule_ptr
, &sep
,
453 rule_buffer
, 32)) != NULL
) {
456 /* try to translate to int */
457 (void)sscanf(rule_num
, "%d", &rule_int
);
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
);
469 bmiFreeVerdict(verdict
);