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