1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999-2021 Free Software Foundation, Inc.
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 3 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General
15 Public License along with this library. If not, see
16 <http://www.gnu.org/licenses/>. */
17
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21
22 #include <errno.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <fcntl.h>
30
31 #include <mailutils/stream.h>
32 #include <mailutils/util.h>
33 #include <mailutils/errno.h>
34 #include <mailutils/sys/stream.h>
35 #include <mailutils/sys/body.h>
36
37 #define BODY_MODIFIED 0x10000
38
39
40 struct _mu_body_stream
41 {
42 struct _mu_stream stream;
43 mu_body_t body;
44 mu_stream_t transport;
45 };
46
47 /* Body stream. */
48 #define BODY_RDONLY 0
49 #define BODY_RDWR 1
50
51 static int
init_tmp_stream(mu_body_t body)52 init_tmp_stream (mu_body_t body)
53 {
54 int rc;
55 mu_off_t off;
56
57 rc = mu_stream_seek (body->data_stream, 0, MU_SEEK_CUR, &off);
58 if (rc)
59 return rc;
60
61 rc = mu_stream_seek (body->data_stream, 0, MU_SEEK_SET, NULL);
62 if (rc)
63 return rc;
64
65 rc = mu_stream_copy (body->temp_stream, body->data_stream, 0, NULL);
66 if (rc)
67 return rc;
68
69 mu_stream_seek (body->data_stream, off, MU_SEEK_SET, NULL);
70
71 return mu_stream_seek (body->temp_stream, off, MU_SEEK_SET, NULL);
72 }
73
74 static int
body_get_transport(mu_body_t body,int mode,mu_stream_t * pstr)75 body_get_transport (mu_body_t body, int mode, mu_stream_t *pstr)
76 {
77 if (!body->data_stream && body->_get_stream)
78 {
79 int status = body->_get_stream (body, &body->data_stream);
80 if (status)
81 return status;
82 }
83 if (mode == BODY_RDWR || !body->data_stream)
84 {
85 /* Create the temporary file. */
86 if (!body->temp_stream)
87 {
88 int rc;
89
90 rc = mu_temp_stream_create (&body->temp_stream, 0);
91 if (rc)
92 return rc;
93 mu_stream_set_buffer (body->temp_stream, mu_buffer_full, 0);
94 if (body->data_stream)
95 {
96 rc = init_tmp_stream (body);
97 if (rc)
98 {
99 mu_stream_destroy (&body->temp_stream);
100 return rc;
101 }
102 }
103 }
104 body->flags |= BODY_MODIFIED;
105 }
106 *pstr = body->temp_stream ? body->temp_stream : body->data_stream;
107 return 0;
108 }
109
110 static int
bstr_close(struct _mu_stream * stream)111 bstr_close (struct _mu_stream *stream)
112 {
113 return 0;
114 }
115
116 void
bstr_done(struct _mu_stream * stream)117 bstr_done (struct _mu_stream *stream)
118 {
119 struct _mu_body_stream *str = (struct _mu_body_stream*) stream;
120 mu_stream_unref (str->transport);
121 mu_body_unref (str->body);
122 }
123
124 static int
bstr_seek(mu_stream_t stream,mu_off_t off,mu_off_t * presult)125 bstr_seek (mu_stream_t stream, mu_off_t off, mu_off_t *presult)
126 {
127 struct _mu_body_stream *str = (struct _mu_body_stream*) stream;
128 return mu_stream_seek (str->transport, off, MU_SEEK_SET, presult);
129 }
130
131 static int
bstr_ioctl(mu_stream_t stream,int code,int opcode,void * ptr)132 bstr_ioctl (mu_stream_t stream, int code, int opcode, void *ptr)
133 {
134 struct _mu_body_stream *str = (struct _mu_body_stream*) stream;
135 return mu_stream_ioctl (str->transport, code, opcode, ptr);
136 }
137
138 static int
bstr_read(mu_stream_t stream,char * buf,size_t size,size_t * pret)139 bstr_read (mu_stream_t stream, char *buf, size_t size, size_t *pret)
140 {
141 struct _mu_body_stream *str = (struct _mu_body_stream*) stream;
142 return mu_stream_read (str->transport, buf, size, pret);
143 }
144
145 static int
bstr_write(mu_stream_t stream,const char * buf,size_t size,size_t * pret)146 bstr_write (mu_stream_t stream, const char *buf, size_t size, size_t *pret)
147 {
148 struct _mu_body_stream *str = (struct _mu_body_stream*) stream;
149 if (!str->body->temp_stream)
150 {
151 int rc;
152 mu_off_t off;
153 mu_stream_t tmp, transport;
154
155 rc = mu_stream_seek (str->transport, 0, MU_SEEK_CUR, &off);
156 if (rc)
157 return rc;
158 rc = body_get_transport (str->body, BODY_RDWR, &tmp);
159 if (rc)
160 return rc;
161 rc = mu_streamref_create (&transport, tmp);
162 if (rc)
163 return rc;
164 mu_stream_destroy (&str->transport);
165 str->transport = transport;
166 rc = mu_stream_seek (str->transport, off, MU_SEEK_SET, NULL);
167 if (rc)
168 return rc;
169 }
170 return mu_stream_write (str->transport, buf, size, pret);
171 }
172
173 static int
bstr_truncate(mu_stream_t stream,mu_off_t n)174 bstr_truncate (mu_stream_t stream, mu_off_t n)
175 {
176 struct _mu_body_stream *str = (struct _mu_body_stream*) stream;
177 return mu_stream_truncate (str->transport, n);
178 }
179
180 static int
bstr_size(mu_stream_t stream,mu_off_t * size)181 bstr_size (mu_stream_t stream, mu_off_t *size)
182 {
183 struct _mu_body_stream *str = (struct _mu_body_stream*) stream;
184 return mu_stream_size (str->transport, size);
185 }
186
187 static int
bstr_flush(mu_stream_t stream)188 bstr_flush (mu_stream_t stream)
189 {
190 struct _mu_body_stream *str = (struct _mu_body_stream*) stream;
191 return mu_stream_flush (str->transport);
192 }
193
194 /* Default function for the body. */
195 static int
body_get_lines(mu_body_t body,size_t * plines)196 body_get_lines (mu_body_t body, size_t *plines)
197 {
198 mu_stream_t transport, null;
199 int status;
200 mu_off_t off;
201 mu_stream_stat_buffer stat;
202
203 status = body_get_transport (body, BODY_RDONLY, &transport);
204 if (status)
205 return status;
206
207 status = mu_stream_flush (transport);
208 if (status)
209 return status;
210 status = mu_stream_seek (transport, 0, MU_SEEK_CUR, &off);
211 if (status)
212 return status;
213 status = mu_stream_seek (transport, 0, MU_SEEK_SET, NULL);
214 if (status)
215 return status;
216 status = mu_nullstream_create (&null, MU_STREAM_WRITE);
217 if (status)
218 return status;
219 mu_stream_set_stat (null, MU_STREAM_STAT_MASK (MU_STREAM_STAT_OUTLN),
220 stat);
221 status = mu_stream_copy (null, transport, 0, NULL);
222 mu_stream_destroy (&null);
223 mu_stream_seek (transport, off, MU_SEEK_SET, NULL);
224 if (status == 0)
225 *plines = stat[MU_STREAM_STAT_OUTLN];
226 return status;
227 }
228
229 static int
body_get_size(mu_body_t body,size_t * psize)230 body_get_size (mu_body_t body, size_t *psize)
231 {
232 mu_stream_t transport;
233 mu_off_t off = 0;
234 int rc;
235
236 rc = body_get_transport (body, BODY_RDONLY, &transport);
237 if (rc)
238 return rc;
239 rc = mu_stream_size (transport, &off);
240 if (rc == 0)
241 *psize = off;
242 return 0;
243 }
244
245
246 static int
body_stream_create(mu_body_t body,mu_stream_t * return_stream)247 body_stream_create (mu_body_t body, mu_stream_t *return_stream)
248 {
249 int rc;
250 mu_stream_t stream, transport;
251 struct _mu_body_stream *str;
252
253 rc = body_get_transport (body, BODY_RDONLY, &stream);
254 if (rc)
255 return rc;
256 rc = mu_streamref_create (&transport, stream);
257 if (rc)
258 return rc;
259
260 str = (struct _mu_body_stream *)
261 _mu_stream_create (sizeof (*str),
262 MU_STREAM_RDWR|MU_STREAM_SEEK|_MU_STR_OPEN);
263 if (!str)
264 return ENOMEM;
265 str->transport = transport;
266 str->body = body;
267 str->stream.ctl = bstr_ioctl;
268 str->stream.read = bstr_read;
269 str->stream.write = bstr_write;
270 str->stream.truncate = bstr_truncate;
271 str->stream.size = bstr_size;
272 str->stream.seek = bstr_seek;
273 str->stream.flush = bstr_flush;
274 str->stream.close = bstr_close;
275 str->stream.done = bstr_done;
276 /* Override the defaults. */
277 body->_lines = body_get_lines;
278 body->_size = body_get_size;
279 mu_body_ref (body);
280 *return_stream = (mu_stream_t) str;
281 return 0;
282 }
283
284 int
mu_body_get_streamref(mu_body_t body,mu_stream_t * pstream)285 mu_body_get_streamref (mu_body_t body, mu_stream_t *pstream)
286 {
287 if (body == NULL)
288 return EINVAL;
289 if (pstream == NULL)
290 return MU_ERR_OUT_PTR_NULL;
291 return body_stream_create (body, pstream);
292 }
293
294 int
mu_body_set_stream(mu_body_t body,mu_stream_t stream,void * owner)295 mu_body_set_stream (mu_body_t body, mu_stream_t stream, void *owner)
296 {
297 if (body == NULL)
298 return EINVAL;
299 if (body->owner != owner)
300 return EACCES;
301 /* make sure we destroy the old one if it is owned by the body */
302 mu_stream_destroy (&body->temp_stream);
303 mu_stream_destroy (&body->data_stream);
304 body->data_stream = stream;
305 body->flags |= BODY_MODIFIED;
306 return 0;
307 }
308
309 int
mu_body_set_get_stream(mu_body_t body,int (* _getstr)(mu_body_t,mu_stream_t *),void * owner)310 mu_body_set_get_stream (mu_body_t body,
311 int (*_getstr) (mu_body_t, mu_stream_t *),
312 void *owner)
313 {
314 if (body == NULL)
315 return EINVAL;
316 if (body->owner != owner)
317 return EACCES;
318 body->_get_stream = _getstr;
319 return 0;
320 }
321
322 int
mu_body_set_lines(mu_body_t body,int (* _lines)(mu_body_t,size_t *),void * owner)323 mu_body_set_lines (mu_body_t body, int (*_lines) (mu_body_t, size_t *),
324 void *owner)
325 {
326 if (body == NULL)
327 return EINVAL;
328 if (body->owner != owner)
329 return EACCES;
330 body->_lines = _lines;
331 return 0;
332 }
333
334 int
mu_body_lines(mu_body_t body,size_t * plines)335 mu_body_lines (mu_body_t body, size_t *plines)
336 {
337 if (body == NULL)
338 return EINVAL;
339 if (plines == NULL)
340 return MU_ERR_OUT_PTR_NULL;
341 if (body->_lines)
342 return body->_lines (body, plines);
343 /* Fall back on the stream. */
344 return body_get_lines (body, plines);
345 }
346
347 int
mu_body_size(mu_body_t body,size_t * psize)348 mu_body_size (mu_body_t body, size_t *psize)
349 {
350 int rc;
351 mu_stream_t str;
352 mu_off_t s;
353
354 if (body == NULL)
355 return EINVAL;
356 if (psize == NULL)
357 return MU_ERR_OUT_PTR_NULL;
358 if (body->_size)
359 return body->_size (body, psize);
360 /* Fall back on the transport stream. */
361 rc = body_get_transport (body, BODY_RDONLY, &str);
362 if (rc)
363 return rc;
364 rc = mu_stream_size (str, &s);
365 *psize = s;
366 return 0;
367 }
368
369 int
mu_body_set_size(mu_body_t body,int (* _size)(mu_body_t,size_t *),void * owner)370 mu_body_set_size (mu_body_t body, int (*_size)(mu_body_t, size_t*),
371 void *owner)
372 {
373 if (body == NULL)
374 return EINVAL;
375 if (body->owner != owner)
376 return EACCES;
377 body->_size = _size;
378 return 0;
379 }
380
381
382 int
mu_body_create(mu_body_t * pbody,void * owner)383 mu_body_create (mu_body_t *pbody, void *owner)
384 {
385 mu_body_t body;
386
387 if (pbody == NULL)
388 return MU_ERR_OUT_PTR_NULL;
389 if (owner == NULL)
390 return EINVAL;
391
392 body = calloc (1, sizeof (*body));
393 if (body == NULL)
394 return ENOMEM;
395
396 body->owner = owner;
397 mu_body_ref (body);
398 *pbody = body;
399 return 0;
400 }
401
402 static void
_mu_body_free(mu_body_t body)403 _mu_body_free (mu_body_t body)
404 {
405 mu_stream_destroy (&body->data_stream);
406 mu_stream_destroy (&body->temp_stream);
407 free (body);
408 }
409
410 void
mu_body_ref(mu_body_t body)411 mu_body_ref (mu_body_t body)
412 {
413 if (body)
414 body->ref_count++;
415 }
416
417 void
mu_body_unref(mu_body_t body)418 mu_body_unref (mu_body_t body)
419 {
420 if (body && --body->ref_count == 0)
421 _mu_body_free (body);
422 }
423
424 void
mu_body_destroy(mu_body_t * pbody,void * owner)425 mu_body_destroy (mu_body_t *pbody, void *owner)
426 {
427 if (pbody && *pbody)
428 {
429 mu_body_t body = *pbody;
430 if (body->owner == owner && --body->ref_count == 0)
431 {
432 _mu_body_free (body);
433 *pbody = NULL;
434 }
435 }
436 }
437
438 void *
mu_body_get_owner(mu_body_t body)439 mu_body_get_owner (mu_body_t body)
440 {
441 return (body) ? body->owner : NULL;
442 }
443
444 int
mu_body_is_modified(mu_body_t body)445 mu_body_is_modified (mu_body_t body)
446 {
447 return (body) ? (body->flags & BODY_MODIFIED) : 0;
448 }
449
450 int
mu_body_clear_modified(mu_body_t body)451 mu_body_clear_modified (mu_body_t body)
452 {
453 if (body)
454 body->flags &= ~BODY_MODIFIED;
455 return 0;
456 }
457
458
459
460