libetpan - fdik
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

316 lines
9.9 KiB

  1. /*
  2. * libEtPan! -- a mail stuff library
  3. *
  4. * Copyright (C) 2001, 2013 - DINH Viet Hoa
  5. * All rights reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. * 1. Redistributions of source code must retain the above copyright
  11. * notice, this list of conditions and the following disclaimer.
  12. * 2. Redistributions in binary form must reproduce the above copyright
  13. * notice, this list of conditions and the following disclaimer in the
  14. * documentation and/or other materials provided with the distribution.
  15. * 3. Neither the name of the libEtPan! project nor the names of its
  16. * contributors may be used to endorse or promote products derived
  17. * from this software without specific prior written permission.
  18. *
  19. * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
  20. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  21. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  22. * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
  23. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  24. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  25. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  26. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  27. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  28. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  29. * SUCH DAMAGE.
  30. */
  31. /* Created by Ian Ragsdale on 3/8/13. */
  32. #include "mailstream_compress.h"
  33. #if !HAVE_ZLIB
  34. mailstream_low * mailstream_low_compress_open(mailstream_low * ms)
  35. {
  36. return NULL;
  37. }
  38. #else
  39. #include <stdio.h>
  40. #include <stdlib.h>
  41. #include <zlib.h>
  42. #include <assert.h>
  43. #include "mailstream_low.h"
  44. #include "mailstream_low.h"
  45. #include "mailstream_cancel.h"
  46. #define CHUNK_SIZE 1024
  47. static ssize_t mailstream_low_compress_read(mailstream_low * s, void * buf, size_t count);
  48. static ssize_t mailstream_low_compress_write(mailstream_low * s, const void * buf, size_t count);
  49. static int mailstream_low_compress_close(mailstream_low * s);
  50. static int mailstream_low_compress_get_fd(mailstream_low * s);
  51. static struct mailstream_cancel * mailstream_low_compress_get_cancel(mailstream_low * s);
  52. static void mailstream_low_compress_free(mailstream_low * s);
  53. static void mailstream_low_compress_cancel(mailstream_low * s);
  54. typedef struct mailstream_compress_data
  55. {
  56. mailstream_low * ms;
  57. z_stream *compress_stream;
  58. z_stream *decompress_stream;
  59. unsigned char input_buf[CHUNK_SIZE];
  60. unsigned char output_buf[CHUNK_SIZE];
  61. } compress_data;
  62. static mailstream_low_driver local_mailstream_compress_driver = {
  63. /* mailstream_read */ mailstream_low_compress_read,
  64. /* mailstream_write */ mailstream_low_compress_write,
  65. /* mailstream_close */ mailstream_low_compress_close,
  66. /* mailstream_get_fd */ mailstream_low_compress_get_fd,
  67. /* mailstream_free */ mailstream_low_compress_free,
  68. /* mailstream_cancel */ mailstream_low_compress_cancel,
  69. /* mailstream_get_cancel */ mailstream_low_compress_get_cancel,
  70. };
  71. mailstream_low_driver * mailstream_compress_driver = &local_mailstream_compress_driver;
  72. mailstream_low * mailstream_low_compress_open(mailstream_low * ms)
  73. {
  74. mailstream_low * s;
  75. /* stores the original mailstream */
  76. struct mailstream_compress_data * compress_data = malloc(sizeof(* compress_data));
  77. if (compress_data == NULL)
  78. goto err;
  79. /* allocate deflate state */
  80. compress_data->compress_stream = malloc(sizeof(z_stream));
  81. compress_data->compress_stream->zalloc = Z_NULL;
  82. compress_data->compress_stream->zfree = Z_NULL;
  83. compress_data->compress_stream->opaque = Z_NULL;
  84. /* these specific settings are very important - don't change without looking at the COMPRESS RFC */
  85. int ret = deflateInit2(compress_data->compress_stream, Z_BEST_SPEED, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
  86. if (ret != Z_OK) {
  87. goto free_compress_data;
  88. }
  89. compress_data->compress_stream->avail_in = 0;
  90. compress_data->compress_stream->avail_out = 0;
  91. /* allocate inflate state */
  92. compress_data->decompress_stream = malloc(sizeof(z_stream));
  93. compress_data->decompress_stream->zalloc = Z_NULL;
  94. compress_data->decompress_stream->zfree = Z_NULL;
  95. compress_data->decompress_stream->opaque = Z_NULL;
  96. /* these specific settings are very important - don't change without looking at the COMPRESS RFC */
  97. ret = inflateInit2(compress_data->decompress_stream, -15);
  98. if (ret != Z_OK) {
  99. goto free_compress_data;
  100. }
  101. compress_data->decompress_stream->avail_in = 0;
  102. compress_data->decompress_stream->avail_out = 0;
  103. compress_data->ms = ms;
  104. s = mailstream_low_new(compress_data, mailstream_compress_driver);
  105. if (s == NULL)
  106. goto free_compress_data;
  107. return s;
  108. free_compress_data:
  109. if (compress_data->compress_stream) {
  110. deflateEnd(compress_data->compress_stream);
  111. free(compress_data->compress_stream);
  112. }
  113. if (compress_data->decompress_stream) {
  114. inflateEnd(compress_data->decompress_stream);
  115. free(compress_data->decompress_stream);
  116. }
  117. free(compress_data);
  118. err:
  119. return NULL;
  120. }
  121. ssize_t mailstream_low_compress_read(mailstream_low * s, void * buf, size_t count) {
  122. compress_data *data = s->data;
  123. data->ms->timeout = s->timeout;
  124. z_stream *strm = data->decompress_stream;
  125. int zr;
  126. do {
  127. /* if there is no compressed data, read more */
  128. if (strm->avail_in == 0) {
  129. int read = data->ms->driver->mailstream_read(data->ms, data->input_buf, CHUNK_SIZE);
  130. if (read <= 0) {
  131. return read;
  132. }
  133. strm->avail_in = read;
  134. strm->next_in = data->input_buf;
  135. }
  136. /* set the output buffer */
  137. strm->next_out = buf;
  138. strm->avail_out = count;
  139. /* uncompress any waiting data */
  140. zr = inflate(strm, Z_NO_FLUSH);
  141. }
  142. /*
  143. it's possible that there was data in the stream, but not enough that zlib could figure
  144. out what to do with it. in this case, read some more and try again.
  145. */
  146. while (zr == Z_OK && strm->avail_in == 0 && strm->avail_out == count);
  147. /* if we got an error, return -1 to close the connection */
  148. if (zr < 0) {
  149. return -1;
  150. }
  151. /* let the client know how much data was read */
  152. return count - strm->avail_out;
  153. }
  154. /*
  155. mostly copied from mailstream_ssl.c
  156. removed their windows support - we only need iOS
  157. */
  158. static int wait_write_compress(mailstream_low * s)
  159. {
  160. fd_set fds_read;
  161. fd_set fds_write;
  162. struct timeval timeout;
  163. int r;
  164. int fd;
  165. int max_fd;
  166. int cancelled;
  167. int write_enabled;
  168. // use the session timeout if set
  169. if (s->timeout) {
  170. timeout.tv_sec = s->timeout;
  171. timeout.tv_usec = 0;
  172. } else {
  173. timeout = mailstream_network_delay;
  174. }
  175. FD_ZERO(&fds_read);
  176. struct mailstream_cancel * cancel = mailstream_low_compress_get_cancel(s);
  177. fd = mailstream_cancel_get_fd(cancel);
  178. FD_SET(fd, &fds_read);
  179. FD_ZERO(&fds_write);
  180. FD_SET(mailstream_low_compress_get_fd(s), &fds_write);
  181. max_fd = mailstream_low_compress_get_fd(s);
  182. if (fd > max_fd)
  183. max_fd = fd;
  184. r = select(max_fd + 1, &fds_read, &fds_write, NULL, &timeout);
  185. if (r <= 0)
  186. return -1;
  187. cancelled = FD_ISSET(fd, &fds_read);
  188. write_enabled = FD_ISSET(mailstream_low_compress_get_fd(s), &fds_write);
  189. if (cancelled) {
  190. /* cancelled */
  191. mailstream_cancel_ack(cancel);
  192. return -1;
  193. }
  194. if (!write_enabled)
  195. return 0;
  196. return 1;
  197. }
  198. ssize_t mailstream_low_compress_write(mailstream_low * s,
  199. const void * buf, size_t count) {
  200. int zr, wr;
  201. compress_data *data = s->data;
  202. data->ms->timeout = s->timeout;
  203. z_stream *strm = data->compress_stream;
  204. strm->next_in = (Bytef *)buf;
  205. /* we won't try to compress more than CHUNK_SIZE at a time so we always have enough buffer space */
  206. int compress_len = MIN(count, CHUNK_SIZE);
  207. strm->avail_in = compress_len;
  208. strm->avail_out = CHUNK_SIZE;
  209. strm->next_out = data->output_buf;
  210. zr = deflate(strm, Z_PARTIAL_FLUSH);
  211. assert(zr == Z_OK);
  212. if (zr < 0) {
  213. printf("Error deflating: %d %s", zr, strm->msg);
  214. return -1;
  215. }
  216. /*
  217. wait until we can write to the buffer - we want to avoid any situation where we can have a partial write,
  218. because with a partial write we can't tell the caller how much uncompressed data was written
  219. */
  220. wait_write_compress(s);
  221. /* write the data to the underlying mailstream */
  222. wr = data->ms->driver->mailstream_write(data->ms, data->output_buf, CHUNK_SIZE - strm->avail_out);
  223. /*
  224. if we were unable to write all the compressed data to the underlying stream, we're in a bit of trouble
  225. we don't know how much UNcompressed data was written, so we can't let the client know how much to retry
  226. so, we return -1 and hope that the wait_write call ensures this never happens
  227. */
  228. int len = CHUNK_SIZE-strm->avail_out;
  229. if (wr < len) {
  230. return -1;
  231. assert(0);
  232. }
  233. /* let the caller know how much data we wrote */
  234. return compress_len - strm->avail_in;
  235. }
  236. int mailstream_low_compress_close(mailstream_low * s) {
  237. compress_data *data = s->data;
  238. return data->ms->driver->mailstream_close(data->ms);
  239. }
  240. int mailstream_low_compress_get_fd(mailstream_low * s) {
  241. compress_data *data = s->data;
  242. return data->ms->driver->mailstream_get_fd(data->ms);
  243. }
  244. struct mailstream_cancel * mailstream_low_compress_get_cancel(mailstream_low * s) {
  245. compress_data *data = s->data;
  246. return data->ms->driver->mailstream_get_cancel(data->ms);
  247. }
  248. void mailstream_low_compress_free(mailstream_low * s) {
  249. compress_data *data = s->data;
  250. data->ms->driver->mailstream_free(data->ms);
  251. if (data->compress_stream) {
  252. deflateEnd(data->compress_stream);
  253. free(data->compress_stream);
  254. }
  255. if (data->decompress_stream) {
  256. inflateEnd(data->decompress_stream);
  257. free(data->decompress_stream);
  258. }
  259. free(data);
  260. free(s);
  261. }
  262. void mailstream_low_compress_cancel(mailstream_low * s) {
  263. compress_data *data = s->data;
  264. data->ms->driver->mailstream_cancel(data->ms);
  265. }
  266. #endif