diff options
Diffstat (limited to 'src/cleanup/cleanup_body_edit.c')
-rw-r--r-- | src/cleanup/cleanup_body_edit.c | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/src/cleanup/cleanup_body_edit.c b/src/cleanup/cleanup_body_edit.c new file mode 100644 index 0000000..3d4e866 --- /dev/null +++ b/src/cleanup/cleanup_body_edit.c @@ -0,0 +1,243 @@ +/*++ +/* NAME +/* cleanup_body_edit 3 +/* SUMMARY +/* edit body content +/* SYNOPSIS +/* #include "cleanup.h" +/* +/* int cleanup_body_edit_start(state) +/* CLEANUP_STATE *state; +/* +/* int cleanup_body_edit_write(state, type, buf) +/* CLEANUP_STATE *state; +/* int type; +/* VSTRING *buf; +/* +/* int cleanup_body_edit_finish(state) +/* CLEANUP_STATE *state; +/* +/* void cleanup_body_edit_free(state) +/* CLEANUP_STATE *state; +/* DESCRIPTION +/* This module maintains queue file regions with body content. +/* Regions are created on the fly, and can be reused multiple +/* times. This module must not be called until the queue file +/* is complete, and there must be no other read/write access +/* to the queue file between the cleanup_body_edit_start() and +/* cleanup_body_edit_finish() calls. +/* +/* cleanup_body_edit_start() performs initialization and sets +/* the queue file write pointer to the start of the first body +/* region. +/* +/* cleanup_body_edit_write() adds a queue file record to the +/* queue file. When the current body region fills up, some +/* unused region is reused, or a new region is created. +/* +/* cleanup_body_edit_finish() makes some final adjustments +/* after the last body content record is written. +/* +/* cleanup_body_edit_free() frees up memory that was allocated +/* by cleanup_body_edit_start() and cleanup_body_edit_write(). +/* +/* Arguments: +/* .IP state +/* Queue file and message processing state. This state is updated +/* as records are processed and as errors happen. +/* .IP type +/* Record type. +/* .IP buf +/* Record content. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> + +/* Utility library. */ + +#include <msg.h> +#include <mymalloc.h> +#include <vstream.h> +#include <vstring.h> + +/* Global library. */ + +#include <rec_type.h> +#include <record.h> + +/* Application-specific. */ + +#include <cleanup.h> + +#define LEN(s) VSTRING_LEN(s) + +static int cleanup_body_edit_ptr_rec_len; + +/* cleanup_body_edit_start - rewrite body region pool */ + +int cleanup_body_edit_start(CLEANUP_STATE *state) +{ + const char *myname = "cleanup_body_edit_start"; + CLEANUP_REGION *curr_rp; + + /* + * Calculate the payload size sans body. + */ + state->cont_length = state->body_offset - state->data_offset; + + /* + * One-time initialization. + */ + if (state->body_regions == 0) { + REC_SPACE_NEED(REC_TYPE_PTR_PAYL_SIZE, cleanup_body_edit_ptr_rec_len); + cleanup_region_init(state); + } + + /* + * Return all body regions to the free pool. + */ + cleanup_region_return(state, state->body_regions); + + /* + * Select the first region. XXX This will usually be the original body + * segment, but we must not count on that. Region assignments may change + * when header editing also uses queue file regions. XXX We don't really + * know if the first region will be large enough to hold the first body + * text record, but this problem is so rare that we will not complicate + * the code for it. If the first region is too small then we will simply + * waste it. + */ + curr_rp = state->curr_body_region = state->body_regions = + cleanup_region_open(state, cleanup_body_edit_ptr_rec_len); + + /* + * Link the first body region to the last message header. + */ + if (vstream_fseek(state->dst, state->append_hdr_pt_offset, SEEK_SET) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + return (-1); + } + state->append_hdr_pt_target = curr_rp->start; + rec_fprintf(state->dst, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, + (long) state->append_hdr_pt_target); + + /* + * Move the file write pointer to the start of the current region. + */ + if (vstream_ftell(state->dst) != curr_rp->start + && vstream_fseek(state->dst, curr_rp->start, SEEK_SET) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + return (-1); + } + return (0); +} + +/* cleanup_body_edit_write - add record to body region pool */ + +int cleanup_body_edit_write(CLEANUP_STATE *state, int rec_type, + VSTRING *buf) +{ + const char *myname = "cleanup_body_edit_write"; + CLEANUP_REGION *curr_rp = state->curr_body_region; + CLEANUP_REGION *next_rp; + off_t space_used; + ssize_t space_needed; + ssize_t rec_len; + + if (msg_verbose) + msg_info("%s: where %ld, buflen %ld region start %ld len %ld", + myname, (long) curr_rp->write_offs, (long) LEN(buf), + (long) curr_rp->start, (long) curr_rp->len); + + /* + * Switch to the next body region if we filled up the current one (we + * always append to an open-ended region). Besides space to write the + * specified record, we need to leave space for a final pointer record + * that will link this body region to the next region or to the content + * terminator record. + */ + if (curr_rp->len > 0) { + space_used = curr_rp->write_offs - curr_rp->start; + REC_SPACE_NEED(LEN(buf), rec_len); + space_needed = rec_len + cleanup_body_edit_ptr_rec_len; + if (space_needed > curr_rp->len - space_used) { + + /* + * Update the payload size. Connect the filled up body region to + * its successor. + */ + state->cont_length += space_used; + next_rp = cleanup_region_open(state, space_needed); + if (msg_verbose) + msg_info("%s: link %ld -> %ld", myname, + (long) curr_rp->write_offs, (long) next_rp->start); + rec_fprintf(state->dst, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, + (long) next_rp->start); + curr_rp->write_offs = vstream_ftell(state->dst); + cleanup_region_close(state, curr_rp); + curr_rp->next = next_rp; + + /* + * Select the new body region. + */ + state->curr_body_region = curr_rp = next_rp; + if (vstream_fseek(state->dst, curr_rp->start, SEEK_SET) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + return (-1); + } + } + } + + /* + * Finally, output the queue file record. + */ + CLEANUP_OUT_BUF(state, rec_type, buf); + curr_rp->write_offs = vstream_ftell(state->dst); + + /* + * Sanity check. + */ + if (curr_rp->len > 0 + && curr_rp->write_offs > curr_rp->start + curr_rp->len) + msg_panic("%s: write past end of body segment", myname); + + return (0); +} + +/* cleanup_body_edit_finish - wrap up body region pool */ + +int cleanup_body_edit_finish(CLEANUP_STATE *state) +{ + CLEANUP_REGION *curr_rp = state->curr_body_region; + + /* + * Update the payload size. + */ + state->cont_length += curr_rp->write_offs - curr_rp->start; + + /* + * Link the last body region to the content terminator record. + */ + rec_fprintf(state->dst, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, + (long) state->xtra_offset); + curr_rp->write_offs = vstream_ftell(state->dst); + cleanup_region_close(state, curr_rp); + + return (CLEANUP_OUT_OK(state) ? 0 : -1); +} |