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