NeoMutt  2024-03-23-147-g885fbc
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
zstrm.c
Go to the documentation of this file.
1
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
58{
62};
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 mutt_debug(LL_DEBUG5, "read %lu->%lu (%.1fx) wrote %lu<-%lu (%.1fx)\n",
107 zctx->read.z.total_in, zctx->read.z.total_out,
108 (float) zctx->read.z.total_out / (float) zctx->read.z.total_in,
109 zctx->write.z.total_in, zctx->write.z.total_out,
110 (float) zctx->write.z.total_in / (float) zctx->write.z.total_out);
111
112 // Restore the Connection's original functions
113 conn->sockdata = zctx->next_conn.sockdata;
114 conn->open = zctx->next_conn.open;
115 conn->close = zctx->next_conn.close;
116 conn->read = zctx->next_conn.read;
117 conn->write = zctx->next_conn.write;
118 conn->poll = zctx->next_conn.poll;
119
120 inflateEnd(&zctx->read.z);
121 deflateEnd(&zctx->write.z);
122 FREE(&zctx->read.buf);
123 FREE(&zctx->write.buf);
124 FREE(&zctx);
125
126 return rc;
127}
128
132static int zstrm_read(struct Connection *conn, char *buf, size_t len)
133{
134 struct ZstrmContext *zctx = conn->sockdata;
135 int rc = 0;
136 int zrc = 0;
137
138retry:
139 if (zctx->read.stream_eof)
140 return 0;
141
142 /* when avail_out was 0 on last call, we need to call inflate again, because
143 * more data might be available using the current input, so avoid calling
144 * read on the underlying stream in that case (for it might block) */
145 if ((zctx->read.pos == 0) && !zctx->read.conn_eof)
146 {
147 rc = zctx->next_conn.read(&zctx->next_conn, zctx->read.buf, zctx->read.len);
148 mutt_debug(LL_DEBUG5, "consuming data from next stream: %d bytes\n", rc);
149 if (rc < 0)
150 return rc;
151 else if (rc == 0)
152 zctx->read.conn_eof = true;
153 else
154 zctx->read.pos += rc;
155 }
156
157 zctx->read.z.avail_in = (uInt) zctx->read.pos;
158 zctx->read.z.next_in = (Bytef *) zctx->read.buf;
159 zctx->read.z.avail_out = (uInt) len;
160 zctx->read.z.next_out = (Bytef *) buf;
161
162 zrc = inflate(&zctx->read.z, Z_SYNC_FLUSH);
163 mutt_debug(LL_DEBUG5, "rc=%d, consumed %u/%u bytes, produced %zu/%zu bytes\n",
164 zrc, zctx->read.pos - zctx->read.z.avail_in, zctx->read.pos,
165 len - zctx->read.z.avail_out, len);
166
167 /* shift any remaining input data to the front of the buffer */
168 if ((Bytef *) zctx->read.buf != zctx->read.z.next_in)
169 {
170 memmove(zctx->read.buf, zctx->read.z.next_in, zctx->read.z.avail_in);
171 zctx->read.pos = zctx->read.z.avail_in;
172 }
173
174 switch (zrc)
175 {
176 case Z_OK: /* progress has been made */
177 zrc = len - zctx->read.z.avail_out; /* "returned" bytes */
178 if (zrc == 0)
179 {
180 /* there was progress, so must have been reading input */
181 mutt_debug(LL_DEBUG5, "inflate just consumed\n");
182 goto retry;
183 }
184 break;
185
186 case Z_STREAM_END: /* everything flushed, nothing remaining */
187 mutt_debug(LL_DEBUG5, "inflate returned Z_STREAM_END\n");
188 zrc = len - zctx->read.z.avail_out; /* "returned" bytes */
189 zctx->read.stream_eof = true;
190 break;
191
192 case Z_BUF_ERROR: /* no progress was possible */
193 if (!zctx->read.conn_eof)
194 {
195 mutt_debug(LL_DEBUG5, "inflate returned Z_BUF_ERROR. retrying\n");
196 goto retry;
197 }
198 zrc = 0;
199 break;
200
201 default:
202 /* bail on other rcs, such as Z_DATA_ERROR, or Z_MEM_ERROR */
203 mutt_debug(LL_DEBUG5, "inflate returned %d. aborting\n", zrc);
204 zrc = -1;
205 break;
206 }
207
208 return zrc;
209}
210
214static int zstrm_poll(struct Connection *conn, time_t wait_secs)
215{
216 struct ZstrmContext *zctx = conn->sockdata;
217
218 mutt_debug(LL_DEBUG5, "%s\n",
219 (zctx->read.z.avail_out == 0) || (zctx->read.pos > 0) ?
220 "last read wrote full buffer" :
221 "falling back on next stream");
222 if ((zctx->read.z.avail_out == 0) || (zctx->read.pos > 0))
223 return 1;
224
225 return zctx->next_conn.poll(&zctx->next_conn, wait_secs);
226}
227
231static int zstrm_write(struct Connection *conn, const char *buf, size_t count)
232{
233 struct ZstrmContext *zctx = conn->sockdata;
234 int rc;
235
236 zctx->write.z.avail_in = (uInt) count;
237 zctx->write.z.next_in = (Bytef *) buf;
238 zctx->write.z.avail_out = (uInt) zctx->write.len;
239 zctx->write.z.next_out = (Bytef *) zctx->write.buf;
240
241 do
242 {
243 int zrc = deflate(&zctx->write.z, Z_PARTIAL_FLUSH);
244 if (zrc == Z_OK)
245 {
246 /* push out produced data to the underlying stream */
247 zctx->write.pos = zctx->write.len - zctx->write.z.avail_out;
248 char *wbufp = zctx->write.buf;
249 mutt_debug(LL_DEBUG5, "deflate consumed %zu/%zu bytes\n",
250 count - zctx->write.z.avail_in, count);
251 while (zctx->write.pos > 0)
252 {
253 rc = zctx->next_conn.write(&zctx->next_conn, wbufp, zctx->write.pos);
254 mutt_debug(LL_DEBUG5, "next stream wrote: %d bytes\n", rc);
255 if (rc < 0)
256 return -1; /* we can't recover from write failure */
257
258 wbufp += rc;
259 zctx->write.pos -= rc;
260 }
261
262 /* see if there's more for us to do, retry if the output buffer
263 * was full (there may be something in zlib buffers), and retry
264 * when there is still available input data */
265 if ((zctx->write.z.avail_out != 0) && (zctx->write.z.avail_in == 0))
266 break;
267
268 zctx->write.z.avail_out = (uInt) zctx->write.len;
269 zctx->write.z.next_out = (Bytef *) zctx->write.buf;
270 }
271 else
272 {
273 /* compression went wrong, but this is basically impossible
274 * according to the docs */
275 return -1;
276 }
277 } while (true);
278
279 rc = (int) count;
280 return (rc <= 0) ? 1 : rc; /* avoid wrong behaviour due to overflow */
281}
282
292{
293 struct ZstrmContext *zctx = mutt_mem_calloc(1, sizeof(struct ZstrmContext));
294
295 /* store wrapped stream as next stream */
296 zctx->next_conn.fd = conn->fd;
297 zctx->next_conn.sockdata = conn->sockdata;
298 zctx->next_conn.open = conn->open;
299 zctx->next_conn.close = conn->close;
300 zctx->next_conn.read = conn->read;
301 zctx->next_conn.write = conn->write;
302 zctx->next_conn.poll = conn->poll;
303
304 /* replace connection with our wrappers, where appropriate */
305 conn->sockdata = zctx;
306 conn->open = zstrm_open;
307 conn->read = zstrm_read;
308 conn->write = zstrm_write;
309 conn->close = zstrm_close;
310 conn->poll = zstrm_poll;
311
312 /* allocate/setup (de)compression buffers */
313 zctx->read.len = 8192;
314 zctx->read.buf = mutt_mem_malloc(zctx->read.len);
315 zctx->read.pos = 0;
316 zctx->write.len = 8192;
317 zctx->write.buf = mutt_mem_malloc(zctx->write.len);
318 zctx->write.pos = 0;
319
320 /* initialise zlib for inflate and deflate for RFC4978 */
321 zctx->read.z.zalloc = zstrm_malloc;
322 zctx->read.z.zfree = zstrm_free;
323 zctx->read.z.opaque = NULL;
324 zctx->read.z.avail_out = zctx->read.len;
325 (void) inflateInit2(&zctx->read.z, -15);
326 zctx->write.z.zalloc = zstrm_malloc;
327 zctx->write.z.zfree = zstrm_free;
328 zctx->write.z.opaque = NULL;
329 zctx->write.z.avail_out = zctx->write.len;
330 (void) deflateInit2(&zctx->write.z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8,
331 Z_DEFAULT_STRATEGY);
332}
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:214
static int zstrm_read(struct Connection *conn, char *buf, size_t len)
Read compressed data from a socket - Implements Connection::read() -.
Definition: zstrm.c:132
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:231
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
@ LL_DEBUG5
Log at debug level 5.
Definition: logging2.h:47
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:45
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:291
Zlib compression of network traffic.