NeoMutt  2021-02-05-666-ge300cd
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 {
56  struct ZstrmDirection read;
57  struct ZstrmDirection write;
58  struct Connection next_conn;
59 };
60 
68 static void *zstrm_malloc(void *opaque, unsigned int items, unsigned int size)
69 {
70  return mutt_mem_calloc(items, size);
71 }
72 
78 static void zstrm_free(void *opaque, void *address)
79 {
80  FREE(&address);
81 }
82 
89 static int zstrm_open(struct Connection *conn)
90 {
91  return -1;
92 }
93 
97 static 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 %llu->%llu (%.1fx) wrote %llu<-%llu (%.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 
129 static 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 
135 retry:
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 %u/%u 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 
211 static 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 
228 static 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 %d/%d 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  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  deflateInit2(&zctx->write.z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
328 }
void mutt_zstrm_wrap_conn(struct Connection *conn)
Wrap a compression layer around a Connection.
Definition: zstrm.c:288
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
Data compression layer.
Definition: zstrm.c:54
struct ZstrmDirection write
Data being compressed and written.
Definition: zstrm.c:57
int(* read)(struct Connection *conn, char *buf, size_t count)
Definition: connection.h:82
A stream of data being (de-)compressed.
Definition: zstrm.c:41
static void zstrm_free(void *opaque, void *address)
Redirector function for zlib&#39;s free()
Definition: zstrm.c:78
void * sockdata
Backend-specific socket data.
Definition: connection.h:46
bool conn_eof
Connection end-of-file reached.
Definition: zstrm.c:47
static int zstrm_close(struct Connection *conn)
Close a socket - Implements Connection::close() -.
Definition: zstrm.c:97
static int zstrm_poll(struct Connection *conn, time_t wait_secs)
Checks whether reads would block - Implements Connection::poll() -.
Definition: zstrm.c:211
struct Connection next_conn
Underlying stream.
Definition: zstrm.c:58
int(* open)(struct Connection *conn)
Definition: connection.h:69
struct ZstrmDirection read
Data being read and de-compressed.
Definition: zstrm.c:56
static void * zstrm_malloc(void *opaque, unsigned int items, unsigned int size)
Redirector function for zlib&#39;s malloc()
Definition: zstrm.c:68
int(* close)(struct Connection *conn)
Definition: connection.h:119
bool stream_eof
Stream end-of-file reached.
Definition: zstrm.c:48
int fd
Socket file descriptor.
Definition: connection.h:44
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
unsigned int len
Length of data.
Definition: zstrm.c:45
unsigned int pos
Current position.
Definition: zstrm.c:46
char * buf
Buffer for data being (de-)compressed.
Definition: zstrm.c:44
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
static int zstrm_open(struct Connection *conn)
Open a socket - Implements Connection::open() -.
Definition: zstrm.c:89
int(* poll)(struct Connection *conn, time_t wait_secs)
Definition: connection.h:108
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
int(* write)(struct Connection *conn, const char *buf, size_t count)
Definition: connection.h:95
Connection Library.
#define FREE(x)
Definition: memory.h:40
z_stream z
zlib compression handle
Definition: zstrm.c:43
Log at debug level 5.
Definition: logging.h:44
Convenience wrapper for the library headers.
Zlib compression of network traffic.
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