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