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