1 /* Functions for formatting RFC-2231-compliant mail headers fields.
2 GNU Mailutils -- a suite of utilities for electronic mail
3 Copyright (C) 1999-2021 Free Software Foundation, Inc.
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 3 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General
16 Public License along with this library. If not,
17 see <http://www.gnu.org/licenses/>. */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <mailutils/mime.h>
26 #include <mailutils/cctype.h>
27 #include <mailutils/cstr.h>
28 #include <mailutils/header.h>
29 #include <mailutils/stream.h>
30 #include <mailutils/filter.h>
31 #include <mailutils/assoc.h>
32 #include <mailutils/errno.h>
33
34 struct header_buffer
35 {
36 mu_stream_t str; /* Output stream */
37 size_t line_len; /* Length of current line */
38 size_t line_max; /* Max. line length */
39 };
40
41 static int
mime_store_parameter(char const * name,void * value,void * data)42 mime_store_parameter (char const *name, void *value, void *data)
43 {
44 struct mu_mime_param *p = value;
45 struct header_buffer *hbuf = data;
46 size_t nlen; /* Length of parameter name
47 (eq. sign, eventual seqence no. and language info mark
48 included) */
49 size_t vlen; /* Length of lang'charset' part */
50 int langinfo; /* True if language info is available */
51 int quote = 0; /* 2 if the value should be quoted, 0 otherwise */
52 int segment = -1; /* Segment sequence number */
53 mu_stream_t valstr; /* Value stream (properly encoded) */
54 mu_off_t valsize; /* Number of octets left in valstr */
55 char const *filter_name = NULL; /* Name of the filter for the value */
56 int rc;
57
58 rc = mu_static_memory_stream_create (&valstr, p->value, strlen (p->value));
59 if (rc)
60 return rc;
61
62 nlen = strlen (name);
63 if (p->lang || p->cset)
64 {
65 vlen = 2;
66 if (p->lang)
67 vlen += strlen (p->lang);
68 if (p->cset)
69 vlen += strlen (p->cset);
70 langinfo = 1;
71 filter_name = "percent";
72 }
73 else
74 {
75 if (*mu_str_skip_class_comp (p->value, MU_CTYPE_TSPEC|MU_CTYPE_BLANK))
76 {
77 /* Must be in quoted-string, to use within parameter values */
78 quote = 2;
79 filter_name = "dq";
80 }
81 else
82 quote = 0;
83 vlen = 0;
84 langinfo = 0;
85 }
86
87 if (filter_name)
88 {
89 mu_stream_t tmp;
90
91 rc = mu_filter_create (&tmp, valstr, filter_name, MU_FILTER_ENCODE,
92 MU_STREAM_READ | MU_STREAM_SEEK);
93 if (rc)
94 goto err;
95 mu_stream_unref (valstr);
96 valstr = tmp;
97 rc = mu_memory_stream_create (&tmp, MU_STREAM_RDWR);
98 if (rc == 0)
99 {
100 rc = mu_stream_copy (tmp, valstr, 0, &valsize);
101 mu_stream_destroy (&tmp);
102 }
103 }
104 else
105 rc = mu_stream_size (valstr, &valsize);
106
107 if (rc)
108 goto err;
109
110 nlen += langinfo;
111
112 rc = mu_stream_seek (valstr, 0, MU_SEEK_SET, NULL);
113
114 if (hbuf->line_max == 0)
115 {
116 /* No line wrapping requested. Store the value as it is */
117 mu_stream_printf (hbuf->str, "%s", name);
118 if (langinfo)
119 mu_stream_write (hbuf->str, "*", 1, NULL);
120 mu_stream_write (hbuf->str, "=", 1, NULL);
121 if (vlen)
122 {
123 mu_stream_printf (hbuf->str, "%s'%s'",
124 mu_prstr (p->lang),
125 mu_prstr (p->cset));
126 vlen = 0;
127 }
128 else if (quote)
129 mu_stream_write (hbuf->str, "\"", 1, NULL);
130 mu_stream_copy (hbuf->str, valstr, 0, NULL);
131 if (quote)
132 mu_stream_write (hbuf->str, "\"", 1, NULL);
133 if (mu_stream_err (hbuf->str))
134 rc = mu_stream_last_error (hbuf->str);
135 }
136 else
137 {
138 /* Split the value into sequentially indexed segments, each one no
139 wider than the requested line width.
140
141 Without special precautions, an encoded character occurring at
142 the end of a segment can be split between this and the following
143 segment to satisfy line width requirements. To avoid this, the
144 following approach is used:
145
146 1. The value stream is put to unbuffered mode.
147 2. Before each write, the size of the transcoder output buffer
148 in valstr is set to the number of bytes left in the current
149 line.
150
151 This way the transcoder will write as many bytes as possible
152 without breaking the encoded constructs while the unbuffered mode
153 will ensure that it will not be called again to fill up the stream
154 buffer.
155
156 If the line width is insufficient, MU_ERR_BUFSPACE will be returned.
157 */
158 char *iobuf;
159
160 iobuf = malloc (hbuf->line_max + 1);
161 if (!iobuf)
162 {
163 rc = errno;
164 goto err;
165 }
166
167 mu_stream_set_buffer (valstr, mu_buffer_none, 0);
168
169 while (rc == 0 && valsize)
170 {
171 mu_off_t start, nr; /* Start and end positions in stream */
172 size_t sz, n;
173
174 mu_stream_write (hbuf->str, ";", 1, NULL);
175 mu_stream_seek (hbuf->str, 0, MU_SEEK_CUR, &start);
176
177 if (segment >= 0)
178 {
179 mu_stream_write (hbuf->str, "\n", 1, NULL);
180 hbuf->line_len = 0;
181 segment++;
182 }
183 else if (hbuf->line_len + valsize + quote + vlen + nlen + 1 >
184 hbuf->line_max)
185 {
186 mu_stream_write (hbuf->str, "\n", 1, NULL);
187 hbuf->line_len = 0;
188 if (hbuf->line_len + valsize + quote + vlen + nlen + 1 >
189 hbuf->line_max)
190 segment++;
191 }
192
193 mu_stream_write (hbuf->str, " ", 1, NULL);
194
195 if (segment >= 0)
196 mu_stream_printf (hbuf->str, "%s*%d", name, segment);
197 else
198 mu_stream_printf (hbuf->str, "%s", name);
199 if (langinfo)
200 mu_stream_write (hbuf->str, "*", 1, NULL);
201 mu_stream_write (hbuf->str, "=", 1, NULL);
202 mu_stream_seek (hbuf->str, 0, MU_SEEK_CUR, &nr);
203 nlen = nr - start;
204 hbuf->line_len += nlen;
205 start = nr;
206
207 /* Compute the number of octets to put into the current line.
208 If the requested line width is not enough to accomodate
209 the line, signal the error */
210 if (hbuf->line_max <= (hbuf->line_len + quote + vlen))
211 {
212 rc = MU_ERR_BUFSPACE;
213 break;
214 }
215
216 sz = hbuf->line_max - (hbuf->line_len + quote + vlen);
217 mu_stream_ioctl (valstr, MU_IOCTL_FILTER,
218 MU_IOCTL_FILTER_SET_OUTBUF_SIZE, &sz);
219
220 rc = mu_stream_read (valstr, iobuf, sz, &n);
221 if (rc || n == 0)
222 break;
223
224 if (vlen)
225 {
226 mu_stream_printf (hbuf->str, "%s'%s'",
227 mu_prstr (p->lang),
228 mu_prstr (p->cset));
229 vlen = 0;
230 }
231 else if (quote)
232 mu_stream_write (hbuf->str, "\"", 1, NULL);
233
234 mu_stream_write (hbuf->str, iobuf, n, NULL);
235
236 if (quote)
237 mu_stream_write (hbuf->str, "\"", 1, NULL);
238 mu_stream_seek (hbuf->str, 0, MU_SEEK_CUR, &nr);
239 nr -= start;
240 hbuf->line_len += nr;
241 valsize -= n;
242
243 if (mu_stream_err (hbuf->str))
244 rc = mu_stream_last_error (hbuf->str);
245 }
246 free (iobuf);
247 }
248 err:
249 mu_stream_destroy (&valstr);
250
251 return rc;
252 }
253
254 static int
mime_header_format(const char * value,mu_assoc_t params,struct header_buffer * hbuf)255 mime_header_format (const char *value, mu_assoc_t params,
256 struct header_buffer *hbuf)
257 {
258 size_t l = strlen (value);
259
260 mu_stream_write (hbuf->str, value, l, NULL);
261 hbuf->line_len += l;
262 return mu_assoc_foreach (params, mime_store_parameter, hbuf);
263 }
264
265 /* Store a header in accordance with RFC 2231, Section 3,
266 "Parameter Value Continuations"
267
268 HDR - Message header object
269 NAME - Header name
270 VALUE - Header value part
271 PARAMS - Named parameters (assoc of struct mu_mime_param *)
272 LINE_WIDTH - Maximum line width.
273 */
274
275 int
mu_mime_header_set_w(mu_header_t hdr,const char * name,const char * value,mu_assoc_t params,size_t line_width)276 mu_mime_header_set_w (mu_header_t hdr, const char *name,
277 const char *value, mu_assoc_t params, size_t line_width)
278 {
279 struct header_buffer hbuf;
280 int rc;
281
282 rc = mu_memory_stream_create (&hbuf.str, MU_STREAM_RDWR);
283 if (rc)
284 return rc;
285 hbuf.line_len = strlen (name) + 2;
286 hbuf.line_max = line_width;
287 rc = mime_header_format (value, params, &hbuf);
288 if (rc == 0)
289 {
290 mu_off_t pos;
291 char *fmtval;
292
293 mu_stream_seek (hbuf.str, 0, MU_SEEK_CUR, &pos);
294 fmtval = malloc (pos + 1);
295 mu_stream_seek (hbuf.str, 0, MU_SEEK_SET, NULL);
296 mu_stream_read (hbuf.str, fmtval, pos, NULL);
297 fmtval[pos] = 0;
298 rc = mu_header_set_value (hdr, name, fmtval, 1);
299 free (fmtval);
300 }
301 mu_stream_destroy (&hbuf.str);
302 return rc;
303 }
304
305 int
mu_mime_header_set(mu_header_t hdr,const char * name,const char * value,mu_assoc_t params)306 mu_mime_header_set (mu_header_t hdr, const char *name,
307 const char *value, mu_assoc_t params)
308 {
309 return mu_mime_header_set_w (hdr, name, value, params, 76);
310 }
311
312
313
314