1 /* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
2 */
3
4 /* Extension encoded-character
5 * ---------------------------
6 *
7 * Authors: Stephan Bosch
8 * Specification: RFC 5228
9 * Implementation: full
10 * Status: testing
11 *
12 */
13
14 #include "lib.h"
15 #include "unichar.h"
16
17 #include "sieve-extensions.h"
18 #include "sieve-commands.h"
19 #include "sieve-validator.h"
20
21 #include <ctype.h>
22
23 /*
24 * Extension
25 */
26
27 static bool ext_encoded_character_validator_load
28 (const struct sieve_extension *ext, struct sieve_validator *valdtr);
29
30 const struct sieve_extension_def encoded_character_extension = {
31 .name = "encoded-character",
32 .validator_load = ext_encoded_character_validator_load,
33 };
34
35 /*
36 * Encoded string argument
37 */
38
39 bool arg_encoded_string_validate
40 (struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
41 struct sieve_command *context);
42
43 const struct sieve_argument_def encoded_string_argument = {
44 .identifier = "@encoded-string",
45 .validate = arg_encoded_string_validate
46 };
47
48 /* Parsing */
49
_skip_whitespace(const char ** in,const char * inend)50 static bool _skip_whitespace
51 (const char **in, const char *inend)
52 {
53 while ( *in < inend ) {
54 if ( **in == '\r' ) {
55 (*in)++;
56 if ( **in != '\n' )
57 return FALSE;
58 continue;
59 }
60
61 /* (Loose LF is non-standard) */
62 if ( **in != ' ' && **in != '\n' && **in != '\t' )
63 break;
64
65 (*in)++;
66 }
67
68 return TRUE;
69 }
70
_parse_hexint(const char ** in,const char * inend,int max_digits,unsigned int * result)71 static bool _parse_hexint
72 (const char **in, const char *inend, int max_digits, unsigned int *result)
73 {
74 int digit = 0;
75 *result = 0;
76
77 while ( *in < inend && (max_digits == 0 || digit < max_digits) ) {
78
79 if ( (**in) >= '0' && (**in) <= '9' )
80 *result = ((*result) << 4) + (**in) - ((unsigned int) '0');
81 else if ( (**in) >= 'a' && (**in) <= 'f' )
82 *result = ((*result) << 4) + (**in) - ((unsigned int) 'a') + 0x0a;
83 else if ( (**in) >= 'A' && (**in) <= 'F' )
84 *result = ((*result) << 4) + (**in) - ((unsigned int) 'A') + 0x0a;
85 else
86 return ( digit > 0 );
87
88 (*in)++;
89 digit++;
90 }
91
92 if ( digit == max_digits ) {
93 /* Hex digit _MUST_ end here */
94 if ( (**in >= '0' && **in <= '9') || (**in >= 'a' && **in <= 'f') ||
95 (**in >= 'A' && **in <= 'F') )
96 return FALSE;
97
98 return TRUE;
99 }
100
101 return ( digit > 0 );
102 }
103
_decode_hex(const char ** in,const char * inend,string_t * result)104 static bool _decode_hex
105 (const char **in, const char *inend, string_t *result)
106 {
107 int values = 0;
108
109 while ( *in < inend ) {
110 unsigned int hexpair;
111
112 if ( !_skip_whitespace(in, inend) ) return FALSE;
113
114 if ( !_parse_hexint(in, inend, 2, &hexpair) ) break;
115
116 str_append_c(result, (unsigned char) hexpair);
117 values++;
118 }
119
120 return ( values > 0 );
121 }
122
_decode_unicode(const char ** in,const char * inend,string_t * result,unsigned int * error_hex)123 static bool _decode_unicode
124 (const char **in, const char *inend, string_t *result,
125 unsigned int *error_hex)
126 {
127 int values = 0;
128 bool valid = TRUE;
129
130 while ( *in < inend ) {
131 unsigned int unicode_hex;
132
133 if ( !_skip_whitespace(in, inend) ) return FALSE;
134
135 if ( !_parse_hexint(in, inend, 0, &unicode_hex) ) break;
136
137 if ( uni_is_valid_ucs4((unichar_t) unicode_hex) )
138 uni_ucs4_to_utf8_c((unichar_t) unicode_hex, result);
139 else {
140 if ( valid ) *error_hex = unicode_hex;
141 valid = FALSE;
142 }
143 values++;
144 }
145
146 return ( values > 0 );
147 }
148
arg_encoded_string_validate(struct sieve_validator * valdtr,struct sieve_ast_argument ** arg,struct sieve_command * cmd)149 bool arg_encoded_string_validate
150 (struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
151 struct sieve_command *cmd)
152 {
153 bool result = TRUE;
154 enum { ST_NONE, ST_OPEN, ST_TYPE, ST_CLOSE }
155 state = ST_NONE;
156 string_t *str = sieve_ast_argument_str(*arg);
157 string_t *tmpstr, *newstr = NULL;
158 const char *p, *mark, *strstart, *substart = NULL;
159 const char *strval = (const char *) str_data(str);
160 const char *strend = strval + str_len(str);
161 unsigned int error_hex = 0;
162
163 T_BEGIN {
164 tmpstr = t_str_new(32);
165
166 p = strval;
167 strstart = p;
168 while ( result && p < strend ) {
169 switch ( state ) {
170 /* Normal string */
171 case ST_NONE:
172 if ( *p == '$' ) {
173 substart = p;
174 state = ST_OPEN;
175 }
176 p++;
177 break;
178 /* Parsed '$' */
179 case ST_OPEN:
180 if ( *p == '{' ) {
181 state = ST_TYPE;
182 p++;
183 } else
184 state = ST_NONE;
185 break;
186 /* Parsed '${' */
187 case ST_TYPE:
188 mark = p;
189 /* Scan for 'hex' or 'unicode' */
190 while ( p < strend && i_isalpha(*p) ) p++;
191
192 if ( *p != ':' ) {
193 state = ST_NONE;
194 break;
195 }
196
197 state = ST_CLOSE;
198
199 str_truncate(tmpstr, 0);
200 if ( strncasecmp(mark, "hex", p - mark) == 0 ) {
201 /* Hexadecimal */
202 p++;
203 if ( !_decode_hex(&p, strend, tmpstr) )
204 state = ST_NONE;
205 } else if ( strncasecmp(mark, "unicode", p - mark) == 0 ) {
206 /* Unicode */
207 p++;
208 if ( !_decode_unicode(&p, strend, tmpstr, &error_hex) )
209 state = ST_NONE;
210 } else {
211 /* Invalid encoding */
212 p++;
213 state = ST_NONE;
214 }
215 break;
216 case ST_CLOSE:
217 if ( *p == '}' ) {
218 /* We now know that the substitution is valid */
219
220 if ( error_hex != 0 ) {
221 sieve_argument_validate_error(valdtr, *arg,
222 "invalid unicode character 0x%08x in encoded character substitution",
223 error_hex);
224 result = FALSE;
225 break;
226 }
227
228 if ( newstr == NULL ) {
229 newstr = str_new(sieve_ast_pool((*arg)->ast), str_len(str)*2);
230 }
231
232 str_append_data(newstr, strstart, substart-strstart);
233 str_append_str(newstr, tmpstr);
234
235 strstart = p + 1;
236 substart = strstart;
237
238 p++;
239 }
240 state = ST_NONE;
241 }
242 }
243 } T_END;
244
245 if ( !result ) return FALSE;
246
247 if ( newstr != NULL ) {
248 if ( strstart != strend )
249 str_append_data(newstr, strstart, strend-strstart);
250
251 sieve_ast_argument_string_set(*arg, newstr);
252 }
253
254 /* Pass the processed string to a (possible) next layer of processing */
255 return sieve_validator_argument_activate_super
256 (valdtr, cmd, *arg, TRUE);
257 }
258
259 /*
260 * Extension implementation
261 */
262
ext_encoded_character_validator_load(const struct sieve_extension * ext,struct sieve_validator * valdtr)263 static bool ext_encoded_character_validator_load
264 (const struct sieve_extension *ext, struct sieve_validator *valdtr)
265 {
266 /* Override the constant string argument with our own */
267 sieve_validator_argument_override
268 (valdtr, SAT_CONST_STRING, ext, &encoded_string_argument);
269
270 return TRUE;
271 }
272