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
cleanup_body_edit_start(CLEANUP_STATE * state)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
cleanup_body_edit_write(CLEANUP_STATE * state,int rec_type,VSTRING * buf)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
cleanup_body_edit_finish(CLEANUP_STATE * state)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