summaryrefslogtreecommitdiffstats
path: root/src/cleanup/cleanup_body_edit.c
blob: 2fff1bcadd4788a92ce4c81daac3d37a6b35be90 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
/*++
/* NAME
/*	cleanup_body_edit 3
/* SUMMARY
/*	edit body content
/* SYNOPSIS
/*	#include "cleanup.h"
/*
/*	int	cleanup_body_edit_start(state)
/*	CLEANUP_STATE *state;
/*
/*	int	cleanup_body_edit_write(state, type, buf)
/*	CLEANUP_STATE *state;
/*	int	type;
/*	VSTRING	*buf;
/*
/*	int	cleanup_body_edit_finish(state)
/*	CLEANUP_STATE *state;
/*
/*	void	cleanup_body_edit_free(state)
/*	CLEANUP_STATE *state;
/* DESCRIPTION
/*	This module maintains queue file regions with body content.
/*	Regions are created on the fly, and can be reused multiple
/*	times. This module must not be called until the queue file
/*	is complete, and there must be no other read/write access
/*	to the queue file between the cleanup_body_edit_start() and
/*	cleanup_body_edit_finish() calls.
/*
/*	cleanup_body_edit_start() performs initialization and sets
/*	the queue file write pointer to the start of the first body
/*	region.
/*
/*	cleanup_body_edit_write() adds a queue file record to the
/*	queue file. When the current body region fills up, some
/*	unused region is reused, or a new region is created.
/*
/*	cleanup_body_edit_finish() makes some final adjustments
/*	after the last body content record is written.
/*
/*	cleanup_body_edit_free() frees up memory that was allocated
/*	by cleanup_body_edit_start() and cleanup_body_edit_write().
/*
/*	Arguments:
/* .IP state
/*	Queue file and message processing state. This state is updated
/*	as records are processed and as errors happen.
/* .IP type
/*	Record type.
/* .IP buf
/*	Record content.
/* LICENSE
/* .ad
/* .fi
/*	The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/*	Wietse Venema
/*	IBM T.J. Watson Research
/*	P.O. Box 704
/*	Yorktown Heights, NY 10598, USA
/*
/*	Wietse Venema
/*	Google, Inc.
/*	111 8th Avenue
/*	New York, NY 10011, USA
/*--*/

/* System library. */

#include <sys_defs.h>

/* Utility library. */

#include <msg.h>
#include <mymalloc.h>
#include <vstream.h>
#include <vstring.h>

/* Global library. */

#include <rec_type.h>
#include <record.h>

/* Application-specific. */

#include <cleanup.h>

#define LEN(s) VSTRING_LEN(s)

static int cleanup_body_edit_ptr_rec_len;

/* cleanup_body_edit_start - rewrite body region pool */

int     cleanup_body_edit_start(CLEANUP_STATE *state)
{
    const char *myname = "cleanup_body_edit_start";
    CLEANUP_REGION *curr_rp;

    /*
     * Calculate the payload size sans body.
     */
    state->cont_length = state->body_offset - state->data_offset;

    /*
     * One-time initialization.
     */
    if (state->body_regions == 0) {
	REC_SPACE_NEED(REC_TYPE_PTR_PAYL_SIZE, cleanup_body_edit_ptr_rec_len);
	cleanup_region_init(state);
    }

    /*
     * Return all body regions to the free pool.
     */
    cleanup_region_return(state, state->body_regions);

    /*
     * Select the first region. XXX This will usually be the original body
     * segment, but we must not count on that. Region assignments may change
     * when header editing also uses queue file regions. XXX We don't really
     * know if the first region will be large enough to hold the first body
     * text record, but this problem is so rare that we will not complicate
     * the code for it. If the first region is too small then we will simply
     * waste it.
     */
    curr_rp = state->curr_body_region = state->body_regions =
	cleanup_region_open(state, cleanup_body_edit_ptr_rec_len);

    /*
     * Link the first body region to the last message header.
     */
    if (vstream_fseek(state->dst, state->append_hdr_pt_offset, SEEK_SET) < 0) {
	msg_warn("%s: seek file %s: %m", myname, cleanup_path);
	return (-1);
    }
    state->append_hdr_pt_target = curr_rp->start;
    rec_fprintf(state->dst, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT,
		(long) state->append_hdr_pt_target);

    /*
     * Move the file write pointer to the start of the current region.
     */
    if (vstream_ftell(state->dst) != curr_rp->start
	&& vstream_fseek(state->dst, curr_rp->start, SEEK_SET) < 0) {
	msg_warn("%s: seek file %s: %m", myname, cleanup_path);
	return (-1);
    }
    return (0);
}

/* cleanup_body_edit_write - add record to body region pool */

int     cleanup_body_edit_write(CLEANUP_STATE *state, int rec_type,
				        VSTRING *buf)
{
    const char *myname = "cleanup_body_edit_write";
    CLEANUP_REGION *curr_rp = state->curr_body_region;
    CLEANUP_REGION *next_rp;
    off_t   space_used;
    ssize_t space_needed;
    ssize_t rec_len;

    if (msg_verbose)
	msg_info("%s: where %ld, buflen %ld region start %ld len %ld",
		 myname, (long) curr_rp->write_offs, (long) LEN(buf),
		 (long) curr_rp->start, (long) curr_rp->len);

    /*
     * Switch to the next body region if we filled up the current one (we
     * always append to an open-ended region). Besides space to write the
     * specified record, we need to leave space for a final pointer record
     * that will link this body region to the next region or to the content
     * terminator record.
     */
    if (curr_rp->len > 0) {
	space_used = curr_rp->write_offs - curr_rp->start;
	REC_SPACE_NEED(LEN(buf), rec_len);
	space_needed = rec_len + cleanup_body_edit_ptr_rec_len;
	if (space_needed > curr_rp->len - space_used) {

	    /*
	     * Update the payload size. Connect the filled up body region to
	     * its successor.
	     */
	    state->cont_length += space_used;
	    next_rp = cleanup_region_open(state, space_needed);
	    if (msg_verbose)
		msg_info("%s: link %ld -> %ld", myname,
			 (long) curr_rp->write_offs, (long) next_rp->start);
	    rec_fprintf(state->dst, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT,
			(long) next_rp->start);
	    curr_rp->write_offs = vstream_ftell(state->dst);
	    cleanup_region_close(state, curr_rp);
	    curr_rp->next = next_rp;

	    /*
	     * Select the new body region.
	     */
	    state->curr_body_region = curr_rp = next_rp;
	    if (vstream_fseek(state->dst, curr_rp->start, SEEK_SET) < 0) {
		msg_warn("%s: seek file %s: %m", myname, cleanup_path);
		return (-1);
	    }
	}
    }

    /*
     * Finally, output the queue file record.
     */
    CLEANUP_OUT_BUF(state, rec_type, buf);
    curr_rp->write_offs = vstream_ftell(state->dst);

    return (0);
}

/* cleanup_body_edit_finish - wrap up body region pool */

int     cleanup_body_edit_finish(CLEANUP_STATE *state)
{
    CLEANUP_REGION *curr_rp = state->curr_body_region;

    /*
     * Update the payload size.
     */
    state->cont_length += curr_rp->write_offs - curr_rp->start;

    /*
     * Link the last body region to the content terminator record.
     */
    rec_fprintf(state->dst, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT,
		(long) state->xtra_offset);
    curr_rp->write_offs = vstream_ftell(state->dst);
    cleanup_region_close(state, curr_rp);

    return (CLEANUP_OUT_OK(state) ? 0 : -1);
}