/* Inflate response buffer 'buf' of length 'len'. */ static int do_inflate(ne_decompress *ctx, const char *buf, size_t len) { NE_DEBUG_WINSCP_CONTEXT(ctx->session); int ret; ctx->zstr.avail_in = (uint32_t)len; ctx->zstr.next_in = (unsigned char *)buf; ctx->zstr.total_in = 0; do { ctx->zstr.avail_out = sizeof ctx->outbuf; ctx->zstr.next_out = (unsigned char *)ctx->outbuf; ctx->zstr.total_out = 0; ret = inflate(&ctx->zstr, Z_NO_FLUSH); NE_DEBUG(NE_DBG_HTTP, "compress: inflate %d, %ld bytes out, %d remaining\n", ret, ctx->zstr.total_out, ctx->zstr.avail_in); #if 0 NE_DEBUG(NE_DBG_HTTPBODY, "Inflated body block (%ld):\n[%.*s]\n", ctx->zstr.total_out, (int)ctx->zstr.total_out, ctx->outbuf); #endif /* update checksum. */ ctx->checksum = crc32(ctx->checksum, (unsigned char *)ctx->outbuf, ctx->zstr.total_out); /* pass on the inflated data, if any */ if (ctx->zstr.total_out > 0) { int rret = ctx->reader(ctx->userdata, ctx->outbuf, ctx->zstr.total_out); if (rret) return rret; } } while (ret == Z_OK && ctx->zstr.avail_in > 0); if (ret == Z_STREAM_END) { NE_DEBUG(NE_DBG_HTTP, "compress: end of data stream, %d bytes remain.\n", ctx->zstr.avail_in); /* process the footer. */ ctx->state = NE_Z_AFTER_DATA; return process_footer(ctx, ctx->zstr.next_in, ctx->zstr.avail_in); } else if (ret != Z_OK) { set_zlib_error(ctx, _("Could not inflate data"), ret); return NE_ERROR; } return 0; }
/* Callback which is passed blocks of the response body. */ static int gz_reader(void *ud, const char *buf, size_t len) { ne_decompress *ctx = ud; const char *zbuf; size_t count; const char *hdr; if (len == 0) { /* End of response: */ switch (ctx->state) { case NE_Z_BEFORE_DATA: hdr = ne_get_response_header(ctx->request, "Content-Encoding"); if (hdr && strcasecmp(hdr, "gzip") == 0) { /* response was truncated: return error. */ break; } /* else, fall through */ case NE_Z_FINISHED: /* complete gzip response */ case NE_Z_PASSTHROUGH: /* complete uncompressed response */ return ctx->reader(ctx->userdata, buf, 0); default: /* invalid state: truncated response. */ break; } /* else: truncated response, fail. */ ne_set_error(ctx->session, "Compressed response was truncated"); return NE_ERROR; } switch (ctx->state) { case NE_Z_PASSTHROUGH: /* move along there. */ return ctx->reader(ctx->userdata, buf, len); case NE_Z_FINISHED: /* Could argue for tolerance, and ignoring trailing content; * but it could mean something more serious. */ if (len > 0) { ne_set_error(ctx->session, "Unexpected content received after compressed stream"); return NE_ERROR; } break; case NE_Z_BEFORE_DATA: /* work out whether this is a compressed response or not. */ hdr = ne_get_response_header(ctx->request, "Content-Encoding"); if (hdr && strcasecmp(hdr, "gzip") == 0) { int ret; NE_DEBUG(NE_DBG_HTTP, "compress: got gzipped stream.\n"); /* inflateInit2() works here where inflateInit() doesn't. */ ret = inflateInit2(&ctx->zstr, -MAX_WBITS); if (ret != Z_OK) { set_zlib_error(ctx, _("Could not initialize zlib"), ret); return -1; } ctx->zstrinit = 1; } else { /* No Content-Encoding header: pass it on. TODO: we could * hack it and register the real callback now. But that * would require add_resp_body_rdr to have defined * ordering semantics etc etc */ ctx->state = NE_Z_PASSTHROUGH; return ctx->reader(ctx->userdata, buf, len); } ctx->state = NE_Z_IN_HEADER; /* FALLTHROUGH */ case NE_Z_IN_HEADER: /* copy as many bytes as possible into the buffer. */ if (len + ctx->hdrcount > 10) { count = 10 - ctx->hdrcount; } else { count = len; } memcpy(ctx->header + ctx->hdrcount, buf, count); ctx->hdrcount += count; /* have we got the full header yet? */ if (ctx->hdrcount != 10) { return 0; } buf += count; len -= count; switch (parse_header(ctx)) { case HDR_EXTENDED: if (len == 0) return 0; break; case HDR_ERROR: return NE_ERROR; case HDR_DONE: if (len > 0) { return do_inflate(ctx, buf, len); } break; } /* FALLTHROUGH */ case NE_Z_POST_HEADER: /* eating the filename string. */ zbuf = memchr(buf, '\0', len); if (zbuf == NULL) { /* not found it yet. */ return 0; } NE_DEBUG(NE_DBG_HTTP, "compresss: skipped %" NE_FMT_SIZE_T " header bytes.\n", zbuf - buf); /* found end of string. */ len -= (1 + zbuf - buf); buf = zbuf + 1; ctx->state = NE_Z_INFLATING; if (len == 0) { /* end of string was at end of buffer. */ return 0; } /* FALLTHROUGH */ case NE_Z_INFLATING: return do_inflate(ctx, buf, len); case NE_Z_AFTER_DATA: return process_footer(ctx, (unsigned char *)buf, len); } return 0; }