Commit 85db1540 authored by jan.koester's avatar jan.koester
Browse files

zlib support

parent b4d53888
Loading
Loading
Loading
Loading
+1 −0
Changes for CMakeLists.txt: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ find_package(smtpclient REQUIRED)
find_package(libuuidp REQUIRED)
find_package(tinyxml2 REQUIRED)
find_package(paritypp REQUIRED)
find_package(ZLIB REQUIRED)

if(APPLE OR ${CMAKE_HOST_SYSTEM_NAME} MATCHES "FreeBSD")
    find_package(date REQUIRED)
+3 −0
Changes for editor/CMakeLists.txt: 3 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ if(NOT TARGET blogidev)
    find_package(smtpclient REQUIRED)
    find_package(Brotli REQUIRED)
    find_package(paritypp REQUIRED)
    find_package(ZLIB REQUIRED)
    add_definitions(-D${CMAKE_HOST_SYSTEM_NAME} -DWIN32_LEAN_AND_MEAN)
endif()

@@ -57,6 +58,7 @@ if(TARGET blogidev)
        blogidev
        json-c::json-c
        tinyxml2::tinyxml2
        ZLIB::ZLIB
    )
    target_sources(blogi-editor PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/../src/plugin.cpp
@@ -75,6 +77,7 @@ else()
        uuidp::uuidp
        authdb::client
        Brotli::Brotli
        ZLIB::ZLIB
        dl
    )
endif()
+50 −0
Changes for editor/src/htmlimport.cpp: 50 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -41,6 +41,8 @@
#include <netinet/in.h>
#include <arpa/inet.h>

#include <zlib.h>

using namespace blogi::htmlimport;

namespace {
@@ -123,6 +125,50 @@ bool isSafeExternalHost(const std::string &host, std::string &error)
    return safe;
}

// Best-effort gzip decompression for fetchExternalResource: plenty of real-
// world servers/CDNs return a gzip-compressed body regardless of what (or
// whether) the client's Accept-Encoding says -- confirmed against a live
// site that ignored an explicit "Accept-Encoding: identity" -- and
// libhttppp's HttpClient doesn't decompress bodies itself or expose the
// Content-Encoding header, so without this every gzipped page/stylesheet/
// script would be handed to the HTML/CSS parser as opaque binary. Detected
// purely by the gzip magic (1f 8b) for that reason. A no-op (returns true,
// @p data untouched) when the magic isn't present. Bounded by @p maxBytes
// on the DECOMPRESSED size to defend against a decompression-bomb response
// (small on the wire, huge once inflated) -- returns false, leaving @p data
// untouched, on any zlib error or if that cap is hit.
bool gunzipIfNeeded(std::vector<std::uint8_t> &data, size_t maxBytes)
{
    if (data.size() < 2 || data[0] != 0x1f || data[1] != 0x8b) return true;

    z_stream strm{};
    // 16 + MAX_WBITS: gzip-header format specifically (plain MAX_WBITS
    // expects a zlib-wrapped stream instead, which the gzip magic above
    // rules out).
    if (inflateInit2(&strm, 16 + MAX_WBITS) != Z_OK) return false;

    std::vector<std::uint8_t> out;
    std::vector<std::uint8_t> chunk(65536);
    strm.next_in = data.data();
    strm.avail_in = static_cast<uInt>(data.size());

    int rc;
    do {
        strm.next_out = chunk.data();
        strm.avail_out = static_cast<uInt>(chunk.size());
        rc = inflate(&strm, Z_NO_FLUSH);
        if (rc != Z_OK && rc != Z_STREAM_END) { inflateEnd(&strm); return false; }

        size_t produced = chunk.size() - strm.avail_out;
        if (out.size() + produced > maxBytes) { inflateEnd(&strm); return false; }
        out.insert(out.end(), chunk.begin(), chunk.begin() + produced);
    } while (rc != Z_STREAM_END);

    inflateEnd(&strm);
    data.swap(out);
    return true;
}

} // namespace

bool blogi::htmlimport::fetchExternalResource(const std::string &url,
@@ -184,6 +230,10 @@ bool blogi::htmlimport::fetchExternalResource(const std::string &url,
        }

        out.assign(resp.begin(), resp.end());
        if (!gunzipIfNeeded(out, maxBytes)) {
            error = "failed to decompress gzip response";
            return false;
        }
        return true;
    } catch (const std::exception &e) {
        error = e.what();
+4 −0
Changes for editor/src/htmlimport.h: 4 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -50,6 +50,10 @@ namespace htmlimport {
     *  streaming more than @p maxBytes before the check runs).
     *  @p contentTypePrefix, if non-empty, rejects responses whose
     *  Content-Type doesn't start with it (a missing header is tolerated).
     *  A gzip-magic-prefixed body is transparently decompressed (many real
     *  servers/CDNs send one regardless of Accept-Encoding, and the result
     *  is re-checked against @p maxBytes to bound decompression-bomb
     *  responses) -- see gunzipIfNeeded in htmlimport.cpp.
     *  Returns false and sets @p error on any failure; never throws. */
    bool fetchExternalResource(const std::string &url,
                               size_t maxBytes,