summaryrefslogtreecommitdiff
path: root/source/fitz/filter-basic.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/fitz/filter-basic.c')
-rw-r--r--source/fitz/filter-basic.c662
1 files changed, 662 insertions, 0 deletions
diff --git a/source/fitz/filter-basic.c b/source/fitz/filter-basic.c
new file mode 100644
index 00000000..3968d193
--- /dev/null
+++ b/source/fitz/filter-basic.c
@@ -0,0 +1,662 @@
+#include "mupdf/fitz.h"
+
+/* Pretend we have a filter that just copies data forever */
+
+fz_stream *
+fz_open_copy(fz_stream *chain)
+{
+ return fz_keep_stream(chain);
+}
+
+/* Null filter copies a specified amount of data */
+
+struct null_filter
+{
+ fz_stream *chain;
+ int remain;
+ int pos;
+};
+
+static int
+read_null(fz_stream *stm, unsigned char *buf, int len)
+{
+ struct null_filter *state = stm->state;
+ int amount = fz_mini(len, state->remain);
+ int n;
+
+ fz_seek(state->chain, state->pos, 0);
+ n = fz_read(state->chain, buf, amount);
+ state->remain -= n;
+ state->pos += n;
+ return n;
+}
+
+static void
+close_null(fz_context *ctx, void *state_)
+{
+ struct null_filter *state = (struct null_filter *)state_;
+ fz_stream *chain = state->chain;
+ fz_free(ctx, state);
+ fz_close(chain);
+}
+
+fz_stream *
+fz_open_null(fz_stream *chain, int len, int offset)
+{
+ struct null_filter *state;
+ fz_context *ctx = chain->ctx;
+
+ if (len < 0)
+ len = 0;
+ fz_try(ctx)
+ {
+ state = fz_malloc_struct(ctx, struct null_filter);
+ state->chain = chain;
+ state->remain = len;
+ state->pos = offset;
+ }
+ fz_catch(ctx)
+ {
+ fz_close(chain);
+ fz_rethrow(ctx);
+ }
+
+ return fz_new_stream(ctx, state, read_null, close_null);
+}
+
+/* Concat filter concatenates several streams into one */
+
+struct concat_filter
+{
+ int max;
+ int count;
+ int current;
+ int pad; /* 1 if we should add whitespace padding between streams */
+ int ws; /* 1 if we should send a whitespace padding byte next */
+ fz_stream *chain[1];
+};
+
+static int
+read_concat(fz_stream *stm, unsigned char *buf, int len)
+{
+ struct concat_filter *state = (struct concat_filter *)stm->state;
+ int n;
+ int read = 0;
+
+ if (len <= 0)
+ return 0;
+
+ while (state->current != state->count && len > 0)
+ {
+ /* If we need to send a whitespace char, do that */
+ if (state->ws)
+ {
+ *buf++ = 32;
+ read++;
+ len--;
+ state->ws = 0;
+ continue;
+ }
+ /* Otherwise, read as much data as will fit in the buffer */
+ n = fz_read(state->chain[state->current], buf, len);
+ read += n;
+ buf += n;
+ len -= n;
+ /* If we didn't read any, then we must have hit the end of
+ * our buffer space. Move to the next stream, and remember to
+ * pad. */
+ if (n == 0)
+ {
+ fz_close(state->chain[state->current]);
+ state->current++;
+ state->ws = state->pad;
+ }
+ }
+
+ return read;
+}
+
+static void
+close_concat(fz_context *ctx, void *state_)
+{
+ struct concat_filter *state = (struct concat_filter *)state_;
+ int i;
+
+ for (i = state->current; i < state->count; i++)
+ {
+ fz_close(state->chain[i]);
+ }
+ fz_free(ctx, state);
+}
+
+fz_stream *
+fz_open_concat(fz_context *ctx, int len, int pad)
+{
+ struct concat_filter *state;
+
+ state = fz_calloc(ctx, 1, sizeof(struct concat_filter) + (len-1)*sizeof(fz_stream *));
+ state->max = len;
+ state->count = 0;
+ state->current = 0;
+ state->pad = pad;
+ state->ws = 0; /* We never send padding byte at the start */
+
+ return fz_new_stream(ctx, state, read_concat, close_concat);
+}
+
+void
+fz_concat_push(fz_stream *concat, fz_stream *chain)
+{
+ struct concat_filter *state = (struct concat_filter *)concat->state;
+
+ if (state->count == state->max)
+ fz_throw(concat->ctx, FZ_ERROR_GENERIC, "Concat filter size exceeded");
+
+ state->chain[state->count++] = chain;
+}
+
+/* ASCII Hex Decode */
+
+typedef struct fz_ahxd_s fz_ahxd;
+
+struct fz_ahxd_s
+{
+ fz_stream *chain;
+ int eod;
+};
+
+static inline int iswhite(int a)
+{
+ switch (a) {
+ case '\n': case '\r': case '\t': case ' ':
+ case '\0': case '\f': case '\b': case 0177:
+ return 1;
+ }
+ return 0;
+}
+
+static inline int ishex(int a)
+{
+ return (a >= 'A' && a <= 'F') ||
+ (a >= 'a' && a <= 'f') ||
+ (a >= '0' && a <= '9');
+}
+
+static inline int unhex(int a)
+{
+ if (a >= 'A' && a <= 'F') return a - 'A' + 0xA;
+ if (a >= 'a' && a <= 'f') return a - 'a' + 0xA;
+ if (a >= '0' && a <= '9') return a - '0';
+ return 0;
+}
+
+static int
+read_ahxd(fz_stream *stm, unsigned char *buf, int len)
+{
+ fz_ahxd *state = stm->state;
+ unsigned char *p = buf;
+ unsigned char *ep = buf + len;
+ int a, b, c, odd;
+
+ odd = 0;
+
+ while (p < ep)
+ {
+ if (state->eod)
+ return p - buf;
+
+ c = fz_read_byte(state->chain);
+ if (c < 0)
+ return p - buf;
+
+ if (ishex(c))
+ {
+ if (!odd)
+ {
+ a = unhex(c);
+ odd = 1;
+ }
+ else
+ {
+ b = unhex(c);
+ *p++ = (a << 4) | b;
+ odd = 0;
+ }
+ }
+ else if (c == '>')
+ {
+ if (odd)
+ *p++ = (a << 4);
+ state->eod = 1;
+ }
+ else if (!iswhite(c))
+ {
+ fz_throw(stm->ctx, FZ_ERROR_GENERIC, "bad data in ahxd: '%c'", c);
+ }
+ }
+
+ return p - buf;
+}
+
+static void
+close_ahxd(fz_context *ctx, void *state_)
+{
+ fz_ahxd *state = (fz_ahxd *)state_;
+ fz_stream *chain = state->chain;
+ fz_free(ctx, state);
+ fz_close(chain);
+}
+
+fz_stream *
+fz_open_ahxd(fz_stream *chain)
+{
+ fz_ahxd *state;
+ fz_context *ctx = chain->ctx;
+
+ fz_try(ctx)
+ {
+ state = fz_malloc_struct(ctx, fz_ahxd);
+ state->chain = chain;
+ state->eod = 0;
+ }
+ fz_catch(ctx)
+ {
+ fz_close(chain);
+ fz_rethrow(ctx);
+ }
+
+ return fz_new_stream(ctx, state, read_ahxd, close_ahxd);
+}
+
+/* ASCII 85 Decode */
+
+typedef struct fz_a85d_s fz_a85d;
+
+struct fz_a85d_s
+{
+ fz_stream *chain;
+ unsigned char bp[4];
+ unsigned char *rp, *wp;
+ int eod;
+};
+
+static int
+read_a85d(fz_stream *stm, unsigned char *buf, int len)
+{
+ fz_a85d *state = stm->state;
+ unsigned char *p = buf;
+ unsigned char *ep = buf + len;
+ int count = 0;
+ int word = 0;
+ int c;
+
+ while (state->rp < state->wp && p < ep)
+ *p++ = *state->rp++;
+
+ while (p < ep)
+ {
+ if (state->eod)
+ return p - buf;
+
+ c = fz_read_byte(state->chain);
+ if (c < 0)
+ return p - buf;
+
+ if (c >= '!' && c <= 'u')
+ {
+ if (count == 4)
+ {
+ word = word * 85 + (c - '!');
+
+ state->bp[0] = (word >> 24) & 0xff;
+ state->bp[1] = (word >> 16) & 0xff;
+ state->bp[2] = (word >> 8) & 0xff;
+ state->bp[3] = (word) & 0xff;
+ state->rp = state->bp;
+ state->wp = state->bp + 4;
+
+ word = 0;
+ count = 0;
+ }
+ else
+ {
+ word = word * 85 + (c - '!');
+ count ++;
+ }
+ }
+
+ else if (c == 'z' && count == 0)
+ {
+ state->bp[0] = 0;
+ state->bp[1] = 0;
+ state->bp[2] = 0;
+ state->bp[3] = 0;
+ state->rp = state->bp;
+ state->wp = state->bp + 4;
+ }
+
+ else if (c == '~')
+ {
+ c = fz_read_byte(state->chain);
+ if (c != '>')
+ fz_warn(stm->ctx, "bad eod marker in a85d");
+
+ switch (count) {
+ case 0:
+ break;
+ case 1:
+ /* Specifically illegal in the spec, but adobe
+ * and gs both cope. See normal_87.pdf for a
+ * case where this matters. */
+ fz_warn(stm->ctx, "partial final byte in a85d");
+ break;
+ case 2:
+ word = word * (85 * 85 * 85) + 0xffffff;
+ state->bp[0] = word >> 24;
+ state->rp = state->bp;
+ state->wp = state->bp + 1;
+ break;
+ case 3:
+ word = word * (85 * 85) + 0xffff;
+ state->bp[0] = word >> 24;
+ state->bp[1] = word >> 16;
+ state->rp = state->bp;
+ state->wp = state->bp + 2;
+ break;
+ case 4:
+ word = word * 85 + 0xff;
+ state->bp[0] = word >> 24;
+ state->bp[1] = word >> 16;
+ state->bp[2] = word >> 8;
+ state->rp = state->bp;
+ state->wp = state->bp + 3;
+ break;
+ }
+ state->eod = 1;
+ }
+
+ else if (!iswhite(c))
+ {
+ fz_throw(stm->ctx, FZ_ERROR_GENERIC, "bad data in a85d: '%c'", c);
+ }
+
+ while (state->rp < state->wp && p < ep)
+ *p++ = *state->rp++;
+ }
+
+ return p - buf;
+}
+
+static void
+close_a85d(fz_context *ctx, void *state_)
+{
+ fz_a85d *state = (fz_a85d *)state_;
+ fz_stream *chain = state->chain;
+
+ fz_free(ctx, state);
+ fz_close(chain);
+}
+
+fz_stream *
+fz_open_a85d(fz_stream *chain)
+{
+ fz_a85d *state;
+ fz_context *ctx = chain->ctx;
+
+ fz_try(ctx)
+ {
+ state = fz_malloc_struct(ctx, fz_a85d);
+ state->chain = chain;
+ state->rp = state->bp;
+ state->wp = state->bp;
+ state->eod = 0;
+ }
+ fz_catch(ctx)
+ {
+ fz_close(chain);
+ fz_rethrow(ctx);
+ }
+
+ return fz_new_stream(ctx, state, read_a85d, close_a85d);
+}
+
+/* Run Length Decode */
+
+typedef struct fz_rld_s fz_rld;
+
+struct fz_rld_s
+{
+ fz_stream *chain;
+ int run, n, c;
+};
+
+static int
+read_rld(fz_stream *stm, unsigned char *buf, int len)
+{
+ fz_rld *state = stm->state;
+ unsigned char *p = buf;
+ unsigned char *ep = buf + len;
+
+ while (p < ep)
+ {
+ if (state->run == 128)
+ return p - buf;
+
+ if (state->n == 0)
+ {
+ state->run = fz_read_byte(state->chain);
+ if (state->run < 0)
+ state->run = 128;
+ if (state->run < 128)
+ state->n = state->run + 1;
+ if (state->run > 128)
+ {
+ state->n = 257 - state->run;
+ state->c = fz_read_byte(state->chain);
+ if (state->c < 0)
+ fz_throw(stm->ctx, FZ_ERROR_GENERIC, "premature end of data in run length decode");
+ }
+ }
+
+ if (state->run < 128)
+ {
+ while (p < ep && state->n)
+ {
+ int c = fz_read_byte(state->chain);
+ if (c < 0)
+ fz_throw(stm->ctx, FZ_ERROR_GENERIC, "premature end of data in run length decode");
+ *p++ = c;
+ state->n--;
+ }
+ }
+
+ if (state->run > 128)
+ {
+ while (p < ep && state->n)
+ {
+ *p++ = state->c;
+ state->n--;
+ }
+ }
+ }
+
+ return p - buf;
+}
+
+static void
+close_rld(fz_context *ctx, void *state_)
+{
+ fz_rld *state = (fz_rld *)state_;
+ fz_stream *chain = state->chain;
+
+ fz_free(ctx, state);
+ fz_close(chain);
+}
+
+fz_stream *
+fz_open_rld(fz_stream *chain)
+{
+ fz_rld *state;
+ fz_context *ctx = chain->ctx;
+
+ fz_try(ctx)
+ {
+ state = fz_malloc_struct(ctx, fz_rld);
+ state->chain = chain;
+ state->run = 0;
+ state->n = 0;
+ state->c = 0;
+ }
+ fz_catch(ctx)
+ {
+ fz_close(chain);
+ fz_rethrow(ctx);
+ }
+
+ return fz_new_stream(ctx, state, read_rld, close_rld);
+}
+
+/* RC4 Filter */
+
+typedef struct fz_arc4c_s fz_arc4c;
+
+struct fz_arc4c_s
+{
+ fz_stream *chain;
+ fz_arc4 arc4;
+};
+
+static int
+read_arc4(fz_stream *stm, unsigned char *buf, int len)
+{
+ fz_arc4c *state = stm->state;
+ int n = fz_read(state->chain, buf, len);
+ fz_arc4_encrypt(&state->arc4, buf, buf, n);
+ return n;
+}
+
+static void
+close_arc4(fz_context *ctx, void *state_)
+{
+ fz_arc4c *state = (fz_arc4c *)state_;
+ fz_stream *chain = state->chain;
+
+ fz_free(ctx, state);
+ fz_close(chain);
+}
+
+fz_stream *
+fz_open_arc4(fz_stream *chain, unsigned char *key, unsigned keylen)
+{
+ fz_arc4c *state;
+ fz_context *ctx = chain->ctx;
+
+ fz_try(ctx)
+ {
+ state = fz_malloc_struct(ctx, fz_arc4c);
+ state->chain = chain;
+ fz_arc4_init(&state->arc4, key, keylen);
+ }
+ fz_catch(ctx)
+ {
+ fz_close(chain);
+ fz_rethrow(ctx);
+ }
+
+ return fz_new_stream(ctx, state, read_arc4, close_arc4);
+}
+
+/* AES Filter */
+
+typedef struct fz_aesd_s fz_aesd;
+
+struct fz_aesd_s
+{
+ fz_stream *chain;
+ fz_aes aes;
+ unsigned char iv[16];
+ int ivcount;
+ unsigned char bp[16];
+ unsigned char *rp, *wp;
+};
+
+static int
+read_aesd(fz_stream *stm, unsigned char *buf, int len)
+{
+ fz_aesd *state = stm->state;
+ unsigned char *p = buf;
+ unsigned char *ep = buf + len;
+
+ while (state->ivcount < 16)
+ {
+ int c = fz_read_byte(state->chain);
+ if (c < 0)
+ fz_throw(stm->ctx, FZ_ERROR_GENERIC, "premature end in aes filter");
+ state->iv[state->ivcount++] = c;
+ }
+
+ while (state->rp < state->wp && p < ep)
+ *p++ = *state->rp++;
+
+ while (p < ep)
+ {
+ int n = fz_read(state->chain, state->bp, 16);
+ if (n == 0)
+ return p - buf;
+ else if (n < 16)
+ fz_throw(stm->ctx, FZ_ERROR_GENERIC, "partial block in aes filter");
+
+ aes_crypt_cbc(&state->aes, AES_DECRYPT, 16, state->iv, state->bp, state->bp);
+ state->rp = state->bp;
+ state->wp = state->bp + 16;
+
+ /* strip padding at end of file */
+ if (fz_is_eof(state->chain))
+ {
+ int pad = state->bp[15];
+ if (pad < 1 || pad > 16)
+ fz_throw(stm->ctx, FZ_ERROR_GENERIC, "aes padding out of range: %d", pad);
+ state->wp -= pad;
+ }
+
+ while (state->rp < state->wp && p < ep)
+ *p++ = *state->rp++;
+ }
+
+ return p - buf;
+}
+
+static void
+close_aesd(fz_context *ctx, void *state_)
+{
+ fz_aesd *state = (fz_aesd *)state_;
+ fz_stream *chain = state->chain;
+
+ fz_free(ctx, state);
+ fz_close(chain);
+}
+
+fz_stream *
+fz_open_aesd(fz_stream *chain, unsigned char *key, unsigned keylen)
+{
+ fz_aesd *state;
+ fz_context *ctx = chain->ctx;
+
+ fz_try(ctx)
+ {
+ state = fz_malloc_struct(ctx, fz_aesd);
+ state->chain = chain;
+ if (aes_setkey_dec(&state->aes, key, keylen * 8))
+ fz_throw(ctx, FZ_ERROR_GENERIC, "AES key init failed (keylen=%d)", keylen * 8);
+ state->ivcount = 0;
+ state->rp = state->bp;
+ state->wp = state->bp;
+ }
+ fz_catch(ctx)
+ {
+ fz_close(chain);
+ fz_rethrow(ctx);
+ }
+
+ return fz_new_stream(ctx, state, read_aesd, close_aesd);
+}