1 /* GNU Mailutils -- a suite of utilities for electronic mail
2    Copyright (C) 2010-2021 Free Software Foundation, Inc.
3 
4    This library is free software; you can redistribute it and/or modify
5    it under the terms of the GNU Lesser General Public License as published by
6    the Free Software Foundation; either version 3, or (at your option)
7    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
12    GNU Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public License
15    along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */
16 
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 #include <stdlib.h>
21 #include <errno.h>
22 #include <mailutils/types.h>
23 #include <mailutils/stream.h>
24 #include <mailutils/errno.h>
25 #include <mailutils/sys/streamref.h>
26 
27 static int
_streamref_read(struct _mu_stream * str,char * buf,size_t bufsize,size_t * pnread)28 _streamref_read (struct _mu_stream *str, char *buf, size_t bufsize,
29 		 size_t *pnread)
30 {
31   struct _mu_streamref *sp = (struct _mu_streamref *)str;
32   int rc;
33   size_t nread;
34   mu_off_t off;
35 
36   rc = mu_stream_seek (sp->transport, sp->offset, MU_SEEK_SET, &off);
37   if (rc == 0)
38     {
39       if (sp->end)
40 	{
41 	  size_t size = sp->end - off + 1;
42 	  if (size < bufsize)
43 	    bufsize = size;
44 	}
45       rc = mu_stream_read (sp->transport, buf, bufsize, &nread);
46       if (rc == 0)
47 	{
48 	  sp->offset += nread;
49 	  *pnread = nread;
50 	}
51     }
52   else if (rc == ESPIPE)
53     {
54       *pnread = 0;
55       mu_stream_clearerr (sp->transport);
56       return 0;
57     }
58   return rc;
59 }
60 
61 static int
_streamref_write(struct _mu_stream * str,const char * buf,size_t bufsize,size_t * pnwrite)62 _streamref_write (struct _mu_stream *str, const char *buf, size_t bufsize,
63 		  size_t *pnwrite)
64 {
65   struct _mu_streamref *sp = (struct _mu_streamref *)str;
66   int rc;
67   size_t nwrite;
68   rc = mu_stream_seek (sp->transport, sp->offset, MU_SEEK_SET, NULL);
69   if (rc == 0)
70     {
71       rc = mu_stream_write (sp->transport, buf, bufsize, &nwrite);
72       if (rc == 0)
73 	{
74 	  sp->offset += nwrite;
75 	  *pnwrite = nwrite;
76 	}
77     }
78   return rc;
79 }
80 
81 static int
_streamref_flush(struct _mu_stream * str)82 _streamref_flush (struct _mu_stream *str)
83 {
84   struct _mu_streamref *sp = (struct _mu_streamref *)str;
85   return mu_stream_flush (sp->transport);
86 }
87 
88 static int
_streamref_open(struct _mu_stream * str)89 _streamref_open (struct _mu_stream *str)
90 {
91   struct _mu_streamref *sp = (struct _mu_streamref *)str;
92   return mu_stream_open (sp->transport);
93 }
94 
95 static int
_streamref_close(struct _mu_stream * str)96 _streamref_close (struct _mu_stream *str)
97 {
98   struct _mu_streamref *sp = (struct _mu_streamref *)str;
99   return mu_stream_close (sp->transport);
100 }
101 
102 static void
_streamref_done(struct _mu_stream * str)103 _streamref_done (struct _mu_stream *str)
104 {
105   struct _mu_streamref *sp = (struct _mu_streamref *)str;
106   mu_stream_unref (sp->transport);
107 }
108 
109 static int
_streamref_seek(struct _mu_stream * str,mu_off_t off,mu_off_t * ppos)110 _streamref_seek (struct _mu_stream *str, mu_off_t off, mu_off_t *ppos)
111 {
112   struct _mu_streamref *sp = (struct _mu_streamref *)str;
113   mu_off_t size;
114   int rc;
115 
116   if (sp->end)
117     size = sp->end - sp->start + 1;
118   else
119     {
120       rc = mu_stream_size (sp->transport, &size);
121       if (rc)
122 	return rc;
123       size -= sp->start;
124     }
125 
126   if (off < 0 || off > size)
127     return sp->stream.last_err = ESPIPE;
128   rc = mu_stream_seek (sp->transport, sp->start + off, MU_SEEK_SET,
129 		       &sp->offset);
130   if (rc)
131     return rc;
132   *ppos = sp->offset - sp->start;
133   return 0;
134 }
135 
136 static int
_streamref_size(struct _mu_stream * str,mu_off_t * psize)137 _streamref_size (struct _mu_stream *str, mu_off_t *psize)
138 {
139   struct _mu_streamref *sp = (struct _mu_streamref *)str;
140   mu_off_t size;
141   int rc = 0;
142 
143   if (sp->end)
144     size = sp->end - sp->start + 1;
145   else
146     {
147       rc = mu_stream_size (sp->transport, &size);
148       if (rc)
149 	return rc;
150       size -= sp->start;
151     }
152   if (rc == 0)
153     *psize = size;
154   return rc;
155 }
156 
157 static int
_streamref_ctl(struct _mu_stream * str,int code,int opcode,void * arg)158 _streamref_ctl (struct _mu_stream *str, int code, int opcode, void *arg)
159 {
160   struct _mu_streamref *sp = (struct _mu_streamref *)str;
161 
162   switch (code)
163     {
164     case MU_IOCTL_SEEK_LIMITS:
165       if (!arg)
166 	return EINVAL;
167       else
168 	{
169 	  mu_off_t *lim = arg;
170 
171 	  switch (opcode)
172 	    {
173 	    case MU_IOCTL_OP_GET:
174 	      lim[0] = sp->start;
175 	      lim[1] = sp->end;
176 	      return 0;
177 
178 	    case MU_IOCTL_OP_SET:
179 	      sp->start = lim[0];
180 	      sp->end = lim[1];
181 	      return 0;
182 
183 	    default:
184 	      return EINVAL;
185 	    }
186 	}
187     }
188   return mu_stream_ioctl (sp->transport, code, opcode, arg);
189 }
190 
191 static int
_streamref_wait(struct _mu_stream * str,int * pflags,struct timeval * tvp)192 _streamref_wait (struct _mu_stream *str, int *pflags, struct timeval *tvp)
193 {
194   struct _mu_streamref *sp = (struct _mu_streamref *)str;
195   return mu_stream_wait (sp->transport, pflags, tvp);
196 }
197 
198 static int
_streamref_truncate(struct _mu_stream * str,mu_off_t size)199 _streamref_truncate (struct _mu_stream *str, mu_off_t size)
200 {
201   struct _mu_streamref *sp = (struct _mu_streamref *)str;
202   return mu_stream_truncate (sp->transport, size);
203 }
204 
205 static int
_streamref_shutdown(struct _mu_stream * str,int how)206 _streamref_shutdown (struct _mu_stream *str, int how)
207 {
208   struct _mu_streamref *sp = (struct _mu_streamref *)str;
209   return mu_stream_shutdown (sp->transport, how);
210 }
211 
212 static const char *
_streamref_error_string(struct _mu_stream * str,int rc)213 _streamref_error_string (struct _mu_stream *str, int rc)
214 {
215   struct _mu_streamref *sp = (struct _mu_streamref *)str;
216   const char *p = mu_stream_strerror (sp->transport, rc);
217   if (!p)
218     p = mu_strerror (rc);
219   return p;
220 }
221 
222 int
mu_streamref_create_abridged(mu_stream_t * pref,mu_stream_t str,mu_off_t start,mu_off_t end)223 mu_streamref_create_abridged (mu_stream_t *pref, mu_stream_t str,
224 			      mu_off_t start, mu_off_t end)
225 {
226   int flags;
227   struct _mu_streamref *sp;
228 
229   mu_stream_get_flags (str, &flags);
230   sp = (struct _mu_streamref *)
231          _mu_stream_create (sizeof (*sp), flags | _MU_STR_OPEN);
232   if (!sp)
233     return ENOMEM;
234 
235   mu_stream_ref (str);
236 
237   sp->stream.read = _streamref_read;
238   sp->stream.write = _streamref_write;
239   sp->stream.flush = _streamref_flush;
240   sp->stream.open = _streamref_open;
241   sp->stream.close = _streamref_close;
242   sp->stream.done = _streamref_done;
243   sp->stream.seek = _streamref_seek;
244   sp->stream.size = _streamref_size;
245   sp->stream.ctl = _streamref_ctl;
246   sp->stream.wait = _streamref_wait;
247   sp->stream.truncate = _streamref_truncate;
248   sp->stream.shutdown = _streamref_shutdown;
249   sp->stream.error_string = _streamref_error_string;
250 
251   sp->transport = str;
252   sp->start = start;
253   sp->end = end;
254   sp->offset = start;
255   *pref = (mu_stream_t) sp;
256 
257   mu_stream_set_buffer (*pref, mu_buffer_full, 0);
258 
259   return 0;
260 }
261 
262 int
mu_streamref_create(mu_stream_t * pref,mu_stream_t str)263 mu_streamref_create (mu_stream_t *pref, mu_stream_t str)
264 {
265   return mu_streamref_create_abridged (pref, str, 0, 0);
266 }
267 
268