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