NeoMutt  2023-05-17-16-g61469c
Teaching an old dog new tricks
DOXYGEN
zstrm.c
Go to the documentation of this file.
1
28#include "config.h"
29#include <stdbool.h>
30#include <string.h>
31#include <time.h>
32#include <zconf.h>
33#include <zlib.h>
34#include "mutt/lib.h"
35#include "zstrm.h"
36#include "lib.h"
37
42{
43 z_stream z;
44 char *buf;
45 unsigned int len;
46 unsigned int pos;
47 bool conn_eof : 1;
48 bool stream_eof : 1;
49};
50
55{
59};
60
68static void *zstrm_malloc(void *opaque, unsigned int items, unsigned int size)
69{
70 return mutt_mem_calloc(items, size);
71}
72
78static void zstrm_free(void *opaque, void *address)
79{
80 FREE(&address);
81}
82
89static int zstrm_open(struct Connection *conn)
90{
91 return -1;
92}
93
97static int zstrm_close(struct Connection *conn)
98{
99 struct ZstrmContext *zctx = conn->sockdata;
100
101 int rc = zctx->next_conn.close(&zctx->next_conn);
102
103 mutt_debug(LL_DEBUG5, "read %lu->%lu (%.1fx) wrote %lu<-%lu (%.1fx)\n",
104 zctx->read.z.total_in, zctx->read.z.total_out,
105 (float) zctx->read.z.total_out / (float) zctx->read.z.total_in,
106 zctx->write.z.total_in, zctx->write.z.total_out,
107 (float) zctx->write.z.total_in / (float) zctx->write.z.total_out);
108
109 // Restore the Connection's original functions
110 conn->sockdata = zctx->next_conn.sockdata;
111 conn->open = zctx->next_conn.open;
112 conn->close = zctx->next_conn.close;
113 conn->read = zctx->next_conn.read;
114 conn->write = zctx->next_conn.write;
115 conn->poll = zctx->next_conn.poll;
116
117 inflateEnd(&zctx->read.z);
118 deflateEnd(&zctx->write.z);
119 FREE(&zctx->read.buf);
120 FREE(&zctx->write.buf);
121 FREE(&zctx);
122
123 return rc;
124}
125
129static int zstrm_read(struct Connection *conn, char *buf, size_t len)
130{
131 struct ZstrmContext *zctx = conn->sockdata;
132 int rc = 0;
133 int zrc = 0;
134
135retry:
136 if (zctx->read.stream_eof)
137 return 0;
138
139 /* when avail_out was 0 on last call, we need to call inflate again, because
140 * more data might be available using the current input, so avoid callling
141 * read on the underlying stream in that case (for it might block) */
142 if ((zctx->read.pos == 0) && !zctx->read.conn_eof)
143 {
144 rc = zctx->next_conn.read(&zctx->next_conn, zctx->read.buf, zctx->read.len);
145 mutt_debug(LL_DEBUG5, "consuming data from next stream: %d bytes\n", rc);
146 if (rc < 0)
147 return rc;
148 else if (rc == 0)
149 zctx->read.conn_eof = true;
150 else
151 zctx->read.pos += rc;
152 }
153
154 zctx->read.z.avail_in = (uInt) zctx->read.pos;
155 zctx->read.z.next_in = (Bytef *) zctx->read.buf;
156 zctx->read.z.avail_out = (uInt) len;
157 zctx->read.z.next_out = (Bytef *) buf;
158
159 zrc = inflate(&zctx->read.z, Z_SYNC_FLUSH);
160 mutt_debug(LL_DEBUG5, "rc=%d, consumed %u/%u bytes, produced %lu/%lu bytes\n",
161 zrc, zctx->read.pos - zctx->read.z.avail_in, zctx->read.pos,
162 len - zctx->read.z.avail_out, len);
163
164 /* shift any remaining input data to the front of the buffer */
165 if ((Bytef *) zctx->read.buf != zctx->read.z.next_in)
166 {
167 memmove(zctx->read.buf, zctx->read.z.next_in, zctx->read.z.avail_in);
168 zctx->read.pos = zctx->read.z.avail_in;
169 }
170
171 switch (zrc)
172 {
173 case Z_OK: /* progress has been made */
174 zrc = len - zctx->read.z.avail_out; /* "returned" bytes */
175 if (zrc == 0)
176 {
177 /* there was progress, so must have been reading input */
178 mutt_debug(LL_DEBUG5, "inflate just consumed\n");
179 goto retry;
180 }
181 break;
182
183 case Z_STREAM_END: /* everything flushed, nothing remaining */
184 mutt_debug(LL_DEBUG5, "inflate returned Z_STREAM_END.\n");
185 zrc = len - zctx->read.z.avail_out; /* "returned" bytes */
186 zctx->read.stream_eof = true;
187 break;
188
189 case Z_BUF_ERROR: /* no progress was possible */
190 if (!zctx->read.conn_eof)
191 {
192 mutt_debug(LL_DEBUG5, "inflate returned Z_BUF_ERROR. retrying.\n");
193 goto retry;
194 }
195 zrc = 0;
196 break;
197
198 default:
199 /* bail on other rcs, such as Z_DATA_ERROR, or Z_MEM_ERROR */
200 mutt_debug(LL_DEBUG5, "inflate returned %d. aborting.\n", zrc);
201 zrc = -1;
202 break;
203 }
204
205 return zrc;
206}
207
211static int zstrm_poll(struct Connection *conn, time_t wait_secs)
212{
213 struct ZstrmContext *zctx = conn->sockdata;
214
215 mutt_debug(LL_DEBUG5, "%s\n",
216 (zctx->read.z.avail_out == 0) || (zctx->read.pos > 0) ?
217 "last read wrote full buffer" :
218 "falling back on next stream");
219 if ((zctx->read.z.avail_out == 0) || (zctx->read.pos > 0))
220 return 1;
221
222 return zctx->next_conn.poll(&zctx->next_conn, wait_secs);
223}
224
228static int zstrm_write(struct Connection *conn, const char *buf, size_t count)
229{
230 struct ZstrmContext *zctx = conn->sockdata;
231 int rc;
232
233 zctx->write.z.avail_in = (uInt) count;
234 zctx->write.z.next_in = (Bytef *) buf;
235 zctx->write.z.avail_out = (uInt) zctx->write.len;
236 zctx->write.z.next_out = (Bytef *) zctx->write.buf;
237
238 do
239 {
240 int zrc = deflate(&zctx->write.z, Z_PARTIAL_FLUSH);
241 if (zrc == Z_OK)
242 {
243 /* push out produced data to the underlying stream */
244 zctx->write.pos = zctx->write.len - zctx->write.z.avail_out;
245 char *wbufp = zctx->write.buf;
246 mutt_debug(LL_DEBUG5, "deflate consumed %lu/%lu bytes\n",
247 count - zctx->write.z.avail_in, count);
248 while (zctx->write.pos > 0)
249 {
250 rc = zctx->next_conn.write(&zctx->next_conn, wbufp, zctx->write.pos);
251 mutt_debug(LL_DEBUG5, "next stream wrote: %d bytes\n", rc);
252 if (rc < 0)
253 return -1; /* we can't recover from write failure */
254
255 wbufp += rc;
256 zctx->write.pos -= rc;
257 }
258
259 /* see if there's more for us to do, retry if the output buffer
260 * was full (there may be something in zlib buffers), and retry
261 * when there is still available input data */
262 if ((zctx->write.z.avail_out != 0) && (zctx->write.z.avail_in == 0))
263 break;
264
265 zctx->write.z.avail_out = (uInt) zctx->write.len;
266 zctx->write.z.next_out = (Bytef *) zctx->write.buf;
267 }
268 else
269 {
270 /* compression went wrong, but this is basically impossible
271 * according to the docs */
272 return -1;
273 }
274 } while (true);
275
276 rc = (int) count;
277 return (rc <= 0) ? 1 : rc; /* avoid wrong behaviour due to overflow */
278}
279
289{
290 struct ZstrmContext *zctx = mutt_mem_calloc(1, sizeof(struct ZstrmContext));
291
292 /* store wrapped stream as next stream */
293 zctx->next_conn.fd = conn->fd;
294 zctx->next_conn.sockdata = conn->sockdata;
295 zctx->next_conn.open = conn->open;
296 zctx->next_conn.close = conn->close;
297 zctx->next_conn.read = conn->read;
298 zctx->next_conn.write = conn->write;
299 zctx->next_conn.poll = conn->poll;
300
301 /* replace connection with our wrappers, where appropriate */
302 conn->sockdata = zctx;
303 conn->open = zstrm_open;
304 conn->read = zstrm_read;
305 conn->write = zstrm_write;
306 conn->close = zstrm_close;
307 conn->poll = zstrm_poll;
308
309 /* allocate/setup (de)compression buffers */
310 zctx->read.len = 8192;
311 zctx->read.buf = mutt_mem_malloc(zctx->read.len);
312 zctx->read.pos = 0;
313 zctx->write.len = 8192;
314 zctx->write.buf = mutt_mem_malloc(zctx->write.len);
315 zctx->write.pos = 0;
316
317 /* initialise zlib for inflate and deflate for RFC4978 */
318 zctx->read.z.zalloc = zstrm_malloc;
319 zctx->read.z.zfree = zstrm_free;
320 zctx->read.z.opaque = NULL;
321 zctx->read.z.avail_out = zctx->read.len;
322 (void) inflateInit2(&zctx->read.z, -15);
323 zctx->write.z.zalloc = zstrm_malloc;
324 zctx->write.z.zfree = zstrm_free;
325 zctx->write.z.opaque = NULL;
326 zctx->write.z.avail_out = zctx->write.len;
327 (void) deflateInit2(&zctx->write.z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8,
328 Z_DEFAULT_STRATEGY);
329}
static int zstrm_close(struct Connection *conn)
Close a socket - Implements Connection::close() -.
Definition: zstrm.c:97
static int zstrm_open(struct Connection *conn)
Open a socket - Implements Connection::open() -.
Definition: zstrm.c:89
static int zstrm_poll(struct Connection *conn, time_t wait_secs)
Checks whether reads would block - Implements Connection::poll() -.
Definition: zstrm.c:211
static int zstrm_read(struct Connection *conn, char *buf, size_t len)
Read compressed data from a socket - Implements Connection::read() -.
Definition: zstrm.c:129
static int zstrm_write(struct Connection *conn, const char *buf, size_t count)
Write compressed data to a socket - Implements Connection::write() -.
Definition: zstrm.c:228
#define mutt_debug(LEVEL,...)
Definition: logging2.h:84
@ LL_DEBUG5
Log at debug level 5.
Definition: logging2.h:44
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
#define FREE(x)
Definition: memory.h:43
Convenience wrapper for the library headers.
Key value store.
void * sockdata
Backend-specific socket data.
Definition: connection.h:56
int(* poll)(struct Connection *conn, time_t wait_secs)
Definition: connection.h:106
int(* write)(struct Connection *conn, const char *buf, size_t count)
Definition: connection.h:93
int(* close)(struct Connection *conn)
Definition: connection.h:117
int(* open)(struct Connection *conn)
Definition: connection.h:67
int fd
Socket file descriptor.
Definition: connection.h:54
int(* read)(struct Connection *conn, char *buf, size_t count)
Definition: connection.h:80
Data compression layer.
Definition: zstrm.c:55
struct ZstrmDirection read
Data being read and de-compressed.
Definition: zstrm.c:56
struct ZstrmDirection write
Data being compressed and written.
Definition: zstrm.c:57
struct Connection next_conn
Underlying stream.
Definition: zstrm.c:58
A stream of data being (de-)compressed.
Definition: zstrm.c:42
unsigned int pos
Current position.
Definition: zstrm.c:46
bool conn_eof
Connection end-of-file reached.
Definition: zstrm.c:47
unsigned int len
Length of data.
Definition: zstrm.c:45
z_stream z
zlib compression handle
Definition: zstrm.c:43
char * buf
Buffer for data being (de-)compressed.
Definition: zstrm.c:44
bool stream_eof
Stream end-of-file reached.
Definition: zstrm.c:48
static void * zstrm_malloc(void *opaque, unsigned int items, unsigned int size)
Redirector function for zlib's malloc()
Definition: zstrm.c:68
static void zstrm_free(void *opaque, void *address)
Redirector function for zlib's free()
Definition: zstrm.c:78
void mutt_zstrm_wrap_conn(struct Connection *conn)
Wrap a compression layer around a Connection.
Definition: zstrm.c:288
Zlib compression of network traffic.