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