113498266Sopenharmony_ci/*************************************************************************** 213498266Sopenharmony_ci * _ _ ____ _ 313498266Sopenharmony_ci * Project ___| | | | _ \| | 413498266Sopenharmony_ci * / __| | | | |_) | | 513498266Sopenharmony_ci * | (__| |_| | _ <| |___ 613498266Sopenharmony_ci * \___|\___/|_| \_\_____| 713498266Sopenharmony_ci * 813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 913498266Sopenharmony_ci * 1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which 1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms 1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html. 1313498266Sopenharmony_ci * 1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell 1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is 1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file. 1713498266Sopenharmony_ci * 1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 1913498266Sopenharmony_ci * KIND, either express or implied. 2013498266Sopenharmony_ci * 2113498266Sopenharmony_ci * SPDX-License-Identifier: curl 2213498266Sopenharmony_ci * 2313498266Sopenharmony_ci ***************************************************************************/ 2413498266Sopenharmony_ci 2513498266Sopenharmony_ci#include "curl_setup.h" 2613498266Sopenharmony_ci 2713498266Sopenharmony_ci#include "urldata.h" 2813498266Sopenharmony_ci#include <curl/curl.h> 2913498266Sopenharmony_ci#include <stddef.h> 3013498266Sopenharmony_ci 3113498266Sopenharmony_ci#ifdef HAVE_LIBZ 3213498266Sopenharmony_ci#include <zlib.h> 3313498266Sopenharmony_ci#endif 3413498266Sopenharmony_ci 3513498266Sopenharmony_ci#ifdef HAVE_BROTLI 3613498266Sopenharmony_ci#if defined(__GNUC__) 3713498266Sopenharmony_ci/* Ignore -Wvla warnings in brotli headers */ 3813498266Sopenharmony_ci#pragma GCC diagnostic push 3913498266Sopenharmony_ci#pragma GCC diagnostic ignored "-Wvla" 4013498266Sopenharmony_ci#endif 4113498266Sopenharmony_ci#include <brotli/decode.h> 4213498266Sopenharmony_ci#if defined(__GNUC__) 4313498266Sopenharmony_ci#pragma GCC diagnostic pop 4413498266Sopenharmony_ci#endif 4513498266Sopenharmony_ci#endif 4613498266Sopenharmony_ci 4713498266Sopenharmony_ci#ifdef HAVE_ZSTD 4813498266Sopenharmony_ci#include <zstd.h> 4913498266Sopenharmony_ci#endif 5013498266Sopenharmony_ci 5113498266Sopenharmony_ci#include "sendf.h" 5213498266Sopenharmony_ci#include "http.h" 5313498266Sopenharmony_ci#include "content_encoding.h" 5413498266Sopenharmony_ci#include "strdup.h" 5513498266Sopenharmony_ci#include "strcase.h" 5613498266Sopenharmony_ci 5713498266Sopenharmony_ci/* The last 3 #include files should be in this order */ 5813498266Sopenharmony_ci#include "curl_printf.h" 5913498266Sopenharmony_ci#include "curl_memory.h" 6013498266Sopenharmony_ci#include "memdebug.h" 6113498266Sopenharmony_ci 6213498266Sopenharmony_ci#define CONTENT_ENCODING_DEFAULT "identity" 6313498266Sopenharmony_ci 6413498266Sopenharmony_ci#ifndef CURL_DISABLE_HTTP 6513498266Sopenharmony_ci 6613498266Sopenharmony_ci/* allow no more than 5 "chained" compression steps */ 6713498266Sopenharmony_ci#define MAX_ENCODE_STACK 5 6813498266Sopenharmony_ci 6913498266Sopenharmony_ci#define DSIZ CURL_MAX_WRITE_SIZE /* buffer size for decompressed data */ 7013498266Sopenharmony_ci 7113498266Sopenharmony_ci 7213498266Sopenharmony_ci#ifdef HAVE_LIBZ 7313498266Sopenharmony_ci 7413498266Sopenharmony_ci/* Comment this out if zlib is always going to be at least ver. 1.2.0.4 7513498266Sopenharmony_ci (doing so will reduce code size slightly). */ 7613498266Sopenharmony_ci#define OLD_ZLIB_SUPPORT 1 7713498266Sopenharmony_ci 7813498266Sopenharmony_ci#define GZIP_MAGIC_0 0x1f 7913498266Sopenharmony_ci#define GZIP_MAGIC_1 0x8b 8013498266Sopenharmony_ci 8113498266Sopenharmony_ci/* gzip flag byte */ 8213498266Sopenharmony_ci#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ 8313498266Sopenharmony_ci#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ 8413498266Sopenharmony_ci#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ 8513498266Sopenharmony_ci#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ 8613498266Sopenharmony_ci#define COMMENT 0x10 /* bit 4 set: file comment present */ 8713498266Sopenharmony_ci#define RESERVED 0xE0 /* bits 5..7: reserved */ 8813498266Sopenharmony_ci 8913498266Sopenharmony_citypedef enum { 9013498266Sopenharmony_ci ZLIB_UNINIT, /* uninitialized */ 9113498266Sopenharmony_ci ZLIB_INIT, /* initialized */ 9213498266Sopenharmony_ci ZLIB_INFLATING, /* inflating started. */ 9313498266Sopenharmony_ci ZLIB_EXTERNAL_TRAILER, /* reading external trailer */ 9413498266Sopenharmony_ci ZLIB_GZIP_HEADER, /* reading gzip header */ 9513498266Sopenharmony_ci ZLIB_GZIP_INFLATING, /* inflating gzip stream */ 9613498266Sopenharmony_ci ZLIB_INIT_GZIP /* initialized in transparent gzip mode */ 9713498266Sopenharmony_ci} zlibInitState; 9813498266Sopenharmony_ci 9913498266Sopenharmony_ci/* Deflate and gzip writer. */ 10013498266Sopenharmony_cistruct zlib_writer { 10113498266Sopenharmony_ci struct Curl_cwriter super; 10213498266Sopenharmony_ci zlibInitState zlib_init; /* zlib init state */ 10313498266Sopenharmony_ci uInt trailerlen; /* Remaining trailer byte count. */ 10413498266Sopenharmony_ci z_stream z; /* State structure for zlib. */ 10513498266Sopenharmony_ci}; 10613498266Sopenharmony_ci 10713498266Sopenharmony_ci 10813498266Sopenharmony_cistatic voidpf 10913498266Sopenharmony_cizalloc_cb(voidpf opaque, unsigned int items, unsigned int size) 11013498266Sopenharmony_ci{ 11113498266Sopenharmony_ci (void) opaque; 11213498266Sopenharmony_ci /* not a typo, keep it calloc() */ 11313498266Sopenharmony_ci return (voidpf) calloc(items, size); 11413498266Sopenharmony_ci} 11513498266Sopenharmony_ci 11613498266Sopenharmony_cistatic void 11713498266Sopenharmony_cizfree_cb(voidpf opaque, voidpf ptr) 11813498266Sopenharmony_ci{ 11913498266Sopenharmony_ci (void) opaque; 12013498266Sopenharmony_ci free(ptr); 12113498266Sopenharmony_ci} 12213498266Sopenharmony_ci 12313498266Sopenharmony_cistatic CURLcode 12413498266Sopenharmony_ciprocess_zlib_error(struct Curl_easy *data, z_stream *z) 12513498266Sopenharmony_ci{ 12613498266Sopenharmony_ci if(z->msg) 12713498266Sopenharmony_ci failf(data, "Error while processing content unencoding: %s", 12813498266Sopenharmony_ci z->msg); 12913498266Sopenharmony_ci else 13013498266Sopenharmony_ci failf(data, "Error while processing content unencoding: " 13113498266Sopenharmony_ci "Unknown failure within decompression software."); 13213498266Sopenharmony_ci 13313498266Sopenharmony_ci return CURLE_BAD_CONTENT_ENCODING; 13413498266Sopenharmony_ci} 13513498266Sopenharmony_ci 13613498266Sopenharmony_cistatic CURLcode 13713498266Sopenharmony_ciexit_zlib(struct Curl_easy *data, 13813498266Sopenharmony_ci z_stream *z, zlibInitState *zlib_init, CURLcode result) 13913498266Sopenharmony_ci{ 14013498266Sopenharmony_ci if(*zlib_init == ZLIB_GZIP_HEADER) 14113498266Sopenharmony_ci Curl_safefree(z->next_in); 14213498266Sopenharmony_ci 14313498266Sopenharmony_ci if(*zlib_init != ZLIB_UNINIT) { 14413498266Sopenharmony_ci if(inflateEnd(z) != Z_OK && result == CURLE_OK) 14513498266Sopenharmony_ci result = process_zlib_error(data, z); 14613498266Sopenharmony_ci *zlib_init = ZLIB_UNINIT; 14713498266Sopenharmony_ci } 14813498266Sopenharmony_ci 14913498266Sopenharmony_ci return result; 15013498266Sopenharmony_ci} 15113498266Sopenharmony_ci 15213498266Sopenharmony_cistatic CURLcode process_trailer(struct Curl_easy *data, 15313498266Sopenharmony_ci struct zlib_writer *zp) 15413498266Sopenharmony_ci{ 15513498266Sopenharmony_ci z_stream *z = &zp->z; 15613498266Sopenharmony_ci CURLcode result = CURLE_OK; 15713498266Sopenharmony_ci uInt len = z->avail_in < zp->trailerlen? z->avail_in: zp->trailerlen; 15813498266Sopenharmony_ci 15913498266Sopenharmony_ci /* Consume expected trailer bytes. Terminate stream if exhausted. 16013498266Sopenharmony_ci Issue an error if unexpected bytes follow. */ 16113498266Sopenharmony_ci 16213498266Sopenharmony_ci zp->trailerlen -= len; 16313498266Sopenharmony_ci z->avail_in -= len; 16413498266Sopenharmony_ci z->next_in += len; 16513498266Sopenharmony_ci if(z->avail_in) 16613498266Sopenharmony_ci result = CURLE_WRITE_ERROR; 16713498266Sopenharmony_ci if(result || !zp->trailerlen) 16813498266Sopenharmony_ci result = exit_zlib(data, z, &zp->zlib_init, result); 16913498266Sopenharmony_ci else { 17013498266Sopenharmony_ci /* Only occurs for gzip with zlib < 1.2.0.4 or raw deflate. */ 17113498266Sopenharmony_ci zp->zlib_init = ZLIB_EXTERNAL_TRAILER; 17213498266Sopenharmony_ci } 17313498266Sopenharmony_ci return result; 17413498266Sopenharmony_ci} 17513498266Sopenharmony_ci 17613498266Sopenharmony_cistatic CURLcode inflate_stream(struct Curl_easy *data, 17713498266Sopenharmony_ci struct Curl_cwriter *writer, int type, 17813498266Sopenharmony_ci zlibInitState started) 17913498266Sopenharmony_ci{ 18013498266Sopenharmony_ci struct zlib_writer *zp = (struct zlib_writer *) writer; 18113498266Sopenharmony_ci z_stream *z = &zp->z; /* zlib state structure */ 18213498266Sopenharmony_ci uInt nread = z->avail_in; 18313498266Sopenharmony_ci Bytef *orig_in = z->next_in; 18413498266Sopenharmony_ci bool done = FALSE; 18513498266Sopenharmony_ci CURLcode result = CURLE_OK; /* Curl_client_write status */ 18613498266Sopenharmony_ci char *decomp; /* Put the decompressed data here. */ 18713498266Sopenharmony_ci 18813498266Sopenharmony_ci /* Check state. */ 18913498266Sopenharmony_ci if(zp->zlib_init != ZLIB_INIT && 19013498266Sopenharmony_ci zp->zlib_init != ZLIB_INFLATING && 19113498266Sopenharmony_ci zp->zlib_init != ZLIB_INIT_GZIP && 19213498266Sopenharmony_ci zp->zlib_init != ZLIB_GZIP_INFLATING) 19313498266Sopenharmony_ci return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR); 19413498266Sopenharmony_ci 19513498266Sopenharmony_ci /* Dynamically allocate a buffer for decompression because it's uncommonly 19613498266Sopenharmony_ci large to hold on the stack */ 19713498266Sopenharmony_ci decomp = malloc(DSIZ); 19813498266Sopenharmony_ci if(!decomp) 19913498266Sopenharmony_ci return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY); 20013498266Sopenharmony_ci 20113498266Sopenharmony_ci /* because the buffer size is fixed, iteratively decompress and transfer to 20213498266Sopenharmony_ci the client via next_write function. */ 20313498266Sopenharmony_ci while(!done) { 20413498266Sopenharmony_ci int status; /* zlib status */ 20513498266Sopenharmony_ci done = TRUE; 20613498266Sopenharmony_ci 20713498266Sopenharmony_ci /* (re)set buffer for decompressed output for every iteration */ 20813498266Sopenharmony_ci z->next_out = (Bytef *) decomp; 20913498266Sopenharmony_ci z->avail_out = DSIZ; 21013498266Sopenharmony_ci 21113498266Sopenharmony_ci#ifdef Z_BLOCK 21213498266Sopenharmony_ci /* Z_BLOCK is only available in zlib ver. >= 1.2.0.5 */ 21313498266Sopenharmony_ci status = inflate(z, Z_BLOCK); 21413498266Sopenharmony_ci#else 21513498266Sopenharmony_ci /* fallback for zlib ver. < 1.2.0.5 */ 21613498266Sopenharmony_ci status = inflate(z, Z_SYNC_FLUSH); 21713498266Sopenharmony_ci#endif 21813498266Sopenharmony_ci 21913498266Sopenharmony_ci /* Flush output data if some. */ 22013498266Sopenharmony_ci if(z->avail_out != DSIZ) { 22113498266Sopenharmony_ci if(status == Z_OK || status == Z_STREAM_END) { 22213498266Sopenharmony_ci zp->zlib_init = started; /* Data started. */ 22313498266Sopenharmony_ci result = Curl_cwriter_write(data, writer->next, type, decomp, 22413498266Sopenharmony_ci DSIZ - z->avail_out); 22513498266Sopenharmony_ci if(result) { 22613498266Sopenharmony_ci exit_zlib(data, z, &zp->zlib_init, result); 22713498266Sopenharmony_ci break; 22813498266Sopenharmony_ci } 22913498266Sopenharmony_ci } 23013498266Sopenharmony_ci } 23113498266Sopenharmony_ci 23213498266Sopenharmony_ci /* Dispatch by inflate() status. */ 23313498266Sopenharmony_ci switch(status) { 23413498266Sopenharmony_ci case Z_OK: 23513498266Sopenharmony_ci /* Always loop: there may be unflushed latched data in zlib state. */ 23613498266Sopenharmony_ci done = FALSE; 23713498266Sopenharmony_ci break; 23813498266Sopenharmony_ci case Z_BUF_ERROR: 23913498266Sopenharmony_ci /* No more data to flush: just exit loop. */ 24013498266Sopenharmony_ci break; 24113498266Sopenharmony_ci case Z_STREAM_END: 24213498266Sopenharmony_ci result = process_trailer(data, zp); 24313498266Sopenharmony_ci break; 24413498266Sopenharmony_ci case Z_DATA_ERROR: 24513498266Sopenharmony_ci /* some servers seem to not generate zlib headers, so this is an attempt 24613498266Sopenharmony_ci to fix and continue anyway */ 24713498266Sopenharmony_ci if(zp->zlib_init == ZLIB_INIT) { 24813498266Sopenharmony_ci /* Do not use inflateReset2(): only available since zlib 1.2.3.4. */ 24913498266Sopenharmony_ci (void) inflateEnd(z); /* don't care about the return code */ 25013498266Sopenharmony_ci if(inflateInit2(z, -MAX_WBITS) == Z_OK) { 25113498266Sopenharmony_ci z->next_in = orig_in; 25213498266Sopenharmony_ci z->avail_in = nread; 25313498266Sopenharmony_ci zp->zlib_init = ZLIB_INFLATING; 25413498266Sopenharmony_ci zp->trailerlen = 4; /* Tolerate up to 4 unknown trailer bytes. */ 25513498266Sopenharmony_ci done = FALSE; 25613498266Sopenharmony_ci break; 25713498266Sopenharmony_ci } 25813498266Sopenharmony_ci zp->zlib_init = ZLIB_UNINIT; /* inflateEnd() already called. */ 25913498266Sopenharmony_ci } 26013498266Sopenharmony_ci result = exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); 26113498266Sopenharmony_ci break; 26213498266Sopenharmony_ci default: 26313498266Sopenharmony_ci result = exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); 26413498266Sopenharmony_ci break; 26513498266Sopenharmony_ci } 26613498266Sopenharmony_ci } 26713498266Sopenharmony_ci free(decomp); 26813498266Sopenharmony_ci 26913498266Sopenharmony_ci /* We're about to leave this call so the `nread' data bytes won't be seen 27013498266Sopenharmony_ci again. If we are in a state that would wrongly allow restart in raw mode 27113498266Sopenharmony_ci at the next call, assume output has already started. */ 27213498266Sopenharmony_ci if(nread && zp->zlib_init == ZLIB_INIT) 27313498266Sopenharmony_ci zp->zlib_init = started; /* Cannot restart anymore. */ 27413498266Sopenharmony_ci 27513498266Sopenharmony_ci return result; 27613498266Sopenharmony_ci} 27713498266Sopenharmony_ci 27813498266Sopenharmony_ci 27913498266Sopenharmony_ci/* Deflate handler. */ 28013498266Sopenharmony_cistatic CURLcode deflate_do_init(struct Curl_easy *data, 28113498266Sopenharmony_ci struct Curl_cwriter *writer) 28213498266Sopenharmony_ci{ 28313498266Sopenharmony_ci struct zlib_writer *zp = (struct zlib_writer *) writer; 28413498266Sopenharmony_ci z_stream *z = &zp->z; /* zlib state structure */ 28513498266Sopenharmony_ci 28613498266Sopenharmony_ci /* Initialize zlib */ 28713498266Sopenharmony_ci z->zalloc = (alloc_func) zalloc_cb; 28813498266Sopenharmony_ci z->zfree = (free_func) zfree_cb; 28913498266Sopenharmony_ci 29013498266Sopenharmony_ci if(inflateInit(z) != Z_OK) 29113498266Sopenharmony_ci return process_zlib_error(data, z); 29213498266Sopenharmony_ci zp->zlib_init = ZLIB_INIT; 29313498266Sopenharmony_ci return CURLE_OK; 29413498266Sopenharmony_ci} 29513498266Sopenharmony_ci 29613498266Sopenharmony_cistatic CURLcode deflate_do_write(struct Curl_easy *data, 29713498266Sopenharmony_ci struct Curl_cwriter *writer, int type, 29813498266Sopenharmony_ci const char *buf, size_t nbytes) 29913498266Sopenharmony_ci{ 30013498266Sopenharmony_ci struct zlib_writer *zp = (struct zlib_writer *) writer; 30113498266Sopenharmony_ci z_stream *z = &zp->z; /* zlib state structure */ 30213498266Sopenharmony_ci 30313498266Sopenharmony_ci if(!(type & CLIENTWRITE_BODY)) 30413498266Sopenharmony_ci return Curl_cwriter_write(data, writer->next, type, buf, nbytes); 30513498266Sopenharmony_ci 30613498266Sopenharmony_ci /* Set the compressed input when this function is called */ 30713498266Sopenharmony_ci z->next_in = (Bytef *) buf; 30813498266Sopenharmony_ci z->avail_in = (uInt) nbytes; 30913498266Sopenharmony_ci 31013498266Sopenharmony_ci if(zp->zlib_init == ZLIB_EXTERNAL_TRAILER) 31113498266Sopenharmony_ci return process_trailer(data, zp); 31213498266Sopenharmony_ci 31313498266Sopenharmony_ci /* Now uncompress the data */ 31413498266Sopenharmony_ci return inflate_stream(data, writer, type, ZLIB_INFLATING); 31513498266Sopenharmony_ci} 31613498266Sopenharmony_ci 31713498266Sopenharmony_cistatic void deflate_do_close(struct Curl_easy *data, 31813498266Sopenharmony_ci struct Curl_cwriter *writer) 31913498266Sopenharmony_ci{ 32013498266Sopenharmony_ci struct zlib_writer *zp = (struct zlib_writer *) writer; 32113498266Sopenharmony_ci z_stream *z = &zp->z; /* zlib state structure */ 32213498266Sopenharmony_ci 32313498266Sopenharmony_ci exit_zlib(data, z, &zp->zlib_init, CURLE_OK); 32413498266Sopenharmony_ci} 32513498266Sopenharmony_ci 32613498266Sopenharmony_cistatic const struct Curl_cwtype deflate_encoding = { 32713498266Sopenharmony_ci "deflate", 32813498266Sopenharmony_ci NULL, 32913498266Sopenharmony_ci deflate_do_init, 33013498266Sopenharmony_ci deflate_do_write, 33113498266Sopenharmony_ci deflate_do_close, 33213498266Sopenharmony_ci sizeof(struct zlib_writer) 33313498266Sopenharmony_ci}; 33413498266Sopenharmony_ci 33513498266Sopenharmony_ci 33613498266Sopenharmony_ci/* Gzip handler. */ 33713498266Sopenharmony_cistatic CURLcode gzip_do_init(struct Curl_easy *data, 33813498266Sopenharmony_ci struct Curl_cwriter *writer) 33913498266Sopenharmony_ci{ 34013498266Sopenharmony_ci struct zlib_writer *zp = (struct zlib_writer *) writer; 34113498266Sopenharmony_ci z_stream *z = &zp->z; /* zlib state structure */ 34213498266Sopenharmony_ci 34313498266Sopenharmony_ci /* Initialize zlib */ 34413498266Sopenharmony_ci z->zalloc = (alloc_func) zalloc_cb; 34513498266Sopenharmony_ci z->zfree = (free_func) zfree_cb; 34613498266Sopenharmony_ci 34713498266Sopenharmony_ci if(strcmp(zlibVersion(), "1.2.0.4") >= 0) { 34813498266Sopenharmony_ci /* zlib ver. >= 1.2.0.4 supports transparent gzip decompressing */ 34913498266Sopenharmony_ci if(inflateInit2(z, MAX_WBITS + 32) != Z_OK) { 35013498266Sopenharmony_ci return process_zlib_error(data, z); 35113498266Sopenharmony_ci } 35213498266Sopenharmony_ci zp->zlib_init = ZLIB_INIT_GZIP; /* Transparent gzip decompress state */ 35313498266Sopenharmony_ci } 35413498266Sopenharmony_ci else { 35513498266Sopenharmony_ci /* we must parse the gzip header and trailer ourselves */ 35613498266Sopenharmony_ci if(inflateInit2(z, -MAX_WBITS) != Z_OK) { 35713498266Sopenharmony_ci return process_zlib_error(data, z); 35813498266Sopenharmony_ci } 35913498266Sopenharmony_ci zp->trailerlen = 8; /* A CRC-32 and a 32-bit input size (RFC 1952, 2.2) */ 36013498266Sopenharmony_ci zp->zlib_init = ZLIB_INIT; /* Initial call state */ 36113498266Sopenharmony_ci } 36213498266Sopenharmony_ci 36313498266Sopenharmony_ci return CURLE_OK; 36413498266Sopenharmony_ci} 36513498266Sopenharmony_ci 36613498266Sopenharmony_ci#ifdef OLD_ZLIB_SUPPORT 36713498266Sopenharmony_ci/* Skip over the gzip header */ 36813498266Sopenharmony_citypedef enum { 36913498266Sopenharmony_ci GZIP_OK, 37013498266Sopenharmony_ci GZIP_BAD, 37113498266Sopenharmony_ci GZIP_UNDERFLOW 37213498266Sopenharmony_ci} gzip_status; 37313498266Sopenharmony_ci 37413498266Sopenharmony_cistatic gzip_status check_gzip_header(unsigned char const *data, ssize_t len, 37513498266Sopenharmony_ci ssize_t *headerlen) 37613498266Sopenharmony_ci{ 37713498266Sopenharmony_ci int method, flags; 37813498266Sopenharmony_ci const ssize_t totallen = len; 37913498266Sopenharmony_ci 38013498266Sopenharmony_ci /* The shortest header is 10 bytes */ 38113498266Sopenharmony_ci if(len < 10) 38213498266Sopenharmony_ci return GZIP_UNDERFLOW; 38313498266Sopenharmony_ci 38413498266Sopenharmony_ci if((data[0] != GZIP_MAGIC_0) || (data[1] != GZIP_MAGIC_1)) 38513498266Sopenharmony_ci return GZIP_BAD; 38613498266Sopenharmony_ci 38713498266Sopenharmony_ci method = data[2]; 38813498266Sopenharmony_ci flags = data[3]; 38913498266Sopenharmony_ci 39013498266Sopenharmony_ci if(method != Z_DEFLATED || (flags & RESERVED) != 0) { 39113498266Sopenharmony_ci /* Can't handle this compression method or unknown flag */ 39213498266Sopenharmony_ci return GZIP_BAD; 39313498266Sopenharmony_ci } 39413498266Sopenharmony_ci 39513498266Sopenharmony_ci /* Skip over time, xflags, OS code and all previous bytes */ 39613498266Sopenharmony_ci len -= 10; 39713498266Sopenharmony_ci data += 10; 39813498266Sopenharmony_ci 39913498266Sopenharmony_ci if(flags & EXTRA_FIELD) { 40013498266Sopenharmony_ci ssize_t extra_len; 40113498266Sopenharmony_ci 40213498266Sopenharmony_ci if(len < 2) 40313498266Sopenharmony_ci return GZIP_UNDERFLOW; 40413498266Sopenharmony_ci 40513498266Sopenharmony_ci extra_len = (data[1] << 8) | data[0]; 40613498266Sopenharmony_ci 40713498266Sopenharmony_ci if(len < (extra_len + 2)) 40813498266Sopenharmony_ci return GZIP_UNDERFLOW; 40913498266Sopenharmony_ci 41013498266Sopenharmony_ci len -= (extra_len + 2); 41113498266Sopenharmony_ci data += (extra_len + 2); 41213498266Sopenharmony_ci } 41313498266Sopenharmony_ci 41413498266Sopenharmony_ci if(flags & ORIG_NAME) { 41513498266Sopenharmony_ci /* Skip over NUL-terminated file name */ 41613498266Sopenharmony_ci while(len && *data) { 41713498266Sopenharmony_ci --len; 41813498266Sopenharmony_ci ++data; 41913498266Sopenharmony_ci } 42013498266Sopenharmony_ci if(!len || *data) 42113498266Sopenharmony_ci return GZIP_UNDERFLOW; 42213498266Sopenharmony_ci 42313498266Sopenharmony_ci /* Skip over the NUL */ 42413498266Sopenharmony_ci --len; 42513498266Sopenharmony_ci ++data; 42613498266Sopenharmony_ci } 42713498266Sopenharmony_ci 42813498266Sopenharmony_ci if(flags & COMMENT) { 42913498266Sopenharmony_ci /* Skip over NUL-terminated comment */ 43013498266Sopenharmony_ci while(len && *data) { 43113498266Sopenharmony_ci --len; 43213498266Sopenharmony_ci ++data; 43313498266Sopenharmony_ci } 43413498266Sopenharmony_ci if(!len || *data) 43513498266Sopenharmony_ci return GZIP_UNDERFLOW; 43613498266Sopenharmony_ci 43713498266Sopenharmony_ci /* Skip over the NUL */ 43813498266Sopenharmony_ci --len; 43913498266Sopenharmony_ci } 44013498266Sopenharmony_ci 44113498266Sopenharmony_ci if(flags & HEAD_CRC) { 44213498266Sopenharmony_ci if(len < 2) 44313498266Sopenharmony_ci return GZIP_UNDERFLOW; 44413498266Sopenharmony_ci 44513498266Sopenharmony_ci len -= 2; 44613498266Sopenharmony_ci } 44713498266Sopenharmony_ci 44813498266Sopenharmony_ci *headerlen = totallen - len; 44913498266Sopenharmony_ci return GZIP_OK; 45013498266Sopenharmony_ci} 45113498266Sopenharmony_ci#endif 45213498266Sopenharmony_ci 45313498266Sopenharmony_cistatic CURLcode gzip_do_write(struct Curl_easy *data, 45413498266Sopenharmony_ci struct Curl_cwriter *writer, int type, 45513498266Sopenharmony_ci const char *buf, size_t nbytes) 45613498266Sopenharmony_ci{ 45713498266Sopenharmony_ci struct zlib_writer *zp = (struct zlib_writer *) writer; 45813498266Sopenharmony_ci z_stream *z = &zp->z; /* zlib state structure */ 45913498266Sopenharmony_ci 46013498266Sopenharmony_ci if(!(type & CLIENTWRITE_BODY)) 46113498266Sopenharmony_ci return Curl_cwriter_write(data, writer->next, type, buf, nbytes); 46213498266Sopenharmony_ci 46313498266Sopenharmony_ci if(zp->zlib_init == ZLIB_INIT_GZIP) { 46413498266Sopenharmony_ci /* Let zlib handle the gzip decompression entirely */ 46513498266Sopenharmony_ci z->next_in = (Bytef *) buf; 46613498266Sopenharmony_ci z->avail_in = (uInt) nbytes; 46713498266Sopenharmony_ci /* Now uncompress the data */ 46813498266Sopenharmony_ci return inflate_stream(data, writer, type, ZLIB_INIT_GZIP); 46913498266Sopenharmony_ci } 47013498266Sopenharmony_ci 47113498266Sopenharmony_ci#ifndef OLD_ZLIB_SUPPORT 47213498266Sopenharmony_ci /* Support for old zlib versions is compiled away and we are running with 47313498266Sopenharmony_ci an old version, so return an error. */ 47413498266Sopenharmony_ci return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR); 47513498266Sopenharmony_ci 47613498266Sopenharmony_ci#else 47713498266Sopenharmony_ci /* This next mess is to get around the potential case where there isn't 47813498266Sopenharmony_ci * enough data passed in to skip over the gzip header. If that happens, we 47913498266Sopenharmony_ci * malloc a block and copy what we have then wait for the next call. If 48013498266Sopenharmony_ci * there still isn't enough (this is definitely a worst-case scenario), we 48113498266Sopenharmony_ci * make the block bigger, copy the next part in and keep waiting. 48213498266Sopenharmony_ci * 48313498266Sopenharmony_ci * This is only required with zlib versions < 1.2.0.4 as newer versions 48413498266Sopenharmony_ci * can handle the gzip header themselves. 48513498266Sopenharmony_ci */ 48613498266Sopenharmony_ci 48713498266Sopenharmony_ci switch(zp->zlib_init) { 48813498266Sopenharmony_ci /* Skip over gzip header? */ 48913498266Sopenharmony_ci case ZLIB_INIT: 49013498266Sopenharmony_ci { 49113498266Sopenharmony_ci /* Initial call state */ 49213498266Sopenharmony_ci ssize_t hlen; 49313498266Sopenharmony_ci 49413498266Sopenharmony_ci switch(check_gzip_header((unsigned char *) buf, nbytes, &hlen)) { 49513498266Sopenharmony_ci case GZIP_OK: 49613498266Sopenharmony_ci z->next_in = (Bytef *) buf + hlen; 49713498266Sopenharmony_ci z->avail_in = (uInt) (nbytes - hlen); 49813498266Sopenharmony_ci zp->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */ 49913498266Sopenharmony_ci break; 50013498266Sopenharmony_ci 50113498266Sopenharmony_ci case GZIP_UNDERFLOW: 50213498266Sopenharmony_ci /* We need more data so we can find the end of the gzip header. It's 50313498266Sopenharmony_ci * possible that the memory block we malloc here will never be freed if 50413498266Sopenharmony_ci * the transfer abruptly aborts after this point. Since it's unlikely 50513498266Sopenharmony_ci * that circumstances will be right for this code path to be followed in 50613498266Sopenharmony_ci * the first place, and it's even more unlikely for a transfer to fail 50713498266Sopenharmony_ci * immediately afterwards, it should seldom be a problem. 50813498266Sopenharmony_ci */ 50913498266Sopenharmony_ci z->avail_in = (uInt) nbytes; 51013498266Sopenharmony_ci z->next_in = malloc(z->avail_in); 51113498266Sopenharmony_ci if(!z->next_in) { 51213498266Sopenharmony_ci return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY); 51313498266Sopenharmony_ci } 51413498266Sopenharmony_ci memcpy(z->next_in, buf, z->avail_in); 51513498266Sopenharmony_ci zp->zlib_init = ZLIB_GZIP_HEADER; /* Need more gzip header data state */ 51613498266Sopenharmony_ci /* We don't have any data to inflate yet */ 51713498266Sopenharmony_ci return CURLE_OK; 51813498266Sopenharmony_ci 51913498266Sopenharmony_ci case GZIP_BAD: 52013498266Sopenharmony_ci default: 52113498266Sopenharmony_ci return exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); 52213498266Sopenharmony_ci } 52313498266Sopenharmony_ci 52413498266Sopenharmony_ci } 52513498266Sopenharmony_ci break; 52613498266Sopenharmony_ci 52713498266Sopenharmony_ci case ZLIB_GZIP_HEADER: 52813498266Sopenharmony_ci { 52913498266Sopenharmony_ci /* Need more gzip header data state */ 53013498266Sopenharmony_ci ssize_t hlen; 53113498266Sopenharmony_ci z->avail_in += (uInt) nbytes; 53213498266Sopenharmony_ci z->next_in = Curl_saferealloc(z->next_in, z->avail_in); 53313498266Sopenharmony_ci if(!z->next_in) { 53413498266Sopenharmony_ci return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY); 53513498266Sopenharmony_ci } 53613498266Sopenharmony_ci /* Append the new block of data to the previous one */ 53713498266Sopenharmony_ci memcpy(z->next_in + z->avail_in - nbytes, buf, nbytes); 53813498266Sopenharmony_ci 53913498266Sopenharmony_ci switch(check_gzip_header(z->next_in, z->avail_in, &hlen)) { 54013498266Sopenharmony_ci case GZIP_OK: 54113498266Sopenharmony_ci /* This is the zlib stream data */ 54213498266Sopenharmony_ci free(z->next_in); 54313498266Sopenharmony_ci /* Don't point into the malloced block since we just freed it */ 54413498266Sopenharmony_ci z->next_in = (Bytef *) buf + hlen + nbytes - z->avail_in; 54513498266Sopenharmony_ci z->avail_in = (uInt) (z->avail_in - hlen); 54613498266Sopenharmony_ci zp->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */ 54713498266Sopenharmony_ci break; 54813498266Sopenharmony_ci 54913498266Sopenharmony_ci case GZIP_UNDERFLOW: 55013498266Sopenharmony_ci /* We still don't have any data to inflate! */ 55113498266Sopenharmony_ci return CURLE_OK; 55213498266Sopenharmony_ci 55313498266Sopenharmony_ci case GZIP_BAD: 55413498266Sopenharmony_ci default: 55513498266Sopenharmony_ci return exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); 55613498266Sopenharmony_ci } 55713498266Sopenharmony_ci 55813498266Sopenharmony_ci } 55913498266Sopenharmony_ci break; 56013498266Sopenharmony_ci 56113498266Sopenharmony_ci case ZLIB_EXTERNAL_TRAILER: 56213498266Sopenharmony_ci z->next_in = (Bytef *) buf; 56313498266Sopenharmony_ci z->avail_in = (uInt) nbytes; 56413498266Sopenharmony_ci return process_trailer(data, zp); 56513498266Sopenharmony_ci 56613498266Sopenharmony_ci case ZLIB_GZIP_INFLATING: 56713498266Sopenharmony_ci default: 56813498266Sopenharmony_ci /* Inflating stream state */ 56913498266Sopenharmony_ci z->next_in = (Bytef *) buf; 57013498266Sopenharmony_ci z->avail_in = (uInt) nbytes; 57113498266Sopenharmony_ci break; 57213498266Sopenharmony_ci } 57313498266Sopenharmony_ci 57413498266Sopenharmony_ci if(z->avail_in == 0) { 57513498266Sopenharmony_ci /* We don't have any data to inflate; wait until next time */ 57613498266Sopenharmony_ci return CURLE_OK; 57713498266Sopenharmony_ci } 57813498266Sopenharmony_ci 57913498266Sopenharmony_ci /* We've parsed the header, now uncompress the data */ 58013498266Sopenharmony_ci return inflate_stream(data, writer, type, ZLIB_GZIP_INFLATING); 58113498266Sopenharmony_ci#endif 58213498266Sopenharmony_ci} 58313498266Sopenharmony_ci 58413498266Sopenharmony_cistatic void gzip_do_close(struct Curl_easy *data, 58513498266Sopenharmony_ci struct Curl_cwriter *writer) 58613498266Sopenharmony_ci{ 58713498266Sopenharmony_ci struct zlib_writer *zp = (struct zlib_writer *) writer; 58813498266Sopenharmony_ci z_stream *z = &zp->z; /* zlib state structure */ 58913498266Sopenharmony_ci 59013498266Sopenharmony_ci exit_zlib(data, z, &zp->zlib_init, CURLE_OK); 59113498266Sopenharmony_ci} 59213498266Sopenharmony_ci 59313498266Sopenharmony_cistatic const struct Curl_cwtype gzip_encoding = { 59413498266Sopenharmony_ci "gzip", 59513498266Sopenharmony_ci "x-gzip", 59613498266Sopenharmony_ci gzip_do_init, 59713498266Sopenharmony_ci gzip_do_write, 59813498266Sopenharmony_ci gzip_do_close, 59913498266Sopenharmony_ci sizeof(struct zlib_writer) 60013498266Sopenharmony_ci}; 60113498266Sopenharmony_ci 60213498266Sopenharmony_ci#endif /* HAVE_LIBZ */ 60313498266Sopenharmony_ci 60413498266Sopenharmony_ci 60513498266Sopenharmony_ci#ifdef HAVE_BROTLI 60613498266Sopenharmony_ci/* Brotli writer. */ 60713498266Sopenharmony_cistruct brotli_writer { 60813498266Sopenharmony_ci struct Curl_cwriter super; 60913498266Sopenharmony_ci BrotliDecoderState *br; /* State structure for brotli. */ 61013498266Sopenharmony_ci}; 61113498266Sopenharmony_ci 61213498266Sopenharmony_cistatic CURLcode brotli_map_error(BrotliDecoderErrorCode be) 61313498266Sopenharmony_ci{ 61413498266Sopenharmony_ci switch(be) { 61513498266Sopenharmony_ci case BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_NIBBLE: 61613498266Sopenharmony_ci case BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_META_NIBBLE: 61713498266Sopenharmony_ci case BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_ALPHABET: 61813498266Sopenharmony_ci case BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_SAME: 61913498266Sopenharmony_ci case BROTLI_DECODER_ERROR_FORMAT_CL_SPACE: 62013498266Sopenharmony_ci case BROTLI_DECODER_ERROR_FORMAT_HUFFMAN_SPACE: 62113498266Sopenharmony_ci case BROTLI_DECODER_ERROR_FORMAT_CONTEXT_MAP_REPEAT: 62213498266Sopenharmony_ci case BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_1: 62313498266Sopenharmony_ci case BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_2: 62413498266Sopenharmony_ci case BROTLI_DECODER_ERROR_FORMAT_TRANSFORM: 62513498266Sopenharmony_ci case BROTLI_DECODER_ERROR_FORMAT_DICTIONARY: 62613498266Sopenharmony_ci case BROTLI_DECODER_ERROR_FORMAT_WINDOW_BITS: 62713498266Sopenharmony_ci case BROTLI_DECODER_ERROR_FORMAT_PADDING_1: 62813498266Sopenharmony_ci case BROTLI_DECODER_ERROR_FORMAT_PADDING_2: 62913498266Sopenharmony_ci#ifdef BROTLI_DECODER_ERROR_COMPOUND_DICTIONARY 63013498266Sopenharmony_ci case BROTLI_DECODER_ERROR_COMPOUND_DICTIONARY: 63113498266Sopenharmony_ci#endif 63213498266Sopenharmony_ci#ifdef BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET 63313498266Sopenharmony_ci case BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET: 63413498266Sopenharmony_ci#endif 63513498266Sopenharmony_ci case BROTLI_DECODER_ERROR_INVALID_ARGUMENTS: 63613498266Sopenharmony_ci return CURLE_BAD_CONTENT_ENCODING; 63713498266Sopenharmony_ci case BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MODES: 63813498266Sopenharmony_ci case BROTLI_DECODER_ERROR_ALLOC_TREE_GROUPS: 63913498266Sopenharmony_ci case BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MAP: 64013498266Sopenharmony_ci case BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_1: 64113498266Sopenharmony_ci case BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_2: 64213498266Sopenharmony_ci case BROTLI_DECODER_ERROR_ALLOC_BLOCK_TYPE_TREES: 64313498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 64413498266Sopenharmony_ci default: 64513498266Sopenharmony_ci break; 64613498266Sopenharmony_ci } 64713498266Sopenharmony_ci return CURLE_WRITE_ERROR; 64813498266Sopenharmony_ci} 64913498266Sopenharmony_ci 65013498266Sopenharmony_cistatic CURLcode brotli_do_init(struct Curl_easy *data, 65113498266Sopenharmony_ci struct Curl_cwriter *writer) 65213498266Sopenharmony_ci{ 65313498266Sopenharmony_ci struct brotli_writer *bp = (struct brotli_writer *) writer; 65413498266Sopenharmony_ci (void) data; 65513498266Sopenharmony_ci 65613498266Sopenharmony_ci bp->br = BrotliDecoderCreateInstance(NULL, NULL, NULL); 65713498266Sopenharmony_ci return bp->br? CURLE_OK: CURLE_OUT_OF_MEMORY; 65813498266Sopenharmony_ci} 65913498266Sopenharmony_ci 66013498266Sopenharmony_cistatic CURLcode brotli_do_write(struct Curl_easy *data, 66113498266Sopenharmony_ci struct Curl_cwriter *writer, int type, 66213498266Sopenharmony_ci const char *buf, size_t nbytes) 66313498266Sopenharmony_ci{ 66413498266Sopenharmony_ci struct brotli_writer *bp = (struct brotli_writer *) writer; 66513498266Sopenharmony_ci const uint8_t *src = (const uint8_t *) buf; 66613498266Sopenharmony_ci char *decomp; 66713498266Sopenharmony_ci uint8_t *dst; 66813498266Sopenharmony_ci size_t dstleft; 66913498266Sopenharmony_ci CURLcode result = CURLE_OK; 67013498266Sopenharmony_ci BrotliDecoderResult r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT; 67113498266Sopenharmony_ci 67213498266Sopenharmony_ci if(!(type & CLIENTWRITE_BODY)) 67313498266Sopenharmony_ci return Curl_cwriter_write(data, writer->next, type, buf, nbytes); 67413498266Sopenharmony_ci 67513498266Sopenharmony_ci if(!bp->br) 67613498266Sopenharmony_ci return CURLE_WRITE_ERROR; /* Stream already ended. */ 67713498266Sopenharmony_ci 67813498266Sopenharmony_ci decomp = malloc(DSIZ); 67913498266Sopenharmony_ci if(!decomp) 68013498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 68113498266Sopenharmony_ci 68213498266Sopenharmony_ci while((nbytes || r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) && 68313498266Sopenharmony_ci result == CURLE_OK) { 68413498266Sopenharmony_ci dst = (uint8_t *) decomp; 68513498266Sopenharmony_ci dstleft = DSIZ; 68613498266Sopenharmony_ci r = BrotliDecoderDecompressStream(bp->br, 68713498266Sopenharmony_ci &nbytes, &src, &dstleft, &dst, NULL); 68813498266Sopenharmony_ci result = Curl_cwriter_write(data, writer->next, type, 68913498266Sopenharmony_ci decomp, DSIZ - dstleft); 69013498266Sopenharmony_ci if(result) 69113498266Sopenharmony_ci break; 69213498266Sopenharmony_ci switch(r) { 69313498266Sopenharmony_ci case BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT: 69413498266Sopenharmony_ci case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT: 69513498266Sopenharmony_ci break; 69613498266Sopenharmony_ci case BROTLI_DECODER_RESULT_SUCCESS: 69713498266Sopenharmony_ci BrotliDecoderDestroyInstance(bp->br); 69813498266Sopenharmony_ci bp->br = NULL; 69913498266Sopenharmony_ci if(nbytes) 70013498266Sopenharmony_ci result = CURLE_WRITE_ERROR; 70113498266Sopenharmony_ci break; 70213498266Sopenharmony_ci default: 70313498266Sopenharmony_ci result = brotli_map_error(BrotliDecoderGetErrorCode(bp->br)); 70413498266Sopenharmony_ci break; 70513498266Sopenharmony_ci } 70613498266Sopenharmony_ci } 70713498266Sopenharmony_ci free(decomp); 70813498266Sopenharmony_ci return result; 70913498266Sopenharmony_ci} 71013498266Sopenharmony_ci 71113498266Sopenharmony_cistatic void brotli_do_close(struct Curl_easy *data, 71213498266Sopenharmony_ci struct Curl_cwriter *writer) 71313498266Sopenharmony_ci{ 71413498266Sopenharmony_ci struct brotli_writer *bp = (struct brotli_writer *) writer; 71513498266Sopenharmony_ci 71613498266Sopenharmony_ci (void) data; 71713498266Sopenharmony_ci 71813498266Sopenharmony_ci if(bp->br) { 71913498266Sopenharmony_ci BrotliDecoderDestroyInstance(bp->br); 72013498266Sopenharmony_ci bp->br = NULL; 72113498266Sopenharmony_ci } 72213498266Sopenharmony_ci} 72313498266Sopenharmony_ci 72413498266Sopenharmony_cistatic const struct Curl_cwtype brotli_encoding = { 72513498266Sopenharmony_ci "br", 72613498266Sopenharmony_ci NULL, 72713498266Sopenharmony_ci brotli_do_init, 72813498266Sopenharmony_ci brotli_do_write, 72913498266Sopenharmony_ci brotli_do_close, 73013498266Sopenharmony_ci sizeof(struct brotli_writer) 73113498266Sopenharmony_ci}; 73213498266Sopenharmony_ci#endif 73313498266Sopenharmony_ci 73413498266Sopenharmony_ci 73513498266Sopenharmony_ci#ifdef HAVE_ZSTD 73613498266Sopenharmony_ci/* Zstd writer. */ 73713498266Sopenharmony_cistruct zstd_writer { 73813498266Sopenharmony_ci struct Curl_cwriter super; 73913498266Sopenharmony_ci ZSTD_DStream *zds; /* State structure for zstd. */ 74013498266Sopenharmony_ci void *decomp; 74113498266Sopenharmony_ci}; 74213498266Sopenharmony_ci 74313498266Sopenharmony_cistatic CURLcode zstd_do_init(struct Curl_easy *data, 74413498266Sopenharmony_ci struct Curl_cwriter *writer) 74513498266Sopenharmony_ci{ 74613498266Sopenharmony_ci struct zstd_writer *zp = (struct zstd_writer *) writer; 74713498266Sopenharmony_ci 74813498266Sopenharmony_ci (void)data; 74913498266Sopenharmony_ci 75013498266Sopenharmony_ci zp->zds = ZSTD_createDStream(); 75113498266Sopenharmony_ci zp->decomp = NULL; 75213498266Sopenharmony_ci return zp->zds ? CURLE_OK : CURLE_OUT_OF_MEMORY; 75313498266Sopenharmony_ci} 75413498266Sopenharmony_ci 75513498266Sopenharmony_cistatic CURLcode zstd_do_write(struct Curl_easy *data, 75613498266Sopenharmony_ci struct Curl_cwriter *writer, int type, 75713498266Sopenharmony_ci const char *buf, size_t nbytes) 75813498266Sopenharmony_ci{ 75913498266Sopenharmony_ci CURLcode result = CURLE_OK; 76013498266Sopenharmony_ci struct zstd_writer *zp = (struct zstd_writer *) writer; 76113498266Sopenharmony_ci ZSTD_inBuffer in; 76213498266Sopenharmony_ci ZSTD_outBuffer out; 76313498266Sopenharmony_ci size_t errorCode; 76413498266Sopenharmony_ci 76513498266Sopenharmony_ci if(!(type & CLIENTWRITE_BODY)) 76613498266Sopenharmony_ci return Curl_cwriter_write(data, writer->next, type, buf, nbytes); 76713498266Sopenharmony_ci 76813498266Sopenharmony_ci if(!zp->decomp) { 76913498266Sopenharmony_ci zp->decomp = malloc(DSIZ); 77013498266Sopenharmony_ci if(!zp->decomp) 77113498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 77213498266Sopenharmony_ci } 77313498266Sopenharmony_ci in.pos = 0; 77413498266Sopenharmony_ci in.src = buf; 77513498266Sopenharmony_ci in.size = nbytes; 77613498266Sopenharmony_ci 77713498266Sopenharmony_ci for(;;) { 77813498266Sopenharmony_ci out.pos = 0; 77913498266Sopenharmony_ci out.dst = zp->decomp; 78013498266Sopenharmony_ci out.size = DSIZ; 78113498266Sopenharmony_ci 78213498266Sopenharmony_ci errorCode = ZSTD_decompressStream(zp->zds, &out, &in); 78313498266Sopenharmony_ci if(ZSTD_isError(errorCode)) { 78413498266Sopenharmony_ci return CURLE_BAD_CONTENT_ENCODING; 78513498266Sopenharmony_ci } 78613498266Sopenharmony_ci if(out.pos > 0) { 78713498266Sopenharmony_ci result = Curl_cwriter_write(data, writer->next, type, 78813498266Sopenharmony_ci zp->decomp, out.pos); 78913498266Sopenharmony_ci if(result) 79013498266Sopenharmony_ci break; 79113498266Sopenharmony_ci } 79213498266Sopenharmony_ci if((in.pos == nbytes) && (out.pos < out.size)) 79313498266Sopenharmony_ci break; 79413498266Sopenharmony_ci } 79513498266Sopenharmony_ci 79613498266Sopenharmony_ci return result; 79713498266Sopenharmony_ci} 79813498266Sopenharmony_ci 79913498266Sopenharmony_cistatic void zstd_do_close(struct Curl_easy *data, 80013498266Sopenharmony_ci struct Curl_cwriter *writer) 80113498266Sopenharmony_ci{ 80213498266Sopenharmony_ci struct zstd_writer *zp = (struct zstd_writer *) writer; 80313498266Sopenharmony_ci 80413498266Sopenharmony_ci (void)data; 80513498266Sopenharmony_ci 80613498266Sopenharmony_ci if(zp->decomp) { 80713498266Sopenharmony_ci free(zp->decomp); 80813498266Sopenharmony_ci zp->decomp = NULL; 80913498266Sopenharmony_ci } 81013498266Sopenharmony_ci if(zp->zds) { 81113498266Sopenharmony_ci ZSTD_freeDStream(zp->zds); 81213498266Sopenharmony_ci zp->zds = NULL; 81313498266Sopenharmony_ci } 81413498266Sopenharmony_ci} 81513498266Sopenharmony_ci 81613498266Sopenharmony_cistatic const struct Curl_cwtype zstd_encoding = { 81713498266Sopenharmony_ci "zstd", 81813498266Sopenharmony_ci NULL, 81913498266Sopenharmony_ci zstd_do_init, 82013498266Sopenharmony_ci zstd_do_write, 82113498266Sopenharmony_ci zstd_do_close, 82213498266Sopenharmony_ci sizeof(struct zstd_writer) 82313498266Sopenharmony_ci}; 82413498266Sopenharmony_ci#endif 82513498266Sopenharmony_ci 82613498266Sopenharmony_ci 82713498266Sopenharmony_ci/* Identity handler. */ 82813498266Sopenharmony_cistatic const struct Curl_cwtype identity_encoding = { 82913498266Sopenharmony_ci "identity", 83013498266Sopenharmony_ci "none", 83113498266Sopenharmony_ci Curl_cwriter_def_init, 83213498266Sopenharmony_ci Curl_cwriter_def_write, 83313498266Sopenharmony_ci Curl_cwriter_def_close, 83413498266Sopenharmony_ci sizeof(struct Curl_cwriter) 83513498266Sopenharmony_ci}; 83613498266Sopenharmony_ci 83713498266Sopenharmony_ci 83813498266Sopenharmony_ci/* supported general content decoders. */ 83913498266Sopenharmony_cistatic const struct Curl_cwtype * const general_unencoders[] = { 84013498266Sopenharmony_ci &identity_encoding, 84113498266Sopenharmony_ci#ifdef HAVE_LIBZ 84213498266Sopenharmony_ci &deflate_encoding, 84313498266Sopenharmony_ci &gzip_encoding, 84413498266Sopenharmony_ci#endif 84513498266Sopenharmony_ci#ifdef HAVE_BROTLI 84613498266Sopenharmony_ci &brotli_encoding, 84713498266Sopenharmony_ci#endif 84813498266Sopenharmony_ci#ifdef HAVE_ZSTD 84913498266Sopenharmony_ci &zstd_encoding, 85013498266Sopenharmony_ci#endif 85113498266Sopenharmony_ci NULL 85213498266Sopenharmony_ci}; 85313498266Sopenharmony_ci 85413498266Sopenharmony_ci/* supported content decoders only for transfer encodings */ 85513498266Sopenharmony_cistatic const struct Curl_cwtype * const transfer_unencoders[] = { 85613498266Sopenharmony_ci#ifndef CURL_DISABLE_HTTP 85713498266Sopenharmony_ci &Curl_httpchunk_unencoder, 85813498266Sopenharmony_ci#endif 85913498266Sopenharmony_ci NULL 86013498266Sopenharmony_ci}; 86113498266Sopenharmony_ci 86213498266Sopenharmony_ci/* Provide a list of comma-separated names of supported encodings. 86313498266Sopenharmony_ci*/ 86413498266Sopenharmony_civoid Curl_all_content_encodings(char *buf, size_t blen) 86513498266Sopenharmony_ci{ 86613498266Sopenharmony_ci size_t len = 0; 86713498266Sopenharmony_ci const struct Curl_cwtype * const *cep; 86813498266Sopenharmony_ci const struct Curl_cwtype *ce; 86913498266Sopenharmony_ci 87013498266Sopenharmony_ci DEBUGASSERT(buf); 87113498266Sopenharmony_ci DEBUGASSERT(blen); 87213498266Sopenharmony_ci buf[0] = 0; 87313498266Sopenharmony_ci 87413498266Sopenharmony_ci for(cep = general_unencoders; *cep; cep++) { 87513498266Sopenharmony_ci ce = *cep; 87613498266Sopenharmony_ci if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT)) 87713498266Sopenharmony_ci len += strlen(ce->name) + 2; 87813498266Sopenharmony_ci } 87913498266Sopenharmony_ci 88013498266Sopenharmony_ci if(!len) { 88113498266Sopenharmony_ci if(blen >= sizeof(CONTENT_ENCODING_DEFAULT)) 88213498266Sopenharmony_ci strcpy(buf, CONTENT_ENCODING_DEFAULT); 88313498266Sopenharmony_ci } 88413498266Sopenharmony_ci else if(blen > len) { 88513498266Sopenharmony_ci char *p = buf; 88613498266Sopenharmony_ci for(cep = general_unencoders; *cep; cep++) { 88713498266Sopenharmony_ci ce = *cep; 88813498266Sopenharmony_ci if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT)) { 88913498266Sopenharmony_ci strcpy(p, ce->name); 89013498266Sopenharmony_ci p += strlen(p); 89113498266Sopenharmony_ci *p++ = ','; 89213498266Sopenharmony_ci *p++ = ' '; 89313498266Sopenharmony_ci } 89413498266Sopenharmony_ci } 89513498266Sopenharmony_ci p[-2] = '\0'; 89613498266Sopenharmony_ci } 89713498266Sopenharmony_ci} 89813498266Sopenharmony_ci 89913498266Sopenharmony_ci/* Deferred error dummy writer. */ 90013498266Sopenharmony_cistatic CURLcode error_do_init(struct Curl_easy *data, 90113498266Sopenharmony_ci struct Curl_cwriter *writer) 90213498266Sopenharmony_ci{ 90313498266Sopenharmony_ci (void)data; 90413498266Sopenharmony_ci (void)writer; 90513498266Sopenharmony_ci return CURLE_OK; 90613498266Sopenharmony_ci} 90713498266Sopenharmony_ci 90813498266Sopenharmony_cistatic CURLcode error_do_write(struct Curl_easy *data, 90913498266Sopenharmony_ci struct Curl_cwriter *writer, int type, 91013498266Sopenharmony_ci const char *buf, size_t nbytes) 91113498266Sopenharmony_ci{ 91213498266Sopenharmony_ci char all[256]; 91313498266Sopenharmony_ci (void)Curl_all_content_encodings(all, sizeof(all)); 91413498266Sopenharmony_ci 91513498266Sopenharmony_ci (void) writer; 91613498266Sopenharmony_ci (void) buf; 91713498266Sopenharmony_ci (void) nbytes; 91813498266Sopenharmony_ci 91913498266Sopenharmony_ci if(!(type & CLIENTWRITE_BODY)) 92013498266Sopenharmony_ci return Curl_cwriter_write(data, writer->next, type, buf, nbytes); 92113498266Sopenharmony_ci 92213498266Sopenharmony_ci failf(data, "Unrecognized content encoding type. " 92313498266Sopenharmony_ci "libcurl understands %s content encodings.", all); 92413498266Sopenharmony_ci return CURLE_BAD_CONTENT_ENCODING; 92513498266Sopenharmony_ci} 92613498266Sopenharmony_ci 92713498266Sopenharmony_cistatic void error_do_close(struct Curl_easy *data, 92813498266Sopenharmony_ci struct Curl_cwriter *writer) 92913498266Sopenharmony_ci{ 93013498266Sopenharmony_ci (void) data; 93113498266Sopenharmony_ci (void) writer; 93213498266Sopenharmony_ci} 93313498266Sopenharmony_ci 93413498266Sopenharmony_cistatic const struct Curl_cwtype error_writer = { 93513498266Sopenharmony_ci "ce-error", 93613498266Sopenharmony_ci NULL, 93713498266Sopenharmony_ci error_do_init, 93813498266Sopenharmony_ci error_do_write, 93913498266Sopenharmony_ci error_do_close, 94013498266Sopenharmony_ci sizeof(struct Curl_cwriter) 94113498266Sopenharmony_ci}; 94213498266Sopenharmony_ci 94313498266Sopenharmony_ci/* Find the content encoding by name. */ 94413498266Sopenharmony_cistatic const struct Curl_cwtype *find_unencode_writer(const char *name, 94513498266Sopenharmony_ci size_t len, 94613498266Sopenharmony_ci Curl_cwriter_phase phase) 94713498266Sopenharmony_ci{ 94813498266Sopenharmony_ci const struct Curl_cwtype * const *cep; 94913498266Sopenharmony_ci 95013498266Sopenharmony_ci if(phase == CURL_CW_TRANSFER_DECODE) { 95113498266Sopenharmony_ci for(cep = transfer_unencoders; *cep; cep++) { 95213498266Sopenharmony_ci const struct Curl_cwtype *ce = *cep; 95313498266Sopenharmony_ci if((strncasecompare(name, ce->name, len) && !ce->name[len]) || 95413498266Sopenharmony_ci (ce->alias && strncasecompare(name, ce->alias, len) 95513498266Sopenharmony_ci && !ce->alias[len])) 95613498266Sopenharmony_ci return ce; 95713498266Sopenharmony_ci } 95813498266Sopenharmony_ci } 95913498266Sopenharmony_ci /* look among the general decoders */ 96013498266Sopenharmony_ci for(cep = general_unencoders; *cep; cep++) { 96113498266Sopenharmony_ci const struct Curl_cwtype *ce = *cep; 96213498266Sopenharmony_ci if((strncasecompare(name, ce->name, len) && !ce->name[len]) || 96313498266Sopenharmony_ci (ce->alias && strncasecompare(name, ce->alias, len) && !ce->alias[len])) 96413498266Sopenharmony_ci return ce; 96513498266Sopenharmony_ci } 96613498266Sopenharmony_ci return NULL; 96713498266Sopenharmony_ci} 96813498266Sopenharmony_ci 96913498266Sopenharmony_ci/* Set-up the unencoding stack from the Content-Encoding header value. 97013498266Sopenharmony_ci * See RFC 7231 section 3.1.2.2. */ 97113498266Sopenharmony_ciCURLcode Curl_build_unencoding_stack(struct Curl_easy *data, 97213498266Sopenharmony_ci const char *enclist, int is_transfer) 97313498266Sopenharmony_ci{ 97413498266Sopenharmony_ci Curl_cwriter_phase phase = is_transfer? 97513498266Sopenharmony_ci CURL_CW_TRANSFER_DECODE:CURL_CW_CONTENT_DECODE; 97613498266Sopenharmony_ci CURLcode result; 97713498266Sopenharmony_ci 97813498266Sopenharmony_ci do { 97913498266Sopenharmony_ci const char *name; 98013498266Sopenharmony_ci size_t namelen; 98113498266Sopenharmony_ci 98213498266Sopenharmony_ci /* Parse a single encoding name. */ 98313498266Sopenharmony_ci while(ISBLANK(*enclist) || *enclist == ',') 98413498266Sopenharmony_ci enclist++; 98513498266Sopenharmony_ci 98613498266Sopenharmony_ci name = enclist; 98713498266Sopenharmony_ci 98813498266Sopenharmony_ci for(namelen = 0; *enclist && *enclist != ','; enclist++) 98913498266Sopenharmony_ci if(!ISSPACE(*enclist)) 99013498266Sopenharmony_ci namelen = enclist - name + 1; 99113498266Sopenharmony_ci 99213498266Sopenharmony_ci if(namelen) { 99313498266Sopenharmony_ci const struct Curl_cwtype *cwt; 99413498266Sopenharmony_ci struct Curl_cwriter *writer; 99513498266Sopenharmony_ci 99613498266Sopenharmony_ci /* if we skip the decoding in this phase, do not look further. 99713498266Sopenharmony_ci * Exception is "chunked" transfer-encoding which always must happen */ 99813498266Sopenharmony_ci if((is_transfer && !data->set.http_transfer_encoding && 99913498266Sopenharmony_ci (namelen != 7 || !strncasecompare(name, "chunked", 7))) || 100013498266Sopenharmony_ci (!is_transfer && data->set.http_ce_skip)) { 100113498266Sopenharmony_ci /* not requested, ignore */ 100213498266Sopenharmony_ci return CURLE_OK; 100313498266Sopenharmony_ci } 100413498266Sopenharmony_ci 100513498266Sopenharmony_ci if(Curl_cwriter_count(data, phase) + 1 >= MAX_ENCODE_STACK) { 100613498266Sopenharmony_ci failf(data, "Reject response due to more than %u content encodings", 100713498266Sopenharmony_ci MAX_ENCODE_STACK); 100813498266Sopenharmony_ci return CURLE_BAD_CONTENT_ENCODING; 100913498266Sopenharmony_ci } 101013498266Sopenharmony_ci 101113498266Sopenharmony_ci cwt = find_unencode_writer(name, namelen, phase); 101213498266Sopenharmony_ci if(!cwt) { 101313498266Sopenharmony_ci failf(data, "ignore this error when the content-encoding is not recognized."); 101413498266Sopenharmony_ci return CURLE_OK; 101513498266Sopenharmony_ci } 101613498266Sopenharmony_ci 101713498266Sopenharmony_ci result = Curl_cwriter_create(&writer, data, cwt, phase); 101813498266Sopenharmony_ci if(result) 101913498266Sopenharmony_ci return result; 102013498266Sopenharmony_ci 102113498266Sopenharmony_ci result = Curl_cwriter_add(data, writer); 102213498266Sopenharmony_ci if(result) { 102313498266Sopenharmony_ci Curl_cwriter_free(data, writer); 102413498266Sopenharmony_ci return result; 102513498266Sopenharmony_ci } 102613498266Sopenharmony_ci } 102713498266Sopenharmony_ci } while(*enclist); 102813498266Sopenharmony_ci 102913498266Sopenharmony_ci return CURLE_OK; 103013498266Sopenharmony_ci} 103113498266Sopenharmony_ci 103213498266Sopenharmony_ci#else 103313498266Sopenharmony_ci/* Stubs for builds without HTTP. */ 103413498266Sopenharmony_ciCURLcode Curl_build_unencoding_stack(struct Curl_easy *data, 103513498266Sopenharmony_ci const char *enclist, int is_transfer) 103613498266Sopenharmony_ci{ 103713498266Sopenharmony_ci (void) data; 103813498266Sopenharmony_ci (void) enclist; 103913498266Sopenharmony_ci (void) is_transfer; 104013498266Sopenharmony_ci return CURLE_NOT_BUILT_IN; 104113498266Sopenharmony_ci} 104213498266Sopenharmony_ci 104313498266Sopenharmony_civoid Curl_all_content_encodings(char *buf, size_t blen) 104413498266Sopenharmony_ci{ 104513498266Sopenharmony_ci DEBUGASSERT(buf); 104613498266Sopenharmony_ci DEBUGASSERT(blen); 104713498266Sopenharmony_ci if(blen < sizeof(CONTENT_ENCODING_DEFAULT)) 104813498266Sopenharmony_ci buf[0] = 0; 104913498266Sopenharmony_ci else 105013498266Sopenharmony_ci strcpy(buf, CONTENT_ENCODING_DEFAULT); 105113498266Sopenharmony_ci} 105213498266Sopenharmony_ci 105313498266Sopenharmony_ci 105413498266Sopenharmony_ci#endif /* CURL_DISABLE_HTTP */ 1055