1/*++ 2/* NAME 3/* cleanup_body_edit 3 4/* SUMMARY 5/* edit body content 6/* SYNOPSIS 7/* #include "cleanup.h" 8/* 9/* int cleanup_body_edit_start(state) 10/* CLEANUP_STATE *state; 11/* 12/* int cleanup_body_edit_write(state, type, buf) 13/* CLEANUP_STATE *state; 14/* int type; 15/* VSTRING *buf; 16/* 17/* int cleanup_body_edit_finish(state) 18/* CLEANUP_STATE *state; 19/* 20/* void cleanup_body_edit_free(state) 21/* CLEANUP_STATE *state; 22/* DESCRIPTION 23/* This module maintains queue file regions with body content. 24/* Regions are created on the fly, and can be reused multiple 25/* times. This module must not be called until the queue file 26/* is complete, and there must be no other read/write access 27/* to the queue file between the cleanup_body_edit_start() and 28/* cleanup_body_edit_finish() calls. 29/* 30/* cleanup_body_edit_start() performs initialization and sets 31/* the queue file write pointer to the start of the first body 32/* region. 33/* 34/* cleanup_body_edit_write() adds a queue file record to the 35/* queue file. When the current body region fills up, some 36/* unused region is reused, or a new region is created. 37/* 38/* cleanup_body_edit_finish() makes some final adjustments 39/* after the last body content record is written. 40/* 41/* cleanup_body_edit_free() frees up memory that was allocated 42/* by cleanup_body_edit_start() and cleanup_body_edit_write(). 43/* 44/* Arguments: 45/* .IP state 46/* Queue file and message processing state. This state is updated 47/* as records are processed and as errors happen. 48/* .IP type 49/* Record type. 50/* .IP buf 51/* Record content. 52/* LICENSE 53/* .ad 54/* .fi 55/* The Secure Mailer license must be distributed with this software. 56/* AUTHOR(S) 57/* Wietse Venema 58/* IBM T.J. Watson Research 59/* P.O. Box 704 60/* Yorktown Heights, NY 10598, USA 61/*--*/ 62 63/* System library. */ 64 65#include <sys_defs.h> 66 67/* Utility library. */ 68 69#include <msg.h> 70#include <mymalloc.h> 71#include <vstream.h> 72#include <vstring.h> 73 74/* Global library. */ 75 76#include <rec_type.h> 77#include <record.h> 78 79/* Application-specific. */ 80 81#include <cleanup.h> 82 83#define LEN(s) VSTRING_LEN(s) 84 85static int cleanup_body_edit_ptr_rec_len; 86 87/* cleanup_body_edit_start - rewrite body region pool */ 88 89int cleanup_body_edit_start(CLEANUP_STATE *state) 90{ 91 const char *myname = "cleanup_body_edit_start"; 92 CLEANUP_REGION *curr_rp; 93 94 /* 95 * Calculate the payload size sans body. 96 */ 97 state->cont_length = state->body_offset - state->data_offset; 98 99 /* 100 * One-time initialization. 101 */ 102 if (state->body_regions == 0) { 103 REC_SPACE_NEED(REC_TYPE_PTR_PAYL_SIZE, cleanup_body_edit_ptr_rec_len); 104 cleanup_region_init(state); 105 } 106 107 /* 108 * Return all body regions to the free pool. 109 */ 110 cleanup_region_return(state, state->body_regions); 111 112 /* 113 * Select the first region. XXX This will usally be the original body 114 * segment, but we must not count on that. Region assignments may change 115 * when header editing also uses queue file regions. XXX We don't really 116 * know if the first region will be large enough to hold the first body 117 * text record, but this problem is so rare that we will not complicate 118 * the code for it. If the first region is too small then we will simply 119 * waste it. 120 */ 121 curr_rp = state->curr_body_region = state->body_regions = 122 cleanup_region_open(state, cleanup_body_edit_ptr_rec_len); 123 124 /* 125 * Link the first body region to the last message header. 126 */ 127 if (vstream_fseek(state->dst, state->append_hdr_pt_offset, SEEK_SET) < 0) { 128 msg_warn("%s: seek file %s: %m", myname, cleanup_path); 129 return (-1); 130 } 131 state->append_hdr_pt_target = curr_rp->start; 132 rec_fprintf(state->dst, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 133 (long) state->append_hdr_pt_target); 134 135 /* 136 * Move the file write pointer to the start of the current region. 137 */ 138 if (vstream_ftell(state->dst) != curr_rp->start 139 && vstream_fseek(state->dst, curr_rp->start, SEEK_SET) < 0) { 140 msg_warn("%s: seek file %s: %m", myname, cleanup_path); 141 return (-1); 142 } 143 return (0); 144} 145 146/* cleanup_body_edit_write - add record to body region pool */ 147 148int cleanup_body_edit_write(CLEANUP_STATE *state, int rec_type, 149 VSTRING *buf) 150{ 151 const char *myname = "cleanup_body_edit_write"; 152 CLEANUP_REGION *curr_rp = state->curr_body_region; 153 CLEANUP_REGION *next_rp; 154 off_t space_used; 155 ssize_t space_needed; 156 ssize_t rec_len; 157 158 if (msg_verbose) 159 msg_info("%s: where %ld, buflen %ld region start %ld len %ld", 160 myname, (long) curr_rp->write_offs, (long) LEN(buf), 161 (long) curr_rp->start, (long) curr_rp->len); 162 163 /* 164 * Switch to the next body region if we filled up the current one (we 165 * always append to an open-ended region). Besides space to write the 166 * specified record, we need to leave space for a final pointer record 167 * that will link this body region to the next region or to the content 168 * terminator record. 169 */ 170 if (curr_rp->len > 0) { 171 space_used = curr_rp->write_offs - curr_rp->start; 172 REC_SPACE_NEED(LEN(buf), rec_len); 173 space_needed = rec_len + cleanup_body_edit_ptr_rec_len; 174 if (space_needed > curr_rp->len - space_used) { 175 176 /* 177 * Update the payload size. Connect the filled up body region to 178 * its successor. 179 */ 180 state->cont_length += space_used; 181 next_rp = cleanup_region_open(state, space_needed); 182 if (msg_verbose) 183 msg_info("%s: link %ld -> %ld", myname, 184 (long) curr_rp->write_offs, (long) next_rp->start); 185 rec_fprintf(state->dst, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 186 (long) next_rp->start); 187 curr_rp->write_offs = vstream_ftell(state->dst); 188 cleanup_region_close(state, curr_rp); 189 curr_rp->next = next_rp; 190 191 /* 192 * Select the new body region. 193 */ 194 state->curr_body_region = curr_rp = next_rp; 195 if (vstream_fseek(state->dst, curr_rp->start, SEEK_SET) < 0) { 196 msg_warn("%s: seek file %s: %m", myname, cleanup_path); 197 return (-1); 198 } 199 } 200 } 201 202 /* 203 * Finally, output the queue file record. 204 */ 205 CLEANUP_OUT_BUF(state, REC_TYPE_NORM, buf); 206 curr_rp->write_offs = vstream_ftell(state->dst); 207 208 return (0); 209} 210 211/* cleanup_body_edit_finish - wrap up body region pool */ 212 213int cleanup_body_edit_finish(CLEANUP_STATE *state) 214{ 215 CLEANUP_REGION *curr_rp = state->curr_body_region; 216 217 /* 218 * Update the payload size. 219 */ 220 state->cont_length += curr_rp->write_offs - curr_rp->start; 221 222 /* 223 * Link the last body region to the content terminator record. 224 */ 225 rec_fprintf(state->dst, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 226 (long) state->xtra_offset); 227 curr_rp->write_offs = vstream_ftell(state->dst); 228 cleanup_region_close(state, curr_rp); 229 230 return (CLEANUP_OUT_OK(state) ? 0 : -1); 231} 232