diff options
Diffstat (limited to 'pigeonhole/src/lib-sieve/util/rfc2822.c')
-rw-r--r-- | pigeonhole/src/lib-sieve/util/rfc2822.c | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/pigeonhole/src/lib-sieve/util/rfc2822.c b/pigeonhole/src/lib-sieve/util/rfc2822.c new file mode 100644 index 0000000..ff3a9ad --- /dev/null +++ b/pigeonhole/src/lib-sieve/util/rfc2822.c @@ -0,0 +1,277 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* NOTE: much of the functionality implemented here should eventually appear + * somewhere in Dovecot itself. + */ + +#include "lib.h" +#include "str.h" +#include "unichar.h" + +#include "rfc2822.h" + +#include "message-header-encode.h" + +#include <stdio.h> +#include <ctype.h> + +bool rfc2822_header_field_name_verify +(const char *field_name, unsigned int len) +{ + const char *p = field_name; + const char *pend = p + len; + + /* field-name = 1*ftext + * ftext = %d33-57 / ; Any character except + * %d59-126 ; controls, SP, and + * ; ":". + */ + + while ( p < pend ) { + if ( *p < 33 || *p == ':' ) + return FALSE; + + p++; + } + + return TRUE; +} + +bool rfc2822_header_field_body_verify +(const char *field_body, unsigned int len, bool allow_crlf, bool allow_utf8) +{ + const unsigned char *p = (const unsigned char *)field_body; + const unsigned char *pend = p + len; + bool is8bit = FALSE; + + /* RFC5322: + * + * unstructured = (*([FWS] VCHAR) *WSP) + * VCHAR = %x21-7E + * FWS = ([*WSP CRLF] 1*WSP) / ; Folding white space + * WSP = SP / HTAB ; White space + */ + + while ( p < pend ) { + if ( *p < 0x20 ) { + if ( (*p == '\r' || *p == '\n') ) { + if ( !allow_crlf ) + return FALSE; + } else if ( *p != '\t' ) { + return FALSE; + } + } + + if ( !is8bit && *p > 127 ) { + if ( !allow_utf8 ) + return FALSE; + + is8bit = TRUE; + } + + p++; + } + + if ( is8bit && !uni_utf8_str_is_valid(field_body) ) { + return FALSE; + } + + return TRUE; +} + +/* + * + */ + +const char *rfc2822_header_field_name_sanitize(const char *name) +{ + char *result = t_strdup_noconst(name); + char *p; + + /* Make the whole name lower case ... */ + result = str_lcase(result); + + /* ... except for the first letter and those that follow '-' */ + p = result; + *p = i_toupper(*p); + while ( *p != '\0' ) { + if ( *p == '-' ) { + p++; + + if ( *p != '\0' ) + *p = i_toupper(*p); + + continue; + } + + p++; + } + + return result; +} + +/* + * Message construction + */ + +/* FIXME: This should be collected into a Dovecot API for composing internet + * mail messages. + */ + +unsigned int rfc2822_header_append +(string_t *header, const char *name, const char *body, bool crlf, + uoff_t *body_offset_r) +{ + static const unsigned int max_line = 80; + + const char *bp = body; /* Pointer */ + const char *sp = body; /* Start pointer */ + const char *wp = NULL; /* Whitespace pointer */ + const char *nlp = NULL; /* New-line pointer */ + unsigned int line_len = strlen(name); + unsigned int lines = 0; + + /* Write header field name first */ + str_append(header, name); + str_append(header, ": "); + + if ( body_offset_r != NULL ) + *body_offset_r = str_len(header); + + line_len += 2; + + /* Add field body; fold it if necessary and account for existing folding */ + while ( *bp != '\0' ) { + bool ws_first = TRUE; + + while ( *bp != '\0' && nlp == NULL && + (wp == NULL || line_len < max_line) ) { + if ( *bp == ' ' || *bp == '\t' ) { + if (ws_first) + wp = bp; + ws_first = FALSE; + } else if ( *bp == '\r' || *bp == '\n' ) { + if (ws_first) + nlp = bp; + else + nlp = wp; + break; + } else { + ws_first = TRUE; + } + + bp++; line_len++; + } + + if ( *bp == '\0' ) break; + + /* Existing newline ? */ + if ( nlp != NULL ) { + /* Replace any consecutive newline and whitespace for + consistency */ + while ( *bp == ' ' || *bp == '\t' || *bp == '\r' || *bp == '\n' ) + bp++; + + str_append_data(header, sp, nlp-sp); + + if ( crlf ) + str_append(header, "\r\n"); + else + str_append(header, "\n"); + + while ( *bp == ' ' || *bp == '\t' ) + bp++; + if ( *bp != '\0' ) { + /* Continued line; replace leading whitespace with single TAB */ + str_append_c(header, '\t'); + } + + sp = bp; + } else { + /* Insert newline at last whitespace within the max_line limit */ + i_assert(wp >= sp); + str_append_data(header, sp, wp-sp); + + /* Force continued line; drop any existing whitespace */ + while ( *wp == ' ' || *wp == '\t' ) + wp++; + + if ( crlf ) + str_append(header, "\r\n"); + else + str_append(header, "\n"); + + /* Insert single TAB instead of the original whitespace */ + str_append_c(header, '\t'); + + sp = wp; + if (sp > bp) + bp = sp; + } + + lines++; + + line_len = bp - sp; + wp = NULL; + nlp = NULL; + } + + if ( bp != sp || lines == 0 ) { + str_append_data(header, sp, bp-sp); + if ( crlf ) + str_append(header, "\r\n"); + else + str_append(header, "\n"); + lines++; + } + + return lines; +} + +void rfc2822_header_printf +(string_t *header, const char *name, const char *fmt, ...) +{ + const char *body; + va_list args; + + va_start(args, fmt); + body = t_strdup_vprintf(fmt, args); + va_end(args); + + rfc2822_header_write(header, name, body); +} + +void rfc2822_header_utf8_printf +(string_t *header, const char *name, const char *fmt, ...) +{ + string_t *body = t_str_new(256); + va_list args; + + va_start(args, fmt); + message_header_encode(t_strdup_vprintf(fmt, args), body); + va_end(args); + + rfc2822_header_write(header, name, str_c(body)); +} + + +void rfc2822_header_write_address(string_t *header, + const char *name, const char *address) +{ + bool has_8bit = FALSE; + const char *p; + + for (p = address; *p != '\0'; p++) { + if ((*p & 0x80) != 0) + has_8bit = TRUE; + } + + if (!has_8bit) { + rfc2822_header_write(header, name, address); + } else { + string_t *body = t_str_new(256); + message_header_encode(address, body); + rfc2822_header_write(header, name, str_c(body)); + } +} |