Home | History | Annotate | Line # | Download | only in cleanup
      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