1 /* $NetBSD: cleanup_body_edit.c,v 1.3 2022/10/08 16:12:45 christos 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 /* Wietse Venema 65 /* Google, Inc. 66 /* 111 8th Avenue 67 /* New York, NY 10011, USA 68 /*--*/ 69 70 /* System library. */ 71 72 #include <sys_defs.h> 73 74 /* Utility library. */ 75 76 #include <msg.h> 77 #include <mymalloc.h> 78 #include <vstream.h> 79 #include <vstring.h> 80 81 /* Global library. */ 82 83 #include <rec_type.h> 84 #include <record.h> 85 86 /* Application-specific. */ 87 88 #include <cleanup.h> 89 90 #define LEN(s) VSTRING_LEN(s) 91 92 static int cleanup_body_edit_ptr_rec_len; 93 94 /* cleanup_body_edit_start - rewrite body region pool */ 95 96 int cleanup_body_edit_start(CLEANUP_STATE *state) 97 { 98 const char *myname = "cleanup_body_edit_start"; 99 CLEANUP_REGION *curr_rp; 100 101 /* 102 * Calculate the payload size sans body. 103 */ 104 state->cont_length = state->body_offset - state->data_offset; 105 106 /* 107 * One-time initialization. 108 */ 109 if (state->body_regions == 0) { 110 REC_SPACE_NEED(REC_TYPE_PTR_PAYL_SIZE, cleanup_body_edit_ptr_rec_len); 111 cleanup_region_init(state); 112 } 113 114 /* 115 * Return all body regions to the free pool. 116 */ 117 cleanup_region_return(state, state->body_regions); 118 119 /* 120 * Select the first region. XXX This will usually be the original body 121 * segment, but we must not count on that. Region assignments may change 122 * when header editing also uses queue file regions. XXX We don't really 123 * know if the first region will be large enough to hold the first body 124 * text record, but this problem is so rare that we will not complicate 125 * the code for it. If the first region is too small then we will simply 126 * waste it. 127 */ 128 curr_rp = state->curr_body_region = state->body_regions = 129 cleanup_region_open(state, cleanup_body_edit_ptr_rec_len); 130 131 /* 132 * Link the first body region to the last message header. 133 */ 134 if (vstream_fseek(state->dst, state->append_hdr_pt_offset, SEEK_SET) < 0) { 135 msg_warn("%s: seek file %s: %m", myname, cleanup_path); 136 return (-1); 137 } 138 state->append_hdr_pt_target = curr_rp->start; 139 rec_fprintf(state->dst, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 140 (long) state->append_hdr_pt_target); 141 142 /* 143 * Move the file write pointer to the start of the current region. 144 */ 145 if (vstream_ftell(state->dst) != curr_rp->start 146 && vstream_fseek(state->dst, curr_rp->start, SEEK_SET) < 0) { 147 msg_warn("%s: seek file %s: %m", myname, cleanup_path); 148 return (-1); 149 } 150 return (0); 151 } 152 153 /* cleanup_body_edit_write - add record to body region pool */ 154 155 int cleanup_body_edit_write(CLEANUP_STATE *state, int rec_type, 156 VSTRING *buf) 157 { 158 const char *myname = "cleanup_body_edit_write"; 159 CLEANUP_REGION *curr_rp = state->curr_body_region; 160 CLEANUP_REGION *next_rp; 161 off_t space_used; 162 ssize_t space_needed; 163 ssize_t rec_len; 164 165 if (msg_verbose) 166 msg_info("%s: where %ld, buflen %ld region start %ld len %ld", 167 myname, (long) curr_rp->write_offs, (long) LEN(buf), 168 (long) curr_rp->start, (long) curr_rp->len); 169 170 /* 171 * Switch to the next body region if we filled up the current one (we 172 * always append to an open-ended region). Besides space to write the 173 * specified record, we need to leave space for a final pointer record 174 * that will link this body region to the next region or to the content 175 * terminator record. 176 */ 177 if (curr_rp->len > 0) { 178 space_used = curr_rp->write_offs - curr_rp->start; 179 REC_SPACE_NEED(LEN(buf), rec_len); 180 space_needed = rec_len + cleanup_body_edit_ptr_rec_len; 181 if (space_needed > curr_rp->len - space_used) { 182 183 /* 184 * Update the payload size. Connect the filled up body region to 185 * its successor. 186 */ 187 state->cont_length += space_used; 188 next_rp = cleanup_region_open(state, space_needed); 189 if (msg_verbose) 190 msg_info("%s: link %ld -> %ld", myname, 191 (long) curr_rp->write_offs, (long) next_rp->start); 192 rec_fprintf(state->dst, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 193 (long) next_rp->start); 194 curr_rp->write_offs = vstream_ftell(state->dst); 195 cleanup_region_close(state, curr_rp); 196 curr_rp->next = next_rp; 197 198 /* 199 * Select the new body region. 200 */ 201 state->curr_body_region = curr_rp = next_rp; 202 if (vstream_fseek(state->dst, curr_rp->start, SEEK_SET) < 0) { 203 msg_warn("%s: seek file %s: %m", myname, cleanup_path); 204 return (-1); 205 } 206 } 207 } 208 209 /* 210 * Finally, output the queue file record. 211 */ 212 CLEANUP_OUT_BUF(state, rec_type, buf); 213 curr_rp->write_offs = vstream_ftell(state->dst); 214 215 /* 216 * Sanity check. 217 */ 218 if (curr_rp->len > 0 219 && curr_rp->write_offs > curr_rp->start + curr_rp->len) 220 msg_panic("%s: write past end of body segment", myname); 221 222 return (0); 223 } 224 225 /* cleanup_body_edit_finish - wrap up body region pool */ 226 227 int cleanup_body_edit_finish(CLEANUP_STATE *state) 228 { 229 CLEANUP_REGION *curr_rp = state->curr_body_region; 230 231 /* 232 * Update the payload size. 233 */ 234 state->cont_length += curr_rp->write_offs - curr_rp->start; 235 236 /* 237 * Link the last body region to the content terminator record. 238 */ 239 rec_fprintf(state->dst, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 240 (long) state->xtra_offset); 241 curr_rp->write_offs = vstream_ftell(state->dst); 242 cleanup_region_close(state, curr_rp); 243 244 return (CLEANUP_OUT_OK(state) ? 0 : -1); 245 } 246