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