summaryrefslogtreecommitdiffstats
path: root/pigeonhole/src/lib-sieve/ext-encoded-character.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--pigeonhole/src/lib-sieve/ext-encoded-character.c271
1 files changed, 271 insertions, 0 deletions
diff --git a/pigeonhole/src/lib-sieve/ext-encoded-character.c b/pigeonhole/src/lib-sieve/ext-encoded-character.c
new file mode 100644
index 0000000..d9e2b18
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/ext-encoded-character.c
@@ -0,0 +1,271 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extension encoded-character
+ * ---------------------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: RFC 5228
+ * Implementation: full
+ * Status: testing
+ *
+ */
+
+#include "lib.h"
+#include "unichar.h"
+
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+
+#include <ctype.h>
+
+/*
+ * Extension
+ */
+
+static bool ext_encoded_character_validator_load
+ (const struct sieve_extension *ext, struct sieve_validator *valdtr);
+
+const struct sieve_extension_def encoded_character_extension = {
+ .name = "encoded-character",
+ .validator_load = ext_encoded_character_validator_load,
+};
+
+/*
+ * Encoded string argument
+ */
+
+bool arg_encoded_string_validate
+ (struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+ struct sieve_command *context);
+
+const struct sieve_argument_def encoded_string_argument = {
+ .identifier = "@encoded-string",
+ .validate = arg_encoded_string_validate
+};
+
+/* Parsing */
+
+static bool _skip_whitespace
+ (const char **in, const char *inend)
+{
+ while ( *in < inend ) {
+ if ( **in == '\r' ) {
+ (*in)++;
+ if ( **in != '\n' )
+ return FALSE;
+ continue;
+ }
+
+ /* (Loose LF is non-standard) */
+ if ( **in != ' ' && **in != '\n' && **in != '\t' )
+ break;
+
+ (*in)++;
+ }
+
+ return TRUE;
+}
+
+static bool _parse_hexint
+(const char **in, const char *inend, int max_digits, unsigned int *result)
+{
+ int digit = 0;
+ *result = 0;
+
+ while ( *in < inend && (max_digits == 0 || digit < max_digits) ) {
+
+ if ( (**in) >= '0' && (**in) <= '9' )
+ *result = ((*result) << 4) + (**in) - ((unsigned int) '0');
+ else if ( (**in) >= 'a' && (**in) <= 'f' )
+ *result = ((*result) << 4) + (**in) - ((unsigned int) 'a') + 0x0a;
+ else if ( (**in) >= 'A' && (**in) <= 'F' )
+ *result = ((*result) << 4) + (**in) - ((unsigned int) 'A') + 0x0a;
+ else
+ return ( digit > 0 );
+
+ (*in)++;
+ digit++;
+ }
+
+ if ( digit == max_digits ) {
+ /* Hex digit _MUST_ end here */
+ if ( (**in >= '0' && **in <= '9') || (**in >= 'a' && **in <= 'f') ||
+ (**in >= 'A' && **in <= 'F') )
+ return FALSE;
+
+ return TRUE;
+ }
+
+ return ( digit > 0 );
+}
+
+static bool _decode_hex
+(const char **in, const char *inend, string_t *result)
+{
+ int values = 0;
+
+ while ( *in < inend ) {
+ unsigned int hexpair;
+
+ if ( !_skip_whitespace(in, inend) ) return FALSE;
+
+ if ( !_parse_hexint(in, inend, 2, &hexpair) ) break;
+
+ str_append_c(result, (unsigned char) hexpair);
+ values++;
+ }
+
+ return ( values > 0 );
+}
+
+static bool _decode_unicode
+(const char **in, const char *inend, string_t *result,
+ unsigned int *error_hex)
+{
+ int values = 0;
+ bool valid = TRUE;
+
+ while ( *in < inend ) {
+ unsigned int unicode_hex;
+
+ if ( !_skip_whitespace(in, inend) ) return FALSE;
+
+ if ( !_parse_hexint(in, inend, 0, &unicode_hex) ) break;
+
+ if ( uni_is_valid_ucs4((unichar_t) unicode_hex) )
+ uni_ucs4_to_utf8_c((unichar_t) unicode_hex, result);
+ else {
+ if ( valid ) *error_hex = unicode_hex;
+ valid = FALSE;
+ }
+ values++;
+ }
+
+ return ( values > 0 );
+}
+
+bool arg_encoded_string_validate
+(struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+ struct sieve_command *cmd)
+{
+ bool result = TRUE;
+ enum { ST_NONE, ST_OPEN, ST_TYPE, ST_CLOSE }
+ state = ST_NONE;
+ string_t *str = sieve_ast_argument_str(*arg);
+ string_t *tmpstr, *newstr = NULL;
+ const char *p, *mark, *strstart, *substart = NULL;
+ const char *strval = (const char *) str_data(str);
+ const char *strend = strval + str_len(str);
+ unsigned int error_hex = 0;
+
+ T_BEGIN {
+ tmpstr = t_str_new(32);
+
+ p = strval;
+ strstart = p;
+ while ( result && p < strend ) {
+ switch ( state ) {
+ /* Normal string */
+ case ST_NONE:
+ if ( *p == '$' ) {
+ substart = p;
+ state = ST_OPEN;
+ }
+ p++;
+ break;
+ /* Parsed '$' */
+ case ST_OPEN:
+ if ( *p == '{' ) {
+ state = ST_TYPE;
+ p++;
+ } else
+ state = ST_NONE;
+ break;
+ /* Parsed '${' */
+ case ST_TYPE:
+ mark = p;
+ /* Scan for 'hex' or 'unicode' */
+ while ( p < strend && i_isalpha(*p) ) p++;
+
+ if ( *p != ':' ) {
+ state = ST_NONE;
+ break;
+ }
+
+ state = ST_CLOSE;
+
+ str_truncate(tmpstr, 0);
+ if ( strncasecmp(mark, "hex", p - mark) == 0 ) {
+ /* Hexadecimal */
+ p++;
+ if ( !_decode_hex(&p, strend, tmpstr) )
+ state = ST_NONE;
+ } else if ( strncasecmp(mark, "unicode", p - mark) == 0 ) {
+ /* Unicode */
+ p++;
+ if ( !_decode_unicode(&p, strend, tmpstr, &error_hex) )
+ state = ST_NONE;
+ } else {
+ /* Invalid encoding */
+ p++;
+ state = ST_NONE;
+ }
+ break;
+ case ST_CLOSE:
+ if ( *p == '}' ) {
+ /* We now know that the substitution is valid */
+
+ if ( error_hex != 0 ) {
+ sieve_argument_validate_error(valdtr, *arg,
+ "invalid unicode character 0x%08x in encoded character substitution",
+ error_hex);
+ result = FALSE;
+ break;
+ }
+
+ if ( newstr == NULL ) {
+ newstr = str_new(sieve_ast_pool((*arg)->ast), str_len(str)*2);
+ }
+
+ str_append_data(newstr, strstart, substart-strstart);
+ str_append_str(newstr, tmpstr);
+
+ strstart = p + 1;
+ substart = strstart;
+
+ p++;
+ }
+ state = ST_NONE;
+ }
+ }
+ } T_END;
+
+ if ( !result ) return FALSE;
+
+ if ( newstr != NULL ) {
+ if ( strstart != strend )
+ str_append_data(newstr, strstart, strend-strstart);
+
+ sieve_ast_argument_string_set(*arg, newstr);
+ }
+
+ /* Pass the processed string to a (possible) next layer of processing */
+ return sieve_validator_argument_activate_super
+ (valdtr, cmd, *arg, TRUE);
+}
+
+/*
+ * Extension implementation
+ */
+
+static bool ext_encoded_character_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *valdtr)
+{
+ /* Override the constant string argument with our own */
+ sieve_validator_argument_override
+ (valdtr, SAT_CONST_STRING, ext, &encoded_string_argument);
+
+ return TRUE;
+}