1 /* GNU Mailutils -- a suite of utilities for electronic mail
2    Copyright (C) 2009-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 
21 #include <stdlib.h>
22 #include <errno.h>
23 #include <mailutils/types.h>
24 #include <mailutils/sys/rdcache_stream.h>
25 
26 size_t mu_rdcache_stream_max_memory_size = 4096;
27 
28 static int
rdcache_read(struct _mu_stream * str,char * buf,size_t size,size_t * pnbytes)29 rdcache_read (struct _mu_stream *str, char *buf, size_t size, size_t *pnbytes)
30 {
31   struct _mu_rdcache_stream *sp = (struct _mu_rdcache_stream *) str;
32   int status = 0;
33   size_t nbytes = 0;
34 
35   if (sp->offset < sp->size)
36     {
37       status = mu_stream_read (sp->cache, buf, size, &nbytes);
38       if (status)
39 	return status;
40       sp->offset += nbytes;
41       sp->size += nbytes;
42       buf += nbytes;
43       size -= nbytes;
44     }
45   else if (sp->offset > sp->size)
46     {
47       size_t left = sp->offset - sp->size;
48       status = mu_stream_seek (sp->cache, 0, MU_SEEK_END, NULL);
49       if (status)
50 	return status;
51       status = mu_stream_copy (sp->cache, sp->transport, left, NULL);
52       if (status)
53 	return status;
54       sp->size = sp->offset;
55     }
56 
57   if (size)
58     {
59       size_t rdbytes;
60       status = mu_stream_read (sp->transport, buf, size, &rdbytes);
61       if (rdbytes)
62 	{
63 	  int rc;
64 
65 	  sp->offset += rdbytes;
66 	  sp->size += rdbytes;
67 	  nbytes += rdbytes;
68 	  rc = mu_stream_write (sp->cache, buf, rdbytes, NULL);
69 	  if (rc)
70 	    {
71 	      if (status == 0)
72 		status = rc;
73 	    }
74 	}
75     }
76   if (pnbytes)
77     *pnbytes = nbytes;
78   return status;
79 }
80 
81 static int
rdcache_size(struct _mu_stream * str,off_t * psize)82 rdcache_size (struct _mu_stream *str, off_t *psize)
83 {
84   struct _mu_rdcache_stream *sp = (struct _mu_rdcache_stream *) str;
85   *psize = sp->size;
86   return 0;
87 }
88 
89 static int
rdcache_seek(struct _mu_stream * str,mu_off_t off,mu_off_t * presult)90 rdcache_seek (struct _mu_stream *str, mu_off_t off, mu_off_t *presult)
91 {
92   struct _mu_rdcache_stream *sp = (struct _mu_rdcache_stream *) str;
93 
94   if (off < 0)
95     return ESPIPE;
96 
97   if (off < sp->size)
98     {
99       int status = mu_stream_seek (sp->cache, off, MU_SEEK_SET, NULL);
100       if (status)
101 	return status;
102     }
103 
104   sp->offset = off;
105   *presult = sp->offset;
106   return 0;
107 }
108 
109 static int
rdcache_wait(struct _mu_stream * str,int * pflags,struct timeval * tvp)110 rdcache_wait (struct _mu_stream *str, int *pflags, struct timeval *tvp)
111 {
112   struct _mu_rdcache_stream *sp = (struct _mu_rdcache_stream *) str;
113   return mu_stream_wait (sp->transport, pflags, tvp);
114 }
115 
116 /* FIXME: Truncate? */
117 
118 static int
rdcache_ioctl(struct _mu_stream * str,int code,int opcode,void * arg)119 rdcache_ioctl (struct _mu_stream *str, int code, int opcode, void *arg)
120 {
121   struct _mu_rdcache_stream *sp = (struct _mu_rdcache_stream *) str;
122 
123   switch (code)
124     {
125     case MU_IOCTL_TRANSPORT:
126       if (!arg)
127 	return EINVAL;
128       else
129 	{
130 	  mu_transport_t *ptrans = arg;
131 
132 	  switch (opcode)
133 	    {
134 	    case MU_IOCTL_OP_GET:
135 	      ptrans[0] = (mu_transport_t) sp->transport;
136 	      ptrans[1] = NULL;
137 	      break;
138 	    case MU_IOCTL_OP_SET:
139 	      return ENOSYS;
140 	    default:
141 	      return EINVAL;
142 	    }
143 	}
144       break;
145 
146     case MU_IOCTL_TRANSPORT_BUFFER:
147       if (!arg)
148 	return EINVAL;
149       else
150 	{
151 	  struct mu_buffer_query *qp = arg;
152 	  if (qp->type != MU_TRANSPORT_INPUT || !sp->transport)
153 	    return EINVAL;
154 	  return mu_stream_ioctl (sp->transport, code, opcode, arg);
155 	}
156 
157     default:
158       return mu_stream_ioctl (sp->transport, code, opcode, arg);
159     }
160   return 0;
161 }
162 
163 static int
rdcache_open(struct _mu_stream * str)164 rdcache_open (struct _mu_stream *str)
165 {
166   struct _mu_rdcache_stream *sp = (struct _mu_rdcache_stream *) str;
167   return mu_stream_open (sp->transport);
168 }
169 
170 static int
rdcache_close(struct _mu_stream * str)171 rdcache_close (struct _mu_stream *str)
172 {
173   struct _mu_rdcache_stream *sp = (struct _mu_rdcache_stream *) str;
174   return mu_stream_close (sp->transport);
175 }
176 
177 static void
rdcache_done(struct _mu_stream * str)178 rdcache_done (struct _mu_stream *str)
179 {
180   struct _mu_rdcache_stream *sp = (struct _mu_rdcache_stream *) str;
181   mu_stream_unref (sp->transport);
182   mu_stream_unref (sp->cache);
183 }
184 
185 int
mu_rdcache_stream_create(mu_stream_t * pstream,mu_stream_t transport,int flags)186 mu_rdcache_stream_create (mu_stream_t *pstream, mu_stream_t transport,
187 			  int flags)
188 {
189   struct _mu_rdcache_stream *sp;
190   int rc;
191   int sflags = MU_STREAM_READ | MU_STREAM_SEEK;
192 
193   if (flags & ~sflags)
194     return EINVAL;
195 
196   sp = (struct _mu_rdcache_stream *)
197           _mu_stream_create (sizeof (*sp), sflags | _MU_STR_OPEN);
198   if (!sp)
199     return ENOMEM;
200 
201   sp->stream.read = rdcache_read;
202   sp->stream.open = rdcache_open;
203   sp->stream.close = rdcache_close;
204   sp->stream.done = rdcache_done;
205   sp->stream.seek = rdcache_seek;
206   sp->stream.size = rdcache_size;
207   sp->stream.ctl = rdcache_ioctl;
208   sp->stream.wait = rdcache_wait;
209 
210   mu_stream_ref (transport);
211   sp->transport = transport;
212 
213   if ((rc = mu_memory_stream_create (&sp->cache, MU_STREAM_RDWR)))
214     {
215       mu_stream_destroy ((mu_stream_t*) &sp);
216       return rc;
217     }
218 
219   *pstream = (mu_stream_t) sp;
220   return 0;
221 }
222 
223