]> source.dussan.org Git - rspamd.git/commitdiff
[Project] Css: Implement styles merging
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Mon, 29 Mar 2021 14:17:09 +0000 (15:17 +0100)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Mon, 29 Mar 2021 14:17:09 +0000 (15:17 +0100)
src/libserver/css/css.cxx
src/libserver/css/css.h
src/libserver/css/css_parser.cxx
src/libserver/css/css_parser.hxx
src/libserver/html.c
src/libserver/html.h

index 17ec165fcd0e72d84c3dfdf862a432a0ebb84d41..033ecdc22d5ac7e45c513a0082782d724394c589 100644 (file)
@@ -32,19 +32,26 @@ rspamd_css_dtor(void *p)
        delete style;
 }
 
-rspamd_css
+rspamd_css_ptr
 rspamd_css_parse_style(rspamd_mempool_t *pool, const guchar *begin, gsize len,
-                                               GError **err)
+                                          rspamd_css_ptr existing_style,
+                                          GError **err)
 {
-       auto parse_res = rspamd::css::parse_css(pool, {(const char* )begin, len});
+       auto parse_res = rspamd::css::parse_css(pool, {(const char* )begin, len},
+                       reinterpret_cast<rspamd::css::css_style_sheet*>(existing_style));
 
        if (parse_res.has_value()) {
                /*
                 * Detach style pointer from the unique_ptr as it will be managed by
                 * C memory pool
                 */
-               auto *detached_style = reinterpret_cast<rspamd_css>(parse_res.value().release());
-               rspamd_mempool_add_destructor(pool, rspamd_css_dtor, (void *)detached_style);
+               auto *detached_style = reinterpret_cast<rspamd_css_ptr>(parse_res.value().release());
+
+               /* We attach dtor merely if the existing style is not null */
+               if (!existing_style) {
+                       rspamd_mempool_add_destructor(pool, rspamd_css_dtor, (void *) detached_style);
+               }
+
                return detached_style;
        }
        else {
index 78f24a5494b4dd7283c1f1ca507f10b915b73c5c..1dabf00b83282c5d75aa9aa650cedb4d096b3ff9 100644 (file)
 #ifdef  __cplusplus
 extern "C" {
 #endif
-typedef void * rspamd_css;
+typedef void * rspamd_css_ptr;
 
-rspamd_css rspamd_css_parse_style (rspamd_mempool_t *pool,
-                                                                  const guchar *begin,
-                                                                  gsize len, GError **err);
+rspamd_css_ptr rspamd_css_parse_style (rspamd_mempool_t *pool,
+                                                                          const guchar *begin,
+                                                                          gsize len,
+                                                                          rspamd_css_ptr existing_style,
+                                                                          GError **err);
 
 /*
  * Unescape css
index 104792693d1f607f5814cfef5dd2a44fc3b006fe..8d37a26a82b2d74b27f5bfc068838d768380e36b 100644 (file)
@@ -147,7 +147,17 @@ auto css_consumed_block::debug_str(void) -> std::string {
 class css_parser {
 public:
        css_parser(void) = delete; /* Require mempool to be set for logging */
-       explicit css_parser(rspamd_mempool_t *pool) : pool (pool) {}
+       explicit css_parser(rspamd_mempool_t *pool) : pool (pool) {
+               /* Int this case we need to remove style in case of parser error */
+               owned_style = true;
+       }
+
+       /*
+        * This constructor captures existing via unique_ptr, but it does not
+        * destruct it on errors (we assume that it is owned somewhere else)
+        */
+       explicit css_parser(css_style_sheet *existing, rspamd_mempool_t *pool) :
+                       style_object(existing), pool(pool) {}
 
        std::unique_ptr<css_consumed_block> consume_css_blocks(const std::string_view &sv);
        bool consume_input(const std::string_view &sv);
@@ -163,6 +173,13 @@ public:
        /* Helper parser methods */
        bool need_unescape(const std::string_view &sv);
 
+       ~css_parser() {
+               if (!owned_style && style_object) {
+                       /* Avoid double free */
+                       (void)style_object.release();
+               }
+       }
+
 private:
        std::unique_ptr<css_style_sheet> style_object;
        std::unique_ptr<css_tokeniser> tokeniser;
@@ -173,6 +190,7 @@ private:
        int rec_level = 0;
        const int max_rec = 20;
        bool eof = false;
+       bool owned_style = false;
 
        /* Consumers */
        auto component_value_consumer(std::unique_ptr<css_consumed_block> &top) -> bool;
@@ -560,7 +578,9 @@ bool css_parser::consume_input(const std::string_view &sv)
                return false;
        }
 
-       style_object = std::make_unique<css_style_sheet>(pool);
+       if (!style_object) {
+               style_object = std::make_unique<css_style_sheet>(pool);
+       }
 
        for (auto &&rule : rules) {
                /*
@@ -686,10 +706,11 @@ get_selectors_parser_functor(rspamd_mempool_t *pool,
 /*
  * Wrapper for the parser
  */
-auto parse_css(rspamd_mempool_t *pool, const std::string_view &st) ->
-               tl::expected<std::unique_ptr<css_style_sheet>, css_parse_error>
+auto parse_css(rspamd_mempool_t *pool, const std::string_view &st,
+                                                 css_style_sheet *other)
+       -> tl::expected<std::unique_ptr<css_style_sheet>, css_parse_error>
 {
-       css_parser parser(pool);
+       css_parser parser(other, pool);
        std::string_view processed_input;
 
        if (parser.need_unescape(st)) {
@@ -712,7 +733,7 @@ auto parse_css(rspamd_mempool_t *pool, const std::string_view &st) ->
        }
 
        return tl::make_unexpected(css_parse_error{css_parse_error_type::PARSE_ERROR_INVALID_SYNTAX,
-                                                                                       "cannot parse input"});
+                                                                                          "cannot parse input"});
 }
 
 TEST_SUITE("css parser") {
@@ -752,9 +773,19 @@ TEST_SUITE("css parser") {
                rspamd_mempool_t *pool = rspamd_mempool_new(rspamd_mempool_suggest_size(),
                                "css", 0);
                for (const auto &c : cases) {
-                       CHECK_UNARY(parse_css(pool, c));
+                       CHECK(parse_css(pool, c, nullptr).value().get() != nullptr);
+               }
+
+               /* We now merge all styles together */
+               css_style_sheet *merged = nullptr;
+               for (const auto &c : cases) {
+                       auto ret = parse_css(pool, c, merged);
+                       /* Avoid destruction as we are using this to interoperate with C */
+                       merged = ret->release();
                }
 
+               CHECK(merged != nullptr);
+
                rspamd_mempool_delete(pool);
        }
 }
index 4b5598fa97ac926035daded04b98db4d236902cd..af79abb68f423ab67805250be1e0549f105befdb 100644 (file)
@@ -185,8 +185,12 @@ extern const css_consumed_block css_parser_eof_block;
 using blocks_gen_functor = fu2::unique_function<const css_consumed_block &(void)>;
 
 class css_style_sheet;
-auto parse_css(rspamd_mempool_t *pool, const std::string_view &st) ->
-       tl::expected<std::unique_ptr<css_style_sheet>, css_parse_error>;
+/*
+ * Update the existing stylesheet with another stylesheet
+ */
+auto parse_css(rspamd_mempool_t *pool, const std::string_view &st,
+                                         css_style_sheet *other)
+       -> tl::expected<std::unique_ptr<css_style_sheet>, css_parse_error>;
 
 auto get_selectors_parser_functor(rspamd_mempool_t *pool,
                                                                  const std::string_view &st) -> blocks_gen_functor;
index b56f3ef320fc48616d960e1ae0faaa30cd85830e..8dc8a8dac1980244e2ef9614bd50f714f134b6ee 100644 (file)
@@ -3137,7 +3137,8 @@ rspamd_html_process_part_full (rspamd_mempool_t *pool,
 
                                if (allow_css) {
                                        GError *err = NULL;
-                                       (void)rspamd_css_parse_style (pool, p, end_style, &err);
+                                       hc->css_style = rspamd_css_parse_style (pool, p, end_style, hc->css_style,
+                                                       &err);
 
                                        if (err) {
                                                msg_info_pool ("cannot parse css: %e", err);
index f8a5e18e4e18cd555eadfa3053078f729e17577c..83a2a7ee7dc2183e6c1f9d133a850c0adbd9a665 100644 (file)
@@ -132,6 +132,7 @@ struct html_content {
        GPtrArray *images;
        GPtrArray *blocks;
        GByteArray *parsed;
+       void *css_style;
 };
 
 /*