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