1 /* $NetBSD: cleanup_envelope_test.c,v 1.2 2025/02/25 19:15:44 christos Exp $ */
2
3
4 /*
5 * System library.
6 */
7 #include <sys_defs.h>
8 #include <string.h>
9 #include <stdlib.h>
10 #include <ctype.h>
11
12 /*
13 * Utility library.
14 */
15 #include <msg.h>
16 #include <msg_vstream.h>
17 #include <vstring.h>
18 #include <vstream.h>
19 #include <stringops.h>
20
21 /*
22 * Global library.
23 */
24 #include <record.h>
25 #include <rec_type.h>
26 #include <cleanup_user.h>
27 #include <mail_params.h>
28 #include <smtputf8.h>
29
30 /*
31 * Application-specific.
32 */
33 #include <cleanup.h>
34
35 /*
36 * Stubs for parameter dependencies.
37 */
38 int var_delay_warn_time = 0;
39 int var_dup_filter_limit = DEF_DUP_FILTER_LIMIT;
40 char *var_remote_rwr_domain = DEF_REM_RWR_DOMAIN;
41 int var_qattr_count_limit = DEF_QATTR_COUNT_LIMIT;
42 VSTRING *cleanup_strip_chars = 0;
43 MILTERS *cleanup_milters = 0;
44 VSTRING *cleanup_trace_path = 0;
45 MAPS *cleanup_virt_alias_maps = 0;
46 char *cleanup_path = "fixed";
47
48 /*
49 * Stubs for cleanup_message.c dependencies. TODO(wietse) replace function
50 * stubs with mocks that can have expectations and that can report
51 * unexpected calls.
52 */
cleanup_message(CLEANUP_STATE * state,int type,const char * buf,ssize_t len)53 void cleanup_message(CLEANUP_STATE *state, int type, const char *buf,
54 ssize_t len)
55 {
56 msg_panic("cleanup_message");
57 }
58
59 /*
60 * Stubs for cleanup_milter.c dependencies.
61 */
cleanup_milter_receive(CLEANUP_STATE * state,int count)62 void cleanup_milter_receive(CLEANUP_STATE *state, int count)
63 {
64 msg_panic("cleanup_milter_receive");
65 }
66
cleanup_milter_emul_mail(CLEANUP_STATE * state,MILTERS * milters,const char * sender)67 void cleanup_milter_emul_mail(CLEANUP_STATE *state, MILTERS *milters,
68 const char *sender)
69 {
70 msg_panic("cleanup_milter_emul_mail");
71 }
72
cleanup_milter_emul_rcpt(CLEANUP_STATE * state,MILTERS * milters,const char * recipient)73 void cleanup_milter_emul_rcpt(CLEANUP_STATE *state, MILTERS *milters,
74 const char *recipient)
75 {
76 msg_panic("cleanup_milter_emul_rcpt");
77 }
78
79 /*
80 * Stubs for cleanup_addr.c dependencies.
81 */
cleanup_addr_sender(CLEANUP_STATE * state,const char * addr)82 off_t cleanup_addr_sender(CLEANUP_STATE *state, const char *addr)
83 {
84 msg_panic("cleanup_addr_sender");
85 }
86
cleanup_addr_recipient(CLEANUP_STATE * state,const char * addr)87 void cleanup_addr_recipient(CLEANUP_STATE *state, const char *addr)
88 {
89 msg_panic("cleanup_addr_recipient");
90 }
91
92 /*
93 * Stubs for cleanup_region.c dependencies.
94 */
cleanup_region_done(CLEANUP_STATE * state)95 void cleanup_region_done(CLEANUP_STATE *state)
96 {
97 }
98
99 /*
100 * Tests and test cases.
101 */
102 typedef struct TEST_CASE {
103 const char *label; /* identifies test case */
104 int (*action) (const struct TEST_CASE *);
105 } TEST_CASE;
106
107 #define PASS 1
108 #define FAIL 0
109
overrides_size_fields(const TEST_CASE * tp)110 static int overrides_size_fields(const TEST_CASE *tp)
111 {
112
113 /*
114 * Generate one SIZE record test payload.
115 */
116 VSTRING *input_buf = vstring_alloc(100);
117
118 vstring_sprintf(input_buf, REC_TYPE_SIZE_FORMAT,
119 (REC_TYPE_SIZE_CAST1) ~ 0, /* message segment size */
120 (REC_TYPE_SIZE_CAST2) ~ 0, /* content offset */
121 (REC_TYPE_SIZE_CAST3) ~ 0, /* recipient count */
122 (REC_TYPE_SIZE_CAST4) ~ 0, /* qmgr options */
123 (REC_TYPE_SIZE_CAST5) ~ 0, /* content length */
124 (REC_TYPE_SIZE_CAST6) SOPT_FLAG_ALL); /* sendopts */
125
126 /*
127 * Instantiate CLEANUP_STATE, and save information that isn't expected to
128 * change. We only need to save simple-type CLEANUP_STATE fields that
129 * correspond with SIZE record fields.
130 */
131 CLEANUP_STATE *state = cleanup_state_alloc((VSTREAM *) 0);
132 CLEANUP_STATE saved_state = *state;
133
134 /*
135 * Process the test SIZE record payload, clear some bits from the
136 * sendopts field, and write an all-zeroes preliminary SIZE record.
137 */
138 if ((state->dst = vstream_fopen("/dev/null", O_WRONLY, 0)) == 0) {
139 msg_warn("vstream_fopen(\"/dev/null\", O_WRONLY, 0): %m");
140 return (FAIL);
141 }
142 cleanup_envelope(state, REC_TYPE_SIZE, vstring_str(input_buf),
143 VSTRING_LEN(input_buf));
144 if (state->errs != CLEANUP_STAT_OK) {
145 msg_warn("cleanup_envelope: got: '%s', want: '%s'",
146 cleanup_strerror(state->errs),
147 cleanup_strerror(CLEANUP_STAT_OK));
148 return (FAIL);
149 }
150 vstring_free(input_buf);
151 input_buf = 0;
152 (void) vstream_fclose(state->dst);
153 state->dst = 0;
154
155 /*
156 * Compare the updated state against the expected content. We expect that
157 * the fields for xtra_offset, data_offset, rcpt_count, qmgr_opts, and
158 * cont_length, are consistent with the saved CLEANUP_STATE, and we
159 * expect to see a specific value for the sendopts field that was
160 * assigned in cleanup_envelope().
161 */
162 if (state->xtra_offset != saved_state.xtra_offset) {
163 msg_warn("state->xtra_offset: got %ld, want: %ld",
164 (long) state->xtra_offset, (long) saved_state.xtra_offset);
165 return (FAIL);
166 }
167 if (state->data_offset != saved_state.data_offset) {
168 msg_warn("state->data_offset: got %ld, want: %ld",
169 (long) state->data_offset, (long) saved_state.data_offset);
170 return (FAIL);
171 }
172 if (state->rcpt_count != saved_state.rcpt_count) {
173 msg_warn("state->rcpt_count: got: %ld, want: %ld",
174 (long) state->rcpt_count, (long) saved_state.rcpt_count);
175 return (FAIL);
176 }
177 if (state->qmgr_opts != saved_state.qmgr_opts) {
178 msg_warn("state=>qmgr_opts: got: %d, want: %d",
179 state->qmgr_opts, saved_state.qmgr_opts);
180 return (FAIL);
181 }
182 if (state->cont_length != saved_state.cont_length) {
183 msg_warn("state->cont_length: got %ld, want: %ld",
184 (long) state->cont_length, (long) saved_state.cont_length);
185 return (FAIL);
186 }
187 if (state->sendopts != (SOPT_FLAG_ALL & ~SOPT_FLAG_DERIVED)) {
188 msg_warn("state->sendopts: got: 0x%x, want: 0x%x",
189 state->sendopts, SOPT_FLAG_ALL & ~SOPT_FLAG_DERIVED);
190 return (FAIL);
191 }
192
193 /*
194 * Cleanup.
195 */
196 cleanup_state_free(state);
197 return (PASS);
198 }
199
200 static const TEST_CASE test_cases[] = {
201 {"overrides_size_fields",
202 overrides_size_fields,
203 },
204 {0},
205 };
206
main(int argc,char ** argv)207 int main(int argc, char **argv)
208 {
209 const TEST_CASE *tp;
210 int pass = 0;
211 int fail = 0;
212
213 /* XXX How to avoid linking in mail_params.o? */
214 var_line_limit = DEF_LINE_LIMIT;
215
216 msg_vstream_init(sane_basename((VSTRING *) 0, argv[0]), VSTREAM_ERR);
217
218 for (tp = test_cases; tp->label != 0; tp++) {
219 msg_info("RUN %s", tp->label);
220 if (tp->action(tp) != PASS) {
221 fail++;
222 msg_info("FAIL %s", tp->label);
223 } else {
224 msg_info("PASS %s", tp->label);
225 pass++;
226 }
227 }
228 msg_info("PASS=%d FAIL=%d", pass, fail);
229 exit(fail != 0);
230 }
231