1 /* $NetBSD: header_body_checks.c,v 1.3 2020/03/18 19:05:16 christos Exp $ */
2
3 /*++
4 /* NAME
5 /* header_body_checks 3
6 /* SUMMARY
7 /* header/body checks
8 /* SYNOPSIS
9 /* #include <header_body_checks.h>
10 /*
11 /* typedef struct {
12 /* void (*logger) (void *context, const char *action,
13 /* const char *where, const char *line,
14 /* const char *optional_text);
15 /* void (*prepend) (void *context, int rec_type,
16 /* const char *buf, ssize_t len, off_t offset);
17 /* char *(*extend) (void *context, const char *command,
18 /* ssize_t cmd_len, const char *cmd_args,
19 /* const char *where, const char *line,
20 /* ssize_t line_len, off_t offset);
21 /* } HBC_CALL_BACKS;
22 /*
23 /* HBC_CHECKS *hbc_header_checks_create(
24 /* header_checks_name, header_checks_value
25 /* mime_header_checks_name, mime_header_checks_value,
26 /* nested_header_checks_name, nested_header_checks_value,
27 /* call_backs)
28 /* const char *header_checks_name;
29 /* const char *header_checks_value;
30 /* const char *mime_header_checks_name;
31 /* const char *mime_header_checks_value;
32 /* const char *nested_header_checks_name;
33 /* const char *nested_header_checks_value;
34 /* HBC_CALL_BACKS *call_backs;
35 /*
36 /* HBC_CHECKS *hbc_body_checks_create(
37 /* body_checks_name, body_checks_value,
38 /* call_backs)
39 /* const char *body_checks_name;
40 /* const char *body_checks_value;
41 /* HBC_CALL_BACKS *call_backs;
42 /*
43 /* char *hbc_header_checks(context, hbc, header_class, hdr_opts, header)
44 /* void *context;
45 /* HBC_CHECKS *hbc;
46 /* int header_class;
47 /* const HEADER_OPTS *hdr_opts;
48 /* VSTRING *header;
49 /*
50 /* char *hbc_body_checks(context, hbc, body_line, body_line_len)
51 /* void *context;
52 /* HBC_CHECKS *hbc;
53 /* const char *body_line;
54 /* ssize_t body_line_len;
55 /*
56 /* void hbc_header_checks_free(hbc)
57 /* HBC_CHECKS *hbc;
58 /*
59 /* void hbc_body_checks_free(hbc)
60 /* HBC_CHECKS *hbc;
61 /* DESCRIPTION
62 /* This module implements header_checks and body_checks.
63 /* Actions are executed while mail is being delivered. The
64 /* following actions are recognized: INFO, WARN, REPLACE,
65 /* PREPEND, IGNORE, DUNNO, and OK. These actions are safe for
66 /* use in delivery agents.
67 /*
68 /* Other actions may be supplied via the extension mechanism
69 /* described below. For example, actions that change the
70 /* message delivery time or destination. Such actions do not
71 /* make sense in delivery agents, but they can be appropriate
72 /* in, for example, before-queue filters.
73 /*
74 /* hbc_header_checks_create() creates a context for header
75 /* inspection. This function is typically called once during
76 /* program initialization. The result is a null pointer when
77 /* all _value arguments specify zero-length strings; in this
78 /* case, hbc_header_checks() and hbc_header_checks_free() must
79 /* not be called.
80 /*
81 /* hbc_header_checks() inspects the specified logical header.
82 /* The result is either the original header, HBC_CHECKS_STAT_IGNORE
83 /* (meaning: discard the header), HBC_CHECKS_STAT_ERROR, or a
84 /* new header (meaning: replace the header and destroy the new
85 /* header with myfree()).
86 /*
87 /* hbc_header_checks_free() returns memory to the pool.
88 /*
89 /* hbc_body_checks_create(), hbc_body_checks(), hbc_body_free()
90 /* perform similar functions for body lines.
91 /*
92 /* Arguments:
93 /* .IP body_line
94 /* One line of body text.
95 /* .IP body_line_len
96 /* Body line length.
97 /* .IP call_backs
98 /* Table with call-back function pointers. This argument is
99 /* not copied. Note: the description below is not necessarily
100 /* in data structure order.
101 /* .RS
102 /* .IP logger
103 /* Call-back function for logging an action with the action's
104 /* name in lower case, a location within a message ("header"
105 /* or "body"), the content of the header or body line that
106 /* triggered the action, and optional text or a zero-length
107 /* string. This call-back feature must be specified.
108 /* .IP prepend
109 /* Call-back function for the PREPEND action. The arguments
110 /* are the same as those of mime_state(3) body output call-back
111 /* functions. Specify a null pointer to disable this action.
112 /* .IP extend
113 /* Call-back function that logs and executes other actions.
114 /* This function receives as arguments the command name and
115 /* name length, the command arguments if any, the location
116 /* within the message ("header" or "body"), the content and
117 /* length of the header or body line that triggered the action,
118 /* and the input byte offset within the current header or body
119 /* segment. The result value is either the original line
120 /* argument, HBC_CHECKS_STAT_IGNORE (delete the line from the
121 /* input stream) or HBC_CHECKS_STAT_UNKNOWN (the command was
122 /* not recognized). Specify a null pointer to disable this
123 /* feature.
124 /* .RE
125 /* .IP context
126 /* Application context for call-back functions specified with the
127 /* call_backs argument.
128 /* .IP header
129 /* A logical message header. Lines within a multi-line header
130 /* are separated by a newline character.
131 /* .IP "header_checks_name, mime_header_checks_name"
132 /* .IP "nested_header_checks_name, body_checks_name"
133 /* The main.cf configuration parameter names for header and body
134 /* map lists.
135 /* .IP "header_checks_value, mime_header_checks_value"
136 /* .IP "nested_header_checks_value, body_checks_value"
137 /* The values of main.cf configuration parameters for header and body
138 /* map lists. Specify a zero-length string to disable a specific list.
139 /* .IP header_class
140 /* A number in the range MIME_HDR_FIRST..MIME_HDR_LAST.
141 /* .IP hbc
142 /* A handle created with hbc_header_checks_create() or
143 /* hbc_body_checks_create().
144 /* .IP hdr_opts
145 /* Message header properties.
146 /* SEE ALSO
147 /* msg(3) diagnostics interface
148 /* DIAGNOSTICS
149 /* Fatal errors: memory allocation problem.
150 /* LICENSE
151 /* .ad
152 /* .fi
153 /* The Secure Mailer license must be distributed with this software.
154 /* AUTHOR(S)
155 /* Wietse Venema
156 /* IBM T.J. Watson Research
157 /* P.O. Box 704
158 /* Yorktown Heights, NY 10598, USA
159 /*
160 /* Wietse Venema
161 /* Google, Inc.
162 /* 111 8th Avenue
163 /* New York, NY 10011, USA
164 /*--*/
165
166 /* System library. */
167
168 #include <sys_defs.h>
169 #include <ctype.h>
170 #include <string.h>
171 #ifdef STRCASECMP_IN_STRINGS_H
172 #include <strings.h>
173 #endif
174
175 /* Utility library. */
176
177 #include <msg.h>
178 #include <mymalloc.h>
179
180 /* Global library. */
181
182 #include <mime_state.h>
183 #include <rec_type.h>
184 #include <is_header.h>
185 #include <cleanup_user.h>
186 #include <dsn_util.h>
187 #include <header_body_checks.h>
188
189 /* Application-specific. */
190
191 /*
192 * Something that is guaranteed to be different from a real string result
193 * from header/body_checks.
194 */
195 char hbc_checks_error;
196 const char hbc_checks_unknown;
197
198 /*
199 * Header checks are stored as an array of HBC_MAP_INFO structures, one
200 * structure for each header class (MIME_HDR_PRIMARY, MIME_HDR_MULTIPART, or
201 * MIME_HDR_NESTED).
202 *
203 * Body checks are stored as one single HBC_MAP_INFO structure, because we make
204 * no distinction between body segments.
205 */
206 #define HBC_HEADER_INDEX(class) ((class) - MIME_HDR_FIRST)
207 #define HBC_BODY_INDEX (0)
208
209 #define HBC_INIT(hbc, index, name, value) do { \
210 HBC_MAP_INFO *_mp = (hbc)->map_info + (index); \
211 if (*(value) != 0) { \
212 _mp->map_class = (name); \
213 _mp->maps = maps_create((name), (value), DICT_FLAG_LOCK); \
214 } else { \
215 _mp->map_class = 0; \
216 _mp->maps = 0; \
217 } \
218 } while (0)
219
220 /* How does the action routine know where we are? */
221
222 #define HBC_CTXT_HEADER "header"
223 #define HBC_CTXT_BODY "body"
224
225 /* Silly little macros. */
226
227 #define STR(x) vstring_str(x)
228 #define LEN(x) VSTRING_LEN(x)
229
230 /* hbc_action - act upon a header/body match */
231
hbc_action(void * context,HBC_CALL_BACKS * cb,const char * map_class,const char * where,const char * cmd,const char * line,ssize_t line_len,off_t offset)232 static char *hbc_action(void *context, HBC_CALL_BACKS *cb,
233 const char *map_class, const char *where,
234 const char *cmd, const char *line,
235 ssize_t line_len, off_t offset)
236 {
237 const char *cmd_args = cmd + strcspn(cmd, " \t");
238 ssize_t cmd_len = cmd_args - cmd;
239 char *ret;
240
241 /*
242 * XXX We don't use a hash table for action lookup. Mail rarely triggers
243 * an action, and mail that triggers multiple actions is even rarer.
244 * Setting up the hash table costs more than we would gain from using it.
245 */
246 while (*cmd_args && ISSPACE(*cmd_args))
247 cmd_args++;
248
249 #define STREQUAL(x,y,l) (strncasecmp((x), (y), (l)) == 0 && (y)[l] == 0)
250
251 if (cb->extend
252 && (ret = cb->extend(context, cmd, cmd_len, cmd_args, where, line,
253 line_len, offset)) != HBC_CHECKS_STAT_UNKNOWN)
254 return (ret);
255
256 if (STREQUAL(cmd, "WARN", cmd_len)) {
257 cb->logger(context, "warning", where, line, cmd_args);
258 return ((char *) line);
259 }
260 if (STREQUAL(cmd, "INFO", cmd_len)) {
261 cb->logger(context, "info", where, line, cmd_args);
262 return ((char *) line);
263 }
264 if (STREQUAL(cmd, "REPLACE", cmd_len)) {
265 if (*cmd_args == 0) {
266 msg_warn("REPLACE action without text in %s map", map_class);
267 return ((char *) line);
268 } else if (strcmp(where, HBC_CTXT_HEADER) == 0
269 && !is_header(cmd_args)) {
270 msg_warn("bad REPLACE header text \"%s\" in %s map -- "
271 "need \"headername: headervalue\"", cmd_args, map_class);
272 return ((char *) line);
273 } else {
274 cb->logger(context, "replace", where, line, cmd_args);
275 return (mystrdup(cmd_args));
276 }
277 }
278 if (cb->prepend && STREQUAL(cmd, "PREPEND", cmd_len)) {
279 if (*cmd_args == 0) {
280 msg_warn("PREPEND action without text in %s map", map_class);
281 } else if (strcmp(where, HBC_CTXT_HEADER) == 0
282 && !is_header(cmd_args)) {
283 msg_warn("bad PREPEND header text \"%s\" in %s map -- "
284 "need \"headername: headervalue\"", cmd_args, map_class);
285 } else {
286 cb->logger(context, "prepend", where, line, cmd_args);
287 cb->prepend(context, REC_TYPE_NORM, cmd_args, strlen(cmd_args), offset);
288 }
289 return ((char *) line);
290 }
291 if (STREQUAL(cmd, "STRIP", cmd_len)) {
292 cb->logger(context, "strip", where, line, cmd_args);
293 return (HBC_CHECKS_STAT_IGNORE);
294 }
295 /* Allow and ignore optional text after the action. */
296
297 if (STREQUAL(cmd, "IGNORE", cmd_len))
298 /* XXX Not logged for compatibility with cleanup(8). */
299 return (HBC_CHECKS_STAT_IGNORE);
300
301 if (STREQUAL(cmd, "DUNNO", cmd_len) /* preferred */
302 ||STREQUAL(cmd, "OK", cmd_len)) /* compatibility */
303 return ((char *) line);
304
305 msg_warn("unsupported command in %s map: %s", map_class, cmd);
306 return ((char *) line);
307 }
308
309 /* hbc_header_checks - process one complete header line */
310
hbc_header_checks(void * context,HBC_CHECKS * hbc,int header_class,const HEADER_OPTS * hdr_opts,VSTRING * header,off_t offset)311 char *hbc_header_checks(void *context, HBC_CHECKS *hbc, int header_class,
312 const HEADER_OPTS *hdr_opts,
313 VSTRING *header, off_t offset)
314 {
315 const char *myname = "hbc_header_checks";
316 const char *action;
317 HBC_MAP_INFO *mp;
318
319 if (msg_verbose)
320 msg_info("%s: '%.30s'", myname, STR(header));
321
322 /*
323 * XXX This is for compatibility with the cleanup(8) server.
324 */
325 if (hdr_opts && (hdr_opts->flags & HDR_OPT_MIME))
326 header_class = MIME_HDR_MULTIPART;
327
328 mp = hbc->map_info + HBC_HEADER_INDEX(header_class);
329
330 if (mp->maps != 0 && (action = maps_find(mp->maps, STR(header), 0)) != 0) {
331 return (hbc_action(context, hbc->call_backs,
332 mp->map_class, HBC_CTXT_HEADER, action,
333 STR(header), LEN(header), offset));
334 } else if (mp->maps && mp->maps->error) {
335 return (HBC_CHECKS_STAT_ERROR);
336 } else {
337 return (STR(header));
338 }
339 }
340
341 /* hbc_body_checks - inspect one body record */
342
hbc_body_checks(void * context,HBC_CHECKS * hbc,const char * line,ssize_t len,off_t offset)343 char *hbc_body_checks(void *context, HBC_CHECKS *hbc, const char *line,
344 ssize_t len, off_t offset)
345 {
346 const char *myname = "hbc_body_checks";
347 const char *action;
348 HBC_MAP_INFO *mp;
349
350 if (msg_verbose)
351 msg_info("%s: '%.30s'", myname, line);
352
353 mp = hbc->map_info;
354
355 if ((action = maps_find(mp->maps, line, 0)) != 0) {
356 return (hbc_action(context, hbc->call_backs,
357 mp->map_class, HBC_CTXT_BODY, action,
358 line, len, offset));
359 } else if (mp->maps->error) {
360 return (HBC_CHECKS_STAT_ERROR);
361 } else {
362 return ((char *) line);
363 }
364 }
365
366 /* hbc_header_checks_create - create header checking context */
367
hbc_header_checks_create(const char * header_checks_name,const char * header_checks_value,const char * mime_header_checks_name,const char * mime_header_checks_value,const char * nested_header_checks_name,const char * nested_header_checks_value,HBC_CALL_BACKS * call_backs)368 HBC_CHECKS *hbc_header_checks_create(const char *header_checks_name,
369 const char *header_checks_value,
370 const char *mime_header_checks_name,
371 const char *mime_header_checks_value,
372 const char *nested_header_checks_name,
373 const char *nested_header_checks_value,
374 HBC_CALL_BACKS *call_backs)
375 {
376 HBC_CHECKS *hbc;
377
378 /*
379 * Optimize for the common case.
380 */
381 if (*header_checks_value == 0 && *mime_header_checks_value == 0
382 && *nested_header_checks_value == 0) {
383 return (0);
384 } else {
385 hbc = (HBC_CHECKS *) mymalloc(sizeof(*hbc)
386 + (MIME_HDR_LAST - MIME_HDR_FIRST) * sizeof(HBC_MAP_INFO));
387 hbc->call_backs = call_backs;
388 HBC_INIT(hbc, HBC_HEADER_INDEX(MIME_HDR_PRIMARY),
389 header_checks_name, header_checks_value);
390 HBC_INIT(hbc, HBC_HEADER_INDEX(MIME_HDR_MULTIPART),
391 mime_header_checks_name, mime_header_checks_value);
392 HBC_INIT(hbc, HBC_HEADER_INDEX(MIME_HDR_NESTED),
393 nested_header_checks_name, nested_header_checks_value);
394 return (hbc);
395 }
396 }
397
398 /* hbc_body_checks_create - create body checking context */
399
hbc_body_checks_create(const char * body_checks_name,const char * body_checks_value,HBC_CALL_BACKS * call_backs)400 HBC_CHECKS *hbc_body_checks_create(const char *body_checks_name,
401 const char *body_checks_value,
402 HBC_CALL_BACKS *call_backs)
403 {
404 HBC_CHECKS *hbc;
405
406 /*
407 * Optimize for the common case.
408 */
409 if (*body_checks_value == 0) {
410 return (0);
411 } else {
412 hbc = (HBC_CHECKS *) mymalloc(sizeof(*hbc));
413 hbc->call_backs = call_backs;
414 HBC_INIT(hbc, HBC_BODY_INDEX, body_checks_name, body_checks_value);
415 return (hbc);
416 }
417 }
418
419 /* _hbc_checks_free - destroy header/body checking context */
420
_hbc_checks_free(HBC_CHECKS * hbc,ssize_t len)421 void _hbc_checks_free(HBC_CHECKS *hbc, ssize_t len)
422 {
423 HBC_MAP_INFO *mp;
424
425 for (mp = hbc->map_info; mp < hbc->map_info + len; mp++)
426 if (mp->maps)
427 maps_free(mp->maps);
428 myfree((void *) hbc);
429 }
430
431 /*
432 * Test program. Specify the four maps on the command line, and feed a
433 * MIME-formatted message on stdin.
434 */
435
436 #ifdef TEST
437
438 #include <stdlib.h>
439 #include <stringops.h>
440 #include <vstream.h>
441 #include <msg_vstream.h>
442 #include <rec_streamlf.h>
443 #include <mail_params.h>
444
445 typedef struct {
446 HBC_CHECKS *header_checks;
447 HBC_CHECKS *body_checks;
448 HBC_CALL_BACKS *call_backs;
449 VSTREAM *fp;
450 VSTRING *buf;
451 const char *queueid;
452 int recno;
453 } HBC_TEST_CONTEXT;
454
455 /*#define REC_LEN 40*/
456 #define REC_LEN 1024
457
458 /* log_cb - log action with context */
459
log_cb(void * context,const char * action,const char * where,const char * content,const char * text)460 static void log_cb(void *context, const char *action, const char *where,
461 const char *content, const char *text)
462 {
463 const HBC_TEST_CONTEXT *dp = (HBC_TEST_CONTEXT *) context;
464
465 if (*text) {
466 msg_info("%s: %s: %s %.200s: %s",
467 dp->queueid, action, where, content, text);
468 } else {
469 msg_info("%s: %s: %s %.200s",
470 dp->queueid, action, where, content);
471 }
472 }
473
474 /* out_cb - output call-back */
475
out_cb(void * context,int rec_type,const char * buf,ssize_t len,off_t offset)476 static void out_cb(void *context, int rec_type, const char *buf,
477 ssize_t len, off_t offset)
478 {
479 const HBC_TEST_CONTEXT *dp = (HBC_TEST_CONTEXT *) context;
480
481 vstream_fwrite(dp->fp, buf, len);
482 VSTREAM_PUTC('\n', dp->fp);
483 vstream_fflush(dp->fp);
484 }
485
486 /* head_out - MIME_STATE header call-back */
487
head_out(void * context,int header_class,const HEADER_OPTS * header_info,VSTRING * buf,off_t offset)488 static void head_out(void *context, int header_class,
489 const HEADER_OPTS *header_info,
490 VSTRING *buf, off_t offset)
491 {
492 HBC_TEST_CONTEXT *dp = (HBC_TEST_CONTEXT *) context;
493 char *out;
494
495 if (dp->header_checks == 0
496 || (out = hbc_header_checks(context, dp->header_checks, header_class,
497 header_info, buf, offset)) == STR(buf)) {
498 vstring_sprintf(dp->buf, "%d %s %ld\t|%s",
499 dp->recno,
500 header_class == MIME_HDR_PRIMARY ? "MAIN" :
501 header_class == MIME_HDR_MULTIPART ? "MULT" :
502 header_class == MIME_HDR_NESTED ? "NEST" :
503 "ERROR", (long) offset, STR(buf));
504 out_cb(dp, REC_TYPE_NORM, STR(dp->buf), LEN(dp->buf), offset);
505 } else if (out != 0) {
506 vstring_sprintf(dp->buf, "%d %s %ld\t|%s",
507 dp->recno,
508 header_class == MIME_HDR_PRIMARY ? "MAIN" :
509 header_class == MIME_HDR_MULTIPART ? "MULT" :
510 header_class == MIME_HDR_NESTED ? "NEST" :
511 "ERROR", (long) offset, out);
512 out_cb(dp, REC_TYPE_NORM, STR(dp->buf), LEN(dp->buf), offset);
513 myfree(out);
514 }
515 dp->recno += 1;
516 }
517
518 /* header_end - MIME_STATE end-of-header call-back */
519
head_end(void * context)520 static void head_end(void *context)
521 {
522 HBC_TEST_CONTEXT *dp = (HBC_TEST_CONTEXT *) context;
523
524 out_cb(dp, 0, "HEADER END", sizeof("HEADER END") - 1, 0);
525 }
526
527 /* body_out - MIME_STATE body line call-back */
528
body_out(void * context,int rec_type,const char * buf,ssize_t len,off_t offset)529 static void body_out(void *context, int rec_type, const char *buf,
530 ssize_t len, off_t offset)
531 {
532 HBC_TEST_CONTEXT *dp = (HBC_TEST_CONTEXT *) context;
533 char *out;
534
535 if (dp->body_checks == 0
536 || (out = hbc_body_checks(context, dp->body_checks,
537 buf, len, offset)) == buf) {
538 vstring_sprintf(dp->buf, "%d BODY %c %ld\t|%s",
539 dp->recno, rec_type, (long) offset, buf);
540 out_cb(dp, rec_type, STR(dp->buf), LEN(dp->buf), offset);
541 } else if (out != 0) {
542 vstring_sprintf(dp->buf, "%d BODY %c %ld\t|%s",
543 dp->recno, rec_type, (long) offset, out);
544 out_cb(dp, rec_type, STR(dp->buf), LEN(dp->buf), offset);
545 myfree(out);
546 }
547 dp->recno += 1;
548 }
549
550 /* body_end - MIME_STATE end-of-message call-back */
551
body_end(void * context)552 static void body_end(void *context)
553 {
554 HBC_TEST_CONTEXT *dp = (HBC_TEST_CONTEXT *) context;
555
556 out_cb(dp, 0, "BODY END", sizeof("BODY END") - 1, 0);
557 }
558
559 /* err_print - print MIME_STATE errors */
560
err_print(void * unused_context,int err_flag,const char * text,ssize_t len)561 static void err_print(void *unused_context, int err_flag,
562 const char *text, ssize_t len)
563 {
564 msg_warn("%s: %.*s", mime_state_error(err_flag),
565 len < 100 ? (int) len : 100, text);
566 }
567
568 int var_header_limit = 2000;
569 int var_mime_maxdepth = 20;
570 int var_mime_bound_len = 2000;
571 char *var_drop_hdrs = DEF_DROP_HDRS;
572
main(int argc,char ** argv)573 int main(int argc, char **argv)
574 {
575 int rec_type;
576 VSTRING *buf;
577 int err;
578 MIME_STATE *mime_state;
579 HBC_TEST_CONTEXT context;
580 static HBC_CALL_BACKS call_backs[1] = {
581 log_cb, /* logger */
582 out_cb, /* prepend */
583 };
584
585 /*
586 * Sanity check.
587 */
588 if (argc != 5)
589 msg_fatal("usage: %s header_checks mime_header_checks nested_header_checks body_checks", argv[0]);
590
591 /*
592 * Initialize.
593 */
594 #define MIME_OPTIONS \
595 (MIME_OPT_REPORT_8BIT_IN_7BIT_BODY \
596 | MIME_OPT_REPORT_8BIT_IN_HEADER \
597 | MIME_OPT_REPORT_ENCODING_DOMAIN \
598 | MIME_OPT_REPORT_TRUNC_HEADER \
599 | MIME_OPT_REPORT_NESTING \
600 | MIME_OPT_DOWNGRADE)
601 msg_vstream_init(basename(argv[0]), VSTREAM_OUT);
602 buf = vstring_alloc(10);
603 mime_state = mime_state_alloc(MIME_OPTIONS,
604 head_out, head_end,
605 body_out, body_end,
606 err_print,
607 (void *) &context);
608 context.header_checks =
609 hbc_header_checks_create("header_checks", argv[1],
610 "mime_header_checks", argv[2],
611 "nested_header_checks", argv[3],
612 call_backs);
613 context.body_checks =
614 hbc_body_checks_create("body_checks", argv[4], call_backs);
615 context.buf = vstring_alloc(100);
616 context.fp = VSTREAM_OUT;
617 context.queueid = "test-queueID";
618 context.recno = 0;
619
620 /*
621 * Main loop.
622 */
623 do {
624 rec_type = rec_streamlf_get(VSTREAM_IN, buf, REC_LEN);
625 VSTRING_TERMINATE(buf);
626 err = mime_state_update(mime_state, rec_type, STR(buf), LEN(buf));
627 vstream_fflush(VSTREAM_OUT);
628 } while (rec_type > 0);
629
630 /*
631 * Error reporting.
632 */
633 if (err & MIME_ERR_TRUNC_HEADER)
634 msg_warn("message header length exceeds safety limit");
635 if (err & MIME_ERR_NESTING)
636 msg_warn("MIME nesting exceeds safety limit");
637 if (err & MIME_ERR_8BIT_IN_HEADER)
638 msg_warn("improper use of 8-bit data in message header");
639 if (err & MIME_ERR_8BIT_IN_7BIT_BODY)
640 msg_warn("improper use of 8-bit data in message body");
641 if (err & MIME_ERR_ENCODING_DOMAIN)
642 msg_warn("improper message/* or multipart/* encoding domain");
643
644 /*
645 * Cleanup.
646 */
647 if (context.header_checks)
648 hbc_header_checks_free(context.header_checks);
649 if (context.body_checks)
650 hbc_body_checks_free(context.body_checks);
651 vstring_free(context.buf);
652 mime_state_free(mime_state);
653 vstring_free(buf);
654 exit(0);
655 }
656
657 #endif
658