summaryrefslogtreecommitdiffstats
path: root/pigeonhole/src/lib-sieve/util/rfc2822.c
diff options
context:
space:
mode:
Diffstat (limited to 'pigeonhole/src/lib-sieve/util/rfc2822.c')
-rw-r--r--pigeonhole/src/lib-sieve/util/rfc2822.c277
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));
+ }
+}