1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
|
/*-
* Copyright 2021 Vsevolod Stakhov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "css_rule.hxx"
namespace rspamd::css {
static auto
allowed_property_value(const css_property &prop, const css_consumed_block &parser_block)
-> std::optional<css_value>
{
if (prop.is_color()) {
if (parser_block.is_token()) {
/* A single token */
const auto &tok = parser_block.get_token_or_empty();
if (tok.type == css_parser_token::token_type::hash_token) {
return css_value::maybe_color_from_hex(tok.get_string_or_default(""));
}
else if (tok.type == css_parser_token::token_type::ident_token) {
return css_value::maybe_color_from_string(tok.get_string_or_default(""));
}
}
else if (parser_block.is_function()) {
const auto &func = parser_block.get_function_or_invalid();
return css_value::maybe_color_from_function(func);
}
}
else if (prop.is_dimension()) {
if (parser_block.is_token()) {
/* A single token */
const auto &tok = parser_block.get_token_or_empty();
if (tok.type == css_parser_token::token_type::number_token) {
return css_value{tok.get_number_or_default(0)};
}
}
}
else if (prop.is_normal_number()) {
if (parser_block.is_token()) {
/* A single token */
const auto &tok = parser_block.get_token_or_empty();
if (tok.type == css_parser_token::token_type::number_token) {
return css_value{tok.get_normal_number_or_default(0)};
}
}
}
return std::nullopt;
}
auto process_declaration_tokens(rspamd_mempool_t *pool,
const blocks_gen_functor &next_block_functor)
-> declarations_vec
{
declarations_vec ret;
bool can_continue = true;
css_property cur_property{css_property_type::PROPERTY_NYI,
css_property_flag::FLAG_NORMAL};
static const css_property bad_property{css_property_type::PROPERTY_NYI,
css_property_flag::FLAG_NORMAL};
std::unique_ptr<css_rule> cur_rule;
enum {
parse_property,
parse_value,
ignore_value, /* For unknown properties */
} state = parse_property;
auto seen_not = false;
while (can_continue) {
const auto &next_tok = next_block_functor();
switch (next_tok.tag) {
case css_consumed_block::parser_tag_type::css_component:
/* Component can be a property or a compound list of values */
if (state == parse_property) {
cur_property = css_property::from_token(next_tok.get_token_or_empty())
.value_or(bad_property);
if (cur_property.type == css_property_type::PROPERTY_NYI) {
state = ignore_value;
/* Ignore everything till ; */
continue;
}
msg_debug_css("got css property: %s", cur_property.to_string());
/* We now expect colon block */
const auto &expect_colon_block = next_block_functor();
if (expect_colon_block.tag != css_consumed_block::parser_tag_type::css_component) {
state = ignore_value; /* Ignore up to the next rule */
}
else {
const auto &expect_colon_tok = expect_colon_block.get_token_or_empty();
if (expect_colon_tok.type != css_parser_token::token_type::colon_token) {
msg_debug_css("invalid rule, no colon after property");
state = ignore_value; /* Ignore up to the next rule */
}
else {
state = parse_value;
cur_rule = std::make_unique<css_rule>(cur_property);
}
}
}
else if (state == parse_value) {
/* Check semicolon */
if (next_tok.is_token()) {
const auto &parser_tok = next_tok.get_token_or_empty();
if (parser_tok.type == css_parser_token::token_type::semicolon_token) {
ret.push_back(std::move(cur_rule));
state = parse_property;
seen_not = false;
continue;
}
else if (parser_tok.type == css_parser_token::token_type::delim_token) {
if (parser_tok.get_string_or_default("") == "!") {
/* Probably something like !important */
seen_not = true;
}
}
else if (parser_tok.type == css_parser_token::token_type::ident_token) {
if (parser_tok.get_string_or_default("") == "important") {
if (seen_not) {
msg_debug_css("add !important flag to property %s",
cur_property.to_string());
cur_property.flag = css_property_flag::FLAG_NOT_IMPORTANT;
}
else {
msg_debug_css("add important flag to property %s",
cur_property.to_string());
cur_property.flag = css_property_flag::FLAG_IMPORTANT;
}
seen_not = false;
continue;
}
else {
seen_not = false;
}
}
}
auto maybe_value = allowed_property_value(cur_property, next_tok);
if (maybe_value) {
msg_debug_css("added value %s to the property %s",
maybe_value.value().debug_str().c_str(),
cur_property.to_string());
cur_rule->add_value(maybe_value.value());
}
}
else {
/* Ignore all till ; */
if (next_tok.is_token()) {
const auto &parser_tok = next_tok.get_token_or_empty();
if (parser_tok.type == css_parser_token::token_type::semicolon_token) {
state = parse_property;
}
}
}
break;
case css_consumed_block::parser_tag_type::css_function:
if (state == parse_value) {
auto maybe_value = allowed_property_value(cur_property, next_tok);
if (maybe_value) {
msg_debug_css("added value %s to the property %s",
maybe_value.value().debug_str().c_str(),
cur_property.to_string());
cur_rule->add_value(maybe_value.value());
}
}
break;
case css_consumed_block::parser_tag_type::css_eof_block:
if (state == parse_value) {
ret.push_back(std::move(cur_rule));
}
can_continue = false;
break;
default:
can_continue = false;
break;
}
}
return ret; /* copy elision */
}
void css_rule::add_value(std::unique_ptr<css_value> &&value)
{
values.emplace_back(std::forward<std::unique_ptr<css_value>>(value));
}
void css_rule::add_value(const css_value &value)
{
values.emplace_back(std::make_unique<css_value>(css_value{value}));
}
}
|